Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
set_cover_lagrangian.h
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_ALGORITHMS_SET_COVER_LAGRANGIAN_H_
15#define OR_TOOLS_ALGORITHMS_SET_COVER_LAGRANGIAN_H_
16
17#include <memory>
18#include <new>
19#include <tuple>
20#include <vector>
21
25
26namespace operations_research {
27
28// The SetCoverLagrangian class implements the Lagrangian relaxation of the set
29// cover problem.
30
31// In the following, we refer to the following articles:
32// [1] Caprara, Alberto, Matteo Fischetti, and Paolo Toth. 1999. “A Heuristic
33// Method for the Set Covering Problem.” Operations Research 47 (5): 730–43.
34// https://www.jstor.org/stable/223097
35// [2] Fisher, Marshall L. 1981. “The Lagrangian Relaxation Method for Solving
36// Integer Programming Problems.” Management Science 27 (1): 1–18.
37// https://www.jstor.org/stable/2631139
38// [3] Held, M., Karp, R.M. The traveling-salesman problem and minimum spanning
39// trees: Part II. Mathematical Programming 1, 6–25 (1971).
40// https://link.springer.com/article/10.1007/BF01584070
41// [4] Williamson, David P. 2002. “The Primal-Dual Method for Approximation
42// Algorithms.” Mathematical Programming, 91 (3): 447–78.
43// https://link.springer.com/article/10.1007/s101070100262
44
46 public:
47 explicit SetCoverLagrangian(SetCoverInvariant* inv, int num_threads = 1)
48 : inv_(inv),
49 model_(*inv->model()),
50 num_threads_(num_threads),
51 thread_pool_(new ThreadPool(num_threads)) {
52 thread_pool_->StartWorkers();
53 }
54
55 // Returns true if a solution was found.
56 // TODO(user): Add time-outs and exit with a partial solution. This seems
57 // unlikely, though.
59
60 // Computes the next partial solution considering only the subsets whose
61 // indices are in focus.
62 bool NextSolution(const std::vector<SubsetIndex>& focus);
63
64 // Initializes the multipliers vector (u) based on the cost per subset.
66
67 // Computes the Lagrangian (row-)cost vector.
68 // For a subset j, c_j(u) = c_j - sum_{i \in I_j} u_i.
69 // I_j denotes the indices of elements present in subset j.
71 const SubsetCostVector& costs,
72 const ElementCostVector& multipliers) const;
73
74 // Same as above, but parallelized, using the number of threads specified in
75 // the constructor.
77 const SubsetCostVector& costs,
78 const ElementCostVector& multipliers) const;
79
80 // Computes the subgradient (column-)cost vector.
81 // For all element indices i, s_i(u) = 1 - sum_{j \in J_i} x_j(u),
82 // where J_i denotes the set of indices of subsets j covering element i.
84 const SubsetCostVector& reduced_costs) const;
85
86 // Same as above, but parallelized, using the number of threads specified in
87 // the constructor.
89 const SubsetCostVector& reduced_costs) const;
90
91 // Computes the value of the Lagrangian L(u).
92 // L(u) = min \sum_{j \in N} c_j(u) x_j + \sum{i \in M} u_i.
93 // If c_j(u) < 0: x_j(u) = 1, if c_j(u) > 0: x_j(u) = 0,
94 // otherwise x_j(u) is unbound, it can take any value in {0, 1}.
95 Cost ComputeLagrangianValue(const SubsetCostVector& reduced_costs,
96 const ElementCostVector& multipliers) const;
97
98 // Same as above, but parallelized, using the number of threads specified in
99 // the constructor.
101 const SubsetCostVector& reduced_costs,
102 const ElementCostVector& multipliers) const;
103
104 // Updates the multipliers vector (u) with the given step size.
105 // Following [3], each of the coordinates is defined as: u^{k+1}_i =
106 // max(u^k_i + lambda_k * (UB - L(u^k)) / |s^k(u)|^2 * s^k_i(u), 0).
107 // lambda_k is step_size in the function signature below. UB is upper_bound.
108 void UpdateMultipliers(double step_size, Cost lagrangian_value,
109 Cost upper_bound,
110 const SubsetCostVector& reduced_costs,
111 ElementCostVector* multipliers) const;
112
113 // Same as above, but parallelized, using the number of threads specified in
114 // the constructor.
115 void ParallelUpdateMultipliers(double step_size, Cost lagrangian_value,
116 Cost upper_bound,
117 const SubsetCostVector& reduced_costs,
118 ElementCostVector* multipliers) const;
119
120 // Computes the gap between the current solution and the optimal solution.
121 // This is the sum of the multipliers for the elements that are not covered
122 // by the current solution.
123 Cost ComputeGap(const SubsetCostVector& reduced_costs,
125 const ElementCostVector& multipliers) const;
126
127 // Performs the three-phase algorithm.
128 void ThreePhase(Cost upper_bound);
129
130 // Computes a lower bound on the optimal cost.
131 // The returned value is the lower bound, the reduced costs, and the
132 // multipliers.
133 std::tuple<Cost, SubsetCostVector, ElementCostVector> ComputeLowerBound(
134 const SubsetCostVector& costs, Cost upper_bound);
135
136 private:
137 // The invariant on which the algorithm will run.
138 SetCoverInvariant* inv_;
139
140 // The model on which the invariant is defined.
141 const SetCoverModel& model_;
142
143 // The number of threads to use for parallelization.
144 int num_threads_;
145
146 // The thread pool used for parallelization.
147 std::unique_ptr<ThreadPool> thread_pool_;
148
149 // Total (scalar) Lagrangian cost.
150 Cost lagrangian_;
151
152 // Lagrangian cost vector, per subset.
153 SubsetCostVector lagrangians_;
154
155 // Computes the delta vector.
156 // This is definition (9) in [1].
157 SubsetCostVector ComputeDelta(const SubsetCostVector& reduced_costs,
158 const ElementCostVector& multipliers) const;
159};
160
161} // namespace operations_research
162
163#endif // OR_TOOLS_ALGORITHMS_SET_COVER_LAGRANGIAN_H_
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 ThreePhase(Cost upper_bound)
Performs the three-phase algorithm.
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.
bool NextSolution(const std::vector< SubsetIndex > &focus)
Cost ParallelComputeLagrangianValue(const SubsetCostVector &reduced_costs, const ElementCostVector &multipliers) const
SetCoverLagrangian(SetCoverInvariant *inv, int num_threads=1)
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
Main class for describing a weighted set-covering problem.
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< SubsetIndex, bool > SubsetBoolVector
double Cost
Basic non-strict type for cost. The speed penalty for using double is ~2%.