OR-Tools  9.6
gscip.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 #include "ortools/gscip/gscip.h"
15 
16 #include <stdio.h>
17 
18 #include <algorithm>
19 #include <cstdint>
20 #include <functional>
21 #include <limits>
22 #include <memory>
23 #include <string>
24 #include <utility>
25 #include <vector>
26 
27 #include "absl/container/flat_hash_map.h"
28 #include "absl/container/flat_hash_set.h"
29 #include "absl/memory/memory.h"
30 #include "absl/status/status.h"
31 #include "absl/status/statusor.h"
32 #include "absl/strings/str_cat.h"
33 #include "absl/strings/str_format.h"
34 #include "absl/strings/string_view.h"
35 #include "absl/types/span.h"
36 #include "ortools/base/logging.h"
38 #include "ortools/gscip/gscip.pb.h"
44 #include "scip/cons_linear.h"
45 #include "scip/cons_quadratic.h"
46 #include "scip/scip.h"
47 #include "scip/scip_general.h"
48 #include "scip/scip_param.h"
49 #include "scip/scip_prob.h"
50 #include "scip/scip_solvingstats.h"
51 #include "scip/scipdefplugins.h"
52 #include "scip/type_cons.h"
53 #include "scip/type_scip.h"
54 #include "scip/type_var.h"
55 
56 namespace operations_research {
57 
58 #define RETURN_ERROR_UNLESS(x) \
59  if (!(x)) \
60  return util::StatusBuilder(absl::InvalidArgumentError(absl::StrFormat( \
61  "Condition violated at %s:%d: %s", __FILE__, __LINE__, #x)))
62 
63 namespace {
64 
65 constexpr absl::string_view kLinearConstraintHandlerName = "linear";
66 
67 SCIP_VARTYPE ConvertVarType(const GScipVarType var_type) {
68  switch (var_type) {
70  return SCIP_VARTYPE_CONTINUOUS;
72  return SCIP_VARTYPE_BINARY;
74  return SCIP_VARTYPE_IMPLINT;
76  return SCIP_VARTYPE_INTEGER;
77  }
78 }
79 
80 GScipVarType ConvertVarType(const SCIP_VARTYPE var_type) {
81  switch (var_type) {
82  case SCIP_VARTYPE_CONTINUOUS:
84  case SCIP_VARTYPE_IMPLINT:
86  case SCIP_VARTYPE_INTEGER:
88  case SCIP_VARTYPE_BINARY:
89  return GScipVarType::kBinary;
90  }
91 }
92 
93 GScipOutput::Status ConvertStatus(const SCIP_STATUS scip_status) {
94  switch (scip_status) {
95  case SCIP_STATUS_UNKNOWN:
96  return GScipOutput::UNKNOWN;
97  case SCIP_STATUS_USERINTERRUPT:
98  return GScipOutput::USER_INTERRUPT;
99  case SCIP_STATUS_BESTSOLLIMIT:
100  return GScipOutput::BEST_SOL_LIMIT;
101  case SCIP_STATUS_MEMLIMIT:
102  return GScipOutput::MEM_LIMIT;
103  case SCIP_STATUS_NODELIMIT:
104  return GScipOutput::NODE_LIMIT;
105  case SCIP_STATUS_RESTARTLIMIT:
106  return GScipOutput::RESTART_LIMIT;
107  case SCIP_STATUS_SOLLIMIT:
108  return GScipOutput::SOL_LIMIT;
109  case SCIP_STATUS_STALLNODELIMIT:
110  return GScipOutput::STALL_NODE_LIMIT;
111  case SCIP_STATUS_TIMELIMIT:
112  return GScipOutput::TIME_LIMIT;
113  case SCIP_STATUS_TOTALNODELIMIT:
114  return GScipOutput::TOTAL_NODE_LIMIT;
115  case SCIP_STATUS_OPTIMAL:
116  return GScipOutput::OPTIMAL;
117  case SCIP_STATUS_GAPLIMIT:
118  return GScipOutput::GAP_LIMIT;
119  case SCIP_STATUS_INFEASIBLE:
121  case SCIP_STATUS_UNBOUNDED:
122  return GScipOutput::UNBOUNDED;
123  case SCIP_STATUS_INFORUNBD:
124  return GScipOutput::INF_OR_UNBD;
125  case SCIP_STATUS_TERMINATE:
126  return GScipOutput::TERMINATE;
127  default:
128  LOG(FATAL) << "Unrecognized scip status: " << scip_status;
129  }
130 }
131 
132 SCIP_PARAMEMPHASIS ConvertEmphasis(
133  const GScipParameters::Emphasis gscip_emphasis) {
134  switch (gscip_emphasis) {
135  case GScipParameters::DEFAULT_EMPHASIS:
136  return SCIP_PARAMEMPHASIS_DEFAULT;
137  case GScipParameters::CP_SOLVER:
138  return SCIP_PARAMEMPHASIS_CPSOLVER;
139  case GScipParameters::EASY_CIP:
140  return SCIP_PARAMEMPHASIS_EASYCIP;
141  case GScipParameters::FEASIBILITY:
142  return SCIP_PARAMEMPHASIS_FEASIBILITY;
143  case GScipParameters::HARD_LP:
144  return SCIP_PARAMEMPHASIS_HARDLP;
145  case GScipParameters::OPTIMALITY:
146  return SCIP_PARAMEMPHASIS_OPTIMALITY;
147  case GScipParameters::COUNTER:
148  return SCIP_PARAMEMPHASIS_COUNTER;
149  case GScipParameters::PHASE_FEAS:
150  return SCIP_PARAMEMPHASIS_PHASEFEAS;
151  case GScipParameters::PHASE_IMPROVE:
152  return SCIP_PARAMEMPHASIS_PHASEIMPROVE;
153  case GScipParameters::PHASE_PROOF:
154  return SCIP_PARAMEMPHASIS_PHASEPROOF;
155  default:
156  LOG(FATAL) << "Unrecognized gscip_emphasis: "
157  << ProtoEnumToString(gscip_emphasis);
158  }
159 }
160 
161 SCIP_PARAMSETTING ConvertMetaParamValue(
162  const GScipParameters::MetaParamValue gscip_meta_param_value) {
163  switch (gscip_meta_param_value) {
164  case GScipParameters::DEFAULT_META_PARAM_VALUE:
165  return SCIP_PARAMSETTING_DEFAULT;
166  case GScipParameters::AGGRESSIVE:
167  return SCIP_PARAMSETTING_AGGRESSIVE;
168  case GScipParameters::FAST:
169  return SCIP_PARAMSETTING_FAST;
170  case GScipParameters::OFF:
171  return SCIP_PARAMSETTING_OFF;
172  default:
173  LOG(FATAL) << "Unrecognized gscip_meta_param_value: "
174  << ProtoEnumToString(gscip_meta_param_value);
175  }
176 }
177 
178 absl::Status CheckSolutionsInOrder(const GScipResult& result,
179  const bool is_maximize) {
180  auto objective_as_good_as = [is_maximize](double left, double right) {
181  if (is_maximize) {
182  return left >= right;
183  }
184  return left <= right;
185  };
186  for (int i = 1; i < result.objective_values.size(); ++i) {
187  const double previous = result.objective_values[i - 1];
188  const double current = result.objective_values[i];
189  if (!objective_as_good_as(previous, current)) {
191  << "Expected SCIP solutions to be in best objective order "
192  "first, but for "
193  << (is_maximize ? "maximization" : "minimization")
194  << " problem, the " << i - 1 << " objective is " << previous
195  << " and the " << i << " objective is " << current;
196  }
197  }
198  return absl::OkStatus();
199 }
200 
201 } // namespace
202 
204  static GScipVariableOptions var_options;
205  return var_options;
206 }
207 
209  static GScipConstraintOptions constraint_options;
210  return constraint_options;
211 }
212 
213 absl::Status GScip::SetParams(const GScipParameters& params,
214  const std::string& legacy_params) {
215  if (params.has_silence_output()) {
216  SCIPsetMessagehdlrQuiet(scip_, params.silence_output());
217  }
218  if (!params.search_logs_filename().empty()) {
219  SCIPsetMessagehdlrLogfile(scip_, params.search_logs_filename().c_str());
220  }
221 
222  const SCIP_Bool set_param_quiet =
223  static_cast<SCIP_Bool>(!params.silence_output());
224 
225  RETURN_IF_SCIP_ERROR(SCIPsetEmphasis(
226  scip_, ConvertEmphasis(params.emphasis()), set_param_quiet));
227  if (params.has_heuristics()) {
228  RETURN_IF_SCIP_ERROR(SCIPsetHeuristics(
229  scip_, ConvertMetaParamValue(params.heuristics()), set_param_quiet));
230  }
231  if (params.has_presolve()) {
232  RETURN_IF_SCIP_ERROR(SCIPsetPresolving(
233  scip_, ConvertMetaParamValue(params.presolve()), set_param_quiet));
234  }
235  if (params.has_separating()) {
236  RETURN_IF_SCIP_ERROR(SCIPsetSeparating(
237  scip_, ConvertMetaParamValue(params.separating()), set_param_quiet));
238  }
239  for (const auto& bool_param : params.bool_params()) {
241  (SCIPsetBoolParam(scip_, bool_param.first.c_str(), bool_param.second)));
242  }
243  for (const auto& int_param : params.int_params()) {
245  (SCIPsetIntParam(scip_, int_param.first.c_str(), int_param.second)));
246  }
247  for (const auto& long_param : params.long_params()) {
248  RETURN_IF_SCIP_ERROR((SCIPsetLongintParam(scip_, long_param.first.c_str(),
249  long_param.second)));
250  }
251  for (const auto& char_param : params.char_params()) {
252  if (char_param.second.size() != 1) {
253  return absl::InvalidArgumentError(
254  absl::StrCat("Character parameters must be single character strings, "
255  "but parameter: ",
256  char_param.first, " was: ", char_param.second));
257  }
258  RETURN_IF_SCIP_ERROR((SCIPsetCharParam(scip_, char_param.first.c_str(),
259  char_param.second[0])));
260  }
261  for (const auto& string_param : params.string_params()) {
262  RETURN_IF_SCIP_ERROR((SCIPsetStringParam(scip_, string_param.first.c_str(),
263  string_param.second.c_str())));
264  }
265  for (const auto& real_param : params.real_params()) {
267  (SCIPsetRealParam(scip_, real_param.first.c_str(), real_param.second)));
268  }
269  if (!legacy_params.empty()) {
271  LegacyScipSetSolverSpecificParameters(legacy_params, scip_));
272  }
273  return absl::OkStatus();
274 }
275 
276 absl::StatusOr<std::unique_ptr<GScip>> GScip::Create(
277  const std::string& problem_name) {
278  SCIP* scip = nullptr;
279  RETURN_IF_SCIP_ERROR(SCIPcreate(&scip));
280  RETURN_IF_SCIP_ERROR(SCIPincludeDefaultPlugins(scip));
281  RETURN_IF_SCIP_ERROR(SCIPcreateProbBasic(scip, problem_name.c_str()));
282  // NOTE(user): the constructor is private, so we cannot call make_unique.
283  return absl::WrapUnique(new GScip(scip));
284 }
285 
286 GScip::GScip(SCIP* scip) : scip_(scip) {}
287 
288 double GScip::ScipInf() { return SCIPinfinity(scip_); }
289 
290 absl::Status GScip::FreeTransform() {
291  return SCIP_TO_STATUS(SCIPfreeTransform(scip_));
292 }
293 
294 std::string GScip::ScipVersion() {
295  return absl::StrFormat("SCIP %d.%d.%d [LP solver: %s]", SCIPmajorVersion(),
296  SCIPminorVersion(), SCIPtechVersion(),
297  SCIPlpiGetSolverName());
298 }
299 
301  if (scip_ == nullptr) {
302  return true;
303  }
304  return SCIPinterruptSolve(scip_) == SCIP_OKAY;
305 }
306 
307 absl::Status GScip::CleanUp() {
308  if (scip_ != nullptr) {
309  for (SCIP_VAR* variable : variables_) {
310  if (variable != nullptr) {
311  RETURN_IF_SCIP_ERROR(SCIPreleaseVar(scip_, &variable));
312  }
313  }
314  for (SCIP_CONS* constraint : constraints_) {
315  if (constraint != nullptr) {
316  RETURN_IF_SCIP_ERROR(SCIPreleaseCons(scip_, &constraint));
317  }
318  }
319  RETURN_IF_SCIP_ERROR(SCIPfree(&scip_));
320  }
321  return absl::OkStatus();
322 }
323 
325  const absl::Status clean_up_status = CleanUp();
326  LOG_IF(DFATAL, !clean_up_status.ok()) << clean_up_status;
327 }
328 
329 absl::StatusOr<SCIP_VAR*> GScip::AddVariable(
330  double lb, double ub, double obj_coef, GScipVarType var_type,
331  const std::string& var_name, const GScipVariableOptions& options) {
332  SCIP_VAR* var = nullptr;
333  OR_ASSIGN_OR_RETURN3(lb, ScipInfClamp(lb),
334  _ << "invalid lower bound for variable: " << var_name);
335  OR_ASSIGN_OR_RETURN3(ub, ScipInfClamp(ub),
336  _ << "invalid upper bound for variable: " << var_name);
337  RETURN_IF_ERROR(CheckScipFinite(obj_coef))
338  << "invalid objective coefficient for variable: " << var_name;
339  RETURN_IF_SCIP_ERROR(SCIPcreateVarBasic(scip_, /*var=*/&var,
340  /*name=*/var_name.c_str(),
341  /*lb=*/lb, /*ub=*/ub,
342  /*obj=*/obj_coef,
343  ConvertVarType(var_type)));
344  RETURN_IF_SCIP_ERROR(SCIPvarSetInitial(var, options.initial));
345  RETURN_IF_SCIP_ERROR(SCIPvarSetRemovable(var, options.removable));
346  RETURN_IF_SCIP_ERROR(SCIPaddVar(scip_, var));
347  if (options.keep_alive) {
348  variables_.insert(var);
349  } else {
350  RETURN_IF_SCIP_ERROR(SCIPreleaseVar(scip_, &var));
351  }
352  return var;
353 }
354 
355 absl::Status GScip::MaybeKeepConstraintAlive(
356  SCIP_CONS* constraint, const GScipConstraintOptions& options) {
357  if (options.keep_alive) {
358  constraints_.insert(constraint);
359  } else {
360  RETURN_IF_SCIP_ERROR(SCIPreleaseCons(scip_, &constraint));
361  }
362  return absl::OkStatus();
363 }
364 
365 absl::StatusOr<SCIP_CONS*> GScip::AddLinearConstraint(
366  const GScipLinearRange& range, const std::string& name,
367  const GScipConstraintOptions& options) {
368  SCIP_CONS* constraint = nullptr;
369  RETURN_ERROR_UNLESS(range.variables.size() == range.coefficients.size())
370  << "Error adding constraint: " << name << ".";
371  OR_ASSIGN_OR_RETURN3(const double lb, ScipInfClamp(range.lower_bound),
372  _ << "invalid lower bound for constraint: " << name);
373  OR_ASSIGN_OR_RETURN3(const double ub, ScipInfClamp(range.upper_bound),
374  _ << "invalid upper bound for constraint: " << name);
375  for (int i = 0; i < range.coefficients.size(); ++i) {
376  RETURN_IF_ERROR(CheckScipFinite(range.coefficients[i]))
377  << "invalid coefficient at index " << i << " of constraint: " << name;
378  }
379  RETURN_IF_SCIP_ERROR(SCIPcreateConsLinear(
380  scip_, &constraint, name.c_str(), range.variables.size(),
381  const_cast<SCIP_VAR**>(range.variables.data()),
382  const_cast<double*>(range.coefficients.data()),
383  /*lhs=*/lb, /*rhs=*/ub,
384  /*initial=*/options.initial,
385  /*separate=*/options.separate,
386  /*enforce=*/options.enforce,
387  /*check=*/options.check,
388  /*propagate=*/options.propagate,
389  /*local=*/options.local,
390  /*modifiable=*/options.modifiable,
391  /*dynamic=*/options.dynamic,
392  /*removable=*/options.removable,
393  /*stickingatnode=*/options.sticking_at_node));
394  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
395  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
396  return constraint;
397 }
398 
399 absl::StatusOr<SCIP_CONS*> GScip::AddQuadraticConstraint(
400  const GScipQuadraticRange& range, const std::string& name,
401  const GScipConstraintOptions& options) {
402  SCIP_CONS* constraint = nullptr;
403  const int num_lin_vars = range.linear_variables.size();
404  RETURN_ERROR_UNLESS(num_lin_vars == range.linear_coefficients.size())
405  << "Error adding quadratic constraint: " << name << " in linear term.";
406  const int num_quad_vars = range.quadratic_variables1.size();
407  RETURN_ERROR_UNLESS(num_quad_vars == range.quadratic_variables2.size())
408  << "Error adding quadratic constraint: " << name << " in quadratic term.";
409  RETURN_ERROR_UNLESS(num_quad_vars == range.quadratic_coefficients.size())
410  << "Error adding quadratic constraint: " << name << " in quadratic term.";
411  OR_ASSIGN_OR_RETURN3(const double lb, ScipInfClamp(range.lower_bound),
412  _ << "invalid lower bound for constraint: " << name);
413  OR_ASSIGN_OR_RETURN3(const double ub, ScipInfClamp(range.upper_bound),
414  _ << "invalid upper bound for constraint: " << name);
415  for (int i = 0; i < range.linear_coefficients.size(); ++i) {
416  RETURN_IF_ERROR(CheckScipFinite(range.linear_coefficients[i]))
417  << "invalid linear coefficient at index " << i
418  << " of constraint: " << name;
419  }
420  for (int i = 0; i < range.quadratic_coefficients.size(); ++i) {
421  RETURN_IF_ERROR(CheckScipFinite(range.quadratic_coefficients[i]))
422  << "invalid quadratic coefficient at index " << i
423  << " of constraint: " << name;
424  }
425  RETURN_IF_SCIP_ERROR(SCIPcreateConsQuadratic(
426  scip_, &constraint, name.c_str(), num_lin_vars,
427  const_cast<SCIP_Var**>(range.linear_variables.data()),
428  const_cast<double*>(range.linear_coefficients.data()), num_quad_vars,
429  const_cast<SCIP_Var**>(range.quadratic_variables1.data()),
430  const_cast<SCIP_Var**>(range.quadratic_variables2.data()),
431  const_cast<double*>(range.quadratic_coefficients.data()),
432  /*lhs=*/lb, /*rhs=*/ub,
433  /*initial=*/options.initial,
434  /*separate=*/options.separate,
435  /*enforce=*/options.enforce,
436  /*check=*/options.check,
437  /*propagate=*/options.propagate,
438  /*local=*/options.local,
439  /*modifiable=*/options.modifiable,
440  /*dynamic=*/options.dynamic,
441  /*removable=*/options.removable));
442  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
443  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
444  return constraint;
445 }
446 
447 absl::StatusOr<SCIP_CONS*> GScip::AddIndicatorConstraint(
448  const GScipIndicatorConstraint& indicator_constraint,
449  const std::string& name, const GScipConstraintOptions& options) {
450  SCIP_VAR* indicator = indicator_constraint.indicator_variable;
451  RETURN_ERROR_UNLESS(indicator != nullptr)
452  << "Error adding indicator constraint: " << name << ".";
453  if (indicator_constraint.negate_indicator) {
454  RETURN_IF_SCIP_ERROR(SCIPgetNegatedVar(scip_, indicator, &indicator));
455  }
456 
457  SCIP_CONS* constraint = nullptr;
458  RETURN_ERROR_UNLESS(indicator_constraint.variables.size() ==
459  indicator_constraint.coefficients.size())
460  << "Error adding indicator constraint: " << name << ".";
461  OR_ASSIGN_OR_RETURN3(const double ub,
462  ScipInfClamp(indicator_constraint.upper_bound),
463  _ << "invalid upper bound for constraint: " << name);
464  for (int i = 0; i < indicator_constraint.coefficients.size(); ++i) {
465  RETURN_IF_ERROR(CheckScipFinite(indicator_constraint.coefficients[i]))
466  << "invalid coefficient at index " << i << " of constraint: " << name;
467  }
468  RETURN_IF_SCIP_ERROR(SCIPcreateConsIndicator(
469  scip_, &constraint, name.c_str(), indicator,
470  indicator_constraint.variables.size(),
471  const_cast<SCIP_Var**>(indicator_constraint.variables.data()),
472  const_cast<double*>(indicator_constraint.coefficients.data()),
473  /*rhs=*/ub,
474  /*initial=*/options.initial,
475  /*separate=*/options.separate,
476  /*enforce=*/options.enforce,
477  /*check=*/options.check,
478  /*propagate=*/options.propagate,
479  /*local=*/options.local,
480  /*dynamic=*/options.dynamic,
481  /*removable=*/options.removable,
482  /*stickingatnode=*/options.sticking_at_node));
483  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
484  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
485  return constraint;
486 }
487 
488 absl::StatusOr<SCIP_CONS*> GScip::AddAndConstraint(
489  const GScipLogicalConstraintData& logical_data, const std::string& name,
490  const GScipConstraintOptions& options) {
491  RETURN_ERROR_UNLESS(logical_data.resultant != nullptr)
492  << "Error adding and constraint: " << name << ".";
493  SCIP_CONS* constraint = nullptr;
495  SCIPcreateConsAnd(scip_, &constraint, name.c_str(),
496  logical_data.resultant, logical_data.operators.size(),
497  const_cast<SCIP_VAR**>(logical_data.operators.data()),
498  /*initial=*/options.initial,
499  /*separate=*/options.separate,
500  /*enforce=*/options.enforce,
501  /*check=*/options.check,
502  /*propagate=*/options.propagate,
503  /*local=*/options.local,
504  /*modifiable=*/options.modifiable,
505  /*dynamic=*/options.dynamic,
506  /*removable=*/options.removable,
507  /*stickingatnode=*/options.sticking_at_node));
508  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
509  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
510  return constraint;
511 }
512 
513 absl::StatusOr<SCIP_CONS*> GScip::AddOrConstraint(
514  const GScipLogicalConstraintData& logical_data, const std::string& name,
515  const GScipConstraintOptions& options) {
516  RETURN_ERROR_UNLESS(logical_data.resultant != nullptr)
517  << "Error adding or constraint: " << name << ".";
518  SCIP_CONS* constraint = nullptr;
520  SCIPcreateConsOr(scip_, &constraint, name.c_str(), logical_data.resultant,
521  logical_data.operators.size(),
522  const_cast<SCIP_Var**>(logical_data.operators.data()),
523  /*initial=*/options.initial,
524  /*separate=*/options.separate,
525  /*enforce=*/options.enforce,
526  /*check=*/options.check,
527  /*propagate=*/options.propagate,
528  /*local=*/options.local,
529  /*modifiable=*/options.modifiable,
530  /*dynamic=*/options.dynamic,
531  /*removable=*/options.removable,
532  /*stickingatnode=*/options.sticking_at_node));
533  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
534  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
535  return constraint;
536 }
537 
538 namespace {
539 
540 absl::Status ValidateSOSData(const GScipSOSData& sos_data,
541  absl::string_view name) {
542  RETURN_ERROR_UNLESS(!sos_data.variables.empty())
543  << "Error adding SOS constraint: " << name << ".";
544  if (!sos_data.weights.empty()) {
545  RETURN_ERROR_UNLESS(sos_data.variables.size() == sos_data.weights.size())
546  << " Error adding SOS constraint: " << name << ".";
547  }
548  absl::flat_hash_set<double> distinct_weights;
549  for (const double w : sos_data.weights) {
550  RETURN_ERROR_UNLESS(!distinct_weights.contains(w))
551  << "Error adding SOS constraint: " << name
552  << ", weights must be distinct, but found value " << w << " twice.";
553  distinct_weights.insert(w);
554  }
555  return absl::OkStatus();
556 }
557 
558 } // namespace
559 
560 absl::StatusOr<SCIP_CONS*> GScip::AddSOS1Constraint(
561  const GScipSOSData& sos_data, const std::string& name,
562  const GScipConstraintOptions& options) {
563  RETURN_IF_ERROR(ValidateSOSData(sos_data, name));
564  SCIP_CONS* constraint = nullptr;
565  double* weights = nullptr;
566  if (!sos_data.weights.empty()) {
567  weights = const_cast<double*>(sos_data.weights.data());
568  }
569 
570  RETURN_IF_SCIP_ERROR(SCIPcreateConsSOS1(
571  scip_, &constraint, name.c_str(), sos_data.variables.size(),
572  const_cast<SCIP_Var**>(sos_data.variables.data()), weights,
573  /*initial=*/options.initial,
574  /*separate=*/options.separate,
575  /*enforce=*/options.enforce,
576  /*check=*/options.check,
577  /*propagate=*/options.propagate,
578  /*local=*/options.local,
579  /*dynamic=*/options.dynamic,
580  /*removable=*/options.removable,
581  /*stickingatnode=*/options.sticking_at_node));
582  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
583  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
584  return constraint;
585 }
586 
587 absl::StatusOr<SCIP_CONS*> GScip::AddSOS2Constraint(
588  const GScipSOSData& sos_data, const std::string& name,
589  const GScipConstraintOptions& options) {
590  RETURN_IF_ERROR(ValidateSOSData(sos_data, name));
591  SCIP_CONS* constraint = nullptr;
592  double* weights = nullptr;
593  if (!sos_data.weights.empty()) {
594  weights = const_cast<double*>(sos_data.weights.data());
595  }
596  RETURN_IF_SCIP_ERROR(SCIPcreateConsSOS2(
597  scip_, &constraint, name.c_str(), sos_data.variables.size(),
598  const_cast<SCIP_Var**>(sos_data.variables.data()), weights,
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  /*dynamic=*/options.dynamic,
606  /*removable=*/options.removable,
607  /*stickingatnode=*/options.sticking_at_node));
608  RETURN_IF_SCIP_ERROR(SCIPaddCons(scip_, constraint));
609  RETURN_IF_ERROR(MaybeKeepConstraintAlive(constraint, options));
610  return constraint;
611 }
612 
613 absl::Status GScip::SetMaximize(bool is_maximize) {
614  RETURN_IF_SCIP_ERROR(SCIPsetObjsense(
615  scip_, is_maximize ? SCIP_OBJSENSE_MAXIMIZE : SCIP_OBJSENSE_MINIMIZE));
616  return absl::OkStatus();
617 }
618 
619 absl::Status GScip::SetObjectiveOffset(double offset) {
620  RETURN_IF_ERROR(CheckScipFinite(offset)) << "invalid objective offset";
621  double old_offset = SCIPgetOrigObjoffset(scip_);
622  double delta_offset = offset - old_offset;
623  RETURN_IF_SCIP_ERROR(SCIPaddOrigObjoffset(scip_, delta_offset));
624  return absl::OkStatus();
625 }
626 
628  return SCIPgetObjsense(scip_) == SCIP_OBJSENSE_MAXIMIZE;
629 }
630 
631 double GScip::ObjectiveOffset() { return SCIPgetOrigObjoffset(scip_); }
632 
633 absl::Status GScip::SetBranchingPriority(SCIP_VAR* var, int priority) {
634  RETURN_IF_SCIP_ERROR(SCIPchgVarBranchPriority(scip_, var, priority));
635  return absl::OkStatus();
636 }
637 
638 absl::Status GScip::SetLb(SCIP_VAR* var, double lb) {
639  OR_ASSIGN_OR_RETURN3(lb, ScipInfClamp(lb), _ << "invalid lower bound");
640  RETURN_IF_SCIP_ERROR(SCIPchgVarLb(scip_, var, lb));
641  return absl::OkStatus();
642 }
643 
644 absl::Status GScip::SetUb(SCIP_VAR* var, double ub) {
645  OR_ASSIGN_OR_RETURN3(ub, ScipInfClamp(ub), _ << "invalid upper bound");
646  RETURN_IF_SCIP_ERROR(SCIPchgVarUb(scip_, var, ub));
647  return absl::OkStatus();
648 }
649 
650 absl::Status GScip::SetObjCoef(SCIP_VAR* var, double obj_coef) {
651  RETURN_IF_ERROR(CheckScipFinite(obj_coef)) << "invalid objective coefficient";
652  RETURN_IF_SCIP_ERROR(SCIPchgVarObj(scip_, var, obj_coef));
653  return absl::OkStatus();
654 }
655 
656 absl::Status GScip::SetVarType(SCIP_VAR* var, GScipVarType var_type) {
657  SCIP_Bool infeasible;
659  SCIPchgVarType(scip_, var, ConvertVarType(var_type), &infeasible));
660  return absl::OkStatus();
661 }
662 
663 absl::Status GScip::DeleteVariable(SCIP_VAR* var) {
664  SCIP_Bool did_delete;
665  RETURN_IF_SCIP_ERROR(SCIPdelVar(scip_, var, &did_delete));
666  RETURN_ERROR_UNLESS(static_cast<bool>(did_delete))
667  << "Failed to delete variable named: " << Name(var);
668  variables_.erase(var);
669  RETURN_IF_SCIP_ERROR(SCIPreleaseVar(scip_, &var));
670  return absl::OkStatus();
671 }
672 
674  const absl::flat_hash_set<SCIP_VAR*>& vars) {
675  if (vars.empty()) {
676  return absl::OkStatus();
677  }
678  for (SCIP_CONS* constraint : constraints_) {
679  if (!IsConstraintLinear(constraint)) {
680  return absl::InvalidArgumentError(absl::StrCat(
681  "Model contains nonlinear constraint: ", Name(constraint)));
682  }
683  }
684  return absl::OkStatus();
685 }
686 
687 absl::Status GScip::SafeBulkDelete(const absl::flat_hash_set<SCIP_VAR*>& vars) {
689  if (vars.empty()) {
690  return absl::OkStatus();
691  }
692  // Now, we can assume that all constraints are linear.
693  for (SCIP_CONS* constraint : constraints_) {
694  const absl::Span<SCIP_VAR* const> nonzeros =
695  LinearConstraintVariables(constraint);
696  const std::vector<SCIP_VAR*> nonzeros_copy(nonzeros.begin(),
697  nonzeros.end());
698  for (SCIP_VAR* var : nonzeros_copy) {
699  if (vars.contains(var)) {
700  RETURN_IF_ERROR(SetLinearConstraintCoef(constraint, var, 0.0));
701  }
702  }
703  }
704  for (SCIP_VAR* const var : vars) {
706  }
707  return absl::OkStatus();
708 }
709 
710 double GScip::Lb(SCIP_VAR* var) {
711  return ScipInfUnclamp(SCIPvarGetLbOriginal(var));
712 }
713 
714 double GScip::Ub(SCIP_VAR* var) {
715  return ScipInfUnclamp(SCIPvarGetUbOriginal(var));
716 }
717 
718 double GScip::ObjCoef(SCIP_VAR* var) { return SCIPvarGetObj(var); }
719 
721  return ConvertVarType(SCIPvarGetType(var));
722 }
723 
724 absl::string_view GScip::Name(SCIP_VAR* var) { return SCIPvarGetName(var); }
725 
726 absl::string_view GScip::ConstraintType(SCIP_CONS* constraint) {
727  return absl::string_view(SCIPconshdlrGetName(SCIPconsGetHdlr(constraint)));
728 }
729 
730 bool GScip::IsConstraintLinear(SCIP_CONS* constraint) {
731  return ConstraintType(constraint) == kLinearConstraintHandlerName;
732 }
733 
734 absl::Span<const double> GScip::LinearConstraintCoefficients(
735  SCIP_CONS* constraint) {
736  int num_vars = SCIPgetNVarsLinear(scip_, constraint);
737  return absl::MakeConstSpan(SCIPgetValsLinear(scip_, constraint), num_vars);
738 }
739 
740 absl::Span<SCIP_VAR* const> GScip::LinearConstraintVariables(
741  SCIP_CONS* constraint) {
742  int num_vars = SCIPgetNVarsLinear(scip_, constraint);
743  return absl::MakeConstSpan(SCIPgetVarsLinear(scip_, constraint), num_vars);
744 }
745 
746 double GScip::LinearConstraintLb(SCIP_CONS* constraint) {
747  return ScipInfUnclamp(SCIPgetLhsLinear(scip_, constraint));
748 }
749 
750 double GScip::LinearConstraintUb(SCIP_CONS* constraint) {
751  return ScipInfUnclamp(SCIPgetRhsLinear(scip_, constraint));
752 }
753 
754 absl::string_view GScip::Name(SCIP_CONS* constraint) {
755  return SCIPconsGetName(constraint);
756 }
757 
758 absl::Status GScip::SetLinearConstraintLb(SCIP_CONS* constraint, double lb) {
759  OR_ASSIGN_OR_RETURN3(lb, ScipInfClamp(lb), _ << "invalid lower bound");
760  RETURN_IF_SCIP_ERROR(SCIPchgLhsLinear(scip_, constraint, lb));
761  return absl::OkStatus();
762 }
763 
764 absl::Status GScip::SetLinearConstraintUb(SCIP_CONS* constraint, double ub) {
765  OR_ASSIGN_OR_RETURN3(ub, ScipInfClamp(ub), _ << "invalid upper bound");
766  RETURN_IF_SCIP_ERROR(SCIPchgRhsLinear(scip_, constraint, ub));
767  return absl::OkStatus();
768 }
769 
770 absl::Status GScip::DeleteConstraint(SCIP_CONS* constraint) {
771  RETURN_IF_SCIP_ERROR(SCIPdelCons(scip_, constraint));
772  constraints_.erase(constraint);
773  RETURN_IF_SCIP_ERROR(SCIPreleaseCons(scip_, &constraint));
774  return absl::OkStatus();
775 }
776 
777 absl::Status GScip::SetLinearConstraintCoef(SCIP_CONS* constraint,
778  SCIP_VAR* var, double value) {
779  // TODO(user): this operation is slow (linear in the nnz in the constraint).
780  // It would be better to just use a bulk operation, but there doesn't appear
781  // to be any?
782  RETURN_IF_ERROR(CheckScipFinite(value)) << "invalid coefficient";
783  RETURN_IF_SCIP_ERROR(SCIPchgCoefLinear(scip_, constraint, var, value));
784  return absl::OkStatus();
785 }
786 
787 absl::Status GScip::AddLinearConstraintCoef(SCIP_CONS* const constraint,
788  SCIP_VAR* const var,
789  const double value) {
790  RETURN_IF_ERROR(CheckScipFinite(value)) << "invalid coefficient";
791  RETURN_IF_SCIP_ERROR(SCIPaddCoefLinear(scip_, constraint, var, value));
792  return absl::OkStatus();
793 }
794 
795 absl::StatusOr<GScipHintResult> GScip::SuggestHint(
796  const GScipSolution& partial_solution) {
797  SCIP_SOL* solution;
798  const int scip_num_vars = SCIPgetNOrigVars(scip_);
799  const bool is_solution_partial = partial_solution.size() < scip_num_vars;
800  if (is_solution_partial) {
801  RETURN_IF_SCIP_ERROR(SCIPcreatePartialSol(scip_, &solution, nullptr));
802  } else {
803  // This is actually a full solution
804  RETURN_ERROR_UNLESS(partial_solution.size() == scip_num_vars)
805  << "Error suggesting hint.";
806  RETURN_IF_SCIP_ERROR(SCIPcreateSol(scip_, &solution, nullptr));
807  }
808  for (const auto& var_value_pair : partial_solution) {
809  RETURN_IF_SCIP_ERROR(SCIPsetSolVal(scip_, solution, var_value_pair.first,
810  var_value_pair.second));
811  }
812  if (!is_solution_partial) {
813  SCIP_Bool is_feasible;
814  RETURN_IF_SCIP_ERROR(SCIPcheckSol(
815  scip_, solution, /*printreason=*/false, /*completely=*/true,
816  /*checkbounds=*/true, /*checkintegrality=*/true, /*checklprows=*/true,
817  &is_feasible));
818  if (!static_cast<bool>(is_feasible)) {
819  RETURN_IF_SCIP_ERROR(SCIPfreeSol(scip_, &solution));
821  }
822  }
823  SCIP_Bool is_stored;
824  RETURN_IF_SCIP_ERROR(SCIPaddSolFree(scip_, &solution, &is_stored));
825  if (static_cast<bool>(is_stored)) {
827  } else {
829  }
830 }
831 
832 absl::StatusOr<GScipResult> GScip::Solve(
833  const GScipParameters& params, const std::string& legacy_params,
834  const GScipMessageHandler message_handler) {
835  // A four step process:
836  // 1. Apply parameters.
837  // 2. Solve the problem.
838  // 3. Extract solution and solve statistics.
839  // 4. Prepare the solver for further modification/solves (reset parameters,
840  // free the solutions found).
841  GScipResult result;
842 
843  // Step 1: apply parameters.
844  const absl::Status param_status = SetParams(params, legacy_params);
845  if (!param_status.ok()) {
847  // Conversion to std::string for open source build.
848  result.gscip_output.set_status_detail(
849  std::string(param_status.message())); // NOLINT
850  return result;
851  }
852  if (params.print_scip_model()) {
853  RETURN_IF_SCIP_ERROR(SCIPwriteOrigProblem(scip_, nullptr, "cip", FALSE));
854  }
855  if (!params.scip_model_filename().empty()) {
856  RETURN_IF_SCIP_ERROR(SCIPwriteOrigProblem(
857  scip_, params.scip_model_filename().c_str(), "cip", FALSE));
858  }
859  if (params.has_objective_limit()) {
860  OR_ASSIGN_OR_RETURN3(const double scip_obj_limit,
861  ScipInfClamp(params.objective_limit()),
862  _ << "invalid objective_limit");
863  RETURN_IF_SCIP_ERROR(SCIPsetObjlimit(scip_, scip_obj_limit));
864  }
865 
866  // Install the message handler if necessary. We do this after setting the
867  // parameters so that parameters that applies to the default message handler
868  // like `quiet` are indeed applied to it and not to our temporary
869  // handler.
872  MessageHandlerPtr previous_handler;
873  MessageHandlerPtr new_handler;
874  if (message_handler != nullptr) {
875  previous_handler = CaptureMessageHandlerPtr(SCIPgetMessagehdlr(scip_));
876  ASSIGN_OR_RETURN(new_handler,
877  internal::MakeSCIPMessageHandler(message_handler));
878  SCIPsetMessagehdlr(scip_, new_handler.get());
879  }
880  // Make sure we prevent any call of message_handler after this function has
881  // returned, until the new_handler is reset (see below).
882  const internal::ScopedSCIPMessageHandlerDisabler new_handler_disabler(
883  new_handler);
884 
885  // Step 2: Solve.
886  // NOTE(user): after solve, SCIP will either be in stage PRESOLVING,
887  // SOLVING, OR SOLVED.
888  if (GScipMaxNumThreads(params) > 1) {
889  RETURN_IF_SCIP_ERROR(SCIPsolveConcurrent(scip_));
890  } else {
891  RETURN_IF_SCIP_ERROR(SCIPsolve(scip_));
892  }
893  const SCIP_STAGE stage = SCIPgetStage(scip_);
894  if (stage != SCIP_STAGE_PRESOLVING && stage != SCIP_STAGE_SOLVING &&
895  stage != SCIP_STAGE_SOLVED) {
896  result.gscip_output.set_status(GScipOutput::UNKNOWN);
897  result.gscip_output.set_status_detail(
898  absl::StrCat("Unpexpected SCIP final stage= ", stage,
899  " was expected to be either SCIP_STAGE_PRESOLVING, "
900  "SCIP_STAGE_SOLVING, or SCIP_STAGE_SOLVED"));
901  return result;
902  }
903  if (params.print_detailed_solving_stats()) {
904  RETURN_IF_SCIP_ERROR(SCIPprintStatistics(scip_, nullptr));
905  }
906  if (!params.detailed_solving_stats_filename().empty()) {
907  FILE* file = fopen(params.detailed_solving_stats_filename().c_str(), "w");
908  if (file == nullptr) {
909  return absl::InvalidArgumentError(absl::StrCat(
910  "Could not open file: ", params.detailed_solving_stats_filename(),
911  " to write SCIP solve stats."));
912  }
913  RETURN_IF_SCIP_ERROR(SCIPprintStatistics(scip_, file));
914  int close_result = fclose(file);
915  if (close_result != 0) {
916  return absl::InvalidArgumentError(absl::StrCat(
917  "Error: ", close_result,
918  " closing file: ", params.detailed_solving_stats_filename(),
919  " when writing solve stats."));
920  }
921  }
922  // Step 3: Extract solution information.
923  // Some outputs are available unconditionally, and some are only ready if at
924  // least presolve succeeded.
925  GScipSolvingStats* stats = result.gscip_output.mutable_stats();
926  const int num_scip_solutions = SCIPgetNSols(scip_);
927  const int num_returned_solutions =
928  std::min(num_scip_solutions, std::max(1, params.num_solutions()));
929  SCIP_SOL** all_solutions = SCIPgetSols(scip_);
930  stats->set_best_objective(ScipInfUnclamp(SCIPgetPrimalbound(scip_)));
931  for (int i = 0; i < num_returned_solutions; ++i) {
932  SCIP_SOL* scip_sol = all_solutions[i];
933  const double obj_value = ScipInfUnclamp(SCIPgetSolOrigObj(scip_, scip_sol));
934  GScipSolution solution;
935  for (SCIP_VAR* v : variables_) {
936  solution[v] = SCIPgetSolVal(scip_, scip_sol, v);
937  }
938  result.solutions.push_back(solution);
939  result.objective_values.push_back(obj_value);
940  }
941  RETURN_IF_ERROR(CheckSolutionsInOrder(result, ObjectiveIsMaximize()));
942  // Can only check for primal ray if we made it past presolve.
943  if (stage != SCIP_STAGE_PRESOLVING && SCIPhasPrimalRay(scip_)) {
944  for (SCIP_VAR* v : variables_) {
945  result.primal_ray[v] = SCIPgetPrimalRayVal(scip_, v);
946  }
947  }
948  // TODO(user): refactor this into a new method.
949  stats->set_best_bound(ScipInfUnclamp(SCIPgetDualbound(scip_)));
950  stats->set_node_count(SCIPgetNTotalNodes(scip_));
951  stats->set_first_lp_relaxation_bound(SCIPgetFirstLPDualboundRoot(scip_));
952  stats->set_root_node_bound(SCIPgetDualboundRoot(scip_));
953  if (stage != SCIP_STAGE_PRESOLVING) {
954  stats->set_total_lp_iterations(SCIPgetNLPIterations(scip_));
955  stats->set_primal_simplex_iterations(SCIPgetNPrimalLPIterations(scip_));
956  stats->set_dual_simplex_iterations(SCIPgetNDualLPIterations(scip_));
957  stats->set_deterministic_time(SCIPgetDeterministicTime(scip_));
958  }
959  result.gscip_output.set_status(ConvertStatus(SCIPgetStatus(scip_)));
960 
961  // Step 4: clean up.
962  RETURN_IF_ERROR(FreeTransform());
963 
964  // Restore the previous message handler. We must do so AFTER we reset the
965  // stage of the problem with FreeTransform(). Doing so before will fail since
966  // changing the message handler is only possible in INIT and PROBLEM stages.
967  if (message_handler != nullptr) {
968  RETURN_IF_SCIP_ERROR(SCIPsetMessagehdlr(scip_, previous_handler.get()));
969 
970  // Resetting the unique_ptr will free the associated handler which will
971  // flush the buffer if the last log line was unfinished. If we were not
972  // resetting it, the last new_handler_disabler would disable the handler and
973  // the remainder of the buffer content would be lost.
974  new_handler.reset();
975  }
976  if (params.has_objective_limit()) {
977  RETURN_IF_SCIP_ERROR(SCIPsetObjlimit(scip_, SCIP_INVALID));
978  }
979 
980  RETURN_IF_SCIP_ERROR(SCIPresetParams(scip_));
981  // The `silence_output` and `search_logs_filename` parameters are special
982  // since those are not parameters but properties of the SCIP message
983  // handler. Hence we reset them explicitly.
984  SCIPsetMessagehdlrQuiet(scip_, false);
985  SCIPsetMessagehdlrLogfile(scip_, nullptr);
986 
987  return result;
988 }
989 
990 absl::StatusOr<bool> GScip::DefaultBoolParamValue(
991  const std::string& parameter_name) {
992  SCIP_Bool default_value;
994  SCIPgetBoolParam(scip_, parameter_name.c_str(), &default_value));
995  return static_cast<bool>(default_value);
996 }
997 
998 absl::StatusOr<int> GScip::DefaultIntParamValue(
999  const std::string& parameter_name) {
1000  int default_value;
1002  SCIPgetIntParam(scip_, parameter_name.c_str(), &default_value));
1003  return default_value;
1004 }
1005 
1006 absl::StatusOr<int64_t> GScip::DefaultLongParamValue(
1007  const std::string& parameter_name) {
1008  SCIP_Longint result;
1010  SCIPgetLongintParam(scip_, parameter_name.c_str(), &result));
1011  return static_cast<int64_t>(result);
1012 }
1013 
1014 absl::StatusOr<double> GScip::DefaultRealParamValue(
1015  const std::string& parameter_name) {
1016  double result;
1018  SCIPgetRealParam(scip_, parameter_name.c_str(), &result));
1019  return result;
1020 }
1021 
1022 absl::StatusOr<char> GScip::DefaultCharParamValue(
1023  const std::string& parameter_name) {
1024  char result;
1026  SCIPgetCharParam(scip_, parameter_name.c_str(), &result));
1027  return result;
1028 }
1029 
1030 absl::StatusOr<std::string> GScip::DefaultStringParamValue(
1031  const std::string& parameter_name) {
1032  char* result;
1034  SCIPgetStringParam(scip_, parameter_name.c_str(), &result));
1035  return std::string(result);
1036 }
1037 
1038 absl::StatusOr<double> GScip::ScipInfClamp(const double d) {
1039  const double kScipInf = ScipInf();
1040  if (d == std::numeric_limits<double>::infinity()) {
1041  return kScipInf;
1042  }
1043  if (d == -std::numeric_limits<double>::infinity()) {
1044  return -kScipInf;
1045  }
1046  // NaN is considered finite here.
1047  if (d >= kScipInf || d <= -kScipInf) {
1049  << d << " is not in SCIP's finite range: (" << -kScipInf << ", "
1050  << kScipInf << ")";
1051  }
1052  return d;
1053 }
1054 
1055 double GScip::ScipInfUnclamp(double d) {
1056  const double kScipInf = ScipInf();
1057  if (d >= kScipInf) return std::numeric_limits<double>::infinity();
1058  if (d <= -kScipInf) return -std::numeric_limits<double>::infinity();
1059  return d;
1060 }
1061 
1062 absl::Status GScip::CheckScipFinite(double d) {
1063  const double kScipInf = ScipInf();
1064  // NaN is considered finite here.
1065  if (d >= kScipInf || d <= -kScipInf) {
1067  << d << " is not in SCIP's finite range: (" << -kScipInf << ", "
1068  << kScipInf << ")";
1069  }
1070  return absl::OkStatus();
1071 }
1072 
1073 #undef RETURN_ERROR_UNLESS
1074 
1075 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
double LinearConstraintUb(SCIP_CONS *constraint)
Definition: gscip.cc:750
absl::Status SetObjectiveOffset(double offset)
Definition: gscip.cc:619
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:329
absl::Status DeleteVariable(SCIP_VAR *var)
Definition: gscip.cc:663
absl::StatusOr< SCIP_CONS * > AddOrConstraint(const GScipLogicalConstraintData &logical_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:513
absl::StatusOr< double > DefaultRealParamValue(const std::string &parameter_name)
Definition: gscip.cc:1014
absl::Status CanSafeBulkDelete(const absl::flat_hash_set< SCIP_VAR * > &vars)
Definition: gscip.cc:673
absl::StatusOr< SCIP_CONS * > AddAndConstraint(const GScipLogicalConstraintData &logical_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:488
absl::StatusOr< SCIP_CONS * > AddLinearConstraint(const GScipLinearRange &range, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:365
bool IsConstraintLinear(SCIP_CONS *constraint)
Definition: gscip.cc:730
absl::string_view ConstraintType(SCIP_CONS *constraint)
Definition: gscip.cc:726
absl::StatusOr< int64_t > DefaultLongParamValue(const std::string &parameter_name)
Definition: gscip.cc:1006
absl::StatusOr< int > DefaultIntParamValue(const std::string &parameter_name)
Definition: gscip.cc:998
absl::Status DeleteConstraint(SCIP_CONS *constraint)
Definition: gscip.cc:770
absl::Status SetLinearConstraintUb(SCIP_CONS *constraint, double ub)
Definition: gscip.cc:764
absl::Status SafeBulkDelete(const absl::flat_hash_set< SCIP_VAR * > &vars)
Definition: gscip.cc:687
static absl::StatusOr< std::unique_ptr< GScip > > Create(const std::string &problem_name)
Definition: gscip.cc:276
double Ub(SCIP_VAR *var)
Definition: gscip.cc:714
double ObjCoef(SCIP_VAR *var)
Definition: gscip.cc:718
absl::Status SetMaximize(bool is_maximize)
Definition: gscip.cc:613
absl::Span< SCIP_VAR *const > LinearConstraintVariables(SCIP_CONS *constraint)
Definition: gscip.cc:740
absl::StatusOr< std::string > DefaultStringParamValue(const std::string &parameter_name)
Definition: gscip.cc:1030
absl::StatusOr< bool > DefaultBoolParamValue(const std::string &parameter_name)
Definition: gscip.cc:990
absl::StatusOr< SCIP_CONS * > AddIndicatorConstraint(const GScipIndicatorConstraint &indicator_constraint, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:447
double Lb(SCIP_VAR *var)
Definition: gscip.cc:710
absl::Status SetLb(SCIP_VAR *var, double lb)
Definition: gscip.cc:638
double LinearConstraintLb(SCIP_CONS *constraint)
Definition: gscip.cc:746
absl::Status SetLinearConstraintCoef(SCIP_CONS *constraint, SCIP_VAR *var, double value)
Definition: gscip.cc:777
absl::Status SetLinearConstraintLb(SCIP_CONS *constraint, double lb)
Definition: gscip.cc:758
absl::StatusOr< GScipHintResult > SuggestHint(const GScipSolution &partial_solution)
Definition: gscip.cc:795
absl::StatusOr< GScipResult > Solve(const GScipParameters &params=GScipParameters(), const std::string &legacy_params="", GScipMessageHandler message_handler=nullptr)
Definition: gscip.cc:832
GScipVarType VarType(SCIP_VAR *var)
Definition: gscip.cc:720
absl::Status SetVarType(SCIP_VAR *var, GScipVarType var_type)
Definition: gscip.cc:656
absl::Status SetBranchingPriority(SCIP_VAR *var, int priority)
Definition: gscip.cc:633
absl::StatusOr< char > DefaultCharParamValue(const std::string &parameter_name)
Definition: gscip.cc:1022
absl::Span< const double > LinearConstraintCoefficients(SCIP_CONS *constraint)
Definition: gscip.cc:734
absl::Status SetUb(SCIP_VAR *var, double ub)
Definition: gscip.cc:644
absl::Status SetObjCoef(SCIP_VAR *var, double obj_coef)
Definition: gscip.cc:650
absl::StatusOr< SCIP_CONS * > AddSOS2Constraint(const GScipSOSData &sos_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:587
static std::string ScipVersion()
Definition: gscip.cc:294
absl::StatusOr< SCIP_CONS * > AddQuadraticConstraint(const GScipQuadraticRange &range, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:399
absl::string_view Name(SCIP_VAR *var)
Definition: gscip.cc:724
absl::Status AddLinearConstraintCoef(SCIP_CONS *constraint, SCIP_VAR *var, double value)
Definition: gscip.cc:787
absl::StatusOr< SCIP_CONS * > AddSOS1Constraint(const GScipSOSData &sos_data, const std::string &name="", const GScipConstraintOptions &options=DefaultGScipConstraintOptions())
Definition: gscip.cc:560
const std::string name
int64_t value
IntVar * var
Definition: expr_array.cc:1874
#define RETURN_ERROR_UNLESS(x)
Definition: gscip.cc:58
absl::StatusOr< MessageHandlerPtr > MakeSCIPMessageHandler(const GScipMessageHandler gscip_message_handler)
std::unique_ptr< SCIP_MESSAGEHDLR, ReleaseSCIPMessageHandler > MessageHandlerPtr
MessageHandlerPtr CaptureMessageHandlerPtr(SCIP_MESSAGEHDLR *const handler)
Collection of objects used to extend the Constraint Solver library.
const GScipConstraintOptions & DefaultGScipConstraintOptions()
Definition: gscip.cc:208
std::function< void(GScipMessageType type, absl::string_view message)> GScipMessageHandler
int GScipMaxNumThreads(const GScipParameters &parameters)
std::string ProtoEnumToString(ProtoEnumType enum_value)
absl::Status LegacyScipSetSolverSpecificParameters(absl::string_view parameters, SCIP *scip)
const GScipVariableOptions & DefaultGScipVariableOptions()
Definition: gscip.cc:203
absl::flat_hash_map< SCIP_VAR *, double > GScipSolution
Definition: gscip.h:74
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
#define SCIP_TO_STATUS(x)
#define RETURN_IF_SCIP_ERROR(x)
const std::optional< Range > & range
Definition: statistics.cc:36
std::vector< SCIP_Var * > variables
Definition: gscip.h:454
std::vector< SCIP_VAR * > operators
Definition: gscip.h:467
absl::flat_hash_map< SCIP_VAR *, double > primal_ray
Definition: gscip.h:90
std::vector< double > objective_values
Definition: gscip.h:85
std::vector< GScipSolution > solutions
Definition: gscip.h:83
std::vector< SCIP_VAR * > variables
Definition: gscip.h:435
std::vector< double > weights
Definition: gscip.h:444
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)