From 96bddc82f9aa3b5dc43a682f39ec465d0a2322f7 Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Wed, 19 Nov 2025 14:21:05 +0100 Subject: [PATCH] graph: export from google3 --- ortools/graph/connected_components.cc | 2 + ortools/graph/flow_graph.h | 9 +- ortools/graph/graph.h | 134 ++++++++++++++------------ ortools/graph/graph_test.cc | 4 + ortools/graph/topologicalsorter.h | 5 +- 5 files changed, 87 insertions(+), 67 deletions(-) diff --git a/ortools/graph/connected_components.cc b/ortools/graph/connected_components.cc index 62afd3994c..a07def46e5 100644 --- a/ortools/graph/connected_components.cc +++ b/ortools/graph/connected_components.cc @@ -20,6 +20,7 @@ #include #include +#include "absl/log/check.h" #include "ortools/base/stl_util.h" void DenseConnectedComponentsFinder::SetNumberOfNodes(int num_nodes) { @@ -27,6 +28,7 @@ void DenseConnectedComponentsFinder::SetNumberOfNodes(int num_nodes) { if (num_nodes == old_num_nodes) { return; } + CHECK_GE(num_nodes, 0) << "Number of nodes overflowed the `int` type."; CHECK_GT(num_nodes, old_num_nodes); // Each new node starts as an isolated component: // It has itself as root. diff --git a/ortools/graph/flow_graph.h b/ortools/graph/flow_graph.h index 4bb84b7daf..6718efa2a5 100644 --- a/ortools/graph/flow_graph.h +++ b/ortools/graph/flow_graph.h @@ -49,10 +49,13 @@ namespace util { // TODO(user): Currently only max-flow handles this graph, but not // min-cost-flow. template -class FlowGraph : public BaseGraph { +class FlowGraph : public BaseGraph, + NodeIndexType, ArcIndexType, false> { // Note that we do NOT use negated indices for reverse arc. So we use false // for the last template argument here HasNegativeReverseArcs. - typedef BaseGraph Base; + typedef BaseGraph, NodeIndexType, + ArcIndexType, false> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; @@ -146,7 +149,7 @@ class FlowGraph : public BaseGraph { } void Build() { Build(nullptr); } - void Build(std::vector* permutation); + void Build(std::vector* permutation) final; // This influence what Build() does. If true, we will detect already existing // pairs of (arc, reverse_arc) and only construct new reverse arc for the one diff --git a/ortools/graph/graph.h b/ortools/graph/graph.h index 5bd1cfd66d..6d598f0613 100644 --- a/ortools/graph/graph.h +++ b/ortools/graph/graph.h @@ -201,9 +201,10 @@ class Vector; // strong integer types (e.g. `StrongInt`). Strong integer types are types that // behave like integers (comparison, arithmetic, etc.), and are (explicitly) // constructible/convertible from/to integers. -template -class BaseGraph { +template +class BaseGraph // +{ public: // Typedef so you can use Graph::NodeIndex and Graph::ArcIndex to be generic // but also to improve the readability of your code. We also recommend @@ -291,6 +292,21 @@ class BaseGraph { static constexpr ArcIndexType kNilArc = std::numeric_limits::max(); + // Some graph implementations need to be finalized with Build() before they + // can be used. Build() may change the arc indices (which had been the + // return values of previous AddArc() calls): the new index of former arc #i + // will be stored in permutation[i] if #i is smaller than permutation.size(), + // or will be unchanged otherwise. If you don't care about these, just call + // the simple no-output version Build(). + // + // Note that some implementations become immutable after calling Build(). + // By default, Build() is a no-op. + virtual void Build(std::vector* permutation) { + if (permutation != nullptr) permutation->clear(); + } + void Build() { Build(nullptr); } + virtual bool IsBuilt() const { return true; } + protected: // Functions commented when defined because they are implementation details. void ComputeCumulativeSum(internal::Vector* v); @@ -643,8 +659,11 @@ struct GraphTraits { // result in the same order). // template -class ListGraph : public BaseGraph { - typedef BaseGraph Base; +class ListGraph : public BaseGraph, + NodeIndexType, ArcIndexType, false> { + typedef BaseGraph, NodeIndexType, + ArcIndexType, false> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; @@ -679,17 +698,6 @@ class ListGraph : public BaseGraph { // Note: Self referencing arcs and duplicate arcs are supported. ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head); - // Some graph implementations need to be finalized with Build() before they - // can be used. After Build() is called, the arc indices (which had been the - // return values of previous AddArc() calls) may change: the new index of - // former arc #i will be stored in permutation[i] if #i is smaller than - // permutation.size() or will be unchanged otherwise. If you don't care about - // these, just call the simple no-output version Build(). - // - // Note that some implementations become immutable after calling Build(). - void Build() { Build(nullptr); } - void Build(std::vector* permutation); - // Returns the tail/head of a valid arc. NodeIndexType Tail(ArcIndexType arc) const; NodeIndexType Head(ArcIndexType arc) const; @@ -759,8 +767,11 @@ class ListGraph : public BaseGraph { // StaticGraphWithoutTail<>. This almost corresponds to a past implementation // of StaticGraph<> @CL 116144340. template -class StaticGraph : public BaseGraph { - typedef BaseGraph Base; +class StaticGraph : public BaseGraph, + NodeIndexType, ArcIndexType, false> { + typedef BaseGraph, NodeIndexType, + ArcIndexType, false> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; @@ -811,8 +822,9 @@ class StaticGraph : public BaseGraph { void AddNode(NodeIndexType node); ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head); + void Build(std::vector* permutation) final; void Build() { Build(nullptr); } - void Build(std::vector* permutation); + bool IsBuilt() const final { return is_built_; } private: ArcIndexType DirectArcLimit(NodeIndexType node) const { @@ -839,11 +851,14 @@ class StaticGraph : public BaseGraph { // + 2 * (ArcIndexType + NodeIndexType) * arc_capacity() memory. template class ReverseArcListGraph - : public BaseGraph { + : public BaseGraph, + NodeIndexType, ArcIndexType, true> { static_assert(internal::IsSigned(), "ArcIndexType must be signed"); - typedef BaseGraph Base; + typedef BaseGraph, + NodeIndexType, ArcIndexType, true> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; @@ -954,9 +969,6 @@ class ReverseArcListGraph void AddNode(NodeIndexType node); ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head); - void Build() { Build(nullptr); } - void Build(std::vector* permutation); - private: internal::Vector start_; internal::Vector reverse_start_; @@ -975,11 +987,14 @@ class ReverseArcListGraph // time lookup function). template class ReverseArcStaticGraph - : public BaseGraph { + : public BaseGraph, + NodeIndexType, ArcIndexType, true> { static_assert(internal::IsSigned(), "ArcIndexType must be signed"); - typedef BaseGraph Base; + typedef BaseGraph, + NodeIndexType, ArcIndexType, true> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; @@ -1069,8 +1084,9 @@ class ReverseArcStaticGraph void AddNode(NodeIndexType node); ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head); + void Build(std::vector* permutation) final; void Build() { Build(nullptr); } - void Build(std::vector* permutation); + bool IsBuilt() const final { return is_built_; } private: ArcIndexType DirectArcLimit(NodeIndexType node) const { @@ -1124,41 +1140,42 @@ void Permute(const IntVector& permutation, Array* array_to_permute) { // BaseGraph implementation ---------------------------------------------------- -template -IntegerRange BaseGraph< - NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::AllNodes() const { +IntegerRange +BaseGraph::AllNodes() + const { return IntegerRange(NodeIndexType(0), num_nodes_); } -template -IntegerRange -BaseGraph::AllForwardArcs() +IntegerRange BaseGraph::AllForwardArcs() const { return IntegerRange(ArcIndexType(0), num_arcs_); } -template -NodeIndexType BaseGraph::node_capacity() const { // TODO(user): Is it needed? remove completely? return the real capacities // at the cost of having a different implementation for each graphs? return node_capacity_ > num_nodes_ ? node_capacity_ : num_nodes_; } -template -ArcIndexType BaseGraph::arc_capacity() const { // TODO(user): Same questions as the ones in node_capacity(). return arc_capacity_ > num_arcs_ ? arc_capacity_ : num_arcs_; } -template -void BaseGraph::FreezeCapacities() { // TODO(user): Only define this in debug mode at the cost of having a lot // of ifndef NDEBUG all over the place? remove the function completely ? @@ -1169,9 +1186,9 @@ void BaseGraph -void BaseGraph:: +void BaseGraph:: ComputeCumulativeSum(internal::Vector* v) { DCHECK_EQ(v->size(), num_nodes_ + NodeIndexType(1)); ArcIndexType sum(0); @@ -1189,9 +1206,9 @@ void BaseGraph:: // - Put the head of the new arc #i in (*head)[i]. // - Put in start[i] the index of the first arc with tail >= i. // - Update "permutation" to reflect the change, unless it is NULL. -template -void BaseGraph:: +void BaseGraph:: BuildStartAndForwardHead( internal::SVector* head, internal::Vector* start, @@ -1362,14 +1379,6 @@ void ListGraph::ReserveArcs(ArcIndexType bound) { next_.reserve(bound); } -template -void ListGraph::Build( - std::vector* permutation) { - if (permutation != nullptr) { - permutation->clear(); - } -} - // StaticGraph implementation -------------------------------------------------- template @@ -1627,14 +1636,6 @@ ArcIndexType ReverseArcListGraph::AddArc( return num_arcs_++; } -template -void ReverseArcListGraph::Build( - std::vector* permutation) { - if (permutation != nullptr) { - permutation->clear(); - } -} - template class ReverseArcListGraph::OutgoingOrOppositeIncomingArcIterator { @@ -1852,8 +1853,12 @@ class ReverseArcStaticGraph< // Nodes and arcs are implicit and not stored. template -class CompleteGraph : public BaseGraph { - typedef BaseGraph Base; +class CompleteGraph + : public BaseGraph, + NodeIndexType, ArcIndexType, false> { + typedef BaseGraph, NodeIndexType, + ArcIndexType, false> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; @@ -1936,8 +1941,11 @@ CompleteGraph::operator[]( template class CompleteBipartiteGraph - : public BaseGraph { - typedef BaseGraph Base; + : public BaseGraph, + NodeIndexType, ArcIndexType, false> { + typedef BaseGraph, + NodeIndexType, ArcIndexType, false> + Base; using Base::arc_capacity_; using Base::const_capacities_; using Base::node_capacity_; diff --git a/ortools/graph/graph_test.cc b/ortools/graph/graph_test.cc index 3cb1c6338c..fda59ac761 100644 --- a/ortools/graph/graph_test.cc +++ b/ortools/graph/graph_test.cc @@ -25,6 +25,7 @@ #include #include "absl/algorithm/container.h" +#include "absl/base/log_severity.h" #include "absl/log/check.h" #include "absl/random/random.h" #include "absl/strings/str_cat.h" @@ -37,7 +38,10 @@ namespace util { +using testing::_; +using testing::AllOf; using testing::ElementsAre; +using testing::Field; using testing::Pair; using testing::UnorderedElementsAre; diff --git a/ortools/graph/topologicalsorter.h b/ortools/graph/topologicalsorter.h index e171b5c805..d5295cd06f 100644 --- a/ortools/graph/topologicalsorter.h +++ b/ortools/graph/topologicalsorter.h @@ -63,6 +63,9 @@ namespace graph { // StaticGraph<> in ./graph.h: FastTopologicalSort() can take any such graph as // input. // +// If you have a util_graph::Graph and don't need input validation, consider +// util_graph::TopoOrder(): it has an even simpler API and is only 1.5x slower. +// // ERRORS: returns InvalidArgumentError if the input is broken (negative or // out-of-bounds integers) or if the graph is cyclic. In the latter case, the // error message will contain "cycle". Note that if cycles may occur in your @@ -72,7 +75,7 @@ namespace graph { // TIE BREAKING: the returned topological order is deterministic and fixed, and // corresponds to iterating on nodes in a LIFO (Breadth-first) order. // -// Benchmark: gpaste/6147236302946304, 4-10x faster than util_graph::TopoSort(). +// Benchmark: gpaste/4894742655664128. // // EXAMPLES: // std::vector> adj = {{..}, {..}, ..};