Files
maxflow-rs/src/random_generator.rs
2025-08-27 00:06:12 +02:00

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
}
}
}