1use super::{ProperCutInstance, InstanceResult};
7use super::witness::WitnessHandle;
8use crate::graph::{DynamicGraph, VertexId, EdgeId};
9use crate::localkcut::paper_impl::{
10 DeterministicLocalKCut, LocalKCutOracle, LocalKCutQuery, LocalKCutResult,
11};
12use crate::certificate::{CutCertificate, LocalKCutResponse, CertLocalKCutQuery, LocalKCutResultSummary};
13use crate::cluster::ClusterHierarchy;
14use crate::fragment::FragmentingAlgorithm;
15use roaring::RoaringBitmap;
16use std::collections::{HashMap, HashSet, VecDeque};
17use std::sync::{Arc, Mutex};
18
19#[derive(Clone, Default)]
21struct BoundaryCache {
22 value: u64,
24 valid: bool,
26}
27
28pub struct BoundedInstance {
33 lambda_min: u64,
35 lambda_max: u64,
36 edges: Vec<(EdgeId, VertexId, VertexId)>,
38 vertices: HashSet<VertexId>,
39 adjacency: HashMap<VertexId, Vec<(VertexId, EdgeId)>>,
41 best_witness: Mutex<Option<(u64, WitnessHandle)>>,
43 oracle: DeterministicLocalKCut,
45 certificate: Mutex<CutCertificate>,
47 max_radius: usize,
49 cluster_hierarchy: Option<ClusterHierarchy>,
51 fragmenting: Option<FragmentingAlgorithm>,
53 boundary_cache: Mutex<BoundaryCache>,
55}
56
57impl BoundedInstance {
58 pub fn new(lambda_min: u64, lambda_max: u64) -> Self {
60 Self {
61 lambda_min,
62 lambda_max,
63 edges: Vec::new(),
64 vertices: HashSet::new(),
65 adjacency: HashMap::new(),
66 best_witness: Mutex::new(None),
67 oracle: DeterministicLocalKCut::new(20), certificate: Mutex::new(CutCertificate::new()),
69 max_radius: 20,
70 cluster_hierarchy: None,
71 fragmenting: None,
72 boundary_cache: Mutex::new(BoundaryCache::default()),
73 }
74 }
75
76 fn ensure_hierarchy(&mut self, graph: &DynamicGraph) {
78 if self.cluster_hierarchy.is_none() && self.vertices.len() > 50 {
79 self.cluster_hierarchy = Some(ClusterHierarchy::new(Arc::new(graph.clone())));
80 }
81 }
82
83 fn rebuild_adjacency(&mut self) {
85 self.adjacency.clear();
86 for &(edge_id, u, v) in &self.edges {
87 self.adjacency.entry(u).or_default().push((v, edge_id));
88 self.adjacency.entry(v).or_default().push((u, edge_id));
89 }
90 }
91
92 fn insert(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
94 self.vertices.insert(u);
95 self.vertices.insert(v);
96 self.edges.push((edge_id, u, v));
97
98 self.adjacency.entry(u).or_default().push((v, edge_id));
99 self.adjacency.entry(v).or_default().push((u, edge_id));
100
101 self.update_boundary_on_insert(u, v);
103
104 self.maybe_invalidate_witness(u, v);
106 }
107
108 fn delete(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
110 self.update_boundary_on_delete(u, v);
112
113 self.edges.retain(|(eid, _, _)| *eid != edge_id);
114 self.rebuild_adjacency();
115
116 *self.best_witness.lock().unwrap() = None;
118 }
120
121 fn update_boundary_on_insert(&self, u: VertexId, v: VertexId) {
123 let witness_ref = self.best_witness.lock().unwrap();
124 if let Some((_, ref witness)) = *witness_ref {
125 let u_in = witness.contains(u);
126 let v_in = witness.contains(v);
127
128 if u_in != v_in {
130 let mut cache = self.boundary_cache.lock().unwrap();
131 if cache.valid {
132 cache.value += 1;
133 }
134 }
135 }
136 }
137
138 fn update_boundary_on_delete(&self, u: VertexId, v: VertexId) {
140 let witness_ref = self.best_witness.lock().unwrap();
141 if let Some((_, ref witness)) = *witness_ref {
142 let u_in = witness.contains(u);
143 let v_in = witness.contains(v);
144
145 if u_in != v_in {
147 let mut cache = self.boundary_cache.lock().unwrap();
148 if cache.valid {
149 cache.value = cache.value.saturating_sub(1);
150 }
151 }
152 }
153 }
154
155 fn maybe_invalidate_witness(&mut self, u: VertexId, v: VertexId) {
157 let mut witness_ref = self.best_witness.lock().unwrap();
158 if let Some((_, ref witness)) = *witness_ref {
159 let u_in = witness.contains(u);
160 let v_in = witness.contains(v);
161
162 if u_in != v_in {
165 *witness_ref = None;
166 drop(witness_ref); self.invalidate_boundary_cache();
169 }
170 }
171 }
172
173 fn is_connected(&self) -> bool {
175 if self.vertices.is_empty() {
176 return true;
177 }
178
179 let start = *self.vertices.iter().next().unwrap();
180 let mut visited = HashSet::new();
181 let mut queue = VecDeque::new();
182
183 queue.push_back(start);
184 visited.insert(start);
185
186 while let Some(current) = queue.pop_front() {
187 if let Some(neighbors) = self.adjacency.get(¤t) {
188 for &(neighbor, _) in neighbors {
189 if visited.insert(neighbor) {
190 queue.push_back(neighbor);
191 }
192 }
193 }
194 }
195
196 visited.len() == self.vertices.len()
197 }
198
199 fn search_for_cuts(&mut self) -> Option<(u64, WitnessHandle)> {
201 let graph = Arc::new(DynamicGraph::new());
203 for &(_, u, v) in &self.edges {
204 let _ = graph.insert_edge(u, v, 1.0);
205 }
206
207 self.ensure_hierarchy(&graph);
209
210 let seed_vertices: Vec<VertexId> = if let Some(ref hierarchy) = self.cluster_hierarchy {
212 let mut boundary_vertices = HashSet::new();
214
215 for cluster in hierarchy.clusters.values() {
217 for &v in &cluster.vertices {
219 if let Some(neighbors) = self.adjacency.get(&v) {
220 for &(neighbor, _) in neighbors {
221 if !cluster.vertices.contains(&neighbor) {
223 boundary_vertices.insert(v);
224 }
225 }
226 }
227 }
228 }
229
230 if boundary_vertices.is_empty() {
232 self.vertices.iter().copied().collect()
233 } else {
234 boundary_vertices.into_iter().collect()
235 }
236 } else {
237 self.vertices.iter().copied().collect()
239 };
240
241 for budget in self.lambda_min..=self.lambda_max {
243 for &seed in &seed_vertices {
245 let query = LocalKCutQuery {
246 seed_vertices: vec![seed],
247 budget_k: budget,
248 radius: self.max_radius,
249 };
250
251 self.certificate.lock().unwrap().add_response(LocalKCutResponse {
253 query: CertLocalKCutQuery {
254 seed_vertices: vec![seed],
255 budget_k: budget,
256 radius: self.max_radius,
257 },
258 result: LocalKCutResultSummary::NoneInLocality,
259 timestamp: 0,
260 trigger: None,
261 });
262
263 match self.oracle.search(&graph, query) {
264 LocalKCutResult::Found { witness, cut_value } => {
265 let mut cert = self.certificate.lock().unwrap();
267 if let Some(last) = cert.localkcut_responses.last_mut() {
268 last.result = LocalKCutResultSummary::Found {
269 cut_value,
270 witness_hash: witness.hash(),
271 };
272 }
273
274 if cut_value >= self.lambda_min && cut_value <= self.lambda_max {
275 return Some((cut_value, witness));
276 }
277 }
278 LocalKCutResult::NoneInLocality => {
279 }
281 }
282 }
283 }
284
285 None
286 }
287
288 fn brute_force_min_cut(&self) -> Option<(u64, WitnessHandle)> {
290 if self.vertices.len() >= 20 {
291 return None;
292 }
293
294 let vertex_vec: Vec<_> = self.vertices.iter().copied().collect();
295 let n = vertex_vec.len();
296
297 if n <= 1 {
298 return None;
299 }
300
301 let mut min_cut = u64::MAX;
302 let mut best_set = HashSet::new();
303
304 let max_mask = 1u64 << n;
305 for mask in 1..max_mask - 1 {
306 let mut subset = HashSet::new();
307 for (i, &v) in vertex_vec.iter().enumerate() {
308 if mask & (1 << i) != 0 {
309 subset.insert(v);
310 }
311 }
312
313 if !self.is_subset_connected(&subset) {
315 continue;
316 }
317
318 let boundary = self.compute_boundary(&subset);
320
321 if boundary < min_cut {
322 min_cut = boundary;
323 best_set = subset;
324 }
325 }
326
327 if min_cut == u64::MAX || best_set.is_empty() {
328 return None;
329 }
330
331 let membership: RoaringBitmap = best_set.iter().map(|&v| v as u32).collect();
332 let seed = *best_set.iter().next().unwrap();
333 let witness = WitnessHandle::new(seed, membership, min_cut);
334
335 Some((min_cut, witness))
336 }
337
338 fn is_subset_connected(&self, subset: &HashSet<VertexId>) -> bool {
340 if subset.is_empty() {
341 return true;
342 }
343
344 let start = *subset.iter().next().unwrap();
345 let mut visited = HashSet::new();
346 let mut queue = VecDeque::new();
347
348 queue.push_back(start);
349 visited.insert(start);
350
351 while let Some(current) = queue.pop_front() {
352 if let Some(neighbors) = self.adjacency.get(¤t) {
353 for &(neighbor, _) in neighbors {
354 if subset.contains(&neighbor) && visited.insert(neighbor) {
355 queue.push_back(neighbor);
356 }
357 }
358 }
359 }
360
361 visited.len() == subset.len()
362 }
363
364 fn compute_boundary(&self, subset: &HashSet<VertexId>) -> u64 {
366 let mut boundary = 0u64;
367
368 for &(_, u, v) in &self.edges {
369 let u_in = subset.contains(&u);
370 let v_in = subset.contains(&v);
371 if u_in != v_in {
372 boundary += 1;
373 }
374 }
375
376 boundary
377 }
378
379 fn get_cached_boundary(&self) -> Option<u64> {
385 let cache = self.boundary_cache.lock().unwrap();
386 if cache.valid {
387 Some(cache.value)
388 } else {
389 None
390 }
391 }
392
393 fn set_boundary_cache(&self, value: u64) {
395 let mut cache = self.boundary_cache.lock().unwrap();
396 cache.value = value;
397 cache.valid = true;
398 }
399
400 fn invalidate_boundary_cache(&self) {
402 let mut cache = self.boundary_cache.lock().unwrap();
403 cache.valid = false;
404 }
405
406 pub fn certificate(&self) -> CutCertificate {
408 self.certificate.lock().unwrap().clone()
409 }
410}
411
412impl ProperCutInstance for BoundedInstance {
413 fn init(_graph: &DynamicGraph, lambda_min: u64, lambda_max: u64) -> Self {
414 Self::new(lambda_min, lambda_max)
415 }
416
417 fn apply_inserts(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
418 for &(edge_id, u, v) in edges {
419 self.insert(edge_id, u, v);
420 }
421 }
422
423 fn apply_deletes(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
424 for &(edge_id, u, v) in edges {
425 self.delete(edge_id, u, v);
426 }
427 }
428
429 fn query(&mut self) -> InstanceResult {
430 if let Some(ref frag) = self.fragmenting {
432 if !frag.is_connected() {
433 let v = *self.vertices.iter().next().unwrap_or(&0);
435 let mut membership = RoaringBitmap::new();
436 membership.insert(v as u32);
437 let witness = WitnessHandle::new(v, membership, 0);
438 return InstanceResult::ValueInRange { value: 0, witness };
439 }
440 } else {
441 if !self.is_connected() && !self.vertices.is_empty() {
443 let v = *self.vertices.iter().next().unwrap();
444 let mut membership = RoaringBitmap::new();
445 membership.insert(v as u32);
446 let witness = WitnessHandle::new(v, membership, 0);
447 return InstanceResult::ValueInRange { value: 0, witness };
448 }
449 }
450
451 {
453 let witness_ref = self.best_witness.lock().unwrap();
454 if let Some((value, ref witness)) = *witness_ref {
455 if value >= self.lambda_min && value <= self.lambda_max {
456 return InstanceResult::ValueInRange {
457 value,
458 witness: witness.clone()
459 };
460 }
461 }
462 }
463
464 if self.vertices.len() < 20 {
466 if let Some((value, witness)) = self.brute_force_min_cut() {
467 *self.best_witness.lock().unwrap() = Some((value, witness.clone()));
469 self.set_boundary_cache(value);
470
471 if value <= self.lambda_max {
472 return InstanceResult::ValueInRange { value, witness };
473 } else {
474 return InstanceResult::AboveRange;
475 }
476 }
477 }
478
479 if let Some((value, witness)) = self.search_for_cuts() {
481 *self.best_witness.lock().unwrap() = Some((value, witness.clone()));
483 self.set_boundary_cache(value);
484 return InstanceResult::ValueInRange { value, witness };
485 }
486
487 InstanceResult::AboveRange
489 }
490
491 fn bounds(&self) -> (u64, u64) {
492 (self.lambda_min, self.lambda_max)
493 }
494}
495
496#[cfg(test)]
497mod tests {
498 use super::*;
499
500 #[test]
501 fn test_empty_instance() {
502 let instance = BoundedInstance::new(1, 10);
503 assert_eq!(instance.bounds(), (1, 10));
504 }
505
506 #[test]
507 fn test_path_graph() {
508 let mut instance = BoundedInstance::new(0, 10);
509 instance.apply_inserts(&[
510 (0, 0, 1),
511 (1, 1, 2),
512 ]);
513
514 match instance.query() {
515 InstanceResult::ValueInRange { value, .. } => {
516 assert_eq!(value, 1);
517 }
518 _ => panic!("Expected ValueInRange"),
519 }
520 }
521
522 #[test]
523 fn test_cycle_graph() {
524 let mut instance = BoundedInstance::new(0, 10);
525 instance.apply_inserts(&[
526 (0, 0, 1),
527 (1, 1, 2),
528 (2, 2, 0),
529 ]);
530
531 match instance.query() {
532 InstanceResult::ValueInRange { value, .. } => {
533 assert_eq!(value, 2);
534 }
535 _ => panic!("Expected ValueInRange"),
536 }
537 }
538
539 #[test]
540 fn test_above_range() {
541 let mut instance = BoundedInstance::new(5, 10);
542 instance.apply_inserts(&[
543 (0, 0, 1),
544 (1, 1, 2),
545 ]);
546
547 match instance.query() {
550 InstanceResult::ValueInRange { value, .. } => {
551 assert_eq!(value, 1);
552 }
553 _ => {}
554 }
555 }
556
557 #[test]
558 fn test_dynamic_updates() {
559 let mut instance = BoundedInstance::new(0, 10);
560
561 instance.apply_inserts(&[(0, 0, 1), (1, 1, 2)]);
562
563 match instance.query() {
564 InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
565 _ => panic!("Expected ValueInRange"),
566 }
567
568 instance.apply_inserts(&[(2, 2, 0)]);
570
571 match instance.query() {
572 InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 2),
573 _ => panic!("Expected ValueInRange"),
574 }
575 }
576
577 #[test]
578 fn test_disconnected_graph() {
579 let mut instance = BoundedInstance::new(0, 10);
580 instance.apply_inserts(&[
581 (0, 0, 1),
582 (1, 2, 3),
583 ]);
584
585 match instance.query() {
586 InstanceResult::ValueInRange { value, .. } => {
587 assert_eq!(value, 0);
588 }
589 _ => panic!("Expected ValueInRange with value 0"),
590 }
591 }
592
593 #[test]
594 fn test_certificate_tracking() {
595 let mut instance = BoundedInstance::new(0, 10);
596 instance.apply_inserts(&[(0, 0, 1), (1, 1, 2)]);
597
598 let _ = instance.query();
599
600 let cert = instance.certificate();
601 assert!(!cert.localkcut_responses.is_empty() || instance.vertices.len() < 20);
603 }
604}