petgraph/acyclic/
order_map.rs

1//! A bijective map between node indices and a `TopologicalPosition`, to store
2//! the total topological order of the graph.
3//!
4//! This data structure is an implementation detail and is not exposed in the
5//! public API.
6use std::{collections::BTreeMap, fmt, ops::RangeBounds};
7
8use crate::{
9    algo::{toposort, Cycle},
10    visit::{GraphBase, IntoNeighborsDirected, IntoNodeIdentifiers, NodeIndexable, Visitable},
11};
12
13/// A position in the topological order of the graph.
14///
15/// This defines a total order over the set of nodes in the graph.
16///
17/// Note that the positions of all nodes in a graph may not form a contiguous
18/// interval.
19#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Default)]
20#[repr(transparent)]
21pub struct TopologicalPosition(pub(super) usize);
22
23/// A bijective map between node indices and their position in a topological order.
24///
25/// Note that this map does not check for injectivity or surjectivity, this
26/// must be enforced by the user. Map mutations that invalidate these properties
27/// are allowed to make it easy to perform batch modifications that temporarily
28/// break the invariants.
29#[derive(Clone)]
30pub(super) struct OrderMap<N> {
31    /// Map topological position to node index.
32    pos_to_node: BTreeMap<TopologicalPosition, N>,
33    /// The inverse of `pos_to_node`, i.e. map node indices to their position.
34    ///
35    /// This is a Vec, relying on `N: NodeIndexable` for indexing.
36    node_to_pos: Vec<TopologicalPosition>,
37}
38
39impl<N> Default for OrderMap<N> {
40    fn default() -> Self {
41        Self {
42            pos_to_node: Default::default(),
43            node_to_pos: Default::default(),
44        }
45    }
46}
47
48impl<N: fmt::Debug> fmt::Debug for OrderMap<N> {
49    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
50        f.debug_struct("OrderMap")
51            .field("order", &self.pos_to_node)
52            .finish()
53    }
54}
55
56impl<N: Copy> OrderMap<N> {
57    pub(super) fn try_from_graph<G>(graph: G) -> Result<Self, Cycle<G::NodeId>>
58    where
59        G: NodeIndexable<NodeId = N> + IntoNeighborsDirected + IntoNodeIdentifiers + Visitable,
60    {
61        // Compute the topological order.
62        let topo_vec = toposort(graph, None)?;
63
64        // Create the two map directions.
65        let mut pos_to_node = BTreeMap::new();
66        let mut node_to_pos = vec![TopologicalPosition::default(); graph.node_bound()];
67
68        // Populate the maps.
69        for (i, &id) in topo_vec.iter().enumerate() {
70            let pos = TopologicalPosition(i);
71            pos_to_node.insert(pos, id);
72            node_to_pos[graph.to_index(id)] = pos;
73        }
74
75        Ok(Self {
76            pos_to_node,
77            node_to_pos,
78        })
79    }
80
81    pub(super) fn with_capacity(nodes: usize) -> Self {
82        Self {
83            pos_to_node: BTreeMap::new(),
84            node_to_pos: Vec::with_capacity(nodes),
85        }
86    }
87
88    /// Map a node to its position in the topological order.
89    ///
90    /// Panics if the node index is out of bounds.
91    pub(super) fn get_position(
92        &self,
93        id: N,
94        graph: impl NodeIndexable<NodeId = N>,
95    ) -> TopologicalPosition {
96        let idx = graph.to_index(id);
97        assert!(idx < self.node_to_pos.len());
98        self.node_to_pos[idx]
99    }
100
101    /// Map a position in the topological order to a node, if it exists.
102    pub(super) fn at_position(&self, pos: TopologicalPosition) -> Option<N> {
103        self.pos_to_node.get(&pos).copied()
104    }
105
106    /// Get an iterator over the nodes, ordered by their position.
107    pub(super) fn nodes_iter(&self) -> impl Iterator<Item = N> + '_ {
108        self.pos_to_node.values().copied()
109    }
110
111    /// Get an iterator over the nodes within the range of positions.
112    pub(super) fn range(
113        &self,
114        range: impl RangeBounds<TopologicalPosition>,
115    ) -> impl Iterator<Item = N> + '_ {
116        self.pos_to_node.range(range).map(|(_, &n)| n)
117    }
118
119    /// Add a node to the order map and assign it an arbitrary position.
120    ///
121    /// Return the position of the new node.
122    pub(super) fn add_node(
123        &mut self,
124        id: N,
125        graph: impl NodeIndexable<NodeId = N>,
126    ) -> TopologicalPosition {
127        // The position and node index
128        let new_pos = self
129            .pos_to_node
130            .iter()
131            .next_back()
132            .map(|(TopologicalPosition(idx), _)| TopologicalPosition(idx + 1))
133            .unwrap_or_default();
134        let idx = graph.to_index(id);
135
136        // Make sure the order_inv is large enough.
137        if idx >= self.node_to_pos.len() {
138            self.node_to_pos
139                .resize(graph.node_bound(), TopologicalPosition::default());
140        }
141
142        // Insert both map directions.
143        self.pos_to_node.insert(new_pos, id);
144        self.node_to_pos[idx] = new_pos;
145
146        new_pos
147    }
148
149    /// Remove a node from the order map.
150    ///
151    /// Panics if the node index is out of bounds.
152    pub(super) fn remove_node(&mut self, id: N, graph: impl NodeIndexable<NodeId = N>) {
153        let idx = graph.to_index(id);
154        assert!(idx < self.node_to_pos.len());
155
156        let pos = self.node_to_pos[idx];
157        self.node_to_pos[idx] = TopologicalPosition::default();
158        self.pos_to_node.remove(&pos);
159    }
160
161    /// Set the position of a node.
162    ///
163    /// Panics if the node index is out of bounds.
164    pub(super) fn set_position(
165        &mut self,
166        id: N,
167        pos: TopologicalPosition,
168        graph: impl NodeIndexable<NodeId = N>,
169    ) {
170        let idx = graph.to_index(id);
171        assert!(idx < self.node_to_pos.len());
172
173        self.pos_to_node.insert(pos, id);
174        self.node_to_pos[idx] = pos;
175    }
176}
177
178impl<G: Visitable> super::Acyclic<G> {
179    /// Get the position of a node in the topological sort.
180    ///
181    /// Panics if the node index is out of bounds.
182    pub fn get_position<'a>(&'a self, id: G::NodeId) -> TopologicalPosition
183    where
184        &'a G: NodeIndexable + GraphBase<NodeId = G::NodeId>,
185    {
186        self.order_map.get_position(id, &self.graph)
187    }
188
189    /// Get the node at a given position in the topological sort, if it exists.
190    pub fn at_position(&self, pos: TopologicalPosition) -> Option<G::NodeId> {
191        self.order_map.at_position(pos)
192    }
193}