Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
topologicalsorter.h
Go to the documentation of this file.
1// Copyright 2010-2024 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"
46#include "absl/types/span.h"
52#include "ortools/graph/graph.h"
53
54namespace util {
55namespace 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//
86template <class AdjacencyLists> // vector<vector<int>>, util::StaticGraph<>, ..
87absl::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.
95template <class AdjacencyLists> // vector<vector<int>>, util::StaticGraph<>, ..
96absl::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".
111template <typename T>
112ABSL_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.
116template <typename T>
117ABSL_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.
121template <typename T>
122std::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.
128template <typename T>
129ABSL_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.
133template <typename T>
134ABSL_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.
138template <typename T>
139std::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.
145inline 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().
159ABSL_MUST_USE_RESULT inline bool DenseIntTopologicalSort(
160 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
161 std::vector<int>* topological_order);
162inline std::vector<int> DenseIntStableTopologicalSortOrDie(
163 int num_nodes, const std::vector<std::pair<int, int>>& arcs);
164ABSL_MUST_USE_RESULT inline bool DenseIntStableTopologicalSort(
165 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
166 std::vector<int>* topological_order);
167inline std::vector<int> DenseIntTopologicalSortOrDie(
168 int num_nodes, const std::vector<std::pair<int, int>>& arcs);
169
170namespace internal {
171// Internal wrapper around the *TopologicalSort classes.
172template <typename T, typename Sorter>
173ABSL_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)).
189template <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 // This type is neither copyable nor movable.
214 delete;
215
216 // Performs in constant amortized time. Calling this will make all
217 // node indices in [0 .. node_index] be valid node indices. If you
218 // can avoid using AddNode(), you should! If you know the number of
219 // nodes in advance, you should specify that at construction time --
220 // it will be faster and use less memory.
221 void AddNode(int node_index);
222
223 // Performs AddEdge() in bulk. Much faster if you add *all* edges at once.
224 void AddEdges(absl::Span<const std::pair<int, int>> edges);
225
226 // Performs in constant amortized time. Calling this will make all
227 // node indices in [0, max(from, to)] be valid node indices.
228 // THIS IS MUCH SLOWER than calling AddEdges() if you already have all the
229 // edges.
230 void AddEdge(int from, int to);
231
232 // Performs in O(average degree) in average. If a cycle is detected
233 // and "output_cycle_nodes" isn't NULL, it will require an additional
234 // O(number of edges + number of nodes in the graph) time.
235 bool GetNext(int* next_node_index, bool* cyclic,
236 std::vector<int>* output_cycle_nodes = nullptr);
237
240 return nodes_with_zero_indegree_.size();
241 }
242
243 void StartTraversal();
244
245 bool TraversalStarted() const { return traversal_started_; }
246
247 // Given a vector<AdjacencyList> of size n such that elements of the
248 // AdjacencyList are in [0, n-1], remove the duplicates within each
249 // AdjacencyList of size greater or equal to skip_lists_smaller_than,
250 // in linear time. Returns the total number of duplicates removed.
251 // This method is exposed for unit testing purposes only.
252 static int RemoveDuplicates(std::vector<AdjacencyList>* lists,
253 int skip_lists_smaller_than);
254
255 // To extract a cycle. When there is no cycle, cycle_nodes will be empty.
256 void ExtractCycle(std::vector<int>* cycle_nodes) const;
257
258 private:
259 // Outgoing adjacency lists.
260 std::vector<AdjacencyList> adjacency_lists_;
261
262 bool traversal_started_;
263
264 // Only valid after a traversal started.
265 int num_nodes_left_;
266 typename std::conditional<
267 stable_sort,
268 // We use greater<int> so that the lowest elements gets popped first.
269 std::priority_queue<int, std::vector<int>, std::greater<int>>,
270 std::queue<int>>::type nodes_with_zero_indegree_;
271 std::vector<int> indegree_;
272
273 // Used internally by AddEdge() to decide whether to trigger
274 // RemoveDuplicates(). See the .cc.
275 int num_edges_; // current total number of edges.
276 int num_edges_added_since_last_duplicate_removal_;
277};
278
279extern template class DenseIntTopologicalSorterTpl<false>;
280extern template class DenseIntTopologicalSorterTpl<true>;
281
282} // namespace internal
283
284// Recommended version for general usage. The stability makes it more
285// deterministic, and its behavior is guaranteed to never change.
287 /*stable_sort=*/true>
289
290// Use this version if you are certain you don't care about the
291// tie-breaking order and need the 5 to 10% performance gain. The
292// performance gain can be more significant for large graphs with large
293// numbers of source nodes (for example 2 Million nodes with 2 Million
294// random edges sees a factor of 0.7 difference in completion time).
296 /*stable_sort=*/false>
298
299// A copy of each Node is stored internally. Duplicated edges are allowed,
300// and discarded lazily so that AddEdge() keeps an amortized constant
301// time, yet the total memory usage remains O(number of different edges +
302// number of nodes).
303//
304// DenseIntTopologicalSorter implements the core topological sort
305// algorithm. For greater efficiency it can be used directly
306// (TopologicalSorter<int> is about 1.5-3x slower).
307//
308// TopologicalSorter requires that all nodes and edges be added before
309// traversing the nodes, otherwise it will die with a fatal error.
310//
311// TopologicalSorter is -compatible
312//
313// Note(user): since all the real work is done by
314// DenseIntTopologicalSorterTpl, and this class is a template, we inline
315// every function here in the .h.
316//
317// If stable_sort is true then the topological sort will preserve the
318// original order of the nodes as much as possible. Note, the order
319// which is preserved is the order in which the nodes are added (if you
320// use AddEdge it will add the first argument and then the second).
321template <typename T, bool stable_sort = false,
322 typename Hash = typename absl::flat_hash_map<T, int>::hasher,
323 typename KeyEqual =
324 typename absl::flat_hash_map<T, int, Hash>::key_equal>
326 public:
328
329 // This type is neither copyable nor movable.
333
334 // Adds a node to the graph, if it has not already been added via
335 // previous calls to AddNode()/AddEdge(). If no edges are later
336 // added connecting this node, then it remains an isolated node in
337 // the graph. AddNode() only exists to support isolated nodes. There
338 // is no requirement (nor is it an error) to call AddNode() for the
339 // endpoints used in a call to AddEdge(). Dies with a fatal error if
340 // called after a traversal has been started (see TraversalStarted()),
341 // or if more than INT_MAX nodes are being added.
342 void AddNode(const T& node) { int_sorter_.AddNode(LookupOrInsertNode(node)); }
343
344 // Shortcut to AddEdge() in bulk. Not optimized.
345 void AddEdges(const std::vector<std::pair<T, T>>& edges) {
346 for (const auto& [from, to] : edges) AddEdge(from, to);
347 }
348
349 // Adds a directed edge with the given endpoints to the graph. There
350 // is no requirement (nor is it an error) to call AddNode() for the
351 // endpoints. Dies with a fatal error if called after a traversal
352 // has been started (see TraversalStarted()).
353 void AddEdge(const T& from, const T& to) {
354 // The lookups are not inlined into AddEdge because we need to ensure that
355 // "from" is inserted before "to".
356 const int from_int = LookupOrInsertNode(from);
357 const int to_int = LookupOrInsertNode(to);
358 int_sorter_.AddEdge(from_int, to_int);
359 }
360
361 // Visits the least node in topological order over the current set of
362 // nodes and edges, and marks that node as visited, so that repeated
363 // calls to GetNext() will visit all nodes in order. Writes the newly
364 // visited node in *node and returns true with *cyclic set to false
365 // (assuming the graph has not yet been discovered to be cyclic).
366 // Returns false if all nodes have been visited, or if the graph is
367 // discovered to be cyclic, in which case *cyclic is also set to true.
368 //
369 // If you set the optional argument "output_cycle_nodes" to non-NULL and
370 // a cycle is detected, it will dump an arbitrary cycle of the graph
371 // (whose length will be between 1 and #number_of_nodes, inclusive),
372 // in the natural order: for example if "output_cycle_nodes" is filled
373 // with ["A", "C", "B"], it means that A->C->B->A is a directed cycle
374 // of the graph.
375 //
376 // This starts a traversal (if not started already). Note that the
377 // graph can only be traversed once.
378 bool GetNext(T* node, bool* cyclic_ptr,
379 std::vector<T>* output_cycle_nodes = nullptr) {
381 int node_index;
382 if (!int_sorter_.GetNext(
383 &node_index, cyclic_ptr,
384 output_cycle_nodes ? &cycle_int_nodes_ : nullptr)) {
385 if (*cyclic_ptr && output_cycle_nodes != nullptr) {
386 output_cycle_nodes->clear();
387 for (const int int_node : cycle_int_nodes_) {
388 output_cycle_nodes->push_back(nodes_[int_node]);
389 }
390 }
391 return false;
392 }
393 *node = nodes_[node_index];
394 return true;
395 }
396
397 // Returns the number of nodes that currently have zero indegree.
398 // This starts a traversal (if not started already).
401 return int_sorter_.GetCurrentFringeSize();
402 }
403
404 // Start a traversal. See TraversalStarted(). This initializes the
405 // various data structures of the sorter. Since this takes O(num_nodes
406 // + num_edges) time, users may want to call this at their convenience,
407 // instead of making it happen with the first GetNext().
409 if (TraversalStarted()) return;
410 nodes_.resize(node_to_index_.size());
411 // We move elements from the absl::flat_hash_map to this vector, without
412 // extra copy (if they are movable).
413 for (auto& node_and_index : node_to_index_) {
414 nodes_[node_and_index.second] = std::move(node_and_index.first);
415 }
416 gtl::STLClearHashIfBig(&node_to_index_, 1 << 16);
417 int_sorter_.StartTraversal();
418 }
419
420 // Whether a traversal has started. If true, AddNode() and AddEdge()
421 // can no longer be called.
422 bool TraversalStarted() const { return int_sorter_.TraversalStarted(); }
423
424 private:
425 // A simple mapping from node to their dense index, in 0..num_nodes-1,
426 // which will be their index in nodes_[]. Cleared when a traversal
427 // starts, and replaced by nodes_[].
428 absl::flat_hash_map<T, int, Hash, KeyEqual> node_to_index_;
429
430 // Stores all the nodes as soon as a traversal starts.
431 std::vector<T> nodes_;
432
433 // An internal DenseIntTopologicalSorterTpl that does all the real work.
435
436 // Used internally to extract cycles from the underlying
437 // DenseIntTopologicalSorterTpl.
438 std::vector<int> cycle_int_nodes_;
439
440 // Lookup an existing node's index, or add the node and return the
441 // new index that was assigned to it.
442 int LookupOrInsertNode(const T& node) {
443 return gtl::LookupOrInsert(&node_to_index_, node, node_to_index_.size());
444 }
445};
446
447namespace internal {
448// If successful, returns true and outputs the order in "topological_order".
449// If not, returns false and outputs a cycle in "cycle" (if not null).
450template <typename T, typename Sorter>
451ABSL_MUST_USE_RESULT bool RunTopologicalSorter(
452 Sorter* sorter, const std::vector<std::pair<T, T>>& arcs,
453 std::vector<T>* topological_order, std::vector<T>* cycle) {
454 topological_order->clear();
455 sorter->AddEdges(arcs);
456 bool cyclic = false;
457 sorter->StartTraversal();
458 T next;
459 while (sorter->GetNext(&next, &cyclic, cycle)) {
460 topological_order->push_back(next);
461 }
462 return !cyclic;
463}
464
465template <bool stable_sort = false>
466ABSL_MUST_USE_RESULT bool DenseIntTopologicalSortImpl(
467 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
468 std::vector<int>* topological_order) {
470 topological_order->reserve(num_nodes);
472 &sorter, arcs, topological_order, nullptr);
473}
474
475template <typename T, bool stable_sort = false>
476ABSL_MUST_USE_RESULT bool TopologicalSortImpl(
477 absl::Span<const T> nodes, const std::vector<std::pair<T, T>>& arcs,
478 std::vector<T>* topological_order, std::vector<T>* cycle) {
480 for (const T& node : nodes) {
481 sorter.AddNode(node);
482 }
484 topological_order, cycle);
485}
486
487// Now, the OrDie() versions, which directly return the topological order.
488template <typename T, typename Sorter>
490 Sorter* sorter, int num_nodes, const std::vector<std::pair<T, T>>& arcs) {
491 std::vector<T> topo_order;
492 topo_order.reserve(num_nodes);
493 CHECK(RunTopologicalSorter(sorter, arcs, &topo_order, &topo_order))
494 << "Found cycle: " << gtl::LogContainer(topo_order);
495 return topo_order;
496}
497
498template <bool stable_sort = false>
500 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
502 return RunTopologicalSorterOrDie(&sorter, num_nodes, arcs);
503}
504
505template <typename T, bool stable_sort = false>
507 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
509 for (const T& node : nodes) {
510 sorter.AddNode(node);
511 }
512 return RunTopologicalSorterOrDie(&sorter, nodes.size(), arcs);
513}
514} // namespace internal
515
516// Implementations of the "simple API" functions declared at the top.
518 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
519 std::vector<int>* topological_order) {
522}
523
525 int num_nodes, const std::vector<std::pair<int, int>>& arcs,
526 std::vector<int>* topological_order) {
527 return internal::DenseIntTopologicalSortImpl<true>(num_nodes, arcs,
529}
530
531template <typename T>
532bool TopologicalSort(const std::vector<T>& nodes,
533 const std::vector<std::pair<T, T>>& arcs,
534 std::vector<T>* topological_order) {
536 nullptr);
537}
538
539template <typename T>
540bool TopologicalSort(const std::vector<T>& nodes,
541 const std::vector<std::pair<T, T>>& arcs,
542 std::vector<T>* topological_order, std::vector<T>* cycle) {
544 cycle);
545}
546
547template <typename T>
548bool StableTopologicalSort(const std::vector<T>& nodes,
549 const std::vector<std::pair<T, T>>& arcs,
550 std::vector<T>* topological_order) {
552 nullptr);
553}
554
555template <typename T>
556bool StableTopologicalSort(const std::vector<T>& nodes,
557 const std::vector<std::pair<T, T>>& arcs,
558 std::vector<T>* topological_order,
559 std::vector<T>* cycle) {
561 cycle);
562}
563
564inline std::vector<int> DenseIntTopologicalSortOrDie(
565 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
567}
568
570 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
572}
573
574template <typename T>
575std::vector<T> TopologicalSortOrDie(const std::vector<T>& nodes,
576 const std::vector<std::pair<T, T>>& arcs) {
578}
579
580template <typename T>
582 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
584}
585
586} // namespace util
587
588// BACKWARDS COMPATIBILITY
589// Some of the classes or functions have been exposed under the global namespace
590// or the util::graph:: namespace. Until all clients are fixed to use the
591// util:: namespace, we keep those versions around.
594template <typename T, bool stable_sort = false,
595 typename Hash = typename absl::flat_hash_map<T, int>::hasher,
596 typename KeyEqual =
597 typename absl::flat_hash_map<T, int, Hash>::key_equal>
599 : public ::util::TopologicalSorter<T, stable_sort, Hash, KeyEqual> {};
600
601namespace util {
602namespace graph {
603inline std::vector<int> DenseIntTopologicalSortOrDie(
604 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
605 return ::util::DenseIntTopologicalSortOrDie(num_nodes, arcs);
606}
608 int num_nodes, const std::vector<std::pair<int, int>>& arcs) {
609 return ::util::DenseIntStableTopologicalSortOrDie(num_nodes, arcs);
610}
611template <typename T>
613 const std::vector<T>& nodes, const std::vector<std::pair<T, T>>& arcs) {
614 return ::util::StableTopologicalSortOrDie<T>(nodes, arcs);
615}
616
617template <class AdjacencyLists>
618absl::StatusOr<std::vector<int>> FastTopologicalSort(
619 const AdjacencyLists& adj) {
620 const size_t num_nodes = adj.size();
621 if (num_nodes > std::numeric_limits<int>::max()) {
622 return absl::InvalidArgumentError("More than kint32max nodes");
623 }
624 std::vector<int> indegree(num_nodes, 0);
625 std::vector<int> topo_order;
626 topo_order.reserve(num_nodes);
627 for (int from = 0; from < num_nodes; ++from) {
628 for (const int head : adj[from]) {
629 // We cast to unsigned int to test "head < 0 || head ≥ num_nodes" with a
630 // single test. Microbenchmarks showed a ~1% overall performance gain.
631 if (static_cast<uint32_t>(head) >= num_nodes) {
632 return absl::InvalidArgumentError(
633 absl::StrFormat("Invalid arc in adj[%d]: %d (num_nodes=%d)", from,
634 head, num_nodes));
635 }
636 // NOTE(user): We could detect self-arcs here (head == from) and exit
637 // early, but microbenchmarks show a 2 to 4% slow-down if we do it, so we
638 // simply rely on self-arcs being detected as cycles in the topo sort.
639 ++indegree[head];
640 }
641 }
642 for (int i = 0; i < num_nodes; ++i) {
643 if (!indegree[i]) topo_order.push_back(i);
644 }
645 size_t num_visited = 0;
646 while (num_visited < topo_order.size()) {
647 const int from = topo_order[num_visited++];
648 for (const int head : adj[from]) {
649 if (!--indegree[head]) topo_order.push_back(head);
650 }
651 }
652 if (topo_order.size() < static_cast<size_t>(num_nodes)) {
653 return absl::InvalidArgumentError("The graph has a cycle");
654 }
655 return topo_order;
656}
657
658template <class AdjacencyLists>
659absl::StatusOr<std::vector<int>> FindCycleInGraph(const AdjacencyLists& adj) {
660 const size_t num_nodes = adj.size();
661 if (num_nodes > std::numeric_limits<int>::max()) {
662 return absl::InvalidArgumentError(
663 absl::StrFormat("Too many nodes: adj.size()=%d", adj.size()));
664 }
665
666 // To find a cycle, we start a DFS from each yet-unvisited node and
667 // try to find a cycle, if we don't find it then we know for sure that
668 // no cycle is reachable from any of the explored nodes (so, we don't
669 // explore them in later DFSs).
670 std::vector<bool> no_cycle_reachable_from(num_nodes, false);
671 // The DFS stack will contain a chain of nodes, from the root of the
672 // DFS to the current leaf.
673 struct DfsState {
674 int node;
675 // Points at the first child node that we did *not* yet look at.
676 int adj_list_index;
677 explicit DfsState(int _node) : node(_node), adj_list_index(0) {}
678 };
679 std::vector<DfsState> dfs_stack;
680 std::vector<bool> in_cur_stack(num_nodes, false);
681 for (int start_node = 0; start_node < static_cast<int>(num_nodes);
682 ++start_node) {
683 if (no_cycle_reachable_from[start_node]) continue;
684 // Start the DFS.
685 dfs_stack.push_back(DfsState(start_node));
686 in_cur_stack[start_node] = true;
687 while (!dfs_stack.empty()) {
688 DfsState* cur_state = &dfs_stack.back();
689 if (static_cast<size_t>(cur_state->adj_list_index) >=
690 adj[cur_state->node].size()) {
691 no_cycle_reachable_from[cur_state->node] = true;
692 in_cur_stack[cur_state->node] = false;
693 dfs_stack.pop_back();
694 continue;
695 }
696 // Look at the current child, and increase the current state's
697 // adj_list_index.
698 // TODO(user): Caching adj[cur_state->node] in a local stack to improve
699 // locality and so that the [] operator is called exactly once per node.
700 const int child = adj[cur_state->node][cur_state->adj_list_index++];
701 if (static_cast<size_t>(child) >= num_nodes) {
702 return absl::InvalidArgumentError(absl::StrFormat(
703 "Invalid child %d in adj[%d]", child, cur_state->node));
704 }
705 if (no_cycle_reachable_from[child]) continue;
706 if (in_cur_stack[child]) {
707 // We detected a cycle! It corresponds to the tail end of dfs_stack,
708 // in reverse order, until we find "child".
709 int cycle_start = dfs_stack.size() - 1;
710 while (dfs_stack[cycle_start].node != child) --cycle_start;
711 const int cycle_size = dfs_stack.size() - cycle_start;
712 std::vector<int> cycle(cycle_size);
713 for (int c = 0; c < cycle_size; ++c) {
714 cycle[c] = dfs_stack[cycle_start + c].node;
715 }
716 return cycle;
717 }
718 // Push the child onto the stack.
719 dfs_stack.push_back(DfsState(child));
720 in_cur_stack[child] = true;
721 // Verify that its adjacency list seems valid.
722 if (adj[child].size() > std::numeric_limits<int>::max()) {
723 return absl::InvalidArgumentError(absl::StrFormat(
724 "Invalid adj[%d].size() = %d", child, adj[child].size()));
725 }
726 }
727 }
728 // If we're here, then all the DFS stopped, and there is no cycle.
729 return std::vector<int>{};
730}
731
732} // namespace graph
733} // namespace util
734
735#endif // UTIL_GRAPH_TOPOLOGICALSORTER_H__
IntegerValue size
void AddEdges(const std::vector< std::pair< T, T > > &edges)
Shortcut to AddEdge() in bulk. Not optimized.
TopologicalSorter & operator=(const TopologicalSorter &)=delete
void AddNode(const T &node)
TopologicalSorter(const TopologicalSorter &)=delete
This type is neither copyable nor movable.
bool GetNext(T *node, bool *cyclic_ptr, std::vector< T > *output_cycle_nodes=nullptr)
void AddEdge(const T &from, const T &to)
static int RemoveDuplicates(std::vector< AdjacencyList > *lists, int skip_lists_smaller_than)
static
void AddEdges(absl::Span< const std::pair< int, int > > edges)
Performs AddEdge() in bulk. Much faster if you add all edges at once.
absl::InlinedVector< int, 4 > AdjacencyList
To store the adjacency lists efficiently.
DenseIntTopologicalSorterTpl & operator=(const DenseIntTopologicalSorterTpl &)=delete
bool GetNext(int *next_node_index, bool *cyclic, std::vector< int > *output_cycle_nodes=nullptr)
DenseIntTopologicalSorterTpl(const DenseIntTopologicalSorterTpl &)=delete
This type is neither copyable nor movable.
void ExtractCycle(std::vector< int > *cycle_nodes) const
To extract a cycle. When there is no cycle, cycle_nodes will be empty.
Block * next
std::vector< NodeIndex > topological_order
GraphType graph
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:242
void STLClearHashIfBig(T *obj, size_t limit)
Definition stl_util.h:180
absl::StatusOr< std::vector< int > > FastTopologicalSort(const AdjacencyLists &adj)
std::vector< int > DenseIntStableTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
absl::StatusOr< std::vector< int > > FindCycleInGraph(const AdjacencyLists &adj)
std::vector< T > StableTopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
std::vector< int > DenseIntTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
std::vector< T > TopologicalSortOrDieImpl(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
ABSL_MUST_USE_RESULT bool DenseIntTopologicalSortImpl(int num_nodes, const std::vector< std::pair< int, int > > &arcs, std::vector< int > *topological_order)
std::vector< int > DenseIntTopologicalSortOrDieImpl(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
ABSL_MUST_USE_RESULT bool TopologicalSortImpl(absl::Span< const 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)
Now, the OrDie() versions, which directly return the topological order.
ABSL_MUST_USE_RESULT bool RunTopologicalSorter(Sorter *sorter, const std::vector< std::pair< T, T > > &arcs, std::vector< T > *topological_order_or_cycle)
Internal wrapper around the *TopologicalSort classes.
A collections of i/o utilities for the Graph classes in ./graph.h.
std::vector< int > DenseIntTopologicalSortOrDie(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
::util::internal::DenseIntTopologicalSorterTpl< false > DenseIntTopologicalSorter
ABSL_MUST_USE_RESULT bool DenseIntStableTopologicalSort(int num_nodes, const std::vector< std::pair< int, int > > &arcs, std::vector< int > *topological_order)
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 > 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)
OrDie() variant of the above.
::util::internal::DenseIntTopologicalSorterTpl< true > DenseIntStableTopologicalSorter
std::vector< T > StableTopologicalSortOrDie(const std::vector< T > &nodes, const std::vector< std::pair< T, T > > &arcs)
OrDie() variant of the above.
ABSL_MUST_USE_RESULT bool DenseIntTopologicalSort(int num_nodes, const std::vector< std::pair< int, int > > &arcs, std::vector< int > *topological_order)
Implementations of the "simple API" functions declared at the top.
ABSL_MUST_USE_RESULT std::vector< int > FindCycleInDenseIntGraph(int num_nodes, const std::vector< std::pair< int, int > > &arcs)
______________________ END OF THE RECOMMENDED API ___________________________
trees with all degrees equal to
int head
int nodes
::util::DenseIntStableTopologicalSorter DenseIntStableTopologicalSorter
::util::DenseIntTopologicalSorter DenseIntTopologicalSorter