mesh_sieve/algs/
reduction.rs1use crate::mesh_error::MeshSieveError;
2use crate::topology::sieve::{Sieve, SieveRef};
3
4trait ReachRow {
10 fn with_size(n: usize) -> Self;
12 fn set(&mut self, i: usize);
14 fn get(&self, i: usize) -> bool;
16 fn or_assign_from(&mut self, other: &Self);
18}
19
20#[derive(Clone)]
22struct DenseRow {
23 words: Vec<u64>,
24}
25
26impl ReachRow for DenseRow {
27 #[inline]
28 fn with_size(n: usize) -> Self {
29 Self {
30 words: vec![0; n.div_ceil(64)],
31 }
32 }
33 #[inline]
34 fn set(&mut self, i: usize) {
35 self.words[i / 64] |= 1u64 << (i % 64);
36 }
37 #[inline]
38 fn get(&self, i: usize) -> bool {
39 (self.words[i / 64] >> (i % 64)) & 1 == 1
40 }
41 #[inline]
42 fn or_assign_from(&mut self, other: &Self) {
43 for (a, b) in self.words.iter_mut().zip(&other.words) {
44 *a |= *b;
45 }
46 }
47}
48
49#[cfg(feature = "sparse-bitset")]
50mod sparse {
51 use super::ReachRow;
52 use std::collections::BTreeMap;
53
54 pub struct SparseRow {
56 chunks: BTreeMap<usize, u64>,
57 }
58
59 impl ReachRow for SparseRow {
60 fn with_size(_n: usize) -> Self {
61 Self {
62 chunks: BTreeMap::new(),
63 }
64 }
65 #[inline]
66 fn set(&mut self, i: usize) {
67 let w = i / 64;
68 let b = 1u64 << (i % 64);
69 *self.chunks.entry(w).or_insert(0) |= b;
70 }
71 #[inline]
72 fn get(&self, i: usize) -> bool {
73 let w = i / 64;
74 let b = 1u64 << (i % 64);
75 self.chunks.get(&w).map_or(0, |&x| x) & b != 0
76 }
77 fn or_assign_from(&mut self, other: &Self) {
78 for (&w, &bits) in &other.chunks {
79 *self.chunks.entry(w).or_insert(0) |= bits;
80 }
81 }
82 }
83}
84
85#[cfg(feature = "sparse-bitset")]
86type Row = sparse::SparseRow;
87#[cfg(not(feature = "sparse-bitset"))]
88type Row = DenseRow;
89
90#[inline]
91#[cfg(any(
92 debug_assertions,
93 feature = "strict-invariants",
94 feature = "check-invariants"
95))]
96fn is_acyclic_by_chart<S>(s: &S, chart: &[S::Point]) -> bool
97where
98 S: Sieve + SieveRef,
99{
100 use std::collections::HashMap;
101 let mut idx = HashMap::with_capacity(chart.len());
102 for (i, &p) in chart.iter().enumerate() {
103 idx.insert(p, i);
104 }
105 for &u in chart {
106 let ui = idx[&u];
107 for (v, _) in s.cone_ref(u) {
108 if idx[&v] <= ui {
109 return false;
110 }
111 }
112 }
113 true
114}
115
116pub fn transitive_reduction_dag<S>(s: &mut S) -> Result<usize, MeshSieveError>
128where
129 S: Sieve + SieveRef,
130 S::Point: Copy + Eq + std::hash::Hash + Ord + std::fmt::Debug,
131{
132 use std::collections::HashMap;
133 let chart = s.chart_points()?;
134 #[cfg(any(
135 debug_assertions,
136 feature = "strict-invariants",
137 feature = "check-invariants"
138 ))]
139 debug_assert!(is_acyclic_by_chart(s, &chart), "chart must be acyclic");
140 let n = chart.len();
141 let mut idx = HashMap::with_capacity(n);
142 for (i, &p) in chart.iter().enumerate() {
143 idx.insert(p, i);
144 }
145 let mut reach: Vec<Row> = (0..n).map(|_| Row::with_size(n)).collect();
146 for &u in chart.iter().rev() {
147 let ui = idx[&u];
148 for (v, _) in s.cone_ref(u) {
149 let vi = idx[&v];
150 let (row_u, row_v) = {
152 let (pre, suf) = reach.split_at_mut(vi);
153 (&mut pre[ui], &suf[0])
154 };
155 row_u.or_assign_from(row_v);
156 row_u.set(vi);
157 }
158 }
159 let mut to_remove = Vec::new();
160 for &u in &chart {
161 let mut neigh: Vec<_> = SieveRef::cone_points(s, u).collect();
162 neigh.sort_unstable();
163 neigh.dedup();
164 for &v in &neigh {
165 let vi = idx[&v];
166 let implied = neigh
167 .iter()
168 .copied()
169 .any(|w| w != v && reach[idx[&w]].get(vi));
170 if implied {
171 to_remove.push((u, v));
172 }
173 }
174 }
175 to_remove.sort_unstable_by_key(|&(u, v)| (u, v));
176 for (u, v) in &to_remove {
177 let _ = s.remove_arrow(*u, *v);
178 }
179 Ok(to_remove.len())
180}
181
182pub fn transitive_closure_edges<S>(s: &mut S) -> Result<Vec<(S::Point, S::Point)>, MeshSieveError>
194where
195 S: Sieve + SieveRef,
196 S::Point: Copy + Eq + std::hash::Hash + Ord + std::fmt::Debug,
197{
198 use std::collections::{HashMap, HashSet};
199 let chart = s.chart_points()?;
200 #[cfg(any(
201 debug_assertions,
202 feature = "strict-invariants",
203 feature = "check-invariants"
204 ))]
205 debug_assert!(is_acyclic_by_chart(s, &chart), "chart must be acyclic");
206 let n = chart.len();
207 let mut idx = HashMap::with_capacity(n);
208 for (i, &p) in chart.iter().enumerate() {
209 idx.insert(p, i);
210 }
211 let mut reach: Vec<Row> = (0..n).map(|_| Row::with_size(n)).collect();
212 let mut direct = HashSet::new();
213 for &u in chart.iter().rev() {
214 let ui = idx[&u];
215 let mut neigh: Vec<_> = s.cone_ref(u).map(|(v, _)| idx[&v]).collect();
216 neigh.sort_unstable();
217 neigh.dedup();
218 for &vi in &neigh {
219 direct.insert((ui, vi));
220 let (row_u, row_v) = {
221 let (pre, suf) = reach.split_at_mut(vi);
222 (&mut pre[ui], &suf[0])
223 };
224 row_u.or_assign_from(row_v);
225 row_u.set(vi);
226 }
227 }
228 let mut out = Vec::new();
229 for (ui, &u) in chart.iter().enumerate() {
230 for vi in 0..n {
231 if ui == vi {
232 continue;
233 }
234 if reach[ui].get(vi) && !direct.contains(&(ui, vi)) {
235 out.push((u, chart[vi]));
236 }
237 }
238 }
239 Ok(out)
240}
241
242#[derive(Debug, Clone, Copy, Eq, PartialEq)]
244pub struct ReductionStats {
245 pub removed: usize,
246 pub remaining: usize,
247}
248
249pub fn transitive_reduction_dag_stats<S>(s: &mut S) -> Result<ReductionStats, MeshSieveError>
251where
252 S: Sieve + SieveRef,
253 S::Point: Copy + Eq + std::hash::Hash + Ord + std::fmt::Debug,
254{
255 fn arrow_count<S2>(s: &S2) -> usize
256 where
257 S2: Sieve + SieveRef,
258 S2::Point: Copy + Eq + std::hash::Hash + Ord + std::fmt::Debug,
259 {
260 s.base_points()
261 .map(|u| SieveRef::cone_points(s, u).count())
262 .sum()
263 }
264
265 let before = arrow_count(s);
266 let removed = transitive_reduction_dag(s)?;
267 let after = arrow_count(s);
268 debug_assert_eq!(before - removed, after);
269 Ok(ReductionStats {
270 removed,
271 remaining: after,
272 })
273}