diff --git a/src/algorithms/edmonds_karp.rs b/src/algorithms/edmonds_karp.rs index 23a10d3..59d8d8c 100644 --- a/src/algorithms/edmonds_karp.rs +++ b/src/algorithms/edmonds_karp.rs @@ -1,5 +1,61 @@ // Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS), +use std::cmp::min; +use std::collections::VecDeque; -//pub fn edmonds_karp(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> { -// -//} \ No newline at end of file +use petgraph::{stable_graph::{NodeIndex, StableGraph, EdgeReference}, visit::{EdgeRef, VisitMap, Visitable}, Direction}; + +fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 { + edge.weight().1 - edge.weight().0 +} + +// 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> { + let mut visited = graph.visit_map(); + let mut queue = VecDeque::from([(source, vec![source])]); + //queue.push_back((source, vec![source])); + + // work through the main queue + while let Some((node, path)) = queue.pop_front() { + let outgoing_edges = graph.edges_directed(node, Direction::Outgoing); + visited.visit(node); + + for edge in outgoing_edges { + let neighbor = edge.target(); + if !visited.is_visited(&neighbor) && available_capacity(edge) > 0 { + visited.visit(neighbor); + let mut new_path = path.clone(); + new_path.push(neighbor); + visited.visit(neighbor); + if neighbor == destination { + return Some(new_path); + } else { + queue.push_back((neighbor, new_path)); + } + } + } + } + + None +} + +pub fn edmonds_karp(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> { + // continue while there are augmenting paths + while let Some(path) = bfs(&graph, source, sink) { + // find all edges along the path + let edges: Vec = 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 +} \ No newline at end of file diff --git a/src/algorithms/ford_fulkerson.rs b/src/algorithms/ford_fulkerson.rs index a210bec..698ed59 100644 --- a/src/algorithms/ford_fulkerson.rs +++ b/src/algorithms/ford_fulkerson.rs @@ -4,13 +4,8 @@ // Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt). use std::cmp::min; use std::collections::VecDeque; -use egui_graphs::{Graph, DefaultNodeShape, DefaultEdgeShape}; -use petgraph::adj::EdgeIndex; -use petgraph::data::Build; -use petgraph::{Directed, Direction}; -use petgraph::stable_graph::EdgeReference; -use petgraph::visit::{Dfs, EdgeRef, NodeRef, VisitMap, Visitable}; -use petgraph::stable_graph::{StableGraph, NodeIndex}; + +use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction}; use crate::random_generator::MaxflowProblem; //mod random_generator; @@ -19,15 +14,15 @@ fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 { edge.weight().1 - edge.weight().0 } -// Returns the first augmenting path +// Runs a depth Depth First Search (DFS) and returns an augmenting path from source to destination if possible fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option> { let mut visited = graph.visit_map(); - let mut queue = VecDeque::new(); - queue.push_back((source, vec![source])); + let mut stack = VecDeque::from([(source, vec![source])]); - // main priority queue - while let Some((node, path)) = queue.pop_front() { + // 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 for edge in outgoing_edges { @@ -40,7 +35,7 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati if neighbor == destination { return Some(new_path); } else { - queue.push_back((neighbor, new_path)); + stack.push_front((neighbor, new_path)); } }; } diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index 36224ab..1bff3c2 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -1,4 +1,5 @@ mod edmonds_karp; mod ford_fulkerson; -pub use ford_fulkerson::ford_fulkerson; \ No newline at end of file +pub use ford_fulkerson::ford_fulkerson; +pub use edmonds_karp::edmonds_karp; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index e74576a..fc88ac8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,27 +1,32 @@ use eframe::{run_native, App, CreationContext, NativeOptions, Frame}; -use egui::{CentralPanel, SidePanel, Context}; +use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePanel}; use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle}; +use geo::algorithm; +use petgraph::{stable_graph::{NodeIndex, StableGraph}, Directed}; +use std::{fmt::Display, ptr::fn_addr_eq}; -use petgraph::Directed; use random_generator::MaxflowProblem; use layout::CustomEdgeShape; -use crate::algorithms::ford_fulkerson; +use crate::algorithms::{ford_fulkerson, edmonds_karp}; mod random_generator; mod algorithms; mod layout; +type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>; + pub struct MaxflowApp { g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>, p: MaxflowProblem, node_count: u64, max_capacity: u64, + algorithm: MaxflowFn, } impl MaxflowApp { fn new(_: &CreationContext<'_>) -> Self { let problem = MaxflowProblem::new(10, 10); - Self { g: problem.to_gui_graph(), p: problem, node_count: 10, max_capacity: 5 } + Self { g: problem.to_gui_graph(), p: problem, node_count: 10, max_capacity: 5, algorithm: ford_fulkerson } } } @@ -34,19 +39,45 @@ impl App for MaxflowApp { SidePanel::right("right_panel") .min_width(200.) .show(ctx, |ui| { - ui.label("node count"); - ui.add(egui::DragValue::new(&mut self.node_count).range(2..=1000)); - ui.label("maximum capacity"); - ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100)); - if ui.button("generate graph").clicked() { - self.p = random_generator::MaxflowProblem::new(self.node_count, self.max_capacity); - self.g = self.p.to_gui_graph(); - } - 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); - self.g = self.p.to_gui_graph(); - } + ScrollArea::vertical().show(ui, |ui| { + CollapsingHeader::new("Graph generation") + .default_open(true) + .show(ui, |ui| { + ui.label("node count"); + ui.add(egui::DragValue::new(&mut self.node_count).range(2..=1000)); + ui.label("maximum capacity"); + ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=100)); + // TODO: add generation strategy (random, pseudo-random) + if ui.button("generate graph").clicked() { + self.p = random_generator::MaxflowProblem::new(self.node_count, self.max_capacity); + self.g = self.p.to_gui_graph(); + } + }); + + CollapsingHeader::new("Max-flow algorithms") + .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", + _ => "unknown" + })) + .show_ui(ui, |ui| { + ui.selectable_value(&mut self.algorithm, ford_fulkerson, "Ford-Fulkerson"); + ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp"); + }); + 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); + self.g = self.p.to_gui_graph(); + } + // reset button + // step button (disable when finished) + }); + }); + + }); } }