graph: export from google3

This commit is contained in:
Corentin Le Molgat
2025-11-19 14:21:05 +01:00
parent 8159ee45fc
commit 96bddc82f9
5 changed files with 87 additions and 67 deletions

View File

@@ -20,6 +20,7 @@
#include <numeric>
#include <vector>
#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.

View File

@@ -49,10 +49,13 @@ namespace util {
// TODO(user): Currently only max-flow handles this graph, but not
// min-cost-flow.
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class FlowGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
class FlowGraph : public BaseGraph<FlowGraph<NodeIndexType, ArcIndexType>,
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<NodeIndexType, ArcIndexType, false> Base;
typedef BaseGraph<FlowGraph<NodeIndexType, ArcIndexType>, NodeIndexType,
ArcIndexType, false>
Base;
using Base::arc_capacity_;
using Base::const_capacities_;
using Base::node_capacity_;
@@ -146,7 +149,7 @@ class FlowGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
}
void Build() { Build(nullptr); }
void Build(std::vector<ArcIndexType>* permutation);
void Build(std::vector<ArcIndexType>* 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

View File

@@ -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 <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t,
bool HasNegativeReverseArcs = false>
class BaseGraph {
template <typename Impl, typename NodeIndexType = int32_t,
typename ArcIndexType = int32_t, bool HasNegativeReverseArcs = false>
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<ArcIndexType>::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<ArcIndexType>* 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<NodeIndexType, ArcIndexType>* v);
@@ -643,8 +659,11 @@ struct GraphTraits {
// result in the same order).
//
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class ListGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<NodeIndexType, ArcIndexType, false> Base;
class ListGraph : public BaseGraph<ListGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<ListGraph<NodeIndexType, ArcIndexType>, NodeIndexType,
ArcIndexType, false>
Base;
using Base::arc_capacity_;
using Base::const_capacities_;
using Base::node_capacity_;
@@ -679,17 +698,6 @@ class ListGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
// 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<ArcIndexType>* 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<NodeIndexType, ArcIndexType, false> {
// StaticGraphWithoutTail<>. This almost corresponds to a past implementation
// of StaticGraph<> @CL 116144340.
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class StaticGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<NodeIndexType, ArcIndexType, false> Base;
class StaticGraph : public BaseGraph<StaticGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<StaticGraph<NodeIndexType, ArcIndexType>, NodeIndexType,
ArcIndexType, false>
Base;
using Base::arc_capacity_;
using Base::const_capacities_;
using Base::node_capacity_;
@@ -811,8 +822,9 @@ class StaticGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
void AddNode(NodeIndexType node);
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head);
void Build(std::vector<ArcIndexType>* permutation) final;
void Build() { Build(nullptr); }
void Build(std::vector<ArcIndexType>* permutation);
bool IsBuilt() const final { return is_built_; }
private:
ArcIndexType DirectArcLimit(NodeIndexType node) const {
@@ -839,11 +851,14 @@ class StaticGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
// + 2 * (ArcIndexType + NodeIndexType) * arc_capacity() memory.
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class ReverseArcListGraph
: public BaseGraph<NodeIndexType, ArcIndexType, true> {
: public BaseGraph<ReverseArcListGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, true> {
static_assert(internal::IsSigned<ArcIndexType>(),
"ArcIndexType must be signed");
typedef BaseGraph<NodeIndexType, ArcIndexType, true> Base;
typedef BaseGraph<ReverseArcListGraph<NodeIndexType, ArcIndexType>,
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<ArcIndexType>* permutation);
private:
internal::Vector<NodeIndexType, ArcIndexType> start_;
internal::Vector<NodeIndexType, ArcIndexType> reverse_start_;
@@ -975,11 +987,14 @@ class ReverseArcListGraph
// time lookup function).
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class ReverseArcStaticGraph
: public BaseGraph<NodeIndexType, ArcIndexType, true> {
: public BaseGraph<ReverseArcStaticGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, true> {
static_assert(internal::IsSigned<ArcIndexType>(),
"ArcIndexType must be signed");
typedef BaseGraph<NodeIndexType, ArcIndexType, true> Base;
typedef BaseGraph<ReverseArcStaticGraph<NodeIndexType, ArcIndexType>,
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<ArcIndexType>* permutation) final;
void Build() { Build(nullptr); }
void Build(std::vector<ArcIndexType>* 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 <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
IntegerRange<NodeIndexType> BaseGraph<
NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::AllNodes() const {
IntegerRange<NodeIndexType>
BaseGraph<Impl, NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::AllNodes()
const {
return IntegerRange<NodeIndexType>(NodeIndexType(0), num_nodes_);
}
template <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
IntegerRange<ArcIndexType>
BaseGraph<NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::AllForwardArcs()
IntegerRange<ArcIndexType> BaseGraph<Impl, NodeIndexType, ArcIndexType,
HasNegativeReverseArcs>::AllForwardArcs()
const {
return IntegerRange<ArcIndexType>(ArcIndexType(0), num_arcs_);
}
template <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
NodeIndexType BaseGraph<NodeIndexType, ArcIndexType,
NodeIndexType BaseGraph<Impl, NodeIndexType, ArcIndexType,
HasNegativeReverseArcs>::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 <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
ArcIndexType BaseGraph<NodeIndexType, ArcIndexType,
ArcIndexType BaseGraph<Impl, NodeIndexType, ArcIndexType,
HasNegativeReverseArcs>::arc_capacity() const {
// TODO(user): Same questions as the ones in node_capacity().
return arc_capacity_ > num_arcs_ ? arc_capacity_ : num_arcs_;
}
template <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
void BaseGraph<NodeIndexType, ArcIndexType,
void BaseGraph<Impl, NodeIndexType, ArcIndexType,
HasNegativeReverseArcs>::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<NodeIndexType, ArcIndexType,
// Computes the cumulative sum of the entry in v. We only use it with
// in/out degree distribution, hence the Check() at the end.
template <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
void BaseGraph<NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::
void BaseGraph<Impl, NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::
ComputeCumulativeSum(internal::Vector<NodeIndexType, ArcIndexType>* v) {
DCHECK_EQ(v->size(), num_nodes_ + NodeIndexType(1));
ArcIndexType sum(0);
@@ -1189,9 +1206,9 @@ void BaseGraph<NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::
// - 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 <typename NodeIndexType, typename ArcIndexType,
template <typename Impl, typename NodeIndexType, typename ArcIndexType,
bool HasNegativeReverseArcs>
void BaseGraph<NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::
void BaseGraph<Impl, NodeIndexType, ArcIndexType, HasNegativeReverseArcs>::
BuildStartAndForwardHead(
internal::SVector<ArcIndexType, NodeIndexType>* head,
internal::Vector<NodeIndexType, ArcIndexType>* start,
@@ -1362,14 +1379,6 @@ void ListGraph<NodeIndexType, ArcIndexType>::ReserveArcs(ArcIndexType bound) {
next_.reserve(bound);
}
template <typename NodeIndexType, typename ArcIndexType>
void ListGraph<NodeIndexType, ArcIndexType>::Build(
std::vector<ArcIndexType>* permutation) {
if (permutation != nullptr) {
permutation->clear();
}
}
// StaticGraph implementation --------------------------------------------------
template <typename NodeIndexType, typename ArcIndexType>
@@ -1627,14 +1636,6 @@ ArcIndexType ReverseArcListGraph<NodeIndexType, ArcIndexType>::AddArc(
return num_arcs_++;
}
template <typename NodeIndexType, typename ArcIndexType>
void ReverseArcListGraph<NodeIndexType, ArcIndexType>::Build(
std::vector<ArcIndexType>* permutation) {
if (permutation != nullptr) {
permutation->clear();
}
}
template <typename NodeIndexType, typename ArcIndexType>
class ReverseArcListGraph<NodeIndexType,
ArcIndexType>::OutgoingOrOppositeIncomingArcIterator {
@@ -1852,8 +1853,12 @@ class ReverseArcStaticGraph<
// Nodes and arcs are implicit and not stored.
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class CompleteGraph : public BaseGraph<NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<NodeIndexType, ArcIndexType, false> Base;
class CompleteGraph
: public BaseGraph<CompleteGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<CompleteGraph<NodeIndexType, ArcIndexType>, NodeIndexType,
ArcIndexType, false>
Base;
using Base::arc_capacity_;
using Base::const_capacities_;
using Base::node_capacity_;
@@ -1936,8 +1941,11 @@ CompleteGraph<NodeIndexType, ArcIndexType>::operator[](
template <typename NodeIndexType = int32_t, typename ArcIndexType = int32_t>
class CompleteBipartiteGraph
: public BaseGraph<NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<NodeIndexType, ArcIndexType, false> Base;
: public BaseGraph<CompleteBipartiteGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, false> {
typedef BaseGraph<CompleteBipartiteGraph<NodeIndexType, ArcIndexType>,
NodeIndexType, ArcIndexType, false>
Base;
using Base::arc_capacity_;
using Base::const_capacities_;
using Base::node_capacity_;

View File

@@ -25,6 +25,7 @@
#include <vector>
#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;

View File

@@ -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<std::vector<int>> adj = {{..}, {..}, ..};