Compare commits
6 Commits
3f93cb57d3
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b67d74b31 | |||
| 5e064c73ed | |||
| ba3cd9591e | |||
| 8d28ef44be | |||
| a13c1a5001 | |||
| c21d648c8f |
13
Cargo.lock
generated
13
Cargo.lock
generated
@@ -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",
|
||||||
|
|||||||
@@ -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"
|
||||||
|
|||||||
101
README.md
Normal file
101
README.md
Normal file
@@ -0,0 +1,101 @@
|
|||||||
|
# Maxflow-rs
|
||||||
|
|
||||||
|
## Idea
|
||||||
|
|
||||||
|
- Generate StableGraph (petgraph)
|
||||||
|
- run algo on that
|
||||||
|
- Create a custom to_graph method which displays a maxflow graph
|
||||||
|
|
||||||
|
TODOs
|
||||||
|
- [x] Implementation of Algos:
|
||||||
|
- [x] Ford-Fulkersson (DFS zur Berechnung des flusserhöhenden/augmentierenden Pfads)
|
||||||
|
- [x] Edmonds-Karp (BFS zur Berechnung des flusserhöhenden/augmnetierenden Pfads)
|
||||||
|
- [x] Dinic
|
||||||
|
- [x] Goldberg-Tarjan/Preflow-Push
|
||||||
|
- [x] Step-by-step implementation for all algos
|
||||||
|
- [x] Ford-Fulkerson
|
||||||
|
- [x] Edmonds-Karp
|
||||||
|
- [x] Dinic
|
||||||
|
- [x] Goldberg-Tarjan/Preflow-Push
|
||||||
|
- [x] Mark edges which have active flows with a color
|
||||||
|
- [x] Move GUI Graph conversion to a trait of StableGraph
|
||||||
|
- [ ] Add graph positioning strategy which looks "nicer"
|
||||||
|
- [x] Only insert nodes which are at least a distance of 40 away (default)
|
||||||
|
- [ ] Pseudo-random node positioning strategy
|
||||||
|
- [x] Handle residual edges properly
|
||||||
|
- [x] Add display option for residual edges
|
||||||
|
- [x] Only show active flows (filter all edges with f=0)
|
||||||
|
- [x] Show normal vs residual graph -> not needed, residual graph is shown by default
|
||||||
|
- [x] When increasing flows, handle residual path
|
||||||
|
- [x] Add unit tests
|
||||||
|
- [x] With small problems to prove correctness
|
||||||
|
- [x] For Benchmarking the algos
|
||||||
|
- [x] Check validity of the generated flows (incoming flows = outgoing flows; flow <= capacity)
|
||||||
|
- [x] Add info display pane
|
||||||
|
- [x] is valid flow? (add trait to graph)
|
||||||
|
- [x] current total flow (add trait to calculate)
|
||||||
|
- [x] number of steps (is implemented for stepwise)
|
||||||
|
- [x] implement PartialEq for StableGraph (not really possible -> how do you compare graphs?) -> implemented a prune_zero trait & compare node/edge weights for same index
|
||||||
|
- [x] Step-by-step vizualisation with choosable time delay
|
||||||
|
- [ ] Dinic: add GUI options to show Layer Graph and Blocking flow
|
||||||
|
- [ ] Goldberg-Tarjan: show distance labels at nodes
|
||||||
|
- [x] change GUI colors to accomodate color-blindness (blue-yellow rather than red-green)
|
||||||
|
- [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
|
||||||
|
|
||||||
|
# Code erklären
|
||||||
|
|
||||||
|
- Graph generieren (random_generator.rs)
|
||||||
|
- GUI, Custom Layout (main.rs, layout.rs)
|
||||||
|
- Graph Hilfsfunktionen (graph.rs)
|
||||||
|
- Algorithmen (ford_fulkerson.rs, edmonds_karp.rs, dinic.rs, goldberg_tarjan.rs)
|
||||||
61
src/algorithms/common.rs
Normal file
61
src/algorithms/common.rs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
#[enum_dispatch(MaxflowAlgorithmEnum)]
|
||||||
|
pub trait MaxflowAlgorithm {
|
||||||
|
// perform a single step; returns true if the algorithm terminates
|
||||||
|
fn step(&mut self) -> bool;
|
||||||
|
|
||||||
|
// runs the whole algorithm in a single step
|
||||||
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
// returns the current graph
|
||||||
|
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
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available_capacity(edge: (u64, u64)) -> u64 {
|
||||||
|
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"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,107 +1,148 @@
|
|||||||
// Dinic
|
|
||||||
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);
|
||||||
|
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 {
|
||||||
|
self.residual_graph.add_flow(edge, 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,61 +1,100 @@
|
|||||||
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
|
||||||
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: EdgeReference<'_, (u64, u64)>) -> u64 {
|
pub struct EdmondsKarp {
|
||||||
edge.weight().1 - edge.weight().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) > 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 {
|
||||||
|
self.residual_graph.add_flow(edge, 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,118 +1,99 @@
|
|||||||
// Ford-Fulkerson mit DFS zur Berechnung des flusserhöhenden Pfades,
|
|
||||||
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
|
||||||
// Dinic
|
|
||||||
// Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt).
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
||||||
|
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
|
||||||
|
|
||||||
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
pub struct FordFulkerson {
|
||||||
|
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
//use crate::random_generator::MaxflowProblem;
|
source: NodeIndex,
|
||||||
//mod random_generator;
|
sink: NodeIndex,
|
||||||
|
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
|
|
||||||
edge.weight().1 - edge.weight().0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs a depth Depth First Search (DFS) and returns an augmenting path from source to destination if possible
|
impl FordFulkerson {
|
||||||
fn dfs(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 stack = VecDeque::from([(source, vec![source])]);
|
residual_graph: p.g.residual(),
|
||||||
|
graph: p.g.clone(),
|
||||||
// work through the main stack
|
source: p.s,
|
||||||
while let Some((node, path)) = stack.pop_front() {
|
sink: p.t,
|
||||||
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
|
|
||||||
visited.visit(node);
|
|
||||||
|
|
||||||
// iterate over all outgoing edges
|
|
||||||
for edge in outgoing_edges {
|
|
||||||
let neighbor = edge.target();
|
|
||||||
if !visited.is_visited(&neighbor) && available_capacity(edge) > 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
|
// 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>> {
|
||||||
|
let mut visited = self.residual_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.residual_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);
|
||||||
|
if neighbor == self.sink {
|
||||||
|
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)> {
|
impl MaxflowAlgorithm for FordFulkerson {
|
||||||
//let augmenting_path = dfs(graph, source, sink);
|
fn step(&mut self) -> bool {
|
||||||
|
// 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)
|
||||||
|
});
|
||||||
|
|
||||||
// continue while there are augmenting paths
|
// increase flow with bottleneck capacity along the augmenting path
|
||||||
while let Some(path) = dfs(&graph, source, sink) {
|
for edge in edges {
|
||||||
// find all edges along the path
|
self.residual_graph.add_flow(edge, bottleneck_capacity);
|
||||||
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
|
false
|
||||||
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
} else {
|
||||||
let edge = graph.edge_weight(*x).expect("edge index not found");
|
true
|
||||||
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
|
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");
|
||||||
//pub struct FordFulkerson {
|
(*weight).0 = 0;
|
||||||
// problem: MaxflowProblem,
|
|
||||||
// flows: Graph<(), u64, Directed, u32, DefaultNodeShape, DefaultEdgeShape>,
|
|
||||||
// alertnatively, use a map from EdgeIndex to flow value
|
|
||||||
//dfs: Dfs<N, VM>,
|
|
||||||
//}
|
|
||||||
|
|
||||||
/* impl FordFulkerson {
|
|
||||||
fn new(problem: MaxflowProblem) -> S/elf {
|
|
||||||
Self { problem: problem, flows: Graph::new(StableGraph::default()) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fn graph(&mut self) -> &Graph<bool, (u64, u64)> {
|
|
||||||
&self.problem.g
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flows(&mut self) -> &Graph<(), u64> {
|
|
||||||
&self.flows
|
|
||||||
}
|
|
||||||
|
|
||||||
fn dfs(&mut self, ni: NodeIndex) -> Vec<Vec<EdgeIndex>>{
|
|
||||||
// set own node to visited
|
|
||||||
self.problem.g.node_mut(ni).expect("node index not found").set_visited(true);
|
|
||||||
//self.graph().node_mut(ni).expect("node index not found").set_visited(true);
|
|
||||||
|
|
||||||
let augmenting_paths: Vec<Vec<EdgeIndex>> = Vec::new();
|
|
||||||
let neighboring_edges = self.graph().edges_directed(ni, Direction::Outgoing);
|
|
||||||
|
|
||||||
for edge in neighboring_edges {
|
|
||||||
//let neighbor = self.problem.g.node_mut(edge.target()).expect("node index not found");
|
|
||||||
//if neighbor.visited() == false {
|
|
||||||
// println!("{:?}", edge.weight());
|
|
||||||
//}
|
|
||||||
}
|
}
|
||||||
// iterate over all unvisited! neighbors. for each:
|
// reset residual graph as well
|
||||||
// - check if there is capacity left
|
self.residual_graph = self.graph.residual();
|
||||||
// - if neighbor = target, return path
|
|
||||||
// - else, do dfs starting from neighbor
|
|
||||||
// - if neighbor returns augmenting path add it to own list & prepend own edge
|
|
||||||
// return list of augmenting paths
|
|
||||||
augmenting_paths
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn step(&self) {
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
//self.dfs(self.problem.s);
|
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 {
|
||||||
|
"Ford-Fulkerson"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
161
src/algorithms/goldberg_tarjan.rs
Normal file
161
src/algorithms/goldberg_tarjan.rs
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop: while there is an active node, continue
|
||||||
|
if let Some((node, excess)) = self.active_node() {
|
||||||
|
// find all outgoing edges with available capacity
|
||||||
|
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)
|
||||||
|
let edge = valid_edges[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);
|
||||||
|
self.residual_graph.add_flow(edge, 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<_>>();
|
||||||
|
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();
|
||||||
|
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"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,13 @@
|
|||||||
mod edmonds_karp;
|
mod common;
|
||||||
mod ford_fulkerson;
|
mod ford_fulkerson;
|
||||||
|
mod edmonds_karp;
|
||||||
mod dinic;
|
mod dinic;
|
||||||
|
mod goldberg_tarjan;
|
||||||
|
|
||||||
pub use ford_fulkerson::ford_fulkerson;
|
pub use common::MaxflowAlgorithm;
|
||||||
pub use edmonds_karp::edmonds_karp;
|
pub use common::MaxflowAlgorithmEnum;
|
||||||
pub use dinic::dinic;
|
|
||||||
|
pub use ford_fulkerson::FordFulkerson;
|
||||||
|
pub use edmonds_karp::EdmondsKarp;
|
||||||
|
pub use dinic::Dinic;
|
||||||
|
pub use goldberg_tarjan::GoldbergTarjan;
|
||||||
256
src/graph.rs
256
src/graph.rs
@@ -1,42 +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::{StableGraph, NodeIndex}, 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 - flow));
|
|
||||||
// update the reverse edge
|
|
||||||
res_graph.update_edge(target, source, (reverse_flow, reverse_capacity + flow - 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
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
@@ -55,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 prune_zero(&self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
fn valid_flow(&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)> {
|
||||||
@@ -77,4 +103,166 @@ 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// removes all edges where the current flow is zero & returns a pruned graph
|
||||||
|
fn prune_zero(&self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
let mut zero_graph = self.clone();
|
||||||
|
for edge in zero_graph.edge_indices().collect::<Vec<_>>() {
|
||||||
|
let weight = zero_graph.edge_weight(edge).expect("edge not found");
|
||||||
|
if weight.0 == 0 {
|
||||||
|
zero_graph.remove_edge(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
zero_graph
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if flow is valid:
|
||||||
|
// - for every edge, the flow doesn't exceed the capacity
|
||||||
|
// - for every node, the amount of incoming flows is equal to the amount of outgoing flows
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
if total_flow_sink != total_flow_source {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
// adds the specified flow to the edge of a residual graph
|
||||||
|
fn add_flow(&mut self, forward_edge: EdgeIndex, flow: u64) {
|
||||||
|
// search for reverse edge
|
||||||
|
let (source, target) = self.edge_endpoints(forward_edge).expect("edge not connected");
|
||||||
|
let reverse_edge = self.find_edge(target, source).expect("reverse edge not found");
|
||||||
|
|
||||||
|
// calculate how much flow needs to be added on the reverse edge
|
||||||
|
let current_reverse_flow = self.edge_weight(reverse_edge).expect("reverse erge not found").0;
|
||||||
|
// maximum flow we can subtract from reverse edge
|
||||||
|
let reverse_flow = min(current_reverse_flow, flow);
|
||||||
|
let forward_flow = flow - reverse_flow;
|
||||||
|
if reverse_flow > 0 {
|
||||||
|
// decrease reverse flow
|
||||||
|
let reverse_weight: &mut (u64, u64) = self.edge_weight_mut(reverse_edge).expect("reverse edge not found");
|
||||||
|
(*reverse_weight).0 -= reverse_flow;
|
||||||
|
assert!(reverse_weight.0 <= reverse_weight.1);
|
||||||
|
// decrease forward capacity
|
||||||
|
let forward_weight: &mut (u64, u64) = self.edge_weight_mut(forward_edge).expect("edge not found");
|
||||||
|
(*forward_weight).1 -= reverse_flow;
|
||||||
|
assert!(forward_weight.0 <= forward_weight.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// add remaining flow to forward edge
|
||||||
|
if forward_flow > 0 {
|
||||||
|
// 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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)]
|
||||||
@@ -34,9 +30,9 @@ impl<E: Clone> From<EdgeProps<E>> for CustomEdgeShape {
|
|||||||
label_text: props.label,
|
label_text: props.label,
|
||||||
|
|
||||||
width: 1.,
|
width: 1.,
|
||||||
tip_size: 10.,
|
tip_size: 6.,
|
||||||
tip_angle: std::f32::consts::TAU / 30.,
|
tip_angle: std::f32::consts::TAU / 20.,
|
||||||
curve_size: 15.,
|
curve_size: 10.,
|
||||||
loop_size: 3.,
|
loop_size: 3.,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
5
src/lib.rs
Normal file
5
src/lib.rs
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
pub mod algorithms;
|
||||||
|
pub mod graph;
|
||||||
|
pub mod random_generator;
|
||||||
|
pub mod layout;
|
||||||
|
pub mod testing;
|
||||||
266
src/main.rs
266
src/main.rs
@@ -1,56 +1,151 @@
|
|||||||
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 std::{fmt::Display, ptr::fn_addr_eq};
|
|
||||||
|
|
||||||
use random_generator::MaxflowProblem;
|
use random_generator::MaxflowProblem;
|
||||||
use layout::CustomEdgeShape;
|
use layout::CustomEdgeShape;
|
||||||
use crate::algorithms::{ford_fulkerson, edmonds_karp, dinic};
|
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)>;
|
||||||
|
|
||||||
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: MaxflowFn,
|
algorithm: MaxflowAlgorithmEnum,
|
||||||
display_residual: bool,
|
|
||||||
display_active_flows: bool,
|
display_active_flows: bool,
|
||||||
|
display_layer_graph: bool,
|
||||||
|
algorithm_finished: bool,
|
||||||
|
num_steps: u64,
|
||||||
|
test_thread: Option<JoinHandle<String>>,
|
||||||
|
test_results: String,
|
||||||
|
test_results_open: bool,
|
||||||
|
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: ford_fulkerson,
|
|
||||||
display_residual: false,
|
|
||||||
display_active_flows: false,
|
display_active_flows: false,
|
||||||
|
display_layer_graph: false,
|
||||||
|
algorithm_finished: false,
|
||||||
|
num_steps: 0,
|
||||||
|
test_thread: None,
|
||||||
|
test_results: String::default(),
|
||||||
|
test_results_open: false,
|
||||||
|
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;
|
||||||
|
// set steps to zero
|
||||||
|
self.num_steps = 0;
|
||||||
|
// reinitialize the current graph from the problem
|
||||||
|
self.current_graph = self.problem.g.clone();
|
||||||
|
// assign new maxflow problem to the graph
|
||||||
|
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 mut 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.num_steps += 1;
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::Dinic(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.num_steps += 1;
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -59,7 +154,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.)
|
||||||
@@ -69,12 +164,12 @@ 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)
|
|
||||||
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();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -83,43 +178,118 @@ 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(format!("{}", match self.algorithm {
|
.selected_text(self.algorithm.to_string())
|
||||||
_ if fn_addr_eq(self.algorithm, ford_fulkerson as MaxflowFn) => "Ford-Fulkerson",
|
|
||||||
_ if fn_addr_eq(self.algorithm, edmonds_karp as MaxflowFn) => "Edmonds-Karp",
|
|
||||||
_ if fn_addr_eq(self.algorithm, dinic as MaxflowFn) => "Dinic",
|
|
||||||
_ => "unknown"
|
|
||||||
}))
|
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
ui.selectable_value(&mut self.algorithm, ford_fulkerson, "Ford-Fulkerson");
|
let mut changed = ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(&self.problem).into(), "Ford-Fulkerson").changed();
|
||||||
ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
changed = changed || ui.selectable_value(&mut self.algorithm, EdmondsKarp::from_problem(&self.problem).into(), "Edmonds-Karp").changed();
|
||||||
ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
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, dinic, "Dinic");
|
||||||
|
if changed {
|
||||||
|
self.reset_state();
|
||||||
|
self.update_graph();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
if ui.button("run algorithm").clicked() {
|
ui.add(Slider::new(&mut self.delay, 0..=1000).text("delay").suffix("ms"));
|
||||||
let max_flow_graph = ford_fulkerson(self.p.g.clone(), self.p.s, self.p.t);
|
ui.add_enabled_ui(!self.algorithm_finished, |ui| {
|
||||||
self.p = MaxflowProblem::from(max_flow_graph, self.p.s, self.p.t);
|
if ui.button("step").clicked() {
|
||||||
self.update_graph();
|
// check if algo is initialized
|
||||||
}
|
// if not, initialize
|
||||||
|
// run step function
|
||||||
|
self.algorithm_finished = self.step_algorithm();
|
||||||
|
self.update_graph();
|
||||||
|
}
|
||||||
|
if ui.button("run").clicked() {
|
||||||
|
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)));
|
||||||
|
|
||||||
|
ui.label(format!("number of steps: {}", self.num_steps))
|
||||||
|
});
|
||||||
|
|
||||||
|
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());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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 loop 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);
|
||||||
}
|
}
|
||||||
@@ -53,7 +61,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 +89,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 +140,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
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
81
src/testing.rs
Normal file
81
src/testing.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
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();
|
||||||
|
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();
|
||||||
|
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 {
|
||||||
|
println!("Running test: {} times, {} nodes, {} capacity", problem_count, nodes, capacity);
|
||||||
|
response += format!("=== Testing with problem_count={}, nodes={}, capacity={}\n", problem_count, nodes, capacity).as_str();
|
||||||
|
let mut problems: Vec<_> = Vec::new();
|
||||||
|
for _ in 0..problem_count {
|
||||||
|
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, 10, 100),
|
||||||
|
(10, 50, 10),
|
||||||
|
(10, 50, 100),
|
||||||
|
(10, 100, 10),
|
||||||
|
(10, 100, 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, 200, 10),
|
||||||
|
(10, 200, 100),
|
||||||
|
(10, 200, 1000),
|
||||||
|
(10, 300, 10),
|
||||||
|
(10, 300, 100),
|
||||||
|
(10, 300, 1000)
|
||||||
|
];
|
||||||
|
return run_tests(test_tuples.into());
|
||||||
|
}
|
||||||
54
tests.txt
Normal file
54
tests.txt
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
=== Testing with problem_count=10, nodes=100, capacity=10
|
||||||
|
Ford-Fulkerson: 0.00019723s
|
||||||
|
Edmonds-Karp: 0.00017104s
|
||||||
|
Dinic: 0.00114540s
|
||||||
|
Goldberg-Tarjan: 0.11118784s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=100, capacity=100
|
||||||
|
Ford-Fulkerson: 0.00044646s
|
||||||
|
Edmonds-Karp: 0.00030087s
|
||||||
|
Dinic: 0.00212037s
|
||||||
|
Goldberg-Tarjan: 0.15394399s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=100, capacity=1000
|
||||||
|
Ford-Fulkerson: 0.00028169s
|
||||||
|
Edmonds-Karp: 0.00017956s
|
||||||
|
Dinic: 0.00128019s
|
||||||
|
Goldberg-Tarjan: 0.11455003s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=200, capacity=10
|
||||||
|
Ford-Fulkerson: 0.00057035s
|
||||||
|
Edmonds-Karp: 0.00042638s
|
||||||
|
Dinic: 0.00270890s
|
||||||
|
Goldberg-Tarjan: 1.70483265s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=200, capacity=100
|
||||||
|
Ford-Fulkerson: 0.00121046s
|
||||||
|
Edmonds-Karp: 0.00045426s
|
||||||
|
Dinic: 0.00301752s
|
||||||
|
Goldberg-Tarjan: 1.48397911s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=200, capacity=1000
|
||||||
|
Ford-Fulkerson: 0.00183244s
|
||||||
|
Edmonds-Karp: 0.00062984s
|
||||||
|
Dinic: 0.00411386s
|
||||||
|
Goldberg-Tarjan: 1.49089764s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=300, capacity=10
|
||||||
|
Ford-Fulkerson: 0.00086230s
|
||||||
|
Edmonds-Karp: 0.00071712s
|
||||||
|
Dinic: 0.00423448s
|
||||||
|
Goldberg-Tarjan: 5.84010118s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=300, capacity=100
|
||||||
|
Ford-Fulkerson: 0.00164213s
|
||||||
|
Edmonds-Karp: 0.00040957s
|
||||||
|
Dinic: 0.00322493s
|
||||||
|
Goldberg-Tarjan: 1.97116912s
|
||||||
|
|
||||||
|
=== Testing with problem_count=10, nodes=300, capacity=1000
|
||||||
|
Ford-Fulkerson: 0.00362413s
|
||||||
|
Edmonds-Karp: 0.00094858s
|
||||||
|
Dinic: 0.00600265s
|
||||||
|
Goldberg-Tarjan: 5.16341617s
|
||||||
|
|
||||||
108
tests/example_flows.rs
Normal file
108
tests/example_flows.rs
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
use maxflow_rs::graph::FlowGraph;
|
||||||
|
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
|
||||||
|
fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) {
|
||||||
|
let mut g: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
|
||||||
|
let s = g.add_node((0., 5.));
|
||||||
|
let one = g.add_node((10., 0.));
|
||||||
|
let two = g.add_node((10., 10.));
|
||||||
|
let three = g.add_node((20., 0.));
|
||||||
|
let four = g.add_node((20., 10.));
|
||||||
|
let t = g.add_node((30., 5.));
|
||||||
|
g.add_edge(s, one, (0, 10));
|
||||||
|
g.add_edge(s, two, (0, 10));
|
||||||
|
g.add_edge(one, three, (0, 4));
|
||||||
|
g.add_edge(one, four, (0, 8));
|
||||||
|
g.add_edge(one, two, (0,2));
|
||||||
|
g.add_edge(two, four, (0,9));
|
||||||
|
g.add_edge(three, t, (0,10));
|
||||||
|
g.add_edge(four, three, (0,5)); // changed capacity to 5 to enforce a single optimal solution
|
||||||
|
g.add_edge(four, t, (0,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,5)); // changed capacity to 5 to enforce a single optimal solution
|
||||||
|
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
|
||||||
|
let edges = one.edge_indices().map(|e| e).collect::<Vec<_>>();
|
||||||
|
for edge in edges {
|
||||||
|
if one.edge_weight(edge).expect("edge index not found") != other.edge_weight(edge).expect("edge index not found") {
|
||||||
|
println!("Edge weights don't match");
|
||||||
|
println!("Graph 1: {:?}", one);
|
||||||
|
println!("Graph 2: {:?}", other);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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") {
|
||||||
|
println!("Node weights don't match");
|
||||||
|
println!("Graph 1: {:?}", one);
|
||||||
|
println!("Graph 2: {:?}", other);
|
||||||
|
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.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()));
|
||||||
|
}
|
||||||
2
tests/valid_flow.rs
Normal file
2
tests/valid_flow.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// check capacity at each edge (incoming flows = outgoing flows)
|
||||||
|
// check capacity isn't violated (flow <= capacity)
|
||||||
Reference in New Issue
Block a user