use scirs2_core::ndarray::{Array1, Array2, ArrayView1, ArrayView2, Axis, Dimension, Ix1, Ix2};
use scirs2_core::numeric::{Float, FloatConst, FromPrimitive, Zero};
use crate::error::{SciRS2Error, SciRS2Result, check_value};
#[allow(dead_code)]
pub fn linspace<T>(start: T, stop: T, num: usize, endpoint: bool) -> Array1<T>
where
T: Float + FromPrimitive + std::fmt::Debug,
{
if num < 1 {
return Array1::zeros(0);
}
if num == 1 {
return Array1::from_elem(1, start);
}
let mut result = Array1::zeros(num);
let div = if endpoint {
T::from(num - 1).expect("Operation failed")
} else {
T::from(num).expect("Operation failed")
};
let step = (stop - start) / div;
for (i, val) in result.iter_mut().enumerate() {
*val = start + step * T::from(i).expect("Operation failed");
}
result
}
#[allow(dead_code)]
pub fn logspace<T>(start: T, stop: T, num: usize, endpoint: bool, base: T) -> SciRS2Result<Array1<T>>
where
T: Float + FromPrimitive + std::fmt::Debug,
{
check_value(base > T::zero(), "Base must be positive")?;
let log_base = base.ln();
let bases = linspace(start, stop, num, endpoint);
Ok(bases.mapv(|x| (x * log_base).exp()))
}
#[allow(dead_code)]
pub fn geomspace<T>(start: T, stop: T, num: usize, endpoint: bool, base: T) -> SciRS2Result<Array1<T>>
where
T: Float + FromPrimitive + std::fmt::Debug,
{
check_value(start > T::zero() && stop > T::zero(), "Start and stop must be positive")?;
check_value(base > T::zero(), "Base must be positive")?;
let log_base = base.ln();
let log_start = start.ln() / log_base;
let log_stop = stop.ln() / log_base;
let bases = linspace(log_start, log_stop, num, endpoint);
Ok(bases.mapv(|x| (x * log_base).exp()))
}
#[allow(dead_code)]
pub fn allclose<A, D, T>(a: &A, b: &A, rtol: T, atol: T) -> bool
where
A: scirs2_core::ndarray::NdIndex<D>,
D: Dimension,
T: Float,
{
if a.shape() != b.shape() {
return false;
}
for (a_val, b_val) in a.iter().zip(b.iter()) {
let diff = (*a_val - *b_val).abs();
if diff > atol + rtol * b_val.abs() {
return false;
}
}
true
}
#[allow(dead_code)]
pub fn meshgrid<T: Float + Copy>(x: &[T], y: &[T]) -> (scirs2_core::ndarray::Array2<T>, scirs2_core::ndarray::Array2<T>) {
let nx = x.len();
let ny = y.len();
let mut xx = scirs2_core::ndarray::Array2::<T>::zeros((ny, nx));
let mut yy = scirs2_core::ndarray::Array2::<T>::zeros((ny, nx));
for i in 0..ny {
for j in 0..nx {
xx[[i, j]] = x[j];
yy[[i, j]] = y[i];
}
}
(xx, yy)
}
#[allow(dead_code)]
pub fn isclose<T: Float>(a: T, b: T, rtol: T, atol: T) -> bool {
let diff = (a - b).abs();
diff <= atol + rtol * b.abs()
}
#[allow(dead_code)]
pub fn cartesian_product<T: Clone + Copy>(arrays: &[&[T]]) -> Vec<Vec<T>> {
if arrays.is_empty() {
return vec![vec![]];
}
let mut result = vec![];
let mut temp = vec![vec![arrays[0][0]]];
for i in 0..arrays.len() {
result = vec![];
for t in temp.iter() {
for &item in arrays[i].iter() {
let mut new_tuple = t.clone();
new_tuple.push(item);
result.push(new_tuple);
}
}
temp = result.clone();
}
result
}
#[allow(dead_code)]
pub fn arange<T: Float + FromPrimitive>(start: T, stop: T, step: T) -> Array1<T> {
if start >= stop && step > T::zero() || start <= stop && step < T::zero() {
return Array1::zeros(0);
}
let size = ((stop - start) / step).abs().floor().to_usize().unwrap_or(0);
let mut result = Array1::zeros(size);
for i in 0..size {
result[i] = start + T::from(i).expect("Operation failed") * step;
}
result
}
#[allow(dead_code)]
pub fn fill_diagonal<T: Clone>(mut a: Array2<T>, val: T) -> Array2<T> {
let min_dim = a.nrows().min(a.ncols());
for i in 0..min_dim {
a[[i, i]] = val.clone();
}
a
}
#[allow(dead_code)]
pub fn is_symmetric<T: Float>(a: &ArrayView2<T>, tol: T) -> bool {
if a.nrows() != a.ncols() {
return false;
}
for i in 0..a.nrows() {
for j in 0..i {
if (a[[i, j]] - a[[j, i]]).abs() > tol {
return false;
}
}
}
true
}