Compare commits

..

2 Commits

Author SHA1 Message Date
5b67d74b31 checkpoint 2025-08-27 2025-08-27 00:06:12 +02:00
5e064c73ed checkpoint 2025-08-22 2025-08-22 15:28:33 +02:00
12 changed files with 196 additions and 137 deletions

View File

@@ -7,36 +7,36 @@
- Create a custom to_graph method which displays a maxflow graph
TODOs
- [ ] Implementation of Algos:
- [x] Implementation of Algos:
- [x] Ford-Fulkersson (DFS zur Berechnung des flusserhöhenden/augmentierenden Pfads)
- [x] Edmonds-Karp (BFS zur Berechnung des flusserhöhenden/augmnetierenden Pfads)
- [x] Dinic
- [ ] Goldberg-Tarjan/Preflow-Push
- [ ] Step-by-step implementation for all algos
- [x] Goldberg-Tarjan/Preflow-Push
- [x] Step-by-step implementation for all algos
- [x] Ford-Fulkerson
- [x] Edmonds-Karp
- [x] Dinic
- [ ] Goldberg-Tarjan/Preflow-Push
- [x] Goldberg-Tarjan/Preflow-Push
- [x] Mark edges which have active flows with a color
- [x] Move GUI Graph conversion to a trait of StableGraph
- [ ] Add graph positioning strategy which looks "nicer"#
- [ ] Add graph positioning strategy which looks "nicer"
- [x] Only insert nodes which are at least a distance of 40 away (default)
- [ ] Pseudo-random node positioning strategy
- [ ] Handle residual edges properly
- [x] Handle residual edges properly
- [x] Add display option for residual edges
- [x] Only show active flows (filter all edges with f=0)
- [ ] Show normal vs residual graph
- [x] Show normal vs residual graph -> not needed, residual graph is shown by default
- [x] When increasing flows, handle residual path
- [ ] Add unit tests
- [ ] With small problems to prove correctness
- [ ] For Benchmarking the algos
- [ ] Check validity of the generated flows (incoming flows = outgoing flows; flow <= capacity)
- [ ] Add info display pane
- [x] Add unit tests
- [x] With small problems to prove correctness
- [x] For Benchmarking the algos
- [x] Check validity of the generated flows (incoming flows = outgoing flows; flow <= capacity)
- [x] Add info display pane
- [x] is valid flow? (add trait to graph)
- [x] current total flow (add trait to calculate)
- [ ] number of steps
- [ ] implement PartialEq for StableGraph (not really possible -> how do you compare graphs?)
- [ ] Step-by-step vizualisation with choosable time delay
- [x] number of steps (is implemented for stepwise)
- [x] implement PartialEq for StableGraph (not really possible -> how do you compare graphs?) -> implemented a prune_zero trait & compare node/edge weights for same index
- [x] Step-by-step vizualisation with choosable time delay
- [ ] Dinic: add GUI options to show Layer Graph and Blocking flow
- [ ] Goldberg-Tarjan: show distance labels at nodes
- [x] change GUI colors to accomodate color-blindness (blue-yellow rather than red-green)
@@ -92,3 +92,10 @@ note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
thread '<unnamed>' panicked at src/algorithms/goldberg_tarjan.rs:109:238:
No distance label found
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
# Code erklären
- Graph generieren (random_generator.rs)
- GUI, Custom Layout (main.rs, layout.rs)
- Graph Hilfsfunktionen (graph.rs)
- Algorithmen (ford_fulkerson.rs, edmonds_karp.rs, dinic.rs, goldberg_tarjan.rs)

View File

