Add int128.proto

This commit is contained in:
Corentin Le Molgat
2023-12-08 14:28:53 +01:00
parent 5559677b6c
commit 2649b4284a
14 changed files with 117 additions and 73 deletions

View File

@@ -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(

View File

@@ -60,8 +60,8 @@ bool RandomSolutionGenerator::NextSolution(
std::vector<SubsetIndex> 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<SubsetIndex>& 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<SubsetIndex>& 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<SubsetIndex>& 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<SubsetIndex> impacted_subsets =
ledger_->Toggle(best_subset, false);
UpdatePriorities(impacted_subsets);
@@ -191,13 +191,13 @@ void GuidedTabuSearch::UpdatePenalties(const std::vector<SubsetIndex>& 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<SubsetIndex>& focus,
const SubsetCostVector& subset_costs = ledger_->model()->subset_costs();
constexpr Cost kMaxPossibleCost = std::numeric_limits<Cost>::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<SubsetIndex>& 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<SubsetIndex>& 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<SubsetIndex> impacted_subsets =
ledger_->UnsafeToggle(best_subset, !ledger_->is_selected(best_subset));
const std::vector<SubsetIndex> 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<SubsetIndex>& 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<SubsetIndex> ClearRandomSubsets(
CHECK_GE(num_subsets, 0);
std::vector<SubsetIndex> 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<SubsetIndex> 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);
}
}

View File

@@ -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;
}

View File

@@ -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;

View File

@@ -36,7 +36,7 @@ bool SetCoverMip::NextSolution(const std::vector<SubsetIndex>& 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<SubsetIndex>& 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<double>::infinity();
constraints[element] = solver.MakeRowConstraint(1.0, kInfinity);

View File

@@ -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_; }

View File

@@ -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<Cost>::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<Cost>::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();
}

View File

@@ -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 "

View File

@@ -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,

View File

@@ -14,6 +14,9 @@
// Minimal example to call the GLOP solver.
// [START program]
// [START import]
#include <memory>
#include <ostream>
#include "ortools/linear_solver/linear_solver.h"
// [END import]

View File

@@ -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",

View File

@@ -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(

27
ortools/util/int128.proto Normal file
View File

@@ -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;
}

View File

@@ -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