use crate::types::chain::Chain;
use crate::types::differential_form::DifferentialForm;
use crate::{BaseTopology, SimplicialComplex};
use deep_causality_haft::Pure; use deep_causality_haft::{Adjunction, HKT, NoConstraint, Satisfies};
use deep_causality_num::Float;
use deep_causality_sparse::CsrMatrix;
use deep_causality_sparse::CsrMatrixWitness; use std::collections::HashMap;
use std::sync::Arc;
#[derive(Debug, Clone, Copy, Default)]
pub struct ExteriorDerivativeWitness;
impl HKT for ExteriorDerivativeWitness {
type Constraint = NoConstraint;
type Type<T>
= DifferentialForm<T>
where
T: Satisfies<NoConstraint>;
}
#[derive(Debug, Clone, Copy, Default)]
pub struct BoundaryWitness;
impl HKT for BoundaryWitness {
type Constraint = NoConstraint;
type Type<T>
= Chain<T>
where
T: Satisfies<NoConstraint>;
}
#[derive(Debug, Clone)]
pub struct StokesContext<T> {
complex: Arc<SimplicialComplex<T>>,
}
impl<T> StokesContext<T> {
pub fn new(complex: SimplicialComplex<T>) -> Self {
Self {
complex: Arc::new(complex),
}
}
pub fn from_arc(complex: Arc<SimplicialComplex<T>>) -> Self {
Self { complex }
}
pub fn complex(&self) -> &SimplicialComplex<T> {
&self.complex
}
pub fn complex_arc(&self) -> Arc<SimplicialComplex<T>> {
Arc::clone(&self.complex)
}
pub fn dim(&self) -> usize {
self.complex.dimension()
}
pub fn num_simplices(&self, k: usize) -> usize {
if k < self.complex.skeletons().len() {
self.complex.skeletons()[k].simplices().len()
} else {
0
}
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct StokesAdjunction;
impl<T> Adjunction<ExteriorDerivativeWitness, BoundaryWitness, StokesContext<T>>
for StokesAdjunction
where
T: Satisfies<NoConstraint>,
{
fn unit<A>(ctx: &StokesContext<T>, a: A) -> Chain<DifferentialForm<A>>
where
A: Satisfies<NoConstraint> + Clone,
DifferentialForm<A>: Satisfies<NoConstraint>,
{
let dim = ctx.dim();
let coefficients = vec![a];
let form = DifferentialForm::from_coefficients(0, dim, coefficients);
let form_complex = SimplicialComplex::<DifferentialForm<A>> {
skeletons: ctx.complex.skeletons.clone(),
boundary_operators: ctx.complex.boundary_operators.clone(),
coboundary_operators: ctx.complex.coboundary_operators.clone(),
..Default::default()
};
let arc_form_complex = Arc::new(form_complex);
let inner_weights = <CsrMatrixWitness as Pure<CsrMatrixWitness>>::pure(form);
Chain::new(arc_form_complex, 0, inner_weights)
}
fn counit<B>(_ctx: &StokesContext<T>, lrb: DifferentialForm<Chain<B>>) -> B
where
B: Satisfies<NoConstraint> + Clone,
Chain<B>: Satisfies<NoConstraint>,
{
let chain = &lrb.coefficients().as_slice()[0];
if let Some(val) = chain.weights().values().first() {
return val.clone();
}
panic!("Counit requires at least one value in the form's chain to evaluate")
}
fn left_adjunct<A, B, Func>(ctx: &StokesContext<T>, a: A, f: Func) -> Chain<B>
where
A: Satisfies<NoConstraint> + Clone,
B: Satisfies<NoConstraint>,
DifferentialForm<A>: Satisfies<NoConstraint>,
Func: Fn(DifferentialForm<A>) -> B,
{
let dim = ctx.dim();
let form = DifferentialForm::from_coefficients(0, dim, vec![a]);
let b = f(form);
let b_complex = SimplicialComplex::<B> {
skeletons: ctx.complex.skeletons.clone(),
boundary_operators: ctx.complex.boundary_operators.clone(),
coboundary_operators: ctx.complex.coboundary_operators.clone(),
..Default::default()
};
let arc_b_complex = Arc::new(b_complex);
let weights = <CsrMatrixWitness as Pure<CsrMatrixWitness>>::pure(b);
Chain::new(arc_b_complex, 0, weights)
}
fn right_adjunct<A, B, Func>(_ctx: &StokesContext<T>, la: DifferentialForm<A>, mut f: Func) -> B
where
A: Satisfies<NoConstraint> + Clone,
B: Satisfies<NoConstraint> + Clone,
Chain<B>: Satisfies<NoConstraint>,
Func: FnMut(A) -> Chain<B>,
{
let a = &la.coefficients().as_slice()[0];
let chain = f(a.clone());
if let Some(b) = chain.weights().values().first() {
return b.clone();
}
panic!("Right adjunct requires at least one value in the generated chain")
}
}
impl StokesAdjunction {
pub fn exterior_derivative<T>(
ctx: &StokesContext<T>,
form: &DifferentialForm<T>,
) -> DifferentialForm<T>
where
T: Float + Default + From<f64>,
{
let k = form.degree();
let dim = ctx.dim();
if k >= dim {
return DifferentialForm::zero(k + 1, form.dim());
}
let coboundary_ops = &ctx.complex().coboundary_operators;
if k >= coboundary_ops.len() {
return DifferentialForm::zero(k + 1, form.dim());
}
let coboundary = &coboundary_ops[k];
let coeffs = form.coefficients().as_slice();
let shape = coboundary.shape();
let nrows = shape.0;
let mut result_coeffs: Vec<T> = Vec::with_capacity(nrows);
for row_idx in 0..nrows {
let mut sum = T::zero();
let row_start = coboundary.row_indices()[row_idx];
let row_end = coboundary.row_indices()[row_idx + 1];
for idx in row_start..row_end {
let col = coboundary.col_indices()[idx];
let sign = coboundary.values()[idx];
if col < coeffs.len() {
let sign_t = if sign > 0 { T::one() } else { -T::one() };
sum += coeffs[col] * sign_t;
}
}
result_coeffs.push(sum);
}
DifferentialForm::from_coefficients(k + 1, form.dim(), result_coeffs)
}
pub fn boundary<T>(ctx: &StokesContext<T>, chain: &Chain<T>) -> Chain<T>
where
T: Float + Default,
{
let k = chain.grade();
if k == 0 {
let empty_weights: CsrMatrix<T> = CsrMatrix::new();
return Chain::new(ctx.complex_arc(), 0, empty_weights);
}
let boundary_ops = &ctx.complex().boundary_operators;
if k > boundary_ops.len() {
let empty_weights: CsrMatrix<T> = CsrMatrix::new();
return Chain::new(ctx.complex_arc(), k - 1, empty_weights);
}
let boundary_op = &boundary_ops[k];
let shape = boundary_op.shape();
let nrows = shape.0;
let chain_weights = chain.weights();
let row_indices = chain_weights.row_indices();
let col_indices = chain_weights.col_indices();
let values = chain_weights.values();
if row_indices.is_empty() {
let empty_weights: CsrMatrix<T> = CsrMatrix::default();
return Chain::new(ctx.complex_arc(), k - 1, empty_weights);
}
let chain_map: HashMap<usize, T> = col_indices
.iter()
.zip(values.iter())
.map(|(&c, v)| (c, *v))
.collect();
let mut result_triplets: Vec<(usize, usize, T)> = Vec::new();
for row_idx in 0..nrows {
let mut sum = T::zero();
let row_start = boundary_op.row_indices()[row_idx];
let row_end = boundary_op.row_indices()[row_idx + 1];
for idx in row_start..row_end {
let col = boundary_op.col_indices()[idx]; let sign = boundary_op.values()[idx];
if let Some(val) = chain_map.get(&col) {
let sign_t = if sign > 0 { T::one() } else { -T::one() };
sum += *val * sign_t;
}
}
if sum != T::zero() {
result_triplets.push((0, row_idx, sum));
}
}
let num_k_minus_1_simplices = ctx.num_simplices(k - 1);
let result_matrix = CsrMatrix::from_triplets(1, num_k_minus_1_simplices, &result_triplets)
.unwrap_or_else(|_| CsrMatrix::new());
Chain::new(ctx.complex_arc(), k - 1, result_matrix)
}
pub fn integrate<T>(form: &DifferentialForm<T>, chain: &Chain<T>) -> T
where
T: Float + Default,
{
if form.degree() != chain.grade() {
return T::zero();
}
let coeffs = form.coefficients().as_slice();
let weights = chain.weights();
let mut result = T::zero();
let col_indices = weights.col_indices();
let values = weights.values();
for (idx, &col) in col_indices.iter().enumerate() {
if col < coeffs.len() {
result += values[idx] * coeffs[col];
}
}
result
}
}