Google OR-Tools v9.11
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-2024 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"
25#include "ortools/math_opt/callback.pb.h"
32#include "ortools/math_opt/infeasible_subsystem.pb.h"
33#include "ortools/math_opt/model.pb.h"
34#include "ortools/math_opt/model_update.pb.h"
35#include "ortools/math_opt/parameters.pb.h"
36#include "ortools/math_opt/result.pb.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(const 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
123 SolverInterface::Callback cb = nullptr;
124 if (arguments.user_cb != nullptr) {
126 arguments.callback_registration, model_summary_));
127 cb = [&](const CallbackDataProto& callback_data)
128 -> absl::StatusOr<CallbackResultProto> {
130 callback_data, arguments.callback_registration, model_summary_));
131 auto callback_result = arguments.user_cb(callback_data);
133 callback_result, callback_data.event(),
134 arguments.callback_registration, model_summary_));
135 return callback_result;
136 };
137 }
138
139 ASSIGN_OR_RETURN(SolveResultProto result,
140 underlying_solver_->Solve(arguments.parameters,
141 arguments.model_parameters,
142 arguments.message_callback,
143 arguments.callback_registration,
144 cb, arguments.interrupter));
145 // TODO(b/290091715): Remove once language specific structs can use new
146 // messages.
148 // We consider errors in `result` to be internal errors, but
149 // `ValidateResult()` will return an InvalidArgumentError. So here we convert
150 // the error.
151 RETURN_IF_ERROR(ToInternalError(
152 ValidateResult(result, arguments.model_parameters, model_summary_)));
153
154 fatal_failure_occurred_ = false;
155 return result;
156}
157
158absl::StatusOr<bool> Solver::Update(const ModelUpdateProto model_update) {
159 ASSIGN_OR_RETURN(const auto guard,
160 ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
161
162 if (fatal_failure_occurred_) {
163 return PreviousFatalFailureOccurred();
164 }
165 CHECK(underlying_solver_ != nullptr);
166
167 // We will reset it in code paths where no error occur.
168 fatal_failure_occurred_ = true;
169
170 RETURN_IF_ERROR(ValidateModelUpdate(model_update, model_summary_));
171 ASSIGN_OR_RETURN(const bool updated,
172 underlying_solver_->Update(model_update));
173 if (!updated) {
174 // We only destroy underlying_solver_ in this specific case as it would be
175 // incorrect to destroy if the solver is GLPK and the error is that we are
176 // trying to use it in a different thread. Here we know this is not the case
177 // as Update() would have returned an error.
178 underlying_solver_ = nullptr;
179 return false;
180 }
181
182 fatal_failure_occurred_ = false;
183
184 return true;
185}
186
187absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
189 const ComputeInfeasibleSubsystemArgs& arguments) {
190 ASSIGN_OR_RETURN(const auto guard,
191 ConcurrentCallsGuard::TryAcquire(concurrent_calls_tracker_));
192
193 if (fatal_failure_occurred_) {
194 return PreviousFatalFailureOccurred();
195 }
196 CHECK(underlying_solver_ != nullptr);
197
198 // We will reset it in code paths where no error occur.
199 fatal_failure_occurred_ = true;
200
202 << "invalid parameters";
203
204 ASSIGN_OR_RETURN(const ComputeInfeasibleSubsystemResultProto result,
205 underlying_solver_->ComputeInfeasibleSubsystem(
206 arguments.parameters, arguments.message_callback,
207 arguments.interrupter));
208
209 // We consider errors in `result` to be internal errors, but
210 // `ValidateInfeasibleSubsystemResult()` will return an InvalidArgumentError.
211 // So here we convert the error.
212 RETURN_IF_ERROR(ToInternalError(
213 ValidateComputeInfeasibleSubsystemResult(result, model_summary_)));
214
215 fatal_failure_occurred_ = false;
216 return result;
217}
218
219absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
221 const ModelProto& model, const SolverTypeProto solver_type,
222 const InitArgs& init_args,
223 const ComputeInfeasibleSubsystemArgs& compute_infeasible_subsystem_args) {
224 ASSIGN_OR_RETURN(std::unique_ptr<Solver> solver,
225 Solver::New(solver_type, model, init_args));
226 return solver->ComputeInfeasibleSubsystem(compute_infeasible_subsystem_args);
227}
228
229namespace internal {
230
231absl::Status ValidateInitArgs(const Solver::InitArgs& init_args,
232 const SolverTypeProto solver_type) {
233 if (solver_type == SOLVER_TYPE_UNSPECIFIED) {
234 return absl::InvalidArgumentError(
235 "can't use SOLVER_TYPE_UNSPECIFIED as solver_type parameter");
236 }
237
238 if (init_args.non_streamable != nullptr &&
239 init_args.non_streamable->solver_type() != solver_type) {
240 return absl::InvalidArgumentError(
241 absl::StrCat("input non_streamable init arguments are for ",
243 " but solver_type is ", ProtoEnumToString(solver_type)));
244 }
245
246 return absl::OkStatus();
247}
248
249} // namespace internal
250} // namespace math_opt
251} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
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)
A shortcut for calling Solver::New() and then Solver::Solve().
Definition solver.cc:70
absl::StatusOr< bool > Update(ModelUpdateProto model_update) override
Definition solver.cc:158
absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const ComputeInfeasibleSubsystemArgs &arguments) override
Computes an infeasible subsystem of model (including all updates).
Definition solver.cc:188
absl::StatusOr< SolveResultProto > Solve(const SolveArgs &arguments) override
Solves the current model (including all updates).
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:220
static absl::StatusOr< std::unique_ptr< Solver > > New(SolverTypeProto solver_type, const ModelProto &model, const InitArgs &arguments)
Definition solver.cc:88
GRBmodel * model
std::atomic< int64_t > debug_num_solver
absl::Status ValidateInitArgs(const Solver::InitArgs &init_args, const SolverTypeProto solver_type)
Definition solver.cc:231
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)
Validates the input result.
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)
Checks that CallbackRegistrationProto is valid given a valid model summary.
In SWIG mode, we don't want anything besides these top-level includes.
std::string ProtoEnumToString(ProtoEnumType enum_value)
Definition proto_utils.h:50
STL namespace.
Arguments used when calling ComputeInfeasibleSubsystem().
Definition base_solver.h:79
Arguments used when calling Solve() to solve the problem.
Definition base_solver.h:57
virtual SolverTypeProto solver_type() const =0
Returns the type of solver that the implementation is for.
const NonStreamableSolverInitArguments * non_streamable