use crate::error::SheafError;
use crate::sheaf::CellularSheaf;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct SheafLaplacian {
pub matrix: Vec<Vec<f64>>,
pub n: usize,
}
impl SheafLaplacian {
pub fn from_sheaf(sheaf: &CellularSheaf) -> Result<Self, SheafError> {
sheaf.validate()?;
let n = sheaf.total_dim();
let mut matrix = vec![vec![0.0; n]; n];
let mut offsets = Vec::with_capacity(sheaf.node_count());
let mut off = 0;
for &d in &sheaf.stalk_dims {
offsets.push(off);
off += d;
}
for (src, tgt, f_map) in &sheaf.restriction_maps {
let i = *src;
let j = *tgt;
let ft_f = mat_mul_transpose(f_map);
add_to_block(&mut matrix, offsets[i], offsets[i], &ft_f);
add_to_block(&mut matrix, offsets[j], offsets[j], &ft_f);
sub_from_block(&mut matrix, offsets[i], offsets[j], &ft_f);
sub_from_block(&mut matrix, offsets[j], offsets[i], &ft_f);
}
Ok(Self { matrix, n })
}
pub fn apply(&self, x: &[f64]) -> Vec<f64> {
mat_vec(&self.matrix, x)
}
pub fn quadratic_form(&self, x: &[f64]) -> f64 {
let lx = self.apply(x);
dot(x, &lx)
}
pub fn residual_norm(&self, x: &[f64]) -> f64 {
let lx = self.apply(x);
norm(&lx)
}
pub fn smallest_eigenvalue(&self, max_iter: usize, tol: f64) -> (f64, Vec<f64>) {
let n = self.n;
if n == 0 {
return (0.0, vec![]);
}
let largest = self.power_iteration(max_iter, tol);
let shift = largest.0;
let mut v: Vec<f64> = (0..n).map(|i| ((i + 1) as f64).sqrt()).collect();
let v_norm = norm(&v);
for x in &mut v { *x /= v_norm; }
for _ in 0..max_iter {
let mut lv = self.apply(&v);
for (lvi, vi) in lv.iter_mut().zip(&v) {
*lvi -= shift * vi;
}
for val in lv.iter_mut() {
*val = -*val;
}
let lv_norm = norm(&lv);
if lv_norm < tol {
break;
}
for (vi, lvi) in v.iter_mut().zip(&lv) {
*vi = lvi / lv_norm;
}
}
let lx = self.apply(&v);
let eigenvalue = dot(&v, &lx) / dot(&v, &v).max(1e-15);
(eigenvalue, v)
}
pub fn power_iteration(&self, max_iter: usize, tol: f64) -> (f64, Vec<f64>) {
let n = self.n;
if n == 0 {
return (0.0, vec![]);
}
let mut v: Vec<f64> = (0..n).map(|i| ((i + 1) as f64).sqrt()).collect();
let v_norm = norm(&v);
for x in &mut v { *x /= v_norm; }
let mut eigenvalue = 0.0;
for _ in 0..max_iter {
let lv = self.apply(&v);
let new_eigenvalue = dot(&v, &lv);
let lv_norm = norm(&lv);
if lv_norm < tol {
break;
}
for i in 0..n {
v[i] = lv[i] / lv_norm;
}
if (new_eigenvalue - eigenvalue).abs() < tol {
eigenvalue = new_eigenvalue;
break;
}
eigenvalue = new_eigenvalue;
}
(eigenvalue, v)
}
pub fn eigenvalues(&self, max_iter: usize) -> Vec<f64> {
let n = self.n;
if n == 0 {
return vec![];
}
let mut eigenvalues = Vec::new();
let mut deflated = self.matrix.clone();
for _ in 0..n {
let lap = SheafLaplacian { matrix: deflated.clone(), n };
let (ev, v) = lap.power_iteration(max_iter, 1e-10);
eigenvalues.push(ev);
let vv = dot(&v, &v);
if vv > 1e-15 {
for i in 0..n {
for j in 0..n {
deflated[i][j] -= ev * v[i] * v[j] / vv;
}
}
}
}
eigenvalues.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
eigenvalues
}
}
fn mat_mul_transpose(f: &[Vec<f64>]) -> Vec<Vec<f64>> {
let rows = f.len();
let cols = if rows == 0 { 0 } else { f[0].len() };
let mut result = vec![vec![0.0; cols]; cols];
for i in 0..cols {
for j in 0..cols {
let sum: f64 = f.iter().map(|row| row[i] * row[j]).sum();
result[i][j] = sum;
}
}
result
}
fn add_to_block(mat: &mut [Vec<f64>], r: usize, c: usize, block: &[Vec<f64>]) {
for (di, row) in block.iter().enumerate() {
for (dj, &val) in row.iter().enumerate() {
mat[r + di][c + dj] += val;
}
}
}
fn sub_from_block(mat: &mut [Vec<f64>], r: usize, c: usize, block: &[Vec<f64>]) {
for (di, row) in block.iter().enumerate() {
for (dj, &val) in row.iter().enumerate() {
mat[r + di][c + dj] -= val;
}
}
}
fn mat_vec(m: &[Vec<f64>], v: &[f64]) -> Vec<f64> {
m.iter()
.map(|row| row.iter().zip(v.iter()).map(|(a, b)| a * b).sum())
.collect()
}
fn dot(a: &[f64], b: &[f64]) -> f64 {
a.iter().zip(b.iter()).map(|(x, y)| x * y).sum()
}
fn norm(v: &[f64]) -> f64 {
dot(v, v).sqrt()
}