#[cfg(not(feature = "structural"))]
use std::collections::HashMap;
pub type VertexId = u32;
pub type Weight = f64;
#[derive(Debug, Clone)]
pub struct MinCutResult {
pub value: f64,
pub is_exact: bool,
pub cut_edges: Option<Vec<(VertexId, VertexId)>>,
pub witness_hash: Option<[u8; 32]>,
}
#[cfg(feature = "structural")]
pub struct DynamicMinCutEngine {
inner: ruvector_mincut::subpolynomial::SubpolynomialMinCut,
cached_cut: Option<f64>,
generation: u64,
}
#[cfg(feature = "structural")]
impl DynamicMinCutEngine {
pub fn new() -> Self {
use ruvector_mincut::subpolynomial::{SubpolynomialMinCut, SubpolyConfig};
let config = SubpolyConfig {
phi: 0.01,
lambda_max: 1000,
epsilon: 0.1,
target_levels: 4,
track_recourse: false,
certify_cuts: true,
parallel: false,
..Default::default()
};
Self {
inner: SubpolynomialMinCut::new(config),
cached_cut: None,
generation: 0,
}
}
#[inline]
pub fn insert_edge(&mut self, u: VertexId, v: VertexId, weight: Weight) {
let _ = self.inner.insert_edge(u as u64, v as u64, weight);
self.cached_cut = None;
self.generation += 1;
}
#[inline]
pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
let _ = self.inner.delete_edge(u as u64, v as u64);
self.cached_cut = None;
self.generation += 1;
}
#[inline]
pub fn update_weight(&mut self, u: VertexId, v: VertexId, new_weight: Weight) {
let _ = self.inner.delete_edge(u as u64, v as u64);
let _ = self.inner.insert_edge(u as u64, v as u64, new_weight);
self.cached_cut = None;
self.generation += 1;
}
#[inline]
pub fn min_cut_value(&mut self) -> f64 {
if let Some(cached) = self.cached_cut {
return cached;
}
let value = self.inner.min_cut_value();
self.cached_cut = Some(value);
value
}
pub fn min_cut(&mut self) -> MinCutResult {
let result = self.inner.min_cut();
let mut hasher = blake3::Hasher::new();
hasher.update(&result.value.to_le_bytes());
hasher.update(if result.is_exact { &[1u8] } else { &[0u8] });
hasher.update(if result.complexity_verified { &[1u8] } else { &[0u8] });
let witness_hash = Some(*hasher.finalize().as_bytes());
MinCutResult {
value: result.value,
is_exact: result.is_exact,
cut_edges: result.cut_edges.map(|edges| {
edges.into_iter()
.map(|(u, v)| (u as VertexId, v as VertexId))
.collect()
}),
witness_hash,
}
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn is_connected(&self) -> bool {
self.inner.min_cut_value() > 0.0
}
pub fn num_vertices(&self) -> usize {
self.inner.num_vertices()
}
pub fn num_edges(&self) -> usize {
self.inner.num_edges()
}
}
#[cfg(feature = "structural")]
impl Default for DynamicMinCutEngine {
fn default() -> Self {
Self::new()
}
}
#[cfg(not(feature = "structural"))]
pub struct DynamicMinCutEngine {
edges: HashMap<(VertexId, VertexId), Weight>,
degrees: HashMap<VertexId, u32>,
total_weight: f64,
generation: u64,
}
#[cfg(not(feature = "structural"))]
impl DynamicMinCutEngine {
pub fn new() -> Self {
Self {
edges: HashMap::new(),
degrees: HashMap::new(),
total_weight: 0.0,
generation: 0,
}
}
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.insert(key, weight).is_none() {
*self.degrees.entry(u).or_insert(0) += 1;
*self.degrees.entry(v).or_insert(0) += 1;
self.total_weight += weight;
}
self.generation += 1;
}
pub fn delete_edge(&mut self, u: VertexId, v: VertexId) {
let key = if u < v { (u, v) } else { (v, u) };
if let Some(weight) = self.edges.remove(&key) {
if let Some(deg) = self.degrees.get_mut(&u) {
*deg = deg.saturating_sub(1);
}
if let Some(deg) = self.degrees.get_mut(&v) {
*deg = deg.saturating_sub(1);
}
self.total_weight -= weight;
}
self.generation += 1;
}
pub fn update_weight(&mut self, u: VertexId, v: VertexId, new_weight: Weight) {
let key = if u < v { (u, v) } else { (v, u) };
if let Some(old_weight) = self.edges.get_mut(&key) {
self.total_weight -= *old_weight;
*old_weight = new_weight;
self.total_weight += new_weight;
}
self.generation += 1;
}
pub fn min_cut_value(&mut self) -> f64 {
if self.degrees.is_empty() || self.edges.is_empty() {
return 0.0;
}
let min_degree = self.degrees.values().copied().min().unwrap_or(0) as f64;
let avg_weight = self.total_weight / self.edges.len() as f64;
min_degree * avg_weight
}
pub fn min_cut(&mut self) -> MinCutResult {
MinCutResult {
value: self.min_cut_value(),
is_exact: false,
cut_edges: None,
witness_hash: None,
}
}
pub fn generation(&self) -> u64 {
self.generation
}
pub fn is_connected(&self) -> bool {
!self.edges.is_empty()
}
pub fn num_vertices(&self) -> usize {
self.degrees.len()
}
pub fn num_edges(&self) -> usize {
self.edges.len()
}
}
#[cfg(not(feature = "structural"))]
impl Default for DynamicMinCutEngine {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_engine_basic() {
let mut engine = DynamicMinCutEngine::new();
engine.insert_edge(0, 1, 1.0);
engine.insert_edge(1, 2, 1.0);
engine.insert_edge(2, 0, 1.0);
let cut = engine.min_cut_value();
assert!(cut > 0.0, "Triangle should have positive min-cut");
engine.insert_edge(2, 3, 1.0);
let cut2 = engine.min_cut_value();
assert!(cut2 <= cut + 0.1 || cut2 >= 0.9);
}
#[test]
fn test_engine_delete() {
let mut engine = DynamicMinCutEngine::new();
engine.insert_edge(0, 1, 1.0);
engine.insert_edge(1, 2, 1.0);
engine.insert_edge(2, 0, 1.0);
let gen1 = engine.generation();
engine.delete_edge(2, 0);
let gen2 = engine.generation();
assert!(gen2 > gen1, "Generation should increase on delete");
assert_eq!(engine.num_edges(), 2);
}
#[test]
fn test_min_cut_result() {
let mut engine = DynamicMinCutEngine::new();
engine.insert_edge(0, 1, 2.0);
engine.insert_edge(1, 2, 3.0);
let result = engine.min_cut();
assert!(result.value >= 0.0);
#[cfg(feature = "structural")]
assert!(result.is_exact, "With structural feature, should be exact");
#[cfg(not(feature = "structural"))]
assert!(!result.is_exact, "Without structural feature, is heuristic");
}
}