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