2025-08-03: checkpoint
This commit is contained in:
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"
|
||||||
|
|||||||
94
README.md
Normal file
94
README.md
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
# Maxflow-rs
|
||||||
|
|
||||||
|
## Idea
|
||||||
|
|
||||||
|
- Generate StableGraph (petgraph)
|
||||||
|
- run algo on that
|
||||||
|
- Create a custom to_graph method which displays a maxflow graph
|
||||||
|
|
||||||
|
TODOs
|
||||||
|
- [ ] Implementation of Algos:
|
||||||
|
- [x] Ford-Fulkersson (DFS zur Berechnung des flusserhöhenden/augmentierenden Pfads)
|
||||||
|
- [x] Edmonds-Karp (BFS zur Berechnung des flusserhöhenden/augmnetierenden Pfads)
|
||||||
|
- [x] Dinic
|
||||||
|
- [ ] Goldberg-Tarjan/Preflow-Push
|
||||||
|
- [ ] Step-by-step implementation for all algos
|
||||||
|
- [x] Ford-Fulkerson
|
||||||
|
- [x] Edmonds-Karp
|
||||||
|
- [x] Dinic
|
||||||
|
- [ ] Goldberg-Tarjan/Preflow-Push
|
||||||
|
- [x] Mark edges which have active flows with a color
|
||||||
|
- [x] Move GUI Graph conversion to a trait of StableGraph
|
||||||
|
- [ ] Add graph positioning strategy which looks "nicer"#
|
||||||
|
- [x] Only insert nodes which are at least a distance of 40 away (default)
|
||||||
|
- [ ] Pseudo-random node positioning strategy
|
||||||
|
- [ ] Handle residual edges properly
|
||||||
|
- [x] Add display option for residual edges
|
||||||
|
- [x] Only show active flows (filter all edges with f=0)
|
||||||
|
- [ ] Show normal vs residual graph
|
||||||
|
- [x] When increasing flows, handle residual path
|
||||||
|
- [ ] Add unit tests
|
||||||
|
- [ ] With small problems to prove correctness
|
||||||
|
- [ ] For Benchmarking the algos
|
||||||
|
- [ ] Check validity of the generated flows (incoming flows = outgoing flows; flow <= capacity)
|
||||||
|
- [ ] Add info display pane
|
||||||
|
- [x] is valid flow? (add trait to graph)
|
||||||
|
- [x] current total flow (add trait to calculate)
|
||||||
|
- [ ] number of steps
|
||||||
|
- [ ] implement PartialEq for StableGraph (not really possible -> how do you compare graphs?)
|
||||||
|
- [ ] Step-by-step vizualisation with choosable time delay
|
||||||
|
- [ ] Dinic: add GUI options to show Layer Graph and Blocking flow
|
||||||
|
- [ ] Goldberg-Tarjan: show distance labels at nodes
|
||||||
|
- [x] change GUI colors to accomodate color-blindness (blue-yellow rather than red-green)
|
||||||
|
- [x] remove display option for residual graph -> this should be the default
|
||||||
|
|
||||||
|
DFS: Stack
|
||||||
|
BFS: Queue
|
||||||
|
|
||||||
|
Bugs:
|
||||||
|
- [x] if I generate a new graph, the algo runs on the old graph
|
||||||
|
- [x] if I click reset & then step, the algo runs in one step
|
||||||
|
- [x] when using edmonds karp the updated flow isn't displayed correctly
|
||||||
|
- [ ] the capacity on residual edges also gets displayed when using
|
||||||
|
|
||||||
|
Residual Graph:
|
||||||
|
- parallel arcs in the same direction can be summarized into a single arc by summing up the capcities
|
||||||
|
-
|
||||||
|
|
||||||
|
TODOs:
|
||||||
|
- [x] gui colors in yellow blue (should be easy)
|
||||||
|
- [x] no residual graph option, residual graph is default (medium, bit of effort)
|
||||||
|
- [ ] do we need to delete the graph field in algos & use residual_graph instead?
|
||||||
|
- [ ] check correctness of all algos by testing a small sample#
|
||||||
|
- [ ] gui display options (medium)
|
||||||
|
- [ ] layer graph (done - TODO: check if correct) & blocking flow (for Dinic)
|
||||||
|
- [ ] goldberg-tarjan: push-relabel step - current pre-flow, flow change in every step
|
||||||
|
- [x] add time delay for algorithm (hard due to multithreading)
|
||||||
|
- [ ] fix glitchy graph display (somewhere in update_graph function)
|
||||||
|
- [ ] implement goldberg tarjan (hard)
|
||||||
|
- [x] fix residual edges: algo pushes flows back but increases flow on the residual edge instead of decreasing the reverse edge
|
||||||
|
- [ ] fix loops: somehow the algo produces loops towards the source node (not critical for calculating the total flow, but should be fixed)
|
||||||
|
- [ ] big tests sometimes fail for goldberg-tarjan (panic when reading node from distance labelling)
|
||||||
|
- [x] make MaxflowAlgorithm object-safe (remove from_problem & implement it per struct, then create a generic wrapper which creates new instances for each specific algorithm)
|
||||||
|
- [ ] add unit test environment (hard)
|
||||||
|
- [x] callable from gui
|
||||||
|
- [x] several test tuples [number of problems, node count, max capacity]
|
||||||
|
- [x] split MaxflowProblem:new into two functions (one for display, one for test environment)
|
||||||
|
- [x] apply to every algo
|
||||||
|
- [x] große testumgebung
|
||||||
|
- [x] testfunktion in seperatem thread ausführen, damit programm nicht hängt
|
||||||
|
- [ ] check for:
|
||||||
|
- [x] flusserhaltung
|
||||||
|
- [x] kapazitätsbedingungen
|
||||||
|
- [ ] saturierter schritt
|
||||||
|
- [ ] testumgebung durch änderungen der lösung automatisch überprüfen (korrektheit, optimalität)
|
||||||
|
- [x] reduce warnings
|
||||||
|
|
||||||
|
|
||||||
|
thread '<unnamed>' panicked at src/graph.rs:191:31
|
||||||
|
attempt to subtract with overflow
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
|
|
||||||
|
thread '<unnamed>' panicked at src/algorithms/goldberg_tarjan.rs:109:238:
|
||||||
|
No distance label found
|
||||||
|
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
|
use crate::algorithms::ford_fulkerson::FordFulkerson;
|
||||||
|
use crate::algorithms::edmonds_karp::EdmondsKarp;
|
||||||
|
use crate::algorithms::dinic::Dinic;
|
||||||
|
use crate::algorithms::goldberg_tarjan::GoldbergTarjan;
|
||||||
|
|
||||||
|
use std::fmt;
|
||||||
|
use enum_dispatch::enum_dispatch;
|
||||||
use petgraph::stable_graph::StableGraph;
|
use petgraph::stable_graph::StableGraph;
|
||||||
use crate::random_generator::MaxflowProblem;
|
|
||||||
|
|
||||||
|
#[enum_dispatch(MaxflowAlgorithmEnum)]
|
||||||
pub trait MaxflowAlgorithm {
|
pub trait MaxflowAlgorithm {
|
||||||
// initialize algorithm from new problem
|
|
||||||
fn from_problem(p: MaxflowProblem) -> Self;
|
|
||||||
|
|
||||||
// perform a single step; returns true if the algorithm terminates
|
// perform a single step; returns true if the algorithm terminates
|
||||||
fn step(&mut self) -> bool;
|
fn step(&mut self) -> bool;
|
||||||
|
|
||||||
@@ -14,6 +18,9 @@ pub trait MaxflowAlgorithm {
|
|||||||
// returns the current graph
|
// returns the current graph
|
||||||
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)>;
|
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
// resets the current flow to the zero flow
|
||||||
|
fn reset(&mut self);
|
||||||
|
|
||||||
// returns the algorithm name
|
// returns the algorithm name
|
||||||
fn name(&self) -> &'static str;
|
fn name(&self) -> &'static str;
|
||||||
}
|
}
|
||||||
@@ -21,3 +28,34 @@ pub trait MaxflowAlgorithm {
|
|||||||
pub fn available_capacity(edge: (u64, u64)) -> u64 {
|
pub fn available_capacity(edge: (u64, u64)) -> u64 {
|
||||||
edge.1 - edge.0
|
edge.1 - edge.0
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[enum_dispatch]
|
||||||
|
pub enum MaxflowAlgorithmEnum {
|
||||||
|
FordFulkerson,
|
||||||
|
EdmondsKarp,
|
||||||
|
Dinic,
|
||||||
|
GoldbergTarjan
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PartialEq for MaxflowAlgorithmEnum {
|
||||||
|
fn eq(&self, other: &Self) -> bool {
|
||||||
|
match (self, other) {
|
||||||
|
(&MaxflowAlgorithmEnum::FordFulkerson(_), &MaxflowAlgorithmEnum::FordFulkerson(_)) => true,
|
||||||
|
(&MaxflowAlgorithmEnum::EdmondsKarp(_), &MaxflowAlgorithmEnum::EdmondsKarp(_)) => true,
|
||||||
|
(&MaxflowAlgorithmEnum::Dinic(_), &MaxflowAlgorithmEnum::Dinic(_)) => true,
|
||||||
|
(&MaxflowAlgorithmEnum::GoldbergTarjan(_), &MaxflowAlgorithmEnum::GoldbergTarjan(_)) => true,
|
||||||
|
_ => false,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for MaxflowAlgorithmEnum {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||||
|
match &self {
|
||||||
|
&MaxflowAlgorithmEnum::FordFulkerson(_) => write!(f, "Ford-Fulkerson"),
|
||||||
|
&MaxflowAlgorithmEnum::EdmondsKarp(_) => write!(f, "Edmonds-Karp"),
|
||||||
|
&MaxflowAlgorithmEnum::Dinic(_) => write!(f, "Dinic"),
|
||||||
|
&MaxflowAlgorithmEnum::GoldbergTarjan(_) => write!(f, "Goldberg-Tarjan"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +1,32 @@
|
|||||||
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)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// BFS
|
||||||
fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
fn layer_graph(&self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
// filter graph for edges with no remaining capacity
|
// 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)
|
Some(*n)
|
||||||
}, |_, &e| {
|
}, |_, &e| {
|
||||||
match available_capacity(e) {
|
match available_capacity(e) {
|
||||||
@@ -21,7 +37,7 @@ fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, de
|
|||||||
|
|
||||||
// filter graph for s-t paths
|
// filter graph for s-t paths
|
||||||
let mut visited = layer_graph.visit_map();
|
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() {
|
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).collect::<Vec<_>>();
|
||||||
let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>();
|
let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>();
|
||||||
@@ -33,7 +49,7 @@ fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, de
|
|||||||
if !visited.is_visited(&neighbor) {
|
if !visited.is_visited(&neighbor) {
|
||||||
visited.visit(neighbor);
|
visited.visit(neighbor);
|
||||||
// stop traversing this path if destination is reached
|
// stop traversing this path if destination is reached
|
||||||
if neighbor == destination {
|
if neighbor == self.sink {
|
||||||
continue;
|
continue;
|
||||||
} else {
|
} else {
|
||||||
// add neighbor to queue if destination is not reached
|
// add neighbor to queue if destination is not reached
|
||||||
@@ -50,13 +66,13 @@ fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, de
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
||||||
fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> {
|
fn dfs(&self) -> Option<Vec<NodeIndex>> {
|
||||||
let mut visited = graph.visit_map();
|
let mut visited = self.layer_graph.visit_map();
|
||||||
let mut stack = VecDeque::from([(source, vec![source])]);
|
let mut stack = VecDeque::from([(self.source, vec![self.source])]);
|
||||||
|
|
||||||
// work through the main stack
|
// work through the main stack
|
||||||
while let Some((node, path)) = stack.pop_front() {
|
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);
|
visited.visit(node);
|
||||||
|
|
||||||
// iterate over all outgoing edges & add neighboring nodes to the stack
|
// iterate over all outgoing edges & add neighboring nodes to the stack
|
||||||
@@ -67,7 +83,7 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
|||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(neighbor);
|
new_path.push(neighbor);
|
||||||
// TODO: is this right?
|
// TODO: is this right?
|
||||||
if neighbor == destination {
|
if neighbor == self.sink {
|
||||||
return Some(new_path);
|
return Some(new_path);
|
||||||
} else {
|
} else {
|
||||||
stack.push_front((neighbor, new_path));
|
stack.push_front((neighbor, new_path));
|
||||||
@@ -75,32 +91,67 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
None
|
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)
|
// 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
|
// construct a blocking flow in the layer graph using a DFS
|
||||||
// continue while there are augmenting paths
|
// 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
|
// 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
|
// find bottleneck capacity along path
|
||||||
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
let bottleneck_capacity = edges.iter().fold(u64::MAX, |m, x| {
|
||||||
let edge = g_l.edge_weight(*x).expect("edge index not found");
|
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
|
||||||
min(m, edge.1 - edge.0)
|
min(m, edge.1 - edge.0)
|
||||||
});
|
});
|
||||||
|
|
||||||
// increase flow with bottleneck capacity along the augmenting path
|
// increase flow with bottleneck capacity along the augmenting path
|
||||||
for edge in edges {
|
for edge in edges {
|
||||||
let weight = g_l.edge_weight_mut(edge).expect("edge not found");
|
// get source and target of edge
|
||||||
(*weight).0 += increase_value;
|
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
|
||||||
|
// increase flow of the forward edge with the calculated bottleneck value
|
||||||
|
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 += bottleneck_capacity;
|
||||||
|
// increase capacity of the residual edge of the with the calculated bottleneck value
|
||||||
|
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
|
||||||
|
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
|
||||||
|
(*residual_weight).1 += bottleneck_capacity;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// add blocking flow to flow of original graph
|
fn reset(&mut self) {
|
||||||
g_l
|
// set all flows in graph to 0
|
||||||
|
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
|
||||||
|
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 = 0;
|
||||||
|
}
|
||||||
|
// reset the residual & layer graph as well
|
||||||
|
self.residual_graph = self.graph.residual();
|
||||||
|
self.layer_graph = StableGraph::default();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
while !self.step() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Dinic"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,21 +1,35 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use petgraph::{stable_graph::{NodeIndex, StableGraph, EdgeReference}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
||||||
|
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
|
||||||
|
|
||||||
fn available_capacity(edge: (u64, u64)) -> u64 {
|
pub struct EdmondsKarp {
|
||||||
edge.1 - edge.0
|
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
|
source: NodeIndex,
|
||||||
|
sink: NodeIndex,
|
||||||
|
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
// 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>> {
|
fn bfs(&mut self) -> Option<Vec<NodeIndex>> {
|
||||||
let mut visited = graph.visit_map();
|
let mut visited = self.residual_graph.visit_map();
|
||||||
let mut queue = VecDeque::from([(source, vec![source])]);
|
let mut queue = VecDeque::from([(self.source, vec![self.source])]);
|
||||||
//queue.push_back((source, vec![source]));
|
//queue.push_back((source, vec![source]));
|
||||||
|
|
||||||
// work through the main queue
|
// work through the main queue
|
||||||
while let Some((node, path)) = queue.pop_front() {
|
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);
|
visited.visit(node);
|
||||||
|
|
||||||
for edge in outgoing_edges {
|
for edge in outgoing_edges {
|
||||||
@@ -25,7 +39,7 @@ fn bfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
|||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(neighbor);
|
new_path.push(neighbor);
|
||||||
visited.visit(neighbor);
|
visited.visit(neighbor);
|
||||||
if neighbor == destination {
|
if neighbor == self.sink {
|
||||||
return Some(new_path);
|
return Some(new_path);
|
||||||
} else {
|
} else {
|
||||||
queue.push_back((neighbor, new_path));
|
queue.push_back((neighbor, new_path));
|
||||||
@@ -33,28 +47,62 @@ fn bfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
fn step(&mut self) -> bool {
|
||||||
|
if let Some(path) = self.bfs() {
|
||||||
// find all edges along the path
|
// 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
|
// find bottleneck capacity along path
|
||||||
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
let bottleneck_capacity = edges.iter().fold(u64::MAX, |m, x| {
|
||||||
let edge = graph.edge_weight(*x).expect("edge index not found");
|
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
|
||||||
min(m, edge.1 - edge.0)
|
min(m, edge.1 - edge.0)
|
||||||
});
|
});
|
||||||
|
|
||||||
// increase flow with bottleneck capacity along the augmenting path
|
// increase flow with bottleneck capacity along the augmenting path
|
||||||
for edge in edges {
|
for edge in edges {
|
||||||
let weight = graph.edge_weight_mut(edge).expect("edge not found");
|
// get source and target of edge
|
||||||
(*weight).0 += increase_value;
|
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
|
||||||
|
// increase flow of the forward edge with the calculated bottleneck value
|
||||||
|
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 += bottleneck_capacity;
|
||||||
|
// increase capacity of the residual edge of the with the calculated bottleneck value
|
||||||
|
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
|
||||||
|
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
|
||||||
|
(*residual_weight).1 += bottleneck_capacity;
|
||||||
|
}
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// return graph with augmented flows
|
fn reset(&mut self) {
|
||||||
graph
|
// set all flows in graph to 0
|
||||||
|
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
|
||||||
|
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 = 0;
|
||||||
|
}
|
||||||
|
// reset the residual graph as well
|
||||||
|
self.residual_graph = self.graph.residual();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
while !self.step() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Edmonds-Karp"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,8 +1,7 @@
|
|||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
||||||
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::ResidualGraph};
|
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
|
||||||
use crate::MaxflowProblem;
|
|
||||||
|
|
||||||
pub struct FordFulkerson {
|
pub struct FordFulkerson {
|
||||||
graph: StableGraph<(f32, f32), (u64, u64)>,
|
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
@@ -12,6 +11,15 @@ pub struct FordFulkerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FordFulkerson {
|
impl FordFulkerson {
|
||||||
|
pub fn from_problem(p: &MaxflowProblem) -> Self {
|
||||||
|
Self {
|
||||||
|
residual_graph: p.g.residual(),
|
||||||
|
graph: p.g.clone(),
|
||||||
|
source: p.s,
|
||||||
|
sink: p.t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
||||||
fn dfs(&mut self) -> Option<Vec<NodeIndex>> {
|
fn dfs(&mut self) -> Option<Vec<NodeIndex>> {
|
||||||
let mut visited = self.residual_graph.visit_map();
|
let mut visited = self.residual_graph.visit_map();
|
||||||
@@ -44,32 +52,28 @@ impl FordFulkerson {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MaxflowAlgorithm for FordFulkerson {
|
impl MaxflowAlgorithm for FordFulkerson {
|
||||||
fn from_problem(p: MaxflowProblem) -> Self {
|
|
||||||
Self {
|
|
||||||
residual_graph: p.g.residual(),
|
|
||||||
graph: p.g,
|
|
||||||
source: p.s,
|
|
||||||
sink: p.t,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn step(&mut self) -> bool {
|
fn step(&mut self) -> bool {
|
||||||
// continue while there are augmenting paths
|
// continue while there are augmenting paths
|
||||||
if let Some(path) = self.dfs() {
|
if let Some(path) = self.dfs() {
|
||||||
// find all edges along the path
|
// find all edges along the path
|
||||||
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
|
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
|
||||||
// find bottleneck capacity along path
|
// find bottleneck capacity along path
|
||||||
let bottlenack_capacity = edges.iter().fold(u64::MAX, |m, x| {
|
let bottleneck_capacity = edges.iter().fold(u64::MAX, |m, x| {
|
||||||
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
|
let edge = self.residual_graph.edge_weight(*x).expect("edge index not found");
|
||||||
min(m, edge.1 - edge.0)
|
min(m, edge.1 - edge.0)
|
||||||
});
|
});
|
||||||
|
|
||||||
// increase flow with bottleneck capacity along the augmenting path
|
// increase flow with bottleneck capacity along the augmenting path
|
||||||
for edge in edges {
|
for edge in edges {
|
||||||
|
// get source and target of edge
|
||||||
|
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
|
||||||
// increase flow of the forward edge with the calculated bottleneck value
|
// increase flow of the forward edge with the calculated bottleneck value
|
||||||
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
|
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
(*weight).0 += bottlenack_capacity;
|
(*weight).0 += bottleneck_capacity;
|
||||||
// increase capacity of the residual edge of the with the calculated bottleneck value
|
// increase capacity of the residual edge of the with the calculated bottleneck value
|
||||||
|
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
|
||||||
|
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
|
||||||
|
(*residual_weight).1 += bottleneck_capacity;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
@@ -77,6 +81,16 @@ impl MaxflowAlgorithm for FordFulkerson {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
// set all flows in graph to 0
|
||||||
|
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
|
||||||
|
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 = 0;
|
||||||
|
}
|
||||||
|
// reset residual graph as well
|
||||||
|
self.residual_graph = self.graph.residual();
|
||||||
|
}
|
||||||
|
|
||||||
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
while !self.step() {
|
while !self.step() {
|
||||||
continue;
|
continue;
|
||||||
@@ -92,57 +106,3 @@ impl MaxflowAlgorithm for FordFulkerson {
|
|||||||
"Ford-Fulkerson"
|
"Ford-Fulkerson"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
|
||||||
fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> {
|
|
||||||
let mut visited = graph.visit_map();
|
|
||||||
let mut stack = VecDeque::from([(source, vec![source])]);
|
|
||||||
|
|
||||||
// work through the main stack
|
|
||||||
while let Some((node, path)) = stack.pop_front() {
|
|
||||||
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
|
|
||||||
visited.visit(node);
|
|
||||||
|
|
||||||
// iterate over all outgoing edges & add neighboring nodes to the stack
|
|
||||||
for edge in outgoing_edges {
|
|
||||||
let neighbor = edge.target();
|
|
||||||
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
|
|
||||||
visited.visit(neighbor);
|
|
||||||
let mut new_path = path.clone();
|
|
||||||
new_path.push(neighbor);
|
|
||||||
// TODO: is this right?
|
|
||||||
if neighbor == destination {
|
|
||||||
return Some(new_path);
|
|
||||||
} else {
|
|
||||||
stack.push_front((neighbor, new_path));
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn ford_fulkerson(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
|
||||||
//let augmenting_path = dfs(graph, source, sink);
|
|
||||||
|
|
||||||
// continue while there are augmenting paths
|
|
||||||
while let Some(path) = dfs(&graph, source, sink) {
|
|
||||||
// find all edges along the path
|
|
||||||
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
|
|
||||||
// find bottleneck capacity along path
|
|
||||||
let increase_value = edges.iter().fold(u64::MAX, |m, x| {
|
|
||||||
let edge = graph.edge_weight(*x).expect("edge index not found");
|
|
||||||
min(m, edge.1 - edge.0)
|
|
||||||
});
|
|
||||||
|
|
||||||
// increase flow with bottleneck capacity along the augmenting path
|
|
||||||
for edge in edges {
|
|
||||||
let weight = graph.edge_weight_mut(edge).expect("edge not found");
|
|
||||||
(*weight).0 += increase_value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// return graph with augmented flows
|
|
||||||
graph
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -0,0 +1,172 @@
|
|||||||
|
use std::cmp::min;
|
||||||
|
use std::collections::{VecDeque, HashMap};
|
||||||
|
|
||||||
|
use itertools::Itertools;
|
||||||
|
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{EdgeRef, NodeRef, VisitMap, Visitable}, Direction};
|
||||||
|
use crate::{algorithms::common::{MaxflowAlgorithm}, graph::FlowGraph, random_generator::MaxflowProblem};
|
||||||
|
|
||||||
|
pub struct GoldbergTarjan {
|
||||||
|
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
|
source: NodeIndex,
|
||||||
|
sink: NodeIndex,
|
||||||
|
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
|
first_iteration: bool,
|
||||||
|
pub distance_label: Option<HashMap<NodeIndex, u64>>
|
||||||
|
// psi?
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GoldbergTarjan {
|
||||||
|
pub fn from_problem(p: &MaxflowProblem) -> Self {
|
||||||
|
Self {
|
||||||
|
residual_graph: p.g.residual(),
|
||||||
|
graph: p.g.clone(),
|
||||||
|
source: p.s,
|
||||||
|
sink: p.t,
|
||||||
|
first_iteration: true,
|
||||||
|
distance_label: None
|
||||||
|
}
|
||||||
|
// initialize with a valid preflow (max flow on all outgoing edges of s)
|
||||||
|
// assign height/label to every node using the labeling/height function (using BFS)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn active_node(&self) -> Option<(NodeIndex, u64)> {
|
||||||
|
for node in self.residual_graph.node_indices() {
|
||||||
|
// s and t are ne
|
||||||
|
if node == self.source || node == self.sink {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let outgoing_flows: u64 = self.residual_graph.edges_directed(node, Direction::Outgoing).map(|e| e.weight().0).sum();
|
||||||
|
let incoming_flows: u64 = self.residual_graph.edges_directed(node, Direction::Incoming).map(|e| e.weight().0).sum();
|
||||||
|
if (incoming_flows - outgoing_flows) > 0 {
|
||||||
|
// this node has an excess flow - return node index & excess flow
|
||||||
|
return Some((node, incoming_flows - outgoing_flows));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
// performs a BFS from sink to source and labels all nodes with their distance to the sink
|
||||||
|
fn compute_distances(&mut self) {
|
||||||
|
let mut distance_label = HashMap::new();
|
||||||
|
let mut visited = self.residual_graph.visit_map();
|
||||||
|
let mut queue = VecDeque::from([(self.sink, 0 as u64)]);
|
||||||
|
visited.visit(self.sink);
|
||||||
|
|
||||||
|
while let Some((node, distance)) = queue.pop_front() {
|
||||||
|
// first, visit nodes in queue
|
||||||
|
let outgoing_edges = self.residual_graph.edges_directed(node, Direction::Outgoing);
|
||||||
|
|
||||||
|
for edge in outgoing_edges {
|
||||||
|
let neighbor = edge.target();
|
||||||
|
if !visited.is_visited(&neighbor) {
|
||||||
|
visited.visit(neighbor);
|
||||||
|
distance_label.insert(neighbor, distance + 1);
|
||||||
|
queue.push_back((neighbor, distance + 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// set d(source) := |V|
|
||||||
|
distance_label.insert(self.source, self.residual_graph.node_count() as u64);
|
||||||
|
// set d(target) := 0
|
||||||
|
distance_label.insert(self.sink, 0);
|
||||||
|
|
||||||
|
assert!(distance_label.len() == self.residual_graph.node_count(), "Distance mapping is incomplete");
|
||||||
|
self.distance_label = Some(distance_label);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaxflowAlgorithm for GoldbergTarjan {
|
||||||
|
fn step(&mut self) -> bool {
|
||||||
|
// Initialization: if this is the first iteration, set the current flow on all outgoing edges of the source to their capacity
|
||||||
|
if self.first_iteration {
|
||||||
|
for edge in self.residual_graph.edges_directed(self.source, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>() {
|
||||||
|
let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
|
||||||
|
// set all outgoing edges to their maximum capacity
|
||||||
|
let weight = self.residual_graph.edge_weight_mut(edge).expect("edge id not found");
|
||||||
|
let capacity = (*weight).1.clone();
|
||||||
|
(*weight).0 = capacity;
|
||||||
|
// increase capacity of the residual edge of the with the calculated bottleneck value
|
||||||
|
let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
|
||||||
|
let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
|
||||||
|
(*residual_weight).1 += capacity;
|
||||||
|
}
|
||||||
|
self.first_iteration = false;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// if the distance labels were not already computed, compute them now
|
||||||
|
if self.distance_label == None {
|
||||||
|
self.compute_distances();
|
||||||
|
//println!("distance labeling: {:?}", self.distance_label);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main loop: while there is an active node, continue
|
||||||
|
if let Some((node, excess)) = self.active_node() {
|
||||||
|
// find all outgoing edges with available capacity
|
||||||
|
//println!("active node {:?} source {:?} sink {:?}", node, self.source, self.sink);
|
||||||
|
let valid_edges: Vec<petgraph::prelude::EdgeIndex> = self.residual_graph
|
||||||
|
.edges_directed(node, Direction::Outgoing)
|
||||||
|
.filter(|e| (e.weight().1 - e.weight().0) > 0 )
|
||||||
|
.filter(|e| self.distance_label.as_ref().expect("distance labelling not present").get(&e.target().id()).expect("No distance label 1 found") < self.distance_label.as_ref().expect("distance labelling not present").get(&node).expect("No distance label 2 found") )
|
||||||
|
.map(|e| e.id())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
if valid_edges.len() > 0 {
|
||||||
|
// node has valid edges -> push (move min[excess, capacity] to first valid edge)
|
||||||
|
//println!("push");
|
||||||
|
let edge = valid_edges[0];
|
||||||
|
// let (source, target) = self.residual_graph.edge_endpoints(edge).expect("edge not connected");
|
||||||
|
let available_capacity = self.residual_graph.edge_weight(edge).expect("Invalid edge index").1 - self.residual_graph.edge_weight(edge).expect("Invalid edge index").0;
|
||||||
|
let additional_flow = min(excess, available_capacity);
|
||||||
|
self.residual_graph.add_flow(edge, additional_flow);
|
||||||
|
|
||||||
|
// // increase flow of the forward edge with the calculated bottleneck value
|
||||||
|
// let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
// (*weight).0 += additional_flow;
|
||||||
|
// // increase capacity of the residual edge of the with the calculated bottleneck value
|
||||||
|
// let residual_edge = self.residual_graph.find_edge(target, source).expect("residual edge not found");
|
||||||
|
// let residual_weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(residual_edge).expect("edge not found");
|
||||||
|
// (*residual_weight).1 += additional_flow;
|
||||||
|
} else {
|
||||||
|
// node has no valid edges -> relabel (increase node distance label to min[neighbors] + 1)
|
||||||
|
let neighbors = self.residual_graph
|
||||||
|
.edges_directed(node, Direction::Outgoing)
|
||||||
|
.filter(|e| (e.weight().1 - e.weight().0) > 0 )
|
||||||
|
.sorted_by_key(|e| self.distance_label.as_ref().expect("distance labelling not present").get(&e.target()))
|
||||||
|
.map(|e| e.target())
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let lowest_distance = self.distance_label.as_ref().expect("distance labelling not present").get(&neighbors[0]).expect("No distance label found").clone();
|
||||||
|
//println!("relabel {} + 1 (index: {:?})", lowest_distance, node);
|
||||||
|
self.distance_label.as_mut().expect("distance labelling not present").insert(node, lowest_distance + 1);
|
||||||
|
}
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
// algorithm terminates if there are no more active nodes
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn reset(&mut self) {
|
||||||
|
// set all flows in graph to 0
|
||||||
|
for edge in self.graph.edge_indices().collect::<Vec<_>>() {
|
||||||
|
let weight = self.graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 = 0;
|
||||||
|
}
|
||||||
|
// reset the residual & layer graph as well
|
||||||
|
self.residual_graph = self.graph.residual();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
while !self.step() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Goldberg-Tarjan"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
mod edmonds_karp;
|
|
||||||
mod ford_fulkerson;
|
|
||||||
mod dinic;
|
|
||||||
mod common;
|
mod common;
|
||||||
|
mod ford_fulkerson;
|
||||||
|
mod edmonds_karp;
|
||||||
|
mod dinic;
|
||||||
|
mod goldberg_tarjan;
|
||||||
|
|
||||||
pub use ford_fulkerson::ford_fulkerson;
|
|
||||||
pub use edmonds_karp::edmonds_karp;
|
|
||||||
pub use dinic::dinic;
|
|
||||||
pub use ford_fulkerson::FordFulkerson;
|
|
||||||
pub use common::MaxflowAlgorithm;
|
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;
|
||||||
241
src/graph.rs
241
src/graph.rs
@@ -1,43 +1,19 @@
|
|||||||
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
|
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
|
||||||
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{VisitMap, Visitable}, Directed};
|
use petgraph::{algo::has_path_connecting, stable_graph::{EdgeIndex, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Directed, Direction};
|
||||||
use egui::Pos2;
|
use egui::{Pos2};
|
||||||
|
use std::collections::{HashMap, HashSet, VecDeque};
|
||||||
|
use std::cmp::min;
|
||||||
use crate::layout::CustomEdgeShape;
|
use crate::layout::CustomEdgeShape;
|
||||||
use egui::Color32;
|
use egui::Color32;
|
||||||
|
|
||||||
pub trait ResidualGraph {
|
// All methods which convert the graph into a GUI representations
|
||||||
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ResidualGraph for StableGraph<(f32, f32), (u64, u64)> {
|
|
||||||
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)> {
|
|
||||||
let mut res_graph = self.clone();
|
|
||||||
|
|
||||||
for edge in res_graph.edge_indices().map(|e| e).collect::<Vec<_>>() {
|
|
||||||
let (source, target) = res_graph.edge_endpoints(edge).expect("edge not connected");
|
|
||||||
if let Some(reverse_edge) = res_graph.find_edge(target, source) {
|
|
||||||
let (flow, capacity) = *res_graph.edge_weight(edge).expect("forward edge not found");
|
|
||||||
let (reverse_flow, reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
|
|
||||||
// update the forward edge
|
|
||||||
// TODO: this seems to overflow in certain cases
|
|
||||||
|
|
||||||
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
|
|
||||||
// update the reverse edge
|
|
||||||
//res_graph.update_edge(target, source, (reverse_flow, reverse_capacity + flow));
|
|
||||||
} else {
|
|
||||||
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
|
|
||||||
res_graph.add_edge(target, source, (0, 0));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
res_graph
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait GuiGraph {
|
pub trait GuiGraph {
|
||||||
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex, active_flows_only: bool) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
|
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
|
||||||
|
fn to_gui_graph_distance(&self, s: NodeIndex, t: NodeIndex, distance_labels: HashMap<NodeIndex, u64>) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
|
impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
|
||||||
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex, active_flows_only: bool) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
|
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
|
||||||
let mut graph = to_graph_custom(&self,
|
let mut graph = to_graph_custom(&self,
|
||||||
|n| {
|
|n| {
|
||||||
let (x, y) = *n.payload();
|
let (x, y) = *n.payload();
|
||||||
@@ -56,14 +32,63 @@ impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
|
|||||||
);
|
);
|
||||||
graph.node_mut(s).expect("node index not found").set_label(String::from("s"));
|
graph.node_mut(s).expect("node index not found").set_label(String::from("s"));
|
||||||
graph.node_mut(t).expect("node index not found").set_label(String::from("t"));
|
graph.node_mut(t).expect("node index not found").set_label(String::from("t"));
|
||||||
graph.node_mut(s).expect("node index not found").set_color(Color32::RED);
|
graph.node_mut(s).expect("node index not found").set_color(Color32::BLUE);
|
||||||
graph.node_mut(t).expect("node index not found").set_color(Color32::GREEN);
|
graph.node_mut(t).expect("node index not found").set_color(Color32::ORANGE);
|
||||||
|
graph
|
||||||
|
}
|
||||||
|
|
||||||
|
fn to_gui_graph_distance(&self, s: NodeIndex, t: NodeIndex, distance_labels: HashMap<NodeIndex, u64>) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
|
||||||
|
let mut graph = to_graph_custom(&self,
|
||||||
|
|n| {
|
||||||
|
let (x, y) = *n.payload();
|
||||||
|
default_node_transform(n);
|
||||||
|
n.set_location(Pos2::new(x, y));
|
||||||
|
if let Some(d) = distance_labels.get(&n.id()) {
|
||||||
|
if n.id() == s {
|
||||||
|
n.set_label(format!("s ({})", d));
|
||||||
|
} else if n.id() == t {
|
||||||
|
n.set_label(format!("t ({})", d));
|
||||||
|
} else {
|
||||||
|
n.set_label(format!("{}", d));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
n.set_label(String::from(""));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|e| {
|
||||||
|
let (flow, capacity): (u64, u64) = *e.payload();
|
||||||
|
default_edge_transform(e);
|
||||||
|
e.set_label(format!("{flow}:{capacity}"));
|
||||||
|
if flow > 0 {
|
||||||
|
e.set_selected(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
graph.node_mut(s).expect("node index not found").set_color(Color32::BLUE);
|
||||||
|
graph.node_mut(t).expect("node index not found").set_color(Color32::ORANGE);
|
||||||
graph
|
graph
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// all methods specific to MaxFlow problems
|
||||||
pub trait FlowGraph {
|
pub trait FlowGraph {
|
||||||
fn filter_empty_flows(&self) -> StableGraph<(f32, f32), (u64, u64)>;
|
fn filter_empty_flows(&self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
fn reset_flow(&mut self);
|
||||||
|
|
||||||
|
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
|
||||||
|
|
||||||
|
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool;
|
||||||
|
|
||||||
|
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64;
|
||||||
|
|
||||||
|
fn available_capacity(&self, edge: EdgeIndex) -> u64;
|
||||||
|
|
||||||
|
fn layer_graph(&self, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
fn add_flow(&mut self, edge: EdgeIndex, flow: u64);
|
||||||
|
|
||||||
|
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
|
impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
|
||||||
@@ -78,4 +103,152 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn layer_graph(&self, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
// filter graph for edges with no remaining capacity
|
||||||
|
let mut layer_graph = self.filter_map(|_, n| {
|
||||||
|
Some(*n)
|
||||||
|
}, |_, &e| {
|
||||||
|
match e.1 - e.0 {
|
||||||
|
0 => None,
|
||||||
|
_ => Some(e),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// filter graph for s-t paths
|
||||||
|
let mut visited = layer_graph.visit_map();
|
||||||
|
let mut queue = VecDeque::from([(source, 0)]);
|
||||||
|
while let Some((node, layer)) = queue.pop_front() {
|
||||||
|
//let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).collect::<Vec<_>>();
|
||||||
|
let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::<Vec<_>>();
|
||||||
|
visited.visit(node);
|
||||||
|
|
||||||
|
for edge in outgoing_edges {
|
||||||
|
let neighbor = layer_graph.edge_endpoints(edge).expect("edge index not found").1;
|
||||||
|
// if neighbor is unvisited, this is the shortest path to neighbor -> keep the edge
|
||||||
|
if !visited.is_visited(&neighbor) {
|
||||||
|
visited.visit(neighbor);
|
||||||
|
// stop traversing this path if destination is reached
|
||||||
|
if neighbor == sink {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
// add neighbor to queue if destination is not reached
|
||||||
|
queue.push_back((neighbor, layer+1));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if neighbor was visited before this edge is not on the shortest path -> remove it
|
||||||
|
layer_graph.remove_edge(edge);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
layer_graph
|
||||||
|
}
|
||||||
|
|
||||||
|
// set all flows back to zero
|
||||||
|
fn reset_flow(&mut self) {
|
||||||
|
for edge in self.edge_indices().collect::<Vec<_>>() {
|
||||||
|
let weight = self.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if flow is valid:
|
||||||
|
// - for every edge, the flow doesn't exceed the capacity
|
||||||
|
// - for every node, the amount of incoming flows is equal to the amount of outgoing flows
|
||||||
|
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool {
|
||||||
|
// capacity isn't exceeded
|
||||||
|
for edge in self.edge_indices().map(|e| e).collect::<Vec<_>>() {
|
||||||
|
if self.edge_weight(edge).expect("edge index not found").0 > self.edge_weight(edge).expect("edge index not found").1 {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// sum incoming flows = sum outgoing flows
|
||||||
|
for node in self.node_indices().map(|n| n).collect::<Vec<_>>() {
|
||||||
|
let outgoing_flows: u64 = self
|
||||||
|
.edges_directed(node, Direction::Outgoing)
|
||||||
|
.map(|e| e.weight().0)
|
||||||
|
.sum();
|
||||||
|
let incoming_flows: u64 = self
|
||||||
|
.edges_directed(node, Direction::Incoming)
|
||||||
|
.map(|e| e.weight().0)
|
||||||
|
.sum();
|
||||||
|
if incoming_flows != outgoing_flows {
|
||||||
|
if node != source && node != sink {
|
||||||
|
// println!("invalid flow at {:?} incoming flows: {} outgoing flows: {}", node, incoming_flows, outgoing_flows);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
// checks if there exists a saturated cut in the graph (a bip)
|
||||||
|
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool {
|
||||||
|
let mut s = HashSet::new();
|
||||||
|
let mut t = HashSet::new();
|
||||||
|
|
||||||
|
for node in self.node_indices().into_iter().collect::<Vec<_>>() {
|
||||||
|
if has_path_connecting(self, source, node, None) {
|
||||||
|
s.insert(node);
|
||||||
|
} else {
|
||||||
|
t.insert(node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: assert that incoming flows at sink are equal to outgoing flows at source (otherwise return 0)
|
||||||
|
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64 {
|
||||||
|
let outgoing_flows_source: u64 = self.edges_directed(source, Direction::Outgoing).map(|e| e.weight().0).sum();
|
||||||
|
let incoming_flows_source: u64 = self.edges_directed(source, Direction::Incoming).map(|e| e.weight().0).sum();
|
||||||
|
let outgoing_flows_sink: u64 = self.edges_directed(sink, Direction::Outgoing).map(|e| e.weight().0).sum();
|
||||||
|
let incoming_flows_sink: u64 = self.edges_directed(sink, Direction::Incoming).map(|e| e.weight().0).sum();
|
||||||
|
let total_flow_source = outgoing_flows_source - incoming_flows_source;
|
||||||
|
let total_flow_sink = incoming_flows_sink - outgoing_flows_sink;
|
||||||
|
assert!(total_flow_sink == total_flow_source);
|
||||||
|
return total_flow_source;
|
||||||
|
}
|
||||||
|
|
||||||
|
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
let mut res_graph = self.clone();
|
||||||
|
|
||||||
|
for edge in res_graph.edge_indices().map(|e| e).collect::<Vec<_>>() {
|
||||||
|
let (source, target) = res_graph.edge_endpoints(edge).expect("edge not connected");
|
||||||
|
if let Some(reverse_edge) = res_graph.find_edge(target, source) {
|
||||||
|
let (flow, capacity) = *res_graph.edge_weight(edge).expect("forward edge not found");
|
||||||
|
let (reverse_flow, _reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
|
||||||
|
// update the edge
|
||||||
|
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
|
||||||
|
} else {
|
||||||
|
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
|
||||||
|
res_graph.add_edge(target, source, (0, 0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res_graph
|
||||||
|
}
|
||||||
|
|
||||||
|
fn available_capacity(&self, edge: EdgeIndex) -> u64 {
|
||||||
|
self.edge_weight(edge).expect("edge not found").1 - self.edge_weight(edge).expect("edge not found").0
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_flow(&mut self, edge: EdgeIndex, flow: u64) {
|
||||||
|
// search for reverse edge and subtract existing flow if possible
|
||||||
|
let (source, target) = self.edge_endpoints(edge).expect("edge not connected");
|
||||||
|
let residual_edge = self.find_edge(target, source).expect("residual edge not found");
|
||||||
|
let residual_weight: &mut (u64, u64) = self.edge_weight_mut(residual_edge).expect("residual edge not found");
|
||||||
|
let forward_flow = i128::from(flow) - i128::from((*residual_weight).0);
|
||||||
|
// subtract minimum of additional_flow and residual_weight
|
||||||
|
if residual_weight.0 > 0 {
|
||||||
|
(*residual_weight).0 -= min(residual_weight.0, flow);
|
||||||
|
}
|
||||||
|
assert!((*residual_weight).0 <= (*residual_weight).1);
|
||||||
|
// add remaining flow to forward edge
|
||||||
|
let forward_weight: &mut (u64, u64) = self.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
if forward_flow > 0 {
|
||||||
|
(*forward_weight).0 += u64::try_from(forward_flow).expect("error casting flow to u64");
|
||||||
|
}
|
||||||
|
assert!((*forward_weight).0 <= (*forward_weight).1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,16 +1,12 @@
|
|||||||
use std::f32::consts::PI;
|
use std::f32::consts::PI;
|
||||||
|
|
||||||
use egui::{
|
use egui::{
|
||||||
epaint::{CubicBezierShape, QuadraticBezierShape, TextShape}, text::LayoutJob, Color32, FontFamily, FontId, Pos2, Shape, Stroke, TextFormat, Vec2
|
epaint::{CubicBezierShape, QuadraticBezierShape, TextShape}, Color32, FontFamily, FontId, Pos2, Shape, Stroke, Vec2
|
||||||
};
|
};
|
||||||
|
|
||||||
use petgraph::{matrix_graph::Nullable, stable_graph::IndexType, EdgeType};
|
use petgraph::{matrix_graph::Nullable, stable_graph::IndexType, EdgeType};
|
||||||
|
|
||||||
use egui_graphs::{
|
use egui_graphs::{DisplayEdge, EdgeProps, DisplayNode, Node, DrawContext};
|
||||||
DefaultEdgeShape, DisplayEdge, EdgeProps,
|
|
||||||
DefaultNodeShape, DisplayNode, NodeProps,
|
|
||||||
Node, DrawContext
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
|
|||||||
5
src/lib.rs
Normal file
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;
|
||||||
245
src/main.rs
245
src/main.rs
@@ -1,62 +1,143 @@
|
|||||||
use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
|
use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
|
||||||
use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel};
|
use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel, Slider, Window};
|
||||||
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
use egui_graphs::{DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
||||||
use geo::algorithm;
|
use petgraph::{stable_graph::{StableGraph}, Directed};
|
||||||
use petgraph::{stable_graph::{NodeIndex, StableGraph}, Directed};
|
use std::{sync::{Arc, Mutex}, thread, thread::JoinHandle, time::Instant};
|
||||||
|
|
||||||
use random_generator::MaxflowProblem;
|
use random_generator::MaxflowProblem;
|
||||||
use layout::CustomEdgeShape;
|
use layout::CustomEdgeShape;
|
||||||
use crate::algorithms::{MaxflowAlgorithm, FordFulkerson};
|
use crate::algorithms::{MaxflowAlgorithm, MaxflowAlgorithmEnum, FordFulkerson, EdmondsKarp, Dinic, GoldbergTarjan};
|
||||||
use graph::{ResidualGraph, GuiGraph, FlowGraph};
|
use graph::{GuiGraph, FlowGraph};
|
||||||
|
use testing::{run_small_tests,run_large_tests};
|
||||||
|
|
||||||
mod random_generator;
|
mod random_generator;
|
||||||
mod algorithms;
|
mod algorithms;
|
||||||
mod layout;
|
mod layout;
|
||||||
mod graph;
|
mod graph;
|
||||||
|
mod testing;
|
||||||
|
|
||||||
type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
|
//type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
enum MaxflowAlgos {
|
|
||||||
FordFulkerson,
|
|
||||||
EdmondsKarp,
|
|
||||||
Dinic,
|
|
||||||
GoldbergTarjan,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct MaxflowApp {
|
pub struct MaxflowApp {
|
||||||
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
gui_graph: Arc<Mutex<Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>>>,
|
||||||
p: MaxflowProblem,
|
problem: MaxflowProblem,
|
||||||
r: StableGraph<(f32, f32), (u64, u64)>,
|
current_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
node_count: u64,
|
node_count: u64,
|
||||||
max_capacity: u64,
|
max_capacity: u64,
|
||||||
algorithm: Box<dyn MaxflowAlgorithm>,
|
algorithm: MaxflowAlgorithmEnum,
|
||||||
display_residual: bool,
|
|
||||||
display_active_flows: bool,
|
display_active_flows: bool,
|
||||||
|
display_layer_graph: bool,
|
||||||
|
algorithm_finished: bool,
|
||||||
|
test_thread: Option<JoinHandle<String>>,
|
||||||
|
test_results: String,
|
||||||
|
test_results_open: bool,
|
||||||
|
delay: u64,
|
||||||
|
animation_runnning: bool,
|
||||||
|
last_update: Instant
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MaxflowApp {
|
impl MaxflowApp {
|
||||||
fn new(_: &CreationContext<'_>) -> Self {
|
fn new(_: &CreationContext<'_>) -> Self {
|
||||||
let problem = MaxflowProblem::new(10, 10);
|
let problem = MaxflowProblem::new_planar_graph(10, 10);
|
||||||
Self {
|
Self {
|
||||||
g: problem.g.to_gui_graph(problem.s, problem.t, false),
|
current_graph: problem.g.clone(),
|
||||||
r: StableGraph::default(),
|
gui_graph: Arc::new(Mutex::new(problem.g.to_gui_graph(problem.s, problem.t))),
|
||||||
p: problem, node_count: 10,
|
algorithm: FordFulkerson::from_problem(&problem).into(),
|
||||||
|
problem,
|
||||||
|
node_count: 10,
|
||||||
max_capacity: 5,
|
max_capacity: 5,
|
||||||
algorithm: FordFulkerson,
|
|
||||||
display_residual: false,
|
|
||||||
display_active_flows: false,
|
display_active_flows: false,
|
||||||
|
display_layer_graph: false,
|
||||||
|
algorithm_finished: false,
|
||||||
|
test_thread: None,
|
||||||
|
test_results: String::default(),
|
||||||
|
test_results_open: false,
|
||||||
|
delay: 200,
|
||||||
|
animation_runnning: false,
|
||||||
|
last_update: Instant::now()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// reinitialize the current algorithm & reset its internal state
|
||||||
|
fn reset_state(&mut self) {
|
||||||
|
// undo finished run
|
||||||
|
self.algorithm_finished = false;
|
||||||
|
// reinitialize the current graph from the problem
|
||||||
|
self.current_graph = self.problem.g.clone();
|
||||||
|
// assign new maxflow problem to the graph
|
||||||
|
match self.algorithm {
|
||||||
|
MaxflowAlgorithmEnum::FordFulkerson(_) => self.algorithm = FordFulkerson::from_problem(&self.problem).into(),
|
||||||
|
MaxflowAlgorithmEnum::EdmondsKarp(_) => self.algorithm = EdmondsKarp::from_problem(&self.problem).into(),
|
||||||
|
MaxflowAlgorithmEnum::Dinic(_) => self.algorithm = Dinic::from_problem(&self.problem).into(),
|
||||||
|
MaxflowAlgorithmEnum::GoldbergTarjan(_) => self.algorithm = GoldbergTarjan::from_problem(&self.problem).into(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// display the current graph & apply display options from GUI
|
||||||
fn update_graph(&mut self) {
|
fn update_graph(&mut self) {
|
||||||
let graph = match self.display_residual {
|
let graph = &self.current_graph;
|
||||||
true => &self.p.g.residual(),
|
let mut gui_graph_lock = self.gui_graph.lock().unwrap();
|
||||||
false => &self.p.g,
|
|
||||||
};
|
|
||||||
if self.display_active_flows {
|
if self.display_active_flows {
|
||||||
self.g = graph.filter_empty_flows().to_gui_graph(self.p.s, self.p.t, self.display_active_flows);
|
*gui_graph_lock = graph.filter_empty_flows().to_gui_graph(self.problem.s, self.problem.t);
|
||||||
} else {
|
} else {
|
||||||
self.g = graph.to_gui_graph(self.p.s, self.p.t, self.display_active_flows);
|
match &mut self.algorithm {
|
||||||
|
MaxflowAlgorithmEnum::GoldbergTarjan(g) => {
|
||||||
|
if let Some(l) = g.distance_label.clone() {
|
||||||
|
*gui_graph_lock = graph.to_gui_graph_distance(self.problem.s, self.problem.t, l);
|
||||||
|
} else {
|
||||||
|
*gui_graph_lock = graph.to_gui_graph(self.problem.s, self.problem.t);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::Dinic(_) => {
|
||||||
|
if self.display_layer_graph {
|
||||||
|
*gui_graph_lock = graph.layer_graph(self.problem.s, self.problem.t).to_gui_graph(self.problem.s, self.problem.t);
|
||||||
|
} else {
|
||||||
|
*gui_graph_lock = graph.to_gui_graph(self.problem.s, self.problem.t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => *gui_graph_lock = graph.to_gui_graph(self.problem.s, self.problem.t)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_algorithm(&mut self) {
|
||||||
|
match &mut self.algorithm {
|
||||||
|
MaxflowAlgorithmEnum::FordFulkerson(x) => {
|
||||||
|
self.current_graph = x.run();
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
|
||||||
|
self.current_graph = x.run();
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::Dinic(x) => {
|
||||||
|
self.current_graph = x.run();
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
|
||||||
|
self.current_graph = x.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step_algorithm(&mut self) -> bool {
|
||||||
|
match &mut self.algorithm {
|
||||||
|
MaxflowAlgorithmEnum::FordFulkerson(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::EdmondsKarp(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::Dinic(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
},
|
||||||
|
MaxflowAlgorithmEnum::GoldbergTarjan(x) => {
|
||||||
|
let finished = x.step();
|
||||||
|
self.current_graph = x.graph();
|
||||||
|
finished
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -65,7 +146,7 @@ impl App for MaxflowApp {
|
|||||||
fn update(&mut self, ctx: &Context, _: &mut Frame) {
|
fn update(&mut self, ctx: &Context, _: &mut Frame) {
|
||||||
ctx.set_theme(egui::Theme::Light);
|
ctx.set_theme(egui::Theme::Light);
|
||||||
CentralPanel::default().show(ctx, |ui| {
|
CentralPanel::default().show(ctx, |ui| {
|
||||||
ui.add(&mut GraphView::<_, _, _, _, _, CustomEdgeShape, LayoutStateRandom, LayoutRandom>::new(&mut self.g).with_styles(&SettingsStyle::default().with_labels_always(true)));
|
ui.add(&mut GraphView::<_, _, _, _, _, CustomEdgeShape, LayoutStateRandom, LayoutRandom>::new(&mut self.gui_graph.lock().unwrap()).with_styles(&SettingsStyle::default().with_labels_always(true)));
|
||||||
});
|
});
|
||||||
SidePanel::right("right_panel")
|
SidePanel::right("right_panel")
|
||||||
.min_width(200.)
|
.min_width(200.)
|
||||||
@@ -75,12 +156,13 @@ impl App for MaxflowApp {
|
|||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ui.label("node count");
|
ui.label("node count");
|
||||||
ui.add(egui::DragValue::new(&mut self.node_count).range(2..=1000));
|
ui.add(egui::DragValue::new(&mut self.node_count).range(2..=50));
|
||||||
ui.label("maximum capacity");
|
ui.label("maximum capacity");
|
||||||
ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100));
|
ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100));
|
||||||
// TODO: add generation strategy (random, pseudo-random)
|
// TODO: add generation strategy (random, pseudo-random)
|
||||||
if ui.button("generate graph").clicked() {
|
if ui.button("generate graph").clicked() {
|
||||||
self.p = random_generator::MaxflowProblem::new(self.node_count, self.max_capacity);
|
self.problem = random_generator::MaxflowProblem::new_planar_graph(self.node_count, self.max_capacity);
|
||||||
|
self.reset_state();
|
||||||
self.update_graph();
|
self.update_graph();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -89,45 +171,116 @@ impl App for MaxflowApp {
|
|||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ComboBox::from_label("algorithm")
|
ComboBox::from_label("algorithm")
|
||||||
.selected_text(self.algorithm.name())
|
.selected_text(self.algorithm.to_string())
|
||||||
.show_ui(ui, |ui| {
|
.show_ui(ui, |ui| {
|
||||||
ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(self.p), "Ford-Fulkerson");
|
let mut changed = ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(&self.problem).into(), "Ford-Fulkerson").changed();
|
||||||
|
changed = changed || ui.selectable_value(&mut self.algorithm, EdmondsKarp::from_problem(&self.problem).into(), "Edmonds-Karp").changed();
|
||||||
|
changed = changed || ui.selectable_value(&mut self.algorithm, Dinic::from_problem(&self.problem).into(), "Dinic").changed();
|
||||||
|
changed = changed || ui.selectable_value(&mut self.algorithm, GoldbergTarjan::from_problem(&self.problem).into(), "Goldberg-Tarjan").changed();
|
||||||
|
//for algo in MaxflowAlgos::iter() {
|
||||||
|
//}
|
||||||
//ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
//ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
||||||
//ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
//ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
||||||
|
if changed {
|
||||||
|
self.reset_state();
|
||||||
|
self.update_graph();
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
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() {
|
if ui.button("step").clicked() {
|
||||||
// check if algo is initialized
|
// check if algo is initialized
|
||||||
// if not, initialize
|
// if not, initialize
|
||||||
// run step function
|
// run step function
|
||||||
|
self.algorithm_finished = self.step_algorithm();
|
||||||
|
self.update_graph();
|
||||||
}
|
}
|
||||||
if ui.button("run").clicked() {
|
if ui.button("run").clicked() {
|
||||||
self.algorithm.run();
|
self.animation_runnning = true;
|
||||||
self.p = MaxflowProblem::from(self.algorithm.graph(), self.p.s, self.p.t);
|
|
||||||
self.update_graph();
|
|
||||||
//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);
|
|
||||||
}
|
}
|
||||||
|
});
|
||||||
if ui.button("reset").clicked() {
|
if ui.button("reset").clicked() {
|
||||||
self.p.reset_flow();
|
self.reset_state();
|
||||||
self.update_graph();
|
self.update_graph();
|
||||||
}
|
}
|
||||||
// step button (disable when finished)
|
// step button (disable when finished)
|
||||||
|
if self.animation_runnning {
|
||||||
|
if self.last_update.elapsed().as_millis() > self.delay as u128 {
|
||||||
|
self.algorithm_finished = self.step_algorithm();
|
||||||
|
self.update_graph();
|
||||||
|
self.animation_runnning = !self.algorithm_finished;
|
||||||
|
self.last_update = Instant::now();
|
||||||
|
}
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
CollapsingHeader::new("Display options")
|
CollapsingHeader::new("Display options")
|
||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
if ui.checkbox(&mut self.display_residual, "show residual graph").changed() {
|
|
||||||
self.update_graph();
|
|
||||||
}
|
|
||||||
if ui.checkbox(&mut self.display_active_flows, "show active flows only").changed() {
|
if ui.checkbox(&mut self.display_active_flows, "show active flows only").changed() {
|
||||||
self.update_graph();
|
self.update_graph();
|
||||||
}
|
}
|
||||||
|
ui.add_enabled_ui(matches!(self.algorithm, MaxflowAlgorithmEnum::Dinic(_)), |ui| {
|
||||||
|
if ui.checkbox(&mut self.display_layer_graph, "show layer graph (Dinic)").changed() {
|
||||||
|
self.update_graph();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
CollapsingHeader::new("Properties")
|
||||||
|
.default_open(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
ui.label(format!("is valid flow: {}", self.current_graph.valid_flow(self.problem.s, self.problem.t)));
|
||||||
|
|
||||||
|
ui.label(format!("total flow: {}", self.current_graph.total_flow(self.problem.s, self.problem.t)));
|
||||||
|
});
|
||||||
|
|
||||||
|
CollapsingHeader::new("Tests")
|
||||||
|
.default_open(true)
|
||||||
|
.show(ui, |ui| {
|
||||||
|
if ui.button("run tests (small)").clicked() {
|
||||||
|
// run tests
|
||||||
|
match &mut self.test_thread {
|
||||||
|
// other thread currently running - ignore
|
||||||
|
Some(_) => {},
|
||||||
|
None => {
|
||||||
|
self.test_thread = Some(thread::spawn(move || { run_small_tests() }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if ui.button("run tests (large)").clicked() {
|
||||||
|
// run tests
|
||||||
|
match &mut self.test_thread {
|
||||||
|
// other thread currently running - ignore
|
||||||
|
Some(_) => {},
|
||||||
|
None => {
|
||||||
|
self.test_thread = Some(thread::spawn(|| { run_large_tests() }));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
match &mut self.test_thread {
|
||||||
|
Some(t) => {
|
||||||
|
if t.is_finished() {
|
||||||
|
self.test_results = self.test_thread.take().map(JoinHandle::join).expect("No running thread found").expect("Could not join thread");
|
||||||
|
self.test_results_open = true;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
None => {},
|
||||||
|
}
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
if self.test_results_open {
|
||||||
|
Window::new("Test Results")
|
||||||
|
.open(&mut self.test_results_open)
|
||||||
|
.show(ctx, |ui| {
|
||||||
|
ui.label(self.test_results.clone());
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,16 +1,10 @@
|
|||||||
use petgraph::data::Build;
|
|
||||||
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
||||||
use petgraph::{Directed};
|
|
||||||
use egui_graphs::{default_edge_transform, default_node_transform, to_graph_custom, DefaultEdgeShape, DefaultNodeShape, Graph};
|
|
||||||
use egui::Pos2;
|
|
||||||
use rand::thread_rng;
|
use rand::thread_rng;
|
||||||
use rand::Rng;
|
use rand::Rng;
|
||||||
use rand::seq::SliceRandom;
|
use rand::seq::SliceRandom;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
||||||
use egui::Color32;
|
use std::cmp::min;
|
||||||
|
|
||||||
use crate::layout::CustomEdgeShape;
|
|
||||||
|
|
||||||
pub struct MaxflowProblem {
|
pub struct MaxflowProblem {
|
||||||
pub g: StableGraph<(f32, f32), (u64, u64)>,
|
pub g: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
@@ -19,7 +13,9 @@ pub struct MaxflowProblem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl MaxflowProblem {
|
impl MaxflowProblem {
|
||||||
pub fn new(num_nodes: u64, max_capacity: u64) -> Self {
|
// this function generates a maximally connected, planar graph with x/y coordinates which can be used for
|
||||||
|
// displaying a max-flow problem in a GUI. since the algorithm is rather inefficient, it only scales up to 50 nodes
|
||||||
|
pub fn new_planar_graph(num_nodes: u64, max_capacity: u64) -> Self {
|
||||||
// Node Type: (x,y) Edge Type: (current_flow, capacity)
|
// Node Type: (x,y) Edge Type: (current_flow, capacity)
|
||||||
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
|
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
|
||||||
let mut nodes: Vec<NodeIndex> = Vec::new();
|
let mut nodes: Vec<NodeIndex> = Vec::new();
|
||||||
@@ -27,8 +23,20 @@ impl MaxflowProblem {
|
|||||||
|
|
||||||
// generate nodes
|
// generate nodes
|
||||||
for _i in 0..num_nodes {
|
for _i in 0..num_nodes {
|
||||||
let x: f32 = rng.gen_range(0.0..=400.0);
|
// check if node is too close to another node (distance < 40)
|
||||||
let y: f32 = rng.gen_range(0.0..=300.0);
|
let mut too_close = true;
|
||||||
|
let (mut x, mut y) = (0.0, 0.0);
|
||||||
|
// this is a poor attempt at modeling a do..while look in Rust
|
||||||
|
while too_close {
|
||||||
|
too_close = false;
|
||||||
|
x = rng.gen_range(0.0..=400.0);
|
||||||
|
y = rng.gen_range(0.0..=300.0);
|
||||||
|
for node in &nodes {
|
||||||
|
if MaxflowProblem::distance(*graph.node_weight(*node).expect("node index not found"), (x,y)) < 40.0 {
|
||||||
|
too_close = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
let n = graph.add_node((x,y));
|
let n = graph.add_node((x,y));
|
||||||
nodes.push(n);
|
nodes.push(n);
|
||||||
}
|
}
|
||||||
@@ -36,6 +44,7 @@ impl MaxflowProblem {
|
|||||||
// select source (s) and sink (t)
|
// select source (s) and sink (t)
|
||||||
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
||||||
|
|
||||||
|
// TODO: this probably takes forever...
|
||||||
// iterate over all possible edges
|
// iterate over all possible edges
|
||||||
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
|
||||||
for pair in nodes.clone().into_iter().combinations(2) {
|
for pair in nodes.clone().into_iter().combinations(2) {
|
||||||
@@ -53,7 +62,7 @@ impl MaxflowProblem {
|
|||||||
|
|
||||||
// insert the edge if possible
|
// insert the edge if possible
|
||||||
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
|
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
|
||||||
for (node1, node2, distance) in possible_edges {
|
for (node1, node2, _) in possible_edges {
|
||||||
// check for collision with already inserted edges
|
// check for collision with already inserted edges
|
||||||
let mut intersects = false;
|
let mut intersects = false;
|
||||||
for (node3, node4) in &inserted_edges {
|
for (node3, node4) in &inserted_edges {
|
||||||
@@ -81,6 +90,36 @@ impl MaxflowProblem {
|
|||||||
Self { g: graph, s: s, t: t }
|
Self { g: graph, s: s, t: t }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// this function generates a fully connected random graph which can be used for solving max-flow problems
|
||||||
|
pub fn new_random_graph(num_nodes: u64, max_capacity: u64) -> Self {
|
||||||
|
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
|
||||||
|
let mut nodes: Vec<NodeIndex> = Vec::new();
|
||||||
|
let mut rng = thread_rng();
|
||||||
|
|
||||||
|
for _i in 0..num_nodes {
|
||||||
|
let x = rng.gen_range(0.0..=400.0);
|
||||||
|
let y = rng.gen_range(0.0..=300.0);
|
||||||
|
let n = graph.add_node((x,y));
|
||||||
|
|
||||||
|
// choose a random number of nodes between 1 and 5 to connect to
|
||||||
|
let num_neighbors = rng.gen_range(min(nodes.len(), 1)..=min(nodes.len(), 5));
|
||||||
|
let neighbors = nodes.choose_multiple(&mut rng, num_neighbors);
|
||||||
|
for neighbor in neighbors {
|
||||||
|
// insert edges for each neighbor
|
||||||
|
let capacity1: u64 = rng.gen_range(1..=max_capacity);
|
||||||
|
let capacity2: u64 = rng.gen_range(1..=max_capacity);
|
||||||
|
graph.add_edge(n, *neighbor, (0, capacity1));
|
||||||
|
graph.add_edge(*neighbor, n, (0, capacity2));
|
||||||
|
}
|
||||||
|
nodes.push(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// choose source & sink nodes
|
||||||
|
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
||||||
|
|
||||||
|
Self { g: graph, s: s, t: t }
|
||||||
|
}
|
||||||
|
|
||||||
pub fn from(g: StableGraph<(f32, f32), (u64, u64)>, s: NodeIndex, t: NodeIndex) -> Self{
|
pub fn from(g: StableGraph<(f32, f32), (u64, u64)>, s: NodeIndex, t: NodeIndex) -> Self{
|
||||||
Self {g: g, s: s, t: t}
|
Self {g: g, s: s, t: t}
|
||||||
}
|
}
|
||||||
@@ -102,36 +141,4 @@ impl MaxflowProblem {
|
|||||||
_ => false
|
_ => false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// set all flows back to zero
|
|
||||||
pub fn reset_flow(&mut self) {
|
|
||||||
for edge in self.g.edge_indices().collect::<Vec<_>>() {
|
|
||||||
let weight = self.g.edge_weight_mut(edge).expect("edge not found");
|
|
||||||
(*weight).0 = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn to_gui_graph(&self) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
|
|
||||||
let mut graph = to_graph_custom(&self.g,
|
|
||||||
|n| {
|
|
||||||
let (x, y) = *n.payload();
|
|
||||||
default_node_transform(n);
|
|
||||||
n.set_location(Pos2::new(x, y));
|
|
||||||
n.set_label(String::from(""));
|
|
||||||
},
|
|
||||||
|e| {
|
|
||||||
let (flow, capacity): (u64, u64) = *e.payload();
|
|
||||||
default_edge_transform(e);
|
|
||||||
e.set_label(format!("{flow}:{capacity}"));
|
|
||||||
if flow > 0 {
|
|
||||||
e.set_selected(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
graph.node_mut(self.s).expect("node index not found").set_label(String::from("s"));
|
|
||||||
graph.node_mut(self.t).expect("node index not found").set_label(String::from("t"));
|
|
||||||
graph.node_mut(self.s).expect("node index not found").set_color(Color32::RED);
|
|
||||||
graph.node_mut(self.t).expect("node index not found").set_color(Color32::GREEN);
|
|
||||||
graph
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
87
src/testing.rs
Normal file
87
src/testing.rs
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
use itertools::Itertools;
|
||||||
|
use crate::{algorithms::{Dinic, EdmondsKarp, FordFulkerson, GoldbergTarjan, MaxflowAlgorithm, MaxflowAlgorithmEnum}, random_generator::MaxflowProblem, graph::FlowGraph};
|
||||||
|
|
||||||
|
use std::time::{Instant};
|
||||||
|
|
||||||
|
fn solve_problem_all_algos(problem: &MaxflowProblem) -> (f64, f64, f64, f64) {
|
||||||
|
let ff = FordFulkerson::from_problem(&problem);
|
||||||
|
let ek = EdmondsKarp::from_problem(&problem);
|
||||||
|
let di = Dinic::from_problem(&problem);
|
||||||
|
let gt = GoldbergTarjan::from_problem(&problem);
|
||||||
|
let mut instances:Vec<MaxflowAlgorithmEnum> = vec![ff.into(), ek.into(), di.into(), gt.into()];
|
||||||
|
let mut durations = vec![0.0; 4];
|
||||||
|
for (i,a) in instances.iter_mut().enumerate() {
|
||||||
|
let now = Instant::now();
|
||||||
|
match a {
|
||||||
|
MaxflowAlgorithmEnum::GoldbergTarjan(_) => { a.run(); },
|
||||||
|
_ => {
|
||||||
|
a.run();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//a.run();
|
||||||
|
durations[i] = now.elapsed().as_secs_f64();
|
||||||
|
|
||||||
|
// check if the flow is valid
|
||||||
|
assert!(a.graph().valid_flow(problem.s, problem.t), "invalid flow");
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if all algorithms return the same total flow
|
||||||
|
let total_flows: Vec<u64> = instances.iter_mut().map(|x| x.graph().total_flow(problem.s, problem.t)).collect();
|
||||||
|
println!("total flows: {:?}", total_flows);
|
||||||
|
assert!(total_flows.iter().all(|&x| x == total_flows[0]), "algorithms return different total flows");
|
||||||
|
|
||||||
|
return durations.into_iter().collect_tuple().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_tests(test_tuples: Vec<(u64, u64, u64)>) -> String {
|
||||||
|
let mut response = String::new();
|
||||||
|
|
||||||
|
for (problem_count, nodes, capacity) in test_tuples {
|
||||||
|
response += format!("=== Testing with problem_count={}, nodes={}, capacity={}\n", problem_count, nodes, capacity).as_str();
|
||||||
|
let mut problems: Vec<_> = Vec::new();
|
||||||
|
for _ in 0..problem_count {
|
||||||
|
problems.push(MaxflowProblem::new_random_graph(nodes, capacity));
|
||||||
|
}
|
||||||
|
let mut durations = Vec::new();
|
||||||
|
for (_, problem) in problems.iter_mut().enumerate() {
|
||||||
|
durations.push(solve_problem_all_algos(problem));
|
||||||
|
}
|
||||||
|
|
||||||
|
let d0 = durations.iter().map(|x| x.0).sum::<f64>() / problem_count as f64;
|
||||||
|
let d1 = durations.iter().map(|x| x.1).sum::<f64>() / problem_count as f64;
|
||||||
|
let d2 = durations.iter().map(|x| x.2).sum::<f64>() / problem_count as f64;
|
||||||
|
let d3 = durations.iter().map(|x| x.3).sum::<f64>() / problem_count as f64;
|
||||||
|
response += format!("Ford-Fulkerson:\t{:02.8}s\nEdmonds-Karp:\t{:02.8}s\nDinic:\t\t\t\t\t\t{:02.8}s\nGoldberg-Tarjan:\t{:02.8}s\n\n", d0, d1, d2, d3).as_str();
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_small_tests() -> String {
|
||||||
|
let test_tuples = [
|
||||||
|
// (number of problems, number of nodes, max. capacity)
|
||||||
|
(10, 10, 10),
|
||||||
|
(10, 100, 10),
|
||||||
|
(10, 100, 100),
|
||||||
|
(10, 200, 10),
|
||||||
|
(10, 200, 100)
|
||||||
|
];
|
||||||
|
return run_tests(test_tuples.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn run_large_tests() -> String {
|
||||||
|
let test_tuples = [
|
||||||
|
// (number of problems, number of nodes, max. capacity)
|
||||||
|
(10, 100, 10),
|
||||||
|
(10, 100, 100),
|
||||||
|
(10, 100, 1000),
|
||||||
|
(10, 500, 10),
|
||||||
|
(10, 500, 100),
|
||||||
|
(10, 500, 1000),
|
||||||
|
(10, 1000, 10),
|
||||||
|
(10, 1000, 100),
|
||||||
|
(10, 1000, 1000),
|
||||||
|
(10, 10000, 10)
|
||||||
|
];
|
||||||
|
return run_tests(test_tuples.into());
|
||||||
|
}
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
|
use maxflow_rs::graph::FlowGraph;
|
||||||
use petgraph::prelude::StableGraph;
|
use petgraph::prelude::StableGraph;
|
||||||
|
use petgraph::stable_graph::NodeIndex;
|
||||||
|
use maxflow_rs::random_generator::MaxflowProblem;
|
||||||
|
use maxflow_rs::algorithms::{MaxflowAlgorithm, FordFulkerson, EdmondsKarp, Dinic, GoldbergTarjan};
|
||||||
|
|
||||||
// Example taken from: https://en.wikipedia.org/wiki/Dinic%27s_algorithm#Example
|
// Example taken from: https://en.wikipedia.org/wiki/Dinic%27s_algorithm#Example
|
||||||
#[test]
|
fn generate_small_maxflow_example() -> (StableGraph<(f32, f32), (u64, u64)>, StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) {
|
||||||
fn test_maxflow_small_graph() {
|
|
||||||
let mut g: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
|
let mut g: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
|
||||||
let s = g.add_node((0., 5.));
|
let s = g.add_node((0., 5.));
|
||||||
let one = g.add_node((10., 0.));
|
let one = g.add_node((10., 0.));
|
||||||
@@ -22,4 +24,66 @@ fn test_maxflow_small_graph() {
|
|||||||
g.add_edge(four, t, (0,10));
|
g.add_edge(four, t, (0,10));
|
||||||
|
|
||||||
// TODO:
|
// TODO:
|
||||||
|
// let mut m: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
|
||||||
|
// let s = m.add_node((0., 5.));
|
||||||
|
// let one = m.add_node((10., 0.));
|
||||||
|
// let two = m.add_node((10., 10.));
|
||||||
|
// let three = m.add_node((20., 0.));
|
||||||
|
// let four = m.add_node((20., 10.));
|
||||||
|
// let t = m.add_node((30., 5.));
|
||||||
|
// m.add_edge(s, one, (10, 10));
|
||||||
|
// m.add_edge(s, two, (9, 10));
|
||||||
|
// m.add_edge(one, three, (4, 4));
|
||||||
|
// m.add_edge(one, four, (6, 8));
|
||||||
|
// m.add_edge(one, two, (0,2));
|
||||||
|
// m.add_edge(two, four, (9,9));
|
||||||
|
// m.add_edge(three, t, (9,10));
|
||||||
|
// m.add_edge(four, three, (5,6));
|
||||||
|
// m.add_edge(four, t, (10,10));
|
||||||
|
|
||||||
|
let mut m = g.clone();
|
||||||
|
m.update_edge(s, one, (10, 10));
|
||||||
|
m.update_edge(s, two, (9, 10));
|
||||||
|
m.update_edge(one, three, (4, 4));
|
||||||
|
m.update_edge(one, four, (6, 8));
|
||||||
|
m.update_edge(one, two, (0,2));
|
||||||
|
m.update_edge(two, four, (9,9));
|
||||||
|
m.update_edge(three, t, (9,10));
|
||||||
|
m.update_edge(four, three, (5,6));
|
||||||
|
m.update_edge(four, t, (10,10));
|
||||||
|
|
||||||
|
return (g, m, s, t);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graph_equal(one: StableGraph<(f32, f32), (u64, u64)>, other: StableGraph<(f32, f32), (u64, u64)>) -> bool {
|
||||||
|
// ensure all edges have the same weights
|
||||||
|
println!("Graph 1: {:?}", one);
|
||||||
|
println!("Graph 2: {:?}", other);
|
||||||
|
let edges = one.edge_indices().map(|e| e).collect::<Vec<_>>();
|
||||||
|
for edge in edges {
|
||||||
|
if one.edge_weight(edge).expect("edge index not found") != other.edge_weight(edge).expect("edge index not found") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ensure all nodes have the same weights
|
||||||
|
let nodes = one.node_indices().map(|n| n).collect::<Vec<_>>();
|
||||||
|
for node in nodes {
|
||||||
|
if one.node_weight(node).expect("node index not found") != other.node_weight(node).expect("node index not found") {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_ford_fulkerson() {
|
||||||
|
let (g, m, s, t) = generate_small_maxflow_example();
|
||||||
|
|
||||||
|
let problem = MaxflowProblem::from(g, s, t);
|
||||||
|
let mut algo = FordFulkerson::from_problem(&problem);
|
||||||
|
let solution = algo.run();
|
||||||
|
|
||||||
|
assert!(graph_equal(solution, m.residual()));
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user