OR-Tools  9.6
knapsack_interface.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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"
30 #include "ortools/util/fp_utils.h"
31 
32 namespace 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* const ct) override;
49  void AddVariable(MPVariable* const var) override;
50  void SetCoefficient(MPConstraint* const constraint,
51  const MPVariable* const variable, double new_value,
52  double old_value) override;
53  void ClearConstraint(MPConstraint* const constraint) override;
54  void SetObjectiveCoefficient(const MPVariable* const variable,
55  double coefficient) override;
56  void SetObjectiveOffset(double value) override;
57  void ClearObjective() override;
58 
59  // ------ Query statistics on the solution and the solve ------
60  int64_t iterations() const override;
61  int64_t nodes() const override;
62  MPSolver::BasisStatus row_status(int constraint_index) const override;
63  MPSolver::BasisStatus column_status(int variable_index) const override;
64 
65  // ----- Misc -----
66  bool IsContinuous() const override;
67  bool IsLP() const override;
68  bool IsMIP() const override;
69 
70  std::string SolverVersion() const override;
71  void* underlying_solver() override;
72 
73  void ExtractNewVariables() override;
74  void ExtractNewConstraints() override;
75  void ExtractObjective() override;
76 
77  void SetParameters(const MPSolverParameters& param) override;
78  void SetRelativeMipGap(double value) override;
79  void SetPrimalTolerance(double value) override;
80  void SetDualTolerance(double value) override;
81  void SetPresolveMode(int value) override;
82  void SetScalingMode(int value) override;
83  void SetLpAlgorithm(int value) override;
84 
85  private:
86  bool IsKnapsackModel() const;
87  bool IsVariableFixedToValue(const MPVariable* var, double value) const;
88  bool IsVariableFixed(const MPVariable* var) const;
89  double GetVariableValueFromSolution(const MPVariable* var) const;
90  void NonIncrementalChange() { sync_status_ = MUST_RELOAD; }
91 
92  std::unique_ptr<KnapsackSolver> knapsack_solver_;
93  std::vector<int64_t> profits_;
94  std::vector<std::vector<int64_t>> weights_;
95  std::vector<int64_t> capacities_;
96 };
97 
99  : MPSolverInterface(solver) {}
100 
102 
104  const MPSolverParameters& param) {
105  Reset();
106  if (!IsKnapsackModel()) {
107  LOG(ERROR) << "Model is not a knapsack model";
110  }
111  ExtractModel();
112  SetParameters(param);
114  // TODO(user): Refine Analysis of the model to choose better solvers.
115  KnapsackSolver::SolverType solver_type =
117  if (profits_.size() <= 64 && capacities_.size() == 1) {
119  }
120  knapsack_solver_ =
121  std::make_unique<KnapsackSolver>(solver_type, "linear_solver");
122  const double time_limit_seconds =
124  ? (static_cast<double>(solver_->time_limit()) / 1000.0)
125  : std::numeric_limits<double>::infinity();
126  knapsack_solver_->set_time_limit(time_limit_seconds);
127  knapsack_solver_->Init(profits_, weights_, capacities_);
128  knapsack_solver_->Solve();
129  result_status_ = knapsack_solver_->IsSolutionOptimal() ? MPSolver::OPTIMAL
131  objective_value_ = solver_->objective_->offset();
132  for (int var_id = 0; var_id < solver_->variables_.size(); ++var_id) {
133  MPVariable* const var = solver_->variables_[var_id];
134  const double value = GetVariableValueFromSolution(var);
135  objective_value_ += value * solver_->objective_->GetCoefficient(var);
136  var->set_solution_value(value);
137  }
138  return result_status_;
139 }
140 
143  profits_.clear();
144  weights_.clear();
145  capacities_.clear();
146  knapsack_solver_.reset(nullptr);
147 }
148 
150  NonIncrementalChange();
151 }
152 
153 void KnapsackInterface::SetVariableBounds(int index, double lb, double ub) {
154  NonIncrementalChange();
155 }
156 
158  NonIncrementalChange();
159 }
160 
161 void KnapsackInterface::SetConstraintBounds(int index, double lb, double ub) {
162  NonIncrementalChange();
163 }
164 
166  NonIncrementalChange();
167 }
168 
170  NonIncrementalChange();
171 }
172 
174  const MPVariable* const variable,
175  double new_value, double old_value) {
176  NonIncrementalChange();
177 }
178 
180  NonIncrementalChange();
181 }
182 
184  const MPVariable* const variable, double coefficient) {
185  NonIncrementalChange();
186 }
187 
189  NonIncrementalChange();
190 }
191 
192 void KnapsackInterface::ClearObjective() { NonIncrementalChange(); }
193 
194 int64_t KnapsackInterface::iterations() const { return 0; }
195 
197 
199  int constraint_index) const {
200  // TODO(user): set properly.
201  return MPSolver::FREE;
202 }
203 
205  int variable_index) const {
206  // TODO(user): set properly.
207  return MPSolver::FREE;
208 }
209 
210 bool KnapsackInterface::IsContinuous() const { return false; }
211 
212 bool KnapsackInterface::IsLP() const { return false; }
213 
214 bool KnapsackInterface::IsMIP() const { return true; }
215 
216 std::string KnapsackInterface::SolverVersion() const {
217  return "knapsack_solver-0.0";
218 }
219 
220 void* KnapsackInterface::underlying_solver() { return knapsack_solver_.get(); }
221 
223  DCHECK_EQ(0, last_variable_index_);
224  for (int column = 0; column < solver_->variables_.size(); ++column) {
226  }
227 }
228 
230  DCHECK_EQ(0, last_constraint_index_);
231  weights_.resize(solver_->constraints_.size());
232  capacities_.resize(solver_->constraints_.size(),
234  for (int row = 0; row < solver_->constraints_.size(); ++row) {
235  MPConstraint* const ct = solver_->constraints_[row];
236  double fixed_usage = 0.0;
238  std::vector<double> coefficients(solver_->variables_.size() + 1, 0.0);
239  for (const auto& entry : ct->coefficients_) {
240  const int var_index = entry.first->index();
241  DCHECK(variable_is_extracted(var_index));
242  if (IsVariableFixedToValue(entry.first, 1.0)) {
243  fixed_usage += entry.second;
244  } else if (!IsVariableFixedToValue(entry.first, 0.0)) {
245  coefficients[var_index] = entry.second;
246  }
247  }
248  // Removing the contribution of variables fixed to 1 from the constraint
249  // upper bound. All fixed variables have a zero coefficient.
250  const double capacity = ct->ub() - fixed_usage;
251  // Adding upper bound to the coefficients to scale.
252  coefficients[solver_->variables_.size()] = capacity;
253  double relative_error = 0.0;
254  double scaling_factor = 0.0;
257  &scaling_factor, &relative_error);
258  const int64_t gcd =
259  ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
260  std::vector<int64_t> scaled_coefficients(solver_->variables_.size(), 0);
261  for (const auto& entry : ct->coefficients_) {
262  if (!IsVariableFixed(entry.first)) {
263  scaled_coefficients[entry.first->index()] =
264  static_cast<int64_t>(round(scaling_factor * entry.second)) / gcd;
265  }
266  }
267  weights_[row].swap(scaled_coefficients);
268  capacities_[row] =
269  static_cast<int64_t>(round(scaling_factor * capacity)) / gcd;
270  }
271 }
272 
274  std::vector<double> coefficients(solver_->variables_.size(), 0.0);
275  for (const auto& entry : solver_->objective_->coefficients_) {
276  // Whether fixed to 0 or 1, fixed variables are removed from the
277  // profit function, which for the current implementation means their
278  // coefficient is set to 0.
279  if (!IsVariableFixed(entry.first)) {
280  coefficients[entry.first->index()] = entry.second;
281  }
282  }
283  double relative_error = 0.0;
284  double scaling_factor = 0.0;
287  &scaling_factor, &relative_error);
288  const int64_t gcd = ComputeGcdOfRoundedDoubles(coefficients, scaling_factor);
289  std::vector<int64_t> scaled_coefficients(solver_->variables_.size(), 0);
290  for (const auto& entry : solver_->objective_->coefficients_) {
291  scaled_coefficients[entry.first->index()] =
292  static_cast<int64_t>(round(scaling_factor * entry.second)) / gcd;
293  }
294  profits_.swap(scaled_coefficients);
295 }
296 
298  SetCommonParameters(param);
299 }
300 
302 
304 
306 
308 
310 
312 
313 bool KnapsackInterface::IsKnapsackModel() const {
314  // Check variables are boolean.
315  for (int column = 0; column < solver_->variables_.size(); ++column) {
316  MPVariable* const var = solver_->variables_[column];
317  if (var->lb() <= -1.0 || var->ub() >= 2.0 || !var->integer()) {
318  return false;
319  }
320  }
321  // Check objective coefficients are positive.
322  for (const auto& entry : solver_->objective_->coefficients_) {
323  if (entry.second < 0) {
324  return false;
325  }
326  }
327  // Check constraints are knapsack constraints.
328  for (int row = 0; row < solver_->constraints_.size(); ++row) {
329  MPConstraint* const ct = solver_->constraints_[row];
330  if (ct->lb() > 0.0) {
331  return false;
332  }
333  for (const auto& entry : ct->coefficients_) {
334  if (entry.second < 0) {
335  return false;
336  }
337  }
338  }
339  // Check we are maximizing.
340  return maximize_;
341 }
342 
343 bool KnapsackInterface::IsVariableFixedToValue(const MPVariable* var,
344  double value) const {
345  const double lb_round_up = ceil(var->lb());
346  return value == lb_round_up && floor(var->ub()) == lb_round_up;
347 }
348 
349 bool KnapsackInterface::IsVariableFixed(const MPVariable* var) const {
350  return IsVariableFixedToValue(var, 0.0) || IsVariableFixedToValue(var, 1.0);
351 }
352 
353 double KnapsackInterface::GetVariableValueFromSolution(
354  const MPVariable* var) const {
355  return !IsVariableFixedToValue(var, 0.0) &&
356  (knapsack_solver_->BestSolutionContains(var->index()) ||
357  IsVariableFixedToValue(var, 1.0))
358  ? 1.0
359  : 0.0;
360 }
361 
362 // Register Knapsack solver in the global linear solver factory.
364  return new KnapsackInterface(solver);
365 }
366 
367 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
void SetDualTolerance(double value) override
void AddRowConstraint(MPConstraint *const ct) override
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
void SetPrimalTolerance(double value) override
void ClearConstraint(MPConstraint *const constraint) override
void SetObjectiveCoefficient(const MPVariable *const variable, double coefficient) override
void SetCoefficient(MPConstraint *const constraint, const MPVariable *const variable, double new_value, double old_value) override
MPSolver::BasisStatus row_status(int constraint_index) const override
void SetObjectiveOffset(double value) override
void SetVariableInteger(int index, bool integer) override
void SetParameters(const MPSolverParameters &param) override
std::string SolverVersion() const override
void SetRelativeMipGap(double value) override
void SetConstraintBounds(int index, double lb, double ub) override
void SetVariableBounds(int index, double lb, double ub) override
void AddVariable(MPVariable *const var) override
void SetOptimizationDirection(bool maximize) override
MPSolver::BasisStatus column_status(int variable_index) const override
SolverType
Enum controlling which underlying algorithm is used.
@ KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER
Generic Solver.
@ KNAPSACK_64ITEMS_SOLVER
Optimized method for single dimension small problems.
The class for constraints of a Mathematical Programming (MP) model.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
@ MODEL_INVALID
the model is trivially invalid (NaN coefficients, etc).
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
void set_constraint_as_extracted(int ct_index, bool extracted)
static constexpr int64_t kUnknownNumberOfNodes
bool variable_is_extracted(int var_index) const
void set_variable_as_extracted(int var_index, bool extracted)
void SetCommonParameters(const MPSolverParameters &param)
This class stores parameter settings for LP and MIP solvers.
The class for variables of a Mathematical Programming (MP) model.
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Span< const double > coefficients
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
int index
RowIndex row
Definition: markowitz.cc:185
Collection of objects used to extend the Constraint Solver library.
int64_t ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
Definition: fp_utils.cc:202
double GetBestScalingOfDoublesToInt64(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, int64_t max_absolute_sum)
Definition: fp_utils.cc:181
MPSolverInterface * BuildKnapsackInterface(MPSolver *const solver)
int column
Definition: parse_proto.cc:32
int64_t coefficient
int64_t capacity