mod degree_rips;
mod validation;
#[cfg(test)]
mod tests;
pub use degree_rips::{DegreeRipsBifiltration, DegreeRipsParams};
use validation::{
validate_axes, validate_face_support, validate_simplex_key, validate_vertex_keys,
};
use std::collections::BTreeMap;
use crate::filtration::ComplexLimits;
use crate::{Error, Result, SparseDistanceMatrix};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Bigrade {
scale: usize,
density: usize,
}
impl Bigrade {
pub fn new(scale: usize, density: usize) -> Self {
Self { scale, density }
}
pub fn scale(self) -> usize {
self.scale
}
pub fn density(self) -> usize {
self.density
}
pub fn precedes(self, other: Self) -> bool {
self.scale <= other.scale && self.density <= other.density
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BirthAntichain {
grades: Vec<Bigrade>,
}
impl BirthAntichain {
pub fn new(mut grades: Vec<Bigrade>) -> Result<Self> {
grades.sort_unstable();
if grades.is_empty() {
return Err(Error::InvalidInput(
"a multicritical simplex needs at least one birth grade".into(),
));
}
for (position, grade) in grades.iter().copied().enumerate() {
if grades
.iter()
.copied()
.enumerate()
.any(|(other, candidate)| other != position && candidate.precedes(grade))
{
return Err(Error::InvalidInput(
"multicritical birth grades must be pairwise incomparable".into(),
));
}
}
Ok(Self { grades })
}
pub fn grades(&self) -> &[Bigrade] {
&self.grades
}
pub fn supports(&self, grade: Bigrade) -> bool {
self.grades
.iter()
.copied()
.any(|birth| birth.precedes(grade))
}
fn from_candidates(candidates: impl IntoIterator<Item = Bigrade>) -> Result<Self> {
let mut minimal = Vec::<Bigrade>::new();
for grade in candidates {
if minimal.iter().copied().any(|birth| birth.precedes(grade)) {
continue;
}
minimal.retain(|birth| !grade.precedes(*birth));
minimal.push(grade);
}
Self::new(minimal)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MulticriticalSimplex {
vertices: Vec<usize>,
births: BirthAntichain,
}
impl MulticriticalSimplex {
pub fn new(vertices: Vec<usize>, births: BirthAntichain) -> Self {
Self { vertices, births }
}
pub fn vertices(&self) -> &[usize] {
&self.vertices
}
pub fn births(&self) -> &BirthAntichain {
&self.births
}
pub fn dimension(&self) -> usize {
self.vertices.len().saturating_sub(1)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[non_exhaustive]
pub struct BifiltrationLimits {
pub max_scales: usize,
pub max_density_levels: usize,
pub max_birth_grades: usize,
pub complex: ComplexLimits,
}
impl Default for BifiltrationLimits {
fn default() -> Self {
Self {
max_scales: 100_000,
max_density_levels: 1_000_000,
max_birth_grades: 100_000_000,
complex: ComplexLimits::default(),
}
}
}
impl BifiltrationLimits {
fn simplex_limit(self, dimension: usize) -> usize {
match dimension {
0 => self.complex.max_vertices,
1 => self.complex.max_edges,
2 => self.complex.max_triangles,
_ => self.complex.max_higher_simplices,
}
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct MulticriticalBifiltration {
vertex_count: usize,
scale_bits: Vec<u64>,
minimum_degrees: Vec<usize>,
simplices: Vec<Vec<MulticriticalSimplex>>,
}
impl MulticriticalBifiltration {
pub fn new(
vertex_count: usize,
scales: Vec<f64>,
minimum_degrees: Vec<usize>,
mut simplices: Vec<Vec<MulticriticalSimplex>>,
limits: BifiltrationLimits,
) -> Result<Self> {
let scale_bits = validate_axes(vertex_count, &scales, &minimum_degrees, limits)?;
if simplices.is_empty() {
simplices.push(Vec::new());
}
for dimension in &mut simplices {
dimension.sort_by(|left, right| left.vertices.cmp(&right.vertices));
}
let bifiltration = Self {
vertex_count,
scale_bits,
minimum_degrees,
simplices,
};
bifiltration.validate(limits)?;
Ok(bifiltration)
}
pub fn vertex_count(&self) -> usize {
self.vertex_count
}
pub fn scales(&self) -> impl ExactSizeIterator<Item = f64> + '_ {
self.scale_bits.iter().copied().map(f64::from_bits)
}
pub fn minimum_degrees(&self) -> &[usize] {
&self.minimum_degrees
}
pub fn simplices(&self) -> &[Vec<MulticriticalSimplex>] {
&self.simplices
}
pub fn maximum_grade(&self) -> Bigrade {
Bigrade::new(self.scale_bits.len() - 1, self.minimum_degrees.len() - 1)
}
pub fn slice(&self, grade: Bigrade) -> Result<BifiltrationSlice> {
self.validate_grade(grade)?;
let simplices = self
.simplices
.iter()
.map(|dimension| {
dimension
.iter()
.filter(|simplex| simplex.births.supports(grade))
.map(|simplex| simplex.vertices.clone())
.collect::<Vec<_>>()
})
.collect::<Vec<_>>();
let active_vertices = simplices
.first()
.into_iter()
.flat_map(|vertices| vertices.iter().map(|simplex| simplex[0]))
.collect();
Ok(BifiltrationSlice {
grade,
active_vertices,
simplices,
})
}
fn validate_grade(&self, grade: Bigrade) -> Result<()> {
if grade.scale >= self.scale_bits.len() || grade.density >= self.minimum_degrees.len() {
return Err(Error::InvalidInput(
"bifiltration grade is outside the finite parameter grid".into(),
));
}
Ok(())
}
fn validate(&self, limits: BifiltrationLimits) -> Result<()> {
let by_key = self.validate_simplices(limits)?;
validate_vertex_keys(self.vertex_count, &self.simplices[0])?;
validate_face_support(&by_key)
}
fn validate_simplices(
&self,
limits: BifiltrationLimits,
) -> Result<BTreeMap<Vec<usize>, &BirthAntichain>> {
let mut births = 0usize;
let mut by_key = BTreeMap::<Vec<usize>, &BirthAntichain>::new();
for (dimension, simplices) in self.simplices.iter().enumerate() {
if simplices.len() > limits.simplex_limit(dimension) {
return Err(Error::InvalidInput(format!(
"bifiltration dimension {dimension} exceeds its simplex limit"
)));
}
for simplex in simplices {
validate_simplex_key(simplex, dimension, self.vertex_count)?;
for grade in simplex.births.grades() {
self.validate_grade(*grade)?;
}
births = births
.checked_add(simplex.births.grades().len())
.ok_or_else(|| {
Error::InvalidInput("bifiltration birth-grade count overflows".into())
})?;
if births > limits.max_birth_grades {
return Err(Error::InvalidInput(
"bifiltration birth-grade count exceeds its limit".into(),
));
}
if by_key
.insert(simplex.vertices.clone(), &simplex.births)
.is_some()
{
return Err(Error::InvalidInput(format!(
"bifiltration simplex {:?} occurs more than once",
simplex.vertices
)));
}
}
}
Ok(by_key)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BifiltrationSlice {
grade: Bigrade,
active_vertices: Vec<usize>,
simplices: Vec<Vec<Vec<usize>>>,
}
impl BifiltrationSlice {
pub fn grade(&self) -> Bigrade {
self.grade
}
pub fn active_vertices(&self) -> &[usize] {
&self.active_vertices
}
pub fn simplices(&self) -> &[Vec<Vec<usize>>] {
&self.simplices
}
pub fn h1_graph(&self, vertex_count: usize) -> Result<SparseDistanceMatrix> {
let edges = self
.simplices
.get(1)
.into_iter()
.flatten()
.map(|edge| (edge[0], edge[1], 0.0))
.collect::<Vec<_>>();
SparseDistanceMatrix::from_triplets(vertex_count, &edges)
}
}