22#include "absl/log/check.h"
48 std::numeric_limits<Cost>::infinity());
51 for (
const SubsetIndex subset : model_.
SubsetRange()) {
52 marginal_costs[subset] =
56 for (
const ElementIndex element : model_.
ElementRange()) {
58 Cost min_marginal_cost = std::numeric_limits<Cost>::infinity();
62 for (
const SubsetIndex subset : rows[element]) {
63 min_marginal_cost = std::min(min_marginal_cost, marginal_costs[subset]);
65 multipliers[element] = min_marginal_cost;
77 for (ColumnEntryIndex pos(0); pos.value() <
column.size(); ++pos) {
78 result += dual[
column[pos]];
89void FillReducedCostsSlice(SubsetIndex
start, SubsetIndex
end,
94 for (SubsetIndex subset =
start; subset <
end; ++subset) {
95 (*reduced_costs)[subset] =
104 const SubsetIndex num_subsets(model_.
num_subsets());
106 const SubsetIndex block_size =
107 SubsetIndex(1) + num_subsets / num_threads_;
110 ThreadPool thread_pool(
"ParallelComputeReducedCosts", num_threads_);
123 thread_pool.
Schedule([
start, block_size, num_subsets, &costs,
124 &multipliers, &columns, &reduced_costs]() {
125 const SubsetIndex
end = std::min(
start + block_size, num_subsets);
126 FillReducedCostsSlice(
start,
end, costs, multipliers, columns,
131 return reduced_costs;
143 FillReducedCostsSlice(SubsetIndex(0), SubsetIndex(reduced_costs.size()),
144 costs, multipliers, columns, &reduced_costs);
145 return reduced_costs;
153void FillSubgradientSlice(SubsetIndex
start, SubsetIndex
end,
157 for (SubsetIndex subset(
start); subset <
end; ++subset) {
158 if (reduced_costs[subset] < 0.0) {
159 for (
const ElementIndex element : columns[subset]) {
160 (*subgradient)[element] -= 1.0;
176 FillSubgradientSlice(SubsetIndex(0), SubsetIndex(reduced_costs.size()),
177 model_.
columns(), reduced_costs, &subgradient);
183 const SubsetIndex num_subsets(model_.
num_subsets());
184 const SubsetIndex block_size =
185 SubsetIndex(1) + num_subsets / num_threads_;
192 std::vector<ElementCostVector> subgradients(
194 ThreadPool thread_pool(
"ParallelComputeSubgradient", num_threads_);
197 int thread_index = 0;
198 for (SubsetIndex
start(0);
start < num_subsets;
199 start += block_size, ++thread_index) {
200 thread_pool.
Schedule([
start, block_size, num_subsets, &reduced_costs,
201 &columns, &subgradients, thread_index]() {
202 const SubsetIndex
end = std::min(
start + block_size, num_subsets);
203 FillSubgradientSlice(
start,
end, columns, reduced_costs,
204 &subgradients[thread_index]);
208 for (
int thread_index = 0; thread_index < num_threads_; ++thread_index) {
209 for (
const ElementIndex element : model_.
ElementRange()) {
210 subgradient[element] += subgradients[thread_index][element];
223void FillLagrangianValueSlice(SubsetIndex
start, SubsetIndex
end,
225 Cost* lagrangian_value) {
229 for (SubsetIndex subset(
start); subset <
end; ++subset) {
230 if (reduced_costs[subset] < 0.0) {
231 *lagrangian_value += reduced_costs[subset];
248 Cost lagrangian_value = 0.0;
250 for (
const Cost u : multipliers) {
251 lagrangian_value += u;
253 FillLagrangianValueSlice(SubsetIndex(0), SubsetIndex(reduced_costs.size()),
254 reduced_costs, &lagrangian_value);
255 return lagrangian_value;
261 const SubsetIndex num_subsets(model_.
num_subsets());
262 const SubsetIndex block_size =
263 SubsetIndex(1) + num_subsets / num_threads_;
264 Cost lagrangian_value = 0.0;
267 for (
const Cost u : multipliers) {
268 lagrangian_value += u;
270 std::vector<Cost> lagrangian_values(num_threads_, 0.0);
271 ThreadPool thread_pool(
"ParallelComputeLagrangianValue", num_threads_);
274 int thread_index = 0;
276 thread_pool.
Schedule([
start, block_size, num_subsets, thread_index,
277 &reduced_costs, &lagrangian_values]() {
278 const SubsetIndex
end = std::min(
start + block_size, num_subsets);
279 FillLagrangianValueSlice(
start,
end, reduced_costs,
280 &lagrangian_values[thread_index]);
285 for (
const Cost l : lagrangian_values) {
286 lagrangian_value += l;
288 return lagrangian_value;
310 DCHECK_GT(step_size, 0);
313 Cost subgradient_square_norm = 0.0;
314 for (
const Cost x : subgradient) {
315 subgradient_square_norm +=
x *
x;
319 step_size * (
upper_bound - lagrangian_value) / subgradient_square_norm;
320 for (
const ElementIndex element : model_.
ElementRange()) {
323 (*multipliers)[element] = std::clamp(
324 (*multipliers)[element] + factor * subgradient[element], 0.0, 1e6);
333 DCHECK_GT(step_size, 0);
337 Cost subgradient_square_norm = 0.0;
338 for (
const Cost x : subgradient) {
339 subgradient_square_norm +=
x *
x;
343 step_size * (
upper_bound - lagrangian_value) / subgradient_square_norm;
344 for (
const ElementIndex element : model_.
ElementRange()) {
347 (*multipliers)[element] = std::clamp(
348 (*multipliers)[element] + factor * subgradient[element], 0.0, 1e6);
357 for (
const SubsetIndex subset : model_.
SubsetRange()) {
358 if (
solution[subset] && reduced_costs[subset] > 0.0) {
359 gap += reduced_costs[subset];
360 }
else if (!
solution[subset] && reduced_costs[subset] < 0.0) {
362 gap -= reduced_costs[subset];
366 for (
const ElementIndex element : model_.
ElementRange()) {
367 gap += (coverage[element] - 1) * multipliers[element];
380 for (
const SubsetIndex subset : model_.
SubsetRange()) {
381 delta[subset] = std::max(reduced_costs[subset], 0.0);
382 for (
const ElementIndex element : columns[subset]) {
383 const double size = coverage[element];
399 StepSizer(
int period,
double step_size)
400 : period_(period), iter_to_check_(period), step_size_(step_size) {
406 if (iter == iter_to_check_) {
407 iter_to_check_ += period_;
412 max_lb_ == 0.0 ? 0.0 : (max_lb_ - min_lb_) / std::abs(max_lb_);
413 DCHECK_GE(lb_gap, 0.0);
416 }
else if (lb_gap <= 0.001) {
419 step_size_ = std::clamp(step_size_, 1e-6, 10.0);
427 min_lb_ = std::numeric_limits<Cost>::infinity();
428 max_lb_ = -std::numeric_limits<Cost>::infinity();
442 explicit Stopper(
int period)
444 iter_to_check_(period),
445 lower_bound_(
std::numeric_limits<
Cost>::
min()) {}
446 bool DecideWhetherToStop(
int iter,
Cost lb) {
447 DCHECK_GE(lb, lower_bound_);
448 if (iter == iter_to_check_) {
449 iter_to_check_ += period_;
452 DCHECK_GE(
delta, 0.0);
453 DCHECK_GE(relative_delta, 0.0);
455 return relative_delta < 0.001 &&
delta < 1;
470template <
typename Priority,
typename Index,
bool IsMaxHeap>
473 explicit TopKHeap(
int max_size) : heap_(), max_size_(max_size) {}
474 void Clear() { heap_.Clear(); }
475 void Add(Priority priority, Index
index) {
476 if (heap_.Size() < max_size_) {
477 heap_.Add(priority,
index);
478 }
else if (heap_.HasPriority(priority, heap_.TopPriority())) {
479 heap_.ReplaceTop(priority,
index);
490std::tuple<Cost, SubsetCostVector, ElementCostVector>
495 double step_size = 0.1;
496 StepSizer step_sizer(20, step_size);
497 Stopper stopper(100);
503 for (
int iter = 0; iter < 1000; ++iter) {
505 const Cost lagrangian_value =
517 return std::make_tuple(
lower_bound, reduced_costs, multipliers);
const ElementToIntVector & coverage() const
Returns vector containing number of subsets covering each element.
void UpdateMultipliers(double step_size, Cost lagrangian_value, Cost upper_bound, const SubsetCostVector &reduced_costs, ElementCostVector *multipliers) const
Cost ComputeLagrangianValue(const SubsetCostVector &reduced_costs, const ElementCostVector &multipliers) const
ElementCostVector ComputeSubgradient(const SubsetCostVector &reduced_costs) const
void ParallelUpdateMultipliers(double step_size, Cost lagrangian_value, Cost upper_bound, const SubsetCostVector &reduced_costs, ElementCostVector *multipliers) const
ElementCostVector ParallelComputeSubgradient(const SubsetCostVector &reduced_costs) const
SubsetCostVector ParallelComputeReducedCosts(const SubsetCostVector &costs, const ElementCostVector &multipliers) const
Computes the reduced costs for all subsets in parallel using ThreadPool.
Cost ParallelComputeLagrangianValue(const SubsetCostVector &reduced_costs, const ElementCostVector &multipliers) const
ElementCostVector InitializeLagrangeMultipliers() const
Initializes the multipliers vector (u) based on the cost per subset.
std::tuple< Cost, SubsetCostVector, ElementCostVector > ComputeLowerBound(const SubsetCostVector &costs, Cost upper_bound)
Computes a lower bound on the optimal cost.
SubsetCostVector ComputeReducedCosts(const SubsetCostVector &costs, const ElementCostVector &multipliers) const
Cost ComputeGap(const SubsetCostVector &reduced_costs, const SubsetBoolVector &solution, const ElementCostVector &multipliers) const
util_intops::StrongIntRange< ElementIndex > ElementRange() const
const SparseRowView & rows() const
Row view of the set covering problem.
BaseInt num_elements() const
util_intops::StrongIntRange< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
const SubsetCostVector & subset_costs() const
Vector of costs for each subset.
const SparseColumnView & columns() const
Column view of the set covering problem.
BaseInt num_subsets() const
void Schedule(std::function< void()> closure)
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
In SWIG mode, we don't want anything besides these top-level includes.
util_intops::StrongVector< ElementIndex, Cost, CostAllocator > ElementCostVector
util_intops::StrongVector< SubsetIndex, SparseColumn > SparseColumnView
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.
util_intops::StrongVector< SubsetIndex, Cost, CostAllocator > SubsetCostVector
std::optional< int64_t > end