use crate::{Cost, Error, Result, SolverStats, SolverStatus};
use std::collections::VecDeque;
use std::time::Instant;
#[derive(Debug, Clone)]
pub struct FlowNetwork {
pub num_nodes: usize,
adj: Vec<Vec<usize>>,
edges: Vec<FlowEdge>,
}
#[derive(Debug, Clone, Copy)]
struct FlowEdge {
to: usize,
capacity: i64,
cost: i64,
flow: i64,
rev: usize,
}
impl FlowNetwork {
pub fn new(num_nodes: usize) -> Self {
Self {
num_nodes,
adj: vec![Vec::new(); num_nodes],
edges: Vec::new(),
}
}
pub fn add_edge(&mut self, from: usize, to: usize, capacity: i64, cost: i64) {
let forward_idx = self.edges.len();
let reverse_idx = self.edges.len() + 1;
self.edges.push(FlowEdge {
to,
capacity,
cost,
flow: 0,
rev: reverse_idx,
});
self.adj[from].push(forward_idx);
self.edges.push(FlowEdge {
to: from,
capacity: 0, cost: -cost, flow: 0,
rev: forward_idx,
});
self.adj[to].push(reverse_idx);
}
pub fn add_edge_with_capacity(&mut self, from: usize, to: usize, capacity: i64) {
self.add_edge(from, to, capacity, 0);
}
fn residual(&self, edge_idx: usize) -> i64 {
self.edges[edge_idx].capacity - self.edges[edge_idx].flow
}
fn push_flow(&mut self, edge_idx: usize, amount: i64) {
self.edges[edge_idx].flow += amount;
let rev = self.edges[edge_idx].rev;
self.edges[rev].flow -= amount;
}
}
#[derive(Debug, Clone)]
pub struct MaxFlowResult {
pub max_flow: i64,
pub edge_flows: Vec<i64>,
pub status: SolverStatus,
pub stats: SolverStats,
}
pub fn max_flow(network: &FlowNetwork, source: usize, sink: usize) -> Result<MaxFlowResult> {
if source >= network.num_nodes || sink >= network.num_nodes {
return Err(Error::invalid_input("source or sink out of range"));
}
if source == sink {
return Err(Error::invalid_input("source and sink must be different"));
}
let start = Instant::now();
let n = network.num_nodes;
let mut net = network.clone();
let mut height = vec![0usize; n];
let mut excess = vec![0i64; n];
let mut current = vec![0usize; n];
let mut active: VecDeque<usize> = VecDeque::new();
let mut in_queue = vec![false; n];
height[source] = n;
let source_edges: Vec<usize> = net.adj[source].clone();
for edge_idx in source_edges {
let cap = net.residual(edge_idx);
if cap > 0 {
let to = net.edges[edge_idx].to;
net.push_flow(edge_idx, cap);
excess[to] += cap;
excess[source] -= cap;
if to != sink && to != source && !in_queue[to] {
active.push_back(to);
in_queue[to] = true;
}
}
}
let mut iterations = 0;
while let Some(u) = active.pop_front() {
in_queue[u] = false;
let activated = discharge(
&mut net,
&mut height,
&mut excess,
&mut current,
u,
source,
sink,
);
iterations += 1;
for v in activated {
if !in_queue[v] {
active.push_back(v);
in_queue[v] = true;
}
}
if excess[u] > 0 && u != source && u != sink && !in_queue[u] {
active.push_back(u);
in_queue[u] = true;
}
}
let edge_flows: Vec<i64> = (0..net.edges.len())
.step_by(2)
.map(|i| net.edges[i].flow)
.collect();
let elapsed = start.elapsed().as_secs_f64();
Ok(MaxFlowResult {
max_flow: excess[sink],
edge_flows,
status: SolverStatus::Optimal,
stats: SolverStats {
solve_time_seconds: elapsed,
iterations,
objective_value: Some(excess[sink] as f64),
..Default::default()
},
})
}
fn discharge(
net: &mut FlowNetwork,
height: &mut [usize],
excess: &mut [i64],
current: &mut [usize],
u: usize,
source: usize,
sink: usize,
) -> Vec<usize> {
let mut activated = Vec::new();
while excess[u] > 0 {
if current[u] >= net.adj[u].len() {
relabel(net, height, u);
current[u] = 0;
} else {
let edge_idx = net.adj[u][current[u]];
let v = net.edges[edge_idx].to;
let residual = net.residual(edge_idx);
if residual > 0 && height[u] == height[v] + 1 {
let push_amount = excess[u].min(residual);
net.push_flow(edge_idx, push_amount);
excess[u] -= push_amount;
let was_zero = excess[v] == 0;
excess[v] += push_amount;
if was_zero && v != source && v != sink {
activated.push(v);
}
} else {
current[u] += 1;
}
}
}
activated
}
fn relabel(net: &FlowNetwork, height: &mut [usize], u: usize) {
let mut min_height = usize::MAX;
for &edge_idx in &net.adj[u] {
if net.residual(edge_idx) > 0 {
let v = net.edges[edge_idx].to;
min_height = min_height.min(height[v]);
}
}
if min_height < usize::MAX {
height[u] = min_height + 1;
}
}
#[derive(Debug, Clone)]
pub struct MinCostFlowResult {
pub flow: i64,
pub cost: Cost,
pub edge_flows: Vec<i64>,
pub status: SolverStatus,
pub stats: SolverStats,
}
#[derive(Debug, Clone)]
pub struct MinCostFlowProblem {
pub network: FlowNetwork,
pub supplies: Vec<i64>,
}
impl MinCostFlowProblem {
pub fn new(network: FlowNetwork, supplies: Vec<i64>) -> Result<Self> {
if supplies.len() != network.num_nodes {
return Err(Error::dimension_mismatch(network.num_nodes, supplies.len()));
}
let total: i64 = supplies.iter().sum();
if total != 0 {
return Err(Error::invalid_input(format!(
"supplies must sum to 0, got {}",
total
)));
}
Ok(Self { network, supplies })
}
pub fn source_sink(
network: FlowNetwork,
source: usize,
sink: usize,
flow_demand: i64,
) -> Result<Self> {
let mut supplies = vec![0i64; network.num_nodes];
supplies[source] = flow_demand;
supplies[sink] = -flow_demand;
Self::new(network, supplies)
}
}
pub fn min_cost_flow(problem: &MinCostFlowProblem) -> Result<MinCostFlowResult> {
let start = Instant::now();
let n = problem.network.num_nodes;
let mut net = problem.network.clone();
let mut supply = problem.supplies.clone();
let mut total_flow: i64 = 0;
let mut total_cost: Cost = 0;
let mut iterations = 0;
loop {
iterations += 1;
let source = supply.iter().position(|&s| s > 0);
let sink = supply.iter().position(|&s| s < 0);
match (source, sink) {
(Some(s), Some(t)) => {
match bellman_ford_path(&net, s, t) {
Some((path, path_cost)) => {
let mut bottleneck = supply[s].min(-supply[t]);
for &edge_idx in &path {
bottleneck = bottleneck.min(net.residual(edge_idx));
}
if bottleneck <= 0 {
break; }
for &edge_idx in &path {
net.push_flow(edge_idx, bottleneck);
}
supply[s] -= bottleneck;
supply[t] += bottleneck;
total_flow += bottleneck;
total_cost += bottleneck * path_cost;
}
None => {
if supply.iter().any(|&s| s != 0) {
return Err(Error::infeasible(
"no augmenting path but unsatisfied supply/demand",
));
}
break;
}
}
}
_ => break, }
if iterations > n * net.edges.len() * 1000 {
return Err(Error::NoConvergence { iterations });
}
}
if supply.iter().any(|&s| s != 0) {
return Err(Error::infeasible("could not satisfy all supplies/demands"));
}
let edge_flows: Vec<i64> = (0..net.edges.len())
.step_by(2)
.map(|i| net.edges[i].flow)
.collect();
let elapsed = start.elapsed().as_secs_f64();
Ok(MinCostFlowResult {
flow: total_flow,
cost: total_cost,
edge_flows,
status: SolverStatus::Optimal,
stats: SolverStats {
solve_time_seconds: elapsed,
iterations,
objective_value: Some(total_cost as f64),
..Default::default()
},
})
}
fn bellman_ford_path(net: &FlowNetwork, source: usize, sink: usize) -> Option<(Vec<usize>, Cost)> {
let n = net.num_nodes;
let mut dist = vec![i64::MAX; n];
let mut parent_edge: Vec<Option<usize>> = vec![None; n];
dist[source] = 0;
for _ in 0..n {
let mut changed = false;
for u in 0..n {
if dist[u] == i64::MAX {
continue;
}
for &edge_idx in &net.adj[u] {
let edge = &net.edges[edge_idx];
if net.residual(edge_idx) > 0 {
let new_dist = dist[u].saturating_add(edge.cost);
if new_dist < dist[edge.to] {
dist[edge.to] = new_dist;
parent_edge[edge.to] = Some(edge_idx);
changed = true;
}
}
}
}
if !changed {
break;
}
}
if dist[sink] == i64::MAX {
return None;
}
let mut path = Vec::new();
let mut current = sink;
while let Some(edge_idx) = parent_edge[current] {
path.push(edge_idx);
let rev_idx = net.edges[edge_idx].rev;
current = net.edges[rev_idx].to;
if current == source {
break;
}
}
path.reverse();
Some((path, dist[sink]))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_simple_max_flow() {
let mut net = FlowNetwork::new(5);
net.add_edge_with_capacity(0, 1, 10);
net.add_edge_with_capacity(0, 2, 10);
net.add_edge_with_capacity(1, 2, 2);
net.add_edge_with_capacity(1, 3, 4);
net.add_edge_with_capacity(1, 4, 8);
net.add_edge_with_capacity(2, 4, 9);
net.add_edge_with_capacity(3, 4, 10);
let result = max_flow(&net, 0, 4).unwrap();
assert!(result.max_flow >= 17, "max_flow was {}", result.max_flow);
assert!(result.max_flow <= 20, "max_flow was {}", result.max_flow);
}
#[test]
fn test_max_flow_simple_path() {
let mut net = FlowNetwork::new(3);
net.add_edge_with_capacity(0, 1, 5);
net.add_edge_with_capacity(1, 2, 3);
let result = max_flow(&net, 0, 2).unwrap();
assert_eq!(result.max_flow, 3); }
#[test]
fn test_max_flow_parallel_paths() {
let mut net = FlowNetwork::new(4);
net.add_edge_with_capacity(0, 1, 10);
net.add_edge_with_capacity(1, 3, 10);
net.add_edge_with_capacity(0, 2, 10);
net.add_edge_with_capacity(2, 3, 10);
let result = max_flow(&net, 0, 3).unwrap();
assert_eq!(result.max_flow, 20); }
#[test]
fn test_min_cost_flow_simple() {
let mut net = FlowNetwork::new(4);
net.add_edge(0, 1, 10, 1);
net.add_edge(0, 2, 10, 5);
net.add_edge(1, 3, 10, 1);
net.add_edge(2, 3, 10, 1);
let problem = MinCostFlowProblem::source_sink(net, 0, 3, 5).unwrap();
let result = min_cost_flow(&problem).unwrap();
assert_eq!(result.flow, 5);
assert_eq!(result.cost, 10); }
#[test]
fn test_min_cost_flow_with_supplies() {
let mut net = FlowNetwork::new(4);
net.add_edge(0, 2, 10, 1);
net.add_edge(0, 3, 10, 3);
net.add_edge(1, 2, 10, 2);
net.add_edge(1, 3, 10, 1);
let supplies = vec![5, 5, -5, -5];
let problem = MinCostFlowProblem::new(net, supplies).unwrap();
let result = min_cost_flow(&problem).unwrap();
assert_eq!(result.flow, 10); assert_eq!(result.cost, 10);
}
#[test]
fn test_infeasible_supplies() {
let net = FlowNetwork::new(2);
let supplies = vec![5, 0];
let result = MinCostFlowProblem::new(net, supplies);
assert!(result.is_err());
}
}