1mod degree_rips;
8mod validation;
9
10#[cfg(test)]
11mod tests;
12
13pub use degree_rips::{DegreeRipsBifiltration, DegreeRipsParams};
14
15use validation::{
16 validate_axes, validate_face_support, validate_simplex_key, validate_vertex_keys,
17};
18
19use std::collections::BTreeMap;
20
21use crate::filtration::ComplexLimits;
22use crate::{Error, Result, SparseDistanceMatrix};
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct Bigrade {
30 scale: usize,
31 density: usize,
32}
33
34impl Bigrade {
35 pub fn new(scale: usize, density: usize) -> Self {
37 Self { scale, density }
38 }
39
40 pub fn scale(self) -> usize {
42 self.scale
43 }
44
45 pub fn density(self) -> usize {
47 self.density
48 }
49
50 pub fn precedes(self, other: Self) -> bool {
52 self.scale <= other.scale && self.density <= other.density
53 }
54}
55
56#[derive(Debug, Clone, PartialEq, Eq)]
58pub struct BirthAntichain {
59 grades: Vec<Bigrade>,
60}
61
62impl BirthAntichain {
63 pub fn new(mut grades: Vec<Bigrade>) -> Result<Self> {
65 grades.sort_unstable();
66 if grades.is_empty() {
67 return Err(Error::InvalidInput(
68 "a multicritical simplex needs at least one birth grade".into(),
69 ));
70 }
71 for (position, grade) in grades.iter().copied().enumerate() {
72 if grades
73 .iter()
74 .copied()
75 .enumerate()
76 .any(|(other, candidate)| other != position && candidate.precedes(grade))
77 {
78 return Err(Error::InvalidInput(
79 "multicritical birth grades must be pairwise incomparable".into(),
80 ));
81 }
82 }
83 Ok(Self { grades })
84 }
85
86 pub fn grades(&self) -> &[Bigrade] {
88 &self.grades
89 }
90
91 pub fn supports(&self, grade: Bigrade) -> bool {
93 self.grades
94 .iter()
95 .copied()
96 .any(|birth| birth.precedes(grade))
97 }
98
99 fn from_candidates(candidates: impl IntoIterator<Item = Bigrade>) -> Result<Self> {
100 let mut minimal = Vec::<Bigrade>::new();
101 for grade in candidates {
102 if minimal.iter().copied().any(|birth| birth.precedes(grade)) {
103 continue;
104 }
105 minimal.retain(|birth| !grade.precedes(*birth));
106 minimal.push(grade);
107 }
108 Self::new(minimal)
109 }
110}
111
112#[derive(Debug, Clone, PartialEq, Eq)]
114pub struct MulticriticalSimplex {
115 vertices: Vec<usize>,
116 births: BirthAntichain,
117}
118
119impl MulticriticalSimplex {
120 pub fn new(vertices: Vec<usize>, births: BirthAntichain) -> Self {
122 Self { vertices, births }
123 }
124
125 pub fn vertices(&self) -> &[usize] {
127 &self.vertices
128 }
129
130 pub fn births(&self) -> &BirthAntichain {
132 &self.births
133 }
134
135 pub fn dimension(&self) -> usize {
137 self.vertices.len().saturating_sub(1)
138 }
139}
140
141#[derive(Debug, Clone, Copy, PartialEq, Eq)]
143#[non_exhaustive]
144pub struct BifiltrationLimits {
145 pub max_scales: usize,
147 pub max_density_levels: usize,
149 pub max_birth_grades: usize,
151 pub complex: ComplexLimits,
153}
154
155impl Default for BifiltrationLimits {
156 fn default() -> Self {
157 Self {
158 max_scales: 100_000,
159 max_density_levels: 1_000_000,
160 max_birth_grades: 100_000_000,
161 complex: ComplexLimits::default(),
162 }
163 }
164}
165
166impl BifiltrationLimits {
167 fn simplex_limit(self, dimension: usize) -> usize {
168 match dimension {
169 0 => self.complex.max_vertices,
170 1 => self.complex.max_edges,
171 2 => self.complex.max_triangles,
172 _ => self.complex.max_higher_simplices,
173 }
174 }
175}
176
177#[derive(Debug, Clone, PartialEq, Eq)]
179pub struct MulticriticalBifiltration {
180 vertex_count: usize,
181 scale_bits: Vec<u64>,
182 minimum_degrees: Vec<usize>,
183 simplices: Vec<Vec<MulticriticalSimplex>>,
184}
185
186impl MulticriticalBifiltration {
187 pub fn new(
189 vertex_count: usize,
190 scales: Vec<f64>,
191 minimum_degrees: Vec<usize>,
192 mut simplices: Vec<Vec<MulticriticalSimplex>>,
193 limits: BifiltrationLimits,
194 ) -> Result<Self> {
195 let scale_bits = validate_axes(vertex_count, &scales, &minimum_degrees, limits)?;
196 if simplices.is_empty() {
197 simplices.push(Vec::new());
198 }
199 for dimension in &mut simplices {
200 dimension.sort_by(|left, right| left.vertices.cmp(&right.vertices));
201 }
202 let bifiltration = Self {
203 vertex_count,
204 scale_bits,
205 minimum_degrees,
206 simplices,
207 };
208 bifiltration.validate(limits)?;
209 Ok(bifiltration)
210 }
211
212 pub fn vertex_count(&self) -> usize {
214 self.vertex_count
215 }
216
217 pub fn scales(&self) -> impl ExactSizeIterator<Item = f64> + '_ {
219 self.scale_bits.iter().copied().map(f64::from_bits)
220 }
221
222 pub fn minimum_degrees(&self) -> &[usize] {
224 &self.minimum_degrees
225 }
226
227 pub fn simplices(&self) -> &[Vec<MulticriticalSimplex>] {
229 &self.simplices
230 }
231
232 pub fn maximum_grade(&self) -> Bigrade {
234 Bigrade::new(self.scale_bits.len() - 1, self.minimum_degrees.len() - 1)
235 }
236
237 pub fn slice(&self, grade: Bigrade) -> Result<BifiltrationSlice> {
239 self.validate_grade(grade)?;
240 let simplices = self
241 .simplices
242 .iter()
243 .map(|dimension| {
244 dimension
245 .iter()
246 .filter(|simplex| simplex.births.supports(grade))
247 .map(|simplex| simplex.vertices.clone())
248 .collect::<Vec<_>>()
249 })
250 .collect::<Vec<_>>();
251 let active_vertices = simplices
252 .first()
253 .into_iter()
254 .flat_map(|vertices| vertices.iter().map(|simplex| simplex[0]))
255 .collect();
256 Ok(BifiltrationSlice {
257 grade,
258 active_vertices,
259 simplices,
260 })
261 }
262
263 fn validate_grade(&self, grade: Bigrade) -> Result<()> {
264 if grade.scale >= self.scale_bits.len() || grade.density >= self.minimum_degrees.len() {
265 return Err(Error::InvalidInput(
266 "bifiltration grade is outside the finite parameter grid".into(),
267 ));
268 }
269 Ok(())
270 }
271
272 fn validate(&self, limits: BifiltrationLimits) -> Result<()> {
273 let by_key = self.validate_simplices(limits)?;
274 validate_vertex_keys(self.vertex_count, &self.simplices[0])?;
275 validate_face_support(&by_key)
276 }
277
278 fn validate_simplices(
279 &self,
280 limits: BifiltrationLimits,
281 ) -> Result<BTreeMap<Vec<usize>, &BirthAntichain>> {
282 let mut births = 0usize;
283 let mut by_key = BTreeMap::<Vec<usize>, &BirthAntichain>::new();
284 for (dimension, simplices) in self.simplices.iter().enumerate() {
285 if simplices.len() > limits.simplex_limit(dimension) {
286 return Err(Error::InvalidInput(format!(
287 "bifiltration dimension {dimension} exceeds its simplex limit"
288 )));
289 }
290 for simplex in simplices {
291 validate_simplex_key(simplex, dimension, self.vertex_count)?;
292 for grade in simplex.births.grades() {
293 self.validate_grade(*grade)?;
294 }
295 births = births
296 .checked_add(simplex.births.grades().len())
297 .ok_or_else(|| {
298 Error::InvalidInput("bifiltration birth-grade count overflows".into())
299 })?;
300 if births > limits.max_birth_grades {
301 return Err(Error::InvalidInput(
302 "bifiltration birth-grade count exceeds its limit".into(),
303 ));
304 }
305 if by_key
306 .insert(simplex.vertices.clone(), &simplex.births)
307 .is_some()
308 {
309 return Err(Error::InvalidInput(format!(
310 "bifiltration simplex {:?} occurs more than once",
311 simplex.vertices
312 )));
313 }
314 }
315 }
316 Ok(by_key)
317 }
318}
319
320#[derive(Debug, Clone, PartialEq, Eq)]
322pub struct BifiltrationSlice {
323 grade: Bigrade,
324 active_vertices: Vec<usize>,
325 simplices: Vec<Vec<Vec<usize>>>,
326}
327
328impl BifiltrationSlice {
329 pub fn grade(&self) -> Bigrade {
331 self.grade
332 }
333
334 pub fn active_vertices(&self) -> &[usize] {
336 &self.active_vertices
337 }
338
339 pub fn simplices(&self) -> &[Vec<Vec<usize>>] {
341 &self.simplices
342 }
343
344 pub fn h1_graph(&self, vertex_count: usize) -> Result<SparseDistanceMatrix> {
350 let edges = self
351 .simplices
352 .get(1)
353 .into_iter()
354 .flatten()
355 .map(|edge| (edge[0], edge[1], 0.0))
356 .collect::<Vec<_>>();
357 SparseDistanceMatrix::from_triplets(vertex_count, &edges)
358 }
359}