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