use std::collections::VecDeque;
use crate::error::{GraphalgError, GraphalgResult};
#[derive(Debug, Clone)]
struct Edge {
to: usize,
cap: f64,
cost: f64,
flow: f64,
}
#[derive(Debug, Clone)]
pub struct MinCostFlowNetwork {
pub n: usize,
edges: Vec<Edge>,
graph: Vec<Vec<usize>>,
}
impl MinCostFlowNetwork {
pub fn new(n: usize) -> Self {
Self {
n,
edges: Vec::new(),
graph: vec![Vec::new(); n],
}
}
pub fn add_edge(&mut self, u: usize, v: usize, cap: f64, cost: f64) -> GraphalgResult<()> {
if u >= self.n || v >= self.n {
return Err(GraphalgError::IndexOutOfBounds {
index: u.max(v),
len: self.n,
});
}
if cap < 0.0 {
return Err(GraphalgError::InvalidParameter(
"negative capacity".to_string(),
));
}
if !cost.is_finite() {
return Err(GraphalgError::InvalidEdgeWeight(
"non-finite cost".to_string(),
));
}
let fwd_idx = self.edges.len();
let rev_idx = fwd_idx + 1;
self.edges.push(Edge {
to: v,
cap,
cost,
flow: 0.0,
});
self.edges.push(Edge {
to: u,
cap: 0.0,
cost: -cost,
flow: 0.0,
});
self.graph[u].push(fwd_idx);
self.graph[v].push(rev_idx);
Ok(())
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct MinCostFlowResult {
pub flow: f64,
pub cost: f64,
}
fn spfa_shortest_path(
net: &MinCostFlowNetwork,
s: usize,
t: usize,
) -> GraphalgResult<(Vec<f64>, Vec<usize>)> {
let n = net.n;
let mut dist = vec![f64::INFINITY; n];
let mut prev_edge = vec![usize::MAX; n];
let mut in_queue = vec![false; n];
let mut relax_count = vec![0usize; n];
dist[s] = 0.0;
let mut queue: VecDeque<usize> = VecDeque::new();
queue.push_back(s);
in_queue[s] = true;
while let Some(u) = queue.pop_front() {
in_queue[u] = false;
for &edge_idx in &net.graph[u] {
let e = &net.edges[edge_idx];
if e.cap - e.flow <= 1e-9 {
continue;
}
let new_dist = dist[u] + e.cost;
if new_dist < dist[e.to] - 1e-9 {
dist[e.to] = new_dist;
prev_edge[e.to] = edge_idx;
if !in_queue[e.to] {
relax_count[e.to] += 1;
if relax_count[e.to] > n {
return Err(GraphalgError::NegativeCycle);
}
queue.push_back(e.to);
in_queue[e.to] = true;
}
}
}
}
let _ = t; Ok((dist, prev_edge))
}
fn augment_path(
net: &mut MinCostFlowNetwork,
s: usize,
t: usize,
prev_edge: &[usize],
path_cost: f64,
flow_limit: f64,
) -> f64 {
let mut path_edges: Vec<usize> = Vec::new();
let mut v = t;
while v != s {
let eidx = prev_edge[v];
path_edges.push(eidx);
v = net.edges[eidx ^ 1].to; }
let mut bottleneck = flow_limit;
for &eidx in &path_edges {
let e = &net.edges[eidx];
bottleneck = bottleneck.min(e.cap - e.flow);
}
for &eidx in &path_edges {
net.edges[eidx].flow += bottleneck;
net.edges[eidx ^ 1].flow -= bottleneck;
}
let _ = path_cost; bottleneck
}
pub fn min_cost_max_flow(
net: &MinCostFlowNetwork,
s: usize,
t: usize,
) -> GraphalgResult<MinCostFlowResult> {
min_cost_flow_bounded(net, s, t, f64::INFINITY)
}
pub fn min_cost_flow_bounded(
net: &MinCostFlowNetwork,
s: usize,
t: usize,
max_flow: f64,
) -> GraphalgResult<MinCostFlowResult> {
let n = net.n;
if s >= n || t >= n {
return Err(GraphalgError::SourceOutOfRange { node: s.max(t), n });
}
if s == t {
return Err(GraphalgError::InvalidParameter(
"source == sink".to_string(),
));
}
if max_flow < 0.0 {
return Err(GraphalgError::InvalidParameter(
"max_flow must be non-negative".to_string(),
));
}
if max_flow == 0.0 {
return Ok(MinCostFlowResult {
flow: 0.0,
cost: 0.0,
});
}
let mut residual = net.clone();
let mut total_flow = 0.0;
let mut total_cost = 0.0;
loop {
let (dist, prev_edge) = spfa_shortest_path(&residual, s, t)?;
if dist[t].is_infinite() {
break;
}
let remaining = max_flow - total_flow;
if remaining <= 1e-9 {
break;
}
let path_cost = dist[t];
let pushed = augment_path(&mut residual, s, t, &prev_edge, path_cost, remaining);
if pushed <= 1e-9 {
break;
}
total_flow += pushed;
total_cost += pushed * path_cost;
}
Ok(MinCostFlowResult {
flow: total_flow,
cost: total_cost,
})
}
#[cfg(test)]
mod tests {
use super::*;
use crate::max_flow::edmonds_karp::{FlowNetwork, edmonds_karp};
fn flow_net_from(n: usize, edges: &[(usize, usize, f64)]) -> FlowNetwork {
let mut net = FlowNetwork::new(n);
for &(u, v, c) in edges {
net.add_edge(u, v, c).expect("add_edge ok");
}
net
}
fn mcf_net(n: usize, edges: &[(usize, usize, f64, f64)]) -> MinCostFlowNetwork {
let mut net = MinCostFlowNetwork::new(n);
for &(u, v, cap, cost) in edges {
net.add_edge(u, v, cap, cost).expect("add_edge ok");
}
net
}
#[test]
fn diamond_max_flow_cost() {
let net = mcf_net(
4,
&[
(0, 1, 2.0, 1.0),
(0, 2, 2.0, 2.0),
(1, 3, 2.0, 1.0),
(2, 3, 2.0, 2.0),
],
);
let r = min_cost_max_flow(&net, 0, 3).expect("ok");
assert!((r.flow - 4.0).abs() < 1e-9, "flow={}", r.flow);
assert!((r.cost - 12.0).abs() < 1e-9, "cost={}", r.cost);
}
#[test]
fn single_path() {
let net = mcf_net(3, &[(0, 1, 1.0, 3.0), (1, 2, 1.0, 4.0)]);
let r = min_cost_max_flow(&net, 0, 2).expect("ok");
assert!((r.flow - 1.0).abs() < 1e-9);
assert!((r.cost - 7.0).abs() < 1e-9);
}
#[test]
fn cross_check_with_edmonds_karp() {
let edge_caps: &[(usize, usize, f64)] = &[
(0, 1, 10.0),
(0, 2, 6.0),
(1, 3, 8.0),
(2, 3, 5.0),
(1, 4, 4.0),
(3, 4, 9.0),
];
let ek_net = flow_net_from(5, edge_caps);
let ek_flow = edmonds_karp(&ek_net, 0, 4).expect("ek ok");
let mcf = mcf_net(
5,
&edge_caps
.iter()
.map(|&(u, v, c)| (u, v, c, 1.0))
.collect::<Vec<_>>(),
);
let r = min_cost_max_flow(&mcf, 0, 4).expect("mcf ok");
assert!(
(r.flow - ek_flow).abs() < 1e-9,
"SSP flow={} but EK flow={}",
r.flow,
ek_flow
);
}
#[test]
fn bounded_flow_diamond() {
let net = mcf_net(
4,
&[
(0, 1, 2.0, 1.0),
(0, 2, 2.0, 2.0),
(1, 3, 2.0, 1.0),
(2, 3, 2.0, 2.0),
],
);
let r = min_cost_flow_bounded(&net, 0, 3, 2.0).expect("ok");
assert!((r.flow - 2.0).abs() < 1e-9, "flow={}", r.flow);
assert!((r.cost - 4.0).abs() < 1e-9, "cost={}", r.cost);
}
#[test]
fn negative_cost_edge() {
let net = mcf_net(3, &[(0, 1, 1.0, -2.0), (1, 2, 1.0, 5.0)]);
let r = min_cost_max_flow(&net, 0, 2).expect("ok");
assert!((r.flow - 1.0).abs() < 1e-9);
assert!((r.cost - 3.0).abs() < 1e-9, "cost={}", r.cost);
}
#[test]
fn antiparallel_edges() {
let net = mcf_net(3, &[(0, 1, 1.0, 1.0), (1, 0, 1.0, 1.0), (1, 2, 1.0, 1.0)]);
let r = min_cost_max_flow(&net, 0, 2).expect("ok");
assert!((r.flow - 1.0).abs() < 1e-9, "flow={}", r.flow);
assert!((r.cost - 2.0).abs() < 1e-9, "cost={}", r.cost);
}
#[test]
fn disconnected() {
let net = mcf_net(4, &[(1, 2, 1.0, 1.0), (2, 3, 1.0, 1.0)]);
let r = min_cost_max_flow(&net, 0, 3).expect("ok");
assert!((r.flow).abs() < 1e-9);
assert!((r.cost).abs() < 1e-9);
}
#[test]
fn zero_capacity_edge() {
let net = mcf_net(3, &[(0, 1, 0.0, 1.0), (1, 2, 0.0, 1.0)]);
let r = min_cost_max_flow(&net, 0, 2).expect("ok");
assert!((r.flow).abs() < 1e-9);
}
#[test]
fn source_equals_sink() {
let net = MinCostFlowNetwork::new(3);
assert!(matches!(
min_cost_max_flow(&net, 1, 1),
Err(GraphalgError::InvalidParameter(_))
));
}
#[test]
fn source_out_of_range() {
let net = MinCostFlowNetwork::new(3);
assert!(matches!(
min_cost_max_flow(&net, 5, 1),
Err(GraphalgError::SourceOutOfRange { .. })
));
}
#[test]
fn add_edge_negative_cap() {
let mut net = MinCostFlowNetwork::new(3);
assert!(matches!(
net.add_edge(0, 1, -1.0, 1.0),
Err(GraphalgError::InvalidParameter(_))
));
}
#[test]
fn add_edge_nonfinite_cost() {
let mut net = MinCostFlowNetwork::new(3);
assert!(matches!(
net.add_edge(0, 1, 1.0, f64::INFINITY),
Err(GraphalgError::InvalidEdgeWeight(_))
));
assert!(matches!(
net.add_edge(0, 1, 1.0, f64::NAN),
Err(GraphalgError::InvalidEdgeWeight(_))
));
}
#[test]
fn bounded_max_flow_zero() {
let net = mcf_net(3, &[(0, 1, 5.0, 1.0), (1, 2, 5.0, 1.0)]);
let r = min_cost_flow_bounded(&net, 0, 2, 0.0).expect("ok");
assert!((r.flow).abs() < 1e-9);
assert!((r.cost).abs() < 1e-9);
}
#[test]
fn bounded_negative_max_flow() {
let net = MinCostFlowNetwork::new(3);
assert!(matches!(
min_cost_flow_bounded(&net, 0, 2, -1.0),
Err(GraphalgError::InvalidParameter(_))
));
}
#[test]
fn three_paths_cost_order() {
let net = mcf_net(
8,
&[
(0, 1, 1.0, 1.0),
(1, 7, 1.0, 0.0), (0, 2, 1.0, 2.0),
(2, 7, 1.0, 0.0), (0, 3, 1.0, 3.0),
(3, 7, 1.0, 0.0), ],
);
let r = min_cost_flow_bounded(&net, 0, 7, 2.0).expect("ok");
assert!((r.flow - 2.0).abs() < 1e-9, "flow={}", r.flow);
assert!((r.cost - 3.0).abs() < 1e-9, "cost={}", r.cost); }
#[test]
fn deterministic_result() {
let net = mcf_net(
4,
&[
(0, 1, 2.0, 1.0),
(0, 2, 2.0, 2.0),
(1, 3, 2.0, 1.0),
(2, 3, 2.0, 2.0),
],
);
let r1 = min_cost_max_flow(&net, 0, 3).expect("r1");
let r2 = min_cost_max_flow(&net, 0, 3).expect("r2");
assert_eq!(r1, r2);
}
#[test]
fn large_network() {
let edges: Vec<(usize, usize, f64, f64)> = vec![
(0, 1, 5.0, 1.0),
(0, 2, 3.0, 2.0),
(0, 3, 4.0, 3.0),
(1, 4, 2.0, 1.0),
(1, 5, 3.0, 2.0),
(2, 4, 2.0, 3.0),
(2, 6, 2.0, 1.0),
(3, 5, 2.0, 2.0),
(3, 6, 2.0, 1.0),
(4, 7, 3.0, 1.0),
(5, 7, 3.0, 2.0),
(6, 7, 2.0, 3.0),
(4, 8, 2.0, 1.0),
(5, 8, 2.0, 2.0),
(6, 8, 2.0, 1.0),
(7, 9, 5.0, 1.0),
(8, 9, 5.0, 1.0),
(1, 3, 1.0, 1.0),
(2, 5, 1.0, 2.0),
(3, 6, 1.0, 1.0),
];
let net = mcf_net(10, &edges);
let r = min_cost_max_flow(&net, 0, 9).expect("ok");
assert!(r.flow >= 0.0, "flow must be non-negative");
assert!(r.cost.is_finite(), "cost must be finite");
}
#[test]
fn flow_conservation_diamond() {
let net = mcf_net(
4,
&[
(0, 1, 2.0, 1.0),
(0, 2, 2.0, 2.0),
(1, 3, 2.0, 1.0),
(2, 3, 2.0, 2.0),
],
);
let r = min_cost_max_flow(&net, 0, 3).expect("ok");
let max_out_s = 2.0 + 2.0; assert!(r.flow <= max_out_s + 1e-9);
assert!((r.flow - 4.0).abs() < 1e-9);
}
#[test]
fn empty_network_add_edge_errors() {
let mut net = MinCostFlowNetwork::new(0);
assert!(matches!(
net.add_edge(0, 0, 1.0, 1.0),
Err(GraphalgError::IndexOutOfBounds { .. })
));
}
#[test]
fn original_unchanged_after_algorithm() {
let net = mcf_net(3, &[(0, 1, 5.0, 1.0), (1, 2, 5.0, 1.0)]);
let original_edge_count = net.edges.len();
let original_n = net.n;
let _result = min_cost_max_flow(&net, 0, 2).expect("ok");
assert_eq!(net.n, original_n);
assert_eq!(net.edges.len(), original_edge_count);
for (i, e) in net.edges.iter().enumerate() {
assert_eq!(
e.flow, 0.0,
"edge {} flow should be 0 in original, got {}",
i, e.flow
);
}
}
}