use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::random::prelude::*;
use scirs2_core::random::rngs::StdRng;
use scirs2_core::random::{RngExt, StandardNormal};
use sklears_core::error::{Result, SklearsError};
use std::f64::consts::PI;
pub fn make_swiss_roll(
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(),
));
}
if noise < 0.0 {
return Err(SklearsError::InvalidInput(
"noise must be non-negative".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 coordinates = Array2::zeros((n_samples, 3));
let mut intrinsic = Array1::zeros(n_samples);
for i in 0..n_samples {
let t = 1.5 * PI * (1.0 + 2.0 * rng.random::<f64>());
coordinates[[i, 0]] = t * t.cos();
coordinates[[i, 1]] = 21.0 * rng.random::<f64>();
coordinates[[i, 2]] = t * t.sin();
if noise > 0.0 {
coordinates[[i, 0]] += noise * rng.sample::<f64, _>(StandardNormal);
coordinates[[i, 1]] += noise * rng.sample::<f64, _>(StandardNormal);
coordinates[[i, 2]] += noise * rng.sample::<f64, _>(StandardNormal);
}
intrinsic[i] = t;
}
Ok((coordinates, intrinsic))
}
pub fn make_s_curve(
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(),
));
}
if noise < 0.0 {
return Err(SklearsError::InvalidInput(
"noise must be non-negative".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 coordinates = Array2::zeros((n_samples, 3));
let mut intrinsic = Array1::zeros(n_samples);
for i in 0..n_samples {
let t = 3.0 * PI * (rng.random::<f64>() - 0.5);
coordinates[[i, 0]] = t.sin();
coordinates[[i, 1]] = 2.0 * rng.random::<f64>();
coordinates[[i, 2]] = t.signum() * (t.cos() - 1.0);
if noise > 0.0 {
coordinates[[i, 0]] += noise * rng.sample::<f64, _>(StandardNormal);
coordinates[[i, 1]] += noise * rng.sample::<f64, _>(StandardNormal);
coordinates[[i, 2]] += noise * rng.sample::<f64, _>(StandardNormal);
}
intrinsic[i] = t;
}
Ok((coordinates, intrinsic))
}
pub fn make_severed_sphere(
n_samples: usize,
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 mut coordinates = Array2::zeros((n_samples, 3));
let mut intrinsic = Array1::zeros(n_samples);
for i in 0..n_samples {
let p = rng.random_range(0.0..(2.0 * PI - 0.55));
let t = rng.random_range((PI / 8.0)..(PI - PI / 8.0));
coordinates[[i, 0]] = t.sin() * p.cos();
coordinates[[i, 1]] = t.sin() * p.sin();
coordinates[[i, 2]] = t.cos();
intrinsic[i] = t;
}
Ok((coordinates, intrinsic))
}
pub fn make_helix(
n_samples: usize,
n_turns: f64,
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(),
));
}
if n_turns <= 0.0 {
return Err(SklearsError::InvalidInput(
"n_turns must be positive".to_string(),
));
}
if noise < 0.0 {
return Err(SklearsError::InvalidInput(
"noise must be non-negative".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 coordinates = Array2::zeros((n_samples, 3));
let mut intrinsic = Array1::zeros(n_samples);
for i in 0..n_samples {
let t = rng.random::<f64>();
let angle = 2.0 * PI * n_turns * t;
coordinates[[i, 0]] = angle.cos();
coordinates[[i, 1]] = angle.sin();
coordinates[[i, 2]] = t;
if noise > 0.0 {
coordinates[[i, 0]] += noise * rng.sample::<f64, _>(StandardNormal);
coordinates[[i, 1]] += noise * rng.sample::<f64, _>(StandardNormal);
coordinates[[i, 2]] += noise * rng.sample::<f64, _>(StandardNormal);
}
intrinsic[i] = t;
}
Ok((coordinates, intrinsic))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_make_swiss_roll_shape() {
let (coords, target) =
make_swiss_roll(150, 0.0, Some(42)).expect("operation should succeed");
assert_eq!(coords.shape(), &[150, 3]);
assert_eq!(target.len(), 150);
}
#[test]
fn test_make_swiss_roll_deterministic_with_seed() {
let (coords1, target1) =
make_swiss_roll(80, 0.05, Some(7)).expect("operation should succeed");
let (coords2, target2) =
make_swiss_roll(80, 0.05, Some(7)).expect("operation should succeed");
assert_eq!(coords1, coords2);
assert_eq!(target1, target2);
}
#[test]
fn test_make_swiss_roll_invalid_inputs() {
assert!(make_swiss_roll(0, 0.0, Some(42)).is_err());
assert!(make_swiss_roll(10, -1.0, Some(42)).is_err());
}
#[test]
fn test_make_swiss_roll_geometry() {
let (coords, target) =
make_swiss_roll(200, 0.0, Some(3)).expect("operation should succeed");
for i in 0..coords.nrows() {
let xi = coords[[i, 0]];
let zi = coords[[i, 2]];
let ti = target[i];
assert!((xi * xi + zi * zi - ti * ti).abs() < 1e-6);
}
}
#[test]
fn test_make_s_curve_shape() {
let (coords, target) = make_s_curve(150, 0.0, Some(42)).expect("operation should succeed");
assert_eq!(coords.shape(), &[150, 3]);
assert_eq!(target.len(), 150);
}
#[test]
fn test_make_s_curve_deterministic_with_seed() {
let (coords1, target1) = make_s_curve(80, 0.05, Some(7)).expect("operation should succeed");
let (coords2, target2) = make_s_curve(80, 0.05, Some(7)).expect("operation should succeed");
assert_eq!(coords1, coords2);
assert_eq!(target1, target2);
}
#[test]
fn test_make_s_curve_invalid_inputs() {
assert!(make_s_curve(0, 0.0, Some(42)).is_err());
assert!(make_s_curve(10, -1.0, Some(42)).is_err());
}
#[test]
fn test_make_s_curve_geometry() {
let (coords, target) = make_s_curve(200, 0.0, Some(3)).expect("operation should succeed");
for i in 0..coords.nrows() {
let xi = coords[[i, 0]];
assert!(xi.abs() <= 1.0 + 1e-9);
}
let baseline = target[0];
let has_variation = target.iter().any(|&value| (value - baseline).abs() > 1e-9);
assert!(has_variation);
}
#[test]
fn test_make_severed_sphere_shape() {
let (coords, target) =
make_severed_sphere(150, Some(42)).expect("operation should succeed");
assert_eq!(coords.shape(), &[150, 3]);
assert_eq!(target.len(), 150);
}
#[test]
fn test_make_severed_sphere_deterministic_with_seed() {
let (coords1, target1) =
make_severed_sphere(80, Some(7)).expect("operation should succeed");
let (coords2, target2) =
make_severed_sphere(80, Some(7)).expect("operation should succeed");
assert_eq!(coords1, coords2);
assert_eq!(target1, target2);
}
#[test]
fn test_make_severed_sphere_invalid_inputs() {
assert!(make_severed_sphere(0, Some(42)).is_err());
}
#[test]
fn test_make_severed_sphere_geometry() {
let (coords, target) = make_severed_sphere(200, Some(3)).expect("operation should succeed");
for i in 0..coords.nrows() {
let xi = coords[[i, 0]];
let yi = coords[[i, 1]];
let zi = coords[[i, 2]];
let norm = (xi * xi + yi * yi + zi * zi).sqrt();
assert!((norm - 1.0).abs() < 1e-9);
let ti = target[i];
assert!(ti > PI / 8.0);
assert!(ti < PI - PI / 8.0);
}
}
#[test]
fn test_make_helix_shape() {
let (coords, target) =
make_helix(150, 3.0, 0.0, Some(42)).expect("operation should succeed");
assert_eq!(coords.shape(), &[150, 3]);
assert_eq!(target.len(), 150);
}
#[test]
fn test_make_helix_deterministic_with_seed() {
let (coords1, target1) =
make_helix(80, 3.0, 0.05, Some(7)).expect("operation should succeed");
let (coords2, target2) =
make_helix(80, 3.0, 0.05, Some(7)).expect("operation should succeed");
assert_eq!(coords1, coords2);
assert_eq!(target1, target2);
}
#[test]
fn test_make_helix_invalid_inputs() {
assert!(make_helix(0, 3.0, 0.0, Some(42)).is_err());
assert!(make_helix(10, 0.0, 0.0, Some(42)).is_err());
assert!(make_helix(10, 3.0, -1.0, Some(42)).is_err());
}
#[test]
fn test_make_helix_geometry() {
let (coords, target) =
make_helix(200, 4.0, 0.0, Some(3)).expect("operation should succeed");
for i in 0..coords.nrows() {
let xi = coords[[i, 0]];
let yi = coords[[i, 1]];
assert!((xi * xi + yi * yi - 1.0).abs() < 1e-6);
let ti = target[i];
assert!((0.0..1.0).contains(&ti));
}
}
}