Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
gscip.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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
15
16#include <algorithm>
17#include <cstdint>
18#include <cstdio>
19#include <limits>
20#include <memory>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/base/optimization.h"
26#include "absl/cleanup/cleanup.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/container/flat_hash_set.h"
29#include "absl/log/check.h"
30#include "absl/log/log.h"
31#include "absl/memory/memory.h"
32#include "absl/status/status.h"
33#include "absl/status/statusor.h"
34#include "absl/strings/str_cat.h"
35#include "absl/strings/str_format.h"
36#include "absl/strings/string_view.h"
37#include "absl/synchronization/mutex.h"
38#include "absl/types/span.h"
39#include "lpi/lpi.h"
40#include "scip/cons_and.h"
41#include "scip/cons_indicator.h"
42#include "scip/cons_linear.h"
43#include "scip/cons_or.h"
44#if SCIP_VERSION_MAJOR >= 10
45#include "scip/cons_nonlinear.h"
46#define SCIPcreateConsQuadratic SCIPcreateConsQuadraticNonlinear
47#else
48#include "scip/cons_quadratic.h"
49#endif // SCIP_VERSION_MAJOR >= 10
57#include "scip/cons_sos1.h"
58#include "scip/cons_sos2.h"
59#include "scip/def.h"
60#include "scip/pub_cons.h"
61#include "scip/pub_var.h"
62#include "scip/scip_cons.h"
63#include "scip/scip_general.h"
64#include "scip/scip_message.h"
65#include "scip/scip_numerics.h"
66#include "scip/scip_param.h"
67#include "scip/scip_prob.h"
68#include "scip/scip_sol.h"
69#include "scip/scip_solve.h"
70#include "scip/scip_solvingstats.h"
71#include "scip/scip_var.h"
72#include "scip/scipdefplugins.h"
73#include "scip/type_cons.h"
74#include "scip/type_event.h"
75#include "scip/type_paramset.h"
76#include "scip/type_prob.h"
77#include "scip/type_retcode.h"
78#include "scip/type_scip.h"
79#include "scip/type_set.h"
80#include "scip/type_sol.h"
81#include "scip/type_stat.h"
82#include "scip/type_var.h"
83
84namespace operations_research {
85
86#define RETURN_ERROR_UNLESS(x) \
87 if (!(x)) \
88 return util::StatusBuilder(absl::InvalidArgumentError(absl::StrFormat( \
89 "Condition violated at %s:%d: %s", __FILE__, __LINE__, #x)))
90
91namespace {
92
93constexpr absl::string_view kLinearConstraintHandlerName = "linear";
94
95SCIP_VARTYPE ConvertVarType(const GScipVarType var_type) {
96 switch (var_type) {
98 return SCIP_VARTYPE_CONTINUOUS;
100 return SCIP_VARTYPE_BINARY;
102 return SCIP_VARTYPE_IMPLINT;
104 return SCIP_VARTYPE_INTEGER;
105 }
106 ABSL_UNREACHABLE();
107}
108
109GScipVarType ConvertVarType(const SCIP_VARTYPE var_type) {
110 switch (var_type) {
111 case SCIP_VARTYPE_CONTINUOUS:
113 case SCIP_VARTYPE_IMPLINT:
115 case SCIP_VARTYPE_INTEGER:
117 case SCIP_VARTYPE_BINARY:
119 }
120 LOG(FATAL) << "Unrecognized SCIP_VARTYPE: " << var_type;
121}
122
123GScipOutput::Status ConvertStatus(const SCIP_STATUS scip_status) {
124 switch (scip_status) {
125 case SCIP_STATUS_UNKNOWN:
127 case SCIP_STATUS_USERINTERRUPT:
129 case SCIP_STATUS_BESTSOLLIMIT:
131 case SCIP_STATUS_MEMLIMIT:
133 case SCIP_STATUS_NODELIMIT:
135 case SCIP_STATUS_RESTARTLIMIT:
137 case SCIP_STATUS_SOLLIMIT:
139 case SCIP_STATUS_STALLNODELIMIT:
141 case SCIP_STATUS_TIMELIMIT:
143 case SCIP_STATUS_TOTALNODELIMIT:
145 case SCIP_STATUS_OPTIMAL:
147 case SCIP_STATUS_GAPLIMIT:
149 case SCIP_STATUS_INFEASIBLE:
151 case SCIP_STATUS_UNBOUNDED:
153 case SCIP_STATUS_INFORUNBD:
155 case SCIP_STATUS_TERMINATE:
157 default:
158 LOG(FATAL) << "Unrecognized scip status: " << scip_status;
159 }
160}
161
162SCIP_PARAMEMPHASIS ConvertEmphasis(
163 const GScipParameters::Emphasis gscip_emphasis) {
164 switch (gscip_emphasis) {
166 return SCIP_PARAMEMPHASIS_DEFAULT;
168 return SCIP_PARAMEMPHASIS_CPSOLVER;
170 return SCIP_PARAMEMPHASIS_EASYCIP;
172 return SCIP_PARAMEMPHASIS_FEASIBILITY;
174 return SCIP_PARAMEMPHASIS_HARDLP;
176 return SCIP_PARAMEMPHASIS_OPTIMALITY;
178 return SCIP_PARAMEMPHASIS_COUNTER;
180 return SCIP_PARAMEMPHASIS_PHASEFEAS;
182 return SCIP_PARAMEMPHASIS_PHASEIMPROVE;
184 return SCIP_PARAMEMPHASIS_PHASEPROOF;
185 default:
186 LOG(FATAL) << "Unrecognized gscip_emphasis: "
187 << ProtoEnumToString(gscip_emphasis);
188 }
189}
190
191SCIP_PARAMSETTING ConvertMetaParamValue(
192 const GScipParameters::MetaParamValue gscip_meta_param_value) {
193 switch (gscip_meta_param_value) {
195 return SCIP_PARAMSETTING_DEFAULT;
197 return SCIP_PARAMSETTING_AGGRESSIVE;
199 return SCIP_PARAMSETTING_FAST;
201 return SCIP_PARAMSETTING_OFF;
202 default:
203 LOG(FATAL) << "Unrecognized gscip_meta_param_value: "
204 << ProtoEnumToString(gscip_meta_param_value);
205 }
206}
207
208absl::Status CheckSolutionsInOrder(const GScipResult& result,
209 const bool is_maximize) {
210 auto objective_as_good_as = [is_maximize](double left, double right) {
211 if (is_maximize) {
212 return left >= right;
213 }
214 return left <= right;
215 };
216 for (int i = 1; i < result.objective_values.size(); ++i) {
217 const double previous = result.objective_values[i - 1];
218 const double current = result.objective_values[i];
219 if (!objective_as_good_as(previous, current)) {
221 << "Expected SCIP solutions to be in best objective order "
222 "first, but for "
223 << (is_maximize ? "maximization" : "minimization")
224 << " problem, the " << i - 1 << " objective is " << previous
225 << " and the " << i << " objective is " << current;
226 }
227 }
228 return absl::OkStatus();
229}
230
231} // namespace
232
233void GScip::InterruptEventHandler::set_interrupter(
234 const GScip::Interrupter* interrupter) {
235 interrupter_ = interrupter;
236}
237
238GScip::InterruptEventHandler::InterruptEventHandler()
239 : GScipEventHandler(
240 {.name = "interrupt event handler",
241 .description = "Event handler to call SCIPinterruptSolve() when a "
242 "user SolveInterrupter is triggered."}) {}
243
244SCIP_RETCODE GScip::InterruptEventHandler::Init(GScip* const gscip) {
245 // We don't register any event if we don't have an interrupter.
246 if (interrupter_ == nullptr) {
247 return SCIP_OKAY;
248 }
249
250 // TODO(b/193537362): see if these events are enough or if we should have more
251 // of these.
252 CatchEvent(SCIP_EVENTTYPE_PRESOLVEROUND);
253 CatchEvent(SCIP_EVENTTYPE_NODEEVENT);
254 CatchEvent(SCIP_EVENTTYPE_ROWEVENT);
255
256 return TryCallInterruptIfNeeded(gscip);
257}
258
259SCIP_RETCODE GScip::InterruptEventHandler::Execute(
260 const GScipEventHandlerContext context) {
261 return TryCallInterruptIfNeeded(context.gscip());
262}
263
264SCIP_RETCODE GScip::InterruptEventHandler::TryCallInterruptIfNeeded(
265 GScip* const gscip) {
266 if (interrupter_ == nullptr) {
267 LOG(WARNING) << "TryCallInterruptIfNeeded() called after interrupter has "
268 "been reset!";
269 return SCIP_OKAY;
270 }
271
272 if (!interrupter_->is_interrupted()) {
273 return SCIP_OKAY;
274 }
275
276 const SCIP_STAGE stage = SCIPgetStage(gscip->scip());
277 switch (stage) {
278 case SCIP_STAGE_INIT:
279 case SCIP_STAGE_FREE:
280 // This should never happen anyway; but if this happens, we may want to
281 // know about it in unit tests.
282 LOG(DFATAL) << "TryCallInterruptIfNeeded() called in stage "
283 << (stage == SCIP_STAGE_INIT ? "INIT" : "FREE");
284 return SCIP_OKAY;
285 case SCIP_STAGE_INITSOLVE:
286 LOG(WARNING) << "TryCallInterruptIfNeeded() called in INITSOLVE stage; "
287 "we can't call SCIPinterruptSolve() in this stage.";
288 return SCIP_OKAY;
289 default:
290 return SCIPinterruptSolve(gscip->scip());
291 }
292}
293
295 static GScipVariableOptions var_options;
296 return var_options;
297}
298
300 static GScipConstraintOptions constraint_options;
301 return constraint_options;
302}
303
304absl::Status GScip::SetParams(const GScipParameters& params) {
305 if (params.has_silence_output()) {
306 SCIPsetMessagehdlrQuiet(scip_, params.silence_output());
307 }
308 if (!params.search_logs_filename().empty()) {
309 SCIPsetMessagehdlrLogfile(scip_, params.search_logs_filename().c_str());
310 }
311
312 const SCIP_Bool set_param_quiet =
313 static_cast<SCIP_Bool>(!params.silence_output());
314
315 RETURN_IF_SCIP_ERROR(SCIPsetEmphasis(
316 scip_, ConvertEmphasis(params.emphasis()), set_param_quiet));
317 if (params.has_heuristics()) {
318 RETURN_IF_SCIP_ERROR(SCIPsetHeuristics(
319 scip_, ConvertMetaParamValue(params.heuristics()), set_param_quiet));
320 }
321 if (params.has_presolve()) {
322 RETURN_IF_SCIP_ERROR(SCIPsetPresolving(
323 scip_, ConvertMetaParamValue(params.presolve()), set_param_quiet));
324 }
325 if (params.has_separating()) {
326 RETURN_IF_SCIP_ERROR(SCIPsetSeparating(
327 scip_, ConvertMetaParamValue(params.separating()), set_param_quiet));
328 }
329 for (const auto& bool_param : params.bool_params()) {
331 SCIPsetBoolParam(scip_, bool_param.first.c_str(), bool_param.second));
332 }
333 for (const auto& int_param : params.int_params()) {
335 SCIPsetIntParam(scip_, int_param.first.c_str(), int_param.second));
336 }
337 for (const auto& long_param : params.long_params()) {
338 RETURN_IF_SCIP_ERROR(SCIPsetLongintParam(scip_, long_param.first.c_str(),
339 long_param.second));
340 }
341 for (const auto& char_param : params.char_params()) {
342 if (char_param.second.size() != 1) {
343 return absl::InvalidArgumentError(
344 absl::StrCat("Character parameters must be single character strings, "
345 "but parameter: ",
346 char_param.first, " was: ", char_param.second));
347 }
348 RETURN_IF_SCIP_ERROR(SCIPsetCharParam(scip_, char_param.first.c_str(),
349 char_param.second[0]));
350 }
351 for (const auto& string_param : params.string_params()) {
352 RETURN_IF_SCIP_ERROR(SCIPsetStringParam(scip_, string_param.first.c_str(),
353 string_param.second.c_str()));
354 }
355 for (const auto& real_param : params.real_params()) {
357 SCIPsetRealParam(scip_, real_param.first.c_str(), real_param.second));
358 }
359 return absl::OkStatus();
360}
361
362absl::StatusOr<std::unique_ptr<GScip>> GScip::Create(
363 const std::string& problem_name) {
364 SCIP* scip = nullptr;
365 RETURN_IF_SCIP_ERROR(SCIPcreate(&scip));
366 absl::Cleanup scip_cleanup = [&]() { SCIPfree(&scip); };
367 RETURN_IF_SCIP_ERROR(SCIPincludeDefaultPlugins(scip));
368 RETURN_IF_SCIP_ERROR(SCIPcreateProbBasic(scip, problem_name.c_str()));
369 auto result = absl::WrapUnique(new GScip(scip));
370 // GScip takes ownership of `scip`.
371 std::move(scip_cleanup).Cancel();
372 RETURN_IF_ERROR(result->interrupt_event_handler_.Register(result.get()));
373 return result;
374}
375
376GScip::GScip(SCIP* scip) : scip_(scip) {}
377
378double GScip::ScipInf() { return SCIPinfinity(scip_); }
379
380absl::Status GScip::FreeTransform() {
381 return SCIP_TO_STATUS(SCIPfreeTransform(scip_));
382}
383
384std::string GScip::ScipVersion() {
385 return absl::StrFormat("SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
386 SCIPminorVersion(), SCIPtechVersion(),
387 SCIPlpiGetSolverName());
388}
389
391 absl::Status error_status) {
392 CHECK(!error_status.ok());
393 {
394 const absl::MutexLock lock(callback_status_mutex_);
395 if (!callback_status_.ok()) {
396 return;
397 }
398 callback_status_ = std::move(error_status);
399 }
400 // We are already in an error state, do not propagate.
401 const absl::Status status = SCIP_TO_STATUS(SCIPinterruptSolve(scip_));
402 if (!status.ok()) {
403 LOG(WARNING) << "Error trying to interrupt solve after error in callback: "
404 << status;
405 }
406}
407
408absl::Status GScip::CleanUp() {
409 if (scip_ != nullptr) {
410 for (SCIP_VAR* variable : variables_) {
411 if (variable != nullptr) {
412 RETURN_IF_SCIP_ERROR(SCIPreleaseVar(scip_, &variable));
413 }
414 }
415 for (SCIP_CONS* constraint : constraints_) {
416 if (constraint != nullptr) {
417 RETURN_IF_SCIP_ERROR(SCIPreleaseCons(scip_, &constraint));
418 }
419 }
420 RETURN_IF_SCIP_ERROR(SCIPfree(&scip_));
421 }
422 return absl::OkStatus();
423}
424
426 const absl::Status clean_up_status = CleanUp();
427 LOG_IF(DFATAL, !clean_up_status.ok()) << clean_up_status;
428}
429
430absl::StatusOr<SCIP_VAR*> GScip::AddVariable(
431 double lb, double ub, double obj_coef, GScipVarType var_type,
432 const std::string& var_name, const GScipVariableOptions& options) {
433 SCIP_VAR* var = nullptr;
435 _ << "invalid lower bound for variable: " << var_name);
437 _ << "invalid upper bound for variable: " << var_name);
438 RETURN_IF_ERROR(CheckScipFinite(obj_coef))
439 << "invalid objective coefficient for variable: " << var_name;
440 RETURN_IF_SCIP_ERROR(SCIPcreateVarBasic(scip_, /*var=*/&var,
441 /*name=*/var_name.c_str(),
442 /*lb=*/lb, /*ub=*/ub,
443 /*obj=*/obj_coef,
444 ConvertVarType(var_type)));
445 RETURN_IF_SCIP_ERROR(SCIPvarSetInitial(var, options.initial));
446 RETURN_IF_SCIP_ERROR(SCIPvarSetRemovable(var, options.removable));
447 RETURN_IF_SCIP_ERROR(SCIPaddVar(scip_, var));
448 if (options.keep_alive) {
449 variables_.insert(var);
450 } else {
451 RETURN_IF_SCIP_ERROR(SCIPreleaseVar(scip_, &var));
452 }
453 return var;
454}
455
456absl::Status GScip::MaybeKeepConstraintAlive(
457 SCIP_CONS* constraint, const GScipConstraintOptions& options) {
458 if (options.keep_alive) {
459 constraints_.insert(constraint);
460 } else {
461 RETURN_IF_SCIP_ERROR(SCIPreleaseCons(scip_, &constraint));
462 }
463 return absl::OkStatus();
464}
465
466absl::StatusOr<SCIP_CONS*> GScip::AddLinearConstraint(
467 const GScipLinearRange& range, const std::string& name,
468 const GScipConstraintOptions& options) {
469 SCIP_CONS* constraint = nullptr;
470 RETURN_ERROR_UNLESS(range.variables.size() == range.coefficients.size())
471 << "Error adding constraint: " << name << ".";
472 OR_ASSIGN_OR_RETURN3(const double lb, ScipInfClamp(range.lower_bound),
473 _ << "invalid lower bound for constraint: " << name);
474 OR_ASSIGN_OR_RETURN3(const double ub, ScipInfClamp(range.upper_bound),
475 _ << "invalid upper bound for constraint: " << name);
476 for (int i = 0; i < range.coefficients.size(); ++i) {
477 RETURN_IF_ERROR(CheckScipFinite(range.coefficients[i]))
478 << "invalid coefficient at index " << i << " of constraint: " << name;
479 }
480 RETURN_IF_SCIP_ERROR(SCIPcreateConsLinear(
481 scip_, &constraint, name.c_str(), range.variables.size(),
482 const_cast<SCIP_VAR**>(range.variables.data()),
483 const_cast<double*>(range.coefficients.data()),
484 /*lhs=*/lb, /*rhs=*/ub,
485 /*initial=*/options.initial,
486 /*separate=*/options.separate,
487 /*enforce=*/options.enforce,
488 /*check=*/options.check,
489 /*propagate=*/options.propagate,
490 /*local=*/options.local,
491 /*modifiable=*/options.modifiable,
492 /*dynamic=*/options.dynamic,
493 /*removable=*/options.removable,
494 /*stickingatnode=*/options.sticking_at_node));
495 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
496 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
497 return constraint;
498}
499
500absl::StatusOr<SCIP_CONS*> GScip::AddQuadraticConstraint(
501 const GScipQuadraticRange& range, const std::string& name,
502 const GScipConstraintOptions& options) {
503 SCIP_CONS* constraint = nullptr;
504 const int num_lin_vars = range.linear_variables.size();
505 RETURN_ERROR_UNLESS(num_lin_vars == range.linear_coefficients.size())
506 << "Error adding quadratic constraint: " << name << " in linear term.";
507 const int num_quad_vars = range.quadratic_variables1.size();
508 RETURN_ERROR_UNLESS(num_quad_vars == range.quadratic_variables2.size())
509 << "Error adding quadratic constraint: " << name << " in quadratic term.";
510 RETURN_ERROR_UNLESS(num_quad_vars == range.quadratic_coefficients.size())
511 << "Error adding quadratic constraint: " << name << " in quadratic term.";
512 OR_ASSIGN_OR_RETURN3(const double lb, ScipInfClamp(range.lower_bound),
513 _ << "invalid lower bound for constraint: " << name);
514 OR_ASSIGN_OR_RETURN3(const double ub, ScipInfClamp(range.upper_bound),
515 _ << "invalid upper bound for constraint: " << name);
516 for (int i = 0; i < range.linear_coefficients.size(); ++i) {
517 RETURN_IF_ERROR(CheckScipFinite(range.linear_coefficients[i]))
518 << "invalid linear coefficient at index " << i
519 << " of constraint: " << name;
520 }
521 for (int i = 0; i < range.quadratic_coefficients.size(); ++i) {
522 RETURN_IF_ERROR(CheckScipFinite(range.quadratic_coefficients[i]))
523 << "invalid quadratic coefficient at index " << i
524 << " of constraint: " << name;
525 }
526 RETURN_IF_SCIP_ERROR(SCIPcreateConsQuadratic(
527 scip_, &constraint, name.c_str(), num_lin_vars,
528 const_cast<SCIP_Var**>(range.linear_variables.data()),
529 const_cast<double*>(range.linear_coefficients.data()), num_quad_vars,
530 const_cast<SCIP_Var**>(range.quadratic_variables1.data()),
531 const_cast<SCIP_Var**>(range.quadratic_variables2.data()),
532 const_cast<double*>(range.quadratic_coefficients.data()),
533 /*lhs=*/lb, /*rhs=*/ub,
534 /*initial=*/options.initial,
535 /*separate=*/options.separate,
536 /*enforce=*/options.enforce,
537 /*check=*/options.check,
538 /*propagate=*/options.propagate,
539 /*local=*/options.local,
540 /*modifiable=*/options.modifiable,
541 /*dynamic=*/options.dynamic,
542 /*removable=*/options.removable));
543 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
544 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
545 return constraint;
546}
547
548absl::StatusOr<SCIP_CONS*> GScip::AddIndicatorConstraint(
549 const GScipIndicatorConstraint& indicator_constraint,
550 const std::string& name, const GScipConstraintOptions& options) {
551 SCIP_VAR* indicator = indicator_constraint.indicator_variable;
552 RETURN_ERROR_UNLESS(indicator != nullptr)
553 << "Error adding indicator constraint: " << name << ".";
554 if (indicator_constraint.negate_indicator) {
555 RETURN_IF_SCIP_ERROR(SCIPgetNegatedVar(scip_, indicator, &indicator));
556 }
557
558 SCIP_CONS* constraint = nullptr;
559 RETURN_ERROR_UNLESS(indicator_constraint.variables.size() ==
560 indicator_constraint.coefficients.size())
561 << "Error adding indicator constraint: " << name << ".";
562 OR_ASSIGN_OR_RETURN3(const double ub,
563 ScipInfClamp(indicator_constraint.upper_bound),
564 _ << "invalid upper bound for constraint: " << name);
565 for (int i = 0; i < indicator_constraint.coefficients.size(); ++i) {
566 RETURN_IF_ERROR(CheckScipFinite(indicator_constraint.coefficients[i]))
567 << "invalid coefficient at index " << i << " of constraint: " << name;
568 }
569 RETURN_IF_SCIP_ERROR(SCIPcreateConsIndicator(
570 scip_, &constraint, name.c_str(), indicator,
571 indicator_constraint.variables.size(),
572 const_cast<SCIP_Var**>(indicator_constraint.variables.data()),
573 const_cast<double*>(indicator_constraint.coefficients.data()),
574 /*rhs=*/ub,
575 /*initial=*/options.initial,
576 /*separate=*/options.separate,
577 /*enforce=*/options.enforce,
578 /*check=*/options.check,
579 /*propagate=*/options.propagate,
580 /*local=*/options.local,
581 /*dynamic=*/options.dynamic,
582 /*removable=*/options.removable,
583 /*stickingatnode=*/options.sticking_at_node));
584 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
585 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
586 return constraint;
587}
588
589absl::StatusOr<SCIP_CONS*> GScip::AddAndConstraint(
590 const GScipLogicalConstraintData& logical_data, const std::string& name,
591 const GScipConstraintOptions& options) {
592 RETURN_ERROR_UNLESS(logical_data.resultant != nullptr)
593 << "Error adding and constraint: " << name << ".";
594 SCIP_CONS* constraint = nullptr;
596 SCIPcreateConsAnd(scip_, &constraint, name.c_str(),
597 logical_data.resultant, logical_data.operators.size(),
598 const_cast<SCIP_VAR**>(logical_data.operators.data()),
599 /*initial=*/options.initial,
600 /*separate=*/options.separate,
601 /*enforce=*/options.enforce,
602 /*check=*/options.check,
603 /*propagate=*/options.propagate,
604 /*local=*/options.local,
605 /*modifiable=*/options.modifiable,
606 /*dynamic=*/options.dynamic,
607 /*removable=*/options.removable,
608 /*stickingatnode=*/options.sticking_at_node));
609 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
610 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
611 return constraint;
612}
613
614absl::StatusOr<SCIP_CONS*> GScip::AddOrConstraint(
615 const GScipLogicalConstraintData& logical_data, const std::string& name,
616 const GScipConstraintOptions& options) {
617 RETURN_ERROR_UNLESS(logical_data.resultant != nullptr)
618 << "Error adding or constraint: " << name << ".";
619 SCIP_CONS* constraint = nullptr;
621 SCIPcreateConsOr(scip_, &constraint, name.c_str(), logical_data.resultant,
622 logical_data.operators.size(),
623 const_cast<SCIP_Var**>(logical_data.operators.data()),
624 /*initial=*/options.initial,
625 /*separate=*/options.separate,
626 /*enforce=*/options.enforce,
627 /*check=*/options.check,
628 /*propagate=*/options.propagate,
629 /*local=*/options.local,
630 /*modifiable=*/options.modifiable,
631 /*dynamic=*/options.dynamic,
632 /*removable=*/options.removable,
633 /*stickingatnode=*/options.sticking_at_node));
634 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
635 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
636 return constraint;
637}
638
639namespace {
640
641absl::Status ValidateSOSData(const GScipSOSData& sos_data,
642 absl::string_view name) {
643 RETURN_ERROR_UNLESS(!sos_data.variables.empty())
644 << "Error adding SOS constraint: " << name << ".";
645 if (!sos_data.weights.empty()) {
646 RETURN_ERROR_UNLESS(sos_data.variables.size() == sos_data.weights.size())
647 << " Error adding SOS constraint: " << name << ".";
648 }
649 absl::flat_hash_set<double> distinct_weights;
650 for (const double w : sos_data.weights) {
651 RETURN_ERROR_UNLESS(!distinct_weights.contains(w))
652 << "Error adding SOS constraint: " << name
653 << ", weights must be distinct, but found value " << w << " twice.";
654 distinct_weights.insert(w);
655 }
656 return absl::OkStatus();
657}
658
659} // namespace
660
661absl::StatusOr<SCIP_CONS*> GScip::AddSOS1Constraint(
662 const GScipSOSData& sos_data, const std::string& name,
663 const GScipConstraintOptions& options) {
664 RETURN_IF_ERROR(ValidateSOSData(sos_data, name));
665 SCIP_CONS* constraint = nullptr;
666 double* weights = nullptr;
667 if (!sos_data.weights.empty()) {
668 weights = const_cast<double*>(sos_data.weights.data());
669 }
670
671 RETURN_IF_SCIP_ERROR(SCIPcreateConsSOS1(
672 scip_, &constraint, name.c_str(), sos_data.variables.size(),
673 const_cast<SCIP_Var**>(sos_data.variables.data()), weights,
674 /*initial=*/options.initial,
675 /*separate=*/options.separate,
676 /*enforce=*/options.enforce,
677 /*check=*/options.check,
678 /*propagate=*/options.propagate,
679 /*local=*/options.local,
680 /*dynamic=*/options.dynamic,
681 /*removable=*/options.removable,
682 /*stickingatnode=*/options.sticking_at_node));
683 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
684 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
685 return constraint;
686}
687
688absl::StatusOr<SCIP_CONS*> GScip::AddSOS2Constraint(
689 const GScipSOSData& sos_data, const std::string& name,
690 const GScipConstraintOptions& options) {
691 RETURN_IF_ERROR(ValidateSOSData(sos_data, name));
692 SCIP_CONS* constraint = nullptr;
693 double* weights = nullptr;
694 if (!sos_data.weights.empty()) {
695 weights = const_cast<double*>(sos_data.weights.data());
696 }
697 RETURN_IF_SCIP_ERROR(SCIPcreateConsSOS2(
698 scip_, &constraint, name.c_str(), sos_data.variables.size(),
699 const_cast<SCIP_Var**>(sos_data.variables.data()), weights,
700 /*initial=*/options.initial,
701 /*separate=*/options.separate,
702 /*enforce=*/options.enforce,
703 /*check=*/options.check,
704 /*propagate=*/options.propagate,
705 /*local=*/options.local,
706 /*dynamic=*/options.dynamic,
707 /*removable=*/options.removable,
708 /*stickingatnode=*/options.sticking_at_node));
709 RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
710 RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
711 return constraint;
712}
713
714absl::Status GScip::SetMaximize(bool is_maximize) {
715 RETURN_IF_SCIP_ERROR(SCIPsetObjsense(
716 scip_, is_maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
717 return absl::OkStatus();
718}
719
720absl::Status GScip::SetObjectiveOffset(double offset) {
721 RETURN_IF_ERROR(CheckScipFinite(offset)) << "invalid objective offset";
722 double old_offset = SCIPgetOrigObjoffset(scip_);
723 double delta_offset = offset - old_offset;
724 RETURN_IF_SCIP_ERROR(SCIPaddOrigObjoffset(scip_, delta_offset));
725 return absl::OkStatus();
726}
727
729 return SCIPgetObjsense(scip_) == SCIP_OBJSENSE_MAXIMIZE;
730}
731
732double GScip::ObjectiveOffset() { return SCIPgetOrigObjoffset(scip_); }
733
734absl::Status GScip::SetBranchingPriority(SCIP_VAR* var, int priority) {
735 RETURN_IF_SCIP_ERROR(SCIPchgVarBranchPriority(scip_, var, priority));
736 return absl::OkStatus();
737}
738
739absl::Status GScip::SetLb(SCIP_VAR* var, double lb) {
740 OR_ASSIGN_OR_RETURN3(lb, ScipInfClamp(lb), _ << "invalid lower bound");
741 RETURN_IF_SCIP_ERROR(SCIPchgVarLb(scip_, var, lb));
742 return absl::OkStatus();
743}
744
745absl::Status GScip::SetUb(SCIP_VAR* var, double ub) {
746 OR_ASSIGN_OR_RETURN3(ub, ScipInfClamp(ub), _ << "invalid upper bound");
747 RETURN_IF_SCIP_ERROR(SCIPchgVarUb(scip_, var, ub));
748 return absl::OkStatus();
749}
750
751absl::Status GScip::SetObjCoef(SCIP_VAR* var, double obj_coef) {
752 RETURN_IF_ERROR(CheckScipFinite(obj_coef)) << "invalid objective coefficient";
753 RETURN_IF_SCIP_ERROR(SCIPchgVarObj(scip_, var, obj_coef));
754 return absl::OkStatus();
755}
756
757absl::Status GScip::SetVarType(SCIP_VAR* var, GScipVarType var_type) {
758 SCIP_Bool infeasible;
760 SCIPchgVarType(scip_, var, ConvertVarType(var_type), &infeasible));
761 return absl::OkStatus();
762}
763
764absl::Status GScip::DeleteVariable(SCIP_VAR* var) {
765 SCIP_Bool did_delete;
766 RETURN_IF_SCIP_ERROR(SCIPdelVar(scip_, var, &did_delete));
767 RETURN_ERROR_UNLESS(static_cast<bool>(did_delete))
768 << "Failed to delete variable named: " << Name(var);
769 variables_.erase(var);
770 RETURN_IF_SCIP_ERROR(SCIPreleaseVar(scip_, &var));
771 return absl::OkStatus();
772}
773
775 const absl::flat_hash_set<SCIP_VAR*>& vars) {
776 if (vars.empty()) {
777 return absl::OkStatus();
778 }
779 for (SCIP_CONS* constraint : constraints_) {
780 if (!IsConstraintLinear(constraint)) {
781 return absl::InvalidArgumentError(absl::StrCat(
782 "Model contains nonlinear constraint: ", Name(constraint)));
783 }
784 }
785 return absl::OkStatus();
786}
787
788absl::Status GScip::SafeBulkDelete(const absl::flat_hash_set<SCIP_VAR*>& vars) {
790 if (vars.empty()) {
791 return absl::OkStatus();
792 }
793 // Now, we can assume that all constraints are linear.
794 for (SCIP_CONS* constraint : constraints_) {
795 const absl::Span<SCIP_VAR* const> nonzeros =
796 LinearConstraintVariables(constraint);
797 const std::vector<SCIP_VAR*> nonzeros_copy(nonzeros.begin(),
798 nonzeros.end());
799 for (SCIP_VAR* var : nonzeros_copy) {
800 if (vars.contains(var)) {
801 RETURN_IF_ERROR(SetLinearConstraintCoef(constraint, var, 0.0));
802 }
803 }
804 }
805 for (SCIP_VAR* const var : vars) {
807 }
808 return absl::OkStatus();
809}
810
811double GScip::Lb(SCIP_VAR* var) {
812 return ScipInfUnclamp(SCIPvarGetLbOriginal(var));
813}
814
815double GScip::Ub(SCIP_VAR* var) {
816 return ScipInfUnclamp(SCIPvarGetUbOriginal(var));
817}
818
819double GScip::ObjCoef(SCIP_VAR* var) { return SCIPvarGetObj(var); }
820
822 return ConvertVarType(SCIPvarGetType(var));
823}
824
825absl::string_view GScip::Name(SCIP_VAR* var) { return SCIPvarGetName(var); }
826
827absl::string_view GScip::ConstraintType(SCIP_CONS* constraint) {
828 return absl::string_view(SCIPconshdlrGetName(SCIPconsGetHdlr(constraint)));
829}
830
831bool GScip::IsConstraintLinear(SCIP_CONS* constraint) {
832 return ConstraintType(constraint) == kLinearConstraintHandlerName;
833}
834
835absl::Span<const double> GScip::LinearConstraintCoefficients(
836 SCIP_CONS* constraint) {
837 int num_vars = SCIPgetNVarsLinear(scip_, constraint);
838 return absl::MakeConstSpan(SCIPgetValsLinear(scip_, constraint), num_vars);
839}
840
841absl::Span<SCIP_VAR* const> GScip::LinearConstraintVariables(
842 SCIP_CONS* constraint) {
843 int num_vars = SCIPgetNVarsLinear(scip_, constraint);
844 return absl::MakeConstSpan(SCIPgetVarsLinear(scip_, constraint), num_vars);
845}
846
847double GScip::LinearConstraintLb(SCIP_CONS* constraint) {
848 return ScipInfUnclamp(SCIPgetLhsLinear(scip_, constraint));
849}
850
851double GScip::LinearConstraintUb(SCIP_CONS* constraint) {
852 return ScipInfUnclamp(SCIPgetRhsLinear(scip_, constraint));
853}
854
855absl::string_view GScip::Name(SCIP_CONS* constraint) {
856 return SCIPconsGetName(constraint);
857}
858
859absl::Status GScip::SetLinearConstraintLb(SCIP_CONS* constraint, double lb) {
860 OR_ASSIGN_OR_RETURN3(lb, ScipInfClamp(lb), _ << "invalid lower bound");
861 RETURN_IF_SCIP_ERROR(SCIPchgLhsLinear(scip_, constraint, lb));
862 return absl::OkStatus();
863}
864
865absl::Status GScip::SetLinearConstraintUb(SCIP_CONS* constraint, double ub) {
866 OR_ASSIGN_OR_RETURN3(ub, ScipInfClamp(ub), _ << "invalid upper bound");
867 RETURN_IF_SCIP_ERROR(SCIPchgRhsLinear(scip_, constraint, ub));
868 return absl::OkStatus();
869}
870
871absl::Status GScip::DeleteConstraint(SCIP_CONS* constraint) {
872 RETURN_IF_SCIP_ERROR(SCIPdelCons(scip_, constraint));
873 constraints_.erase(constraint);
874 RETURN_IF_SCIP_ERROR(SCIPreleaseCons(scip_, &constraint));
875 return absl::OkStatus();
876}
877
878absl::Status GScip::SetLinearConstraintCoef(SCIP_CONS* constraint,
879 SCIP_VAR* var, double value) {
880 // TODO(user): this operation is slow (linear in the nnz in the constraint).
881 // It would be better to just use a bulk operation, but there doesn't appear
882 // to be any?
883 RETURN_IF_ERROR(CheckScipFinite(value)) << "invalid coefficient";
884 RETURN_IF_SCIP_ERROR(SCIPchgCoefLinear(scip_, constraint, var, value));
885 return absl::OkStatus();
886}
887
888absl::Status GScip::AddLinearConstraintCoef(SCIP_CONS* const constraint,
889 SCIP_VAR* const var,
890 const double value) {
891 RETURN_IF_ERROR(CheckScipFinite(value)) << "invalid coefficient";
892 RETURN_IF_SCIP_ERROR(SCIPaddCoefLinear(scip_, constraint, var, value));
893 return absl::OkStatus();
894}
895
896absl::StatusOr<GScipHintResult> GScip::SuggestHint(
897 const GScipSolution& partial_solution) {
898 SCIP_SOL* solution;
899 const int scip_num_vars = SCIPgetNOrigVars(scip_);
900 const bool is_solution_partial = partial_solution.size() < scip_num_vars;
901 if (is_solution_partial) {
902 RETURN_IF_SCIP_ERROR(SCIPcreatePartialSol(scip_, &solution, nullptr));
903 } else {
904 // This is actually a full solution
905 RETURN_ERROR_UNLESS(partial_solution.size() == scip_num_vars)
906 << "Error suggesting hint.";
907 RETURN_IF_SCIP_ERROR(SCIPcreateSol(scip_, &solution, nullptr));
908 }
909 for (const auto& var_value_pair : partial_solution) {
910 RETURN_IF_SCIP_ERROR(SCIPsetSolVal(scip_, solution, var_value_pair.first,
911 var_value_pair.second));
912 }
913 if (!is_solution_partial) {
914 SCIP_Bool is_feasible;
915 RETURN_IF_SCIP_ERROR(SCIPcheckSol(
916 scip_, solution, /*printreason=*/false, /*completely=*/true,
917 /*checkbounds=*/true, /*checkintegrality=*/true, /*checklprows=*/true,
918 &is_feasible));
919 if (!static_cast<bool>(is_feasible)) {
920 RETURN_IF_SCIP_ERROR(SCIPfreeSol(scip_, &solution));
922 }
923 }
924 SCIP_Bool is_stored;
925 RETURN_IF_SCIP_ERROR(SCIPaddSolFree(scip_, &solution, &is_stored));
926 if (static_cast<bool>(is_stored)) {
928 } else {
930 }
931}
932
933absl::StatusOr<GScipResult> GScip::Solve(
934 const GScipParameters& params, const GScipMessageHandler message_handler,
935 const Interrupter* const interrupter) {
936 if (InErrorState()) {
937 return absl::InvalidArgumentError(
938 "GScip is in an error state due to a previous GScip::Solve()");
939 }
940 interrupt_event_handler_.set_interrupter(interrupter);
941 const absl::Cleanup interrupt_cleanup = [this]() {
942 interrupt_event_handler_.set_interrupter(nullptr);
943 };
944
945 // A four step process:
946 // 1. Apply parameters.
947 // 2. Solve the problem.
948 // 3. Extract solution and solve statistics.
949 // 4. Prepare the solver for further modification/solves (reset parameters,
950 // free the solutions found).
951 GScipResult result;
952
953 // Step 1: apply parameters.
954 const absl::Status param_status = SetParams(params);
955 if (!param_status.ok()) {
957 // Conversion to std::string for open source build.
959 std::string(param_status.message())); // NOLINT
960 return result;
961 }
962 if (params.print_scip_model()) {
963 RETURN_IF_SCIP_ERROR(SCIPwriteOrigProblem(scip_, nullptr, "cip", FALSE));
964 }
965 if (!params.scip_model_filename().empty()) {
966 RETURN_IF_SCIP_ERROR(SCIPwriteOrigProblem(
967 scip_, params.scip_model_filename().c_str(), "cip", FALSE));
968 }
969 if (params.has_objective_limit()) {
970 OR_ASSIGN_OR_RETURN3(const double scip_obj_limit,
972 _ << "invalid objective_limit");
973 RETURN_IF_SCIP_ERROR(SCIPsetObjlimit(scip_, scip_obj_limit));
974 }
975
976 // Install the message handler if necessary. We do this after setting the
977 // parameters so that parameters that applies to the default message handler
978 // like `quiet` are indeed applied to it and not to our temporary
979 // handler.
982 MessageHandlerPtr previous_handler;
983 MessageHandlerPtr new_handler;
984 if (message_handler != nullptr) {
985 previous_handler = CaptureMessageHandlerPtr(SCIPgetMessagehdlr(scip_));
986 ASSIGN_OR_RETURN(new_handler,
987 internal::MakeSCIPMessageHandler(message_handler));
988 SCIPsetMessagehdlr(scip_, new_handler.get());
989 }
990 // Make sure we prevent any call of message_handler after this function has
991 // returned, until the new_handler is reset (see below).
992 const internal::ScopedSCIPMessageHandlerDisabler new_handler_disabler(
993 new_handler);
994
995 // Step 2: Solve.
996 // NOTE(user): after solve, SCIP will either be in stage PRESOLVING,
997 // SOLVING, OR SOLVED.
998 if (GScipMaxNumThreads(params) > 1) {
999 RETURN_IF_SCIP_ERROR(SCIPsolveConcurrent(scip_));
1000 } else {
1001 RETURN_IF_SCIP_ERROR(SCIPsolve(scip_));
1002 }
1003 const SCIP_STAGE stage = SCIPgetStage(scip_);
1004 if (stage != SCIP_STAGE_PRESOLVING && stage != SCIP_STAGE_PRESOLVED &&
1005 stage != SCIP_STAGE_SOLVING && stage != SCIP_STAGE_SOLVED) {
1008 absl::StrCat("Unexpected SCIP final stage= ", stage,
1009 " was expected to be either "
1010 "SCIP_STAGE_PRESOLVING, SCIP_STAGE_PRESOLVED, "
1011 "SCIP_STAGE_SOLVING, or SCIP_STAGE_SOLVED"));
1012 return result;
1013 }
1014 if (params.print_detailed_solving_stats()) {
1015 RETURN_IF_SCIP_ERROR(SCIPprintStatistics(scip_, nullptr));
1016 }
1017 if (!params.detailed_solving_stats_filename().empty()) {
1018 FILE* file = fopen(params.detailed_solving_stats_filename().c_str(), "w");
1019 if (file == nullptr) {
1020 return absl::InvalidArgumentError(absl::StrCat(
1021 "Could not open file: ", params.detailed_solving_stats_filename(),
1022 " to write SCIP solve stats."));
1023 }
1024 RETURN_IF_SCIP_ERROR(SCIPprintStatistics(scip_, file));
1025 int close_result = fclose(file);
1026 if (close_result != 0) {
1027 return absl::InvalidArgumentError(absl::StrCat(
1028 "Error: ", close_result,
1029 " closing file: ", params.detailed_solving_stats_filename(),
1030 " when writing solve stats."));
1031 }
1032 }
1033 absl::Status callback_status;
1034 {
1035 const absl::MutexLock callback_status_lock(callback_status_mutex_);
1036 callback_status = util::StatusBuilder(callback_status_)
1037 << "error in a callback that interrupted the solve";
1038 }
1039 if (!callback_status.ok()) {
1040 const absl::Status status = FreeTransform();
1041 if (status.ok()) {
1042 return callback_status;
1043 }
1044 LOG(ERROR) << "GScip::FreeTransform() failed after interrupting "
1045 "the solve due to an error in a callback: "
1046 << callback_status;
1047 return status;
1048 }
1049 // Step 3: Extract solution information.
1050 // Some outputs are available unconditionally, and some are only ready if at
1051 // least presolve succeeded.
1053 const int num_scip_solutions = SCIPgetNSols(scip_);
1054 const int num_returned_solutions =
1055 std::min(num_scip_solutions, std::max(1, params.num_solutions()));
1056 SCIP_SOL** all_solutions = SCIPgetSols(scip_);
1057 stats->set_best_objective(ScipInfUnclamp(SCIPgetPrimalbound(scip_)));
1058 for (int i = 0; i < num_returned_solutions; ++i) {
1059 SCIP_SOL* scip_sol = all_solutions[i];
1060 const double obj_value = ScipInfUnclamp(SCIPgetSolOrigObj(scip_, scip_sol));
1062 for (SCIP_VAR* v : variables_) {
1063 solution[v] = SCIPgetSolVal(scip_, scip_sol, v);
1064 }
1065 result.solutions.push_back(solution);
1066 result.objective_values.push_back(obj_value);
1067 }
1068 RETURN_IF_ERROR(CheckSolutionsInOrder(result, ObjectiveIsMaximize()));
1069 // Can only check for primal ray if we made it past presolve.
1070 if (stage != SCIP_STAGE_PRESOLVING && SCIPhasPrimalRay(scip_)) {
1071 for (SCIP_VAR* v : variables_) {
1072 result.primal_ray[v] = SCIPgetPrimalRayVal(scip_, v);
1073 }
1074 }
1075 // TODO(user): refactor this into a new method.
1076 stats->set_best_bound(ScipInfUnclamp(SCIPgetDualbound(scip_)));
1077 stats->set_node_count(SCIPgetNTotalNodes(scip_));
1078 stats->set_first_lp_relaxation_bound(SCIPgetFirstLPDualboundRoot(scip_));
1079 stats->set_root_node_bound(SCIPgetDualboundRoot(scip_));
1080 if (stage != SCIP_STAGE_PRESOLVING) {
1081 stats->set_total_lp_iterations(SCIPgetNLPIterations(scip_));
1082 stats->set_primal_simplex_iterations(SCIPgetNPrimalLPIterations(scip_));
1083 stats->set_dual_simplex_iterations(SCIPgetNDualLPIterations(scip_));
1084 stats->set_barrier_iterations(SCIPgetNBarrierLPIterations(scip_));
1085 stats->set_deterministic_time(SCIPgetDeterministicTime(scip_));
1086 }
1087 result.gscip_output.set_status(ConvertStatus(SCIPgetStatus(scip_)));
1088
1089 // Step 4: clean up.
1090 RETURN_IF_ERROR(FreeTransform());
1091
1092 // Restore the previous message handler. We must do so AFTER we reset the
1093 // stage of the problem with FreeTransform(). Doing so before will fail since
1094 // changing the message handler is only possible in INIT and PROBLEM stages.
1095 if (message_handler != nullptr) {
1096 RETURN_IF_SCIP_ERROR(SCIPsetMessagehdlr(scip_, previous_handler.get()));
1097
1098 // Resetting the unique_ptr will free the associated handler which will
1099 // flush the buffer if the last log line was unfinished. If we were not
1100 // resetting it, the last new_handler_disabler would disable the handler and
1101 // the remainder of the buffer content would be lost.
1102 new_handler.reset();
1103 }
1104 if (params.has_objective_limit()) {
1105 RETURN_IF_SCIP_ERROR(SCIPsetObjlimit(scip_, SCIP_INVALID));
1106 }
1107
1108 RETURN_IF_SCIP_ERROR(SCIPresetParams(scip_));
1109 // The `silence_output` and `search_logs_filename` parameters are special
1110 // since those are not parameters but properties of the SCIP message
1111 // handler. Hence we reset them explicitly.
1112 SCIPsetMessagehdlrQuiet(scip_, false);
1113 SCIPsetMessagehdlrLogfile(scip_, nullptr);
1114
1115 return result;
1116}
1117
1118absl::StatusOr<bool> GScip::DefaultBoolParamValue(
1119 const std::string& parameter_name) {
1120 SCIP_Bool default_value;
1122 SCIPgetBoolParam(scip_, parameter_name.c_str(), &default_value));
1123 return static_cast<bool>(default_value);
1124}
1125
1127 const std::string& parameter_name) {
1128 int default_value;
1130 SCIPgetIntParam(scip_, parameter_name.c_str(), &default_value));
1131 return default_value;
1132}
1133
1134absl::StatusOr<int64_t> GScip::DefaultLongParamValue(
1135 const std::string& parameter_name) {
1136 SCIP_Longint result;
1138 SCIPgetLongintParam(scip_, parameter_name.c_str(), &result));
1139 return static_cast<int64_t>(result);
1140}
1141
1142absl::StatusOr<double> GScip::DefaultRealParamValue(
1143 const std::string& parameter_name) {
1144 double result;
1146 SCIPgetRealParam(scip_, parameter_name.c_str(), &result));
1147 return result;
1148}
1149
1150absl::StatusOr<char> GScip::DefaultCharParamValue(
1151 const std::string& parameter_name) {
1152 char result;
1154 SCIPgetCharParam(scip_, parameter_name.c_str(), &result));
1155 return result;
1156}
1157
1158absl::StatusOr<std::string> GScip::DefaultStringParamValue(
1159 const std::string& parameter_name) {
1160 char* result;
1162 SCIPgetStringParam(scip_, parameter_name.c_str(), &result));
1163 return std::string(result);
1164}
1165
1166absl::StatusOr<double> GScip::ScipInfClamp(const double d) {
1167 const double kScipInf = ScipInf();
1168 if (d == std::numeric_limits<double>::infinity()) {
1169 return kScipInf;
1170 }
1171 if (d == -std::numeric_limits<double>::infinity()) {
1172 return -kScipInf;
1173 }
1174 // NaN is considered finite here.
1175 if (d >= kScipInf || d <= -kScipInf) {
1177 << d << " is not in SCIP's finite range: (" << -kScipInf << ", "
1178 << kScipInf << ")";
1179 }
1180 return d;
1181}
1182
1183double GScip::ScipInfUnclamp(double d) {
1184 const double kScipInf = ScipInf();
1185 if (d >= kScipInf) return std::numeric_limits<double>::infinity();
1186 if (d <= -kScipInf) return -std::numeric_limits<double>::infinity();
1187 return d;
1188}
1189
1190absl::Status GScip::CheckScipFinite(double d) {
1191 const double kScipInf = ScipInf();
1192 // NaN is considered finite here.
1193 if (d >= kScipInf || d <= -kScipInf) {
1195 << d << " is not in SCIP's finite range: (" << -kScipInf << ", "
1196 << kScipInf << ")";
1197 }
1198 return absl::OkStatus();
1199}
1200
1202 const absl::MutexLock lock(callback_status_mutex_);
1203 return !callback_status_.ok();
1204}
1205
1206#undef RETURN_ERROR_UNLESS
1207
1208} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static constexpr Status STALL_NODE_LIMIT
Definition gscip.pb.h:1457
static constexpr Status OPTIMAL
Definition gscip.pb.h:1464
static constexpr Status INVALID_SOLVER_PARAMETERS
Definition gscip.pb.h:1469
::operations_research::GScipSolvingStats *PROTOBUF_NONNULL mutable_stats()
Definition gscip.pb.h:2665
static constexpr Status INFEASIBLE
Definition gscip.pb.h:1465
static constexpr Status UNKNOWN
Definition gscip.pb.h:1453
static constexpr Status TOTAL_NODE_LIMIT
Definition gscip.pb.h:1456
static constexpr Status TIME_LIMIT
Definition gscip.pb.h:1458
static constexpr Status TERMINATE
Definition gscip.pb.h:1468
static constexpr Status BEST_SOL_LIMIT
Definition gscip.pb.h:1462
static constexpr Status INF_OR_UNBD
Definition gscip.pb.h:1467
static constexpr Status GAP_LIMIT
Definition gscip.pb.h:1460
void set_status(::operations_research::GScipOutput_Status value)
Definition gscip.pb.h:2515
static constexpr Status UNBOUNDED
Definition gscip.pb.h:1466
static constexpr Status USER_INTERRUPT
Definition gscip.pb.h:1454
void set_status_detail(Arg_ &&arg, Args_... args)
static constexpr Status NODE_LIMIT
Definition gscip.pb.h:1455
static constexpr Status SOL_LIMIT
Definition gscip.pb.h:1461
static constexpr Status RESTART_LIMIT
Definition gscip.pb.h:1463
static constexpr Status MEM_LIMIT
Definition gscip.pb.h:1459
GScipParameters_Emphasis Emphasis
Definition gscip.pb.h:938
static constexpr Emphasis OPTIMALITY
Definition gscip.pb.h:945
static constexpr Emphasis PHASE_IMPROVE
Definition gscip.pb.h:947
static constexpr MetaParamValue AGGRESSIVE
Definition gscip.pb.h:968
static constexpr MetaParamValue FAST
Definition gscip.pb.h:969
static constexpr MetaParamValue DEFAULT_META_PARAM_VALUE
Definition gscip.pb.h:967
static constexpr Emphasis PHASE_PROOF
Definition gscip.pb.h:948
static constexpr Emphasis HARD_LP
Definition gscip.pb.h:944
static constexpr Emphasis PHASE_FEAS
Definition gscip.pb.h:946
static constexpr Emphasis EASY_CIP
Definition gscip.pb.h:942
static constexpr MetaParamValue OFF
Definition gscip.pb.h:970
static constexpr Emphasis DEFAULT_EMPHASIS
Definition gscip.pb.h:939
static constexpr Emphasis CP_SOLVER
Definition gscip.pb.h:941
const ::std::string & scip_model_filename() const
Definition gscip.pb.h:2128
static constexpr Emphasis FEASIBILITY
Definition gscip.pb.h:943
const ::std::string & detailed_solving_stats_filename() const
Definition gscip.pb.h:2063
static constexpr Emphasis COUNTER
Definition gscip.pb.h:940
GScipParameters_MetaParamValue MetaParamValue
Definition gscip.pb.h:966
void set_total_lp_iterations(::int64_t value)
Definition gscip.pb.h:2385
void set_primal_simplex_iterations(::int64_t value)
Definition gscip.pb.h:2310
void set_deterministic_time(double value)
Definition gscip.pb.h:2485
void set_node_count(::int64_t value)
Definition gscip.pb.h:2410
void set_first_lp_relaxation_bound(double value)
Definition gscip.pb.h:2435
void set_barrier_iterations(::int64_t value)
Definition gscip.pb.h:2360
void set_dual_simplex_iterations(::int64_t value)
Definition gscip.pb.h:2335
double Lb(SCIP_VAR *var)
Definition gscip.cc:811
absl::Status DeleteConstraint(SCIP_CONS *constraint)
Definition gscip.cc:871
double LinearConstraintLb(SCIP_CONS *constraint)
Definition gscip.cc:847
double LinearConstraintUb(SCIP_CONS *constraint)
Definition gscip.cc:851
absl::Span< SCIP_VAR *const > LinearConstraintVariables(SCIP_CONS *constraint)
Definition gscip.cc:841
absl::Status SafeBulkDelete(const absl::flat_hash_set< SCIP_VAR * > &vars)
Definition gscip.cc:788
absl::Status SetVarType(SCIP_VAR *var, GScipVarType var_type)
Definition gscip.cc:757
absl::StatusOr< char > DefaultCharParamValue(const std::string &parameter_name)
Definition gscip.cc:1150
double Ub(SCIP_VAR *var)
Definition gscip.cc:815
absl::Status DeleteVariable(SCIP_VAR *var)
Definition gscip.cc:764
absl::StatusOr< SCIP_CONS * > AddQuadraticConstraint(const GScipQuadraticRange &range, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:500
absl::Status SetObjCoef(SCIP_VAR *var, double obj_coef)
Definition gscip.cc:751
absl::StatusOr< GScipResult > Solve(const GScipParameters &params=GScipParameters(), GScipMessageHandler message_handler=nullptr, const Interrupter *interrupter=nullptr)
Definition gscip.cc:933
bool IsConstraintLinear(SCIP_CONS *constraint)
Definition gscip.cc:831
void InterruptSolveFromCallbackOnCallbackError(absl::Status error_status)
Definition gscip.cc:390
double ScipInfUnclamp(double d)
Definition gscip.cc:1183
absl::StatusOr< double > DefaultRealParamValue(const std::string &parameter_name)
Definition gscip.cc:1142
absl::Status AddLinearConstraintCoef(SCIP_CONS *constraint, SCIP_VAR *var, double value)
Definition gscip.cc:888
absl::StatusOr< SCIP_CONS * > AddAndConstraint(const GScipLogicalConstraintData &logical_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:589
absl::StatusOr< double > ScipInfClamp(double d)
Definition gscip.cc:1166
absl::Status CanSafeBulkDelete(const absl::flat_hash_set< SCIP_VAR * > &vars)
Definition gscip.cc:774
absl::StatusOr< int > DefaultIntParamValue(const std::string &parameter_name)
Definition gscip.cc:1126
double ObjCoef(SCIP_VAR *var)
Definition gscip.cc:819
absl::StatusOr< SCIP_VAR * > AddVariable(double lb, double ub, double obj_coef, GScipVarType var_type, const std::string &var_name="", const GScipVariableOptions &options=DefaultGScipVariableOptions())
Definition gscip.cc:430
absl::StatusOr< SCIP_CONS * > AddSOS2Constraint(const GScipSOSData &sos_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:688
absl::StatusOr< SCIP_CONS * > AddSOS1Constraint(const GScipSOSData &sos_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:661
absl::string_view ConstraintType(SCIP_CONS *constraint)
Definition gscip.cc:827
absl::Status SetObjectiveOffset(double offset)
Definition gscip.cc:720
absl::StatusOr< SCIP_CONS * > AddLinearConstraint(const GScipLinearRange &range, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:466
absl::Span< const double > LinearConstraintCoefficients(SCIP_CONS *constraint)
Definition gscip.cc:835
absl::Status SetLb(SCIP_VAR *var, double lb)
Definition gscip.cc:739
absl::StatusOr< SCIP_CONS * > AddOrConstraint(const GScipLogicalConstraintData &logical_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:614
absl::Status SetBranchingPriority(SCIP_VAR *var, int priority)
Definition gscip.cc:734
absl::string_view Name(SCIP_VAR *var)
Definition gscip.cc:825
absl::StatusOr< SCIP_CONS * > AddIndicatorConstraint(const GScipIndicatorConstraint &indicator_constraint, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition gscip.cc:548
absl::StatusOr< bool > DefaultBoolParamValue(const std::string &parameter_name)
Definition gscip.cc:1118
absl::Status SetLinearConstraintUb(SCIP_CONS *constraint, double ub)
Definition gscip.cc:865
absl::StatusOr< GScipHintResult > SuggestHint(const GScipSolution &partial_solution)
Definition gscip.cc:896
absl::Status SetMaximize(bool is_maximize)
Definition gscip.cc:714
absl::Status SetUb(SCIP_VAR *var, double ub)
Definition gscip.cc:745
absl::Status SetLinearConstraintLb(SCIP_CONS *constraint, double lb)
Definition gscip.cc:859
GScipVarType VarType(SCIP_VAR *var)
Definition gscip.cc:821
absl::StatusOr< std::string > DefaultStringParamValue(const std::string &parameter_name)
Definition gscip.cc:1158
absl::StatusOr< int64_t > DefaultLongParamValue(const std::string &parameter_name)
Definition gscip.cc:1134
static absl::StatusOr< std::unique_ptr< GScip > > Create(const std::string &problem_name)
Definition gscip.cc:362
absl::Status SetLinearConstraintCoef(SCIP_CONS *constraint, SCIP_VAR *var, double value)
Definition gscip.cc:878
static std::string ScipVersion()
Definition gscip.cc:384
#define RETURN_ERROR_UNLESS(x)
Definition gscip.cc:86
Definition file.cc:327
absl::StatusOr< MessageHandlerPtr > MakeSCIPMessageHandler(const GScipMessageHandler gscip_message_handler)
MessageHandlerPtr CaptureMessageHandlerPtr(SCIP_MESSAGEHDLR *const handler)
std::unique_ptr< SCIP_MESSAGEHDLR, ReleaseSCIPMessageHandler > MessageHandlerPtr
OR-Tools root namespace.
const GScipConstraintOptions & DefaultGScipConstraintOptions()
Definition gscip.cc:299
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
std::string ProtoEnumToString(ProtoEnumType enum_value)
Definition proto_utils.h:63
int GScipMaxNumThreads(const GScipParameters &parameters)
absl::flat_hash_map< SCIP_VAR *, double > GScipSolution
Definition gscip.h:89
std::function< void(GScipMessageType type, absl::string_view message)> GScipMessageHandler
const GScipVariableOptions & DefaultGScipVariableOptions()
Definition gscip.cc:294
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
trees with all degrees equal w the current value of w
#define SCIP_TO_STATUS(x)
#define RETURN_IF_SCIP_ERROR(x)
std::vector< SCIP_Var * > variables
Definition gscip.h:571
std::vector< SCIP_VAR * > variables
Definition gscip.h:112
std::vector< double > coefficients
Definition gscip.h:113
std::vector< SCIP_VAR * > operators
Definition gscip.h:584
std::vector< double > linear_coefficients
Definition gscip.h:519
std::vector< SCIP_Var * > quadratic_variables2
Definition gscip.h:532
*quadratic_variables2[i] std::vector< SCIP_Var * > quadratic_variables1
Definition gscip.h:531
std::vector< SCIP_Var * > linear_variables
Definition gscip.h:518
std::vector< double > quadratic_coefficients
Definition gscip.h:533
std::vector< GScipSolution > solutions
Definition gscip.h:98
absl::flat_hash_map< SCIP_VAR *, double > primal_ray
Definition gscip.h:105
std::vector< double > objective_values
Definition gscip.h:100
std::vector< double > weights
Definition gscip.h:561
std::vector< SCIP_VAR * > variables
Definition gscip.h:552
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)