use super::witness::WitnessHandle;
use super::{InstanceResult, ProperCutInstance};
use crate::graph::{DynamicGraph, EdgeId, VertexId};
use roaring::RoaringBitmap;
use std::collections::{HashMap, HashSet, VecDeque};
pub struct StubInstance {
lambda_min: u64,
lambda_max: u64,
edges: Vec<(VertexId, VertexId, EdgeId)>,
vertices: HashSet<VertexId>,
adjacency: HashMap<VertexId, Vec<(VertexId, EdgeId)>>,
}
impl StubInstance {
pub fn new(graph: &DynamicGraph, lambda_min: u64, lambda_max: u64) -> Self {
let mut instance = Self {
lambda_min,
lambda_max,
edges: Vec::new(),
vertices: HashSet::new(),
adjacency: HashMap::new(),
};
for edge in graph.edges() {
instance.vertices.insert(edge.source);
instance.vertices.insert(edge.target);
instance.edges.push((edge.source, edge.target, edge.id));
}
instance.rebuild_adjacency();
instance
}
pub fn new_empty(lambda_min: u64, lambda_max: u64) -> Self {
Self {
lambda_min,
lambda_max,
edges: Vec::new(),
vertices: HashSet::new(),
adjacency: HashMap::new(),
}
}
fn compute_min_cut(&self) -> Option<(u64, WitnessHandle)> {
if self.vertices.is_empty() {
return None;
}
let n = self.vertices.len();
if n == 1 {
return None;
}
if n >= 20 {
return None;
}
if !self.is_connected() {
let membership =
RoaringBitmap::from_iter(self.vertices.iter().take(1).map(|&v| v as u32));
let seed = *self.vertices.iter().next().unwrap();
let witness = WitnessHandle::new(seed, membership, 0);
return Some((0, witness));
}
let vertex_vec: Vec<VertexId> = self.vertices.iter().copied().collect();
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, &vertex) in vertex_vec.iter().enumerate() {
if mask & (1 << i) != 0 {
subset.insert(vertex);
}
}
if !self.is_connected_set(&subset) {
continue;
}
let (boundary_value, _boundary_edges) = self.compute_boundary(&subset);
if boundary_value < min_cut {
min_cut = boundary_value;
best_set = subset.clone();
}
}
if min_cut == u64::MAX {
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_connected_set(&self, vertices: &HashSet<VertexId>) -> bool {
if vertices.is_empty() {
return true;
}
let start = *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, _edge_id) in neighbors {
if vertices.contains(&neighbor) && visited.insert(neighbor) {
queue.push_back(neighbor);
}
}
}
}
visited.len() == vertices.len()
}
fn compute_boundary(&self, set: &HashSet<VertexId>) -> (u64, Vec<EdgeId>) {
let mut boundary_value = 0u64;
let mut boundary_edges = Vec::new();
for &(u, v, edge_id) in &self.edges {
let u_in_set = set.contains(&u);
let v_in_set = set.contains(&v);
if u_in_set != v_in_set {
boundary_value += 1;
boundary_edges.push(edge_id);
}
}
(boundary_value, boundary_edges)
}
fn is_connected(&self) -> bool {
self.is_connected_set(&self.vertices)
}
fn rebuild_adjacency(&mut self) {
self.adjacency.clear();
for &(u, v, edge_id) in &self.edges {
self.adjacency
.entry(u)
.or_insert_with(Vec::new)
.push((v, edge_id));
self.adjacency
.entry(v)
.or_insert_with(Vec::new)
.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((u, v, edge_id));
self.rebuild_adjacency();
}
fn delete(&mut self, edge_id: EdgeId, _u: VertexId, _v: VertexId) {
self.edges.retain(|(_, _, eid)| *eid != edge_id);
self.rebuild_adjacency();
}
}
impl ProperCutInstance for StubInstance {
fn init(_graph: &DynamicGraph, lambda_min: u64, lambda_max: u64) -> Self {
Self::new_empty(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 {
match self.compute_min_cut() {
Some((value, witness)) if value <= self.lambda_max => {
InstanceResult::ValueInRange { value, witness }
}
_ => InstanceResult::AboveRange,
}
}
fn bounds(&self) -> (u64, u64) {
(self.lambda_min, self.lambda_max)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::graph::DynamicGraph;
#[test]
fn test_empty_graph() {
let graph = DynamicGraph::new();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
assert!(matches!(result, InstanceResult::AboveRange));
}
#[test]
fn test_single_vertex() {
let graph = DynamicGraph::new();
graph.add_vertex(1);
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
assert!(matches!(result, InstanceResult::AboveRange));
}
#[test]
fn test_path_graph() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 1);
}
_ => panic!("Expected ValueInRange result"),
}
}
#[test]
fn test_cycle_graph() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
graph.insert_edge(3, 1, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 2);
}
_ => panic!("Expected ValueInRange result"),
}
}
#[test]
fn test_complete_graph_k4() {
let graph = DynamicGraph::new();
for i in 1..=4 {
for j in i + 1..=4 {
graph.insert_edge(i, j, 1.0).unwrap();
}
}
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 3);
}
_ => panic!("Expected ValueInRange result"),
}
}
#[test]
fn test_disconnected_graph() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(3, 4, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 0);
}
_ => panic!("Expected ValueInRange with value 0"),
}
}
#[test]
fn test_bridge_graph() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
graph.insert_edge(3, 1, 1.0).unwrap();
graph.insert_edge(3, 4, 1.0).unwrap(); graph.insert_edge(4, 5, 1.0).unwrap();
graph.insert_edge(5, 6, 1.0).unwrap();
graph.insert_edge(6, 4, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => {
assert_eq!(value, 1);
}
_ => panic!("Expected ValueInRange result"),
}
}
#[test]
fn test_is_connected_set() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
graph.insert_edge(3, 4, 1.0).unwrap();
let instance = StubInstance::new(&graph, 0, 10);
let mut subset = HashSet::new();
subset.insert(1);
subset.insert(2);
subset.insert(3);
assert!(instance.is_connected_set(&subset));
let mut subset = HashSet::new();
subset.insert(1);
subset.insert(4);
assert!(!instance.is_connected_set(&subset));
let mut subset = HashSet::new();
subset.insert(1);
assert!(instance.is_connected_set(&subset));
}
#[test]
fn test_compute_boundary() {
let graph = DynamicGraph::new();
let e1 = graph.insert_edge(1, 2, 1.0).unwrap();
let e2 = graph.insert_edge(2, 3, 1.0).unwrap();
let _e3 = graph.insert_edge(3, 4, 1.0).unwrap();
let instance = StubInstance::new(&graph, 0, 10);
let mut set = HashSet::new();
set.insert(1);
set.insert(2);
let (value, edges) = instance.compute_boundary(&set);
assert_eq!(value, 1); assert_eq!(edges.len(), 1);
assert!(edges.contains(&e2));
let mut set = HashSet::new();
set.insert(2);
let (value, edges) = instance.compute_boundary(&set);
assert_eq!(value, 2); assert_eq!(edges.len(), 2);
assert!(edges.contains(&e1));
assert!(edges.contains(&e2));
}
#[test]
fn test_dynamic_updates() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
_ => panic!("Expected ValueInRange"),
}
let e3_id = 100; instance.apply_inserts(&[(e3_id, 3, 1)]);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 2),
_ => panic!("Expected ValueInRange"),
}
instance.apply_deletes(&[(e3_id, 3, 1)]);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
_ => panic!("Expected ValueInRange"),
}
}
#[test]
fn test_range_bounds() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 2, 5);
let result = instance.query();
let mut instance = StubInstance::new(&graph, 0, 1);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, .. } => assert_eq!(value, 1),
_ => panic!("Expected ValueInRange"),
}
let mut instance = StubInstance::new(&graph, 0, 0);
let result = instance.query();
assert!(matches!(result, InstanceResult::AboveRange));
}
#[test]
fn test_witness_information() {
let graph = DynamicGraph::new();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let mut instance = StubInstance::new(&graph, 0, 10);
let result = instance.query();
match result {
InstanceResult::ValueInRange { value, witness } => {
assert_eq!(value, 1);
assert_eq!(witness.boundary_size(), 1);
assert!(witness.cardinality() > 0);
assert!(witness.cardinality() < 3); }
_ => panic!("Expected ValueInRange with witness"),
}
}
}