use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
use std::collections::HashMap;
use super::functions::*;
#[derive(Debug, Clone)]
pub struct Convergence {
pub e_infty: SpectralSequencePage,
pub filtration_grades: HashMap<(i32, i32), usize>,
}
impl Convergence {
pub fn new(e_infty: SpectralSequencePage) -> Self {
let filtration_grades = e_infty.entries.clone();
Self {
e_infty,
filtration_grades,
}
}
pub fn cohomology_rank(&self, n: i32) -> usize {
self.e_infty
.entries
.iter()
.filter(|(&(p, q), _)| p + q == n)
.map(|(_, &r)| r)
.sum()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct LocalCohomologyGroup {
pub degree: usize,
pub rank: usize,
}
impl LocalCohomologyGroup {
pub fn new(degree: usize, rank: usize) -> Self {
Self { degree, rank }
}
pub fn is_zero(&self) -> bool {
self.rank == 0
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct GradedGroup {
pub rank: usize,
pub name: String,
}
impl GradedGroup {
pub fn new(rank: usize, name: &str) -> Self {
Self {
rank,
name: name.to_string(),
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PersistenceInterval {
pub dimension: usize,
pub birth: f64,
pub death: f64,
}
#[allow(dead_code)]
impl PersistenceInterval {
pub fn new(dim: usize, birth: f64, death: f64) -> Self {
Self {
dimension: dim,
birth,
death,
}
}
pub fn persistence(&self) -> f64 {
self.death - self.birth
}
pub fn is_essential(&self) -> bool {
self.death == f64::INFINITY
}
pub fn contains(&self, t: f64) -> bool {
t >= self.birth && t < self.death
}
}
#[derive(Debug, Clone, Default)]
pub struct PersistenceBarcode {
pub intervals: Vec<BirthDeathPair>,
}
impl PersistenceBarcode {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, birth: f64, death: f64, degree: usize) {
self.intervals
.push(BirthDeathPair::new(birth, death, degree));
}
pub fn intervals_in_degree(&self, n: usize) -> Vec<&BirthDeathPair> {
self.intervals.iter().filter(|p| p.degree == n).collect()
}
pub fn betti_number(&self, n: usize) -> usize {
self.intervals_in_degree(n).len()
}
pub fn bottleneck_distance(&self, other: &PersistenceBarcode, n: usize) -> f64 {
let mut a: Vec<f64> = self
.intervals_in_degree(n)
.iter()
.map(|p| p.persistence().min(1e9))
.collect();
let mut b: Vec<f64> = other
.intervals_in_degree(n)
.iter()
.map(|p| p.persistence().min(1e9))
.collect();
a.sort_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal));
b.sort_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal));
let mut dist = 0.0_f64;
let len = a.len().max(b.len());
for i in 0..len {
let ai = a.get(i).copied().unwrap_or(0.0);
let bi = b.get(i).copied().unwrap_or(0.0);
dist = dist.max((ai - bi).abs());
}
dist
}
}
#[derive(Debug, Clone, Default)]
pub struct SpectralSequencePageManager {
pub current_page: usize,
pub pages: Vec<SpectralSequencePage>,
pub differentials: Vec<Vec<DifferentialMap>>,
}
impl SpectralSequencePageManager {
pub fn new() -> Self {
Self {
current_page: 2,
pages: vec![],
differentials: vec![],
}
}
pub fn add_page(&mut self, entries: HashMap<(i32, i32), usize>) {
let r = self.current_page;
let mut page = SpectralSequencePage::new(r);
for ((p, q), rank) in entries {
page.set(p, q, rank);
}
self.pages.push(page);
self.differentials.push(vec![]);
self.current_page += 1;
}
pub fn add_differential(&mut self, p: i32, q: i32, image_rank: usize) {
let r = self.current_page.saturating_sub(1);
let d = DifferentialMap::new(r, p, q, image_rank);
if let Some(last) = self.differentials.last_mut() {
last.push(d);
}
}
pub fn advance(&mut self) {
let last_idx = self.pages.len().saturating_sub(1);
if self.pages.is_empty() {
return;
}
let current = &self.pages[last_idx];
let diffs = &self.differentials[last_idx];
let r = current.page;
let ri = r as i32;
let mut next = SpectralSequencePage::new(r + 1);
for (&(p, q), &rank) in ¤t.entries {
let incoming = diffs
.iter()
.find(|d| d.target == (p, q))
.map_or(0, |d| d.image_rank);
let outgoing = diffs
.iter()
.find(|d| d.source == (p, q))
.map_or(0, |d| d.image_rank);
let new_rank = rank.saturating_sub(outgoing).saturating_sub(incoming);
if new_rank > 0 {
next.set(p + ri, q - ri + 1, new_rank);
}
}
self.pages.push(next);
self.differentials.push(vec![]);
self.current_page += 1;
}
pub fn page(&self, r: usize) -> Option<&SpectralSequencePage> {
r.checked_sub(2).and_then(|idx| self.pages.get(idx))
}
pub fn has_collapsed(&self) -> bool {
self.differentials
.iter()
.all(|diffs| diffs.iter().all(|d| d.image_rank == 0))
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct TorExtData {
pub functor: String,
pub ring: String,
pub module1: String,
pub module2: String,
pub degree: usize,
pub value: String,
}
#[allow(dead_code)]
impl TorExtData {
pub fn tor(ring: &str, m: &str, n_mod: &str, degree: usize) -> Self {
Self {
functor: "Tor".to_string(),
ring: ring.to_string(),
module1: m.to_string(),
module2: n_mod.to_string(),
degree,
value: format!("Tor_{}^{}({},{})", degree, ring, m, n_mod),
}
}
pub fn ext(ring: &str, m: &str, n_mod: &str, degree: usize) -> Self {
Self {
functor: "Ext".to_string(),
ring: ring.to_string(),
module1: m.to_string(),
module2: n_mod.to_string(),
degree,
value: format!("Ext^{}_{}({},{})", degree, ring, m, n_mod),
}
}
pub fn is_balanced(&self) -> bool {
self.functor == "Tor"
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct TruncationFunctor {
pub category: String,
pub cutoff: i64,
pub is_cohomological: bool,
}
#[allow(dead_code)]
impl TruncationFunctor {
pub fn new(cat: &str, cutoff: i64, cohom: bool) -> Self {
TruncationFunctor {
category: cat.to_string(),
cutoff,
is_cohomological: cohom,
}
}
pub fn truncation_description(&self) -> String {
if self.is_cohomological {
format!("τ_≥{}: kills H^i for i < {}", self.cutoff, self.cutoff)
} else {
format!("τ_≤{}: kills H^i for i > {}", self.cutoff, self.cutoff)
}
}
pub fn composed_truncation(&self, other_cutoff: i64) -> String {
format!(
"τ_≥{} ∘ τ_≤{}: concentrated in degrees [{}, {}]",
self.cutoff, other_cutoff, self.cutoff, other_cutoff
)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct TorGroup {
pub degree: usize,
pub rank: usize,
pub torsion: Vec<u64>,
}
impl TorGroup {
pub fn new(degree: usize, rank: usize) -> Self {
Self {
degree,
rank,
torsion: vec![],
}
}
pub fn is_zero(&self) -> bool {
self.rank == 0 && self.torsion.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct ExtFunctor {
pub module_m: String,
pub module_n: String,
pub values: Vec<ExtGroup>,
}
impl ExtFunctor {
pub fn compute(module_m: &str, module_n: &str, resolution: &ProjectiveResolution) -> Self {
let betti = resolution.betti_numbers();
let values = betti
.iter()
.enumerate()
.map(|(n, &r)| ExtGroup::new(n, r))
.collect();
Self {
module_m: module_m.to_string(),
module_n: module_n.to_string(),
values,
}
}
pub fn ext_at(&self, n: usize) -> Option<&ExtGroup> {
self.values.get(n)
}
pub fn injective_dimension(&self) -> Option<usize> {
self.values.iter().rposition(|e| !e.is_zero())
}
}
#[derive(Debug, Clone)]
pub struct LyndonHochschildSerre {
pub normal_subgroup: String,
pub quotient_group: String,
pub module: String,
pub e2_page: SpectralSequencePage,
}
impl LyndonHochschildSerre {
pub fn new(
normal_subgroup: &str,
quotient_group: &str,
module: &str,
e2_entries: HashMap<(i32, i32), usize>,
) -> Self {
let mut e2_page = SpectralSequencePage::new(2);
for ((p, q), rank) in e2_entries {
e2_page.set(p, q, rank);
}
Self {
normal_subgroup: normal_subgroup.to_string(),
quotient_group: quotient_group.to_string(),
module: module.to_string(),
e2_page,
}
}
pub fn abutment_rank(&self, n: i32) -> usize {
self.e2_page
.entries
.iter()
.filter(|(&(p, q), _)| p + q == n)
.map(|(_, &r)| r)
.sum()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct HochschildComplex {
pub algebra: String,
pub module: String,
pub dimension: usize,
pub is_free_algebra: bool,
}
#[allow(dead_code)]
impl HochschildComplex {
pub fn new(alg: &str, mod_: &str, dim: usize) -> Self {
HochschildComplex {
algebra: alg.to_string(),
module: mod_.to_string(),
dimension: dim,
is_free_algebra: false,
}
}
pub fn hochschild_kostant_rosenberg(&self) -> String {
"HKR: for smooth commutative algebra A, HH_*(A) ≅ Ω^*_{A/k} (differential forms)"
.to_string()
}
pub fn cyclic_homology_connection(&self) -> String {
format!(
"HC_*({}): Connes' SBI exact sequence HC → HC → HH → (shift)",
self.algebra
)
}
pub fn loday_quillen_tsygan(&self) -> String {
"Loday-Quillen-Tsygan: HC(A) ≅ primitive elements of H*(gl(A))".to_string()
}
pub fn degeneration_at_e2(&self) -> bool {
self.is_free_algebra
}
}
#[derive(Debug, Clone, Default)]
pub struct SpectralSequence {
pub pages: Vec<SpectralSequencePage>,
pub differentials: Vec<Vec<DifferentialMap>>,
}
impl SpectralSequence {
pub fn new() -> Self {
Self::default()
}
pub fn add_page(&mut self, entries: HashMap<(i32, i32), usize>) {
let page = self.pages.len();
let mut p = SpectralSequencePage::new(page);
for ((px, q), rank) in entries {
p.set(px, q, rank);
}
self.pages.push(p);
self.differentials.push(vec![]);
}
pub fn add_differential(&mut self, r: usize, p: i32, q: i32, image_rank: usize) {
let d = DifferentialMap::new(r, p, q, image_rank);
if r < self.differentials.len() {
self.differentials[r].push(d);
}
}
pub fn e_term(&self, r: usize, p: i32, q: i32) -> Option<usize> {
self.pages.get(r).map(|pg| pg.get(p, q))
}
pub fn compute_next_page(&self, r: usize) -> SpectralSequencePage {
if r >= self.pages.len() {
return SpectralSequencePage::new(r + 1);
}
let current = &self.pages[r];
let mut next = SpectralSequencePage::new(r + 1);
let ri = r as i32;
for (&(p, q), &rank) in ¤t.entries {
let incoming_im = if r < self.differentials.len() {
self.differentials[r]
.iter()
.find(|d| d.target == (p, q))
.map_or(0, |d| d.image_rank)
} else {
0
};
let outgoing_im = if r < self.differentials.len() {
self.differentials[r]
.iter()
.find(|d| d.source == (p, q))
.map_or(0, |d| d.image_rank)
} else {
0
};
let ker_rank = rank.saturating_sub(outgoing_im);
let new_rank = ker_rank.saturating_sub(incoming_im);
if new_rank > 0 {
next.set(p + ri, q - ri + 1, new_rank);
}
}
next
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct KunnethFormula {
pub betti_x: Vec<i64>,
pub betti_y: Vec<i64>,
}
#[allow(dead_code)]
impl KunnethFormula {
pub fn new(betti_x: Vec<i64>, betti_y: Vec<i64>) -> Self {
Self { betti_x, betti_y }
}
pub fn product_betti(&self) -> Vec<i64> {
let nx = self.betti_x.len();
let ny = self.betti_y.len();
let n = nx + ny - 1;
let mut result = vec![0i64; n];
for i in 0..nx {
for j in 0..ny {
result[i + j] += self.betti_x[i] * self.betti_y[j];
}
}
result
}
pub fn euler_product(&self) -> i64 {
let chi = |betti: &Vec<i64>| -> i64 {
betti
.iter()
.enumerate()
.map(|(k, &b)| if k % 2 == 0 { b } else { -b })
.sum()
};
chi(&self.betti_x) * chi(&self.betti_y)
}
}
#[derive(Debug, Clone)]
pub struct GroupCohomologyBar {
pub group_name: String,
pub group_order: usize,
pub module_name: String,
pub cohomology_ranks: Vec<usize>,
}
impl GroupCohomologyBar {
pub fn new(group_name: &str, group_order: usize, module_name: &str) -> Self {
Self {
group_name: group_name.to_string(),
group_order,
module_name: module_name.to_string(),
cohomology_ranks: vec![],
}
}
pub fn cochain_rank(&self, n: usize, module_rank: usize) -> usize {
self.group_order.saturating_pow(n as u32) * module_rank
}
pub fn compute_cohomology(&mut self, module_rank: usize, max_degree: usize) {
self.cohomology_ranks = (0..=max_degree)
.map(|n| if n == 0 { module_rank.min(1) } else { 0 })
.collect();
}
pub fn cohomology_at(&self, n: usize) -> usize {
self.cohomology_ranks.get(n).copied().unwrap_or(0)
}
pub fn euler_characteristic(&self) -> i64 {
self.cohomology_ranks
.iter()
.enumerate()
.map(|(n, &r)| if n % 2 == 0 { r as i64 } else { -(r as i64) })
.sum()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CyclicHomologyData {
pub algebra_type: String,
pub hc_0: String,
pub negative_cyclic: String,
pub periodic_cyclic: String,
}
#[allow(dead_code)]
impl CyclicHomologyData {
pub fn for_polynomial_ring(vars: usize) -> Self {
CyclicHomologyData {
algebra_type: format!("k[x_1,...,x_{}]", vars),
hc_0: format!("Ω^0 = k[x_1,...,x_{}]", vars),
negative_cyclic: "HC^-_{*} related to de Rham".to_string(),
periodic_cyclic: "HP_* = de Rham cohomology (periodic)".to_string(),
}
}
pub fn connes_differential(&self) -> String {
"Connes B-operator: B: HC_n → HC_{n+1} (degree +1 boundary-like)".to_string()
}
pub fn primary_characteristic_class(&self) -> String {
"Chern character: K_0(A) → HC_0(A) ≅ A/[A,A] (trace map)".to_string()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct MayerVietorisSequence {
pub space_name: String,
pub a_name: String,
pub b_name: String,
pub c_name: String,
pub betti_a: Vec<i64>,
pub betti_b: Vec<i64>,
pub betti_c: Vec<i64>,
pub betti_x: Vec<i64>,
}
#[allow(dead_code)]
impl MayerVietorisSequence {
pub fn new(
space_name: &str,
a_name: &str,
b_name: &str,
c_name: &str,
betti_a: Vec<i64>,
betti_b: Vec<i64>,
betti_c: Vec<i64>,
betti_x: Vec<i64>,
) -> Self {
Self {
space_name: space_name.to_string(),
a_name: a_name.to_string(),
b_name: b_name.to_string(),
c_name: c_name.to_string(),
betti_a,
betti_b,
betti_c,
betti_x,
}
}
pub fn euler_characteristic(&self) -> i64 {
self.betti_x
.iter()
.enumerate()
.map(|(k, &b)| if k % 2 == 0 { b } else { -b })
.sum()
}
pub fn verify_euler_relation(&self) -> bool {
let chi = |betti: &Vec<i64>| -> i64 {
betti
.iter()
.enumerate()
.map(|(k, &b)| if k % 2 == 0 { b } else { -b })
.sum()
};
chi(&self.betti_x) == chi(&self.betti_a) + chi(&self.betti_b) - chi(&self.betti_c)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct UniversalCoefficients {
pub integral_homology: Vec<(usize, Vec<u64>)>,
pub coeff_ring: String,
}
#[allow(dead_code)]
impl UniversalCoefficients {
pub fn new(integral_homology: Vec<(usize, Vec<u64>)>, coeff_ring: &str) -> Self {
Self {
integral_homology,
coeff_ring: coeff_ring.to_string(),
}
}
pub fn betti_over_field(&self) -> Vec<usize> {
self.integral_homology.iter().map(|(r, _)| *r).collect()
}
pub fn torsion_part(&self, k: usize) -> Option<&Vec<u64>> {
self.integral_homology.get(k).map(|(_, t)| t)
}
}
#[derive(Debug, Clone, Default)]
pub struct SimplexBoundaryMatrix {
pub rows: usize,
pub cols: usize,
pub entries: Vec<(usize, usize, i64)>,
}
impl SimplexBoundaryMatrix {
pub fn new(rows: usize, cols: usize) -> Self {
Self {
rows,
cols,
entries: vec![],
}
}
pub fn set(&mut self, i: usize, j: usize, v: i64) {
self.entries.retain(|(r, c, _)| !(*r == i && *c == j));
if v != 0 {
self.entries.push((i, j, v));
}
}
pub fn get(&self, i: usize, j: usize) -> i64 {
self.entries
.iter()
.find(|(r, c, _)| *r == i && *c == j)
.map_or(0, |(_, _, v)| *v)
}
pub fn to_dense(&self) -> Vec<Vec<i64>> {
let mut m = vec![vec![0i64; self.cols]; self.rows];
for &(r, c, v) in &self.entries {
if r < self.rows && c < self.cols {
m[r][c] = v;
}
}
m
}
pub fn smith_normal_form_diagonal(&self) -> Vec<i64> {
let mut m = self.to_dense();
let rows = self.rows;
let cols = self.cols;
let mut diag = vec![];
let mut pivot = 0usize;
for col in 0..cols {
if pivot >= rows {
break;
}
let found = (pivot..rows).find(|&r| m[r][col] != 0);
if found.is_none() {
continue;
}
let pr = found.expect("found is Some: checked by is_none guard above");
m.swap(pivot, pr);
let pv = m[pivot][col];
for r in (pivot + 1)..rows {
let factor = m[r][col];
if factor != 0 {
for c in 0..cols {
m[r][c] = m[r][c] * pv - m[pivot][c] * factor;
}
}
}
if m[pivot][col] != 0 {
diag.push(m[pivot][col].abs());
}
pivot += 1;
}
diag
}
pub fn rank(&self) -> usize {
self.smith_normal_form_diagonal().len()
}
pub fn torsion_coefficients(&self) -> Vec<i64> {
self.smith_normal_form_diagonal()
.into_iter()
.filter(|&d| d > 1)
.collect()
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ExtGroup {
pub degree: usize,
pub rank: usize,
pub torsion: Vec<u64>,
}
impl ExtGroup {
pub fn new(degree: usize, rank: usize) -> Self {
Self {
degree,
rank,
torsion: vec![],
}
}
pub fn is_zero(&self) -> bool {
self.rank == 0 && self.torsion.is_empty()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct LongExactSequence {
pub groups: Vec<String>,
pub connecting_homomorphism: String,
}
#[allow(dead_code)]
impl LongExactSequence {
pub fn from_short(a: &str, b: &str, c: &str) -> Self {
let groups = vec![
format!("H_n({})", a),
format!("H_n({})", b),
format!("H_n({})", c),
format!("H_{{n-1}}({})", a),
];
Self {
groups,
connecting_homomorphism: format!("delta: H_n({}) -> H_{{n-1}}({})", c, a),
}
}
pub fn length(&self) -> usize {
self.groups.len()
}
}
#[derive(Debug, Clone)]
pub struct InjectiveResolution {
pub module_name: String,
pub steps: Vec<ResolutionStep>,
}
impl InjectiveResolution {
pub fn new(module_name: &str) -> Self {
Self {
module_name: module_name.to_string(),
steps: vec![],
}
}
pub fn add_step(&mut self, rank: usize, boundary: Vec<Vec<i64>>) {
let degree = self.steps.len();
self.steps.push(ResolutionStep {
degree,
rank,
boundary,
});
}
pub fn injective_dimension(&self) -> Option<usize> {
self.steps.iter().rposition(|s| s.rank > 0)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct HomologyGroup {
pub degree: i32,
pub rank: usize,
pub torsion: Vec<u64>,
}
impl HomologyGroup {
pub fn new(degree: i32, rank: usize, torsion: Vec<u64>) -> Self {
Self {
degree,
rank,
torsion,
}
}
pub fn is_trivial(&self) -> bool {
self.rank == 0 && self.torsion.is_empty()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CWComplex {
pub cells: Vec<usize>,
pub attaching_degrees: Vec<Vec<i32>>,
}
#[allow(dead_code)]
impl CWComplex {
pub fn new(cells: Vec<usize>) -> Self {
let m = if cells.is_empty() { 0 } else { cells.len() - 1 };
Self {
cells,
attaching_degrees: vec![vec![]; m],
}
}
pub fn euler_characteristic(&self) -> i64 {
self.cells
.iter()
.enumerate()
.map(|(k, &c)| if k % 2 == 0 { c as i64 } else { -(c as i64) })
.sum()
}
pub fn betti_upper_bound(&self) -> Vec<usize> {
self.cells.clone()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ExtGroupComputation {
pub module_a: String,
pub module_b: String,
pub projective_resolution_length: usize,
pub computed_exts: Vec<String>,
}
#[allow(dead_code)]
impl ExtGroupComputation {
pub fn new(a: &str, b: &str) -> Self {
ExtGroupComputation {
module_a: a.to_string(),
module_b: b.to_string(),
projective_resolution_length: 0,
computed_exts: vec![],
}
}
pub fn compute_ext_0(&self) -> String {
format!(
"Ext^0({}, {}) = Hom({}, {})",
self.module_a, self.module_b, self.module_a, self.module_b
)
}
pub fn compute_ext_1(&self) -> String {
format!(
"Ext^1({}, {}): obstruction to extension 0→{}→E→{}→0",
self.module_a, self.module_b, self.module_b, self.module_a
)
}
pub fn horseshoe_lemma(&self) -> String {
"Horseshoe lemma: given short exact sequence of modules, combine projective resolutions"
.to_string()
}
pub fn global_dimension(&self) -> String {
format!(
"gl.dim ≤ {}: ext vanishes above degree {}",
self.projective_resolution_length, self.projective_resolution_length
)
}
}
#[derive(Debug, Clone, Default)]
pub struct PersistentHomologyComputer {
pub filtration: Vec<(f64, usize)>,
}
impl PersistentHomologyComputer {
pub fn new() -> Self {
Self::default()
}
pub fn add_simplex(&mut self, filtration_value: f64, dimension: usize) {
self.filtration.push((filtration_value, dimension));
self.filtration.sort_by(|a, b| {
a.0.partial_cmp(&b.0)
.unwrap_or(std::cmp::Ordering::Equal)
.then(a.1.cmp(&b.1))
});
}
pub fn compute_barcode(&self) -> PersistenceBarcode {
let mut barcode = PersistenceBarcode::new();
let max_dim = self.filtration.iter().map(|(_, d)| *d).max().unwrap_or(0);
for dim in 0..=max_dim {
let birth_times: Vec<f64> = self
.filtration
.iter()
.filter(|(_, d)| *d == dim)
.map(|(v, _)| *v)
.collect();
let kill_times: Vec<f64> = self
.filtration
.iter()
.filter(|(_, d)| *d == dim + 1)
.map(|(v, _)| *v)
.collect();
let mut kill_iter = kill_times.iter().peekable();
for birth in &birth_times {
if let Some(&death) = kill_iter.next() {
barcode.add(*birth, death, dim);
} else {
barcode.add(*birth, f64::INFINITY, dim);
}
}
}
barcode
}
}
#[derive(Debug, Clone)]
pub struct ChainMap {
pub source: ChainComplex,
pub target: ChainComplex,
pub components: Vec<Vec<Vec<i64>>>,
}
impl ChainMap {
pub fn new(source: ChainComplex, target: ChainComplex, components: Vec<Vec<Vec<i64>>>) -> Self {
Self {
source,
target,
components,
}
}
pub fn induced_homology_rank(&self, n: usize) -> usize {
self.components
.get(n)
.map(|m| image_rank(m, self.source.groups.get(n).map_or(0, |g| g.rank)))
.unwrap_or(0)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct SpectralSequenceData {
pub name: String,
pub filtration_type: String,
pub converges_to: String,
pub page: usize,
}
#[allow(dead_code)]
impl SpectralSequenceData {
pub fn serre(base: &str, fiber: &str, total: &str) -> Self {
Self {
name: format!("Serre({} -> {} -> {})", fiber, total, base),
filtration_type: "Serre filtration".to_string(),
converges_to: format!("H*({})", total),
page: 2,
}
}
pub fn leray(map: &str) -> Self {
Self {
name: format!("Leray({})", map),
filtration_type: "sheaf cohomology".to_string(),
converges_to: format!("H*(domain({}))", map),
page: 2,
}
}
pub fn e2_page_description(&self) -> String {
format!(
"E_2 page of {}: converges to {}",
self.name, self.converges_to
)
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct FibrationSequence {
pub total_space: String,
pub base_space: String,
pub fiber: String,
pub pi_base: Vec<i64>,
pub pi_fiber: Vec<i64>,
pub pi_total: Vec<i64>,
}
#[allow(dead_code)]
impl FibrationSequence {
pub fn new(
total_space: &str,
base_space: &str,
fiber: &str,
pi_base: Vec<i64>,
pi_fiber: Vec<i64>,
pi_total: Vec<i64>,
) -> Self {
Self {
total_space: total_space.to_string(),
base_space: base_space.to_string(),
fiber: fiber.to_string(),
pi_base,
pi_fiber,
pi_total,
}
}
pub fn euler_product_fibration(&self, chi_f: i64, chi_b: i64) -> i64 {
chi_f * chi_b
}
}
#[derive(Debug, Clone)]
pub struct LocalCohomology {
pub ideal_name: String,
pub module_name: String,
pub groups: Vec<LocalCohomologyGroup>,
}
impl LocalCohomology {
pub fn new(ideal_name: &str, module_name: &str) -> Self {
Self {
ideal_name: ideal_name.to_string(),
module_name: module_name.to_string(),
groups: vec![],
}
}
pub fn add_group(&mut self, degree: usize, rank: usize) {
self.groups.push(LocalCohomologyGroup::new(degree, rank));
}
pub fn cohomological_dimension(&self) -> Option<usize> {
self.groups.iter().rposition(|g| !g.is_zero())
}
}
#[derive(Debug, Clone)]
pub struct FlatResolution {
pub module_name: String,
pub flat_dim: Option<usize>,
pub ranks: Vec<usize>,
}
impl FlatResolution {
pub fn new(module_name: &str, ranks: Vec<usize>) -> Self {
let flat_dim = ranks.iter().rposition(|&r| r > 0);
Self {
module_name: module_name.to_string(),
flat_dim,
ranks,
}
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PersistentBettiNumbers {
pub pairs: Vec<Vec<(f64, f64)>>,
}
#[allow(dead_code)]
impl PersistentBettiNumbers {
pub fn new(pairs: Vec<Vec<(f64, f64)>>) -> Self {
Self { pairs }
}
pub fn betti_at(&self, k: usize, t: f64) -> usize {
if k >= self.pairs.len() {
return 0;
}
self.pairs[k]
.iter()
.filter(|&&(b, d)| b <= t && t < d)
.count()
}
pub fn total_persistence(&self, k: usize) -> f64 {
if k >= self.pairs.len() {
return 0.0;
}
self.pairs[k].iter().map(|&(b, d)| d - b).sum()
}
pub fn bottleneck_approx(&self, other: &Self, k: usize) -> f64 {
if k >= self.pairs.len() || k >= other.pairs.len() {
return 0.0;
}
let a = &self.pairs[k];
let b = &other.pairs[k];
if a.is_empty() && b.is_empty() {
return 0.0;
}
let inf_dist =
|p: (f64, f64), q: (f64, f64)| -> f64 { (p.0 - q.0).abs().max((p.1 - q.1).abs()) };
let mut max_min = 0.0f64;
for &pa in a {
let min_d = b
.iter()
.map(|&pb| inf_dist(pa, pb))
.fold(f64::INFINITY, f64::min);
max_min = max_min.max(min_d);
}
max_min
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct DeRhamCohomology {
pub manifold_name: String,
pub dimension: usize,
pub betti_numbers: Vec<i64>,
}
#[allow(dead_code)]
impl DeRhamCohomology {
pub fn new(manifold_name: &str, dimension: usize, betti_numbers: Vec<i64>) -> Self {
Self {
manifold_name: manifold_name.to_string(),
dimension,
betti_numbers,
}
}
pub fn check_poincare_duality(&self) -> bool {
let n = self.dimension;
if self.betti_numbers.len() != n + 1 {
return false;
}
(0..=n).all(|k| self.betti_numbers[k] == self.betti_numbers[n - k])
}
pub fn euler_characteristic(&self) -> i64 {
self.betti_numbers
.iter()
.enumerate()
.map(|(k, &b)| if k % 2 == 0 { b } else { -b })
.sum()
}
}
#[derive(Debug, Clone, Default)]
pub struct ChainComplex {
pub groups: Vec<GradedGroup>,
pub boundaries: Vec<Vec<Vec<i64>>>,
}
impl ChainComplex {
pub fn new() -> Self {
Self::default()
}
pub fn add_group(&mut self, rank: usize, name: &str) {
self.groups.push(GradedGroup::new(rank, name));
}
pub fn add_boundary(&mut self, matrix: Vec<Vec<i64>>) {
self.boundaries.push(matrix);
}
pub fn betti_numbers(&self) -> Vec<i64> {
let n = self.groups.len();
(0..n)
.map(|i| {
let ker = if i > 0 && i - 1 < self.boundaries.len() {
kernel_rank(&self.boundaries[i - 1], self.groups[i].rank) as i64
} else {
self.groups[i].rank as i64
};
let img = if i < self.boundaries.len() && i + 1 < self.groups.len() {
image_rank(&self.boundaries[i], self.groups[i + 1].rank) as i64
} else {
0
};
ker - img
})
.collect()
}
pub fn compute_homology(&self) -> Vec<HomologyGroup> {
self.betti_numbers()
.into_iter()
.enumerate()
.map(|(n, rank)| HomologyGroup {
degree: n as i32,
rank: rank.max(0) as usize,
torsion: vec![],
})
.collect()
}
pub fn euler_characteristic(&self) -> i64 {
self.betti_numbers()
.iter()
.enumerate()
.map(|(n, &b)| if n % 2 == 0 { b } else { -b })
.sum()
}
pub fn is_exact_at(&self, k: usize) -> bool {
let betti = self.betti_numbers();
betti.get(k).copied().unwrap_or(0) == 0
}
pub fn is_exact(&self) -> bool {
self.betti_numbers().iter().all(|&b| b == 0)
}
}
#[derive(Debug, Clone)]
pub struct DifferentialMap {
pub page: usize,
pub source: (i32, i32),
pub target: (i32, i32),
pub image_rank: usize,
}
impl DifferentialMap {
pub fn new(r: usize, p: i32, q: i32, image_rank: usize) -> Self {
let ri = r as i32;
Self {
page: r,
source: (p, q),
target: (p + ri, q - ri + 1),
image_rank,
}
}
}
#[derive(Debug, Clone)]
pub struct BarResolution {
pub group_name: String,
pub num_steps: usize,
pub ranks: Vec<usize>,
}
impl BarResolution {
pub fn new(group_name: &str, group_order: usize, num_steps: usize) -> Self {
let ranks = (0..num_steps)
.map(|n| group_order.saturating_pow(n as u32))
.collect();
Self {
group_name: group_name.to_string(),
num_steps,
ranks,
}
}
pub fn rank_at(&self, n: usize) -> usize {
self.ranks.get(n).copied().unwrap_or(0)
}
}
#[derive(Debug, Clone)]
pub struct ResolutionStep {
pub degree: usize,
pub rank: usize,
pub boundary: Vec<Vec<i64>>,
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct PerverseSheafData {
pub stratification: Vec<String>,
pub perversity: Vec<i64>,
pub is_ic_sheaf: bool,
pub support_dimension: Vec<usize>,
}
#[allow(dead_code)]
impl PerverseSheafData {
pub fn new(strat: Vec<String>, perversity: Vec<i64>) -> Self {
let n = strat.len();
PerverseSheafData {
stratification: strat,
perversity,
is_ic_sheaf: false,
support_dimension: (0..n).collect(),
}
}
pub fn intersection_cohomology_description(&self) -> String {
"IC sheaf: intermediate extension j_!* F of local system F".to_string()
}
pub fn bbdg_decomposition(&self) -> String {
"BBDG: semisimple complexes over finite fields decompose into shifts of IC sheaves"
.to_string()
}
pub fn verdier_duality(&self) -> String {
"Verdier duality: D(IC_X(L)) ≅ IC_X(L^∨) for selfdual local system".to_string()
}
pub fn support_condition(&self) -> String {
format!(
"Perversity condition: dim supp H^i ≤ -i on {} strata",
self.stratification.len()
)
}
}
#[derive(Debug, Clone, Default)]
pub struct SpectralSequencePage {
pub entries: HashMap<(i32, i32), usize>,
pub page: usize,
}
impl SpectralSequencePage {
pub fn new(page: usize) -> Self {
Self {
entries: HashMap::new(),
page,
}
}
pub fn set(&mut self, p: i32, q: i32, rank: usize) {
self.entries.insert((p, q), rank);
}
pub fn get(&self, p: i32, q: i32) -> usize {
self.entries.get(&(p, q)).copied().unwrap_or(0)
}
pub fn total_rank(&self) -> usize {
self.entries.values().sum()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct ChainCplxExt {
pub chain_groups: Vec<usize>,
pub boundary_ranks: Vec<usize>,
}
#[allow(dead_code)]
impl ChainCplxExt {
pub fn new(chain_groups: Vec<usize>, boundary_ranks: Vec<usize>) -> Self {
assert!(
chain_groups.len() == boundary_ranks.len() + 1
|| chain_groups.len() == boundary_ranks.len(),
"chain_groups.len() should be boundary_ranks.len() or boundary_ranks.len()+1"
);
Self {
chain_groups,
boundary_ranks,
}
}
pub fn betti_numbers(&self) -> Vec<i64> {
let n = self.chain_groups.len();
(0..n)
.map(|k| {
let ck = self.chain_groups[k] as i64;
let im_kp1 = if k + 1 < self.boundary_ranks.len() {
self.boundary_ranks[k + 1] as i64
} else {
0
};
let rk_dk = if k < self.boundary_ranks.len() {
self.boundary_ranks[k] as i64
} else {
0
};
ck - im_kp1 - rk_dk
})
.collect()
}
pub fn euler_characteristic(&self) -> i64 {
let betti = self.betti_numbers();
betti
.iter()
.enumerate()
.map(|(k, &b)| if k % 2 == 0 { b } else { -b })
.sum()
}
}
#[derive(Debug, Clone)]
pub struct ProjectiveResolution {
pub module_name: String,
pub steps: Vec<ResolutionStep>,
}
impl ProjectiveResolution {
pub fn new(module_name: &str) -> Self {
Self {
module_name: module_name.to_string(),
steps: vec![],
}
}
pub fn add_step(&mut self, rank: usize, boundary: Vec<Vec<i64>>) {
let degree = self.steps.len();
self.steps.push(ResolutionStep {
degree,
rank,
boundary,
});
}
pub fn projective_dimension(&self) -> Option<usize> {
self.steps.iter().rposition(|s| s.rank > 0)
}
pub fn betti_numbers(&self) -> Vec<usize> {
self.steps.iter().map(|s| s.rank).collect()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct KunnethData {
pub space_x: String,
pub space_y: String,
pub field_coefficients: bool,
}
#[allow(dead_code)]
impl KunnethData {
pub fn over_field(x: &str, y: &str) -> Self {
Self {
space_x: x.to_string(),
space_y: y.to_string(),
field_coefficients: true,
}
}
pub fn kunneth_description(&self) -> String {
if self.field_coefficients {
format!(
"H*({} x {}; k) ≅ H*({};k) ⊗ H*({};k)",
self.space_x, self.space_y, self.space_x, self.space_y
)
} else {
format!(
"Künneth: H_n({} x {}) has Tor correction term",
self.space_x, self.space_y
)
}
}
}
#[derive(Debug, Clone)]
pub struct TorFunctor {
pub module_m: String,
pub module_n: String,
pub values: Vec<TorGroup>,
}
impl TorFunctor {
pub fn compute(module_m: &str, module_n: &str, resolution: &ProjectiveResolution) -> Self {
let betti = resolution.betti_numbers();
let values = betti
.iter()
.enumerate()
.map(|(n, &r)| TorGroup::new(n, r))
.collect();
Self {
module_m: module_m.to_string(),
module_n: module_n.to_string(),
values,
}
}
pub fn tor_at(&self, n: usize) -> Option<&TorGroup> {
self.values.get(n)
}
pub fn projective_dimension(&self) -> Option<usize> {
self.values.iter().rposition(|t| !t.is_zero())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct BirthDeathPair {
pub birth: f64,
pub death: f64,
pub degree: usize,
}
impl BirthDeathPair {
pub fn new(birth: f64, death: f64, degree: usize) -> Self {
Self {
birth,
death,
degree,
}
}
pub fn persistence(&self) -> f64 {
self.death - self.birth
}
pub fn is_essential(&self) -> bool {
self.death.is_infinite()
}
}
#[allow(dead_code)]
#[derive(Debug, Clone)]
pub struct CechCocycle {
pub open_cover_size: usize,
pub degree: usize,
pub cochain: Vec<Vec<f64>>,
pub is_cocycle: bool,
}
#[allow(dead_code)]
impl CechCocycle {
pub fn new(cover_size: usize, degree: usize) -> Self {
let cochain = vec![vec![0.0; cover_size]; cover_size.pow(degree as u32)];
CechCocycle {
open_cover_size: cover_size,
degree,
cochain,
is_cocycle: true,
}
}
pub fn leray_theorem(&self) -> String {
"Leray: for acyclic covers, Čech cohomology = sheaf cohomology".to_string()
}
pub fn refinement_map_description(&self) -> String {
format!(
"Refinement: Čech H^{}(U;F) → Čech H^{}(V;F) for V refinement of U",
self.degree, self.degree
)
}
pub fn mayer_vietoris_for_two_opens(&self) -> String {
"Mayer-Vietoris: 0 → F(U∪V) → F(U)⊕F(V) → F(U∩V) → H^1 → ...".to_string()
}
}