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