use super::config::{ManifoldConfig, ManifoldType};
use crate::error::{DatasetsError, Result};
use crate::utils::Dataset;
use scirs2_core::ndarray::{Array1, Array2};
use scirs2_core::random::prelude::*;
use scirs2_core::random::rand_distributions::{Distribution, Uniform};
use std::f64::consts::PI;
#[allow(dead_code)]
pub fn make_swiss_roll(n_samples: usize, noise: f64, randomseed: Option<u64>) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut color = Array1::zeros(n_samples);
let normal = if noise > 0.0 {
Some(scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed"))
} else {
None
};
for i in 0..n_samples {
let t = 1.5 * PI * (1.0 + 2.0 * i as f64 / n_samples as f64);
let height = 21.0 * i as f64 / n_samples as f64;
let mut x = t * t.cos();
let mut y = height;
let mut z = t * t.sin();
if let Some(ref normal_dist) = normal {
x += normal_dist.sample(&mut rng);
y += normal_dist.sample(&mut rng);
z += normal_dist.sample(&mut rng);
}
data[[i, 0]] = x;
data[[i, 1]] = y;
data[[i, 2]] = z;
color[i] = t; }
let mut dataset = Dataset::new(data, Some(color));
dataset = dataset
.with_featurenames(vec!["x".to_string(), "y".to_string(), "z".to_string()])
.with_description("Swiss roll manifold dataset for dimensionality reduction".to_string())
.with_metadata("noise", &noise.to_string())
.with_metadata("dimensions", "3")
.with_metadata("manifold_dim", "2");
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_s_curve(n_samples: usize, noise: f64, randomseed: Option<u64>) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut color = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
for i in 0..n_samples {
let t = 4.0 * PI * (i as f64) / (n_samples as f64 - 1.0);
data[[i, 0]] = t.sin() + noise_dist.sample(&mut rng);
data[[i, 1]] = 2.0 * t + noise_dist.sample(&mut rng);
data[[i, 2]] = (t / 2.0).sin() + noise_dist.sample(&mut rng);
color[i] = t;
}
let mut dataset = Dataset::new(data, Some(color));
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_description("S-curve manifold embedded in 3D space".to_string());
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_swiss_roll_advanced(
n_samples: usize,
noise: f64,
hole: bool,
randomseed: Option<u64>,
) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut color = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
let uniform = scirs2_core::random::Uniform::new(0.0, 1.0).expect("Operation failed");
for i in 0..n_samples {
let mut t = uniform.sample(&mut rng) * 3.0 * PI / 2.0;
let mut y = uniform.sample(&mut rng) * 20.0;
if hole {
while t > PI / 2.0 && t < PI && y > 8.0 && y < 12.0 {
t = uniform.sample(&mut rng) * 3.0 * PI / 2.0;
y = uniform.sample(&mut rng) * 20.0;
}
}
data[[i, 0]] = t * t.cos() + noise_dist.sample(&mut rng);
data[[i, 1]] = y + noise_dist.sample(&mut rng);
data[[i, 2]] = t * t.sin() + noise_dist.sample(&mut rng);
color[i] = t;
}
let mut dataset = Dataset::new(data, Some(color));
let description = if hole {
"Swiss roll manifold with hole embedded in 3D space"
} else {
"Swiss roll manifold embedded in 3D space"
};
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_description(description.to_string());
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_severed_sphere(
n_samples: usize,
noise: f64,
randomseed: Option<u64>,
) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut color = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
let uniform = scirs2_core::random::Uniform::new(0.0, 1.0).expect("Operation failed");
for i in 0..n_samples {
let mut phi = uniform.sample(&mut rng) * 2.0 * PI; let mut theta = uniform.sample(&mut rng) * PI;
while phi > PI / 3.0 && phi < 2.0 * PI / 3.0 && theta > PI / 3.0 && theta < 2.0 * PI / 3.0 {
phi = uniform.sample(&mut rng) * 2.0 * PI;
theta = uniform.sample(&mut rng) * PI;
}
let radius = 1.0;
data[[i, 0]] = radius * theta.sin() * phi.cos() + noise_dist.sample(&mut rng);
data[[i, 1]] = radius * theta.sin() * phi.sin() + noise_dist.sample(&mut rng);
data[[i, 2]] = radius * theta.cos() + noise_dist.sample(&mut rng);
color[i] = phi;
}
let mut dataset = Dataset::new(data, Some(color));
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_description("Severed sphere manifold with discontinuities".to_string());
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_twin_peaks(n_samples: usize, noise: f64, randomseed: Option<u64>) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut labels = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
let uniform = scirs2_core::random::Uniform::new(-2.0, 2.0).expect("Operation failed");
for i in 0..n_samples {
let x = uniform.sample(&mut rng);
let y = uniform.sample(&mut rng);
let peak1 = (-(((x as f64) - 1.0).powi(2) + ((y as f64) - 1.0).powi(2))).exp();
let peak2 = (-(((x as f64) + 1.0).powi(2) + ((y as f64) + 1.0).powi(2))).exp();
let z = peak1 + peak2 + noise_dist.sample(&mut rng);
data[[i, 0]] = x;
data[[i, 1]] = y;
data[[i, 2]] = z;
labels[i] = if ((x as f64) - 1.0).powi(2) + ((y as f64) - 1.0).powi(2)
< ((x as f64) + 1.0).powi(2) + ((y as f64) + 1.0).powi(2)
{
0.0
} else {
1.0
};
}
let mut dataset = Dataset::new(data, Some(labels));
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_targetnames(vec!["peak_0".to_string(), "peak_1".to_string()])
.with_description("Twin peaks manifold with two connected Gaussian peaks".to_string());
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_helix(
n_samples: usize,
n_turns: f64,
noise: f64,
randomseed: Option<u64>,
) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if n_turns <= 0.0 {
return Err(DatasetsError::InvalidFormat(
"n_turns must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut color = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
for i in 0..n_samples {
let t = n_turns * 2.0 * PI * (i as f64) / (n_samples as f64 - 1.0);
data[[i, 0]] = t.cos() + noise_dist.sample(&mut rng);
data[[i, 1]] = t.sin() + noise_dist.sample(&mut rng);
data[[i, 2]] = t / (n_turns * 2.0 * PI) + noise_dist.sample(&mut rng);
color[i] = t;
}
let mut dataset = Dataset::new(data, Some(color));
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_description(format!("Helix manifold with {n_turns} _turns in 3D space"));
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_intersecting_manifolds(
n_samples: usize,
noise: f64,
randomseed: Option<u64>,
) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let samples_per_manifold = n_samples / 2;
let remainder = n_samples % 2;
let mut data = Array2::zeros((n_samples, 3));
let mut labels = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
let uniform = scirs2_core::random::Uniform::new(-2.0, 2.0).expect("Operation failed");
let mut sample_idx = 0;
for _ in 0..samples_per_manifold + remainder {
let x = uniform.sample(&mut rng);
let y = uniform.sample(&mut rng);
let z = x + noise_dist.sample(&mut rng);
data[[sample_idx, 0]] = x;
data[[sample_idx, 1]] = y;
data[[sample_idx, 2]] = z;
labels[sample_idx] = 0.0;
sample_idx += 1;
}
for _ in 0..samples_per_manifold {
let x = uniform.sample(&mut rng);
let y = uniform.sample(&mut rng);
let z = -x + noise_dist.sample(&mut rng);
data[[sample_idx, 0]] = x;
data[[sample_idx, 1]] = y;
data[[sample_idx, 2]] = z;
labels[sample_idx] = 1.0;
sample_idx += 1;
}
let mut dataset = Dataset::new(data, Some(labels));
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_targetnames(vec!["manifold_0".to_string(), "manifold_1".to_string()])
.with_description("Two intersecting plane manifolds in 3D space".to_string());
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_torus(
n_samples: usize,
major_radius: f64,
minor_radius: f64,
noise: f64,
randomseed: Option<u64>,
) -> Result<Dataset> {
if n_samples == 0 {
return Err(DatasetsError::InvalidFormat(
"n_samples must be > 0".to_string(),
));
}
if major_radius <= 0.0 || minor_radius <= 0.0 {
return Err(DatasetsError::InvalidFormat(
"major_radius and minor_radius must be > 0".to_string(),
));
}
if minor_radius >= major_radius {
return Err(DatasetsError::InvalidFormat(
"minor_radius must be < major_radius".to_string(),
));
}
if noise < 0.0 {
return Err(DatasetsError::InvalidFormat(
"noise must be >= 0".to_string(),
));
}
let mut rng = match randomseed {
Some(_seed) => StdRng::seed_from_u64(_seed),
None => {
let mut r = thread_rng();
StdRng::seed_from_u64(r.next_u64())
}
};
let mut data = Array2::zeros((n_samples, 3));
let mut color = Array1::zeros(n_samples);
let noise_dist = scirs2_core::random::Normal::new(0.0, noise).expect("Operation failed");
let uniform = scirs2_core::random::Uniform::new(0.0, 2.0 * PI).expect("Operation failed");
for i in 0..n_samples {
let theta = uniform.sample(&mut rng); let phi = uniform.sample(&mut rng);
data[[i, 0]] =
(major_radius + minor_radius * phi.cos()) * theta.cos() + noise_dist.sample(&mut rng);
data[[i, 1]] =
(major_radius + minor_radius * phi.cos()) * theta.sin() + noise_dist.sample(&mut rng);
data[[i, 2]] = minor_radius * phi.sin() + noise_dist.sample(&mut rng);
color[i] = theta;
}
let mut dataset = Dataset::new(data, Some(color));
dataset = dataset
.with_featurenames(vec!["X".to_string(), "Y".to_string(), "Z".to_string()])
.with_description(format!(
"Torus manifold with major _radius {major_radius} and minor _radius {minor_radius}"
));
Ok(dataset)
}
#[allow(dead_code)]
pub fn make_manifold(config: ManifoldConfig) -> Result<Dataset> {
match config.manifold_type {
ManifoldType::SCurve => make_s_curve(config.n_samples, config.noise, config.randomseed),
ManifoldType::SwissRoll { hole } => {
make_swiss_roll_advanced(config.n_samples, config.noise, hole, config.randomseed)
}
ManifoldType::SeveredSphere => {
make_severed_sphere(config.n_samples, config.noise, config.randomseed)
}
ManifoldType::TwinPeaks => {
make_twin_peaks(config.n_samples, config.noise, config.randomseed)
}
ManifoldType::Helix { n_turns } => {
make_helix(config.n_samples, n_turns, config.noise, config.randomseed)
}
ManifoldType::IntersectingManifolds => {
make_intersecting_manifolds(config.n_samples, config.noise, config.randomseed)
}
ManifoldType::Torus {
major_radius,
minor_radius,
} => make_torus(
config.n_samples,
major_radius,
minor_radius,
config.noise,
config.randomseed,
),
}
}