Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solver.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <memory>
17#include <utility>
18
19#include "absl/log/check.h"
20#include "absl/memory/memory.h"
21#include "absl/status/status.h"
22#include "absl/status/statusor.h"
23#include "absl/strings/str_cat.h"
44
45namespace operations_research {
46namespace math_opt {
47
48namespace {
49
50// Returns an InternalError with the input status message if the input status is
51// not OK.
52absl::Status ToInternalError(absl::Status original) {
53 if (original.ok()) {
54 return original;
55 }
56
57 return absl::InternalError(original.message());
58}
59
60// Returns the Status returned by Solve() & Update() when called after a
61// previous call to one of them failed.
62absl::Status PreviousFatalFailureOccurred() {
63 return absl::InvalidArgumentError(
64 "a previous call to Solve(), ComputeInfeasibleSubsystem(), or Update() "
65 "failed, the Solver can't be used anymore");
66}
67
68} // namespace
69
70absl::StatusOr<SolveResultProto> Solver::NonIncrementalSolve(
71 const ModelProto& model, const SolverTypeProto solver_type,
72 const InitArgs& init_args, const SolveArgs& solve_args) {
73 ASSIGN_OR_RETURN(std::unique_ptr<Solver> solver,
74 Solver::New(solver_type, model, init_args));
75 return solver->Solve(solve_args);
76}
77
78Solver::Solver(std::unique_ptr<SolverInterface> underlying_solver,
79 ModelSummary model_summary)
80 : underlying_solver_(std::move(underlying_solver)),
81 model_summary_(std::move(model_summary)) {
82 CHECK(underlying_solver_ != nullptr);
84}
85
87
88absl::StatusOr<std::unique_ptr<Solver>> Solver::New(
89 const SolverTypeProto solver_type, const ModelProto& model,
90 const InitArgs& arguments) {
91 RETURN_IF_ERROR(internal::ValidateInitArgs(arguments, solver_type));
94 auto underlying_solver,
95 AllSolversRegistry::Instance()->Create(solver_type, model, arguments));
96 auto result = absl::WrapUnique(
97 new Solver(std::move(underlying_solver), std::move(summary)));
98 return result;
99}
100
101absl::StatusOr<SolveResultProto> Solver::Solve(const SolveArgs& arguments) {
102 ASSIGN_OR_RETURN(const auto guard,
103 ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
104
105 if (fatal_failure_occurred_) {
106 return PreviousFatalFailureOccurred();
107 }
108 CHECK(underlying_solver_ != nullptr);
109
110 // We will reset it in code paths where no error occur.
111 fatal_failure_occurred_ = true;
112
113 // TODO(b/168037341): we should validate the result maths. Since the result
114 // can be filtered, this should be included in the solver_interface
115 // implementations.
116
118 << "invalid parameters";
120 ValidateModelSolveParameters(arguments.model_parameters, model_summary_))
121 << "invalid model_parameters";
122
124 model_summary_));
125 SolverInterface::Callback cb = nullptr;
126 if (!arguments.callback_registration.request_registration().empty() &&
127 arguments.user_cb == nullptr) {
128 return absl::InvalidArgumentError(
129 "no callback function was provided but callback events were "
130 "registered");
131 }
132 if (arguments.user_cb != nullptr) {
133 cb = [&](const CallbackDataProto& callback_data)
134 -> absl::StatusOr<CallbackResultProto> {
136 callback_data, arguments.callback_registration, model_summary_));
137 auto callback_result = arguments.user_cb(callback_data);
139 callback_result, callback_data.event(),
140 arguments.callback_registration, model_summary_));
141 return callback_result;
142 };
143 }
144
146 underlying_solver_->Solve(arguments.parameters,
147 arguments.model_parameters,
148 arguments.message_callback,
149 arguments.callback_registration,
150 cb, arguments.interrupter));
151 // TODO(b/290091715): Remove once language specific structs can use new
152 // messages.
154 // We consider errors in `result` to be internal errors, but
155 // `ValidateResult()` will return an InvalidArgumentError. So here we convert
156 // the error.
157 RETURN_IF_ERROR(ToInternalError(
158 ValidateResult(result, arguments.model_parameters, model_summary_)));
159
160 fatal_failure_occurred_ = false;
161 return result;
162}
163
164absl::StatusOr<bool> Solver::Update(const ModelUpdateProto model_update) {
165 ASSIGN_OR_RETURN(const auto guard,
166 ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
167
168 if (fatal_failure_occurred_) {
169 return PreviousFatalFailureOccurred();
170 }
171 CHECK(underlying_solver_ != nullptr);
172
173 // We will reset it in code paths where no error occur.
174 fatal_failure_occurred_ = true;
175
176 RETURN_IF_ERROR(ValidateModelUpdate(model_update, model_summary_));
177 ASSIGN_OR_RETURN(const bool updated,
178 underlying_solver_->Update(model_update));
179 if (!updated) {
180 // We only destroy underlying_solver_ in this specific case as it would be
181 // incorrect to destroy if the solver is GLPK and the error is that we are
182 // trying to use it in a different thread. Here we know this is not the case
183 // as Update() would have returned an error.
184 underlying_solver_ = nullptr;
185 return false;
186 }
187
188 fatal_failure_occurred_ = false;
189
190 return true;
191}
192
193absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
195 const ComputeInfeasibleSubsystemArgs& arguments) {
196 ASSIGN_OR_RETURN(const auto guard,
197 ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
198
199 if (fatal_failure_occurred_) {
200 return PreviousFatalFailureOccurred();
201 }
202 CHECK(underlying_solver_ != nullptr);
203
204 // We will reset it in code paths where no error occur.
205 fatal_failure_occurred_ = true;
206
208 << "invalid parameters";
209
211 underlying_solver_->ComputeInfeasibleSubsystem(
212 arguments.parameters, arguments.message_callback,
213 arguments.interrupter));
214
215 // We consider errors in `result` to be internal errors, but
216 // `ValidateInfeasibleSubsystemResult()` will return an InvalidArgumentError.
217 // So here we convert the error.
218 RETURN_IF_ERROR(ToInternalError(
219 ValidateComputeInfeasibleSubsystemResult(result, model_summary_)));
220
221 fatal_failure_occurred_ = false;
222 return result;
223}
224
225absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
227 const ModelProto& model, const SolverTypeProto solver_type,
228 const InitArgs& init_args,
229 const ComputeInfeasibleSubsystemArgs& compute_infeasible_subsystem_args) {
230 ASSIGN_OR_RETURN(std::unique_ptr<Solver> solver,
231 Solver::New(solver_type, model, init_args));
232 return solver->ComputeInfeasibleSubsystem(compute_infeasible_subsystem_args);
233}
234
235namespace internal {
236
237absl::Status ValidateInitArgs(const Solver::InitArgs& init_args,
238 const SolverTypeProto solver_type) {
239 if (solver_type == SOLVER_TYPE_UNSPECIFIED) {
240 return absl::InvalidArgumentError(
241 "can't use SOLVER_TYPE_UNSPECIFIED as solver_type parameter");
242 }
243
244 if (init_args.non_streamable != nullptr &&
245 init_args.non_streamable->solver_type() != solver_type) {
246 return absl::InvalidArgumentError(
247 absl::StrCat("input non_streamable init arguments are for ",
249 " but solver_type is ", ProtoEnumToString(solver_type)));
250 }
251
252 return absl::OkStatus();
253}
254
255} // namespace internal
256} // namespace math_opt
257} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static AllSolversRegistry *absl_nonnull Instance()
::operations_research::math_opt::CallbackEventProto request_registration(int index) const
static absl::StatusOr< ConcurrentCallsGuard > TryAcquire(Tracker &tracker)
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
static absl::StatusOr< SolveResultProto > NonIncrementalSolve(const ModelProto &model, SolverTypeProto solver_type, const InitArgs &init_args, const SolveArgs &solve_args)
Definition solver.cc:70
SolverInterface::InitArgs InitArgs
Definition solver.h:68
absl::StatusOr< bool > Update(ModelUpdateProto model_update) override
Definition solver.cc:164
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const ComputeInfeasibleSubsystemArgs &arguments) override
Definition solver.cc:194
absl::StatusOr< SolveResultProto > Solve(const SolveArgs &arguments) override
Definition solver.cc:101
static absl::StatusOr< ComputeInfeasibleSubsystemResultProto > NonIncrementalComputeInfeasibleSubsystem(const ModelProto &model, SolverTypeProto solver_type, const InitArgs &init_args, const ComputeInfeasibleSubsystemArgs &compute_infeasible_subsystem_args)
Definition solver.cc:226
static absl::StatusOr< std::unique_ptr< Solver > > New(SolverTypeProto solver_type, const ModelProto &model, const InitArgs &arguments)
Definition solver.cc:88
std::atomic< int64_t > debug_num_solver
absl::Status ValidateInitArgs(const Solver::InitArgs &init_args, const SolverTypeProto solver_type)
Definition solver.cc:237
absl::Status ValidateCallbackResultProto(const CallbackResultProto &callback_result, const CallbackEventProto callback_event, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
void UpgradeSolveResultProtoForStatsMigration(SolveResultProto &solve_result_proto)
absl::Status ValidateModelUpdate(const ModelUpdateProto &model_update, ModelSummary &model_summary)
absl::Status ValidateComputeInfeasibleSubsystemResult(const ComputeInfeasibleSubsystemResultProto &result, const ModelSummary &model_summary)
absl::Status ValidateCallbackDataProto(const CallbackDataProto &cb_data, const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
absl::Status ValidateResult(const SolveResultProto &result, const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::Status ValidateModelSolveParameters(const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::StatusOr< ModelSummary > ValidateModel(const ModelProto &model, const bool check_names)
absl::Status ValidateSolveParameters(const SolveParametersProto &parameters)
absl::Status ValidateCallbackRegistration(const CallbackRegistrationProto &callback_registration, const ModelSummary &model_summary)
OR-Tools root namespace.
std::string ProtoEnumToString(ProtoEnumType enum_value)
Definition proto_utils.h:63
STL namespace.
const SolveInterrupter *absl_nullable interrupter
Definition base_solver.h:82
const NonStreamableSolverInitArguments *absl_nullable non_streamable