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