persistence_agent/
barcode.rs1use serde::{Deserialize, Deserializer, Serialize, Serializer};
2use crate::boundary::BoundaryMatrix;
3use crate::error::PersistenceError;
4use crate::vietoris_rips::VietorisRipsComplex;
5
6fn serialize_death<S: Serializer>(death: &f64, s: S) -> Result<S::Ok, S::Error> {
7 if death.is_infinite() {
8 s.serialize_str("inf")
9 } else {
10 s.serialize_f64(*death)
11 }
12}
13
14fn deserialize_death<'de, D: Deserializer<'de>>(d: D) -> Result<f64, D::Error> {
15 use serde::de;
16 struct DeathVisitor;
17 impl<'de> de::Visitor<'de> for DeathVisitor {
18 type Value = f64;
19 fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str("f64 or \"inf\"") }
20 fn visit_f64<E: de::Error>(self, v: f64) -> Result<f64, E> { Ok(v) }
21 fn visit_str<E: de::Error>(self, v: &str) -> Result<f64, E> {
22 if v == "inf" { Ok(f64::INFINITY) } else { Err(E::custom("expected \"inf\"")) }
23 }
24 }
25 d.deserialize_any(DeathVisitor)
26}
27
28#[derive(Debug, Clone, Serialize, Deserialize)]
30pub struct PersistencePair {
31 pub birth: f64,
32 #[serde(serialize_with = "serialize_death", deserialize_with = "deserialize_death")]
33 pub death: f64,
34 pub dimension: usize,
35}
36
37impl PersistencePair {
38 pub fn persistence(&self) -> f64 {
39 self.death - self.birth
40 }
41}
42
43#[derive(Debug, Clone, Serialize, Deserialize)]
45pub struct Barcode {
46 pub pairs: Vec<PersistencePair>,
47 pub betti_curve: Vec<(f64, Vec<usize>)>,
50}
51
52impl Barcode {
53 pub fn compute(vr: &VietorisRipsComplex) -> Result<Self, PersistenceError> {
55 let mut bm = BoundaryMatrix::build(vr)?;
56 let low_map = bm.reduce();
57
58 let n = vr.n_simplices();
59 let mut pairs = Vec::new();
60
61 for (&row, &col) in &low_map {
62 let birth_dim = vr.simplex_dimension(row);
63 let birth_eps = vr.filtration_values[row];
65 let death_eps = vr.filtration_values[col];
66 pairs.push(PersistencePair {
67 birth: birth_eps,
68 death: death_eps,
69 dimension: birth_dim,
70 });
71 }
72
73 let mut is_low_of_some_col = vec![false; n];
77 for &row in low_map.keys() {
78 is_low_of_some_col[row] = true;
79 }
80
81 for (i, is_low) in is_low_of_some_col.iter().enumerate() {
82 if vr.simplex_dimension(i) == 0 && !is_low {
83 pairs.push(PersistencePair {
85 birth: vr.filtration_values[i],
86 death: f64::INFINITY,
87 dimension: 0,
88 });
89 }
90 }
91
92 pairs.sort_by(|a, b| {
94 a.dimension
95 .cmp(&b.dimension)
96 .then_with(|| a.birth.partial_cmp(&b.birth).unwrap_or(std::cmp::Ordering::Equal))
97 });
98
99 let max_dim = vr.max_dimension;
101 let mut thresholds = vr.filtration_values.to_vec();
102 thresholds.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
103 thresholds.dedup_by(|a, b| (*a - *b).abs() < 1e-12);
104
105 let betti_curve = compute_betti_curve(&pairs, &thresholds, max_dim);
106
107 Ok(Self { pairs, betti_curve })
108 }
109
110 pub fn pairs_of_dimension(&self, dim: usize) -> Vec<&PersistencePair> {
112 self.pairs.iter().filter(|p| p.dimension == dim).collect()
113 }
114
115 pub fn max_persistence(&self) -> f64 {
117 self.pairs
118 .iter()
119 .filter(|p| p.death.is_finite())
120 .map(|p| p.persistence())
121 .fold(0.0_f64, f64::max)
122 }
123
124 pub fn persistence_entropy(&self) -> f64 {
126 let persistences: Vec<f64> = self
127 .pairs
128 .iter()
129 .filter(|p| p.death.is_finite() && p.persistence() > 1e-12)
130 .map(|p| p.persistence())
131 .collect();
132 if persistences.is_empty() {
133 return 0.0;
134 }
135 let total: f64 = persistences.iter().sum();
136 if total < 1e-12 {
137 return 0.0;
138 }
139 persistences
140 .iter()
141 .map(|&p| {
142 let q = p / total;
143 if q > 1e-12 {
144 -q * q.ln()
145 } else {
146 0.0
147 }
148 })
149 .sum()
150 }
151
152 pub fn betti_numbers_at(&self, eps: f64) -> Vec<usize> {
154 if self.betti_curve.is_empty() {
155 return vec![];
156 }
157 let mut result = vec![0usize; self.betti_curve[0].1.len()];
159 for &(e, ref bettis) in &self.betti_curve {
160 if e > eps {
161 break;
162 }
163 result = bettis.clone();
164 }
165 result
166 }
167}
168
169fn compute_betti_curve(
170 pairs: &[PersistencePair],
171 thresholds: &[f64],
172 max_dim: usize,
173) -> Vec<(f64, Vec<usize>)> {
174 let n_dims = max_dim + 1;
175 let mut curve = Vec::with_capacity(thresholds.len());
176
177 for &eps in thresholds {
178 let mut bettis = vec![0usize; n_dims];
179 for pair in pairs {
180 if pair.dimension < n_dims {
181 let alive = pair.birth <= eps + 1e-12
182 && (pair.death > eps - 1e-12 || !pair.death.is_finite());
183 if alive {
184 bettis[pair.dimension] += 1;
185 }
186 }
187 }
188 curve.push((eps, bettis));
189 }
190
191 curve
192}