use super::witness::WitnessHandle;
use super::{InstanceResult, ProperCutInstance};
use crate::certificate::{
CertLocalKCutQuery, CutCertificate, LocalKCutResponse, LocalKCutResultSummary,
};
use crate::cluster::ClusterHierarchy;
use crate::fragment::FragmentingAlgorithm;
use crate::graph::{DynamicGraph, EdgeId, VertexId};
use crate::localkcut::paper_impl::{
DeterministicLocalKCut, LocalKCutOracle, LocalKCutQuery, LocalKCutResult,
};
use roaring::RoaringBitmap;
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::{Arc, Mutex};
#[derive(Clone, Default)]
struct BoundaryCache {
value: u64,
valid: bool,
}
pub struct BoundedInstance {
lambda_min: u64,
lambda_max: u64,
edges: Vec<(EdgeId, VertexId, VertexId)>,
vertices: HashSet<VertexId>,
adjacency: HashMap<VertexId, Vec<(VertexId, EdgeId)>>,
best_witness: Mutex<Option<(u64, WitnessHandle)>>,
oracle: DeterministicLocalKCut,
certificate: Mutex<CutCertificate>,
max_radius: usize,
cluster_hierarchy: Option<ClusterHierarchy>,
fragmenting: Option<FragmentingAlgorithm>,
boundary_cache: Mutex<BoundaryCache>,
}
impl BoundedInstance {
pub fn new(lambda_min: u64, lambda_max: u64) -> Self {
Self {
lambda_min,
lambda_max,
edges: Vec::new(),
vertices: HashSet::new(),
adjacency: HashMap::new(),
best_witness: Mutex::new(None),
oracle: DeterministicLocalKCut::new(20), certificate: Mutex::new(CutCertificate::new()),
max_radius: 20,
cluster_hierarchy: None,
fragmenting: None,
boundary_cache: Mutex::new(BoundaryCache::default()),
}
}
fn ensure_hierarchy(&mut self, graph: &DynamicGraph) {
if self.cluster_hierarchy.is_none() && self.vertices.len() > 50 {
self.cluster_hierarchy = Some(ClusterHierarchy::new(Arc::new(graph.clone())));
}
}
fn rebuild_adjacency(&mut self) {
self.adjacency.clear();
for &(edge_id, u, v) in &self.edges {
self.adjacency.entry(u).or_default().push((v, edge_id));
self.adjacency.entry(v).or_default().push((u, edge_id));
}
}
fn insert(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
self.vertices.insert(u);
self.vertices.insert(v);
self.edges.push((edge_id, u, v));
self.adjacency.entry(u).or_default().push((v, edge_id));
self.adjacency.entry(v).or_default().push((u, edge_id));
self.update_boundary_on_insert(u, v);
self.maybe_invalidate_witness(u, v);
}
fn delete(&mut self, edge_id: EdgeId, u: VertexId, v: VertexId) {
self.update_boundary_on_delete(u, v);
self.edges.retain(|(eid, _, _)| *eid != edge_id);
self.rebuild_adjacency();
*self.best_witness.lock().unwrap() = None;
}
fn update_boundary_on_insert(&self, u: VertexId, v: VertexId) {
let witness_ref = self.best_witness.lock().unwrap();
if let Some((_, ref witness)) = *witness_ref {
let u_in = witness.contains(u);
let v_in = witness.contains(v);
if u_in != v_in {
let mut cache = self.boundary_cache.lock().unwrap();
if cache.valid {
cache.value += 1;
}
}
}
}
fn update_boundary_on_delete(&self, u: VertexId, v: VertexId) {
let witness_ref = self.best_witness.lock().unwrap();
if let Some((_, ref witness)) = *witness_ref {
let u_in = witness.contains(u);
let v_in = witness.contains(v);
if u_in != v_in {
let mut cache = self.boundary_cache.lock().unwrap();
if cache.valid {
cache.value = cache.value.saturating_sub(1);
}
}
}
}
fn maybe_invalidate_witness(&mut self, u: VertexId, v: VertexId) {
let mut witness_ref = self.best_witness.lock().unwrap();
if let Some((_, ref witness)) = *witness_ref {
let u_in = witness.contains(u);
let v_in = witness.contains(v);
if u_in != v_in {
*witness_ref = None;
drop(witness_ref); self.invalidate_boundary_cache();
}
}
}
fn is_connected(&self) -> bool {
if self.vertices.is_empty() {
return true;
}
let start = *self.vertices.iter().next().unwrap();
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(start);
visited.insert(start);
while let Some(current) = queue.pop_front() {
if let Some(neighbors) = self.adjacency.get(¤t) {
for &(neighbor, _) in neighbors {
if visited.insert(neighbor) {
queue.push_back(neighbor);
}
}
}
}
visited.len() == self.vertices.len()
}
fn search_for_cuts(&mut self) -> Option<(u64, WitnessHandle)> {
let graph = Arc::new(DynamicGraph::new());
for &(_, u, v) in &self.edges {
let _ = graph.insert_edge(u, v, 1.0);
}
self.ensure_hierarchy(&graph);
let seed_vertices: Vec<VertexId> = if let Some(ref hierarchy) = self.cluster_hierarchy {
let mut boundary_vertices = HashSet::new();
for cluster in hierarchy.clusters.values() {
for &v in &cluster.vertices {
if let Some(neighbors) = self.adjacency.get(&v) {
for &(neighbor, _) in neighbors {
if !cluster.vertices.contains(&neighbor) {
boundary_vertices.insert(v);
}
}
}
}
}
if boundary_vertices.is_empty() {
self.vertices.iter().copied().collect()
} else {
boundary_vertices.into_iter().collect()
}
} else {
self.vertices.iter().copied().collect()
};
for budget in self.lambda_min..=self.lambda_max {
for &seed in &seed_vertices {
let query = LocalKCutQuery {
seed_vertices: vec![seed],
budget_k: budget,
radius: self.max_radius,
};
self.certificate
.lock()
.unwrap()
.add_response(LocalKCutResponse {
query: CertLocalKCutQuery {
seed_vertices: vec![seed],
budget_k: budget,
radius: self.max_radius,
},
result: LocalKCutResultSummary::NoneInLocality,
timestamp: 0,
trigger: None,
});
match self.oracle.search(&graph, query) {
LocalKCutResult::Found { witness, cut_value } => {
let mut cert = self.certificate.lock().unwrap();
if let Some(last) = cert.localkcut_responses.last_mut() {
last.result = LocalKCutResultSummary::Found {
cut_value,
witness_hash: witness.hash(),
};
}
if cut_value >= self.lambda_min && cut_value <= self.lambda_max {
return Some((cut_value, witness));
}
}
LocalKCutResult::NoneInLocality => {
}
}
}
}
None
}
fn brute_force_min_cut(&self) -> Option<(u64, WitnessHandle)> {
if self.vertices.len() >= 20 {
return None;
}
let vertex_vec: Vec<_> = self.vertices.iter().copied().collect();
let n = vertex_vec.len();
if n <= 1 {
return None;
}
let mut min_cut = u64::MAX;
let mut best_set = HashSet::new();
let max_mask = 1u64 << n;
for mask in 1..max_mask - 1 {
let mut subset = HashSet::new();
for (i, &v) in vertex_vec.iter().enumerate() {
if mask & (1 << i) != 0 {
subset.insert(v);
}
}
if !self.is_subset_connected(&subset) {
continue;
}
let boundary = self.compute_boundary(&subset);
if boundary < min_cut {
min_cut = boundary;
best_set = subset;
}
}
if min_cut == u64::MAX || best_set.is_empty() {
return None;
}
let membership: RoaringBitmap = best_set.iter().map(|&v| v as u32).collect();
let seed = *best_set.iter().next().unwrap();
let witness = WitnessHandle::new(seed, membership, min_cut);
Some((min_cut, witness))
}
fn is_subset_connected(&self, subset: &HashSet<VertexId>) -> bool {
if subset.is_empty() {
return true;
}
let start = *subset.iter().next().unwrap();
let mut visited = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(start);
visited.insert(start);
while let Some(current) = queue.pop_front() {
if let Some(neighbors) = self.adjacency.get(¤t) {
for &(neighbor, _) in neighbors {
if subset.contains(&neighbor) && visited.insert(neighbor) {
queue.push_back(neighbor);
}
}
}
}
visited.len() == subset.len()
}
fn compute_boundary(&self, subset: &HashSet<VertexId>) -> u64 {
let mut boundary = 0u64;
for &(_, u, v) in &self.edges {
let u_in = subset.contains(&u);
let v_in = subset.contains(&v);
if u_in != v_in {
boundary += 1;
}
}
boundary
}
fn get_cached_boundary(&self) -> Option<u64> {
let cache = self.boundary_cache.lock().unwrap();
if cache.valid {
Some(cache.value)
} else {
None
}
}
fn set_boundary_cache(&self, value: u64) {
let mut cache = self.boundary_cache.lock().unwrap();
cache.value = value;
cache.valid = true;
}
fn invalidate_boundary_cache(&self) {
let mut cache = self.boundary_cache.lock().unwrap();
cache.valid = false;
}
pub fn certificate(&self) -> CutCertificate {
self.certificate.lock().unwrap().clone()
}
}
impl ProperCutInstance for BoundedInstance {
fn init(_graph: &DynamicGraph, lambda_min: u64, lambda_max: u64) -> Self {
Self::new(lambda_min, lambda_max)
}
fn apply_inserts(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
for &(edge_id, u, v) in edges {
self.insert(edge_id, u, v);
}
}
fn apply_deletes(&mut self, edges: &[(EdgeId, VertexId, VertexId)]) {
for &(edge_id, u, v) in edges {
self.delete(edge_id, u, v);
}
}
fn query(&mut self) -> InstanceResult {
if let Some(ref frag) = self.fragmenting {
if !frag.is_connected() {
let v = *self.vertices.iter().next().unwrap_or(&0);
let mut membership = RoaringBitmap::new();
membership.insert(v as u32);
let witness = WitnessHandle::new(v, membership, 0);
return InstanceResult::ValueInRange { value: 0, witness };
}
} else {
if !self.is_connected() && !self.vertices.is_empty() {
let v = *self.vertices.iter().next().unwrap();
let mut membership = RoaringBitmap::new();
membership.insert(v as u32);
let witness = WitnessHandle::new(v, membership, 0);
return InstanceResult::ValueInRange { value: 0, witness };
}
}
{
let witness_ref = self.best_witness.lock().unwrap();
if let Some((value, ref witness)) = *witness_ref {
if value >= self.lambda_min && value <= self.lambda_max {
return InstanceResult::ValueInRange {
value,
witness: witness.clone(),
};
}
}
}
if self.vertices.len() < 20 {
if let Some((value, witness)) = self.brute_force_min_cut() {
*self.best_witness.lock().unwrap() = Some((value, witness.clone()));
self.set_boundary_cache(value);
if value <= self.lambda_max {
return InstanceResult::ValueInRange { value, witness };
} else {
return InstanceResult::AboveRange;
}
}
}
if let Some((value, witness)) = self.search_for_cuts() {
*self.best_witness.lock().unwrap() = Some((value, witness.clone()));
self.set_boundary_cache(value);
return InstanceResult::ValueInRange { value, witness };
}
InstanceResult::AboveRange
}
fn bounds(&self) -> (u64, u64) {
(self.lambda_min, self.lambda_max)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_instance() {
let instance = BoundedInstance::new(1, 10);
assert_eq!(instance.bounds(), (1, 10));
}
#[test]
fn test_path_graph() {
let mut instance = BoundedInstance::new(0, 10);
instance.apply_inserts(&[(0, 0, 1), (1, 1, 2)]);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 1);
}
_ => panic!("Expected ValueInRange"),
}
}
#[test]
fn test_cycle_graph() {
let mut instance = BoundedInstance::new(0, 10);
instance.apply_inserts(&[(0, 0, 1), (1, 1, 2), (2, 2, 0)]);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 2);
}
_ => panic!("Expected ValueInRange"),
}
}
#[test]
fn test_above_range() {
let mut instance = BoundedInstance::new(5, 10);
instance.apply_inserts(&[(0, 0, 1), (1, 1, 2)]);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 1);
}
_ => {}
}
}
#[test]
fn test_dynamic_updates() {
let mut instance = BoundedInstance::new(0, 10);
instance.apply_inserts(&[(0, 0, 1), (1, 1, 2)]);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
_ => panic!("Expected ValueInRange"),
}
instance.apply_inserts(&[(2, 2, 0)]);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 2),
_ => panic!("Expected ValueInRange"),
}
}
#[test]
fn test_disconnected_graph() {
let mut instance = BoundedInstance::new(0, 10);
instance.apply_inserts(&[(0, 0, 1), (1, 2, 3)]);
match instance.query() {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 0);
}
_ => panic!("Expected ValueInRange with value 0"),
}
}
#[test]
fn test_certificate_tracking() {
let mut instance = BoundedInstance::new(0, 10);
instance.apply_inserts(&[(0, 0, 1), (1, 1, 2)]);
let _ = instance.query();
let cert = instance.certificate();
assert!(!cert.localkcut_responses.is_empty() || instance.vertices.len() < 20);
}
}