2025-08-03: checkpoint

This commit is contained in:
2025-08-03 18:34:13 +02:00
parent 8d28ef44be
commit ba3cd9591e
16 changed files with 1219 additions and 354 deletions

13
Cargo.lock generated
View File

@@ -1004,6 +1004,18 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
[[package]]
name = "enum_dispatch"
version = "0.3.13"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
]
[[package]] [[package]]
name = "enumflags2" name = "enumflags2"
version = "0.7.11" version = "0.7.11"
@@ -1908,6 +1920,7 @@ dependencies = [
"eframe", "eframe",
"egui", "egui",
"egui_graphs", "egui_graphs",
"enum_dispatch",
"geo", "geo",
"itertools 0.14.0", "itertools 0.14.0",
"petgraph", "petgraph",

View File

@@ -9,6 +9,7 @@ ecolor = "0.31.1"
eframe = "0.30" eframe = "0.30"
egui = "0.30" egui = "0.30"
egui_graphs = "0.23.0" egui_graphs = "0.23.0"
enum_dispatch = "0.3.13"
geo = "0.29.3" geo = "0.29.3"
itertools = "0.14.0" itertools = "0.14.0"
petgraph = "0.6" petgraph = "0.6"

94
README.md Normal file
View File

@@ -0,0 +1,94 @@
# Maxflow-rs
## Idea
- Generate StableGraph (petgraph)
- run algo on that
- Create a custom to_graph method which displays a maxflow graph
TODOs
- [ ] 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] Ford-Fulkerson
- [x] Edmonds-Karp
- [x] Dinic
- [ ] 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"#
- [x] Only insert nodes which are at least a distance of 40 away (default)
- [ ] Pseudo-random node positioning strategy
- [ ] 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] 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] 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
- [ ] 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)
- [x] remove display option for residual graph -> this should be the default
DFS: Stack
BFS: Queue
Bugs:
- [x] if I generate a new graph, the algo runs on the old graph
- [x] if I click reset & then step, the algo runs in one step
- [x] when using edmonds karp the updated flow isn't displayed correctly
- [ ] the capacity on residual edges also gets displayed when using
Residual Graph:
- parallel arcs in the same direction can be summarized into a single arc by summing up the capcities
-
TODOs:
- [x] gui colors in yellow blue (should be easy)
- [x] no residual graph option, residual graph is default (medium, bit of effort)
- [ ] do we need to delete the graph field in algos & use residual_graph instead?
- [ ] check correctness of all algos by testing a small sample#
- [ ] gui display options (medium)
- [ ] layer graph (done - TODO: check if correct) & blocking flow (for Dinic)
- [ ] goldberg-tarjan: push-relabel step - current pre-flow, flow change in every step
- [x] add time delay for algorithm (hard due to multithreading)
- [ ] fix glitchy graph display (somewhere in update_graph function)
- [ ] implement goldberg tarjan (hard)
- [x] fix residual edges: algo pushes flows back but increases flow on the residual edge instead of decreasing the reverse edge
- [ ] fix loops: somehow the algo produces loops towards the source node (not critical for calculating the total flow, but should be fixed)
- [ ] big tests sometimes fail for goldberg-tarjan (panic when reading node from distance labelling)
- [x] make MaxflowAlgorithm object-safe (remove from_problem & implement it per struct, then create a generic wrapper which creates new instances for each specific algorithm)
- [ ] add unit test environment (hard)
- [x] callable from gui
- [x] several test tuples [number of problems, node count, max capacity]
- [x] split MaxflowProblem:new into two functions (one for display, one for test environment)
- [x] apply to every algo
- [x] große testumgebung
- [x] testfunktion in seperatem thread ausführen, damit programm nicht hängt
- [ ] check for:
- [x] flusserhaltung
- [x] kapazitätsbedingungen
- [ ] saturierter schritt
- [ ] testumgebung durch änderungen der lösung automatisch überprüfen (korrektheit, optimalität)
- [x] reduce warnings
thread '<unnamed>' panicked at src/graph.rs:191:31
attempt to subtract with overflow
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

View File

@@ -1,10 +1,14 @@
use crate::algorithms::ford_fulkerson::FordFulkerson;
use crate::algorithms::edmonds_karp::EdmondsKarp;
use crate::algorithms::dinic::Dinic;
use crate::algorithms::goldberg_tarjan::GoldbergTarjan;
use std::fmt;
use enum_dispatch::enum_dispatch;
use petgraph::stable_graph::StableGraph; use petgraph::stable_graph::StableGraph;
use crate::random_generator::MaxflowProblem;
#[enum_dispatch(MaxflowAlgorithmEnum)]
pub trait MaxflowAlgorithm { pub trait MaxflowAlgorithm {
// initialize algorithm from new problem
fn from_problem(p: MaxflowProblem) -> Self;
// perform a single step; returns true if the algorithm terminates // perform a single step; returns true if the algorithm terminates
fn step(&mut self) -> bool; fn step(&mut self) -> bool;
@@ -14,10 +18,44 @@ pub trait MaxflowAlgorithm {
// returns the current graph // returns the current graph
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)>; fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)>;
// resets the current flow to the zero flow
fn reset(&mut self);
// returns the algorithm name // returns the algorithm name
fn name(&self) -> &'static str; fn name(&self) -> &'static str;
} }
pub fn available_capacity(edge: (u64, u64)) -> u64 { pub fn available_capacity(edge: (u64, u64)) -> u64 {
edge.1 - edge.0 edge.1 - edge.0
}
#[enum_dispatch]
pub enum MaxflowAlgorithmEnum {
FordFulkerson,
EdmondsKarp,
Dinic,
GoldbergTarjan
}
impl PartialEq for MaxflowAlgorithmEnum {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(&MaxflowAlgorithmEnum::FordFulkerson(_), &MaxflowAlgorithmEnum::FordFulkerson(_)) => true,
(&MaxflowAlgorithmEnum::EdmondsKarp(_), &MaxflowAlgorithmEnum::EdmondsKarp(_)) => true,
(&MaxflowAlgorithmEnum::Dinic(_), &MaxflowAlgorithmEnum::Dinic(_)) => true,
(&MaxflowAlgorithmEnum::GoldbergTarjan(_), &MaxflowAlgorithmEnum::GoldbergTarjan(_)) => true,
_ => false,
}
}
}
impl fmt::Display for MaxflowAlgorithmEnum {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match &self {
&MaxflowAlgorithmEnum::FordFulkerson(_) => write!(f, "Ford-Fulkerson"),
&MaxflowAlgorithmEnum::EdmondsKarp(_) => write!(f, "Edmonds-Karp"),
&MaxflowAlgorithmEnum::Dinic(_) => write!(f, "Dinic"),
&MaxflowAlgorithmEnum::GoldbergTarjan(_) => write!(f, "Goldberg-Tarjan"),
}
}
} }

View File

