diff --git a/ortools/algorithms/BUILD.bazel b/ortools/algorithms/BUILD.bazel index 59fde0f2e6..ebb140a50b 100644 --- a/ortools/algorithms/BUILD.bazel +++ b/ortools/algorithms/BUILD.bazel @@ -102,9 +102,7 @@ cc_library( proto_library( name = "set_cover_proto", srcs = ["set_cover.proto"], - #deps = [ - # "//storage/util/int128:int128_proto", - #], + deps = ["//ortools/util:int128_proto"], ) cc_proto_library( diff --git a/ortools/algorithms/set_cover.cc b/ortools/algorithms/set_cover.cc index a298cb9709..3581d7aef2 100644 --- a/ortools/algorithms/set_cover.cc +++ b/ortools/algorithms/set_cover.cc @@ -60,8 +60,8 @@ bool RandomSolutionGenerator::NextSolution( std::vector shuffled = focus; std::shuffle(shuffled.begin(), shuffled.end(), absl::BitGen()); for (const SubsetIndex subset : shuffled) { - if (ledger_->is_selected(subset)) continue; - if (ledger_->marginal_impacts(subset) != 0) { + if (ledger_->is_selected()[subset]) continue; + if (ledger_->marginal_impacts()[subset] != 0) { ledger_->Toggle(subset, true); } } @@ -75,7 +75,7 @@ void GreedySolutionGenerator::UpdatePriorities( const std::vector& impacted_subsets) { const SubsetCostVector& subset_costs = ledger_->model()->subset_costs(); for (const SubsetIndex subset : impacted_subsets) { - const ElementIndex marginal_impact(ledger_->marginal_impacts(subset)); + const ElementIndex marginal_impact(ledger_->marginal_impacts()[subset]); if (marginal_impact != 0) { const Cost marginal_cost_increase = subset_costs[subset] / marginal_impact.value(); @@ -98,10 +98,10 @@ bool GreedySolutionGenerator::NextSolution( // The priority is the minimum marginal cost increase. Since the // priority queue returns the smallest value, we use the opposite. for (const SubsetIndex subset : focus) { - if (!ledger_->is_selected(subset) && - ledger_->marginal_impacts(subset) != 0) { + if (!ledger_->is_selected()[subset] && + ledger_->marginal_impacts()[subset] != 0) { const Cost marginal_cost_increase = - subset_costs[subset] / ledger_->marginal_impacts(subset).value(); + subset_costs[subset] / ledger_->marginal_impacts()[subset].value(); pq_.Add(subset, -marginal_cost_increase); } } @@ -149,7 +149,7 @@ bool SteepestSearch::NextSolution(const std::vector& focus, // Do it only for removable subsets. for (const SubsetIndex subset : focus) { // The priority is the gain from removing the subset from the solution. - if (ledger_->is_selected(subset) && ledger_->is_removable(subset)) { + if (ledger_->is_selected()[subset] && ledger_->is_removable()[subset]) { pq_.Add(subset, subset_costs[subset]); } } @@ -158,8 +158,8 @@ bool SteepestSearch::NextSolution(const std::vector& focus, const SubsetIndex best_subset = pq_.TopSubset(); const Cost cost_decrease = subset_costs[best_subset]; DCHECK_GT(cost_decrease, 0.0); - DCHECK(ledger_->is_removable(best_subset)); - DCHECK(ledger_->is_selected(best_subset)); + DCHECK(ledger_->is_removable()[best_subset]); + DCHECK(ledger_->is_selected()[best_subset]); const std::vector impacted_subsets = ledger_->Toggle(best_subset, false); UpdatePriorities(impacted_subsets); @@ -191,13 +191,13 @@ void GuidedTabuSearch::UpdatePenalties(const std::vector& focus) { const SubsetCostVector& subset_costs = ledger_->model()->subset_costs(); Cost max_utility = -1.0; for (const SubsetIndex subset : focus) { - if (ledger_->is_selected(subset)) { + if (ledger_->is_selected()[subset]) { max_utility = std::max(max_utility, utilities_[subset]); } } const double epsilon_utility = epsilon_ * max_utility; for (const SubsetIndex subset : focus) { - if (ledger_->is_selected(subset)) { + if (ledger_->is_selected()[subset]) { const double utility = utilities_[subset]; if ((max_utility - utility <= epsilon_utility) && FlipCoin()) { ++times_penalized_[subset]; @@ -221,7 +221,7 @@ bool GuidedTabuSearch::NextSolution(const std::vector& focus, const SubsetCostVector& subset_costs = ledger_->model()->subset_costs(); constexpr Cost kMaxPossibleCost = std::numeric_limits::max(); Cost best_cost = ledger_->cost(); - SubsetBoolVector best_choices = ledger_->GetSolution(); + SubsetBoolVector best_choices = ledger_->is_selected(); Cost augmented_cost = std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0); for (int iteration = 0; iteration < num_iterations; ++iteration) { @@ -230,15 +230,15 @@ bool GuidedTabuSearch::NextSolution(const std::vector& focus, for (const SubsetIndex subset : focus) { const Cost delta = augmented_costs_[subset]; DVLOG(1) << "Subset, " << subset.value() << ", at ," - << ledger_->is_selected(subset) << ", is removable =, " - << ledger_->is_removable(subset) << ", delta =, " << delta + << ledger_->is_selected()[subset] << ", is removable =, " + << ledger_->is_removable()[subset] << ", delta =, " << delta << ", best_delta =, " << best_delta; - if (ledger_->is_selected(subset)) { + if (ledger_->is_selected()[subset]) { // Try to remove subset from solution, if the gain from removing is // worth it: if (-delta < best_delta && // and it can be removed, and - ledger_->is_removable(subset) && + ledger_->is_removable()[subset] && // it is not Tabu OR decreases the actual cost (aspiration): (!tabu_list_.Contains(subset) || ledger_->cost() - subset_costs[subset] < best_cost)) { @@ -262,14 +262,14 @@ bool GuidedTabuSearch::NextSolution(const std::vector& focus, return true; } DVLOG(1) << "Best subset, " << best_subset.value() << ", at ," - << ledger_->is_selected(best_subset) << ", is removable = ," - << ledger_->is_removable(best_subset) << ", best_delta = ," + << ledger_->is_selected()[best_subset] << ", is removable = ," + << ledger_->is_removable()[best_subset] << ", best_delta = ," << best_delta; UpdatePenalties(focus); tabu_list_.Add(best_subset); - const std::vector impacted_subsets = - ledger_->UnsafeToggle(best_subset, !ledger_->is_selected(best_subset)); + const std::vector impacted_subsets = ledger_->UnsafeToggle( + best_subset, !ledger_->is_selected()[best_subset]); // TODO(user): make the cost computation incremental. augmented_cost = std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0); @@ -283,7 +283,7 @@ bool GuidedTabuSearch::NextSolution(const std::vector& focus, << ledger_->cost() << ", best cost = ," << best_cost << ", penalized cost = ," << augmented_cost; best_cost = ledger_->cost(); - best_choices = ledger_->GetSolution(); + best_choices = ledger_->is_selected(); } } ledger_->LoadSolution(best_choices); @@ -314,7 +314,7 @@ std::vector ClearRandomSubsets( CHECK_GE(num_subsets, 0); std::vector chosen_indices; for (const SubsetIndex subset : focus) { - if (ledger->is_selected(subset)) { + if (ledger->is_selected()[subset]) { chosen_indices.push_back(subset); } } @@ -355,7 +355,7 @@ std::vector ClearMostCoveredElements( for (ElementIndex element : permutation) { if (coverage[element] <= 1) break; for (SubsetIndex subset : ledger->model()->rows()[element]) { - if (ledger->is_selected(subset)) { + if (ledger->is_selected()[subset]) { used_subsets_collection.insert(subset); } } diff --git a/ortools/algorithms/set_cover.proto b/ortools/algorithms/set_cover.proto index b04366098a..e39a25936c 100644 --- a/ortools/algorithms/set_cover.proto +++ b/ortools/algorithms/set_cover.proto @@ -15,6 +15,8 @@ syntax = "proto3"; package operations_research; +import "ortools/util/int128.proto"; + option java_package = "com.google.ortools.algorithms"; option java_multiple_files = true; @@ -32,6 +34,9 @@ message SetCoverProto { // A user-defined name for the model. optional string name = 2; + + // An automatically fingerprint for the model. TODO(user): Implement. + optional Int128 fingerprint = 3; } message SetCoverSolutionResponse { @@ -64,4 +69,10 @@ message SetCoverSolutionResponse { // A lower bound of the solution, as computed by the algorithm. optional double cost_lower_bound = 5; + + // An automatically fingerprint for the solution. TODO(user): Implement. + optional Int128 fingerprint = 6; + + // A reference to the model the solution applies to. TODO(user): Implement. + optional Int128 model_fingerprint = 7; } diff --git a/ortools/algorithms/set_cover_ledger.h b/ortools/algorithms/set_cover_ledger.h index b73b120ed9..04a360e526 100644 --- a/ortools/algorithms/set_cover_ledger.h +++ b/ortools/algorithms/set_cover_ledger.h @@ -64,24 +64,21 @@ class SetCoverLedger { // Returns the cost of current solution. Cost cost() const { return cost_; } - // Returns whether subset is selected in the solution. - bool is_selected(SubsetIndex subset) const { return is_selected_[subset]; } + // Returns the subset assignment vector. + const SubsetBoolVector& is_selected() const { return is_selected_; } - // Returns the number of elements in each subset that are not covered in the - // current solution. - ElementIndex marginal_impacts(SubsetIndex subset) const { - return marginal_impacts_[subset]; + // Returns vector containing the number of elements in each subset that are + // not covered in the current solution. + const SubsetToElementVector& marginal_impacts() const { + return marginal_impacts_; } - // Returns the number of subsets covering each element. - SubsetIndex coverage(ElementIndex element) const { - return coverage_[element]; - } + // Returns vector containing number of subsets covering each element. + const ElementToSubsetVector& coverage() const { return coverage_; } - ElementToSubsetVector coverage() const { return coverage_; } - - // Returns whether subset can be removed from the solution. - bool is_removable(SubsetIndex subset) const { return is_removable_[subset]; } + // Returns vector of Booleans telling whether each subset can be removed from + // the solution. + const SubsetBoolVector& is_removable() const { return is_removable_; } // Returns the number of elements covered. ElementIndex num_elements_covered() const { return num_elements_covered_; } @@ -89,9 +86,6 @@ class SetCoverLedger { // Stores the solution and recomputes the data in the ledger. void LoadSolution(const SubsetBoolVector& c); - // Returns the current solution. - SubsetBoolVector GetSolution() const { return is_selected_; } - // Returns true if the data stored in the ledger is consistent. bool CheckConsistency() const; diff --git a/ortools/algorithms/set_cover_mip.cc b/ortools/algorithms/set_cover_mip.cc index 5ff2daf298..b3c045e162 100644 --- a/ortools/algorithms/set_cover_mip.cc +++ b/ortools/algorithms/set_cover_mip.cc @@ -36,7 +36,7 @@ bool SetCoverMip::NextSolution(const std::vector& focus) { SetCoverModel* model = ledger_->model(); const SubsetIndex num_subsets(model->num_subsets()); const ElementIndex num_elements(model->num_elements()); - SubsetBoolVector choices = ledger_->GetSolution(); + SubsetBoolVector choices = ledger_->is_selected(); MPSolver::OptimizationProblemType problem_type; switch (mip_solver_) { case SetCoverMipSolver::SCIP: @@ -68,7 +68,7 @@ bool SetCoverMip::NextSolution(const std::vector& focus) { vars[subset] = solver.MakeBoolVar(""); objective->SetCoefficient(vars[subset], model->subset_costs()[subset]); for (ElementIndex element : model->columns()[subset]) { - if (ledger_->coverage(element) > 0) continue; + if (ledger_->coverage()[element] > 0) continue; if (constraints[element] == nullptr) { constexpr double kInfinity = std::numeric_limits::infinity(); constraints[element] = solver.MakeRowConstraint(1.0, kInfinity); diff --git a/ortools/algorithms/set_cover_model.h b/ortools/algorithms/set_cover_model.h index a5c9186fcf..bc4bf82034 100644 --- a/ortools/algorithms/set_cover_model.h +++ b/ortools/algorithms/set_cover_model.h @@ -19,6 +19,7 @@ #include "absl/log/check.h" #include "ortools/algorithms/set_cover.pb.h" #include "ortools/lp_data/lp_types.h" // For StrictITIVector. +#include "ortools/util/strong_integers.h" // Representation class for the weighted set-covering problem. // @@ -88,24 +89,18 @@ class SetCoverModel { // number of columns. SubsetIndex num_subsets() const { return columns_.size(); } + // Vector of costs for each subset. const SubsetCostVector& subset_costs() const { return subset_costs_; } + // Column view of the set covering problem. const SparseColumnView& columns() const { return columns_; } - const SparseColumn& columns(SubsetIndex subset) const { - return columns_[subset]; - } - + // Row view of the set covering problem. const SparseRowView& rows() const { DCHECK(row_view_is_valid_); return rows_; } - const SparseRow& rows(ElementIndex element) const { - DCHECK(row_view_is_valid_); - return rows_[element]; - } - // Returns true if rows_ and columns_ represent the same problem. bool row_view_is_valid() const { return row_view_is_valid_; } diff --git a/ortools/algorithms/set_cover_test.cc b/ortools/algorithms/set_cover_test.cc index d1a4b5d51b..25cf5037ef 100644 --- a/ortools/algorithms/set_cover_test.cc +++ b/ortools/algorithms/set_cover_test.cc @@ -138,9 +138,9 @@ TEST(SetCoverTest, Infeasible) { } #ifdef NDEBUG -static constexpr int SIZE = 512; +static constexpr int SIZE = 128; #else -static constexpr int SIZE = 32; +static constexpr int SIZE = 16; #endif TEST(SetCoverTest, KnightsCoverCreation) { @@ -238,7 +238,7 @@ TEST(SetCoverTest, KnightsCoverGreedyAndTabu) { CHECK(gts.NextSolution(10000)); LOG(INFO) << "GuidedTabuSearch cost: " << ledger.cost(); EXPECT_TRUE(ledger.CheckSolution()); - DisplayKnightsCoverSolution(ledger.GetSolution(), BoardSize, BoardSize); + DisplayKnightsCoverSolution(ledger.is_selected(), BoardSize, BoardSize); } TEST(SetCoverTest, KnightsCoverRandomClear) { @@ -250,7 +250,7 @@ TEST(SetCoverTest, KnightsCoverRandomClear) { SetCoverModel model = CreateKnightsCoverModel(BoardSize, BoardSize); SetCoverLedger ledger(&model); Cost best_cost = std::numeric_limits::max(); - SubsetBoolVector best_choices = ledger.GetSolution(); + SubsetBoolVector best_choices = ledger.is_selected(); for (int i = 0; i < 10000; ++i) { ledger.LoadSolution(best_choices); ClearRandomSubsets(0.1 * model.num_subsets().value(), &ledger); @@ -264,7 +264,7 @@ TEST(SetCoverTest, KnightsCoverRandomClear) { EXPECT_TRUE(ledger.CheckSolution()); if (ledger.cost() < best_cost) { best_cost = ledger.cost(); - best_choices = ledger.GetSolution(); + best_choices = ledger.is_selected(); LOG(INFO) << "Best cost: " << best_cost << " at iteration = " << i; } } @@ -287,7 +287,7 @@ TEST(SetCoverTest, KnightsCoverRandomClearMip) { SetCoverModel model = CreateKnightsCoverModel(BoardSize, BoardSize); SetCoverLedger ledger(&model); Cost best_cost = std::numeric_limits::max(); - SubsetBoolVector best_choices = ledger.GetSolution(); + SubsetBoolVector best_choices = ledger.is_selected(); GreedySolutionGenerator greedy(&ledger); CHECK(greedy.NextSolution()); LOG(INFO) << "GreedySolutionGenerator cost: " << ledger.cost(); @@ -297,7 +297,7 @@ TEST(SetCoverTest, KnightsCoverRandomClearMip) { LOG(INFO) << "SteepestSearch cost: " << ledger.cost(); best_cost = ledger.cost(); - best_choices = ledger.GetSolution(); + best_choices = ledger.is_selected(); for (int i = 0; i < 100; ++i) { ledger.LoadSolution(best_choices); auto focus = ClearRandomSubsets(0.1 * model.num_subsets().value(), &ledger); @@ -307,7 +307,7 @@ TEST(SetCoverTest, KnightsCoverRandomClearMip) { EXPECT_TRUE(ledger.CheckSolution()); if (ledger.cost() < best_cost) { best_cost = ledger.cost(); - best_choices = ledger.GetSolution(); + best_choices = ledger.is_selected(); LOG(INFO) << "Best cost: " << best_cost << " at iteration = " << i; } } @@ -332,7 +332,7 @@ TEST(SetCoverTest, KnightsCoverMip) { SetCoverMip mip(&ledger); mip.SetTimeLimitInSeconds(10); mip.NextSolution(); - SubsetBoolVector best_choices = ledger.GetSolution(); + SubsetBoolVector best_choices = ledger.is_selected(); DisplayKnightsCoverSolution(best_choices, BoardSize, BoardSize); LOG(INFO) << "Mip cost: " << ledger.cost(); } diff --git a/ortools/algorithms/set_cover_utils.cc b/ortools/algorithms/set_cover_utils.cc index 1f72bb4465..4437896d5e 100644 --- a/ortools/algorithms/set_cover_utils.cc +++ b/ortools/algorithms/set_cover_utils.cc @@ -29,7 +29,7 @@ void SubsetPriorityQueue::Add(SubsetIndex subset, Cost priority) { void SubsetPriorityQueue::ChangePriority(SubsetIndex subset, Cost priority) { // TODO(user): see if the reference to ledger_ can be removed. - if (ledger_->marginal_impacts(subset) != 0) { + if (ledger_->marginal_impacts()[subset] != 0) { pq_elements_[subset].SetPriority(priority); max_pq_.NoteChangedPriority(&pq_elements_[subset]); DVLOG(1) << "Priority of subset " << subset << " is now " diff --git a/ortools/graph/README.md b/ortools/graph/README.md index 511ec82aa6..da5c0fb4be 100644 --- a/ortools/graph/README.md +++ b/ortools/graph/README.md @@ -4,12 +4,14 @@ This directory contains data structures and algorithms for graph and network flow problems. It contains in particular: + * well-tuned algorithms (for example shortest, paths and [Hamiltonian paths](https://en.wikipedia.org/wiki/Hamiltonian_path)). * hard-to-find algorithms (Hamiltonian paths, push-relabel flow algorithms). * other, more common algorithm, that are useful to use with `EbertGraph`. Graph representations: + * [ebert_graph.h](./ebert_graph.h): entry point for a directed graph class. Deprecated. Prefer using [`//util/graph/graph.h`](../../graph/graph.h). @@ -28,6 +30,7 @@ Paths: `digraph.h`.) Graph decompositions: + * [connected_components.h](./connected_components.h): entry point for computing connected components in an undirected graph. (It does not need `ebert_graph.h` or `digraph.h`.) @@ -40,6 +43,7 @@ Graph decompositions: (It does not need `ebert_graph.h` or `digraph.h`.) Flow algorithms: + * [linear_assignment.h](./linear_assignment.h): entry point for solving linear sum assignment problems (classical assignment problems where the total cost is the sum of the costs of each arc used) on directed graphs with arc costs, diff --git a/ortools/linear_solver/samples/basic_example.cc b/ortools/linear_solver/samples/basic_example.cc index 8eabe89c88..7e7fcc4e33 100644 --- a/ortools/linear_solver/samples/basic_example.cc +++ b/ortools/linear_solver/samples/basic_example.cc @@ -14,6 +14,9 @@ // Minimal example to call the GLOP solver. // [START program] // [START import] +#include +#include + #include "ortools/linear_solver/linear_solver.h" // [END import] diff --git a/ortools/linear_solver/wrappers/BUILD.bazel b/ortools/linear_solver/wrappers/BUILD.bazel index dda005ac74..48fa1d8435 100644 --- a/ortools/linear_solver/wrappers/BUILD.bazel +++ b/ortools/linear_solver/wrappers/BUILD.bazel @@ -37,9 +37,9 @@ cc_library( visibility = ["//visibility:public"], deps = [ "//ortools/base:file", - "//ortools/linear_solver:solve_mp_model", "//ortools/linear_solver:linear_solver_cc_proto", "//ortools/linear_solver:model_exporter", + "//ortools/linear_solver:solve_mp_model", "//ortools/linear_solver/proto_solver:glop_proto_solver", "//ortools/linear_solver/proto_solver:gurobi_proto_solver", "//ortools/linear_solver/proto_solver:pdlp_proto_solver", diff --git a/ortools/util/BUILD.bazel b/ortools/util/BUILD.bazel index 1779bfde78..0d05385118 100644 --- a/ortools/util/BUILD.bazel +++ b/ortools/util/BUILD.bazel @@ -315,6 +315,23 @@ cc_library( ], ) +# Int128 +proto_library( + name = "int128_proto", + srcs = ["int128.proto"], +) + +cc_proto_library( + name = "int128_cc_proto", + deps = [":int128_proto"], +) + +py_proto_library( + name = "int128_py_pb2", + deps = [":int128_proto"], +) + +# OptionalBoolean proto_library( name = "optional_boolean_proto", srcs = ["optional_boolean.proto"], @@ -327,10 +344,10 @@ cc_proto_library( py_proto_library( name = "optional_boolean_py_pb2", - visibility = ["//visibility:public"], deps = [":optional_boolean_proto"], ) +# SWIG cc_library( name = "functions_swig_helpers", hdrs = [ @@ -386,7 +403,6 @@ cc_library( cc_library( name = "vector_or_function", hdrs = ["vector_or_function.h"], - visibility = ["//visibility:public"], deps = [ "//ortools/base", ], @@ -407,7 +423,6 @@ cc_library( # name = "bp_parser", # srcs = ["bp_parser.cc"], # hdrs = ["bp_parser.h"], -# visibility = ["//visibility:public"], # deps = [ # "@com_google_absl//absl/strings", # ":filelineiter", @@ -429,7 +444,6 @@ cc_library( cc_library( name = "sort", hdrs = ["sort.h"], - visibility = ["//visibility:public"], deps = [ "//ortools/base", ], @@ -439,7 +453,6 @@ cc_library( name = "logging", srcs = ["logging.cc"], hdrs = ["logging.h"], - visibility = ["//visibility:public"], deps = [ "//ortools/base", "//ortools/base:timer", @@ -450,7 +463,6 @@ cc_library( cc_library( name = "testing_utils", hdrs = ["testing_utils.h"], - visibility = ["//visibility:public"], ) cc_library( diff --git a/ortools/util/int128.proto b/ortools/util/int128.proto new file mode 100644 index 0000000000..b0a4136a8c --- /dev/null +++ b/ortools/util/int128.proto @@ -0,0 +1,27 @@ +// Copyright 2010-2022 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. + +syntax = "proto3"; + +package operations_research; + +option java_package = "com.google.ortools.util"; +option java_multiple_files = true; +option csharp_namespace = "Google.OrTools.Util"; + +// The low 64 bits are stored in "low", and the high 64-bits (including the +// sign) are stored in "high". +message Int128 { + int64 high = 1; + uint64 low = 2; +} diff --git a/ortools/util/optional_boolean.proto b/ortools/util/optional_boolean.proto index 59f79fea33..2f86f4119d 100644 --- a/ortools/util/optional_boolean.proto +++ b/ortools/util/optional_boolean.proto @@ -13,12 +13,12 @@ syntax = "proto3"; +package operations_research; + option java_package = "com.google.ortools.util"; option java_multiple_files = true; option csharp_namespace = "Google.OrTools.Util"; -package operations_research; - // A "three-way" boolean: unspecified, false or true. // // We don't use the value of 1 to increase the chance to catch bugs: eg. in