diff options
Diffstat (limited to 'graph/src/labelled.rs')
-rw-r--r-- | graph/src/labelled.rs | 879 |
1 files changed, 0 insertions, 879 deletions
diff --git a/graph/src/labelled.rs b/graph/src/labelled.rs deleted file mode 100644 index 6341787..0000000 --- a/graph/src/labelled.rs +++ /dev/null @@ -1,879 +0,0 @@ -#![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. - -use std::ops::{Deref, DerefMut}; - -use crate::{builder::Builder, error::Error, Graph, GraphLabel, LabelExtGraph, LabelGraph}; - -// 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)] -struct DLNode<T: GraphLabel> { - by_target: Map<usize, Set<T>>, - by_label: Map<T, Set<usize>>, - flat: Vec<(T, usize)>, -} - -impl<T: GraphLabel> Default for DLNode<T> { - fn default() -> Self { - Self { - by_target: Default::default(), - by_label: Default::default(), - flat: Default::default(), - } - } -} - -impl<T: GraphLabel> DLNode<T> { - fn new( - by_target: Map<usize, Set<T>>, - by_label: Map<T, Set<usize>>, - flat: Vec<(T, usize)>, - ) -> Self { - Self { - by_target, - by_label, - flat, - } - } -} - -/// Mapping a set of edges to an index of node. -type EdgeMap<T> = HMap<Set<(T, usize)>, 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<T: GraphLabel> { - nodes: Vec<DLNode<T>>, - edges_table: EdgeMap<T>, -} - -impl<T: GraphLabel> DLGraph<T> { - #[inline] - /// Return an empty graph. - pub fn new() -> Self { - Self { - nodes: Vec::new(), - edges_table: HMap::default(), - } - } - - /// Return a builder. - #[inline] - pub fn builder(self) -> DLGBuilder<T> { - DLGBuilder(self) - } - - /// Return a builder from a reference. - #[inline] - pub fn builder_ref(&self) -> DLGBuilder<T> { - DLGBuilder(self.clone()) - } -} - -impl<T: GraphLabel> Default for DLGraph<T> { - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl<T: GraphLabel> Graph for DLGraph<T> { - // Not using a boxed pointer is supposed to save some allocations. - type Iter<'a> = std::iter::Copied<Keys<'a, usize, Set<T>>> 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<Self::Iter<'_>, 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<usize, Error> { - 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<bool, Error> { - 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<bool, Error> { - match self.nodes.get(source) { - Some(source_node) => { - if self.nodes.get(target).is_none() { - Err(Error::IndexOutOfBounds(target, self.nodes.len())) - } else { - Ok(source_node.by_target.contains_key(&target) - && !source_node.by_target.get(&target).unwrap().is_empty()) - } - } - None => Err(Error::IndexOutOfBounds(source, self.nodes.len())), - } - } - - fn print_viz(&self, filename: &str) -> Result<(), std::io::Error> { - let preamble = "digraph nfa { - fontname=\"Helvetica,Arial,sans-serif\" - node [fontname=\"Helvetica,Arial,sans-serif\"] - edge [fontname=\"Helvetica,Arial,sans-serif\"] - rankdir=LR;\n"; - - let mut post = String::new(); - - use std::fmt::Write as FWrite; - - for (source, target) in self.edges() { - for label in self.edge_label(source, target).unwrap() { - let _ = writeln!(post, " {source} -> {target} [label = \"{label}\"]"); - } - } - - post.push_str("}\n"); - - let result = format!("{preamble}{post}"); - - if std::fs::metadata(filename).is_ok() { - std::fs::remove_file(filename)?; - } - - let mut file = std::fs::File::options() - .write(true) - .create(true) - .open(filename)?; - - use std::io::Write; - - file.write_all(result.as_bytes()) - } - - #[inline] - fn replace_by_builder(&mut self, builder: impl Builder<Result = Self>) { - let new_graph = builder.build(); - - self.nodes = new_graph.nodes; - self.edges_table = new_graph.edges_table; - } -} - -/// A delegation of iterators. -/// -/// This is used to avoid a boxed pointer to an iterator. -#[derive(Default, Debug, Clone)] -pub struct LabelIndexIter<'a> { - iter: Option<std::iter::Copied<Iter<'a, usize>>>, -} - -impl<'a> Iterator for LabelIndexIter<'a> { - type Item = usize; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - self.iter.as_mut().and_then(|iterator| iterator.next()) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - 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<Iter<'a, usize>>) -> Self { - let iter = Some(iter); - Self { iter } - } -} - -// A convenience method -impl<'a> From<&'a Set<usize>> for LabelIndexIter<'a> { - fn from(set: &'a Set<usize>) -> 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<usize>>, -} - -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<usize>>) -> Self { - Self { iter } - } -} - -impl<'a, T> Iterator for LabelIter<'a, T> { - type Item = (&'a T, LabelIndexIter<'a>); - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - self.iter.next().map(|(label, set)| (label, set.into())) - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - self.iter.size_hint() - } -} - -/// This is used to avoid a boxed pointer to an iterator. -#[derive(Debug)] -pub struct EdgeLabelIter<'a, T> { - iter: Option<Iter<'a, T>>, -} - -impl<'a, T> Default for EdgeLabelIter<'a, T> { - #[inline] - fn default() -> Self { - Self { iter: None } - } -} - -impl<'a, T: Copy + Clone> ExactSizeIterator for EdgeLabelIter<'a, T> { - #[inline] - fn len(&self) -> usize { - if let Some(iter) = &self.iter { - iter.len() - } else { - 0 - } - } -} - -impl<'a, T: Copy + Clone> EdgeLabelIter<'a, T> { - fn new(iter: Iter<'a, T>) -> Self { - Self { iter: Some(iter) } - } -} - -impl<'a, T: Copy + Clone> Iterator for EdgeLabelIter<'a, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option<Self::Item> { - if let Some(iter) = &mut self.iter { - iter.next().copied() - } else { - None - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option<usize>) { - if let Some(iter) = &self.iter { - iter.size_hint() - } else { - (0, Some(0)) - } - } -} - -impl<T: GraphLabel> LabelGraph<T> for DLGraph<T> { - type Iter<'a> = LabelIndexIter<'a> where T: 'a; - - type LabelIter<'a> = LabelIter<'a,T> where T: 'a; - - type EdgeLabelIter<'a> = EdgeLabelIter<'a,T> - where - Self: 'a, - T: 'a + Copy + Clone; - - fn edge_label(&self, source: usize, target: usize) -> Result<Self::EdgeLabelIter<'_>, Error> { - if self.has_edge(source, target)? { - Ok(EdgeLabelIter::new( - self.nodes - .get(source) - .unwrap() - .by_target - .get(&target) - .unwrap() - .iter(), - )) - } else { - Ok(Default::default()) - } - } - - fn find_children_with_label( - &self, - node_id: usize, - label: &T, - ) -> Result<<Self as LabelGraph<T>>::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<Self::LabelIter<'_>, 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())), - } - } - - fn has_edge_label(&self, node_id: usize, label: &T, target: usize) -> Result<bool, Error> { - let nodes_len = self.nodes.len(); - - match self.nodes.get(node_id) { - Some(node) => { - if target >= nodes_len { - return Err(Error::IndexOutOfBounds(target, nodes_len)); - } - - Ok(node - .by_target - .get(&target) - .map(|labels| labels.contains(label)) - .unwrap_or(false)) - } - None => Err(Error::IndexOutOfBounds(node_id, nodes_len)), - } - } -} - -impl<T: GraphLabel> LabelExtGraph<T> for DLGraph<T> { - fn extend(&mut self, edges: impl IntoIterator<Item = (T, usize)>) -> Result<usize, Error> { - let mut by_target: Map<usize, Set<T>> = Map::default(); - let mut by_label: Map<T, Set<usize>> = 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) - } - } - } -} - -// Builder for this implementation of graph - -/// A builder for labelled adjacency doubly linked graphs. -#[derive(Debug, Clone)] -pub struct DLGBuilder<T: GraphLabel>(DLGraph<T>); - -impl<T: GraphLabel> Default for DLGBuilder<T> { - fn default() -> Self { - Self(Default::default()) - } -} - -impl<T: GraphLabel> Deref for DLGBuilder<T> { - type Target = DLGraph<T>; - - fn deref(&self) -> &Self::Target { - &self.0 - } -} - -impl<T: GraphLabel> DerefMut for DLGBuilder<T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 - } -} - -impl<T: GraphLabel> Builder for DLGBuilder<T> { - type Label = T; - - type Result = DLGraph<T>; - - #[inline] - fn with_capacity(size: usize) -> Self { - Self(DLGraph { - nodes: Vec::with_capacity(size), - edges_table: Default::default(), - }) - } - - #[inline] - fn add_vertex(&mut self) -> usize { - self.nodes.push(DLNode::default()); - self.nodes.len() - 1 - } - - #[inline] - fn add_vertices(&mut self, n: usize) { - self.nodes - .extend(std::iter::repeat_with(Default::default).take(n)); - } - - fn add_edge(&mut self, source: usize, target: usize, label: Self::Label) -> Result<(), Error> { - let nodes_len = self.nodes.len(); - - let source_node = self - .nodes - .get_mut(source) - .ok_or(Error::IndexOutOfBounds(source, nodes_len))?; - - if !(0..nodes_len).contains(&target) { - return Err(Error::IndexOutOfBounds(target, nodes_len)); - } - - let new_edge_p = !matches!( - source_node - .by_target - .get(&target) - .map(|set| set.contains(&label)), - Some(true) - ); - - if new_edge_p { - let old_children_set: Set<(T, usize)> = source_node.flat.iter().copied().collect(); - - source_node - .by_target - .entry(target) - .or_insert_with(Set::default) - .insert(label); - - source_node - .by_label - .entry(label) - .or_insert_with(Set::default) - .insert(target); - - source_node.flat.push((label, target)); - - let new_children_set: Set<(T, usize)> = source_node.flat.iter().copied().collect(); - - // When source_node is in use, we cannot borrow self - // mutably again, so we move the following two statements - // to after all uses of source_node. - - self.edges_table.remove(&old_children_set); - - self.edges_table.insert(new_children_set, source); - } - - Ok(()) - } - - fn remove_edge<F>(&mut self, source: usize, target: usize, predicate: F) -> Result<(), Error> - where - F: Fn(Self::Label) -> bool, - { - let nodes_len = self.nodes.len(); - - let source_node = self - .nodes - .get_mut(source) - .ok_or(Error::IndexOutOfBounds(source, nodes_len))?; - - if !(0..nodes_len).contains(&target) { - return Err(Error::IndexOutOfBounds(target, nodes_len)); - } - - if let Some(target_labels_set) = source_node.by_target.get(&target) { - let mut to_remove = Vec::new(); - - for label in target_labels_set.iter() { - if predicate(*label) { - to_remove.push(*label); - } - } - - if !to_remove.is_empty() { - let old_children_set: Set<(T, usize)> = source_node.flat.iter().copied().collect(); - - for label in to_remove.iter().copied() { - // This must be "Some", as the outer "if" checks - source_node - .by_target - .get_mut(&target) - .map(|set| set.remove(&label)); - - source_node - .by_label - .get_mut(&label) - .map(|set| set.remove(&target)); - - source_node.flat.retain(|(child_label, child_target)| { - (*child_label, *child_target) != (label, target) - }); - } - - // If after removal no labels remain for the target, - // we remove the edge entirely, to avoid false empty - // edges. - - if let Some(set) = source_node.by_target.get(&target) { - if set.is_empty() { - source_node.by_target.remove(&target); - } - } - - for label in to_remove.iter() { - if let Some(set) = source_node.by_label.get(label) { - if set.is_empty() { - source_node.by_label.remove(label); - } - } - } - - // if source_node.flat.is_empty() { - // source_node.by_target.remove(&target); - - // for label in to_remove.iter() { - // source_node.by_label.remove(label); - // } - // } - - let new_children_set: Set<(T, usize)> = source_node.flat.iter().copied().collect(); - - // When source_node is in use, we cannot borrow self - // mutably again, so we move the following two - // statements to after all uses of source_node. - - self.edges_table.remove(&old_children_set); - - self.edges_table.insert(new_children_set, source); - } - } - - Ok(()) - } - - fn build_ref(&self) -> Self::Result { - self.0.clone() - } - - fn build(self) -> Self::Result { - self.0 - } -} - -#[cfg(test)] -mod label_test { - use super::*; - - macro_rules! set { - () => { Set::<usize>::default() }; - ($($num:literal),*) => { - { - let mut set: Set<usize> = Set::default(); - $(set.insert($num);)* - set - } - }; - } - - macro_rules! map { - () => { Map::<usize, Set<usize>>::default() }; - ($(($key:literal, $value:expr)),*) => { - { - let mut map: Map<usize, Set<usize>> = Map::default(); - $(map.insert($key, $value);)* - map - } - }; - } - - #[test] - fn test_graph_apis() -> Result<(), Error> { - let mut graph: DLGraph<usize> = 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<_>>(), set!(1, 3, 2)); - - // testing find_children_with_label - assert_eq!( - graph.find_children_with_label(5, &2)?.collect::<Set<_>>(), - set!(1, 3) - ); - - // testing edge_label - assert_eq!(graph.edge_label(5, 2)?.collect::<Set<_>>(), 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<usize, Set<usize>> = 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(()) - } -} - -#[cfg(test)] -mod test_dlgraph_builder { - use super::*; - - #[test] - fn test_builder() -> Result<(), Box<dyn std::error::Error>> { - let mut builder = DLGBuilder::<usize>::default(); - - // Add five nodes - builder.add_vertex(); - builder.add_vertex(); - builder.add_vertex(); - builder.add_vertex(); - builder.add_vertex(); - - println!("five empty nodes: {builder:?}"); - - // Link each node to its successor and link the last node with - // the first one to form a cycle. - for i in 0..5 { - builder.add_edge(i, if i < 4 { i + 1 } else { 0 }, i)?; - } - - println!("a cycle of five nodes: {builder:?}"); - - // Remove the link from the last node to the first node. - builder.remove_edge(4, 0, |_| true)?; - - println!("a line of five nodes: {builder:?}"); - - // build a graph - - let graph = builder.build(); - - println!("final graph: {graph:?}"); - - Ok(()) - } - - #[test] - fn test_errors() -> Result<(), Box<dyn std::error::Error>> { - let mut builder = DLGBuilder::default(); - - // Add five nodes - builder.add_vertex(); - builder.add_vertex(); - builder.add_vertex(); - builder.add_vertex(); - builder.add_vertex(); - - println!("five empty nodes: {builder:?}"); - - // Errors in add_edge - - println!(); - println!("Testing errors in add_edge:"); - println!(); - - assert!(matches!( - builder.add_edge(0, 5, 0), - Err(Error::IndexOutOfBounds(5, 5)) - )); - - println!("Right error for an index out of bounds as the target"); - - assert!(matches!( - builder.add_edge(10, 5, 0), - Err(Error::IndexOutOfBounds(10, 5)) - )); - - println!("Right error for an index out of bounds as the source"); - - assert!(matches!( - builder.add_edge(10, 50, 0), - Err(Error::IndexOutOfBounds(10, 5)) - )); - - println!("Right error for both indices out of bounds"); - - // Errors in remove_edge - - println!(); - println!("Testing errors in remove_edge:"); - println!(); - - assert!(matches!( - builder.remove_edge(0, 5, |_| true), - Err(Error::IndexOutOfBounds(5, 5)) - )); - - println!("Right error for an index out of bounds as the target"); - - assert!(matches!( - builder.remove_edge(10, 5, |_| true), - Err(Error::IndexOutOfBounds(10, 5)) - )); - - println!("Right error for an index out of bounds as the source"); - - assert!(matches!( - builder.remove_edge(10, 50, |_| true), - Err(Error::IndexOutOfBounds(10, 5)) - )); - - println!("Right error for both indices out of bounds"); - - assert!(matches!(builder.remove_edge(0, 1, |_| true), Ok(()),)); - - println!("No errors for removing a non-existing edge"); - - println!(); - - let graph = builder.build(); - - println!("final graph: {graph:?}"); - - Ok(()) - } -} |