OR-Tools  9.6
topologicalsorter.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 // This file provides topologically sorted traversal of the nodes of a directed
15 // acyclic graph (DAG) with up to INT_MAX nodes.
16 // It sorts ancestor nodes before their descendants. Multi-arcs are fine.
17 //
18 // If your graph is not a DAG and you're reading this, you are probably
19 // looking for ortools/graph/strongly_connected_components.h which does
20 // the topological decomposition of a directed graph.
21 //
22 // USAGE:
23 // - If performance matters, use FastTopologicalSort().
24 // - If your nodes are non-integers, or you need to break topological ties by
25 // node index (like "stable_sort"), use one of the DenseIntTopologicalSort()
26 // or TopologicalSort variants (see below).
27 // - If you need more control (cycle extraction?), or a step-by-step topological
28 // sort, see the TopologicalSorter classes below.
29 
30 #ifndef UTIL_GRAPH_TOPOLOGICALSORTER_H__
31 #define UTIL_GRAPH_TOPOLOGICALSORTER_H__
32 
33 #include <functional>
34 #include <limits>
35 #include <queue>
36 #include <type_traits>
37 #include <utility>
38 #include <vector>
39 
40 #include "absl/base/attributes.h"
41 #include "absl/container/flat_hash_map.h"
42 #include "absl/container/inlined_vector.h"
43 #include "absl/status/status.h"
44 #include "absl/status/statusor.h"
45 #include "absl/strings/str_format.h"
47 #include "ortools/base/logging.h"
48 #include "ortools/base/macros.h"
49 #include "ortools/base/map_util.h"
51 #include "ortools/base/stl_util.h"
52 #include "ortools/graph/graph.h"
53 
54 namespace util {
55 namespace graph {
56 
57 // This is the recommended API when performance matters. It's also very simple.
58 // AdjacencyList is any type that lets you iterate over the neighbors of
59 // node with the [] operator, for example vector<vector<int>> or util::Graph.
60 //
61 // If you don't already have an adjacency list representation, build one using
62 // StaticGraph<> in ./graph.h: FastTopologicalSort() can take any such graph as
63 // input.
64 //
65 // ERRORS: returns InvalidArgumentError if the input is broken (negative or
66 // out-of-bounds integers) or if the graph is cyclic. In the latter case, the
67 // error message will contain "cycle". Note that if cycles may occur in your
68 // input, you can probably assume that your input isn't broken, and thus rely
69 // on failures to detect that the graph is cyclic.
70 //
71 // TIE BREAKING: the returned topological order is deterministic and fixed, and
72 // corresponds to iterating on nodes in a LIFO (Breadth-first) order.
73 //
74 // Benchmark: gpaste/6147236302946304, 4-10x faster than util_graph::TopoSort().
75 //
76 // EXAMPLES:
77 // std::vector<std::vector<int>> adj = {{..}, {..}, ..};
78 // ASSIGN_OR_RETURN(std::vector<int> topo_order, FastTopologicalSort(adj));
79 //
80 // or
81 // std::vector<pair<int, int>> arcs = {{.., ..}, ..., };
82 // ASSIGN_OR_RETURN(
83 // std::vector<int> topo_order,
84 // FastTopologicalSort(util::StaticGraph<>::FromArcs(num_nodes, arcs)));
85 //
86 template <class AdjacencyLists> // vector<vector<int>>, util::StaticGraph<>, ..
87 absl::StatusOr<std::vector<int>> FastTopologicalSort(const AdjacencyLists& adj);
88 
89 // Finds a cycle in the directed graph given as argument: nodes are dense
90 // integers in 0..num_nodes-1, and (directed) arcs are pairs of nodes
91 // {from, to}.
92 // The returned cycle is a list of nodes that form a cycle, eg. {1, 4, 3}
93 // if the cycle 1->4->3->1 exists.
94 // If the graph is acyclic, returns an empty vector.
95 template <class AdjacencyLists> // vector<vector<int>>, util::StaticGraph<>, ..
96 absl::StatusOr<std::vector<int>> FindCycleInGraph(const AdjacencyLists& adj);
97 
98 } // namespace graph
99 
100 // [Stable]TopologicalSort[OrDie]:
101 //
102 // These variants are much slower than FastTopologicalSort(), but support
103 // non-integer (or integer, but sparse) nodes.
104 // Note that if performance matters, you're probably better off building your
105 // own mapping from node to dense index with a flat_hash_map and calling
106 // FastTopologicalSort().
107 
108 // Returns true if the graph was a DAG, and outputs the topological order in
109 // "topological_order". Returns false if the graph is cyclic, and outputs the
110 // detected cycle in "cycle".
111 template <typename T>
112 ABSL_MUST_USE_RESULT bool TopologicalSort(
113  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
114  std::vector<T>* topological_order);
115 // Override of the above that outputs the detected cycle.
116 template <typename T>
117 ABSL_MUST_USE_RESULT bool TopologicalSort(
118  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
119  std::vector<T>* topological_order, std::vector<T>* cycle);
120 // OrDie() variant of the above.
121 template <typename T>
122 std::vector<T> TopologicalSortOrDie(const std::vector<T>& nodes,
123  const std::vector<std::pair<T, T>>& arcs);
124 // The "Stable" variants are a little slower but preserve the input order of
125 // nodes, if possible. More precisely, the returned topological order will be
126 // the lexicographically minimal valid order, where "lexicographic" applies to
127 // the indices of the nodes.
128 template <typename T>
129 ABSL_MUST_USE_RESULT bool StableTopologicalSort(
130  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
131  std::vector<T>* topological_order);
132 // Override of the above that outputs the detected cycle.
133 template <typename T>
134 ABSL_MUST_USE_RESULT bool StableTopologicalSort(
135  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
136  std::vector<T>* topological_order, std::vector<T>* cycle);
137 // OrDie() variant of the above.
138 template <typename T>
139 std::vector<T> StableTopologicalSortOrDie(
140  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs);
141 
142 // ______________________ END OF THE RECOMMENDED API ___________________________
143 
144 // DEPRECATED. Use util::graph::FindCycleInGraph() directly.
145 inline ABSL_MUST_USE_RESULT std::vector<int> FindCycleInDenseIntGraph(
146  int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
148  util::StaticGraph<>::FromArcs(num_nodes, arcs))
149  .value();
150 }
151 
152 // DEPRECATED: DenseInt[Stable]TopologicalSort[OrDie].
153 // Kept here for legacy reasons, but most new users should use
154 // FastTopologicalSort():
155 // - If your input is a list of edges, build you own StaticGraph<> (see
156 // ./graph.h) and pass it to FastTopologicalSort().
157 // - If you need the "stable sort" bit, contact viger@ and/or or-core-team@
158 // to see if they can create FastStableTopologicalSort().
159 ABSL_MUST_USE_RESULT inline bool DenseIntTopologicalSort(
160  int num_nodes, const std::vector<std::pair<int, int>>& arcs,
161  std::vector<int>* topological_order);
162 inline std::vector<int> DenseIntStableTopologicalSortOrDie(
163  int num_nodes, const std::vector<std::pair<int, int>>& arcs);
164 ABSL_MUST_USE_RESULT inline bool DenseIntStableTopologicalSort(
165  int num_nodes, const std::vector<std::pair<int, int>>& arcs,
166  std::vector<int>* topological_order);
167 inline std::vector<int> DenseIntTopologicalSortOrDie(
168  int num_nodes, const std::vector<std::pair<int, int>>& arcs);
169 
170 namespace internal {
171 // Internal wrapper around the *TopologicalSort classes.
172 template <typename T, typename Sorter>
173 ABSL_MUST_USE_RESULT bool RunTopologicalSorter(
174  Sorter* sorter, const std::vector<std::pair<T, T>>& arcs,
175  std::vector<T>* topological_order_or_cycle);
176 
177 // Do not use the templated class directly, instead use one of the
178 // typedefs DenseIntTopologicalSorter or DenseIntStableTopologicalSorter.
179 //
180 // The equivalent of a TopologicalSorter<int> which nodes are the
181 // N integers from 0 to N-1 (see the toplevel comment). The API is
182 // exactly similar to that of TopologicalSorter, please refer to the
183 // TopologicalSorter class below for more detailed comments.
184 //
185 // If the template parameter is true then the sort will be stable.
186 // This means that the order of the nodes will be maintained as much as
187 // possible. A non-stable sort is more efficient, since the complexity
188 // of getting the next node is O(1) rather than O(log(Nodes)).
189 template <bool stable_sort = false>
191  public:
192  // To store the adjacency lists efficiently.
193  typedef absl::InlinedVector<int, 4> AdjacencyList;
194 
195  // For efficiency, it is best to specify how many nodes are required
196  // by using the next constructor.
198  : traversal_started_(false),
199  num_edges_(0),
200  num_edges_added_since_last_duplicate_removal_(0) {}
201 
202  // One may also construct a DenseIntTopologicalSorterTpl with a predefined
203  // number of empty nodes. One can thus bypass the AddNode() API,
204  // which may yield a lower memory usage.
205  explicit DenseIntTopologicalSorterTpl(int num_nodes)
206  : adjacency_lists_(num_nodes),
207  traversal_started_(false),
208  num_edges_(0),
209  num_edges_added_since_last_duplicate_removal_(0) {}
210 
211  // Performs in constant amortized time. Calling this will make all
212  // node indices in [0 .. node_index] be valid node indices. If you
213  // can avoid using AddNode(), you should! If you know the number of
214  // nodes in advance, you should specify that at construction time --
215  // it will be faster and use less memory.
216  void AddNode(int node_index);
217 
218  // Performs AddEdge() in bulk. Much faster if you add *all* edges at once.
219  void AddEdges(const std::vector<std::pair<int, int>>& edges);
220 
221  // Performs in constant amortized time. Calling this will make all
222  // node indices in [0, max(from, to)] be valid node indices.
223  // THIS IS MUCH SLOWER than calling AddEdges() if you already have all the
224  // edges.
225  void AddEdge(int from, int to);
226 
227  // Performs in O(average degree) in average. If a cycle is detected
228  // and "output_cycle_nodes" isn't NULL, it will require an additional
229  // O(number of edges + number of nodes in the graph) time.
230  bool GetNext(int* next_node_index, bool* cyclic,
231  std::vector<int>* output_cycle_nodes = nullptr);
232 
234  StartTraversal();
235  return nodes_with_zero_indegree_.size();
236  }
237 
238  void StartTraversal();
239 
240  bool TraversalStarted() const { return traversal_started_; }
241 
242  // Given a vector<AdjacencyList> of size n such that elements of the
243  // AdjacencyList are in [0, n-1], remove the duplicates within each
244  // AdjacencyList of size greater or equal to skip_lists_smaller_than,
245  // in linear time. Returns the total number of duplicates removed.
246  // This method is exposed for unit testing purposes only.
247  static int RemoveDuplicates(std::vector<AdjacencyList>* lists,
248  int skip_lists_smaller_than);
249 
250  // To extract a cycle. When there is no cycle, cycle_nodes will be empty.
251  void ExtractCycle(std::vector<int>* cycle_nodes) const;
252 
253  private:
254  // Outgoing adjacency lists.
255  std::vector<AdjacencyList> adjacency_lists_;
256 
257  bool traversal_started_;
258 
259  // Only valid after a traversal started.
260  int num_nodes_left_;
261  typename std::conditional<
262  stable_sort,
263  // We use greater<int> so that the lowest elements gets popped first.
264  std::priority_queue<int, std::vector<int>, std::greater<int>>,
265  std::queue<int>>::type nodes_with_zero_indegree_;
266  std::vector<int> indegree_;
267 
268  // Used internally by AddEdge() to decide whether to trigger
269  // RemoveDuplicates(). See the .cc.
270  int num_edges_; // current total number of edges.
271  int num_edges_added_since_last_duplicate_removal_;
272 
273  private:
274  DISALLOW_COPY_AND_ASSIGN(DenseIntTopologicalSorterTpl);
275 };
276 
277 extern template class DenseIntTopologicalSorterTpl<false>;
278 extern template class DenseIntTopologicalSorterTpl<true>;
279 
280 } // namespace internal
281 
282 // Recommended version for general usage. The stability makes it more
283 // deterministic, and its behavior is guaranteed to never change.
284 typedef ::util::internal::DenseIntTopologicalSorterTpl<
285  /*stable_sort=*/true>
287 
288 // Use this version if you are certain you don't care about the
289 // tie-breaking order and need the 5 to 10% performance gain. The
290 // performance gain can be more significant for large graphs with large
291 // numbers of source nodes (for example 2 Million nodes with 2 Million
292 // random edges sees a factor of 0.7 difference in completion time).
293 typedef ::util::internal::DenseIntTopologicalSorterTpl<
294  /*stable_sort=*/false>
296 
297 // A copy of each Node is stored internally. Duplicated edges are allowed,
298 // and discarded lazily so that AddEdge() keeps an amortized constant
299 // time, yet the total memory usage remains O(number of different edges +
300 // number of nodes).
301 //
302 // DenseIntTopologicalSorter implements the core topological sort
303 // algorithm. For greater efficiency it can be used directly
304 // (TopologicalSorter<int> is about 1.5-3x slower).
305 //
306 // TopologicalSorter requires that all nodes and edges be added before
307 // traversing the nodes, otherwise it will die with a fatal error.
308 //
309 // TopologicalSorter is -compatible
310 //
311 // Note(user): since all the real work is done by
312 // DenseIntTopologicalSorterTpl, and this class is a template, we inline
313 // every function here in the .h.
314 //
315 // If stable_sort is true then the topological sort will preserve the
316 // original order of the nodes as much as possible. Note, the order
317 // which is preserved is the order in which the nodes are added (if you
318 // use AddEdge it will add the first argument and then the second).
319 template <typename T, bool stable_sort = false,
320  typename Hash = typename absl::flat_hash_map<T, int>::hasher,
321  typename KeyEqual =
322  typename absl::flat_hash_map<T, int, Hash>::key_equal>
324  public:
327 
328  // Adds a node to the graph, if it has not already been added via
329  // previous calls to AddNode()/AddEdge(). If no edges are later
330  // added connecting this node, then it remains an isolated node in
331  // the graph. AddNode() only exists to support isolated nodes. There
332  // is no requirement (nor is it an error) to call AddNode() for the
333  // endpoints used in a call to AddEdge(). Dies with a fatal error if
334  // called after a traversal has been started (see TraversalStarted()),
335  // or if more than INT_MAX nodes are being added.
336  void AddNode(const T& node) { int_sorter_.AddNode(LookupOrInsertNode(node)); }
337 
338  // Shortcut to AddEdge() in bulk. Not optimized.
339  void AddEdges(const std::vector<std::pair<T, T>>& edges) {
340  for (const auto& [from, to] : edges) AddEdge(from, to);
341  }
342 
343  // Adds a directed edge with the given endpoints to the graph. There
344  // is no requirement (nor is it an error) to call AddNode() for the
345  // endpoints. Dies with a fatal error if called after a traversal
346  // has been started (see TraversalStarted()).
347  void AddEdge(const T& from, const T& to) {
348  // The lookups are not inlined into AddEdge because we need to ensure that
349  // "from" is inserted before "to".
350  const int from_int = LookupOrInsertNode(from);
351  const int to_int = LookupOrInsertNode(to);
352  int_sorter_.AddEdge(from_int, to_int);
353  }
354 
355  // Visits the least node in topological order over the current set of
356  // nodes and edges, and marks that node as visited, so that repeated
357  // calls to GetNext() will visit all nodes in order. Writes the newly
358  // visited node in *node and returns true with *cyclic set to false
359  // (assuming the graph has not yet been discovered to be cyclic).
360  // Returns false if all nodes have been visited, or if the graph is
361  // discovered to be cyclic, in which case *cyclic is also set to true.
362  //
363  // If you set the optional argument "output_cycle_nodes" to non-NULL and
364  // a cycle is detected, it will dump an arbitrary cycle of the graph
365  // (whose length will be between 1 and #number_of_nodes, inclusive),
366  // in the natural order: for example if "output_cycle_nodes" is filled
367  // with ["A", "C", "B"], it means that A->C->B->A is a directed cycle
368  // of the graph.
369  //
370  // This starts a traversal (if not started already). Note that the
371  // graph can only be traversed once.
372  bool GetNext(T* node, bool* cyclic_ptr,
373  std::vector<T>* output_cycle_nodes = nullptr) {
374  StartTraversal();
375  int node_index;
376  if (!int_sorter_.GetNext(
377  &node_index, cyclic_ptr,
378  output_cycle_nodes ? &cycle_int_nodes_ : nullptr)) {
379  if (*cyclic_ptr && output_cycle_nodes != nullptr) {
380  output_cycle_nodes->clear();
381  for (const int int_node : cycle_int_nodes_) {
382  output_cycle_nodes->push_back(nodes_[int_node]);
383  }
384  }
385  return false;
386  }
387  *node = nodes_[node_index];
388  return true;
389  }
390 
391  // Returns the number of nodes that currently have zero indegree.
392  // This starts a traversal (if not started already).
394  StartTraversal();
395  return int_sorter_.GetCurrentFringeSize();
396  }
397 
398  // Start a traversal. See TraversalStarted(). This initializes the
399  // various data structures of the sorter. Since this takes O(num_nodes
400  // + num_edges) time, users may want to call this at their convenience,
401  // instead of making it happen with the first GetNext().
402  void StartTraversal() {
403  if (TraversalStarted()) return;
404  nodes_.resize(node_to_index_.size());
405  // We move elements from the absl::flat_hash_map to this vector, without
406  // extra copy (if they are movable).
407  for (auto& node_and_index : node_to_index_) {
408  nodes_[node_and_index.second] = std::move(node_and_index.first);
409  }
410  gtl::STLClearHashIfBig(&node_to_index_, 1 << 16);
411  int_sorter_.StartTraversal();
412  }
413 
414  // Whether a traversal has started. If true, AddNode() and AddEdge()
415  // can no longer be called.
416  bool TraversalStarted() const { return int_sorter_.TraversalStarted(); }
417 
418  private:
419  // A simple mapping from node to their dense index, in 0..num_nodes-1,
420  // which will be their index in nodes_[]. Cleared when a traversal
421  // starts, and replaced by nodes_[].
422  absl::flat_hash_map<T, int, Hash, KeyEqual> node_to_index_;
423 
424  // Stores all the nodes as soon as a traversal starts.
425  std::vector<T> nodes_;
426 
427  // An internal DenseIntTopologicalSorterTpl that does all the real work.
429 
430  // Used internally to extract cycles from the underlying
431  // DenseIntTopologicalSorterTpl.
432  std::vector<int> cycle_int_nodes_;
433 
434  // Lookup an existing node's index, or add the node and return the
435  // new index that was assigned to it.
436  int LookupOrInsertNode(const T& node) {
437  return gtl::LookupOrInsert(&node_to_index_, node, node_to_index_.size());
438  }
439 
440  DISALLOW_COPY_AND_ASSIGN(TopologicalSorter);
441 };
442 
443 namespace internal {
444 // If successful, returns true and outputs the order in "topological_order".
445 // If not, returns false and outputs a cycle in "cycle" (if not null).
446 template <typename T, typename Sorter>
447 ABSL_MUST_USE_RESULT bool RunTopologicalSorter(
448  Sorter* sorter, const std::vector<std::pair<T, T>>& arcs,
449  std::vector<T>* topological_order, std::vector<T>* cycle) {
450  topological_order->clear();
451  sorter->AddEdges(arcs);
452  bool cyclic = false;
453  sorter->StartTraversal();
454  T next;
455  while (sorter->GetNext(&next, &cyclic, cycle)) {
456  topological_order->push_back(next);
457  }
458  return !cyclic;
459 }
460 
461 template <bool stable_sort = false>
462 ABSL_MUST_USE_RESULT bool DenseIntTopologicalSortImpl(
463  int num_nodes, const std::vector<std::pair<int, int>>& arcs,
464  std::vector<int>* topological_order) {
466  topological_order->reserve(num_nodes);
467  return RunTopologicalSorter<int, decltype(sorter)>(
468  &sorter, arcs, topological_order, nullptr);
469 }
470 
471 template <typename T, bool stable_sort = false>
472 ABSL_MUST_USE_RESULT bool TopologicalSortImpl(
473  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs,
474  std::vector<T>* topological_order, std::vector<T>* cycle) {
476  for (const T& node : nodes) {
477  sorter.AddNode(node);
478  }
479  return RunTopologicalSorter<T, decltype(sorter)>(&sorter, arcs,
480  topological_order, cycle);
481 }
482 
483 // Now, the OrDie() versions, which directly return the topological order.
484 template <typename T, typename Sorter>
486  Sorter* sorter, int num_nodes, const std::vector<std::pair<T, T>>& arcs) {
487  std::vector<T> topo_order;
488  topo_order.reserve(num_nodes);
489  CHECK(RunTopologicalSorter(sorter, arcs, &topo_order, &topo_order))
490  << "Found cycle: " << gtl::LogContainer(topo_order);
491  return topo_order;
492 }
493 
494 template <bool stable_sort = false>
496  int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
498  return RunTopologicalSorterOrDie(&sorter, num_nodes, arcs);
499 }
500 
501 template <typename T, bool stable_sort = false>
503  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
505  for (const T& node : nodes) {
506  sorter.AddNode(node);
507  }
508  return RunTopologicalSorterOrDie(&sorter, nodes.size(), arcs);
509 }
510 } // namespace internal
511 
512 // Implementations of the "simple API" functions declared at the top.
514  int num_nodes, const std::vector<std::pair<int, int>>& arcs,
515  std::vector<int>* topological_order) {
516  return internal::DenseIntTopologicalSortImpl<false>(num_nodes, arcs,
517  topological_order);
518 }
519 
521  int num_nodes, const std::vector<std::pair<int, int>>& arcs,
522  std::vector<int>* topological_order) {
523  return internal::DenseIntTopologicalSortImpl<true>(num_nodes, arcs,
524  topological_order);
525 }
526 
527 template <typename T>
528 bool TopologicalSort(const std::vector<T>& nodes,
529  const std::vector<std::pair<T, T>>& arcs,
530  std::vector<T>* topological_order) {
531  return internal::TopologicalSortImpl<T, false>(nodes, arcs, topological_order,
532  nullptr);
533 }
534 
535 template <typename T>
536 bool TopologicalSort(const std::vector<T>& nodes,
537  const std::vector<std::pair<T, T>>& arcs,
538  std::vector<T>* topological_order, std::vector<T>* cycle) {
539  return internal::TopologicalSortImpl<T, false>(nodes, arcs, topological_order,
540  cycle);
541 }
542 
543 template <typename T>
544 bool StableTopologicalSort(const std::vector<T>& nodes,
545  const std::vector<std::pair<T, T>>& arcs,
546  std::vector<T>* topological_order) {
547  return internal::TopologicalSortImpl<T, true>(nodes, arcs, topological_order,
548  nullptr);
549 }
550 
551 template <typename T>
552 bool StableTopologicalSort(const std::vector<T>& nodes,
553  const std::vector<std::pair<T, T>>& arcs,
554  std::vector<T>* topological_order,
555  std::vector<T>* cycle) {
556  return internal::TopologicalSortImpl<T, true>(nodes, arcs, topological_order,
557  cycle);
558 }
559 
560 inline std::vector<int> DenseIntTopologicalSortOrDie(
561  int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
562  return internal::DenseIntTopologicalSortOrDieImpl<false>(num_nodes, arcs);
563 }
564 
565 inline std::vector<int> DenseIntStableTopologicalSortOrDie(
566  int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
567  return internal::DenseIntTopologicalSortOrDieImpl<true>(num_nodes, arcs);
568 }
569 
570 template <typename T>
571 std::vector<T> TopologicalSortOrDie(const std::vector<T>& nodes,
572  const std::vector<std::pair<T, T>>& arcs) {
573  return internal::TopologicalSortOrDieImpl<T, false>(nodes, arcs);
574 }
575 
576 template <typename T>
578  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
579  return internal::TopologicalSortOrDieImpl<T, true>(nodes, arcs);
580 }
581 
582 } // namespace util
583 
584 // BACKWARDS COMPATIBILITY
585 // Some of the classes or functions have been exposed under the global namespace
586 // or the util::graph:: namespace. Until all clients are fixed to use the
587 // util:: namespace, we keep those versions around.
590 template <typename T, bool stable_sort = false,
591  typename Hash = typename absl::flat_hash_map<T, int>::hasher,
592  typename KeyEqual =
593  typename absl::flat_hash_map<T, int, Hash>::key_equal>
595  : public ::util::TopologicalSorter<T, stable_sort, Hash, KeyEqual> {};
596 
597 namespace util {
598 namespace graph {
599 inline std::vector<int> DenseIntTopologicalSortOrDie(
600  int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
602 }
603 inline std::vector<int> DenseIntStableTopologicalSortOrDie(
604  int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
606 }
607 template <typename T>
609  const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
610  return ::util::StableTopologicalSortOrDie<T>(nodes, arcs);
611 }
612 
613 template <class AdjacencyLists>
614 absl::StatusOr<std::vector<int>> FastTopologicalSort(
615  const AdjacencyLists& adj) {
616  const size_t num_nodes = adj.size();
617  if (num_nodes > std::numeric_limits<int>::max()) {
618  return absl::InvalidArgumentError("More than kint32max nodes");
619  }
620  std::vector<int> indegree(num_nodes, 0);
621  std::vector<int> topo_order;
622  topo_order.reserve(num_nodes);
623  for (int from = 0; from < num_nodes; ++from) {
624  for (const int head : adj[from]) {
625  // We cast to unsigned int to test "head < 0 || head ≥ num_nodes" with a
626  // single test. Microbenchmarks showed a ~1% overall performance gain.
627  if (static_cast<uint32_t>(head) >= num_nodes) {
628  return absl::InvalidArgumentError(
629  absl::StrFormat("Invalid arc in adj[%d]: %d (num_nodes=%d)", from,
630  head, num_nodes));
631  }
632  // NOTE(user): We could detect self-arcs here (head == from) and exit
633  // early, but microbenchmarks show a 2 to 4% slow-down if we do it, so we
634  // simply rely on self-arcs being detected as cycles in the topo sort.
635  ++indegree[head];
636  }
637  }
638  for (int i = 0; i < num_nodes; ++i) {
639  if (!indegree[i]) topo_order.push_back(i);
640  }
641  size_t num_visited = 0;
642  while (num_visited < topo_order.size()) {
643  const int from = topo_order[num_visited++];
644  for (const int head : adj[from]) {
645  if (!--indegree[head]) topo_order.push_back(head);
646  }
647  }
648  if (topo_order.size() < static_cast<size_t>(num_nodes)) {
649  return absl::InvalidArgumentError("The graph has a cycle");
650  }
651  return topo_order;
652 }
653 
654 template <class AdjacencyLists>
655 absl::StatusOr<std::vector<int>> FindCycleInGraph(const AdjacencyLists& adj) {
656  const size_t num_nodes = adj.size();
657  if (num_nodes > std::numeric_limits<int>::max()) {
658  return absl::InvalidArgumentError(
659  absl::StrFormat("Too many nodes: adj.size()=%d", adj.size()));
660  }
661 
662  // To find a cycle, we start a DFS from each yet-unvisited node and
663  // try to find a cycle, if we don't find it then we know for sure that
664  // no cycle is reachable from any of the explored nodes (so, we don't
665  // explore them in later DFSs).
666  std::vector<bool> no_cycle_reachable_from(num_nodes, false);
667  // The DFS stack will contain a chain of nodes, from the root of the
668  // DFS to the current leaf.
669  struct DfsState {
670  int node;
671  // Points at the first child node that we did *not* yet look at.
672  int adj_list_index;
673  explicit DfsState(int _node) : node(_node), adj_list_index(0) {}
674  };
675  std::vector<DfsState> dfs_stack;
676  std::vector<bool> in_cur_stack(num_nodes, false);
677  for (int start_node = 0; start_node < static_cast<int>(num_nodes);
678  ++start_node) {
679  if (no_cycle_reachable_from[start_node]) continue;
680  // Start the DFS.
681  dfs_stack.push_back(DfsState(start_node));
682  in_cur_stack[start_node] = true;
683  while (!dfs_stack.empty()) {
684  DfsState* cur_state = &dfs_stack.back();
685  if (static_cast<size_t>(cur_state->adj_list_index) >=
686  adj[cur_state->node].size()) {
687  no_cycle_reachable_from[cur_state->node] = true;
688  in_cur_stack[cur_state->node] = false;
689  dfs_stack.pop_back();
690  continue;
691  }
692  // Look at the current child, and increase the current state's
693  // adj_list_index.
694  // TODO(user): Caching adj[cur_state->node] in a local stack to improve
695  // locality and so that the [] operator is called exactly once per node.
696  const int child = adj[cur_state->node][cur_state->adj_list_index++];
697  if (static_cast<size_t>(child) >= num_nodes) {
698  return absl::InvalidArgumentError(absl::StrFormat(
699  "Invalid child %d in adj[%d]", child, cur_state->node));
700  }
701  if (no_cycle_reachable_from[child]) continue;
702  if (in_cur_stack[child]) {
703  // We detected a cycle! It corresponds to the tail end of dfs_stack,
704  // in reverse order, until we find "child".
705  int cycle_start = dfs_stack.size() - 1;
706  while (dfs_stack[cycle_start].node != child) --cycle_start;
707  const int cycle_size = dfs_stack.size() - cycle_start;
708  std::vector<int> cycle(cycle_size);
709  for (int c = 0; c < cycle_size; ++c) {
710  cycle[c] = dfs_stack[cycle_start + c].node;
711  }
712  return cycle;
713  }
714  // Push the child onto the stack.
715  dfs_stack.push_back(DfsState(child));
716  in_cur_stack[child] = true;
717  // Verify that its adjacency list seems valid.
718  if (adj[child].size() > std::numeric_limits<int>::max()) {
719  return absl::InvalidArgumentError(absl::StrFormat(
720  "Invalid adj[%d].size() = %d", child, adj[child].size()));
721  }
722  }
723  }
724  // If we're here, then all the DFS stopped, and there is no cycle.
725  return std::vector<int>{};
726 }
727 
728 } // namespace graph
729 } // namespace util
730 
731 #endif // UTIL_GRAPH_TOPOLOGICALSORTER_H__
int64_t max
Definition: alldiff_cst.cc:140
void AddEdge(const T &from, const T &to)
void AddEdges(const std::vector< std::pair< T, T >> &edges)
bool GetNext(T *node, bool *cyclic_ptr, std::vector< T > *output_cycle_nodes=nullptr)
void AddNode(const T &node)
void ExtractCycle(std::vector< int > *cycle_nodes) const
absl::InlinedVector< int, 4 > AdjacencyList
static int RemoveDuplicates(std::vector< AdjacencyList > *lists, int skip_lists_smaller_than)
void AddEdges(const std::vector< std::pair< int, int >> &edges)
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
Block * next
auto LogContainer(const ContainerT &container, const PolicyT &policy) -> decltype(gtl::LogRange(container.begin(), container.end(), policy))
Collection::value_type::second_type & LookupOrInsert(Collection *const collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:237
void STLClearHashIfBig(T *obj, size_t limit)
Definition: stl_util.h:180
std::vector< T > StableTopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs)
absl::StatusOr< std::vector< int > > FindCycleInGraph(const AdjacencyLists &adj)
std::vector< int > DenseIntStableTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
std::vector< int > DenseIntTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
absl::StatusOr< std::vector< int > > FastTopologicalSort(const AdjacencyLists &adj)
std::vector< T > TopologicalSortOrDieImpl(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs)
ABSL_MUST_USE_RESULT bool RunTopologicalSorter(Sorter *sorter, const std::vector< std::pair< T, T >> &arcs, std::vector< T > *topological_order_or_cycle)
ABSL_MUST_USE_RESULT bool TopologicalSortImpl(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs, std::vector< T > *topological_order, std::vector< T > *cycle)
std::vector< T > RunTopologicalSorterOrDie(Sorter *sorter, int num_nodes, const std::vector< std::pair< T, T >> &arcs)
std::vector< int > DenseIntTopologicalSortOrDieImpl(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
ABSL_MUST_USE_RESULT bool DenseIntTopologicalSortImpl(int num_nodes, const std::vector< std::pair< int, int >> &arcs, std::vector< int > *topological_order)
uint64_t Hash(uint64_t num, uint64_t c)
Definition: hash.h:74
ABSL_MUST_USE_RESULT bool DenseIntStableTopologicalSort(int num_nodes, const std::vector< std::pair< int, int >> &arcs, std::vector< int > *topological_order)
::util::internal::DenseIntTopologicalSorterTpl< true > DenseIntStableTopologicalSorter
ABSL_MUST_USE_RESULT std::vector< int > FindCycleInDenseIntGraph(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
ABSL_MUST_USE_RESULT bool DenseIntTopologicalSort(int num_nodes, const std::vector< std::pair< int, int >> &arcs, std::vector< int > *topological_order)
std::vector< int > DenseIntStableTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
ABSL_MUST_USE_RESULT bool StableTopologicalSort(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs, std::vector< T > *topological_order)
std::vector< T > TopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs)
::util::internal::DenseIntTopologicalSorterTpl< false > DenseIntTopologicalSorter
ABSL_MUST_USE_RESULT bool TopologicalSort(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs, std::vector< T > *topological_order)
std::vector< int > DenseIntTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int >> &arcs)
std::vector< T > StableTopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T >> &arcs)
int64_t head
int nodes
::util::DenseIntStableTopologicalSorter DenseIntStableTopologicalSorter
::util::DenseIntTopologicalSorter DenseIntTopologicalSorter