ruvector_math/homology/
filtration.rs1use super::{PointCloud, Simplex, SimplicialComplex};
6
7#[derive(Debug, Clone)]
9pub struct FilteredSimplex {
10 pub simplex: Simplex,
12 pub birth: f64,
14}
15
16impl FilteredSimplex {
17 pub fn new(simplex: Simplex, birth: f64) -> Self {
18 Self { simplex, birth }
19 }
20}
21
22#[derive(Debug, Clone)]
24pub struct Filtration {
25 pub simplices: Vec<FilteredSimplex>,
27 pub max_dim: usize,
29}
30
31impl Filtration {
32 pub fn new() -> Self {
34 Self {
35 simplices: Vec::new(),
36 max_dim: 0,
37 }
38 }
39
40 pub fn add(&mut self, simplex: Simplex, birth: f64) {
42 self.max_dim = self.max_dim.max(simplex.dim());
43 self.simplices.push(FilteredSimplex::new(simplex, birth));
44 }
45
46 pub fn sort(&mut self) {
48 self.simplices.sort_by(|a, b| {
50 a.birth
51 .partial_cmp(&b.birth)
52 .unwrap_or(std::cmp::Ordering::Equal)
53 .then_with(|| a.simplex.dim().cmp(&b.simplex.dim()))
54 });
55 }
56
57 pub fn complex_at(&self, t: f64) -> SimplicialComplex {
59 let simplices: Vec<Simplex> = self
60 .simplices
61 .iter()
62 .filter(|fs| fs.birth <= t)
63 .map(|fs| fs.simplex.clone())
64 .collect();
65 SimplicialComplex::from_simplices(simplices)
66 }
67
68 pub fn len(&self) -> usize {
70 self.simplices.len()
71 }
72
73 pub fn is_empty(&self) -> bool {
75 self.simplices.is_empty()
76 }
77
78 pub fn filtration_values(&self) -> Vec<f64> {
80 let mut values: Vec<f64> = self.simplices.iter().map(|fs| fs.birth).collect();
81 values.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal));
82 values.dedup();
83 values
84 }
85}
86
87impl Default for Filtration {
88 fn default() -> Self {
89 Self::new()
90 }
91}
92
93#[derive(Debug, Clone)]
97pub struct VietorisRips {
98 pub max_dim: usize,
100 pub max_scale: f64,
102}
103
104impl VietorisRips {
105 pub fn new(max_dim: usize, max_scale: f64) -> Self {
107 Self { max_dim, max_scale }
108 }
109
110 pub fn build(&self, cloud: &PointCloud) -> Filtration {
112 let n = cloud.len();
113 let dist = cloud.distance_matrix();
114
115 let mut filtration = Filtration::new();
116
117 for i in 0..n {
119 filtration.add(Simplex::vertex(i), 0.0);
120 }
121
122 for i in 0..n {
124 for j in i + 1..n {
125 let d = dist[i * n + j];
126 if d <= self.max_scale {
127 filtration.add(Simplex::edge(i, j), d);
128 }
129 }
130 }
131
132 if self.max_dim >= 2 {
134 for i in 0..n {
136 for j in i + 1..n {
137 for k in j + 1..n {
138 let d_ij = dist[i * n + j];
139 let d_ik = dist[i * n + k];
140 let d_jk = dist[j * n + k];
141 let diameter = d_ij.max(d_ik).max(d_jk);
142
143 if diameter <= self.max_scale {
144 filtration.add(Simplex::triangle(i, j, k), diameter);
145 }
146 }
147 }
148 }
149 }
150
151 if self.max_dim >= 3 {
152 for i in 0..n {
154 for j in i + 1..n {
155 for k in j + 1..n {
156 for l in k + 1..n {
157 let d_ij = dist[i * n + j];
158 let d_ik = dist[i * n + k];
159 let d_il = dist[i * n + l];
160 let d_jk = dist[j * n + k];
161 let d_jl = dist[j * n + l];
162 let d_kl = dist[k * n + l];
163 let diameter = d_ij.max(d_ik).max(d_il).max(d_jk).max(d_jl).max(d_kl);
164
165 if diameter <= self.max_scale {
166 filtration.add(Simplex::new(vec![i, j, k, l]), diameter);
167 }
168 }
169 }
170 }
171 }
172 }
173
174 filtration.sort();
175 filtration
176 }
177}
178
179#[derive(Debug, Clone)]
183pub struct AlphaComplex {
184 pub max_alpha: f64,
186}
187
188impl AlphaComplex {
189 pub fn new(max_alpha: f64) -> Self {
191 Self { max_alpha }
192 }
193
194 pub fn build(&self, cloud: &PointCloud) -> Filtration {
199 let n = cloud.len();
200 let dist = cloud.distance_matrix();
201
202 let mut filtration = Filtration::new();
203
204 for i in 0..n {
206 filtration.add(Simplex::vertex(i), 0.0);
207 }
208
209 for i in 0..n {
211 for j in i + 1..n {
212 let alpha = dist[i * n + j] / 2.0;
213 if alpha <= self.max_alpha {
214 filtration.add(Simplex::edge(i, j), alpha);
215 }
216 }
217 }
218
219 for i in 0..n {
221 for j in i + 1..n {
222 for k in j + 1..n {
223 let d_ij = dist[i * n + j];
224 let d_ik = dist[i * n + k];
225 let d_jk = dist[j * n + k];
226
227 let s = (d_ij + d_ik + d_jk) / 2.0;
229 let area_sq = s * (s - d_ij) * (s - d_ik) * (s - d_jk);
230 let alpha = if area_sq > 0.0 {
231 (d_ij * d_ik * d_jk) / (4.0 * area_sq.sqrt())
232 } else {
233 d_ij.max(d_ik).max(d_jk) / 2.0
234 };
235
236 if alpha <= self.max_alpha {
237 filtration.add(Simplex::triangle(i, j, k), alpha);
238 }
239 }
240 }
241 }
242
243 filtration.sort();
244 filtration
245 }
246}
247
248#[cfg(test)]
249mod tests {
250 use super::*;
251
252 #[test]
253 fn test_filtration_creation() {
254 let mut filtration = Filtration::new();
255 filtration.add(Simplex::vertex(0), 0.0);
256 filtration.add(Simplex::vertex(1), 0.0);
257 filtration.add(Simplex::edge(0, 1), 1.0);
258
259 assert_eq!(filtration.len(), 3);
260 }
261
262 #[test]
263 fn test_filtration_sort() {
264 let mut filtration = Filtration::new();
265 filtration.add(Simplex::edge(0, 1), 1.0);
266 filtration.add(Simplex::vertex(0), 0.0);
267 filtration.add(Simplex::vertex(1), 0.0);
268
269 filtration.sort();
270
271 assert!(filtration.simplices[0].simplex.is_vertex());
273 assert!(filtration.simplices[1].simplex.is_vertex());
274 assert!(filtration.simplices[2].simplex.is_edge());
275 }
276
277 #[test]
278 fn test_vietoris_rips() {
279 let cloud = PointCloud::from_flat(&[0.0, 0.0, 1.0, 0.0, 0.5, 0.866], 2);
281 let rips = VietorisRips::new(2, 2.0);
282
283 let filtration = rips.build(&cloud);
284
285 let values = filtration.filtration_values();
287 assert!(!values.is_empty());
288 }
289
290 #[test]
291 fn test_complex_at() {
292 let cloud = PointCloud::from_flat(&[0.0, 0.0, 1.0, 0.0, 2.0, 0.0], 2);
293 let rips = VietorisRips::new(1, 2.0);
294 let filtration = rips.build(&cloud);
295
296 let complex_0 = filtration.complex_at(0.5);
298 assert_eq!(complex_0.count_dim(0), 3);
299 assert_eq!(complex_0.count_dim(1), 0);
300
301 let complex_1 = filtration.complex_at(1.5);
303 assert_eq!(complex_1.count_dim(0), 3);
304 assert!(complex_1.count_dim(1) >= 2); }
306
307 #[test]
308 fn test_alpha_complex() {
309 let cloud = PointCloud::from_flat(&[0.0, 0.0, 1.0, 0.0, 0.0, 1.0], 2);
310 let alpha = AlphaComplex::new(2.0);
311
312 let filtration = alpha.build(&cloud);
313
314 assert!(filtration.len() >= 3); }
316}