1use oxilean_kernel::{BinderInfo, Declaration, Environment, Expr, Level, Name};
6use std::collections::HashMap;
7
8use super::functions::*;
9
10#[derive(Debug, Clone)]
12pub struct Convergence {
13 pub e_infty: SpectralSequencePage,
15 pub filtration_grades: HashMap<(i32, i32), usize>,
17}
18impl Convergence {
19 pub fn new(e_infty: SpectralSequencePage) -> Self {
21 let filtration_grades = e_infty.entries.clone();
22 Self {
23 e_infty,
24 filtration_grades,
25 }
26 }
27 pub fn cohomology_rank(&self, n: i32) -> usize {
29 self.e_infty
30 .entries
31 .iter()
32 .filter(|(&(p, q), _)| p + q == n)
33 .map(|(_, &r)| r)
34 .sum()
35 }
36}
37#[derive(Debug, Clone, PartialEq, Eq)]
39pub struct LocalCohomologyGroup {
40 pub degree: usize,
42 pub rank: usize,
44}
45impl LocalCohomologyGroup {
46 pub fn new(degree: usize, rank: usize) -> Self {
48 Self { degree, rank }
49 }
50 pub fn is_zero(&self) -> bool {
52 self.rank == 0
53 }
54}
55#[derive(Debug, Clone, PartialEq, Eq)]
57pub struct GradedGroup {
58 pub rank: usize,
60 pub name: String,
62}
63impl GradedGroup {
64 pub fn new(rank: usize, name: &str) -> Self {
66 Self {
67 rank,
68 name: name.to_string(),
69 }
70 }
71}
72#[allow(dead_code)]
74#[derive(Debug, Clone)]
75pub struct PersistenceInterval {
76 pub dimension: usize,
77 pub birth: f64,
78 pub death: f64,
79}
80#[allow(dead_code)]
81impl PersistenceInterval {
82 pub fn new(dim: usize, birth: f64, death: f64) -> Self {
84 Self {
85 dimension: dim,
86 birth,
87 death,
88 }
89 }
90 pub fn persistence(&self) -> f64 {
92 self.death - self.birth
93 }
94 pub fn is_essential(&self) -> bool {
96 self.death == f64::INFINITY
97 }
98 pub fn contains(&self, t: f64) -> bool {
100 t >= self.birth && t < self.death
101 }
102}
103#[derive(Debug, Clone, Default)]
105pub struct PersistenceBarcode {
106 pub intervals: Vec<BirthDeathPair>,
108}
109impl PersistenceBarcode {
110 pub fn new() -> Self {
112 Self::default()
113 }
114 pub fn add(&mut self, birth: f64, death: f64, degree: usize) {
116 self.intervals
117 .push(BirthDeathPair::new(birth, death, degree));
118 }
119 pub fn intervals_in_degree(&self, n: usize) -> Vec<&BirthDeathPair> {
121 self.intervals.iter().filter(|p| p.degree == n).collect()
122 }
123 pub fn betti_number(&self, n: usize) -> usize {
125 self.intervals_in_degree(n).len()
126 }
127 pub fn bottleneck_distance(&self, other: &PersistenceBarcode, n: usize) -> f64 {
131 let mut a: Vec<f64> = self
132 .intervals_in_degree(n)
133 .iter()
134 .map(|p| p.persistence().min(1e9))
135 .collect();
136 let mut b: Vec<f64> = other
137 .intervals_in_degree(n)
138 .iter()
139 .map(|p| p.persistence().min(1e9))
140 .collect();
141 a.sort_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal));
142 b.sort_by(|x, y| x.partial_cmp(y).unwrap_or(std::cmp::Ordering::Equal));
143 let mut dist = 0.0_f64;
144 let len = a.len().max(b.len());
145 for i in 0..len {
146 let ai = a.get(i).copied().unwrap_or(0.0);
147 let bi = b.get(i).copied().unwrap_or(0.0);
148 dist = dist.max((ai - bi).abs());
149 }
150 dist
151 }
152}
153#[derive(Debug, Clone, Default)]
155pub struct SpectralSequencePageManager {
156 pub current_page: usize,
158 pub pages: Vec<SpectralSequencePage>,
160 pub differentials: Vec<Vec<DifferentialMap>>,
162}
163impl SpectralSequencePageManager {
164 pub fn new() -> Self {
166 Self {
167 current_page: 2,
168 pages: vec![],
169 differentials: vec![],
170 }
171 }
172 pub fn add_page(&mut self, entries: HashMap<(i32, i32), usize>) {
174 let r = self.current_page;
175 let mut page = SpectralSequencePage::new(r);
176 for ((p, q), rank) in entries {
177 page.set(p, q, rank);
178 }
179 self.pages.push(page);
180 self.differentials.push(vec![]);
181 self.current_page += 1;
182 }
183 pub fn add_differential(&mut self, p: i32, q: i32, image_rank: usize) {
185 let r = self.current_page.saturating_sub(1);
186 let d = DifferentialMap::new(r, p, q, image_rank);
187 if let Some(last) = self.differentials.last_mut() {
188 last.push(d);
189 }
190 }
191 pub fn advance(&mut self) {
193 let last_idx = self.pages.len().saturating_sub(1);
194 if self.pages.is_empty() {
195 return;
196 }
197 let current = &self.pages[last_idx];
198 let diffs = &self.differentials[last_idx];
199 let r = current.page;
200 let ri = r as i32;
201 let mut next = SpectralSequencePage::new(r + 1);
202 for (&(p, q), &rank) in ¤t.entries {
203 let incoming = diffs
204 .iter()
205 .find(|d| d.target == (p, q))
206 .map_or(0, |d| d.image_rank);
207 let outgoing = diffs
208 .iter()
209 .find(|d| d.source == (p, q))
210 .map_or(0, |d| d.image_rank);
211 let new_rank = rank.saturating_sub(outgoing).saturating_sub(incoming);
212 if new_rank > 0 {
213 next.set(p + ri, q - ri + 1, new_rank);
214 }
215 }
216 self.pages.push(next);
217 self.differentials.push(vec![]);
218 self.current_page += 1;
219 }
220 pub fn page(&self, r: usize) -> Option<&SpectralSequencePage> {
222 r.checked_sub(2).and_then(|idx| self.pages.get(idx))
223 }
224 pub fn has_collapsed(&self) -> bool {
226 self.differentials
227 .iter()
228 .all(|diffs| diffs.iter().all(|d| d.image_rank == 0))
229 }
230}
231#[allow(dead_code)]
233#[derive(Debug, Clone)]
234pub struct TorExtData {
235 pub functor: String,
236 pub ring: String,
237 pub module1: String,
238 pub module2: String,
239 pub degree: usize,
240 pub value: String,
241}
242#[allow(dead_code)]
243impl TorExtData {
244 pub fn tor(ring: &str, m: &str, n_mod: &str, degree: usize) -> Self {
246 Self {
247 functor: "Tor".to_string(),
248 ring: ring.to_string(),
249 module1: m.to_string(),
250 module2: n_mod.to_string(),
251 degree,
252 value: format!("Tor_{}^{}({},{})", degree, ring, m, n_mod),
253 }
254 }
255 pub fn ext(ring: &str, m: &str, n_mod: &str, degree: usize) -> Self {
257 Self {
258 functor: "Ext".to_string(),
259 ring: ring.to_string(),
260 module1: m.to_string(),
261 module2: n_mod.to_string(),
262 degree,
263 value: format!("Ext^{}_{}({},{})", degree, ring, m, n_mod),
264 }
265 }
266 pub fn is_balanced(&self) -> bool {
268 self.functor == "Tor"
269 }
270}
271#[allow(dead_code)]
272#[derive(Debug, Clone)]
273pub struct TruncationFunctor {
274 pub category: String,
275 pub cutoff: i64,
276 pub is_cohomological: bool,
277}
278#[allow(dead_code)]
279impl TruncationFunctor {
280 pub fn new(cat: &str, cutoff: i64, cohom: bool) -> Self {
281 TruncationFunctor {
282 category: cat.to_string(),
283 cutoff,
284 is_cohomological: cohom,
285 }
286 }
287 pub fn truncation_description(&self) -> String {
288 if self.is_cohomological {
289 format!("τ_≥{}: kills H^i for i < {}", self.cutoff, self.cutoff)
290 } else {
291 format!("τ_≤{}: kills H^i for i > {}", self.cutoff, self.cutoff)
292 }
293 }
294 pub fn composed_truncation(&self, other_cutoff: i64) -> String {
295 format!(
296 "τ_≥{} ∘ τ_≤{}: concentrated in degrees [{}, {}]",
297 self.cutoff, other_cutoff, self.cutoff, other_cutoff
298 )
299 }
300}
301#[derive(Debug, Clone, PartialEq, Eq)]
303pub struct TorGroup {
304 pub degree: usize,
306 pub rank: usize,
308 pub torsion: Vec<u64>,
310}
311impl TorGroup {
312 pub fn new(degree: usize, rank: usize) -> Self {
314 Self {
315 degree,
316 rank,
317 torsion: vec![],
318 }
319 }
320 pub fn is_zero(&self) -> bool {
322 self.rank == 0 && self.torsion.is_empty()
323 }
324}
325#[derive(Debug, Clone)]
327pub struct ExtFunctor {
328 pub module_m: String,
330 pub module_n: String,
332 pub values: Vec<ExtGroup>,
334}
335impl ExtFunctor {
336 pub fn compute(module_m: &str, module_n: &str, resolution: &ProjectiveResolution) -> Self {
338 let betti = resolution.betti_numbers();
339 let values = betti
340 .iter()
341 .enumerate()
342 .map(|(n, &r)| ExtGroup::new(n, r))
343 .collect();
344 Self {
345 module_m: module_m.to_string(),
346 module_n: module_n.to_string(),
347 values,
348 }
349 }
350 pub fn ext_at(&self, n: usize) -> Option<&ExtGroup> {
352 self.values.get(n)
353 }
354 pub fn injective_dimension(&self) -> Option<usize> {
356 self.values.iter().rposition(|e| !e.is_zero())
357 }
358}
359#[derive(Debug, Clone)]
364pub struct LyndonHochschildSerre {
365 pub normal_subgroup: String,
367 pub quotient_group: String,
369 pub module: String,
371 pub e2_page: SpectralSequencePage,
373}
374impl LyndonHochschildSerre {
375 pub fn new(
377 normal_subgroup: &str,
378 quotient_group: &str,
379 module: &str,
380 e2_entries: HashMap<(i32, i32), usize>,
381 ) -> Self {
382 let mut e2_page = SpectralSequencePage::new(2);
383 for ((p, q), rank) in e2_entries {
384 e2_page.set(p, q, rank);
385 }
386 Self {
387 normal_subgroup: normal_subgroup.to_string(),
388 quotient_group: quotient_group.to_string(),
389 module: module.to_string(),
390 e2_page,
391 }
392 }
393 pub fn abutment_rank(&self, n: i32) -> usize {
398 self.e2_page
399 .entries
400 .iter()
401 .filter(|(&(p, q), _)| p + q == n)
402 .map(|(_, &r)| r)
403 .sum()
404 }
405}
406#[allow(dead_code)]
407#[derive(Debug, Clone)]
408pub struct HochschildComplex {
409 pub algebra: String,
410 pub module: String,
411 pub dimension: usize,
412 pub is_free_algebra: bool,
413}
414#[allow(dead_code)]
415impl HochschildComplex {
416 pub fn new(alg: &str, mod_: &str, dim: usize) -> Self {
417 HochschildComplex {
418 algebra: alg.to_string(),
419 module: mod_.to_string(),
420 dimension: dim,
421 is_free_algebra: false,
422 }
423 }
424 pub fn hochschild_kostant_rosenberg(&self) -> String {
425 "HKR: for smooth commutative algebra A, HH_*(A) ≅ Ω^*_{A/k} (differential forms)"
426 .to_string()
427 }
428 pub fn cyclic_homology_connection(&self) -> String {
429 format!(
430 "HC_*({}): Connes' SBI exact sequence HC → HC → HH → (shift)",
431 self.algebra
432 )
433 }
434 pub fn loday_quillen_tsygan(&self) -> String {
435 "Loday-Quillen-Tsygan: HC(A) ≅ primitive elements of H*(gl(A))".to_string()
436 }
437 pub fn degeneration_at_e2(&self) -> bool {
438 self.is_free_algebra
439 }
440}
441#[derive(Debug, Clone, Default)]
443pub struct SpectralSequence {
444 pub pages: Vec<SpectralSequencePage>,
446 pub differentials: Vec<Vec<DifferentialMap>>,
448}
449impl SpectralSequence {
450 pub fn new() -> Self {
452 Self::default()
453 }
454 pub fn add_page(&mut self, entries: HashMap<(i32, i32), usize>) {
456 let page = self.pages.len();
457 let mut p = SpectralSequencePage::new(page);
458 for ((px, q), rank) in entries {
459 p.set(px, q, rank);
460 }
461 self.pages.push(p);
462 self.differentials.push(vec![]);
463 }
464 pub fn add_differential(&mut self, r: usize, p: i32, q: i32, image_rank: usize) {
466 let d = DifferentialMap::new(r, p, q, image_rank);
467 if r < self.differentials.len() {
468 self.differentials[r].push(d);
469 }
470 }
471 pub fn e_term(&self, r: usize, p: i32, q: i32) -> Option<usize> {
473 self.pages.get(r).map(|pg| pg.get(p, q))
474 }
475 pub fn compute_next_page(&self, r: usize) -> SpectralSequencePage {
479 if r >= self.pages.len() {
480 return SpectralSequencePage::new(r + 1);
481 }
482 let current = &self.pages[r];
483 let mut next = SpectralSequencePage::new(r + 1);
484 let ri = r as i32;
485 for (&(p, q), &rank) in ¤t.entries {
486 let incoming_im = if r < self.differentials.len() {
487 self.differentials[r]
488 .iter()
489 .find(|d| d.target == (p, q))
490 .map_or(0, |d| d.image_rank)
491 } else {
492 0
493 };
494 let outgoing_im = if r < self.differentials.len() {
495 self.differentials[r]
496 .iter()
497 .find(|d| d.source == (p, q))
498 .map_or(0, |d| d.image_rank)
499 } else {
500 0
501 };
502 let ker_rank = rank.saturating_sub(outgoing_im);
503 let new_rank = ker_rank.saturating_sub(incoming_im);
504 if new_rank > 0 {
505 next.set(p + ri, q - ri + 1, new_rank);
506 }
507 }
508 next
509 }
510}
511#[allow(dead_code)]
513#[derive(Debug, Clone)]
514pub struct KunnethFormula {
515 pub betti_x: Vec<i64>,
516 pub betti_y: Vec<i64>,
517}
518#[allow(dead_code)]
519impl KunnethFormula {
520 pub fn new(betti_x: Vec<i64>, betti_y: Vec<i64>) -> Self {
521 Self { betti_x, betti_y }
522 }
523 pub fn product_betti(&self) -> Vec<i64> {
525 let nx = self.betti_x.len();
526 let ny = self.betti_y.len();
527 let n = nx + ny - 1;
528 let mut result = vec![0i64; n];
529 for i in 0..nx {
530 for j in 0..ny {
531 result[i + j] += self.betti_x[i] * self.betti_y[j];
532 }
533 }
534 result
535 }
536 pub fn euler_product(&self) -> i64 {
538 let chi = |betti: &Vec<i64>| -> i64 {
539 betti
540 .iter()
541 .enumerate()
542 .map(|(k, &b)| if k % 2 == 0 { b } else { -b })
543 .sum()
544 };
545 chi(&self.betti_x) * chi(&self.betti_y)
546 }
547}
548#[derive(Debug, Clone)]
553pub struct GroupCohomologyBar {
554 pub group_name: String,
556 pub group_order: usize,
558 pub module_name: String,
560 pub cohomology_ranks: Vec<usize>,
562}
563impl GroupCohomologyBar {
564 pub fn new(group_name: &str, group_order: usize, module_name: &str) -> Self {
566 Self {
567 group_name: group_name.to_string(),
568 group_order,
569 module_name: module_name.to_string(),
570 cohomology_ranks: vec![],
571 }
572 }
573 pub fn cochain_rank(&self, n: usize, module_rank: usize) -> usize {
577 self.group_order.saturating_pow(n as u32) * module_rank
578 }
579 pub fn compute_cohomology(&mut self, module_rank: usize, max_degree: usize) {
581 self.cohomology_ranks = (0..=max_degree)
582 .map(|n| if n == 0 { module_rank.min(1) } else { 0 })
583 .collect();
584 }
585 pub fn cohomology_at(&self, n: usize) -> usize {
587 self.cohomology_ranks.get(n).copied().unwrap_or(0)
588 }
589 pub fn euler_characteristic(&self) -> i64 {
591 self.cohomology_ranks
592 .iter()
593 .enumerate()
594 .map(|(n, &r)| if n % 2 == 0 { r as i64 } else { -(r as i64) })
595 .sum()
596 }
597}
598#[allow(dead_code)]
599#[derive(Debug, Clone)]
600pub struct CyclicHomologyData {
601 pub algebra_type: String,
602 pub hc_0: String,
603 pub negative_cyclic: String,
604 pub periodic_cyclic: String,
605}
606#[allow(dead_code)]
607impl CyclicHomologyData {
608 pub fn for_polynomial_ring(vars: usize) -> Self {
609 CyclicHomologyData {
610 algebra_type: format!("k[x_1,...,x_{}]", vars),
611 hc_0: format!("Ω^0 = k[x_1,...,x_{}]", vars),
612 negative_cyclic: "HC^-_{*} related to de Rham".to_string(),
613 periodic_cyclic: "HP_* = de Rham cohomology (periodic)".to_string(),
614 }
615 }
616 pub fn connes_differential(&self) -> String {
617 "Connes B-operator: B: HC_n → HC_{n+1} (degree +1 boundary-like)".to_string()
618 }
619 pub fn primary_characteristic_class(&self) -> String {
620 "Chern character: K_0(A) → HC_0(A) ≅ A/[A,A] (trace map)".to_string()
621 }
622}
623#[allow(dead_code)]
625#[derive(Debug, Clone)]
626pub struct MayerVietorisSequence {
627 pub space_name: String,
629 pub a_name: String,
630 pub b_name: String,
631 pub c_name: String,
632 pub betti_a: Vec<i64>,
634 pub betti_b: Vec<i64>,
635 pub betti_c: Vec<i64>,
636 pub betti_x: Vec<i64>,
637}
638#[allow(dead_code)]
639impl MayerVietorisSequence {
640 pub fn new(
641 space_name: &str,
642 a_name: &str,
643 b_name: &str,
644 c_name: &str,
645 betti_a: Vec<i64>,
646 betti_b: Vec<i64>,
647 betti_c: Vec<i64>,
648 betti_x: Vec<i64>,
649 ) -> Self {
650 Self {
651 space_name: space_name.to_string(),
652 a_name: a_name.to_string(),
653 b_name: b_name.to_string(),
654 c_name: c_name.to_string(),
655 betti_a,
656 betti_b,
657 betti_c,
658 betti_x,
659 }
660 }
661 pub fn euler_characteristic(&self) -> i64 {
663 self.betti_x
664 .iter()
665 .enumerate()
666 .map(|(k, &b)| if k % 2 == 0 { b } else { -b })
667 .sum()
668 }
669 pub fn verify_euler_relation(&self) -> bool {
671 let chi = |betti: &Vec<i64>| -> i64 {
672 betti
673 .iter()
674 .enumerate()
675 .map(|(k, &b)| if k % 2 == 0 { b } else { -b })
676 .sum()
677 };
678 chi(&self.betti_x) == chi(&self.betti_a) + chi(&self.betti_b) - chi(&self.betti_c)
679 }
680}
681#[allow(dead_code)]
683#[derive(Debug, Clone)]
684pub struct UniversalCoefficients {
685 pub integral_homology: Vec<(usize, Vec<u64>)>,
687 pub coeff_ring: String,
689}
690#[allow(dead_code)]
691impl UniversalCoefficients {
692 pub fn new(integral_homology: Vec<(usize, Vec<u64>)>, coeff_ring: &str) -> Self {
693 Self {
694 integral_homology,
695 coeff_ring: coeff_ring.to_string(),
696 }
697 }
698 pub fn betti_over_field(&self) -> Vec<usize> {
700 self.integral_homology.iter().map(|(r, _)| *r).collect()
701 }
702 pub fn torsion_part(&self, k: usize) -> Option<&Vec<u64>> {
704 self.integral_homology.get(k).map(|(_, t)| t)
705 }
706}
707#[derive(Debug, Clone, Default)]
709pub struct SimplexBoundaryMatrix {
710 pub rows: usize,
712 pub cols: usize,
714 pub entries: Vec<(usize, usize, i64)>,
716}
717impl SimplexBoundaryMatrix {
718 pub fn new(rows: usize, cols: usize) -> Self {
720 Self {
721 rows,
722 cols,
723 entries: vec![],
724 }
725 }
726 pub fn set(&mut self, i: usize, j: usize, v: i64) {
728 self.entries.retain(|(r, c, _)| !(*r == i && *c == j));
729 if v != 0 {
730 self.entries.push((i, j, v));
731 }
732 }
733 pub fn get(&self, i: usize, j: usize) -> i64 {
735 self.entries
736 .iter()
737 .find(|(r, c, _)| *r == i && *c == j)
738 .map_or(0, |(_, _, v)| *v)
739 }
740 pub fn to_dense(&self) -> Vec<Vec<i64>> {
742 let mut m = vec![vec![0i64; self.cols]; self.rows];
743 for &(r, c, v) in &self.entries {
744 if r < self.rows && c < self.cols {
745 m[r][c] = v;
746 }
747 }
748 m
749 }
750 pub fn smith_normal_form_diagonal(&self) -> Vec<i64> {
754 let mut m = self.to_dense();
755 let rows = self.rows;
756 let cols = self.cols;
757 let mut diag = vec![];
758 let mut pivot = 0usize;
759 for col in 0..cols {
760 if pivot >= rows {
761 break;
762 }
763 let found = (pivot..rows).find(|&r| m[r][col] != 0);
764 if found.is_none() {
765 continue;
766 }
767 let pr = found.expect("found is Some: checked by is_none guard above");
768 m.swap(pivot, pr);
769 let pv = m[pivot][col];
770 for r in (pivot + 1)..rows {
771 let factor = m[r][col];
772 if factor != 0 {
773 for c in 0..cols {
774 m[r][c] = m[r][c] * pv - m[pivot][c] * factor;
775 }
776 }
777 }
778 if m[pivot][col] != 0 {
779 diag.push(m[pivot][col].abs());
780 }
781 pivot += 1;
782 }
783 diag
784 }
785 pub fn rank(&self) -> usize {
787 self.smith_normal_form_diagonal().len()
788 }
789 pub fn torsion_coefficients(&self) -> Vec<i64> {
791 self.smith_normal_form_diagonal()
792 .into_iter()
793 .filter(|&d| d > 1)
794 .collect()
795 }
796}
797#[derive(Debug, Clone, PartialEq, Eq)]
799pub struct ExtGroup {
800 pub degree: usize,
802 pub rank: usize,
804 pub torsion: Vec<u64>,
806}
807impl ExtGroup {
808 pub fn new(degree: usize, rank: usize) -> Self {
810 Self {
811 degree,
812 rank,
813 torsion: vec![],
814 }
815 }
816 pub fn is_zero(&self) -> bool {
818 self.rank == 0 && self.torsion.is_empty()
819 }
820}
821#[allow(dead_code)]
823#[derive(Debug, Clone)]
824pub struct LongExactSequence {
825 pub groups: Vec<String>,
826 pub connecting_homomorphism: String,
827}
828#[allow(dead_code)]
829impl LongExactSequence {
830 pub fn from_short(a: &str, b: &str, c: &str) -> Self {
832 let groups = vec![
833 format!("H_n({})", a),
834 format!("H_n({})", b),
835 format!("H_n({})", c),
836 format!("H_{{n-1}}({})", a),
837 ];
838 Self {
839 groups,
840 connecting_homomorphism: format!("delta: H_n({}) -> H_{{n-1}}({})", c, a),
841 }
842 }
843 pub fn length(&self) -> usize {
845 self.groups.len()
846 }
847}
848#[derive(Debug, Clone)]
850pub struct InjectiveResolution {
851 pub module_name: String,
853 pub steps: Vec<ResolutionStep>,
855}
856impl InjectiveResolution {
857 pub fn new(module_name: &str) -> Self {
859 Self {
860 module_name: module_name.to_string(),
861 steps: vec![],
862 }
863 }
864 pub fn add_step(&mut self, rank: usize, boundary: Vec<Vec<i64>>) {
866 let degree = self.steps.len();
867 self.steps.push(ResolutionStep {
868 degree,
869 rank,
870 boundary,
871 });
872 }
873 pub fn injective_dimension(&self) -> Option<usize> {
875 self.steps.iter().rposition(|s| s.rank > 0)
876 }
877}
878#[derive(Debug, Clone, PartialEq, Eq)]
880pub struct HomologyGroup {
881 pub degree: i32,
883 pub rank: usize,
885 pub torsion: Vec<u64>,
887}
888impl HomologyGroup {
889 pub fn new(degree: i32, rank: usize, torsion: Vec<u64>) -> Self {
891 Self {
892 degree,
893 rank,
894 torsion,
895 }
896 }
897 pub fn is_trivial(&self) -> bool {
899 self.rank == 0 && self.torsion.is_empty()
900 }
901}
902#[allow(dead_code)]
904#[derive(Debug, Clone)]
905pub struct CWComplex {
906 pub cells: Vec<usize>,
908 pub attaching_degrees: Vec<Vec<i32>>,
910}
911#[allow(dead_code)]
912impl CWComplex {
913 pub fn new(cells: Vec<usize>) -> Self {
914 let m = if cells.is_empty() { 0 } else { cells.len() - 1 };
915 Self {
916 cells,
917 attaching_degrees: vec![vec![]; m],
918 }
919 }
920 pub fn euler_characteristic(&self) -> i64 {
922 self.cells
923 .iter()
924 .enumerate()
925 .map(|(k, &c)| if k % 2 == 0 { c as i64 } else { -(c as i64) })
926 .sum()
927 }
928 pub fn betti_upper_bound(&self) -> Vec<usize> {
930 self.cells.clone()
931 }
932}
933#[allow(dead_code)]
934#[derive(Debug, Clone)]
935pub struct ExtGroupComputation {
936 pub module_a: String,
937 pub module_b: String,
938 pub projective_resolution_length: usize,
939 pub computed_exts: Vec<String>,
940}
941#[allow(dead_code)]
942impl ExtGroupComputation {
943 pub fn new(a: &str, b: &str) -> Self {
944 ExtGroupComputation {
945 module_a: a.to_string(),
946 module_b: b.to_string(),
947 projective_resolution_length: 0,
948 computed_exts: vec![],
949 }
950 }
951 pub fn compute_ext_0(&self) -> String {
952 format!(
953 "Ext^0({}, {}) = Hom({}, {})",
954 self.module_a, self.module_b, self.module_a, self.module_b
955 )
956 }
957 pub fn compute_ext_1(&self) -> String {
958 format!(
959 "Ext^1({}, {}): obstruction to extension 0→{}→E→{}→0",
960 self.module_a, self.module_b, self.module_b, self.module_a
961 )
962 }
963 pub fn horseshoe_lemma(&self) -> String {
964 "Horseshoe lemma: given short exact sequence of modules, combine projective resolutions"
965 .to_string()
966 }
967 pub fn global_dimension(&self) -> String {
968 format!(
969 "gl.dim ≤ {}: ext vanishes above degree {}",
970 self.projective_resolution_length, self.projective_resolution_length
971 )
972 }
973}
974#[derive(Debug, Clone, Default)]
978pub struct PersistentHomologyComputer {
979 pub filtration: Vec<(f64, usize)>,
981}
982impl PersistentHomologyComputer {
983 pub fn new() -> Self {
985 Self::default()
986 }
987 pub fn add_simplex(&mut self, filtration_value: f64, dimension: usize) {
989 self.filtration.push((filtration_value, dimension));
990 self.filtration.sort_by(|a, b| {
991 a.0.partial_cmp(&b.0)
992 .unwrap_or(std::cmp::Ordering::Equal)
993 .then(a.1.cmp(&b.1))
994 });
995 }
996 pub fn compute_barcode(&self) -> PersistenceBarcode {
1001 let mut barcode = PersistenceBarcode::new();
1002 let max_dim = self.filtration.iter().map(|(_, d)| *d).max().unwrap_or(0);
1003 for dim in 0..=max_dim {
1004 let birth_times: Vec<f64> = self
1005 .filtration
1006 .iter()
1007 .filter(|(_, d)| *d == dim)
1008 .map(|(v, _)| *v)
1009 .collect();
1010 let kill_times: Vec<f64> = self
1011 .filtration
1012 .iter()
1013 .filter(|(_, d)| *d == dim + 1)
1014 .map(|(v, _)| *v)
1015 .collect();
1016 let mut kill_iter = kill_times.iter().peekable();
1017 for birth in &birth_times {
1018 if let Some(&death) = kill_iter.next() {
1019 barcode.add(*birth, death, dim);
1020 } else {
1021 barcode.add(*birth, f64::INFINITY, dim);
1022 }
1023 }
1024 }
1025 barcode
1026 }
1027}
1028#[derive(Debug, Clone)]
1030pub struct ChainMap {
1031 pub source: ChainComplex,
1033 pub target: ChainComplex,
1035 pub components: Vec<Vec<Vec<i64>>>,
1037}
1038impl ChainMap {
1039 pub fn new(source: ChainComplex, target: ChainComplex, components: Vec<Vec<Vec<i64>>>) -> Self {
1041 Self {
1042 source,
1043 target,
1044 components,
1045 }
1046 }
1047 pub fn induced_homology_rank(&self, n: usize) -> usize {
1049 self.components
1050 .get(n)
1051 .map(|m| image_rank(m, self.source.groups.get(n).map_or(0, |g| g.rank)))
1052 .unwrap_or(0)
1053 }
1054}
1055#[allow(dead_code)]
1057#[derive(Debug, Clone)]
1058pub struct SpectralSequenceData {
1059 pub name: String,
1060 pub filtration_type: String,
1061 pub converges_to: String,
1062 pub page: usize,
1063}
1064#[allow(dead_code)]
1065impl SpectralSequenceData {
1066 pub fn serre(base: &str, fiber: &str, total: &str) -> Self {
1068 Self {
1069 name: format!("Serre({} -> {} -> {})", fiber, total, base),
1070 filtration_type: "Serre filtration".to_string(),
1071 converges_to: format!("H*({})", total),
1072 page: 2,
1073 }
1074 }
1075 pub fn leray(map: &str) -> Self {
1077 Self {
1078 name: format!("Leray({})", map),
1079 filtration_type: "sheaf cohomology".to_string(),
1080 converges_to: format!("H*(domain({}))", map),
1081 page: 2,
1082 }
1083 }
1084 pub fn e2_page_description(&self) -> String {
1086 format!(
1087 "E_2 page of {}: converges to {}",
1088 self.name, self.converges_to
1089 )
1090 }
1091}
1092#[allow(dead_code)]
1094#[derive(Debug, Clone)]
1095pub struct FibrationSequence {
1096 pub total_space: String,
1097 pub base_space: String,
1098 pub fiber: String,
1099 pub pi_base: Vec<i64>,
1101 pub pi_fiber: Vec<i64>,
1102 pub pi_total: Vec<i64>,
1103}
1104#[allow(dead_code)]
1105impl FibrationSequence {
1106 pub fn new(
1107 total_space: &str,
1108 base_space: &str,
1109 fiber: &str,
1110 pi_base: Vec<i64>,
1111 pi_fiber: Vec<i64>,
1112 pi_total: Vec<i64>,
1113 ) -> Self {
1114 Self {
1115 total_space: total_space.to_string(),
1116 base_space: base_space.to_string(),
1117 fiber: fiber.to_string(),
1118 pi_base,
1119 pi_fiber,
1120 pi_total,
1121 }
1122 }
1123 pub fn euler_product_fibration(&self, chi_f: i64, chi_b: i64) -> i64 {
1125 chi_f * chi_b
1126 }
1127}
1128#[derive(Debug, Clone)]
1130pub struct LocalCohomology {
1131 pub ideal_name: String,
1133 pub module_name: String,
1135 pub groups: Vec<LocalCohomologyGroup>,
1137}
1138impl LocalCohomology {
1139 pub fn new(ideal_name: &str, module_name: &str) -> Self {
1141 Self {
1142 ideal_name: ideal_name.to_string(),
1143 module_name: module_name.to_string(),
1144 groups: vec![],
1145 }
1146 }
1147 pub fn add_group(&mut self, degree: usize, rank: usize) {
1149 self.groups.push(LocalCohomologyGroup::new(degree, rank));
1150 }
1151 pub fn cohomological_dimension(&self) -> Option<usize> {
1153 self.groups.iter().rposition(|g| !g.is_zero())
1154 }
1155}
1156#[derive(Debug, Clone)]
1158pub struct FlatResolution {
1159 pub module_name: String,
1161 pub flat_dim: Option<usize>,
1163 pub ranks: Vec<usize>,
1165}
1166impl FlatResolution {
1167 pub fn new(module_name: &str, ranks: Vec<usize>) -> Self {
1169 let flat_dim = ranks.iter().rposition(|&r| r > 0);
1170 Self {
1171 module_name: module_name.to_string(),
1172 flat_dim,
1173 ranks,
1174 }
1175 }
1176}
1177#[allow(dead_code)]
1179#[derive(Debug, Clone)]
1180pub struct PersistentBettiNumbers {
1181 pub pairs: Vec<Vec<(f64, f64)>>,
1183}
1184#[allow(dead_code)]
1185impl PersistentBettiNumbers {
1186 pub fn new(pairs: Vec<Vec<(f64, f64)>>) -> Self {
1187 Self { pairs }
1188 }
1189 pub fn betti_at(&self, k: usize, t: f64) -> usize {
1191 if k >= self.pairs.len() {
1192 return 0;
1193 }
1194 self.pairs[k]
1195 .iter()
1196 .filter(|&&(b, d)| b <= t && t < d)
1197 .count()
1198 }
1199 pub fn total_persistence(&self, k: usize) -> f64 {
1201 if k >= self.pairs.len() {
1202 return 0.0;
1203 }
1204 self.pairs[k].iter().map(|&(b, d)| d - b).sum()
1205 }
1206 pub fn bottleneck_approx(&self, other: &Self, k: usize) -> f64 {
1208 if k >= self.pairs.len() || k >= other.pairs.len() {
1209 return 0.0;
1210 }
1211 let a = &self.pairs[k];
1212 let b = &other.pairs[k];
1213 if a.is_empty() && b.is_empty() {
1214 return 0.0;
1215 }
1216 let inf_dist =
1217 |p: (f64, f64), q: (f64, f64)| -> f64 { (p.0 - q.0).abs().max((p.1 - q.1).abs()) };
1218 let mut max_min = 0.0f64;
1219 for &pa in a {
1220 let min_d = b
1221 .iter()
1222 .map(|&pb| inf_dist(pa, pb))
1223 .fold(f64::INFINITY, f64::min);
1224 max_min = max_min.max(min_d);
1225 }
1226 max_min
1227 }
1228}
1229#[allow(dead_code)]
1231#[derive(Debug, Clone)]
1232pub struct DeRhamCohomology {
1233 pub manifold_name: String,
1234 pub dimension: usize,
1235 pub betti_numbers: Vec<i64>,
1237}
1238#[allow(dead_code)]
1239impl DeRhamCohomology {
1240 pub fn new(manifold_name: &str, dimension: usize, betti_numbers: Vec<i64>) -> Self {
1241 Self {
1242 manifold_name: manifold_name.to_string(),
1243 dimension,
1244 betti_numbers,
1245 }
1246 }
1247 pub fn check_poincare_duality(&self) -> bool {
1249 let n = self.dimension;
1250 if self.betti_numbers.len() != n + 1 {
1251 return false;
1252 }
1253 (0..=n).all(|k| self.betti_numbers[k] == self.betti_numbers[n - k])
1254 }
1255 pub fn euler_characteristic(&self) -> i64 {
1257 self.betti_numbers
1258 .iter()
1259 .enumerate()
1260 .map(|(k, &b)| if k % 2 == 0 { b } else { -b })
1261 .sum()
1262 }
1263}
1264#[derive(Debug, Clone, Default)]
1268pub struct ChainComplex {
1269 pub groups: Vec<GradedGroup>,
1271 pub boundaries: Vec<Vec<Vec<i64>>>,
1273}
1274impl ChainComplex {
1275 pub fn new() -> Self {
1277 Self::default()
1278 }
1279 pub fn add_group(&mut self, rank: usize, name: &str) {
1281 self.groups.push(GradedGroup::new(rank, name));
1282 }
1283 pub fn add_boundary(&mut self, matrix: Vec<Vec<i64>>) {
1285 self.boundaries.push(matrix);
1286 }
1287 pub fn betti_numbers(&self) -> Vec<i64> {
1289 let n = self.groups.len();
1290 (0..n)
1291 .map(|i| {
1292 let ker = if i > 0 && i - 1 < self.boundaries.len() {
1293 kernel_rank(&self.boundaries[i - 1], self.groups[i].rank) as i64
1294 } else {
1295 self.groups[i].rank as i64
1296 };
1297 let img = if i < self.boundaries.len() && i + 1 < self.groups.len() {
1298 image_rank(&self.boundaries[i], self.groups[i + 1].rank) as i64
1299 } else {
1300 0
1301 };
1302 ker - img
1303 })
1304 .collect()
1305 }
1306 pub fn compute_homology(&self) -> Vec<HomologyGroup> {
1308 self.betti_numbers()
1309 .into_iter()
1310 .enumerate()
1311 .map(|(n, rank)| HomologyGroup {
1312 degree: n as i32,
1313 rank: rank.max(0) as usize,
1314 torsion: vec![],
1315 })
1316 .collect()
1317 }
1318 pub fn euler_characteristic(&self) -> i64 {
1320 self.betti_numbers()
1321 .iter()
1322 .enumerate()
1323 .map(|(n, &b)| if n % 2 == 0 { b } else { -b })
1324 .sum()
1325 }
1326 pub fn is_exact_at(&self, k: usize) -> bool {
1328 let betti = self.betti_numbers();
1329 betti.get(k).copied().unwrap_or(0) == 0
1330 }
1331 pub fn is_exact(&self) -> bool {
1333 self.betti_numbers().iter().all(|&b| b == 0)
1334 }
1335}
1336#[derive(Debug, Clone)]
1338pub struct DifferentialMap {
1339 pub page: usize,
1341 pub source: (i32, i32),
1343 pub target: (i32, i32),
1345 pub image_rank: usize,
1347}
1348impl DifferentialMap {
1349 pub fn new(r: usize, p: i32, q: i32, image_rank: usize) -> Self {
1351 let ri = r as i32;
1352 Self {
1353 page: r,
1354 source: (p, q),
1355 target: (p + ri, q - ri + 1),
1356 image_rank,
1357 }
1358 }
1359}
1360#[derive(Debug, Clone)]
1364pub struct BarResolution {
1365 pub group_name: String,
1367 pub num_steps: usize,
1369 pub ranks: Vec<usize>,
1371}
1372impl BarResolution {
1373 pub fn new(group_name: &str, group_order: usize, num_steps: usize) -> Self {
1377 let ranks = (0..num_steps)
1378 .map(|n| group_order.saturating_pow(n as u32))
1379 .collect();
1380 Self {
1381 group_name: group_name.to_string(),
1382 num_steps,
1383 ranks,
1384 }
1385 }
1386 pub fn rank_at(&self, n: usize) -> usize {
1388 self.ranks.get(n).copied().unwrap_or(0)
1389 }
1390}
1391#[derive(Debug, Clone)]
1393pub struct ResolutionStep {
1394 pub degree: usize,
1396 pub rank: usize,
1398 pub boundary: Vec<Vec<i64>>,
1400}
1401#[allow(dead_code)]
1402#[derive(Debug, Clone)]
1403pub struct PerverseSheafData {
1404 pub stratification: Vec<String>,
1405 pub perversity: Vec<i64>,
1406 pub is_ic_sheaf: bool,
1407 pub support_dimension: Vec<usize>,
1408}
1409#[allow(dead_code)]
1410impl PerverseSheafData {
1411 pub fn new(strat: Vec<String>, perversity: Vec<i64>) -> Self {
1412 let n = strat.len();
1413 PerverseSheafData {
1414 stratification: strat,
1415 perversity,
1416 is_ic_sheaf: false,
1417 support_dimension: (0..n).collect(),
1418 }
1419 }
1420 pub fn intersection_cohomology_description(&self) -> String {
1421 "IC sheaf: intermediate extension j_!* F of local system F".to_string()
1422 }
1423 pub fn bbdg_decomposition(&self) -> String {
1424 "BBDG: semisimple complexes over finite fields decompose into shifts of IC sheaves"
1425 .to_string()
1426 }
1427 pub fn verdier_duality(&self) -> String {
1428 "Verdier duality: D(IC_X(L)) ≅ IC_X(L^∨) for selfdual local system".to_string()
1429 }
1430 pub fn support_condition(&self) -> String {
1431 format!(
1432 "Perversity condition: dim supp H^i ≤ -i on {} strata",
1433 self.stratification.len()
1434 )
1435 }
1436}
1437#[derive(Debug, Clone, Default)]
1441pub struct SpectralSequencePage {
1442 pub entries: HashMap<(i32, i32), usize>,
1444 pub page: usize,
1446}
1447impl SpectralSequencePage {
1448 pub fn new(page: usize) -> Self {
1450 Self {
1451 entries: HashMap::new(),
1452 page,
1453 }
1454 }
1455 pub fn set(&mut self, p: i32, q: i32, rank: usize) {
1457 self.entries.insert((p, q), rank);
1458 }
1459 pub fn get(&self, p: i32, q: i32) -> usize {
1461 self.entries.get(&(p, q)).copied().unwrap_or(0)
1462 }
1463 pub fn total_rank(&self) -> usize {
1465 self.entries.values().sum()
1466 }
1467}
1468#[allow(dead_code)]
1470#[derive(Debug, Clone)]
1471pub struct ChainCplxExt {
1472 pub chain_groups: Vec<usize>,
1474 pub boundary_ranks: Vec<usize>,
1476}
1477#[allow(dead_code)]
1478impl ChainCplxExt {
1479 pub fn new(chain_groups: Vec<usize>, boundary_ranks: Vec<usize>) -> Self {
1480 assert!(
1481 chain_groups.len() == boundary_ranks.len() + 1
1482 || chain_groups.len() == boundary_ranks.len(),
1483 "chain_groups.len() should be boundary_ranks.len() or boundary_ranks.len()+1"
1484 );
1485 Self {
1486 chain_groups,
1487 boundary_ranks,
1488 }
1489 }
1490 pub fn betti_numbers(&self) -> Vec<i64> {
1492 let n = self.chain_groups.len();
1493 (0..n)
1494 .map(|k| {
1495 let ck = self.chain_groups[k] as i64;
1496 let im_kp1 = if k + 1 < self.boundary_ranks.len() {
1497 self.boundary_ranks[k + 1] as i64
1498 } else {
1499 0
1500 };
1501 let rk_dk = if k < self.boundary_ranks.len() {
1502 self.boundary_ranks[k] as i64
1503 } else {
1504 0
1505 };
1506 ck - im_kp1 - rk_dk
1507 })
1508 .collect()
1509 }
1510 pub fn euler_characteristic(&self) -> i64 {
1512 let betti = self.betti_numbers();
1513 betti
1514 .iter()
1515 .enumerate()
1516 .map(|(k, &b)| if k % 2 == 0 { b } else { -b })
1517 .sum()
1518 }
1519}
1520#[derive(Debug, Clone)]
1522pub struct ProjectiveResolution {
1523 pub module_name: String,
1525 pub steps: Vec<ResolutionStep>,
1527}
1528impl ProjectiveResolution {
1529 pub fn new(module_name: &str) -> Self {
1531 Self {
1532 module_name: module_name.to_string(),
1533 steps: vec![],
1534 }
1535 }
1536 pub fn add_step(&mut self, rank: usize, boundary: Vec<Vec<i64>>) {
1538 let degree = self.steps.len();
1539 self.steps.push(ResolutionStep {
1540 degree,
1541 rank,
1542 boundary,
1543 });
1544 }
1545 pub fn projective_dimension(&self) -> Option<usize> {
1547 self.steps.iter().rposition(|s| s.rank > 0)
1548 }
1549 pub fn betti_numbers(&self) -> Vec<usize> {
1551 self.steps.iter().map(|s| s.rank).collect()
1552 }
1553}
1554#[allow(dead_code)]
1556#[derive(Debug, Clone)]
1557pub struct KunnethData {
1558 pub space_x: String,
1559 pub space_y: String,
1560 pub field_coefficients: bool,
1561}
1562#[allow(dead_code)]
1563impl KunnethData {
1564 pub fn over_field(x: &str, y: &str) -> Self {
1566 Self {
1567 space_x: x.to_string(),
1568 space_y: y.to_string(),
1569 field_coefficients: true,
1570 }
1571 }
1572 pub fn kunneth_description(&self) -> String {
1574 if self.field_coefficients {
1575 format!(
1576 "H*({} x {}; k) ≅ H*({};k) ⊗ H*({};k)",
1577 self.space_x, self.space_y, self.space_x, self.space_y
1578 )
1579 } else {
1580 format!(
1581 "Künneth: H_n({} x {}) has Tor correction term",
1582 self.space_x, self.space_y
1583 )
1584 }
1585 }
1586}
1587#[derive(Debug, Clone)]
1589pub struct TorFunctor {
1590 pub module_m: String,
1592 pub module_n: String,
1594 pub values: Vec<TorGroup>,
1596}
1597impl TorFunctor {
1598 pub fn compute(module_m: &str, module_n: &str, resolution: &ProjectiveResolution) -> Self {
1600 let betti = resolution.betti_numbers();
1601 let values = betti
1602 .iter()
1603 .enumerate()
1604 .map(|(n, &r)| TorGroup::new(n, r))
1605 .collect();
1606 Self {
1607 module_m: module_m.to_string(),
1608 module_n: module_n.to_string(),
1609 values,
1610 }
1611 }
1612 pub fn tor_at(&self, n: usize) -> Option<&TorGroup> {
1614 self.values.get(n)
1615 }
1616 pub fn projective_dimension(&self) -> Option<usize> {
1618 self.values.iter().rposition(|t| !t.is_zero())
1619 }
1620}
1621#[derive(Debug, Clone, PartialEq)]
1623pub struct BirthDeathPair {
1624 pub birth: f64,
1626 pub death: f64,
1628 pub degree: usize,
1630}
1631impl BirthDeathPair {
1632 pub fn new(birth: f64, death: f64, degree: usize) -> Self {
1634 Self {
1635 birth,
1636 death,
1637 degree,
1638 }
1639 }
1640 pub fn persistence(&self) -> f64 {
1642 self.death - self.birth
1643 }
1644 pub fn is_essential(&self) -> bool {
1646 self.death.is_infinite()
1647 }
1648}
1649#[allow(dead_code)]
1650#[derive(Debug, Clone)]
1651pub struct CechCocycle {
1652 pub open_cover_size: usize,
1653 pub degree: usize,
1654 pub cochain: Vec<Vec<f64>>,
1655 pub is_cocycle: bool,
1656}
1657#[allow(dead_code)]
1658impl CechCocycle {
1659 pub fn new(cover_size: usize, degree: usize) -> Self {
1660 let cochain = vec![vec![0.0; cover_size]; cover_size.pow(degree as u32)];
1661 CechCocycle {
1662 open_cover_size: cover_size,
1663 degree,
1664 cochain,
1665 is_cocycle: true,
1666 }
1667 }
1668 pub fn leray_theorem(&self) -> String {
1669 "Leray: for acyclic covers, Čech cohomology = sheaf cohomology".to_string()
1670 }
1671 pub fn refinement_map_description(&self) -> String {
1672 format!(
1673 "Refinement: Čech H^{}(U;F) → Čech H^{}(V;F) for V refinement of U",
1674 self.degree, self.degree
1675 )
1676 }
1677 pub fn mayer_vietoris_for_two_opens(&self) -> String {
1678 "Mayer-Vietoris: 0 → F(U∪V) → F(U)⊕F(V) → F(U∩V) → H^1 → ...".to_string()
1679 }
1680}