@@ -82,7 +82,6 @@ impl Dinic {
visited.visit(neighbor);
let mut new_path = path.clone();
new_path.push(neighbor);
// TODO: is this right?
if neighbor == self.sink {
return Some(new_path);
} else {
@@ -113,15 +112,7 @@ impl MaxflowAlgorithm for Dinic {
// increase flow with bottleneck capacity along the augmenting path
for edge in edges {
// get source and target of edge
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
// increase flow of the forward edge with the calculated bottleneck value
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 += bottleneck_capacity;
// increase capacity of the residual edge of the with the calculated bottleneck value
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
(*residual_weight).1 += bottleneck_capacity;
self.residual_graph.add_flow(edge, bottleneck_capacity);
}
false
} else {

View File

@@ -65,15 +65,7 @@ impl MaxflowAlgorithm for EdmondsKarp {
// increase flow with bottleneck capacity along the augmenting path
for edge in edges {
// get source and target of edge
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
// increase flow of the forward edge with the calculated bottleneck value
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 += bottleneck_capacity;
// increase capacity of the residual edge of the with the calculated bottleneck value
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
(*residual_weight).1 += bottleneck_capacity;
self.residual_graph.add_flow(edge, bottleneck_capacity);
}
false
} else {

View File

@@ -37,7 +37,6 @@ impl FordFulkerson {
visited.visit(neighbor);
let mut new_path = path.clone();
new_path.push(neighbor);
// TODO: is this right?
if neighbor == self.sink {
return Some(new_path);
} else {
@@ -65,15 +64,7 @@ impl MaxflowAlgorithm for FordFulkerson {
// increase flow with bottleneck capacity along the augmenting path
for edge in edges {
// get source and target of edge
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
// increase flow of the forward edge with the calculated bottleneck value
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 += bottleneck_capacity;
// increase capacity of the residual edge of the with the calculated bottleneck value
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
(*residual_weight).1 += bottleneck_capacity;
self.residual_graph.add_flow(edge, bottleneck_capacity);
}
false
} else {

View File

@@ -97,13 +97,11 @@ impl MaxflowAlgorithm for GoldbergTarjan {
// if the distance labels were not already computed, compute them now
if self.distance_label == None {
self.compute_distances();
//println!("distance labeling: {:?}", self.distance_label);
}
// Main loop: while there is an active node, continue
if let Some((node, excess)) = self.active_node() {
// find all outgoing edges with available capacity
//println!("active node {:?} source {:?} sink {:?}", node, self.source, self.sink);
let valid_edges: Vec<petgraph::prelude::EdgeIndex> = self.residual_graph
.edges_directed(node, Direction::Outgoing)
.filter(|e| (e.weight().1 - e.weight().0) > 0 )
@@ -112,20 +110,11 @@ impl MaxflowAlgorithm for GoldbergTarjan {
.collect::<Vec<_>>();
if valid_edges.len() > 0 {
// node has valid edges -> push (move min[excess, capacity] to first valid edge)
//println!("push");
let edge = valid_edges[0];
// let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
let available_capacity = self.residual_graph.edge_weight(edge).expect("Invalid edge index").1 - self.residual_graph.edge_weight(edge).expect("Invalid edge index").0;
let additional_flow = min(excess, available_capacity);
self.residual_graph.add_flow(edge, additional_flow);
// // increase flow of the forward edge with the calculated bottleneck value
// let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
// (*weight).0 += additional_flow;
// // increase capacity of the residual edge of the with the calculated bottleneck value
// let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
// let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
// (*residual_weight).1 += additional_flow;
} else {
// node has no valid edges -> relabel (increase node distance label to min[neighbors] + 1)
let neighbors = self.residual_graph
@@ -134,8 +123,8 @@ impl MaxflowAlgorithm for GoldbergTarjan {
.sorted_by_key(|e| self.distance_label.as_ref().expect("distance labelling not present").get(&e.target()))
.map(|e| e.target())
.collect::<Vec<_>>();
assert!(neighbors.len() > 0);
let lowest_distance = self.distance_label.as_ref().expect("distance labelling not present").get(&neighbors[0]).expect("No distance label found").clone();
//println!("relabel {} + 1 (index: {:?})", lowest_distance, node);
self.distance_label.as_mut().expect("distance labelling not present").insert(node, lowest_distance + 1);
}
false

View File

@@ -76,9 +76,9 @@ pub trait FlowGraph {
fn reset_flow(&mut self);
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn prune_zero(&self) -> StableGraph<(f32, f32), (u64, u64)>;
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64;
@@ -153,6 +153,19 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
}
}
// removes all edges where the current flow is zero & returns a pruned graph
fn prune_zero(&self) -> StableGraph<(f32, f32), (u64, u64)> {
let mut zero_graph = self.clone();
for edge in zero_graph.edge_indices().collect::<Vec<_>>() {
let weight = zero_graph.edge_weight(edge).expect("edge not found");
if weight.0 == 0 {
zero_graph.remove_edge(edge);
}
}
zero_graph
}
// check if flow is valid:
// - for every edge, the flow doesn't exceed the capacity
// - for every node, the amount of incoming flows is equal to the amount of outgoing flows
@@ -183,23 +196,6 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
true
}
// checks if there exists a saturated cut in the graph (a bip)
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool {
let mut s = HashSet::new();
let mut t = HashSet::new();
for node in self.node_indices().into_iter().collect::<Vec<_>>() {
if has_path_connecting(self, source, node, None) {
s.insert(node);
} else {
t.insert(node);
}
}
return true;
}
// TODO: assert that incoming flows at sink are equal to outgoing flows at source (otherwise return 0)
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64 {
let outgoing_flows_source: u64 = self.edges_directed(source, Direction::Outgoing).map(|e| e.weight().0).sum();
let incoming_flows_source: u64 = self.edges_directed(source, Direction::Incoming).map(|e| e.weight().0).sum();
@@ -207,7 +203,9 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
let incoming_flows_sink: u64 = self.edges_directed(sink, Direction::Incoming).map(|e| e.weight().0).sum();
let total_flow_source = outgoing_flows_source - incoming_flows_source;
let total_flow_sink = incoming_flows_sink - outgoing_flows_sink;
assert!(total_flow_sink == total_flow_source);
if total_flow_sink != total_flow_source {
return 0;
}
return total_flow_source;
}
@@ -233,22 +231,38 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
self.edge_weight(edge).expect("edge not found").1 - self.edge_weight(edge).expect("edge not found").0
}
fn add_flow(&mut self, edge: EdgeIndex, flow: u64) {
// search for reverse edge and subtract existing flow if possible
let (source, target) = self.edge_endpoints(edge).expect("edge not connected");
let residual_edge = self.find_edge(target, source).expect("residual edge not found");
let residual_weight: &mut (u64, u64) = self.edge_weight_mut(residual_edge).expect("residual edge not found");
let forward_flow = i128::from(flow) - i128::from((*residual_weight).0);
// subtract minimum of additional_flow and residual_weight
if residual_weight.0 > 0 {
(*residual_weight).0 -= min(residual_weight.0, flow);
// adds the specified flow to the edge of a residual graph
fn add_flow(&mut self, forward_edge: EdgeIndex, flow: u64) {
// search for reverse edge
let (source, target) = self.edge_endpoints(forward_edge).expect("edge not connected");
let reverse_edge = self.find_edge(target, source).expect("reverse edge not found");
// calculate how much flow needs to be added on the reverse edge
let current_reverse_flow = self.edge_weight(reverse_edge).expect("reverse erge not found").0;
// maximum flow we can subtract from reverse edge
let reverse_flow = min(current_reverse_flow, flow);
let forward_flow = flow - reverse_flow;
if reverse_flow > 0 {
// decrease reverse flow
let reverse_weight: &mut (u64, u64) = self.edge_weight_mut(reverse_edge).expect("reverse edge not found");
(*reverse_weight).0 -= reverse_flow;
assert!(reverse_weight.0 <= reverse_weight.1);
// decrease forward capacity
let forward_weight: &mut (u64, u64) = self.edge_weight_mut(forward_edge).expect("edge not found");
(*forward_weight).1 -= reverse_flow;
assert!(forward_weight.0 <= forward_weight.1);
}
assert!((*residual_weight).0 <= (*residual_weight).1);
// add remaining flow to forward edge
let forward_weight: &mut (u64, u64) = self.edge_weight_mut(edge).expect("edge not found");
if forward_flow > 0 {
(*forward_weight).0 += u64::try_from(forward_flow).expect("error casting flow to u64");
// increase forward flow
let forward_weight: &mut (u64, u64) = self.edge_weight_mut(forward_edge).expect("edge not found");
(*forward_weight).0 += forward_flow;
assert!(forward_weight.0 <= forward_weight.1);
// increase reverse capacity
let reverse_weight: &mut (u64, u64) = self.edge_weight_mut(reverse_edge).expect("reverse edge not found");
(*reverse_weight).1 += forward_flow;
assert!(reverse_weight.0 <= reverse_weight.1);
}
assert!((*forward_weight).0 <= (*forward_weight).1);
}
}

View File

@@ -30,9 +30,9 @@ impl<E: Clone> From<EdgeProps<E>> for CustomEdgeShape {
label_text: props.label,
width: 1.,
tip_size: 10.,
tip_angle: std::f32::consts::TAU / 30.,
curve_size: 15.,
tip_size: 6.,
tip_angle: std::f32::consts::TAU / 20.,
curve_size: 10.,
loop_size: 3.,
}
}

View File

@@ -27,6 +27,7 @@ pub struct MaxflowApp {
display_active_flows: bool,
display_layer_graph: bool,
algorithm_finished: bool,
num_steps: u64,
test_thread: Option<JoinHandle<String>>,
test_results: String,
test_results_open: bool,
@@ -48,6 +49,7 @@ impl MaxflowApp {
display_active_flows: false,
display_layer_graph: false,
algorithm_finished: false,
num_steps: 0,
test_thread: None,
test_results: String::default(),
test_results_open: false,
@@ -61,6 +63,8 @@ impl MaxflowApp {
fn reset_state(&mut self) {
// undo finished run
self.algorithm_finished = false;
// set steps to zero
self.num_steps = 0;
// reinitialize the current graph from the problem
self.current_graph = self.problem.g.clone();
// assign new maxflow problem to the graph
@@ -120,21 +124,25 @@ impl MaxflowApp {
match &mut self.algorithm {
MaxflowAlgorithmEnum::FordFulkerson(x) => {
let finished = x.step();
self.num_steps += 1;
self.current_graph = x.graph();
finished
},
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
let finished = x.step();
self.num_steps += 1;
self.current_graph = x.graph();
finished
},
MaxflowAlgorithmEnum::Dinic(x) => {
let finished = x.step();
self.num_steps += 1;
self.current_graph = x.graph();
finished
},
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
let finished = x.step();
self.num_steps += 1;
self.current_graph = x.graph();
finished
}
@@ -159,7 +167,6 @@ impl App for MaxflowApp {
ui.add(egui::DragValue::new(&mut self.node_count).range(2..=50));
ui.label("maximum capacity");
ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100));
// TODO: add generation strategy (random, pseudo-random)
if ui.button("generate graph").clicked() {
self.problem = random_generator::MaxflowProblem::new_planar_graph(self.node_count, self.max_capacity);
self.reset_state();
@@ -234,6 +241,8 @@ impl App for MaxflowApp {
ui.label(format!("is valid flow: {}", self.current_graph.valid_flow(self.problem.s, self.problem.t)));
ui.label(format!("total flow: {}", self.current_graph.total_flow(self.problem.s, self.problem.t)));
ui.label(format!("number of steps: {}", self.num_steps))
});
CollapsingHeader::new("Tests")

View File

@@ -26,7 +26,7 @@ impl MaxflowProblem {
// check if node is too close to another node (distance < 40)
let mut too_close = true;
let (mut x, mut y) = (0.0, 0.0);
// this is a poor attempt at modeling a do..while look in Rust
// this is a poor attempt at modeling a do..while loop in Rust
while too_close {
too_close = false;
x = rng.gen_range(0.0..=400.0);
@@ -44,7 +44,6 @@ impl MaxflowProblem {
// select source (s) and sink (t)
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
// TODO: this probably takes forever...
// iterate over all possible edges
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
for pair in nodes.clone().into_iter().combinations(2) {

View File

@@ -12,13 +12,7 @@ fn solve_problem_all_algos(problem: &MaxflowProblem) -> (f64, f64, f64, f64) {
let mut durations = vec![0.0; 4];
for (i,a) in instances.iter_mut().enumerate() {
let now = Instant::now();
match a {
MaxflowAlgorithmEnum::GoldbergTarjan(_) => { a.run(); },
_ => {
a.run();
}
}
//a.run();
durations[i] = now.elapsed().as_secs_f64();
// check if the flow is valid
@@ -27,7 +21,6 @@ fn solve_problem_all_algos(problem: &MaxflowProblem) -> (f64, f64, f64, f64) {
// check if all algorithms return the same total flow
let total_flows: Vec<u64> = instances.iter_mut().map(|x| x.graph().total_flow(problem.s, problem.t)).collect();
println!("total flows: {:?}", total_flows);
assert!(total_flows.iter().all(|&x| x == total_flows[0]), "algorithms return different total flows");
return durations.into_iter().collect_tuple().unwrap();
@@ -37,6 +30,7 @@ fn run_tests(test_tuples: Vec<(u64, u64, u64)>) -> String {
let mut response = String::new();
for (problem_count, nodes, capacity) in test_tuples {
println!("Running test: {} times, {} nodes, {} capacity", problem_count, nodes, capacity);
response += format!("=== Testing with problem_count={}, nodes={}, capacity={}\n", problem_count, nodes, capacity).as_str();
let mut problems: Vec<_> = Vec::new();
for _ in 0..problem_count {
@@ -61,10 +55,11 @@ pub fn run_small_tests() -> String {
let test_tuples = [
// (number of problems, number of nodes, max. capacity)
(10, 10, 10),
(10, 10, 100),
(10, 50, 10),
(10, 50, 100),
(10, 100, 10),
(10, 100, 100),
(10, 200, 10),
(10, 200, 100)
(10, 100, 100)
];
return run_tests(test_tuples.into());
}
@@ -75,13 +70,12 @@ pub fn run_large_tests() -> String {
(10, 100, 10),
(10, 100, 100),
(10, 100, 1000),
(10, 500, 10),
(10, 500, 100),
(10, 500, 1000),
(10, 1000, 10),
(10, 1000, 100),
(10, 1000, 1000),
(10, 10000, 10)
(10, 200, 10),
(10, 200, 100),
(10, 200, 1000),
(10, 300, 10),
(10, 300, 100),
(10, 300, 1000)
];
return run_tests(test_tuples.into());
}

54
tests.txt Normal file
View File

@@ -0,0 +1,54 @@
=== Testing with problem_count=10, nodes=100, capacity=10
Ford-Fulkerson: 0.00019723s
Edmonds-Karp: 0.00017104s
Dinic: 0.00114540s
Goldberg-Tarjan: 0.11118784s
=== Testing with problem_count=10, nodes=100, capacity=100
Ford-Fulkerson: 0.00044646s
Edmonds-Karp: 0.00030087s
Dinic: 0.00212037s
Goldberg-Tarjan: 0.15394399s
=== Testing with problem_count=10, nodes=100, capacity=1000
Ford-Fulkerson: 0.00028169s
Edmonds-Karp: 0.00017956s
Dinic: 0.00128019s
Goldberg-Tarjan: 0.11455003s
=== Testing with problem_count=10, nodes=200, capacity=10
Ford-Fulkerson: 0.00057035s
Edmonds-Karp: 0.00042638s
Dinic: 0.00270890s
Goldberg-Tarjan: 1.70483265s
=== Testing with problem_count=10, nodes=200, capacity=100
Ford-Fulkerson: 0.00121046s
Edmonds-Karp: 0.00045426s
Dinic: 0.00301752s
Goldberg-Tarjan: 1.48397911s
=== Testing with problem_count=10, nodes=200, capacity=1000
Ford-Fulkerson: 0.00183244s
Edmonds-Karp: 0.00062984s
Dinic: 0.00411386s
Goldberg-Tarjan: 1.49089764s
=== Testing with problem_count=10, nodes=300, capacity=10
Ford-Fulkerson: 0.00086230s
Edmonds-Karp: 0.00071712s
Dinic: 0.00423448s
Goldberg-Tarjan: 5.84010118s
=== Testing with problem_count=10, nodes=300, capacity=100
Ford-Fulkerson: 0.00164213s
Edmonds-Karp: 0.00040957s
Dinic: 0.00322493s
Goldberg-Tarjan: 1.97116912s
=== Testing with problem_count=10, nodes=300, capacity=1000
Ford-Fulkerson: 0.00362413s
Edmonds-Karp: 0.00094858s
Dinic: 0.00600265s
Goldberg-Tarjan: 5.16341617s

View File

@@ -20,27 +20,9 @@ fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, Sta
g.add_edge(one, two, (0,2));
g.add_edge(two, four, (0,9));
g.add_edge(three, t, (0,10));
g.add_edge(four, three, (0,6));
g.add_edge(four, three, (0,5)); // changed capacity to 5 to enforce a single optimal solution
g.add_edge(four, t, (0,10));
// TODO:
// let mut m: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
// let s = m.add_node((0., 5.));
// let one = m.add_node((10., 0.));
// let two = m.add_node((10., 10.));
// let three = m.add_node((20., 0.));
// let four = m.add_node((20., 10.));
// let t = m.add_node((30., 5.));
// m.add_edge(s, one, (10, 10));
// m.add_edge(s, two, (9, 10));
// m.add_edge(one, three, (4, 4));
// m.add_edge(one, four, (6, 8));
// m.add_edge(one, two, (0,2));
// m.add_edge(two, four, (9,9));
// m.add_edge(three, t, (9,10));
// m.add_edge(four, three, (5,6));
// m.add_edge(four, t, (10,10));
let mut m = g.clone();
m.update_edge(s, one, (10, 10));
m.update_edge(s, two, (9, 10));
@@ -49,7 +31,7 @@ fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, Sta
m.update_edge(one, two, (0,2));
m.update_edge(two, four, (9,9));
m.update_edge(three, t, (9,10));
m.update_edge(four, three, (5,6));
m.update_edge(four, three, (5,5)); // changed capacity to 5 to enforce a single optimal solution
m.update_edge(four, t, (10,10));
return (g, m, s, t);
@@ -57,11 +39,12 @@ fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, Sta
fn graph_equal(one: StableGraph<(f32, f32), (u64, u64)>, other: StableGraph<(f32, f32), (u64, u64)>) -> bool {
// ensure all edges have the same weights
println!("Graph 1: {:?}", one);
println!("Graph 2: {:?}", other);
let edges = one.edge_indices().map(|e| e).collect::<Vec<_>>();
for edge in edges {
if one.edge_weight(edge).expect("edge index not found") != other.edge_weight(edge).expect("edge index not found") {
println!("Edge weights don't match");
println!("Graph 1: {:?}", one);
println!("Graph 2: {:?}", other);
return false;
}
}
@@ -70,6 +53,9 @@ fn graph_equal(one: StableGraph<(f32, f32), (u64, u64)>, other: StableGraph<(f32
let nodes = one.node_indices().map(|n| n).collect::<Vec<_>>();
for node in nodes {
if one.node_weight(node).expect("node index not found") != other.node_weight(node).expect("node index not found") {
println!("Node weights don't match");
println!("Graph 1: {:?}", one);
println!("Graph 2: {:?}", other);
return false;
}
}
@@ -85,5 +71,38 @@ fn test_ford_fulkerson() {
let mut algo = FordFulkerson::from_problem(&problem);
let solution = algo.run();
assert!(graph_equal(solution, m.residual()));
assert!(graph_equal(solution.prune_zero(), m.residual().prune_zero()));
}
#[test]
fn test_edmonds_karp() {
let (g, m, s, t) = generate_small_maxflow_example();
let problem = MaxflowProblem::from(g, s, t);
let mut algo = EdmondsKarp::from_problem(&problem);
let solution = algo.run();
assert!(graph_equal(solution.prune_zero(), m.residual().prune_zero()));
}
#[test]
fn test_dinic() {
let (g, m, s, t) = generate_small_maxflow_example();
let problem = MaxflowProblem::from(g, s, t);
let mut algo = Dinic::from_problem(&problem);
let solution = algo.run();
assert!(graph_equal(solution.prune_zero(), m.residual().prune_zero()));
}
#[test]
fn test_goldberg_tarjan() {
let (g, m, s, t) = generate_small_maxflow_example();
let problem = MaxflowProblem::from(g, s, t);
let mut algo = GoldbergTarjan::from_problem(&problem);
let solution = algo.run();
assert!(graph_equal(solution.prune_zero(), m.residual().prune_zero()));
}