Compare commits

..

5 Commits

8 changed files with 809 additions and 56 deletions

View File

@@ -1,13 +0,0 @@
// Ford-Fulkerson mit DFS zur Berechnung des flusserhöhenden Pfades,
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
// Dinic
// Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt).
use random_generator::MaxflowProblem;
mod random_generator;
impl FordFulkerson {
fn solve(problem: MaxflowProblem) -> ???solution??? {
}
}

107
src/algorithms/dinic.rs Normal file
View 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
}

View File

@@ -0,0 +1,61 @@
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
use std::cmp::min;
use std::collections::VecDeque;
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

@@ -0,0 +1,118 @@
// Ford-Fulkerson mit DFS zur Berechnung des flusserhöhenden Pfades,
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
// Dinic
// Goldberg-Tarjan (auch Preflow-Push oder Push-Relabel genannt).
use std::cmp::min;
use std::collections::VecDeque;
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
//use crate::random_generator::MaxflowProblem;
//mod random_generator;
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
edge.weight().1 - edge.weight().0
}
// 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 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
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);
// TODO: is this right?
if neighbor == destination {
return Some(new_path);
} else {
stack.push_front((neighbor, new_path));
}
};
}
}
None
}
pub fn ford_fulkerson(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
//let augmenting_path = dfs(graph, source, sink);
// continue while there are augmenting paths
while let Some(path) = dfs(&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
}
//pub struct FordFulkerson {
// problem: MaxflowProblem,
// flows: Graph<(), u64, Directed, u32, DefaultNodeShape, DefaultEdgeShape>,
// alertnatively, use a map from EdgeIndex to flow value
//dfs: Dfs<N, VM>,
//}
/* impl FordFulkerson {
fn new(problem: MaxflowProblem) -> S/elf {
Self { problem: problem, flows: Graph::new(StableGraph::default()) }
}
fn graph(&mut self) -> &Graph<bool, (u64, u64)> {
&self.problem.g
}
fn flows(&mut self) -> &Graph<(), u64> {
&self.flows
}
fn dfs(&mut self, ni: NodeIndex) -> Vec<Vec<EdgeIndex>>{
// set own node to visited
self.problem.g.node_mut(ni).expect("node index not found").set_visited(true);
//self.graph().node_mut(ni).expect("node index not found").set_visited(true);
let augmenting_paths: Vec<Vec<EdgeIndex>> = Vec::new();
let neighboring_edges = self.graph().edges_directed(ni, Direction::Outgoing);
for edge in neighboring_edges {
//let neighbor = self.problem.g.node_mut(edge.target()).expect("node index not found");
//if neighbor.visited() == false {
// println!("{:?}", edge.weight());
//}
}
// iterate over all unvisited! neighbors. for each:
// - check if there is capacity left
// - if neighbor = target, return path
// - else, do dfs starting from neighbor
// - if neighbor returns augmenting path add it to own list & prepend own edge
// return list of augmenting paths
augmenting_paths
}
fn step(&self) {
//self.dfs(self.problem.s);
}
} */

7
src/algorithms/mod.rs Normal file
View File

@@ -0,0 +1,7 @@
mod edmonds_karp;
mod ford_fulkerson;
mod dinic;
pub use ford_fulkerson::ford_fulkerson;
pub use edmonds_karp::edmonds_karp;
pub use dinic::dinic;

357
src/layout.rs Normal file
View File

@@ -0,0 +1,357 @@
use std::f32::consts::PI;
use egui::{
epaint::{CubicBezierShape, QuadraticBezierShape, TextShape}, text::LayoutJob, Color32, FontFamily, FontId, Pos2, Shape, Stroke, TextFormat, Vec2
};
use petgraph::{matrix_graph::Nullable, stable_graph::IndexType, EdgeType};
use egui_graphs::{
DefaultEdgeShape, DisplayEdge, EdgeProps,
DefaultNodeShape, DisplayNode, NodeProps,
Node, DrawContext
};
#[derive(Clone)]
pub struct CustomEdgeShape {
pub order: usize,
pub selected: bool,
pub width: f32,
pub tip_size: f32,
pub tip_angle: f32,
pub curve_size: f32,
pub loop_size: f32,
pub label_text: String,
}
impl<E: Clone> From<EdgeProps<E>> for CustomEdgeShape {
fn from(props: EdgeProps<E>) -> Self {
Self {
order: props.order,
selected: props.selected,
label_text: props.label,
width: 1.,
tip_size: 10.,
tip_angle: std::f32::consts::TAU / 30.,
curve_size: 15.,
loop_size: 3.,
}
}
}
impl<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>
DisplayEdge<N, E, Ty, Ix, D> for CustomEdgeShape
{
// helper functions
// copied from: https://github.com/SeverinAlexB/pknames/blob/d042b6843ee503d92ae36742bab81b14574d9ad3/cli/src/visualization/edge_vis.rs
fn is_inside(
&self,
start: &Node<N, E, Ty, Ix, D>,
end: &Node<N, E, Ty, Ix, D>,
pos: egui::Pos2,
) -> bool {
if start.id() == end.id() {
return is_inside_loop(start, self, pos);
}
let pos_start = start.location();
let pos_end = end.location();
if self.order == 0 {
return is_inside_line(pos_start, pos_end, pos, self);
}
is_inside_curve(start, end, self, pos)
}
fn shapes(
&mut self,
start: &Node<N, E, Ty, Ix, D>,
end: &Node<N, E, Ty, Ix, D>,
ctx: &DrawContext,
) -> Vec<egui::Shape> {
// fixed properties
let edge_color = match self.selected {
true => Color32::LIGHT_BLUE,
false => Color32::LIGHT_GRAY
};
let label_color = Color32::DARK_GRAY;
//let label_visible = true;
//let style = ctx.ctx.style().visuals.widgets.inactive;
// calculate coordinates
let dir = (end.location() - start.location()).normalized();
let start_connector_point = start.display().closest_boundary_point(dir);
let end_connector_point = end.display().closest_boundary_point(-dir);
let tip_end = end_connector_point;
let edge_start = start_connector_point;
let edge_end = end_connector_point;
// draw curved edge
let stroke_edge = Stroke::new(self.width * ctx.meta.zoom, edge_color);
let dir_perpendicular = Vec2::new(-dir.y, dir.x);
let center_point = (edge_start + edge_end.to_vec2()).to_vec2() / 2.;
let control_point = (center_point + dir_perpendicular * self.curve_size as f32).to_pos2();
let tip_dir = (control_point-tip_end).normalized();
//println!("edge_start: {:?} edge_end {:?} center_point {:?} dir_p {:?} control_point {:?}", edge_start, edge_end, center_point, dir_perpendicular, control_point);
let arrow_tip_dir_1 = rotate_vector(tip_dir, self.tip_angle) * self.tip_size;
let arrow_tip_dir_2 = rotate_vector(tip_dir, -self.tip_angle) * self.tip_size;
let tip_start_1 = tip_end + arrow_tip_dir_1;
let tip_start_2 = tip_end + arrow_tip_dir_2;
let edge_end_curved = point_between(tip_start_1, tip_start_2);
let line_curved = QuadraticBezierShape::from_points_stroke(
[
ctx.meta.canvas_to_screen_pos(edge_start),
ctx.meta.canvas_to_screen_pos(control_point),
ctx.meta.canvas_to_screen_pos(edge_end_curved),
],
false,
Color32::TRANSPARENT,
stroke_edge,
);
// draw tip/arrow
let stroke_tip = Stroke::new(0., edge_color);
let line_curved_tip = Shape::convex_polygon(
vec![
ctx.meta.canvas_to_screen_pos(tip_end),
ctx.meta.canvas_to_screen_pos(tip_start_1),
ctx.meta.canvas_to_screen_pos(tip_start_2),
],
edge_color,
stroke_tip,
);
// draw label
let size = (::egui_graphs::node_size(start, dir) + egui_graphs::node_size(end, dir)) / 2.;
let galley = ctx.ctx.fonts(|f| {
f.layout_no_wrap(
self.label_text.clone(),
FontId::new(ctx.meta.canvas_to_screen_size(size), FontFamily::Monospace),
label_color,
)
});
let flattened_curve = line_curved.flatten(None);
let median = *flattened_curve.get(flattened_curve.len() / 2).unwrap();
let label_width = galley.rect.width();
let label_height = galley.rect.height();
let pos = Pos2::new(median.x - label_width / 2., median.y - label_height);
let label_shape = TextShape::new(pos, galley, label_color);
vec![line_curved.into(), line_curved_tip, label_shape.into()]
}
fn update(&mut self, state: &egui_graphs::EdgeProps<E>) {
self.order = state.order;
self.selected = state.selected;
self.label_text = state.label.to_string();
}
}
// Everything below is copied from:
// https://github.com/SeverinAlexB/pknames/blob/d042b6843ee503d92ae36742bab81b14574d9ad3/cli/src/visualization/edge_vis.rs
fn shape_looped(
node_size: f32,
node_center: Pos2,
stroke: Stroke,
e: &CustomEdgeShape,
) -> CubicBezierShape {
let center_horizon_angle = PI / 4.;
let y_intersect = node_center.y - node_size * center_horizon_angle.sin();
let edge_start = Pos2::new(
node_center.x - node_size * center_horizon_angle.cos(),
y_intersect,
);
let edge_end = Pos2::new(
node_center.x + node_size * center_horizon_angle.cos(),
y_intersect,
);
let loop_size = node_size * (e.loop_size + e.order as f32);
let control_point1 = Pos2::new(node_center.x + loop_size, node_center.y - loop_size);
let control_point2 = Pos2::new(node_center.x - loop_size, node_center.y - loop_size);
CubicBezierShape::from_points_stroke(
[edge_end, control_point1, control_point2, edge_start],
false,
Color32::default(),
stroke,
)
}
fn shape_curved(
pos_start: Pos2,
pos_end: Pos2,
size_start: f32,
size_end: f32,
stroke: Stroke,
e: &CustomEdgeShape,
) -> QuadraticBezierShape {
let vec = pos_end - pos_start;
let dist: f32 = vec.length();
let dir = vec / dist;
let start_node_radius_vec = Vec2::new(size_start, size_start) * dir;
let end_node_radius_vec = Vec2::new(size_end, size_end) * dir;
let tip_end = pos_start + vec - end_node_radius_vec;
let edge_start = pos_start + start_node_radius_vec;
let edge_end = pos_end + end_node_radius_vec;
let dir_perpendicular = Vec2::new(-dir.y, dir.x);
let center_point = (edge_start + tip_end.to_vec2()).to_vec2() / 2.0;
let control_point =
(center_point + dir_perpendicular * e.curve_size * e.order as f32).to_pos2();
QuadraticBezierShape::from_points_stroke(
[edge_start, control_point, edge_end],
false,
stroke.color,
stroke,
)
}
fn is_inside_loop<E: Clone, N: Clone, Ix: IndexType, Ty: EdgeType, D: DisplayNode<N, E, Ty, Ix>>(
node: &Node<N, E, Ty, Ix, D>,
e: &CustomEdgeShape,
pos: Pos2,
) -> bool {
let node_size = node_size(node);
let shape = shape_looped(node_size, node.location(), Stroke::default(), e);
is_point_on_cubic_bezier_curve(pos, shape, e.width)
}
fn is_inside_line(pos_start: Pos2, pos_end: Pos2, pos: Pos2, e: &CustomEdgeShape) -> bool {
let distance = distance_segment_to_point(pos_start, pos_end, pos);
distance <= e.width
}
fn is_inside_curve<
N: Clone,
E: Clone,
Ty: EdgeType,
Ix: IndexType,
D: DisplayNode<N, E, Ty, Ix>,
>(
node_start: &Node<N, E, Ty, Ix, D>,
node_end: &Node<N, E, Ty, Ix, D>,
e: &CustomEdgeShape,
pos: Pos2,
) -> bool {
let pos_start = node_start.location();
let pos_end = node_end.location();
let size_start = node_size(node_start);
let size_end = node_size(node_end);
let shape = shape_curved(
pos_start,
pos_end,
size_start,
size_end,
Stroke::default(),
e,
);
is_point_on_quadratic_bezier_curve(pos, shape, e.width)
}
fn node_size<N: Clone, E: Clone, Ty: EdgeType, Ix: IndexType, D: DisplayNode<N, E, Ty, Ix>>(
node: &Node<N, E, Ty, Ix, D>,
) -> f32 {
let left_dir = Vec2::new(-1., 0.);
let connector_left = node.display().closest_boundary_point(left_dir);
let connector_right = node.display().closest_boundary_point(-left_dir);
(connector_right.x - connector_left.x) / 2.
}
/// Returns the distance from line segment `a``b` to point `c`.
/// Adapted from https://stackoverflow.com/questions/1073336/circle-line-segment-collision-detection-algorithm
fn distance_segment_to_point(a: Pos2, b: Pos2, point: Pos2) -> f32 {
let ac = point - a;
let ab = b - a;
let d = a + proj(ac, ab);
let ad = d - a;
let k = if ab.x.abs() > ab.y.abs() {
ad.x / ab.x
} else {
ad.y / ab.y
};
if k <= 0.0 {
return hypot2(point.to_vec2(), a.to_vec2()).sqrt();
} else if k >= 1.0 {
return hypot2(point.to_vec2(), b.to_vec2()).sqrt();
}
hypot2(point.to_vec2(), d.to_vec2()).sqrt()
}
/// Calculates the square of the Euclidean distance between vectors `a` and `b`.
fn hypot2(a: Vec2, b: Vec2) -> f32 {
(a - b).dot(a - b)
}
/// Calculates the projection of vector `a` onto vector `b`.
fn proj(a: Vec2, b: Vec2) -> Vec2 {
let k = a.dot(b) / b.dot(b);
Vec2::new(k * b.x, k * b.y)
}
fn is_point_on_cubic_bezier_curve(point: Pos2, curve: CubicBezierShape, width: f32) -> bool {
is_point_on_bezier_curve(point, curve.flatten(Option::new(10.0)), width)
}
fn is_point_on_quadratic_bezier_curve(
point: Pos2,
curve: QuadraticBezierShape,
width: f32,
) -> bool {
is_point_on_bezier_curve(point, curve.flatten(Option::new(0.3)), width)
}
fn is_point_on_bezier_curve(point: Pos2, curve_points: Vec<Pos2>, width: f32) -> bool {
let mut previous_point = None;
for p in curve_points {
if let Some(pp) = previous_point {
let distance = distance_segment_to_point(p, pp, point);
if distance < width {
return true;
}
}
previous_point = Some(p);
}
false
}
/// rotates vector by angle
fn rotate_vector(vec: Vec2, angle: f32) -> Vec2 {
let cos = angle.cos();
let sin = angle.sin();
Vec2::new(cos * vec.x - sin * vec.y, sin * vec.x + cos * vec.y)
}
/// finds point exactly in the middle between 2 points
fn point_between(p1: Pos2, p2: Pos2) -> Pos2 {
let base = p1 - p2;
let base_len = base.length();
let dir = base / base_len;
p1 - (base_len / 2.) * dir
}

View File

@@ -1,46 +1,124 @@
use eframe::{run_native, App, CreationContext, NativeOptions, Frame};
use egui::{CentralPanel, SidePanel, Context};
use egui_graphs::{GraphView, Graph, SettingsStyle, LayoutRandom, LayoutStateRandom};
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 random_generator::MaxflowProblem;
use algorithms::{ford_fulk
//use petgraph::stable_graph::StableGraph;
use layout::CustomEdgeShape;
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<(), u64>,
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.g.clone(), p: problem, node_count: 10, max_capacity: 5 }
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);
}
}
}
impl App for MaxflowApp {
fn update(&mut self, ctx: &Context, _: &mut Frame) {
ctx.set_theme(egui::Theme::Light);
CentralPanel::default().show(ctx, |ui| {
ui.add(&mut GraphView::<_, _, _, _, _, _, LayoutStateRandom, LayoutRandom>::new(&mut self.g).with_styles(&SettingsStyle::default().with_labels_always(true)));
ui.add(&mut GraphView::<_, _, _, _, _, CustomEdgeShape, LayoutStateRandom, LayoutRandom>::new(&mut self.g).with_styles(&SettingsStyle::default().with_labels_always(true)));
});
SidePanel::right("right_panel")
.min_width(200.)
.show(ctx, |ui| {
ui.label("node count");
ui.add(egui::DragValue::new(&mut self.node_count).range(1..=1000));
ui.label("maximum capacity");
ui.add(egui::DragValue::new(&mut self.max_capacity).range(1..=10));
if ui.button("generate graph").clicked() {
let problem = random_generator::MaxflowProblem::new(self.node_count, self.max_capacity);
self.g = problem.g;
}
if ui.button("run algorithm").clicked() {
}
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.update_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",
_ 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.update_graph();
}
if ui.button("reset").clicked() {
self.p.reset_flow();
self.update_graph();
}
// 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();
}
})
});
});
}
}

