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