exo_hypergraph/
topology.rs1use exo_core::{EntityId, Error};
7use serde::{Deserialize, Serialize};
8use std::collections::{HashMap, HashSet};
9
10#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
12pub struct Simplex {
13 pub vertices: Vec<EntityId>,
15}
16
17impl Simplex {
18 pub fn new(mut vertices: Vec<EntityId>) -> Self {
20 vertices.sort_by_key(|v| v.0);
21 vertices.dedup();
22 Self { vertices }
23 }
24
25 pub fn dimension(&self) -> usize {
27 self.vertices.len().saturating_sub(1)
28 }
29
30 pub fn faces(&self) -> Vec<Simplex> {
32 if self.vertices.is_empty() {
33 return vec![];
34 }
35
36 let mut faces = Vec::new();
37
38 for i in 0..self.vertices.len() {
40 let mut face_vertices = self.vertices.clone();
41 face_vertices.remove(i);
42 if !face_vertices.is_empty() {
43 faces.push(Simplex::new(face_vertices));
44 }
45 }
46
47 faces
48 }
49}
50
51#[derive(Clone, Debug, Serialize, Deserialize)]
56pub struct SimplicialComplex {
57 simplices: HashMap<usize, HashSet<Simplex>>,
59 max_dimension: usize,
61}
62
63impl SimplicialComplex {
64 pub fn new() -> Self {
66 Self {
67 simplices: HashMap::new(),
68 max_dimension: 0,
69 }
70 }
71
72 pub fn add_simplex(&mut self, vertices: &[EntityId]) {
74 if vertices.is_empty() {
75 return;
76 }
77
78 let simplex = Simplex::new(vertices.to_vec());
79 let dim = simplex.dimension();
80
81 self.simplices
83 .entry(dim)
84 .or_insert_with(HashSet::new)
85 .insert(simplex.clone());
86
87 if dim > self.max_dimension {
88 self.max_dimension = dim;
89 }
90
91 for face in simplex.faces() {
93 self.add_simplex(&face.vertices);
94 }
95 }
96
97 pub fn get_simplices(&self, dimension: usize) -> Vec<Simplex> {
99 self.simplices
100 .get(&dimension)
101 .map(|set| set.iter().cloned().collect())
102 .unwrap_or_default()
103 }
104
105 pub fn count_simplices(&self, dimension: usize) -> usize {
107 self.simplices
108 .get(&dimension)
109 .map(|set| set.len())
110 .unwrap_or(0)
111 }
112
113 pub fn betti_number(&self, dimension: usize) -> usize {
122 if dimension == 0 {
123 self.count_connected_components()
125 } else {
126 0
129 }
130 }
131
132 fn count_connected_components(&self) -> usize {
134 let vertices = self.get_simplices(0);
135 if vertices.is_empty() {
136 return 0;
137 }
138
139 let mut parent: HashMap<EntityId, EntityId> = HashMap::new();
141
142 for simplex in &vertices {
144 if let Some(v) = simplex.vertices.first() {
145 parent.insert(*v, *v);
146 }
147 }
148
149 let edges = self.get_simplices(1);
151 for edge in edges {
152 if edge.vertices.len() == 2 {
153 let v1 = edge.vertices[0];
154 let v2 = edge.vertices[1];
155 self.union(&mut parent, v1, v2);
156 }
157 }
158
159 let mut roots = HashSet::new();
161 for v in parent.keys() {
162 roots.insert(self.find(&parent, *v));
163 }
164
165 roots.len()
166 }
167
168 fn find(&self, parent: &HashMap<EntityId, EntityId>, mut x: EntityId) -> EntityId {
170 while parent.get(&x) != Some(&x) {
171 if let Some(&p) = parent.get(&x) {
172 x = p;
173 } else {
174 break;
175 }
176 }
177 x
178 }
179
180 fn union(&self, parent: &mut HashMap<EntityId, EntityId>, x: EntityId, y: EntityId) {
182 let root_x = self.find(parent, x);
183 let root_y = self.find(parent, y);
184 if root_x != root_y {
185 parent.insert(root_x, root_y);
186 }
187 }
188
189 pub fn filtration(&self, _epsilon_range: (f32, f32)) -> Filtration {
194 Filtration {
195 complexes: vec![],
196 epsilon_values: vec![],
197 }
198 }
199
200 pub fn persistent_homology(
208 &self,
209 _dimension: usize,
210 _epsilon_range: (f32, f32),
211 ) -> PersistenceDiagram {
212 PersistenceDiagram { pairs: vec![] }
214 }
215}
216
217impl Default for SimplicialComplex {
218 fn default() -> Self {
219 Self::new()
220 }
221}
222
223#[derive(Debug, Clone, Serialize, Deserialize)]
227pub struct Filtration {
228 pub complexes: Vec<SimplicialComplex>,
230 pub epsilon_values: Vec<f32>,
232}
233
234impl Filtration {
235 pub fn birth_time(&self, _simplex_index: usize) -> f32 {
237 0.0
238 }
239}
240
241#[derive(Debug, Clone, Serialize, Deserialize)]
243pub struct PersistenceDiagram {
244 pub pairs: Vec<(f32, f32)>,
247}
248
249impl PersistenceDiagram {
250 pub fn significant_features(&self, min_persistence: f32) -> Vec<(f32, f32)> {
252 self.pairs
253 .iter()
254 .filter(|(birth, death)| {
255 if death.is_infinite() {
256 true
257 } else {
258 death - birth >= min_persistence
259 }
260 })
261 .copied()
262 .collect()
263 }
264}
265
266#[allow(dead_code)]
271fn column_reduction(_matrix: &BoundaryMatrix) -> BoundaryMatrix {
272 BoundaryMatrix { columns: vec![] }
274}
275
276#[derive(Debug, Clone)]
278struct BoundaryMatrix {
279 columns: Vec<Vec<usize>>,
280}
281
282impl BoundaryMatrix {
283 #[allow(dead_code)]
284 fn low(&self, _col: usize) -> Option<usize> {
285 None
286 }
287
288 #[allow(dead_code)]
289 fn column(&self, _index: usize) -> Vec<usize> {
290 vec![]
291 }
292
293 #[allow(dead_code)]
294 fn num_cols(&self) -> usize {
295 self.columns.len()
296 }
297}
298
299#[cfg(test)]
300mod tests {
301 use super::*;
302
303 #[test]
304 fn test_simplex_dimension() {
305 let e1 = EntityId::new();
306 let e2 = EntityId::new();
307 let e3 = EntityId::new();
308
309 let s0 = Simplex::new(vec![e1]);
311 assert_eq!(s0.dimension(), 0);
312
313 let s1 = Simplex::new(vec![e1, e2]);
315 assert_eq!(s1.dimension(), 1);
316
317 let s2 = Simplex::new(vec![e1, e2, e3]);
319 assert_eq!(s2.dimension(), 2);
320 }
321
322 #[test]
323 fn test_simplex_faces() {
324 let e1 = EntityId::new();
325 let e2 = EntityId::new();
326 let e3 = EntityId::new();
327
328 let triangle = Simplex::new(vec![e1, e2, e3]);
330 let faces = triangle.faces();
331 assert_eq!(faces.len(), 3);
332 assert!(faces.iter().all(|f| f.dimension() == 1));
333 }
334
335 #[test]
336 fn test_simplicial_complex() {
337 let mut complex = SimplicialComplex::new();
338
339 let e1 = EntityId::new();
340 let e2 = EntityId::new();
341 let e3 = EntityId::new();
342
343 complex.add_simplex(&[e1, e2, e3]);
345
346 assert_eq!(complex.count_simplices(0), 3);
348 assert_eq!(complex.count_simplices(1), 3);
349 assert_eq!(complex.count_simplices(2), 1);
350
351 assert_eq!(complex.betti_number(0), 1);
353 }
354
355 #[test]
356 fn test_betti_number_disconnected() {
357 let mut complex = SimplicialComplex::new();
358
359 let e1 = EntityId::new();
360 let e2 = EntityId::new();
361 let e3 = EntityId::new();
362 let e4 = EntityId::new();
363
364 complex.add_simplex(&[e1, e2]);
366 complex.add_simplex(&[e3, e4]);
367
368 assert_eq!(complex.betti_number(0), 2);
370 }
371}