weavatrix_graph/algo/
feedback.rs1use crate::IndexGraphView;
2use crate::Vec;
3
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub struct FeedbackArcSet<Node, Edge> {
6 order: Vec<Node>,
7 edges: Vec<Edge>,
8}
9
10impl<Node, Edge> FeedbackArcSet<Node, Edge> {
11 #[must_use]
12 pub fn order(&self) -> &[Node] {
13 &self.order
14 }
15
16 #[must_use]
17 pub fn edges(&self) -> &[Edge] {
18 &self.edges
19 }
20}
21
22pub fn feedback_arc_set_heuristic<G>(graph: &G) -> FeedbackArcSet<G::Node, G::Edge>
28where
29 G: IndexGraphView,
30{
31 let bound = graph.node_bound();
32 let mut nodes = vec![None; bound];
33 let mut outgoing = vec![Vec::new(); bound];
34 let mut incoming = vec![Vec::new(); bound];
35 for node in graph.node_indices() {
36 nodes[G::node_slot(node)] = Some(node);
37 }
38 for (_, endpoints) in graph.edge_references() {
39 let source = G::node_slot(endpoints.source());
40 let target = G::node_slot(endpoints.target());
41 outgoing[source].push(target);
42 incoming[target].push(source);
43 }
44 let active = nodes.iter().map(Option::is_some).collect::<Vec<_>>();
45 let mut state = OrderingState::new(active, outgoing, incoming);
46 let mut left = Vec::new();
47 let mut right = Vec::new();
48 while state.remaining > 0 {
49 let previous = state.remaining;
50 while let Some(node) = state.pop_sink() {
51 right.push(node);
52 state.remove(node);
53 }
54 while let Some(node) = state.pop_source() {
55 left.push(node);
56 state.remove(node);
57 }
58 if let Some(node) = state.pop_delta() {
59 left.push(node);
60 state.remove(node);
61 }
62 if state.remaining == previous {
63 break;
64 }
65 }
66 right.reverse();
67 left.extend(right);
68 let mut position = vec![usize::MAX; bound];
69 for (index, &node) in left.iter().enumerate() {
70 position[node] = index;
71 }
72 let mut edges = graph
73 .edge_references()
74 .filter_map(|(edge, endpoints)| {
75 (position[G::node_slot(endpoints.source())]
76 >= position[G::node_slot(endpoints.target())])
77 .then_some(edge)
78 })
79 .collect::<Vec<_>>();
80 edges.sort_unstable_by_key(|edge| G::edge_slot(*edge));
81 let order = left.into_iter().filter_map(|slot| nodes[slot]).collect();
82 FeedbackArcSet { order, edges }
83}
84
85struct OrderingState {
86 active: Vec<bool>,
87 outgoing: Vec<Vec<usize>>,
88 incoming: Vec<Vec<usize>>,
89 out_degree: Vec<usize>,
90 in_degree: Vec<usize>,
91 stamps: Vec<u64>,
92 sinks: Vec<(u64, usize)>,
93 sources: Vec<(u64, usize)>,
94 positive: Vec<Vec<(u64, usize)>>,
95 negative: Vec<Vec<(u64, usize)>>,
96 clock: u64,
97 remaining: usize,
98}
99
100impl OrderingState {
101 fn new(active: Vec<bool>, outgoing: Vec<Vec<usize>>, incoming: Vec<Vec<usize>>) -> Self {
102 let out_degree = outgoing.iter().map(Vec::len).collect();
103 let in_degree = incoming.iter().map(Vec::len).collect();
104 let remaining = active.iter().filter(|active| **active).count();
105 let bound = active.len();
106 let mut state = Self {
107 active,
108 outgoing,
109 incoming,
110 out_degree,
111 in_degree,
112 stamps: vec![0; bound],
113 sinks: Vec::new(),
114 sources: Vec::new(),
115 positive: Vec::new(),
116 negative: Vec::new(),
117 clock: 0,
118 remaining,
119 };
120 for node in 0..state.active.len() {
121 state.refresh(node);
122 }
123 state
124 }
125
126 fn pop_sink(&mut self) -> Option<usize> {
127 while let Some((stamp, node)) = self.sinks.pop() {
128 if self.active[node] && self.out_degree[node] == 0 && self.stamps[node] == stamp {
129 return Some(node);
130 }
131 }
132 None
133 }
134
135 fn pop_source(&mut self) -> Option<usize> {
136 while let Some((stamp, node)) = self.sources.pop() {
137 if self.active[node]
138 && self.in_degree[node] == 0
139 && self.out_degree[node] > 0
140 && self.stamps[node] == stamp
141 {
142 return Some(node);
143 }
144 }
145 None
146 }
147
148 fn pop_delta(&mut self) -> Option<usize> {
149 while !self.positive.is_empty() {
150 let index = self.positive.len() - 1;
151 let delta = isize::try_from(index).unwrap_or(isize::MAX);
152 let bucket = &mut self.positive[index];
153 while let Some((stamp, node)) = bucket.pop() {
154 if self.active[node]
155 && self.in_degree[node] > 0
156 && self.out_degree[node] > 0
157 && signed(self.out_degree[node]) - signed(self.in_degree[node]) == delta
158 && self.stamps[node] == stamp
159 {
160 return Some(node);
161 }
162 }
163 self.positive.pop();
164 }
165 for index in 0..self.negative.len() {
166 while let Some((stamp, node)) = self.negative[index].pop() {
167 let delta = -isize::try_from(index + 1).unwrap_or(isize::MAX);
168 if self.active[node]
169 && self.in_degree[node] > 0
170 && self.out_degree[node] > 0
171 && self.delta(node) == delta
172 && self.stamps[node] == stamp
173 {
174 return Some(node);
175 }
176 }
177 }
178 None
179 }
180
181 fn remove(&mut self, node: usize) {
182 self.active[node] = false;
183 self.remaining = self.remaining.saturating_sub(1);
184 for index in 0..self.outgoing[node].len() {
185 let target = self.outgoing[node][index];
186 if self.active[target] {
187 self.in_degree[target] = self.in_degree[target].saturating_sub(1);
188 self.refresh(target);
189 }
190 }
191 for index in 0..self.incoming[node].len() {
192 let source = self.incoming[node][index];
193 if self.active[source] {
194 self.out_degree[source] = self.out_degree[source].saturating_sub(1);
195 self.refresh(source);
196 }
197 }
198 }
199
200 fn refresh(&mut self, node: usize) {
201 if !self.active[node] {
202 return;
203 }
204 self.clock = self.clock.saturating_add(1);
205 self.stamps[node] = self.clock;
206 if self.out_degree[node] == 0 {
207 self.sinks.push((self.clock, node));
208 } else if self.in_degree[node] == 0 {
209 self.sources.push((self.clock, node));
210 } else {
211 let delta = self.delta(node);
212 if delta >= 0 {
213 let index = delta.unsigned_abs();
214 if self.positive.len() <= index {
215 self.positive.resize_with(index + 1, Vec::new);
216 }
217 self.positive[index].push((self.clock, node));
218 } else {
219 let index = delta.unsigned_abs() - 1;
220 if self.negative.len() <= index {
221 self.negative.resize_with(index + 1, Vec::new);
222 }
223 self.negative[index].push((self.clock, node));
224 }
225 }
226 }
227
228 fn delta(&self, node: usize) -> isize {
229 signed(self.out_degree[node]) - signed(self.in_degree[node])
230 }
231}
232
233fn signed(value: usize) -> isize {
234 isize::try_from(value).unwrap_or(isize::MAX)
235}