1 use std::{
2     collections::{HashMap, VecDeque},
3     ops::{Index, IndexMut},
4 };
5 
6 use crate::{
7     graph::{GraphIndex, NodeIndex},
8     visit::{EdgeRef, GraphProp, IntoEdgeReferences},
9     Directed,
10 };
11 
12 use self::linked_list::{LinkedList, LinkedListEntry};
13 
14 /// \[Generic\] Finds a [feedback arc set]: a set of edges in the given directed graph, which when
15 /// removed, make the graph acyclic.
16 ///
17 /// Uses a [greedy heuristic algorithm] to select a small number of edges, but does not necessarily
18 /// find the minimum feedback arc set. Time complexity is roughly **O(|E|)** for an input graph with
19 /// edges **E**.
20 ///
21 /// Does not consider edge/node weights when selecting edges for the feedback arc set.
22 ///
23 /// Loops (edges to and from the same node) are always included in the returned set.
24 ///
25 /// # Example
26 ///
list_table_columns(GdaDataModel * data)27 /// ```
28 /// # #[cfg(feature = "stable_graph")] {
29 /// use petgraph::{
30 ///     algo::{greedy_feedback_arc_set, is_cyclic_directed},
31 ///     graph::EdgeIndex,
32 ///     stable_graph::StableGraph,
33 ///     visit::EdgeRef,
34 /// };
35 ///
36 /// let mut g: StableGraph<(), ()> = StableGraph::from_edges(&[
37 ///     (0, 1),
38 ///     (1, 2),
39 ///     (2, 3),
40 ///     (3, 4),
41 ///     (4, 5),
42 ///     (5, 0),
43 ///     (4, 1),
44 ///     (1, 3),
45 /// ]);
46 ///
47 /// assert!(is_cyclic_directed(&g));
48 ///
49 /// let fas: Vec<EdgeIndex> = greedy_feedback_arc_set(&g).map(|e| e.id()).collect();
50 ///
51 /// // Remove edges in feedback arc set from original graph
52 /// for edge_id in fas {
53 ///     g.remove_edge(edge_id);
54 /// }
55 ///
56 /// assert!(!is_cyclic_directed(&g));
57 /// # }
58 /// ```
59 ///
60 /// [feedback arc set]: https://en.wikipedia.org/wiki/Feedback_arc_set
61 /// [greedy heuristic algorithm]: https://doi.org/10.1016/0020-0190(93)90079-O
62 pub fn greedy_feedback_arc_set<G>(g: G) -> impl Iterator<Item = G::EdgeRef>
63 where
64     G: IntoEdgeReferences + GraphProp<EdgeType = Directed>,
65     G::NodeId: GraphIndex,
66     G: crate::visit::NodeCount,
67 {
68     let node_seq = good_node_sequence(g.edge_references().map(|e| {
69         (
70             NodeIndex::new(e.source().index()),
71             NodeIndex::new(e.target().index()),
72         )
73     }));
74 
75     g.edge_references()
76         .filter(move |e| node_seq[&e.source().index()] >= node_seq[&e.target().index()])
77 }
78 
79 fn good_node_sequence(
80     edge_refs: impl Iterator<Item = (NodeIndex<usize>, NodeIndex<usize>)>,
81 ) -> HashMap<usize, usize> {
82     let mut nodes = FasNodeContainer { nodes: Vec::new() };
83     let mut buckets = Buckets {
84         sinks_or_isolated: NodeLinkedList::new(),
85         sources: NodeLinkedList::new(),
86         bidirectional_pve_dd: Vec::new(),
87         bidirectional_nve_dd: Vec::new(),
88     };
89     // Lookup of node indices from input graph to indices into `nodes`
90     let mut graph_ix_lookup = HashMap::new();
91 
92     // Build node entries
93     for (from_g_ix, to_g_ix) in edge_refs {
94         let mut fas_node_entry = |g_ix: NodeIndex<usize>| -> FasNodeIndex {
95             match graph_ix_lookup.get(&g_ix) {
96                 Some(fas_ix) => *fas_ix,
97                 None => {
98                     let fas_ix = FasNodeIndex(nodes.nodes.len());
99 
100                     nodes.nodes.push(LinkedListEntry::new(FasNode {
101                         graph_ix: g_ix,
102                         out_edges: Vec::new(),
103                         in_edges: Vec::new(),
104                         out_degree: 0,
105                         in_degree: 0,
106                     }));
107 
108                     graph_ix_lookup.insert(g_ix, fas_ix);
109 
110                     fas_ix
111                 }
112             }
113         };
114 
115         let from_fas_ix = fas_node_entry(from_g_ix);
116         let to_fas_ix = fas_node_entry(to_g_ix);
117 
118         nodes[from_fas_ix].data().out_edges.push(to_fas_ix);
119         nodes[to_fas_ix].data().in_edges.push(from_fas_ix);
120     }
121 
122     // Set initial in/out-degrees
123     for entry in nodes.nodes.iter_mut() {
124         let node = entry.data();
125         node.out_degree = node.out_edges.len();
126         node.in_degree = node.in_edges.len();
127     }
128 
129     // Add nodes to initial lists
130     for i in 0..nodes.nodes.len() {
131         let fas_ix = FasNodeIndex(i);
132         buckets
133             .suitable_bucket(fas_ix, &mut nodes)
134             .push_front(fas_ix, &mut nodes);
135     }
136 
137     let mut s_1 = VecDeque::new();
138     let mut s_2 = VecDeque::new();
139 
140     loop {
141         let mut some_moved = false;
142 
143         while let Some(sink_fas_ix) = buckets.sinks_or_isolated.pop(&mut nodes) {
144             some_moved = true;
145             buckets.update_neighbour_node_buckets(sink_fas_ix, &mut nodes);
146             s_2.push_front(nodes[sink_fas_ix].data().graph_ix);
147         }
148 
149         while let Some(source_fas_ix) = buckets.sources.pop(&mut nodes) {
150             some_moved = true;
151             buckets.update_neighbour_node_buckets(source_fas_ix, &mut nodes);
152             s_1.push_back(nodes[source_fas_ix].data().graph_ix);
153         }
154 
155         if let Some(list) = buckets
156             .bidirectional_pve_dd
157             .iter_mut()
158             .rev()
159             .chain(buckets.bidirectional_nve_dd.iter_mut())
160             .find(|b| b.start.is_some())
161         {
162             let highest_dd_fas_ix = list.pop(&mut nodes).unwrap();
163             some_moved = true;
164             buckets.update_neighbour_node_buckets(highest_dd_fas_ix, &mut nodes);
165             s_1.push_back(nodes[highest_dd_fas_ix].data().graph_ix);
166 
167             Buckets::trim_bucket_list(&mut buckets.bidirectional_pve_dd);
168             Buckets::trim_bucket_list(&mut buckets.bidirectional_nve_dd);
169         }
170 
171         if !some_moved {
172             break;
173         }
174     }
175 
176     s_1.into_iter()
177         .chain(s_2)
178         .enumerate()
179         .map(|(seq_order, node_index)| (node_index.index(), seq_order))
180         .collect()
181 }
182 
183 type NodeLinkedList = LinkedList<FasNode, FasNodeContainer, FasNodeIndex>;
184 
185 #[derive(Debug)]
186 struct FasNodeContainer {
187     nodes: Vec<LinkedListEntry<FasNode, FasNodeIndex>>,
188 }
189 
190 impl Index<FasNodeIndex> for FasNodeContainer {
191     type Output = LinkedListEntry<FasNode, FasNodeIndex>;
192 
193     fn index(&self, index: FasNodeIndex) -> &Self::Output {
194         &self.nodes[index.0]
195     }
196 }
197 
198 impl IndexMut<FasNodeIndex> for FasNodeContainer {
199     fn index_mut(&mut self, index: FasNodeIndex) -> &mut Self::Output {
200         &mut self.nodes[index.0]
201     }
202 }
203 
204 #[derive(Debug)]
205 struct Buckets {
206     sinks_or_isolated: NodeLinkedList,
207     sources: NodeLinkedList,
208     /// Bidirectional nodes with positive-or-0 delta degree
209     bidirectional_pve_dd: Vec<NodeLinkedList>,
210     /// Bidirectional nodes with negative delta degree (index 0 is -1 dd, 1 is -2 etc)
211     bidirectional_nve_dd: Vec<NodeLinkedList>,
212 }
213 
214 #[derive(Clone, Copy, PartialEq, Debug)]
215 struct FasNodeIndex(usize);
216 
217 /// Represents a node from the input graph, tracking its current delta degree
218 #[derive(Debug)]
219 struct FasNode {
220     /// Node index in input graph.
221     graph_ix: NodeIndex<usize>,
222 
223     /// All outward edges from this node (not removed during processing)
224     out_edges: Vec<FasNodeIndex>,
225 
226     /// All inward edges from this node (not removed during processing)
227     in_edges: Vec<FasNodeIndex>,
228 
229     /// Current out-degree of this node (decremented during processing as connected nodes are
230     /// removed)
231     out_degree: usize,
232 
233     /// Current in-degree of this node (decremented during processing as connected nodes are
234     /// removed)
235     in_degree: usize,
236 }
237 
238 impl Buckets {
239     fn suitable_bucket(
240         &mut self,
241         ix: FasNodeIndex,
242         nodes: &mut FasNodeContainer,
243     ) -> &mut NodeLinkedList {
244         let node = nodes[ix].data();
245 
246         if node.out_degree == 0 {
247             &mut self.sinks_or_isolated
248         } else if node.in_degree == 0 {
249             &mut self.sources
250         } else {
251             let delta_degree = node.out_degree as isize - node.in_degree as isize;
252 
253             if delta_degree >= 0 {
254                 let bucket_ix = delta_degree as usize;
255 
256                 if self.bidirectional_pve_dd.len() <= bucket_ix {
257                     self.bidirectional_pve_dd
258                         .resize_with(bucket_ix + 1, NodeLinkedList::new);
259                 }
260 
261                 &mut self.bidirectional_pve_dd[bucket_ix]
262             } else {
263                 let bucket_ix = (-delta_degree - 1) as usize;
264 
265                 if self.bidirectional_nve_dd.len() <= bucket_ix {
266                     self.bidirectional_nve_dd
267                         .resize_with(bucket_ix + 1, NodeLinkedList::new);
268                 }
269 
270                 &mut self.bidirectional_nve_dd[bucket_ix]
271             }
272         }
273     }
274 
275     fn update_neighbour_node_buckets(&mut self, ix: FasNodeIndex, nodes: &mut FasNodeContainer) {
276         for i in 0..nodes[ix].data().out_edges.len() {
277             let out_ix = nodes[ix].data().out_edges[i];
278 
279             if out_ix == ix {
280                 continue;
281             }
282 
283             // Ignore nodes which have already been moved to the good sequence
284             if !nodes[out_ix].is_in_list() {
285                 continue;
286             }
287 
288             self.suitable_bucket(out_ix, nodes).remove(out_ix, nodes);
289 
290             // Other node has lost an in-edge; reduce in-degree by 1
291             nodes[out_ix].data().in_degree -= 1;
292 
293             self.suitable_bucket(out_ix, nodes)
294                 .push_front(out_ix, nodes);
295         }
296 
297         for i in 0..nodes[ix].data().in_edges.len() {
298             let in_ix = nodes[ix].data().in_edges[i];
299 
300             if in_ix == ix {
301                 continue;
302             }
303 
304             // Ignore nodes which have already been moved to the good sequence
305             if !nodes[in_ix].is_in_list() {
306                 continue;
307             }
308 
309             self.suitable_bucket(in_ix, nodes).remove(in_ix, nodes);
310 
311             // Other node has lost an out-edge; reduce out-degree by 1
312             nodes[in_ix].data().out_degree -= 1;
313 
314             self.suitable_bucket(in_ix, nodes).push_front(in_ix, nodes);
315         }
316     }
317 
318     fn trim_bucket_list(list: &mut Vec<NodeLinkedList>) {
319         let trunc_len = if let Some(highest_populated_index) =
320             (0..list.len()).rev().find(|i| list[*i].start.is_some())
321         {
322             highest_populated_index + 1
323         } else {
324             0
325         };
326 
327         list.truncate(trunc_len);
328     }
329 }
330 
331 mod linked_list {
332     use std::{marker::PhantomData, ops::IndexMut};
333 
334     #[derive(PartialEq, Debug)]
335     pub struct LinkedList<Data, Container, Ix> {
336         pub start: Option<Ix>,
337         marker: PhantomData<(Data, Container)>,
338     }
339 
340     #[derive(Debug)]
341     pub struct LinkedListEntry<Data, Ix> {
342         pos: Option<LinkedListPosition<Ix>>,
343         data: Data,
344     }
345 
346     #[derive(Debug)]
347     struct LinkedListPosition<Ix> {
348         prev: Option<Ix>,
349         next: Option<Ix>,
350     }
351 
352     impl<Data, Ix> LinkedListEntry<Data, Ix> {
353         pub fn new(data: Data) -> Self {
354             LinkedListEntry { pos: None, data }
355         }
356 
357         pub fn data(&mut self) -> &mut Data {
358             &mut self.data
359         }
360 
361         pub fn is_in_list(&mut self) -> bool {
362             self.pos.is_some()
363         }
364 
365         fn pos_mut(&mut self) -> &mut LinkedListPosition<Ix> {
366             self.pos
367                 .as_mut()
368                 .expect("expected linked list entry to have populated position")
369         }
370     }
371 
372     impl<Data, Container, Ix> LinkedList<Data, Container, Ix>
373     where
374         Container: IndexMut<Ix, Output = LinkedListEntry<Data, Ix>>,
375         Ix: PartialEq + Copy,
376     {
377         pub fn new() -> Self {
378             LinkedList {
379                 start: None,
380                 marker: PhantomData,
381             }
382         }
383 
384         pub fn push_front(&mut self, push_ix: Ix, container: &mut Container) {
385             if let Some(start_ix) = self.start {
386                 let entry = &mut container[start_ix];
387                 entry.pos_mut().prev = Some(push_ix);
388             }
389 
390             let push_entry = &mut container[push_ix];
391             push_entry.pos = Some(LinkedListPosition {
392                 next: self.start,
393                 prev: None,
394             });
395 
396             self.start = Some(push_ix);
397         }
398 
399         pub fn pop(&mut self, container: &mut Container) -> Option<Ix> {
400             if let Some(remove_ix) = self.start {
401                 self.remove(remove_ix, container);
402                 Some(remove_ix)
403             } else {
404                 None
405             }
406         }
407 
408         /// `remove_ix` **must** be a member of the list headed by `self`
409         pub fn remove(&mut self, remove_ix: Ix, container: &mut Container) {
410             debug_assert!(
411                 self.to_vec(container).contains(&remove_ix),
412                 "node to remove should be member of current linked list"
413             );
414 
415             let remove_entry = &mut container[remove_ix];
416             let ll_entry = remove_entry.pos.take().unwrap();
417 
418             if let Some(prev_ix) = ll_entry.prev {
419                 let prev_node = &mut container[prev_ix];
420                 prev_node.pos_mut().next = ll_entry.next;
421             }
422 
423             if let Some(next_ix) = ll_entry.next {
424                 let next_node = &mut container[next_ix];
425                 next_node.pos_mut().prev = ll_entry.prev;
426             }
427 
428             // If the removed node was head of the list
429             if self.start == Some(remove_ix) {
430                 self.start = ll_entry.next;
431             }
432         }
433 
434         /// For debug purposes
435         fn to_vec(&self, container: &mut Container) -> Vec<Ix> {
436             let mut ixs = Vec::new();
437 
438             let mut node_ix = self.start;
439 
440             while let Some(n_ix) = node_ix {
441                 ixs.push(n_ix);
442 
443                 node_ix = container[n_ix].pos_mut().next;
444             }
445 
446             ixs
447         }
448     }
449 }
450