Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
knapsack_interface.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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// Interface to dedicated knapsack solvers covering multi-dimensional 0-1
15// knapsacks.
16// Current solvers handle only integer coefficients so a scaling phase is
17// performed before solving the problem.
18// TODO(user): handle timeouts, compute row and column statuses.
19
20#include <cstdint>
21#include <limits>
22#include <memory>
23#include <string>
24#include <vector>
25
26#include "absl/base/attributes.h"
27#include "absl/memory/memory.h"
31
32namespace operations_research {
33
35 public:
36 explicit KnapsackInterface(MPSolver* solver);
37 ~KnapsackInterface() override;
38
39 // ----- Solve -----
40 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
41
42 // ----- Model modifications and extraction -----
43 void Reset() override;
44 void SetOptimizationDirection(bool maximize) override;
45 void SetVariableBounds(int index, double lb, double ub) override;
46 void SetVariableInteger(int index, bool integer) override;
47 void SetConstraintBounds(int index, double lb, double ub) override;
48 void AddRowConstraint(MPConstraint* ct) override;
49 void AddVariable(MPVariable* var) override;
50 void SetCoefficient(MPConstraint* constraint, const MPVariable* variable,
51 double new_value, double old_value) override;
52 void ClearConstraint(MPConstraint* constraint) override;
53 void SetObjectiveCoefficient(const MPVariable* variable,
54 double coefficient) override;
55 void SetObjectiveOffset(double value) override;
56 void ClearObjective() override;
57
58 // ------ Query statistics on the solution and the solve ------
59 int64_t iterations() const override;
60 int64_t nodes() const override;
62 MPSolver::BasisStatus column_status(int variable_index) const override;
63
64 // ----- Misc -----
65 bool IsContinuous() const override;
66 bool IsLP() const override;
67 bool IsMIP() const override;
68
69 std::string SolverVersion() const override;
70 void* underlying_solver() override;
71
72 void ExtractNewVariables() override;
73 void ExtractNewConstraints() override;
74 void ExtractObjective() override;
75
76 void SetParameters(const MPSolverParameters& param) override;
77 void SetRelativeMipGap(double value) override;
78 void SetPrimalTolerance(double value) override;
79 void SetDualTolerance(double value) override;
80 void SetPresolveMode(int value) override;
81 void SetScalingMode(int value) override;
82 void SetLpAlgorithm(int value) override;
83
84 private:
85 bool IsKnapsackModel() const;
86 bool IsVariableFixedToValue(const MPVariable* var, double value) const;
87 bool IsVariableFixed(const MPVariable* var) const;
88 double GetVariableValueFromSolution(const MPVariable* var) const;
89 void NonIncrementalChange() { sync_status_ = MUST_RELOAD; }
90
91 std::unique_ptr<KnapsackSolver> knapsack_solver_;
92 std::vector<int64_t> profits_;
93 std::vector<std::vector<int64_t>> weights_;
94 std::vector<int64_t> capacities_;
95};
96
99
101
103 const MPSolverParameters& param) {
104 Reset();
105 if (!IsKnapsackModel()) {
106 LOG(ERROR) << "Model is not a knapsack model";
109 }
110 ExtractModel();
111 SetParameters(param);
113 // TODO(user): Refine Analysis of the model to choose better solvers.
114 KnapsackSolver::SolverType solver_type =
116 if (profits_.size() <= 64 && capacities_.size() == 1) {
118 }
119 knapsack_solver_ =
120 std::make_unique<KnapsackSolver>(solver_type, "linear_solver");
121 const double time_limit_seconds =
123 ? (static_cast<double>(solver_->time_limit()) / 1000.0)
124 : std::numeric_limits<double>::infinity();
125 knapsack_solver_->set_time_limit(time_limit_seconds);
126 knapsack_solver_->Init(profits_, weights_, capacities_);
127 knapsack_solver_->Solve();
128 result_status_ = knapsack_solver_->IsSolutionOptimal() ? MPSolver::OPTIMAL
130 objective_value_ = solver_->objective_->offset();
131 for (int var_id = 0; var_id < solver_->variables_.size(); ++var_id) {
132 MPVariable* const var = solver_->variables_[var_id];
133 const double value = GetVariableValueFromSolution(var);
134 objective_value_ += value * solver_->objective_->GetCoefficient(var);
135 var->set_solution_value(value);
136 }
137 return result_status_;
138}
139
142 profits_.clear();
143 weights_.clear();
144 capacities_.clear();
145 knapsack_solver_.reset(nullptr);
146}
147
149 NonIncrementalChange();
150}
151
152void KnapsackInterface::SetVariableBounds(int index, double lb, double ub) {
153 NonIncrementalChange();
154}
155
157 NonIncrementalChange();
158}
159
160void KnapsackInterface::SetConstraintBounds(int index, double lb, double ub) {
161 NonIncrementalChange();
162}
163
165 NonIncrementalChange();
166}
167
169 NonIncrementalChange();
170}
171
173 const MPVariable* const variable,
174 double new_value, double old_value) {
175 NonIncrementalChange();
176}
177
179 NonIncrementalChange();
180}
181
183 const MPVariable* const variable, double coefficient) {
184 NonIncrementalChange();
185}
186
188 NonIncrementalChange();
189}
190
191void KnapsackInterface::ClearObjective() { NonIncrementalChange(); }
192
193int64_t KnapsackInterface::iterations() const { return 0; }
194
196
198 int constraint_index) const {
199 // TODO(user): set properly.
200 return MPSolver::FREE;
201}
202
204 int variable_index) const {
205 // TODO(user): set properly.
206 return MPSolver::FREE;
207}
208
209bool KnapsackInterface::IsContinuous() const { return false; }
210
211bool KnapsackInterface::IsLP() const { return false; }
212
213bool KnapsackInterface::IsMIP() const { return true; }
214
216 return "knapsack_solver-0.0";
217}
218
219void* KnapsackInterface::underlying_solver() { return knapsack_solver_.get(); }
220
222 DCHECK_EQ(0, last_variable_index_);
223 for (int column = 0; column < solver_->variables_.size(); ++column) {
225 }
226}
227
229 DCHECK_EQ(0, last_constraint_index_);
230 weights_.resize(solver_->constraints_.size());
231 capacities_.resize(solver_->constraints_.size(),
232 std::numeric_limits<int64_t>::max());
233 for (int row = 0; row < solver_->constraints_.size(); ++row) {
234 MPConstraint* const ct = solver_->constraints_[row];
235 double fixed_usage = 0.0;
237 std::vector<double> coefficients(solver_->variables_.size() + 1, 0.0);
238 for (const auto& entry : ct->coefficients_) {
239 const int var_index = entry.first->index();
241 if (IsVariableFixedToValue(entry.first, 1.0)) {
242 fixed_usage += entry.second;
243 } else if (!IsVariableFixedToValue(entry.first, 0.0)) {
244 coefficients[var_index] = entry.second;
245 }
246 }
247 // Removing the contribution of variables fixed to 1 from the constraint
248 // upper bound. All fixed variables have a zero coefficient.
249 const double capacity = ct->ub() - fixed_usage;
250 // Adding upper bound to the coefficients to scale.
251 coefficients[solver_->variables_.size()] = capacity;
252 double relative_error = 0.0;
253 double scaling_factor = 0.0;
255 std::numeric_limits<int64_t>::max(),
256 &scaling_factor, &relative_error);
257 const int64_t gcd =
259 std::vector<int64_t> scaled_coefficients(solver_->variables_.size(), 0);
260 for (const auto& entry : ct->coefficients_) {
261 if (!IsVariableFixed(entry.first)) {
262 scaled_coefficients[entry.first->index()] =
263 static_cast<int64_t>(round(scaling_factor * entry.second)) / gcd;
264 }
265 }
266 weights_[row].swap(scaled_coefficients);
267 capacities_[row] =
268 static_cast<int64_t>(round(scaling_factor * capacity)) / gcd;
269 }
270}
271
273 std::vector<double> coefficients(solver_->variables_.size(), 0.0);
274 for (const auto& entry : solver_->objective_->coefficients_) {
275 // Whether fixed to 0 or 1, fixed variables are removed from the
276 // profit function, which for the current implementation means their
277 // coefficient is set to 0.
278 if (!IsVariableFixed(entry.first)) {
279 coefficients[entry.first->index()] = entry.second;
280 }
281 }
282 double relative_error = 0.0;
283 double scaling_factor = 0.0;
285 std::numeric_limits<int64_t>::max(),
286 &scaling_factor, &relative_error);
287 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
288 std::vector<int64_t> scaled_coefficients(solver_->variables_.size(), 0);
289 for (const auto& entry : solver_->objective_->coefficients_) {
290 scaled_coefficients[entry.first->index()] =
291 static_cast<int64_t>(round(scaling_factor * entry.second)) / gcd;
292 }
293 profits_.swap(scaled_coefficients);
294}
295
299
301
303
305
307
309
311
312bool KnapsackInterface::IsKnapsackModel() const {
313 // Check variables are boolean.
314 for (int column = 0; column < solver_->variables_.size(); ++column) {
315 MPVariable* const var = solver_->variables_[column];
316 if (var->lb() <= -1.0 || var->ub() >= 2.0 || !var->integer()) {
317 return false;
318 }
319 }
320 // Check objective coefficients are positive.
321 for (const auto& entry : solver_->objective_->coefficients_) {
322 if (entry.second < 0) {
323 return false;
324 }
325 }
326 // Check constraints are knapsack constraints.
327 for (int row = 0; row < solver_->constraints_.size(); ++row) {
328 MPConstraint* const ct = solver_->constraints_[row];
329 if (ct->lb() > 0.0) {
330 return false;
331 }
332 for (const auto& entry : ct->coefficients_) {
333 if (entry.second < 0) {
334 return false;
335 }
336 }
337 }
338 // Check we are maximizing.
339 return maximize_;
340}
341
342bool KnapsackInterface::IsVariableFixedToValue(const MPVariable* var,
343 double value) const {
344 const double lb_round_up = ceil(var->lb());
345 return value == lb_round_up && floor(var->ub()) == lb_round_up;
346}
347
348bool KnapsackInterface::IsVariableFixed(const MPVariable* var) const {
349 return IsVariableFixedToValue(var, 0.0) || IsVariableFixedToValue(var, 1.0);
350}
351
352double KnapsackInterface::GetVariableValueFromSolution(
353 const MPVariable* var) const {
354 return !IsVariableFixedToValue(var, 0.0) &&
355 (knapsack_solver_->BestSolutionContains(var->index()) ||
356 IsVariableFixedToValue(var, 1.0))
357 ? 1.0
358 : 0.0;
359}
360
361// Register Knapsack solver in the global linear solver factory.
363 return new KnapsackInterface(solver);
364}
365
366} // namespace operations_research
void SetDualTolerance(double value) override
void SetVariableInteger(int index, bool integer) override
Modifies integrality of an extracted variable.
void ClearConstraint(MPConstraint *constraint) override
Clears a constraint from all its terms.
void ExtractNewVariables() override
Extracts the variables that have not been extracted yet.
int64_t iterations() const override
---— Query statistics on the solution and the solve ---—
std::string SolverVersion() const override
Returns a string describing the underlying solver and its version.
void Reset() override
--— Model modifications and extraction --—
void ExtractNewConstraints() override
Extracts the constraints that have not been extracted yet.
void SetVariableBounds(int index, double lb, double ub) override
Modifies bounds of an extracted variable.
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
Changes a coefficient in a constraint.
void AddVariable(MPVariable *var) override
Add a variable.
void SetRelativeMipGap(double value) override
Sets each parameter in the underlying solver.
void AddRowConstraint(MPConstraint *ct) override
Adds a linear constraint.
void * underlying_solver() override
Returns the underlying solver.
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
Changes a coefficient in the linear objective.
MPSolver::BasisStatus row_status(int constraint_index) const override
Returns the basis status of a row.
void SetObjectiveOffset(double value) override
Changes the constant term in the linear objective.
void SetConstraintBounds(int index, double lb, double ub) override
Modify bounds of an extracted variable.
void ClearObjective() override
Clears the objective from all its terms.
void SetPrimalTolerance(double value) override
void SetOptimizationDirection(bool maximize) override
Sets the optimization direction (min/max).
MPSolver::BasisStatus column_status(int variable_index) const override
Returns the basis status of a constraint.
void ExtractObjective() override
Extracts the objective.
void SetParameters(const MPSolverParameters &param) override
Sets all parameters in the underlying solver.
void SetScalingMode(int value) override
Sets the scaling mode.
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
--— Solve --—
bool IsContinuous() const override
--— Misc --—
bool IsMIP() const override
Returns true if the problem is discrete and linear.
bool IsLP() const override
Returns true if the problem is continuous and linear.
void set_variable_as_extracted(int var_index, bool extracted)
friend class MPConstraint
To access the maximize_ bool and the MPSolver.
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
Resets the extraction information.
int last_variable_index_
Index in MPSolver::constraints_ of last variable extracted.
int last_constraint_index_
Index in MPSolver::variables_ of last constraint extracted.
bool variable_is_extracted(int var_index) const
static constexpr int64_t kUnknownNumberOfNodes
void ExtractModel()
Extracts model stored in MPSolver.
double objective_value_
The value of the objective function.
bool maximize_
Optimization direction.
void SetCommonParameters(const MPSolverParameters &param)
Sets parameters common to LP and MIP in the underlying solver.
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
@ MODEL_INVALID
the model is trivially invalid (NaN coefficients, etc).
@ FEASIBLE
feasible, or stopped by limit.
The class for variables of a Mathematical Programming (MP) model.
const Constraint * ct
int64_t value
IntVar * var
absl::Span< const double > coefficients
int constraint_index
int index
RowIndex row
Definition markowitz.cc:186
In SWIG mode, we don't want anything besides these top-level includes.
MPSolverInterface * BuildKnapsackInterface(MPSolver *const solver)
Register Knapsack solver in the global linear solver factory.
double GetBestScalingOfDoublesToInt64(absl::Span< const double > input, absl::Span< const double > lb, absl::Span< const double > ub, int64_t max_absolute_sum)
Definition fp_utils.cc:186
int64_t ComputeGcdOfRoundedDoubles(absl::Span< const double > x, double scaling_factor)
Definition fp_utils.cc:209
int column
int64_t coefficient
int var_index
Definition search.cc:3268