ruvector_mincut/fragmentation/
mod.rs1use std::collections::{HashMap, HashSet, VecDeque};
18use crate::graph::{VertexId, Weight};
19
20#[derive(Debug, Clone)]
22pub struct FragmentationConfig {
23 pub phi: f64,
25 pub max_fragment_size: usize,
27 pub min_fragment_size: usize,
29 pub boundary_sparsity: f64,
31}
32
33impl Default for FragmentationConfig {
34 fn default() -> Self {
35 Self {
36 phi: 0.1,
37 max_fragment_size: 1000,
38 min_fragment_size: 10,
39 boundary_sparsity: 0.5,
40 }
41 }
42}
43
44#[derive(Debug, Clone)]
46pub struct Fragment {
47 pub id: u64,
49 pub vertices: HashSet<VertexId>,
51 pub boundary: Vec<(VertexId, VertexId)>,
53 pub internal_edge_count: usize,
55 pub volume: usize,
57 pub parent: Option<u64>,
59 pub children: Vec<u64>,
61 pub is_expander: bool,
63}
64
65impl Fragment {
66 pub fn new(id: u64, vertices: HashSet<VertexId>) -> Self {
68 Self {
69 id,
70 vertices,
71 boundary: Vec::new(),
72 internal_edge_count: 0,
73 volume: 0,
74 parent: None,
75 children: Vec::new(),
76 is_expander: false,
77 }
78 }
79
80 pub fn size(&self) -> usize {
82 self.vertices.len()
83 }
84
85 pub fn contains(&self, v: VertexId) -> bool {
87 self.vertices.contains(&v)
88 }
89
90 pub fn boundary_sparsity(&self) -> f64 {
92 if self.volume == 0 {
93 return 0.0;
94 }
95 self.boundary.len() as f64 / self.volume as f64
96 }
97}
98
99#[derive(Debug, Clone)]
101pub struct TrimResult {
102 pub trimmed_vertices: HashSet<VertexId>,
104 pub cut_edges: Vec<(VertexId, VertexId)>,
106 pub cut_value: f64,
108 pub success: bool,
110}
111
112#[derive(Debug)]
114pub struct Fragmentation {
115 config: FragmentationConfig,
117 fragments: HashMap<u64, Fragment>,
119 vertex_fragment: HashMap<VertexId, u64>,
121 next_id: u64,
123 adjacency: HashMap<VertexId, HashMap<VertexId, Weight>>,
125 roots: Vec<u64>,
127}
128
129impl Fragmentation {
130 pub fn new(config: FragmentationConfig) -> Self {
132 Self {
133 config,
134 fragments: HashMap::new(),
135 vertex_fragment: HashMap::new(),
136 next_id: 1,
137 adjacency: HashMap::new(),
138 roots: Vec::new(),
139 }
140 }
141
142 pub fn with_defaults() -> Self {
144 Self::new(FragmentationConfig::default())
145 }
146
147 pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) {
149 self.adjacency.entry(u).or_default().insert(v, weight);
150 self.adjacency.entry(v).or_default().insert(u, weight);
151 }
152
153 pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
155 if let Some(neighbors) = self.adjacency.get_mut(&u) {
156 neighbors.remove(&v);
157 }
158 if let Some(neighbors) = self.adjacency.get_mut(&v) {
159 neighbors.remove(&u);
160 }
161 }
162
163 pub fn vertices(&self) -> Vec<VertexId> {
165 self.adjacency.keys().copied().collect()
166 }
167
168 pub fn neighbors(&self, v: VertexId) -> Vec<(VertexId, Weight)> {
170 self.adjacency.get(&v)
171 .map(|n| n.iter().map(|(&v, &w)| (v, w)).collect())
172 .unwrap_or_default()
173 }
174
175 pub fn degree(&self, v: VertexId) -> usize {
177 self.adjacency.get(&v).map_or(0, |n| n.len())
178 }
179
180 pub fn fragment(&mut self) -> Vec<u64> {
184 let vertices: HashSet<_> = self.vertices().into_iter().collect();
185
186 if vertices.is_empty() {
187 return Vec::new();
188 }
189
190 let root_id = self.create_fragment(vertices);
192 self.roots.push(root_id);
193
194 let mut to_process = vec![root_id];
196
197 while let Some(fragment_id) = to_process.pop() {
198 if let Some(children) = self.try_fragment_recursive(fragment_id) {
199 to_process.extend(children);
200 }
201 }
202
203 self.roots.clone()
204 }
205
206 fn try_fragment_recursive(&mut self, fragment_id: u64) -> Option<Vec<u64>> {
208 let fragment = self.fragments.get(&fragment_id)?;
209
210 if fragment.size() <= self.config.min_fragment_size {
212 return None;
213 }
214
215 if fragment.size() <= self.config.max_fragment_size && fragment.is_expander {
217 return None;
218 }
219
220 let vertices: Vec<_> = fragment.vertices.iter().copied().collect();
222 let trim_result = self.trim(&vertices);
223
224 if !trim_result.success || trim_result.trimmed_vertices.is_empty() {
225 if let Some(f) = self.fragments.get_mut(&fragment_id) {
227 f.is_expander = true;
228 }
229 return None;
230 }
231
232 let remaining: HashSet<_> = fragment.vertices
234 .difference(&trim_result.trimmed_vertices)
235 .copied()
236 .collect();
237
238 if remaining.is_empty() || trim_result.trimmed_vertices.len() == fragment.vertices.len() {
239 return None;
240 }
241
242 let child1_id = self.create_fragment(trim_result.trimmed_vertices);
244 let child2_id = self.create_fragment(remaining);
245
246 if let Some(f) = self.fragments.get_mut(&fragment_id) {
248 f.children = vec![child1_id, child2_id];
249 }
250 if let Some(f) = self.fragments.get_mut(&child1_id) {
251 f.parent = Some(fragment_id);
252 }
253 if let Some(f) = self.fragments.get_mut(&child2_id) {
254 f.parent = Some(fragment_id);
255 }
256
257 Some(vec![child1_id, child2_id])
258 }
259
260 fn create_fragment(&mut self, vertices: HashSet<VertexId>) -> u64 {
262 let id = self.next_id;
263 self.next_id += 1;
264
265 let mut internal_edge_count = 0;
267 let mut boundary = Vec::new();
268 let mut volume = 0;
269
270 for &v in &vertices {
271 let neighbors = self.neighbors(v);
272 volume += neighbors.len();
273
274 for (neighbor, _weight) in neighbors {
275 if vertices.contains(&neighbor) {
276 internal_edge_count += 1;
277 } else {
278 boundary.push((v, neighbor));
279 }
280 }
281 }
282
283 internal_edge_count /= 2;
285
286 let fragment = Fragment {
287 id,
288 vertices: vertices.clone(),
289 boundary,
290 internal_edge_count,
291 volume,
292 parent: None,
293 children: Vec::new(),
294 is_expander: false,
295 };
296
297 for &v in &vertices {
299 self.vertex_fragment.insert(v, id);
300 }
301
302 self.fragments.insert(id, fragment);
303 id
304 }
305
306 pub fn trim(&self, vertices: &[VertexId]) -> TrimResult {
313 if vertices.is_empty() {
314 return TrimResult {
315 trimmed_vertices: HashSet::new(),
316 cut_edges: Vec::new(),
317 cut_value: 0.0,
318 success: false,
319 };
320 }
321
322 let vertex_set: HashSet<_> = vertices.iter().copied().collect();
323
324 let mut sorted_vertices: Vec<_> = vertices.to_vec();
326 sorted_vertices.sort_by_key(|&v| self.degree(v));
327
328 let mut trimmed = HashSet::new();
329 let mut trimmed_volume = 0usize;
330 let mut boundary_count = 0usize;
331
332 for &v in &sorted_vertices {
334 let neighbors = self.neighbors(v);
335 let degree = neighbors.len();
336
337 let mut internal_neighbors = 0usize;
339 let mut external_neighbors = 0usize;
340
341 for (neighbor, _) in &neighbors {
342 if trimmed.contains(neighbor) {
343 internal_neighbors += 1;
344 } else if vertex_set.contains(neighbor) {
345 external_neighbors += 1;
347 }
348 }
349
350 let new_boundary = boundary_count - internal_neighbors + external_neighbors;
354 let new_volume = trimmed_volume + degree;
355
356 let sparsity = if new_volume > 0 {
358 new_boundary as f64 / new_volume as f64
359 } else {
360 0.0
361 };
362
363 if sparsity <= self.config.boundary_sparsity {
364 trimmed.insert(v);
365 trimmed_volume = new_volume;
366 boundary_count = new_boundary;
367 }
368
369 if trimmed.len() >= vertex_set.len() / 2 {
371 break;
372 }
373 }
374
375 if trimmed.is_empty() || trimmed.len() >= vertex_set.len() {
377 return TrimResult {
378 trimmed_vertices: HashSet::new(),
379 cut_edges: Vec::new(),
380 cut_value: 0.0,
381 success: false,
382 };
383 }
384
385 let mut cut_edges = Vec::new();
387 let mut cut_value = 0.0;
388
389 for &v in &trimmed {
390 for (neighbor, weight) in self.neighbors(v) {
391 if !trimmed.contains(&neighbor) && vertex_set.contains(&neighbor) {
392 cut_edges.push((v, neighbor));
393 cut_value += weight;
394 }
395 }
396 }
397
398 TrimResult {
399 trimmed_vertices: trimmed,
400 cut_edges,
401 cut_value,
402 success: true,
403 }
404 }
405
406 pub fn is_expander(&self, fragment_id: u64) -> bool {
411 let fragment = match self.fragments.get(&fragment_id) {
412 Some(f) => f,
413 None => return false,
414 };
415
416 if fragment.vertices.len() <= 2 {
417 return true; }
419
420 let vertices: Vec<_> = fragment.vertices.iter().copied().collect();
422 let trim = self.trim(&vertices);
423
424 if !trim.success {
425 return true; }
427
428 let cut_volume = trim.trimmed_vertices.iter()
430 .map(|&v| self.degree(v))
431 .sum::<usize>();
432
433 let remaining_volume = fragment.volume - cut_volume;
434 let min_volume = cut_volume.min(remaining_volume);
435
436 if min_volume == 0 {
437 return true;
438 }
439
440 let expansion_ratio = trim.cut_edges.len() as f64 / min_volume as f64;
441 expansion_ratio >= self.config.phi
442 }
443
444 pub fn get_fragment(&self, id: u64) -> Option<&Fragment> {
446 self.fragments.get(&id)
447 }
448
449 pub fn get_vertex_fragment(&self, v: VertexId) -> Option<&Fragment> {
451 self.vertex_fragment.get(&v)
452 .and_then(|&id| self.fragments.get(&id))
453 }
454
455 pub fn leaf_fragments(&self) -> Vec<&Fragment> {
457 self.fragments.values()
458 .filter(|f| f.children.is_empty())
459 .collect()
460 }
461
462 pub fn num_fragments(&self) -> usize {
464 self.fragments.len()
465 }
466
467 pub fn max_depth(&self) -> usize {
469 fn depth_of(fragments: &HashMap<u64, Fragment>, id: u64) -> usize {
470 match fragments.get(&id) {
471 Some(f) if f.children.is_empty() => 0,
472 Some(f) => 1 + f.children.iter()
473 .map(|&c| depth_of(fragments, c))
474 .max()
475 .unwrap_or(0),
476 None => 0,
477 }
478 }
479
480 self.roots.iter()
481 .map(|&r| depth_of(&self.fragments, r))
482 .max()
483 .unwrap_or(0)
484 }
485}
486
487impl Default for Fragmentation {
488 fn default() -> Self {
489 Self::with_defaults()
490 }
491}
492
493#[cfg(test)]
494mod tests {
495 use super::*;
496
497 fn build_path_graph(frag: &mut Fragmentation, n: usize) {
498 for i in 0..n-1 {
499 frag.insert_edge(i as u64, (i + 1) as u64, 1.0);
500 }
501 }
502
503 fn build_clique(frag: &mut Fragmentation, vertices: &[u64]) {
504 for i in 0..vertices.len() {
505 for j in i+1..vertices.len() {
506 frag.insert_edge(vertices[i], vertices[j], 1.0);
507 }
508 }
509 }
510
511 #[test]
512 fn test_fragmentation_empty() {
513 let mut frag = Fragmentation::with_defaults();
514 let roots = frag.fragment();
515 assert!(roots.is_empty());
516 }
517
518 #[test]
519 fn test_fragmentation_single_vertex() {
520 let mut frag = Fragmentation::with_defaults();
521 frag.insert_edge(1, 1, 0.0); frag.adjacency.entry(1).or_default(); let mut frag2 = Fragmentation::with_defaults();
526 frag2.insert_edge(1, 2, 1.0);
527 let roots = frag2.fragment();
528 assert_eq!(roots.len(), 1);
529 }
530
531 #[test]
532 fn test_fragmentation_path() {
533 let mut frag = Fragmentation::new(FragmentationConfig {
534 min_fragment_size: 2,
535 max_fragment_size: 5,
536 ..Default::default()
537 });
538
539 build_path_graph(&mut frag, 10);
540 let roots = frag.fragment();
541
542 assert!(!roots.is_empty());
543 assert!(frag.num_fragments() >= 1);
544 }
545
546 #[test]
547 fn test_fragmentation_clique() {
548 let mut frag = Fragmentation::with_defaults();
549 let vertices: Vec<u64> = (1..=6).collect();
550 build_clique(&mut frag, &vertices);
551
552 let roots = frag.fragment();
553
554 assert!(!roots.is_empty());
555 let leaves = frag.leaf_fragments();
557 let leaf = leaves.first().unwrap();
558 assert!(leaf.size() <= 6);
559 }
560
561 #[test]
562 fn test_trim_basic() {
563 let mut frag = Fragmentation::with_defaults();
564 build_path_graph(&mut frag, 10);
565
566 let vertices: Vec<u64> = (0..10).collect();
567 let result = frag.trim(&vertices);
568
569 assert!(result.trimmed_vertices.len() <= vertices.len());
572 }
573
574 #[test]
575 fn test_fragment_properties() {
576 let mut frag = Fragmentation::with_defaults();
577
578 build_clique(&mut frag, &[1, 2, 3, 4]);
580 build_clique(&mut frag, &[5, 6, 7, 8]);
581 frag.insert_edge(4, 5, 1.0); let roots = frag.fragment();
584
585 assert!(!roots.is_empty());
587
588 let f1 = frag.get_vertex_fragment(1);
590 assert!(f1.is_some());
591 }
592
593 #[test]
594 fn test_is_expander() {
595 let mut frag = Fragmentation::new(FragmentationConfig {
596 phi: 0.1,
597 min_fragment_size: 2,
598 ..Default::default()
599 });
600
601 build_clique(&mut frag, &[1, 2, 3, 4, 5]);
603
604 let roots = frag.fragment();
605 assert!(!roots.is_empty());
606
607 let is_exp = frag.is_expander(roots[0]);
609 assert!(is_exp || frag.leaf_fragments().len() > 1);
611 }
612
613 #[test]
614 fn test_hierarchy_depth() {
615 let mut frag = Fragmentation::new(FragmentationConfig {
616 min_fragment_size: 3,
617 max_fragment_size: 10,
618 ..Default::default()
619 });
620
621 build_path_graph(&mut frag, 20);
622 frag.fragment();
623
624 let depth = frag.max_depth();
625 assert!(depth >= 0);
627 }
628}