use crate::graph::{VertexId, Weight};
use std::collections::{HashMap, HashSet, VecDeque};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum EdgeColor {
Red,
Blue,
Green,
Yellow,
}
#[derive(Debug, Clone)]
pub struct EdgeColoring {
colors: HashMap<(VertexId, VertexId), EdgeColor>,
pub a: usize,
pub b: usize,
}
impl EdgeColoring {
pub fn new(a: usize, b: usize) -> Self {
Self {
colors: HashMap::new(),
a,
b,
}
}
pub fn get(&self, u: VertexId, v: VertexId) -> Option<EdgeColor> {
let key = if u < v { (u, v) } else { (v, u) };
self.colors.get(&key).copied()
}
pub fn set(&mut self, u: VertexId, v: VertexId, color: EdgeColor) {
let key = if u < v { (u, v) } else { (v, u) };
self.colors.insert(key, color);
}
pub fn has_color(&self, u: VertexId, v: VertexId, color: EdgeColor) -> bool {
self.get(u, v) == Some(color)
}
}
pub fn generate_coloring_family(a: usize, b: usize, num_edges: usize) -> Vec<EdgeColoring> {
let log_n = (num_edges.max(2) as f64).log2().ceil() as usize;
let family_size = (1 << (a.min(b) * (a + b).max(1).ilog2() as usize + 1)) * log_n;
let family_size = family_size.min(100);
let mut family = Vec::with_capacity(family_size);
for seed in 0..family_size {
let coloring = EdgeColoring::new(a, b);
family.push(coloring);
}
family
}
#[derive(Debug, Clone)]
pub struct GreedyForestPacking {
pub num_forests: usize,
edge_forest: HashMap<(VertexId, VertexId), usize>,
forests: Vec<HashSet<(VertexId, VertexId)>>,
forest_parents: Vec<HashMap<VertexId, VertexId>>,
}
impl GreedyForestPacking {
pub fn new(num_forests: usize) -> Self {
Self {
num_forests,
edge_forest: HashMap::new(),
forests: vec![HashSet::new(); num_forests],
forest_parents: vec![HashMap::new(); num_forests],
}
}
fn find_root(&mut self, forest_id: usize, v: VertexId) -> VertexId {
if !self.forest_parents[forest_id].contains_key(&v) {
self.forest_parents[forest_id].insert(v, v);
return v;
}
let parent = self.forest_parents[forest_id][&v];
if parent == v {
return v;
}
let root = self.find_root(forest_id, parent);
self.forest_parents[forest_id].insert(v, root);
root
}
fn union(&mut self, forest_id: usize, u: VertexId, v: VertexId) {
let root_u = self.find_root(forest_id, u);
let root_v = self.find_root(forest_id, v);
if root_u != root_v {
self.forest_parents[forest_id].insert(root_u, root_v);
}
}
fn would_create_cycle(&mut self, forest_id: usize, u: VertexId, v: VertexId) -> bool {
self.find_root(forest_id, u) == self.find_root(forest_id, v)
}
pub fn insert_edge(&mut self, u: VertexId, v: VertexId) -> Option<usize> {
let key = if u < v { (u, v) } else { (v, u) };
if self.edge_forest.contains_key(&key) {
return self.edge_forest.get(&key).copied();
}
for forest_id in 0..self.num_forests {
if !self.would_create_cycle(forest_id, u, v) {
self.forests[forest_id].insert(key);
self.edge_forest.insert(key, forest_id);
self.union(forest_id, u, v);
return Some(forest_id);
}
}
None
}
pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> Option<usize> {
let key = if u < v { (u, v) } else { (v, u) };
if let Some(forest_id) = self.edge_forest.remove(&key) {
self.forests[forest_id].remove(&key);
self.rebuild_forest_connectivity(forest_id);
return Some(forest_id);
}
None
}
fn rebuild_forest_connectivity(&mut self, forest_id: usize) {
self.forest_parents[forest_id].clear();
let edges: Vec<_> = self.forests[forest_id].iter().copied().collect();
for (u, v) in edges {
self.union(forest_id, u, v);
}
}
pub fn is_tree_edge(&self, u: VertexId, v: VertexId) -> bool {
let key = if u < v { (u, v) } else { (v, u) };
self.edge_forest.contains_key(&key)
}
pub fn get_forest(&self, u: VertexId, v: VertexId) -> Option<usize> {
let key = if u < v { (u, v) } else { (v, u) };
self.edge_forest.get(&key).copied()
}
pub fn forest_edges(&self, forest_id: usize) -> &HashSet<(VertexId, VertexId)> {
&self.forests[forest_id]
}
}
#[derive(Debug, Clone)]
pub struct LocalCut {
pub vertices: HashSet<VertexId>,
pub boundary: Vec<(VertexId, VertexId)>,
pub cut_value: f64,
pub volume: usize,
}
#[derive(Debug)]
pub struct DeterministicLocalKCut {
lambda_max: u64,
nu: usize,
beta: usize,
forests: GreedyForestPacking,
red_blue_colorings: Vec<EdgeColoring>,
green_yellow_colorings: Vec<EdgeColoring>,
adjacency: HashMap<VertexId, HashMap<VertexId, Weight>>,
edges: HashSet<(VertexId, VertexId)>,
}
impl DeterministicLocalKCut {
pub fn new(lambda_max: u64, nu: usize, beta: usize) -> Self {
let num_forests = ((6 * lambda_max) as usize).max(10);
let a_rb = 2 * beta;
let b_rb = nu;
let a_gy = 2 * beta - 1;
let b_gy = lambda_max as usize;
Self {
lambda_max,
nu,
beta,
forests: GreedyForestPacking::new(num_forests),
red_blue_colorings: generate_coloring_family(a_rb, b_rb, 1000),
green_yellow_colorings: generate_coloring_family(a_gy, b_gy, 1000),
adjacency: HashMap::new(),
edges: HashSet::new(),
}
}
pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) {
let key = if u < v { (u, v) } else { (v, u) };
if self.edges.contains(&key) {
return;
}
self.edges.insert(key);
self.adjacency.entry(u).or_default().insert(v, weight);
self.adjacency.entry(v).or_default().insert(u, weight);
if let Some(forest_id) = self.forests.insert_edge(u, v) {
for coloring in &mut self.red_blue_colorings {
let color = if (u + v + forest_id as u64) % 2 == 0 {
EdgeColor::Blue
} else {
EdgeColor::Red
};
coloring.set(u, v, color);
}
} else {
for coloring in &mut self.green_yellow_colorings {
let color = if (u * v) % 2 == 0 {
EdgeColor::Green
} else {
EdgeColor::Yellow
};
coloring.set(u, v, color);
}
}
}
pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
let key = if u < v { (u, v) } else { (v, u) };
if !self.edges.remove(&key) {
return;
}
if let Some(neighbors) = self.adjacency.get_mut(&u) {
neighbors.remove(&v);
}
if let Some(neighbors) = self.adjacency.get_mut(&v) {
neighbors.remove(&u);
}
self.forests.delete_edge(u, v);
}
pub fn query(&self, v: VertexId) -> Vec<LocalCut> {
let mut results = Vec::new();
let mut seen_cuts: HashSet<Vec<VertexId>> = HashSet::new();
for forest_id in 0..self.forests.num_forests {
for rb_coloring in &self.red_blue_colorings {
for gy_coloring in &self.green_yellow_colorings {
if let Some(cut) = self.color_coded_dfs(v, forest_id, rb_coloring, gy_coloring)
{
let mut sorted_vertices: Vec<_> = cut.vertices.iter().copied().collect();
sorted_vertices.sort();
if !seen_cuts.contains(&sorted_vertices)
&& cut.cut_value <= self.lambda_max as f64
{
seen_cuts.insert(sorted_vertices);
results.push(cut);
}
}
}
}
}
results
}
fn color_coded_dfs(
&self,
start: VertexId,
_forest_id: usize,
rb_coloring: &EdgeColoring,
gy_coloring: &EdgeColoring,
) -> Option<LocalCut> {
let mut visited = HashSet::new();
let mut stack = vec![start];
let mut volume = 0usize;
let mut boundary = Vec::new();
while let Some(u) = stack.pop() {
if visited.contains(&u) {
continue;
}
visited.insert(u);
if let Some(neighbors) = self.adjacency.get(&u) {
volume += neighbors.len();
if volume > self.nu {
return None;
}
for (&v, &_weight) in neighbors {
let is_tree_edge = self.forests.is_tree_edge(u, v);
if is_tree_edge {
if rb_coloring.has_color(u, v, EdgeColor::Blue) {
if !visited.contains(&v) {
stack.push(v);
}
} else {
boundary.push((u, v));
}
} else {
if gy_coloring.has_color(u, v, EdgeColor::Green) {
if !visited.contains(&v) {
stack.push(v);
}
} else {
if !visited.contains(&v) {
boundary.push((u, v));
}
}
}
}
}
}
let cut_value: f64 = boundary
.iter()
.map(|&(u, v)| {
self.adjacency
.get(&u)
.and_then(|n| n.get(&v))
.copied()
.unwrap_or(1.0)
})
.sum();
Some(LocalCut {
vertices: visited,
boundary,
cut_value,
volume,
})
}
pub fn vertices(&self) -> Vec<VertexId> {
self.adjacency.keys().copied().collect()
}
pub fn neighbors(&self, v: VertexId) -> Vec<(VertexId, Weight)> {
self.adjacency
.get(&v)
.map(|n| n.iter().map(|(&v, &w)| (v, w)).collect())
.unwrap_or_default()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_forest_packing_basic() {
let mut packing = GreedyForestPacking::new(3);
assert!(packing.insert_edge(1, 2).is_some());
assert!(packing.insert_edge(2, 3).is_some());
assert!(packing.insert_edge(3, 4).is_some());
assert!(packing.is_tree_edge(1, 2));
assert!(packing.is_tree_edge(2, 3));
}
#[test]
fn test_forest_packing_cycle() {
let mut packing = GreedyForestPacking::new(3);
packing.insert_edge(1, 2);
packing.insert_edge(2, 3);
let forest = packing.insert_edge(1, 3);
assert!(forest.is_some());
}
#[test]
fn test_localkcut_query() {
let mut lkc = DeterministicLocalKCut::new(10, 100, 2);
lkc.insert_edge(1, 2, 1.0);
lkc.insert_edge(2, 3, 1.0);
lkc.insert_edge(3, 4, 1.0);
let cuts = lkc.query(1);
assert!(!cuts.is_empty());
assert!(cuts.iter().any(|c| c.vertices.contains(&1)));
}
#[test]
fn test_coloring_family() {
let family = generate_coloring_family(2, 5, 100);
assert!(!family.is_empty());
}
#[test]
fn test_edge_deletion() {
let mut lkc = DeterministicLocalKCut::new(10, 100, 2);
lkc.insert_edge(1, 2, 1.0);
lkc.insert_edge(2, 3, 1.0);
lkc.delete_edge(1, 2);
assert!(!lkc.forests.is_tree_edge(1, 2));
}
}