graph: export from google3

dump_vars: Add support for StrongInt and StrongVector
This commit is contained in:
Corentin Le Molgat
2025-06-16 14:54:04 +02:00
parent 56e565a2e7
commit 7096031050
16 changed files with 674 additions and 467 deletions

View File

@@ -183,6 +183,8 @@ cc_library(
"//conditions:default": [],
}),
deps = [
":strong_int",
":strong_vector",
"@abseil-cpp//absl/container:inlined_vector",
],
)
@@ -199,6 +201,8 @@ cc_test(
}),
deps = [
":dump_vars",
":strong_int",
":strong_vector",
"@abseil-cpp//absl/strings",
"@googletest//:gtest_main",
],

View File

@@ -48,6 +48,8 @@
#include <vector>
#include "absl/container/inlined_vector.h"
#include "ortools/base/strong_int.h"
#include "ortools/base/strong_vector.h"
/* need extra level to force extra eval */
#define DUMP_FOR_EACH_N0(F)
@@ -138,6 +140,15 @@ std::ostream& operator<<(std::ostream& os, const ::std::optional<T>& opt) {
return os;
}
// needed by graph tests
template <typename T, typename U>
std::ostream& operator<<(std::ostream& os, const ::util_intops::StrongVector<T, U>& vec) {
for (U it : vec) {
os << ::std::to_string(it) << ',';
}
return os;
}
using DumpNames = ::std::vector<::std::string>;
struct print_fields {

View File

@@ -21,6 +21,12 @@
#include <vector>
#include "gtest/gtest.h"
#include "ortools/base/strong_int.h"
#include "ortools/base/strong_vector.h"
namespace util_intops {
DEFINE_STRONG_INT_TYPE(CustomStrongInt, uint32_t);
} // namespace util_intops
namespace operations_research::base {
namespace {
@@ -124,6 +130,18 @@ TEST(DumpVars, Vector) {
EXPECT_EQ("vec = 49.299999,3.140000,", DUMP_VARS(vec).str());
}
TEST(DumpVars, StrongInt) {
::util_intops::CustomStrongInt val(42);
EXPECT_EQ(R"(val = 42)", ToString(DUMP_VARS(val)));
EXPECT_EQ(R"(val = 42)", DUMP_VARS(val).str());
}
TEST(DumpVars, StrongVector) {
::util_intops::StrongVector<::util_intops::CustomStrongInt, float> vec = {49.3, 3.14};
EXPECT_EQ(R"(vec = 49.299999,3.140000,)", ToString(DUMP_VARS(vec)));
EXPECT_EQ(R"(vec = 49.299999,3.140000,)", DUMP_VARS(vec).str());
}
TEST(DumpVars, Optional) {
std::optional<float> of = {};
EXPECT_EQ("of = (none)", ToString(DUMP_VARS(of)));

View File

@@ -52,6 +52,8 @@ cc_test(
":graph",
"//ortools/base:gmock_main",
"//ortools/base:intops",
"//ortools/base:strong_vector",
"@abseil-cpp//absl/algorithm:container",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/random",
"@abseil-cpp//absl/strings",
@@ -86,7 +88,9 @@ cc_library(
hdrs = ["bounded_dijkstra.h"],
deps = [
":graph",
"//ortools/base:intops",
"//ortools/base:iterator_adaptors",
"//ortools/base:strong_vector",
"//ortools/base:threadpool",
"//ortools/base:top_n",
"@abseil-cpp//absl/algorithm:container",
@@ -107,6 +111,7 @@ cc_test(
":test_util",
"//ortools/base:dump_vars",
"//ortools/base:gmock_main",
"//ortools/base:intops",
"//ortools/util:flat_matrix",
"@abseil-cpp//absl/log:check",
"@abseil-cpp//absl/random",
@@ -858,7 +863,7 @@ cc_test(
deps = [
":iterators",
"//ortools/base:gmock_main",
"//ortools/base:strong_int",
"//ortools/base:intops",
],
)

View File

@@ -15,8 +15,10 @@
#define OR_TOOLS_GRAPH_BOUNDED_DIJKSTRA_H_
#include <algorithm>
#include <cstddef>
#include <functional>
#include <limits>
#include <type_traits>
#include <utility>
#include <vector>
@@ -25,6 +27,8 @@
#include "absl/log/check.h"
#include "absl/types/span.h"
#include "ortools/base/iterator_adaptors.h"
#include "ortools/base/strong_int.h"
#include "ortools/base/strong_vector.h"
#include "ortools/base/top_n.h"
#include "ortools/graph/graph.h"
@@ -54,22 +58,40 @@ namespace operations_research {
// is >= limit we will return {limit, {}}. As a consequence any arc length >=
// limit is the same as no arc. The code is also overflow-safe and will behave
// correctly if the limit is int64max or infinity.
template <typename DistanceType>
std::pair<DistanceType, std::vector<int>> SimpleOneToOneShortestPath(
int source, int destination, absl::Span<const int> tails,
absl::Span<const int> heads, absl::Span<const DistanceType> lengths,
template <typename NodeIndex, typename DistanceType>
std::pair<DistanceType, std::vector<NodeIndex>> SimpleOneToOneShortestPath(
NodeIndex source, NodeIndex destination, absl::Span<const NodeIndex> tails,
absl::Span<const NodeIndex> heads, absl::Span<const DistanceType> lengths,
DistanceType limit = std::numeric_limits<DistanceType>::max());
template <class T>
namespace internal {
// TODO(user): We should move `is_strong_int` to util/intops/strong_int.h.
template <typename T>
struct is_strong_int : std::false_type {};
template <typename Tag, typename Native, typename Validator>
struct is_strong_int<::util_intops::StrongInt<Tag, Native, Validator>>
: std::true_type {};
template <typename IndexType, typename ValueType>
using IndexedVector =
std::conditional_t<is_strong_int<IndexType>::value,
::util_intops::StrongVector<IndexType, ValueType>,
std::vector<ValueType>>;
template <class T, typename ArcIndex>
class ElementGetter {
public:
explicit ElementGetter(const std::vector<T>& c) : c_(c) {}
const T& operator()(int index) const { return c_[index]; }
explicit ElementGetter(const IndexedVector<ArcIndex, T>& c) : c_(c) {}
const T& operator()(ArcIndex index) const { return c_[index]; }
private:
const std::vector<T>& c_;
const IndexedVector<ArcIndex, T>& c_;
};
} // namespace internal
// A wrapper that holds the memory needed to run many bounded shortest path
// computations on the given graph. The graph must implement the
// interface described in graph.h (without the need for reverse arcs).
@@ -92,12 +114,20 @@ class ElementGetter {
// negative source_offset, arc with a length greater than the distance_limit can
// still be considered!
template <class GraphType, class DistanceType,
class ArcLengthFunctor = ElementGetter<DistanceType>>
class ArcLengthFunctor = internal::ElementGetter<
DistanceType, typename GraphType::ArcIndex>>
class BoundedDijkstraWrapper {
public:
typedef typename GraphType::NodeIndex node_type;
typedef typename GraphType::NodeIndex NodeIndex;
typedef typename GraphType::ArcIndex ArcIndex;
typedef DistanceType distance_type;
// A vector of T, indexed by NodeIndex/ArcIndex.
template <typename T>
using ByNode = internal::IndexedVector<NodeIndex, T>;
template <typename T>
using ByArc = internal::IndexedVector<ArcIndex, T>;
// IMPORTANT: Both arguments must outlive the class. The arc lengths cannot be
// negative and the vector must be of the correct size (both preconditions are
// CHECKed).
@@ -106,7 +136,7 @@ class BoundedDijkstraWrapper {
// RunBoundedDijkstra(). That's fine. Doing so will obviously invalidate the
// reader API of the last Dijkstra run, which could return junk, or crash.
BoundedDijkstraWrapper(const GraphType* graph,
const std::vector<DistanceType>* arc_lengths);
const ByArc<DistanceType>* arc_lengths);
// Variant that takes a custom arc length functor and copies it locally.
BoundedDijkstraWrapper(const GraphType* graph,
@@ -116,8 +146,8 @@ class BoundedDijkstraWrapper {
// of the graph within the distance limit (exclusive). The first element of
// the returned vector will always be the source_node with a distance of zero.
// See RunBoundedDijkstraFromMultipleSources() for more information.
const std::vector<int>& RunBoundedDijkstra(int source_node,
DistanceType distance_limit) {
const std::vector<NodeIndex>& RunBoundedDijkstra(
NodeIndex source_node, DistanceType distance_limit) {
return RunBoundedDijkstraFromMultipleSources({{source_node, 0}},
distance_limit);
}
@@ -127,7 +157,8 @@ class BoundedDijkstraWrapper {
//
// If this returns true, you can get the path distance with distances()[to]
// and the path with ArcPathTo(to) or NodePathTo(to).
bool OneToOneShortestPath(int from, int to, DistanceType distance_limit);
bool OneToOneShortestPath(NodeIndex from, NodeIndex to,
DistanceType distance_limit);
// Returns the list of all the nodes which are under the given distance limit
// (exclusive) from at least one of the given source nodes (which also have
@@ -136,8 +167,8 @@ class BoundedDijkstraWrapper {
// By "distance", we mean the length of the shortest path from any source
// plus the source's distance offset, where the length of a path is the
// sum of the length of its arcs
const std::vector<int>& RunBoundedDijkstraFromMultipleSources(
const std::vector<std::pair<int, DistanceType>>&
const std::vector<NodeIndex>& RunBoundedDijkstraFromMultipleSources(
const std::vector<std::pair<NodeIndex, DistanceType>>&
sources_with_distance_offsets,
DistanceType distance_limit);
@@ -162,10 +193,11 @@ class BoundedDijkstraWrapper {
//
// Note that the distances() will take the source offsets into account,
// but not the destination offsets.
std::vector<int> RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
const std::vector<std::pair<int, DistanceType>>&
std::vector<NodeIndex>
RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
const std::vector<std::pair<NodeIndex, DistanceType>>&
sources_with_distance_offsets,
const std::vector<std::pair<int, DistanceType>>&
const std::vector<std::pair<NodeIndex, DistanceType>>&
destinations_with_distance_offsets,
int num_destinations_to_reach, DistanceType distance_limit);
@@ -174,19 +206,19 @@ class BoundedDijkstraWrapper {
// happens at most once per node, when popping it from the Dijkstra queue,
// meaning that the node has been fully 'processed'). This callback may modify
// the distance limit dynamically, thus affecting the stopping criterion.
const std::vector<int>& RunBoundedDijkstraWithSettledNodeCallback(
const std::vector<std::pair<int, DistanceType>>&
const std::vector<NodeIndex>& RunBoundedDijkstraWithSettledNodeCallback(
const std::vector<std::pair<NodeIndex, DistanceType>>&
sources_with_distance_offsets,
std::function<void(node_type settled_node, DistanceType settled_distance,
std::function<void(NodeIndex settled_node, DistanceType settled_distance,
DistanceType* distance_limit)>
settled_node_callback,
DistanceType distance_limit);
// Returns true if `node` was reached by the last Run*() call.
bool IsReachable(int node) const { return is_reached_[node]; }
bool IsReachable(NodeIndex node) const { return is_reached_[node]; }
// Returns all the reached nodes form the previous Run*() call.
const std::vector<int>& reached_nodes() const { return reached_nodes_; }
const ByNode<NodeIndex>& reached_nodes() const { return reached_nodes_; }
// The following vectors are all indexed by graph node indices.
//
@@ -194,7 +226,7 @@ class BoundedDijkstraWrapper {
// reached nodes are updated, the others will contain junk.
// The distance of the nodes from their source.
const std::vector<DistanceType>& distances() const { return distances_; }
const ByNode<DistanceType>& distances() const { return distances_; }
// The parent of the nodes in the shortest path from their source.
// When a node doesn't have any parent (it has to be a source), its parent
@@ -203,27 +235,29 @@ class BoundedDijkstraWrapper {
// arcs have a length of zero.
// Note also that some sources may have parents, because of the initial
// distances.
const std::vector<int>& parents() const { return parents_; }
const ByNode<NodeIndex>& parents() const { return parents_; }
// The arc reaching a given node in the path from their source.
// arc_from_source()[x] is undefined (i.e. junk) when parents()[x] == x.
const std::vector<int>& arc_from_source() const { return arc_from_source_; }
const ByNode<ArcIndex>& arc_from_source() const { return arc_from_source_; }
// Returns the list of all the arcs in the shortest path from the node's
// source to the node.
std::vector<int> ArcPathTo(int node) const;
std::vector<ArcIndex> ArcPathTo(NodeIndex node) const;
ABSL_DEPRECATED("Use ArcPathTo() instead.")
std::vector<int> ArcPathToNode(int node) const { return ArcPathTo(node); }
std::vector<ArcIndex> ArcPathToNode(NodeIndex node) const {
return ArcPathTo(node);
}
// Returns the list of all the nodes in the shortest path from the node's
// source to the node. This always start by the node's source, and end by
// the given node. In the case that source == node, returns {node}.
std::vector<int> NodePathTo(int node) const;
std::vector<NodeIndex> NodePathTo(NodeIndex node) const;
// Returns the node's source. This is especially useful when running
// Dijkstras from multiple sources.
int SourceOfShortestPathToNode(int node) const;
NodeIndex SourceOfShortestPathToNode(NodeIndex node) const;
// Original Source/Destination index extraction, after a call to the
// multi-source and/or multi-destination variants:
@@ -239,16 +273,16 @@ class BoundedDijkstraWrapper {
// rely on the value.
//
// These methods are invalidated by the next RunBoundedDijkstra*() call.
int GetSourceIndex(int node) const;
int GetDestinationIndex(int node) const;
int GetSourceIndex(NodeIndex node) const;
int GetDestinationIndex(NodeIndex node) const;
// Trivial accessors to the underlying graph and arc lengths.
const GraphType& graph() const { return *graph_; }
const std::vector<DistanceType>& arc_lengths() const {
const ByArc<DistanceType>& arc_lengths() const {
CHECK(arc_lengths_);
return *arc_lengths_;
}
DistanceType GetArcLength(int arc) const {
DistanceType GetArcLength(ArcIndex arc) const {
const DistanceType length = arc_length_functor_(arc);
DCHECK_GE(length, 0);
return length;
@@ -262,18 +296,18 @@ class BoundedDijkstraWrapper {
// The Graph and length of each arc.
const GraphType* const graph_;
ArcLengthFunctor arc_length_functor_;
const std::vector<DistanceType>* const arc_lengths_;
const ByArc<DistanceType>* const arc_lengths_;
// Data about the last Dijkstra run.
std::vector<DistanceType> distances_;
std::vector<int> parents_;
std::vector<int> arc_from_source_;
std::vector<bool> is_reached_;
std::vector<int> reached_nodes_;
ByNode<DistanceType> distances_;
ByNode<NodeIndex> parents_;
ByNode<ArcIndex> arc_from_source_;
ByNode<bool> is_reached_;
std::vector<NodeIndex> reached_nodes_;
// Priority queue of nodes, ordered by their distance to the source.
struct NodeDistance {
node_type node; // The target node.
NodeIndex node; // The target node.
DistanceType distance; // Its distance from the source.
bool operator<(const NodeDistance& other) const {
@@ -287,7 +321,7 @@ class BoundedDijkstraWrapper {
// or ieee754 floating-point, when the machine is little endian, and
// when the total size of NodeDistance equals 16 bytes).
// And here are the speeds of the BM_GridGraph benchmark (in which
// DistanceType=int64_t and node_type=int32_t), done with benchy
// DistanceType=int64_t and NodeIndex=int32_t), done with benchy
// --runs=20: 0) BM_GridGraph<true> 9.22ms ± 5% BM_GridGraph<false> 3.19ms
// ± 6% 1) BM_GridGraph<true> 8.89ms ± 4% BM_GridGraph<false> 3.07ms ±
// 3% 2) BM_GridGraph<true> 8.61ms ± 3% BM_GridGraph<false> 3.13ms ± 6%
@@ -303,8 +337,8 @@ class BoundedDijkstraWrapper {
// The vectors are only allocated after they are first used.
// Between calls, is_destination_ is all false, and the rest is junk.
std::vector<bool> is_destination_;
std::vector<int> node_to_source_index_;
std::vector<int> node_to_destination_index_;
ByNode<int> node_to_source_index_;
ByNode<int> node_to_destination_index_;
};
// -----------------------------------------------------------------------------
@@ -314,12 +348,12 @@ class BoundedDijkstraWrapper {
template <class GraphType, class DistanceType, class ArcLengthFunctor>
BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
BoundedDijkstraWrapper(const GraphType* graph,
const std::vector<DistanceType>* arc_lengths)
const ByArc<DistanceType>* arc_lengths)
: graph_(graph),
arc_length_functor_(*arc_lengths),
arc_lengths_(arc_lengths) {
CHECK(arc_lengths_ != nullptr);
CHECK_EQ(arc_lengths_->size(), graph->num_arcs());
CHECK_EQ(ArcIndex(arc_lengths_->size()), graph->num_arcs());
for (const DistanceType length : *arc_lengths) {
CHECK_GE(length, 0);
}
@@ -341,10 +375,10 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
arc_lengths_(other.arc_lengths_) {}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
const std::vector<int>&
const std::vector<typename GraphType::NodeIndex>&
BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
RunBoundedDijkstraFromMultipleSources(
const std::vector<std::pair<int, DistanceType>>&
const std::vector<std::pair<NodeIndex, DistanceType>>&
sources_with_distance_offsets,
DistanceType distance_limit) {
return RunBoundedDijkstraWithSettledNodeCallback(
@@ -352,12 +386,12 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
std::vector<int>
std::vector<typename GraphType::NodeIndex>
BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
const std::vector<std::pair<int, DistanceType>>&
const std::vector<std::pair<NodeIndex, DistanceType>>&
sources_with_distance_offsets,
const std::vector<std::pair<int, DistanceType>>&
const std::vector<std::pair<NodeIndex, DistanceType>>&
destinations_with_distance_offsets,
int num_destinations_to_reach, DistanceType distance_limit) {
if (destinations_with_distance_offsets.empty()) return {};
@@ -368,22 +402,22 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
// to reduce the search space.
DCHECK_GE(num_destinations_to_reach, 0);
int num_destinations = 0;
is_destination_.resize(graph_->num_nodes(), false);
is_destination_.resize(static_cast<size_t>(graph_->num_nodes()), false);
node_to_destination_index_.resize(graph_->num_nodes(), -1);
DistanceType min_destination_distance_offset =
destinations_with_distance_offsets[0].second;
for (int i = 0; i < destinations_with_distance_offsets.size(); ++i) {
const int node = destinations_with_distance_offsets[i].first;
const NodeIndex node = destinations_with_distance_offsets[i].first;
const DistanceType distance = destinations_with_distance_offsets[i].second;
if (!is_destination_[node]) ++num_destinations;
if (!is_destination_[static_cast<size_t>(node)]) ++num_destinations;
// Skip useless repetitions.
if (is_destination_[node] &&
if (is_destination_[static_cast<size_t>(node)] &&
distance >=
destinations_with_distance_offsets[node_to_destination_index_[node]]
.second) {
continue;
}
is_destination_[node] = true;
is_destination_[static_cast<size_t>(node)] = true;
node_to_destination_index_[node] = i;
min_destination_distance_offset =
std::min(min_destination_distance_offset, distance);
@@ -395,13 +429,13 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
gtl::TopN<NodeDistance, std::less<NodeDistance>> closest_destinations(
/*limit=*/num_destinations_to_reach);
std::function<void(node_type, DistanceType, DistanceType*)>
std::function<void(NodeIndex, DistanceType, DistanceType*)>
settled_node_callback =
[this, num_destinations_to_reach, min_destination_distance_offset,
&destinations_with_distance_offsets, &closest_destinations](
node_type settled_node, DistanceType settled_distance,
NodeIndex settled_node, DistanceType settled_distance,
DistanceType* distance_limit) {
if (!is_destination_[settled_node]) return;
if (!is_destination_[static_cast<size_t>(settled_node)]) return;
const DistanceType distance =
settled_distance +
destinations_with_distance_offsets
@@ -423,12 +457,12 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
// Clean up, sparsely, for the next call.
for (const auto& [node, _] : destinations_with_distance_offsets) {
is_destination_[node] = false;
is_destination_[static_cast<size_t>(node)] = false;
}
// Return the closest "num_destinations_to_reach" reached destinations,
// sorted by distance.
std::vector<int> sorted_destinations;
std::vector<NodeIndex> sorted_destinations;
sorted_destinations.reserve(closest_destinations.size());
for (const NodeDistance& d : closest_destinations.Take()) {
sorted_destinations.push_back(d.node);
@@ -438,10 +472,11 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
template <class GraphType, class DistanceType, class ArcLengthFunctor>
bool BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
OneToOneShortestPath(int from, int to, DistanceType distance_limit) {
OneToOneShortestPath(NodeIndex from, NodeIndex to,
DistanceType distance_limit) {
bool reached = false;
std::function<void(node_type, DistanceType, DistanceType*)>
settled_node_callback = [to, &reached](node_type node,
std::function<void(NodeIndex, DistanceType, DistanceType*)>
settled_node_callback = [to, &reached](NodeIndex node,
DistanceType distance,
DistanceType* distance_limit) {
if (node != to) return;
@@ -456,18 +491,18 @@ bool BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
const std::vector<int>&
const std::vector<typename GraphType::NodeIndex>&
BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
RunBoundedDijkstraWithSettledNodeCallback(
const std::vector<std::pair<int, DistanceType>>&
const std::vector<std::pair<NodeIndex, DistanceType>>&
sources_with_distance_offsets,
std::function<void(node_type settled_node,
std::function<void(NodeIndex settled_node,
DistanceType settled_distance,
DistanceType* distance_limit)>
settled_node_callback,
DistanceType distance_limit) {
// Sparse clear is_reached_ from the last call.
for (const int node : reached_nodes_) {
for (const NodeIndex node : reached_nodes_) {
is_reached_[node] = false;
}
reached_nodes_.clear();
@@ -475,15 +510,15 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
is_reached_.resize(graph_->num_nodes(), false);
distances_.resize(graph_->num_nodes(), distance_limit);
parents_.resize(graph_->num_nodes(), std::numeric_limits<int>::min());
arc_from_source_.resize(graph_->num_nodes(), -1);
parents_.resize(graph_->num_nodes(), std::numeric_limits<NodeIndex>::min());
arc_from_source_.resize(graph_->num_nodes(), GraphType::kNilArc);
// Initialize sources.
CHECK(queue_.empty());
node_to_source_index_.resize(graph_->num_nodes(), -1);
for (int i = 0; i < sources_with_distance_offsets.size(); ++i) {
const int node = sources_with_distance_offsets[i].first;
DCHECK_GE(node, 0);
const NodeIndex node = sources_with_distance_offsets[i].first;
DCHECK_GE(node, NodeIndex(0));
DCHECK_LT(node, graph_->num_nodes());
const DistanceType distance = sources_with_distance_offsets[i].second;
// Sources with an initial distance ≥ limit are *not* reached.
@@ -498,7 +533,7 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
node_to_source_index_[node] = i;
distances_[node] = distance;
}
for (const int source : reached_nodes_) {
for (const NodeIndex source : reached_nodes_) {
queue_.push_back({source, distances_[source]});
}
std::make_heap(queue_.begin(), queue_.end(), std::greater<NodeDistance>());
@@ -533,7 +568,8 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
// Visit the neighbors.
const DistanceType limit = distance_limit - top.distance;
for (const int arc : graph_->OutgoingArcs(top.node)) {
for (const typename GraphType::ArcIndex arc :
graph_->OutgoingArcs(top.node)) {
// Overflow-safe check of top.distance + arc_length >= distance_limit.
// This works since we know top.distance < distance_limit, as long as we
// don't have negative top.distance (which might happen with negative
@@ -543,7 +579,7 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
if (arc_length >= limit) continue;
const DistanceType candidate_distance = top.distance + arc_length;
const int head = graph_->Head(arc);
const NodeIndex head = graph_->Head(arc);
if (is_reached_[head]) {
if (candidate_distance >= distances_[head]) continue;
} else {
@@ -563,14 +599,14 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
std::vector<int>
std::vector<typename GraphType::ArcIndex>
BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::ArcPathTo(
int node) const {
std::vector<int> output;
NodeIndex node) const {
std::vector<typename GraphType::ArcIndex> output;
int loop_detector = 0;
while (true) {
DCHECK_GE(node, 0);
DCHECK_LT(node, parents_.size());
DCHECK_GE(node, NodeIndex(0));
DCHECK_LT(node, NodeIndex(parents_.size()));
CHECK_LT(loop_detector++, parents_.size());
if (parents_[node] == node) break;
output.push_back(arc_from_source_[node]);
@@ -581,14 +617,14 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::ArcPathTo(
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
std::vector<int>
std::vector<typename GraphType::NodeIndex>
BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::NodePathTo(
int node) const {
std::vector<int> output;
NodeIndex node) const {
std::vector<NodeIndex> output;
int loop_detector = 0;
while (true) {
DCHECK_GE(node, 0);
DCHECK_LT(node, parents_.size());
DCHECK_GE(node, NodeIndex(0));
DCHECK_LT(node, NodeIndex(parents_.size()));
CHECK_LT(loop_detector++, parents_.size());
output.push_back(node);
if (parents_[node] == node) break;
@@ -599,27 +635,28 @@ BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::NodePathTo(
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
int BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
SourceOfShortestPathToNode(int node) const {
int parent = node;
typename GraphType::NodeIndex BoundedDijkstraWrapper<
GraphType, DistanceType,
ArcLengthFunctor>::SourceOfShortestPathToNode(NodeIndex node) const {
NodeIndex parent = node;
while (parents_[parent] != parent) parent = parents_[parent];
return parent;
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
int BoundedDijkstraWrapper<GraphType, DistanceType,
ArcLengthFunctor>::GetSourceIndex(int node) const {
DCHECK_GE(node, 0);
DCHECK_LT(node, node_to_source_index_.size());
ArcLengthFunctor>::GetSourceIndex(NodeIndex node)
const {
DCHECK_GE(node, NodeIndex(0));
DCHECK_LT(node, NodeIndex(node_to_source_index_.size()));
return node_to_source_index_[node];
}
template <class GraphType, class DistanceType, class ArcLengthFunctor>
int BoundedDijkstraWrapper<GraphType, DistanceType,
ArcLengthFunctor>::GetDestinationIndex(int node)
const {
DCHECK_GE(node, 0);
DCHECK_LT(node, node_to_destination_index_.size());
int BoundedDijkstraWrapper<GraphType, DistanceType, ArcLengthFunctor>::
GetDestinationIndex(NodeIndex node) const {
DCHECK_GE(node, NodeIndex(0));
DCHECK_LT(node, NodeIndex(node_to_destination_index_.size()));
return node_to_destination_index_[node];
}
@@ -627,37 +664,38 @@ int BoundedDijkstraWrapper<GraphType, DistanceType,
// Example usage.
// -----------------------------------------------------------------------------
template <typename DistanceType>
std::pair<DistanceType, std::vector<int>> SimpleOneToOneShortestPath(
int source, int destination, absl::Span<const int> tails,
absl::Span<const int> heads, absl::Span<const DistanceType> lengths,
template <typename NodeIndex, typename DistanceType>
std::pair<DistanceType, std::vector<NodeIndex>> SimpleOneToOneShortestPath(
NodeIndex source, NodeIndex destination, absl::Span<const NodeIndex> tails,
absl::Span<const NodeIndex> heads, absl::Span<const DistanceType> lengths,
DistanceType limit) {
using ArcIndex = NodeIndex;
// Compute the number of nodes.
//
// This is not necessary, but is a good practice to allocate the graph size in
// one go. We also do some basic validation.
CHECK_GE(source, 0);
CHECK_GE(destination, 0);
int num_nodes = std::max(source + 1, destination + 1);
for (const int tail : tails) {
NodeIndex num_nodes = std::max(source + 1, destination + 1);
for (const NodeIndex tail : tails) {
CHECK_GE(tail, 0);
num_nodes = std::max(tail + 1, num_nodes);
}
for (const int head : heads) {
for (const NodeIndex head : heads) {
CHECK_GE(head, 0);
num_nodes = std::max(head + 1, num_nodes);
}
// The number of arcs.
const int num_arcs = tails.size();
const ArcIndex num_arcs = tails.size();
CHECK_EQ(num_arcs, heads.size());
CHECK_EQ(num_arcs, lengths.size());
// Build the graph. Note that this permutes arc indices for speed, but we
// don't care here since we will return a node path.
util::StaticGraph<> graph(num_nodes, num_arcs);
util::StaticGraph<NodeIndex, ArcIndex> graph(num_nodes, num_arcs);
std::vector<DistanceType> arc_lengths(lengths.begin(), lengths.end());
for (int a = 0; a < num_arcs; ++a) {
for (ArcIndex a = 0; a < num_arcs; ++a) {
// Negative length can cause the algo to loop forever and/or use a lot of
// memory. So it should be validated.
CHECK_GE(lengths[a], 0);

View File

@@ -30,6 +30,7 @@
#include "gtest/gtest.h"
#include "ortools/base/dump_vars.h"
#include "ortools/base/gmock.h"
#include "ortools/base/strong_int.h"
#include "ortools/graph/graph.h"
#include "ortools/graph/graph_io.h"
#include "ortools/graph/test_util.h"
@@ -45,122 +46,140 @@ using ::testing::Pair;
using ::testing::UnorderedElementsAreArray;
using ::util::ListGraph;
DEFINE_STRONG_INT_TYPE(NodeIndex, int32_t);
DEFINE_STRONG_INT_TYPE(ArcIndex, int64_t);
using TestGraph = ListGraph<NodeIndex, ArcIndex>;
template <typename DistanceType>
using DijkstraWrapper = BoundedDijkstraWrapper<TestGraph, DistanceType>;
TEST(BoundedDijkstraWrapperDeathTest, Accessors) {
ListGraph<> graph;
graph.AddArc(1, 3);
std::vector<float> arc_lengths = {2.5};
BoundedDijkstraWrapper<ListGraph<>, float> dijkstra(&graph, &arc_lengths);
TestGraph graph;
graph.AddArc(NodeIndex(1), NodeIndex(3));
DijkstraWrapper<float>::ByArc<float> arc_lengths = {2.5};
DijkstraWrapper<float> dijkstra(&graph, &arc_lengths);
const std::is_same<float, decltype(dijkstra)::distance_type> same_type;
ASSERT_TRUE(same_type.value);
ASSERT_EQ(&dijkstra.graph(), &graph);
ASSERT_EQ(dijkstra.GetArcLength(0), 2.5);
ASSERT_EQ(dijkstra.GetArcLength(ArcIndex(0)), 2.5);
}
TEST(BoundedDijkstraWrapperDeathTest, WithArcLengthFunctor) {
ListGraph<> graph;
graph.AddArc(1, 3);
BoundedDijkstraWrapper<ListGraph<>, float, std::function<float(int)>>
dijkstra(&graph, [](int) { return 2.34; });
ASSERT_FLOAT_EQ(dijkstra.GetArcLength(0), 2.34f);
TestGraph graph;
graph.AddArc(NodeIndex(1), NodeIndex(3));
BoundedDijkstraWrapper<TestGraph, float, std::function<float(ArcIndex)>>
dijkstra(&graph, [](ArcIndex) { return 2.34; });
ASSERT_FLOAT_EQ(dijkstra.GetArcLength(ArcIndex(0)), 2.34f);
}
TEST(BoundedDijkstraWrapperDeathTest, ConstructorPreconditions) {
ListGraph<> graph;
for (int i = 0; i < 50; ++i) graph.AddArc(i, i + 1);
TestGraph graph;
for (int i = 0; i < 50; ++i) graph.AddArc(NodeIndex(i), NodeIndex(i + 1));
std::vector<int> arc_lengths(13, 0);
typedef BoundedDijkstraWrapper<ListGraph<>, int> TestedClass;
typedef DijkstraWrapper<int> TestedClass;
TestedClass::ByArc<int> arc_lengths(13, 0);
EXPECT_DEATH(new TestedClass(&graph, &arc_lengths), "13");
arc_lengths.resize(50, 0);
arc_lengths[20] = -132;
arc_lengths[ArcIndex(20)] = -132;
EXPECT_DEATH(new TestedClass(&graph, &arc_lengths), "-132");
}
TEST(BoundedDijkstraWrapper, ArcPathToAndSourceOfShortestPathToNode) {
ListGraph<> graph;
std::vector<int> arc_lengths = {1, 2, 3, 4, 6, 5};
graph.AddArc(0, 1);
graph.AddArc(0, 1);
graph.AddArc(1, 2);
graph.AddArc(1, 2);
graph.AddArc(2, 3);
graph.AddArc(2, 3);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths = {1, 2, 3, 4, 6, 5};
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(1), NodeIndex(2));
graph.AddArc(NodeIndex(1), NodeIndex(2));
graph.AddArc(NodeIndex(2), NodeIndex(3));
graph.AddArc(NodeIndex(2), NodeIndex(3));
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
const std::vector<int> reached = dijkstra.RunBoundedDijkstra(0, 10);
EXPECT_THAT(reached, ElementsAre(0, 1, 2, 3));
EXPECT_EQ(9, dijkstra.distances()[3]);
EXPECT_THAT(dijkstra.ArcPathTo(3), ElementsAre(0, 2, 5));
EXPECT_THAT(dijkstra.NodePathTo(3), ElementsAre(0, 1, 2, 3));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(3));
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
const auto reached = dijkstra.RunBoundedDijkstra(NodeIndex(0), 10);
EXPECT_THAT(reached, ElementsAre(NodeIndex(0), NodeIndex(1), NodeIndex(2),
NodeIndex(3)));
EXPECT_EQ(9, dijkstra.distances()[NodeIndex(3)]);
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(3)),
ElementsAre(ArcIndex(0), ArcIndex(2), ArcIndex(5)));
EXPECT_THAT(
dijkstra.NodePathTo(NodeIndex(3)),
ElementsAre(NodeIndex(0), NodeIndex(1), NodeIndex(2), NodeIndex(3)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(3)));
}
TEST(BoundedDijkstraWrapper, EmptyPath) {
ListGraph<> graph;
std::vector<int> arc_lengths = {1, 2};
graph.AddArc(0, 1);
graph.AddArc(2, 3);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths = {1, 2};
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(2), NodeIndex(3));
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
const std::vector<int> reached = dijkstra.RunBoundedDijkstra(0, 10);
EXPECT_THAT(reached, ElementsAre(0, 1));
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
const auto reached = dijkstra.RunBoundedDijkstra(NodeIndex(0), 10);
EXPECT_THAT(reached, ElementsAre(NodeIndex(0), NodeIndex(1)));
EXPECT_EQ(0, dijkstra.distances()[0]);
EXPECT_THAT(dijkstra.ArcPathTo(0), ElementsAre());
EXPECT_THAT(dijkstra.NodePathTo(0), ElementsAre(0));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(0));
EXPECT_EQ(0, dijkstra.distances()[NodeIndex(0)]);
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(0)), ElementsAre());
EXPECT_THAT(dijkstra.NodePathTo(NodeIndex(0)), ElementsAre(NodeIndex(0)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(0)));
}
TEST(BoundedDijkstraWrapper, OverflowSafe) {
ListGraph<> graph;
TestGraph graph;
const int64_t int_max = std::numeric_limits<int64_t>::max();
std::vector<int64_t> arc_lengths = {int_max, int_max / 2, int_max / 2, 1};
graph.AddArc(0, 1);
graph.AddArc(0, 1);
graph.AddArc(1, 2);
graph.AddArc(2, 3);
DijkstraWrapper<int64_t>::ByArc<int64_t> arc_lengths = {int_max, int_max / 2,
int_max / 2, 1};
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(1), NodeIndex(2));
graph.AddArc(NodeIndex(2), NodeIndex(3));
BoundedDijkstraWrapper<ListGraph<>, int64_t> dijkstra(&graph, &arc_lengths);
const std::vector<int> reached = dijkstra.RunBoundedDijkstra(0, int_max);
BoundedDijkstraWrapper<TestGraph, int64_t> dijkstra(&graph, &arc_lengths);
const auto reached = dijkstra.RunBoundedDijkstra(NodeIndex(0), int_max);
// This works because int_max is odd, i.e. 2 * (int_max / 2) = int_max - 1
EXPECT_THAT(reached, ElementsAre(0, 1, 2));
EXPECT_EQ(0, dijkstra.distances()[0]);
EXPECT_EQ(int_max / 2, dijkstra.distances()[1]);
EXPECT_EQ(int_max - 1, dijkstra.distances()[2]);
EXPECT_THAT(reached, ElementsAre(NodeIndex(0), NodeIndex(1), NodeIndex(2)));
EXPECT_EQ(0, dijkstra.distances()[NodeIndex(0)]);
EXPECT_EQ(int_max / 2, dijkstra.distances()[NodeIndex(1)]);
EXPECT_EQ(int_max - 1, dijkstra.distances()[NodeIndex(2)]);
}
TEST(BoundedDijkstraWrapper,
ArcPathToAndSourceOfShortestPathToNode_WithArcLengthFunction) {
ListGraph<> graph;
std::vector<int> arc_lengths = {1, 2, 3, 4, 6, 5};
graph.AddArc(0, 1);
graph.AddArc(0, 1);
graph.AddArc(1, 2);
graph.AddArc(1, 2);
graph.AddArc(2, 3);
graph.AddArc(2, 3);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths = {1, 2, 3, 4, 6, 5};
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(1), NodeIndex(2));
graph.AddArc(NodeIndex(1), NodeIndex(2));
graph.AddArc(NodeIndex(2), NodeIndex(3));
graph.AddArc(NodeIndex(2), NodeIndex(3));
class MyArcLengthFunctor {
public:
explicit MyArcLengthFunctor(const std::vector<int>& arc_lengths)
explicit MyArcLengthFunctor(
const DijkstraWrapper<int>::ByArc<int>& arc_lengths)
: arc_lengths_(arc_lengths) {}
int operator()(int arc) const {
return arc % 2 == 1 ? arc_lengths_[arc] : 100;
int operator()(ArcIndex arc) const {
return arc.value() % 2 == 1 ? arc_lengths_[arc] : 100;
}
private:
const std::vector<int>& arc_lengths_;
const DijkstraWrapper<int>::ByArc<int>& arc_lengths_;
};
BoundedDijkstraWrapper<ListGraph<>, int, MyArcLengthFunctor> dijkstra(
BoundedDijkstraWrapper<TestGraph, int, MyArcLengthFunctor> dijkstra(
&graph, MyArcLengthFunctor(arc_lengths));
const std::vector<int> reached = dijkstra.RunBoundedDijkstra(0, 20);
EXPECT_THAT(reached, ElementsAre(0, 1, 2, 3));
EXPECT_EQ(11, dijkstra.distances()[3]);
EXPECT_THAT(dijkstra.ArcPathTo(3), ElementsAre(1, 3, 5));
EXPECT_THAT(dijkstra.NodePathTo(3), ElementsAre(0, 1, 2, 3));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(3));
const auto reached = dijkstra.RunBoundedDijkstra(NodeIndex(0), 20);
EXPECT_THAT(reached, ElementsAre(NodeIndex(0), NodeIndex(1), NodeIndex(2),
NodeIndex(3)));
EXPECT_EQ(11, dijkstra.distances()[NodeIndex(3)]);
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(3)),
ElementsAre(ArcIndex(1), ArcIndex(3), ArcIndex(5)));
EXPECT_THAT(
dijkstra.NodePathTo(NodeIndex(3)),
ElementsAre(NodeIndex(0), NodeIndex(1), NodeIndex(2), NodeIndex(3)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(3)));
}
TEST(BoundedDijkstraWrapperTest, RandomDenseGraph) {
@@ -168,12 +187,12 @@ TEST(BoundedDijkstraWrapperTest, RandomDenseGraph) {
const int num_nodes = 50;
std::vector<std::vector<int>> lengths(num_nodes, std::vector<int>(num_nodes));
ListGraph<> graph;
std::vector<int> arc_lengths;
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths;
for (int i = 0; i < num_nodes; ++i) {
for (int j = 0; j < num_nodes; ++j) {
lengths[i][j] = (i == j) ? 0 : absl::Uniform(random, 0, 1000);
graph.AddArc(i, j);
graph.AddArc(NodeIndex(i), NodeIndex(j));
arc_lengths.push_back(lengths[i][j]);
}
}
@@ -191,15 +210,15 @@ TEST(BoundedDijkstraWrapperTest, RandomDenseGraph) {
std::vector<int> reached_sizes;
for (int source = 0; source < num_nodes; ++source) {
const int limit = 100;
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
const std::vector<int> reached = dijkstra.RunBoundedDijkstra(source, limit);
for (const int node : reached) {
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
const auto reached = dijkstra.RunBoundedDijkstra(NodeIndex(source), limit);
for (const NodeIndex node : reached) {
EXPECT_LT(dijkstra.distances()[node], limit);
EXPECT_EQ(dijkstra.distances()[node], lengths[source][node]);
EXPECT_EQ(dijkstra.distances()[node], lengths[source][node.value()]);
// Check that we never have the same node twice in the paths.
std::vector<int> path = {node};
int parent = node;
std::vector<NodeIndex> path = {node};
NodeIndex parent = node;
while (dijkstra.parents()[parent] != parent) {
parent = dijkstra.parents()[parent];
path.push_back(parent);
@@ -230,7 +249,7 @@ TEST(SimpleOneToOneShortestPathTest, PathTooLong) {
{
const auto [distance, path] =
SimpleOneToOneShortestPath<int>(0, 3, tails, heads, lengths);
SimpleOneToOneShortestPath<int, int>(0, 3, tails, heads, lengths);
EXPECT_EQ(distance, std::numeric_limits<int>::max());
EXPECT_TRUE(path.empty());
}
@@ -238,7 +257,7 @@ TEST(SimpleOneToOneShortestPathTest, PathTooLong) {
{
// from 0 to 2 work because 2 * big_length < int_max.
const auto [distance, path] =
SimpleOneToOneShortestPath<int>(0, 2, tails, heads, lengths);
SimpleOneToOneShortestPath<int, int>(0, 2, tails, heads, lengths);
EXPECT_EQ(distance, std::numeric_limits<int>::max() - 1);
EXPECT_THAT(path, ElementsAre(0, 1, 2));
}
@@ -256,7 +275,7 @@ TEST(SimpleOneToOneShortestPathTest, Random) {
// This will be the "sparse" representation.
std::vector<int> tails;
std::vector<int> heads;
std::vector<int> arc_lengths;
DijkstraWrapper<int>::ByArc<int> arc_lengths;
// We permutes the arc order to properly test that it do not matter.
std::vector<int> nodes(num_nodes);
@@ -292,8 +311,8 @@ TEST(SimpleOneToOneShortestPathTest, Random) {
// No limit. There should always be a path with our generated data.
{
const auto [distance, path] =
SimpleOneToOneShortestPath<int>(from, to, tails, heads, arc_lengths);
const auto [distance, path] = SimpleOneToOneShortestPath<int, int>(
from, to, tails, heads, arc_lengths);
EXPECT_EQ(distance, shortest_distance[from][to]);
EXPECT_FALSE(path.empty());
EXPECT_EQ(path.front(), from);
@@ -302,7 +321,7 @@ TEST(SimpleOneToOneShortestPathTest, Random) {
// A limit of shortest_distance[from][to] + 1 works too.
{
const auto [distance, path] = SimpleOneToOneShortestPath<int>(
const auto [distance, path] = SimpleOneToOneShortestPath<int, int>(
from, to, tails, heads, arc_lengths, shortest_distance[from][to] + 1);
EXPECT_EQ(distance, shortest_distance[from][to]);
EXPECT_FALSE(path.empty());
@@ -312,7 +331,7 @@ TEST(SimpleOneToOneShortestPathTest, Random) {
// But a limit of shortest_distance[from][to] should fail.
{
const auto [distance, path] = SimpleOneToOneShortestPath<int>(
const auto [distance, path] = SimpleOneToOneShortestPath<int, int>(
from, to, tails, heads, arc_lengths, shortest_distance[from][to]);
EXPECT_EQ(distance, shortest_distance[from][to]);
EXPECT_TRUE(path.empty());
@@ -321,101 +340,116 @@ TEST(SimpleOneToOneShortestPathTest, Random) {
}
TEST(BoundedDijkstraWrapperTest, MultiRunsOverDynamicGraphAndLengths) {
ListGraph<> graph;
graph.AddArc(0, 1);
graph.AddArc(0, 1);
std::vector<int> arc_lengths = {4, 3};
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
TestGraph graph;
graph.AddArc(NodeIndex(0), NodeIndex(1));
graph.AddArc(NodeIndex(0), NodeIndex(1));
DijkstraWrapper<int>::ByArc<int> arc_lengths = {4, 3};
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
EXPECT_THAT(dijkstra.RunBoundedDijkstra(0, 5), ElementsAre(0, 1));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(1));
EXPECT_THAT(dijkstra.ArcPathTo(1), ElementsAre(1));
EXPECT_THAT(dijkstra.RunBoundedDijkstra(NodeIndex(0), 5),
ElementsAre(NodeIndex(0), NodeIndex(1)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(1)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(1)), ElementsAre(ArcIndex(1)));
EXPECT_THAT(dijkstra.RunBoundedDijkstra(0, 2), ElementsAre(0));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(0));
EXPECT_THAT(dijkstra.ArcPathTo(0), IsEmpty());
EXPECT_THAT(dijkstra.RunBoundedDijkstra(NodeIndex(0), 2),
ElementsAre(NodeIndex(0)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(0)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(0)), IsEmpty());
EXPECT_THAT(dijkstra.RunBoundedDijkstra(1, 99), ElementsAre(1));
EXPECT_EQ(1, dijkstra.SourceOfShortestPathToNode(1));
EXPECT_THAT(dijkstra.ArcPathTo(1), IsEmpty());
EXPECT_THAT(dijkstra.RunBoundedDijkstra(NodeIndex(1), 99),
ElementsAre(NodeIndex(1)));
EXPECT_EQ(NodeIndex(1), dijkstra.SourceOfShortestPathToNode(NodeIndex(1)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(1)), IsEmpty());
// Add some arcs and nodes...
graph.AddArc(0, 2);
graph.AddArc(NodeIndex(0), NodeIndex(2));
arc_lengths.push_back(1);
graph.AddArc(1, 2);
graph.AddArc(NodeIndex(1), NodeIndex(2));
arc_lengths.push_back(0);
graph.AddArc(2, 1);
graph.AddArc(NodeIndex(2), NodeIndex(1));
arc_lengths.push_back(1);
graph.AddArc(1, 3);
graph.AddArc(NodeIndex(1), NodeIndex(3));
arc_lengths.push_back(5);
EXPECT_THAT(dijkstra.RunBoundedDijkstra(0, 10), ElementsAre(0, 2, 1, 3));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(3));
EXPECT_THAT(dijkstra.ArcPathTo(3), ElementsAre(2, 4, 5));
EXPECT_THAT(
dijkstra.RunBoundedDijkstra(NodeIndex(0), 10),
ElementsAre(NodeIndex(0), NodeIndex(2), NodeIndex(1), NodeIndex(3)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(3)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(3)),
ElementsAre(ArcIndex(2), ArcIndex(4), ArcIndex(5)));
EXPECT_THAT(dijkstra.RunBoundedDijkstra(0, 6), ElementsAre(0, 2, 1));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(1));
EXPECT_THAT(dijkstra.ArcPathTo(1), ElementsAre(2, 4));
EXPECT_THAT(dijkstra.RunBoundedDijkstra(NodeIndex(0), 6),
ElementsAre(NodeIndex(0), NodeIndex(2), NodeIndex(1)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(1)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(1)),
ElementsAre(ArcIndex(2), ArcIndex(4)));
}
TEST(BoundedDijkstraWrapperTest, MultipleSources) {
// Use this graph. Source nodes have their initial distance in [ ].
//
// N1[0] --(2)--> N0[4] --(1)--> N2 --(5)--> N3 <--(4)-- N4[3] --(5)--> N5
ListGraph<> graph;
std::vector<int> arc_lengths;
graph.AddArc(1, 0);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths;
graph.AddArc(NodeIndex(1), NodeIndex(0));
arc_lengths.push_back(2);
graph.AddArc(0, 2);
graph.AddArc(NodeIndex(0), NodeIndex(2));
arc_lengths.push_back(1);
graph.AddArc(2, 3);
graph.AddArc(NodeIndex(2), NodeIndex(3));
arc_lengths.push_back(5);
graph.AddArc(4, 3);
graph.AddArc(NodeIndex(4), NodeIndex(3));
arc_lengths.push_back(4);
graph.AddArc(4, 5);
graph.AddArc(NodeIndex(4), NodeIndex(5));
arc_lengths.push_back(5);
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
// The distance limit is exclusive, so we can't reach Node 5.
ASSERT_THAT(dijkstra.RunBoundedDijkstraFromMultipleSources(
{{1, 0}, {0, 4}, {4, 3}}, 8),
{{NodeIndex(1), 0}, {NodeIndex(0), 4}, {NodeIndex(4), 3}}, 8),
// The order is deterministic: node 4 comes before node 2, despite
// having equal distance and higher index, because it's a source.
ElementsAre(1, 0, 4, 2, 3));
EXPECT_EQ(2, dijkstra.distances()[0]);
EXPECT_EQ(1, dijkstra.SourceOfShortestPathToNode(0));
EXPECT_THAT(dijkstra.ArcPathTo(0), ElementsAre(0));
EXPECT_EQ(0, dijkstra.distances()[1]);
EXPECT_EQ(1, dijkstra.SourceOfShortestPathToNode(1));
EXPECT_THAT(dijkstra.ArcPathTo(1), IsEmpty());
EXPECT_EQ(3, dijkstra.distances()[2]);
EXPECT_EQ(1, dijkstra.SourceOfShortestPathToNode(2));
EXPECT_THAT(dijkstra.ArcPathTo(2), ElementsAre(0, 1));
EXPECT_EQ(7, dijkstra.distances()[3]);
EXPECT_EQ(4, dijkstra.SourceOfShortestPathToNode(3));
EXPECT_THAT(dijkstra.ArcPathTo(3), ElementsAre(3));
EXPECT_EQ(3, dijkstra.distances()[4]);
EXPECT_EQ(4, dijkstra.SourceOfShortestPathToNode(4));
EXPECT_THAT(dijkstra.ArcPathTo(4), IsEmpty());
ElementsAre(NodeIndex(1), NodeIndex(0), NodeIndex(4),
NodeIndex(2), NodeIndex(3)));
EXPECT_EQ(2, dijkstra.distances()[NodeIndex(0)]);
EXPECT_EQ(NodeIndex(1), dijkstra.SourceOfShortestPathToNode(NodeIndex(0)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(0)), ElementsAre(ArcIndex(0)));
EXPECT_EQ(0, dijkstra.distances()[NodeIndex(1)]);
EXPECT_EQ(NodeIndex(1), dijkstra.SourceOfShortestPathToNode(NodeIndex(1)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(1)), IsEmpty());
EXPECT_EQ(3, dijkstra.distances()[NodeIndex(2)]);
EXPECT_EQ(NodeIndex(1), dijkstra.SourceOfShortestPathToNode(NodeIndex(2)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(2)),
ElementsAre(ArcIndex(0), ArcIndex(1)));
EXPECT_EQ(7, dijkstra.distances()[NodeIndex(3)]);
EXPECT_EQ(NodeIndex(4), dijkstra.SourceOfShortestPathToNode(NodeIndex(3)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(3)), ElementsAre(ArcIndex(3)));
EXPECT_EQ(3, dijkstra.distances()[NodeIndex(4)]);
EXPECT_EQ(NodeIndex(4), dijkstra.SourceOfShortestPathToNode(NodeIndex(4)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(4)), IsEmpty());
}
TEST(BoundedDijkstraWrapperTest, SourcesAtOrBeyondDistanceLimitAreNotReached) {
ListGraph<> graph(/*num_nodes=*/5, /*arc_capacity=*/0);
std::vector<int> arc_lengths; // No arcs.
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
EXPECT_THAT(dijkstra.RunBoundedDijkstraFromMultipleSources(
{{0, 10}, {1, 11}, {2, 12}, {3, 13}}, 12),
ElementsAre(0, 1));
TestGraph graph(/*num_nodes=*/NodeIndex(5), /*arc_capacity=*/ArcIndex(0));
DijkstraWrapper<int>::ByArc<int> arc_lengths; // No arcs.
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
EXPECT_THAT(
dijkstra.RunBoundedDijkstraFromMultipleSources({{NodeIndex(0), 10},
{NodeIndex(1), 11},
{NodeIndex(2), 12},
{NodeIndex(3), 13}},
12),
ElementsAre(NodeIndex(0), NodeIndex(1)));
}
TEST(BoundedDijkstraWrapperTest, SourcesListedMultipleTimesKeepsMinDistance) {
ListGraph<> graph(/*num_nodes=*/5, /*arc_capacity=*/1);
graph.AddArc(1, 3);
std::vector<int> arc_lengths = {20};
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
EXPECT_THAT(dijkstra.RunBoundedDijkstraFromMultipleSources(
{{1, 12}, {1, 10}, {1, 14}}, 31),
ElementsAre(1, 3));
EXPECT_EQ(dijkstra.distances()[3], 30);
TestGraph graph(/*num_nodes=*/NodeIndex(5), /*arc_capacity=*/ArcIndex(1));
graph.AddArc(NodeIndex(1), NodeIndex(3));
DijkstraWrapper<int>::ByArc<int> arc_lengths = {20};
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
EXPECT_THAT(
dijkstra.RunBoundedDijkstraFromMultipleSources(
{{NodeIndex(1), 12}, {NodeIndex(1), 10}, {NodeIndex(1), 14}}, 31),
ElementsAre(NodeIndex(1), NodeIndex(3)));
EXPECT_EQ(dijkstra.distances()[NodeIndex(3)], 30);
}
TEST(BoundedDijkstraWrapperTest, MultipleSourcesMultipleDestinations) {
@@ -430,38 +464,45 @@ TEST(BoundedDijkstraWrapperTest, MultipleSourcesMultipleDestinations) {
// `------(0)-----'
//
// The shortest path is S0->D1->N5->D4, of distance 2 + 3 + 1 + 1 + 1 = 8.
ListGraph<> graph;
std::vector<int> arc_lengths;
graph.AddArc(0, 1);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths;
graph.AddArc(NodeIndex(0), NodeIndex(1));
arc_lengths.push_back(3);
graph.AddArc(2, 3);
graph.AddArc(NodeIndex(2), NodeIndex(3));
arc_lengths.push_back(3);
graph.AddArc(1, 5);
graph.AddArc(NodeIndex(1), NodeIndex(5));
arc_lengths.push_back(1);
graph.AddArc(3, 5);
graph.AddArc(NodeIndex(3), NodeIndex(5));
arc_lengths.push_back(0);
graph.AddArc(5, 3);
graph.AddArc(NodeIndex(5), NodeIndex(3));
arc_lengths.push_back(0);
graph.AddArc(5, 4);
graph.AddArc(NodeIndex(5), NodeIndex(4));
arc_lengths.push_back(1);
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
// Repeat the same source and destination multiple times, to verify that
// it's supported.
std::vector<std::pair<int, int>> sources = {{0, 5}, {2, 4}, {0, 2}, {0, 9}};
std::vector<std::pair<int, int>> destinations = {
{1, 7}, {4, 5}, {3, 3}, {4, 1}, {4, 3}};
std::vector<std::pair<NodeIndex, int>> sources = {{NodeIndex(0), 5},
{NodeIndex(2), 4},
{NodeIndex(0), 2},
{NodeIndex(0), 9}};
std::vector<std::pair<NodeIndex, int>> destinations = {{NodeIndex(1), 7},
{NodeIndex(4), 5},
{NodeIndex(3), 3},
{NodeIndex(4), 1},
{NodeIndex(4), 3}};
EXPECT_THAT(
dijkstra.RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
sources, destinations, /*num_destinations_to_reach=*/1,
/*distance_limit=*/1000),
Contains(4));
EXPECT_EQ(2 + 3 + 1 + 1, dijkstra.distances()[4]);
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(4));
EXPECT_THAT(dijkstra.ArcPathTo(4),
ElementsAre(/*0->1*/ 0, /*1->5*/ 2, /*5->4*/ 5));
EXPECT_EQ(2, dijkstra.GetSourceIndex(0));
EXPECT_EQ(3, dijkstra.GetDestinationIndex(4));
Contains(NodeIndex(4)));
EXPECT_EQ(2 + 3 + 1 + 1, dijkstra.distances()[NodeIndex(4)]);
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(4)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(4)),
ElementsAre(/*0->1*/ ArcIndex(0), /*1->5*/ ArcIndex(2),
/*5->4*/ ArcIndex(5)));
EXPECT_EQ(2, dijkstra.GetSourceIndex(NodeIndex(0)));
EXPECT_EQ(3, dijkstra.GetDestinationIndex(NodeIndex(4)));
// Run it with a limit too small: it'll fail to discover any destination.
EXPECT_THAT(
@@ -475,18 +516,20 @@ TEST(BoundedDijkstraWrapperTest, MultipleSourcesMultipleDestinations) {
dijkstra.RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
sources, destinations, /*num_destinations_to_reach=*/2,
/*distance_limit=*/9), // Limit is exclusive.
ElementsAre(4));
ElementsAre(NodeIndex(4)));
// Slightly modify the graph and try again. We want a case where the best
// destination isn't the one with the smallest distance offset.
destinations.push_back({1, 2}); // D1 will be the closest destination now.
destinations.push_back(
{NodeIndex(1), 2}); // D1 will be the closest destination now.
EXPECT_THAT(
dijkstra.RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
sources, destinations, /*num_destinations_to_reach=*/1,
/*distance_limit=*/8), // Limit is exclusive.
ElementsAre(1));
EXPECT_EQ(0, dijkstra.SourceOfShortestPathToNode(1));
EXPECT_THAT(dijkstra.ArcPathTo(1), ElementsAre(/*0->1*/ 0));
ElementsAre(NodeIndex(1)));
EXPECT_EQ(NodeIndex(0), dijkstra.SourceOfShortestPathToNode(NodeIndex(1)));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(1)),
ElementsAre(/*0->1*/ ArcIndex(0)));
// Corner case: run with no destinations.
EXPECT_THAT(
@@ -505,8 +548,8 @@ TEST(BoundedDijkstraWrapperTest, MultipleSourcesMultipleDestinations) {
// Call Get{Source,Destination}Index() on nodes that aren't sources or
// destinations. This returns junk; so we don't check the returned values,
// but we do check that it doesn't crash.
dijkstra.GetDestinationIndex(4);
dijkstra.GetSourceIndex(1);
dijkstra.GetDestinationIndex(NodeIndex(4));
dijkstra.GetSourceIndex(NodeIndex(1));
// Setting num_reached_destinations=1 now should make '1' the only reachable
// destination, even if the limit is infinite.
@@ -514,85 +557,88 @@ TEST(BoundedDijkstraWrapperTest, MultipleSourcesMultipleDestinations) {
dijkstra.RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
sources, destinations, /*num_destinations_to_reach=*/1,
/*distance_limit=*/1000),
ElementsAre(1));
ElementsAre(NodeIndex(1)));
// Verify that if we set the number of destinations to infinity, they're all
// explored, and the search still stops before exploring the whole graph. To
// do that, we add one extra arc that's beyond the farthest destination's
// distance (including its destination offset), i.e. 1 (distance 2+3+7 = 12).
graph.AddArc(5, 6);
graph.AddArc(NodeIndex(5), NodeIndex(6));
arc_lengths.push_back(2);
graph.AddArc(6, 7);
graph.AddArc(NodeIndex(6), NodeIndex(7));
arc_lengths.push_back(0);
EXPECT_THAT(
dijkstra.RunBoundedDijkstraFromMultipleSourcesToMultipleDestinations(
sources, destinations, /*num_destinations_to_reach=*/1000,
/*distance_limit=*/1000),
ElementsAre(1, 4, 3));
EXPECT_GE(dijkstra.distances()[1], 5);
EXPECT_GE(dijkstra.distances()[4], 7);
EXPECT_GE(dijkstra.distances()[3], 6);
ElementsAre(NodeIndex(1), NodeIndex(4), NodeIndex(3)));
EXPECT_GE(dijkstra.distances()[NodeIndex(1)], 5);
EXPECT_GE(dijkstra.distances()[NodeIndex(4)], 7);
EXPECT_GE(dijkstra.distances()[NodeIndex(3)], 6);
// To verify that node #7 isn't reached, we can check its distance, which will
// still be set to the initialized "distance_limit - min_destination_offset".
EXPECT_GE(dijkstra.distances()[7], 1000 - 1);
EXPECT_GE(dijkstra.distances()[NodeIndex(7)], 1000 - 1);
}
TEST(BoundedDijkstraWrapperTest, OneToOneShortestPath) {
// Since we already tested the multiple sources - multiple destinations
// variant, we only need to test the "plumbing" here.
ListGraph<> graph;
std::vector<int> arc_lengths;
graph.AddArc(0, 1);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths;
graph.AddArc(NodeIndex(0), NodeIndex(1));
arc_lengths.push_back(3);
graph.AddArc(1, 2);
graph.AddArc(NodeIndex(1), NodeIndex(2));
arc_lengths.push_back(2);
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &arc_lengths);
DijkstraWrapper<int> dijkstra(&graph, &arc_lengths);
EXPECT_TRUE(dijkstra.OneToOneShortestPath(0, 2, 6));
EXPECT_THAT(dijkstra.ArcPathTo(2), ElementsAre(0, 1));
EXPECT_TRUE(dijkstra.OneToOneShortestPath(NodeIndex(0), NodeIndex(2), 6));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(2)),
ElementsAre(ArcIndex(0), ArcIndex(1)));
EXPECT_TRUE(dijkstra.OneToOneShortestPath(0, 0, 1));
EXPECT_THAT(dijkstra.ArcPathTo(0), ElementsAre());
EXPECT_TRUE(dijkstra.OneToOneShortestPath(NodeIndex(0), NodeIndex(0), 1));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(0)), ElementsAre());
EXPECT_TRUE(dijkstra.OneToOneShortestPath(1, 2, 3));
EXPECT_THAT(dijkstra.ArcPathTo(2), ElementsAre(1));
EXPECT_TRUE(dijkstra.OneToOneShortestPath(NodeIndex(1), NodeIndex(2), 3));
EXPECT_THAT(dijkstra.ArcPathTo(NodeIndex(2)), ElementsAre(ArcIndex(1)));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(0, 2, 5));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(0, 0, 0));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(1, 2, 2));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(2, 1, 1000));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(NodeIndex(0), NodeIndex(2), 5));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(NodeIndex(0), NodeIndex(0), 0));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(NodeIndex(1), NodeIndex(2), 2));
EXPECT_FALSE(dijkstra.OneToOneShortestPath(NodeIndex(2), NodeIndex(0), 1000));
}
TEST(BoundedDijkstraWrapperTest, CustomSettledNodeCallback) {
// A small chain: 8 --[3]--> 1 --[2]--> 42 --[3]--> 3 --[2]--> 4.
ListGraph<> graph;
std::vector<int> arc_lengths;
graph.AddArc(8, 1);
TestGraph graph;
DijkstraWrapper<int>::ByArc<int> arc_lengths;
graph.AddArc(NodeIndex(8), NodeIndex(1));
arc_lengths.push_back(3);
graph.AddArc(1, 42);
graph.AddArc(NodeIndex(1), NodeIndex(42));
arc_lengths.push_back(2);
graph.AddArc(42, 3);
graph.AddArc(NodeIndex(42), NodeIndex(3));
arc_lengths.push_back(3);
graph.AddArc(3, 4);
graph.AddArc(NodeIndex(3), NodeIndex(4));
arc_lengths.push_back(2);
typedef BoundedDijkstraWrapper<ListGraph<>, int> DijkstraType;
typedef DijkstraWrapper<int> DijkstraType;
DijkstraType dijkstra(&graph, &arc_lengths);
// Tracks each NodeDistance it's called on, and sets the distance limit
// to 10 if it gets called on node 42.
std::vector<std::pair<int, int>> settled_node_dists;
auto callback = [&settled_node_dists](int node, int distance,
std::vector<std::pair<NodeIndex, int>> settled_node_dists;
auto callback = [&settled_node_dists](NodeIndex node, int distance,
int* distance_limit) {
settled_node_dists.push_back({node, distance});
if (node == 42) *distance_limit = 10;
if (node == NodeIndex(42)) *distance_limit = 10;
};
EXPECT_THAT(dijkstra.RunBoundedDijkstraWithSettledNodeCallback({{8, 0}},
EXPECT_THAT(
dijkstra.RunBoundedDijkstraWithSettledNodeCallback({{NodeIndex(8), 0}},
callback, 999),
ElementsAre(8, 1, 42, 3));
ElementsAre(NodeIndex(8), NodeIndex(1), NodeIndex(42), NodeIndex(3)));
EXPECT_THAT(settled_node_dists,
ElementsAre(Pair(8, 0), Pair(1, 3), Pair(42, 5), Pair(3, 8)));
ElementsAre(Pair(NodeIndex(8), 0), Pair(NodeIndex(1), 3),
Pair(NodeIndex(42), 5), Pair(NodeIndex(3), 8)));
}
TEST(BoundedDisjktraTest, RandomizedStressTest) {
@@ -601,49 +647,51 @@ TEST(BoundedDisjktraTest, RandomizedStressTest) {
constexpr int kint32max = std::numeric_limits<int>::max();
for (int test = 0; test < kNumTests; ++test) {
// Generate a random graph with random weights.
const int num_nodes = absl::Uniform(random, 1, 12);
const int num_arcs =
absl::Uniform(absl::IntervalClosed, random, 0,
std::min(num_nodes * (num_nodes - 1), 15));
ListGraph<> graph(num_nodes, num_arcs);
for (int a = 0; a < num_arcs; ++a) {
graph.AddArc(absl::Uniform(random, 0, num_nodes),
absl::Uniform(random, 0, num_nodes));
const NodeIndex num_nodes(absl::Uniform(random, 1, 12));
const ArcIndex num_arcs(absl::Uniform(
absl::IntervalClosed, random, 0,
std::min(num_nodes.value() * (num_nodes.value() - 1), 15)));
TestGraph graph(num_nodes, num_arcs);
for (ArcIndex a(0); a < num_arcs; ++a) {
graph.AddArc(NodeIndex(absl::Uniform(random, 0, num_nodes.value())),
NodeIndex(absl::Uniform(random, 0, num_nodes.value())));
}
std::vector<int> lengths(num_arcs);
DijkstraWrapper<int>::ByArc<int> lengths(num_arcs);
for (int& w : lengths) w = absl::Uniform(random, 0, 5);
// Run Floyd-Warshall as a 'reference' shortest path algorithm.
FlatMatrix<int> ref_dist(num_nodes, num_nodes, kint32max);
for (int a = 0; a < num_arcs; ++a) {
int& d = ref_dist[graph.Tail(a)][graph.Head(a)];
FlatMatrix<int> ref_dist(num_nodes.value(), num_nodes.value(), kint32max);
for (ArcIndex a(0); a < num_arcs; ++a) {
int& d = ref_dist[graph.Tail(a).value()][graph.Head(a).value()];
if (lengths[a] < d) d = lengths[a];
}
for (int node = 0; node < num_nodes; ++node) {
ref_dist[node][node] = 0;
for (NodeIndex node(0); node < num_nodes; ++node) {
ref_dist[node.value()][node.value()] = 0;
}
for (int k = 0; k < num_nodes; ++k) {
for (int i = 0; i < num_nodes; ++i) {
for (int j = 0; j < num_nodes; ++j) {
for (NodeIndex k(0); k < num_nodes; ++k) {
for (NodeIndex i(0); i < num_nodes; ++i) {
for (NodeIndex j(0); j < num_nodes; ++j) {
const int64_t dist_through_k =
static_cast<int64_t>(ref_dist[i][k]) + ref_dist[k][j];
if (dist_through_k < ref_dist[i][j]) ref_dist[i][j] = dist_through_k;
static_cast<int64_t>(ref_dist[i.value()][k.value()]) +
ref_dist[k.value()][j.value()];
if (dist_through_k < ref_dist[i.value()][j.value()])
ref_dist[i.value()][j.value()] = dist_through_k;
}
}
}
// Compute the graph's largest distance below kint32max.
int max_distance = 0;
for (int i = 0; i < num_nodes; ++i) {
for (int j = 0; j < num_nodes; ++j) {
const int d = ref_dist[i][j];
for (NodeIndex i(0); i < num_nodes; ++i) {
for (NodeIndex j(0); j < num_nodes; ++j) {
const int d = ref_dist[i.value()][j.value()];
if (d != kint32max && d > max_distance) max_distance = d;
}
}
// Now, run some Dijkstras and verify that they match. To balance out the
// FW (Floyd-Warshall) which is O(N³), we run more than one Dijkstra per FW.
BoundedDijkstraWrapper<ListGraph<>, int> dijkstra(&graph, &lengths);
DijkstraWrapper<int> dijkstra(&graph, &lengths);
for (int num_dijkstra = 0; num_dijkstra < 20; ++num_dijkstra) {
// Draw the distance limit.
const int limit =
@@ -652,33 +700,34 @@ TEST(BoundedDisjktraTest, RandomizedStressTest) {
: absl::Uniform(absl::IntervalClosed, random, 0, max_distance);
// Draw sources (*with* repetition) with initial distances.
const int num_sources = absl::Uniform(random, 1, 5);
std::vector<std::pair<int, int>> sources(num_sources);
std::vector<std::pair<NodeIndex, int>> sources(num_sources);
for (auto& [s, dist] : sources) {
s = absl::Uniform(random, 0, num_nodes);
s = NodeIndex(absl::Uniform(random, 0, num_nodes.value()));
dist = absl::Uniform(absl::IntervalClosed, random, 0, max_distance + 1);
}
// Precompute the reference minimum distance to each node (using any of
// the sources), and the expected reached nodes: any node whose distance
// is < limit. That includes the sources: if a source's initial distance
// is ≥ limit, it won't be reached.That includes the source themselves.
std::vector<int> node_min_dist(num_nodes, kint32max);
std::vector<int> expected_reached_nodes;
for (int node = 0; node < num_nodes; ++node) {
DijkstraWrapper<int>::ByNode<int> node_min_dist(num_nodes, kint32max);
DijkstraWrapper<int>::ByNode<NodeIndex> expected_reached_nodes;
for (NodeIndex node(0); node < num_nodes; ++node) {
int min_dist = kint32max;
for (const auto& [src, dist] : sources) {
// Cast to int64_t to avoid overflows.
min_dist = std::min<int64_t>(
min_dist, static_cast<int64_t>(ref_dist[src][node]) + dist);
min_dist,
static_cast<int64_t>(ref_dist[src.value()][node.value()]) + dist);
}
node_min_dist[node] = min_dist;
if (min_dist < limit) expected_reached_nodes.push_back(node);
}
const std::vector<int> reached_nodes =
const auto reached_nodes =
dijkstra.RunBoundedDijkstraFromMultipleSources(sources, limit);
EXPECT_THAT(reached_nodes,
UnorderedElementsAreArray(expected_reached_nodes));
for (const int node : reached_nodes) {
for (const NodeIndex node : reached_nodes) {
EXPECT_EQ(dijkstra.distances()[node], node_min_dist[node]) << node;
}
ASSERT_FALSE(HasFailure())
@@ -697,7 +746,8 @@ void BM_GridGraph(benchmark::State& state) {
const int kSourceNode = static_cast<int>(kWidth * kHeight / 2);
std::unique_ptr<Graph> graph =
util::Create2DGridGraph<Graph>(/*width=*/kWidth, /*height=*/kHeight);
std::vector<int64_t> arc_lengths(graph->num_arcs(), 0);
BoundedDijkstraWrapper<Graph, int64_t>::ByArc<int64_t> arc_lengths(
graph->num_arcs(), 0);
const int64_t min_length = arc_lengths_are_discrete ? 0 : 1;
const int64_t max_length = arc_lengths_are_discrete ? 2 : 1000000000000000L;
std::mt19937 random(12345);

View File

@@ -420,6 +420,8 @@ class Vector : public std::vector<T> {
template <typename IndexT, typename T>
class SVector {
public:
using value_type = T;
SVector() : base_(nullptr), size_(0), capacity_(0) {}
~SVector() { clear_and_dealloc(); }
@@ -434,7 +436,7 @@ class SVector {
capacity_ = other.size_;
base_ = Allocate(capacity_);
CHECK(base_ != nullptr);
base_ += capacity_;
base_ += static_cast<ptrdiff_t>(capacity_);
} else { // capacity_ >= other.size
clear();
}
@@ -488,6 +490,9 @@ class SVector {
T* data() const { return base_; }
const T* begin() const { return base_; }
const T* end() const { return base_ + static_cast<ptrdiff_t>(size_); }
void swap(SVector<IndexT, T>& x) noexcept {
std::swap(base_, x.base_);
std::swap(size_, x.size_);
@@ -564,8 +569,9 @@ class SVector {
// Copies other.base_ to base_ in this SVector. Safe for all types as it uses
// constructor for each entry.
void CopyInternal(const SVector& other, std::false_type) {
for (int i = -size_; i < size_; ++i) {
new (base_ + i) T(other.base_[i]);
for (IndexT i = -size_; i < size_; ++i) {
new (base_ + static_cast<ptrdiff_t>(i))
T(other.base_[static_cast<ptrdiff_t>(i)]);
}
}
@@ -1091,41 +1097,21 @@ class ReverseArcStaticGraph
// TODO(user): consider slower but more memory efficient implementations that
// follow the cycles of the permutation and use a bitmap to indicate what has
// been permuted or to mark the beginning of each cycle.
// Some compiler do not know typeof(), so we have to use this extra function
// internally.
template <class IntVector, class Array, class ElementType>
void PermuteWithExplicitElementType(const IntVector& permutation,
Array& array_to_permute,
ElementType unused) {
std::vector<ElementType> temp(permutation.size());
for (size_t i = 0; i < permutation.size(); ++i) {
temp[i] = array_to_permute[i];
}
for (size_t i = 0; i < permutation.size(); ++i) {
array_to_permute[static_cast<size_t>(permutation[i])] = temp[i];
}
}
template <class IntVector, class Array>
void Permute(const IntVector& permutation, Array* array_to_permute) {
if (permutation.empty()) {
return;
}
PermuteWithExplicitElementType(permutation, *array_to_permute,
(*array_to_permute)[0]);
}
// We need a specialization for vector<bool>, because the default code uses
// (*array_to_permute)[0] as ElementType, which isn't 'bool' in that case.
template <class IntVector>
void Permute(const IntVector& permutation,
std::vector<bool>* array_to_permute) {
if (permutation.empty()) {
return;
const auto size = permutation.size();
auto& array = *array_to_permute;
using ElementType =
typename std::iterator_traits<decltype(std::begin(array))>::value_type;
std::vector<ElementType> temp(size);
auto array_begin = std::begin(array);
std::copy_n(array_begin, size, temp.begin());
for (size_t i = 0; i < permutation.size(); ++i) {
*(array_begin + static_cast<size_t>(permutation[i])) = temp[i];
}
bool unused = false;
PermuteWithExplicitElementType(permutation, *array_to_permute, unused);
}
// BaseGraph implementation ----------------------------------------------------

View File

@@ -97,12 +97,12 @@ std::string GraphToString(const Graph& graph, GraphToStringFormat format) {
} else { // PRINT_GRAPH_ADJACENCY_LISTS[_SORTED]
adj.clear();
for (const typename Graph::ArcIndex arc : graph.OutgoingArcs(node)) {
adj.push_back(graph.Head(arc));
adj.push_back(static_cast<uint64_t>(graph.Head(arc)));
}
if (format == PRINT_GRAPH_ADJACENCY_LISTS_SORTED) {
std::sort(adj.begin(), adj.end());
}
if (node != 0) out += '\n';
if (node != typename Graph::NodeIndex(0)) out += '\n';
absl::StrAppend(&out, static_cast<uint64_t>(node), ": ",
absl::StrJoin(adj, " "));
}

View File

@@ -24,6 +24,7 @@
#include <utility>
#include <vector>
#include "absl/algorithm/container.h"
#include "absl/log/check.h"
#include "absl/random/random.h"
#include "absl/strings/str_cat.h"
@@ -32,9 +33,11 @@
#include "gtest/gtest.h"
#include "ortools/base/gmock.h"
#include "ortools/base/strong_int.h"
#include "ortools/base/strong_vector.h"
namespace util {
using testing::ElementsAre;
using testing::Pair;
using testing::UnorderedElementsAre;
@@ -289,98 +292,144 @@ void ConstructAndCheckGraph(
// Return the size of the memory block allocated by malloc when asking for x
// bytes.
inline int UpperBoundOfMallocBlockSizeOf(int x) {
template <typename IndexType>
inline IndexType UpperBoundOfMallocBlockSizeOf(IndexType x) {
// Note(user): as of 2012-09, the rule seems to be: round x up to the
// next multiple of 16.
// WARNING: This may change, and may already be wrong for small values.
return 16 * ((x + 15) / 16);
return IndexType((16 * (static_cast<int64_t>(x) + 15)) / 16);
}
TEST(SVectorTest, DynamicGrowth) {
internal::SVector<int, int> v;
EXPECT_EQ(0, v.size());
EXPECT_EQ(0, v.capacity());
for (int i = 0; i < 100; i++) {
template <typename IndexType>
class SVectorTest : public ::testing::Test {};
typedef ::testing::Types<std::pair<int, int>, std::pair<int, StrongArcId>,
std::pair<StrongArcId, int>,
std::pair<StrongArcId, StrongArcId>>
TestSVectorIndexTypes;
TYPED_TEST_SUITE(SVectorTest, TestSVectorIndexTypes);
TYPED_TEST(SVectorTest, CopyMoveIterate) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
using VectorT = internal::SVector<IndexT, ValueT>;
VectorT v;
v.resize(IndexT(2));
v[IndexT(0)] = ValueT(1);
v[IndexT(1)] = ValueT(2);
{
EXPECT_THAT(VectorT(v), ElementsAre(ValueT(1), ValueT(2)));
VectorT v2 = v;
EXPECT_THAT(v2, ElementsAre(ValueT(1), ValueT(2)));
EXPECT_THAT(v, ElementsAre(ValueT(1), ValueT(2)));
}
{
VectorT v2 = std::move(v);
EXPECT_THAT(v2, ElementsAre(ValueT(1), ValueT(2)));
EXPECT_THAT(VectorT(std::move(v2)), ElementsAre(ValueT(1), ValueT(2)));
}
}
TYPED_TEST(SVectorTest, DynamicGrowth) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
internal::SVector<IndexT, ValueT> v;
EXPECT_EQ(IndexT(0), v.size());
EXPECT_EQ(IndexT(0), v.capacity());
for (ValueT i(0); i < ValueT(100); i++) {
v.grow(-i, i);
}
EXPECT_EQ(100, v.size());
EXPECT_GE(v.capacity(), 100);
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(100));
for (int i = 0; i < 100; i++) {
EXPECT_EQ(-i, v[~i]);
EXPECT_EQ(i, v[i]);
EXPECT_EQ(IndexT(100), v.size());
EXPECT_GE(v.capacity(), IndexT(100));
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(IndexT(100)));
for (IndexT i(0); i < IndexT(100); ++i) {
EXPECT_EQ(ValueT(static_cast<int>(-i)), v[~i]);
EXPECT_EQ(ValueT(static_cast<int>(i)), v[i]);
}
}
TEST(SVectorTest, Reserve) {
internal::SVector<int, int> v;
v.reserve(100);
EXPECT_EQ(0, v.size());
EXPECT_GE(v.capacity(), 100);
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(100));
for (int i = 0; i < 100; i++) {
TYPED_TEST(SVectorTest, Reserve) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
internal::SVector<IndexT, ValueT> v;
v.reserve(IndexT(100));
EXPECT_EQ(IndexT(0), v.size());
EXPECT_GE(v.capacity(), IndexT(100));
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(IndexT(100)));
for (ValueT i(0); i < ValueT(100); i++) {
v.grow(-i, i);
}
EXPECT_EQ(100, v.size());
EXPECT_GE(v.capacity(), 100);
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(100));
for (int i = 0; i < 10; i++) {
EXPECT_EQ(-i, v[~i]);
EXPECT_EQ(i, v[i]);
EXPECT_EQ(IndexT(100), v.size());
EXPECT_GE(v.capacity(), IndexT(100));
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(IndexT(100)));
for (IndexT i(0); i < IndexT(10); i++) {
EXPECT_EQ(ValueT(static_cast<int>(-i)), v[~i]);
EXPECT_EQ(ValueT(static_cast<int>(i)), v[i]);
}
}
TEST(SVectorTest, Resize) {
internal::SVector<int, int> v;
v.resize(100);
EXPECT_EQ(100, v.size());
EXPECT_GE(v.capacity(), 100);
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(100));
for (int i = 0; i < 100; i++) {
EXPECT_EQ(0, v[-i - 1]);
EXPECT_EQ(0, v[i]);
TYPED_TEST(SVectorTest, Resize) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
internal::SVector<IndexT, ValueT> v;
v.resize(IndexT(100));
EXPECT_EQ(IndexT(100), v.size());
EXPECT_GE(v.capacity(), IndexT(100));
EXPECT_LE(v.capacity(), UpperBoundOfMallocBlockSizeOf(IndexT(100)));
for (IndexT i(0); i < IndexT(100); ++i) {
EXPECT_EQ(ValueT(0), v[-i - IndexT(1)]);
EXPECT_EQ(ValueT(0), v[i]);
}
}
TEST(SVectorTest, ResizeToZero) {
internal::SVector<int, char> s;
s.resize(1);
s.resize(0);
EXPECT_EQ(0, s.size());
TYPED_TEST(SVectorTest, ResizeToZero) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
internal::SVector<IndexT, ValueT> v;
v.resize(IndexT(1));
v.resize(IndexT(0));
EXPECT_EQ(IndexT(0), v.size());
}
TEST(SVectorTest, Swap) {
internal::SVector<int, char> s;
internal::SVector<int, char> t;
s.resize(1);
s[0] = 's';
s[-1] = 's';
t.resize(2);
for (int i = -2; i <= 1; ++i) {
t[i] = 't';
TYPED_TEST(SVectorTest, Swap) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
internal::SVector<IndexT, ValueT> s;
internal::SVector<IndexT, ValueT> t;
s.resize(IndexT(1));
s[IndexT(0)] = ValueT('s');
s[IndexT(-1)] = ValueT('s');
t.resize(IndexT(2));
for (IndexT i(-2); i <= IndexT(1); ++i) {
t[i] = ValueT('t');
}
s.swap(t);
EXPECT_EQ(1, t.size());
EXPECT_EQ('s', t[-1]);
EXPECT_EQ('s', t[0]);
EXPECT_EQ(2, s.size());
EXPECT_EQ('t', s[-2]);
EXPECT_EQ('t', s[-1]);
EXPECT_EQ('t', s[0]);
EXPECT_EQ('t', s[1]);
EXPECT_EQ(IndexT(1), t.size());
EXPECT_EQ(ValueT('s'), t[IndexT(-1)]);
EXPECT_EQ(ValueT('s'), t[IndexT(0)]);
EXPECT_EQ(IndexT(2), s.size());
EXPECT_EQ(ValueT('t'), s[IndexT(-2)]);
EXPECT_EQ(ValueT('t'), s[IndexT(-1)]);
EXPECT_EQ(ValueT('t'), s[IndexT(0)]);
EXPECT_EQ(ValueT('t'), s[IndexT(1)]);
}
TEST(SVectorTest, SwapAndDestroy) {
internal::SVector<int, int> s;
TYPED_TEST(SVectorTest, SwapAndDestroy) {
using IndexT = typename TypeParam::first_type;
using ValueT = typename TypeParam::second_type;
internal::SVector<IndexT, ValueT> s;
{
internal::SVector<int, int> t;
t.resize(2);
t[-2] = 42;
internal::SVector<IndexT, ValueT> t;
t.resize(IndexT(2));
t[IndexT(-2)] = ValueT(42);
t.swap(s);
}
EXPECT_EQ(2, s.size());
EXPECT_EQ(42, s[-2]);
EXPECT_EQ(0, s[1]);
EXPECT_EQ(IndexT(2), s.size());
EXPECT_EQ(ValueT(42), s[IndexT(-2)]);
EXPECT_EQ(ValueT(0), s[IndexT(1)]);
}
// Use a more complex type to better check the invocations of
@@ -458,7 +507,7 @@ class MoveOnlyObject {
int MoveOnlyObject::sequence_ = 1;
int MoveOnlyObject::object_count_ = 0;
TEST(SVectorTest, MoveWithMoveOnlyObject) {
TEST(SVectorMoveOnlyTest, MoveWithMoveOnlyObject) {
EXPECT_EQ(0, MoveOnlyObject::GetObjectCount());
internal::SVector<int, MoveOnlyObject> a;
a.resize(10);
@@ -472,7 +521,7 @@ TEST(SVectorTest, MoveWithMoveOnlyObject) {
EXPECT_EQ(0, a.size()); // NOLINT
}
TEST(SVectorTest, ShrinkWithMoveOnlyObject) {
TEST(SVectorMoveOnlyTest, ShrinkWithMoveOnlyObject) {
EXPECT_EQ(0, MoveOnlyObject::GetObjectCount());
{
internal::SVector<int, MoveOnlyObject> a;
@@ -484,7 +533,7 @@ TEST(SVectorTest, ShrinkWithMoveOnlyObject) {
EXPECT_EQ(0, MoveOnlyObject::GetObjectCount());
}
TEST(SVectorTest, GrowMoveOnlyObject) {
TEST(SVectorMoveOnlyTest, GrowMoveOnlyObject) {
EXPECT_EQ(0, MoveOnlyObject::GetObjectCount());
{
internal::SVector<int, MoveOnlyObject> a;
@@ -501,7 +550,7 @@ TEST(SVectorTest, GrowMoveOnlyObject) {
EXPECT_EQ(0, MoveOnlyObject::GetObjectCount());
}
TEST(SVectorTest, ReserveMoveOnlyObject) {
TEST(SVectorMoveOnlyTest, ReserveMoveOnlyObject) {
EXPECT_EQ(0, MoveOnlyObject::GetObjectCount());
{
internal::SVector<int, MoveOnlyObject> a;
@@ -554,7 +603,7 @@ int TrackedObject::num_destructions = 0;
int TrackedObject::num_moves = 0;
int TrackedObject::num_copies = 0;
TEST(SVectorTest, CopyConstructor) {
TEST(SVectorTrackingTest, CopyConstructor) {
TrackedObject::ResetCounters();
ASSERT_EQ(TrackedObject::Counters(),
"constructions: 0, destructions: 0, moves: 0, copies: 0");
@@ -573,7 +622,7 @@ TEST(SVectorTest, CopyConstructor) {
ASSERT_EQ(v_copy.size(), 5);
}
TEST(SVectorTest, AssignmentOperator) {
TEST(SVectorTrackingTest, AssignmentOperator) {
TrackedObject::ResetCounters();
ASSERT_EQ(TrackedObject::Counters(),
"constructions: 0, destructions: 0, moves: 0, copies: 0");
@@ -595,7 +644,7 @@ TEST(SVectorTest, AssignmentOperator) {
ASSERT_EQ(other.size(), 5);
}
TEST(SVectorTest, CopyConstructorIntegralType) {
TEST(SVectorTrackingTest, CopyConstructorIntegralType) {
auto v = internal::SVector<int, int32_t>();
v.resize(3);
v[-3] = 1;
@@ -613,7 +662,7 @@ TEST(SVectorTest, CopyConstructorIntegralType) {
}
}
TEST(SVectorTest, AssignmentOperatorIntegralType) {
TEST(SVectorTrackingTest, AssignmentOperatorIntegralType) {
internal::SVector<int, int32_t> other;
auto v = internal::SVector<int, int32_t>();
v.resize(3);
@@ -632,7 +681,7 @@ TEST(SVectorTest, AssignmentOperatorIntegralType) {
}
}
TEST(SVectorTest, MoveConstructor) {
TEST(SVectorTrackingTest, MoveConstructor) {
TrackedObject::ResetCounters();
ASSERT_EQ(TrackedObject::Counters(),
"constructions: 0, destructions: 0, moves: 0, copies: 0");
@@ -650,7 +699,7 @@ TEST(SVectorTest, MoveConstructor) {
ASSERT_EQ(b.size(), 5);
}
TEST(SVectorTest, MoveAssignmentOperator) {
TEST(SVectorTrackingTest, MoveAssignmentOperator) {
TrackedObject::ResetCounters();
ASSERT_EQ(TrackedObject::Counters(),
"constructions: 0, destructions: 0, moves: 0, copies: 0");
@@ -1011,6 +1060,28 @@ TEST(SVector, NoHeapCheckerFalsePositive) {
EXPECT_EQ(kVector->size(), 5000);
}
TEST(Permute, IntArray) {
int array[] = {4, 5, 6};
std::vector<int> permutation = {0, 2, 1};
util::Permute(permutation, &array);
EXPECT_THAT(array, ElementsAre(4, 6, 5));
}
TEST(Permute, BoolVector) {
std::vector<bool> array = {true, false, true};
std::vector<int> permutation = {0, 2, 1};
util::Permute(permutation, &array);
EXPECT_THAT(array, ElementsAre(true, true, false));
}
TEST(Permute, StrongVector) {
util_intops::StrongVector<StrongArcId, int> array = {4, 5, 6};
std::vector<StrongArcId> permutation = {StrongArcId(0), StrongArcId(2),
StrongArcId(1)};
util::Permute(permutation, &array);
EXPECT_THAT(array, ElementsAre(4, 6, 5));
}
template <typename GraphType, bool reserve>
static void BM_RandomArcs(benchmark::State& state) {
const int kRandomSeed = 0;
@@ -1304,4 +1375,23 @@ static void BM_CompleteBipartiteGraphTailHead(benchmark::State& state) {
BENCHMARK_TEMPLATE(BM_CompleteBipartiteGraphTailHead, int32_t);
BENCHMARK_TEMPLATE(BM_CompleteBipartiteGraphTailHead, int16_t);
template <typename ArrayT, typename IndexT>
void BM_Permute(benchmark::State& state) {
const int size = state.range(0);
ArrayT array(size);
std::vector<IndexT> permutation(size);
absl::c_iota(permutation, IndexT(0));
for (const auto s : state) {
util::Permute(permutation, &array);
benchmark::DoNotOptimize(array);
benchmark::DoNotOptimize(permutation);
}
}
BENCHMARK(BM_Permute<util_intops::StrongVector<StrongArcId, int>, StrongArcId>)
->Arg(128);
BENCHMARK(BM_Permute<std::vector<int>, int>)->Arg(128);
BENCHMARK(BM_Permute<std::vector<bool>, int>)->Arg(128);
} // namespace util

View File

@@ -18,6 +18,7 @@
import numpy as np
from ortools.graph.python import linear_sum_assignment
# [END import]

View File

@@ -16,6 +16,7 @@
"""Linear assignment example."""
# [START import]
from ortools.graph.python import min_cost_flow
# [END import]

View File

@@ -16,6 +16,7 @@
"""Assignment with teams of workers."""
# [START import]
from ortools.graph.python import min_cost_flow
# [END import]

View File

@@ -50,8 +50,8 @@ int main(int argc, char** argv) {
// Solve the shortest path problem from 0 to 5.
std::pair<int, std::vector<int>> result =
operations_research::SimpleOneToOneShortestPath<int>(0, 5, tails, heads,
lengths);
operations_research::SimpleOneToOneShortestPath<int, int>(0, 5, tails,
heads, lengths);
// Print to length of the path and then the nodes in the path.
std::cout << "Shortest path length: " << result.first << std::endl;

View File

@@ -59,8 +59,8 @@ int main(int argc, char** argv) {
// Solve the shortest path problem from 0 to 4.
std::pair<int, std::vector<int>> result =
operations_research::SimpleOneToOneShortestPath<int>(0, 4, tails, heads,
lengths);
operations_research::SimpleOneToOneShortestPath<int, int>(0, 4, tails,
heads, lengths);
// Print to length of the path and then the nodes in the path.
std::cout << "Shortest path length: " << result.first << std::endl;

View File

@@ -18,6 +18,7 @@
import numpy as np
from ortools.graph.python import max_flow
# [END import]

View File

@@ -18,6 +18,7 @@
import numpy as np
from ortools.graph.python import min_cost_flow
# [END import]