WIP: refactor algorithms for step-by-step implementation
This commit is contained in:
23
src/algorithms/common.rs
Normal file
23
src/algorithms/common.rs
Normal file
@@ -0,0 +1,23 @@
|
||||
use petgraph::stable_graph::StableGraph;
|
||||
use crate::random_generator::MaxflowProblem;
|
||||
|
||||
pub trait MaxflowAlgorithm {
|
||||
// initialize algorithm from new problem
|
||||
fn from_problem(p: MaxflowProblem) -> Self;
|
||||
|
||||
// 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)>;
|
||||
|
||||
// returns the algorithm name
|
||||
fn name(&self) -> &'static str;
|
||||
}
|
||||
|
||||
pub fn available_capacity(edge: (u64, u64)) -> u64 {
|
||||
edge.1 - edge.0
|
||||
}
|
||||
@@ -1,11 +1,96 @@
|
||||
use std::cmp::min;
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
||||
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::ResidualGraph};
|
||||
use crate::MaxflowProblem;
|
||||
|
||||
pub struct FordFulkerson {
|
||||
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||
source: NodeIndex,
|
||||
sink: NodeIndex,
|
||||
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||
}
|
||||
|
||||
fn available_capacity(edge: (u64, u64)) -> u64 {
|
||||
edge.1 - edge.0
|
||||
impl FordFulkerson {
|
||||
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
||||
fn dfs(&mut self) -> Option<Vec<NodeIndex>> {
|
||||
let mut visited = self.residual_graph.visit_map();
|
||||
let mut stack = VecDeque::from([(self.source, vec![self.source])]);
|
||||
|
||||
// work through the main stack
|
||||
while let Some((node, path)) = stack.pop_front() {
|
||||
let outgoing_edges = self.residual_graph.edges_directed(node, Direction::Outgoing);
|
||||
visited.visit(node);
|
||||
|
||||
// iterate over all outgoing edges & add neighboring nodes to the stack
|
||||
for edge in outgoing_edges {
|
||||
let neighbor = edge.target();
|
||||
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
|
||||
visited.visit(neighbor);
|
||||
let mut new_path = path.clone();
|
||||
new_path.push(neighbor);
|
||||
// TODO: is this right?
|
||||
if neighbor == self.sink {
|
||||
return Some(new_path);
|
||||
} else {
|
||||
stack.push_front((neighbor, new_path));
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
// continue while there are augmenting paths
|
||||
if let Some(path) = self.dfs() {
|
||||
// find all edges along the path
|
||||
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
|
||||
// find bottleneck capacity along path
|
||||
let bottlenack_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 {
|
||||
// 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 += bottlenack_capacity;
|
||||
// increase capacity of the residual edge of the with the calculated bottleneck value
|
||||
}
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
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"
|
||||
}
|
||||
}
|
||||
|
||||
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
||||
|
||||
0
src/algorithms/goldberg_tarjan.rs
Normal file
0
src/algorithms/goldberg_tarjan.rs
Normal file
@@ -1,7 +1,10 @@
|
||||
mod edmonds_karp;
|
||||
mod ford_fulkerson;
|
||||
mod dinic;
|
||||
mod common;
|
||||
|
||||
pub use ford_fulkerson::ford_fulkerson;
|
||||
pub use edmonds_karp::edmonds_karp;
|
||||
pub use dinic::dinic;
|
||||
pub use dinic::dinic;
|
||||
pub use ford_fulkerson::FordFulkerson;
|
||||
pub use common::MaxflowAlgorithm;
|
||||
42
src/main.rs
42
src/main.rs
@@ -3,11 +3,10 @@ use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePa
|
||||
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 random_generator::MaxflowProblem;
|
||||
use layout::CustomEdgeShape;
|
||||
use crate::algorithms::{ford_fulkerson, edmonds_karp, dinic};
|
||||
use crate::algorithms::{MaxflowAlgorithm, FordFulkerson};
|
||||
use graph::{ResidualGraph, GuiGraph, FlowGraph};
|
||||
|
||||
mod random_generator;
|
||||
@@ -17,13 +16,20 @@ mod graph;
|
||||
|
||||
type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||
|
||||
enum MaxflowAlgos {
|
||||
FordFulkerson,
|
||||
EdmondsKarp,
|
||||
Dinic,
|
||||
GoldbergTarjan,
|
||||
}
|
||||
|
||||
pub struct MaxflowApp {
|
||||
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
||||
p: MaxflowProblem,
|
||||
r: StableGraph<(f32, f32), (u64, u64)>,
|
||||
node_count: u64,
|
||||
max_capacity: u64,
|
||||
algorithm: MaxflowFn,
|
||||
algorithm: Box<dyn MaxflowAlgorithm>,
|
||||
display_residual: bool,
|
||||
display_active_flows: bool,
|
||||
}
|
||||
@@ -36,14 +42,14 @@ impl MaxflowApp {
|
||||
r: StableGraph::default(),
|
||||
p: problem, node_count: 10,
|
||||
max_capacity: 5,
|
||||
algorithm: ford_fulkerson,
|
||||
algorithm: FordFulkerson,
|
||||
display_residual: false,
|
||||
display_active_flows: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn update_graph(&mut self) {
|
||||
let mut graph = match self.display_residual {
|
||||
let graph = match self.display_residual {
|
||||
true => &self.p.g.residual(),
|
||||
false => &self.p.g,
|
||||
};
|
||||
@@ -83,21 +89,23 @@ 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.name())
|
||||
.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");
|
||||
ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(self.p), "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);
|
||||
if ui.button("step").clicked() {
|
||||
// check if algo is initialized
|
||||
// if not, initialize
|
||||
// run step function
|
||||
}
|
||||
if ui.button("run").clicked() {
|
||||
self.algorithm.run();
|
||||
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() {
|
||||
self.p.reset_flow();
|
||||
|
||||
Reference in New Issue
Block a user