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