use super::{PointCloud, Simplex, SimplicialComplex};
#[derive(Debug, Clone)]
pub struct FilteredSimplex {
pub simplex: Simplex,
pub birth: f64,
}
impl FilteredSimplex {
pub fn new(simplex: Simplex, birth: f64) -> Self {
Self { simplex, birth }
}
}
#[derive(Debug, Clone)]
pub struct Filtration {
pub simplices: Vec<FilteredSimplex>,
pub max_dim: usize,
}
impl Filtration {
pub fn new() -> Self {
Self {
simplices: Vec::new(),
max_dim: 0,
}
}
pub fn add(&mut self, simplex: Simplex, birth: f64) {
self.max_dim = self.max_dim.max(simplex.dim());
self.simplices.push(FilteredSimplex::new(simplex, birth));
}
pub fn sort(&mut self) {
self.simplices.sort_by(|a, b| {
a.birth
.partial_cmp(&b.birth)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.simplex.dim().cmp(&b.simplex.dim()))
});
}
pub fn complex_at(&self, t: f64) -> SimplicialComplex {
let simplices: Vec<Simplex> = self
.simplices
.iter()
.filter(|fs| fs.birth <= t)
.map(|fs| fs.simplex.clone())
.collect();
SimplicialComplex::from_simplices(simplices)
}
pub fn len(&self) -> usize {
self.simplices.len()
}
pub fn is_empty(&self) -> bool {
self.simplices.is_empty()
}
pub fn filtration_values(&self) -> Vec<f64> {
let mut values: Vec<f64> = self.simplices.iter().map(|fs| fs.birth).collect();
values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
values.dedup();
values
}
}
impl Default for Filtration {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Clone)]
pub struct VietorisRips {
pub max_dim: usize,
pub max_scale: f64,
}
impl VietorisRips {
pub fn new(max_dim: usize, max_scale: f64) -> Self {
Self { max_dim, max_scale }
}
pub fn build(&self, cloud: &PointCloud) -> Filtration {
let n = cloud.len();
let dist = cloud.distance_matrix();
let mut filtration = Filtration::new();
for i in 0..n {
filtration.add(Simplex::vertex(i), 0.0);
}
for i in 0..n {
for j in i + 1..n {
let d = dist[i * n + j];
if d <= self.max_scale {
filtration.add(Simplex::edge(i, j), d);
}
}
}
if self.max_dim >= 2 {
for i in 0..n {
for j in i + 1..n {
for k in j + 1..n {
let d_ij = dist[i * n + j];
let d_ik = dist[i * n + k];
let d_jk = dist[j * n + k];
let diameter = d_ij.max(d_ik).max(d_jk);
if diameter <= self.max_scale {
filtration.add(Simplex::triangle(i, j, k), diameter);
}
}
}
}
}
if self.max_dim >= 3 {
for i in 0..n {
for j in i + 1..n {
for k in j + 1..n {
for l in k + 1..n {
let d_ij = dist[i * n + j];
let d_ik = dist[i * n + k];
let d_il = dist[i * n + l];
let d_jk = dist[j * n + k];
let d_jl = dist[j * n + l];
let d_kl = dist[k * n + l];
let diameter = d_ij.max(d_ik).max(d_il).max(d_jk).max(d_jl).max(d_kl);
if diameter <= self.max_scale {
filtration.add(Simplex::new(vec![i, j, k, l]), diameter);
}
}
}
}
}
}
filtration.sort();
filtration
}
}
#[derive(Debug, Clone)]
pub struct AlphaComplex {
pub max_alpha: f64,
}
impl AlphaComplex {
pub fn new(max_alpha: f64) -> Self {
Self { max_alpha }
}
pub fn build(&self, cloud: &PointCloud) -> Filtration {
let n = cloud.len();
let dist = cloud.distance_matrix();
let mut filtration = Filtration::new();
for i in 0..n {
filtration.add(Simplex::vertex(i), 0.0);
}
for i in 0..n {
for j in i + 1..n {
let alpha = dist[i * n + j] / 2.0;
if alpha <= self.max_alpha {
filtration.add(Simplex::edge(i, j), alpha);
}
}
}
for i in 0..n {
for j in i + 1..n {
for k in j + 1..n {
let d_ij = dist[i * n + j];
let d_ik = dist[i * n + k];
let d_jk = dist[j * n + k];
let s = (d_ij + d_ik + d_jk) / 2.0;
let area_sq = s * (s - d_ij) * (s - d_ik) * (s - d_jk);
let alpha = if area_sq > 0.0 {
(d_ij * d_ik * d_jk) / (4.0 * area_sq.sqrt())
} else {
d_ij.max(d_ik).max(d_jk) / 2.0
};
if alpha <= self.max_alpha {
filtration.add(Simplex::triangle(i, j, k), alpha);
}
}
}
}
filtration.sort();
filtration
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_filtration_creation() {
let mut filtration = Filtration::new();
filtration.add(Simplex::vertex(0), 0.0);
filtration.add(Simplex::vertex(1), 0.0);
filtration.add(Simplex::edge(0, 1), 1.0);
assert_eq!(filtration.len(), 3);
}
#[test]
fn test_filtration_sort() {
let mut filtration = Filtration::new();
filtration.add(Simplex::edge(0, 1), 1.0);
filtration.add(Simplex::vertex(0), 0.0);
filtration.add(Simplex::vertex(1), 0.0);
filtration.sort();
assert!(filtration.simplices[0].simplex.is_vertex());
assert!(filtration.simplices[1].simplex.is_vertex());
assert!(filtration.simplices[2].simplex.is_edge());
}
#[test]
fn test_vietoris_rips() {
let cloud = PointCloud::from_flat(&[0.0, 0.0, 1.0, 0.0, 0.5, 0.866], 2);
let rips = VietorisRips::new(2, 2.0);
let filtration = rips.build(&cloud);
let values = filtration.filtration_values();
assert!(!values.is_empty());
}
#[test]
fn test_complex_at() {
let cloud = PointCloud::from_flat(&[0.0, 0.0, 1.0, 0.0, 2.0, 0.0], 2);
let rips = VietorisRips::new(1, 2.0);
let filtration = rips.build(&cloud);
let complex_0 = filtration.complex_at(0.5);
assert_eq!(complex_0.count_dim(0), 3);
assert_eq!(complex_0.count_dim(1), 0);
let complex_1 = filtration.complex_at(1.5);
assert_eq!(complex_1.count_dim(0), 3);
assert!(complex_1.count_dim(1) >= 2); }
#[test]
fn test_alpha_complex() {
let cloud = PointCloud::from_flat(&[0.0, 0.0, 1.0, 0.0, 0.0, 1.0], 2);
let alpha = AlphaComplex::new(2.0);
let filtration = alpha.build(&cloud);
assert!(filtration.len() >= 3); }
}