use super::functions::*;
#[derive(Clone, Debug)]
pub struct DenseVector {
pub data: Vec<f64>,
}
impl DenseVector {
pub fn zero(n: usize) -> Self {
Self { data: vec![0.0; n] }
}
pub fn dim(&self) -> usize {
self.data.len()
}
pub fn dot(&self, other: &DenseVector) -> f64 {
self.data
.iter()
.zip(other.data.iter())
.map(|(a, b)| a * b)
.sum()
}
pub fn norm_sq(&self) -> f64 {
self.dot(self)
}
pub fn norm(&self) -> f64 {
self.norm_sq().sqrt()
}
pub fn normalize(&self) -> Option<DenseVector> {
let n = self.norm();
if n < 1e-12 {
return None;
}
Some(DenseVector {
data: self.data.iter().map(|x| x / n).collect(),
})
}
pub fn add(&self, other: &DenseVector) -> Option<DenseVector> {
if self.dim() != other.dim() {
return None;
}
Some(DenseVector {
data: self
.data
.iter()
.zip(other.data.iter())
.map(|(a, b)| a + b)
.collect(),
})
}
pub fn scale(&self, s: f64) -> DenseVector {
DenseVector {
data: self.data.iter().map(|x| x * s).collect(),
}
}
pub fn apply(mat: &DenseMatrix, v: &DenseVector) -> Option<DenseVector> {
if mat.cols != v.dim() {
return None;
}
let mut result = vec![0.0; mat.rows];
for i in 0..mat.rows {
for j in 0..mat.cols {
result[i] += mat.data[i][j] * v.data[j];
}
}
Some(DenseVector { data: result })
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct SparseMatrix {
pub rows: usize,
pub cols: usize,
pub row_ptr: Vec<usize>,
pub col_indices: Vec<usize>,
pub values: Vec<f64>,
}
#[allow(dead_code)]
impl SparseMatrix {
pub fn new(rows: usize, cols: usize) -> Self {
Self {
rows,
cols,
row_ptr: vec![0; rows + 1],
col_indices: vec![],
values: vec![],
}
}
pub fn from_coo(rows: usize, cols: usize, entries: &[(usize, usize, f64)]) -> Self {
let mut row_ptr = vec![0usize; rows + 1];
for &(r, _, _) in entries {
row_ptr[r + 1] += 1;
}
for i in 0..rows {
row_ptr[i + 1] += row_ptr[i];
}
let col_indices: Vec<usize> = entries.iter().map(|&(_, c, _)| c).collect();
let values: Vec<f64> = entries.iter().map(|&(_, _, v)| v).collect();
Self {
rows,
cols,
row_ptr,
col_indices,
values,
}
}
pub fn nnz(&self) -> usize {
self.values.len()
}
pub fn get(&self, r: usize, c: usize) -> f64 {
let start = self.row_ptr[r];
let end = self.row_ptr[r + 1];
for idx in start..end {
if self.col_indices[idx] == c {
return self.values[idx];
}
}
0.0
}
pub fn matvec(&self, x: &[f64]) -> Option<Vec<f64>> {
if x.len() != self.cols {
return None;
}
let mut y = vec![0.0f64; self.rows];
for r in 0..self.rows {
let start = self.row_ptr[r];
let end = self.row_ptr[r + 1];
for idx in start..end {
y[r] += self.values[idx] * x[self.col_indices[idx]];
}
}
Some(y)
}
pub fn transpose(&self) -> SparseMatrix {
let mut entries: Vec<(usize, usize, f64)> = Vec::with_capacity(self.nnz());
for r in 0..self.rows {
let start = self.row_ptr[r];
let end = self.row_ptr[r + 1];
for idx in start..end {
entries.push((self.col_indices[idx], r, self.values[idx]));
}
}
entries.sort_by_key(|&(r, c, _)| (r, c));
SparseMatrix::from_coo(self.cols, self.rows, &entries)
}
pub fn norm_sq(&self) -> f64 {
self.values.iter().map(|v| v * v).sum()
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct QRDecomposition {
pub q: DenseMatrix,
pub r: DenseMatrix,
}
#[allow(dead_code)]
impl QRDecomposition {
pub fn compute(a: &DenseMatrix) -> Option<QRDecomposition> {
let m = a.rows;
let n = a.cols;
if m < n {
return None;
}
let mut mat = a.data.clone();
let mut vs: Vec<Vec<f64>> = Vec::new();
for k in 0..n {
let mut x: Vec<f64> = (k..m).map(|i| mat[i][k]).collect();
let norm: f64 = x.iter().map(|xi| xi * xi).sum::<f64>().sqrt();
if norm < 1e-14 {
vs.push(vec![0.0; m - k]);
continue;
}
x[0] += if x[0] >= 0.0 { norm } else { -norm };
let v_norm: f64 = x.iter().map(|xi| xi * xi).sum::<f64>().sqrt();
let v: Vec<f64> = x.iter().map(|xi| xi / v_norm).collect();
for j in k..n {
let dot: f64 = v.iter().enumerate().map(|(i, vi)| vi * mat[k + i][j]).sum();
for i in 0..(m - k) {
mat[k + i][j] -= 2.0 * v[i] * dot;
}
}
vs.push(v);
}
let mut r_data = vec![vec![0.0; n]; n];
for i in 0..n {
for j in i..n {
r_data[i][j] = mat[i][j];
}
}
let r = DenseMatrix {
rows: n,
cols: n,
data: r_data,
};
let mut q_data = vec![vec![0.0; n]; m];
for j in 0..n {
q_data[j][j] = 1.0;
}
for k in (0..n).rev() {
let v = &vs[k];
let len = v.len();
for j in 0..n {
let dot: f64 = v
.iter()
.enumerate()
.map(|(i, vi)| vi * q_data[k + i][j])
.sum();
for i in 0..len {
q_data[k + i][j] -= 2.0 * v[i] * dot;
}
}
}
let q = DenseMatrix {
rows: m,
cols: n,
data: q_data,
};
Some(QRDecomposition { q, r })
}
pub fn solve(&self, b: &[f64]) -> Option<Vec<f64>> {
let m = self.q.rows;
let n = self.q.cols;
if b.len() != m {
return None;
}
let mut qtb = vec![0.0f64; n];
for j in 0..n {
qtb[j] = (0..m).map(|i| self.q.data[i][j] * b[i]).sum();
}
let mut x = vec![0.0f64; n];
for i in (0..n).rev() {
let mut s = qtb[i];
for j in (i + 1)..n {
s -= self.r.data[i][j] * x[j];
}
if self.r.data[i][i].abs() < 1e-14 {
return None;
}
x[i] = s / self.r.data[i][i];
}
Some(x)
}
}
#[derive(Clone, Debug)]
pub struct DenseMatrix {
pub rows: usize,
pub cols: usize,
pub data: Vec<Vec<f64>>,
}
impl DenseMatrix {
pub fn zero(rows: usize, cols: usize) -> Self {
Self {
rows,
cols,
data: vec![vec![0.0; cols]; rows],
}
}
pub fn identity(n: usize) -> Self {
let mut m = Self::zero(n, n);
for i in 0..n {
m.data[i][i] = 1.0;
}
m
}
pub fn get(&self, r: usize, c: usize) -> f64 {
self.data[r][c]
}
pub fn set(&mut self, r: usize, c: usize, v: f64) {
self.data[r][c] = v;
}
pub fn add(&self, other: &DenseMatrix) -> Option<DenseMatrix> {
if self.rows != other.rows || self.cols != other.cols {
return None;
}
let mut result = Self::zero(self.rows, self.cols);
for i in 0..self.rows {
for j in 0..self.cols {
result.data[i][j] = self.data[i][j] + other.data[i][j];
}
}
Some(result)
}
pub fn mul(&self, other: &DenseMatrix) -> Option<DenseMatrix> {
if self.cols != other.rows {
return None;
}
let mut result = Self::zero(self.rows, other.cols);
for i in 0..self.rows {
for k in 0..self.cols {
for j in 0..other.cols {
result.data[i][j] += self.data[i][k] * other.data[k][j];
}
}
}
Some(result)
}
pub fn scale(&self, s: f64) -> DenseMatrix {
let mut result = self.clone();
for i in 0..self.rows {
for j in 0..self.cols {
result.data[i][j] *= s;
}
}
result
}
pub fn transpose(&self) -> DenseMatrix {
let mut result = Self::zero(self.cols, self.rows);
for i in 0..self.rows {
for j in 0..self.cols {
result.data[j][i] = self.data[i][j];
}
}
result
}
pub fn trace(&self) -> f64 {
let n = self.rows.min(self.cols);
(0..n).map(|i| self.data[i][i]).sum()
}
pub fn norm_sq(&self) -> f64 {
self.data
.iter()
.flat_map(|row| row.iter())
.map(|x| x * x)
.sum()
}
pub fn det(&self) -> Option<f64> {
if self.rows != self.cols {
return None;
}
let n = self.rows;
if n == 0 {
return Some(1.0);
}
if n == 1 {
return Some(self.data[0][0]);
}
if n == 2 {
return Some(self.data[0][0] * self.data[1][1] - self.data[0][1] * self.data[1][0]);
}
let mut m = self.data.clone();
let mut sign = 1.0_f64;
for col in 0..n {
let mut pivot_row = col;
let mut max_val = m[col][col].abs();
for row in (col + 1)..n {
if m[row][col].abs() > max_val {
max_val = m[row][col].abs();
pivot_row = row;
}
}
if max_val < 1e-12 {
return Some(0.0);
}
if pivot_row != col {
m.swap(col, pivot_row);
sign = -sign;
}
let pivot = m[col][col];
for row in (col + 1)..n {
let factor = m[row][col] / pivot;
for k in col..n {
let v = m[col][k] * factor;
m[row][k] -= v;
}
}
}
let diag_prod: f64 = (0..n).map(|i| m[i][i]).product();
Some(sign * diag_prod)
}
pub fn row_echelon(&self) -> (DenseMatrix, usize) {
let mut m = self.data.clone();
let mut rank = 0;
let mut pivot_col = 0;
while rank < self.rows && pivot_col < self.cols {
let mut pivot_row = rank;
while pivot_row < self.rows && m[pivot_row][pivot_col].abs() < 1e-12 {
pivot_row += 1;
}
if pivot_row == self.rows {
pivot_col += 1;
continue;
}
if pivot_row != rank {
m.swap(rank, pivot_row);
}
let pivot = m[rank][pivot_col];
for j in pivot_col..self.cols {
m[rank][j] /= pivot;
}
for i in 0..self.rows {
if i != rank && m[i][pivot_col].abs() > 1e-12 {
let factor = m[i][pivot_col];
for j in pivot_col..self.cols {
let v = m[rank][j] * factor;
m[i][j] -= v;
}
}
}
rank += 1;
pivot_col += 1;
}
let result = DenseMatrix {
rows: self.rows,
cols: self.cols,
data: m,
};
(result, rank)
}
pub fn rank(&self) -> usize {
self.row_echelon().1
}
pub fn is_symmetric(&self) -> bool {
if self.rows != self.cols {
return false;
}
for i in 0..self.rows {
for j in 0..self.cols {
if (self.data[i][j] - self.data[j][i]).abs() > 1e-12 {
return false;
}
}
}
true
}
pub fn solve(&self, b: &[f64]) -> Option<Vec<f64>> {
if self.rows != self.cols || self.rows != b.len() {
return None;
}
let n = self.rows;
let mut aug: Vec<Vec<f64>> = self
.data
.iter()
.enumerate()
.map(|(i, row)| {
let mut r = row.clone();
r.push(b[i]);
r
})
.collect();
for col in 0..n {
let mut pivot_row = col;
let mut max_val = aug[col][col].abs();
for row in (col + 1)..n {
if aug[row][col].abs() > max_val {
max_val = aug[row][col].abs();
pivot_row = row;
}
}
if max_val < 1e-12 {
return None;
}
if pivot_row != col {
aug.swap(col, pivot_row);
}
let pivot = aug[col][col];
for j in col..=n {
aug[col][j] /= pivot;
}
for row in 0..n {
if row != col && aug[row][col].abs() > 1e-12 {
let factor = aug[row][col];
for j in col..=n {
let v = aug[col][j] * factor;
aug[row][j] -= v;
}
}
}
}
Some((0..n).map(|i| aug[i][n]).collect())
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct BandedMatrix {
pub rows: usize,
pub cols: usize,
pub kl: usize,
pub ku: usize,
pub band: Vec<Vec<f64>>,
}
#[allow(dead_code)]
impl BandedMatrix {
pub fn zero(rows: usize, cols: usize, kl: usize, ku: usize) -> Self {
let ndiag = kl + ku + 1;
Self {
rows,
cols,
kl,
ku,
band: vec![vec![0.0; cols]; ndiag],
}
}
pub fn get(&self, r: usize, c: usize) -> f64 {
let offset = c as isize - r as isize;
if offset < -(self.kl as isize) || offset > self.ku as isize {
return 0.0;
}
let k = (offset + self.kl as isize) as usize;
self.band[k][c]
}
pub fn set(&mut self, r: usize, c: usize, v: f64) {
let offset = c as isize - r as isize;
assert!(
offset >= -(self.kl as isize) && offset <= self.ku as isize,
"Index outside band"
);
let k = (offset + self.kl as isize) as usize;
self.band[k][c] = v;
}
pub fn matvec(&self, x: &[f64]) -> Option<Vec<f64>> {
if x.len() != self.cols {
return None;
}
let mut y = vec![0.0f64; self.rows];
for r in 0..self.rows {
let c_start = r.saturating_sub(self.kl);
let c_end = (r + self.ku + 1).min(self.cols);
for c in c_start..c_end {
y[r] += self.get(r, c) * x[c];
}
}
Some(y)
}
pub fn diagonal(&self) -> Vec<f64> {
let n = self.rows.min(self.cols);
(0..n).map(|i| self.get(i, i)).collect()
}
}
#[allow(dead_code)]
#[derive(Clone, Debug)]
pub struct LanczosResult {
pub basis: Vec<DenseVector>,
pub alpha: Vec<f64>,
pub beta: Vec<f64>,
}
#[allow(dead_code)]
impl LanczosResult {
pub fn compute(a: &DenseMatrix, v0: &DenseVector, k: usize) -> Option<LanczosResult> {
if a.rows != a.cols || a.rows != v0.dim() || k == 0 {
return None;
}
let n = a.rows;
let mut basis: Vec<DenseVector> = Vec::with_capacity(k + 1);
let mut alpha = Vec::with_capacity(k);
let mut beta: Vec<f64> = Vec::with_capacity(k);
let v0_norm = v0.norm();
if v0_norm < 1e-14 {
return None;
}
let v_init = v0.scale(1.0 / v0_norm);
basis.push(v_init);
let mut w_prev = DenseVector::zero(n);
for j in 0..k {
let vj = &basis[j];
let av = DenseVector::apply(a, vj)?;
let aj = vj.dot(&av);
alpha.push(aj);
let mut w_data = vec![0.0f64; n];
for i in 0..n {
w_data[i] = av.data[i] - aj * vj.data[i];
if j > 0 {
w_data[i] -= beta[j - 1] * w_prev.data[i];
}
}
let w = DenseVector { data: w_data };
let bj = w.norm();
if j + 1 < k {
beta.push(bj);
if bj < 1e-14 {
break;
}
let v_next = w.scale(1.0 / bj);
w_prev = basis[j].clone();
basis.push(v_next);
}
}
Some(LanczosResult { basis, alpha, beta })
}
pub fn tridiagonal(&self) -> DenseMatrix {
let n = self.alpha.len();
let mut t = DenseMatrix::zero(n, n);
for i in 0..n {
t.data[i][i] = self.alpha[i];
}
for i in 0..self.beta.len() {
t.data[i][i + 1] = self.beta[i];
t.data[i + 1][i] = self.beta[i];
}
t
}
}