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