use serde::{Deserialize, Deserializer, Serialize, Serializer};
use crate::boundary::BoundaryMatrix;
use crate::error::PersistenceError;
use crate::vietoris_rips::VietorisRipsComplex;
fn serialize_death<S: Serializer>(death: &f64, s: S) -> Result<S::Ok, S::Error> {
if death.is_infinite() {
s.serialize_str("inf")
} else {
s.serialize_f64(*death)
}
}
fn deserialize_death<'de, D: Deserializer<'de>>(d: D) -> Result<f64, D::Error> {
use serde::de;
struct DeathVisitor;
impl<'de> de::Visitor<'de> for DeathVisitor {
type Value = f64;
fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { f.write_str("f64 or \"inf\"") }
fn visit_f64<E: de::Error>(self, v: f64) -> Result<f64, E> { Ok(v) }
fn visit_str<E: de::Error>(self, v: &str) -> Result<f64, E> {
if v == "inf" { Ok(f64::INFINITY) } else { Err(E::custom("expected \"inf\"")) }
}
}
d.deserialize_any(DeathVisitor)
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PersistencePair {
pub birth: f64,
#[serde(serialize_with = "serialize_death", deserialize_with = "deserialize_death")]
pub death: f64,
pub dimension: usize,
}
impl PersistencePair {
pub fn persistence(&self) -> f64 {
self.death - self.birth
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Barcode {
pub pairs: Vec<PersistencePair>,
pub betti_curve: Vec<(f64, Vec<usize>)>,
}
impl Barcode {
pub fn compute(vr: &VietorisRipsComplex) -> Result<Self, PersistenceError> {
let mut bm = BoundaryMatrix::build(vr)?;
let low_map = bm.reduce();
let n = vr.n_simplices();
let mut pairs = Vec::new();
for (&row, &col) in &low_map {
let birth_dim = vr.simplex_dimension(row);
let birth_eps = vr.filtration_values[row];
let death_eps = vr.filtration_values[col];
pairs.push(PersistencePair {
birth: birth_eps,
death: death_eps,
dimension: birth_dim,
});
}
let mut is_low_of_some_col = vec![false; n];
for &row in low_map.keys() {
is_low_of_some_col[row] = true;
}
for (i, is_low) in is_low_of_some_col.iter().enumerate() {
if vr.simplex_dimension(i) == 0 && !is_low {
pairs.push(PersistencePair {
birth: vr.filtration_values[i],
death: f64::INFINITY,
dimension: 0,
});
}
}
pairs.sort_by(|a, b| {
a.dimension
.cmp(&b.dimension)
.then_with(|| a.birth.partial_cmp(&b.birth).unwrap_or(std::cmp::Ordering::Equal))
});
let max_dim = vr.max_dimension;
let mut thresholds = vr.filtration_values.to_vec();
thresholds.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
thresholds.dedup_by(|a, b| (*a - *b).abs() < 1e-12);
let betti_curve = compute_betti_curve(&pairs, &thresholds, max_dim);
Ok(Self { pairs, betti_curve })
}
pub fn pairs_of_dimension(&self, dim: usize) -> Vec<&PersistencePair> {
self.pairs.iter().filter(|p| p.dimension == dim).collect()
}
pub fn max_persistence(&self) -> f64 {
self.pairs
.iter()
.filter(|p| p.death.is_finite())
.map(|p| p.persistence())
.fold(0.0_f64, f64::max)
}
pub fn persistence_entropy(&self) -> f64 {
let persistences: Vec<f64> = self
.pairs
.iter()
.filter(|p| p.death.is_finite() && p.persistence() > 1e-12)
.map(|p| p.persistence())
.collect();
if persistences.is_empty() {
return 0.0;
}
let total: f64 = persistences.iter().sum();
if total < 1e-12 {
return 0.0;
}
persistences
.iter()
.map(|&p| {
let q = p / total;
if q > 1e-12 {
-q * q.ln()
} else {
0.0
}
})
.sum()
}
pub fn betti_numbers_at(&self, eps: f64) -> Vec<usize> {
if self.betti_curve.is_empty() {
return vec![];
}
let mut result = vec![0usize; self.betti_curve[0].1.len()];
for &(e, ref bettis) in &self.betti_curve {
if e > eps {
break;
}
result = bettis.clone();
}
result
}
}
fn compute_betti_curve(
pairs: &[PersistencePair],
thresholds: &[f64],
max_dim: usize,
) -> Vec<(f64, Vec<usize>)> {
let n_dims = max_dim + 1;
let mut curve = Vec::with_capacity(thresholds.len());
for &eps in thresholds {
let mut bettis = vec![0usize; n_dims];
for pair in pairs {
if pair.dimension < n_dims {
let alive = pair.birth <= eps + 1e-12
&& (pair.death > eps - 1e-12 || !pair.death.is_finite());
if alive {
bettis[pair.dimension] += 1;
}
}
}
curve.push((eps, bettis));
}
curve
}