use crate::core::{Graph, IgraphError, IgraphResult};
#[derive(Debug, Clone)]
pub struct ResidualGraphResult {
pub graph: Graph,
pub capacity: Vec<f64>,
}
pub fn residual_graph(
graph: &Graph,
capacity: &[f64],
flow: &[f64],
) -> IgraphResult<ResidualGraphResult> {
let m = graph.ecount();
if capacity.len() != m {
return Err(IgraphError::InvalidArgument(format!(
"residual_graph: capacity length {} != edge count {m}",
capacity.len()
)));
}
if flow.len() != m {
return Err(IgraphError::InvalidArgument(format!(
"residual_graph: flow length {} != edge count {m}",
flow.len()
)));
}
let n = graph.vcount();
let mut new_graph = Graph::new(n, true)?;
let mut res_capacity: Vec<f64> = Vec::new();
for eid in 0..m {
let residual = capacity[eid] - flow[eid];
if residual > 0.0 {
let eid32 = u32::try_from(eid).map_err(|_| {
IgraphError::InvalidArgument("residual_graph: edge id overflow".into())
})?;
let (from, to) = graph.edge(eid32)?;
new_graph.add_edge(from, to)?;
res_capacity.push(residual);
}
}
Ok(ResidualGraphResult {
graph: new_graph,
capacity: res_capacity,
})
}
pub fn reverse_residual_graph(
graph: &Graph,
capacity: Option<&[f64]>,
flow: &[f64],
) -> IgraphResult<Graph> {
let m = graph.ecount();
if let Some(cap) = capacity {
if cap.len() != m {
return Err(IgraphError::InvalidArgument(format!(
"reverse_residual_graph: capacity length {} != edge count {m}",
cap.len()
)));
}
}
if flow.len() != m {
return Err(IgraphError::InvalidArgument(format!(
"reverse_residual_graph: flow length {} != edge count {m}",
flow.len()
)));
}
let n = graph.vcount();
let mut new_graph = Graph::new(n, true)?;
for eid in 0..m {
let cap = capacity.map_or(1.0, |c| c[eid]);
let eid32 = u32::try_from(eid).map_err(|_| {
IgraphError::InvalidArgument("reverse_residual_graph: edge id overflow".into())
})?;
let (from, to) = graph.edge(eid32)?;
if flow[eid] > 0.0 {
new_graph.add_edge(from, to)?;
}
if flow[eid] < cap {
new_graph.add_edge(to, from)?;
}
}
Ok(new_graph)
}
#[cfg(test)]
#[allow(clippy::float_cmp)]
mod tests {
use super::*;
#[test]
fn residual_empty() {
let g = Graph::new(0, true).unwrap();
let result = residual_graph(&g, &[], &[]).unwrap();
assert_eq!(result.graph.vcount(), 0);
assert_eq!(result.graph.ecount(), 0);
}
#[test]
fn residual_no_flow() {
let mut g = Graph::new(3, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
let cap = vec![10.0, 5.0];
let flow = vec![0.0, 0.0];
let result = residual_graph(&g, &cap, &flow).unwrap();
assert_eq!(result.graph.ecount(), 2);
assert_eq!(result.capacity, vec![10.0, 5.0]);
}
#[test]
fn residual_saturated() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
let cap = vec![5.0];
let flow = vec![5.0];
let result = residual_graph(&g, &cap, &flow).unwrap();
assert_eq!(result.graph.ecount(), 0);
assert!(result.capacity.is_empty());
}
#[test]
fn residual_partial_flow() {
let mut g = Graph::new(4, true).unwrap();
g.add_edge(0, 1).unwrap();
g.add_edge(1, 2).unwrap();
g.add_edge(0, 3).unwrap();
let cap = vec![10.0, 10.0, 5.0];
let flow = vec![7.0, 10.0, 3.0];
let result = residual_graph(&g, &cap, &flow).unwrap();
assert_eq!(result.graph.ecount(), 2);
assert_eq!(result.capacity, vec![3.0, 2.0]);
}
#[test]
fn residual_invalid_capacity_len() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
assert!(residual_graph(&g, &[1.0, 2.0], &[0.0]).is_err());
}
#[test]
fn residual_invalid_flow_len() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
assert!(residual_graph(&g, &[1.0], &[0.0, 0.0]).is_err());
}
#[test]
fn reverse_residual_empty() {
let g = Graph::new(0, true).unwrap();
let result = reverse_residual_graph(&g, Some(&[]), &[]).unwrap();
assert_eq!(result.vcount(), 0);
assert_eq!(result.ecount(), 0);
}
#[test]
fn reverse_residual_no_flow() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
let cap = vec![5.0];
let flow = vec![0.0];
let result = reverse_residual_graph(&g, Some(&cap), &flow).unwrap();
assert_eq!(result.ecount(), 1);
assert_eq!(result.edge(0).unwrap(), (1, 0));
}
#[test]
fn reverse_residual_full_flow() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
let cap = vec![5.0];
let flow = vec![5.0];
let result = reverse_residual_graph(&g, Some(&cap), &flow).unwrap();
assert_eq!(result.ecount(), 1);
assert_eq!(result.edge(0).unwrap(), (0, 1));
}
#[test]
fn reverse_residual_partial() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
let cap = vec![10.0];
let flow = vec![7.0];
let result = reverse_residual_graph(&g, Some(&cap), &flow).unwrap();
assert_eq!(result.ecount(), 2);
assert_eq!(result.edge(0).unwrap(), (0, 1));
assert_eq!(result.edge(1).unwrap(), (1, 0));
}
#[test]
fn reverse_residual_no_capacity() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
let flow = vec![0.5];
let result = reverse_residual_graph(&g, None, &flow).unwrap();
assert_eq!(result.ecount(), 2);
}
#[test]
fn reverse_residual_invalid_len() {
let mut g = Graph::new(2, true).unwrap();
g.add_edge(0, 1).unwrap();
assert!(reverse_residual_graph(&g, Some(&[1.0, 2.0]), &[0.0]).is_err());
assert!(reverse_residual_graph(&g, Some(&[1.0]), &[0.0, 0.0]).is_err());
}
}