@@ -1,106 +1,157 @@
use std::cmp::min; use std::cmp::min;
use std::collections::VecDeque; use std::collections::VecDeque;
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction}; use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
fn available_capacity(edge: (u64, u64)) -> u64 { pub struct Dinic {
edge.1 - edge.0 graph: StableGraph<(f32, f32), (u64, u64)>,
source: NodeIndex,
sink: NodeIndex,
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
layer_graph: StableGraph<(f32, f32), (u64, u64)>,
} }
// BFS impl Dinic {
fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> { pub fn from_problem(p: &MaxflowProblem) -> Self {
// filter graph for edges with no remaining capacity Self {
let mut layer_graph = graph.filter_map(|_, n| { residual_graph: p.g.residual(),
Some(*n) graph: p.g.clone(),
}, |_, &e| { layer_graph: StableGraph::default(),
match available_capacity(e) { source: p.s,
0 => None, sink: p.t,
_ => Some(e),
} }
}); }
// filter graph for s-t paths // BFS
let mut visited = layer_graph.visit_map(); fn layer_graph(&self) -> StableGraph<(f32, f32), (u64, u64)> {
let mut queue = VecDeque::from([(source, 0)]); // filter graph for edges with no remaining capacity
while let Some((node, layer)) = queue.pop_front() { let mut layer_graph = self.residual_graph.filter_map(|_, n| {
//let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).collect::<Vec<_>>(); Some(*n)
let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>(); }, |_, &e| {
visited.visit(node); match available_capacity(e) {
0 => None,
for edge in outgoing_edges { _ => Some(e),
let neighbor = layer_graph.edge_endpoints(edge).expect("edge index not found").1;
// if neighbor is unvisited, this is the shortest path to neighbor -> keep the edge
if !visited.is_visited(&neighbor) {
visited.visit(neighbor);
// stop traversing this path if destination is reached
if neighbor == destination {
continue;
} else {
// add neighbor to queue if destination is not reached
queue.push_back((neighbor, layer+1));
}
} else {
// if neighbor was visited before this edge is not on the shortest path -> remove it
layer_graph.remove_edge(edge);
} }
}
}
layer_graph
}
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> {
let mut visited = graph.visit_map();
let mut stack = VecDeque::from([(source, vec![source])]);
// work through the main stack
while let Some((node, path)) = stack.pop_front() {
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
visited.visit(node);
// iterate over all outgoing edges & add neighboring nodes to the stack
for edge in outgoing_edges {
let neighbor = edge.target();
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
visited.visit(neighbor);
let mut new_path = path.clone();
new_path.push(neighbor);
// TODO: is this right?
if neighbor == destination {
return Some(new_path);
} else {
stack.push_front((neighbor, new_path));
}
};
}
}
None
}
pub fn dinic(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
// construct layer graph (from s to t)
let mut g_l = layer_graph(graph, source, sink);
// construct a blocking flow in the layer graph using a DFS
// continue while there are augmenting paths
while let Some(path) = dfs(&g_l, source, sink) {
// find all edges along the path
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| g_l.find_edge(w[0], w[1]).expect("edge not found")).collect();
// find bottleneck capacity along path
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
let edge = g_l.edge_weight(*x).expect("edge index not found");
min(m, edge.1 - edge.0)
}); });
// increase flow with bottleneck capacity along the augmenting path // filter graph for s-t paths
for edge in edges { let mut visited = layer_graph.visit_map();
let weight = g_l.edge_weight_mut(edge).expect("edge not found"); let mut queue = VecDeque::from([(self.source, 0)]);
(*weight).0 += increase_value; while let Some((node, layer)) = queue.pop_front() {
//let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).collect::<Vec<_>>();
let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>();
visited.visit(node);
for edge in outgoing_edges {
let neighbor = layer_graph.edge_endpoints(edge).expect("edge index not found").1;
// if neighbor is unvisited, this is the shortest path to neighbor -> keep the edge
if !visited.is_visited(&neighbor) {
visited.visit(neighbor);
// stop traversing this path if destination is reached
if neighbor == self.sink {
continue;
} else {
// add neighbor to queue if destination is not reached
queue.push_back((neighbor, layer+1));
}
} else {
// if neighbor was visited before this edge is not on the shortest path -> remove it
layer_graph.remove_edge(edge);
}
}
}
layer_graph
}
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
fn dfs(&self) -> Option<Vec<NodeIndex>> {
let mut visited = self.layer_graph.visit_map();
let mut stack = VecDeque::from([(self.source, vec![self.source])]);
// work through the main stack
while let Some((node, path)) = stack.pop_front() {
let outgoing_edges = self.layer_graph.edges_directed(node, Direction::Outgoing);
visited.visit(node);
// iterate over all outgoing edges & add neighboring nodes to the stack
for edge in outgoing_edges {
let neighbor = edge.target();
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
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 {
stack.push_front((neighbor, new_path));
}
};
}
}
None
}
}
impl MaxflowAlgorithm for Dinic {
fn step(&mut self) -> bool {
// construct layer graph (from s to t)
self.layer_graph = self.layer_graph();
// construct a blocking flow in the layer graph using a DFS
// continue while there are augmenting paths
if let Some(path) = self.dfs() {
// find all edges along the path
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
// find bottleneck capacity along path
let bottleneck_capacity = edges.iter().fold(u64::MAX, |m, x| {
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
min(m, edge.1 - edge.0)
});
// 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;
}
false
} else {
true
} }
} }
// add blocking flow to flow of original graph fn reset(&mut self) {
g_l // set all flows in graph to 0
} for edge in self.graph.edge_indices().collect::<Vec<_>>() {
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
// reset the residual & layer graph as well
self.residual_graph = self.graph.residual();
self.layer_graph = StableGraph::default();
}
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
while !self.step() {
continue;
}
self.residual_graph.clone()
}
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
self.residual_graph.clone()
}
fn name(&self) -> &'static str {
"Dinic"
}
}

View File

