graph: export from google3
dump_vars: Add support for StrongInt and StrongVector
This commit is contained in:
@@ -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",
|
||||
],
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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)));
|
||||
|
||||
@@ -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",
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 ----------------------------------------------------
|
||||
|
||||
@@ -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, " "));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import numpy as np
|
||||
|
||||
from ortools.graph.python import linear_sum_assignment
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Linear assignment example."""
|
||||
# [START import]
|
||||
from ortools.graph.python import min_cost_flow
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"""Assignment with teams of workers."""
|
||||
# [START import]
|
||||
from ortools.graph.python import min_cost_flow
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import numpy as np
|
||||
|
||||
from ortools.graph.python import max_flow
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
import numpy as np
|
||||
|
||||
from ortools.graph.python import min_cost_flow
|
||||
|
||||
# [END import]
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user