Google OR-Tools v9.14
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
util.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// A collections of utilities for the Graph classes in ./graph.h.
15
16#ifndef UTIL_GRAPH_UTIL_H_
17#define UTIL_GRAPH_UTIL_H_
18
19#include <algorithm>
20#include <cstdint>
21#include <limits>
22#include <memory>
23#include <set>
24#include <utility>
25#include <vector>
26
27#include "absl/container/btree_map.h"
28#include "absl/container/flat_hash_map.h"
29#include "absl/container/inlined_vector.h"
30#include "absl/log/check.h"
31#include "absl/types/span.h"
34#include "ortools/graph/graph.h"
36
37namespace util {
38
39// Here's a set of simple diagnosis tools. Notes:
40// - A self-arc is an arc from a node to itself.
41// - We say that an arc A->B is duplicate when there is another arc A->B in the
42// same graph.
43// - A graph is said "weakly connected" if it is connected when considering all
44// arcs as undirected edges.
45// - A graph is said "symmetric" iff for all (a, b), the number of arcs a->b
46// is equal to the number of arcs b->a.
47//
48// All these diagnosis work in O(graph size), since the inverse Ackerman
49// function is <= 5 for all practical instances, and are very fast.
50//
51// If the graph is a "static" kind, they must be finalized, except for
52// GraphHasSelfArcs() and GraphIsWeaklyConnected() which also support
53// non-finalized StaticGraph<>.
54template <class Graph>
55bool GraphHasSelfArcs(const Graph& graph);
56template <class Graph>
58template <class Graph>
59bool GraphIsSymmetric(const Graph& graph);
60template <class Graph>
62
63// Returns a fresh copy of a given graph.
64template <class Graph>
65std::unique_ptr<Graph> CopyGraph(const Graph& graph);
66
67// Creates a remapped copy of graph "graph", where node i becomes node
68// new_node_index[i].
69// "new_node_index" must be a valid permutation of [0..num_nodes-1] or the
70// behavior is undefined (it may die).
71// Note that you can call IsValidPermutation() to check it yourself.
72template <class Graph>
73std::unique_ptr<Graph> RemapGraph(const Graph& graph,
74 absl::Span<const int> new_node_index);
75
76// Gets the induced subgraph of "graph" restricted to the nodes in "nodes":
77// the resulting graph will have exactly nodes.size() nodes, and its
78// node #0 will be the former graph's node #nodes[0], etc.
79// See https://en.wikipedia.org/wiki/Induced_subgraph .
80// The "nodes" must be a valid subset (no repetitions) of
81// [0..graph.num_nodes()-1], or the behavior is undefined (it may die).
82// Note that you can call IsSubsetOf0N() to check it yourself.
83//
84// Current complexity: O(num old nodes + num new arcs). It could easily
85// be done in O(num new nodes + num new arcs) but with a higher constant.
86template <class Graph>
87std::unique_ptr<Graph> GetSubgraphOfNodes(const Graph& graph,
88 absl::Span<const int> nodes);
89
90// This can be used to view a directed graph (that supports reverse arcs)
91// from graph.h as un undirected graph: operator[](node) returns a
92// pseudo-container that iterates over all nodes adjacent to "node" (from
93// outgoing or incoming arcs).
94// CAVEAT: Self-arcs (aka loops) will appear twice.
95//
96// Example:
97// ReverseArcsStaticGraph<> dgraph;
98// ...
99// UndirectedAdjacencyListsOfDirectedGraph ugraph(dgraph);
100// for (int neighbor_of_node_42 : ugraph[42]) { ... }
101template <class Graph>
103 public:
104 static_assert(Graph::kHasNegativeReverseArcs,
105 "for forward graphs, directly use `Graph::operator[]`");
106
108 : graph_(graph) {}
109
110 typedef typename Graph::OutgoingOrOppositeIncomingArcIterator ArcIterator;
112 public:
113 explicit AdjacencyListIterator(const Graph& graph, ArcIterator&& arc_it)
114 : ArcIterator(arc_it), graph_(graph) {}
115 // Overwrite operator* to return the heads of the arcs.
116 typename Graph::NodeIndex operator*() const {
117 return graph_.Head(ArcIterator::operator*());
118 }
119
120 private:
121 const Graph& graph_;
122 };
123
124 // Returns a pseudo-container of all the nodes adjacent to "node".
126 const auto& arc_range = graph_.OutgoingOrOppositeIncomingArcs(node);
127 return {AdjacencyListIterator(graph_, arc_range.begin()),
128 AdjacencyListIterator(graph_, arc_range.end())};
129 }
130
131 private:
132 const Graph& graph_;
133};
134
135// CTAD for UndirectedAdjacencyListsOfDirectedGraph.
136template <class Graph>
139
140// Computes the weakly connected components of a directed graph that
141// provides the OutgoingOrOppositeIncomingArcs() API, and returns them
142// as a mapping from node to component index. See GetConnectedComponents().
143template <class Graph>
148
149// Returns true iff the given vector is a subset of [0..n-1], i.e.
150// all elements i are such that 0 <= i < n and no two elements are equal.
151// "n" must be >= 0 or the result is undefined.
152bool IsSubsetOf0N(absl::Span<const int> v, int n);
153
154// Returns true iff the given vector is a permutation of [0..size()-1].
155inline bool IsValidPermutation(absl::Span<const int> v) {
156 return IsSubsetOf0N(v, v.size());
157}
158
159// Returns a copy of "graph", without self-arcs and duplicate arcs.
160template <class Graph>
161std::unique_ptr<Graph> RemoveSelfArcsAndDuplicateArcs(const Graph& graph);
162
163// Given an arc path, changes it to a sub-path with the same source and
164// destination but without any cycle. Nothing happen if the path was already
165// without cycle.
166//
167// The graph class should support Tail(arc) and Head(arc). They should both
168// return an integer representing the corresponding tail/head of the passed arc.
169//
170// TODO(user): In some cases, there is more than one possible solution. We could
171// take some arc costs and return the cheapest path instead. Or return the
172// shortest path in term of number of arcs.
173template <class Graph>
174void RemoveCyclesFromPath(const Graph& graph, std::vector<int>* arc_path);
175
176// Returns true iff the given path contains a cycle.
177template <class Graph>
178bool PathHasCycle(const Graph& graph, absl::Span<const int> arc_path);
179
180// Returns a vector representing a mapping from arcs to arcs such that each arc
181// is mapped to another arc with its (tail, head) flipped, if such an arc
182// exists (otherwise it is mapped to -1).
183// If the graph is symmetric, the returned mapping is bijective and reflexive,
184// i.e. out[out[arc]] = arc for all "arc", where "out" is the returned vector.
185// If "die_if_not_symmetric" is true, this function CHECKs() that the graph
186// is symmetric.
187//
188// Self-arcs are always mapped to themselves.
189//
190// Note that since graphs may have multi-arcs, the mapping isn't necessarily
191// unique, hence the function name.
192//
193// PERFORMANCE: If you see this function taking too much memory and/or too much
194// time, reach out to viger@: one could halve the memory usage and speed it up.
195template <class Graph>
196std::vector<int> ComputeOnePossibleReverseArcMapping(const Graph& graph,
197 bool die_if_not_symmetric);
198
199// Implementations of the templated methods.
200
201template <class Graph>
203 for (const auto arc : graph.AllForwardArcs()) {
204 if (graph.Tail(arc) == graph.Head(arc)) return true;
205 }
206 return false;
207}
208
209template <class Graph>
211 typedef typename Graph::ArcIndex ArcIndex;
212 typedef typename Graph::NodeIndex NodeIndex;
213 std::vector<bool> tmp_node_mask(graph.num_nodes(), false);
214 for (const NodeIndex tail : graph.AllNodes()) {
215 for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
216 const NodeIndex head = graph.Head(arc);
217 if (tmp_node_mask[head]) return true;
218 tmp_node_mask[head] = true;
219 }
220 for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
221 tmp_node_mask[graph.Head(arc)] = false;
222 }
223 }
224 return false;
225}
226
227template <class Graph>
229 typedef typename Graph::NodeIndex NodeIndex;
230 typedef typename Graph::ArcIndex ArcIndex;
231 // Create a reverse copy of the graph.
232 StaticGraph<NodeIndex, ArcIndex> reverse_graph(graph.num_nodes(),
233 graph.num_arcs());
234 for (const NodeIndex node : graph.AllNodes()) {
235 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
236 reverse_graph.AddArc(graph.Head(arc), node);
237 }
238 }
239 reverse_graph.Build();
240 // Compare the graph to its reverse, one adjacency list at a time.
241 std::vector<ArcIndex> count(graph.num_nodes(), 0);
242 for (const NodeIndex node : graph.AllNodes()) {
243 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
244 ++count[graph.Head(arc)];
245 }
246 for (const ArcIndex arc : reverse_graph.OutgoingArcs(node)) {
247 if (--count[reverse_graph.Head(arc)] < 0) return false;
248 }
249 for (const ArcIndex arc : graph.OutgoingArcs(node)) {
250 if (count[graph.Head(arc)] != 0) return false;
251 }
252 }
253 return true;
254}
255
256template <class Graph>
258 typedef typename Graph::NodeIndex NodeIndex;
259 static_assert(std::numeric_limits<NodeIndex>::max() <= INT_MAX,
260 "GraphIsWeaklyConnected() isn't yet implemented for graphs"
261 " that support more than INT_MAX nodes. Reach out to"
262 " or-core-team@ if you need this.");
263 if (graph.num_nodes() == 0) return true;
265 union_find.SetNumberOfNodes(graph.num_nodes());
266 for (typename Graph::ArcIndex arc = 0; arc < graph.num_arcs(); ++arc) {
267 union_find.AddEdge(graph.Tail(arc), graph.Head(arc));
268 }
269 return union_find.GetNumberOfComponents() == 1;
270}
271
272template <class Graph>
273std::unique_ptr<Graph> CopyGraph(const Graph& graph) {
274 std::unique_ptr<Graph> new_graph(
275 new Graph(graph.num_nodes(), graph.num_arcs()));
276 for (const auto node : graph.AllNodes()) {
277 for (const auto arc : graph.OutgoingArcs(node)) {
278 new_graph->AddArc(node, graph.Head(arc));
279 }
280 }
281 new_graph->Build();
282 return new_graph;
283}
284
285template <class Graph>
286std::unique_ptr<Graph> RemapGraph(const Graph& old_graph,
287 absl::Span<const int> new_node_index) {
288 DCHECK(IsValidPermutation(new_node_index)) << "Invalid permutation";
289 const int num_nodes = old_graph.num_nodes();
290 CHECK_EQ(new_node_index.size(), num_nodes);
291 std::unique_ptr<Graph> new_graph(new Graph(num_nodes, old_graph.num_arcs()));
292 typedef typename Graph::NodeIndex NodeIndex;
293 typedef typename Graph::ArcIndex ArcIndex;
294 for (const NodeIndex node : old_graph.AllNodes()) {
295 for (const ArcIndex arc : old_graph.OutgoingArcs(node)) {
296 new_graph->AddArc(new_node_index[node],
297 new_node_index[old_graph.Head(arc)]);
298 }
299 }
300 new_graph->Build();
301 return new_graph;
302}
303
304template <class Graph>
305std::unique_ptr<Graph> GetSubgraphOfNodes(const Graph& old_graph,
306 absl::Span<const int> nodes) {
307 typedef typename Graph::NodeIndex NodeIndex;
308 typedef typename Graph::ArcIndex ArcIndex;
309 DCHECK(IsSubsetOf0N(nodes, old_graph.num_nodes())) << "Invalid subset";
310 std::vector<NodeIndex> new_node_index(old_graph.num_nodes(), -1);
311 for (NodeIndex new_index = 0; new_index < nodes.size(); ++new_index) {
312 new_node_index[nodes[new_index]] = new_index;
313 }
314 // Do a first pass to count the arcs, so that we don't allocate more memory
315 // than needed.
316 ArcIndex num_arcs = 0;
317 for (const NodeIndex node : nodes) {
318 for (const ArcIndex arc : old_graph.OutgoingArcs(node)) {
319 if (new_node_index[old_graph.Head(arc)] != -1) ++num_arcs;
320 }
321 }
322 // A second pass where we actually copy the subgraph.
323 // NOTE(user): there might seem to be a bit of duplication with RemapGraph(),
324 // but there is a key difference: the loop below only iterates on "nodes",
325 // which could be much smaller than all the graph's nodes.
326 std::unique_ptr<Graph> new_graph(new Graph(nodes.size(), num_arcs));
327 for (NodeIndex new_tail = 0; new_tail < nodes.size(); ++new_tail) {
328 const NodeIndex old_tail = nodes[new_tail];
329 for (const ArcIndex arc : old_graph.OutgoingArcs(old_tail)) {
330 const NodeIndex new_head = new_node_index[old_graph.Head(arc)];
331 if (new_head != -1) new_graph->AddArc(new_tail, new_head);
332 }
333 }
334 new_graph->Build();
335 return new_graph;
336}
337
338template <class Graph>
339std::unique_ptr<Graph> RemoveSelfArcsAndDuplicateArcs(const Graph& graph) {
340 std::unique_ptr<Graph> g(new Graph(graph.num_nodes(), graph.num_arcs()));
341 typedef typename Graph::ArcIndex ArcIndex;
342 typedef typename Graph::NodeIndex NodeIndex;
343 std::vector<bool> tmp_node_mask(graph.num_nodes(), false);
344 for (const NodeIndex tail : graph.AllNodes()) {
345 for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
346 const NodeIndex head = graph.Head(arc);
347 if (head != tail && !tmp_node_mask[head]) {
348 tmp_node_mask[head] = true;
349 g->AddArc(tail, head);
350 }
351 }
352 for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
353 tmp_node_mask[graph.Head(arc)] = false;
354 }
355 }
356 g->Build();
357 return g;
358}
359
360template <class Graph>
361void RemoveCyclesFromPath(const Graph& graph, std::vector<int>* arc_path) {
362 if (arc_path->empty()) return;
363
364 // This maps each node to the latest arc in the given path that leaves it.
365 absl::btree_map<int, int> last_arc_leaving_node;
366 for (const int arc : *arc_path) last_arc_leaving_node[graph.Tail(arc)] = arc;
367
368 // Special case for the destination.
369 // Note that this requires that -1 is not a valid arc of Graph.
370 last_arc_leaving_node[graph.Head(arc_path->back())] = -1;
371
372 // Reconstruct the path by starting at the source and then following the
373 // "next" arcs. We override the given arc_path at the same time.
374 int node = graph.Tail(arc_path->front());
375 int new_size = 0;
376 while (new_size < arc_path->size()) { // To prevent cycle on bad input.
377 const int arc = gtl::FindOrDie(last_arc_leaving_node, node);
378 if (arc == -1) break;
379 (*arc_path)[new_size++] = arc;
380 node = graph.Head(arc);
381 }
382 arc_path->resize(new_size);
383}
384
385template <class Graph>
386bool PathHasCycle(const Graph& graph, absl::Span<const int> arc_path) {
387 if (arc_path.empty()) return false;
388 std::set<int> seen;
389 seen.insert(graph.Tail(arc_path.front()));
390 for (const int arc : arc_path) {
391 if (!gtl::InsertIfNotPresent(&seen, graph.Head(arc))) return true;
392 }
393 return false;
394}
395
396template <class Graph>
398 const Graph& graph, bool die_if_not_symmetric) {
399 std::vector<int> reverse_arc(graph.num_arcs(), -1);
400 // We need a multi-map since a given (tail,head) may appear several times.
401 // NOTE(user): It's free, in terms of space, to use InlinedVector<int, 4>
402 // rather than std::vector<int>.
403 absl::flat_hash_map<std::pair</*tail*/ int, /*head*/ int>,
404 absl::InlinedVector<int, 4>>
405 arc_map;
406
407 for (int arc = 0; arc < graph.num_arcs(); ++arc) {
408 const int tail = graph.Tail(arc);
409 const int head = graph.Head(arc);
410 if (tail == head) {
411 // Special case: directly map any self-arc to itself.
412 reverse_arc[arc] = arc;
413 continue;
414 }
415 // Lookup for the reverse arc of the current one...
416 auto it = arc_map.find({head, tail});
417 if (it != arc_map.end()) {
418 // Found a reverse arc! Store the mapping and remove the
419 // reverse arc from the map.
420 reverse_arc[arc] = it->second.back();
421 reverse_arc[it->second.back()] = arc;
422 if (it->second.size() > 1) {
423 it->second.pop_back();
424 } else {
425 arc_map.erase(it);
426 }
427 } else {
428 // Reverse arc not in the map. Add the current arc to the map.
429 arc_map[{tail, head}].push_back(arc);
430 }
431 }
432 // Algorithm check, for debugging.
433 if (DEBUG_MODE) {
434 int64_t num_unmapped_arcs = 0;
435 for (const auto& p : arc_map) {
436 num_unmapped_arcs += p.second.size();
437 }
438 DCHECK_EQ(std::count(reverse_arc.begin(), reverse_arc.end(), -1),
439 num_unmapped_arcs);
440 }
441 if (die_if_not_symmetric) {
442 CHECK_EQ(arc_map.size(), 0)
443 << "The graph is not symmetric: " << arc_map.size() << " of "
444 << graph.num_arcs() << " arcs did not have a reverse.";
445 }
446 return reverse_arc;
447}
448
449} // namespace util
450
451#endif // UTIL_GRAPH_UTIL_H_
A connected components finder that only works on dense ints.
bool AddEdge(int node1, int node2)
NodeIndexType num_nodes() const
Definition graph.h:230
static constexpr bool kHasNegativeReverseArcs
Definition graph.h:215
ArcIndexType num_arcs() const
Returns the number of valid arcs in the graph.
Definition graph.h:234
IntegerRange< NodeIndex > AllNodes() const
BaseGraph implementation -------------------------------------------------—.
Definition graph.h:1126
BeginEndWrapper< OutgoingArcIterator > OutgoingArcs(NodeIndexType node) const
Definition graph.h:709
NodeIndexType Head(ArcIndexType arc) const
Definition graph.h:1311
ArcIndexType AddArc(NodeIndexType tail, NodeIndexType head)
Definition graph.h:1423
IntegerRange< ArcIndexType > OutgoingArcs(NodeIndexType node) const
Definition graph.h:789
NodeIndexType Head(ArcIndexType arc) const
Definition graph.h:1451
AdjacencyListIterator(const Graph &graph, ArcIterator &&arc_it)
Definition util.h:113
Graph::NodeIndex operator*() const
Overwrite operator* to return the heads of the arcs.
Definition util.h:116
Graph::OutgoingOrOppositeIncomingArcIterator ArcIterator
Definition util.h:110
UndirectedAdjacencyListsOfDirectedGraph(const Graph &graph)
Definition util.h:107
BeginEndWrapper< AdjacencyListIterator > operator[](int node) const
Returns a pseudo-container of all the nodes adjacent to "node".
Definition util.h:125
const bool DEBUG_MODE
Definition macros.h:24
bool InsertIfNotPresent(Collection *const collection, const typename Collection::value_type &value)
Definition map_util.h:127
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition map_util.h:211
A collections of i/o utilities for the Graph classes in ./graph.h.
void RemoveCyclesFromPath(const Graph &graph, std::vector< int > *arc_path)
Definition util.h:361
bool IsValidPermutation(absl::Span< const int > v)
Returns true iff the given vector is a permutation of [0..size()-1].
Definition util.h:155
std::unique_ptr< Graph > CopyGraph(const Graph &graph)
Returns a fresh copy of a given graph.
Definition util.h:273
std::vector< int > GetConnectedComponents(int num_nodes, const UndirectedGraph &graph)
UndirectedAdjacencyListsOfDirectedGraph(const Graph &) -> UndirectedAdjacencyListsOfDirectedGraph< Graph >
CTAD for UndirectedAdjacencyListsOfDirectedGraph.
bool PathHasCycle(const Graph &graph, absl::Span< const int > arc_path)
Returns true iff the given path contains a cycle.
Definition util.h:386
bool GraphHasDuplicateArcs(const Graph &graph)
Definition util.h:210
bool GraphIsSymmetric(const Graph &graph)
Definition util.h:228
std::unique_ptr< Graph > RemapGraph(const Graph &graph, absl::Span< const int > new_node_index)
Definition util.h:286
std::unique_ptr< Graph > RemoveSelfArcsAndDuplicateArcs(const Graph &graph)
Returns a copy of "graph", without self-arcs and duplicate arcs.
Definition util.h:339
std::vector< int > GetWeaklyConnectedComponents(const Graph &graph)
Definition util.h:144
bool GraphIsWeaklyConnected(const Graph &graph)
Definition util.h:257
bool GraphHasSelfArcs(const Graph &graph)
Implementations of the templated methods.
Definition util.h:202
bool IsSubsetOf0N(absl::Span< const int > v, int n)
Definition util.cc:22
std::unique_ptr< Graph > GetSubgraphOfNodes(const Graph &graph, absl::Span< const int > nodes)
Definition util.h:305
std::vector< int > ComputeOnePossibleReverseArcMapping(const Graph &graph, bool die_if_not_symmetric)
Definition util.h:397
ListGraph Graph
Defining the simplest Graph interface as Graph for convenience.
Definition graph.h:2053