@@ -1,60 +1,108 @@
use std::cmp::min; use std::cmp::min;
use std::collections::VecDeque; use std::collections::VecDeque;
use petgraph::{stable_graph::{NodeIndex, StableGraph, EdgeReference}, visit::{EdgeRef, VisitMap, Visitable}, Direction}; use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
fn available_capacity(edge: (u64, u64)) -> u64 { pub struct EdmondsKarp {
edge.1 - edge.0 graph: StableGraph<(f32, f32), (u64, u64)>,
source: NodeIndex,
sink: NodeIndex,
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
} }
// Runs a depth Breadth First Search (BFS) and returns an augmenting path from source to destination if possible impl EdmondsKarp {
fn bfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> { pub fn from_problem(p: &MaxflowProblem) -> Self {
let mut visited = graph.visit_map(); Self {
let mut queue = VecDeque::from([(source, vec![source])]); residual_graph: p.g.residual(),
//queue.push_back((source, vec![source])); graph: p.g.clone(),
source: p.s,
sink: p.t,
}
}
// work through the main queue // Runs a depth Breadth First Search (BFS) and returns an augmenting path from source to destination if possible
while let Some((node, path)) = queue.pop_front() { fn bfs(&mut self) -> Option<Vec<NodeIndex>> {
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing); let mut visited = self.residual_graph.visit_map();
visited.visit(node); let mut queue = VecDeque::from([(self.source, vec![self.source])]);
//queue.push_back((source, vec![source]));
for edge in outgoing_edges { // work through the main queue
let neighbor = edge.target(); while let Some((node, path)) = queue.pop_front() {
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 { let outgoing_edges = self.residual_graph.edges_directed(node, Direction::Outgoing);
visited.visit(neighbor); visited.visit(node);
let mut new_path = path.clone();
new_path.push(neighbor); for edge in outgoing_edges {
visited.visit(neighbor); let neighbor = edge.target();
if neighbor == destination { if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
return Some(new_path); visited.visit(neighbor);
} else { let mut new_path = path.clone();
queue.push_back((neighbor, new_path)); new_path.push(neighbor);
visited.visit(neighbor);
if neighbor == self.sink {
return Some(new_path);
} else {
queue.push_back((neighbor, new_path));
}
} }
} }
} }
None
} }
None
} }
pub fn edmonds_karp(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> { impl MaxflowAlgorithm for EdmondsKarp {
// continue while there are augmenting paths
while let Some(path) = bfs(&graph, source, sink) {
// find all edges along the path
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
// find bottleneck capacity along path
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
let edge = graph.edge_weight(*x).expect("edge index not found");
min(m, edge.1 - edge.0)
});
// increase flow with bottleneck capacity along the augmenting path fn step(&mut self) -> bool {
for edge in edges { if let Some(path) = self.bfs() {
let weight = graph.edge_weight_mut(edge).expect("edge not found"); // find all edges along the path
(*weight).0 += increase_value; let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
// find bottleneck capacity along path
let bottleneck_capacity = edges.iter().fold(u64::MAX, |m, x| {
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
min(m, edge.1 - edge.0)
});
// 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;
}
false
} else {
true
} }
} }
// return graph with augmented flows fn reset(&mut self) {
graph // set all flows in graph to 0
} for edge in self.graph.edge_indices().collect::<Vec<_>>() {
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
// reset the residual graph as well
self.residual_graph = self.graph.residual();
}
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
while !self.step() {
continue;
}
self.residual_graph.clone()
}
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
self.residual_graph.clone()
}
fn name(&self) -> &'static str {
"Edmonds-Karp"
}
}

View File

@@ -1,8 +1,7 @@
use std::cmp::min; use std::cmp::min;
use std::collections::VecDeque; use std::collections::VecDeque;
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction}; use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::ResidualGraph}; use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
use crate::MaxflowProblem;
pub struct FordFulkerson { pub struct FordFulkerson {
graph: StableGraph<(f32, f32), (u64, u64)>, graph: StableGraph<(f32, f32), (u64, u64)>,
@@ -12,6 +11,15 @@ pub struct FordFulkerson {
} }
impl FordFulkerson { impl FordFulkerson {
pub fn from_problem(p: &MaxflowProblem) -> Self {
Self {
residual_graph: p.g.residual(),
graph: p.g.clone(),
source: p.s,
sink: p.t,
}
}
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible // Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
fn dfs(&mut self) -> Option<Vec<NodeIndex>> { fn dfs(&mut self) -> Option<Vec<NodeIndex>> {
let mut visited = self.residual_graph.visit_map(); let mut visited = self.residual_graph.visit_map();
@@ -44,32 +52,28 @@ impl FordFulkerson {
} }
impl MaxflowAlgorithm for FordFulkerson { impl MaxflowAlgorithm for FordFulkerson {
fn from_problem(p: MaxflowProblem) -> Self {
Self {
residual_graph: p.g.residual(),
graph: p.g,
source: p.s,
sink: p.t,
}
}
fn step(&mut self) -> bool { fn step(&mut self) -> bool {
// continue while there are augmenting paths // continue while there are augmenting paths
if let Some(path) = self.dfs() { if let Some(path) = self.dfs() {
// find all edges along the path // find all edges along the path
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect(); let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
// find bottleneck capacity along path // find bottleneck capacity along path
let bottlenack_capacity = edges.iter().fold(u64::MAX, |m, x| { let bottleneck_capacity = edges.iter().fold(u64::MAX, |m, x| {
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found"); let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
min(m, edge.1 - edge.0) min(m, edge.1 - edge.0)
}); });
// increase flow with bottleneck capacity along the augmenting path // increase flow with bottleneck capacity along the augmenting path
for edge in edges { 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 // 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"); let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 += bottlenack_capacity; (*weight).0 += bottleneck_capacity;
// increase capacity of the residual edge of the with the calculated bottleneck value // 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;
} }
false false
} else { } else {
@@ -77,6 +81,16 @@ impl MaxflowAlgorithm for FordFulkerson {
} }
} }
fn reset(&mut self) {
// set all flows in graph to 0
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
// reset residual graph as well
self.residual_graph = self.graph.residual();
}
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> { fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
while !self.step() { while !self.step() {
continue; continue;
@@ -92,57 +106,3 @@ impl MaxflowAlgorithm for FordFulkerson {
"Ford-Fulkerson" "Ford-Fulkerson"
} }
} }
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> {
let mut visited = graph.visit_map();
let mut stack = VecDeque::from([(source, vec![source])]);
// work through the main stack
while let Some((node, path)) = stack.pop_front() {
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
visited.visit(node);
// iterate over all outgoing edges & add neighboring nodes to the stack
for edge in outgoing_edges {
let neighbor = edge.target();
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
visited.visit(neighbor);
let mut new_path = path.clone();
new_path.push(neighbor);
// TODO: is this right?
if neighbor == destination {
return Some(new_path);
} else {
stack.push_front((neighbor, new_path));
}
};
}
}
None
}
pub fn ford_fulkerson(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
//let augmenting_path = dfs(graph, source, sink);
// continue while there are augmenting paths
while let Some(path) = dfs(&graph, source, sink) {
// find all edges along the path
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
// find bottleneck capacity along path
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
let edge = graph.edge_weight(*x).expect("edge index not found");
min(m, edge.1 - edge.0)
});
// increase flow with bottleneck capacity along the augmenting path
for edge in edges {
let weight = graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 += increase_value;
}
}
// return graph with augmented flows
graph
}

View File

@@ -0,0 +1,172 @@
use std::cmp::min;
use std::collections::{VecDeque, HashMap};
use itertools::Itertools;
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, NodeRef, VisitMap, Visitable}, Direction};
use crate::{algorithms::common::{MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
pub struct GoldbergTarjan {
graph: StableGraph<(f32, f32), (u64, u64)>,
source: NodeIndex,
sink: NodeIndex,
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
first_iteration: bool,
pub distance_label: Option<HashMap<NodeIndex, u64>>
// psi?
}
impl GoldbergTarjan {
pub fn from_problem(p: &MaxflowProblem) -> Self {
Self {
residual_graph: p.g.residual(),
graph: p.g.clone(),
source: p.s,
sink: p.t,
first_iteration: true,
distance_label: None
}
// initialize with a valid preflow (max flow on all outgoing edges of s)
// assign height/label to every node using the labeling/height function (using BFS)
}
fn active_node(&self) -> Option<(NodeIndex, u64)> {
for node in self.residual_graph.node_indices() {
// s and t are ne
if node == self.source || node == self.sink {
continue;
}
let outgoing_flows: u64 = self.residual_graph.edges_directed(node, Direction::Outgoing).map(|e| e.weight().0).sum();
let incoming_flows: u64 = self.residual_graph.edges_directed(node, Direction::Incoming).map(|e| e.weight().0).sum();
if (incoming_flows - outgoing_flows) > 0 {
// this node has an excess flow - return node index & excess flow
return Some((node, incoming_flows - outgoing_flows));
}
}
None
}
// performs a BFS from sink to source and labels all nodes with their distance to the sink
fn compute_distances(&mut self) {
let mut distance_label = HashMap::new();
let mut visited = self.residual_graph.visit_map();
let mut queue = VecDeque::from([(self.sink, 0 as u64)]);
visited.visit(self.sink);
while let Some((node, distance)) = queue.pop_front() {
// first, visit nodes in queue
let outgoing_edges = self.residual_graph.edges_directed(node, Direction::Outgoing);
for edge in outgoing_edges {
let neighbor = edge.target();
if !visited.is_visited(&neighbor) {
visited.visit(neighbor);
distance_label.insert(neighbor, distance + 1);
queue.push_back((neighbor, distance + 1));
}
}
}
// set d(source) := |V|
distance_label.insert(self.source, self.residual_graph.node_count() as u64);
// set d(target) := 0
distance_label.insert(self.sink, 0);
assert!(distance_label.len() == self.residual_graph.node_count(), "Distance mapping is incomplete");
self.distance_label = Some(distance_label);
}
}
impl MaxflowAlgorithm for GoldbergTarjan {
fn step(&mut self) -> bool {
// Initialization: if this is the first iteration, set the current flow on all outgoing edges of the source to their capacity
if self.first_iteration {
for edge in self.residual_graph.edges_directed(self.source, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>() {
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
// set all outgoing edges to their maximum capacity
let weight = self.residual_graph.edge_weight_mut(edge).expect("edge id not found");
let capacity = (*weight).1.clone();
(*weight).0 = 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 += capacity;
}
self.first_iteration = false;
return false;
}
// 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 )
.filter(|e| self.distance_label.as_ref().expect("distance labelling not present").get(&e.target().id()).expect("No distance label 1 found") < self.distance_label.as_ref().expect("distance labelling not present").get(&node).expect("No distance label 2 found") )
.map(|e| e.id())
.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
.edges_directed(node, Direction::Outgoing)
.filter(|e| (e.weight().1 - e.weight().0) > 0 )
.sorted_by_key(|e| self.distance_label.as_ref().expect("distance labelling not present").get(&e.target()))
.map(|e| e.target())
.collect::<Vec<_>>();
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
} else {
// algorithm terminates if there are no more active nodes
true
}
}
fn reset(&mut self) {
// set all flows in graph to 0
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
// reset the residual & layer graph as well
self.residual_graph = self.graph.residual();
}
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
while !self.step() {
continue;
}
self.residual_graph.clone()
}
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
self.residual_graph.clone()
}
fn name(&self) -> &'static str {
"Goldberg-Tarjan"
}
}

