use crate::complex::SimplicialStateComplex;
use crate::error::SubstrateError;
use crate::solver::conjugate_gradient;
pub fn try_reconcile_state_delta(
complex: &SimplicialStateComplex,
delta_s: &[f64],
) -> Result<Vec<f64>, SubstrateError> {
if delta_s.len() != complex.edges.len() {
return Err(SubstrateError::DimensionMismatch {
expected: complex.edges.len(),
actual: delta_s.len(),
});
}
let t_count = complex.triangles.len();
if t_count == 0 {
return Ok(delta_s.to_vec());
}
let d1 = complex.d1()?;
let d1_t = d1.transpose()?;
let b2 = d1.mul_vec(delta_s)?;
let l2 = d1.mul_mat(&d1_t)?;
let x0 = vec![0.0; t_count];
let beta = conjugate_gradient(&l2, &b2, &x0, 1e-8, 1000).unwrap_or(x0);
let s_coexact = d1_t.mul_vec(&beta)?;
let mut reconciled = vec![0.0; delta_s.len()];
for i in 0..delta_s.len() {
reconciled[i] = delta_s[i] - s_coexact[i];
}
Ok(reconciled)
}
pub fn reconcile_state_delta(complex: &SimplicialStateComplex, delta_s: &[f64]) -> Vec<f64> {
match try_reconcile_state_delta(complex, delta_s) {
Ok(v) => v,
Err(_) => delta_s.to_vec(),
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::complex::SimplicialStateComplex;
#[test]
fn test_topological_reconciliation() {
let mut complex = SimplicialStateComplex::new();
complex.add_vertex("A", 10.0);
complex.add_vertex("B", 10.0);
complex.add_vertex("C", 10.0);
complex.add_edge("A", "B", 1.0);
complex.add_edge("B", "C", 1.0);
complex.add_edge("A", "C", 1.5);
let delta_s = vec![1.0, 1.0, 0.0];
let reconciled = reconcile_state_delta(&complex, &delta_s);
let d1 = complex.d1().expect("d1 is well-defined for this fixture");
let curl = d1.mul_vec(&reconciled).expect("dims match by construction");
assert!(curl.len() > 0);
for &val in &curl {
assert!(val.abs() < 1e-7, "Curl should be projected out, got {}", val);
}
}
#[test]
fn test_try_reconciliation_surfaces_dim_mismatch() {
let mut complex = SimplicialStateComplex::new();
complex.add_vertex("A", 0.0);
complex.add_vertex("B", 0.0);
complex.add_edge("A", "B", 0.0);
let bad = vec![1.0, 2.0, 3.0];
let err = try_reconcile_state_delta(&complex, &bad);
assert!(matches!(err, Err(SubstrateError::DimensionMismatch { .. })));
let out = reconcile_state_delta(&complex, &bad);
assert_eq!(out, bad);
}
}