2025-08-03: checkpoint

This commit is contained in:
2025-08-03 18:34:13 +02:00
parent 8d28ef44be
commit ba3cd9591e
16 changed files with 1219 additions and 354 deletions

View File

@@ -1,43 +1,19 @@
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{VisitMap, Visitable}, Directed};
use egui::Pos2;
use petgraph::{algo::has_path_connecting, stable_graph::{EdgeIndex, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Directed, Direction};
use egui::{Pos2};
use std::collections::{HashMap, HashSet, VecDeque};
use std::cmp::min;
use crate::layout::CustomEdgeShape;
use egui::Color32;
pub trait ResidualGraph {
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)>;
}
impl ResidualGraph for StableGraph<(f32, f32), (u64, u64)> {
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)> {
let mut res_graph = self.clone();
for edge in res_graph.edge_indices().map(|e| e).collect::<Vec<_>>() {
let (source, target) = res_graph.edge_endpoints(edge).expect("edge not connected");
if let Some(reverse_edge) = res_graph.find_edge(target, source) {
let (flow, capacity) = *res_graph.edge_weight(edge).expect("forward edge not found");
let (reverse_flow, reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
// update the forward edge
// TODO: this seems to overflow in certain cases
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
// update the reverse edge
//res_graph.update_edge(target, source, (reverse_flow, reverse_capacity + flow));
} else {
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
res_graph.add_edge(target, source, (0, 0));
}
}
res_graph
}
}
// All methods which convert the graph into a GUI representations
pub trait GuiGraph {
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex, active_flows_only: bool) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
fn to_gui_graph_distance(&self, s: NodeIndex, t: NodeIndex, distance_labels: HashMap<NodeIndex, u64>) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>;
}
impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex, active_flows_only: bool) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
fn to_gui_graph(&self, s: NodeIndex, t: NodeIndex) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
let mut graph = to_graph_custom(&self,
|n| {
let (x, y) = *n.payload();
@@ -56,14 +32,63 @@ impl GuiGraph for StableGraph<(f32, f32), (u64, u64)> {
);
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);
graph.node_mut(s).expect("node index not found").set_color(Color32::BLUE);
graph.node_mut(t).expect("node index not found").set_color(Color32::ORANGE);
graph
}
fn to_gui_graph_distance(&self, s: NodeIndex, t: NodeIndex, distance_labels: HashMap<NodeIndex, u64>) -> Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape> {
let mut graph = to_graph_custom(&self,
|n| {
let (x, y) = *n.payload();
default_node_transform(n);
n.set_location(Pos2::new(x, y));
if let Some(d) = distance_labels.get(&n.id()) {
if n.id() == s {
n.set_label(format!("s ({})", d));
} else if n.id() == t {
n.set_label(format!("t ({})", d));
} else {
n.set_label(format!("{}", d));
}
} else {
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(s).expect("node index not found").set_color(Color32::BLUE);
graph.node_mut(t).expect("node index not found").set_color(Color32::ORANGE);
graph
}
}
// all methods specific to MaxFlow problems
pub trait FlowGraph {
fn filter_empty_flows(&self) -> StableGraph<(f32, f32), (u64, u64)>;
fn reset_flow(&mut self);
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool;
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64;
fn available_capacity(&self, edge: EdgeIndex) -> u64;
fn layer_graph(&self, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
fn add_flow(&mut self, edge: EdgeIndex, flow: u64);
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)>;
}
impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
@@ -78,4 +103,152 @@ impl FlowGraph for StableGraph<(f32, f32), (u64, u64)> {
}
})
}
}
fn layer_graph(&self, source: NodeIndex, sink: NodeIndex) -> StableGraph<(f32, f32), (u64, u64)> {
// filter graph for edges with no remaining capacity
let mut layer_graph = self.filter_map(|_, n| {
Some(*n)
}, |_, &e| {
match e.1 - e.0 {
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 == sink {
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
}
// set all flows back to zero
fn reset_flow(&mut self) {
for edge in self.edge_indices().collect::<Vec<_>>() {
let weight = self.edge_weight_mut(edge).expect("edge not found");
(*weight).0 = 0;
}
}
// check if flow is valid:
// - for every edge, the flow doesn't exceed the capacity
// - for every node, the amount of incoming flows is equal to the amount of outgoing flows
fn valid_flow(&self, source: NodeIndex, sink: NodeIndex) -> bool {
// capacity isn't exceeded
for edge in self.edge_indices().map(|e| e).collect::<Vec<_>>() {
if self.edge_weight(edge).expect("edge index not found").0 > self.edge_weight(edge).expect("edge index not found").1 {
return false;
}
}
// sum incoming flows = sum outgoing flows
for node in self.node_indices().map(|n| n).collect::<Vec<_>>() {
let outgoing_flows: u64 = self
.edges_directed(node, Direction::Outgoing)
.map(|e| e.weight().0)
.sum();
let incoming_flows: u64 = self
.edges_directed(node, Direction::Incoming)
.map(|e| e.weight().0)
.sum();
if incoming_flows != outgoing_flows {
if node != source && node != sink {
// println!("invalid flow at {:?} incoming flows: {} outgoing flows: {}", node, incoming_flows, outgoing_flows);
return false;
}
}
}
true
}
// checks if there exists a saturated cut in the graph (a bip)
fn saturated_cut(&self, source: NodeIndex, sink: NodeIndex) -> bool {
let mut s = HashSet::new();
let mut t = HashSet::new();
for node in self.node_indices().into_iter().collect::<Vec<_>>() {
if has_path_connecting(self, source, node, None) {
s.insert(node);
} else {
t.insert(node);
}
}
return true;
}
// TODO: assert that incoming flows at sink are equal to outgoing flows at source (otherwise return 0)
fn total_flow(&self, source: NodeIndex, sink: NodeIndex) -> u64 {
let outgoing_flows_source: u64 = self.edges_directed(source, Direction::Outgoing).map(|e| e.weight().0).sum();
let incoming_flows_source: u64 = self.edges_directed(source, Direction::Incoming).map(|e| e.weight().0).sum();
let outgoing_flows_sink: u64 = self.edges_directed(sink, Direction::Outgoing).map(|e| e.weight().0).sum();
let incoming_flows_sink: u64 = self.edges_directed(sink, Direction::Incoming).map(|e| e.weight().0).sum();
let total_flow_source = outgoing_flows_source - incoming_flows_source;
let total_flow_sink = incoming_flows_sink - outgoing_flows_sink;
assert!(total_flow_sink == total_flow_source);
return total_flow_source;
}
fn residual(&self) -> StableGraph<(f32, f32), (u64, u64)> {
let mut res_graph = self.clone();
for edge in res_graph.edge_indices().map(|e| e).collect::<Vec<_>>() {
let (source, target) = res_graph.edge_endpoints(edge).expect("edge not connected");
if let Some(reverse_edge) = res_graph.find_edge(target, source) {
let (flow, capacity) = *res_graph.edge_weight(edge).expect("forward edge not found");
let (reverse_flow, _reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
// update the edge
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
} else {
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
res_graph.add_edge(target, source, (0, 0));
}
}
res_graph
}
fn available_capacity(&self, edge: EdgeIndex) -> u64 {
self.edge_weight(edge).expect("edge not found").1 - self.edge_weight(edge).expect("edge not found").0
}
fn add_flow(&mut self, edge: EdgeIndex, flow: u64) {
// search for reverse edge and subtract existing flow if possible
let (source, target) = self.edge_endpoints(edge).expect("edge not connected");
let residual_edge = self.find_edge(target, source).expect("residual edge not found");
let residual_weight: &mut (u64, u64) = self.edge_weight_mut(residual_edge).expect("residual edge not found");
let forward_flow = i128::from(flow) - i128::from((*residual_weight).0);
// subtract minimum of additional_flow and residual_weight
if residual_weight.0 > 0 {
(*residual_weight).0 -= min(residual_weight.0, flow);
}
assert!((*residual_weight).0 <= (*residual_weight).1);
// add remaining flow to forward edge
let forward_weight: &mut (u64, u64) = self.edge_weight_mut(edge).expect("edge not found");
if forward_flow > 0 {
(*forward_weight).0 += u64::try_from(forward_flow).expect("error casting flow to u64");
}
assert!((*forward_weight).0 <= (*forward_weight).1);
}
}