#![warn(missing_docs)] //! This file implements a labelled graph. See the //! [trait][super::LabelGraph] for details. //! //! Since the method //! [`find_children_with_label`][super::LabelGraph::find_children_with_label] //! needs to be implemented efficiently, we store the mappings between //! labels and edges in both directions. #[allow(unused_imports)] use super::{Graph, GraphLabel, LabelExtGraph, LabelGraph}; #[allow(unused_imports)] use crate::error::Error; // We use BTreeMap and BTreeSet here as we need to exclude duplicate // edge sets, while an ordinary hashmap and hashset do not allow // hashing. use std::collections::{ btree_map::{Iter as MapIter, Keys}, btree_set::Iter, BTreeMap as Map, BTreeSet as Set, HashMap as HMap, }; #[derive(Debug, Clone, Default)] struct DLNode { by_target: Map>, by_label: Map>, flat: Vec<(T, usize)>, } impl DLNode { fn new( by_target: Map>, by_label: Map>, flat: Vec<(T, usize)>, ) -> Self { Self { by_target, by_label, flat, } } } /// Mapping a set of edges to an index of node. type EdgeMap = HMap, usize>; /// Double direction Labelled Graph. /// /// Each node is supposed to have a unique edge set. Constructing /// methods such as from the trait /// [`LabelExtGraph`][super::LabelExtGraph] already handles the /// elimination of duplication. #[derive(Debug, Clone)] pub struct DLGraph { nodes: Vec>, edges_table: EdgeMap, } impl DLGraph { #[inline] /// Return an empty graph. pub fn new() -> Self { Self { nodes: Vec::new(), edges_table: HMap::default(), } } } impl Default for DLGraph { #[inline] fn default() -> Self { Self::new() } } impl Graph for DLGraph { // Not using a boxed pointer is supposed to save some allocations. type Iter<'a> = std::iter::Copied>> where T: 'a; #[inline] fn is_empty(&self) -> bool { self.nodes.is_empty() } #[inline] fn nodes_len(&self) -> usize { self.nodes.len() } #[inline] fn children_of(&self, node_id: usize) -> Result, Error> { match self.nodes.get(node_id) { Some(node) => Ok(node.by_target.keys().copied()), None => Err(Error::IndexOutOfBounds(node_id, self.nodes.len())), } } #[inline] /// Return the number of "children" of a node, or an error if the /// node is not a member of the graph. /// /// This counts edges with different labels as different edges. fn degree(&self, node_id: usize) -> Result { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| node.flat.len()) } #[inline] fn is_empty_node(&self, node_id: usize) -> Result { self.nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len())) .map(|node| node.flat.is_empty()) } fn has_edge(&self, source: usize, target: usize) -> Result { match self.nodes.get(source) { Some(source_node) => { if self.nodes.get(target).is_none() { return Err(Error::IndexOutOfBounds(target, self.nodes.len())); } Ok(source_node.by_target.contains_key(&target)) } None => Err(Error::IndexOutOfBounds(source, self.nodes.len())), } } } /// A delegation of iterators. /// /// This is used to avoid a boxed pointer to an iterator. #[derive(Default, Debug)] pub struct LabelIndexIter<'a> { iter: Option>>, } impl<'a> Iterator for LabelIndexIter<'a> { type Item = usize; #[inline] fn next(&mut self) -> Option { self.iter.as_mut().and_then(|iterator| iterator.next()) } #[inline] fn size_hint(&self) -> (usize, Option) { match &self.iter { Some(iter) => iter.size_hint(), None => (0, Some(0)), } } } impl<'a> ExactSizeIterator for LabelIndexIter<'a> { #[inline] fn len(&self) -> usize { match &self.iter { Some(iter) => iter.len(), None => 0, } } } impl<'a> LabelIndexIter<'a> { fn new(iter: std::iter::Copied>) -> Self { let iter = Some(iter); Self { iter } } } // A convenience method impl<'a> From<&'a Set> for LabelIndexIter<'a> { fn from(set: &'a Set) -> Self { Self::new(set.iter().copied()) } } #[derive(Debug)] /// A delegation of iterators. /// /// This is used to avoid a boxed pointer to an iterator. pub struct LabelIter<'a, T> { iter: MapIter<'a, T, Set>, } impl<'a, T> ExactSizeIterator for LabelIter<'a, T> { #[inline] fn len(&self) -> usize { self.iter.len() } } impl<'a, T> LabelIter<'a, T> { fn new(iter: MapIter<'a, T, Set>) -> Self { Self { iter } } } impl<'a, T> Iterator for LabelIter<'a, T> { type Item = (&'a T, LabelIndexIter<'a>); #[inline] fn next(&mut self) -> Option { self.iter.next().map(|(label, set)| (label, set.into())) } #[inline] fn size_hint(&self) -> (usize, Option) { self.iter.size_hint() } } impl LabelGraph for DLGraph { type Iter<'a> = LabelIndexIter<'a> where T: 'a; type LabelIter<'a> = LabelIter<'a,T> where T: 'a; fn edge_label(&self, source: usize, target: usize) -> Result, Error> { if self.has_edge(source, target)? { Ok(self .nodes .get(source) .unwrap() .by_target .get(&target) .unwrap() .iter() .copied() .collect()) } else { Ok(Vec::new()) } } fn find_children_with_label( &self, node_id: usize, label: &T, ) -> Result<>::Iter<'_>, Error> { match self .nodes .get(node_id) .ok_or(Error::IndexOutOfBounds(node_id, self.nodes.len()))? .by_label .get(label) { Some(set) => Ok(set.into()), None => Ok(Default::default()), } } #[inline] fn labels_of(&self, node_id: usize) -> Result, Error> { match self.nodes.get(node_id) { Some(node) => Ok(Self::LabelIter::new(node.by_label.iter())), None => Err(Error::IndexOutOfBounds(node_id, self.nodes.len())), } } } impl LabelExtGraph for DLGraph { fn extend(&mut self, edges: impl IntoIterator) -> Result { let mut by_target: Map> = Map::default(); let mut by_label: Map> = Map::default(); let mut flat = Vec::new(); let mut edges_set = Set::new(); for (label, to) in edges { if !self.has_node(to) { return Err(Error::IndexOutOfBounds(to, self.nodes.len())); } edges_set.insert((label, to)); if let Some(set) = by_target.get(&to) { if !set.contains(&label) { flat.push((label, to)); by_target.get_mut(&to).unwrap().insert(label); by_label .entry(label) .or_insert_with(Default::default) .insert(to); } } else { flat.push((label, to)); by_target .entry(to) .or_insert_with(Default::default) .insert(label); by_label .entry(label) .or_insert_with(Default::default) .insert(to); } } match self.edges_table.get(&edges_set) { Some(old_index) => Ok(*old_index), None => { let new_node = DLNode::new(by_target, by_label, flat); let new_index = self.nodes_len(); self.edges_table.insert(edges_set, new_index); self.nodes.push(new_node); Ok(new_index) } } } } #[cfg(test)] mod label_test { use super::*; macro_rules! set { () => { Set::::default() }; ($($num:literal),*) => { { let mut set: Set = Set::default(); $(set.insert($num);)* set } }; } macro_rules! map { () => { Map::>::default() }; ($(($key:literal, $value:expr)),*) => { { let mut map: Map> = Map::default(); $(map.insert($key, $value);)* map } }; } #[test] fn test_graph_apis() -> Result<(), Error> { let mut graph: DLGraph = Default::default(); // testing empty graph assert!(graph.is_empty()); // testing adding an empty node assert_eq!(graph.extend(std::iter::empty())?, 0); // testing nodes_len assert_eq!(graph.nodes_len(), 1); // testing extension assert_eq!(graph.extend([(0, 0)].iter().copied())?, 1); assert_eq!(graph.extend([(1, 0), (1, 1)].iter().copied())?, 2); assert_eq!(graph.extend([(3, 0), (3, 2)].iter().copied())?, 3); assert_eq!(graph.extend([(1, 1), (1, 2)].iter().copied())?, 4); assert_eq!(graph.extend([(2, 1), (3, 2), (2, 3)].iter().copied())?, 5); // testing adding a duplicated edge set assert_eq!(graph.extend([(2, 1), (2, 3), (3, 2)].iter().copied())?, 5); assert_eq!(graph.extend([(3, 2), (3, 0)].iter().copied())?, 3); let graph = graph; // ensuring the correct length assert_eq!(graph.nodes_len(), 6); // testing children_of assert_eq!(graph.children_of(5)?.collect::>(), set!(1, 3, 2)); // testing find_children_with_label assert_eq!( graph.find_children_with_label(5, &2)?.collect::>(), set!(1, 3) ); // testing edge_label assert_eq!( graph.edge_label(5, 2)?.into_iter().collect::>(), set!(3) ); assert!(matches!( graph.edge_label(6, 2), Err(Error::IndexOutOfBounds(6, 6)) )); // testing degree assert_eq!(graph.degree(4)?, 2); // testing is_empty_node assert!(graph.is_empty_node(0)?); assert!(!graph.is_empty_node(1)?); // testing has_edge assert!(graph.has_edge(3, 2)?); assert!(!graph.has_edge(3, 1)?); assert!(matches!( graph.has_edge(3, 6), Err(Error::IndexOutOfBounds(6, 6)) )); // testing labels_of let mut label_map: Map> = Map::default(); for (label, children) in graph.labels_of(5)? { label_map.insert(*label, children.collect()); } let compare_map = map!((2, set!(1, 3)), (3, set!(2))); assert_eq!(label_map, compare_map); assert!(matches!( graph.labels_of(6), Err(Error::IndexOutOfBounds(6, 6)) )); Ok(()) } }