Google OR-Tools v9.15
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-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// 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"
30
31namespace operations_research {
32
34 public:
35 explicit KnapsackInterface(MPSolver* solver);
36 ~KnapsackInterface() override;
37
38 // ----- Solve -----
39 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
40
41 // ----- Model modifications and extraction -----
42 void Reset() override;
43 void SetOptimizationDirection(bool maximize) override;
44 void SetVariableBounds(int index, double lb, double ub) override;
45 void SetVariableInteger(int index, bool integer) override;
46 void SetConstraintBounds(int index, double lb, double ub) override;
47 void AddRowConstraint(MPConstraint* ct) override;
48 void AddVariable(MPVariable* var) override;
49 void SetCoefficient(MPConstraint* constraint, const MPVariable* variable,
50 double new_value, double old_value) override;
51 void ClearConstraint(MPConstraint* constraint) override;
52 void SetObjectiveCoefficient(const MPVariable* variable,
53 double coefficient) override;
54 void SetObjectiveOffset(double value) override;
55 void ClearObjective() override;
56
57 // ------ Query statistics on the solution and the solve ------
58 int64_t iterations() const override;
59 int64_t nodes() const override;
60 MPSolver::BasisStatus row_status(int constraint_index) const override;
61 MPSolver::BasisStatus column_status(int variable_index) const override;
62
63 // ----- Misc -----
64 bool IsContinuous() const override;
65 bool IsLP() const override;
66 bool IsMIP() const override;
67
68 std::string SolverVersion() const override;
69 void* underlying_solver() override;
70
71 void ExtractNewVariables() override;
72 void ExtractNewConstraints() override;
73 void ExtractObjective() override;
74
75 void SetParameters(const MPSolverParameters& param) override;
76 void SetRelativeMipGap(double value) override;
77 void SetPrimalTolerance(double value) override;
78 void SetDualTolerance(double value) override;
79 void SetPresolveMode(int value) override;
80 void SetScalingMode(int value) override;
81 void SetLpAlgorithm(int value) override;
82
83 private:
84 bool IsKnapsackModel() const;
85 bool IsVariableFixedToValue(const MPVariable* var, double value) const;
86 bool IsVariableFixed(const MPVariable* var) const;
87 double GetVariableValueFromSolution(const MPVariable* var) const;
88 void NonIncrementalChange() { sync_status_ = MUST_RELOAD; }
89
90 std::unique_ptr<KnapsackSolver> knapsack_solver_;
91 std::vector<int64_t> profits_;
92 std::vector<std::vector<int64_t>> weights_;
93 std::vector<int64_t> capacities_;
94};
95
98
100
102 const MPSolverParameters& param) {
103 Reset();
104 if (!IsKnapsackModel()) {
105 LOG(ERROR) << "Model is not a knapsack model";
108 }
109 ExtractModel();
110 SetParameters(param);
112 // TODO(user): Refine Analysis of the model to choose better solvers.
113 KnapsackSolver::SolverType solver_type =
115 if (profits_.size() <= 64 && capacities_.size() == 1) {
117 }
118 knapsack_solver_ =
119 std::make_unique<KnapsackSolver>(solver_type, "linear_solver");
120 const double time_limit_seconds =
121 solver_->time_limit()
122 ? (static_cast<double>(solver_->time_limit()) / 1000.0)
123 : std::numeric_limits<double>::infinity();
124 knapsack_solver_->set_time_limit(time_limit_seconds);
125 knapsack_solver_->Init(profits_, weights_, capacities_);
126 knapsack_solver_->Solve();
127 result_status_ = knapsack_solver_->IsSolutionOptimal() ? MPSolver::OPTIMAL
129 objective_value_ = solver_->objective_->offset();
130 for (int var_id = 0; var_id < solver_->variables_.size(); ++var_id) {
131 MPVariable* const var = solver_->variables_[var_id];
132 const double value = GetVariableValueFromSolution(var);
133 objective_value_ += value * solver_->objective_->GetCoefficient(var);
134 var->set_solution_value(value);
135 }
136 return result_status_;
137}
138
141 profits_.clear();
142 weights_.clear();
143 capacities_.clear();
144 knapsack_solver_.reset(nullptr);
145}
146
148 NonIncrementalChange();
149}
150
151void KnapsackInterface::SetVariableBounds(int index, double lb, double ub) {
152 NonIncrementalChange();
153}
154
155void KnapsackInterface::SetVariableInteger(int index, bool integer) {
156 NonIncrementalChange();
157}
158
159void KnapsackInterface::SetConstraintBounds(int index, double lb, double ub) {
160 NonIncrementalChange();
161}
162
164 NonIncrementalChange();
165}
166
168 NonIncrementalChange();
169}
170
172 const MPVariable* const variable,
173 double new_value, double old_value) {
174 NonIncrementalChange();
175}
176
178 NonIncrementalChange();
179}
180
182 const MPVariable* const variable, double coefficient) {
183 NonIncrementalChange();
184}
185
187 NonIncrementalChange();
188}
189
190void KnapsackInterface::ClearObjective() { NonIncrementalChange(); }
191
192int64_t KnapsackInterface::iterations() const { return 0; }
193
195
197 int constraint_index) const {
198 // TODO(user): set properly.
199 return MPSolver::FREE;
200}
201
203 int variable_index) const {
204 // TODO(user): set properly.
205 return MPSolver::FREE;
206}
207
208bool KnapsackInterface::IsContinuous() const { return false; }
209
210bool KnapsackInterface::IsLP() const { return false; }
211
212bool KnapsackInterface::IsMIP() const { return true; }
213
215 return "knapsack_solver-0.0";
216}
217
218void* KnapsackInterface::underlying_solver() { return knapsack_solver_.get(); }
219
221 DCHECK_EQ(0, last_variable_index_);
222 for (int column = 0; column < solver_->variables_.size(); ++column) {
223 set_variable_as_extracted(column, true);
224 }
225}
226
228 DCHECK_EQ(0, last_constraint_index_);
229 weights_.resize(solver_->constraints_.size());
230 capacities_.resize(solver_->constraints_.size(),
231 std::numeric_limits<int64_t>::max());
232 for (int row = 0; row < solver_->constraints_.size(); ++row) {
233 MPConstraint* const ct = solver_->constraints_[row];
234 double fixed_usage = 0.0;
236 std::vector<double> coefficients(solver_->variables_.size() + 1, 0.0);
237 for (const auto& entry : ct->coefficients_) {
238 const int var_index = entry.first->index();
239 DCHECK(variable_is_extracted(var_index));
240 if (IsVariableFixedToValue(entry.first, 1.0)) {
241 fixed_usage += entry.second;
242 } else if (!IsVariableFixedToValue(entry.first, 0.0)) {
243 coefficients[var_index] = entry.second;
244 }
245 }
246 // Removing the contribution of variables fixed to 1 from the constraint
247 // upper bound. All fixed variables have a zero coefficient.
248 const double capacity = ct->ub() - fixed_usage;
249 // Adding upper bound to the coefficients to scale.
250 coefficients[solver_->variables_.size()] = capacity;
251 double relative_error = 0.0;
252 double scaling_factor = 0.0;
254 std::numeric_limits<int64_t>::max(),
255 &scaling_factor, &relative_error);
256 const int64_t gcd =
257 ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
258 std::vector<int64_t> scaled_coefficients(solver_->variables_.size(), 0);
259 for (const auto& entry : ct->coefficients_) {
260 if (!IsVariableFixed(entry.first)) {
261 scaled_coefficients[entry.first->index()] =
262 static_cast<int64_t>(round(scaling_factor * entry.second)) / gcd;
263 }
264 }
265 weights_[row].swap(scaled_coefficients);
266 capacities_[row] =
267 static_cast<int64_t>(round(scaling_factor * capacity)) / gcd;
268 }
269}
270
272 std::vector<double> coefficients(solver_->variables_.size(), 0.0);
273 for (const auto& entry : solver_->objective_->coefficients_) {
274 // Whether fixed to 0 or 1, fixed variables are removed from the
275 // profit function, which for the current implementation means their
276 // coefficient is set to 0.
277 if (!IsVariableFixed(entry.first)) {
278 coefficients[entry.first->index()] = entry.second;
279 }
280 }
281 double relative_error = 0.0;
282 double scaling_factor = 0.0;
284 std::numeric_limits<int64_t>::max(),
285 &scaling_factor, &relative_error);
286 const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
287 std::vector<int64_t> scaled_coefficients(solver_->variables_.size(), 0);
288 for (const auto& entry : solver_->objective_->coefficients_) {
289 scaled_coefficients[entry.first->index()] =
290 static_cast<int64_t>(round(scaling_factor * entry.second)) / gcd;
291 }
292 profits_.swap(scaled_coefficients);
293}
294
298
300
302
304
306
308
310
311bool KnapsackInterface::IsKnapsackModel() const {
312 // Check variables are boolean.
313 for (int column = 0; column < solver_->variables_.size(); ++column) {
314 MPVariable* const var = solver_->variables_[column];
315 if (var->lb() <= -1.0 || var->ub() >= 2.0 || !var->integer()) {
316 return false;
317 }
318 }
319 // Check objective coefficients are positive.
320 for (const auto& entry : solver_->objective_->coefficients_) {
321 if (entry.second < 0) {
322 return false;
323 }
324 }
325 // Check constraints are knapsack constraints.
326 for (int row = 0; row < solver_->constraints_.size(); ++row) {
327 MPConstraint* const ct = solver_->constraints_[row];
328 if (ct->lb() > 0.0) {
329 return false;
330 }
331 for (const auto& entry : ct->coefficients_) {
332 if (entry.second < 0) {
333 return false;
334 }
335 }
336 }
337 // Check we are maximizing.
338 return maximize_;
339}
340
341bool KnapsackInterface::IsVariableFixedToValue(const MPVariable* var,
342 double value) const {
343 const double lb_round_up = ceil(var->lb());
344 return value == lb_round_up && floor(var->ub()) == lb_round_up;
345}
346
347bool KnapsackInterface::IsVariableFixed(const MPVariable* var) const {
348 return IsVariableFixedToValue(var, 0.0) || IsVariableFixedToValue(var, 1.0);
349}
350
351double KnapsackInterface::GetVariableValueFromSolution(
352 const MPVariable* var) const {
353 return !IsVariableFixedToValue(var, 0.0) &&
354 (knapsack_solver_->BestSolutionContains(var->index()) ||
355 IsVariableFixedToValue(var, 1.0))
356 ? 1.0
357 : 0.0;
358}
359
360namespace {
361
362// See MpSolverInterfaceFactoryRepository for details.
363const void* const kRegisterKnapsack ABSL_ATTRIBUTE_UNUSED = [] {
365 [](MPSolver* const solver) { return new KnapsackInterface(solver); },
367 return nullptr;
368}();
369
370} // namespace
371
372} // namespace operations_research
void SetDualTolerance(double value) override
void SetVariableInteger(int index, bool integer) override
void ClearConstraint(MPConstraint *constraint) override
std::string SolverVersion() const override
void SetVariableBounds(int index, double lb, double ub) override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
void AddVariable(MPVariable *var) override
void SetRelativeMipGap(double value) override
void AddRowConstraint(MPConstraint *ct) override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
MPSolver::BasisStatus row_status(int constraint_index) const override
void SetObjectiveOffset(double value) override
void SetConstraintBounds(int index, double lb, double ub) override
void SetPrimalTolerance(double value) override
void SetOptimizationDirection(bool maximize) override
MPSolver::BasisStatus column_status(int variable_index) const override
void SetParameters(const MPSolverParameters &param) override
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
SolverType
Enum controlling which underlying algorithm is used.
double ub() const
Returns the upper bound.
static MPSolverInterfaceFactoryRepository * GetInstance()
void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})
void set_variable_as_extracted(int var_index, bool extracted)
void set_constraint_as_extracted(int ct_index, bool extracted)
bool variable_is_extracted(int var_index) const
static constexpr int64_t kUnknownNumberOfNodes
void SetCommonParameters(const MPSolverParameters &param)
@ 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.
bool integer() const
Returns the integrality requirement of the variable.
double lb() const
Returns the lower bound.
double ub() const
Returns the upper bound.
void set_solution_value(double value)
OR-Tools root namespace.
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:184
int64_t ComputeGcdOfRoundedDoubles(absl::Span< const double > x, double scaling_factor)
Definition fp_utils.cc:207