use scirs2_core::ndarray::{s, Array1, Array2, Array3};
use scirs2_core::random::prelude::*;
use scirs2_core::random::rngs::StdRng;
use scirs2_core::random::{Gamma, Normal, StandardNormal};
use sklears_core::error::{Result, SklearsError};
use std::f64::consts::PI;
pub fn make_blobs(
n_samples: usize,
n_features: usize,
centers: usize,
cluster_std: f64,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if n_samples == 0 || n_features == 0 || centers == 0 {
return Err(SklearsError::InvalidInput(
"n_samples, n_features, and centers must be positive".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let mut center_points = Array2::zeros((centers, n_features));
for i in 0..centers {
for j in 0..n_features {
center_points[[i, j]] = rng.gen_range(-10.0..10.0);
}
}
let samples_per_center = n_samples / centers;
let extra_samples = n_samples % centers;
let mut x = Array2::zeros((n_samples, n_features));
let mut y = Array1::zeros(n_samples);
let mut sample_idx = 0;
for center_idx in 0..centers {
let n_samples_for_center = if center_idx < extra_samples {
samples_per_center + 1
} else {
samples_per_center
};
let normal = Normal::new(0.0, cluster_std).expect("operation should succeed");
for _ in 0..n_samples_for_center {
y[sample_idx] = center_idx as i32;
for feature_idx in 0..n_features {
let center_value = center_points[[center_idx, feature_idx]];
let noise: f64 = rng.sample(normal);
x[[sample_idx, feature_idx]] = center_value + noise;
}
sample_idx += 1;
}
}
Ok((x, y))
}
pub fn make_classification(
n_samples: usize,
n_features: usize,
n_informative: usize,
n_redundant: usize,
n_classes: usize,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if n_samples == 0 || n_features == 0 || n_classes < 2 {
return Err(SklearsError::InvalidInput(
"n_samples and n_features must be positive, n_classes must be >= 2".to_string(),
));
}
if n_informative + n_redundant > n_features {
return Err(SklearsError::InvalidInput(
"n_informative + n_redundant cannot exceed n_features".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let normal = StandardNormal;
let mut x = Array2::zeros((n_samples, n_features));
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples {
y[i] = rng.gen_range(0..n_classes) as i32;
}
for i in 0..n_samples {
let class = y[i] as usize;
for j in 0..n_informative {
let class_offset = (class as f64 - (n_classes as f64 - 1.0) / 2.0) * 2.0;
x[[i, j]] = rng.sample::<f64, _>(normal) + class_offset;
}
}
for j in n_informative..(n_informative + n_redundant) {
let informative_idx = rng.gen_range(0..n_informative);
let weight = rng.gen_range(-1.0..1.0);
for i in 0..n_samples {
x[[i, j]] = x[[i, informative_idx]] * weight + rng.sample::<f64, _>(normal) * 0.1;
}
}
for j in (n_informative + n_redundant)..n_features {
for i in 0..n_samples {
x[[i, j]] = rng.sample::<f64, _>(normal);
}
}
Ok((x, y))
}
pub fn make_regression(
n_samples: usize,
n_features: usize,
n_informative: usize,
noise: f64,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<f64>)> {
if n_samples == 0 || n_features == 0 {
return Err(SklearsError::InvalidInput(
"n_samples and n_features must be positive".to_string(),
));
}
if n_informative > n_features {
return Err(SklearsError::InvalidInput(
"n_informative cannot exceed n_features".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let normal = StandardNormal;
let mut x = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
for j in 0..n_features {
x[[i, j]] = rng.sample::<f64, _>(normal);
}
}
let mut coef = Array1::zeros(n_features);
for i in 0..n_informative {
coef[i] = rng.gen_range(-1.0..1.0) * 100.0;
}
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples {
let mut target = 0.0;
for j in 0..n_informative {
target += x[[i, j]] * coef[j];
}
if noise > 0.0 {
let noise_dist = Normal::new(0.0, noise).expect("operation should succeed");
target += rng.sample(noise_dist);
}
y[i] = target;
}
Ok((x, y))
}
pub fn make_circles(
n_samples: usize,
noise: Option<f64>,
factor: Option<f64>,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if n_samples == 0 {
return Err(SklearsError::InvalidInput(
"n_samples must be positive".to_string(),
));
}
let noise = noise.unwrap_or(0.0);
let factor = factor.unwrap_or(0.8);
if factor <= 0.0 || factor >= 1.0 {
return Err(SklearsError::InvalidInput(
"factor must be between 0 and 1".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let n_samples_out = n_samples / 2;
let _n_samples_in = n_samples - n_samples_out;
let mut x = Array2::zeros((n_samples, 2));
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples_out {
let angle = rng.gen() * 2.0 * PI;
x[[i, 0]] = angle.cos();
x[[i, 1]] = angle.sin();
y[i] = 0;
}
for i in n_samples_out..n_samples {
let angle = rng.gen() * 2.0 * PI;
x[[i, 0]] = angle.cos() * factor;
x[[i, 1]] = angle.sin() * factor;
y[i] = 1;
}
if noise > 0.0 {
let noise_dist = Normal::new(0.0, noise).expect("operation should succeed");
for i in 0..n_samples {
for j in 0..2 {
x[[i, j]] += rng.sample(noise_dist);
}
}
}
Ok((x, y))
}
pub fn make_moons(
n_samples: usize,
noise: Option<f64>,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if n_samples == 0 {
return Err(SklearsError::InvalidInput(
"n_samples must be positive".to_string(),
));
}
let noise = noise.unwrap_or(0.0);
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let n_samples_out = n_samples / 2;
let _n_samples_in = n_samples - n_samples_out;
let mut x = Array2::zeros((n_samples, 2));
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples_out {
let angle = rng.gen() * PI;
x[[i, 0]] = angle.cos();
x[[i, 1]] = angle.sin();
y[i] = 0;
}
for i in n_samples_out..n_samples {
let angle = rng.gen() * PI;
x[[i, 0]] = 1.0 - angle.cos();
x[[i, 1]] = 1.0 - angle.sin() - 0.5;
y[i] = 1;
}
if noise > 0.0 {
let noise_dist = Normal::new(0.0, noise).expect("operation should succeed");
for i in 0..n_samples {
for j in 0..2 {
x[[i, j]] += rng.sample(noise_dist);
}
}
}
Ok((x, y))
}
pub fn make_gaussian_quantiles(
mean: Option<Array1<f64>>,
cov: Option<f64>,
n_samples: usize,
n_features: usize,
n_classes: usize,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if n_samples == 0 || n_features == 0 || n_classes < 2 {
return Err(SklearsError::InvalidInput(
"n_samples and n_features must be positive, n_classes must be >= 2".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let normal = StandardNormal;
let cov_value = cov.unwrap_or(1.0);
let mean_vec = mean.unwrap_or_else(|| Array1::zeros(n_features));
if mean_vec.len() != n_features {
return Err(SklearsError::InvalidInput(
"Mean vector length must match n_features".to_string(),
));
}
let mut x = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
for j in 0..n_features {
x[[i, j]] = rng.sample::<f64, _>(normal) * cov_value.sqrt() + mean_vec[j];
}
}
let mut distances: Vec<(f64, usize)> = Vec::with_capacity(n_samples);
for i in 0..n_samples {
let mut distance_squared = 0.0;
for j in 0..n_features {
let diff = x[[i, j]] - mean_vec[j];
distance_squared += diff * diff;
}
distances.push((distance_squared.sqrt() as f64, i));
}
distances.sort_by(|a, b| a.0.partial_cmp(&b.0).expect("operation should succeed"));
let mut y = Array1::zeros(n_samples);
let samples_per_class = n_samples / n_classes;
let extra_samples = n_samples % n_classes;
let mut current_idx = 0;
for class in 0..n_classes {
let n_samples_for_class = if class < extra_samples {
samples_per_class + 1
} else {
samples_per_class
};
for _ in 0..n_samples_for_class {
if current_idx < distances.len() {
let sample_idx = distances[current_idx].1;
y[sample_idx] = class as i32;
current_idx += 1;
}
}
}
Ok((x, y))
}
pub fn make_hastie_10_2(
n_samples: usize,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<i32>)> {
if n_samples == 0 {
return Err(SklearsError::InvalidInput(
"n_samples must be positive".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let normal = StandardNormal;
let n_features = 10;
let mut x = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
for j in 0..n_features {
x[[i, j]] = rng.sample::<f64, _>(normal);
}
}
let mut y = Array1::zeros(n_samples);
let threshold = 9.34;
for i in 0..n_samples {
let mut sum_squares = 0.0;
for j in 0..n_features {
sum_squares += x[[i, j]] * x[[i, j]];
}
y[i] = if sum_squares > threshold { 1 } else { 0 };
}
Ok((x, y))
}
pub fn make_friedman1(
n_samples: usize,
n_features: usize,
noise: f64,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<f64>)> {
if n_samples == 0 || n_features < 5 {
return Err(SklearsError::InvalidInput(
"n_samples must be positive and n_features must be >= 5".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let mut x = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
for j in 0..n_features {
x[[i, j]] = rng.gen();
}
}
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples {
let x1 = x[[i, 0]];
let x2 = x[[i, 1]];
let x3 = x[[i, 2]];
let x4 = x[[i, 3]];
let x5 = x[[i, 4]];
y[i] = 10.0 * (PI * x1 * x2).sin() + 20.0 * (x3 - 0.5).powi(2) + 10.0 * x4 + 5.0 * x5;
if noise > 0.0 {
let noise_dist = Normal::new(0.0, noise).expect("operation should succeed");
y[i] += rng.sample(noise_dist);
}
}
Ok((x, y))
}
pub fn make_friedman2(
n_samples: usize,
noise: f64,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<f64>)> {
if n_samples == 0 {
return Err(SklearsError::InvalidInput(
"n_samples must be positive".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let n_features = 4;
let mut x = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
x[[i, 0]] = rng.gen_range(0.0..100.0); x[[i, 1]] = rng.gen_range(40.0 * PI..560.0 * PI); x[[i, 2]] = rng.gen(); x[[i, 3]] = rng.gen_range(1.0..11.0); }
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples {
let x1 = x[[i, 0]];
let x2 = x[[i, 1]];
let x3 = x[[i, 2]];
let x4 = x[[i, 3]];
let term1 = x1 * x1;
let term2 = x2 * x3 - 1.0 / (x2 * x4);
let term2_squared = term2 * term2;
y[i] = (term1 + term2_squared).sqrt();
if noise > 0.0 {
let noise_dist = Normal::new(0.0, noise).expect("operation should succeed");
y[i] += rng.sample(noise_dist);
}
}
Ok((x, y))
}
pub fn make_friedman3(
n_samples: usize,
noise: f64,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array1<f64>)> {
if n_samples == 0 {
return Err(SklearsError::InvalidInput(
"n_samples must be positive".to_string(),
));
}
let mut rng = if let Some(seed) = random_state {
StdRng::seed_from_u64(seed)
} else {
StdRng::from_rng(&mut scirs2_core::random::thread_rng())
};
let n_features = 4;
let mut x = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
x[[i, 0]] = rng.gen_range(0.0..100.0); x[[i, 1]] = rng.gen_range(40.0 * PI..560.0 * PI); x[[i, 2]] = rng.gen(); x[[i, 3]] = rng.gen_range(1.0..11.0); }
let mut y = Array1::zeros(n_samples);
for i in 0..n_samples {
let x1 = x[[i, 0]];
let x2 = x[[i, 1]];
let x3 = x[[i, 2]];
let x4 = x[[i, 3]];
let numerator = x2 * x3 - 1.0 / (x2 * x4);
let denominator = x1;
y[i] = (numerator / denominator).atan();
if noise > 0.0 {
let noise_dist = Normal::new(0.0, noise).expect("operation should succeed");
y[i] += rng.sample(noise_dist);
}
}
Ok((x, y))
}