2025-08-03: checkpoint
This commit is contained in:
243
src/graph.rs
243
src/graph.rs
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user