144 lines
6.3 KiB
Rust
144 lines
6.3 KiB
Rust
use petgraph::stable_graph::{StableGraph, NodeIndex};
|
|
use rand::thread_rng;
|
|
use rand::Rng;
|
|
use rand::seq::SliceRandom;
|
|
use itertools::Itertools;
|
|
use geo::{Line, coord, line_intersection::{line_intersection, LineIntersection}};
|
|
use std::cmp::min;
|
|
|
|
pub struct MaxflowProblem {
|
|
pub g: StableGraph<(f32, f32), (u64, u64)>,
|
|
pub s: NodeIndex,
|
|
pub t: NodeIndex,
|
|
}
|
|
|
|
impl MaxflowProblem {
|
|
// this function generates a maximally connected, planar graph with x/y coordinates which can be used for
|
|
// displaying a max-flow problem in a GUI. since the algorithm is rather inefficient, it only scales up to 50 nodes
|
|
pub fn new_planar_graph(num_nodes: u64, max_capacity: u64) -> Self {
|
|
// 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();
|
|
|
|
// generate nodes
|
|
for _i in 0..num_nodes {
|
|
// check if node is too close to another node (distance < 40)
|
|
let mut too_close = true;
|
|
let (mut x, mut y) = (0.0, 0.0);
|
|
// this is a poor attempt at modeling a do..while loop in Rust
|
|
while too_close {
|
|
too_close = false;
|
|
x = rng.gen_range(0.0..=400.0);
|
|
y = rng.gen_range(0.0..=300.0);
|
|
for node in &nodes {
|
|
if MaxflowProblem::distance(*graph.node_weight(*node).expect("node index not found"), (x,y)) < 40.0 {
|
|
too_close = true;
|
|
}
|
|
}
|
|
}
|
|
let n = graph.add_node((x,y));
|
|
nodes.push(n);
|
|
}
|
|
|
|
// select source (s) and sink (t)
|
|
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_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());
|
|
|
|
// insert the edge if possible
|
|
let mut inserted_edges: Vec<(NodeIndex, NodeIndex)> = Vec::new();
|
|
for (node1, node2, _) in possible_edges {
|
|
// check for collision with already inserted edges
|
|
let mut intersects = false;
|
|
for (node3, node4) in &inserted_edges {
|
|
if Self::intersects(
|
|
*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;
|
|
}
|
|
}
|
|
if intersects == false {
|
|
// otherwise insert the edge and its residual edge
|
|
inserted_edges.push((node1, node2));
|
|
inserted_edges.push((node2, node1));
|
|
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 }
|
|
}
|
|
|
|
// this function generates a fully connected random graph which can be used for solving max-flow problems
|
|
pub fn new_random_graph(num_nodes: u64, max_capacity: u64) -> Self {
|
|
let mut graph: StableGraph<(f32, f32), (u64, u64)> = StableGraph::default();
|
|
let mut nodes: Vec<NodeIndex> = Vec::new();
|
|
let mut rng = thread_rng();
|
|
|
|
for _i in 0..num_nodes {
|
|
let x = rng.gen_range(0.0..=400.0);
|
|
let y = rng.gen_range(0.0..=300.0);
|
|
let n = graph.add_node((x,y));
|
|
|
|
// choose a random number of nodes between 1 and 5 to connect to
|
|
let num_neighbors = rng.gen_range(min(nodes.len(), 1)..=min(nodes.len(), 5));
|
|
let neighbors = nodes.choose_multiple(&mut rng, num_neighbors);
|
|
for neighbor in neighbors {
|
|
// insert edges for each neighbor
|
|
let capacity1: u64 = rng.gen_range(1..=max_capacity);
|
|
let capacity2: u64 = rng.gen_range(1..=max_capacity);
|
|
graph.add_edge(n, *neighbor, (0, capacity1));
|
|
graph.add_edge(*neighbor, n, (0, capacity2));
|
|
}
|
|
nodes.push(n);
|
|
}
|
|
|
|
// choose source & sink nodes
|
|
let (s,t) = nodes.choose_multiple(&mut rng, 2).copied().collect_tuple::<(NodeIndex, NodeIndex)>().expect("not enough nodes");
|
|
|
|
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: (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: (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
|
|
}
|
|
}
|
|
}
|