1use roaring::RoaringBitmap;
11
12use crate::csr::{Csr, CsrError};
13use crate::ordinal::{Ordinal, to_usize};
14
15#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
17pub enum ClosureError {
18 #[error("the is-a hierarchy has a cycle through {} node(s), for example {first}", .members.len())]
20 Cycle {
21 first: Ordinal,
23 members: Vec<Ordinal>,
25 },
26 #[error(transparent)]
28 Csr(#[from] CsrError),
29}
30
31#[derive(Debug, Clone, PartialEq, Eq)]
33pub struct Closure {
34 ancestors: Vec<RoaringBitmap>,
35 descendants: Vec<RoaringBitmap>,
36}
37
38static EMPTY: std::sync::LazyLock<RoaringBitmap> = std::sync::LazyLock::new(RoaringBitmap::new);
39
40impl Closure {
41 pub fn compute(is_a: &Csr) -> Result<Self, ClosureError> {
47 let parents = is_a;
48 let children = is_a.transpose()?;
49 let order = topological_order(parents, &children)?;
50 let nodes = to_usize(parents.nodes());
51 Ok(Self {
52 ancestors: sweep(parents, order.iter(), nodes),
53 descendants: sweep(&children, order.iter().rev(), nodes),
54 })
55 }
56
57 #[must_use]
59 pub fn from_parts(ancestors: Vec<RoaringBitmap>, descendants: Vec<RoaringBitmap>) -> Self {
60 Self {
61 ancestors,
62 descendants,
63 }
64 }
65
66 #[must_use]
68 pub fn size_in_bytes(&self) -> usize {
69 crate::footprint::bitmaps(&self.ancestors)
70 .saturating_add(crate::footprint::bitmaps(&self.descendants))
71 }
72
73 #[must_use]
78 pub fn ancestors_size_in_bytes(&self) -> usize {
79 crate::footprint::bitmaps(&self.ancestors)
80 }
81
82 #[must_use]
84 pub fn nodes(&self) -> u32 {
85 u32::try_from(self.ancestors.len()).unwrap_or(u32::MAX)
86 }
87
88 #[must_use]
90 pub fn ancestors(&self, node: Ordinal) -> &RoaringBitmap {
91 self.ancestors
92 .get(node.as_usize())
93 .unwrap_or_else(|| &*EMPTY)
94 }
95
96 #[must_use]
98 pub fn descendants(&self, node: Ordinal) -> &RoaringBitmap {
99 self.descendants
100 .get(node.as_usize())
101 .unwrap_or_else(|| &*EMPTY)
102 }
103
104 #[must_use]
106 pub fn descendants_or_self(&self, node: Ordinal) -> RoaringBitmap {
107 let mut set = self.descendants(node).clone();
108 set.insert(node.index());
109 set
110 }
111
112 #[must_use]
114 pub fn ancestors_or_self(&self, node: Ordinal) -> RoaringBitmap {
115 let mut set = self.ancestors(node).clone();
116 set.insert(node.index());
117 set
118 }
119
120 #[must_use]
122 pub fn is_ancestor(&self, ancestor: Ordinal, node: Ordinal) -> bool {
123 self.ancestors(node).contains(ancestor.index())
124 }
125
126 #[must_use]
128 pub fn ancestor_sets(&self) -> &[RoaringBitmap] {
129 &self.ancestors
130 }
131
132 #[must_use]
134 pub fn descendant_sets(&self) -> &[RoaringBitmap] {
135 &self.descendants
136 }
137}
138
139fn sweep<'a>(
145 edges: &Csr,
146 order: impl Iterator<Item = &'a Ordinal>,
147 nodes: usize,
148) -> Vec<RoaringBitmap> {
149 let mut sets: Vec<RoaringBitmap> = vec![RoaringBitmap::new(); nodes];
150 for node in order {
151 let mut set = RoaringBitmap::new();
152 for neighbour in edges.neighbours(*node) {
153 set.insert(*neighbour);
154 if let Some(reached) = sets.get(to_usize(*neighbour)) {
155 set |= reached;
156 }
157 }
158 if let Some(slot) = sets.get_mut(node.as_usize()) {
159 *slot = set;
160 }
161 }
162 sets
163}
164
165fn topological_order(parents: &Csr, children: &Csr) -> Result<Vec<Ordinal>, ClosureError> {
167 let nodes = parents.nodes();
168 let mut remaining: Vec<u32> = (0..nodes)
169 .map(|n| u32::try_from(parents.neighbours(Ordinal::new(n)).len()).unwrap_or(u32::MAX))
170 .collect();
171 let mut ready: Vec<Ordinal> = (0..nodes)
172 .filter(|n| remaining.get(to_usize(*n)) == Some(&0))
173 .map(Ordinal::new)
174 .collect();
175 let mut order = Vec::with_capacity(to_usize(nodes));
176 while let Some(node) = ready.pop() {
177 order.push(node);
178 for child in children.neighbours(node) {
179 if let Some(count) = remaining.get_mut(to_usize(*child)) {
180 *count = count.saturating_sub(1);
181 if *count == 0 {
182 ready.push(Ordinal::new(*child));
183 }
184 }
185 }
186 }
187 if order.len() != to_usize(nodes) {
188 let members: Vec<Ordinal> = (0..nodes)
189 .filter(|n| remaining.get(to_usize(*n)).is_some_and(|c| *c > 0))
190 .map(Ordinal::new)
191 .collect();
192 let first = members.first().copied().unwrap_or(Ordinal::new(0));
193 return Err(ClosureError::Cycle { first, members });
194 }
195 Ok(order)
196}
197
198#[cfg(test)]
199mod tests {
200 use super::{Closure, ClosureError};
201 use crate::csr::Csr;
202 use crate::ordinal::Ordinal;
203
204 fn o(i: u32) -> Ordinal {
205 Ordinal::new(i)
206 }
207
208 fn diamond() -> Closure {
210 let is_a = Csr::build(4, [(o(1), o(0)), (o(2), o(0)), (o(3), o(1)), (o(3), o(2))])
211 .expect("builds");
212 Closure::compute(&is_a).expect("acyclic")
213 }
214
215 #[test]
216 fn ancestors_and_descendants_are_transitive_and_inverse() {
217 let closure = diamond();
218 assert_eq!(
219 closure.ancestors(o(3)).iter().collect::<Vec<_>>(),
220 vec![0, 1, 2]
221 );
222 assert_eq!(
223 closure.descendants(o(0)).iter().collect::<Vec<_>>(),
224 vec![1, 2, 3]
225 );
226 assert_eq!(
227 closure.descendants(o(1)).iter().collect::<Vec<_>>(),
228 vec![3]
229 );
230 assert!(closure.ancestors(o(0)).is_empty());
231 assert!(closure.descendants(o(3)).is_empty());
232 assert!(closure.is_ancestor(o(0), o(3)));
233 assert!(!closure.is_ancestor(o(3), o(0)));
234 assert!(closure.descendants_or_self(o(3)).contains(3));
235 assert_eq!(closure.ancestors_or_self(o(3)).len(), 4);
236 }
237
238 #[test]
239 fn a_cycle_is_refused() {
240 let is_a = Csr::build(3, [(o(0), o(1)), (o(1), o(2)), (o(2), o(0))]).expect("builds");
241 match Closure::compute(&is_a) {
242 Err(ClosureError::Cycle { members, .. }) => assert_eq!(members.len(), 3),
243 other => panic!("expected a cycle, got {other:?}"),
244 }
245 }
246
247 #[test]
248 fn unknown_nodes_have_empty_sets() {
249 let closure = diamond();
250 assert!(closure.ancestors(o(99)).is_empty());
251 assert!(closure.descendants(o(99)).is_empty());
252 }
253}