add utilities to graph API
This commit is contained in:
@@ -1029,8 +1029,14 @@ void BaseGraph<NodeIndexType, ArcIndexType, HasReverseArcs>::
|
||||
t##ArcIterator(*this, node, e)); \
|
||||
}
|
||||
|
||||
// Adapt our old iteration style to support range-based for loops.
|
||||
// Adapt our old iteration style to support range-based for loops. Add typedefs
|
||||
// required by std::iterator_traits.
|
||||
#define DEFINE_STL_ITERATOR_FUNCTIONS(iterator_class_name) \
|
||||
using iterator_category = std::input_iterator_tag; \
|
||||
using difference_type = ptrdiff_t; \
|
||||
using pointer = const ArcIndexType*; \
|
||||
using reference = const ArcIndexType&; \
|
||||
using value_type = ArcIndexType; \
|
||||
bool operator!=(const iterator_class_name& other) const { \
|
||||
return index_ != other.index_; \
|
||||
} \
|
||||
@@ -1152,6 +1158,12 @@ class ListGraph<NodeIndexType, ArcIndexType>::OutgoingArcIterator {
|
||||
template <typename NodeIndexType, typename ArcIndexType>
|
||||
class ListGraph<NodeIndexType, ArcIndexType>::OutgoingHeadIterator {
|
||||
public:
|
||||
using iterator_category = std::input_iterator_tag;
|
||||
using difference_type = ptrdiff_t;
|
||||
using pointer = const NodeIndexType*;
|
||||
using reference = const NodeIndexType&;
|
||||
using value_type = NodeIndexType;
|
||||
|
||||
OutgoingHeadIterator(const ListGraph& graph, NodeIndexType node)
|
||||
: graph_(graph), index_(graph.start_[node]) {
|
||||
DCHECK(graph.IsNodeValid(node));
|
||||
@@ -1583,13 +1595,8 @@ class ReverseArcListGraph<NodeIndexType, ArcIndexType>::OutgoingHeadIterator {
|
||||
DCHECK(Ok());
|
||||
index_ = graph_->next_[index_];
|
||||
}
|
||||
bool operator!=(
|
||||
const typename ReverseArcListGraph<
|
||||
NodeIndexType, ArcIndexType>::OutgoingHeadIterator& other) const {
|
||||
return index_ != other.index_;
|
||||
}
|
||||
ArcIndexType operator*() const { return Index(); }
|
||||
void operator++() { Next(); }
|
||||
|
||||
DEFINE_STL_ITERATOR_FUNCTIONS(OutgoingHeadIterator);
|
||||
|
||||
private:
|
||||
const ReverseArcListGraph* graph_;
|
||||
@@ -2156,6 +2163,23 @@ class CompleteBipartiteGraph
|
||||
ArcIndexType from) const;
|
||||
IntegerRange<NodeIndexType> operator[](NodeIndexType node) const;
|
||||
|
||||
// Deprecated interface.
|
||||
class OutgoingArcIterator {
|
||||
public:
|
||||
OutgoingArcIterator(const CompleteBipartiteGraph& graph, NodeIndexType node)
|
||||
: index_(graph.right_nodes_ * node),
|
||||
limit_(node >= graph.left_nodes_ ? index_
|
||||
: graph.right_nodes_ * (node + 1)) {}
|
||||
|
||||
bool Ok() const { return index_ < limit_; }
|
||||
ArcIndexType Index() const { return index_; }
|
||||
void Next() { index_++; }
|
||||
|
||||
private:
|
||||
ArcIndexType index_;
|
||||
const ArcIndexType limit_;
|
||||
};
|
||||
|
||||
private:
|
||||
const NodeIndexType left_nodes_;
|
||||
const NodeIndexType right_nodes_;
|
||||
@@ -2165,7 +2189,7 @@ template <typename NodeIndexType, typename ArcIndexType>
|
||||
NodeIndexType CompleteBipartiteGraph<NodeIndexType, ArcIndexType>::Head(
|
||||
ArcIndexType arc) const {
|
||||
DCHECK(this->IsArcValid(arc));
|
||||
return arc % right_nodes_;
|
||||
return left_nodes_ + arc % right_nodes_;
|
||||
}
|
||||
|
||||
template <typename NodeIndexType, typename ArcIndexType>
|
||||
@@ -2207,7 +2231,7 @@ template <typename NodeIndexType, typename ArcIndexType>
|
||||
IntegerRange<NodeIndexType> CompleteBipartiteGraph<
|
||||
NodeIndexType, ArcIndexType>::operator[](NodeIndexType node) const {
|
||||
if (node < left_nodes_) {
|
||||
return IntegerRange<NodeIndexType>(0, right_nodes_);
|
||||
return IntegerRange<NodeIndexType>(left_nodes_, left_nodes_ + right_nodes_);
|
||||
} else {
|
||||
return IntegerRange<NodeIndexType>(0, 0);
|
||||
}
|
||||
|
||||
@@ -27,15 +27,14 @@
|
||||
// anywhere, but you have to return to your start location.
|
||||
//
|
||||
// By complete we mean that the algorithm guarantees to compute the optimal
|
||||
// solution.
|
||||
// The algorithm uses dynamic programming. Its time complexity is
|
||||
// O(n * 2 ^ (n - 1)), where n is the number of nodes to be visited, and '^'
|
||||
// denotes exponentiation. Its space complexity is also O(n * 2 ^ (n - 1)).
|
||||
// solution. The algorithm uses dynamic programming. Its time complexity is
|
||||
// O(n^2 * 2^(n-1)), where n is the number of nodes to be visited, and '^'
|
||||
// denotes exponentiation. Its space complexity is O(n * 2 ^ (n - 1)).
|
||||
//
|
||||
// Note that the naive implementation of the SHPP
|
||||
// exploring all permutations without memorizing intermediate results would
|
||||
// have a complexity of (n - 1)! (factorial of (n - 1) ), which is much higher
|
||||
// than n * 2 ^ (n - 1). To convince oneself of this, just use Stirling's
|
||||
// than n^2 * 2^(n-1). To convince oneself of this, just use Stirling's
|
||||
// formula: n! ~ sqrt(2 * pi * n)*( n / exp(1)) ^ n.
|
||||
// Because of these complexity figures, the algorithm is not practical for
|
||||
// problems with more than 20 nodes.
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include <string>
|
||||
|
||||
#include "base/numbers.h"
|
||||
#include "base/hash.h"
|
||||
#include "base/split.h"
|
||||
#include "base/join.h"
|
||||
#include "base/murmur.h"
|
||||
@@ -34,8 +35,7 @@ namespace operations_research {
|
||||
|
||||
// Diagnoses whether a graph is symmetric. A graph is symmetric iff
|
||||
// for all (a, b), the number of arcs a->b is equal to the number of arcs b->a.
|
||||
// If it returns "false", the graph is certainly not symmetric; if it returns
|
||||
// "true" then the graph is most likely symmetric. It works in O(graph size).
|
||||
// Works in O(graph size).
|
||||
template <class Graph>
|
||||
bool GraphIsSymmetric(const Graph& graph);
|
||||
|
||||
@@ -47,6 +47,16 @@ template <class Graph>
|
||||
util::StatusOr<Graph*> RemapGraph(const Graph& graph,
|
||||
const std::vector<int>& new_node_index);
|
||||
|
||||
// Returns a std::string representation of a graph: one arc per line. Eg.:
|
||||
// "1->2\n3->3" for a graph with 4 nodes and 2 arcs (1->2) and (3->3).
|
||||
// Arcs are sorted by their tail, then by the order of OutgoingArcs().
|
||||
template <class Graph>
|
||||
std::string GraphToString(const Graph& graph);
|
||||
|
||||
// Returns a copy of "graph", without self-arcs and duplicate arcs.
|
||||
template <class Graph>
|
||||
std::unique_ptr<Graph> RemoveSelfArcsAndDuplicateArcs(const Graph& graph);
|
||||
|
||||
// Read a graph file in the simple ".g" format: the file should be a text file
|
||||
// containing only space-separated integers, whose first line is:
|
||||
// <num nodes> <num edges> [<num_colors> <index of first node with color #1>
|
||||
@@ -166,6 +176,36 @@ util::StatusOr<Graph*> RemapGraph(const Graph& old_graph,
|
||||
return new_graph.release();
|
||||
}
|
||||
|
||||
template <class Graph>
|
||||
std::string GraphToString(const Graph& graph) {
|
||||
std::string out;
|
||||
for (const typename Graph::NodeIndex node : graph.AllNodes()) {
|
||||
for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) {
|
||||
if (!out.empty()) out += '\n';
|
||||
StrAppend(&out, node, "->", graph.Head(arc));
|
||||
}
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
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;
|
||||
hash_set<std::pair<NodeIndex, NodeIndex>> arcs;
|
||||
for (const NodeIndex tail : graph.AllNodes()) {
|
||||
for (const ArcIndex arc : graph.OutgoingArcs(tail)) {
|
||||
const NodeIndex head = graph.Head(arc);
|
||||
if (head != tail && arcs.insert({tail, head}).second) {
|
||||
g->AddArc(tail, head);
|
||||
}
|
||||
}
|
||||
}
|
||||
g->Build();
|
||||
return g;
|
||||
}
|
||||
|
||||
template <class Graph>
|
||||
util::StatusOr<Graph*> ReadGraphFile(
|
||||
const std::string& filename, bool directed,
|
||||
|
||||
Reference in New Issue
Block a user