OR-Tools  8.2
scip_interface.cc
Go to the documentation of this file.
1 // Copyright 2010-2018 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 #if defined(USE_SCIP)
15 
16 #include <stddef.h>
17 
18 #include <algorithm>
19 #include <limits>
20 #include <memory>
21 #include <string>
22 #include <vector>
23 
24 #include "absl/status/status.h"
25 #include "absl/strings/str_format.h"
26 #include "absl/types/optional.h"
27 #include "ortools/base/cleanup.h"
29 #include "ortools/base/hash.h"
31 #include "ortools/base/logging.h"
33 #include "ortools/base/timer.h"
36 #include "ortools/linear_solver/linear_solver.pb.h"
41 #include "scip/cons_indicator.h"
42 #include "scip/scip.h"
43 #include "scip/scip_copy.h"
44 #include "scip/scip_param.h"
45 #include "scip/scip_prob.h"
46 #include "scip/scipdefplugins.h"
47 
48 ABSL_FLAG(bool, scip_feasibility_emphasis, false,
49  "When true, emphasize search towards feasibility. This may or "
50  "may not result in speedups in some problems.");
51 
52 namespace operations_research {
53 namespace {
54 // See the class ScipConstraintHandlerForMPCallback below.
55 struct EmptyStruct {};
56 } // namespace
57 
58 class ScipConstraintHandlerForMPCallback;
59 
61  public:
62  explicit SCIPInterface(MPSolver* solver);
63  ~SCIPInterface() override;
64 
65  void SetOptimizationDirection(bool maximize) override;
66  MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
67  absl::optional<MPSolutionResponse> DirectlySolveProto(
68  const MPModelRequest& request) override;
69  void Reset() override;
70 
71  void SetVariableBounds(int var_index, double lb, double ub) override;
72  void SetVariableInteger(int var_index, bool integer) override;
73  void SetConstraintBounds(int row_index, double lb, double ub) override;
74 
75  void AddRowConstraint(MPConstraint* ct) override;
76  bool AddIndicatorConstraint(MPConstraint* ct) override;
77  void AddVariable(MPVariable* var) override;
78  void SetCoefficient(MPConstraint* constraint, const MPVariable* variable,
79  double new_value, double old_value) override;
80  void ClearConstraint(MPConstraint* constraint) override;
81  void SetObjectiveCoefficient(const MPVariable* variable,
82  double coefficient) override;
83  void SetObjectiveOffset(double value) override;
84  void ClearObjective() override;
85  void BranchingPriorityChangedForVariable(int var_index) override;
86 
87  int64 iterations() const override;
88  int64 nodes() const override;
89  MPSolver::BasisStatus row_status(int constraint_index) const override {
90  LOG(DFATAL) << "Basis status only available for continuous problems";
91  return MPSolver::FREE;
92  }
93  MPSolver::BasisStatus column_status(int variable_index) const override {
94  LOG(DFATAL) << "Basis status only available for continuous problems";
95  return MPSolver::FREE;
96  }
97 
98  bool IsContinuous() const override { return false; }
99  bool IsLP() const override { return false; }
100  bool IsMIP() const override { return true; }
101 
102  void ExtractNewVariables() override;
103  void ExtractNewConstraints() override;
104  void ExtractObjective() override;
105 
106  std::string SolverVersion() const override {
107  return absl::StrFormat("SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
108  SCIPminorVersion(), SCIPtechVersion(),
109  SCIPlpiGetSolverName());
110  }
111 
112  bool InterruptSolve() override {
113  const absl::MutexLock lock(&hold_interruptions_mutex_);
114  if (scip_ == nullptr) {
115  LOG_IF(DFATAL, status_.ok()) << "scip_ is null is unexpected here, since "
116  "status_ did not report any error";
117  return true;
118  }
119  return SCIPinterruptSolve(scip_) == SCIP_OKAY;
120  }
121 
122  void* underlying_solver() override { return reinterpret_cast<void*>(scip_); }
123 
124  // MULTIPLE SOLUTIONS SUPPORT
125  // The default behavior of scip is to store the top incidentally generated
126  // integer solutions in the solution pool. The default maximum size is 100.
127  // This can be adjusted by setting the param limits/maxsol. There is no way
128  // to ensure that the pool will actually be full.
129  //
130  // You can also ask SCIP to enumerate all feasible solutions. Combined with
131  // an equality or inequality constraint on the objective (after solving once
132  // to find the optimal solution), you can use this to find all high quality
133  // solutions. See https://scip.zib.de/doc/html/COUNTER.php. This behavior is
134  // not supported directly through MPSolver, but in theory can be controlled
135  // entirely through scip parameters.
136  bool NextSolution() override;
137 
138  // CALLBACK SUPPORT:
139  // * We support MPSolver's callback API via MPCallback.
140  // See ./linear_solver_callback.h.
141  // * We also support SCIP's more general callback interface, built on
142  // 'constraint handlers'. See ./scip_callback.h and test, these are added
143  // directly to the underlying SCIP object, bypassing SCIPInterface.
144  // The former works by calling the latter. See go/scip-callbacks for
145  // a complete documentation of this design.
146 
147  // MPCallback API
148  void SetCallback(MPCallback* mp_callback) override;
149  bool SupportsCallbacks() const override { return true; }
150 
151  private:
152  void SetParameters(const MPSolverParameters& param) override;
153  void SetRelativeMipGap(double value) override;
154  void SetPrimalTolerance(double value) override;
155  void SetDualTolerance(double value) override;
156  void SetPresolveMode(int presolve) override;
157  void SetScalingMode(int scaling) override;
158  void SetLpAlgorithm(int lp_algorithm) override;
159 
160  // SCIP parameters allow to lower and upper bound the number of threads used
161  // (via "parallel/minnthreads" and "parallel/maxnthread", respectively). Here,
162  // we interpret "num_threads" to mean "parallel/maxnthreads", as this is what
163  // most clients probably want to do. To change "parallel/minnthreads" use
164  // SetSolverSpecificParametersAsString(). However, one must change
165  // "parallel/maxnthread" with SetNumThreads() because only this will inform
166  // the interface to run SCIPsolveConcurrent() instead of SCIPsolve() which is
167  // necessery to enable multi-threading.
168  absl::Status SetNumThreads(int num_threads) override;
169 
170  bool SetSolverSpecificParametersAsString(
171  const std::string& parameters) override;
172 
173  void SetUnsupportedIntegerParam(
174  MPSolverParameters::IntegerParam param) override;
175  void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param,
176  int value) override;
177  // How many solutions SCIP found.
178  int SolutionCount();
179  // Copy sol from SCIP to MPSolver.
180  void SetSolution(SCIP_SOL* solution);
181 
182  absl::Status CreateSCIP();
183  // Deletes variables and constraints from scip_ and reset scip_ to null. If
184  // return_scip is false, deletes the SCIP object; if true, returns it (but
185  // scip_ is still set to null).
186  SCIP* DeleteSCIP(bool return_scip = false);
187 
188  // SCIP has many internal checks (many of which are numerical) that can fail
189  // during various phases: upon startup, when loading the model, when solving,
190  // etc. Often, the user is meant to stop at the first error, but since most
191  // of the linear solver interface API doesn't support "error reporting", we
192  // store a potential error status here.
193  // If this status isn't OK, then most operations will silently be cancelled.
194  absl::Status status_;
195 
196  SCIP* scip_;
197  std::vector<SCIP_VAR*> scip_variables_;
198  std::vector<SCIP_CONS*> scip_constraints_;
199  int current_solution_index_ = 0;
200  MPCallback* callback_ = nullptr;
201  std::unique_ptr<ScipConstraintHandlerForMPCallback> scip_constraint_handler_;
202  // See ScipConstraintHandlerForMPCallback below.
203  EmptyStruct constraint_data_for_handler_;
204  bool branching_priority_reset_ = false;
205  bool callback_reset_ = false;
206 
207  // Mutex that is held to prevent InterruptSolve() to call SCIPinterruptSolve()
208  // when scip_ is being built. It also prevents rebuilding scip_ until
209  // SCIPinterruptSolve() has returned.
210  mutable absl::Mutex hold_interruptions_mutex_;
211 };
212 
214  : public ScipConstraintHandler<EmptyStruct> {
215  public:
217 
218  std::vector<CallbackRangeConstraint> SeparateFractionalSolution(
219  const ScipConstraintHandlerContext& context, const EmptyStruct&) override;
220 
221  std::vector<CallbackRangeConstraint> SeparateIntegerSolution(
222  const ScipConstraintHandlerContext& context, const EmptyStruct&) override;
223 
224  MPCallback* const mp_callback() const { return mp_callback_; }
225 
226  private:
227  std::vector<CallbackRangeConstraint> SeparateSolution(
229  const bool at_integer_solution);
230 
231  MPCallback* const mp_callback_;
232 };
233 
234 #define RETURN_IF_ALREADY_IN_ERROR_STATE \
235  do { \
236  if (!status_.ok()) { \
237  VLOG_EVERY_N(1, 10) << "Early abort: SCIP is in error state."; \
238  return; \
239  } \
240  } while (false)
241 
242 #define RETURN_AND_STORE_IF_SCIP_ERROR(x) \
243  do { \
244  status_ = SCIP_TO_STATUS(x); \
245  if (!status_.ok()) return; \
246  } while (false)
247 
249  : MPSolverInterface(solver), scip_(nullptr) {
250  status_ = CreateSCIP();
251 }
252 
253 SCIPInterface::~SCIPInterface() { DeleteSCIP(); }
254 
256  // We hold calls to SCIPinterruptSolve() until the new scip_ is fully built.
257  const absl::MutexLock lock(&hold_interruptions_mutex_);
258 
259  // Remove existing one but keep it alive to copy parameters from it.
260  SCIP* old_scip = DeleteSCIP(/*return_scip=*/true);
261  const auto scip_deleter = absl::MakeCleanup(
262  [&old_scip]() { CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY); });
263 
264  scip_constraint_handler_.reset();
266 
267  // Install the new one.
268  status_ = CreateSCIP();
269  if (!status_.ok()) {
270  return;
271  }
272 
273  // Copy all existing parameters from the previous SCIP to the new one. This
274  // ensures that if a user calls multiple times
275  // SetSolverSpecificParametersAsString() and then Reset() is called, we still
276  // take into account all parameters. Note though that at the end of Solve(),
277  // parameters are reset so after Solve() has been called, only the last set
278  // parameters are kept.
279  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPcopyParamSettings(old_scip, scip_));
280 }
281 
282 absl::Status SCIPInterface::CreateSCIP() {
283  RETURN_IF_SCIP_ERROR(SCIPcreate(&scip_));
284  RETURN_IF_SCIP_ERROR(SCIPincludeDefaultPlugins(scip_));
285  // Set the emphasis to enum SCIP_PARAMEMPHASIS_FEASIBILITY. Do not print
286  // the new parameter (quiet = true).
287  if (absl::GetFlag(FLAGS_scip_feasibility_emphasis)) {
288  RETURN_IF_SCIP_ERROR(SCIPsetEmphasis(scip_, SCIP_PARAMEMPHASIS_FEASIBILITY,
289  /*quiet=*/true));
290  }
291  // Default clock type. We use wall clock time because getting CPU user seconds
292  // involves calling times() which is very expensive.
293  // NOTE(user): Also, time limit based on CPU user seconds is *NOT* thread
294  // safe. We observed that different instances of SCIP running concurrently
295  // in different threads consume the time limit *together*. E.g., 2 threads
296  // running SCIP with time limit 10s each will both terminate after ~5s.
298  SCIPsetIntParam(scip_, "timing/clocktype", SCIP_CLOCKTYPE_WALL));
299  RETURN_IF_SCIP_ERROR(SCIPcreateProb(scip_, solver_->name_.c_str(), nullptr,
300  nullptr, nullptr, nullptr, nullptr,
301  nullptr, nullptr));
302  RETURN_IF_SCIP_ERROR(SCIPsetObjsense(
303  scip_, maximize_ ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
304  return absl::OkStatus();
305 }
306 
307 SCIP* SCIPInterface::DeleteSCIP(bool return_scip) {
308  // NOTE(user): DeleteSCIP() shouldn't "give up" mid-stage if it fails, since
309  // it might be the user's chance to reset the solver to start fresh without
310  // errors. The current code isn't perfect, since some CHECKs() remain, but
311  // hopefully they'll never be triggered in practice.
312  CHECK(scip_ != nullptr);
313  for (int i = 0; i < scip_variables_.size(); ++i) {
314  CHECK_EQ(SCIPreleaseVar(scip_, &scip_variables_[i]), SCIP_OKAY);
315  }
316  scip_variables_.clear();
317  for (int j = 0; j < scip_constraints_.size(); ++j) {
318  CHECK_EQ(SCIPreleaseCons(scip_, &scip_constraints_[j]), SCIP_OKAY);
319  }
320  scip_constraints_.clear();
321 
322  SCIP* old_scip = scip_;
323  scip_ = nullptr;
324  if (!return_scip) {
325  CHECK_EQ(SCIPfree(&old_scip), SCIP_OKAY);
326  }
327  return old_scip;
328 }
329 
330 // Not cached.
334  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
335  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPsetObjsense(
336  scip_, maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
337 }
338 
339 void SCIPInterface::SetVariableBounds(int var_index, double lb, double ub) {
342  if (variable_is_extracted(var_index)) {
343  // Not cached if the variable has been extracted.
344  DCHECK_LT(var_index, last_variable_index_);
345  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
347  SCIPchgVarLb(scip_, scip_variables_[var_index], lb));
349  SCIPchgVarUb(scip_, scip_variables_[var_index], ub));
350  } else {
352  }
353 }
354 
355 void SCIPInterface::SetVariableInteger(int var_index, bool integer) {
358  if (variable_is_extracted(var_index)) {
359  // Not cached if the variable has been extracted.
360  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
361 #if (SCIP_VERSION >= 210)
362  SCIP_Bool infeasible = false;
363  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPchgVarType(
364  scip_, scip_variables_[var_index],
365  integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, &infeasible));
366 #else
367  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPchgVarType(
368  scip_, scip_variables_[var_index],
369  integer ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS));
370 #endif // SCIP_VERSION >= 210
371  } else {
373  }
374 }
375 
376 void SCIPInterface::SetConstraintBounds(int index, double lb, double ub) {
380  // Not cached if the row has been extracted.
382  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
384  SCIPchgLhsLinear(scip_, scip_constraints_[index], lb));
386  SCIPchgRhsLinear(scip_, scip_constraints_[index], ub));
387  } else {
389  }
390 }
391 
393  const MPVariable* variable, double new_value,
394  double old_value) {
397  if (variable_is_extracted(variable->index()) &&
398  constraint_is_extracted(constraint->index())) {
399  // The modification of the coefficient for an extracted row and
400  // variable is not cached.
401  DCHECK_LT(constraint->index(), last_constraint_index_);
402  DCHECK_LT(variable->index(), last_variable_index_);
403  // SCIP does not allow to set a coefficient directly, so we add the
404  // difference between the new and the old value instead.
405  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
406  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPaddCoefLinear(
407  scip_, scip_constraints_[constraint->index()],
408  scip_variables_[variable->index()], new_value - old_value));
409  } else {
410  // The modification of an unextracted row or variable is cached
411  // and handled in ExtractModel.
413  }
414 }
415 
416 // Not cached
420  const int constraint_index = constraint->index();
421  // Constraint may not have been extracted yet.
422  if (!constraint_is_extracted(constraint_index)) return;
423  for (const auto& entry : constraint->coefficients_) {
424  const int var_index = entry.first->index();
425  const double old_coef_value = entry.second;
426  DCHECK(variable_is_extracted(var_index));
427  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
428  // Set coefficient to zero by subtracting the old coefficient value.
430  SCIPaddCoefLinear(scip_, scip_constraints_[constraint_index],
431  scip_variables_[var_index], -old_coef_value));
432  }
433 }
434 
435 // Cached
437  double coefficient) {
439 }
440 
441 // Cached
444 }
445 
446 // Clear objective of all its terms.
450 
452  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
453  // Clear linear terms
454  for (const auto& entry : solver_->objective_->coefficients_) {
455  const int var_index = entry.first->index();
456  // Variable may have not been extracted yet.
457  if (!variable_is_extracted(var_index)) {
459  } else {
461  SCIPchgVarObj(scip_, scip_variables_[var_index], 0.0));
462  }
463  }
464  // Note: we don't clear the objective offset here because it's not necessary
465  // (it's always reset anyway in ExtractObjective) and we sometimes run into
466  // crashes when clearing the whole model (see
467  // http://test/OCL:253365573:BASE:253566457:1560777456754:e181f4ab).
468  // It's not worth to spend time investigating this issue.
469 }
470 
472  // As of 2019-05, SCIP does not support setting branching priority for
473  // variables in models that have already been solved. Therefore, we force
474  // reset the model when setting the priority on an already extracted variable.
475  // Note that this is a more drastic step than merely changing the sync_status.
476  // This may be slightly conservative, as it is technically possible that
477  // the extraction has occurred without a call to Solve().
478  if (variable_is_extracted(var_index)) {
479  branching_priority_reset_ = true;
480  }
481 }
482 
485 }
486 
489  return true;
490 }
491 
493 
496  int total_num_vars = solver_->variables_.size();
497  if (total_num_vars > last_variable_index_) {
498  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
499  // Define new variables
500  for (int j = last_variable_index_; j < total_num_vars; ++j) {
501  MPVariable* const var = solver_->variables_[j];
503  set_variable_as_extracted(j, true);
504  SCIP_VAR* scip_var = nullptr;
505  // The true objective coefficient will be set later in ExtractObjective.
506  double tmp_obj_coef = 0.0;
507  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPcreateVar(
508  scip_, &scip_var, var->name().c_str(), var->lb(), var->ub(),
509  tmp_obj_coef,
510  var->integer() ? SCIP_VARTYPE_INTEGER : SCIP_VARTYPE_CONTINUOUS, true,
511  false, nullptr, nullptr, nullptr, nullptr, nullptr));
512  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPaddVar(scip_, scip_var));
513  scip_variables_.push_back(scip_var);
514  const int branching_priority = var->branching_priority();
515  if (branching_priority != 0) {
516  const int index = var->index();
517  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPchgVarBranchPriority(
518  scip_, scip_variables_[index], branching_priority));
519  }
520  }
521  // Add new variables to existing constraints.
522  for (int i = 0; i < last_constraint_index_; i++) {
523  MPConstraint* const ct = solver_->constraints_[i];
524  for (const auto& entry : ct->coefficients_) {
525  const int var_index = entry.first->index();
526  DCHECK(variable_is_extracted(var_index));
527  if (var_index >= last_variable_index_) {
528  // The variable is new, so we know the previous coefficient
529  // value was 0 and we can directly add the coefficient.
531  SCIPaddCoefLinear(scip_, scip_constraints_[i],
532  scip_variables_[var_index], entry.second));
533  }
534  }
535  }
536  }
537 }
538 
541  int total_num_rows = solver_->constraints_.size();
542  if (last_constraint_index_ < total_num_rows) {
543  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
544  // Find the length of the longest row.
545  int max_row_length = 0;
546  for (int i = last_constraint_index_; i < total_num_rows; ++i) {
547  MPConstraint* const ct = solver_->constraints_[i];
550  if (ct->coefficients_.size() > max_row_length) {
551  max_row_length = ct->coefficients_.size();
552  }
553  }
554  std::unique_ptr<SCIP_VAR*[]> vars(new SCIP_VAR*[max_row_length]);
555  std::unique_ptr<double[]> coeffs(new double[max_row_length]);
556  // Add each new constraint.
557  for (int i = last_constraint_index_; i < total_num_rows; ++i) {
558  MPConstraint* const ct = solver_->constraints_[i];
560  const int size = ct->coefficients_.size();
561  int j = 0;
562  for (const auto& entry : ct->coefficients_) {
563  const int var_index = entry.first->index();
564  DCHECK(variable_is_extracted(var_index));
565  vars[j] = scip_variables_[var_index];
566  coeffs[j] = entry.second;
567  j++;
568  }
569  SCIP_CONS* scip_constraint = nullptr;
570  const bool is_lazy = ct->is_lazy();
571  if (ct->indicator_variable() != nullptr) {
572  const int ind_index = ct->indicator_variable()->index();
573  DCHECK(variable_is_extracted(ind_index));
574  SCIP_VAR* ind_var = scip_variables_[ind_index];
575  if (ct->indicator_value() == 0) {
577  SCIPgetNegatedVar(scip_, scip_variables_[ind_index], &ind_var));
578  }
579 
580  if (ct->ub() < std::numeric_limits<double>::infinity()) {
581  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPcreateConsIndicator(
582  scip_, &scip_constraint, ct->name().c_str(), ind_var, size,
583  vars.get(), coeffs.get(), ct->ub(),
584  /*initial=*/!is_lazy,
585  /*separate=*/true,
586  /*enforce=*/true,
587  /*check=*/true,
588  /*propagate=*/true,
589  /*local=*/false,
590  /*dynamic=*/false,
591  /*removable=*/is_lazy,
592  /*stickingatnode=*/false));
593  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPaddCons(scip_, scip_constraint));
594  scip_constraints_.push_back(scip_constraint);
595  }
596  if (ct->lb() > -std::numeric_limits<double>::infinity()) {
597  for (int i = 0; i < size; ++i) {
598  coeffs[i] *= -1;
599  }
600  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPcreateConsIndicator(
601  scip_, &scip_constraint, ct->name().c_str(), ind_var, size,
602  vars.get(), coeffs.get(), -ct->lb(),
603  /*initial=*/!is_lazy,
604  /*separate=*/true,
605  /*enforce=*/true,
606  /*check=*/true,
607  /*propagate=*/true,
608  /*local=*/false,
609  /*dynamic=*/false,
610  /*removable=*/is_lazy,
611  /*stickingatnode=*/false));
612  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPaddCons(scip_, scip_constraint));
613  scip_constraints_.push_back(scip_constraint);
614  }
615  } else {
616  // See
617  // http://scip.zib.de/doc/html/cons__linear_8h.php#aa7aed137a4130b35b168812414413481
618  // for an explanation of the parameters.
619  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPcreateConsLinear(
620  scip_, &scip_constraint, ct->name().c_str(), size, vars.get(),
621  coeffs.get(), ct->lb(), ct->ub(),
622  /*initial=*/!is_lazy,
623  /*separate=*/true,
624  /*enforce=*/true,
625  /*check=*/true,
626  /*propagate=*/true,
627  /*local=*/false,
628  /*modifiable=*/false,
629  /*dynamic=*/false,
630  /*removable=*/is_lazy,
631  /*stickingatnode=*/false));
632  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPaddCons(scip_, scip_constraint));
633  scip_constraints_.push_back(scip_constraint);
634  }
635  }
636  }
637 }
638 
641  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPfreeTransform(scip_));
642  // Linear objective: set objective coefficients for all variables (some might
643  // have been modified).
644  for (const auto& entry : solver_->objective_->coefficients_) {
645  const int var_index = entry.first->index();
646  const double obj_coef = entry.second;
648  SCIPchgVarObj(scip_, scip_variables_[var_index], obj_coef));
649  }
650 
651  // Constant term: change objective offset.
652  RETURN_AND_STORE_IF_SCIP_ERROR(SCIPaddOrigObjoffset(
653  scip_, solver_->Objective().offset() - SCIPgetOrigObjoffset(scip_)));
654 }
655 
656 #define RETURN_ABNORMAL_IF_BAD_STATUS \
657  do { \
658  if (!status_.ok()) { \
659  LOG_IF(INFO, solver_->OutputIsEnabled()) \
660  << "Invalid SCIP status: " << status_; \
661  return result_status_ = MPSolver::ABNORMAL; \
662  } \
663  } while (false)
664 
665 #define RETURN_ABNORMAL_IF_SCIP_ERROR(x) \
666  do { \
667  RETURN_ABNORMAL_IF_BAD_STATUS; \
668  status_ = SCIP_TO_STATUS(x); \
669  RETURN_ABNORMAL_IF_BAD_STATUS; \
670  } while (false);
671 
673  // "status_" may encode a variety of failure scenarios, many of which would
674  // correspond to another MPResultStatus than ABNORMAL, but since SCIP is a
675  // moving target, we use the most likely error code here (abnormalities,
676  // often numeric), and rely on the user enabling output to see more details.
678 
679  WallTimer timer;
680  timer.Start();
681 
682  // Note that SCIP does not provide any incrementality.
683  // TODO(user): Is that still true now (2018) ?
686  branching_priority_reset_ || callback_reset_) {
687  Reset();
688  branching_priority_reset_ = false;
689  callback_reset_ = false;
690  }
691 
692  // Set log level.
693  SCIPsetMessagehdlrQuiet(scip_, quiet_);
694 
695  // Special case if the model is empty since SCIP expects a non-empty model.
696  if (solver_->variables_.empty() && solver_->constraints_.empty()) {
701  return result_status_;
702  }
703 
704  ExtractModel();
705  VLOG(1) << absl::StrFormat("Model built in %s.",
706  absl::FormatDuration(timer.GetDuration()));
707  if (scip_constraint_handler_ != nullptr) {
708  // When the value of `callback_` is changed, `callback_reset_` is set and
709  // code above you call Reset() that should have cleared
710  // `scip_constraint_handler_`. Here we assert that if this has not happened
711  // then `callback_` value has not changed.
712  CHECK_EQ(scip_constraint_handler_->mp_callback(), callback_);
713  } else if (callback_ != nullptr) {
714  scip_constraint_handler_ =
715  absl::make_unique<ScipConstraintHandlerForMPCallback>(callback_);
716  RegisterConstraintHandler<EmptyStruct>(scip_constraint_handler_.get(),
717  scip_);
718  AddCallbackConstraint<EmptyStruct>(scip_, scip_constraint_handler_.get(),
719  "mp_solver_callback_constraint_for_scip",
720  &constraint_data_for_handler_,
722  }
723 
724  // Time limit.
725  if (solver_->time_limit() != 0) {
726  VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
728  SCIPsetRealParam(scip_, "limits/time", solver_->time_limit_in_secs()));
729  } else {
730  RETURN_ABNORMAL_IF_SCIP_ERROR(SCIPresetParam(scip_, "limits/time"));
731  }
732 
733  // We first set our internal MPSolverParameters from param and then set any
734  // user specified internal solver, ie. SCIP, parameters via
735  // solver_specific_parameter_string_.
736  // Default MPSolverParameters can override custom parameters (for example for
737  // presolving) and therefore we apply MPSolverParameters first.
738  SetParameters(param);
740  solver_->solver_specific_parameter_string_);
741 
742  // Use the solution hint if any.
743  if (!solver_->solution_hint_.empty()) {
744  SCIP_SOL* solution;
745  bool is_solution_partial = false;
746  const int num_vars = solver_->variables_.size();
747  if (solver_->solution_hint_.size() != num_vars) {
748  // We start by creating an empty partial solution.
750  SCIPcreatePartialSol(scip_, &solution, nullptr));
751  is_solution_partial = true;
752  } else {
753  // We start by creating the all-zero solution.
754  RETURN_ABNORMAL_IF_SCIP_ERROR(SCIPcreateSol(scip_, &solution, nullptr));
755  }
756 
757  // Fill the other variables from the given solution hint.
758  for (const std::pair<const MPVariable*, double>& p :
759  solver_->solution_hint_) {
760  RETURN_ABNORMAL_IF_SCIP_ERROR(SCIPsetSolVal(
761  scip_, solution, scip_variables_[p.first->index()], p.second));
762  }
763 
764  if (!is_solution_partial) {
765  SCIP_Bool is_feasible;
766  RETURN_ABNORMAL_IF_SCIP_ERROR(SCIPcheckSol(
767  scip_, solution, /*printreason=*/false, /*completely=*/true,
768  /*checkbounds=*/true, /*checkintegrality=*/true, /*checklprows=*/true,
769  &is_feasible));
770  VLOG(1) << "Solution hint is "
771  << (is_feasible ? "FEASIBLE" : "INFEASIBLE");
772  }
773 
774  // TODO(user): I more or less copied this from the SCIPreadSol() code that
775  // reads a solution from a file. I am not sure what SCIPisTransformed() is
776  // or what is the difference between the try and add version. In any case
777  // this seems to always call SCIPaddSolFree() for now and it works.
778  SCIP_Bool is_stored;
779  if (!is_solution_partial && SCIPisTransformed(scip_)) {
780  RETURN_ABNORMAL_IF_SCIP_ERROR(SCIPtrySolFree(
781  scip_, &solution, /*printreason=*/false, /*completely=*/true,
782  /*checkbounds=*/true, /*checkintegrality=*/true, /*checklprows=*/true,
783  &is_stored));
784  } else {
786  SCIPaddSolFree(scip_, &solution, &is_stored));
787  }
788  }
789 
790  // Solve.
791  timer.Restart();
793  ? SCIPsolveConcurrent(scip_)
794  : SCIPsolve(scip_));
795  VLOG(1) << absl::StrFormat("Solved in %s.",
796  absl::FormatDuration(timer.GetDuration()));
797  current_solution_index_ = 0;
798  // Get the results.
799  SCIP_SOL* const solution = SCIPgetBestSol(scip_);
800  if (solution != nullptr) {
801  // If optimal or feasible solution is found.
802  SetSolution(solution);
803  } else {
804  VLOG(1) << "No feasible solution found.";
805  }
806 
807  // Check the status: optimal, infeasible, etc.
808  SCIP_STATUS scip_status = SCIPgetStatus(scip_);
809  switch (scip_status) {
810  case SCIP_STATUS_OPTIMAL:
812  break;
813  case SCIP_STATUS_GAPLIMIT:
814  // To be consistent with the other solvers.
816  break;
817  case SCIP_STATUS_INFEASIBLE:
819  break;
820  case SCIP_STATUS_UNBOUNDED:
822  break;
823  case SCIP_STATUS_INFORUNBD:
824  // TODO(user): We could introduce our own "infeasible or
825  // unbounded" status.
827  break;
828  default:
829  if (solution != nullptr) {
831  } else if (scip_status == SCIP_STATUS_TIMELIMIT ||
832  scip_status == SCIP_STATUS_TOTALNODELIMIT) {
834  } else {
836  }
837  break;
838  }
839 
840  RETURN_ABNORMAL_IF_SCIP_ERROR(SCIPresetParams(scip_));
841 
843  return result_status_;
844 }
845 
846 void SCIPInterface::SetSolution(SCIP_SOL* solution) {
847  objective_value_ = SCIPgetSolOrigObj(scip_, solution);
848  best_objective_bound_ = SCIPgetDualbound(scip_);
849  VLOG(1) << "objective=" << objective_value_
850  << ", bound=" << best_objective_bound_;
851  for (int i = 0; i < solver_->variables_.size(); ++i) {
852  MPVariable* const var = solver_->variables_[i];
853  const int var_index = var->index();
854  const double val =
855  SCIPgetSolVal(scip_, solution, scip_variables_[var_index]);
856  var->set_solution_value(val);
857  VLOG(3) << var->name() << "=" << val;
858  }
859 }
860 
861 absl::optional<MPSolutionResponse> SCIPInterface::DirectlySolveProto(
862  const MPModelRequest& request) {
863  // ScipSolveProto doesn't solve concurrently.
864  if (solver_->GetNumThreads() > 1) return absl::nullopt;
865 
866  const auto status_or = ScipSolveProto(request);
867  if (status_or.ok()) return status_or.value();
868  // Special case: if something is not implemented yet, fall back to solving
869  // through MPSolver.
870  if (absl::IsUnimplemented(status_or.status())) return absl::nullopt;
871 
872  if (request.enable_internal_solver_output()) {
873  LOG(INFO) << "Invalid SCIP status: " << status_or.status();
874  }
875  MPSolutionResponse response;
876  response.set_status(MPSOLVER_NOT_SOLVED);
877  response.set_status_str(status_or.status().ToString());
878  return response;
879 }
880 
881 int SCIPInterface::SolutionCount() { return SCIPgetNSols(scip_); }
882 
884  // Make sure we have successfully solved the problem and not modified it.
886  return false;
887  }
888  if (current_solution_index_ + 1 >= SolutionCount()) {
889  return false;
890  }
891  current_solution_index_++;
892  SCIP_SOL** all_solutions = SCIPgetSols(scip_);
893  SetSolution(all_solutions[current_solution_index_]);
894  return true;
895 }
896 
898  // NOTE(user): As of 2018-12 it doesn't run in the stubby server, and is
899  // a specialized call, so it's ok to crash if the status is broken.
901  return SCIPgetNLPIterations(scip_);
902 }
903 
905  // NOTE(user): Same story as iterations(): it's OK to crash here.
907  // This is the total number of nodes used in the solve, potentially across
908  // multiple branch-and-bound trees. Use limits/totalnodes (rather than
909  // limits/nodes) to control this value.
910  return SCIPgetNTotalNodes(scip_);
911 }
912 
913 void SCIPInterface::SetParameters(const MPSolverParameters& param) {
914  SetCommonParameters(param);
915  SetMIPParameters(param);
916 }
917 
918 void SCIPInterface::SetRelativeMipGap(double value) {
919  // NOTE(user): We don't want to call RETURN_IF_ALREADY_IN_ERROR_STATE here,
920  // because even if the solver is in an error state, the user might be setting
921  // some parameters and then "restoring" the solver to a non-error state by
922  // calling Reset(), which should *not* reset the parameters.
923  // So we want the parameter-setting functions to be resistant to being in an
924  // error state, essentially. What we do is:
925  // - we call the parameter-setting function anyway (I'm assuming that SCIP
926  // won't crash even if we're in an error state. I did *not* verify this).
927  // - if that call yielded an error *and* we weren't already in an error state,
928  // set the state to that error we just got.
929  const auto status =
930  SCIP_TO_STATUS(SCIPsetRealParam(scip_, "limits/gap", value));
931  if (status_.ok()) status_ = status;
932 }
933 
934 void SCIPInterface::SetPrimalTolerance(double value) {
935  // See the NOTE on SetRelativeMipGap().
936  const auto status =
937  SCIP_TO_STATUS(SCIPsetRealParam(scip_, "numerics/feastol", value));
938  if (status_.ok()) status_ = status;
939 }
940 
941 void SCIPInterface::SetDualTolerance(double value) {
942  const auto status =
943  SCIP_TO_STATUS(SCIPsetRealParam(scip_, "numerics/dualfeastol", value));
944  if (status_.ok()) status_ = status;
945 }
946 
947 void SCIPInterface::SetPresolveMode(int presolve) {
948  // See the NOTE on SetRelativeMipGap().
949  switch (presolve) {
951  const auto status =
952  SCIP_TO_STATUS(SCIPsetIntParam(scip_, "presolving/maxrounds", 0));
953  if (status_.ok()) status_ = status;
954  return;
955  }
957  const auto status =
958  SCIP_TO_STATUS(SCIPsetIntParam(scip_, "presolving/maxrounds", -1));
959  if (status_.ok()) status_ = status;
960  return;
961  }
962  default: {
963  SetIntegerParamToUnsupportedValue(MPSolverParameters::PRESOLVE, presolve);
964  return;
965  }
966  }
967 }
968 
969 void SCIPInterface::SetScalingMode(int scaling) {
970  SetUnsupportedIntegerParam(MPSolverParameters::SCALING);
971 }
972 
973 // Only the root LP algorithm is set as setting the node LP to a
974 // non-default value rarely is beneficial. The node LP algorithm could
975 // be set as well with "lp/resolvealgorithm".
976 void SCIPInterface::SetLpAlgorithm(int lp_algorithm) {
977  // See the NOTE on SetRelativeMipGap().
978  switch (lp_algorithm) {
980  const auto status =
981  SCIP_TO_STATUS(SCIPsetCharParam(scip_, "lp/initalgorithm", 'd'));
982  if (status_.ok()) status_ = status;
983  return;
984  }
986  const auto status =
987  SCIP_TO_STATUS(SCIPsetCharParam(scip_, "lp/initalgorithm", 'p'));
988  if (status_.ok()) status_ = status;
989  return;
990  }
992  // Barrier with crossover.
993  const auto status =
994  SCIP_TO_STATUS(SCIPsetCharParam(scip_, "lp/initalgorithm", 'p'));
995  if (status_.ok()) status_ = status;
996  return;
997  }
998  default: {
999  SetIntegerParamToUnsupportedValue(MPSolverParameters::LP_ALGORITHM,
1000  lp_algorithm);
1001  return;
1002  }
1003  }
1004 }
1005 
1006 void SCIPInterface::SetUnsupportedIntegerParam(
1009  if (status_.ok()) {
1010  status_ = absl::InvalidArgumentError(absl::StrFormat(
1011  "Tried to set unsupported integer parameter %d", param));
1012  }
1013 }
1014 
1015 void SCIPInterface::SetIntegerParamToUnsupportedValue(
1018  if (status_.ok()) {
1019  status_ = absl::InvalidArgumentError(absl::StrFormat(
1020  "Tried to set integer parameter %d to unsupported value %d", param,
1021  value));
1022  }
1023 }
1024 
1025 absl::Status SCIPInterface::SetNumThreads(int num_threads) {
1026  if (SetSolverSpecificParametersAsString(
1027  absl::StrFormat("parallel/maxnthreads = %d\n", num_threads))) {
1028  return absl::OkStatus();
1029  }
1030  return absl::InternalError(
1031  "Could not set parallel/maxnthreads, which may "
1032  "indicate that SCIP API has changed.");
1033 }
1034 
1035 bool SCIPInterface::SetSolverSpecificParametersAsString(
1036  const std::string& parameters) {
1037  const absl::Status s =
1039  if (!s.ok()) {
1040  LOG(WARNING) << "Failed to set SCIP parameter string: " << parameters
1041  << ", error is: " << s;
1042  }
1043  return s.ok();
1044 }
1045 
1047  public:
1049  bool at_integer_solution)
1050  : scip_context_(scip_context),
1051  at_integer_solution_(at_integer_solution) {}
1052 
1053  MPCallbackEvent Event() override {
1054  if (at_integer_solution_) {
1056  }
1058  }
1059 
1060  bool CanQueryVariableValues() override {
1061  return !scip_context_->is_pseudo_solution();
1062  }
1063 
1064  double VariableValue(const MPVariable* variable) override {
1066  return scip_context_->VariableValue(variable);
1067  }
1068 
1069  void AddCut(const LinearRange& cutting_plane) override {
1070  CallbackRangeConstraint constraint;
1071  constraint.is_cut = true;
1072  constraint.range = cutting_plane;
1073  constraint.local = false;
1074  constraints_added_.push_back(std::move(constraint));
1075  }
1076 
1077  void AddLazyConstraint(const LinearRange& lazy_constraint) override {
1078  CallbackRangeConstraint constraint;
1079  constraint.is_cut = false;
1080  constraint.range = lazy_constraint;
1081  constraint.local = false;
1082  constraints_added_.push_back(std::move(constraint));
1083  }
1084 
1086  const absl::flat_hash_map<const MPVariable*, double>& solution) override {
1087  LOG(FATAL) << "SuggestSolution() not currently supported for SCIP.";
1088  }
1089 
1091  // scip_context_->NumNodesProcessed() returns:
1092  // 0 before the root node is solved, e.g. if a heuristic finds a solution.
1093  // 1 at the root node
1094  // > 1 after the root node.
1095  // The NumExploredNodes spec requires that we return 0 at the root node,
1096  // (this is consistent with gurobi). Below is a bandaid to try and make the
1097  // behavior consistent, although some information is lost.
1098  return std::max(int64{0}, scip_context_->NumNodesProcessed() - 1);
1099  }
1100 
1101  const std::vector<CallbackRangeConstraint>& constraints_added() {
1102  return constraints_added_;
1103  }
1104 
1105  private:
1106  const ScipConstraintHandlerContext* scip_context_;
1107  bool at_integer_solution_;
1108  // second value of pair is true for cuts and false for lazy constraints.
1109  std::vector<CallbackRangeConstraint> constraints_added_;
1110 };
1111 
1113  MPCallback* mp_callback)
1114  : ScipConstraintHandler<EmptyStruct>(
1115  // MOE(begin-strip):
1116  {/*name=*/"mp_solver_constraint_handler",
1117  /*description=*/
1118  "A single constraint handler for all MPSolver models."}
1119  // MOE(end-strip-and-replace): ScipConstraintHandlerDescription()
1120  ),
1121  mp_callback_(mp_callback) {}
1122 
1123 std::vector<CallbackRangeConstraint>
1125  const ScipConstraintHandlerContext& context, const EmptyStruct&) {
1126  return SeparateSolution(context, /*at_integer_solution=*/false);
1127 }
1128 
1129 std::vector<CallbackRangeConstraint>
1131  const ScipConstraintHandlerContext& context, const EmptyStruct&) {
1132  return SeparateSolution(context, /*at_integer_solution=*/true);
1133 }
1134 
1135 std::vector<CallbackRangeConstraint>
1136 ScipConstraintHandlerForMPCallback::SeparateSolution(
1138  const bool at_integer_solution) {
1139  ScipMPCallbackContext mp_context(&context, at_integer_solution);
1140  mp_callback_->RunCallback(&mp_context);
1141  return mp_context.constraints_added();
1142 }
1143 
1145  if (callback_ != nullptr) {
1146  callback_reset_ = true;
1147  }
1148  callback_ = mp_callback;
1149 }
1150 
1152  return new SCIPInterface(solver);
1153 }
1154 
1155 } // namespace operations_research
1156 #endif // #if defined(USE_SCIP)
1157 
1158 #undef RETURN_AND_STORE_IF_SCIP_ERROR
1159 #undef RETURN_IF_ALREADY_IN_ERROR_STATE
1160 #undef RETURN_ABNORMAL_IF_BAD_STATUS
1161 #undef RETURN_ABNORMAL_IF_SCIP_ERROR
int64 max
Definition: alldiff_cst.cc:139
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
#define CHECK(condition)
Definition: base/logging.h:495
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:886
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:697
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:888
#define LOG(severity)
Definition: base/logging.h:420
#define DCHECK(condition)
Definition: base/logging.h:884
#define VLOG(verboselevel)
Definition: base/logging.h:978
void Start()
Definition: timer.h:31
absl::Duration GetDuration() const
Definition: timer.h:48
void Restart()
Definition: timer.h:35
An expression of the form:
Definition: linear_expr.h:192
virtual void RunCallback(MPCallbackContext *callback_context)=0
The class for constraints of a Mathematical Programming (MP) model.
int index() const
Returns the index of the constraint in the MPSolver::constraints_.
double offset() const
Gets the constant term in the objective.
This mathematical programming (MP) solver class is the main class though which users build and solve ...
const MPObjective & Objective() const
Returns the objective object.
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
@ NOT_SOLVED
not been solved yet.
@ INFEASIBLE
proven infeasible.
@ UNBOUNDED
proven unbounded.
@ ABNORMAL
abnormal, i.e., error of some kind.
bool SetSolverSpecificParametersAsString(const std::string &parameters)
Advanced usage: pass solver specific parameters in text format.
int GetNumThreads() const
Returns the number of threads to be used during solve.
BasisStatus
Advanced usage: possible basis status values for a variable and the slack variable of a linear constr...
static constexpr int64 kUnknownNumberOfNodes
virtual void SetIntegerParamToUnsupportedValue(MPSolverParameters::IntegerParam param, int value)
static constexpr int64 kUnknownNumberOfIterations
void set_constraint_as_extracted(int ct_index, bool extracted)
void SetMIPParameters(const MPSolverParameters &param)
bool constraint_is_extracted(int ct_index) const
bool variable_is_extracted(int var_index) const
virtual void SetUnsupportedIntegerParam(MPSolverParameters::IntegerParam param)
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.
@ INCREMENTALITY_OFF
Start solve from scratch.
IntegerParam
Enumeration of parameters that take integer or categorical values.
@ LP_ALGORITHM
Algorithm to solve linear programs.
@ SCALING
Advanced usage: enable or disable matrix scaling.
@ PRESOLVE
Advanced usage: presolve mode.
@ INCREMENTALITY
Advanced usage: incrementality from one solve to the next.
int GetIntegerParam(MPSolverParameters::IntegerParam param) const
Returns the value of an integer parameter.
The class for variables of a Mathematical Programming (MP) model.
int index() const
Returns the index of the variable in the MPSolver::variables_.
void BranchingPriorityChangedForVariable(int var_index) override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
void AddRowConstraint(MPConstraint *ct) override
bool IsContinuous() const override
void SetConstraintBounds(int row_index, double lb, double ub) override
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
void ClearConstraint(MPConstraint *constraint) override
MPSolver::BasisStatus row_status(int constraint_index) const override
bool SupportsCallbacks() const override
void SetVariableInteger(int var_index, bool integer) override
void SetCallback(MPCallback *mp_callback) override
void SetObjectiveOffset(double value) override
void AddVariable(MPVariable *var) override
std::string SolverVersion() const override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
bool AddIndicatorConstraint(MPConstraint *ct) override
void SetVariableBounds(int var_index, double lb, double ub) override
absl::optional< MPSolutionResponse > DirectlySolveProto(const MPModelRequest &request) override
void SetOptimizationDirection(bool maximize) override
MPSolver::BasisStatus column_status(int variable_index) const override
int64 iterations() const override
double VariableValue(const MPVariable *variable) const
std::vector< CallbackRangeConstraint > SeparateFractionalSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
std::vector< CallbackRangeConstraint > SeparateIntegerSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
ScipMPCallbackContext(const ScipConstraintHandlerContext *scip_context, bool at_integer_solution)
const std::vector< CallbackRangeConstraint > & constraints_added()
void AddLazyConstraint(const LinearRange &lazy_constraint) override
void AddCut(const LinearRange &cutting_plane) override
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
double VariableValue(const MPVariable *variable) override
SatParameters parameters
SharedResponseManager * response
const Constraint * ct
int64 value
IntVar * var
Definition: expr_array.cc:1858
GurobiMPCallbackContext * context
int64_t int64
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
const int WARNING
Definition: log_severity.h:31
const int INFO
Definition: log_severity.h:31
const int FATAL
Definition: log_severity.h:32
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Definition: cleanup.h:120
The vehicle routing library lets one model and solve generic vehicle routing problems ranging from th...
MPSolverInterface * BuildSCIPInterface(MPSolver *const solver)
absl::StatusOr< MPSolutionResponse > ScipSolveProto(const MPModelRequest &request)
absl::Status LegacyScipSetSolverSpecificParameters(const std::string &parameters, SCIP *scip)
int index
Definition: pack.cc:508
int64 coefficient
#define SCIP_TO_STATUS(x)
#define RETURN_IF_SCIP_ERROR(x)
ABSL_FLAG(bool, scip_feasibility_emphasis, false, "When true, emphasize search towards feasibility. This may or " "may not result in speedups in some problems.")
#define RETURN_IF_ALREADY_IN_ERROR_STATE
#define RETURN_ABNORMAL_IF_SCIP_ERROR(x)
#define RETURN_AND_STORE_IF_SCIP_ERROR(x)
#define RETURN_ABNORMAL_IF_BAD_STATUS