diff --git a/src/algorithms/dinic.rs b/src/algorithms/dinic.rs new file mode 100644 index 0000000..8594c15 --- /dev/null +++ b/src/algorithms/dinic.rs @@ -0,0 +1,107 @@ +// Dinic +use std::cmp::min; +use std::collections::VecDeque; + +use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction}; + +fn available_capacity(edge: (u64, u64)) -> u64 { + edge.1 - edge.0 +} + +// BFS +fn layer_graph(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> { + // filter graph for edges with no remaining capacity + let mut layer_graph = graph.filter_map(|_, n| { + Some(*n) + }, |_, &e| { + match available_capacity(e) { + 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::>(); + let outgoing_edges = layer_graph.edges_directed(node, Direction::Outgoing).map(|e| e.id()).collect::>(); + 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 == destination { + 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 +} + +// 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> { + 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 dinic(graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> { + // construct layer graph (from s to t) + let mut g_l = layer_graph(graph, source, sink); + + // construct a blocking flow in the layer graph using a DFS + // continue while there are augmenting paths + while let Some(path) = dfs(&g_l, source, sink) { + // find all edges along the path + let edges: Vec = path.windows(2).map(|w| g_l.find_edge(w[0], w[1]).expect("edge not found")).collect(); + // find bottleneck capacity along path + let increase_value = edges.iter().fold(u64::MAX, |m, x| { + let edge = g_l.edge_weight(*x).expect("edge index not found"); + min(m, edge.1 - edge.0) + }); + + // increase flow with bottleneck capacity along the augmenting path + for edge in edges { + let weight = g_l.edge_weight_mut(edge).expect("edge not found"); + (*weight).0 += increase_value; + } + } + + // add blocking flow to flow of original graph + g_l +} diff --git a/src/algorithms/mod.rs b/src/algorithms/mod.rs index 1bff3c2..827807c 100644 --- a/src/algorithms/mod.rs +++ b/src/algorithms/mod.rs @@ -1,5 +1,7 @@ mod edmonds_karp; mod ford_fulkerson; +mod dinic; pub use ford_fulkerson::ford_fulkerson; -pub use edmonds_karp::edmonds_karp; \ No newline at end of file +pub use edmonds_karp::edmonds_karp; +pub use dinic::dinic; \ No newline at end of file diff --git a/src/main.rs b/src/main.rs index fc88ac8..1eb56af 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,26 +7,51 @@ use std::{fmt::Display, ptr::fn_addr_eq}; use random_generator::MaxflowProblem; use layout::CustomEdgeShape; -use crate::algorithms::{ford_fulkerson, edmonds_karp}; +use crate::algorithms::{ford_fulkerson, edmonds_karp, dinic}; +use graph::{ResidualGraph, GuiGraph, FlowGraph}; mod random_generator; mod algorithms; mod layout; +mod graph; type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>; pub struct MaxflowApp { g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>, p: MaxflowProblem, + r: StableGraph<(f32, f32), (u64, u64)>, node_count: u64, max_capacity: u64, algorithm: MaxflowFn, + display_residual: bool, + display_active_flows: bool, } 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, algorithm: ford_fulkerson } + Self { + g: problem.g.to_gui_graph(problem.s, problem.t, false), + r: StableGraph::default(), + p: problem, node_count: 10, + max_capacity: 5, + algorithm: ford_fulkerson, + display_residual: false, + display_active_flows: false, + } + } + + fn update_graph(&mut self) { + let mut graph = match self.display_residual { + true => &self.p.g.residual(), + false => &self.p.g, + }; + if self.display_active_flows { + self.g = graph.filter_empty_flows().to_gui_graph(self.p.s, self.p.t, self.display_active_flows); + } else { + self.g = graph.to_gui_graph(self.p.s, self.p.t, self.display_active_flows); + } } } @@ -50,7 +75,7 @@ impl App for MaxflowApp { // 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(); + self.update_graph(); } }); @@ -61,20 +86,36 @@ impl App for MaxflowApp { .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" })) .show_ui(ui, |ui| { ui.selectable_value(&mut self.algorithm, ford_fulkerson, "Ford-Fulkerson"); ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp"); + ui.selectable_value(&mut self.algorithm, dinic, "Dinic"); }); if ui.button("run algorithm").clicked() { let max_flow_graph = ford_fulkerson(self.p.g.clone(), self.p.s, self.p.t); self.p = MaxflowProblem::from(max_flow_graph, self.p.s, self.p.t); - self.g = self.p.to_gui_graph(); + self.update_graph(); + } + if ui.button("reset").clicked() { + self.p.reset_flow(); + self.update_graph(); } - // reset button // step button (disable when finished) }); + + CollapsingHeader::new("Display options") + .default_open(true) + .show(ui, |ui| { + if ui.checkbox(&mut self.display_residual, "show residual graph").changed() { + self.update_graph(); + } + if ui.checkbox(&mut self.display_active_flows, "show active flows only").changed() { + self.update_graph(); + } + }) }); diff --git a/src/random_generator.rs b/src/random_generator.rs index 72ab192..b7dd487 100644 --- a/src/random_generator.rs +++ b/src/random_generator.rs @@ -1,3 +1,4 @@ +use petgraph::data::Build; use petgraph::stable_graph::{StableGraph, NodeIndex}; use petgraph::{Directed}; use egui_graphs::{default_edge_transform, default_node_transform, to_graph_custom, DefaultEdgeShape, DefaultNodeShape, Graph}; @@ -70,9 +71,10 @@ impl MaxflowProblem { // otherwise insert the edge and its residual edge inserted_edges.push((node1, node2)); inserted_edges.push((node2, node1)); - let capacity: u64 = rng.gen_range(1..=max_capacity); - graph.add_edge(node1, node2, (0, capacity)); - graph.add_edge(node2, node1, (0, capacity)); + let capacity1: u64 = rng.gen_range(1..=max_capacity); + let capacity2: u64 = rng.gen_range(1..=max_capacity); + graph.add_edge(node1, node2, (0, capacity1)); + graph.add_edge(node2, node1, (0, capacity2)); } } @@ -101,6 +103,14 @@ impl MaxflowProblem { } } + // set all flows back to zero + pub fn reset_flow(&mut self) { + for edge in self.g.edge_indices().collect::>() { + 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| {