bump set cover code
This commit is contained in:
@@ -39,11 +39,21 @@ py_proto_library(
|
||||
deps = [":set_cover_proto"],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "base_types",
|
||||
hdrs = ["base_types.h"],
|
||||
deps = [
|
||||
"//ortools/base:intops",
|
||||
"//ortools/base:strong_vector",
|
||||
],
|
||||
)
|
||||
|
||||
cc_library(
|
||||
name = "set_cover_lagrangian",
|
||||
srcs = ["set_cover_lagrangian.cc"],
|
||||
hdrs = ["set_cover_lagrangian.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_invariant",
|
||||
":set_cover_model",
|
||||
"//ortools/algorithms:adjustable_k_ary_heap",
|
||||
@@ -58,6 +68,7 @@ cc_library(
|
||||
srcs = ["set_cover_model.cc"],
|
||||
hdrs = ["set_cover_model.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_cc_proto",
|
||||
"//ortools/algorithms:radix_sort",
|
||||
"//ortools/base:intops",
|
||||
@@ -78,6 +89,7 @@ cc_library(
|
||||
srcs = ["set_cover_invariant.cc"],
|
||||
hdrs = ["set_cover_invariant.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_cc_proto",
|
||||
":set_cover_model",
|
||||
"//ortools/base",
|
||||
@@ -93,6 +105,7 @@ cc_library(
|
||||
srcs = ["set_cover_heuristics.cc"],
|
||||
hdrs = ["set_cover_heuristics.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_invariant",
|
||||
":set_cover_model",
|
||||
"//ortools/algorithms:adjustable_k_ary_heap",
|
||||
@@ -111,6 +124,7 @@ cc_library(
|
||||
srcs = ["set_cover_mip.cc"],
|
||||
hdrs = ["set_cover_mip.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_invariant",
|
||||
":set_cover_model",
|
||||
"//ortools/linear_solver",
|
||||
@@ -126,6 +140,7 @@ cc_library(
|
||||
srcs = ["set_cover_reader.cc"],
|
||||
hdrs = ["set_cover_reader.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_cc_proto",
|
||||
":set_cover_model",
|
||||
"//ortools/base:file",
|
||||
@@ -143,6 +158,7 @@ cc_library(
|
||||
srcs = ["assignment.cc"],
|
||||
hdrs = ["assignment.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":capacity_invariant",
|
||||
":set_cover_invariant",
|
||||
":set_cover_model",
|
||||
@@ -168,6 +184,7 @@ cc_binary(
|
||||
name = "set_cover_solve",
|
||||
srcs = ["set_cover_solve.cc"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_heuristics",
|
||||
":set_cover_invariant",
|
||||
":set_cover_model",
|
||||
@@ -188,6 +205,7 @@ cc_test(
|
||||
timeout = "eternal",
|
||||
srcs = ["set_cover_test.cc"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":set_cover_cc_proto",
|
||||
":set_cover_heuristics",
|
||||
":set_cover_invariant",
|
||||
@@ -224,6 +242,7 @@ cc_library(
|
||||
srcs = ["capacity_model.cc"],
|
||||
hdrs = ["capacity_model.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":capacity_cc_proto",
|
||||
":set_cover_model",
|
||||
"//ortools/base:intops",
|
||||
@@ -238,7 +257,6 @@ cc_test(
|
||||
srcs = ["capacity_model_test.cc"],
|
||||
deps = [
|
||||
":capacity_model",
|
||||
":set_cover_model",
|
||||
"//ortools/base:gmock_main",
|
||||
],
|
||||
)
|
||||
@@ -248,8 +266,10 @@ cc_library(
|
||||
srcs = ["capacity_invariant.cc"],
|
||||
hdrs = ["capacity_invariant.h"],
|
||||
deps = [
|
||||
":base_types",
|
||||
":capacity_model",
|
||||
":set_cover_model",
|
||||
"//ortools/util:saturated_arithmetic",
|
||||
"@com_google_absl//absl/log",
|
||||
"@com_google_absl//absl/log:check",
|
||||
],
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "ortools/base/mathutil.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/capacity_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/capacity_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
70
ortools/set_cover/base_types.h
Normal file
70
ortools/set_cover/base_types.h
Normal file
@@ -0,0 +1,70 @@
|
||||
// Copyright 2010-2025 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.
|
||||
|
||||
#ifndef OR_TOOLS_SET_COVER_BASE_TYPES_H_
|
||||
#define OR_TOOLS_SET_COVER_BASE_TYPES_H_
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "ortools/base/strong_int.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
// Basic non-strict type for cost. The speed penalty for using double is ~2%.
|
||||
using Cost = double;
|
||||
|
||||
// Base non-strict integer type for counting elements and subsets.
|
||||
// Using ints makes it possible to represent problems with more than 2 billion
|
||||
// (2e9) elements and subsets. If need arises one day, BaseInt can be split
|
||||
// into SubsetBaseInt and ElementBaseInt.
|
||||
// Quick testing has shown a slowdown of about 20-25% when using int64_t.
|
||||
using BaseInt = int32_t;
|
||||
|
||||
// We make heavy use of strong typing to avoid obvious mistakes.
|
||||
// Subset index.
|
||||
DEFINE_STRONG_INT_TYPE(SubsetIndex, BaseInt);
|
||||
|
||||
// Element index.
|
||||
DEFINE_STRONG_INT_TYPE(ElementIndex, BaseInt);
|
||||
|
||||
// Position in a vector. The vector may either represent a column, i.e. a
|
||||
// subset with all its elements, or a row, i,e. the list of subsets which
|
||||
// contain a given element.
|
||||
DEFINE_STRONG_INT_TYPE(ColumnEntryIndex, BaseInt);
|
||||
DEFINE_STRONG_INT_TYPE(RowEntryIndex, BaseInt);
|
||||
|
||||
using SubsetRange = util_intops::StrongIntRange<SubsetIndex>;
|
||||
using ElementRange = util_intops::StrongIntRange<ElementIndex>;
|
||||
using ColumnEntryRange = util_intops::StrongIntRange<ColumnEntryIndex>;
|
||||
|
||||
using SubsetCostVector = util_intops::StrongVector<SubsetIndex, Cost>;
|
||||
using ElementCostVector = util_intops::StrongVector<ElementIndex, Cost>;
|
||||
|
||||
using SparseColumn = util_intops::StrongVector<ColumnEntryIndex, ElementIndex>;
|
||||
using SparseRow = util_intops::StrongVector<RowEntryIndex, SubsetIndex>;
|
||||
|
||||
using ElementToIntVector = util_intops::StrongVector<ElementIndex, BaseInt>;
|
||||
using SubsetToIntVector = util_intops::StrongVector<SubsetIndex, BaseInt>;
|
||||
|
||||
// Views of the sparse vectors. These need not be aligned as it's their contents
|
||||
// that need to be aligned.
|
||||
using SparseColumnView = util_intops::StrongVector<SubsetIndex, SparseColumn>;
|
||||
using SparseRowView = util_intops::StrongVector<ElementIndex, SparseRow>;
|
||||
|
||||
using SubsetBoolVector = util_intops::StrongVector<SubsetIndex, bool>;
|
||||
using ElementBoolVector = util_intops::StrongVector<ElementIndex, bool>;
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_SET_COVER_BASE_TYPES_H_
|
||||
@@ -43,15 +43,15 @@ message CapacityConstraintProto {
|
||||
message CapacityTerm {
|
||||
// The subset this weight corresponds to (index of the subset in the
|
||||
// `subset` repeated field in `SetCoverProto`).
|
||||
int32 subset = 1;
|
||||
int64 subset = 1;
|
||||
|
||||
message ElementWeightPair {
|
||||
// The element this weight corresponds to (value of `element` in
|
||||
// `SetCoverProto.Subset`).
|
||||
int32 element = 1;
|
||||
int64 element = 1;
|
||||
|
||||
// The weight of the element.
|
||||
double weight = 2;
|
||||
int64 weight = 2;
|
||||
}
|
||||
|
||||
repeated ElementWeightPair element_weights = 2;
|
||||
@@ -67,9 +67,9 @@ message CapacityConstraintProto {
|
||||
|
||||
// The minimum amount of resource that must be consumed. At least one of
|
||||
// `min_capacity` and `max_capacity` must be present.
|
||||
double min_capacity = 2;
|
||||
int64 min_capacity = 2;
|
||||
|
||||
// The maximum amount of resource that can be consumed. At least one of
|
||||
// `min_capacity` and `max_capacity` must be present.
|
||||
double max_capacity = 3;
|
||||
int64 max_capacity = 3;
|
||||
}
|
||||
|
||||
@@ -13,10 +13,14 @@
|
||||
|
||||
#include "ortools/set_cover/capacity_invariant.h"
|
||||
|
||||
#include <limits>
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/capacity_model.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
#include "ortools/util/saturated_arithmetic.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -25,48 +29,55 @@ void CapacityInvariant::Clear() {
|
||||
is_selected_.assign(set_cover_model_->num_subsets(), false);
|
||||
}
|
||||
|
||||
double CapacityInvariant::ComputeSlackChange(const SubsetIndex subset) const {
|
||||
double slack_change = 0.0;
|
||||
namespace {
|
||||
// Returns true if the addition of `q` to `sum` overflows, for q >= 0.
|
||||
bool PositiveAddOverflows(CapacityWeight sum, CapacityWeight q) {
|
||||
DCHECK_GE(q, 0);
|
||||
return sum > std::numeric_limits<CapacityWeight>::max() - q;
|
||||
}
|
||||
|
||||
// Returns true if the addition of `q` to `sum` overflows, for q <= 0.
|
||||
bool NegativeAddOverflows(CapacityWeight sum, CapacityWeight q) {
|
||||
DCHECK_LE(q, 0);
|
||||
return sum < std::numeric_limits<CapacityWeight>::min() - q;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
CapacityWeight CapacityInvariant::ComputeSlackChange(
|
||||
const SubsetIndex subset) const {
|
||||
CapacityWeight slack_change = 0;
|
||||
for (CapacityTermIndex term : model_->TermRange()) {
|
||||
if (model_->GetTermSubsetIndex(term) == subset) {
|
||||
// Hypothesis: GetTermSubsetIndex(term) is an element of the subset.
|
||||
// This information is stored in a SetCoverModel instance.
|
||||
slack_change += model_->GetTermCapacityWeight(term);
|
||||
const CapacityWeight term_weight = model_->GetTermCapacityWeight(term);
|
||||
// Make sure that the slack change will not overflow.
|
||||
CHECK(!PositiveAddOverflows(slack_change, term_weight));
|
||||
slack_change += term_weight;
|
||||
}
|
||||
}
|
||||
return slack_change;
|
||||
}
|
||||
|
||||
bool CapacityInvariant::SlackChangeFitsConstraint(
|
||||
const double slack_change) const {
|
||||
const double new_slack = current_slack_ + slack_change;
|
||||
CapacityWeight slack_change) const {
|
||||
CHECK(!AddOverflows(current_slack_, slack_change));
|
||||
const CapacityWeight new_slack = current_slack_ + slack_change;
|
||||
return new_slack >= model_->GetMinimumCapacity() &&
|
||||
new_slack <= model_->GetMaximumCapacity();
|
||||
}
|
||||
|
||||
bool CapacityInvariant::Flip(SubsetIndex subset) {
|
||||
DCHECK_LT(subset.value(), set_cover_model_->num_subsets())
|
||||
<< "Invalid subset: " << subset;
|
||||
return !is_selected_[subset] ? Select(subset) : Deselect(subset);
|
||||
}
|
||||
|
||||
bool CapacityInvariant::CanFlip(SubsetIndex subset) const {
|
||||
DCHECK_LT(subset.value(), set_cover_model_->num_subsets())
|
||||
<< "Invalid subset: " << subset;
|
||||
return !is_selected_[subset] ? CanSelect(subset) : CanDeselect(subset);
|
||||
}
|
||||
|
||||
bool CapacityInvariant::Select(SubsetIndex subset) {
|
||||
DVLOG(1) << "[Capacity constraint] Selecting subset " << subset;
|
||||
DCHECK(!is_selected_[subset]);
|
||||
|
||||
const double slack_change = ComputeSlackChange(subset);
|
||||
const CapacityWeight slack_change = ComputeSlackChange(subset);
|
||||
if (!SlackChangeFitsConstraint(slack_change)) {
|
||||
DVLOG(1) << "[Capacity constraint] Selecting subset " << subset
|
||||
<< ": infeasible";
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK(!PositiveAddOverflows(current_slack_, slack_change));
|
||||
is_selected_[subset] = true;
|
||||
current_slack_ += slack_change;
|
||||
DVLOG(1) << "[Capacity constraint] New slack: " << current_slack_;
|
||||
@@ -77,7 +88,7 @@ bool CapacityInvariant::CanSelect(SubsetIndex subset) const {
|
||||
DVLOG(1) << "[Capacity constraint] Can select subset " << subset << "?";
|
||||
DCHECK(!is_selected_[subset]);
|
||||
|
||||
const double slack_change = ComputeSlackChange(subset);
|
||||
const CapacityWeight slack_change = ComputeSlackChange(subset);
|
||||
DVLOG(1) << "[Capacity constraint] New slack if selecting: "
|
||||
<< current_slack_ + slack_change;
|
||||
return SlackChangeFitsConstraint(slack_change);
|
||||
@@ -87,13 +98,13 @@ bool CapacityInvariant::Deselect(SubsetIndex subset) {
|
||||
DVLOG(1) << "[Capacity constraint] Deselecting subset " << subset;
|
||||
DCHECK(is_selected_[subset]);
|
||||
|
||||
const double slack_change = -ComputeSlackChange(subset);
|
||||
const CapacityWeight slack_change = -ComputeSlackChange(subset);
|
||||
if (!SlackChangeFitsConstraint(slack_change)) {
|
||||
DVLOG(1) << "[Capacity constraint] Deselecting subset " << subset
|
||||
<< ": infeasible";
|
||||
return false;
|
||||
}
|
||||
|
||||
CHECK(!NegativeAddOverflows(current_slack_, slack_change));
|
||||
is_selected_[subset] = false;
|
||||
current_slack_ += slack_change;
|
||||
DVLOG(1) << "[Capacity constraint] New slack: " << current_slack_;
|
||||
@@ -104,7 +115,7 @@ bool CapacityInvariant::CanDeselect(SubsetIndex subset) const {
|
||||
DVLOG(1) << "[Capacity constraint] Can deselect subset " << subset << "?";
|
||||
DCHECK(is_selected_[subset]);
|
||||
|
||||
const double slack_change = -ComputeSlackChange(subset);
|
||||
const CapacityWeight slack_change = -ComputeSlackChange(subset);
|
||||
DVLOG(1) << "[Capacity constraint] New slack if deselecting: "
|
||||
<< current_slack_ + slack_change;
|
||||
return SlackChangeFitsConstraint(slack_change);
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#define OR_TOOLS_SET_COVER_CAPACITY_INVARIANT_H_
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/capacity_model.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
@@ -32,18 +33,6 @@ class CapacityInvariant {
|
||||
// Clears the invariant.
|
||||
void Clear();
|
||||
|
||||
// Returns `true` when the constraint is not violated by this flipping move
|
||||
// and incrementally updates the invariant. Otherwise, returns `false` and
|
||||
// does not change the object.
|
||||
//
|
||||
// Flips is_selected_[subset] to its negation, by calling Select or Deselect
|
||||
// depending on value.
|
||||
bool Flip(SubsetIndex subset);
|
||||
|
||||
// Returns `true` when the constraint would not be violated if this flipping
|
||||
// move is performed. Otherwise, returns `false`. The object never changes.
|
||||
bool CanFlip(SubsetIndex subset) const;
|
||||
|
||||
// Returns `true` when the constraint is not violated by selecting all of the
|
||||
// items in the subset and incrementally updates the invariant. Otherwise,
|
||||
// returns `false` and does not change the object. (If the subset is already
|
||||
@@ -87,7 +76,7 @@ class CapacityInvariant {
|
||||
SetCoverModel* set_cover_model_;
|
||||
|
||||
// Current slack of the constraint.
|
||||
operations_research::CapacityWeight current_slack_;
|
||||
CapacityWeight current_slack_;
|
||||
|
||||
// Current solution assignment.
|
||||
// TODO(user): reuse the assignment of a SetCoverInvariant.
|
||||
@@ -96,11 +85,11 @@ class CapacityInvariant {
|
||||
// Determines the change in slack when (de)selecting the given subset.
|
||||
// The returned value is nonnegative; add it to the slack when selecting
|
||||
// and subtract it when deselecting.
|
||||
double ComputeSlackChange(SubsetIndex subset) const;
|
||||
CapacityWeight ComputeSlackChange(SubsetIndex subset) const;
|
||||
|
||||
// Determines whether the given slack change violates the constraint
|
||||
// (`false`) or not (`true`).
|
||||
bool SlackChangeFitsConstraint(double slack_change) const;
|
||||
bool SlackChangeFitsConstraint(CapacityWeight slack_change) const;
|
||||
};
|
||||
} // namespace operations_research
|
||||
|
||||
|
||||
@@ -34,21 +34,21 @@ TEST(CapacityModel, ChecksConstraintViolation) {
|
||||
|
||||
CapacityInvariant cinv(&m, &sc);
|
||||
// Current assignment: [false, false]. Current activation: 0.
|
||||
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(0))); // All moves are possible.
|
||||
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(1)));
|
||||
EXPECT_TRUE(cinv.CanSelect(SubsetIndex(0))); // All moves are possible.
|
||||
EXPECT_TRUE(cinv.CanSelect(SubsetIndex(1)));
|
||||
|
||||
EXPECT_TRUE(cinv.Flip(SubsetIndex(0))); // Select returns true.
|
||||
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(0))); // Undoing: still valid.
|
||||
EXPECT_FALSE(cinv.CanFlip(SubsetIndex(1))); // Impossible move.
|
||||
EXPECT_FALSE(cinv.Flip(SubsetIndex(1))); // Select returns false.
|
||||
EXPECT_TRUE(cinv.Select(SubsetIndex(0)));
|
||||
EXPECT_TRUE(cinv.CanDeselect(SubsetIndex(0))); // Undoing: still valid.
|
||||
EXPECT_FALSE(cinv.CanSelect(SubsetIndex(1))); // Impossible move.
|
||||
EXPECT_FALSE(cinv.Select(SubsetIndex(1)));
|
||||
// Current assignment: [true, false]. Current activation: 1.
|
||||
|
||||
EXPECT_TRUE(cinv.Flip(SubsetIndex(0))); // Deselect returns true.
|
||||
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(0))); // Undoing: still valid.
|
||||
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(1))); // Valid when 0 not selected.
|
||||
EXPECT_TRUE(cinv.Flip(SubsetIndex(1))); // Select returns true.
|
||||
EXPECT_FALSE(cinv.CanFlip(SubsetIndex(0))); // Impossible move.
|
||||
EXPECT_TRUE(cinv.CanFlip(SubsetIndex(1))); // Undoing: still valid.
|
||||
EXPECT_TRUE(cinv.Deselect(SubsetIndex(0)));
|
||||
EXPECT_TRUE(cinv.CanSelect(SubsetIndex(0))); // Undoing: still valid.
|
||||
EXPECT_TRUE(cinv.CanSelect(SubsetIndex(1))); // Valid when 0 not selected.
|
||||
EXPECT_TRUE(cinv.Select(SubsetIndex(1)));
|
||||
EXPECT_FALSE(cinv.CanSelect(SubsetIndex(0))); // Impossible move.
|
||||
EXPECT_TRUE(cinv.CanDeselect(SubsetIndex(1))); // Undoing: still valid.
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -14,20 +14,18 @@
|
||||
#include "ortools/set_cover/capacity_model.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <numeric>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/log/log.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
void CapacityModel::AddTerm(SubsetIndex subset, ElementIndex element,
|
||||
CapacityWeight weight) {
|
||||
CHECK(std::isfinite(weight));
|
||||
|
||||
subsets_.push_back(subset);
|
||||
elements_.push_back(element);
|
||||
weights_.push_back(weight);
|
||||
@@ -37,13 +35,11 @@ void CapacityModel::AddTerm(SubsetIndex subset, ElementIndex element,
|
||||
}
|
||||
|
||||
void CapacityModel::SetMinimumCapacity(CapacityWeight min_capacity) {
|
||||
CHECK(!std::isnan(min_capacity));
|
||||
CHECK_NE(min_capacity, std::numeric_limits<CapacityWeight>::max());
|
||||
min_capacity_ = min_capacity;
|
||||
}
|
||||
|
||||
void CapacityModel::SetMaximumCapacity(CapacityWeight max_capacity) {
|
||||
CHECK(!std::isnan(max_capacity));
|
||||
CHECK_NE(max_capacity, std::numeric_limits<CapacityWeight>::min());
|
||||
max_capacity_ = max_capacity;
|
||||
}
|
||||
|
||||
@@ -15,12 +15,14 @@
|
||||
#define OR_TOOLS_SET_COVER_CAPACITY_MODEL_H_
|
||||
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "ortools/base/strong_int.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/capacity.pb.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
@@ -36,7 +38,7 @@
|
||||
|
||||
namespace operations_research {
|
||||
// Basic type for weights. For now, the same as `Cost` for the set covering.
|
||||
using CapacityWeight = double;
|
||||
using CapacityWeight = int64_t;
|
||||
|
||||
// Term index in a capacity constraint.
|
||||
DEFINE_STRONG_INT_TYPE(CapacityTermIndex, BaseInt);
|
||||
@@ -65,8 +67,6 @@ class CapacityModel {
|
||||
min_capacity_(min),
|
||||
max_capacity_(max) {
|
||||
// At least one bound must be set. Otherwise, the constraint is vacuous.
|
||||
CHECK(!std::isnan(min_capacity_));
|
||||
CHECK(!std::isnan(max_capacity_));
|
||||
CHECK(min_capacity_ != std::numeric_limits<CapacityWeight>::min() ||
|
||||
max_capacity_ != std::numeric_limits<CapacityWeight>::max());
|
||||
}
|
||||
@@ -89,7 +89,6 @@ class CapacityModel {
|
||||
}
|
||||
|
||||
// Adds a new term to the constraint.
|
||||
// This will CHECK-fail if the weight is infinite or a NaN.
|
||||
void AddTerm(SubsetIndex subset, ElementIndex element, CapacityWeight weight);
|
||||
|
||||
// Returns the element, subset, or capacity of the given term.
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace {
|
||||
@@ -30,12 +29,6 @@ TEST(CapacityModel, ConstructorRequiresOneBound) {
|
||||
"min");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, ConstructorRejectsNaN) {
|
||||
EXPECT_DEATH(CapacityModel(std::numeric_limits<CapacityWeight>::quiet_NaN(),
|
||||
std::numeric_limits<CapacityWeight>::quiet_NaN()),
|
||||
"isnan");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, WithMinimumWeightRequiresNonVacuousMinimum) {
|
||||
EXPECT_DEATH(CapacityModel::WithMinimumWeight(
|
||||
std::numeric_limits<CapacityWeight>::min()),
|
||||
@@ -48,183 +41,95 @@ TEST(CapacityModel, WithMaximumWeightRequiresNonVacuousMaximum) {
|
||||
"min");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, WithMinimumWeightRejectsNaN) {
|
||||
EXPECT_DEATH(CapacityModel::WithMinimumWeight(
|
||||
std::numeric_limits<double>::quiet_NaN()),
|
||||
"isnan");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, WithMaximumWeightRejectsNaN) {
|
||||
EXPECT_DEATH(CapacityModel::WithMaximumWeight(
|
||||
std::numeric_limits<double>::quiet_NaN()),
|
||||
"isnan");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, SetMinimumCapacityRejectsNaN) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
EXPECT_DEATH(m.SetMinimumCapacity(std::numeric_limits<double>::quiet_NaN()),
|
||||
"isnan");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, SetMinimumCapacityRejectsPlusInfinity) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
CapacityModel m(0, 1);
|
||||
EXPECT_DEATH(m.SetMinimumCapacity(std::numeric_limits<CapacityWeight>::max()),
|
||||
"max");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, SetMaximumCapacityRejectsNaN) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
EXPECT_DEATH(m.SetMaximumCapacity(std::numeric_limits<double>::quiet_NaN()),
|
||||
"isnan");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, SetMaximumCapacityRejectsMinusInfinity) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
CapacityModel m(0, 1);
|
||||
EXPECT_DEATH(m.SetMaximumCapacity(std::numeric_limits<CapacityWeight>::min()),
|
||||
"min");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, AddTermRejectsNaN) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
EXPECT_DEATH(m.AddTerm(SubsetIndex(0), ElementIndex(0),
|
||||
std::numeric_limits<double>::quiet_NaN()),
|
||||
"isfinite");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, AddTermRejectsPlusInf) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
EXPECT_DEATH(m.AddTerm(SubsetIndex(0), ElementIndex(0),
|
||||
std::numeric_limits<double>::infinity()),
|
||||
"isfinite");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, AddTermRejectsMinusInf) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
EXPECT_DEATH(m.AddTerm(SubsetIndex(0), ElementIndex(0),
|
||||
-std::numeric_limits<double>::infinity()),
|
||||
"isfinite");
|
||||
}
|
||||
|
||||
TEST(CapacityModel, ComputeFeasibilityWithNoTerms) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
CapacityModel m(0, 1);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(-1.0);
|
||||
m.SetMinimumCapacity(-1);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(0.0);
|
||||
m.SetMaximumCapacity(0);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(-2.0);
|
||||
m.SetMaximumCapacity(-1.0);
|
||||
m.SetMinimumCapacity(-2);
|
||||
m.SetMaximumCapacity(-1);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
}
|
||||
|
||||
TEST(CapacityModel, ComputeFeasibilityWithOnlyPositiveWeights) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(0), 1.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(1), 2.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(2), 3.0);
|
||||
// Activation bounds: [0.0, 6.0].
|
||||
CapacityModel m(0, 1);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(0), 1);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(1), 2);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(2), 3);
|
||||
// Activation bounds: [0, 6].
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(-1.0);
|
||||
m.SetMinimumCapacity(-1);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(-1.0);
|
||||
m.SetMaximumCapacity(-1);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(7.0);
|
||||
m.SetMaximumCapacity(7);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(7.0);
|
||||
m.SetMinimumCapacity(7);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
}
|
||||
|
||||
TEST(CapacityModel, ComputeFeasibilityWithOnlyNegativeWeights) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(0), -1.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(1), -2.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(2), -3.0);
|
||||
// Activation bounds: [-6.0, 0.0].
|
||||
CapacityModel m(0, 1);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(0), -1);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(1), -2);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(2), -3);
|
||||
// Activation bounds: [-6, 0].
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(1.0);
|
||||
m.SetMaximumCapacity(1);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(1.0);
|
||||
m.SetMinimumCapacity(1);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(-7.0);
|
||||
m.SetMinimumCapacity(-7);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(-7.0);
|
||||
m.SetMaximumCapacity(-7);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
}
|
||||
|
||||
TEST(CapacityModel, ComputeFeasibilityWithOnlyMixedWeights) {
|
||||
CapacityModel m(0.0, 1.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(0), -1.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(1), 2.0);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(2), -3.0);
|
||||
// Activation bounds: [-4.0, 2.0].
|
||||
CapacityModel m(0, 1);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(0), -1);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(1), 2);
|
||||
m.AddTerm(SubsetIndex(0), ElementIndex(2), -3);
|
||||
// Activation bounds: [-4, 2].
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(3.0);
|
||||
m.SetMaximumCapacity(3);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(3.0);
|
||||
m.SetMinimumCapacity(3);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMinimumCapacity(-5.0);
|
||||
m.SetMinimumCapacity(-5);
|
||||
EXPECT_TRUE(m.ComputeFeasibility());
|
||||
|
||||
m.SetMaximumCapacity(-5.0);
|
||||
m.SetMaximumCapacity(-5);
|
||||
EXPECT_FALSE(m.ComputeFeasibility());
|
||||
}
|
||||
|
||||
// TEST(CapacityModel, ImportModelFromProto) {
|
||||
// CapacityModel m(0.0, 1.0);
|
||||
// EXPECT_THAT(m.ExportModelAsProto(), EqualsProto(R"pb(min_capacity: 0.0
|
||||
// max_capacity: 1.0)pb"));
|
||||
|
||||
// m.AddTerm(SubsetIndex(0), ElementIndex(0), 1.0);
|
||||
// EXPECT_THAT(m.ExportModelAsProto(),
|
||||
// EqualsProto(R"pb(min_capacity: 0.0
|
||||
// max_capacity: 1.0
|
||||
// capacity_term {
|
||||
// subset: 0
|
||||
// element_weights { element: 0 weight: 1.0 }
|
||||
// })pb"));
|
||||
|
||||
// m.AddTerm(SubsetIndex(0), ElementIndex(1), 1.0);
|
||||
// EXPECT_THAT(m.ExportModelAsProto(),
|
||||
// EqualsProto(
|
||||
// R"pb(min_capacity: 0.0
|
||||
// max_capacity: 1.0
|
||||
// capacity_term {
|
||||
// subset: 0
|
||||
// element_weights { element: 0 weight: 1.0 }
|
||||
// element_weights { element: 1 weight: 1.0 }
|
||||
// })pb"));
|
||||
// }
|
||||
|
||||
// TEST(CapacityModel, ImportModelFromProtoHasCanonicalOrder) {
|
||||
// // Reverse order for the terms compared to
|
||||
// // CapacityModel_ImportModelFromProto, same order in the proto.
|
||||
// CapacityModel m(0.0, 1.0);
|
||||
// m.AddTerm(SubsetIndex(0), ElementIndex(1), 1.0);
|
||||
// m.AddTerm(SubsetIndex(0), ElementIndex(0), 1.0);
|
||||
// EXPECT_THAT(m.ExportModelAsProto(),
|
||||
// EqualsProto(
|
||||
// R"pb(min_capacity: 0.0
|
||||
// max_capacity: 1.0
|
||||
// capacity_term {
|
||||
// subset: 0
|
||||
// element_weights { element: 0 weight: 1.0 }
|
||||
// element_weights { element: 1 weight: 1.0 }
|
||||
// })pb"));
|
||||
// }
|
||||
|
||||
} // namespace
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -333,13 +333,6 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
},
|
||||
arg("subset"))
|
||||
.def("recompute", &SetCoverInvariant::Recompute)
|
||||
.def(
|
||||
"flip",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset,
|
||||
SetCoverInvariant::ConsistencyLevel consistency) {
|
||||
invariant.Flip(SubsetIndex(subset), consistency);
|
||||
},
|
||||
arg("subset"), arg("consistency"))
|
||||
.def(
|
||||
"select",
|
||||
[](SetCoverInvariant& invariant, BaseInt subset,
|
||||
@@ -418,8 +411,8 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
})
|
||||
.def("next_solution",
|
||||
[](ElementDegreeSolutionGenerator& heuristic,
|
||||
const std::vector<BaseInt>& focus,
|
||||
const std::vector<double>& costs) -> bool {
|
||||
absl::Span<const BaseInt> focus,
|
||||
absl::Span<const double> costs) -> bool {
|
||||
return heuristic.NextSolution(
|
||||
VectorIntToVectorSubsetIndex(focus),
|
||||
VectorDoubleToSubsetCostVector(costs));
|
||||
@@ -434,7 +427,7 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
})
|
||||
.def("next_solution",
|
||||
[](LazyElementDegreeSolutionGenerator& heuristic,
|
||||
const std::vector<BaseInt>& focus) -> bool {
|
||||
absl::Span<const BaseInt> focus) -> bool {
|
||||
return heuristic.NextSolution(VectorIntToVectorSubsetIndex(focus));
|
||||
})
|
||||
.def("next_solution",
|
||||
@@ -459,8 +452,8 @@ PYBIND11_MODULE(set_cover, m) {
|
||||
num_iterations);
|
||||
})
|
||||
.def("next_solution",
|
||||
[](SteepestSearch& heuristic, const std::vector<BaseInt>& focus,
|
||||
const std::vector<double>& costs, int num_iterations) -> bool {
|
||||
[](SteepestSearch& heuristic, absl::Span<const BaseInt> focus,
|
||||
absl::Span<const double> costs, int num_iterations) -> bool {
|
||||
return heuristic.NextSolution(
|
||||
VectorIntToVectorSubsetIndex(focus),
|
||||
VectorDoubleToSubsetCostVector(costs), num_iterations);
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/algorithms/adjustable_k_ary_heap.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
@@ -134,9 +135,9 @@ bool GreedySolutionGenerator::NextSolution(absl::Span<const SubsetIndex> focus,
|
||||
std::vector<SubsetIndex> subsets_to_remove;
|
||||
subsets_to_remove.reserve(focus.size());
|
||||
while (!pq.IsEmpty() || inv_->num_uncovered_elements() > 0) {
|
||||
LOG_EVERY_N_SEC(INFO, 5)
|
||||
<< "Queue size: " << pq.heap_size()
|
||||
<< ", #uncovered elements: " << inv_->num_uncovered_elements();
|
||||
// LOG_EVERY_N_SEC(INFO, 5)
|
||||
// << "Queue size: " << pq.heap_size()
|
||||
// << ", #uncovered elements: " << inv_->num_uncovered_elements();
|
||||
const SubsetIndex best_subset(pq.TopIndex());
|
||||
pq.Pop();
|
||||
inv_->Select(best_subset, CL::kFreeAndUncovered);
|
||||
@@ -705,7 +706,11 @@ bool GuidedTabuSearch::NextSolution(absl::Span<const SubsetIndex> focus,
|
||||
|
||||
UpdatePenalties(focus);
|
||||
tabu_list_.Add(best_subset);
|
||||
inv_->Flip(best_subset, CL::kFreeAndUncovered);
|
||||
if (inv_->is_selected()[best_subset]) {
|
||||
inv_->Deselect(best_subset, CL::kFreeAndUncovered);
|
||||
} else {
|
||||
inv_->Select(best_subset, CL::kFreeAndUncovered);
|
||||
}
|
||||
// TODO(user): make the cost computation incremental.
|
||||
augmented_cost =
|
||||
std::accumulate(augmented_costs_.begin(), augmented_costs_.end(), 0.0);
|
||||
@@ -714,9 +719,6 @@ bool GuidedTabuSearch::NextSolution(absl::Span<const SubsetIndex> focus,
|
||||
<< inv_->cost() << ", best cost = ," << best_cost
|
||||
<< ", penalized cost = ," << augmented_cost;
|
||||
if (inv_->cost() < best_cost) {
|
||||
LOG(INFO) << "Updated best cost, " << "Iteration, " << iteration
|
||||
<< ", current cost = ," << inv_->cost() << ", best cost = ,"
|
||||
<< best_cost << ", penalized cost = ," << augmented_cost;
|
||||
best_cost = inv_->cost();
|
||||
best_choices = inv_->is_selected();
|
||||
}
|
||||
@@ -773,17 +775,19 @@ bool GuidedLocalSearch::NextSolution(absl::Span<const SubsetIndex> focus,
|
||||
|
||||
for (int iteration = 0;
|
||||
!priority_heap_.IsEmpty() && iteration < num_iterations; ++iteration) {
|
||||
// Improve current solution respective to the current penalties.
|
||||
// Improve current solution respective to the current penalties by flipping
|
||||
// the best subset.
|
||||
const SubsetIndex best_subset(priority_heap_.TopIndex());
|
||||
if (inv_->is_selected()[best_subset]) {
|
||||
utility_heap_.Insert({0, best_subset.value()});
|
||||
inv_->Deselect(best_subset, CL::kRedundancy);
|
||||
} else {
|
||||
utility_heap_.Insert(
|
||||
{static_cast<float>(inv_->model()->subset_costs()[best_subset] /
|
||||
(1 + penalties_[best_subset])),
|
||||
best_subset.value()});
|
||||
inv_->Select(best_subset, CL::kRedundancy);
|
||||
}
|
||||
inv_->Flip(best_subset, CL::kRedundancy); // Flip the best subset.
|
||||
DCHECK(!utility_heap_.IsEmpty());
|
||||
|
||||
// Getting the subset with highest utility. utility_heap_ is not empty,
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/algorithms/adjustable_k_ary_heap.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/base/logging.h"
|
||||
#include "ortools/base/mathutil.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -301,15 +302,6 @@ BaseInt SetCoverInvariant::ComputeNumFreeElements(SubsetIndex subset) const {
|
||||
return num_free_elements;
|
||||
}
|
||||
|
||||
void SetCoverInvariant::Flip(SubsetIndex subset,
|
||||
ConsistencyLevel target_consistency) {
|
||||
if (!is_selected_[subset]) {
|
||||
Select(subset, target_consistency);
|
||||
} else {
|
||||
Deselect(subset, target_consistency);
|
||||
}
|
||||
}
|
||||
|
||||
void SetCoverInvariant::Select(SubsetIndex subset,
|
||||
ConsistencyLevel target_consistency) {
|
||||
const bool update_redundancy_info = target_consistency >= CL::kRedundancy;
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover.pb.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
@@ -179,11 +180,6 @@ class SetCoverInvariant {
|
||||
// Computes the number of free (uncovered) elements in the given subset.
|
||||
BaseInt ComputeNumFreeElements(SubsetIndex subset) const;
|
||||
|
||||
// Flips is_selected_[subset] to its negation, by calling Select or Deselect
|
||||
// depending on value. Updates the invariant incrementally to the given
|
||||
// consistency level.
|
||||
void Flip(SubsetIndex subset, ConsistencyLevel consistency);
|
||||
|
||||
// Includes subset in the solution by setting is_selected_[subset] to true
|
||||
// and incrementally updating the invariant to the given consistency level.
|
||||
void Select(SubsetIndex subset, ConsistencyLevel consistency);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "absl/synchronization/blocking_counter.h"
|
||||
#include "ortools/algorithms/adjustable_k_ary_heap.h"
|
||||
#include "ortools/base/threadpool.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
|
||||
@@ -15,11 +15,11 @@
|
||||
#define OR_TOOLS_SET_COVER_SET_COVER_LAGRANGIAN_H_
|
||||
|
||||
#include <memory>
|
||||
#include <new>
|
||||
#include <tuple>
|
||||
#include <vector>
|
||||
|
||||
#include "ortools/base/threadpool.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/linear_solver/linear_solver.h"
|
||||
#include "ortools/lp_data/lp_types.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
|
||||
@@ -15,8 +15,8 @@
|
||||
#define OR_TOOLS_SET_COVER_SET_COVER_MIP_H_
|
||||
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
namespace operations_research {
|
||||
enum class SetCoverMipSolver : int {
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#include "absl/strings/str_format.h"
|
||||
#include "absl/types/span.h"
|
||||
#include "ortools/algorithms/radix_sort.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover.pb.h"
|
||||
|
||||
namespace operations_research {
|
||||
@@ -159,15 +160,10 @@ SetCoverModel SetCoverModel::GenerateRandomModelFrom(
|
||||
subset_already_contains_element[element] = false;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Finished generating the model with " << num_elements_covered
|
||||
<< " elements covered.";
|
||||
|
||||
// It can happen -- rarely in practice -- that some of the elements cannot be
|
||||
// covered. Let's add them to randomly chosen subsets.
|
||||
if (num_elements_covered != num_elements) {
|
||||
LOG(INFO) << "Generated model with " << num_elements - num_elements_covered
|
||||
<< " elements that cannot be covered. Adding them to random "
|
||||
"subsets.";
|
||||
SubsetBoolVector element_already_in_subset(num_subsets, false);
|
||||
for (ElementIndex element(0); element.value() < num_elements; ++element) {
|
||||
LOG_EVERY_N_SEC(INFO, 5) << absl::StrFormat(
|
||||
@@ -200,11 +196,7 @@ SetCoverModel SetCoverModel::GenerateRandomModelFrom(
|
||||
++num_elements_covered;
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Finished generating subsets for elements that were not "
|
||||
"covered in the original model.";
|
||||
}
|
||||
LOG(INFO) << "Finished generating the model. There are "
|
||||
<< num_elements - num_elements_covered << " uncovered elements.";
|
||||
|
||||
CHECK_EQ(num_elements_covered, num_elements);
|
||||
|
||||
@@ -338,10 +330,8 @@ void SetCoverModel::SortElementsInSubsets() {
|
||||
|
||||
void SetCoverModel::CreateSparseRowView() {
|
||||
if (row_view_is_valid_) {
|
||||
LOG(INFO) << "CreateSparseRowView: already valid";
|
||||
return;
|
||||
}
|
||||
LOG(INFO) << "CreateSparseRowView started";
|
||||
rows_.resize(num_elements_, SparseRow());
|
||||
ElementToIntVector row_sizes(num_elements_, 0);
|
||||
for (const SubsetIndex subset : SubsetRange()) {
|
||||
@@ -367,7 +357,6 @@ void SetCoverModel::CreateSparseRowView() {
|
||||
}
|
||||
row_view_is_valid_ = true;
|
||||
elements_in_subsets_are_sorted_ = true;
|
||||
LOG(INFO) << "CreateSparseRowView finished";
|
||||
}
|
||||
|
||||
bool SetCoverModel::ComputeFeasibility() const {
|
||||
@@ -382,7 +371,7 @@ bool SetCoverModel::ComputeFeasibility() const {
|
||||
}
|
||||
SubsetIndex column_index(0);
|
||||
for (const SparseColumn& column : columns_) {
|
||||
DLOG_IF(INFO, column.empty()) << "Empty column " << column_index.value();
|
||||
// DLOG_IF(INFO, column.empty()) << "Empty column " << column_index.value();
|
||||
for (const ElementIndex element : column) {
|
||||
++coverage[element];
|
||||
}
|
||||
@@ -420,7 +409,6 @@ SetCoverProto SetCoverModel::ExportModelAsProto() const {
|
||||
subset_proto->add_element(element.value());
|
||||
}
|
||||
}
|
||||
LOG(INFO) << "Finished exporting the model.";
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "ortools/base/strong_int.h"
|
||||
#include "ortools/base/strong_vector.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover.pb.h"
|
||||
|
||||
// Representation class for the weighted set-covering problem.
|
||||
@@ -50,55 +51,6 @@
|
||||
// cardinalities of all the subsets.
|
||||
|
||||
namespace operations_research {
|
||||
// Basic non-strict type for cost. The speed penalty for using double is ~2%.
|
||||
using Cost = double;
|
||||
|
||||
// Base non-strict integer type for counting elements and subsets.
|
||||
// Using ints makes it possible to represent problems with more than 2 billion
|
||||
// (2e9) elements and subsets. If need arises one day, BaseInt can be split
|
||||
// into SubsetBaseInt and ElementBaseInt.
|
||||
// Quick testing has shown a slowdown of about 20-25% when using int64_t.
|
||||
using BaseInt = int32_t;
|
||||
|
||||
// We make heavy use of strong typing to avoid obvious mistakes.
|
||||
// Subset index.
|
||||
DEFINE_STRONG_INT_TYPE(SubsetIndex, BaseInt);
|
||||
|
||||
// Element index.
|
||||
DEFINE_STRONG_INT_TYPE(ElementIndex, BaseInt);
|
||||
|
||||
// Position in a vector. The vector may either represent a column, i.e. a
|
||||
// subset with all its elements, or a row, i,e. the list of subsets which
|
||||
// contain a given element.
|
||||
DEFINE_STRONG_INT_TYPE(ColumnEntryIndex, BaseInt);
|
||||
DEFINE_STRONG_INT_TYPE(RowEntryIndex, BaseInt);
|
||||
|
||||
using SubsetRange = util_intops::StrongIntRange<SubsetIndex>;
|
||||
using ElementRange = util_intops::StrongIntRange<ElementIndex>;
|
||||
using ColumnEntryRange = util_intops::StrongIntRange<ColumnEntryIndex>;
|
||||
|
||||
using SubsetCostVector = util_intops::StrongVector<SubsetIndex, Cost>;
|
||||
using ElementCostVector = util_intops::StrongVector<ElementIndex, Cost>;
|
||||
|
||||
using SparseColumn = util_intops::StrongVector<ColumnEntryIndex, ElementIndex>;
|
||||
using SparseRow = util_intops::StrongVector<RowEntryIndex, SubsetIndex>;
|
||||
|
||||
using ElementToIntVector = util_intops::StrongVector<ElementIndex, BaseInt>;
|
||||
using SubsetToIntVector = util_intops::StrongVector<SubsetIndex, BaseInt>;
|
||||
|
||||
// Views of the sparse vectors. These need not be aligned as it's their contents
|
||||
// that need to be aligned.
|
||||
using SparseColumnView = util_intops::StrongVector<SubsetIndex, SparseColumn>;
|
||||
using SparseRowView = util_intops::StrongVector<ElementIndex, SparseRow>;
|
||||
|
||||
using SubsetBoolVector = util_intops::StrongVector<SubsetIndex, bool>;
|
||||
using ElementBoolVector = util_intops::StrongVector<ElementIndex, bool>;
|
||||
|
||||
// Useful for representing permutations,
|
||||
using ElementToElementVector =
|
||||
util_intops::StrongVector<ElementIndex, ElementIndex>;
|
||||
using SubsetToSubsetVector =
|
||||
util_intops::StrongVector<SubsetIndex, SubsetIndex>;
|
||||
|
||||
// Main class for describing a weighted set-covering problem.
|
||||
class SetCoverModel {
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "ortools/base/filesystem.h"
|
||||
#include "ortools/base/helpers.h"
|
||||
#include "ortools/base/options.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover.pb.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
#include "ortools/util/filelineiter.h"
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "absl/time/time.h"
|
||||
#include "ortools/base/init_google.h"
|
||||
#include "ortools/base/timer.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover_heuristics.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
#include "ortools/set_cover/set_cover_model.h"
|
||||
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "gtest/gtest.h"
|
||||
#include "ortools/base/gmock.h"
|
||||
#include "ortools/base/parse_text_proto.h"
|
||||
#include "ortools/set_cover/base_types.h"
|
||||
#include "ortools/set_cover/set_cover.pb.h"
|
||||
#include "ortools/set_cover/set_cover_heuristics.h"
|
||||
#include "ortools/set_cover/set_cover_invariant.h"
|
||||
|
||||
Reference in New Issue
Block a user