use crate::gridgraph::{EdgeCost, GraphNode, GridGraph, NodeId}; use serde::{Deserialize, Serialize}; use std::cmp::Ordering; use std::collections::BinaryHeap; #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Landmark { pub node: GraphNode, pub distances: Vec, } #[derive(Debug, Clone, Default, Serialize, Deserialize)] pub struct LandmarkSet { pub landmarks: Vec, } #[derive(Debug, Clone)] pub struct LandmarkBestSet<'a> { pub landmark_set: &'a LandmarkSet, pub best_size: usize, best_landmarks: Vec, } impl Landmark { pub fn generate(node: GraphNode, graph: &GridGraph) -> Landmark { let mut landmark = Landmark { node, distances: vec![EdgeCost::MAX; graph.nodes.len()], }; landmark.node = node; #[derive(Eq, PartialEq)] struct DijkstraElement { index: u32, cost: EdgeCost, } impl Ord for DijkstraElement { // inverted cmp function, such that the Max-Heap provided by Rust // can be used as a Min-Heap fn cmp(&self, other: &Self) -> Ordering { other.cost.cmp(&self.cost) } } impl PartialOrd for DijkstraElement { fn partial_cmp(&self, other: &Self) -> Option { Some(self.cmp(other)) } } let mut heap = BinaryHeap::new(); heap.push(DijkstraElement { cost: 0, index: landmark.node.index, }); while let Some(DijkstraElement { index, cost }) = heap.pop() { // the heap does not support "update" operations, so we // insert elements again and if they come out of the heap but have // been processed earlier we simply skip them. if cost > landmark.distances[index as usize] { continue; }; landmark.distances[index as usize] = cost; for edge in graph.get_edges(index as NodeId) { let new_cost = cost + edge.cost; if new_cost < landmark.distances[edge.neighbor as usize] { //println!("adding new element to heap"); heap.push(DijkstraElement { index: edge.neighbor, cost: new_cost, }); landmark.distances[edge.neighbor as usize] = new_cost; } } } // now the costs to all reachable nodes is calculated. landmark } /// calculates the lower-bounding distance estimate between the 2 nodes /// via the landmark. /// If one or more of the nodes are not reachable from the landmark /// an estimate of `0` is returned. pub fn estimate(&self, from: NodeId, to: NodeId) -> EdgeCost { let l_to = self.distances[to]; let l_from = self.distances[from]; if l_to == EdgeCost::MAX || l_from == EdgeCost::MAX { EdgeCost::MAX } else { // since we are working on an undirected graph we can // use the distances once from and once to the landmark. // This leads to l_to - l_from and l_from - l_to (as signed subtractions) // which except for the sign are the same value. // We can simply take the bigger one, which is handled // nicely the abs() function let distance = (l_to as i64 - l_from as i64).abs() as EdgeCost; //println!( // "distance from {} to {} via landmark {} is at least {}", // from, to, self.node.index, distance //); distance } } } impl LandmarkSet { pub fn random_set(size: usize, graph: &GridGraph) -> Self { let mut set = LandmarkSet::default(); let nodes = graph.get_random_nodes(size); for node in nodes.into_iter() { let landmark = Landmark::generate(*node, graph); set.landmarks.push(landmark) } set } } impl LandmarkBestSet<'_> { pub fn select_best(&mut self, from: NodeId, to: NodeId) { let mut results = vec![]; for (index, landmark) in self.landmark_set.landmarks.iter().enumerate() { results.push((index, landmark.estimate(from, to))); } results.sort_by_key(|k| k.1); results.reverse(); self.best_landmarks.clear(); for result in results[..self.best_size].iter() { self.best_landmarks.push(result.0); } } pub fn estimate(&self, from: NodeId, to: NodeId) -> EdgeCost { let mut distance = 0; for index in &self.best_landmarks { let candidate = self.landmark_set.landmarks[*index].estimate(from, to); if candidate == EdgeCost::MAX { continue; } distance = distance.max(candidate) } //println!("calculated estimate {:?} for {} to {}", distance, from, to); distance } pub fn new<'a>(best_size: usize, landmark_set: &'a LandmarkSet) -> LandmarkBestSet<'a> { LandmarkBestSet { best_size, landmark_set, best_landmarks: Vec::new(), } } }