use crate::graph::{VertexId, Weight};
use crate::canonical::FixedWeight;
use crate::canonical::source_anchored::{
canonical_mincut, SourceAnchoredConfig, SourceAnchoredCut, SourceAnchoredReceipt,
make_receipt,
};
use std::collections::HashSet;
#[derive(Debug, Clone)]
pub struct DynamicMinCutConfig {
pub canonical_config: SourceAnchoredConfig,
pub staleness_threshold: u64,
}
impl Default for DynamicMinCutConfig {
fn default() -> Self {
Self {
canonical_config: SourceAnchoredConfig::default(),
staleness_threshold: 100,
}
}
}
#[derive(Debug, Clone)]
pub enum EdgeMutation {
Add(VertexId, VertexId, Weight),
Remove(VertexId, VertexId),
}
pub struct DynamicMinCut {
inner: crate::algorithm::DynamicMinCut,
config: DynamicMinCutConfig,
cached_cut: Option<SourceAnchoredCut>,
epoch: u64,
last_full_epoch: u64,
incremental_count: u64,
dirty: bool,
cut_edge_set: HashSet<(VertexId, VertexId)>,
source_side_set: HashSet<VertexId>,
}
impl DynamicMinCut {
pub fn new() -> Self {
Self {
inner: crate::algorithm::DynamicMinCut::new(crate::MinCutConfig::default()),
config: DynamicMinCutConfig::default(),
cached_cut: None,
epoch: 0,
last_full_epoch: 0,
incremental_count: 0,
dirty: true,
cut_edge_set: HashSet::new(),
source_side_set: HashSet::new(),
}
}
pub fn with_config(config: DynamicMinCutConfig) -> Self {
Self {
inner: crate::algorithm::DynamicMinCut::new(crate::MinCutConfig::default()),
config,
cached_cut: None,
epoch: 0,
last_full_epoch: 0,
incremental_count: 0,
dirty: true,
cut_edge_set: HashSet::new(),
source_side_set: HashSet::new(),
}
}
pub fn with_edges(
edges: Vec<(VertexId, VertexId, Weight)>,
config: DynamicMinCutConfig,
) -> crate::Result<Self> {
let inner = crate::MinCutBuilder::new()
.exact()
.with_edges(edges)
.build()?;
Ok(Self {
inner,
config,
cached_cut: None,
epoch: 0,
last_full_epoch: 0,
incremental_count: 0,
dirty: true,
cut_edge_set: HashSet::new(),
source_side_set: HashSet::new(),
})
}
pub fn epoch(&self) -> u64 {
self.epoch
}
pub fn last_full_epoch(&self) -> u64 {
self.last_full_epoch
}
pub fn incremental_count(&self) -> u64 {
self.incremental_count
}
pub fn is_stale(&self) -> bool {
self.dirty
}
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 min_cut_value(&self) -> f64 {
self.inner.min_cut_value()
}
pub fn config(&self) -> &DynamicMinCutConfig {
&self.config
}
pub fn canonical_cut(&mut self) -> Option<SourceAnchoredCut> {
if !self.dirty && self.cached_cut.is_some() {
if self.config.staleness_threshold > 0
&& self.incremental_count >= self.config.staleness_threshold
{
self.force_recompute();
} else {
return self.cached_cut.clone();
}
}
self.force_recompute();
self.cached_cut.clone()
}
pub fn force_recompute(&mut self) {
let graph = self.inner.graph();
let g = graph.read();
let result = canonical_mincut(&g, &self.config.canonical_config);
drop(g);
if let Some(ref cut) = result {
self.rebuild_cache(cut);
} else {
self.cut_edge_set.clear();
self.source_side_set.clear();
}
self.cached_cut = result;
self.dirty = false;
self.last_full_epoch = self.epoch;
self.incremental_count = 0;
}
pub fn receipt(&mut self) -> Option<SourceAnchoredReceipt> {
let cut = self.canonical_cut()?;
Some(make_receipt(&cut, self.epoch))
}
pub fn add_edge(
&mut self,
u: VertexId,
v: VertexId,
weight: Weight,
) -> crate::Result<f64> {
let val = self.inner.insert_edge(u, v, weight)?;
self.epoch += 1;
if self.cached_cut.is_some() && !self.dirty {
let u_in_source = self.source_side_set.contains(&u);
let v_in_source = self.source_side_set.contains(&v);
if u_in_source == v_in_source {
self.incremental_count += 1;
} else {
self.dirty = true;
}
} else {
self.dirty = true;
}
Ok(val)
}
pub fn remove_edge(
&mut self,
u: VertexId,
v: VertexId,
) -> crate::Result<f64> {
let val = self.inner.delete_edge(u, v)?;
self.epoch += 1;
if self.cached_cut.is_some() && !self.dirty {
let edge_key = normalize_edge(u, v);
if self.cut_edge_set.contains(&edge_key) {
self.dirty = true;
} else {
self.incremental_count += 1;
}
} else {
self.dirty = true;
}
Ok(val)
}
pub fn apply_batch(
&mut self,
mutations: &[EdgeMutation],
) -> crate::Result<()> {
let mut needs_recompute = self.dirty;
for mutation in mutations {
match mutation {
EdgeMutation::Add(u, v, w) => {
self.inner.insert_edge(*u, *v, *w)?;
self.epoch += 1;
if !needs_recompute && self.cached_cut.is_some() {
let u_in = self.source_side_set.contains(u);
let v_in = self.source_side_set.contains(v);
if u_in != v_in {
needs_recompute = true;
}
}
}
EdgeMutation::Remove(u, v) => {
self.inner.delete_edge(*u, *v)?;
self.epoch += 1;
if !needs_recompute && self.cached_cut.is_some() {
let edge_key = normalize_edge(*u, *v);
if self.cut_edge_set.contains(&edge_key) {
needs_recompute = true;
}
}
}
}
}
self.incremental_count += mutations.len() as u64;
if needs_recompute {
self.dirty = true;
}
if self.config.staleness_threshold > 0
&& self.incremental_count >= self.config.staleness_threshold
{
self.force_recompute();
}
Ok(())
}
fn rebuild_cache(&mut self, cut: &SourceAnchoredCut) {
self.cut_edge_set.clear();
for &(u, v) in &cut.cut_edges {
self.cut_edge_set.insert(normalize_edge(u, v));
}
self.source_side_set.clear();
for &v in &cut.side_vertices {
self.source_side_set.insert(v);
}
}
}
impl Default for DynamicMinCut {
fn default() -> Self {
Self::new()
}
}
fn normalize_edge(u: VertexId, v: VertexId) -> (VertexId, VertexId) {
if u <= v { (u, v) } else { (v, u) }
}
#[cfg(test)]
mod tests;