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"
31#include "ortools/base/hash.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
50ABSL_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
54namespace operations_research {
55namespace {
56// See the class ScipConstraintHandlerForMPCallback below.
57struct EmptyStruct {};
58} // namespace
59
60class 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;
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
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
284absl::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
309SCIP* 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
341void 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.
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
357void 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
378void 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_);
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];
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;
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
848void 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
863absl::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
886int 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
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
909int64_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
918void SCIPInterface::SetParameters(const MPSolverParameters& param) {
919 SetCommonParameters(param);
920 SetMIPParameters(param);
921}
922
923void 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
939void 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
946void 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
952void 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
974void 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".
981void 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
1011void SCIPInterface::SetUnsupportedIntegerParam(
1014 if (status_.ok()) {
1015 status_ = absl::InvalidArgumentError(absl::StrFormat(
1016 "Tried to set unsupported integer parameter %d", param));
1017 }
1018}
1019
1020void 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
1030absl::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
1040bool 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
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
1128std::vector<CallbackRangeConstraint>
1130 const ScipConstraintHandlerContext& context, const EmptyStruct&) {
1131 return SeparateSolution(context, /*at_integer_solution=*/false);
1132}
1133
1134std::vector<CallbackRangeConstraint>
1136 const ScipConstraintHandlerContext& context, const EmptyStruct&) {
1137 return SeparateSolution(context, /*at_integer_solution=*/true);
1138}
1139
1140std::vector<CallbackRangeConstraint>
1141ScipConstraintHandlerForMPCallback::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
int64_t max
Definition: alldiff_cst.cc:140
#define LOG_IF(severity, condition)
Definition: base/logging.h:476
#define CHECK(condition)
Definition: base/logging.h:492
#define DCHECK_NE(val1, val2)
Definition: base/logging.h:888
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:699
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:890
#define LOG(severity)
Definition: base/logging.h:417
#define DCHECK(condition)
Definition: base/logging.h:886
#define VLOG(verboselevel)
Definition: base/logging.h:980
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 ...
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.
const MPObjective & Objective() const
Returns the objective object.
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
absl::optional< MPSolutionResponse > DirectlySolveProto(const MPModelRequest &request, std::atomic< bool > *interrupt) override
std::string SolverVersion() const override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
bool AddIndicatorConstraint(MPConstraint *ct) override
int64_t nodes() const override
void SetVariableBounds(int var_index, double lb, double ub) override
int64_t iterations() const override
void SetOptimizationDirection(bool maximize) override
MPSolver::BasisStatus column_status(int variable_index) const override
double VariableValue(const MPVariable *variable) const
std::vector< CallbackRangeConstraint > SeparateFractionalSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
std::vector< CallbackRangeConstraint > SeparateIntegerSolution(const ScipConstraintHandlerContext &context, const EmptyStruct &) override
ScipMPCallbackContext(const ScipConstraintHandlerContext *scip_context, bool at_integer_solution)
const std::vector< CallbackRangeConstraint > & constraints_added()
void AddLazyConstraint(const LinearRange &lazy_constraint) override
void AddCut(const LinearRange &cutting_plane) override
double SuggestSolution(const absl::flat_hash_map< const MPVariable *, double > &solution) override
double VariableValue(const MPVariable *variable) override
SatParameters parameters
SharedResponseManager * response
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
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