Compare commits
3 Commits
3f93cb57d3
...
8d28ef44be
| Author | SHA1 | Date | |
|---|---|---|---|
| 8d28ef44be | |||
| a13c1a5001 | |||
| c21d648c8f |
23
src/algorithms/common.rs
Normal file
23
src/algorithms/common.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use petgraph::stable_graph::StableGraph;
|
||||||
|
use crate::random_generator::MaxflowProblem;
|
||||||
|
|
||||||
|
pub trait MaxflowAlgorithm {
|
||||||
|
// initialize algorithm from new problem
|
||||||
|
fn from_problem(p: MaxflowProblem) -> Self;
|
||||||
|
|
||||||
|
// perform a single step; returns true if the algorithm terminates
|
||||||
|
fn step(&mut self) -> bool;
|
||||||
|
|
||||||
|
// runs the whole algorithm in a single step
|
||||||
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
// returns the current graph
|
||||||
|
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
// returns the algorithm name
|
||||||
|
fn name(&self) -> &'static str;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn available_capacity(edge: (u64, u64)) -> u64 {
|
||||||
|
edge.1 - edge.0
|
||||||
|
}
|
||||||
@@ -1,4 +1,3 @@
|
|||||||
// Dinic
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,10 @@
|
|||||||
// Edmonds-Karp (also Ford-Fulkerson mit BFS statt DFS),
|
|
||||||
use std::cmp::min;
|
use std::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use petgraph::{stable_graph::{NodeIndex, StableGraph, EdgeReference}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
use petgraph::{stable_graph::{NodeIndex, StableGraph, EdgeReference}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
||||||
|
|
||||||
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
|
fn available_capacity(edge: (u64, u64)) -> u64 {
|
||||||
edge.weight().1 - edge.weight().0
|
edge.1 - edge.0
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs a depth Breadth First Search (BFS) and returns an augmenting path from source to destination if possible
|
// Runs a depth Breadth First Search (BFS) and returns an augmenting path from source to destination if possible
|
||||||
@@ -21,7 +20,7 @@ fn bfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
|||||||
|
|
||||||
for edge in outgoing_edges {
|
for edge in outgoing_edges {
|
||||||
let neighbor = edge.target();
|
let neighbor = edge.target();
|
||||||
if !visited.is_visited(&neighbor) && available_capacity(edge) > 0 {
|
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
|
||||||
visited.visit(neighbor);
|
visited.visit(neighbor);
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(neighbor);
|
new_path.push(neighbor);
|
||||||
|
|||||||
@@ -1,20 +1,99 @@
|
|||||||
// 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::cmp::min;
|
||||||
use std::collections::VecDeque;
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
use petgraph::{stable_graph::{EdgeReference, NodeIndex, StableGraph}, visit::{EdgeRef, VisitMap, Visitable}, Direction};
|
||||||
|
use crate::{algorithms::common::{available_capacity, MaxflowAlgorithm}, graph::ResidualGraph};
|
||||||
|
use crate::MaxflowProblem;
|
||||||
|
|
||||||
//use crate::random_generator::MaxflowProblem;
|
pub struct FordFulkerson {
|
||||||
//mod random_generator;
|
graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
|
source: NodeIndex,
|
||||||
fn available_capacity(edge: EdgeReference<'_, (u64, u64)>) -> u64 {
|
sink: NodeIndex,
|
||||||
edge.weight().1 - edge.weight().0
|
residual_graph: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Runs a depth Depth First Search (DFS) and returns an augmenting path from source to destination if possible
|
impl FordFulkerson {
|
||||||
|
// Runs a depth Depth First Search (DFS) which returns an augmenting path from source to destination if possible
|
||||||
|
fn dfs(&mut self) -> Option<Vec<NodeIndex>> {
|
||||||
|
let mut visited = self.residual_graph.visit_map();
|
||||||
|
let mut stack = VecDeque::from([(self.source, vec![self.source])]);
|
||||||
|
|
||||||
|
// work through the main stack
|
||||||
|
while let Some((node, path)) = stack.pop_front() {
|
||||||
|
let outgoing_edges = self.residual_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 == self.sink {
|
||||||
|
return Some(new_path);
|
||||||
|
} else {
|
||||||
|
stack.push_front((neighbor, new_path));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MaxflowAlgorithm for FordFulkerson {
|
||||||
|
fn from_problem(p: MaxflowProblem) -> Self {
|
||||||
|
Self {
|
||||||
|
residual_graph: p.g.residual(),
|
||||||
|
graph: p.g,
|
||||||
|
source: p.s,
|
||||||
|
sink: p.t,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn step(&mut self) -> bool {
|
||||||
|
// continue while there are augmenting paths
|
||||||
|
if let Some(path) = self.dfs() {
|
||||||
|
// find all edges along the path
|
||||||
|
let edges: Vec<petgraph::prelude::EdgeIndex> = path.windows(2).map(|w| self.residual_graph.find_edge(w[0], w[1]).expect("edge not found")).collect();
|
||||||
|
// find bottleneck capacity along path
|
||||||
|
let bottlenack_capacity = edges.iter().fold(u64::MAX, |m, x| {
|
||||||
|
let edge = self.residual_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 {
|
||||||
|
// increase flow of the forward edge with the calculated bottleneck value
|
||||||
|
let weight: &mut (u64, u64) = self.residual_graph.edge_weight_mut(edge).expect("edge not found");
|
||||||
|
(*weight).0 += bottlenack_capacity;
|
||||||
|
// increase capacity of the residual edge of the with the calculated bottleneck value
|
||||||
|
}
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
while !self.step() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn graph(&mut self) -> StableGraph<(f32, f32), (u64, u64)> {
|
||||||
|
self.residual_graph.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn name(&self) -> &'static str {
|
||||||
|
"Ford-Fulkerson"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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>> {
|
fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destination: NodeIndex) -> Option<Vec<NodeIndex>> {
|
||||||
let mut visited = graph.visit_map();
|
let mut visited = graph.visit_map();
|
||||||
let mut stack = VecDeque::from([(source, vec![source])]);
|
let mut stack = VecDeque::from([(source, vec![source])]);
|
||||||
@@ -24,10 +103,10 @@ fn dfs(graph: &StableGraph<(f32, f32), (u64, u64)>, source: NodeIndex, destinati
|
|||||||
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
|
let outgoing_edges = graph.edges_directed(node, Direction::Outgoing);
|
||||||
visited.visit(node);
|
visited.visit(node);
|
||||||
|
|
||||||
// iterate over all outgoing edges
|
// iterate over all outgoing edges & add neighboring nodes to the stack
|
||||||
for edge in outgoing_edges {
|
for edge in outgoing_edges {
|
||||||
let neighbor = edge.target();
|
let neighbor = edge.target();
|
||||||
if !visited.is_visited(&neighbor) && available_capacity(edge) > 0 {
|
if !visited.is_visited(&neighbor) && available_capacity(*edge.weight()) > 0 {
|
||||||
visited.visit(neighbor);
|
visited.visit(neighbor);
|
||||||
let mut new_path = path.clone();
|
let mut new_path = path.clone();
|
||||||
new_path.push(neighbor);
|
new_path.push(neighbor);
|
||||||
@@ -67,52 +146,3 @@ pub fn ford_fulkerson(mut graph: StableGraph<(f32, f32), (u64, u64)>, source: No
|
|||||||
// return graph with augmented flows
|
// return graph with augmented flows
|
||||||
graph
|
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
} */
|
|
||||||
|
|||||||
0
src/algorithms/goldberg_tarjan.rs
Normal file
0
src/algorithms/goldberg_tarjan.rs
Normal file
@@ -1,7 +1,10 @@
|
|||||||
mod edmonds_karp;
|
mod edmonds_karp;
|
||||||
mod ford_fulkerson;
|
mod ford_fulkerson;
|
||||||
mod dinic;
|
mod dinic;
|
||||||
|
mod common;
|
||||||
|
|
||||||
pub use ford_fulkerson::ford_fulkerson;
|
pub use ford_fulkerson::ford_fulkerson;
|
||||||
pub use edmonds_karp::edmonds_karp;
|
pub use edmonds_karp::edmonds_karp;
|
||||||
pub use dinic::dinic;
|
pub use dinic::dinic;
|
||||||
|
pub use ford_fulkerson::FordFulkerson;
|
||||||
|
pub use common::MaxflowAlgorithm;
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
|
use egui_graphs::{DefaultNodeShape, Graph, default_edge_transform, default_node_transform, to_graph_custom};
|
||||||
use petgraph::{stable_graph::{StableGraph, NodeIndex}, Directed};
|
use petgraph::{stable_graph::{NodeIndex, StableGraph}, visit::{VisitMap, Visitable}, Directed};
|
||||||
use egui::Pos2;
|
use egui::Pos2;
|
||||||
use crate::layout::CustomEdgeShape;
|
use crate::layout::CustomEdgeShape;
|
||||||
use egui::Color32;
|
use egui::Color32;
|
||||||
@@ -19,9 +19,10 @@ impl ResidualGraph for StableGraph<(f32, f32), (u64, u64)> {
|
|||||||
let (reverse_flow, reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
|
let (reverse_flow, reverse_capacity) = *res_graph.edge_weight(reverse_edge).expect("reverse edge not found");
|
||||||
// update the forward edge
|
// update the forward edge
|
||||||
// TODO: this seems to overflow in certain cases
|
// TODO: this seems to overflow in certain cases
|
||||||
res_graph.update_edge(source, target, (flow, capacity + reverse_flow - flow));
|
|
||||||
|
res_graph.update_edge(source, target, (flow, capacity + reverse_flow));
|
||||||
// update the reverse edge
|
// update the reverse edge
|
||||||
res_graph.update_edge(target, source, (reverse_flow, reverse_capacity + flow - reverse_flow));
|
//res_graph.update_edge(target, source, (reverse_flow, reverse_capacity + flow));
|
||||||
} else {
|
} else {
|
||||||
// add a residual edge with a flow of 0 if the reverse edge doesn't exist
|
// 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.add_edge(target, source, (0, 0));
|
||||||
|
|||||||
42
src/main.rs
42
src/main.rs
@@ -3,11 +3,10 @@ use egui::{CentralPanel, CollapsingHeader, ComboBox, Context, ScrollArea, SidePa
|
|||||||
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
use egui_graphs::{DefaultEdgeShape, DefaultNodeShape, Graph, GraphView, LayoutRandom, LayoutStateRandom, SettingsStyle};
|
||||||
use geo::algorithm;
|
use geo::algorithm;
|
||||||
use petgraph::{stable_graph::{NodeIndex, StableGraph}, Directed};
|
use petgraph::{stable_graph::{NodeIndex, StableGraph}, Directed};
|
||||||
use std::{fmt::Display, ptr::fn_addr_eq};
|
|
||||||
|
|
||||||
use random_generator::MaxflowProblem;
|
use random_generator::MaxflowProblem;
|
||||||
use layout::CustomEdgeShape;
|
use layout::CustomEdgeShape;
|
||||||
use crate::algorithms::{ford_fulkerson, edmonds_karp, dinic};
|
use crate::algorithms::{MaxflowAlgorithm, FordFulkerson};
|
||||||
use graph::{ResidualGraph, GuiGraph, FlowGraph};
|
use graph::{ResidualGraph, GuiGraph, FlowGraph};
|
||||||
|
|
||||||
mod random_generator;
|
mod random_generator;
|
||||||
@@ -17,13 +16,20 @@ mod graph;
|
|||||||
|
|
||||||
type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
|
type MaxflowFn = fn(StableGraph<(f32, f32), (u64, u64)>, NodeIndex, NodeIndex) -> StableGraph<(f32, f32), (u64, u64)>;
|
||||||
|
|
||||||
|
enum MaxflowAlgos {
|
||||||
|
FordFulkerson,
|
||||||
|
EdmondsKarp,
|
||||||
|
Dinic,
|
||||||
|
GoldbergTarjan,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct MaxflowApp {
|
pub struct MaxflowApp {
|
||||||
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
g: Graph<(f32, f32), (u64, u64), Directed, u32, DefaultNodeShape, CustomEdgeShape>,
|
||||||
p: MaxflowProblem,
|
p: MaxflowProblem,
|
||||||
r: StableGraph<(f32, f32), (u64, u64)>,
|
r: StableGraph<(f32, f32), (u64, u64)>,
|
||||||
node_count: u64,
|
node_count: u64,
|
||||||
max_capacity: u64,
|
max_capacity: u64,
|
||||||
algorithm: MaxflowFn,
|
algorithm: Box<dyn MaxflowAlgorithm>,
|
||||||
display_residual: bool,
|
display_residual: bool,
|
||||||
display_active_flows: bool,
|
display_active_flows: bool,
|
||||||
}
|
}
|
||||||
@@ -36,14 +42,14 @@ impl MaxflowApp {
|
|||||||
r: StableGraph::default(),
|
r: StableGraph::default(),
|
||||||
p: problem, node_count: 10,
|
p: problem, node_count: 10,
|
||||||
max_capacity: 5,
|
max_capacity: 5,
|
||||||
algorithm: ford_fulkerson,
|
algorithm: FordFulkerson,
|
||||||
display_residual: false,
|
display_residual: false,
|
||||||
display_active_flows: false,
|
display_active_flows: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn update_graph(&mut self) {
|
fn update_graph(&mut self) {
|
||||||
let mut graph = match self.display_residual {
|
let graph = match self.display_residual {
|
||||||
true => &self.p.g.residual(),
|
true => &self.p.g.residual(),
|
||||||
false => &self.p.g,
|
false => &self.p.g,
|
||||||
};
|
};
|
||||||
@@ -83,21 +89,23 @@ impl App for MaxflowApp {
|
|||||||
.default_open(true)
|
.default_open(true)
|
||||||
.show(ui, |ui| {
|
.show(ui, |ui| {
|
||||||
ComboBox::from_label("algorithm")
|
ComboBox::from_label("algorithm")
|
||||||
.selected_text(format!("{}", match self.algorithm {
|
.selected_text(self.algorithm.name())
|
||||||
_ 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| {
|
.show_ui(ui, |ui| {
|
||||||
ui.selectable_value(&mut self.algorithm, ford_fulkerson, "Ford-Fulkerson");
|
ui.selectable_value(&mut self.algorithm, FordFulkerson::from_problem(self.p), "Ford-Fulkerson");
|
||||||
ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
//ui.selectable_value(&mut self.algorithm, edmonds_karp, "Edmonds-Karp");
|
||||||
ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
//ui.selectable_value(&mut self.algorithm, dinic, "Dinic");
|
||||||
});
|
});
|
||||||
if ui.button("run algorithm").clicked() {
|
if ui.button("step").clicked() {
|
||||||
let max_flow_graph = ford_fulkerson(self.p.g.clone(), self.p.s, self.p.t);
|
// check if algo is initialized
|
||||||
self.p = MaxflowProblem::from(max_flow_graph, self.p.s, self.p.t);
|
// if not, initialize
|
||||||
|
// run step function
|
||||||
|
}
|
||||||
|
if ui.button("run").clicked() {
|
||||||
|
self.algorithm.run();
|
||||||
|
self.p = MaxflowProblem::from(self.algorithm.graph(), self.p.s, self.p.t);
|
||||||
self.update_graph();
|
self.update_graph();
|
||||||
|
//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);
|
||||||
}
|
}
|
||||||
if ui.button("reset").clicked() {
|
if ui.button("reset").clicked() {
|
||||||
self.p.reset_flow();
|
self.p.reset_flow();
|
||||||
|
|||||||
25
tests/example_flows.rs
Normal file
25
tests/example_flows.rs
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
use petgraph::prelude::StableGraph;
|
||||||
|
|
||||||
|
|
||||||
|
// Example taken from: https://en.wikipedia.org/wiki/Dinic%27s_algorithm#Example
|
||||||
|
#[test]
|
||||||
|
fn test_maxflow_small_graph() {
|
||||||
|
let mut g: StableGraph<(f32, f32), (u64, u64)> = StableGraph::new();
|
||||||
|
let s = g.add_node((0., 5.));
|
||||||
|
let one = g.add_node((10., 0.));
|
||||||
|
let two = g.add_node((10., 10.));
|
||||||
|
let three = g.add_node((20., 0.));
|
||||||
|
let four = g.add_node((20., 10.));
|
||||||
|
let t = g.add_node((30., 5.));
|
||||||
|
g.add_edge(s, one, (0, 10));
|
||||||
|
g.add_edge(s, two, (0, 10));
|
||||||
|
g.add_edge(one, three, (0, 4));
|
||||||
|
g.add_edge(one, four, (0, 8));
|
||||||
|
g.add_edge(one, two, (0,2));
|
||||||
|
g.add_edge(two, four, (0,9));
|
||||||
|
g.add_edge(three, t, (0,10));
|
||||||
|
g.add_edge(four, three, (0,6));
|
||||||
|
g.add_edge(four, t, (0,10));
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
}
|
||||||
2
tests/valid_flow.rs
Normal file
2
tests/valid_flow.rs
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
// check capacity at each edge (incoming flows = outgoing flows)
|
||||||
|
// check capacity isn't violated (flow <= capacity)
|
||||||
Reference in New Issue
Block a user