use crate::error::{NeuralError, Result};
use scirs2_core::ndarray::{Array, Dimension};
use scirs2_core::numeric::{Float, NumAssign};
use scirs2_core::random::{Rng, RngExt};
use std::fmt::Debug;
pub mod colors;
pub mod datasets;
pub mod evaluation;
pub mod initializers;
pub mod metrics;
pub mod model_viz;
pub mod positional_encoding;
pub use colors::{
color_legend, colored_metric_cell, colorize, colorize_and_style, colorize_bg, gradient_color,
stylize, Color, ColorOptions, Style,
};
pub use datasets::{
normalize_features, one_hot_encode as datasets_one_hot_encode, BatchIterator, DataLoader,
Dataset, Normalization,
};
pub use evaluation::{ConfusionMatrix, FeatureImportance, LearningCurve, ROCCurve};
pub use initializers::*;
pub use metrics::*;
pub use model_viz::{sequential_model_dataflow, sequential_model_summary, ModelVizOptions};
pub use positional_encoding::{
LearnedPositionalEncoding, PositionalEncoding, PositionalEncodingFactory,
PositionalEncodingType, RelativePositionalEncoding, RotaryPositionalEncoding,
SinusoidalPositionalEncoding,
};
#[allow(dead_code)]
pub fn random_normal<F: Float + Debug, R: Rng>(
shape: scirs2_core::ndarray::IxDyn,
mean: F,
std: F,
rng: &mut R,
) -> Result<Array<F, scirs2_core::ndarray::IxDyn>> {
let size = shape.as_array_view().iter().product();
let mean_f64 = mean.to_f64().ok_or_else(|| {
NeuralError::InvalidArchitecture("Failed to convert mean to f64".to_string())
})?;
let std_f64 = std.to_f64().ok_or_else(|| {
NeuralError::InvalidArchitecture("Failed to convert std to f64".to_string())
})?;
let values: Vec<F> = (0..size)
.map(|_| {
let u1 = rng.random_range(0.0..1.0);
let u2 = rng.random_range(0.0..1.0);
let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
let val = mean_f64 + std_f64 * z;
F::from(val).ok_or_else(|| {
NeuralError::InvalidArchitecture("Failed to convert random value".to_string())
})
})
.collect::<Result<Vec<F>>>()?;
Array::from_shape_vec(shape, values)
.map_err(|e| NeuralError::InvalidArchitecture(format!("Failed to create array: {e}")))
}
#[allow(dead_code)]
pub fn one_hot_encode<F: Float + Debug + NumAssign>(
indices: &scirs2_core::ndarray::Array1<usize>,
num_classes: usize,
) -> Result<scirs2_core::ndarray::Array2<F>> {
let n_samples = indices.len();
let mut one_hot = scirs2_core::ndarray::Array2::zeros((n_samples, num_classes));
for (i, &idx) in indices.iter().enumerate() {
if idx >= num_classes {
return Err(NeuralError::InvalidArchitecture(format!(
"Index {idx} is out of bounds for {num_classes} _classes"
)));
}
one_hot[[i, idx]] = F::one();
}
Ok(one_hot)
}
pub type TrainTestSplitResult<F> = (
scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>, scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>, scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>, scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>, );
#[allow(dead_code)]
pub fn train_test_split<F: Float + Debug, R: Rng>(
x: &scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>,
y: &scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>,
test_size: f64,
shuffle: bool,
rng: &mut R,
) -> Result<TrainTestSplitResult<F>> {
if test_size <= 0.0 || test_size >= 1.0 {
return Err(NeuralError::InvalidArchitecture(format!(
"test_size must be between 0 and 1, got {test_size}"
)));
}
let n_samples = x.shape()[0];
if n_samples != y.shape()[0] {
return Err(NeuralError::InvalidArchitecture(format!(
"x and y must have the same number of samples, got {} and {}",
n_samples,
y.shape()[0]
)));
}
let n_test = (n_samples as f64 * test_size).round() as usize;
if n_test == 0 || n_test == n_samples {
return Err(NeuralError::InvalidArchitecture(format!(
"test_size {test_size} results in {n_test} test samples, which is invalid"
)));
}
let n_train = n_samples - n_test;
let mut indices: Vec<usize> = (0..n_samples).collect();
if shuffle {
for i in (1..n_samples).rev() {
let j = rng.random_range(0..=i);
indices.swap(i, j);
}
}
let train_indices = &indices[..n_train];
let test_indices = &indices[n_train..];
let mut xshape = x.shape().to_vec();
let mut yshape = y.shape().to_vec();
xshape[0] = n_train;
let mut x_train = scirs2_core::ndarray::Array::zeros(xshape.clone());
xshape[0] = n_test;
let mut x_test = scirs2_core::ndarray::Array::zeros(xshape);
yshape[0] = n_train;
let mut y_train = scirs2_core::ndarray::Array::zeros(yshape.clone());
yshape[0] = n_test;
let mut y_test = scirs2_core::ndarray::Array::zeros(yshape);
for (new_idx, &orig_idx) in train_indices.iter().enumerate() {
let x_slice = x.slice(scirs2_core::ndarray::s![orig_idx, ..]);
let mut x_train_slice = x_train.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
x_train_slice.assign(&x_slice);
let y_slice = y.slice(scirs2_core::ndarray::s![orig_idx, ..]);
let mut y_train_slice = y_train.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
y_train_slice.assign(&y_slice);
}
for (new_idx, &orig_idx) in test_indices.iter().enumerate() {
let x_slice = x.slice(scirs2_core::ndarray::s![orig_idx, ..]);
let mut x_test_slice = x_test.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
x_test_slice.assign(&x_slice);
let y_slice = y.slice(scirs2_core::ndarray::s![orig_idx, ..]);
let mut y_test_slice = y_test.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
y_test_slice.assign(&y_slice);
}
Ok((x_train, x_test, y_train, y_test))
}