use std::hash::Hash;
use petgraph::algo::Measure;
use petgraph::visit::{
IntoEdges, IntoNodeIdentifiers, NodeCount, NodeIndexable, VisitMap, Visitable,
};
#[derive(Debug, PartialEq, Eq)]
pub struct NodeNotReachedError;
pub fn greedy_routing_success_rate<G, F, K>(graph: G, distance: F) -> f64
where
G: IntoEdges + NodeCount + NodeIndexable + IntoNodeIdentifiers + Visitable,
G::NodeId: Eq + Hash,
F: Fn(G::NodeId, G::NodeId) -> K,
K: Measure + Copy,
{
let num_vertices = graph.node_count() as f64;
let num_successes = graph
.node_identifiers()
.flat_map(|u| graph.node_identifiers().map(move |v| (u, v)))
.map(|(u, v)| ((u != v) && greedy_routing(graph, u, v, &distance).is_ok()) as u32)
.sum::<u32>() as f64;
num_successes / (num_vertices * num_vertices - num_vertices)
}
pub fn greedy_routing<G, F, K>(
graph: G,
source: G::NodeId,
destination: G::NodeId,
distance: F,
) -> Result<(Vec<G::NodeId>, K), NodeNotReachedError>
where
G: IntoEdges + NodeCount + NodeIndexable + Visitable,
G::NodeId: Eq + Hash,
F: Fn(G::NodeId, G::NodeId) -> K,
K: Measure + Copy,
{
if source == destination {
return Ok((vec![source], distance(source, destination)));
}
let mut visitmap = graph.visit_map();
let mut path: Vec<G::NodeId> = vec![source];
let mut total_distance = K::default();
let mut current_vertex = source;
while current_vertex != destination {
if visitmap.is_visited(¤t_vertex) {
return Err(NodeNotReachedError {});
}
visitmap.visit(current_vertex);
let mut min_distance = None;
let mut next = current_vertex;
for neighbor in graph.neighbors(current_vertex) {
let d = distance(neighbor, destination);
if min_distance.is_none() || d < min_distance.unwrap() {
next = neighbor;
min_distance = Some(d);
}
}
if min_distance.is_none() {
return Err(NodeNotReachedError {});
}
total_distance = total_distance + distance(current_vertex, next);
current_vertex = next;
path.push(current_vertex);
}
Ok((path, total_distance))
}
#[cfg(test)]
mod tests {
use super::{NodeNotReachedError, greedy_routing, greedy_routing_success_rate};
use crate::geometry::distances;
use crate::petgraph::graph::UnGraph;
use petgraph::Graph;
use petgraph::visit::NodeIndexable;
fn distance1d<G>(graph: &G, positions: &[f64], u: G::NodeId, v: G::NodeId) -> f64
where
G: NodeIndexable,
{
distances::lp_distance(
&[positions[graph.to_index(u)]],
&[positions[graph.to_index(v)]],
1,
)
.unwrap()
}
fn distance2d<G>(graph: &G, positions: &[[f64; 2]], u: G::NodeId, v: G::NodeId) -> f64
where
G: NodeIndexable,
{
distances::euclidean_distance(&positions[graph.to_index(u)], &positions[graph.to_index(v)])
.unwrap()
}
#[test]
fn test_unreachable_destination_error() {
let mut graph: UnGraph<(), ()> = Graph::with_capacity(4, 2);
let a = graph.add_node(());
let b = graph.add_node(());
let positions = vec![1., 2.];
assert_eq!(
greedy_routing(&graph, a, b, |u, v| distance1d(&graph, &positions, u, v)),
Err(NodeNotReachedError {})
)
}
#[test]
fn test_greedy_loop_error() {
let mut graph: UnGraph<(), ()> = Graph::with_capacity(4, 2);
let a = graph.add_node(());
let b = graph.add_node(());
let c = graph.add_node(());
let d = graph.add_node(());
graph.extend_with_edges([(a, b), (b, c), (c, d)]);
let positions = vec![[0., 0.], [1., 0.], [10., 0.], [4., 0.]];
assert_eq!(
greedy_routing(&graph, a, d, |u, v| distance2d(&graph, &positions, u, v)),
Err(NodeNotReachedError {})
)
}
#[test]
fn test_correct_greedy_path() {
let mut graph: UnGraph<(), ()> = Graph::with_capacity(7, 7);
let a = graph.add_node(());
let b = graph.add_node(());
let c = graph.add_node(());
let d = graph.add_node(());
let e = graph.add_node(());
let f = graph.add_node(());
let g = graph.add_node(());
graph.extend_with_edges([(a, b), (b, c), (c, d), (b, e), (e, f), (c, f), (f, g)]);
let positions = vec![
[0., 0.],
[1., 0.],
[2., 0.],
[2.5, 0.],
[1., 1.],
[2., 1.],
[3., 1.],
];
let (path, dist) =
greedy_routing(&graph, a, d, |u, v| distance2d(&graph, &positions, u, v)).unwrap();
assert_eq!(path, vec![a, b, c, d]);
assert!((dist - (1. + 1. + 0.5)).abs() < 1e-15);
let (path, dist) =
greedy_routing(&graph, a, g, |u, v| distance2d(&graph, &positions, u, v)).unwrap();
assert_eq!(path, vec![a, b, c, f, g]);
assert!((dist - (1. + 1. + 1. + 1.)).abs() < 1e-15);
}
#[test]
fn test_correct_greedy_success_rate() {
let mut graph: UnGraph<(), ()> = Graph::with_capacity(6, 6);
let b = graph.add_node(());
let c = graph.add_node(());
let d = graph.add_node(());
let e = graph.add_node(());
let f = graph.add_node(());
let g = graph.add_node(());
graph.extend_with_edges([(b, c), (c, d), (b, e), (e, f), (c, f), (f, g)]);
let positions = vec![
[1., 0.],
[2., 3.],
[3., 0.],
[1., -1.],
[2., -1.],
[3., -1.],
];
let success_rate =
greedy_routing_success_rate(&graph, |u, v| distance2d(&graph, &positions, u, v));
assert!((success_rate - 26. / 30.).abs() < 1e-15);
}
}