use crate::graph::{DynamicGraph, VertexId, Weight};
use crate::time_compat::PortableTimestamp;
use super::FixedWeight;
use std::collections::{HashMap, VecDeque};
#[derive(Debug, Clone)]
pub struct SourceAnchoredCut {
pub lambda: FixedWeight,
pub source_vertex: VertexId,
pub first_separable_vertex: VertexId,
pub side_vertices: Vec<VertexId>,
pub side_size: usize,
pub priority_sum: u64,
pub cut_edges: Vec<(VertexId, VertexId)>,
pub cut_hash: [u8; 32],
}
#[derive(Debug, Clone)]
pub struct SourceAnchoredConfig {
pub source: Option<VertexId>,
pub vertex_order: Option<Vec<VertexId>>,
pub vertex_priorities: Option<Vec<(VertexId, u64)>>,
}
impl Default for SourceAnchoredConfig {
fn default() -> Self {
Self {
source: None,
vertex_order: None,
vertex_priorities: None,
}
}
}
#[derive(Debug, Clone)]
pub struct SourceAnchoredReceipt {
pub epoch: u64,
pub cut_hash: [u8; 32],
pub lambda: FixedWeight,
pub source_vertex: VertexId,
pub first_separable_vertex: VertexId,
pub side_size: usize,
pub priority_sum: u64,
pub timestamp_ns: u64,
}
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 global_mincut_value(&self) -> Option<FixedWeight> {
let n = self.n;
if n < 2 {
return None;
}
let mut w = vec![FixedWeight::zero(); n * n];
for i in 0..n {
let row = i * n;
for &(j, wt) in &self.adj[i] {
w[row + j] = w[row + j].add(wt);
}
}
let mut active: Vec<bool> = vec![true; n];
let mut active_list: Vec<usize> = (0..n).collect();
let mut n_active = n;
let mut global_min = FixedWeight::from_f64(f64::MAX / 2.0);
let mut key = vec![FixedWeight::zero(); n];
let mut in_a = vec![false; n];
for _phase in 0..(n - 1) {
if n_active <= 1 {
break;
}
for k in 0..n_active {
let j = active_list[k];
in_a[j] = false;
key[j] = FixedWeight::zero();
}
let first = active_list[0];
in_a[first] = true;
let first_row = first * n;
for k in 0..n_active {
let j = active_list[k];
key[j] = w[first_row + j];
}
let mut prev = first;
let mut last = first;
for _step in 1..n_active {
let mut best = usize::MAX;
let mut best_key = FixedWeight::zero();
let mut found = false;
for k in 0..n_active {
let j = active_list[k];
if !in_a[j] && (!found || key[j] > best_key) {
best_key = key[j];
best = j;
found = true;
}
}
if !found {
break;
}
in_a[best] = true;
prev = last;
last = best;
let best_row = best * n;
for k in 0..n_active {
let j = active_list[k];
if !in_a[j] {
key[j] = key[j].add(w[best_row + j]);
}
}
}
let cut_value = key[last];
if cut_value < global_min {
global_min = cut_value;
}
let prev_row = prev * n;
let last_row = last * n;
for k in 0..n_active {
let j = active_list[k];
if j != last {
w[prev_row + j] = w[prev_row + j].add(w[last_row + j]);
w[j * n + prev] = w[j * n + prev].add(w[j * n + last]);
}
}
active[last] = false;
if let Some(pos) = active_list[..n_active].iter().position(|&x| x == last) {
active_list.swap(pos, n_active - 1);
}
n_active -= 1;
}
Some(global_min)
}
fn min_st_cut(
&self,
s_idx: usize,
t_idx: usize,
_priority: &[u64],
) -> (FixedWeight, Vec<bool>) {
let n = self.n;
debug_assert!(s_idx < n && t_idx < n && s_idx != t_idx);
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);
}
}
let flow = dinic_maxflow(&mut cap, s_idx, t_idx, n);
let mut source_side = vec![false; n];
let mut queue = VecDeque::with_capacity(n);
queue.push_back(s_idx);
source_side[s_idx] = 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)
}
}
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 stable_cut_hash(
lambda: FixedWeight,
source: VertexId,
first_v: VertexId,
side: &[VertexId],
priority_sum: u64,
) -> [u8; 32] {
let mut data = Vec::with_capacity(40 + side.len() * 8);
data.extend_from_slice(&lambda.raw().to_le_bytes());
data.extend_from_slice(&source.to_le_bytes());
data.extend_from_slice(&first_v.to_le_bytes());
data.extend_from_slice(&priority_sum.to_le_bytes());
data.extend_from_slice(&(side.len() as u64).to_le_bytes());
for &v in side {
data.extend_from_slice(&v.to_le_bytes());
}
sha256(&data)
}
fn sha256(data: &[u8]) -> [u8; 32] {
const K: [u32; 64] = [
0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
];
let mut h: [u32; 8] = [
0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
];
let bit_len = (data.len() as u64) * 8;
let mut msg = data.to_vec();
msg.push(0x80);
while (msg.len() % 64) != 56 {
msg.push(0);
}
msg.extend_from_slice(&bit_len.to_be_bytes());
for chunk in msg.chunks_exact(64) {
let mut w = [0u32; 64];
for i in 0..16 {
w[i] = u32::from_be_bytes([
chunk[i * 4],
chunk[i * 4 + 1],
chunk[i * 4 + 2],
chunk[i * 4 + 3],
]);
}
for i in 16..64 {
let s0 = w[i - 15].rotate_right(7) ^ w[i - 15].rotate_right(18) ^ (w[i - 15] >> 3);
let s1 = w[i - 2].rotate_right(17) ^ w[i - 2].rotate_right(19) ^ (w[i - 2] >> 10);
w[i] = w[i - 16]
.wrapping_add(s0)
.wrapping_add(w[i - 7])
.wrapping_add(s1);
}
let [mut a, mut b, mut c, mut d, mut e, mut f, mut g, mut hh] = h;
for i in 0..64 {
let s1 = e.rotate_right(6) ^ e.rotate_right(11) ^ e.rotate_right(25);
let ch = (e & f) ^ ((!e) & g);
let temp1 = hh
.wrapping_add(s1)
.wrapping_add(ch)
.wrapping_add(K[i])
.wrapping_add(w[i]);
let s0 = a.rotate_right(2) ^ a.rotate_right(13) ^ a.rotate_right(22);
let maj = (a & b) ^ (a & c) ^ (b & c);
let temp2 = s0.wrapping_add(maj);
hh = g;
g = f;
f = e;
e = d.wrapping_add(temp1);
d = c;
c = b;
b = a;
a = temp1.wrapping_add(temp2);
}
h[0] = h[0].wrapping_add(a);
h[1] = h[1].wrapping_add(b);
h[2] = h[2].wrapping_add(c);
h[3] = h[3].wrapping_add(d);
h[4] = h[4].wrapping_add(e);
h[5] = h[5].wrapping_add(f);
h[6] = h[6].wrapping_add(g);
h[7] = h[7].wrapping_add(hh);
}
let mut out = [0u8; 32];
for (i, &val) in h.iter().enumerate() {
out[i * 4..i * 4 + 4].copy_from_slice(&val.to_be_bytes());
}
out
}
pub fn canonical_mincut(
graph: &DynamicGraph,
config: &SourceAnchoredConfig,
) -> Option<SourceAnchoredCut> {
let snap = AdjSnapshot::from_graph(graph);
if snap.n < 2 {
return None;
}
let source = config.source.unwrap_or(snap.vertices[0]);
let source_idx = *snap.id_to_idx.get(&source)?;
let order: Vec<VertexId> = match &config.vertex_order {
Some(o) => o.clone(),
None => snap.vertices.clone(),
};
let mut priority = vec![1u64; snap.n];
if let Some(ref prio) = config.vertex_priorities {
for &(v, p) in prio {
if let Some(&idx) = snap.id_to_idx.get(&v) {
priority[idx] = p;
}
}
}
let lambda_star = snap.global_mincut_value()?;
if lambda_star.raw() == 0 {
return None;
}
for &v in &order {
if v == source {
continue;
}
let v_idx = match snap.id_to_idx.get(&v) {
Some(&idx) => idx,
None => continue,
};
let (cut_value, source_side_mask) = snap.min_st_cut(source_idx, v_idx, &priority);
if cut_value != lambda_star {
continue;
}
let mut side: Vec<VertexId> = source_side_mask
.iter()
.enumerate()
.filter_map(|(i, &in_s)| if in_s { Some(snap.vertices[i]) } else { None })
.collect();
side.sort_unstable();
let side_size = side.len();
let priority_sum: u64 = source_side_mask
.iter()
.enumerate()
.filter_map(|(i, &in_s)| if in_s { Some(priority[i]) } else { None })
.sum();
let mut cut_edges: Vec<(VertexId, VertexId)> = Vec::new();
for i in 0..snap.n {
if !source_side_mask[i] {
continue;
}
for &(j, _w) in &snap.adj[i] {
if !source_side_mask[j] && snap.vertices[i] < snap.vertices[j] {
cut_edges.push((snap.vertices[i], snap.vertices[j]));
}
}
}
cut_edges.sort_unstable();
let cut_hash = stable_cut_hash(lambda_star, source, v, &side, priority_sum);
return Some(SourceAnchoredCut {
lambda: lambda_star,
source_vertex: source,
first_separable_vertex: v,
side_vertices: side,
side_size,
priority_sum,
cut_edges,
cut_hash,
});
}
None
}
pub fn make_receipt(cut: &SourceAnchoredCut, epoch: u64) -> SourceAnchoredReceipt {
let ts = PortableTimestamp::now().as_secs() * 1_000_000_000;
SourceAnchoredReceipt {
epoch,
cut_hash: cut.cut_hash,
lambda: cut.lambda,
source_vertex: cut.source_vertex,
first_separable_vertex: cut.first_separable_vertex,
side_size: cut.side_size,
priority_sum: cut.priority_sum,
timestamp_ns: ts,
}
}
pub fn receipts_agree(a: &SourceAnchoredReceipt, b: &SourceAnchoredReceipt) -> bool {
a.cut_hash == b.cut_hash
&& a.lambda == b.lambda
&& a.source_vertex == b.source_vertex
&& a.first_separable_vertex == b.first_separable_vertex
&& a.side_size == b.side_size
&& a.priority_sum == b.priority_sum
}
pub struct SourceAnchoredMinCut {
inner: crate::algorithm::DynamicMinCut,
config: SourceAnchoredConfig,
cached: Option<SourceAnchoredCut>,
epoch: u64,
dirty: bool,
}
impl SourceAnchoredMinCut {
pub fn new() -> Self {
Self {
inner: crate::algorithm::DynamicMinCut::new(crate::MinCutConfig::default()),
config: SourceAnchoredConfig::default(),
cached: None,
epoch: 0,
dirty: true,
}
}
pub fn with_config(config: SourceAnchoredConfig) -> Self {
Self {
inner: crate::algorithm::DynamicMinCut::new(crate::MinCutConfig::default()),
config,
cached: None,
epoch: 0,
dirty: true,
}
}
pub fn with_edges(
edges: Vec<(VertexId, VertexId, Weight)>,
config: SourceAnchoredConfig,
) -> crate::Result<Self> {
let inner = crate::MinCutBuilder::new()
.exact()
.with_edges(edges)
.build()?;
Ok(Self {
inner,
config,
cached: None,
epoch: 0,
dirty: true,
})
}
pub fn canonical_cut(&mut self) -> Option<SourceAnchoredCut> {
if !self.dirty {
if let Some(ref c) = self.cached {
return Some(c.clone());
}
}
let graph = self.inner.graph();
let g = graph.read();
let result = canonical_mincut(&g, &self.config);
drop(g);
self.cached = result.clone();
self.dirty = false;
result
}
pub fn receipt(&mut self) -> Option<SourceAnchoredReceipt> {
let cut = self.canonical_cut()?;
Some(make_receipt(&cut, self.epoch))
}
pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) -> crate::Result<f64> {
let val = self.inner.insert_edge(u, v, weight)?;
self.epoch += 1;
self.dirty = true;
self.cached = None;
Ok(val)
}
pub fn delete_edge(&mut self, u: VertexId, v: VertexId) -> crate::Result<f64> {
let val = self.inner.delete_edge(u, v)?;
self.epoch += 1;
self.dirty = true;
self.cached = None;
Ok(val)
}
pub fn min_cut_value(&self) -> f64 {
self.inner.min_cut_value()
}
pub fn num_vertices(&self) -> usize {
self.inner.num_vertices()
}
pub fn num_edges(&self) -> usize {
self.inner.num_edges()
}
pub fn is_connected(&self) -> bool {
self.inner.is_connected()
}
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn config(&self) -> &SourceAnchoredConfig {
&self.config
}
}
impl Default for SourceAnchoredMinCut {
fn default() -> Self {
Self::new()
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct CanonicalMinCutResult {
pub lambda_raw: u64,
pub source_vertex: u64,
pub first_separable_vertex: u64,
pub side_size: u32,
pub priority_sum: u64,
pub cut_edge_count: u32,
pub cut_hash: [u8; 32],
}
impl From<&SourceAnchoredCut> for CanonicalMinCutResult {
fn from(cut: &SourceAnchoredCut) -> Self {
Self {
lambda_raw: cut.lambda.raw(),
source_vertex: cut.source_vertex,
first_separable_vertex: cut.first_separable_vertex,
side_size: cut.side_size as u32,
priority_sum: cut.priority_sum,
cut_edge_count: cut.cut_edges.len() as u32,
cut_hash: cut.cut_hash,
}
}
}
#[cfg(test)]
mod tests;