use std::collections::{BTreeMap, BTreeSet};
use super::grade::{FiltrationError, FiltrationGrade, ScalarGrade, ScalarProjection};
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FilteredSimplex<G> {
vertices: Vec<usize>,
grade: G,
}
impl<G> FilteredSimplex<G> {
pub fn new(vertices: Vec<usize>, grade: G) -> Self {
Self { vertices, grade }
}
pub fn vertices(&self) -> &[usize] {
&self.vertices
}
pub fn dimension(&self) -> usize {
self.vertices.len().saturating_sub(1)
}
pub fn grade(&self) -> &G {
&self.grade
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct FilteredSimplicialComplex<G> {
vertex_labels: Vec<usize>,
simplices: Vec<Vec<FilteredSimplex<G>>>,
}
impl<G: FiltrationGrade> FilteredSimplicialComplex<G> {
pub fn new(
vertex_labels: Vec<usize>,
mut simplices: Vec<Vec<FilteredSimplex<G>>>,
) -> Result<Self, FiltrationError> {
if vertex_labels.windows(2).any(|pair| pair[0] >= pair[1]) {
return Err(FiltrationError::new(
"vertex labels must be strictly increasing",
));
}
if simplices.is_empty() {
simplices.push(Vec::new());
}
for dimension in &mut simplices {
dimension.sort_by(|left, right| left.vertices.cmp(&right.vertices));
}
let complex = Self {
vertex_labels,
simplices,
};
complex.validate()?;
Ok(complex)
}
pub fn vertex_labels(&self) -> &[usize] {
&self.vertex_labels
}
pub fn max_dimension(&self) -> usize {
self.simplices.len().saturating_sub(1)
}
pub fn simplices(&self) -> &[Vec<FilteredSimplex<G>>] {
&self.simplices
}
pub fn simplex_count(&self) -> usize {
self.simplices.iter().map(Vec::len).sum()
}
pub fn project<P: ScalarProjection<G>>(
&self,
projection: &P,
) -> Result<FilteredSimplicialComplex<ScalarGrade>, FiltrationError> {
let mut simplices = Vec::with_capacity(self.simplices.len());
for dimension in &self.simplices {
let mut projected = Vec::with_capacity(dimension.len());
for simplex in dimension {
projected.push(FilteredSimplex::new(
simplex.vertices.clone(),
projection.project(&simplex.grade)?,
));
}
simplices.push(projected);
}
FilteredSimplicialComplex::new(self.vertex_labels.clone(), simplices)
}
fn validate(&self) -> Result<(), FiltrationError> {
let labels: BTreeSet<_> = self.vertex_labels.iter().copied().collect();
let mut grades = BTreeMap::<Vec<usize>, &G>::new();
for (dimension, simplices) in self.simplices.iter().enumerate() {
for simplex in simplices {
validate_simplex_key(simplex, dimension, &labels)?;
insert_simplex_grade(simplex, &mut grades)?;
}
}
validate_declared_vertices(&self.simplices[0], &self.vertex_labels)?;
validate_faces(&grades)
}
}
fn validate_simplex_key<G>(
simplex: &FilteredSimplex<G>,
dimension: usize,
labels: &BTreeSet<usize>,
) -> Result<(), FiltrationError> {
if simplex.vertices.len() != dimension + 1 {
return Err(FiltrationError::new(format!(
"simplex {:?} is stored in dimension {dimension}",
simplex.vertices
)));
}
if simplex.vertices.windows(2).any(|pair| pair[0] >= pair[1]) {
return Err(FiltrationError::new(format!(
"simplex {:?} does not have a canonical vertex key",
simplex.vertices
)));
}
if simplex
.vertices
.iter()
.any(|vertex| !labels.contains(vertex))
{
return Err(FiltrationError::new(format!(
"simplex {:?} uses an unknown vertex label",
simplex.vertices
)));
}
Ok(())
}
fn insert_simplex_grade<'a, G>(
simplex: &'a FilteredSimplex<G>,
grades: &mut BTreeMap<Vec<usize>, &'a G>,
) -> Result<(), FiltrationError> {
if grades
.insert(simplex.vertices.clone(), &simplex.grade)
.is_some()
{
return Err(FiltrationError::new(format!(
"simplex {:?} occurs more than once",
simplex.vertices
)));
}
Ok(())
}
fn validate_declared_vertices<G>(
vertices: &[FilteredSimplex<G>],
labels: &[usize],
) -> Result<(), FiltrationError> {
let declared: Vec<_> = vertices.iter().map(|simplex| simplex.vertices[0]).collect();
if declared != labels {
return Err(FiltrationError::new(
"zero-dimensional simplices must match the vertex labels",
));
}
Ok(())
}
fn validate_faces<G: FiltrationGrade>(
grades: &BTreeMap<Vec<usize>, &G>,
) -> Result<(), FiltrationError> {
for (key, grade) in grades {
if key.len() > 1 {
validate_simplex_faces(key, *grade, grades)?;
}
}
Ok(())
}
fn validate_simplex_faces<G: FiltrationGrade>(
key: &[usize],
grade: &G,
grades: &BTreeMap<Vec<usize>, &G>,
) -> Result<(), FiltrationError> {
for removed in 0..key.len() {
let mut face = key.to_vec();
face.remove(removed);
let face_grade = grades.get(&face).ok_or_else(|| {
FiltrationError::new(format!("simplex {key:?} is missing the face {face:?}"))
})?;
if !face_grade.precedes(grade) {
return Err(FiltrationError::new(format!(
"face {face:?} appears after its coface {key:?}"
)));
}
}
Ok(())
}