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