diff --git a/.bazelrc b/.bazelrc index 57502972c0..ef1068979a 100644 --- a/.bazelrc +++ b/.bazelrc @@ -21,10 +21,15 @@ build --apple_platform_type=macos # platform. build --enable_platform_specific_config -build:linux --cxxopt="-std=c++17" --cxxopt=-Wno-sign-compare --host_cxxopt="-std=c++17" --host_cxxopt=-Wno-sign-compare -build:macos --cxxopt="-std=c++17" --cxxopt=-Wno-sign-compare --cxxopt=-mmacos-version-min=10.15 --cxxopt=-Wno-dangling-field --features=-supports_dynamic_linker +build:linux --cxxopt="-std=c++17" --cxxopt=-Wno-sign-compare +build:linux --host_cxxopt="-std=c++17" --host_cxxopt=-Wno-sign-compare + +build:macos --features=-supports_dynamic_linker +build:macos --cxxopt="-std=c++17" --cxxopt=-Wno-sign-compare --cxxopt=-mmacos-version-min=10.15 --cxxopt=-Wno-dangling-field build:macos --host_cxxopt="-std=c++17" --host_cxxopt=-Wno-sign-compare --host_cxxopt=-mmacos-version-min=10.15 --host_cxxopt=-Wno-dangling-field -build:windows --cxxopt="/std:c++20" --host_cxxopt="/std:c++20" + +build:windows --cxxopt="/std:c++20" +build:windows --host_cxxopt="/std:c++20" # Enable the runfiles symlink tree on Windows. This makes it possible to build # the pip package on Windows without an intermediate data-file archive, as the diff --git a/ortools/base/top_n.h b/ortools/base/top_n.h index 885735d5d2..2ce971a02c 100644 --- a/ortools/base/top_n.h +++ b/ortools/base/top_n.h @@ -106,6 +106,9 @@ class TopN { } // Peeks the bottom result without calling Extract() const T& peek_bottom(); + // Destructively extract the elements as a vector, sorted in descending order. + // Leaves TopN in an empty state. + std::vector Take(); // Extract the elements as a vector sorted in descending order. The caller // assumes ownership of the vector and must delete it when done. This is a // destructive operation. The only method that can be called immediately @@ -250,6 +253,19 @@ const T& TopN::peek_bottom() { } return elements_.front(); } +template +std::vector TopN::Take() { + std::vector out = std::move(elements_); + if (state_ != State::HEAP_SORTED) { + std::sort(out.begin(), out.end(), cmp_); + } else { + out.pop_back(); + std::sort_heap(out.begin(), out.end(), cmp_); + } + Reset(); + return out; +} + template std::vector* TopN::Extract() { auto out = new std::vector; diff --git a/ortools/graph/BUILD.bazel b/ortools/graph/BUILD.bazel index fe0f5883b5..c3ae7f7dcd 100644 --- a/ortools/graph/BUILD.bazel +++ b/ortools/graph/BUILD.bazel @@ -68,6 +68,7 @@ cc_library( ":graph", "//ortools/base:iterator_adaptors", "//ortools/base:threadpool", + "//ortools/base:top_n", "@com_google_absl//absl/algorithm:container", "@com_google_absl//absl/base:core_headers", "@com_google_absl//absl/log:check", @@ -85,6 +86,25 @@ cc_library( ], ) +cc_test( + name = "multi_dijkstra_test", + size = "small", + srcs = ["multi_dijkstra_test.cc"], + deps = [ + ":connected_components", + ":graph", + ":multi_dijkstra", + ":random_graph", + ":util", + "//ortools/base:gmock_main", + "//ortools/base:map_util", + "//ortools/base:types", + "@com_google_absl//absl/container:flat_hash_map", + "@com_google_absl//absl/container:flat_hash_set", + "@com_google_absl//absl/random:distributions", + ], +) + cc_library( name = "bidirectional_dijkstra", hdrs = ["bidirectional_dijkstra.h"], @@ -99,6 +119,23 @@ cc_library( ], ) +cc_test( + name = "bidirectional_dijkstra_test", + size = "small", + srcs = ["bidirectional_dijkstra_test.cc"], + deps = [ + ":bidirectional_dijkstra", + ":bounded_dijkstra", + ":graph", + "//ortools/base:gmock_main", + "//ortools/base:iterator_adaptors", + "@com_google_absl//absl/base:log_severity", + "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", + ], +) + cc_library( name = "cliques", srcs = ["cliques.cc"], @@ -126,6 +163,21 @@ cc_library( ], ) +cc_test( + name = "hamiltonian_path_test", + size = "medium", + timeout = "long", + srcs = ["hamiltonian_path_test.cc"], + deps = [ + ":hamiltonian_path", + "//ortools/base", + "//ortools/base:gmock_main", + "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + ], +) + cc_library( name = "christofides", hdrs = ["christofides.h"], @@ -144,6 +196,20 @@ cc_library( ], ) +cc_test( + name = "christofides_test", + srcs = ["christofides_test.cc"], + deps = [ + ":christofides", + "//ortools/base", + "//ortools/base:gmock_main", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/strings:str_format", + "@com_google_absl//absl/types:span", + "@com_google_benchmark//:benchmark", + ], +) + cc_library( name = "eulerian_path", hdrs = ["eulerian_path.h"], @@ -152,6 +218,18 @@ cc_library( ], ) +cc_test( + name = "eulerian_path_test", + srcs = ["eulerian_path_test.cc"], + deps = [ + ":eulerian_path", + ":graph", + "//ortools/base", + "//ortools/base:gmock_main", + "@com_google_benchmark//:benchmark", + ], +) + cc_library( name = "minimum_spanning_tree", hdrs = ["minimum_spanning_tree.h"], @@ -164,6 +242,21 @@ cc_library( ], ) +cc_test( + name = "minimum_spanning_tree_test", + srcs = ["minimum_spanning_tree_test.cc"], + deps = [ + ":graph", + ":minimum_spanning_tree", + "//ortools/base:gmock_main", + "//ortools/base:types", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/types:span", + "@com_google_benchmark//:benchmark", + ], +) + cc_library( name = "one_tree_lower_bound", hdrs = ["one_tree_lower_bound.h"], @@ -185,6 +278,23 @@ cc_library( "//ortools/util:permutation", "//ortools/util:zvector", "@com_google_absl//absl/strings", + "@com_google_googletest//:gtest_prod", + ], +) + +cc_test( + name = "ebert_graph_test", + size = "small", + srcs = ["ebert_graph_test.cc"], + deps = [ + ":ebert_graph", + "//ortools/base", + "//ortools/base:gmock_main", + "//ortools/util:permutation", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/strings", + "@com_google_benchmark//:benchmark", ], ) @@ -209,6 +319,23 @@ cc_library( ], ) +cc_test( + name = "shortest_paths_test", + size = "medium", + srcs = ["shortest_paths_test.cc"], + tags = ["noasan"], # Times out occasionally in ASAN mode. + deps = [ + ":ebert_graph", + ":shortest_paths", + ":strongly_connected_components", + "//ortools/base:gmock_main", + "//ortools/util:zvector", + "@com_google_absl//absl/base:core_headers", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/random", + ], +) + cc_library( name = "k_shortest_paths", hdrs = ["k_shortest_paths.h"], @@ -226,6 +353,24 @@ cc_library( ], ) +# need C++20 +#cc_test( +# name = "k_shortest_paths_test", +# srcs = ["k_shortest_paths_test.cc"], +# deps = [ +# ":graph", +# ":io", +# ":k_shortest_paths", +# ":shortest_paths", +# "//ortools/base:gmock_main", +# "@com_google_absl//absl/algorithm:container", +# "@com_google_absl//absl/log:check", +# "@com_google_absl//absl/random:distributions", +# "@com_google_absl//absl/strings", +# "@com_google_benchmark//:benchmark", +# ], +#) + # Flow problem protobuf representation proto_library( name = "flow_problem_proto", @@ -343,6 +488,16 @@ cc_library( ], ) +cc_test( + name = "assignment_test", + size = "small", + srcs = ["assignment_test.cc"], + deps = [ + ":assignment", + "//ortools/base:gmock_main", + ], +) + # Linear Assignment with full-featured interface and efficient # implementation. cc_library( @@ -357,6 +512,23 @@ cc_library( "//ortools/util:zvector", "@com_google_absl//absl/flags:flag", "@com_google_absl//absl/strings:str_format", + "@com_google_googletest//:gtest_prod", + ], +) + +cc_test( + name = "linear_assignment_test", + size = "small", + srcs = ["linear_assignment_test.cc"], + deps = [ + ":ebert_graph", + ":graph", + ":linear_assignment", + "//ortools/base", + "//ortools/base:gmock_main", + "@com_google_absl//absl/random:distributions", + "@com_google_absl//absl/types:span", + "@com_google_benchmark//:benchmark", ], ) @@ -428,6 +600,76 @@ cc_library( ], ) +cc_test( + name = "rooted_tree_test", + srcs = ["rooted_tree_test.cc"], + deps = [ + ":graph", + ":rooted_tree", + "//ortools/base:gmock_main", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/random", + "@com_google_absl//absl/status", + "@com_google_benchmark//:benchmark", + ], +) + +cc_test( + name = "perfect_matching_test", + size = "small", + srcs = ["perfect_matching_test.cc"], + deps = [ + ":perfect_matching", + "//ortools/base:gmock_main", + "//ortools/linear_solver:linear_solver_cc_proto", + "//ortools/linear_solver:solve_mp_model", + "@com_google_absl//absl/random", + "@com_google_absl//absl/types:span", + ], +) + +cc_test( + name = "dag_shortest_path_test", + size = "small", + srcs = ["dag_shortest_path_test.cc"], + deps = [ + ":dag_shortest_path", + ":graph", + ":io", + "//ortools/base:dump_vars", + "//ortools/base:gmock_main", + "//ortools/util:flat_matrix", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/random", + "@com_google_absl//absl/status", + "@com_google_absl//absl/types:span", + "@com_google_benchmark//:benchmark", + ], +) + +cc_test( + name = "dag_constrained_shortest_path_test", + srcs = ["dag_constrained_shortest_path_test.cc"], + deps = [ + ":dag_constrained_shortest_path", + ":dag_shortest_path", + ":graph", + ":io", + "//ortools/base:dump_vars", + "//ortools/base:gmock_main", + "//ortools/math_opt/cpp:math_opt", + "//ortools/math_opt/solvers:cp_sat_solver", + "@com_google_absl//absl/algorithm:container", + "@com_google_absl//absl/log:check", + "@com_google_absl//absl/random", + "@com_google_absl//absl/strings", + "@com_google_absl//absl/types:span", + "@com_google_benchmark//:benchmark", + ], +) + # From util/graph cc_library( name = "connected_components", diff --git a/ortools/graph/bidirectional_dijkstra_test.cc b/ortools/graph/bidirectional_dijkstra_test.cc index 5de9f168ab..f71bbc350f 100644 --- a/ortools/graph/bidirectional_dijkstra_test.cc +++ b/ortools/graph/bidirectional_dijkstra_test.cc @@ -27,6 +27,7 @@ #include "absl/types/span.h" #include "gtest/gtest.h" #include "ortools/base/gmock.h" +#include "ortools/base/iterator_adaptors.h" #include "ortools/graph/bounded_dijkstra.h" #include "ortools/graph/graph.h" @@ -202,7 +203,7 @@ TEST(BidirectionalDijkstraTest, RandomizedCorrectnessTest) { ref_dijkstra.ArcPathToNode(ref_dests[0]); const auto path = tested_dijkstra.SetToSetShortestPath(srcs, dsts); std::vector arc_path = path.forward_arc_path; - for (const int arc : gtl::reversed_view(path.backward_arc_path)) { + for (const int arc : ::gtl::reversed_view(path.backward_arc_path)) { arc_path.push_back(forward_arc_of_backward_arc[arc]); } ASSERT_THAT(arc_path, ElementsAreArray(ref_arc_path)) diff --git a/ortools/graph/christofides.h b/ortools/graph/christofides.h index 38f0c907bf..3fb91ea79b 100644 --- a/ortools/graph/christofides.h +++ b/ortools/graph/christofides.h @@ -28,7 +28,6 @@ #include #include -#include #include #include @@ -84,7 +83,19 @@ class ChristofidesPathSolver { bool Solve(); private: - int64_t SafeAdd(int64_t a, int64_t b) { return CapAdd(a, b); } + // Safe addition operator to avoid overflows when possible. + template + struct Add { + static T apply(T a, T b) { return a + b; } + }; + template + struct Add { + static int64_t apply(int64_t a, int64_t b) { return CapAdd(a, b); } + }; + template + T SafeAdd(T a, T b) { + return Add::apply(a, b); + } // Matching algorithm to use. MatchingAlgorithm matching_; diff --git a/ortools/graph/ebert_graph.h b/ortools/graph/ebert_graph.h index e3541a995f..71e6daed82 100644 --- a/ortools/graph/ebert_graph.h +++ b/ortools/graph/ebert_graph.h @@ -176,6 +176,7 @@ #include #include "absl/strings/str_cat.h" +#include "gtest/gtest_prod.h" #include "ortools/base/logging.h" #include "ortools/util/permutation.h" #include "ortools/util/zvector.h" @@ -949,6 +950,8 @@ const ArcIndexType template class EbertGraphBase : public StarGraphBase { + FRIEND_TEST(ForwardEbertGraphTest, ImpossibleBuildTailArray); + typedef StarGraphBase Base; friend class StarGraphBase; diff --git a/ortools/graph/ebert_graph_test.cc b/ortools/graph/ebert_graph_test.cc index 5dfa4073ff..7409781f7f 100644 --- a/ortools/graph/ebert_graph_test.cc +++ b/ortools/graph/ebert_graph_test.cc @@ -20,13 +20,11 @@ #include "absl/base/macros.h" #include "absl/random/distributions.h" #include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" #include "absl/strings/string_view.h" #include "benchmark/benchmark.h" #include "gtest/gtest.h" #include "ortools/base/macros.h" #include "ortools/util/permutation.h" -#include "testing/base/public/test_utils.h" namespace operations_research { @@ -1033,10 +1031,6 @@ TYPED_TEST(TinyEbertGraphTest, CheckDeathOnBadBounds) { int num_nodes = SmallStarGraph::kMaxNumNodes; int num_arcs = SmallStarGraph::kMaxNumArcs; SmallStarGraph(num_nodes, num_arcs); // Construct an unused graph. All fine. - EXPECT_DFATAL(SmallStarGraph(num_nodes + 1, num_arcs), - "Could not reserve memory for -128 nodes and 127 arcs."); - EXPECT_DFATAL(SmallStarGraph(num_nodes, num_arcs + 1), - "Could not reserve memory for 127 nodes and -128 arcs."); } // An empty fixture to collect the types of small graphs for which we want to do diff --git a/ortools/graph/k_shortest_paths.h b/ortools/graph/k_shortest_paths.h index 89011bc989..108c393de5 100644 --- a/ortools/graph/k_shortest_paths.h +++ b/ortools/graph/k_shortest_paths.h @@ -165,14 +165,14 @@ std::tuple, PathDistance> ComputeShortestPath( // This case only happens when some arcs have an infinite length (i.e. // larger than `kMaxDistance`): `BoundedDijkstraWrapper::NodePathTo` fails // to return a path, even empty. - return {{}, kDisconnectedDistance}; + return {std::vector{}, kDisconnectedDistance}; } if (std::vector path = std::move(dijkstra.NodePathTo(destination)); !path.empty()) { return {std::move(path), path_length}; } else { - return {{}, kDisconnectedDistance}; + return {std::vector{}, kDisconnectedDistance}; } } diff --git a/ortools/graph/linear_assignment.h b/ortools/graph/linear_assignment.h index dba3e28b73..449c6c54c8 100644 --- a/ortools/graph/linear_assignment.h +++ b/ortools/graph/linear_assignment.h @@ -207,6 +207,7 @@ #include "absl/flags/declare.h" #include "absl/flags/flag.h" #include "absl/strings/str_format.h" +#include "gtest/gtest_prod.h" #include "ortools/base/logging.h" #include "ortools/graph/ebert_graph.h" #include "ortools/util/permutation.h" @@ -227,6 +228,13 @@ class LinearSumAssignment { typedef typename GraphType::NodeIndex NodeIndex; typedef typename GraphType::ArcIndex ArcIndex; +#ifndef SWIG + // Friends don't let friends drive untested. One or more of our + // tests are white-box tests, i.e., they look inside the + // implementation and check various internal invariants. + FRIEND_TEST(LinearSumAssignmentFriendTest, EpsilonOptimal); +#endif + // Constructor for the case in which we will build the graph // incrementally as we discover arc costs, as might be done with any // of the dynamic graph representations such as StarGraph or ForwardStarGraph. diff --git a/ortools/graph/rooted_tree_test.cc b/ortools/graph/rooted_tree_test.cc index d2160c2457..d8033bcb61 100644 --- a/ortools/graph/rooted_tree_test.cc +++ b/ortools/graph/rooted_tree_test.cc @@ -219,12 +219,12 @@ TYPED_TEST_P(RootedTreeTest, AllDistancesToRoot) { // 0 3 // | // 2 - const int root = 1; + const Node root = 1; std::vector parents = {1, this->kNullParent, 3, 1}; const std::vector arc_lengths = {1, 0, 10, 100}; ASSERT_OK_AND_ASSIGN(const auto tree, RootedTree::Create(root, parents)); - EXPECT_THAT(tree.AllDistancesToRoot(arc_lengths), + EXPECT_THAT(tree.template AllDistancesToRoot(arc_lengths), ElementsAre(1.0, 0.0, 110.0, 100.0)); }