OR-Tools  9.6
gscip_solver.h
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 #ifndef OR_TOOLS_MATH_OPT_SOLVERS_GSCIP_SOLVER_H_
15 #define OR_TOOLS_MATH_OPT_SOLVERS_GSCIP_SOLVER_H_
16 
17 #include <cstdint>
18 #include <memory>
19 #include <optional>
20 #include <utility>
21 #include <vector>
22 
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/container/flat_hash_set.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/types/span.h"
28 #include "google/protobuf/map.h"
30 #include "ortools/gscip/gscip.h"
31 #include "ortools/gscip/gscip.pb.h"
33 #include "ortools/math_opt/callback.pb.h"
38 #include "ortools/math_opt/model.pb.h"
39 #include "ortools/math_opt/model_parameters.pb.h"
40 #include "ortools/math_opt/model_update.pb.h"
41 #include "ortools/math_opt/parameters.pb.h"
42 #include "ortools/math_opt/result.pb.h"
43 #include "ortools/math_opt/sparse_containers.pb.h"
44 #include "scip/type_cons.h"
45 #include "scip/type_var.h"
46 
47 namespace operations_research {
48 namespace math_opt {
49 
50 class GScipSolver : public SolverInterface {
51  public:
52  static absl::StatusOr<std::unique_ptr<SolverInterface>> New(
53  const ModelProto& model, const InitArgs& init_args);
54 
55  absl::StatusOr<SolveResultProto> Solve(
56  const SolveParametersProto& parameters,
57  const ModelSolveParametersProto& model_parameters,
58  MessageCallback message_cb,
59  const CallbackRegistrationProto& callback_registration, Callback cb,
60  SolveInterrupter* interrupter) override;
61  absl::StatusOr<bool> Update(const ModelUpdateProto& model_update) override;
62 
63  // Returns the merged parameters and a list of warnings for unsupported
64  // parameters.
65  static absl::StatusOr<GScipParameters> MergeParameters(
66  const SolveParametersProto& solve_parameters);
67 
68  private:
69  // A simple class for managing extra variables and constraints introduced to
70  // model higher-level constructs.
71  //
72  // This is useful when auxiliary variables and constraints are introduced
73  // transparently to the user, and must be deleted when the corresponding
74  // higher-level construct is deleted.
75  struct AuxiliaryStructureHandler {
76  // Removes all registered slack variables and constraints from the model.
77  // Those deleted variables and constraints are then dropped from this
78  // handler (i.e., this function is idempotent).
79  absl::Status DeleteStructure(GScip& gscip);
80 
81  std::vector<SCIP_VAR*> variables;
82  std::vector<SCIP_CONS*> constraints;
83  };
84 
85  // Event handler that it used to call SCIPinterruptSolve() is a safe manner.
86  //
87  // At the start of SCIPsolve(), SCIP resets `userinterrupt` to false. It does
88  // the same in SCIPpresolve(), which is called at the beginning of SCIPsolve()
89  // but also at the beginning of each restart. the `userinterrupt` can also be
90  // reset when the transformed problem is freed when the parameter
91  // "misc/resetstat" is used. On top of that, it is not possible to call
92  // SCIPinterruptSolve() in SCIP_STAGE_INITSOLVE stage; which occurs in the
93  // middle of the solve and at restarts.
94  //
95  // If this was no enough, SCIPinterruptSolve() calls SCIPcheckStage() which is
96  // not thread-safe.
97  //
98  // As a consequence, although it is possible to call SCIPinterruptSolve() from
99  // another thread, it is unreliable at best. Here we take a safer approach: we
100  // call it only from the Exec() of an even handler. This solves all thread
101  // safety issues and, if we have been careful, also ensures we don't call it
102  // in the wrong stage. This also solves the issue the multiple resets of the
103  // `userinterrupt` flag since each time we are called after the interrupter
104  // has been triggered, we simply call SCIPinterruptSolve() until SCIP finally
105  // listens.
106  struct InterruptEventHandler : public GScipEventHandler {
107  InterruptEventHandler();
108 
109  SCIP_RETCODE Init(GScip* gscip) override;
110  SCIP_RETCODE Execute(GScipEventHandlerContext) override;
111 
112  // Calls SCIPinterruptSolve() if the interrupter is set and triggered and
113  // SCIP is in a valid stage for that.
114  SCIP_RETCODE TryCallInterruptIfNeeded(GScip* gscip);
115 
116  // This will be set before SCIPsolve() is called and reset after the end of
117  // the call. It may be null when the user does not provide an interrupter;
118  // in that case we don't register any event.
119  SolveInterrupter* interrupter = nullptr;
120  };
121 
122  explicit GScipSolver(std::unique_ptr<GScip> gscip);
123 
124  // Adds the new variables.
125  absl::Status AddVariables(const VariablesProto& variables,
126  const absl::flat_hash_map<int64_t, double>&
127  linear_objective_coefficients);
128 
129  // Update existing variables' parameters. Returns false if the update cannot
130  // be performed (namely, if attempting to update bounds on a binary variable).
131  absl::StatusOr<bool> UpdateVariables(
132  const VariableUpdatesProto& variable_updates);
133 
134  absl::Status AddQuadraticObjectiveTerms(
135  const SparseDoubleMatrixProto& new_qp_terms, bool maximize);
136 
137  // Adds the new linear constraints.
138  absl::Status AddLinearConstraints(
139  const LinearConstraintsProto& linear_constraints,
140  const SparseDoubleMatrixProto& linear_constraint_matrix);
141 
142  // Updates the existing constraints and the coefficients of the existing
143  // variables of these constraints.
144  absl::Status UpdateLinearConstraints(
145  const LinearConstraintUpdatesProto linear_constraint_updates,
146  const SparseDoubleMatrixProto& linear_constraint_matrix,
147  std::optional<int64_t> first_new_var_id,
148  std::optional<int64_t> first_new_cstr_id);
149 
150  absl::Status AddQuadraticConstraints(
151  const google::protobuf::Map<int64_t, QuadraticConstraintProto>&
152  quadratic_constraints);
153 
154  absl::Status AddIndicatorConstraints(
155  const google::protobuf::Map<int64_t, IndicatorConstraintProto>&
156  indicator_constraints);
157 
158  // Given a linear `expression`, add a new `variable` and constraint such that
159  // `variable == expression`. Returns the associated SCIP pointers to the new
160  // slack variable and constraint.
161  absl::StatusOr<std::pair<SCIP_VAR*, SCIP_CONS*>>
162  AddSlackVariableEqualToExpression(const LinearExpressionProto& expression);
163 
164  // Unpacks a `SosConstraintProto` into the associated data for GScip. As the
165  // MathOpt protos allow SOS terms to be arbitrary linear expressions, slack
166  // variables and constraints to represent nontrivial expressions are added to
167  // the model and tracked by the returned `AuxiliaryStructureHandler`.
168  absl::StatusOr<std::pair<GScipSOSData, AuxiliaryStructureHandler>>
169  ProcessSosProto(const SosConstraintProto& sos_constraint);
170 
171  absl::Status AddSos1Constraints(
172  const google::protobuf::Map<int64_t, SosConstraintProto>&
173  sos1_constraints);
174  absl::Status AddSos2Constraints(
175  const google::protobuf::Map<int64_t, SosConstraintProto>&
176  sos2_constraints);
177 
178  absl::flat_hash_set<SCIP_VAR*> LookupAllVariables(
179  absl::Span<const int64_t> variable_ids);
180  absl::StatusOr<SolveResultProto> CreateSolveResultProto(
181  GScipResult gscip_result,
182  const ModelSolveParametersProto& model_parameters,
183  std::optional<double> cutoff);
184 
185  // Returns the ids of variables and linear constraints with inverted bounds.
186  InvertedBounds ListInvertedBounds() const;
187 
188  // Returns the indicator constraints with non-binary indicator variables.
189  InvalidIndicators ListInvalidIndicators() const;
190 
191  const std::unique_ptr<GScip> gscip_;
192  InterruptEventHandler interrupt_event_handler_;
193  absl::flat_hash_map<int64_t, SCIP_VAR*> variables_;
194  bool has_quadratic_objective_ = false;
195  absl::flat_hash_map<int64_t, SCIP_CONS*> linear_constraints_;
196  absl::flat_hash_map<int64_t, SCIP_CONS*> quadratic_constraints_;
197  // Values, if unset, correspond to indicator constraints with unset indicator
198  // variables. If set, tracks the constraint pointer and indicator variable ID.
199  // The constraint pointer must be non-null.
200  absl::flat_hash_map<int64_t, std::optional<std::pair<SCIP_CONS*, int64_t>>>
201  indicator_constraints_;
202  absl::flat_hash_map<int64_t, std::pair<SCIP_CONS*, AuxiliaryStructureHandler>>
203  sos1_constraints_;
204  absl::flat_hash_map<int64_t, std::pair<SCIP_CONS*, AuxiliaryStructureHandler>>
205  sos2_constraints_;
206 };
207 
208 } // namespace math_opt
209 } // namespace operations_research
210 
211 #endif // OR_TOOLS_MATH_OPT_SOLVERS_GSCIP_SOLVER_H_
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto &parameters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, SolveInterrupter *interrupter) override
static absl::StatusOr< GScipParameters > MergeParameters(const SolveParametersProto &solve_parameters)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
SatParameters parameters
absl::Span< const int64_t > variable_ids
GRBmodel * model
Collection of objects used to extend the Constraint Solver library.