rustc_data_structures/graph/vec_graph/mod.rs
1use rustc_index::{Idx, IndexVec};
2
3use crate::graph::{DirectedGraph, NumEdges, Predecessors, Successors};
4
5#[cfg(test)]
6mod tests;
7
8/// A directed graph, efficient for cases where node indices are pre-existing.
9///
10/// If `BR` is true, the graph will store back-references, allowing you to get predecessors.
11pub struct VecGraph<N: Idx, const BR: bool = false> {
12 // This is basically a `HashMap<N, (Vec<N>, If<BR, Vec<N>>)>` -- a map from a node index, to
13 // a list of targets of outgoing edges and (if enabled) a list of sources of incoming edges.
14 //
15 // However, it is condensed into two arrays as an optimization.
16 //
17 // `node_starts[n]` is the start of the list of targets of outgoing edges for node `n`.
18 // So you can get node's successors with `edge_targets[node_starts[n]..node_starts[n + 1]]`.
19 //
20 // If `BR` is true (back references are enabled), then `node_starts[n + edge_count]` is the
21 // start of the list of *sources* of incoming edges. You can get predecessors of a node
22 // similarly to its successors but offsetting by `edge_count`. `edge_count` is
23 // `edge_targets.len()/2` (again, in case BR is true) because half of the vec is back refs.
24 //
25 // All of this might be confusing, so here is an example graph and its representation:
26 //
27 // n3 ----+
28 // ^ | (if BR = true)
29 // | v outgoing edges incoming edges
30 // n0 -> n1 -> n2 ______________ __________________
31 // / \ / \
32 // node indices[1]: n0, n1, n2, n3, n0, n1, n2, n3, n/a
33 // vec indices: n0, n1, n2, n3, n4, n5, n6, n7, n8
34 // node_starts: [0, 1, 3, 4 4, 4, 5, 7, 8]
35 // | | | | | | | | |
36 // | | +---+ +---+ | +---+ |
37 // | | | | | | |
38 // v v v v v v v
39 // edge_targets: [n1, n2, n3, n2 n0, n1, n3, n1]
40 // / \____/ | | \____/ \
41 // n0->n1 / | | \ n3<-n1
42 // / n3->n2 [2] n1<-n0 [2] \
43 // n1->n2, n1->n3 n2<-n1, n2<-n3
44 //
45 // The incoming edges are basically stored in the same way as outgoing edges, but offset and
46 // the graph they store is the inverse of the original. Last index in the `node_starts` array
47 // always points to one-past-the-end, so that we don't need to bound check `node_starts[n + 1]`
48 //
49 // [1]: "node indices" are the indices a user of `VecGraph` might use,
50 // note that they are different from "vec indices",
51 // which are the real indices you need to index `node_starts`
52 //
53 // [2]: Note that even though n2 also points to here,
54 // the next index also points here, so n2 has no
55 // successors (`edge_targets[3..3] = []`).
56 // Similarly with n0 and incoming edges
57 //
58 // If this is still confusing... then sorry :(
59 //
60 /// Indices into `edge_targets` that signify a start of list of edges.
61 node_starts: IndexVec<N, usize>,
62
63 /// Targets (or sources for back refs) of edges
64 edge_targets: Vec<N>,
65}
66
67impl<N: Idx + Ord, const BR: bool> VecGraph<N, BR> {
68 pub fn new(num_nodes: usize, mut edge_pairs: Vec<(N, N)>) -> Self {
69 let num_edges = edge_pairs.len();
70
71 let nodes_cap = match BR {
72 // +1 for special entry at the end, pointing one past the end of `edge_targets`
73 false => num_nodes + 1,
74 // *2 for back references
75 true => (num_nodes * 2) + 1,
76 };
77
78 let edges_cap = match BR {
79 false => num_edges,
80 // *2 for back references
81 true => num_edges * 2,
82 };
83
84 let mut node_starts = IndexVec::with_capacity(nodes_cap);
85 let mut edge_targets = Vec::with_capacity(edges_cap);
86
87 // Sort the edges by the source -- this is important.
88 edge_pairs.sort();
89
90 // Fill forward references
91 create_index(
92 num_nodes,
93 &mut edge_pairs.iter().map(|&(src, _)| src),
94 &mut edge_pairs.iter().map(|&(_, tgt)| tgt),
95 &mut edge_targets,
96 &mut node_starts,
97 );
98
99 // Fill back references
100 if BR {
101 // Pop the special "last" entry, it will be replaced by first back ref
102 node_starts.pop();
103
104 // Re-sort the edges so that they are sorted by target
105 edge_pairs.sort_by_key(|&(src, tgt)| (tgt, src));
106
107 create_index(
108 // Back essentially double the number of nodes
109 num_nodes * 2,
110 // NB: the source/target are switched here too
111 // NB: we double the key index, so that we can later use *2 to get the back references
112 &mut edge_pairs.iter().map(|&(_, tgt)| N::new(tgt.index() + num_nodes)),
113 &mut edge_pairs.iter().map(|&(src, _)| src),
114 &mut edge_targets,
115 &mut node_starts,
116 );
117 }
118
119 Self { node_starts, edge_targets }
120 }
121
122 /// Gets the successors for `source` as a slice.
123 pub fn successors(&self, source: N) -> &[N] {
124 assert!(source.index() < self.num_nodes());
125
126 let start_index = self.node_starts[source];
127 let end_index = self.node_starts[source.plus(1)];
128 &self.edge_targets[start_index..end_index]
129 }
130}
131
132impl<N: Idx + Ord> VecGraph<N, true> {
133 /// Gets the predecessors for `target` as a slice.
134 pub fn predecessors(&self, target: N) -> &[N] {
135 assert!(target.index() < self.num_nodes());
136
137 let target = N::new(target.index() + self.num_nodes());
138
139 let start_index = self.node_starts[target];
140 let end_index = self.node_starts[target.plus(1)];
141 &self.edge_targets[start_index..end_index]
142 }
143}
144
145/// Creates/initializes the index for the [`VecGraph`]. A helper for [`VecGraph::new`].
146///
147/// - `num_nodes` is the target number of nodes in the graph
148/// - `sorted_edge_sources` are the edge sources, sorted
149/// - `associated_edge_targets` are the edge *targets* in the same order as sources
150/// - `edge_targets` is the vec of targets to be extended
151/// - `node_starts` is the index to be filled
152fn create_index<N: Idx + Ord>(
153 num_nodes: usize,
154 sorted_edge_sources: &mut dyn Iterator<Item = N>,
155 associated_edge_targets: &mut dyn Iterator<Item = N>,
156 edge_targets: &mut Vec<N>,
157 node_starts: &mut IndexVec<N, usize>,
158) {
159 let offset = edge_targets.len();
160
161 // Store the *target* of each edge into `edge_targets`.
162 edge_targets.extend(associated_edge_targets);
163
164 // Create the *edge starts* array. We are iterating over the
165 // (sorted) edge pairs. We maintain the invariant that the
166 // length of the `node_starts` array is enough to store the
167 // current source node -- so when we see that the source node
168 // for an edge is greater than the current length, we grow the
169 // edge-starts array by just enough.
170 for (index, source) in sorted_edge_sources.enumerate() {
171 // If we have a list like `[(0, x), (2, y)]`:
172 //
173 // - Start out with `node_starts` of `[]`
174 // - Iterate to `(0, x)` at index 0:
175 // - Push one entry because `node_starts.len()` (0) is <= the source (0)
176 // - Leaving us with `node_starts` of `[0]`
177 // - Iterate to `(2, y)` at index 1:
178 // - Push one entry because `node_starts.len()` (1) is <= the source (2)
179 // - Push one entry because `node_starts.len()` (2) is <= the source (2)
180 // - Leaving us with `node_starts` of `[0, 1, 1]`
181 // - Loop terminates
182 while node_starts.len() <= source.index() {
183 node_starts.push(index + offset);
184 }
185 }
186
187 // Pad out the `node_starts` array so that it has `num_nodes +
188 // 1` entries. Continuing our example above, if `num_nodes` is
189 // be `3`, we would push one more index: `[0, 1, 1, 2]`.
190 //
191 // Interpretation of that vector:
192 //
193 // [0, 1, 1, 2]
194 // ---- range for N=2
195 // ---- range for N=1
196 // ---- range for N=0
197 while node_starts.len() <= num_nodes {
198 node_starts.push(edge_targets.len());
199 }
200
201 assert_eq!(node_starts.len(), num_nodes + 1);
202}
203
204impl<N: Idx, const BR: bool> DirectedGraph for VecGraph<N, BR> {
205 type Node = N;
206
207 fn num_nodes(&self) -> usize {
208 match BR {
209 false => self.node_starts.len() - 1,
210 // If back refs are enabled, half of the array is said back refs
211 true => (self.node_starts.len() - 1) / 2,
212 }
213 }
214}
215
216impl<N: Idx, const BR: bool> NumEdges for VecGraph<N, BR> {
217 fn num_edges(&self) -> usize {
218 match BR {
219 false => self.edge_targets.len(),
220 // If back refs are enabled, half of the array is reversed edges for them
221 true => self.edge_targets.len() / 2,
222 }
223 }
224}
225
226impl<N: Idx + Ord, const BR: bool> Successors for VecGraph<N, BR> {
227 fn successors(&self, node: N) -> impl Iterator<Item = Self::Node> {
228 self.successors(node).iter().cloned()
229 }
230}
231
232impl<N: Idx + Ord> Predecessors for VecGraph<N, true> {
233 fn predecessors(&self, node: Self::Node) -> impl Iterator<Item = Self::Node> {
234 self.predecessors(node).iter().cloned()
235 }
236}