use crate::dijkstra::epoch_array_dijkstra_node_weight_array::EpochNodeWeightArray;
use crate::dijkstra::performance_counters::DijkstraPerformanceData;
use std::collections::BinaryHeap;
use std::fmt::Debug;
use std::marker::PhantomData;
use std::ops::Add;
use traitgraph::index::{GraphIndex, NodeIndex};
use traitgraph::interface::{GraphBase, StaticGraph};
mod dijkstra_weight_implementations;
pub mod epoch_array_dijkstra_node_weight_array;
#[cfg(feature = "hashbrown_dijkstra_node_weight_array")]
pub mod hashbrown_dijkstra_node_weight_array;
pub mod performance_counters;
pub type DefaultDijkstra<Graph, WeightType> = Dijkstra<
Graph,
WeightType,
EpochNodeWeightArray<WeightType>,
BinaryHeap<std::cmp::Reverse<(WeightType, <Graph as GraphBase>::NodeIndex)>>,
>;
pub trait DijkstraWeight: Ord + Add<Output = Self> + Sized + Clone {
fn infinity() -> Self;
fn zero() -> Self;
}
pub trait DijkstraWeightedEdgeData<WeightType: DijkstraWeight> {
fn weight(&self) -> WeightType;
}
impl<WeightType: DijkstraWeight + Copy> DijkstraWeightedEdgeData<WeightType> for WeightType {
#[inline]
fn weight(&self) -> WeightType {
*self
}
}
pub trait NodeWeightArray<WeightType> {
fn new(size: usize) -> Self;
fn get(&self, node_index: usize) -> WeightType;
fn get_mut<'this: 'result, 'result>(
&'this mut self,
node_index: usize,
) -> &'result mut WeightType;
fn set(&mut self, node_index: usize, weight: WeightType);
fn clear(&mut self);
fn size(&self) -> usize;
}
impl<WeightType: DijkstraWeight + Copy> NodeWeightArray<WeightType> for Vec<WeightType> {
fn new(size: usize) -> Self {
vec![WeightType::infinity(); size]
}
#[inline]
fn get(&self, node_index: usize) -> WeightType {
self[node_index]
}
#[inline]
fn get_mut<'this: 'result, 'result>(
&'this mut self,
node_index: usize,
) -> &'result mut WeightType {
&mut self[node_index]
}
#[inline]
fn set(&mut self, node_index: usize, weight: WeightType) {
self[node_index] = weight;
}
fn clear(&mut self) {
for entry in self.iter_mut() {
*entry = WeightType::infinity();
}
}
#[inline]
fn size(&self) -> usize {
self.len()
}
}
pub trait DijkstraTargetMap<Graph: GraphBase> {
fn is_target(&self, node_index: Graph::NodeIndex) -> bool;
}
impl<Graph: GraphBase> DijkstraTargetMap<Graph> for Vec<bool> {
fn is_target(&self, node_index: Graph::NodeIndex) -> bool {
self[node_index.as_usize()]
}
}
impl<IndexType: Sized + Eq, Graph: GraphBase<NodeIndex = NodeIndex<IndexType>>>
DijkstraTargetMap<Graph> for NodeIndex<IndexType>
{
fn is_target(&self, node_index: Graph::NodeIndex) -> bool {
*self == node_index
}
}
pub trait DijkstraHeap<WeightType, IndexType>: Default {
fn insert(&mut self, weight: WeightType, index: IndexType);
fn remove_min(&mut self) -> Option<(WeightType, IndexType)>;
fn clear(&mut self);
fn size(&mut self) -> usize;
}
impl<WeightType: Ord, IndexType: Ord> DijkstraHeap<WeightType, IndexType>
for BinaryHeap<std::cmp::Reverse<(WeightType, IndexType)>>
{
fn insert(&mut self, weight: WeightType, index: IndexType) {
self.push(std::cmp::Reverse((weight, index)));
}
fn remove_min(&mut self) -> Option<(WeightType, IndexType)> {
self.pop().map(|packed| packed.0)
}
fn clear(&mut self) {
self.clear()
}
fn size(&mut self) -> usize {
self.len()
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub enum DijkstraExhaustiveness {
Complete,
PartialNodeWeights,
PartialHeap,
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct DijkstraStatus<DijkstraPerformance: DijkstraPerformanceData> {
pub exhaustiveness: DijkstraExhaustiveness,
pub performance_data: DijkstraPerformance,
}
pub struct Dijkstra<
Graph: GraphBase,
WeightType: DijkstraWeight,
NodeWeights: NodeWeightArray<WeightType>,
Heap: DijkstraHeap<WeightType, Graph::NodeIndex>,
> {
heap: Heap,
node_weights: NodeWeights,
graph: PhantomData<Graph>,
_weight_type_phantom: PhantomData<WeightType>,
}
impl<
WeightType: DijkstraWeight + Eq + Debug,
EdgeData: DijkstraWeightedEdgeData<WeightType>,
Graph: StaticGraph<EdgeData = EdgeData>,
NodeWeights: NodeWeightArray<WeightType>,
Heap: DijkstraHeap<WeightType, Graph::NodeIndex>,
> Dijkstra<Graph, WeightType, NodeWeights, Heap>
{
pub fn new(graph: &Graph) -> Self {
Self {
heap: Default::default(),
node_weights: NodeWeights::new(graph.node_count()),
graph: Default::default(),
_weight_type_phantom: Default::default(),
}
}
#[inline(never)]
#[allow(clippy::too_many_arguments)]
pub fn shortest_path_lens<
TargetMap: DijkstraTargetMap<Graph>,
DijkstraPerformance: DijkstraPerformanceData,
>(
&mut self,
graph: &Graph,
source: Graph::NodeIndex,
targets: &TargetMap,
target_amount: usize,
max_weight: WeightType,
forbid_source_target: bool,
distances: &mut Vec<(Graph::NodeIndex, WeightType)>,
max_node_weight_data_size: usize,
max_heap_data_size: usize,
mut performance_data: DijkstraPerformance,
) -> DijkstraStatus<DijkstraPerformance> {
self.heap.insert(WeightType::zero(), source);
self.node_weights.set(source.as_usize(), WeightType::zero());
distances.clear();
let mut exhaustiveness = DijkstraExhaustiveness::Complete;
while let Some((weight, node_index)) = self.heap.remove_min() {
performance_data.add_iteration();
let actual_weight = self.node_weights.get(node_index.as_usize());
if actual_weight < weight {
performance_data.add_unnecessary_heap_element();
continue;
}
debug_assert_eq!(actual_weight, weight);
if weight > max_weight {
break;
}
if targets.is_target(node_index) && (!forbid_source_target || node_index != source) {
distances.push((node_index, weight.clone()));
if distances.len() == target_amount {
break;
}
}
for out_neighbor in graph.out_neighbors(node_index) {
let new_neighbor_weight =
weight.clone() + graph.edge_data(out_neighbor.edge_id).weight();
let neighbor_weight = self.node_weights.get_mut(out_neighbor.node_id.as_usize());
if new_neighbor_weight < *neighbor_weight {
*neighbor_weight = new_neighbor_weight.clone();
self.heap.insert(new_neighbor_weight, out_neighbor.node_id);
}
}
performance_data.record_heap_size(self.heap.size());
performance_data.record_distance_array_size(self.node_weights.size());
if self.node_weights.size() > max_node_weight_data_size {
exhaustiveness = DijkstraExhaustiveness::PartialNodeWeights;
break;
} else if self.heap.size() > max_heap_data_size {
exhaustiveness = DijkstraExhaustiveness::PartialHeap;
break;
}
}
self.heap.clear();
self.node_weights.clear();
performance_data.finish_dijkstra();
DijkstraStatus {
exhaustiveness,
performance_data,
}
}
}
#[cfg(test)]
mod tests {
use crate::dijkstra::performance_counters::NoopDijkstraPerformanceCounter;
use crate::dijkstra::DefaultDijkstra;
use traitgraph::implementation::petgraph_impl::PetGraph;
use traitgraph::interface::MutableGraphContainer;
#[test]
fn test_dijkstra_simple() {
let mut graph = PetGraph::new();
let n1 = graph.add_node(());
let n2 = graph.add_node(());
let n3 = graph.add_node(());
graph.add_edge(n1, n2, 2);
graph.add_edge(n2, n3, 2);
graph.add_edge(n1, n3, 5);
let mut dijkstra = DefaultDijkstra::new(&graph);
let mut distances = Vec::new();
let mut targets = vec![false, false, true];
dijkstra.shortest_path_lens(
&graph,
n1,
&targets,
1,
6,
false,
&mut distances,
usize::MAX,
usize::MAX,
NoopDijkstraPerformanceCounter,
);
debug_assert_eq!(distances, vec![(n3, 4)]);
dijkstra.shortest_path_lens(
&graph,
n1,
&targets,
1,
6,
false,
&mut distances,
usize::MAX,
usize::MAX,
NoopDijkstraPerformanceCounter,
);
debug_assert_eq!(distances, vec![(n3, 4)]);
dijkstra.shortest_path_lens(
&graph,
n2,
&targets,
1,
6,
false,
&mut distances,
usize::MAX,
usize::MAX,
NoopDijkstraPerformanceCounter,
);
debug_assert_eq!(distances, vec![(n3, 2)]);
dijkstra.shortest_path_lens(
&graph,
n3,
&targets,
1,
6,
false,
&mut distances,
usize::MAX,
usize::MAX,
NoopDijkstraPerformanceCounter,
);
debug_assert_eq!(distances, vec![(n3, 0)]);
targets = vec![false, true, false];
dijkstra.shortest_path_lens(
&graph,
n3,
&targets,
1,
6,
false,
&mut distances,
usize::MAX,
usize::MAX,
NoopDijkstraPerformanceCounter,
);
debug_assert_eq!(distances, vec![]);
}
#[test]
fn test_dijkstra_cycle() {
let mut graph = PetGraph::new();
let n1 = graph.add_node(());
let n2 = graph.add_node(());
let n3 = graph.add_node(());
graph.add_edge(n1, n2, 2);
graph.add_edge(n2, n3, 2);
graph.add_edge(n3, n1, 5);
let mut dijkstra = DefaultDijkstra::new(&graph);
let mut distances = Vec::new();
let targets = vec![false, false, true];
dijkstra.shortest_path_lens(
&graph,
n1,
&targets,
1,
6,
false,
&mut distances,
usize::MAX,
usize::MAX,
NoopDijkstraPerformanceCounter,
);
debug_assert_eq!(distances, vec![(n3, 4)]);
}
}