Summary
ford_fulkerson allocates edge_to with network.node_count() but indexes it with NodeIndexable::to_index(...). On StableGraph, deleted nodes leave holes: node_count() counts live nodes, while to_index() uses sparse node indices.
Current behavior
A valid StableGraph with a removed node can panic inside the augmenting-path search when the highest live node index is larger than node_count() - 1.
Concrete regression test
This is small enough to add directly as a crate regression test in crates/petgraph/tests/ford_fulkerson.rs:
use petgraph::{
algo::ford_fulkerson,
prelude::StableDiGraph,
};
#[test]
fn test_ford_fulkerson_stable_graph_node_hole_regression() {
let mut graph = StableDiGraph::<(), u32>::new();
let source = graph.add_node(());
let removed = graph.add_node(());
let sink = graph.add_node(());
graph.add_edge(source, sink, 1);
graph.remove_node(removed);
let (max_flow, _) = ford_fulkerson(&graph, source, sink);
assert_eq!(1, max_flow);
}
This leaves a valid directed StableGraph with live node slots 0 and 2, so node_count() == 2 while to_index(sink) == 2.
The test fails with index out of bounds: the len is 2 but the index is 2.
After the fix, the test should pass and report a max flow of 1.
Root cause
The algorithm sizes node scratch state in the compact live-node domain but accesses it in the sparse index domain.
Suggested fix
Allocate edge_to from network.node_bound() rather than network.node_count().
Summary
ford_fulkersonallocatesedge_towithnetwork.node_count()but indexes it withNodeIndexable::to_index(...). OnStableGraph, deleted nodes leave holes:node_count()counts live nodes, whileto_index()uses sparse node indices.Current behavior
A valid
StableGraphwith a removed node can panic inside the augmenting-path search when the highest live node index is larger thannode_count() - 1.Concrete regression test
This is small enough to add directly as a crate regression test in
crates/petgraph/tests/ford_fulkerson.rs:This leaves a valid directed
StableGraphwith live node slots0and2, sonode_count() == 2whileto_index(sink) == 2.The test fails with
index out of bounds: the len is 2 but the index is 2.After the fix, the test should pass and report a max flow of
1.Root cause
The algorithm sizes node scratch state in the compact live-node domain but accesses it in the sparse index domain.
Suggested fix
Allocate
edge_tofromnetwork.node_bound()rather thannetwork.node_count().