use super::simplex::{Simplex, Dimension, CNS, SimplexConverter};
use std::collections::{HashMap, HashSet};
use std::cmp::Ordering;
use super::binomial::BinomialCoeff;
use itertools::Itertools;
#[derive(Debug, PartialEq, Clone)]
pub struct RichSimplex {
pub lifetime: f64,
pub dimension: Dimension,
pub simplex: CNS
}
impl PartialOrd for RichSimplex {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
if self.lifetime == other.lifetime {
if self.dimension == other.dimension {
return other.simplex.partial_cmp(&self.simplex)
}
return self.dimension.partial_cmp(&other.dimension)
}
self.lifetime.partial_cmp(&other.lifetime)
}
}
impl RichSimplex {
pub fn from_vertices(vertices: &[usize], lifetime: f64, converter: &SimplexConverter) -> RichSimplex {
let simplex = converter.simplex_to_cns(&Simplex::construct_simplex(vertices, lifetime));
let dimension = Dimension::from(vertices.len() - 1);
RichSimplex{simplex, dimension, lifetime}
}
pub fn cofacets<'a>(&self, converter: &'a SimplexConverter) -> CofacetIterator<'a> {
let vertices = converter.cns_to_vector(self.simplex, self.dimension);
CofacetIterator{
dimension: self.dimension,
binomial: &converter.binomial,
higher_sum: 0,
lower_sum: usize::from(self.simplex) as i64,
k: usize::from(self.dimension),
j: converter.vertex_count - 1,
vertices,
negative_k: false,
negative_j: false
}
}
}
pub struct CofacetIterator<'a> {
dimension: Dimension,
binomial: &'a BinomialCoeff,
vertices: Vec<usize>,
higher_sum: i64,
lower_sum: i64,
k: usize,
j: usize,
negative_k: bool,
negative_j: bool
}
impl<'a> Iterator for CofacetIterator<'a> {
type Item = RichSimplex;
fn next(&mut self) -> Option<Self::Item> {
if self.negative_j{
return None;
}
let dim = usize::from(self.dimension);
let result = loop {
while !self.negative_k && self.j < self.vertices[self.k] {
self.higher_sum += self.binomial.binomial(self.vertices[self.k], self.k+2) as i64;
self.lower_sum -= self.binomial.binomial(self.vertices[self.k], self.k+1) as i64;
if self.k == 0{
self.negative_k = true;
break;
}
self.k -= 1;
}
let k_offset = if self.negative_k {0} else {1};
if self.negative_k || !(self.j == self.vertices[self.k]) {
let cns = self.higher_sum + self.lower_sum + self.binomial.binomial(self.j, self.k+1+k_offset) as i64;
let cns = CNS::from(cns as usize);
break Some(RichSimplex{
simplex: cns,
dimension: Dimension::from(dim+1),
lifetime: f64::NAN })
}
if self.j == 0 {
break None;
}
self.j -= 1;
};
if result.is_some() {
if self.j > 0 {
self.j -= 1
} else {
self.negative_j = true;
}
};
result
}
}
fn insert_vertices(vertices: &[usize], lifetime: f64, converter: &SimplexConverter, max_dim: Dimension,
neighbors: &HashMap<usize, HashSet<usize>>) -> Vec<RichSimplex> {
let mut simplices: Vec<RichSimplex> = Vec::with_capacity(5); let mut queue: Vec<Vec<usize>> = vec![vertices.iter().copied().collect()];
while let Some(current) = queue.pop() {
if current.len() > usize::from(max_dim)+1 {
continue;
}
let current_simplex = RichSimplex::from_vertices(¤t, lifetime, converter);
let unseen = simplices.iter().all(|s| s != ¤t_simplex);
if !unseen {
continue;
}
simplices.push(current_simplex);
let common_neighbors: HashSet<usize> = current.iter()
.filter_map(|v| neighbors.get(v))
.fold(None, |acc, x| {
match acc {
None => Some(x.clone()),
Some(candidates) => Some(candidates.intersection(x).copied().collect())
}
})
.unwrap_or_else(HashSet::new);
let mut new_vertices: Vec<Vec<usize>> = common_neighbors.iter()
.map(|v| {
let mut new_vertices: Vec<usize> = current.iter().copied().collect();
new_vertices.push(*v);
new_vertices
})
.collect();
queue.append(&mut new_vertices);
}
simplices
}
fn rips(distances: Vec<Vec<f64>>, max_dim: Dimension, max_distance: Option<f64>) -> Vec<RichSimplex> {
let mut labeled_indices: Vec<(f64, usize, usize)> = distances.iter().enumerate()
.map(|(i, row)| {
row.iter().enumerate()
.skip(i+1)
.map(move |(j, &val)| { (val, i, j)
})
})
.flatten()
.collect();
labeled_indices.sort_by(|a, b| a.0.partial_cmp(&b.0).unwrap());
let converter = SimplexConverter::construct_for_vertex_count_and_dim(distances.len(), usize::from(max_dim));
let mut simplices: Vec<RichSimplex> = (0..distances.len())
.map(|i| {
let simplex = CNS::from(i);
let dimension = Dimension::from(0);
let lifetime = 0.0;
RichSimplex{simplex, dimension, lifetime}
})
.collect();
let mut neighbor_lookup: HashMap<usize, HashSet<usize>> = simplices.iter()
.map(|s| {
(usize::from(s.simplex), HashSet::with_capacity(distances.len()))
})
.collect();
for (distance, row, col) in labeled_indices {
if let Some(max_dist) = max_distance {
if distance > max_dist {
break;
}
}
neighbor_lookup.get_mut(&row).unwrap().insert(col);
neighbor_lookup.get_mut(&col).unwrap().insert(row);
let cofaces = insert_vertices(&[col, row], distance, &converter, max_dim, &neighbor_lookup);
simplices.extend(cofaces);
}
simplices.sort_by(|a, b| b.partial_cmp(a).unwrap()); simplices
}
#[cfg(test)]
mod tests {
use super::*;
use super::super::binomial::BinomialCoeff;
use std::cmp;
use proptest::prelude::*;
use proptest::collection::vec;
#[test]
fn test_rips_small() {
let dists = vec![vec![0., 1., 1., 2.],
vec![1., 0., 2., 1.],
vec![1., 2., 0., 1.],
vec![2., 1., 1., 0.]];
let converter = SimplexConverter::construct_for_vertex_count_and_dim(dists.len(), 4);
let expected = vec![
RichSimplex::from_vertices(&[3], 0., &converter),
RichSimplex::from_vertices(&[2], 0., &converter),
RichSimplex::from_vertices(&[1], 0., &converter),
RichSimplex::from_vertices(&[0], 0., &converter),
RichSimplex::from_vertices(&[2, 3], 1., &converter),
RichSimplex::from_vertices(&[1, 3], 1., &converter),
RichSimplex::from_vertices(&[0, 2], 1., &converter),
RichSimplex::from_vertices(&[0, 1], 1., &converter),
RichSimplex::from_vertices(&[0, 3], 2., &converter),
RichSimplex::from_vertices(&[1, 2], 2., &converter),
RichSimplex::from_vertices(&[1, 2, 3], 2., &converter),
RichSimplex::from_vertices(&[0, 2, 3], 2., &converter),
RichSimplex::from_vertices(&[0, 1, 3], 2., &converter),
RichSimplex::from_vertices(&[0, 1, 2], 2., &converter),
RichSimplex::from_vertices(&[0, 1, 2, 3], 2., &converter),
];
let expected: Vec<RichSimplex> = expected.into_iter().rev().collect();
let complex = rips(dists, Dimension::from(4), None);
assert_eq!(expected, complex);
}
#[test]
fn test_rips_small_max_dist() {
let dists = vec![vec![0., 1., 1., 2.],
vec![1., 0., 2., 1.],
vec![1., 2., 0., 1.],
vec![2., 1., 1., 0.]];
let complex = rips(dists, Dimension::from(4), Some(1.0));
assert_eq!(complex.len(), 8);
}
#[test]
fn test_rips_small_max_dim() {
let dists = vec![vec![0., 1., 1., 2.],
vec![1., 0., 2., 1.],
vec![1., 2., 0., 1.],
vec![2., 1., 1., 0.]];
let complex = rips(dists, Dimension::from(2), None);
assert_eq!(complex.len(), 14);
}
#[test]
fn test_cofacet_iterator() {
let converter = SimplexConverter::construct_for_vertex_count_and_dim(7, 4);
let simplex = RichSimplex::from_vertices(&[0, 3, 5], 1., &converter);
let expected = [28, 12, 7 ,6];
for (i, coface) in simplex.cofacets(&converter).enumerate() {
assert_eq!(CNS::from(expected[i]), coface.simplex);
}
}
#[test]
fn test_cofacet_iterator_ends_on_0() {
let converter = SimplexConverter::construct_for_vertex_count_and_dim(3, 3);
let simplex = RichSimplex::from_vertices(&[2], 1., &converter);
let expected = [2, 1];
let mut length = 0;
for (i, coface) in simplex.cofacets(&converter).enumerate() {
assert_eq!(CNS::from(expected[i]), coface.simplex);
length += 1;
}
assert_eq!(expected.len(), length);
}
proptest! {
#[test]
fn test_rips_correct_counts(distances in vec(0.01f64..1000.0, 0..100), max_dim in 0usize..4) {
let n = 1 + ((((1 + 8*distances.len()) as f64).sqrt() - 1.0) / 2.0).floor() as usize;
let mut dists: Vec<Vec<f64>> = (0..n).map(|_| {
(0..n).map(|_| 0.0).collect()
}).collect();
let mut diag = 1;
let mut offset = 0;
for distance in distances {
if diag == n {
break;
}
dists[diag][offset] = distance;
dists[offset][diag] = distance;
offset += 1;
if offset >= diag {
offset = 0;
diag += 1
}
}
let binomial = BinomialCoeff::construct_for_max_k_and_n(n+1, max_dim+1);
let complex = rips(dists, Dimension::from(max_dim), None);
for k in 0..cmp::min(max_dim+1, n){
let simplex_count = complex.iter()
.filter(|s| s.dimension == Dimension::from(k))
.count();
let expected = binomial.binomial(n, k+1);
prop_assert_eq!(expected, simplex_count);
}
}
}
}