View File

@@ -1,10 +1,13 @@
mod edmonds_karp;
mod ford_fulkerson;
mod dinic;
mod common; mod common;
mod ford_fulkerson;
mod edmonds_karp;
mod dinic;
mod goldberg_tarjan;
pub use common::MaxflowAlgorithm;
pub use common::MaxflowAlgorithmEnum;
pub use ford_fulkerson::ford_fulkerson;
pub use edmonds_karp::edmonds_karp;
pub use dinic::dinic;
pub use ford_fulkerson::FordFulkerson; pub use ford_fulkerson::FordFulkerson;
pub use common::MaxflowAlgorithm; pub use edmonds_karp::EdmondsKarp;
pub use dinic::Dinic;
pub use goldberg_tarjan::GoldbergTarjan;

View File

@@ -1,43 +1,19 @@
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom}; use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{VisitMap, Visitable}, Directed}; use petgraph::{algo::has_path_connecting, stable_graph::{EdgeIndex, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Directed, Direction};
use egui::Pos2; use egui::{Pos2};
use std::collections::{HashMap, HashSet, VecDeque};
use std::cmp::min;
use crate::layout::CustomEdgeShape; use crate::layout::CustomEdgeShape;
use egui::Color32; use egui::Color32;
pub trait ResidualGraph { // All methods which convert the graph into a GUI representations
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)>;
}
impl ResidualGraph for StableGraph<(f32, f32), (u64, u64)> {
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)> {
let mut res_graph = self.clone();
for edge in res_graph.edge_indices().map(|e| e).collect::<Vec<_>>() {
let (source, target) = res_graph.edge_endpoints(edge).expect("edge not connected");
if let Some(reverse_edge) = res_graph.find_edge(target, source) {
let (flow, capacity) = *res_graph.edge_weight(edge).expect("forward edge not found");
let (reverse_flow, reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
// update the forward edge
// TODO: this seems to overflow in certain cases
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
// update the reverse edge
//res_graph.update_edge(target, source, (reverse_flow, reverse_capacity + flow));
} else {
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
res_graph.add_edge(target, source, (0, 0));
}
}
res_graph
}
}
pub trait GuiGraph { pub trait GuiGraph {
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex, active_flows_only: bool) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>; fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
fn to_gui_graph_distance(&self, s: NodeIndex, t: NodeIndex, distance_labels: HashMap<NodeIndex, u64>) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
} }
impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> { impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex, active_flows_only: bool) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> { fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
let mut graph = to_graph_custom(&self, let mut graph = to_graph_custom(&self,
|n| { |n| {
let (x, y) = *n.payload(); let (x, y) = *n.payload();
@@ -56,14 +32,63 @@ impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
); );
graph.node_mut(s).expect("node index not found").set_label(String::from("s")); graph.node_mut(s).expect("node index not found").set_label(String::from("s"));
graph.node_mut(t).expect("node index not found").set_label(String::from("t")); graph.node_mut(t).expect("node index not found").set_label(String::from("t"));
graph.node_mut(s).expect("node index not found").set_color(Color32::RED); graph.node_mut(s).expect("node index not found").set_color(Color32::BLUE);
graph.node_mut(t).expect("node index not found").set_color(Color32::GREEN); graph.node_mut(t).expect("node index not found").set_color(Color32::ORANGE);
graph
}
fn to_gui_graph_distance(&self, s: NodeIndex, t: NodeIndex, distance_labels: HashMap<NodeIndex, u64>) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
let mut graph = to_graph_custom(&self,
|n| {
let (x, y) = *n.payload();
default_node_transform(n);
n.set_location(Pos2::new(x, y));
if let Some(d) = distance_labels.get(&n.id()) {
if n.id() == s {
n.set_label(format!("s ({})", d));
} else if n.id() == t {
n.set_label(format!("t ({})", d));
} else {
n.set_label(format!("{}", d));
}
} else {
n.set_label(String::from(""));
}
},
|e| {
let (flow, capacity): (u64, u64) = *e.payload();
default_edge_transform(e);
e.set_label(format!("{flow}:{capacity}"));
if flow > 0 {
e.set_selected(true);
}
}
);
graph.node_mut(s).expect("node index not found").set_color(Color32::BLUE);
graph.node_mut(t).expect("node index not found").set_color(Color32::ORANGE);
graph graph
} }
} }
// all methods specific to MaxFlow problems
pub trait FlowGraph { pub trait FlowGraph {
fn filter_empty_flows(&self) -> StableGraph<(f32, f32), (u64, u64)>; fn filter_empty_flows(&self) -> StableGraph<(f32, f32), (u64, u64)>;
fn reset_flow(&mut self);
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64;
fn available_capacity(&self, edge: EdgeIndex) -> u64;
fn layer_graph(&self, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
fn add_flow(&mut self, edge: EdgeIndex, flow: u64);
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)>;
} }
impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> { impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
@@ -78,4 +103,152 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
} }
}) })
} }
}
fn layer_graph(&self, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
// filter graph for edges with no remaining capacity
let mut layer_graph = self.filter_map(|_, n| {
Some(*n)
}, |_, &e| {
match e.1 - e.0 {
0 => None,
_ => Some(e),
}
});
// filter graph for s-t paths
let mut visited = layer_graph.visit_map();
let mut queue = VecDeque::from([(source, 0)]);
while let Some((node, layer)) = queue.pop_front() {
//let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).collect::<Vec<_>>();
let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>();
visited.visit(node);
for edge in outgoing_edges {
let neighbor = layer_graph.edge_endpoints(edge).expect("edge index not found").1;
// if neighbor is unvisited, this is the shortest path to neighbor -> keep the edge
if !visited.is_visited(&neighbor) {
visited.visit(neighbor);
// stop traversing this path if destination is reached
if neighbor == sink {
continue;
} else {
// add neighbor to queue if destination is not reached
queue.push_back((neighbor, layer+1));
}
} else {
// if neighbor was visited before this edge is not on the shortest path -> remove it
layer_graph.remove_edge(edge);
}
}
}
layer_graph
}
// set all flows back to zero
fn reset_flow(&mut self) {
for edge in self.edge_indices().collect::<Vec<_>>() {
let weight = self.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
}
// 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
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool {
// capacity isn't exceeded
for edge in self.edge_indices().map(|e| e).collect::<Vec<_>>() {
if self.edge_weight(edge).expect("edge index not found").0 > self.edge_weight(edge).expect("edge index not found").1 {
return false;
}
}
// sum incoming flows = sum outgoing flows
for node in self.node_indices().map(|n| n).collect::<Vec<_>>() {
let outgoing_flows: u64 = self
.edges_directed(node, Direction::Outgoing)
.map(|e| e.weight().0)
.sum();
let incoming_flows: u64 = self
.edges_directed(node, Direction::Incoming)
.map(|e| e.weight().0)
.sum();
if incoming_flows != outgoing_flows {
if node != source && node != sink {
// println!("invalid flow at {:?} incoming flows: {} outgoing flows: {}", node, incoming_flows, outgoing_flows);
return false;
}
}
}
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();
let outgoing_flows_sink: u64 = self.edges_directed(sink, Direction::Outgoing).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_sink = incoming_flows_sink - outgoing_flows_sink;
assert!(total_flow_sink == total_flow_source);
return total_flow_source;
}
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)> {
let mut res_graph = self.clone();
for edge in res_graph.edge_indices().map(|e| e).collect::<Vec<_>>() {
let (source, target) = res_graph.edge_endpoints(edge).expect("edge not connected");
if let Some(reverse_edge) = res_graph.find_edge(target, source) {
let (flow, capacity) = *res_graph.edge_weight(edge).expect("forward edge not found");
let (reverse_flow, _reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
// update the edge
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
} else {
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
res_graph.add_edge(target, source, (0, 0));
}
}
res_graph
}
fn available_capacity(&self, edge: EdgeIndex) -> 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);
}
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");
}
assert!((*forward_weight).0 <= (*forward_weight).1);
}
}

