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_low_rank_matrix(
n_samples: usize,
n_features: usize,
effective_rank: usize,
tail_strength: f64,
random_state: Option<u64>,
) -> Result<Array2<f64>> {
if n_samples == 0 || n_features == 0 {
return Err(SklearsError::InvalidInput(
"n_samples and n_features must be positive".to_string(),
));
}
if effective_rank == 0 || effective_rank > std::cmp::min(n_samples, n_features) {
return Err(SklearsError::InvalidInput(
"effective_rank must be positive and <= min(n_samples, n_features)".to_string(),
));
}
if tail_strength < 0.0 || tail_strength > 1.0 {
return Err(SklearsError::InvalidInput(
"tail_strength 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 normal = StandardNormal;
let min_dim = std::cmp::min(n_samples, n_features);
let mut u = Array2::zeros((n_samples, effective_rank));
let mut v = Array2::zeros((effective_rank, n_features));
for i in 0..n_samples {
for j in 0..effective_rank {
u[[i, j]] = rng.sample::<f64, _>(normal);
}
}
for i in 0..effective_rank {
for j in 0..n_features {
v[[i, j]] = rng.sample::<f64, _>(normal);
}
}
let mut singular_values = Array1::zeros(effective_rank);
for i in 0..effective_rank {
let decay_factor = (-tail_strength * i as f64).exp();
singular_values[i] = 10.0 * decay_factor + 0.1; }
let mut result = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
for j in 0..n_features {
let mut sum = 0.0;
for k in 0..effective_rank {
sum += u[[i, k]] * singular_values[k] * v[[k, j]];
}
result[[i, j]] = sum;
}
}
Ok(result)
}
pub fn make_sparse_coded_signal(
n_samples: usize,
n_components: usize,
n_features: usize,
n_nonzero_coefs: usize,
random_state: Option<u64>,
) -> Result<(Array2<f64>, Array2<f64>, Array2<f64>)> {
if n_samples == 0 || n_components == 0 || n_features == 0 {
return Err(SklearsError::InvalidInput(
"n_samples, n_components, and n_features must be positive".to_string(),
));
}
if n_nonzero_coefs == 0 || n_nonzero_coefs > n_components {
return Err(SklearsError::InvalidInput(
"n_nonzero_coefs must be positive and <= n_components".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 dictionary = Array2::zeros((n_features, n_components));
for i in 0..n_features {
for j in 0..n_components {
dictionary[[i, j]] = rng.sample::<f64, _>(normal);
}
}
for j in 0..n_components {
let mut col_norm = 0.0;
for i in 0..n_features {
col_norm += dictionary[[i, j]] * dictionary[[i, j]];
}
col_norm = col_norm.sqrt();
if col_norm > 1e-12 {
for i in 0..n_features {
dictionary[[i, j]] /= col_norm;
}
}
}
let mut code = Array2::zeros((n_samples, n_components));
for i in 0..n_samples {
let mut available_components: Vec<usize> = (0..n_components).collect();
for j in (n_nonzero_coefs..available_components.len()).rev() {
let k = rng.gen_range(0..j + 1);
available_components.swap(j, k);
}
for &comp_idx in available_components.iter().take(n_nonzero_coefs) {
code[[i, comp_idx]] = rng.sample::<f64, _>(normal);
}
}
let mut signal = Array2::zeros((n_samples, n_features));
for i in 0..n_samples {
for j in 0..n_features {
let mut sum = 0.0;
for k in 0..n_components {
sum += code[[i, k]] * dictionary[[j, k]];
}
signal[[i, j]] = sum;
}
}
Ok((signal, code, dictionary))
}
pub fn make_sparse_spd_matrix(
n_dim: usize,
alpha: f64,
norm_diag: bool,
smallest_coef: f64,
largest_coef: f64,
random_state: Option<u64>,
) -> Result<Array2<f64>> {
if n_dim == 0 {
return Err(SklearsError::InvalidInput(
"n_dim must be positive".to_string(),
));
}
if alpha < 0.0 || alpha > 1.0 {
return Err(SklearsError::InvalidInput(
"alpha must be between 0 and 1".to_string(),
));
}
if smallest_coef >= largest_coef {
return Err(SklearsError::InvalidInput(
"smallest_coef must be < largest_coef".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 matrix = Array2::eye(n_dim);
let n_off_diag = ((n_dim * (n_dim - 1)) as f64 * alpha / 2.0) as usize;
let mut positions = Vec::new();
for i in 0..n_dim {
for j in (i + 1)..n_dim {
positions.push((i, j));
}
}
for _ in 0..n_off_diag {
if !positions.is_empty() {
let idx = rng.gen_range(0..positions.len());
let (i, j) = positions.swap_remove(idx);
let coef = rng.gen_range(smallest_coef..largest_coef);
matrix[[i, j]] = coef;
matrix[[j, i]] = coef; }
}
let mut row_sums = Array1::zeros(n_dim);
for i in 0..n_dim {
let mut sum = 0.0;
for j in 0..n_dim {
if i != j {
sum += matrix[[i, j]].abs();
}
}
row_sums[i] = sum;
}
for i in 0..n_dim {
matrix[[i, i]] = row_sums[i] + (largest_coef - smallest_coef).abs() + 0.1;
}
if norm_diag {
for i in 0..n_dim {
matrix[[i, i]] = 1.0;
}
let min_eigenvalue = 0.01;
for i in 0..n_dim {
matrix[[i, i]] += min_eigenvalue;
}
}
Ok(matrix)
}
pub fn make_spd_matrix(n_dim: usize, random_state: Option<u64>) -> Result<Array2<f64>> {
if n_dim == 0 {
return Err(SklearsError::InvalidInput(
"n_dim 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 mut a = Array2::zeros((n_dim, n_dim));
for i in 0..n_dim {
for j in 0..n_dim {
a[[i, j]] = rng.sample::<f64, _>(normal);
}
}
let mut spd = Array2::zeros((n_dim, n_dim));
for i in 0..n_dim {
for j in 0..n_dim {
let mut sum = 0.0;
for k in 0..n_dim {
sum += a[[k, i]] * a[[k, j]];
}
spd[[i, j]] = sum;
}
}
let min_eigenvalue = 0.01;
for i in 0..n_dim {
spd[[i, i]] += min_eigenvalue;
}
Ok(spd)
}