scirs2_neural/utils/
mod.rs1use crate::error::{NeuralError, Result};
8use scirs2_core::ndarray::{Array, Dimension};
9use scirs2_core::numeric::Float;
10use scirs2_core::random::Rng;
11use std::fmt::Debug;
12pub mod colors;
14pub mod datasets;
15pub mod evaluation;
17pub mod initializers;
18pub mod metrics;
19pub mod model_viz;
21pub use colors::{
25 color_legend, colored_metric_cell, colorize, colorize_and_style, colorize_bg, gradient_color,
26 stylize, Color, ColorOptions, Style,
27};
28pub use evaluation::{ConfusionMatrix, FeatureImportance, LearningCurve, ROCCurve};
29pub use initializers::*;
30pub use metrics::*;
31pub use model_viz::{sequential_model_dataflow, sequential_model_summary, ModelVizOptions};
32#[allow(dead_code)]
59pub fn random_normal<F: Float + Debug, R: Rng>(
60 shape: scirs2_core::ndarray::IxDyn,
61 mean: F,
62 std: F,
63 rng: &mut R,
64) -> Result<Array<F, scirs2_core::ndarray::IxDyn>> {
65 let size = shape.as_array_view().iter().product();
66 let mean_f64 = mean.to_f64().ok_or_else(|| {
67 NeuralError::InvalidArchitecture("Failed to convert mean to f64".to_string())
68 })?;
69 let std_f64 = std.to_f64().ok_or_else(|| {
70 NeuralError::InvalidArchitecture("Failed to convert std to f64".to_string())
71 })?;
72 let values: Vec<F> = (0..size)
74 .map(|_| {
75 let u1 = rng.gen_range(0.0..1.0);
77 let u2 = rng.gen_range(0.0..1.0);
78 let z = (-2.0 * u1.ln()).sqrt() * (2.0 * std::f64::consts::PI * u2).cos();
79 let val = mean_f64 + std_f64 * z;
80 F::from(val).ok_or_else(|| {
81 NeuralError::InvalidArchitecture("Failed to convert random value".to_string())
82 })
83 })
84 .collect::<Result<Vec<F>>>()?;
85 Array::from_shape_vec(shape, values)
87 .map_err(|e| NeuralError::InvalidArchitecture(format!("Failed to create array: {e}")))
88}
89#[allow(dead_code)]
102pub fn one_hot_encode<F: Float + Debug>(
103 indices: &scirs2_core::ndarray::Array1<usize>,
104 num_classes: usize,
105) -> Result<scirs2_core::ndarray::Array2<F>> {
106 let n_samples = indices.len();
107 let mut one_hot = scirs2_core::ndarray::Array2::zeros((n_samples, num_classes));
108 for (i, &idx) in indices.iter().enumerate() {
109 if idx >= num_classes {
110 return Err(NeuralError::InvalidArchitecture(format!(
111 "Index {idx} is out of bounds for {num_classes} _classes"
112 )));
113 }
114 one_hot[[i, idx]] = F::one();
115 }
116 Ok(one_hot)
117}
118
119pub type TrainTestSplitResult<F> = (
140 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>, );
145#[allow(dead_code)]
147pub fn train_test_split<F: Float + Debug, R: Rng>(
148 x: &scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>,
149 y: &scirs2_core::ndarray::Array<F, scirs2_core::ndarray::IxDyn>,
150 test_size: f64,
151 shuffle: bool,
152 rng: &mut R,
153) -> Result<TrainTestSplitResult<F>> {
154 if test_size <= 0.0 || test_size >= 1.0 {
155 return Err(NeuralError::InvalidArchitecture(format!(
156 "test_size must be between 0 and 1, got {test_size}"
157 )));
158 }
159
160 let n_samples = x.shape()[0];
161 if n_samples != y.shape()[0] {
162 return Err(NeuralError::InvalidArchitecture(format!(
163 "x and y must have the same number of samples, got {} and {}",
164 n_samples,
165 y.shape()[0]
166 )));
167 }
168
169 let n_test = (n_samples as f64 * test_size).round() as usize;
171 if n_test == 0 || n_test == n_samples {
172 return Err(NeuralError::InvalidArchitecture(format!(
173 "test_size {test_size} results in {n_test} test samples, which is invalid"
174 )));
175 }
176 let n_train = n_samples - n_test;
177 let mut indices: Vec<usize> = (0..n_samples).collect();
179 if shuffle {
180 for i in (1..n_samples).rev() {
182 let j = rng.gen_range(0..=i);
183 indices.swap(i, j);
184 }
185 }
186
187 let train_indices = &indices[..n_train];
189 let test_indices = &indices[n_train..];
190 let mut xshape = x.shape().to_vec();
192 let mut yshape = y.shape().to_vec();
193 xshape[0] = n_train;
194 let mut x_train = scirs2_core::ndarray::Array::zeros(xshape.clone());
195 xshape[0] = n_test;
196 let mut x_test = scirs2_core::ndarray::Array::zeros(xshape);
197 yshape[0] = n_train;
198 let mut y_train = scirs2_core::ndarray::Array::zeros(yshape.clone());
199 yshape[0] = n_test;
200 let mut y_test = scirs2_core::ndarray::Array::zeros(yshape);
201 for (new_idx, &orig_idx) in train_indices.iter().enumerate() {
203 let x_slice = x.slice(scirs2_core::ndarray::s![orig_idx, ..]);
205 let mut x_train_slice = x_train.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
206 x_train_slice.assign(&x_slice);
207 let y_slice = y.slice(scirs2_core::ndarray::s![orig_idx, ..]);
209 let mut y_train_slice = y_train.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
210 y_train_slice.assign(&y_slice);
211 }
212
213 for (new_idx, &orig_idx) in test_indices.iter().enumerate() {
215 let x_slice = x.slice(scirs2_core::ndarray::s![orig_idx, ..]);
217 let mut x_test_slice = x_test.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
218 x_test_slice.assign(&x_slice);
219 let y_slice = y.slice(scirs2_core::ndarray::s![orig_idx, ..]);
221 let mut y_test_slice = y_test.slice_mut(scirs2_core::ndarray::s![new_idx, ..]);
222 y_test_slice.assign(&y_slice);
223 }
224
225 Ok((x_train, x_test, y_train, y_test))
226}