use crate::error::PersistenceError;
use crate::point_cloud::PointCloud;
#[derive(Debug, Clone)]
pub struct VietorisRipsComplex {
pub simplices: Vec<Vec<usize>>,
pub filtration_values: Vec<f64>,
pub max_dimension: usize,
}
impl VietorisRipsComplex {
pub fn build(
cloud: &PointCloud,
max_dimension: usize,
max_eps: f64,
) -> Result<Self, PersistenceError> {
let n = cloud.n_points();
if n == 0 {
return Err(PersistenceError::EmptyCloud);
}
let mut simplices: Vec<Vec<usize>> = Vec::new();
let mut filtration_values: Vec<f64> = Vec::new();
for i in 0..n {
simplices.push(vec![i]);
filtration_values.push(0.0);
}
let mut current_simplices: Vec<Vec<usize>> = (0..n).map(|i| vec![i]).collect();
for _dim in 0..max_dimension {
let mut candidates: Vec<(f64, Vec<usize>)> = Vec::new();
for simplex in ¤t_simplices {
let max_v = *simplex.iter().max().unwrap();
for v in (max_v + 1)..n {
let mut new_simplex = simplex.clone();
new_simplex.push(v);
new_simplex.sort();
let filt = max_pairwise(cloud, &new_simplex);
if filt <= max_eps {
candidates.push((filt, new_simplex));
}
}
}
candidates.sort_by(|a, b| {
a.0.partial_cmp(&b.0)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.1.cmp(&b.1))
});
current_simplices = candidates.iter().map(|(_, s)| s.clone()).collect();
for (filt, s) in &candidates {
simplices.push(s.clone());
filtration_values.push(*filt);
}
if current_simplices.is_empty() {
break;
}
}
Ok(Self {
simplices,
filtration_values,
max_dimension,
})
}
pub fn n_simplices(&self) -> usize {
self.simplices.len()
}
pub fn simplex_dimension(&self, idx: usize) -> usize {
self.simplices[idx].len().saturating_sub(1)
}
pub fn simplices_of_dimension(&self, dim: usize) -> Vec<usize> {
self.simplices
.iter()
.enumerate()
.filter(|(_, s)| s.len().saturating_sub(1) == dim)
.map(|(i, _)| i)
.collect()
}
pub fn sorted_filtration(&self) -> Vec<(usize, usize)> {
let mut indexed: Vec<(usize, usize, f64)> = self
.simplices
.iter()
.enumerate()
.map(|(i, s)| (s.len().saturating_sub(1), i, self.filtration_values[i]))
.collect();
indexed.sort_by(|a, b| {
a.2.partial_cmp(&b.2)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.0.cmp(&b.0))
});
indexed.into_iter().map(|(d, i, _)| (d, i)).collect()
}
}
fn max_pairwise(cloud: &PointCloud, simplex: &[usize]) -> f64 {
let mut mx = 0.0_f64;
for i in 0..simplex.len() {
for j in (i + 1)..simplex.len() {
mx = mx.max(cloud.distance(simplex[i], simplex[j]));
}
}
mx
}