From 927f5c90c9cfe3d3aa02f57be2c58ff22d08c220 Mon Sep 17 00:00:00 2001 From: Laurent Perron Date: Mon, 2 Dec 2024 17:17:58 +0100 Subject: [PATCH] more spans --- ortools/algorithms/python/set_cover.cc | 1 + ortools/algorithms/python/set_cover_test.py | 1 + ortools/algorithms/set_cover_heuristics.cc | 2 +- ortools/algorithms/set_cover_heuristics.h | 2 +- ortools/algorithms/set_cover_model.cc | 23 +++++++++++++++++---- ortools/algorithms/set_cover_model.h | 21 ++++++++++++++----- ortools/algorithms/set_cover_test.cc | 1 + 7 files changed, 40 insertions(+), 11 deletions(-) diff --git a/ortools/algorithms/python/set_cover.cc b/ortools/algorithms/python/set_cover.cc index fa12c278cc..5474dc6d8c 100644 --- a/ortools/algorithms/python/set_cover.cc +++ b/ortools/algorithms/python/set_cover.cc @@ -208,6 +208,7 @@ PYBIND11_MODULE(set_cover, m) { }, arg("subset"), arg("cost")) .def("create_sparse_row_view", &SetCoverModel::CreateSparseRowView) + .def("sort_elements_in_subsets", &SetCoverModel::SortElementsInSubsets) .def("compute_feasibility", &SetCoverModel::ComputeFeasibility) .def( "reserve_num_subsets", diff --git a/ortools/algorithms/python/set_cover_test.py b/ortools/algorithms/python/set_cover_test.py index de0cc107cc..39b6e92715 100644 --- a/ortools/algorithms/python/set_cover_test.py +++ b/ortools/algorithms/python/set_cover_test.py @@ -56,6 +56,7 @@ class SetCoverTest(absltest.TestCase): def test_save_reload(self): model = create_knights_cover_model(10, 10) + model.sort_elements_in_subsets() proto = model.export_model_as_proto() reloaded = set_cover.SetCoverModel() reloaded.import_model_from_proto(proto) diff --git a/ortools/algorithms/set_cover_heuristics.cc b/ortools/algorithms/set_cover_heuristics.cc index f6b911e556..5f5b666943 100644 --- a/ortools/algorithms/set_cover_heuristics.cc +++ b/ortools/algorithms/set_cover_heuristics.cc @@ -95,7 +95,7 @@ bool GreedySolutionGenerator::NextSolution() { } bool GreedySolutionGenerator::NextSolution( - const std::vector& focus) { + absl::Span focus) { return NextSolution(focus, inv_->model()->subset_costs()); } diff --git a/ortools/algorithms/set_cover_heuristics.h b/ortools/algorithms/set_cover_heuristics.h index 314650e3f4..06fa732a06 100644 --- a/ortools/algorithms/set_cover_heuristics.h +++ b/ortools/algorithms/set_cover_heuristics.h @@ -122,7 +122,7 @@ class GreedySolutionGenerator { // Computes the next partial solution considering only the subsets whose // indices are in focus. - bool NextSolution(const std::vector& focus); + bool NextSolution(absl::Span focus); // Same with a different set of costs. bool NextSolution(absl::Span focus, diff --git a/ortools/algorithms/set_cover_model.cc b/ortools/algorithms/set_cover_model.cc index 3ff87e6d36..9c4c47d0a4 100644 --- a/ortools/algorithms/set_cover_model.cc +++ b/ortools/algorithms/set_cover_model.cc @@ -183,6 +183,7 @@ void SetCoverModel::UpdateAllSubsetsList() { } void SetCoverModel::AddEmptySubset(Cost cost) { + elements_in_subsets_are_sorted_ = false; subset_costs_.push_back(cost); columns_.push_back(SparseColumn()); all_subsets_.push_back(SubsetIndex(num_subsets_)); @@ -194,6 +195,7 @@ void SetCoverModel::AddEmptySubset(Cost cost) { } void SetCoverModel::AddElementToLastSubset(BaseInt element) { + elements_in_subsets_are_sorted_ = false; columns_.back().push_back(ElementIndex(element)); num_elements_ = std::max(num_elements_, element + 1); // No need to update the list all_subsets_. @@ -206,6 +208,7 @@ void SetCoverModel::AddElementToLastSubset(ElementIndex element) { } void SetCoverModel::SetSubsetCost(BaseInt subset, Cost cost) { + elements_in_subsets_are_sorted_ = false; CHECK(std::isfinite(cost)); DCHECK_GE(subset, 0); if (subset >= num_subsets()) { @@ -223,6 +226,7 @@ void SetCoverModel::SetSubsetCost(SubsetIndex subset, Cost cost) { } void SetCoverModel::AddElementToSubset(BaseInt element, BaseInt subset) { + elements_in_subsets_are_sorted_ = false; if (subset >= num_subsets()) { num_subsets_ = subset + 1; subset_costs_.resize(num_subsets_, 0.0); @@ -264,6 +268,13 @@ void SetCoverModel::ReserveNumElementsInSubset(ElementIndex num_elements, ReserveNumElementsInSubset(num_elements.value(), subset.value()); } +void SetCoverModel::SortElementsInSubsets() { + for (const SubsetIndex subset : SubsetRange()) { + std::sort(columns_[subset].begin(), columns_[subset].end()); + } + elements_in_subsets_are_sorted_ = true; +} + void SetCoverModel::CreateSparseRowView() { if (row_view_is_valid_) { return; @@ -287,6 +298,7 @@ void SetCoverModel::CreateSparseRowView() { } } row_view_is_valid_ = true; + elements_in_subsets_are_sorted_ = true; } bool SetCoverModel::ComputeFeasibility() const { @@ -319,13 +331,15 @@ bool SetCoverModel::ComputeFeasibility() const { return true; } -SetCoverProto SetCoverModel::ExportModelAsProto() { +SetCoverProto SetCoverModel::ExportModelAsProto() const { + CHECK(elements_in_subsets_are_sorted_); SetCoverProto message; for (const SubsetIndex subset : SubsetRange()) { SetCoverProto::Subset* subset_proto = message.add_subset(); subset_proto->set_cost(subset_costs_[subset]); - std::sort(columns_[subset].begin(), columns_[subset].end()); - for (const ElementIndex element : columns_[subset]) { + SparseColumn column = columns_[subset]; + std::sort(column.begin(), column.end()); + for (const ElementIndex element : column) { subset_proto->add_element(element.value()); } } @@ -433,8 +447,9 @@ std::vector ComputeDeciles(std::vector values) { const int kNumDeciles = 10; std::vector deciles; deciles.reserve(kNumDeciles); + const float step = values.size() / kNumDeciles; for (int i = 1; i <= kNumDeciles; ++i) { - const size_t point = values.size() * i / kNumDeciles - 1; + const size_t point = std::max(0, i * step - 1); std::nth_element(values.begin(), values.begin() + point, values.end()); deciles.push_back(values[point]); } diff --git a/ortools/algorithms/set_cover_model.h b/ortools/algorithms/set_cover_model.h index cf7b18cc81..56e1950b4d 100644 --- a/ortools/algorithms/set_cover_model.h +++ b/ortools/algorithms/set_cover_model.h @@ -109,6 +109,7 @@ class SetCoverModel { num_subsets_(0), num_nonzeros_(0), row_view_is_valid_(false), + elements_in_subsets_are_sorted_(false), subset_costs_(), columns_(), rows_(), @@ -204,7 +205,12 @@ class SetCoverModel { void AddElementToSubset(BaseInt element, BaseInt subset); void AddElementToSubset(ElementIndex element, SubsetIndex subset); - // Creates the sparse ("dual") representation of the problem. + // Sorts the elements in each subset. Should be called before exporting the + // model to a proto. + void SortElementsInSubsets(); + + // Creates the sparse ("dual") representation of the problem. This also sorts + // the elements in each subset. void CreateSparseRowView(); // Returns true if the problem is feasible, i.e. if the subsets cover all @@ -220,10 +226,12 @@ class SetCoverModel { void ReserveNumElementsInSubset(ElementIndex num_elements, SubsetIndex subset); - // Returns the model as a SetCoverProto. The function is not const because - // the element indices in the columns need to be sorted for the representation - // as a protobuf to be canonical. - SetCoverProto ExportModelAsProto(); + // Returns the model as a SetCoverProto. Note that the elements of each subset + // are sorted locally before being exported to the proto. This is done to + // ensure that the proto is deterministic. The function is const because it + // does not modify the model. Therefore, the model as exported by this + // function may be different from the initial model. + SetCoverProto ExportModelAsProto() const; // Imports the model from a SetCoverProto. void ImportModelFromProto(const SetCoverProto& message); @@ -284,6 +292,9 @@ class SetCoverModel { // True when the SparseRowView is up-to-date. bool row_view_is_valid_; + // True when the elements in each subset are sorted. + bool elements_in_subsets_are_sorted_; + // Costs for each subset. SubsetCostVector subset_costs_; diff --git a/ortools/algorithms/set_cover_test.cc b/ortools/algorithms/set_cover_test.cc index a43a18771f..563d39350a 100644 --- a/ortools/algorithms/set_cover_test.cc +++ b/ortools/algorithms/set_cover_test.cc @@ -123,6 +123,7 @@ class KnightsCover { TEST(SetCoverProtoTest, SaveReload) { SetCoverModel model = KnightsCover(10, 10).model(); + model.SortElementsInSubsets(); SetCoverProto proto = model.ExportModelAsProto(); SetCoverModel reloaded; reloaded.ImportModelFromProto(proto);