use crate::connectivity::DynamicConnectivity;
use crate::graph::{DynamicGraph, EdgeId, VertexId};
use crate::instance::WitnessHandle;
use roaring::RoaringBitmap;
use std::collections::{HashMap, HashSet, VecDeque};
use std::sync::Arc;
#[derive(Debug, Clone)]
pub struct Fragment {
pub id: u64,
pub vertices: HashSet<VertexId>,
pub edges: Vec<EdgeId>,
pub min_cut: u64,
pub witness: Option<WitnessHandle>,
}
#[derive(Debug, Clone)]
pub enum FragmentResult {
Connected {
min_cut: u64,
witness: WitnessHandle,
},
Disconnected {
fragments: Vec<Fragment>,
global_min_cut: u64,
},
}
pub struct FragmentingAlgorithm {
graph: Arc<DynamicGraph>,
fragments: Vec<Fragment>,
vertex_fragment: HashMap<VertexId, u64>,
next_id: u64,
connectivity: DynamicConnectivity,
}
impl FragmentingAlgorithm {
pub fn new(graph: Arc<DynamicGraph>) -> Self {
let mut alg = Self {
graph,
fragments: Vec::new(),
vertex_fragment: HashMap::new(),
next_id: 0,
connectivity: DynamicConnectivity::new(),
};
alg.rebuild();
alg
}
pub fn rebuild(&mut self) {
self.fragments.clear();
self.vertex_fragment.clear();
self.next_id = 0;
self.connectivity = DynamicConnectivity::new();
for edge in self.graph.edges() {
self.connectivity.insert_edge(edge.source, edge.target);
}
let components = self.find_connected_components();
for component in components {
self.create_fragment(component);
}
}
fn find_connected_components(&self) -> Vec<HashSet<VertexId>> {
let mut visited = HashSet::new();
let mut components = Vec::new();
for vertex in self.graph.vertices() {
if !visited.contains(&vertex) {
let component = self.bfs_component(vertex, &mut visited);
if !component.is_empty() {
components.push(component);
}
}
}
components
}
fn bfs_component(&self, start: VertexId, visited: &mut HashSet<VertexId>) -> HashSet<VertexId> {
let mut component = HashSet::new();
let mut queue = VecDeque::new();
queue.push_back(start);
visited.insert(start);
component.insert(start);
while let Some(current) = queue.pop_front() {
for (neighbor, _edge_id) in self.graph.neighbors(current) {
if visited.insert(neighbor) {
queue.push_back(neighbor);
component.insert(neighbor);
}
}
}
component
}
fn create_fragment(&mut self, vertices: HashSet<VertexId>) {
let fragment_id = self.next_id;
self.next_id += 1;
let edges: Vec<EdgeId> = self
.graph
.edges()
.into_iter()
.filter(|e| vertices.contains(&e.source) && vertices.contains(&e.target))
.map(|e| e.id)
.collect();
let (min_cut, witness) = self.compute_fragment_min_cut(&vertices);
let fragment = Fragment {
id: fragment_id,
vertices: vertices.clone(),
edges,
min_cut,
witness,
};
for &v in &vertices {
self.vertex_fragment.insert(v, fragment_id);
}
self.fragments.push(fragment);
}
fn compute_fragment_min_cut(
&self,
vertices: &HashSet<VertexId>,
) -> (u64, Option<WitnessHandle>) {
if vertices.len() <= 1 {
return (u64::MAX, None);
}
if vertices.len() < 20 {
return self.brute_force_min_cut(vertices);
}
self.heuristic_min_cut(vertices)
}
fn brute_force_min_cut(&self, vertices: &HashSet<VertexId>) -> (u64, Option<WitnessHandle>) {
let vertex_vec: Vec<_> = vertices.iter().copied().collect();
let n = vertex_vec.len();
if n >= 20 {
return (u64::MAX, 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<VertexId> = HashSet::new();
for (i, &v) in vertex_vec.iter().enumerate() {
if mask & (1 << i) != 0 {
subset.insert(v);
}
}
if !self.is_connected_within(&subset, vertices) {
continue;
}
let boundary = self.compute_boundary_within(&subset, vertices);
if boundary < min_cut {
min_cut = boundary;
best_set = subset;
}
}
if min_cut == u64::MAX || best_set.is_empty() {
return (u64::MAX, 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);
(min_cut, Some(witness))
}
fn heuristic_min_cut(&self, vertices: &HashSet<VertexId>) -> (u64, Option<WitnessHandle>) {
let mut min_degree = u64::MAX;
let mut min_vertex = None;
for &v in vertices {
let degree = self
.graph
.neighbors(v)
.into_iter()
.filter(|(n, _)| vertices.contains(n))
.count() as u64;
if degree < min_degree {
min_degree = degree;
min_vertex = Some(v);
}
}
if let Some(v) = min_vertex {
let mut membership = RoaringBitmap::new();
membership.insert(v as u32);
let witness = WitnessHandle::new(v, membership, min_degree);
return (min_degree, Some(witness));
}
(u64::MAX, None)
}
fn is_connected_within(
&self,
subset: &HashSet<VertexId>,
fragment: &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() {
for (neighbor, _edge_id) in self.graph.neighbors(current) {
if subset.contains(&neighbor)
&& fragment.contains(&neighbor)
&& visited.insert(neighbor)
{
queue.push_back(neighbor);
}
}
}
visited.len() == subset.len()
}
fn compute_boundary_within(
&self,
subset: &HashSet<VertexId>,
fragment: &HashSet<VertexId>,
) -> u64 {
let mut boundary = 0u64;
for edge in self.graph.edges().into_iter() {
if !fragment.contains(&edge.source) || !fragment.contains(&edge.target) {
continue;
}
let src_in = subset.contains(&edge.source);
let tgt_in = subset.contains(&edge.target);
if src_in != tgt_in {
boundary += 1;
}
}
boundary
}
pub fn insert_edge(&mut self, _edge_id: EdgeId, u: VertexId, v: VertexId) {
self.connectivity.insert_edge(u, v);
let u_frag = self.vertex_fragment.get(&u).copied();
let v_frag = self.vertex_fragment.get(&v).copied();
match (u_frag, v_frag) {
(Some(uf), Some(vf)) if uf != vf => {
self.merge_fragments(uf, vf);
}
(None, Some(_)) | (Some(_), None) | (None, None) => {
self.rebuild();
}
_ => {
self.update_fragment_containing(u);
}
}
}
pub fn delete_edge(&mut self, _edge_id: EdgeId, u: VertexId, v: VertexId) {
self.connectivity.delete_edge(u, v);
if !self.connectivity.connected(u, v) {
self.rebuild();
} else {
self.update_fragment_containing(u);
}
}
fn merge_fragments(&mut self, _frag1_id: u64, _frag2_id: u64) {
self.rebuild();
}
fn update_fragment_containing(&mut self, v: VertexId) {
if let Some(&frag_id) = self.vertex_fragment.get(&v) {
let vertices = self
.fragments
.iter()
.find(|f| f.id == frag_id)
.map(|f| f.vertices.clone());
if let Some(vertices) = vertices {
let (min_cut, witness) = self.compute_fragment_min_cut(&vertices);
if let Some(frag) = self.fragments.iter_mut().find(|f| f.id == frag_id) {
frag.min_cut = min_cut;
frag.witness = witness;
}
}
}
}
pub fn query(&self) -> FragmentResult {
if self.fragments.len() <= 1 {
if let Some(frag) = self.fragments.first() {
if let Some(ref witness) = frag.witness {
return FragmentResult::Connected {
min_cut: frag.min_cut,
witness: witness.clone(),
};
}
}
let mut membership = RoaringBitmap::new();
membership.insert(0);
return FragmentResult::Connected {
min_cut: u64::MAX,
witness: WitnessHandle::new(0, membership, u64::MAX),
};
}
FragmentResult::Disconnected {
fragments: self.fragments.clone(),
global_min_cut: 0,
}
}
pub fn num_fragments(&self) -> usize {
self.fragments.len()
}
pub fn is_connected(&self) -> bool {
self.fragments.len() <= 1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_empty_graph() {
let graph = Arc::new(DynamicGraph::new());
let alg = FragmentingAlgorithm::new(graph);
assert_eq!(alg.num_fragments(), 0);
}
#[test]
fn test_connected_graph() {
let graph = Arc::new(DynamicGraph::new());
graph.insert_edge(0, 1, 1.0).unwrap();
graph.insert_edge(1, 2, 1.0).unwrap();
let alg = FragmentingAlgorithm::new(graph);
assert_eq!(alg.num_fragments(), 1);
assert!(alg.is_connected());
}
#[test]
fn test_disconnected_graph() {
let graph = Arc::new(DynamicGraph::new());
graph.insert_edge(0, 1, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let alg = FragmentingAlgorithm::new(graph);
assert_eq!(alg.num_fragments(), 2);
assert!(!alg.is_connected());
}
#[test]
fn test_dynamic_split() {
let graph = Arc::new(DynamicGraph::new());
let _e1 = graph.insert_edge(0, 1, 1.0).unwrap();
let e2 = graph.insert_edge(1, 2, 1.0).unwrap();
let mut alg = FragmentingAlgorithm::new(Arc::clone(&graph));
assert!(alg.is_connected());
graph.delete_edge(1, 2).unwrap();
alg.delete_edge(e2, 1, 2);
assert!(!alg.is_connected());
assert_eq!(alg.num_fragments(), 2);
}
#[test]
fn test_dynamic_merge() {
let graph = Arc::new(DynamicGraph::new());
graph.insert_edge(0, 1, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let mut alg = FragmentingAlgorithm::new(Arc::clone(&graph));
assert!(!alg.is_connected());
let bridge = graph.insert_edge(1, 2, 1.0).unwrap();
alg.insert_edge(bridge, 1, 2);
assert!(alg.is_connected());
assert_eq!(alg.num_fragments(), 1);
}
#[test]
fn test_query_connected() {
let graph = Arc::new(DynamicGraph::new());
graph.insert_edge(0, 1, 1.0).unwrap();
graph.insert_edge(1, 2, 1.0).unwrap();
graph.insert_edge(2, 0, 1.0).unwrap();
let alg = FragmentingAlgorithm::new(graph);
match alg.query() {
FragmentResult::Connected { min_cut, .. } => {
assert_eq!(min_cut, 2); }
_ => panic!("Expected connected result"),
}
}
#[test]
fn test_query_disconnected() {
let graph = Arc::new(DynamicGraph::new());
graph.insert_edge(0, 1, 1.0).unwrap();
graph.insert_edge(2, 3, 1.0).unwrap();
let alg = FragmentingAlgorithm::new(graph);
match alg.query() {
FragmentResult::Disconnected { global_min_cut, .. } => {
assert_eq!(global_min_cut, 0);
}
_ => panic!("Expected disconnected result"),
}
}
}