OR-Tools  9.1
gscip_solver.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <algorithm>
17 #include <cstdint>
18 #include <functional>
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
25 #include "ortools/base/logging.h"
26 #include "absl/container/flat_hash_map.h"
27 #include "absl/container/flat_hash_set.h"
28 #include "absl/memory/memory.h"
29 #include "absl/status/status.h"
30 #include "absl/status/statusor.h"
31 #include "absl/strings/str_cat.h"
32 #include "absl/strings/string_view.h"
33 #include "absl/time/clock.h"
34 #include "absl/time/time.h"
35 #include "absl/types/optional.h"
36 #include "absl/types/span.h"
37 #include "scip/type_cons.h"
38 #include "scip/type_var.h"
39 #include "ortools/base/map_util.h"
40 #include "ortools/gscip/gscip.h"
41 #include "ortools/gscip/gscip.pb.h"
43 #include "ortools/math_opt/callback.pb.h"
47 #include "ortools/math_opt/model.pb.h"
48 #include "ortools/math_opt/model_parameters.pb.h"
49 #include "ortools/math_opt/model_update.pb.h"
50 #include "ortools/math_opt/parameters.pb.h"
51 #include "ortools/math_opt/result.pb.h"
52 #include "ortools/math_opt/solution.pb.h"
54 #include "ortools/math_opt/sparse_containers.pb.h"
56 #include "absl/status/status.h"
58 #include "ortools/base/protoutil.h"
59 
60 namespace operations_research {
61 namespace math_opt {
62 
63 namespace {
64 
65 int64_t SafeId(const VariablesProto& variables, int index) {
66  if (variables.ids().empty()) {
67  return index;
68  }
69  return variables.ids(index);
70 }
71 
72 const std::string& EmptyString() {
73  static const std::string* const empty_string = new std::string;
74  return *empty_string;
75 }
76 
77 const std::string& SafeName(const VariablesProto& variables, int index) {
78  if (variables.names().empty()) {
79  return EmptyString();
80  }
81  return variables.names(index);
82 }
83 
84 int64_t SafeId(const LinearConstraintsProto& linear_constraints, int index) {
85  if (linear_constraints.ids().empty()) {
86  return index;
87  }
88  return linear_constraints.ids(index);
89 }
90 
91 const std::string& SafeName(const LinearConstraintsProto& linear_constraints,
92  int index) {
93  if (linear_constraints.names().empty()) {
94  return EmptyString();
95  }
96  return linear_constraints.names(index);
97 }
98 
99 absl::flat_hash_map<int64_t, double> SparseDoubleVectorAsMap(
100  const SparseDoubleVectorProto& vector) {
101  CHECK_EQ(vector.ids_size(), vector.values_size());
102  absl::flat_hash_map<int64_t, double> result;
103  result.reserve(vector.ids_size());
104  for (int i = 0; i < vector.ids_size(); ++i) {
105  result[vector.ids(i)] = vector.values(i);
106  }
107  return result;
108 }
109 
110 // Viewing matrix as a list of (row, column, value) tuples stored in row major
111 // order, does a linear scan from index scan_start to find the index of the
112 // first entry with row >= row_id. Returns the size the tuple list if there is
113 // no such entry.
114 inline int FindRowStart(const SparseDoubleMatrixProto& matrix, const int row_id,
115  const int scan_start) {
116  int result = scan_start;
117  while (result < matrix.row_ids_size() && matrix.row_ids(result) < row_id) {
118  ++result;
119  }
120  return result;
121 }
122 
123 struct LinearConstraintView {
125  double lower_bound;
126  double upper_bound;
127  absl::string_view name;
128  absl::Span<const int64_t> variable_ids;
129  absl::Span<const double> coefficients;
130 };
131 
132 // Iterates over the constraints from a LinearConstraints, outputting a
133 // LinearConstraintView for each constraint. Requires a SparseDoubleMatrixProto
134 // which may have data from additional constraints not in LinearConstraints.
135 //
136 // The running time to iterate through and read each element once is
137 // O(Size(*linear_constraints) + Size(*linear_constraint_matrix)).
138 class LinearConstraintIterator {
139  public:
140  LinearConstraintIterator(
141  const LinearConstraintsProto* linear_constraints,
142  const SparseDoubleMatrixProto* linear_constraint_matrix)
143  : linear_constraints_(ABSL_DIE_IF_NULL(linear_constraints)),
144  linear_constraint_matrix_(ABSL_DIE_IF_NULL(linear_constraint_matrix)) {
145  if (NumConstraints(*linear_constraints_) > 0) {
146  const int64_t first_constraint = SafeId(*linear_constraints_, 0);
147  matrix_start_ =
148  FindRowStart(*linear_constraint_matrix_, first_constraint, 0);
149  matrix_end_ = FindRowStart(*linear_constraint_matrix_,
150  first_constraint + 1, matrix_start_);
151  } else {
152  matrix_start_ = NumMatrixNonzeros(*linear_constraint_matrix_);
153  matrix_end_ = matrix_start_;
154  }
155  }
156 
157  bool IsDone() const {
158  return current_con_ >= NumConstraints(*linear_constraints_);
159  }
160 
161  // Call only if !IsDone(). Runs in O(1).
162  LinearConstraintView Current() const {
163  CHECK(!IsDone());
164  LinearConstraintView result;
165  result.lower_bound = linear_constraints_->lower_bounds(current_con_);
166  result.upper_bound = linear_constraints_->upper_bounds(current_con_);
167  result.name = SafeName(*linear_constraints_, current_con_);
168  result.linear_constraint_id = SafeId(*linear_constraints_, current_con_);
169 
170  const auto vars_begin = linear_constraint_matrix_->column_ids().begin();
171  result.variable_ids = absl::MakeConstSpan(vars_begin + matrix_start_,
172  vars_begin + matrix_end_);
173  const auto coefficients_begins =
174  linear_constraint_matrix_->coefficients().begin();
175  result.coefficients = absl::MakeConstSpan(
176  coefficients_begins + matrix_start_, coefficients_begins + matrix_end_);
177  return result;
178  }
179 
180  // Call only if !IsDone().
181  void Next() {
182  CHECK(!IsDone());
183  ++current_con_;
184  if (IsDone()) {
185  matrix_start_ = NumMatrixNonzeros(*linear_constraint_matrix_);
186  matrix_end_ = matrix_start_;
187  return;
188  }
189  const int64_t current_row_id = SafeId(*linear_constraints_, current_con_);
190  matrix_start_ =
191  FindRowStart(*linear_constraint_matrix_, current_row_id, matrix_end_);
192 
193  matrix_end_ = FindRowStart(*linear_constraint_matrix_, current_row_id + 1,
194  matrix_start_);
195  }
196 
197  private:
198  // NOT OWNED
199  const LinearConstraintsProto* const linear_constraints_;
200  // NOT OWNED
201  const SparseDoubleMatrixProto* const linear_constraint_matrix_;
202  // An index into linear_constraints_, the constraint currently being viewed,
203  // or Size(linear_constraints_) when IsDone().
204  int current_con_ = 0;
205 
206  // Informal: the interval [matrix_start_, matrix_end_) gives the indices in
207  // linear_constraint_matrix_ for linear_constraints_[current_con_]
208  //
209  // Invariant: if !IsDone():
210  // * matrix_start_: the first index in linear_constraint_matrix_ with row id
211  // >= RowId(linear_constraints_[current_con_])
212  // * matrix_end_: the first index in linear_constraint_matrix_ with row id
213  // >= RowId(linear_constraints_[current_con_]) + 1
214  //
215  // Implementation note: matrix_start_ and matrix_end_ equal
216  // Size(linear_constraint_matrix_) when IsDone().
217  int matrix_start_ = 0;
218  int matrix_end_ = 0;
219 };
220 
221 inline GScipVarType GScipVarTypeFromIsInteger(const bool is_integer) {
223 }
224 
225 // Used to delay the evaluation of a costly computation until the first time it
226 // is actually needed.
227 //
228 // The typical use is when we have two independent branches that need the same
229 // data but we don't want to compute these data if we don't enter any of those
230 // branches.
231 //
232 // Usage:
233 // LazyInitialized<Xxx> xxx([&]() {
234 // return Xxx(...);
235 // });
236 //
237 // if (predicate_1) {
238 // ...
239 // f(xxx.GetOrCreate());
240 // ...
241 // }
242 // if (predicate_2) {
243 // ...
244 // f(xxx.GetOrCreate());
245 // ...
246 // }
247 template <typename T>
248 class LazyInitialized {
249  public:
250  // Checks that the input initializer is not nullptr.
251  explicit LazyInitialized(std::function<T()> initializer)
252  : initializer_(ABSL_DIE_IF_NULL(initializer)) {}
253 
254  // Returns the value returned by initializer(), calling it the first time.
255  const T& GetOrCreate() {
256  if (!value_) {
257  value_ = initializer_();
258  }
259  return *value_;
260  }
261 
262  private:
263  const std::function<T()> initializer_;
264  absl::optional<T> value_;
265 };
266 
267 template <typename T>
268 SparseDoubleVectorProto FillSparseDoubleVector(
269  const std::vector<int64_t>& ids_in_order,
270  const absl::flat_hash_map<int64_t, T>& id_map,
271  const absl::flat_hash_map<T, double>& value_map,
272  const SparseVectorFilterProto& filter) {
273  SparseVectorFilterPredicate predicate(filter);
274  SparseDoubleVectorProto result;
275  for (const int64_t variable_id : ids_in_order) {
276  const double value = value_map.at(id_map.at(variable_id));
277  if (predicate.AcceptsAndUpdate(variable_id, value)) {
278  result.add_ids(variable_id);
279  result.add_values(value);
280  }
281  }
282  return result;
283 }
284 
285 } // namespace
286 
287 absl::Status GScipSolver::AddVariables(
288  const VariablesProto& variables,
289  const absl::flat_hash_map<int64_t, double>& linear_objective_coefficients) {
290  for (int i = 0; i < NumVariables(variables); ++i) {
291  const int64_t id = SafeId(variables, i);
292  CHECK_GE(id, next_unused_variable_id_);
294  SCIP_VAR* const v,
295  gscip_->AddVariable(
296  variables.lower_bounds(i), variables.upper_bounds(i),
297  gtl::FindWithDefault(linear_objective_coefficients, id),
298  GScipVarTypeFromIsInteger(variables.integers(i)),
299  SafeName(variables, i)));
300  gtl::InsertOrDie(&variables_, id, v);
301  next_unused_variable_id_ = id + 1;
302  }
303  return absl::OkStatus();
304 }
305 
306 absl::Status GScipSolver::UpdateVariables(
307  const VariableUpdatesProto& variable_updates) {
308  for (const auto [id, lb] : MakeView(variable_updates.lower_bounds())) {
309  RETURN_IF_ERROR(gscip_->SetLb(variables_.at(id), lb));
310  }
311  for (const auto [id, ub] : MakeView(variable_updates.upper_bounds())) {
312  RETURN_IF_ERROR(gscip_->SetUb(variables_.at(id), ub));
313  }
314  for (const auto [id, is_integer] : MakeView(variable_updates.integers())) {
315  RETURN_IF_ERROR(gscip_->SetVarType(variables_.at(id),
316  GScipVarTypeFromIsInteger(is_integer)));
317  }
318  return absl::OkStatus();
319 }
320 
321 absl::Status GScipSolver::AddLinearConstraints(
322  const LinearConstraintsProto& linear_constraints,
323  const SparseDoubleMatrixProto& linear_constraint_matrix) {
324  for (LinearConstraintIterator lin_con_it(&linear_constraints,
325  &linear_constraint_matrix);
326  !lin_con_it.IsDone(); lin_con_it.Next()) {
327  const LinearConstraintView current = lin_con_it.Current();
328  CHECK_GE(current.linear_constraint_id, next_unused_linear_constraint_id_);
329 
330  GScipLinearRange range;
331  range.lower_bound = current.lower_bound;
332  range.upper_bound = current.upper_bound;
333  range.coefficients = std::vector<double>(current.coefficients.begin(),
334  current.coefficients.end());
335  range.variables.reserve(current.variable_ids.size());
336  for (const int64_t var_id : current.variable_ids) {
337  range.variables.push_back(variables_.at(var_id));
338  }
340  SCIP_CONS* const scip_con,
341  gscip_->AddLinearConstraint(range, std::string(current.name)));
342  gtl::InsertOrDie(&linear_constraints_, current.linear_constraint_id,
343  scip_con);
344  next_unused_linear_constraint_id_ = current.linear_constraint_id + 1;
345  }
346  return absl::OkStatus();
347 }
348 
349 absl::Status GScipSolver::UpdateLinearConstraints(
350  const LinearConstraintUpdatesProto linear_constraint_updates,
351  const SparseDoubleMatrixProto& linear_constraint_matrix) {
352  for (const auto [id, lb] :
353  MakeView(linear_constraint_updates.lower_bounds())) {
355  gscip_->SetLinearConstraintLb(linear_constraints_.at(id), lb));
356  }
357  for (const auto [id, ub] :
358  MakeView(linear_constraint_updates.upper_bounds())) {
360  gscip_->SetLinearConstraintUb(linear_constraints_.at(id), ub));
361  }
362  for (int i = 0; i < linear_constraint_matrix.row_ids_size(); ++i) {
363  const int64_t lin_con_id = linear_constraint_matrix.row_ids(i);
364  if (lin_con_id >= next_unused_linear_constraint_id_) {
365  break;
366  }
367  const int64_t var_id = linear_constraint_matrix.column_ids(i);
368  const double value = linear_constraint_matrix.coefficients(i);
369  RETURN_IF_ERROR(gscip_->SetLinearConstraintCoef(
370  linear_constraints_.at(lin_con_id), variables_.at(var_id), value));
371  }
372  return absl::OkStatus();
373 }
374 
376  switch (emphasis) {
377  case EMPHASIS_OFF:
378  return GScipParameters::OFF;
379  case EMPHASIS_LOW:
380  return GScipParameters::FAST;
381  case EMPHASIS_MEDIUM:
382  case EMPHASIS_UNSPECIFIED:
384  case EMPHASIS_HIGH:
385  case EMPHASIS_VERY_HIGH:
387  default:
388  LOG(FATAL) << "Unsupported MathOpt Emphasis value: "
389  << ProtoEnumToString(emphasis)
390  << " unknown, error setting gSCIP parameters";
391  }
392 }
393 
395  const CommonSolveParametersProto& common_solver_parameters,
396  const GScipParameters& gscip_parameters) {
397  // First build the result by translating common parameters to a
398  // GScipParameters, and then merging with user provided gscip_parameters.
399  // This results in user provided solver specific parameters overwriting
400  // common parameters should there be any conflict.
401  GScipParameters result;
402  if (common_solver_parameters.has_time_limit()) {
404  util_time::DecodeGoogleApiProto(common_solver_parameters.time_limit())
405  .value(),
406  &result);
407  }
408  if (common_solver_parameters.has_threads()) {
409  GScipSetMaxNumThreads(common_solver_parameters.threads(), &result);
410  }
411  if (common_solver_parameters.has_enable_output()) {
412  // GScip has also GScipSetOutputEnabled() but this changes the log
413  // level. Setting `silence_output` sets the `quiet` field on the default
414  // message handler of SCIP which removes the output. Here it is important to
415  // use this rather than changing the log level so that if the user registers
416  // for CALLBACK_EVENT_MESSAGE they do get some messages even when
417  // `enable_output` is false.
418  result.set_silence_output(!common_solver_parameters.enable_output());
419  }
420  if (common_solver_parameters.has_random_seed()) {
421  GScipSetRandomSeed(&result, common_solver_parameters.random_seed());
422  }
423  if (common_solver_parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
424  char alg;
425  switch (common_solver_parameters.lp_algorithm()) {
426  case LP_ALGORITHM_PRIMAL_SIMPLEX:
427  alg = 'p';
428  break;
429  case LP_ALGORITHM_DUAL_SIMPLEX:
430  alg = 'd';
431  break;
432  case LP_ALGORITHM_BARRIER:
433  alg = 'c';
434  break;
435  default:
436  LOG(FATAL) << "LPAlgorithm: "
437  << ProtoEnumToString(common_solver_parameters.lp_algorithm())
438  << " unknown, error setting gSCIP parameters";
439  }
440  (*result.mutable_char_params())["lp/initalgorithm"] = alg;
441  }
442  if (common_solver_parameters.cuts() != EMPHASIS_UNSPECIFIED) {
443  result.set_separating(
444  ConvertMathOptEmphasis(common_solver_parameters.cuts()));
445  }
446  if (common_solver_parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
447  result.set_heuristics(
448  ConvertMathOptEmphasis(common_solver_parameters.heuristics()));
449  }
450  if (common_solver_parameters.presolve() != EMPHASIS_UNSPECIFIED) {
451  result.set_presolve(
452  ConvertMathOptEmphasis(common_solver_parameters.presolve()));
453  }
454  if (common_solver_parameters.scaling() != EMPHASIS_UNSPECIFIED) {
455  int scaling_value;
456  switch (common_solver_parameters.scaling()) {
457  case EMPHASIS_OFF:
458  scaling_value = 0;
459  break;
460  case EMPHASIS_LOW:
461  case EMPHASIS_MEDIUM:
462  scaling_value = 1;
463  break;
464  case EMPHASIS_HIGH:
465  case EMPHASIS_VERY_HIGH:
466  scaling_value = 2;
467  break;
468  default:
469  LOG(FATAL) << "Scaling emphasis: "
470  << ProtoEnumToString(common_solver_parameters.scaling())
471  << " unknown, error setting gSCIP parameters";
472  }
473  (*result.mutable_int_params())["lp/scaling"] = scaling_value;
474  }
475  result.MergeFrom(gscip_parameters);
476  return result;
477 }
478 
479 namespace {
480 
481 std::string JoinDetails(const std::string& gscip_detail,
482  const std::string& math_opt_detail) {
483  if (gscip_detail.empty()) {
484  return math_opt_detail;
485  }
486  if (math_opt_detail.empty()) {
487  return gscip_detail;
488  }
489  return absl::StrCat(gscip_detail, "; ", math_opt_detail);
490 }
491 
492 } // namespace
493 
494 absl::StatusOr<std::pair<SolveResultProto::TerminationReason, std::string>>
495 GScipSolver::ConvertTerminationReason(const GScipOutput::Status gscip_status,
496  const std::string& gscip_status_detail,
497  const bool has_feasible_solution) {
498  switch (gscip_status) {
500  return std::make_pair(SolveResultProto::TERMINATION_REASON_UNSPECIFIED,
501  gscip_status_detail);
503  return std::make_pair(SolveResultProto::INTERRUPTED, gscip_status_detail);
505  return std::make_pair(
506  SolveResultProto::NODE_LIMIT,
507  JoinDetails(gscip_status_detail,
508  "Underlying gSCIP status: NODE_LIMIT."));
510  return std::make_pair(
511  SolveResultProto::NODE_LIMIT,
512  JoinDetails(gscip_status_detail,
513  "Underlying gSCIP status: TOTAL_NODE_LIMIT."));
515  return std::make_pair(SolveResultProto::SLOW_PROGRESS,
516  gscip_status_detail);
518  return std::make_pair(SolveResultProto::TIME_LIMIT, gscip_status_detail);
520  return std::make_pair(SolveResultProto::MEMORY_LIMIT,
521  gscip_status_detail);
522 
524  return std::make_pair(SolveResultProto::SOLUTION_LIMIT,
525  JoinDetails(gscip_status_detail,
526  "Underlying gSCIP status: SOL_LIMIT."));
528  return std::make_pair(
529  SolveResultProto::SOLUTION_LIMIT,
530  JoinDetails(gscip_status_detail,
531  "Underlying gSCIP status: BEST_SOL_LIMIT."));
532 
534  return std::make_pair(
535  SolveResultProto::OTHER_LIMIT,
536  JoinDetails(gscip_status_detail,
537  "Underlying gSCIP status: RESTART_LIMIT."));
539  return std::make_pair(SolveResultProto::OPTIMAL,
540  JoinDetails(gscip_status_detail,
541  "Underlying gSCIP status: OPTIMAL."));
543  return std::make_pair(SolveResultProto::OPTIMAL,
544  JoinDetails(gscip_status_detail,
545  "Underlying gSCIP status: GAP_LIMIT."));
547  return std::make_pair(SolveResultProto::INFEASIBLE, gscip_status_detail);
548  case GScipOutput::UNBOUNDED: {
549  if (has_feasible_solution) {
550  return std::make_pair(
551  SolveResultProto::UNBOUNDED,
552  JoinDetails(gscip_status_detail,
553  "Underlying gSCIP status was UNBOUNDED, both primal "
554  "ray and feasible solution are present."));
555  } else {
556  return std::make_pair(
557  SolveResultProto::DUAL_INFEASIBLE,
558  JoinDetails(
559  gscip_status_detail,
560  "Underlying gSCIP status was UNBOUNDED, but only primal ray "
561  "was given, no feasible solution was found."));
562  }
563  }
564 
566  return std::make_pair(
567  SolveResultProto::DUAL_INFEASIBLE,
568  JoinDetails(gscip_status_detail,
569  "Underlying gSCIP status: INF_OR_UNBD."));
571  return std::make_pair(
572  SolveResultProto::OTHER_ERROR,
573  JoinDetails(gscip_status_detail,
574  "Underlying gSCIP status: OTHER_ERROR."));
576  return absl::InvalidArgumentError(gscip_status_detail);
577  default:
578  return absl::InternalError(JoinDetails(
579  gscip_status_detail, absl::StrCat("Missing GScipOutput.status case: ",
580  ProtoEnumToString(gscip_status))));
581  }
582 }
583 
584 absl::StatusOr<SolveResultProto> GScipSolver::CreateSolveResultProto(
585  GScipResult gscip_result,
586  const ModelSolveParametersProto& model_parameters) {
587  SolveResultProto solve_result;
589  const auto reason_and_detail,
590  ConvertTerminationReason(gscip_result.gscip_output.status(),
591  gscip_result.gscip_output.status_detail(),
592  !gscip_result.solutions.empty()));
593  solve_result.set_termination_reason(reason_and_detail.first);
594  solve_result.set_termination_detail(reason_and_detail.second);
595 
596  const int num_solutions = gscip_result.solutions.size();
597  CHECK_EQ(num_solutions, gscip_result.objective_values.size());
598 
599  LazyInitialized<std::vector<int64_t>> sorted_variables([&]() {
600  std::vector<int64_t> sorted;
601  sorted.reserve(variables_.size());
602  for (const auto& entry : variables_) {
603  sorted.emplace_back(entry.first);
604  }
605  std::sort(sorted.begin(), sorted.end());
606  return sorted;
607  });
608  for (int i = 0; i < gscip_result.solutions.size(); ++i) {
609  PrimalSolutionProto* const primal_solution =
610  solve_result.add_primal_solutions();
611  primal_solution->set_objective_value(gscip_result.objective_values[i]);
612  *primal_solution->mutable_variable_values() = FillSparseDoubleVector(
613  sorted_variables.GetOrCreate(), variables_, gscip_result.solutions[i],
614  model_parameters.primal_variables_filter());
615  }
616  if (!gscip_result.primal_ray.empty()) {
617  *solve_result.add_primal_rays()->mutable_variable_values() =
618  FillSparseDoubleVector(sorted_variables.GetOrCreate(), variables_,
619  gscip_result.primal_ray,
620  model_parameters.primal_variables_filter());
621  }
622  // TODO(user): add support for the basis and dual solutions in gscip, then
623  // populate them here.
624  SolveStatsProto* const common_stats = solve_result.mutable_solve_stats();
625  const GScipSolvingStats& gscip_stats = gscip_result.gscip_output.stats();
626  common_stats->set_best_dual_bound(gscip_stats.best_bound());
627  common_stats->set_best_primal_bound(gscip_stats.best_objective());
628  common_stats->set_node_count(gscip_stats.node_count());
629  common_stats->set_simplex_iterations(gscip_stats.primal_simplex_iterations() +
630  gscip_stats.dual_simplex_iterations());
631  common_stats->set_barrier_iterations(gscip_stats.total_lp_iterations() -
632  common_stats->simplex_iterations());
633  *solve_result.mutable_gscip_output() = std::move(gscip_result.gscip_output);
634  return solve_result;
635 }
636 
637 absl::StatusOr<std::unique_ptr<SolverInterface>> GScipSolver::New(
638  const ModelProto& model, const SolverInitializerProto& initializer) {
639  auto solver = absl::WrapUnique(new GScipSolver);
640  ASSIGN_OR_RETURN(solver->gscip_, GScip::Create(model.name()));
641  RETURN_IF_ERROR(solver->gscip_->SetMaximize(model.objective().maximize()));
643  solver->gscip_->SetObjectiveOffset(model.objective().offset()));
644  RETURN_IF_ERROR(solver->AddVariables(
645  model.variables(),
646  SparseDoubleVectorAsMap(model.objective().linear_coefficients())));
647  RETURN_IF_ERROR(solver->AddLinearConstraints(
648  model.linear_constraints(), model.linear_constraint_matrix()));
649  return solver;
650 }
651 
652 absl::StatusOr<SolveResultProto> GScipSolver::Solve(
653  const SolveParametersProto& parameters,
654  const ModelSolveParametersProto& model_parameters,
655  const CallbackRegistrationProto& callback_registration, const Callback cb) {
656  const absl::Time start = absl::Now();
657 
658  const std::unique_ptr<GScipSolverCallbackHandler> callback_handler =
659  GScipSolverCallbackHandler::RegisterIfNeeded(callback_registration, cb,
660  start, gscip_->scip());
661 
662  const GScipParameters gscip_parameters = MergeCommonParameters(
663  parameters.common_parameters(), parameters.gscip_parameters());
664  // TODO(user): reorganize gscip to respect warning is error argument on bad
665  // parameters.
666 
668  GScipResult gscip_result,
669  gscip_->Solve(
670  gscip_parameters,
671  /*legacy_params=*/"",
672  callback_handler ? callback_handler->MessageHandler() : nullptr));
673  if (callback_handler) {
674  RETURN_IF_ERROR(callback_handler->Flush());
675  }
677  SolveResultProto result,
678  CreateSolveResultProto(std::move(gscip_result), model_parameters));
680  absl::Now() - start, result.mutable_solve_stats()->mutable_solve_time()));
681  return result;
682 }
683 
684 absl::flat_hash_set<SCIP_VAR*> GScipSolver::LookupAllVariables(
685  absl::Span<const int64_t> variable_ids) {
686  absl::flat_hash_set<SCIP_VAR*> result;
687  result.reserve(variable_ids.size());
688  for (const int64_t var_id : variable_ids) {
689  result.insert(variables_.at(var_id));
690  }
691  return result;
692 }
693 
694 bool GScipSolver::CanUpdate(const ModelUpdateProto& model_update) {
695  return gscip_
696  ->CanSafeBulkDelete(
697  LookupAllVariables(model_update.deleted_variable_ids()))
698  .ok();
699 }
700 
701 absl::Status GScipSolver::Update(const ModelUpdateProto& model_update) {
702  for (const int64_t constraint_id :
703  model_update.deleted_linear_constraint_ids()) {
704  SCIP_CONS* const scip_cons = linear_constraints_.at(constraint_id);
705  linear_constraints_.erase(constraint_id);
706  RETURN_IF_ERROR(gscip_->DeleteConstraint(scip_cons));
707  }
708  {
709  const absl::flat_hash_set<SCIP_VAR*> vars_to_delete =
710  LookupAllVariables(model_update.deleted_variable_ids());
711  for (const int64_t deleted_variable_id :
712  model_update.deleted_variable_ids()) {
713  variables_.erase(deleted_variable_id);
714  }
715  RETURN_IF_ERROR(gscip_->SafeBulkDelete(vars_to_delete));
716  }
717  if (model_update.objective_updates().has_direction_update()) {
718  RETURN_IF_ERROR(gscip_->SetMaximize(
719  model_update.objective_updates().direction_update()));
720  }
721  if (model_update.objective_updates().has_offset_update()) {
722  RETURN_IF_ERROR(gscip_->SetObjectiveOffset(
723  model_update.objective_updates().offset_update()));
724  }
725  RETURN_IF_ERROR(UpdateVariables(model_update.variable_updates()));
726  const absl::flat_hash_map<int64_t, double> linear_objective_updates =
727  SparseDoubleVectorAsMap(
728  model_update.objective_updates().linear_coefficients());
729  for (const auto& obj_pair : linear_objective_updates) {
730  if (obj_pair.first < next_unused_variable_id_) {
732  gscip_->SetObjCoef(variables_.at(obj_pair.first), obj_pair.second));
733  }
734  }
736  AddVariables(model_update.new_variables(), linear_objective_updates));
738  UpdateLinearConstraints(model_update.linear_constraint_updates(),
739  model_update.linear_constraint_matrix_updates()));
741  AddLinearConstraints(model_update.new_linear_constraints(),
742  model_update.linear_constraint_matrix_updates()));
743  return absl::OkStatus();
744 }
745 
746 MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_GSCIP, GScipSolver::New)
747 
748 } // namespace math_opt
749 } // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:491
void GScipSetMaxNumThreads(int num_threads, GScipParameters *parameters)
static constexpr Status TERMINATE
Definition: gscip.pb.h:1232
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
static constexpr Status TOTAL_NODE_LIMIT
Definition: gscip.pb.h:1208
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const SolverInitializerProto &initializer)
void set_presolve(::operations_research::GScipParameters_MetaParamValue value)
Definition: gscip.pb.h:1416
static absl::StatusOr< std::unique_ptr< GScip > > Create(const std::string &problem_name)
Definition: gscip.cc:249
const int FATAL
Definition: log_severity.h:32
static constexpr Status STALL_NODE_LIMIT
Definition: gscip.pb.h:1210
#define CHECK_OK(x)
Definition: base/logging.h:42
::PROTOBUF_NAMESPACE_ID::Map< std::string, ::PROTOBUF_NAMESPACE_ID::int32 > * mutable_int_params()
Definition: gscip.pb.h:1502
::PROTOBUF_NAMESPACE_ID::Map< std::string, std::string > * mutable_char_params()
Definition: gscip.pb.h:1589
void MergeFrom(const GScipParameters &from)
Definition: gscip.pb.cc:1417
void InsertOrDie(Collection *const collection, const typename Collection::value_type &value)
Definition: map_util.h:154
static constexpr MetaParamValue OFF
Definition: gscip.pb.h:524
void set_separating(::operations_research::GScipParameters_MetaParamValue value)
Definition: gscip.pb.h:1444
#define LOG(severity)
Definition: base/logging.h:416
GRBmodel * model
static constexpr Status USER_INTERRUPT
Definition: gscip.pb.h:1204
static constexpr Status MEM_LIMIT
Definition: gscip.pb.h:1214
int NumMatrixNonzeros(const SparseDoubleMatrixProto &matrix)
int NumVariables(const VariablesProto &variables)
void set_heuristics(::operations_research::GScipParameters_MetaParamValue value)
Definition: gscip.pb.h:1388
SparseDoubleVectorProto FillSparseDoubleVector(const std::vector< int64_t > &ids_in_order, const absl::flat_hash_map< int64_t, IndexType > &id_map, const glop::StrictITIVector< IndexType, glop::Fractional > &values, const SparseVectorFilterProto &filter)
Definition: glop_solver.cc:322
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
Definition: protoutil.h:27
absl::string_view name
void GScipSetTimeLimit(absl::Duration time_limit, GScipParameters *parameters)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto &parameters, const ModelSolveParametersProto &model_parameters, const CallbackRegistrationProto &callback_registration, Callback cb) override
static constexpr Status BEST_SOL_LIMIT
Definition: gscip.pb.h:1220
double upper_bound
static constexpr Status UNKNOWN
Definition: gscip.pb.h:1202
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:42
static constexpr MetaParamValue FAST
Definition: gscip.pb.h:522
bool CanUpdate(const ModelUpdateProto &model_update) override
static std::unique_ptr< GScipSolverCallbackHandler > RegisterIfNeeded(const CallbackRegistrationProto &callback_registration, SolverInterface::Callback callback, absl::Time solve_start, SCIP *scip)
double lower_bound
static constexpr Status INF_OR_UNBD
Definition: gscip.pb.h:1230
int64_t linear_constraint_id
static constexpr Status GAP_LIMIT
Definition: gscip.pb.h:1216
int index
Definition: pack.cc:509
static constexpr Status TIME_LIMIT
Definition: gscip.pb.h:1212
std::string ProtoEnumToString(ProtoEnumType enum_value)
GScipParameters::MetaParamValue ConvertMathOptEmphasis(Emphasis emphasis)
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
int NumConstraints(const LinearConstraintsProto &linear_constraints)
static constexpr Status NODE_LIMIT
Definition: gscip.pb.h:1206
void GScipSetRandomSeed(GScipParameters *parameters, int random_seed)
static constexpr Status INVALID_SOLVER_PARAMETERS
Definition: gscip.pb.h:1234
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:29
static constexpr MetaParamValue DEFAULT_META_PARAM_VALUE
Definition: gscip.pb.h:518
static constexpr Status OPTIMAL
Definition: gscip.pb.h:1224
absl::Status Update(const ModelUpdateProto &model_update) override
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
absl::Span< const int64_t > variable_ids
static constexpr MetaParamValue AGGRESSIVE
Definition: gscip.pb.h:520
static GScipParameters MergeCommonParameters(const CommonSolveParametersProto &common_solver_parameters, const GScipParameters &gscip_parameters)
Collection of objects used to extend the Constraint Solver library.
static constexpr Status RESTART_LIMIT
Definition: gscip.pb.h:1222
MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_CP_SAT, CpSatSolver::New)
absl::Span< const double > coefficients
SatParameters parameters
GScipOutput_Status Status
Definition: gscip.pb.h:1201
#define RETURN_IF_ERROR(expr)
Definition: status_macros.h:29
#define ABSL_DIE_IF_NULL
Definition: base/logging.h:41
static constexpr Status INFEASIBLE
Definition: gscip.pb.h:1226
static constexpr Status SOL_LIMIT
Definition: gscip.pb.h:1218
int64_t value
#define ASSIGN_OR_RETURN(lhs, rexpr)
Definition: status_macros.h:55
static constexpr Status UNBOUNDED
Definition: gscip.pb.h:1228