diff --git a/cmake/Makefile b/cmake/Makefile index a2e09550c2..d985ec0be8 100644 --- a/cmake/Makefile +++ b/cmake/Makefile @@ -632,7 +632,7 @@ TOOLCHAIN_STAGES := env devel toolchain build test define toolchain-stage-target = #$$(info STAGE: $1) #$$(info Create targets: toolchain_$1 $(addprefix toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))).) -targets_toolchain_$1 = $(addprefix toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) +targets_toolchain_$1 := $(addprefix toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) .PHONY: toolchain_$1 $$(targets_toolchain_$1) toolchain_$1: $$(targets_toolchain_$1) $$(targets_toolchain_$1): toolchain_%_$1: docker/toolchain/Dockerfile @@ -645,7 +645,7 @@ $$(targets_toolchain_$1): toolchain_%_$1: docker/toolchain/Dockerfile .. #$$(info Create targets: save_toolchain_$1 $(addprefix save_toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) (debug).) -save_targets_toolchain_$1 = $(addprefix save_toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) +save_targets_toolchain_$1 := $(addprefix save_toolchain_, $(addsuffix _$1, $(TOOLCHAIN_TARGETS))) .PHONY: save_toolchain_$1 $$(save_targets_toolchain_$1) save_toolchain_$1: $$(save_targets_toolchain_$1) $$(save_targets_toolchain_$1): save_toolchain_%_$1: cache/%/docker_$1.tar @@ -727,7 +727,7 @@ VAGRANT_VMS := \ define make-vagrant-target = #$$(info VMS: $1) #$$(info Create target: $1_.) -$1_targets = $(addprefix $1_, $(LANGUAGES)) +$1_targets := $(addprefix $1_, $(LANGUAGES)) .PHONY: $1 $$($1_targets) $1: $$($1_targets) $$($1_targets): $1_%: vagrant/$1/%/Vagrantfile @@ -736,14 +736,14 @@ $$($1_targets): $1_%: vagrant/$1/%/Vagrantfile cd vagrant/$1/$$* && vagrant up #$$(info Create targets: sh_$1_ vagrant machine (debug).) -sh_$1_targets = $(addprefix sh_$1_, $(LANGUAGES)) +sh_$1_targets := $(addprefix sh_$1_, $(LANGUAGES)) .PHONY: $$(sh_$1_targets) $$(sh_$1_targets): sh_$1_%: cd vagrant/$1/$$* && vagrant up cd vagrant/$1/$$* && vagrant ssh #$$(info Create targets: clean_$1) -clean_$1_targets = $(addprefix clean_$1_, $(LANGUAGES)) +clean_$1_targets := $(addprefix clean_$1_, $(LANGUAGES)) .PHONY: clean_$1 $(clean_$1_targets) clean_$1: $$(clean_$1_targets) $$(clean_$1_targets): clean_$1_%: diff --git a/ortools/algorithms/BUILD.bazel b/ortools/algorithms/BUILD.bazel index 3d7f2284f3..970599bb97 100644 --- a/ortools/algorithms/BUILD.bazel +++ b/ortools/algorithms/BUILD.bazel @@ -385,6 +385,15 @@ cc_library( ], ) +cc_test( + name = "dense_doubly_linked_list_test", + srcs = ["dense_doubly_linked_list_test.cc"], + deps = [ + ":dense_doubly_linked_list", + "//ortools/base:gmock_main", + ], +) + cc_library( name = "dynamic_partition", srcs = ["dynamic_partition.cc"], diff --git a/ortools/algorithms/CMakeLists.txt b/ortools/algorithms/CMakeLists.txt index 8f23481d2f..8419297312 100644 --- a/ortools/algorithms/CMakeLists.txt +++ b/ortools/algorithms/CMakeLists.txt @@ -12,7 +12,7 @@ # limitations under the License. file(GLOB _SRCS "*.h" "*.cc") -list(FILTER _SRCS EXCLUDE REGEX "/[^/]*_test\\.cc$") +list(FILTER _SRCS EXCLUDE REGEX ".*/.*_test.cc") set(NAME ${PROJECT_NAME}_algorithms) @@ -31,3 +31,23 @@ target_link_libraries(${NAME} PRIVATE protobuf::libprotobuf ${PROJECT_NAMESPACE}::ortools_proto) #add_library(${PROJECT_NAMESPACE}::algorithms ALIAS ${NAME}) + +if(BUILD_TESTING) + file(GLOB _TEST_SRCS "*_test.cc") + list(FILTER _TEST_SRCS EXCLUDE REGEX ".*_stress_test.cc") + list(FILTER _TEST_SRCS EXCLUDE REGEX "set_cover_test.cc") + foreach(_FULL_FILE_NAME IN LISTS _TEST_SRCS) + get_filename_component(_NAME ${_FULL_FILE_NAME} NAME_WE) + get_filename_component(_FILE_NAME ${_FULL_FILE_NAME} NAME) + ortools_cxx_test( + NAME + algorithms_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif() diff --git a/ortools/algorithms/duplicate_remover_test.cc b/ortools/algorithms/duplicate_remover_test.cc deleted file mode 100644 index b7c738b938..0000000000 --- a/ortools/algorithms/duplicate_remover_test.cc +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include "ortools/algorithms/duplicate_remover.h" - -#include -#include - -#include "benchmark/benchmark.h" -#include "gtest/gtest.h" -#include "ortools/base/gmock.h" -#include "ortools/base/linked_hash_set.h" -#include "ortools/util/random_engine.h" -#include "util/tuple/dump_vars.h" - -namespace operations_research { -namespace { - -using ::testing::ElementsAre; -using ::testing::ElementsAreArray; -using ::testing::IsEmpty; - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesEmpty) { - std::vector v; - DenseIntDuplicateRemover deduper(10); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, IsEmpty()); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesNZeroAndEmpty) { - std::vector v; - DenseIntDuplicateRemover deduper(0); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, IsEmpty()); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesSimpleCaseWithDuplicates) { - std::vector v = {1, 8, 2, 2, 8, 4, 1, 2, 7, 0, 2}; - DenseIntDuplicateRemover deduper(9); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, ElementsAre(1, 8, 2, 4, 7, 0)); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesSimpleCaseWithNoDuplicates) { - std::vector v = {3, 2, 0, 5, 4, 1}; - const std::vector v_copy = v; - DenseIntDuplicateRemover deduper(6); - deduper.RemoveDuplicates(&v); - EXPECT_THAT(v, ElementsAreArray(v_copy)); -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesWithRepeatedField) { - const std::vector v = {1, 0, 1, 2, 1}; - google::protobuf::RepeatedField r(v.begin(), v.end()); - DenseIntDuplicateRemover deduper(3); - deduper.RemoveDuplicates(&r); - EXPECT_THAT(r, ElementsAre(1, 0, 2)); -} - -std::vector UniqueValues(absl::Span span) { - absl::flat_hash_set set; - std::vector out; - for (int x : span) - if (set.insert(x).second) out.push_back(x); - return out; -} - -TEST(DenseIntDuplicateRemoverTest, RemoveDuplicatesRandomizedStressTest) { - constexpr int kNumValues = 1003; - DenseIntDuplicateRemover deduper(kNumValues); - constexpr int kNumTests = 1'000'000; - absl::BitGen random; - for (int t = 0; t < kNumTests; ++t) { - const int size = absl::LogUniform(random, 0, 16); - const int domain_size = - absl::Uniform(absl::IntervalClosed, random, 1, kNumValues); - std::vector v(size); - for (int& x : v) x = absl::Uniform(random, 0, domain_size); - const std::vector v_initial = v; - const std::vector unique_values = UniqueValues(v); - deduper.RemoveDuplicates(&v); - ASSERT_THAT(v, ElementsAreArray(unique_values)) << DUMP_VARS(t, v_initial); - } -} - -TEST(DenseIntDuplicateRemoverTest, - AppendAndLazilyRemoveDuplicatesRandomizedStressTest) { - constexpr int kNumValues = 103; - constexpr int kNumTests = 1'000; - std::mt19937 random; - gtl::linked_hash_set reference; - std::vector v; - int64_t num_extra_elements = 0; - int64_t num_unique_elements = 0; - for (int t = 0; t < kNumTests; ++t) { - const int num_inserts = absl::LogUniform(random, 2, 1 << 16); - const int domain_size = - absl::Uniform(absl::IntervalClosed, random, 1, kNumValues); - v.clear(); - reference.clear(); - DenseIntDuplicateRemover deduper(domain_size); - for (int i = 0; i < num_inserts; ++i) { - const int x = absl::Uniform(random, 0, domain_size); - deduper.AppendAndLazilyRemoveDuplicates(x, &v); - reference.insert(x); - } - ASSERT_LE(v.size(), domain_size * 2 + 15); - const int old_size = v.size(); - deduper.RemoveDuplicates(&v); - num_unique_elements += v.size(); - num_extra_elements += old_size - v.size(); - ASSERT_THAT(v, ElementsAreArray(reference)) - << DUMP_VARS(t, num_inserts, domain_size, old_size, v.size()); - } - EXPECT_LE(static_cast(num_extra_elements) / num_unique_elements, 0.5); -} - -template -void BM_AppendAndLazilyRemoveDuplicates(benchmark::State& state) { - const int num_inserts = state.range(0); - const int domain_size = state.range(1); - std::vector to_insert(num_inserts); - random_engine_t random; - for (int& x : to_insert) x = absl::Uniform(random, 0, domain_size); - DenseIntDuplicateRemover deduper(domain_size); - std::vector v; - absl::flat_hash_set set; - for (auto _ : state) { - v.clear(); - set.clear(); - for (int x : to_insert) { - if (use_flat_hash_set) { - set.insert(x); - } else { - deduper.AppendAndLazilyRemoveDuplicates(x, &v); - } - } - if (!use_flat_hash_set) deduper.RemoveDuplicates(&v); - benchmark::DoNotOptimize(v); - benchmark::DoNotOptimize(set); - } - state.SetItemsProcessed(state.iterations() * num_inserts); -} - -BENCHMARK(BM_AppendAndLazilyRemoveDuplicates) - ->ArgPair(1, 10) - ->ArgPair(10, 2) - ->ArgPair(10, 10) - ->ArgPair(100, 100) - ->ArgPair(100, 10) - ->ArgPair(10'000, 10'000) - ->ArgPair(10'000, 1'000) - ->ArgPair(10'000, 100) - ->ArgPair(10'000, 10) - ->ArgPair(1'000'000, 1'000'000) - ->ArgPair(1'000'000, 10'000) - ->ArgPair(1'000'000, 100); - -BENCHMARK(BM_AppendAndLazilyRemoveDuplicates) - ->ArgPair(1, 10) - ->ArgPair(10, 2) - ->ArgPair(10, 10) - ->ArgPair(100, 100) - ->ArgPair(100, 10) - ->ArgPair(10'000, 10'000) - ->ArgPair(10'000, 1'000) - ->ArgPair(10'000, 100) - ->ArgPair(10'000, 10) - ->ArgPair(1'000'000, 1'000'000) - ->ArgPair(1'000'000, 10'000) - ->ArgPair(1'000'000, 100); - -} // namespace -} // namespace operations_research diff --git a/ortools/algorithms/set_cover_orlib_test.cc b/ortools/algorithms/set_cover_orlib_test.cc deleted file mode 100644 index 849e2b76ad..0000000000 --- a/ortools/algorithms/set_cover_orlib_test.cc +++ /dev/null @@ -1,428 +0,0 @@ -// Copyright 2010-2024 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include - -#include "absl/log/check.h" -#include "absl/strings/str_cat.h" -#include "absl/strings/str_join.h" -#include "absl/time/time.h" -#include "gtest/gtest.h" -#include "ortools/algorithms/set_cover_heuristics.h" -#include "ortools/algorithms/set_cover_invariant.h" -#include "ortools/algorithms/set_cover_lagrangian.h" -#include "ortools/algorithms/set_cover_mip.h" -#include "ortools/algorithms/set_cover_model.h" -#include "ortools/algorithms/set_cover_reader.h" -#include "ortools/base/logging.h" -#include "ortools/base/path.h" -#include "ortools/base/timer.h" - -namespace operations_research { - -void LogStats(std::string name, SetCoverModel* model) { - LOG(INFO) << ", " << name << ", num_elements, " << model->num_elements() - << ", num_subsets, " << model->num_subsets(); - LOG(INFO) << ", " << name << ", num_nonzeros, " << model->num_nonzeros() - << ", fill rate, " << model->FillRate(); - LOG(INFO) << ", " << name << ", cost, " - << model->ComputeCostStats().DebugString(); - - LOG(INFO) << ", " << name << ", num_rows, " << model->num_elements() - << ", rows sizes, " << model->ComputeRowStats().DebugString(); - LOG(INFO) << ", " << name << ", row size deciles, " - << absl::StrJoin(model->ComputeRowDeciles(), ", "); - LOG(INFO) << ", " << name << ", num_columns, " << model->num_subsets() - << ", columns sizes, " << model->ComputeColumnStats().DebugString(); - LOG(INFO) << ", " << name << ", column size deciles, " - << absl::StrJoin(model->ComputeColumnDeciles(), ", "); - SetCoverInvariant inv(model); - Preprocessor preprocessor(&inv); - preprocessor.NextSolution(); - LOG(INFO) << ", " << name << ", num_columns_fixed_by_singleton_row, " - << preprocessor.num_columns_fixed_by_singleton_row(); -} - -void LogCostAndTiming(std::string name, std::string algo, double cost, - absl::Duration duration) { - LOG(INFO) << ", " << name << ", " << algo << "_cost, " << cost << ", " - << absl::ToInt64Microseconds(duration) << "e-6, s"; -} - -SetCoverInvariant RunChvatalAndSteepest(std::string name, - SetCoverModel* model) { - SetCoverInvariant inv(model); - GreedySolutionGenerator greedy(&inv); - WallTimer timer; - timer.Start(); - CHECK(greedy.NextSolution()); - DCHECK(inv.CheckConsistency()); - LogCostAndTiming(name, "GreedySolutionGenerator", inv.cost(), - timer.GetDuration()); - SteepestSearch steepest(&inv); - steepest.NextSolution(100000); - LogCostAndTiming(name, "GreedySteepestSearch", inv.cost(), - timer.GetDuration()); - DCHECK(inv.CheckConsistency()); - return inv; -} - -SetCoverInvariant RunChvatalAndGLS(std::string name, SetCoverModel* model) { - SetCoverInvariant inv(model); - GreedySolutionGenerator greedy(&inv); - WallTimer timer; - timer.Start(); - CHECK(greedy.NextSolution()); - DCHECK(inv.CheckConsistency()); - LogCostAndTiming(name, "GreedySolutionGenerator", inv.cost(), - timer.GetDuration()); - GuidedLocalSearch gls(&inv); - gls.NextSolution(100'000); - LogCostAndTiming(name, "GLS", inv.cost(), timer.GetDuration()); - DCHECK(inv.CheckConsistency()); - return inv; -} - -SetCoverInvariant RunElementDegreeGreedyAndSteepest(std::string name, - SetCoverModel* model) { - SetCoverInvariant inv(model); - ElementDegreeSolutionGenerator element_degree(&inv); - WallTimer timer; - timer.Start(); - CHECK(element_degree.NextSolution()); - DCHECK(inv.CheckConsistency()); - LogCostAndTiming(name, "ElementDegreeSolutionGenerator", inv.cost(), - timer.GetDuration()); - SteepestSearch steepest(&inv); - steepest.NextSolution(100000); - LogCostAndTiming(name, "ElementDegreeSteepestSearch", inv.cost(), - timer.GetDuration()); - DCHECK(inv.CheckConsistency()); - return inv; -} - -void IterateClearAndMip(std::string name, SetCoverInvariant* inv) { - WallTimer timer; - timer.Start(); - std::vector focus = inv->model()->all_subsets(); - double best_cost = inv->cost(); - SubsetBoolVector best_choices = inv->is_selected(); - for (int i = 0; i < 10; ++i) { - std::vector range = - ClearMostCoveredElements(std::min(100UL, focus.size()), inv); - SetCoverMip mip(inv); - mip.NextSolution(range, true, 0.02); - DCHECK(inv->CheckConsistency()); - if (inv->cost() < best_cost) { - best_cost = inv->cost(); - best_choices = inv->is_selected(); - } - } - timer.Stop(); - LogCostAndTiming(name, "IterateClearAndMip", best_cost, timer.GetDuration()); -} - -SetCoverInvariant ComputeLPLowerBound(std::string name, SetCoverModel* model) { - SetCoverInvariant inv(model); - WallTimer timer; - timer.Start(); - SetCoverMip mip(&inv, SetCoverMipSolver::SCIP); // Use Gurobi for large pbs. - mip.NextSolution(false, .3); // Use 300s or more for large problems. - LogCostAndTiming(name, "LPLowerBound", mip.lower_bound(), - timer.GetDuration()); - return inv; -} - -void ComputeLagrangianLowerBound(std::string name, SetCoverInvariant* inv) { - const SetCoverModel* model = inv->model(); - WallTimer timer; - timer.Start(); - SetCoverLagrangian lagrangian(inv, /*num_threads=*/8); - const auto [lower_bound, reduced_costs, multipliers] = - lagrangian.ComputeLowerBound(model->subset_costs(), inv->cost()); - LogCostAndTiming(name, "LagrangianLowerBound", lower_bound, - timer.GetDuration()); -} - -SetCoverInvariant RunMip(std::string name, SetCoverModel* model) { - SetCoverInvariant inv(model); - WallTimer timer; - timer.Start(); - SetCoverMip mip(&inv, SetCoverMipSolver::SCIP); // Use Gurobi for large pbs. - mip.NextSolution(true, .5); // Use 300s or more for large problems. - timer.Stop(); - LogCostAndTiming(name, "MIP", inv.cost(), timer.GetDuration()); - return inv; -} - -void IterateClearElementDegreeAndSteepest(std::string name, - SetCoverInvariant* inv) { - WallTimer timer; - timer.Start(); - double best_cost = inv->cost(); - SubsetBoolVector best_choices = inv->is_selected(); - ElementDegreeSolutionGenerator element_degree(inv); - SteepestSearch steepest(inv); - for (int i = 0; i < 1000; ++i) { - std::vector range = - ClearRandomSubsets(0.1 * inv->trace().size(), inv); - CHECK(element_degree.NextSolution()); - steepest.NextSolution(range, 100000); - DCHECK(inv->CheckConsistency()); - if (inv->cost() < best_cost) { - best_cost = inv->cost(); - best_choices = inv->is_selected(); - } - } - timer.Stop(); - LogCostAndTiming(name, "IterateClearElementDegreeAndSteepest", best_cost, - timer.GetDuration()); -} - -double RunSolver(std::string name, SetCoverModel* model) { - LogStats(name, model); - WallTimer global_timer; - global_timer.Start(); - RunChvatalAndSteepest(name, model); - // SetCoverInvariant inv = ComputeLPLowerBound(name, model); - // RunMip(name, model); - RunChvatalAndGLS(name, model); - SetCoverInvariant inv = RunElementDegreeGreedyAndSteepest(name, model); - ComputeLagrangianLowerBound(name, &inv); - // IterateClearAndMip(name, inv); - IterateClearElementDegreeAndSteepest(name, &inv); - return inv.cost(); -} - -// We break down the ORLIB set covering problems by their expected runtime with -// our solver (as of July 2023). -enum ProblemSize { - SUBMILLI, // < 1ms - FEWMILLIS, // < 3ms - SUBHUNDREDTH, // < 10ms - FEWHUNDREDTHS, // < 30ms - SUBTENTH, // < 100ms - FEWTENTHS, // < 300ms - SUBSECOND, // < 1s - FEWSECONDS, // < 3s - MANYSECONDS, // >= 3s - UNKNOWN = 999, // Not known (i.e. not benchmarked). -}; - -// These two macros provide indirection which allows the __LINE__ macro -// to be pasted, giving the tests useful names. -#define APPEND(x, y) x##y -#define APPEND_AND_EVAL(x, y) APPEND(x, y) - -const char data_dir[] = - "operations_research_data/operations_research_data/" - "SET_COVERING"; - -// In the following, the lower bounds are taken from: -// [1] Caprara, Alberto, Matteo Fischetti, and Paolo Toth. 1999. “A Heuristic -// Method for the Set Covering Problem.” Operations Research 47 (5): 730–43. -// https://www.jstor.org/stable/223097 , and -// [2] Yagiura, Mutsunori, Masahiro Kishida, and Toshihide Ibaraki. 2006. -// “A 3-Flip Neighborhood Local Search for the Set Covering Problem.” European -// Journal of Operational Research 172 (2): 472–99. -// https://www.sciencedirect.com/science/article/pii/S0377221704008264 - -// This macro makes it possible to declare each test below with a one-liner. -// 'best_objective' denotes the best objective costs found in literature. -// These are the proven optimal values. This can be achieved with MIP. -// For the rail instances, they are the best solution found in the literature -// [1] and [2]. They are not achievable though local search or MIP or a -// combination of the two. -// 'expected_objective' are the costs currently reached by the solver. -// TODO(user): find and add values for the unit cost (aka unicost) case. - -#define ORLIB_TEST(name, best_objective, expected_objective, size, function) \ - TEST(OrlibTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \ - auto filespec = \ - file::JoinPathRespectAbsolute(::testing::SrcDir(), data_dir, name); \ - LOG(INFO) << "Reading " << name; \ - operations_research::SetCoverModel model = function(filespec); \ - double cost = RunSolver(name, &model); \ - (void)cost; \ - } - -#define ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \ - function) \ - TEST(OrlibUnicostTest, APPEND_AND_EVAL(TestOnLine, __LINE__)) { \ - auto filespec = \ - file::JoinPathRespectAbsolute(::testing::SrcDir(), data_dir, name); \ - LOG(INFO) << "Reading " << name; \ - operations_research::SetCoverModel model = function(filespec); \ - for (SubsetIndex i : model.SubsetRange()) { \ - model.SetSubsetCost(i, 1.0); \ - } \ - double cost = RunSolver(absl::StrCat(name, "_unicost"), &model); \ - (void)cost; \ - } - -#define SCP_TEST(name, best_objective, expected_objective, size) \ - ORLIB_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadBeasleySetCoverProblem) \ - ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadBeasleySetCoverProblem) - -#define RAIL_TEST(name, best_objective, expected_objective, size) \ - ORLIB_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadRailSetCoverProblem) \ - ORLIB_UNICOST_TEST(name, best_objective, expected_objective, size, \ - operations_research::ReadRailSetCoverProblem) - -#define BASIC_SCP -#define EXTRA_SCP -#define RAIL - -#ifdef BASIC_SCP -SCP_TEST("scp41.txt", 429, 442, FEWMILLIS); -SCP_TEST("scp42.txt", 512, 555, FEWMILLIS); -SCP_TEST("scp43.txt", 516, 557, FEWMILLIS); -SCP_TEST("scp44.txt", 494, 516, FEWMILLIS); -SCP_TEST("scp45.txt", 512, 530, FEWMILLIS); -SCP_TEST("scp46.txt", 560, 594, FEWMILLIS); -SCP_TEST("scp47.txt", 430, 451, FEWMILLIS); -SCP_TEST("scp48.txt", 492, 502, FEWMILLIS); -SCP_TEST("scp49.txt", 641, 693, FEWMILLIS); -SCP_TEST("scp410.txt", 514, 525, FEWMILLIS); - -SCP_TEST("scp51.txt", 253, 274, FEWMILLIS); -SCP_TEST("scp52.txt", 302, 329, FEWMILLIS); -SCP_TEST("scp53.txt", 226, 233, FEWMILLIS); -SCP_TEST("scp54.txt", 242, 255, FEWMILLIS); -SCP_TEST("scp55.txt", 211, 222, FEWMILLIS); -SCP_TEST("scp56.txt", 213, 234, FEWMILLIS); -SCP_TEST("scp57.txt", 293, 313, FEWMILLIS); -SCP_TEST("scp58.txt", 288, 309, FEWMILLIS); -SCP_TEST("scp59.txt", 279, 292, FEWMILLIS); -SCP_TEST("scp510.txt", 265, 276, FEWMILLIS); - -SCP_TEST("scp61.txt", 138, 151, FEWMILLIS); -SCP_TEST("scp62.txt", 146, 173, FEWMILLIS); -SCP_TEST("scp63.txt", 145, 154, FEWMILLIS); -SCP_TEST("scp64.txt", 131, 137, FEWMILLIS); -SCP_TEST("scp65.txt", 161, 181, FEWMILLIS); - -SCP_TEST("scpa1.txt", 253, 275, FEWHUNDREDTHS); -SCP_TEST("scpa2.txt", 252, 268, FEWHUNDREDTHS); -SCP_TEST("scpa3.txt", 232, 244, FEWHUNDREDTHS); -SCP_TEST("scpa4.txt", 234, 253, FEWHUNDREDTHS); -SCP_TEST("scpa5.txt", 236, 249, FEWHUNDREDTHS); - -SCP_TEST("scpb1.txt", 69, 74, FEWTENTHS); -SCP_TEST("scpb2.txt", 76, 78, FEWTENTHS); -SCP_TEST("scpb3.txt", 80, 85, FEWTENTHS); -SCP_TEST("scpb4.txt", 79, 85, FEWTENTHS); -SCP_TEST("scpb5.txt", 72, 77, FEWTENTHS); - -SCP_TEST("scpc1.txt", 227, 251, FEWHUNDREDTHS); -SCP_TEST("scpc2.txt", 219, 238, FEWHUNDREDTHS); -SCP_TEST("scpc3.txt", 243, 259, FEWHUNDREDTHS); -SCP_TEST("scpc4.txt", 219, 246, FEWHUNDREDTHS); -SCP_TEST("scpc5.txt", 214, 228, FEWHUNDREDTHS); - -SCP_TEST("scpd1.txt", 60, 68, FEWHUNDREDTHS); -SCP_TEST("scpd2.txt", 66, 70, FEWHUNDREDTHS); -SCP_TEST("scpd3.txt", 72, 78, FEWHUNDREDTHS); -SCP_TEST("scpd4.txt", 62, 67, FEWHUNDREDTHS); -SCP_TEST("scpd5.txt", 61, 72, FEWHUNDREDTHS); - -SCP_TEST("scpe1.txt", 5, 5, FEWMILLIS); -SCP_TEST("scpe2.txt", 5, 6, FEWMILLIS); -SCP_TEST("scpe3.txt", 5, 5, FEWMILLIS); -SCP_TEST("scpe4.txt", 5, 6, FEWMILLIS); -SCP_TEST("scpe5.txt", 5, 5, FEWMILLIS); - -SCP_TEST("scpnre1.txt", 29, 31, SUBTENTH); -SCP_TEST("scpnre2.txt", 30, 34, SUBTENTH); -SCP_TEST("scpnre3.txt", 27, 32, SUBTENTH); -SCP_TEST("scpnre4.txt", 28, 32, SUBTENTH); -SCP_TEST("scpnre5.txt", 28, 31, SUBTENTH); - -SCP_TEST("scpnrf1.txt", 14, 17, SUBTENTH); -SCP_TEST("scpnrf2.txt", 15, 16, SUBTENTH); -SCP_TEST("scpnrf3.txt", 14, 16, SUBTENTH); -SCP_TEST("scpnrf4.txt", 14, 15, SUBTENTH); -SCP_TEST("scpnrf5.txt", 13, 15, SUBTENTH); - -SCP_TEST("scpnrg1.txt", 176, 196, SUBTENTH); -SCP_TEST("scpnrg2.txt", 154, 171, SUBTENTH); -SCP_TEST("scpnrg3.txt", 166, 182, SUBTENTH); -SCP_TEST("scpnrg4.txt", 168, 187, SUBTENTH); -SCP_TEST("scpnrg5.txt", 168, 183, SUBTENTH); - -SCP_TEST("scpnrh1.txt", 63, 71, FEWTENTHS); -SCP_TEST("scpnrh2.txt", 63, 70, FEWTENTHS); -SCP_TEST("scpnrh3.txt", 59, 65, FEWTENTHS); -SCP_TEST("scpnrh4.txt", 58, 66, FEWTENTHS); -SCP_TEST("scpnrh5.txt", 55, 62, FEWTENTHS); -#endif - -#ifdef EXTRA_SCP -SCP_TEST("scpclr10.txt", 0, 32, FEWMILLIS); -SCP_TEST("scpclr11.txt", 0, 30, FEWMILLIS); -SCP_TEST("scpclr12.txt", 0, 31, FEWMILLIS); -SCP_TEST("scpclr13.txt", 0, 33, FEWMILLIS); - -SCP_TEST("scpcyc06.txt", 0, 60, FEWMILLIS); -SCP_TEST("scpcyc07.txt", 0, 144, FEWMILLIS); -SCP_TEST("scpcyc08.txt", 0, 360, FEWMILLIS); -SCP_TEST("scpcyc09.txt", 0, 816, SUBHUNDREDTH); -SCP_TEST("scpcyc10.txt", 0, 1920, FEWHUNDREDTHS); -SCP_TEST("scpcyc11.txt", 0, 4284, SUBTENTH); -#endif - -#ifdef RAIL -RAIL_TEST("rail507.txt", 174, 218, FEWTENTHS); -RAIL_TEST("rail516.txt", 182, 204, FEWTENTHS); -RAIL_TEST("rail582.txt", 211, 250, FEWTENTHS); -RAIL_TEST("rail2536.txt", 691, 889, MANYSECONDS); -RAIL_TEST("rail2586.txt", 952, 1139, MANYSECONDS); -RAIL_TEST("rail4284.txt", 1065, 1362, MANYSECONDS); -RAIL_TEST("rail4872.txt", 1527, 1861, MANYSECONDS); // [2] -#endif - -#undef BASIC_SCP -#undef EXTRA_SCP -#undef RAIL - -#undef ORLIB_TEST -#undef ORLIB_UNICOST_TEST -#undef APPEND -#undef APPEND_AND_EVAL -#undef SCP_TEST -#undef RAIL_TEST - -TEST(SetCoverHugeTest, GenerateProblem) { - SetCoverModel seed_model = - ReadRailSetCoverProblem(file::JoinPathRespectAbsolute( - ::testing::SrcDir(), data_dir, "rail4284.txt")); - seed_model.CreateSparseRowView(); - const BaseInt num_wanted_subsets(100'000'000); - const BaseInt num_wanted_elements(40'000); - const double row_scale = 1.1; - const double column_scale = 1.1; - const double cost_scale = 10.0; - SetCoverModel model = SetCoverModel::GenerateRandomModelFrom( - seed_model, num_wanted_elements, num_wanted_subsets, row_scale, - column_scale, cost_scale); - SetCoverInvariant inv = - RunElementDegreeGreedyAndSteepest("rail4284_huge.txt", &model); - LOG(INFO) << "Cost: " << inv.cost(); -} - -} // namespace operations_research diff --git a/ortools/graph/CMakeLists.txt b/ortools/graph/CMakeLists.txt index f76659bd0c..fd86eacac3 100644 --- a/ortools/graph/CMakeLists.txt +++ b/ortools/graph/CMakeLists.txt @@ -39,3 +39,21 @@ target_link_libraries(${NAME} PRIVATE ${PROJECT_NAMESPACE}::ortools_proto $<$:Coin::Cbc>) #add_library(${PROJECT_NAMESPACE}::graph ALIAS ${NAME}) + +if(BUILD_TESTING) + file(GLOB _TEST_SRCS "*_test.cc") + foreach(_FULL_FILE_NAME IN LISTS _TEST_SRCS) + get_filename_component(_NAME ${_FULL_FILE_NAME} NAME_WE) + get_filename_component(_FILE_NAME ${_FULL_FILE_NAME} NAME) + ortools_cxx_test( + NAME + graph_${_NAME} + SOURCES + ${_FILE_NAME} + LINK_LIBRARIES + benchmark::benchmark + GTest::gmock + GTest::gtest_main + ) + endforeach() +endif()