pub mod cholesky;
pub mod jacobi_svd;
pub mod lsqr;
pub mod normal_equations;
pub mod qr_householder;
pub use cholesky::{cholesky_factor, cholesky_solve};
pub use jacobi_svd::{jacobi_svd, jacobi_svd_thin};
pub use lsqr::lsqr;
pub use normal_equations::{normal_equations_solve, solve_subset_ls};
pub use qr_householder::{householder_qr, qr_solve};
use crate::error::{CsError, CsResult};
pub fn dot(x: &[f64], y: &[f64]) -> CsResult<f64> {
if x.len() != y.len() {
return Err(CsError::DimensionMismatch {
a: x.len(),
b: y.len(),
});
}
let mut s = 0.0_f64;
for i in 0..x.len() {
s += x[i] * y[i];
}
Ok(s)
}
#[must_use]
pub fn norm2(x: &[f64]) -> f64 {
let mut s = 0.0_f64;
for &v in x {
s += v * v;
}
s.sqrt()
}
#[must_use]
pub fn norm_inf(x: &[f64]) -> f64 {
let mut m = 0.0_f64;
for &v in x {
let a = v.abs();
if a > m {
m = a;
}
}
m
}
#[must_use]
pub fn norm1(x: &[f64]) -> f64 {
let mut s = 0.0_f64;
for &v in x {
s += v.abs();
}
s
}
pub fn axpy(alpha: f64, x: &[f64], y: &mut [f64]) -> CsResult<()> {
if x.len() != y.len() {
return Err(CsError::DimensionMismatch {
a: x.len(),
b: y.len(),
});
}
for i in 0..x.len() {
y[i] += alpha * x[i];
}
Ok(())
}
pub fn mat_vec(a: &[f64], m: usize, n: usize, x: &[f64]) -> CsResult<Vec<f64>> {
if a.len() != m * n {
return Err(CsError::ShapeMismatch {
expected: vec![m, n],
got: vec![a.len()],
});
}
if x.len() != n {
return Err(CsError::DimensionMismatch { a: x.len(), b: n });
}
let mut y = vec![0.0_f64; m];
for (i, yi) in y.iter_mut().enumerate().take(m) {
let mut s = 0.0_f64;
let row = i * n;
for j in 0..n {
s += a[row + j] * x[j];
}
*yi = s;
}
Ok(y)
}
pub fn mat_t_vec(a: &[f64], m: usize, n: usize, x: &[f64]) -> CsResult<Vec<f64>> {
if a.len() != m * n {
return Err(CsError::ShapeMismatch {
expected: vec![m, n],
got: vec![a.len()],
});
}
if x.len() != m {
return Err(CsError::DimensionMismatch { a: x.len(), b: m });
}
let mut y = vec![0.0_f64; n];
for (i, &xi) in x.iter().enumerate().take(m) {
let row = i * n;
for j in 0..n {
y[j] += a[row + j] * xi;
}
}
Ok(y)
}
pub fn add_scaled(a: &[f64], alpha: f64, b: &[f64]) -> CsResult<Vec<f64>> {
if a.len() != b.len() {
return Err(CsError::DimensionMismatch {
a: a.len(),
b: b.len(),
});
}
Ok(a.iter()
.zip(b.iter())
.map(|(ai, bi)| ai + alpha * bi)
.collect())
}
pub fn submat_columns(a: &[f64], m: usize, n: usize, support: &[usize]) -> CsResult<Vec<f64>> {
if a.len() != m * n {
return Err(CsError::ShapeMismatch {
expected: vec![m, n],
got: vec![a.len()],
});
}
let k = support.len();
let mut out = vec![0.0_f64; m * k];
for i in 0..m {
for (s_idx, &j) in support.iter().enumerate() {
if j >= n {
return Err(CsError::IndexOutOfBounds { index: j, len: n });
}
out[i * k + s_idx] = a[i * n + j];
}
}
Ok(out)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn dot_basic() {
assert!((dot(&[1.0, 2.0, 3.0], &[4.0, 5.0, 6.0]).expect("ok") - 32.0).abs() < 1.0e-12);
}
#[test]
fn norm2_basic() {
assert!((norm2(&[3.0, 4.0]) - 5.0).abs() < 1.0e-12);
}
#[test]
fn norm_inf_basic() {
assert!((norm_inf(&[1.0, -3.0, 2.0]) - 3.0).abs() < 1.0e-12);
}
#[test]
fn norm1_basic() {
assert!((norm1(&[1.0, -3.0, 2.0]) - 6.0).abs() < 1.0e-12);
}
#[test]
fn submat_columns_works() {
let a = vec![1.0, 2.0, 3.0, 4.0, 5.0, 6.0];
let s = submat_columns(&a, 2, 3, &[0, 2]).expect("ok");
assert_eq!(s, vec![1.0, 3.0, 4.0, 6.0]);
}
}