diff --git a/src/algorithms/common.rs b/src/algorithms/common.rs new file mode 100644 index 0000000..c021045 --- /dev/null +++ b/src/algorithms/common.rs @@ -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 +} \ No newline at end of file diff --git a/src/algorithms/ford_fulkerson.rs b/src/algorithms/ford_fulkerson.rs index 966d8ac..6792063 100644 --- a/src/algorithms/ford_fulkerson.rs +++ b/src/algorithms/ford_fulkerson.rs @@ -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> { + 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 = 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 diff --git a/src/algorithms/goldberg_tarjan.rs b/src/algorithms/goldberg_tarjan.rs new file mode 100644 index 0000000..e69de29 diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index 827807c..7873321 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -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; \ No newline at end of file +pub use dinic::dinic; +pub use ford_fulkerson::FordFulkerson; +pub use common::MaxflowAlgorithm; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index 1eb56af..84c4b39 100644 --- a/src/main.rs +++ b/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, 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();