util.h 16.6 KB
Newer Older
Valentin Platzgummer's avatar
Valentin Platzgummer committed
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
// Copyright 2010-2018 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 <map>
#include <memory>
#include <set>
#include <string>
#include <unordered_map>
#include <vector>

#include "absl/container/inlined_vector.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,
                                  const std::vector<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,
                                          const std::vector<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 GetConnectedComponens().
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(const std::vector<int>& v, int n);

// Returns true iff the given vector is a permutation of [0..size()-1].
inline bool IsValidPermutation(const std::vector<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, const std::vector<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 @user: 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,
                                  const std::vector<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,
                                          const std::vector<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.
  std::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, const std::vector<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>. See go/inlined-vector-size.
  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 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_