View File

@@ -1,16 +1,12 @@
use std::f32::consts::PI; use std::f32::consts::PI;
use egui::{ use egui::{
epaint::{CubicBezierShape, QuadraticBezierShape, TextShape}, text::LayoutJob, Color32, FontFamily, FontId, Pos2, Shape, Stroke, TextFormat, Vec2 epaint::{CubicBezierShape, QuadraticBezierShape, TextShape}, Color32, FontFamily, FontId, Pos2, Shape, Stroke, Vec2
}; };
use petgraph::{matrix_graph::Nullable, stable_graph::IndexType, EdgeType}; use petgraph::{matrix_graph::Nullable, stable_graph::IndexType, EdgeType};
use egui_graphs::{ use egui_graphs::{DisplayEdge, EdgeProps, DisplayNode, Node, DrawContext};
DefaultEdgeShape, DisplayEdge, EdgeProps,
DefaultNodeShape, DisplayNode, NodeProps,
Node, DrawContext
};
#[derive(Clone)] #[derive(Clone)]

5
src/lib.rs Normal file
View File

@@ -0,0 +1,5 @@
pub mod algorithms;
pub mod graph;
pub mod random_generator;
pub mod layout;
pub mod testing;

View File

@@ -1,62 +1,143 @@
use eframe::{run_native, App, CreationContext, NativeOptions, Frame}; use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel}; use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel, Slider, Window};
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle}; use egui_graphs::{DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
use geo::algorithm; use petgraph::{stable_graph::{StableGraph}, Directed};
use petgraph::{stable_graph::{NodeIndex, StableGraph}, Directed}; use std::{sync::{Arc, Mutex}, thread, thread::JoinHandle, time::Instant};
use random_generator::MaxflowProblem; use random_generator::MaxflowProblem;
use layout::CustomEdgeShape; use layout::CustomEdgeShape;
use crate::algorithms::{MaxflowAlgorithm, FordFulkerson}; use crate::algorithms::{MaxflowAlgorithm, MaxflowAlgorithmEnum, FordFulkerson, EdmondsKarp, Dinic, GoldbergTarjan};
use graph::{ResidualGraph, GuiGraph, FlowGraph}; use graph::{GuiGraph, FlowGraph};
use testing::{run_small_tests,run_large_tests};
mod random_generator; mod random_generator;
mod algorithms; mod algorithms;
mod layout; mod layout;
mod graph; mod graph;
mod testing;
type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>; //type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
enum MaxflowAlgos {
FordFulkerson,
EdmondsKarp,
Dinic,
GoldbergTarjan,
}
pub struct MaxflowApp { pub struct MaxflowApp {
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>, gui_graph: Arc<Mutex<Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>>>,
p: MaxflowProblem, problem: MaxflowProblem,
r: StableGraph<(f32, f32), (u64, u64)>, current_graph: StableGraph<(f32, f32), (u64, u64)>,
node_count: u64, node_count: u64,
max_capacity: u64, max_capacity: u64,
algorithm: Box<dyn MaxflowAlgorithm>, algorithm: MaxflowAlgorithmEnum,
display_residual: bool,
display_active_flows: bool, display_active_flows: bool,
display_layer_graph: bool,
algorithm_finished: bool,
test_thread: Option<JoinHandle<String>>,
test_results: String,
test_results_open: bool,
delay: u64,
animation_runnning: bool,
last_update: Instant
} }
impl MaxflowApp { impl MaxflowApp {
fn new(_: &CreationContext<'_>) -> Self { fn new(_: &CreationContext<'_>) -> Self {
let problem = MaxflowProblem::new(10, 10); let problem = MaxflowProblem::new_planar_graph(10, 10);
Self { Self {
g: problem.g.to_gui_graph(problem.s, problem.t, false), current_graph: problem.g.clone(),
r: StableGraph::default(), gui_graph: Arc::new(Mutex::new(problem.g.to_gui_graph(problem.s, problem.t))),
p: problem, node_count: 10, algorithm: FordFulkerson::from_problem(&problem).into(),
problem,
node_count: 10,
max_capacity: 5, max_capacity: 5,
algorithm: FordFulkerson,
display_residual: false,
display_active_flows: false, display_active_flows: false,
display_layer_graph: false,
algorithm_finished: false,
test_thread: None,
test_results: String::default(),
test_results_open: false,
delay: 200,
animation_runnning: false,
last_update: Instant::now()
} }
} }
// reinitialize the current algorithm & reset its internal state
fn reset_state(&mut self) {
// undo finished run
self.algorithm_finished = false;
// reinitialize the current graph from the problem
self.current_graph = self.problem.g.clone();
// assign new maxflow problem to the graph
match self.algorithm {
MaxflowAlgorithmEnum::FordFulkerson(_) => self.algorithm = FordFulkerson::from_problem(&self.problem).into(),
MaxflowAlgorithmEnum::EdmondsKarp(_) => self.algorithm = EdmondsKarp::from_problem(&self.problem).into(),
MaxflowAlgorithmEnum::Dinic(_) => self.algorithm = Dinic::from_problem(&self.problem).into(),
MaxflowAlgorithmEnum::GoldbergTarjan(_) => self.algorithm = GoldbergTarjan::from_problem(&self.problem).into(),
}
}
// display the current graph & apply display options from GUI
fn update_graph(&mut self) { fn update_graph(&mut self) {
let graph = match self.display_residual { let graph = &self.current_graph;
true => &self.p.g.residual(), let mut gui_graph_lock = self.gui_graph.lock().unwrap();
false => &self.p.g,
};
if self.display_active_flows { if self.display_active_flows {
self.g = graph.filter_empty_flows().to_gui_graph(self.p.s, self.p.t, self.display_active_flows); *gui_graph_lock = graph.filter_empty_flows().to_gui_graph(self.problem.s, self.problem.t);
} else { } else {
self.g = graph.to_gui_graph(self.p.s, self.p.t, self.display_active_flows); match &mut self.algorithm {
MaxflowAlgorithmEnum::GoldbergTarjan(g) => {
if let Some(l) = g.distance_label.clone() {
*gui_graph_lock = graph.to_gui_graph_distance(self.problem.s, self.problem.t, l);
} else {
*gui_graph_lock = graph.to_gui_graph(self.problem.s, self.problem.t);
}
},
MaxflowAlgorithmEnum::Dinic(_) => {
if self.display_layer_graph {
*gui_graph_lock = graph.layer_graph(self.problem.s, self.problem.t).to_gui_graph(self.problem.s, self.problem.t);
} else {
*gui_graph_lock = graph.to_gui_graph(self.problem.s, self.problem.t);
}
}
_ => *gui_graph_lock = graph.to_gui_graph(self.problem.s, self.problem.t)
}
}
}
fn run_algorithm(&mut self) {
match &mut self.algorithm {
MaxflowAlgorithmEnum::FordFulkerson(x) => {
self.current_graph = x.run();
},
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
self.current_graph = x.run();
},
MaxflowAlgorithmEnum::Dinic(x) => {
self.current_graph = x.run();
},
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
self.current_graph = x.run();
}
}
}
fn step_algorithm(&mut self) -> bool {
match &mut self.algorithm {
MaxflowAlgorithmEnum::FordFulkerson(x) => {
let finished = x.step();
self.current_graph = x.graph();
finished
},
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
let finished = x.step();
self.current_graph = x.graph();
finished
},
MaxflowAlgorithmEnum::Dinic(x) => {
let finished = x.step();
self.current_graph = x.graph();
finished
},
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
let finished = x.step();
self.current_graph = x.graph();
finished
}
} }
} }
} }
@@ -65,7 +146,7 @@ impl App for MaxflowApp {
fn update(&mut self, ctx: &Context, _: &mut Frame) { fn update(&mut self, ctx: &Context, _: &mut Frame) {
ctx.set_theme(egui::Theme::Light); ctx.set_theme(egui::Theme::Light);
CentralPanel::default().show(ctx, |ui| { CentralPanel::default().show(ctx, |ui| {
ui.add(&mut GraphView::<_, _, _, _, _, CustomEdgeShape, LayoutStateRandom, LayoutRandom>::new(&mut self.g).with_styles(&SettingsStyle::default().with_labels_always(true))); ui.add(&mut GraphView::<_, _, _, _, _, CustomEdgeShape, LayoutStateRandom, LayoutRandom>::new(&mut self.gui_graph.lock().unwrap()).with_styles(&SettingsStyle::default().with_labels_always(true)));
}); });
SidePanel::right("right_panel") SidePanel::right("right_panel")
.min_width(200.) .min_width(200.)
@@ -75,12 +156,13 @@ impl App for MaxflowApp {
.default_open(true) .default_open(true)
.show(ui, |ui| { .show(ui, |ui| {
ui.label("node count"); ui.label("node count");
ui.add(egui::DragValue::new(&mut self.node_count).range(2..=1000)); ui.add(egui::DragValue::new(&mut self.node_count).range(2..=50));
ui.label("maximum capacity"); ui.label("maximum capacity");
ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100)); ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100));
// TODO: add generation strategy (random, pseudo-random) // TODO: add generation strategy (random, pseudo-random)
if ui.button("generate graph").clicked() { if ui.button("generate graph").clicked() {
self.p = random_generator::MaxflowProblem::new(self.node_count, self.max_capacity); self.problem = random_generator::MaxflowProblem::new_planar_graph(self.node_count, self.max_capacity);
self.reset_state();
self.update_graph(); self.update_graph();
} }
}); });
@@ -89,45 +171,116 @@ impl App for MaxflowApp {
.default_open(true) .default_open(true)
.show(ui, |ui| { .show(ui, |ui| {
ComboBox::from_label("algorithm") ComboBox::from_label("algorithm")
.selected_text(self.algorithm.name()) .selected_text(self.algorithm.to_string())
.show_ui(ui, |ui| { .show_ui(ui, |ui| {
ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(self.p), "Ford-Fulkerson"); let mut changed = ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(&self.problem).into(), "Ford-Fulkerson").changed();
changed = changed || ui.selectable_value(&mut self.algorithm, EdmondsKarp::from_problem(&self.problem).into(), "Edmonds-Karp").changed();
changed = changed || ui.selectable_value(&mut self.algorithm, Dinic::from_problem(&self.problem).into(), "Dinic").changed();
changed = changed || ui.selectable_value(&mut self.algorithm, GoldbergTarjan::from_problem(&self.problem).into(), "Goldberg-Tarjan").changed();
//for algo in MaxflowAlgos::iter() {
//}
//ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp"); //ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
//ui.selectable_value(&mut self.algorithm, dinic, "Dinic"); //ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
if changed {
self.reset_state();
self.update_graph();
}
}); });
if ui.button("step").clicked() { ui.add(Slider::new(&mut self.delay, 0..=1000).text("delay").suffix("ms"));
// check if algo is initialized ui.add_enabled_ui(!self.algorithm_finished, |ui| {
// if not, initialize if ui.button("step").clicked() {
// run step function // check if algo is initialized
} // if not, initialize
if ui.button("run").clicked() { // run step function
self.algorithm.run(); self.algorithm_finished = self.step_algorithm();
self.p = MaxflowProblem::from(self.algorithm.graph(), self.p.s, self.p.t); self.update_graph();
self.update_graph(); }
//let max_flow_graph = ford_fulkerson(self.p.g.clone(), self.p.s, self.p.t); if ui.button("run").clicked() {
//self.p = MaxflowProblem::from(max_flow_graph, self.p.s, self.p.t); self.animation_runnning = true;
} }
});
if ui.button("reset").clicked() { if ui.button("reset").clicked() {
self.p.reset_flow(); self.reset_state();
self.update_graph(); self.update_graph();
} }
// step button (disable when finished) // step button (disable when finished)
if self.animation_runnning {
if self.last_update.elapsed().as_millis() > self.delay as u128 {
self.algorithm_finished = self.step_algorithm();
self.update_graph();
self.animation_runnning = !self.algorithm_finished;
self.last_update = Instant::now();
}
}
}); });
CollapsingHeader::new("Display options") CollapsingHeader::new("Display options")
.default_open(true) .default_open(true)
.show(ui, |ui| { .show(ui, |ui| {
if ui.checkbox(&mut self.display_residual, "show residual graph").changed() {
self.update_graph();
}
if ui.checkbox(&mut self.display_active_flows, "show active flows only").changed() { if ui.checkbox(&mut self.display_active_flows, "show active flows only").changed() {
self.update_graph(); self.update_graph();
} }
ui.add_enabled_ui(matches!(self.algorithm, MaxflowAlgorithmEnum::Dinic(_)), |ui| {
if ui.checkbox(&mut self.display_layer_graph, "show layer graph (Dinic)").changed() {
self.update_graph();
}
});
});
CollapsingHeader::new("Properties")
.default_open(true)
.show(ui, |ui| {
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)));
});
CollapsingHeader::new("Tests")
.default_open(true)
.show(ui, |ui| {
if ui.button("run tests (small)").clicked() {
// run tests
match &mut self.test_thread {
// other thread currently running - ignore
Some(_) => {},
None => {
self.test_thread = Some(thread::spawn(move || { run_small_tests() }));
}
};
}
if ui.button("run tests (large)").clicked() {
// run tests
match &mut self.test_thread {
// other thread currently running - ignore
Some(_) => {},
None => {
self.test_thread = Some(thread::spawn(|| { run_large_tests() }));
}
};
}
match &mut self.test_thread {
Some(t) => {
if t.is_finished() {
self.test_results = self.test_thread.take().map(JoinHandle::join).expect("No running thread found").expect("Could not join thread");
self.test_results_open = true;
}
},
None => {},
}
}) })
}); });
}); });
if self.test_results_open {
Window::new("Test Results")
.open(&mut self.test_results_open)
.show(ctx, |ui| {
ui.label(self.test_results.clone());
});
}
} }
} }

