170 lines
5.2 KiB
Rust
170 lines
5.2 KiB
Rust
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<EdgeCost>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
|
|
pub struct LandmarkSet {
|
|
pub landmarks: Vec<Landmark>,
|
|
}
|
|
|
|
#[derive(Debug, Clone)]
|
|
pub struct LandmarkBestSet<'a> {
|
|
pub landmark_set: &'a LandmarkSet,
|
|
pub best_size: usize,
|
|
best_landmarks: Vec<usize>,
|
|
}
|
|
|
|
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<Ordering> {
|
|
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(),
|
|
}
|
|
}
|
|
}
|