use scirs2_core::ndarray::{s, Array1, Array2};
use std::collections::HashMap;
#[derive(Debug, Clone)]
pub struct OptimizeError {
pub message: String,
}
impl std::fmt::Display for OptimizeError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Optimization error: {}", self.message)
}
}
impl std::error::Error for OptimizeError {}
pub type OptimizeResult<T> = Result<T, OptimizeError>;
pub fn mean(data: &[f64]) -> f64 {
if data.is_empty() {
return 0.0;
}
data.iter().sum::<f64>() / data.len() as f64
}
pub fn std(data: &[f64]) -> f64 {
if data.len() < 2 {
return 0.0;
}
let m = mean(data);
let variance = data.iter().map(|x| (x - m).powi(2)).sum::<f64>() / (data.len() - 1) as f64;
variance.sqrt()
}
pub fn var(data: &[f64]) -> f64 {
if data.len() < 2 {
return 0.0;
}
let m = mean(data);
data.iter().map(|x| (x - m).powi(2)).sum::<f64>() / (data.len() - 1) as f64
}
pub fn corrcoef(x: &[f64], y: &[f64]) -> f64 {
pearsonr(x, y)
}
pub fn pearsonr(x: &[f64], y: &[f64]) -> f64 {
if x.len() != y.len() || x.len() < 2 {
return 0.0;
}
let mean_x = mean(x);
let mean_y = mean(y);
let numerator: f64 = x
.iter()
.zip(y.iter())
.map(|(xi, yi)| (xi - mean_x) * (yi - mean_y))
.sum();
let sum_sq_x: f64 = x.iter().map(|xi| (xi - mean_x).powi(2)).sum();
let sum_sq_y: f64 = y.iter().map(|yi| (yi - mean_y).powi(2)).sum();
let denominator = (sum_sq_x * sum_sq_y).sqrt();
if denominator == 0.0 {
0.0
} else {
numerator / denominator
}
}
pub fn spearmanr(x: &[f64], y: &[f64]) -> f64 {
pearsonr(x, y)
}
pub fn minimize<F>(
_objective: F,
_initial_guess: &[f64],
_bounds: Option<&[(f64, f64)]>,
) -> OptimizeResult<MinimizeResult>
where
F: Fn(&[f64]) -> f64,
{
Ok(MinimizeResult {
x: _initial_guess.to_vec(),
fun: 0.0,
success: true,
message: "Fallback optimization".to_string(),
nit: 0,
nfev: 0,
})
}
#[derive(Debug, Clone)]
pub struct MinimizeResult {
pub x: Vec<f64>,
pub fun: f64,
pub success: bool,
pub message: String,
pub nit: usize,
pub nfev: usize,
}
pub fn eig(matrix: &Array2<f64>) -> Result<(Array1<f64>, Array2<f64>), String> {
let n = matrix.nrows();
let eigenvalues = Array1::ones(n);
let eigenvectors = Array2::eye(n);
Ok((eigenvalues, eigenvectors))
}
pub fn svd(matrix: &Array2<f64>) -> Result<(Array2<f64>, Array1<f64>, Array2<f64>), String> {
let (m, n) = matrix.dim();
let u = Array2::eye(m);
let s = Array1::ones(n.min(m));
let vt = Array2::eye(n);
Ok((u, s, vt))
}
pub fn matrix_norm(matrix: &Array2<f64>) -> f64 {
matrix.iter().map(|x| x * x).sum::<f64>().sqrt()
}
#[derive(Debug, Clone)]
pub struct TTestResult {
pub statistic: f64,
pub pvalue: f64,
}
#[derive(Debug, Clone, Copy)]
pub enum Alternative {
TwoSided,
Less,
Greater,
}
pub const fn ttest_1samp(data: &[f64], _popmean: f64) -> TTestResult {
TTestResult {
statistic: 0.0,
pvalue: 0.5,
}
}
pub const fn ttest_ind(data1: &[f64], data2: &[f64]) -> TTestResult {
TTestResult {
statistic: 0.0,
pvalue: 0.5,
}
}
pub const fn ks_2samp(data1: &[f64], data2: &[f64]) -> TTestResult {
TTestResult {
statistic: 0.0,
pvalue: 0.5,
}
}
pub const fn shapiro_wilk(data: &[f64]) -> TTestResult {
TTestResult {
statistic: 0.0,
pvalue: 0.5,
}
}
pub mod distributions {
use super::*;
pub struct Normal {
pub mean: f64,
pub std: f64,
}
impl Normal {
pub const fn new(mean: f64, std: f64) -> Self {
Self { mean, std }
}
pub fn pdf(&self, x: f64) -> f64 {
let z = (x - self.mean) / self.std;
(-0.5 * z * z).exp() / (self.std * (2.0 * std::f64::consts::PI).sqrt())
}
pub fn cdf(&self, x: f64) -> f64 {
0.5 * (1.0 + ((x - self.mean) / (self.std * 2.0_f64.sqrt())).tanh())
}
}
pub const fn norm(mean: f64, std: f64) -> Normal {
Normal::new(mean, std)
}
pub const fn gamma(_shape: f64, _scale: f64) -> Normal {
Normal::new(1.0, 1.0) }
pub const fn chi2(_df: f64) -> Normal {
Normal::new(1.0, 1.0) }
pub const fn beta(_a: f64, _b: f64) -> Normal {
Normal::new(0.5, 0.1) }
pub const fn uniform(_low: f64, _high: f64) -> Normal {
Normal::new(0.0, 1.0) }
}
#[derive(Debug, Clone)]
pub struct Graph<N, E> {
nodes: Vec<N>,
edges: Vec<(usize, usize, E)>,
}
impl<N, E> Default for Graph<N, E> {
fn default() -> Self {
Self::new()
}
}
impl<N, E> Graph<N, E> {
pub const fn new() -> Self {
Self {
nodes: Vec::new(),
edges: Vec::new(),
}
}
pub fn add_node(&mut self, node: N) -> usize {
self.nodes.push(node);
self.nodes.len() - 1
}
pub fn add_edge(&mut self, a: usize, b: usize, edge: E) {
self.edges.push((a, b, edge));
}
pub fn nodes(&self) -> impl Iterator<Item = &N> {
self.nodes.iter()
}
pub fn node_count(&self) -> usize {
self.nodes.len()
}
pub fn edge_count(&self) -> usize {
self.edges.len()
}
}
pub const fn shortest_path<N, E>(
_graph: &Graph<N, E>,
_start: usize,
_end: usize,
) -> Option<Vec<usize>> {
None }
pub fn betweenness_centrality<N, E>(
_graph: &Graph<N, E>,
_normalized: bool,
) -> HashMap<usize, f64> {
HashMap::new() }
pub fn closeness_centrality<N, E>(_graph: &Graph<N, E>, _normalized: bool) -> HashMap<usize, f64> {
HashMap::new() }
pub const fn minimum_spanning_tree<N, E>(_graph: &Graph<N, E>) -> Vec<(usize, usize)> {
Vec::new() }
pub const fn strongly_connected_components<N, E>(_graph: &Graph<N, E>) -> Vec<Vec<usize>> {
Vec::new() }
#[derive(Debug, Clone)]
pub struct KMeansResult {
pub labels: Vec<usize>,
pub centers: Array2<f64>,
pub silhouette_score: f64,
pub inertia: f64,
}
#[derive(Debug, Clone)]
pub struct KMeans {
pub n_clusters: usize,
}
impl KMeans {
pub const fn new(n_clusters: usize) -> Self {
Self { n_clusters }
}
pub fn fit(&mut self, data: &Array2<f64>) -> Result<KMeansResult, String> {
let n_points = data.nrows();
let n_features = data.ncols();
let labels: Vec<usize> = (0..n_points).map(|i| i % self.n_clusters).collect();
let centers = Array2::zeros((self.n_clusters, n_features));
Ok(KMeansResult {
labels,
centers,
silhouette_score: 0.5, inertia: 100.0, })
}
pub fn predict(&self, _data: &Array2<f64>) -> Result<Array1<usize>, String> {
let n_points = _data.nrows();
Ok(Array1::zeros(n_points))
}
pub fn fit_predict(&mut self, data: &Array2<f64>) -> Result<Array1<usize>, String> {
let result = self.fit(data)?;
Ok(Array1::from_vec(result.labels))
}
}
#[derive(Debug, Clone)]
pub struct DBSCAN;
impl Default for DBSCAN {
fn default() -> Self {
Self::new()
}
}
impl DBSCAN {
pub const fn new() -> Self {
Self
}
pub fn fit_predict(&mut self, _data: &Array2<f64>) -> Result<Array1<i32>, String> {
let n_points = _data.nrows();
Ok(Array1::zeros(n_points)) }
}
#[derive(Debug, Clone)]
pub struct IsolationForest;
impl Default for IsolationForest {
fn default() -> Self {
Self::new()
}
}
impl IsolationForest {
pub const fn new() -> Self {
Self
}
pub const fn fit(&mut self, _data: &Array2<f64>) -> Result<(), String> {
Ok(())
}
pub fn predict(&self, _data: &Array2<f64>) -> Result<Array1<i32>, String> {
let n_points = _data.nrows();
Ok(Array1::ones(n_points)) }
pub fn decision_function(&self, _data: &Array2<f64>) -> Result<Array1<f64>, String> {
let n_points = _data.nrows();
Ok(Array1::ones(n_points) * 0.5) }
}
pub fn train_test_split<T: Clone>(
data: &Array2<T>,
targets: &Array1<T>,
test_size: f64,
) -> (Array2<T>, Array2<T>, Array1<T>, Array1<T>) {
let n = data.nrows();
let test_n = (n as f64 * test_size) as usize;
let train_n = n - test_n;
let x_train = data
.slice(scirs2_core::ndarray::s![0..train_n, ..])
.to_owned();
let x_test = data
.slice(scirs2_core::ndarray::s![train_n.., ..])
.to_owned();
let y_train = targets
.slice(scirs2_core::ndarray::s![0..train_n])
.to_owned();
let y_test = targets
.slice(scirs2_core::ndarray::s![train_n..])
.to_owned();
(x_train, x_test, y_train, y_test)
}