22#include "absl/log/check.h"
23#include "absl/synchronization/blocking_counter.h"
49 std::numeric_limits<Cost>::infinity());
52 for (
const SubsetIndex subset : model_.SubsetRange()) {
53 marginal_costs[subset] =
54 model_.subset_costs()[subset] / model_.columns()[subset].size();
57 for (
const ElementIndex element : model_.ElementRange()) {
59 Cost min_marginal_cost = std::numeric_limits<Cost>::infinity();
63 for (
const SubsetIndex subset : rows[element]) {
64 min_marginal_cost = std::min(min_marginal_cost, marginal_costs[subset]);
66 multipliers[element] = min_marginal_cost;
78 for (
const ColumnEntryIndex pos : column.
index_range()) {
79 result += dual[column[pos]];
90void FillReducedCostsSlice(SubsetIndex slice_start, SubsetIndex slice_end,
95 for (SubsetIndex subset = slice_start; subset < slice_end; ++subset) {
96 (*reduced_costs)[subset] =
102 return 1 + (size - 1) / num_threads;
109 const SubsetIndex num_subsets(model_.num_subsets());
114 const SubsetIndex block_size(BlockSize(num_subsets.value(), num_threads_));
115 absl::BlockingCounter num_threads_running(num_threads_);
116 SubsetIndex slice_start(0);
117 for (
int thread_index = 0; thread_index < num_threads_; ++thread_index) {
118 const SubsetIndex slice_end =
119 std::min(slice_start + block_size, num_subsets);
120 thread_pool_->Schedule([&num_threads_running, slice_start, slice_end,
121 &costs, &multipliers, &columns, &reduced_costs]() {
122 FillReducedCostsSlice(slice_start, slice_end, costs, multipliers, columns,
124 num_threads_running.DecrementCount();
126 slice_start = slice_end;
128 num_threads_running.Wait();
129 return reduced_costs;
141 FillReducedCostsSlice(SubsetIndex(0), SubsetIndex(reduced_costs.size()),
142 costs, multipliers, columns, &reduced_costs);
143 return reduced_costs;
151void FillSubgradientSlice(SubsetIndex slice_start, SubsetIndex slice_end,
155 for (SubsetIndex subset(slice_start); subset < slice_end; ++subset) {
156 if (reduced_costs[subset] < 0.0) {
157 for (
const ElementIndex element : columns[subset]) {
158 (*subgradient)[element] -= 1.0;
174 FillSubgradientSlice(SubsetIndex(0), SubsetIndex(reduced_costs.size()),
175 model_.columns(), reduced_costs, &subgradient);
181 const SubsetIndex num_subsets(model_.num_subsets());
188 std::vector<ElementCostVector> subgradients(
190 absl::BlockingCounter num_threads_running(num_threads_);
191 const SubsetIndex block_size(BlockSize(num_subsets.value(), num_threads_));
192 SubsetIndex slice_start(0);
193 for (
int thread_index = 0; thread_index < num_threads_; ++thread_index) {
194 const SubsetIndex slice_end =
195 std::min(slice_start + block_size, num_subsets);
196 thread_pool_->Schedule([&num_threads_running, slice_start, slice_end,
197 &reduced_costs, &columns, &subgradients,
199 FillSubgradientSlice(slice_start, slice_end, columns, reduced_costs,
200 &subgradients[thread_index]);
201 num_threads_running.DecrementCount();
203 slice_start = slice_end;
205 num_threads_running.Wait();
206 for (
int thread_index = 0; thread_index < num_threads_; ++thread_index) {
207 for (
const ElementIndex element : model_.ElementRange()) {
208 subgradient[element] += subgradients[thread_index][element];
221void FillLagrangianValueSlice(SubsetIndex slice_start, SubsetIndex slice_end,
223 Cost* lagrangian_value) {
227 for (SubsetIndex subset(slice_start); subset < slice_end; ++subset) {
228 if (reduced_costs[subset] < 0.0) {
229 *lagrangian_value += reduced_costs[subset];
246 Cost lagrangian_value = 0.0;
248 for (
const Cost u : multipliers) {
249 lagrangian_value += u;
251 FillLagrangianValueSlice(SubsetIndex(0), SubsetIndex(reduced_costs.size()),
252 reduced_costs, &lagrangian_value);
253 return lagrangian_value;
259 Cost lagrangian_value = 0.0;
261 for (
const Cost u : multipliers) {
262 lagrangian_value += u;
264 std::vector<Cost> lagrangian_values(num_threads_, 0.0);
265 absl::BlockingCounter num_threads_running(num_threads_);
266 const SubsetIndex block_size(BlockSize(model_.num_subsets(), num_threads_));
267 const SubsetIndex num_subsets(model_.num_subsets());
268 SubsetIndex slice_start(0);
269 for (
int thread_index = 0; thread_index < num_threads_; ++thread_index) {
270 const SubsetIndex slice_end =
271 std::min(slice_start + block_size, num_subsets);
272 thread_pool_->Schedule([&num_threads_running, slice_start, block_size,
273 num_subsets, thread_index, &reduced_costs,
274 &lagrangian_values]() {
275 const SubsetIndex slice_end =
276 std::min(slice_start + block_size, num_subsets);
277 FillLagrangianValueSlice(slice_start, slice_end, reduced_costs,
278 &lagrangian_values[thread_index]);
279 num_threads_running.DecrementCount();
281 slice_start = slice_end;
283 num_threads_running.Wait();
284 for (
const Cost l : lagrangian_values) {
285 lagrangian_value += l;
287 return lagrangian_value;
305 double step_size,
Cost lagrangian_value,
Cost upper_bound,
309 DCHECK_GT(step_size, 0);
312 Cost subgradient_square_norm = 0.0;
313 for (
const Cost x : subgradient) {
314 subgradient_square_norm += x * x;
318 step_size * (upper_bound - lagrangian_value) / subgradient_square_norm;
319 for (
const ElementIndex element : model_.ElementRange()) {
322 (*multipliers)[element] = std::clamp(
323 (*multipliers)[element] + factor * subgradient[element], 0.0, 1e6);
328 double step_size,
Cost lagrangian_value,
Cost upper_bound,
332 DCHECK_GT(step_size, 0);
336 Cost subgradient_square_norm = 0.0;
337 for (
const Cost x : subgradient) {
338 subgradient_square_norm += x * x;
342 step_size * (upper_bound - lagrangian_value) / subgradient_square_norm;
343 for (
const ElementIndex element : model_.ElementRange()) {
345 const Cost kRoof = 1e6;
346 (*multipliers)[element] = std::clamp(
347 (*multipliers)[element] + factor * subgradient[element], 0.0, kRoof);
356 for (
const SubsetIndex subset : model_.SubsetRange()) {
357 if (
solution[subset] && reduced_costs[subset] > 0.0) {
358 gap += reduced_costs[subset];
359 }
else if (!
solution[subset] && reduced_costs[subset] < 0.0) {
361 gap -= reduced_costs[subset];
365 for (
const ElementIndex element : model_.ElementRange()) {
366 gap += (coverage[element] - 1) * multipliers[element];
379 for (
const SubsetIndex subset : model_.
SubsetRange()) {
380 delta[subset] = std::max(reduced_costs[subset], 0.0);
381 for (
const ElementIndex element : columns[subset]) {
382 const double size = coverage[element];
383 delta[subset] += multipliers[element] * (size - 1.0) / size;
398 StepSizer(
int period,
double step_size)
399 : period_(period), iter_to_check_(period), step_size_(step_size) {
402 double UpdateStepSize(
int iter,
Cost lower_bound) {
403 min_lb_ = std::min(min_lb_, lower_bound);
404 max_lb_ = std::max(max_lb_, lower_bound);
405 if (iter == iter_to_check_) {
406 iter_to_check_ += period_;
411 max_lb_ == 0.0 ? 0.0 : (max_lb_ - min_lb_) / std::abs(max_lb_);
412 DCHECK_GE(lb_gap, 0.0);
415 }
else if (lb_gap <= 0.001) {
418 step_size_ = std::clamp(step_size_, 1e-6, 10.0);
426 min_lb_ = std::numeric_limits<Cost>::infinity();
427 max_lb_ = -std::numeric_limits<Cost>::infinity();
441 explicit Stopper(
int period)
443 iter_to_check_(period),
444 lower_bound_(std::numeric_limits<
Cost>::min()) {}
445 bool DecideWhetherToStop(
int iter,
Cost lb) {
446 DCHECK_GE(lb, lower_bound_);
447 if (iter == iter_to_check_) {
448 iter_to_check_ += period_;
449 const Cost delta = lb - lower_bound_;
450 const Cost relative_delta = delta / lb;
451 DCHECK_GE(delta, 0.0);
452 DCHECK_GE(relative_delta, 0.0);
454 return relative_delta < 0.001 && delta < 1;
469template <
typename Priority,
typename Index,
bool IsMaxHeap>
472 explicit TopKHeap(
int max_size) : heap_(), max_size_(max_size) {}
473 void Clear() { heap_.Clear(); }
474 void Add(Priority priority, Index index) {
475 if (heap_.Size() < max_size_) {
476 heap_.Add(priority, index);
477 }
else if (heap_.HasPriority(priority, heap_.TopPriority())) {
478 heap_.ReplaceTop(priority, index);
483 AdjustableKAryHeap<Priority,
Index, 2, !IsMaxHeap> heap_;
489std::tuple<Cost, SubsetCostVector, ElementCostVector>
492 Cost lower_bound = 0.0;
494 double step_size = 0.1;
495 StepSizer step_sizer(20, step_size);
496 Stopper stopper(100);
502 for (
int iter = 0; iter < 1000; ++iter) {
504 const Cost lagrangian_value =
507 reduced_costs, &multipliers);
508 lower_bound = std::max(lower_bound, lagrangian_value);
516 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< SubsetIndex > SubsetRange() const
Access to the ranges of subsets and elements.
const SparseColumnView & columns() const
Column view of the set covering problem.
BaseInt num_subsets() const
StrongIntRange< IntType > index_range() const
Fractional ScalarProduct(const DenseRowOrColumn1 &u, const DenseRowOrColumn2 &v)
In SWIG mode, we don't want anything besides these top-level includes.
util_intops::StrongVector< SubsetIndex, Cost > SubsetCostVector
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
util_intops::StrongVector< ElementIndex, Cost > ElementCostVector
util_intops::StrongVector< ElementIndex, BaseInt > ElementToIntVector
util_intops::StrongVector< SubsetIndex, bool > SubsetBoolVector
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< ColumnEntryIndex, ElementIndex > SparseColumn
util_intops::StrongVector< ElementIndex, SparseRow > SparseRowView