#[cfg(feature = "hgraph")]
use crate::core::view::NodeId;
#[cfg(feature = "hgraph")]
use crate::hgraph::h_graph::HeterogeneousGraph;
#[cfg(feature = "hgraph")]
#[cfg(feature = "hgraph")]
use std::collections::HashMap;
#[cfg(feature = "hgraph")]
use std::fmt::Debug;
#[cfg(feature = "hgraph")]
use std::hash::Hash;
#[cfg(feature = "hgraph")]
use std::ops::Add;
#[cfg(feature = "hgraph")]
#[derive(Debug, thiserror::Error)]
pub enum ShortestPathError {
#[error("Invalid node reference: node ID {0} not found")]
InvalidNodeReference(NodeId),
#[error("{0}")]
UnusableWeight(crate::core::ShortestPathError),
}
#[cfg(feature = "hgraph")]
impl From<crate::core::ShortestPathError> for ShortestPathError {
fn from(error: crate::core::ShortestPathError) -> Self {
use crate::core::ShortestPathError as Core;
match error {
Core::InvalidStartNode(id)
| Core::InvalidTargetNode(id)
| Core::InvalidNeighborNode(id) => ShortestPathError::InvalidNodeReference(id),
other => ShortestPathError::UnusableWeight(other),
}
}
}
#[cfg(feature = "hgraph")]
pub type Result<T> = std::result::Result<T, ShortestPathError>;
#[cfg(feature = "hgraph")]
#[deprecated(
since = "3.0.0",
note = "superseded by `xgraph::core::ShortestPath`; the `*_by_types` variants become \
`graph.of_edge_kinds(&[..])` followed by the ordinary method"
)]
pub trait HeteroShortestPath<W, N, E>
where
W: Add<Output = W> + PartialOrd + Copy + Default + From<u8> + Debug + PartialEq,
N: Clone + Eq + Hash + Debug + crate::hgraph::h_node::NodeType,
E: Clone + Default + Debug + crate::hgraph::h_edge::EdgeType,
{
fn dijkstra(&self, start: NodeId) -> Result<(HashMap<NodeId, W>, HashMap<NodeId, NodeId>)>;
fn dijkstra_by_types(
&self,
start: NodeId,
edge_types: &[&str],
) -> Result<(HashMap<NodeId, W>, HashMap<NodeId, NodeId>)>;
fn dijkstra_path(&self, start: NodeId, end: NodeId) -> Result<Option<Vec<NodeId>>>;
fn dijkstra_path_by_types(
&self,
start: NodeId,
end: NodeId,
edge_types: &[&str],
) -> Result<Option<Vec<NodeId>>>;
}
#[cfg(feature = "hgraph")]
#[allow(deprecated)]
impl<W, N, E> HeteroShortestPath<W, N, E> for HeterogeneousGraph<W, N, E>
where
W: Add<Output = W> + PartialOrd + Copy + Default + From<u8> + Debug + PartialEq,
N: Clone + Eq + Hash + Debug + crate::hgraph::h_node::NodeType,
E: Clone + Default + Debug + crate::hgraph::h_edge::EdgeType,
{
fn dijkstra(&self, start: NodeId) -> Result<(HashMap<NodeId, W>, HashMap<NodeId, NodeId>)> {
crate::core::ShortestPath::dijkstra_with_predecessors(self, start)
.map_err(ShortestPathError::from)
}
fn dijkstra_by_types(
&self,
start: NodeId,
edge_types: &[&str],
) -> Result<(HashMap<NodeId, W>, HashMap<NodeId, NodeId>)> {
crate::core::ShortestPath::dijkstra_with_predecessors(
&self.of_edge_kinds(edge_types),
start,
)
.map_err(ShortestPathError::from)
}
fn dijkstra_path(&self, start: NodeId, end: NodeId) -> Result<Option<Vec<NodeId>>> {
crate::core::ShortestPath::shortest_path_to(self, start, end)
.map(|found| found.map(|(route, _)| route))
.map_err(ShortestPathError::from)
}
fn dijkstra_path_by_types(
&self,
start: NodeId,
end: NodeId,
edge_types: &[&str],
) -> Result<Option<Vec<NodeId>>> {
crate::core::ShortestPath::shortest_path_to(&self.of_edge_kinds(edge_types), start, end)
.map(|found| found.map(|(route, _)| route))
.map_err(ShortestPathError::from)
}
}
#[cfg(test)]
#[allow(deprecated)]
#[cfg(feature = "hgraph")]
mod tests {
use super::*;
use crate::core::view::NodeId;
use crate::hgraph::h_edge::EdgeType;
use crate::hgraph::h_node::NodeType;
#[derive(Clone, Eq, PartialEq, Hash, Debug)]
struct TestNode(String);
impl NodeType for TestNode {
fn as_string(&self) -> String {
self.0.clone()
}
}
#[derive(Clone, Debug, Default)]
struct TestEdge(String);
impl EdgeType for TestEdge {
fn as_string(&self) -> String {
self.0.clone()
}
}
#[test]
fn test_dijkstra() {
let mut graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 4, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 3, TestEdge("bridge".to_string()))
.unwrap();
graph
.add_edge(n0, n2, 8, TestEdge("path".to_string()))
.unwrap();
let (distances, _) = graph.dijkstra(n0).unwrap();
assert_eq!(distances[&n0], 0);
assert_eq!(distances[&n1], 4);
assert_eq!(distances[&n2], 7); }
#[test]
fn test_dijkstra_by_types() {
let mut graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 4, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 3, TestEdge("bridge".to_string()))
.unwrap();
graph
.add_edge(n0, n2, 8, TestEdge("path".to_string()))
.unwrap();
let (distances, _) = graph.dijkstra_by_types(n0, &["road", "bridge"]).unwrap();
assert_eq!(distances[&n0], 0);
assert_eq!(distances[&n1], 4);
assert_eq!(distances[&n2], 7);
let (distances, _) = graph.dijkstra_by_types(n0, &["road"]).unwrap();
assert_eq!(distances[&n0], 0);
assert_eq!(distances[&n1], 4);
assert_eq!(distances.get(&n2), None); }
#[test]
fn test_dijkstra_path() {
let mut graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 4, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 3, TestEdge("bridge".to_string()))
.unwrap();
graph
.add_edge(n0, n2, 8, TestEdge("path".to_string()))
.unwrap();
let path = graph.dijkstra_path(n0, n2).unwrap();
assert_eq!(path, Some(vec![n0, n1, n2]));
}
#[test]
fn test_dijkstra_path_by_types() {
let mut graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let n2 = graph.add_node(TestNode("C".to_string()));
graph
.add_edge(n0, n1, 4, TestEdge("road".to_string()))
.unwrap();
graph
.add_edge(n1, n2, 3, TestEdge("bridge".to_string()))
.unwrap();
graph
.add_edge(n0, n2, 8, TestEdge("path".to_string()))
.unwrap();
let path = graph
.dijkstra_path_by_types(n0, n2, &["road", "bridge"])
.unwrap();
assert_eq!(path, Some(vec![n0, n1, n2]));
let path = graph.dijkstra_path_by_types(n0, n2, &["path"]).unwrap();
assert_eq!(path, Some(vec![n0, n2]));
let path = graph.dijkstra_path_by_types(n0, n2, &["road"]).unwrap();
assert_eq!(path, None); }
#[test]
fn test_unreachable_node() {
let mut graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
let (distances, _) = graph.dijkstra(n0).unwrap();
assert_eq!(distances[&n0], 0);
assert_eq!(distances.get(&n1), None);
let path = graph.dijkstra_path(n0, n1).unwrap();
assert_eq!(path, None);
let path = graph.dijkstra_path_by_types(n0, n1, &["road"]).unwrap();
assert_eq!(path, None);
}
#[test]
fn test_same_start_end() {
let mut graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
let n0 = graph.add_node(TestNode("A".to_string()));
let n1 = graph.add_node(TestNode("B".to_string()));
graph
.add_edge(n0, n1, 4, TestEdge("road".to_string()))
.unwrap();
let path = graph.dijkstra_path(n0, n0).unwrap();
assert_eq!(path, Some(vec![n0]));
let path = graph.dijkstra_path_by_types(n0, n0, &["road"]).unwrap();
assert_eq!(path, Some(vec![n0]));
}
#[test]
fn test_invalid_node() {
let graph = HeterogeneousGraph::<u32, TestNode, TestEdge>::new(true);
assert!(matches!(
graph.dijkstra(NodeId::from_index(0)),
Err(ShortestPathError::InvalidNodeReference(_))
));
assert!(matches!(
graph.dijkstra_path(NodeId::from_index(0), NodeId::from_index(1)),
Err(ShortestPathError::InvalidNodeReference(_))
));
assert!(matches!(
graph.dijkstra_path_by_types(NodeId::from_index(0), NodeId::from_index(1), &["road"]),
Err(ShortestPathError::InvalidNodeReference(_))
));
}
}