View File

@@ -1,16 +1,10 @@
use petgraph::data::Build;
use petgraph::stable_graph::{StableGraph, NodeIndex}; use petgraph::stable_graph::{StableGraph, NodeIndex};
use petgraph::{Directed};
use egui_graphs::{default_edge_transform, default_node_transform, to_graph_custom, DefaultEdgeShape, DefaultNodeShape, Graph};
use egui::Pos2;
use rand::thread_rng; use rand::thread_rng;
use rand::Rng; use rand::Rng;
use rand::seq::SliceRandom; use rand::seq::SliceRandom;
use itertools::Itertools; use itertools::Itertools;
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}}; use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
use egui::Color32; use std::cmp::min;
use crate::layout::CustomEdgeShape;
pub struct MaxflowProblem { pub struct MaxflowProblem {
pub g: StableGraph<(f32, f32), (u64, u64)>, pub g: StableGraph<(f32, f32), (u64, u64)>,
@@ -19,7 +13,9 @@ pub struct MaxflowProblem {
} }
impl MaxflowProblem { impl MaxflowProblem {
pub fn new(num_nodes: u64, max_capacity: u64) -> Self { // this function generates a maximally connected, planar graph with x/y coordinates which can be used for
// displaying a max-flow problem in a GUI. since the algorithm is rather inefficient, it only scales up to 50 nodes
pub fn new_planar_graph(num_nodes: u64, max_capacity: u64) -> Self {
// Node Type: (x,y) Edge Type: (current_flow, capacity) // Node Type: (x,y) Edge Type: (current_flow, capacity)
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default(); let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
let mut nodes: Vec<NodeIndex> = Vec::new(); let mut nodes: Vec<NodeIndex> = Vec::new();
@@ -27,8 +23,20 @@ impl MaxflowProblem {
// generate nodes // generate nodes
for _i in 0..num_nodes { for _i in 0..num_nodes {
let x: f32 = rng.gen_range(0.0..=400.0); // check if node is too close to another node (distance < 40)
let y: f32 = rng.gen_range(0.0..=300.0); 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
while too_close {
too_close = false;
x = rng.gen_range(0.0..=400.0);
y = rng.gen_range(0.0..=300.0);
for node in &nodes {
if MaxflowProblem::distance(*graph.node_weight(*node).expect("node index not found"), (x,y)) < 40.0 {
too_close = true;
}
}
}
let n = graph.add_node((x,y)); let n = graph.add_node((x,y));
nodes.push(n); nodes.push(n);
} }
@@ -36,6 +44,7 @@ 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) {
@@ -53,7 +62,7 @@ impl MaxflowProblem {
// insert the edge if possible // insert the edge if possible
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new(); let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
for (node1, node2, distance) in possible_edges { for (node1, node2, _) in possible_edges {
// check for collision with already inserted edges // check for collision with already inserted edges
let mut intersects = false; let mut intersects = false;
for (node3, node4) in &inserted_edges { for (node3, node4) in &inserted_edges {
@@ -81,6 +90,36 @@ impl MaxflowProblem {
Self { g: graph, s: s, t: t } Self { g: graph, s: s, t: t }
} }
// this function generates a fully connected random graph which can be used for solving max-flow problems
pub fn new_random_graph(num_nodes: u64, max_capacity: u64) -> Self {
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
let mut nodes: Vec<NodeIndex> = Vec::new();
let mut rng = thread_rng();
for _i in 0..num_nodes {
let x = rng.gen_range(0.0..=400.0);
let y = rng.gen_range(0.0..=300.0);
let n = graph.add_node((x,y));
// choose a random number of nodes between 1 and 5 to connect to
let num_neighbors = rng.gen_range(min(nodes.len(), 1)..=min(nodes.len(), 5));
let neighbors = nodes.choose_multiple(&mut rng, num_neighbors);
for neighbor in neighbors {
// insert edges for each neighbor
let capacity1: u64 = rng.gen_range(1..=max_capacity);
let capacity2: u64 = rng.gen_range(1..=max_capacity);
graph.add_edge(n, *neighbor, (0, capacity1));
graph.add_edge(*neighbor, n, (0, capacity2));
}
nodes.push(n);
}
// choose source & sink nodes
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
Self { g: graph, s: s, t: t }
}
pub fn from(g: StableGraph<(f32, f32), (u64, u64)>, s: NodeIndex, t: NodeIndex) -> Self{ pub fn from(g: StableGraph<(f32, f32), (u64, u64)>, s: NodeIndex, t: NodeIndex) -> Self{
Self {g: g, s: s, t: t} Self {g: g, s: s, t: t}
} }
@@ -102,36 +141,4 @@ impl MaxflowProblem {
_ => false _ => false
} }
} }
// set all flows back to zero
pub fn reset_flow(&mut self) {
for edge in self.g.edge_indices().collect::<Vec<_>>() {
let weight = self.g.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
}
pub fn to_gui_graph(&self) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
let mut graph = to_graph_custom(&self.g,
|n| {
let (x, y) = *n.payload();
default_node_transform(n);
n.set_location(Pos2::new(x, y));
n.set_label(String::from(""));
},
|e| {
let (flow, capacity): (u64, u64) = *e.payload();
default_edge_transform(e);
e.set_label(format!("{flow}:{capacity}"));
if flow > 0 {
e.set_selected(true);
}
}
);
graph.node_mut(self.s).expect("node index not found").set_label(String::from("s"));
graph.node_mut(self.t).expect("node index not found").set_label(String::from("t"));
graph.node_mut(self.s).expect("node index not found").set_color(Color32::RED);
graph.node_mut(self.t).expect("node index not found").set_color(Color32::GREEN);
graph
}
} }

87
src/testing.rs Normal file
View File

@@ -0,0 +1,87 @@
use itertools::Itertools;
use crate::{algorithms::{Dinic, EdmondsKarp, FordFulkerson, GoldbergTarjan, MaxflowAlgorithm, MaxflowAlgorithmEnum}, random_generator::MaxflowProblem, graph::FlowGraph};
use std::time::{Instant};
fn solve_problem_all_algos(problem: &MaxflowProblem) -> (f64, f64, f64, f64) {
let ff = FordFulkerson::from_problem(&problem);
let ek = EdmondsKarp::from_problem(&problem);
let di = Dinic::from_problem(&problem);
let gt = GoldbergTarjan::from_problem(&problem);
let mut instances:Vec<MaxflowAlgorithmEnum> = vec![ff.into(), ek.into(), di.into(), gt.into()];
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
assert!(a.graph().valid_flow(problem.s, problem.t), "invalid 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();
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();
}
fn run_tests(test_tuples: Vec<(u64, u64, u64)>) -> String {
let mut response = String::new();
for (problem_count, nodes, capacity) in test_tuples {
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 {
problems.push(MaxflowProblem::new_random_graph(nodes, capacity));
}
let mut durations = Vec::new();
for (_, problem) in problems.iter_mut().enumerate() {
durations.push(solve_problem_all_algos(problem));
}
let d0 = durations.iter().map(|x| x.0).sum::<f64>() / problem_count as f64;
let d1 = durations.iter().map(|x| x.1).sum::<f64>() / problem_count as f64;
let d2 = durations.iter().map(|x| x.2).sum::<f64>() / problem_count as f64;
let d3 = durations.iter().map(|x| x.3).sum::<f64>() / problem_count as f64;
response += format!("Ford-Fulkerson:\t{:02.8}s\nEdmonds-Karp:\t{:02.8}s\nDinic:\t\t\t\t\t\t{:02.8}s\nGoldberg-Tarjan:\t{:02.8}s\n\n", d0, d1, d2, d3).as_str();
}
return response
}
pub fn run_small_tests() -> String {
let test_tuples = [
// (number of problems, number of nodes, max. capacity)
(10, 10, 10),
(10, 100, 10),
(10, 100, 100),
(10, 200, 10),
(10, 200, 100)
];
return run_tests(test_tuples.into());
}
pub fn run_large_tests() -> String {
let test_tuples = [
// (number of problems, number of nodes, max. capacity)
(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)
];
return run_tests(test_tuples.into());
}

View File

@@ -1,9 +1,11 @@
use maxflow_rs::graph::FlowGraph;
use petgraph::prelude::StableGraph; use petgraph::prelude::StableGraph;
use petgraph::stable_graph::NodeIndex;
use maxflow_rs::random_generator::MaxflowProblem;
use maxflow_rs::algorithms::{MaxflowAlgorithm, FordFulkerson, EdmondsKarp, Dinic, GoldbergTarjan};
// Example taken from: https://en.wikipedia.org/wiki/Dinic%27s_algorithm#Example // Example taken from: https://en.wikipedia.org/wiki/Dinic%27s_algorithm#Example
#[test] fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) {
fn test_maxflow_small_graph() {
let mut g: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new(); let mut g: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
let s = g.add_node((0., 5.)); let s = g.add_node((0., 5.));
let one = g.add_node((10., 0.)); let one = g.add_node((10., 0.));
@@ -21,5 +23,67 @@ fn test_maxflow_small_graph() {
g.add_edge(four, three, (0,6)); g.add_edge(four, three, (0,6));
g.add_edge(four, t, (0,10)); g.add_edge(four, t, (0,10));
// TODO: // 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));
m.update_edge(one, three, (4, 4));
m.update_edge(one, four, (6, 8));
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, t, (10,10));
return (g, m, s, t);
} }
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") {
return false;
}
}
// ensure all nodes have the same weights
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") {
return false;
}
}
true
}
#[test]
fn test_ford_fulkerson() {
let (g, m, s, t) = generate_small_maxflow_example();
let problem = MaxflowProblem::from(g, s, t);
let mut algo = FordFulkerson::from_problem(&problem);
let solution = algo.run();
assert!(graph_equal(solution, m.residual()));
}