checkpoint 2025-08-22
This commit is contained in:
30
README.md
30
README.md
@@ -7,36 +7,36 @@
|
|||||||
- Create a custom to_graph method which displays a maxflow graph
|
- Create a custom to_graph method which displays a maxflow graph
|
||||||
|
|
||||||
TODOs
|
TODOs
|
||||||
- [ ] Implementation of Algos:
|
- [x] Implementation of Algos:
|
||||||
- [x] Ford-Fulkersson (DFS zur Berechnung des flusserhöhenden/augmentierenden Pfads)
|
- [x] Ford-Fulkersson (DFS zur Berechnung des flusserhöhenden/augmentierenden Pfads)
|
||||||
- [x] Edmonds-Karp (BFS zur Berechnung des flusserhöhenden/augmnetierenden Pfads)
|
- [x] Edmonds-Karp (BFS zur Berechnung des flusserhöhenden/augmnetierenden Pfads)
|
||||||
- [x] Dinic
|
- [x] Dinic
|
||||||
- [ ] Goldberg-Tarjan/Preflow-Push
|
- [x] Goldberg-Tarjan/Preflow-Push
|
||||||
- [ ] Step-by-step implementation for all algos
|
- [x] Step-by-step implementation for all algos
|
||||||
- [x] Ford-Fulkerson
|
- [x] Ford-Fulkerson
|
||||||
- [x] Edmonds-Karp
|
- [x] Edmonds-Karp
|
||||||
- [x] Dinic
|
- [x] Dinic
|
||||||
- [ ] Goldberg-Tarjan/Preflow-Push
|
- [x] Goldberg-Tarjan/Preflow-Push
|
||||||
- [x] Mark edges which have active flows with a color
|
- [x] Mark edges which have active flows with a color
|
||||||
- [x] Move GUI Graph conversion to a trait of StableGraph
|
- [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)
|
- [x] Only insert nodes which are at least a distance of 40 away (default)
|
||||||
- [ ] Pseudo-random node positioning strategy
|
- [ ] Pseudo-random node positioning strategy
|
||||||
- [ ] Handle residual edges properly
|
- [x] Handle residual edges properly
|
||||||
- [x] Add display option for residual edges
|
- [x] Add display option for residual edges
|
||||||
- [x] Only show active flows (filter all edges with f=0)
|
- [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
|
- [x] When increasing flows, handle residual path
|
||||||
- [ ] Add unit tests
|
- [x] Add unit tests
|
||||||
- [ ] With small problems to prove correctness
|
- [x] With small problems to prove correctness
|
||||||
- [ ] For Benchmarking the algos
|
- [x] For Benchmarking the algos
|
||||||
- [ ] Check validity of the generated flows (incoming flows = outgoing flows; flow <= capacity)
|
- [x] Check validity of the generated flows (incoming flows = outgoing flows; flow <= capacity)
|
||||||
- [ ] Add info display pane
|
- [x] Add info display pane
|
||||||
- [x] is valid flow? (add trait to graph)
|
- [x] is valid flow? (add trait to graph)
|
||||||
- [x] current total flow (add trait to calculate)
|
- [x] current total flow (add trait to calculate)
|
||||||
- [ ] number of steps
|
- [x] number of steps (is implemented for stepwise)
|
||||||
- [ ] implement PartialEq for StableGraph (not really possible -> how do you compare graphs?)
|
- [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
|
||||||
- [ ] Step-by-step vizualisation with choosable time delay
|
- [x] Step-by-step vizualisation with choosable time delay
|
||||||
- [ ] Dinic: add GUI options to show Layer Graph and Blocking flow
|
- [ ] Dinic: add GUI options to show Layer Graph and Blocking flow
|
||||||
- [ ] Goldberg-Tarjan: show distance labels at nodes
|
- [ ] Goldberg-Tarjan: show distance labels at nodes
|
||||||
- [x] change GUI colors to accomodate color-blindness (blue-yellow rather than red-green)
|
- [x] change GUI colors to accomodate color-blindness (blue-yellow rather than red-green)
|
||||||
|
|||||||
@@ -97,13 +97,11 @@ impl MaxflowAlgorithm for GoldbergTarjan {
|
|||||||
// if the distance labels were not already computed, compute them now
|
// if the distance labels were not already computed, compute them now
|
||||||
if self.distance_label == None {
|
if self.distance_label == None {
|
||||||
self.compute_distances();
|
self.compute_distances();
|
||||||
//println!("distance labeling: {:?}", self.distance_label);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop: while there is an active node, continue
|
// Main loop: while there is an active node, continue
|
||||||
if let Some((node, excess)) = self.active_node() {
|
if let Some((node, excess)) = self.active_node() {
|
||||||
// find all outgoing edges with available capacity
|
// 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
|
let valid_edges: Vec<petgraph::prelude::EdgeIndex> = self.residual_graph
|
||||||
.edges_directed(node, Direction::Outgoing)
|
.edges_directed(node, Direction::Outgoing)
|
||||||
.filter(|e| (e.weight().1 - e.weight().0) > 0 )
|
.filter(|e| (e.weight().1 - e.weight().0) > 0 )
|
||||||
@@ -112,20 +110,11 @@ impl MaxflowAlgorithm for GoldbergTarjan {
|
|||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
if valid_edges.len() > 0 {
|
if valid_edges.len() > 0 {
|
||||||
// node has valid edges -> push (move min[excess, capacity] to first valid edge)
|
// node has valid edges -> push (move min[excess, capacity] to first valid edge)
|
||||||
//println!("push");
|
|
||||||
let edge = valid_edges[0];
|
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 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);
|
let additional_flow = min(excess, available_capacity);
|
||||||
self.residual_graph.add_flow(edge, additional_flow);
|
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 {
|
} else {
|
||||||
// node has no valid edges -> relabel (increase node distance label to min[neighbors] + 1)
|
// node has no valid edges -> relabel (increase node distance label to min[neighbors] + 1)
|
||||||
let neighbors = self.residual_graph
|
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()))
|
.sorted_by_key(|e| self.distance_label.as_ref().expect("distance labelling not present").get(&e.target()))
|
||||||
.map(|e| e.target())
|
.map(|e| e.target())
|
||||||
.collect::<Vec<_>>();
|
.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();
|
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);
|
self.distance_label.as_mut().expect("distance labelling not present").insert(node, lowest_distance + 1);
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|||||||
62
src/graph.rs
62
src/graph.rs
@@ -76,6 +76,8 @@ pub trait FlowGraph {
|
|||||||
|
|
||||||
fn reset_flow(&mut self);
|
fn reset_flow(&mut self);
|
||||||
|
|
||||||
|
fn prune_zero(&self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
|
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
|
||||||
|
|
||||||
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool;
|
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool;
|
||||||
@@ -153,6 +155,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:
|
// check if flow is valid:
|
||||||
// - for every edge, the flow doesn't exceed the capacity
|
// - 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
|
// - for every node, the amount of incoming flows is equal to the amount of outgoing flows
|
||||||
@@ -199,7 +214,6 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
|
|||||||
return true;
|
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 {
|
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 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();
|
let incoming_flows_source: u64 = self.edges_directed(source, Direction::Incoming).map(|e| e.weight().0).sum();
|
||||||
@@ -207,7 +221,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 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_source = outgoing_flows_source - incoming_flows_source;
|
||||||
let total_flow_sink = incoming_flows_sink - outgoing_flows_sink;
|
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;
|
return total_flow_source;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -233,22 +249,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
|
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) {
|
// adds the specified flow to the edge of a residual graph
|
||||||
// search for reverse edge and subtract existing flow if possible
|
fn add_flow(&mut self, forward_edge: EdgeIndex, flow: u64) {
|
||||||
let (source, target) = self.edge_endpoints(edge).expect("edge not connected");
|
// search for reverse edge
|
||||||
let residual_edge = self.find_edge(target, source).expect("residual edge not found");
|
let (source, target) = self.edge_endpoints(forward_edge).expect("edge not connected");
|
||||||
let residual_weight: &mut (u64, u64) = self.edge_weight_mut(residual_edge).expect("residual edge not found");
|
let reverse_edge = self.find_edge(target, source).expect("reverse edge not found");
|
||||||
let forward_flow = i128::from(flow) - i128::from((*residual_weight).0);
|
|
||||||
// subtract minimum of additional_flow and residual_weight
|
// calculate how much flow needs to be added on the reverse edge
|
||||||
if residual_weight.0 > 0 {
|
let current_reverse_flow = self.edge_weight(reverse_edge).expect("reverse erge not found").0;
|
||||||
(*residual_weight).0 -= min(residual_weight.0, flow);
|
// 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
|
// 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 {
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/main.rs
10
src/main.rs
@@ -27,6 +27,7 @@ pub struct MaxflowApp {
|
|||||||
display_active_flows: bool,
|
display_active_flows: bool,
|
||||||
display_layer_graph: bool,
|
display_layer_graph: bool,
|
||||||
algorithm_finished: bool,
|
algorithm_finished: bool,
|
||||||
|
num_steps: u64,
|
||||||
test_thread: Option<JoinHandle<String>>,
|
test_thread: Option<JoinHandle<String>>,
|
||||||
test_results: String,
|
test_results: String,
|
||||||
test_results_open: bool,
|
test_results_open: bool,
|
||||||
@@ -48,6 +49,7 @@ impl MaxflowApp {
|
|||||||
display_active_flows: false,
|
display_active_flows: false,
|
||||||
display_layer_graph: false,
|
display_layer_graph: false,
|
||||||
algorithm_finished: false,
|
algorithm_finished: false,
|
||||||
|
num_steps: 0,
|
||||||
test_thread: None,
|
test_thread: None,
|
||||||
test_results: String::default(),
|
test_results: String::default(),
|
||||||
test_results_open: false,
|
test_results_open: false,
|
||||||
@@ -61,6 +63,8 @@ impl MaxflowApp {
|
|||||||
fn reset_state(&mut self) {
|
fn reset_state(&mut self) {
|
||||||
// undo finished run
|
// undo finished run
|
||||||
self.algorithm_finished = false;
|
self.algorithm_finished = false;
|
||||||
|
// set steps to zero
|
||||||
|
self.num_steps = 0;
|
||||||
// reinitialize the current graph from the problem
|
// reinitialize the current graph from the problem
|
||||||
self.current_graph = self.problem.g.clone();
|
self.current_graph = self.problem.g.clone();
|
||||||
// assign new maxflow problem to the graph
|
// assign new maxflow problem to the graph
|
||||||
@@ -120,21 +124,25 @@ impl MaxflowApp {
|
|||||||
match &mut self.algorithm {
|
match &mut self.algorithm {
|
||||||
MaxflowAlgorithmEnum::FordFulkerson(x) => {
|
MaxflowAlgorithmEnum::FordFulkerson(x) => {
|
||||||
let finished = x.step();
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
self.current_graph = x.graph();
|
self.current_graph = x.graph();
|
||||||
finished
|
finished
|
||||||
},
|
},
|
||||||
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
|
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
|
||||||
let finished = x.step();
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
self.current_graph = x.graph();
|
self.current_graph = x.graph();
|
||||||
finished
|
finished
|
||||||
},
|
},
|
||||||
MaxflowAlgorithmEnum::Dinic(x) => {
|
MaxflowAlgorithmEnum::Dinic(x) => {
|
||||||
let finished = x.step();
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
self.current_graph = x.graph();
|
self.current_graph = x.graph();
|
||||||
finished
|
finished
|
||||||
},
|
},
|
||||||
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
|
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
|
||||||
let finished = x.step();
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
self.current_graph = x.graph();
|
self.current_graph = x.graph();
|
||||||
finished
|
finished
|
||||||
}
|
}
|
||||||
@@ -234,6 +242,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!("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!("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")
|
CollapsingHeader::new("Tests")
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ impl MaxflowProblem {
|
|||||||
// select source (s) and sink (t)
|
// select source (s) and sink (t)
|
||||||
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
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
|
// iterate over all possible edges
|
||||||
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
||||||
for pair in nodes.clone().into_iter().combinations(2) {
|
for pair in nodes.clone().into_iter().combinations(2) {
|
||||||
@@ -141,4 +140,9 @@ impl MaxflowProblem {
|
|||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn intersects_poly(a: (f32, f32), b: (f32, f32), c: (f32, f32), d: (f32, f32)) -> bool {
|
||||||
|
let mut intersects = Self::intersects(a, b, c, d);
|
||||||
|
todo!();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ fn solve_problem_all_algos(problem: &MaxflowProblem) -> (f64, f64, f64, f64) {
|
|||||||
|
|
||||||
// check if all algorithms return the same total flow
|
// 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();
|
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");
|
assert!(total_flows.iter().all(|&x| x == total_flows[0]), "algorithms return different total flows");
|
||||||
|
|
||||||
return durations.into_iter().collect_tuple().unwrap();
|
return durations.into_iter().collect_tuple().unwrap();
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, Sta
|
|||||||
g.add_edge(one, two, (0,2));
|
g.add_edge(one, two, (0,2));
|
||||||
g.add_edge(two, four, (0,9));
|
g.add_edge(two, four, (0,9));
|
||||||
g.add_edge(three, t, (0,10));
|
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));
|
g.add_edge(four, t, (0,10));
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
@@ -49,7 +49,7 @@ fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, Sta
|
|||||||
m.update_edge(one, two, (0,2));
|
m.update_edge(one, two, (0,2));
|
||||||
m.update_edge(two, four, (9,9));
|
m.update_edge(two, four, (9,9));
|
||||||
m.update_edge(three, t, (9,10));
|
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));
|
m.update_edge(four, t, (10,10));
|
||||||
|
|
||||||
return (g, m, s, t);
|
return (g, m, s, t);
|
||||||
@@ -57,11 +57,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 {
|
fn graph_equal(one: StableGraph<(f32, f32), (u64, u64)>, other: StableGraph<(f32, f32), (u64, u64)>) -> bool {
|
||||||
// ensure all edges have the same weights
|
// 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<_>>();
|
let edges = one.edge_indices().map(|e| e).collect::<Vec<_>>();
|
||||||
for edge in edges {
|
for edge in edges {
|
||||||
if one.edge_weight(edge).expect("edge index not found") != other.edge_weight(edge).expect("edge index not found") {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,6 +71,9 @@ fn graph_equal(one: StableGraph<(f32, f32), (u64, u64)>, other: StableGraph<(f32
|
|||||||
let nodes = one.node_indices().map(|n| n).collect::<Vec<_>>();
|
let nodes = one.node_indices().map(|n| n).collect::<Vec<_>>();
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
if one.node_weight(node).expect("node index not found") != other.node_weight(node).expect("node index not found") {
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -85,5 +89,38 @@ fn test_ford_fulkerson() {
|
|||||||
let mut algo = FordFulkerson::from_problem(&problem);
|
let mut algo = FordFulkerson::from_problem(&problem);
|
||||||
let solution = algo.run();
|
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()));
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user