use crate::{NumExt, core::Array};
use std::fmt::Debug; pub mod matmul;
pub mod ml;
pub mod stat;
use num_traits::{Float, Pow};
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum MatmulPolicy {
Naive,
Faer,
#[cfg(feature = "blas")]
Blas,
LoopReorder,
#[cfg(target_arch = "aarch64")]
LoopRecorderSimd,
Blocking(usize),
}
impl Default for MatmulPolicy {
fn default() -> Self {
#[cfg(target_os = "macos")]
return Self::Blas;
#[cfg(not(target_os = "macos"))]
return Self::Faer;
}
}
impl<const D: usize, T: NumExt> Array<D, T> {
pub fn pow<U, O>(&self, exponent: U) -> Array<D, O>
where
T: Pow<U, Output = O>,
U: NumExt,
O: NumExt,
{
self.map(|x| x.pow(exponent.clone()))
}
}
macro_rules! unary_ops {
($($(#[$meta:meta])* fn $id:ident)+) => {
$($(#[$meta])*
#[must_use = "method returns a new array and does not mutate the original value"]
pub fn $id(&self) -> Array<D, T> {
self.mapv(T::$id)
})+
};
}
macro_rules! binary_ops {
($($(#[$meta:meta])* fn $id:ident($ty:ty))+) => {
$($(#[$meta])*
#[must_use = "method returns a new array and does not mutate the original value"]
pub fn $id(&self, rhs: $ty) -> Array<D, T> {
self.mapv(|v| T::$id(v, rhs))
})+
};
}
impl<const D: usize, T: NumExt + Float> Array<D, T> {
unary_ops! {
fn floor
fn ceil
fn round
fn trunc
fn fract
fn abs
fn signum
fn recip
fn sqrt
fn exp
fn exp2
fn exp_m1
fn ln
fn log2
fn log10
fn ln_1p
fn cbrt
fn sin
fn cos
fn tan
fn asin
fn acos
fn atan
fn sinh
fn cosh
fn tanh
fn asinh
fn acosh
fn atanh
fn to_degrees
fn to_radians
}
binary_ops! {
fn powi(i32)
fn powf(T)
fn log(T)
fn abs_sub(T)
fn hypot(T)
}
pub fn pow2(&self) -> Array<D, T> {
self.mapv(|v| v * v)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_map() {
let arr = Array::from_vec(vec![1.0, 2.0, 3.0, 4.0], [2, 2]);
let squared = arr.map(|x| x * x);
assert_eq!(squared[[0, 0]], 1.0);
assert_eq!(squared[[1, 1]], 16.0);
}
#[test]
fn test_map_type_conversion() {
let arr = Array::from_vec(vec![1, 2, 3, 4], [2, 2]);
let result: Array<_, f64> = arr.map(|&x| x as f64 * 1.5);
assert_eq!(result[[0, 0]], 1.5);
assert_eq!(result[[1, 1]], 6.0);
}
#[test]
fn test_into_map() {
let arr = Array::from_vec(vec![1, 2, 3, 4], [2, 2]);
let result: Array<_, String> = arr.map_into(|x| format!("value_{}", x));
assert_eq!(result[[0, 0]], "value_1");
assert_eq!(result[[1, 1]], "value_4");
assert_eq!(result.shape(), [2, 2]);
}
#[test]
fn test_into_map_type_conversion() {
let arr = Array::from_vec(vec![1, 2, 3, 4], [2, 2]);
let result: Array<_, f64> = arr.map_into(|x| x as f64 * 2.5);
assert_eq!(result[[0, 0]], 2.5);
assert_eq!(result[[1, 1]], 10.0);
assert_eq!(result.shape(), [2, 2]);
}
#[test]
fn test_trigonometric_functions() {
let arr = Array::from_vec(
vec![
0.0,
std::f64::consts::PI / 6.0,
std::f64::consts::PI / 4.0,
std::f64::consts::PI / 3.0,
std::f64::consts::PI / 2.0,
],
[5],
);
let sin_result = arr.sin();
assert!((sin_result[[0]] - 0.0).abs() < 1e-10);
assert!((sin_result[[1]] - 0.5).abs() < 1e-10);
assert!((sin_result[[4]] - 1.0).abs() < 1e-10);
let cos_result = arr.cos();
assert!((cos_result[[0]] - 1.0).abs() < 1e-10);
assert!((cos_result[[4]] - 0.0).abs() < 1e-10);
let _tan_result = arr.tan();
let values = Array::from_vec(vec![0.0, 0.5, 1.0], [3]);
let asin_result = values.asin();
let acos_result = values.acos();
let atan_result = values.atan();
assert!((asin_result[[0]] - 0.0f64).abs() < 1e-10f64);
assert!((asin_result[[2]] - std::f64::consts::PI / 2.0).abs() < 1e-10);
assert!((acos_result[[0]] - std::f64::consts::PI / 2.0).abs() < 1e-10);
assert!((acos_result[[2]] - 0.0).abs() < 1e-10);
assert!((atan_result[[0]] - 0.0).abs() < 1e-10);
}
#[test]
fn test_hyperbolic_functions() {
let arr = Array::from_vec(vec![0.0, 1.0, -1.0], [3]);
let sinh_result = arr.sinh();
let cosh_result = arr.cosh();
let tanh_result = arr.tanh();
assert!((sinh_result[[0]] - 0.0f64).abs() < 1e-10f64);
assert!((cosh_result[[0]] - 1.0f64).abs() < 1e-10f64);
assert!((tanh_result[[0]] - 0.0f64).abs() < 1e-10f64);
}
#[test]
fn test_logarithmic_functions() {
let arr = Array::from_vec(vec![1.0, std::f64::consts::E, 10.0, 2.0], [4]);
let ln_result = arr.ln();
assert!((ln_result[[0]] - 0.0).abs() < 1e-10); assert!((ln_result[[1]] - 1.0).abs() < 1e-10);
let log10_result = arr.log10();
assert!((log10_result[[0]] - 0.0).abs() < 1e-10); assert!((log10_result[[2]] - 1.0).abs() < 1e-10);
let log2_result = arr.log2();
assert!((log2_result[[0]] - 0.0).abs() < 1e-10); assert!((log2_result[[3]] - 1.0).abs() < 1e-10);
let values = Array::from_vec(vec![1.0, 3.0, 9.0, 27.0], [4]);
let log3_result = values.log(3.0);
assert!((log3_result[[0]] - 0.0f64).abs() < 1e-10f64); assert!((log3_result[[1]] - 1.0f64).abs() < 1e-10f64); assert!((log3_result[[2]] - 2.0f64).abs() < 1e-10f64); assert!((log3_result[[3]] - 3.0f64).abs() < 1e-10f64);
let test_values = Array::from_vec(vec![1.0, 2.0, 3.0], [3]);
let exp_ln = test_values.clone().ln().exp();
for i in 0..3 {
assert!(((exp_ln[[i]] - test_values[[i]]) as f64).abs() < 1e-10);
}
}
#[test]
fn test_exponential_functions() {
let arr = Array::from_vec(vec![0.0, 1.0, 2.0], [3]);
let exp_result = arr.exp();
assert!((exp_result[[0]] - 1.0f64).abs() < 1e-10f64); assert!((exp_result[[1]] - std::f64::consts::E).abs() < 1e-10);
let exp2_result = arr.exp2();
assert!((exp2_result[[0]] - 1.0).abs() < 1e-10); assert!((exp2_result[[1]] - 2.0).abs() < 1e-10); assert!((exp2_result[[2]] - 4.0).abs() < 1e-10);
let small_values = Array::from_vec(vec![0.1, 0.01, 0.001], [3]);
let exp_m1_result = small_values.clone().exp_m1();
let ln_1p_result = exp_m1_result.ln_1p();
for i in 0..3 {
assert!(((ln_1p_result[[i]] - small_values[[i]]) as f64).abs() < 1e-10);
}
let zero_arr = Array::from_vec(vec![0.0], [1]);
assert!((zero_arr.clone().exp_m1()[[0]] - 0.0f64).abs() < 1e-10f64);
assert!((zero_arr.ln_1p()[[0]] - 0.0f64).abs() < 1e-10f64);
}
}