Implement Edmonds-Karp

This commit is contained in:
2025-03-20 22:13:05 +01:00
parent fdcb646bd2
commit 111759fbbb
4 changed files with 117 additions and 34 deletions

View File

@@ -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)> {
//
//}
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<Vec<NodeIndex>> {
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<petgraph::prelude::EdgeIndex> = 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
}

View File

@@ -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<Vec<NodeIndex>> {
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));
}
};
}

View File

@@ -1,4 +1,5 @@
mod edmonds_karp;
mod ford_fulkerson;
pub use ford_fulkerson::ford_fulkerson;
pub use ford_fulkerson::ford_fulkerson;
pub use edmonds_karp::edmonds_karp;

View File

@@ -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)
});
});
});
}
}