View File

@@ -1,6 +1,7 @@
use petgraph::data::Build;
use petgraph::stable_graph::{StableGraph, NodeIndex};
use petgraph::{Directed};
use egui_graphs::{Graph};
use egui_graphs::{default_edge_transform, default_node_transform, to_graph_custom, DefaultEdgeShape, DefaultNodeShape, Graph};
use egui::Pos2;
use rand::thread_rng;
use rand::Rng;
@@ -9,15 +10,18 @@ use itertools::Itertools;
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
use egui::Color32;
use crate::layout::CustomEdgeShape;
pub struct MaxflowProblem {
pub g: Graph<(), u64, Directed, u32, egui_graphs::DefaultNodeShape, egui_graphs::DefaultEdgeShape>,
pub g: StableGraph<(f32, f32), (u64, u64)>,
pub s: NodeIndex,
pub t: NodeIndex,
}
impl MaxflowProblem {
pub fn new(num_nodes: u64, max_capacity: u64) -> Self {
let mut graph: Graph<_, u64> = Graph::new(StableGraph::default());
// Node Type: (x,y) Edge Type: (current_flow, capacity)
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
let mut nodes: Vec<NodeIndex> = Vec::new();
let mut rng = thread_rng();
@@ -25,30 +29,27 @@ impl MaxflowProblem {
for _i in 0..num_nodes {
let x: f32 = rng.gen_range(0.0..=400.0);
let y: f32 = rng.gen_range(0.0..=300.0);
let pos = Pos2::new(x,y);
let n = graph.add_node_with_label_and_location((), String::new(), pos);
let n = graph.add_node((x,y));
nodes.push(n);
}
// select source (s) and sink (t)
let s = *nodes.choose(&mut rng).expect("no nodes found");
let t = *nodes.choose(&mut rng).expect("no nodes found");
graph.node_mut(s).expect("node index not found").set_label(String::from("s"));
graph.node_mut(t).expect("node index not found").set_label(String::from("t"));
graph.node_mut(s).expect("node index not found").set_color(Color32::RED);
graph.node_mut(t).expect("node index not found").set_color(Color32::GREEN);
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
// iterate over all possible edges
let mut possible_edges: Vec<(NodeIndex, NodeIndex, f32)> = Vec::new();
for pair in nodes.clone().into_iter().combinations(2) {
// calculate distance & append tuple (node_a, node_b, distance) to possible_edges
let distance = Self::distance(graph.node(pair[0]).expect("node index not found").location(), graph.node(pair[1]).expect("node index not found").location());
let distance = Self::distance(
*graph.node_weight(pair[0]).expect("node index not found"),
*graph.node_weight(pair[1]).expect("node index not found")
);
//let distance = Self::distance(graph.node(pair[0]).expect("node index not found").location(), graph.node(pair[1]).expect("node index not found").location());
possible_edges.push((pair[0], pair[1], distance));
}
// sort mapping by distance
possible_edges.sort_by(|a, b| a.2.partial_cmp(&b.2).unwrap());
println!("possible_edges len: {}", possible_edges.len());
// insert the edge if possible
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
@@ -57,10 +58,10 @@ impl MaxflowProblem {
let mut intersects = false;
for (node3, node4) in &inserted_edges {
if Self::intersects(
graph.node(node1).expect("node index not found").location(),
graph.node(node2).expect("node index not found").location(),
graph.node(*node3).expect("node index not found").location(),
graph.node(*node4).expect("node index not found").location(),
*graph.node_weight(node1).expect("node index not found"),
*graph.node_weight(node2).expect("node index not found"),
*graph.node_weight(*node3).expect("node index not found"),
*graph.node_weight(*node4).expect("node index not found"),
) {
intersects = true;
break;
@@ -70,30 +71,67 @@ 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_with_label(node1, node2, capacity, capacity.to_string());
graph.add_edge_with_label(node2, node1, capacity, capacity.to_string());
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));
}
}
Self { g: graph, s: s, t: t }
}
pub fn from(g: StableGraph<(f32, f32), (u64, u64)>, s: NodeIndex, t: NodeIndex) -> Self{
Self {g: g, s: s, t: t}
}
// returns the eudclidean distance between two points
fn distance(a: Pos2, b: Pos2) -> f32 {
let delta_x = a.x - b.x;
let delta_y = a.y - b.y;
fn distance(a: (f32, f32), b: (f32, f32)) -> f32 {
let delta_x = a.0 - b.0;
let delta_y = a.1 - b.1;
return f32::sqrt(delta_x.powf(2.) + delta_y.powf(2.));
}
// checks if the lines between a-b and c-d intersect
fn intersects(a: Pos2, b: Pos2, c: Pos2, d: Pos2) -> bool {
let line1 = Line::new(coord! {x: a.x, y: a.y }, coord! {x: b.x, y: b.y } );
let line2 = Line::new(coord! {x: c.x, y: c.y }, coord! {x: d.x, y: d.y } );
fn intersects(a: (f32, f32), b: (f32, f32), c: (f32, f32), d: (f32, f32)) -> bool {
let line1 = Line::new(coord! {x: a.0, y: a.1 }, coord! {x: b.0, y: b.1 } );
let line2 = Line::new(coord! {x: c.0, y: c.1 }, coord! {x: d.0, y: d.1 } );
match line_intersection(line1, line2) {
// only return true for 'proper' intersections which are not at the end of a line
Some(LineIntersection::SinglePoint { is_proper: true, .. }) => true,
_ => false
}
}
// 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| {
let (x, y) = *n.payload();
default_node_transform(n);
n.set_location(Pos2::new(x, y));
n.set_label(String::from(""));
},
|e| {
let (flow, capacity): (u64, u64) = *e.payload();
default_edge_transform(e);
e.set_label(format!("{flow}:{capacity}"));
if flow > 0 {
e.set_selected(true);
}
}
);
graph.node_mut(self.s).expect("node index not found").set_label(String::from("s"));
graph.node_mut(self.t).expect("node index not found").set_label(String::from("t"));
graph.node_mut(self.s).expect("node index not found").set_color(Color32::RED);
graph.node_mut(self.t).expect("node index not found").set_color(Color32::GREEN);
graph
}
}