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