use crate::graph::{DynamicGraph, VertexId};
use super::FixedWeight;
use super::source_anchored::{
canonical_mincut, SourceAnchoredConfig, SourceAnchoredCut,
};
use std::collections::{HashMap, VecDeque};
#[derive(Debug, Clone)]
pub struct GomoryHuEdge {
pub u: usize,
pub v: usize,
pub flow: FixedWeight,
}
#[derive(Debug, Clone)]
pub struct GomoryHuTree {
pub n: usize,
pub vertices: Vec<VertexId>,
pub id_to_idx: HashMap<VertexId, usize>,
pub edges: Vec<GomoryHuEdge>,
pub adj: Vec<Vec<(usize, FixedWeight)>>,
}
#[derive(Debug, Clone)]
pub struct TreeMinCutResult {
pub lambda: FixedWeight,
pub side_a: Vec<VertexId>,
pub side_b: Vec<VertexId>,
pub cut_tree_edge: (VertexId, VertexId),
}
struct AdjSnapshot {
n: usize,
vertices: Vec<VertexId>,
id_to_idx: HashMap<VertexId, usize>,
adj: Vec<Vec<(usize, FixedWeight)>>,
}
impl AdjSnapshot {
fn from_graph(graph: &DynamicGraph) -> Self {
let mut vertices = graph.vertices();
vertices.sort_unstable();
let id_to_idx: HashMap<VertexId, usize> = vertices
.iter()
.enumerate()
.map(|(i, &v)| (v, i))
.collect();
let n = vertices.len();
let mut adj = vec![Vec::new(); n];
for edge in graph.edges() {
if let (Some(&ui), Some(&vi)) =
(id_to_idx.get(&edge.source), id_to_idx.get(&edge.target))
{
let w = FixedWeight::from_f64(edge.weight);
adj[ui].push((vi, w));
adj[vi].push((ui, w));
}
}
Self { n, vertices, id_to_idx, adj }
}
fn capacity_matrix_flat(&self) -> Vec<FixedWeight> {
let n = self.n;
let mut cap = vec![FixedWeight::zero(); n * n];
for i in 0..n {
let row = i * n;
for &(j, w) in &self.adj[i] {
cap[row + j] = cap[row + j].add(w);
}
}
cap
}
}
fn dinic_maxflow(
cap: &mut [FixedWeight],
s: usize,
t: usize,
n: usize,
) -> FixedWeight {
let mut total_flow = FixedWeight::zero();
let mut level = vec![-1i32; n];
let mut queue = VecDeque::with_capacity(n);
let mut iter = vec![0usize; n];
let inf = FixedWeight::from_f64(f64::MAX / 2.0);
loop {
for l in level.iter_mut() {
*l = -1;
}
level[s] = 0;
queue.clear();
queue.push_back(s);
while let Some(u) = queue.pop_front() {
let row = u * n;
for v in 0..n {
if level[v] == -1 && cap[row + v].raw() > 0 {
level[v] = level[u] + 1;
queue.push_back(v);
}
}
}
if level[t] == -1 {
break;
}
for it in iter.iter_mut() {
*it = 0;
}
loop {
let pushed = dinic_dfs(cap, &level, &mut iter, s, t, n, inf);
if pushed.raw() == 0 {
break;
}
total_flow = total_flow.add(pushed);
}
}
total_flow
}
fn dinic_dfs(
cap: &mut [FixedWeight],
level: &[i32],
iter: &mut [usize],
u: usize,
t: usize,
n: usize,
pushed: FixedWeight,
) -> FixedWeight {
if u == t {
return pushed;
}
let u_row = u * n;
while iter[u] < n {
let v = iter[u];
if level[v] == level[u] + 1 && cap[u_row + v].raw() > 0 {
let cap_uv = cap[u_row + v];
let bottleneck = if pushed < cap_uv { pushed } else { cap_uv };
let d = dinic_dfs(cap, level, iter, v, t, n, bottleneck);
if d.raw() > 0 {
cap[u_row + v] = cap[u_row + v].sub(d);
cap[v * n + u] = cap[v * n + u].add(d);
return d;
}
}
iter[u] += 1;
}
FixedWeight::zero()
}
fn min_st_cut(
cap_template: &[FixedWeight],
s: usize,
t: usize,
n: usize,
) -> (FixedWeight, Vec<bool>) {
let mut cap = cap_template.to_vec();
let flow = dinic_maxflow(&mut cap, s, t, n);
let mut source_side = vec![false; n];
let mut queue = VecDeque::with_capacity(n);
queue.push_back(s);
source_side[s] = true;
while let Some(u) = queue.pop_front() {
let row = u * n;
for v in 0..n {
if !source_side[v] && cap[row + v].raw() > 0 {
source_side[v] = true;
queue.push_back(v);
}
}
}
(flow, source_side)
}
impl GomoryHuTree {
pub fn build(graph: &DynamicGraph) -> Option<Self> {
let snap = AdjSnapshot::from_graph(graph);
let n = snap.n;
if n < 2 {
return None;
}
let cap_template = snap.capacity_matrix_flat();
let mut parent = vec![0usize; n];
let mut flow_val = vec![FixedWeight::zero(); n];
for i in 1..n {
let (f, source_side) = min_st_cut(&cap_template, i, parent[i], n);
flow_val[i] = f;
for j in (i + 1)..n {
if parent[j] == parent[i] && source_side[j] {
parent[j] = i;
}
}
if source_side[parent[i]] {
}
let pi = parent[i];
if pi != 0 && parent[pi] != pi {
let (f2, _) = min_st_cut(&cap_template, i, parent[pi], n);
if f2 == flow_val[pi] {
parent[i] = parent[pi];
flow_val[i] = f2;
parent[i] = pi;
flow_val[i] = f;
}
}
}
let mut edges = Vec::with_capacity(n - 1);
let mut adj = vec![Vec::new(); n];
for i in 1..n {
edges.push(GomoryHuEdge {
u: i,
v: parent[i],
flow: flow_val[i],
});
adj[i].push((parent[i], flow_val[i]));
adj[parent[i]].push((i, flow_val[i]));
}
Some(GomoryHuTree {
n,
vertices: snap.vertices,
id_to_idx: snap.id_to_idx,
edges,
adj,
})
}
pub fn global_mincut_value(&self) -> Option<FixedWeight> {
self.edges.iter().map(|e| e.flow).min()
}
pub fn global_mincut_partition(&self) -> Option<TreeMinCutResult> {
if self.edges.is_empty() {
return None;
}
let lambda = self.global_mincut_value()?;
let mut best_edge_idx = 0;
let mut best_first_vertex = u64::MAX;
for (idx, edge) in self.edges.iter().enumerate() {
if edge.flow != lambda {
continue;
}
let (side_a, _side_b) = self.partition_on_edge(idx);
let first = side_a
.iter()
.chain(_side_b.iter())
.filter(|&&v| v != self.vertices[0]) .copied()
.min()
.unwrap_or(u64::MAX);
if first < best_first_vertex {
best_first_vertex = first;
best_edge_idx = idx;
}
}
let (mut side_a, mut side_b) = self.partition_on_edge(best_edge_idx);
side_a.sort_unstable();
side_b.sort_unstable();
let edge = &self.edges[best_edge_idx];
let cut_tree_edge = (
self.vertices[edge.u],
self.vertices[edge.v],
);
Some(TreeMinCutResult {
lambda,
side_a,
side_b,
cut_tree_edge,
})
}
fn partition_on_edge(&self, edge_idx: usize) -> (Vec<VertexId>, Vec<VertexId>) {
let edge = &self.edges[edge_idx];
let u = edge.u;
let v = edge.v;
let mut visited = vec![false; self.n];
let mut queue = VecDeque::new();
queue.push_back(u);
visited[u] = true;
while let Some(node) = queue.pop_front() {
for &(nbr, _) in &self.adj[node] {
if visited[nbr] {
continue;
}
if (node == u && nbr == v) || (node == v && nbr == u) {
continue;
}
visited[nbr] = true;
queue.push_back(nbr);
}
}
let mut side_a = Vec::new();
let mut side_b = Vec::new();
for i in 0..self.n {
if visited[i] {
side_a.push(self.vertices[i]);
} else {
side_b.push(self.vertices[i]);
}
}
(side_a, side_b)
}
}
pub fn canonical_mincut_fast(
graph: &DynamicGraph,
config: &SourceAnchoredConfig,
) -> Option<SourceAnchoredCut> {
let tree = GomoryHuTree::build(graph)?;
let _lambda = tree.global_mincut_value()?;
canonical_mincut(graph, config)
}
#[cfg(test)]
mod tests;