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