Compare commits
7 Commits
6b17e583e6
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 5b67d74b31 | |||
| 5e064c73ed | |||
| ba3cd9591e | |||
| 8d28ef44be | |||
| a13c1a5001 | |||
| c21d648c8f | |||
| 3f93cb57d3 |
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"
|
||||
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]]
|
||||
name = "enumflags2"
|
||||
version = "0.7.11"
|
||||
@@ -1908,6 +1920,7 @@ dependencies = [
|
||||
"eframe",
|
||||
"egui",
|
||||
"egui_graphs",
|
||||
"enum_dispatch",
|
||||
"geo",
|
||||
"itertools 0.14.0",
|
||||
"petgraph",
|
||||
|
||||
@@ -9,6 +9,7 @@ ecolor = "0.31.1"
|
||||
eframe = "0.30"
|
||||
egui = "0.30"
|
||||
egui_graphs = "0.23.0"
|
||||
enum_dispatch = "0.3.13"
|
||||
geo = "0.29.3"
|
||||
itertools = "0.14.0"
|
||||
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,17 +1,32 @@
|
||||
// Dinic
|
||||
use std::cmp::min;
|
||||
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 {
|
||||
edge.1 - edge.0
|
||||
pub struct Dinic {
|
||||
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
|
||||
fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
impl Dinic {
|
||||
pub fn from_problem(p: &MaxflowProblem) -> Self {
|
||||
Self {
|
||||
residual_graph: p.g.residual(),
|
||||
graph: p.g.clone(),
|
||||
layer_graph: StableGraph::default(),
|
||||
source: p.s,
|
||||
sink: p.t,
|
||||
}
|
||||
}
|
||||
|
||||
// BFS
|
||||
fn layer_graph(&self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
// filter graph for edges with no remaining capacity
|
||||
let mut layer_graph = graph.filter_map(|_, n| {
|
||||
let mut layer_graph = self.residual_graph.filter_map(|_, n| {
|
||||
Some(*n)
|
||||
}, |_, &e| {
|
||||
match available_capacity(e) {
|
||||
@@ -22,7 +37,7 @@ fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, de
|
||||
|
||||
// filter graph for s-t paths
|
||||
let mut visited = layer_graph.visit_map();
|
||||
let mut queue = VecDeque::from([(source, 0)]);
|
||||
let mut queue = VecDeque::from([(self.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<_>>();
|
||||
@@ -34,7 +49,7 @@ fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, de
|
||||
if !visited.is_visited(&neighbor) {
|
||||
visited.visit(neighbor);
|
||||
// stop traversing this path if destination is reached
|
||||
if neighbor == destination {
|
||||
if neighbor == self.sink {
|
||||
continue;
|
||||
} else {
|
||||
// add neighbor to queue if destination is not reached
|
||||
@@ -48,16 +63,16 @@ fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, de
|
||||
}
|
||||
|
||||
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])]);
|
||||
// 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 = graph.edges_directed(node, Direction::Outgoing);
|
||||
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
|
||||
@@ -67,8 +82,7 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
||||
visited.visit(neighbor);
|
||||
let mut new_path = path.clone();
|
||||
new_path.push(neighbor);
|
||||
// TODO: is this right?
|
||||
if neighbor == destination {
|
||||
if neighbor == self.sink {
|
||||
return Some(new_path);
|
||||
} else {
|
||||
stack.push_front((neighbor, new_path));
|
||||
@@ -76,32 +90,59 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dinic(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
impl MaxflowAlgorithm for Dinic {
|
||||
fn step(&mut self) -> bool {
|
||||
// construct layer graph (from s to t)
|
||||
let mut g_l = layer_graph(graph, source, sink);
|
||||
self.layer_graph = self.layer_graph();
|
||||
|
||||
// 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) {
|
||||
if let Some(path) = self.dfs() {
|
||||
// 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();
|
||||
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 increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
||||
let edge = g_l.edge_weight(*x).expect("edge index not found");
|
||||
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 {
|
||||
let weight = g_l.edge_weight_mut(edge).expect("edge not found");
|
||||
(*weight).0 += increase_value;
|
||||
self.residual_graph.add_flow(edge, bottleneck_capacity);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// add blocking flow to flow of original graph
|
||||
g_l
|
||||
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();
|
||||
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,32 +1,45 @@
|
||||
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
||||
use std::cmp::min;
|
||||
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 {
|
||||
edge.weight().1 - edge.weight().0
|
||||
pub struct EdmondsKarp {
|
||||
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
|
||||
fn bfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> {
|
||||
let mut visited = graph.visit_map();
|
||||
let mut queue = VecDeque::from([(source, vec![source])]);
|
||||
impl EdmondsKarp {
|
||||
pub fn from_problem(p: &MaxflowProblem) -> Self {
|
||||
Self {
|
||||
residual_graph: p.g.residual(),
|
||||
graph: p.g.clone(),
|
||||
source: p.s,
|
||||
sink: p.t,
|
||||
}
|
||||
}
|
||||
|
||||
// Runs a depth Breadth First Search (BFS) and returns an augmenting path from source to destination if possible
|
||||
fn bfs(&mut self) -> Option<Vec<NodeIndex>> {
|
||||
let mut visited = self.residual_graph.visit_map();
|
||||
let mut queue = VecDeque::from([(self.source, vec![self.source])]);
|
||||
//queue.push_back((source, vec![source]));
|
||||
|
||||
// work through the main queue
|
||||
while let Some((node, path)) = queue.pop_front() {
|
||||
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
|
||||
let outgoing_edges = self.residual_graph.edges_directed(node, Direction::Outgoing);
|
||||
visited.visit(node);
|
||||
|
||||
for edge in outgoing_edges {
|
||||
let neighbor = edge.target();
|
||||
if !visited.is_visited(&neighbor) && available_capacity(edge) > 0 {
|
||||
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
|
||||
visited.visit(neighbor);
|
||||
let mut new_path = path.clone();
|
||||
new_path.push(neighbor);
|
||||
visited.visit(neighbor);
|
||||
if neighbor == destination {
|
||||
if neighbor == self.sink {
|
||||
return Some(new_path);
|
||||
} else {
|
||||
queue.push_back((neighbor, new_path));
|
||||
@@ -34,28 +47,54 @@ fn bfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn edmonds_karp(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
// continue while there are augmenting paths
|
||||
while let Some(path) = bfs(&graph, source, sink) {
|
||||
impl MaxflowAlgorithm for EdmondsKarp {
|
||||
|
||||
fn step(&mut self) -> bool {
|
||||
if let Some(path) = self.bfs() {
|
||||
// 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();
|
||||
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 increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
||||
let edge = graph.edge_weight(*x).expect("edge index not found");
|
||||
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 {
|
||||
let weight = graph.edge_weight_mut(edge).expect("edge not found");
|
||||
(*weight).0 += increase_value;
|
||||
self.residual_graph.add_flow(edge, bottleneck_capacity);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// return graph with augmented flows
|
||||
graph
|
||||
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 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,38 +1,43 @@
|
||||
// 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::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};
|
||||
|
||||
//use crate::random_generator::MaxflowProblem;
|
||||
//mod random_generator;
|
||||
|
||||
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
|
||||
edge.weight().1 - edge.weight().0
|
||||
pub struct FordFulkerson {
|
||||
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||
source: NodeIndex,
|
||||
sink: NodeIndex,
|
||||
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||
}
|
||||
|
||||
// Runs a depth Depth First Search (DFS) and 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])]);
|
||||
impl FordFulkerson {
|
||||
pub fn from_problem(p: &MaxflowProblem) -> Self {
|
||||
Self {
|
||||
residual_graph: p.g.residual(),
|
||||
graph: p.g.clone(),
|
||||
source: p.s,
|
||||
sink: p.t,
|
||||
}
|
||||
}
|
||||
|
||||
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
||||
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 = graph.edges_directed(node, Direction::Outgoing);
|
||||
let outgoing_edges = self.residual_graph.edges_directed(node, Direction::Outgoing);
|
||||
visited.visit(node);
|
||||
|
||||
// iterate over all outgoing edges
|
||||
// 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) > 0 {
|
||||
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 {
|
||||
if neighbor == self.sink {
|
||||
return Some(new_path);
|
||||
} else {
|
||||
stack.push_front((neighbor, new_path));
|
||||
@@ -42,77 +47,53 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ford_fulkerson(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
//let augmenting_path = dfs(graph, source, sink);
|
||||
|
||||
impl MaxflowAlgorithm for FordFulkerson {
|
||||
fn step(&mut self) -> bool {
|
||||
// continue while there are augmenting paths
|
||||
while let Some(path) = dfs(&graph, source, sink) {
|
||||
if let Some(path) = self.dfs() {
|
||||
// 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();
|
||||
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 increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
||||
let edge = graph.edge_weight(*x).expect("edge index not found");
|
||||
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 {
|
||||
let weight = graph.edge_weight_mut(edge).expect("edge not found");
|
||||
(*weight).0 += increase_value;
|
||||
self.residual_graph.add_flow(edge, bottleneck_capacity);
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
// return graph with augmented flows
|
||||
graph
|
||||
fn reset(&mut self) {
|
||||
// set all flows in graph to 0
|
||||
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
|
||||
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
|
||||
(*weight).0 = 0;
|
||||
}
|
||||
// reset residual graph as well
|
||||
self.residual_graph = self.graph.residual();
|
||||
}
|
||||
|
||||
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
//pub struct FordFulkerson {
|
||||
// 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:
|
||||
// - check if there is capacity left
|
||||
// - 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) {
|
||||
//self.dfs(self.problem.s);
|
||||
}
|
||||
|
||||
} */
|
||||
|
||||
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 edmonds_karp;
|
||||
mod dinic;
|
||||
mod goldberg_tarjan;
|
||||
|
||||
pub use ford_fulkerson::ford_fulkerson;
|
||||
pub use edmonds_karp::edmonds_karp;
|
||||
pub use dinic::dinic;
|
||||
pub use common::MaxflowAlgorithm;
|
||||
pub use common::MaxflowAlgorithmEnum;
|
||||
|
||||
pub use ford_fulkerson::FordFulkerson;
|
||||
pub use edmonds_karp::EdmondsKarp;
|
||||
pub use dinic::Dinic;
|
||||
pub use goldberg_tarjan::GoldbergTarjan;
|
||||
268
src/graph.rs
Normal file
268
src/graph.rs
Normal file
@@ -0,0 +1,268 @@
|
||||
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
|
||||
use petgraph::{algo::has_path_connecting, stable_graph::{EdgeIndex, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Directed, Direction};
|
||||
use egui::{Pos2};
|
||||
use std::collections::{HashMap, HashSet, VecDeque};
|
||||
use std::cmp::min;
|
||||
use crate::layout::CustomEdgeShape;
|
||||
use egui::Color32;
|
||||
|
||||
// All methods which convert the graph into a GUI representations
|
||||
pub trait GuiGraph {
|
||||
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)> {
|
||||
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,
|
||||
|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(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(s).expect("node index not found").set_color(Color32::BLUE);
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
// all methods specific to MaxFlow problems
|
||||
pub trait FlowGraph {
|
||||
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)> {
|
||||
fn filter_empty_flows(&self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||
self.filter_map( |_, n| {
|
||||
Some(*n)
|
||||
},
|
||||
|_, &e| {
|
||||
match e.0 {
|
||||
0 => None,
|
||||
_ => Some(e),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
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 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 egui_graphs::{
|
||||
DefaultEdgeShape, DisplayEdge, EdgeProps,
|
||||
DefaultNodeShape, DisplayNode, NodeProps,
|
||||
Node, DrawContext
|
||||
};
|
||||
use egui_graphs::{DisplayEdge, EdgeProps, DisplayNode, Node, DrawContext};
|
||||
|
||||
|
||||
#[derive(Clone)]
|
||||
@@ -34,9 +30,9 @@ impl<E: Clone> From<EdgeProps<E>> for CustomEdgeShape {
|
||||
label_text: props.label,
|
||||
|
||||
width: 1.,
|
||||
tip_size: 10.,
|
||||
tip_angle: std::f32::consts::TAU / 30.,
|
||||
curve_size: 15.,
|
||||
tip_size: 6.,
|
||||
tip_angle: std::f32::consts::TAU / 20.,
|
||||
curve_size: 10.,
|
||||
loop_size: 3.,
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
264
src/main.rs
264
src/main.rs
@@ -1,56 +1,151 @@
|
||||
use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
|
||||
use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel};
|
||||
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
||||
use geo::algorithm;
|
||||
use petgraph::{stable_graph::{NodeIndex, StableGraph}, Directed};
|
||||
use std::{fmt::Display, ptr::fn_addr_eq};
|
||||
|
||||
use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel, Slider, Window};
|
||||
use egui_graphs::{DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
||||
use petgraph::{stable_graph::{StableGraph}, Directed};
|
||||
use std::{sync::{Arc, Mutex}, thread, thread::JoinHandle, time::Instant};
|
||||
use random_generator::MaxflowProblem;
|
||||
use layout::CustomEdgeShape;
|
||||
use crate::algorithms::{ford_fulkerson, edmonds_karp, dinic};
|
||||
use graph::{ResidualGraph, GuiGraph, FlowGraph};
|
||||
use crate::algorithms::{MaxflowAlgorithm, MaxflowAlgorithmEnum, FordFulkerson, EdmondsKarp, Dinic, GoldbergTarjan};
|
||||
use graph::{GuiGraph, FlowGraph};
|
||||
use testing::{run_small_tests,run_large_tests};
|
||||
|
||||
mod random_generator;
|
||||
mod algorithms;
|
||||
mod layout;
|
||||
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 {
|
||||
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
||||
p: MaxflowProblem,
|
||||
r: StableGraph<(f32, f32), (u64, u64)>,
|
||||
gui_graph: Arc<Mutex<Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>>>,
|
||||
problem: MaxflowProblem,
|
||||
current_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||
node_count: u64,
|
||||
max_capacity: u64,
|
||||
algorithm: MaxflowFn,
|
||||
display_residual: bool,
|
||||
algorithm: MaxflowAlgorithmEnum,
|
||||
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 {
|
||||
fn new(_: &CreationContext<'_>) -> Self {
|
||||
let problem = MaxflowProblem::new(10, 10);
|
||||
let problem = MaxflowProblem::new_planar_graph(10, 10);
|
||||
Self {
|
||||
g: problem.g.to_gui_graph(problem.s, problem.t, false),
|
||||
r: StableGraph::default(),
|
||||
p: problem, node_count: 10,
|
||||
current_graph: problem.g.clone(),
|
||||
gui_graph: Arc::new(Mutex::new(problem.g.to_gui_graph(problem.s, problem.t))),
|
||||
algorithm: FordFulkerson::from_problem(&problem).into(),
|
||||
problem,
|
||||
node_count: 10,
|
||||
max_capacity: 5,
|
||||
algorithm: ford_fulkerson,
|
||||
display_residual: 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) {
|
||||
let mut graph = match self.display_residual {
|
||||
true => &self.p.g.residual(),
|
||||
false => &self.p.g,
|
||||
};
|
||||
let graph = &self.current_graph;
|
||||
let mut gui_graph_lock = self.gui_graph.lock().unwrap();
|
||||
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 {
|
||||
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) {
|
||||
ctx.set_theme(egui::Theme::Light);
|
||||
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")
|
||||
.min_width(200.)
|
||||
@@ -69,12 +164,12 @@ impl App for MaxflowApp {
|
||||
.default_open(true)
|
||||
.show(ui, |ui| {
|
||||
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.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100));
|
||||
// TODO: add generation strategy (random, pseudo-random)
|
||||
if ui.button("generate graph").clicked() {
|
||||
self.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();
|
||||
}
|
||||
});
|
||||
@@ -83,43 +178,118 @@ impl App for MaxflowApp {
|
||||
.default_open(true)
|
||||
.show(ui, |ui| {
|
||||
ComboBox::from_label("algorithm")
|
||||
.selected_text(format!("{}", match self.algorithm {
|
||||
_ 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"
|
||||
}))
|
||||
.selected_text(self.algorithm.to_string())
|
||||
.show_ui(ui, |ui| {
|
||||
ui.selectable_value(&mut self.algorithm, ford_fulkerson, "Ford-Fulkerson");
|
||||
ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
||||
ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
||||
});
|
||||
if ui.button("run algorithm").clicked() {
|
||||
let max_flow_graph = ford_fulkerson(self.p.g.clone(), self.p.s, self.p.t);
|
||||
self.p = MaxflowProblem::from(max_flow_graph, self.p.s, self.p.t);
|
||||
let mut changed = ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(&self.problem).into(), "Ford-Fulkerson").changed();
|
||||
changed = changed || ui.selectable_value(&mut self.algorithm, EdmondsKarp::from_problem(&self.problem).into(), "Edmonds-Karp").changed();
|
||||
changed = changed || ui.selectable_value(&mut self.algorithm, Dinic::from_problem(&self.problem).into(), "Dinic").changed();
|
||||
changed = changed || ui.selectable_value(&mut self.algorithm, GoldbergTarjan::from_problem(&self.problem).into(), "Goldberg-Tarjan").changed();
|
||||
//for algo in MaxflowAlgos::iter() {
|
||||
//}
|
||||
//ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
||||
//ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
||||
if changed {
|
||||
self.reset_state();
|
||||
self.update_graph();
|
||||
}
|
||||
});
|
||||
ui.add(Slider::new(&mut self.delay, 0..=1000).text("delay").suffix("ms"));
|
||||
ui.add_enabled_ui(!self.algorithm_finished, |ui| {
|
||||
if ui.button("step").clicked() {
|
||||
// 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() {
|
||||
self.p.reset_flow();
|
||||
self.reset_state();
|
||||
self.update_graph();
|
||||
}
|
||||
// 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")
|
||||
.default_open(true)
|
||||
.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() {
|
||||
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::{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::Rng;
|
||||
use rand::seq::SliceRandom;
|
||||
use itertools::Itertools;
|
||||
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
||||
use egui::Color32;
|
||||
|
||||
use crate::layout::CustomEdgeShape;
|
||||
use std::cmp::min;
|
||||
|
||||
pub struct MaxflowProblem {
|
||||
pub g: StableGraph<(f32, f32), (u64, u64)>,
|
||||
@@ -19,7 +13,9 @@ pub struct 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)
|
||||
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
|
||||
let mut nodes: Vec<NodeIndex> = Vec::new();
|
||||
@@ -27,8 +23,20 @@ impl MaxflowProblem {
|
||||
|
||||
// generate nodes
|
||||
for _i in 0..num_nodes {
|
||||
let x: f32 = rng.gen_range(0.0..=400.0);
|
||||
let y: f32 = rng.gen_range(0.0..=300.0);
|
||||
// check if node is too close to another node (distance < 40)
|
||||
let mut too_close = true;
|
||||
let (mut x, mut y) = (0.0, 0.0);
|
||||
// this is a poor attempt at modeling a do..while 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));
|
||||
nodes.push(n);
|
||||
}
|
||||
@@ -53,7 +61,7 @@ impl MaxflowProblem {
|
||||
|
||||
// insert the edge if possible
|
||||
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
|
||||
let mut intersects = false;
|
||||
for (node3, node4) in &inserted_edges {
|
||||
@@ -81,6 +89,36 @@ impl MaxflowProblem {
|
||||
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{
|
||||
Self {g: g, s: s, t: t}
|
||||
}
|
||||
@@ -102,36 +140,4 @@ impl MaxflowProblem {
|
||||
_ => 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