OR-Tools  9.6
solver.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 
15 
16 #include <stdint.h>
17 
18 #include <functional>
19 #include <memory>
20 #include <string>
21 #include <utility>
22 
23 #include "absl/base/thread_annotations.h"
24 #include "absl/memory/memory.h"
25 #include "absl/status/status.h"
26 #include "absl/status/statusor.h"
27 #include "absl/strings/str_cat.h"
28 #include "absl/synchronization/mutex.h"
29 #include "absl/types/span.h"
31 #include "ortools/base/logging.h"
33 #include "ortools/math_opt/callback.pb.h"
39 #include "ortools/math_opt/model.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"
49 
50 namespace operations_research {
51 namespace math_opt {
52 
53 namespace {
54 
55 // Returns an InternalError with the input status message if the input status is
56 // not OK.
57 absl::Status ToInternalError(const absl::Status original) {
58  if (original.ok()) {
59  return original;
60  }
61 
62  return absl::InternalError(original.message());
63 }
64 
65 // Returns the Status returned by Solve() & Update() when called after a
66 // previous call to one of them failed.
67 absl::Status PreviousFatalFailureOccurred() {
68  return absl::InvalidArgumentError(
69  "a previous call to Solve() or Update() failed, the Solver can't be used "
70  "anymore");
71 }
72 
73 } // namespace
74 
75 absl::StatusOr<SolveResultProto> Solver::NonIncrementalSolve(
76  const ModelProto& model, const SolverTypeProto solver_type,
77  const InitArgs& init_args, const SolveArgs& solve_args) {
78  ASSIGN_OR_RETURN(std::unique_ptr<Solver> solver,
79  Solver::New(solver_type, model, init_args));
80  return solver->Solve(solve_args);
81 }
82 
83 Solver::Solver(std::unique_ptr<SolverInterface> underlying_solver,
84  ModelSummary model_summary)
85  : underlying_solver_(std::move(underlying_solver)),
86  model_summary_(std::move(model_summary)) {
87  CHECK(underlying_solver_ != nullptr);
89 }
90 
92 
93 absl::StatusOr<std::unique_ptr<Solver>> Solver::New(
94  const SolverTypeProto solver_type, const ModelProto& model,
95  const InitArgs& arguments) {
96  RETURN_IF_ERROR(internal::ValidateInitArgs(arguments, solver_type));
99  auto underlying_solver,
100  AllSolversRegistry::Instance()->Create(solver_type, model, arguments));
101  auto result = absl::WrapUnique(
102  new Solver(std::move(underlying_solver), std::move(summary)));
103  return result;
104 }
105 
106 absl::StatusOr<SolveResultProto> Solver::Solve(const SolveArgs& arguments) {
107  ASSIGN_OR_RETURN(const auto guard,
108  ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
109 
110  if (fatal_failure_occurred_) {
111  return PreviousFatalFailureOccurred();
112  }
113  CHECK(underlying_solver_ != nullptr);
114 
115  // We will reset it in code paths where no error occur.
116  fatal_failure_occurred_ = true;
117 
118  // TODO(b/168037341): we should validate the result maths. Since the result
119  // can be filtered, this should be included in the solver_interface
120  // implementations.
121 
123  << "invalid parameters";
125  ValidateModelSolveParameters(arguments.model_parameters, model_summary_))
126  << "invalid model_parameters";
127 
128  SolverInterface::Callback cb = nullptr;
129  if (arguments.user_cb != nullptr) {
131  arguments.callback_registration, model_summary_));
132  cb = [&](const CallbackDataProto& callback_data)
133  -> absl::StatusOr<CallbackResultProto> {
135  callback_data, arguments.callback_registration, model_summary_));
136  auto callback_result = arguments.user_cb(callback_data);
138  callback_result, callback_data.event(),
139  arguments.callback_registration, model_summary_));
140  return callback_result;
141  };
142  }
143 
144  ASSIGN_OR_RETURN(const SolveResultProto result,
145  underlying_solver_->Solve(arguments.parameters,
146  arguments.model_parameters,
147  arguments.message_callback,
148  arguments.callback_registration,
149  cb, arguments.interrupter));
150 
151  // We consider errors in `result` to be internal errors, but
152  // `ValidateResult()` will return an InvalidArgumentError. So here we convert
153  // the error.
154  RETURN_IF_ERROR(ToInternalError(
155  ValidateResult(result, arguments.model_parameters, model_summary_)));
156 
157  fatal_failure_occurred_ = false;
158  return result;
159 }
160 
161 absl::StatusOr<bool> Solver::Update(const ModelUpdateProto& model_update) {
162  ASSIGN_OR_RETURN(const auto guard,
163  ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
164 
165  if (fatal_failure_occurred_) {
166  return PreviousFatalFailureOccurred();
167  }
168  CHECK(underlying_solver_ != nullptr);
169 
170  // We will reset it in code paths where no error occur.
171  fatal_failure_occurred_ = true;
172 
173  RETURN_IF_ERROR(ValidateModelUpdate(model_update, model_summary_));
174  ASSIGN_OR_RETURN(const bool updated,
175  underlying_solver_->Update(model_update));
176  if (!updated) {
177  // We only destroy underlying_solver_ in this specific case as it would be
178  // incorrect to destroy if the solver is GLPK and the error is that we are
179  // trying to use it in a different thread. Here we know this is not the case
180  // as Update() would have returned an error.
181  underlying_solver_ = nullptr;
182  return false;
183  }
184 
185  fatal_failure_occurred_ = false;
186 
187  return true;
188 }
189 
190 namespace internal {
191 
192 absl::Status ValidateInitArgs(const Solver::InitArgs& init_args,
193  const SolverTypeProto solver_type) {
194  if (solver_type == SOLVER_TYPE_UNSPECIFIED) {
195  return absl::InvalidArgumentError(
196  "can't use SOLVER_TYPE_UNSPECIFIED as solver_type parameter");
197  }
198 
199  if (init_args.non_streamable != nullptr &&
200  init_args.non_streamable->solver_type() != solver_type) {
201  return absl::InvalidArgumentError(
202  absl::StrCat("input non_streamable init arguments are for ",
204  " but solver_type is ", ProtoEnumToString(solver_type)));
205  }
206 
207  return absl::OkStatus();
208 }
209 
210 } // namespace internal
211 } // namespace math_opt
212 } // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< ConcurrentCallsGuard > TryAcquire(Tracker &tracker)
static absl::StatusOr< SolveResultProto > NonIncrementalSolve(const ModelProto &model, SolverTypeProto solver_type, const InitArgs &init_args, const SolveArgs &solve_args)
Definition: solver.cc:75
absl::StatusOr< SolveResultProto > Solve(const SolveArgs &arguments)
Definition: solver.cc:106
static absl::StatusOr< std::unique_ptr< Solver > > New(SolverTypeProto solver_type, const ModelProto &model, const InitArgs &arguments)
Definition: solver.cc:93
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update)
Definition: solver.cc:161
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
GRBmodel * model
std::atomic< int64_t > debug_num_solver
Definition: solver_debug.cc:23
absl::Status ValidateInitArgs(const Solver::InitArgs &init_args, const SolverTypeProto solver_type)
Definition: solver.cc:192
absl::Status ValidateResult(const SolveResultProto &result, const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::Status ValidateModelUpdate(const ModelUpdateProto &model_update, ModelSummary &model_summary)
absl::Status ValidateSolveParameters(const SolveParametersProto &parameters)
absl::Status ValidateCallbackResultProto(const CallbackResultProto &callback_result, const CallbackEventProto callback_event, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::Status ValidateCallbackDataProto(const CallbackDataProto &cb_data, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::Status ValidateCallbackRegistration(const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::StatusOr< ModelSummary > ValidateModel(const ModelProto &model, const bool check_names)
absl::Status ValidateModelSolveParameters(const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
CallbackRegistrationProto callback_registration
Definition: solver.h:97
ModelSolveParametersProto model_parameters
Definition: solver.h:88
const NonStreamableSolverInitArguments * non_streamable