fix random generator capacity, add Dinic's algo, add options for filtering empty flows & displaying residual graph
This commit is contained in:
107
src/algorithms/dinic.rs
Normal file
107
src/algorithms/dinic.rs
Normal file
@@ -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::<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 == 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<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 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<petgraph::prelude::EdgeIndex> = 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
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
mod edmonds_karp;
|
||||
mod ford_fulkerson;
|
||||
mod dinic;
|
||||
|
||||
pub use ford_fulkerson::ford_fulkerson;
|
||||
pub use edmonds_karp::edmonds_karp;
|
||||
pub use edmonds_karp::edmonds_karp;
|
||||
pub use dinic::dinic;
|
||||
51
src/main.rs
51
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();
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
|
||||
@@ -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::<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| {
|
||||
|
||||
Reference in New Issue
Block a user