OR-Tools  9.3
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 <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"
33#include "ortools/math_opt/callback.pb.h"
38#include "ortools/math_opt/model.pb.h"
39#include "ortools/math_opt/model_update.pb.h"
40#include "ortools/math_opt/parameters.pb.h"
41#include "ortools/math_opt/result.pb.h"
48
49namespace operations_research {
50namespace math_opt {
51
52namespace {
53
54template <typename IdNameContainer>
55void UpdateIdNameMap(const absl::Span<const int64_t> deleted_ids,
56 const IdNameContainer& container, IdNameBiMap& bimap) {
57 for (const int64_t deleted_id : deleted_ids) {
58 bimap.Erase(deleted_id);
59 }
60 for (int i = 0; i < container.ids_size(); ++i) {
61 std::string name;
62 if (!container.names().empty()) {
63 name = container.names(i);
64 }
65 bimap.Insert(container.ids(i), std::move(name));
66 }
67}
68
69ModelSummary MakeSummary(const ModelProto& model) {
70 ModelSummary summary;
71 UpdateIdNameMap<VariablesProto>({}, model.variables(), summary.variables);
72 UpdateIdNameMap<LinearConstraintsProto>({}, model.linear_constraints(),
73 summary.linear_constraints);
74 return summary;
75}
76
77void UpdateSummaryFromModelUpdate(const ModelUpdateProto& model_update,
78 ModelSummary& summary) {
79 UpdateIdNameMap<VariablesProto>(model_update.deleted_variable_ids(),
80 model_update.new_variables(),
81 summary.variables);
82 UpdateIdNameMap<LinearConstraintsProto>(
83 model_update.deleted_linear_constraint_ids(),
84 model_update.new_linear_constraints(), summary.linear_constraints);
85}
86
87// Returns an InternalError with the input status message if the input status is
88// not OK.
89absl::Status ToInternalError(const absl::Status original) {
90 if (original.ok()) {
91 return original;
92 }
93
94 return absl::InternalError(original.message());
95}
96
97// RAII class that is used to return an error when concurrent calls to some
98// functions are made.
99//
100// Usage:
101//
102// // Calling f() and/or g() concurrently will return an error.
103// class A {
104// public:
105// absl::StatusOr<...> f() {
106// ASSIGN_OR_RETURN(const auto guard,
107// ConcurrentCallsGuard::TryAcquire(mutex_));
108// ...
109// }
110//
111// absl::StatusOr<...> g() {
112// ASSIGN_OR_RETURN(const auto guard,
113// ConcurrentCallsGuard::TryAcquire(mutex_));
114// ...
115// }
116
117// private:
118// absl::Mutex mutex_;
119// };
120//
121class ConcurrentCallsGuard {
122 public:
123 // Returns an errors status when concurrent calls are made, or a guard that
124 // must only be kept on stack during the execution of the call.
125 static absl::StatusOr<ConcurrentCallsGuard> TryAcquire(absl::Mutex& mutex)
126 ABSL_NO_THREAD_SAFETY_ANALYSIS {
127 // ABSL_NO_THREAD_SAFETY_ANALYSIS is needed since the analyser is confused
128 // by TryLock. See b/34113867, b/16712284.
129
130 if (!mutex.TryLock()) {
131 return absl::InvalidArgumentError("concurrent calls are forbidden");
132 }
133 return ConcurrentCallsGuard(mutex);
134 }
135
136 ConcurrentCallsGuard(const ConcurrentCallsGuard&) = delete;
137 ConcurrentCallsGuard& operator=(const ConcurrentCallsGuard&) = delete;
138 ConcurrentCallsGuard& operator=(ConcurrentCallsGuard&&) = delete;
139
140 ConcurrentCallsGuard(ConcurrentCallsGuard&& other)
141 : mutex_(std::exchange(other.mutex_, nullptr)) {}
142
143 // Release the guard.
144 ~ConcurrentCallsGuard() {
145 if (mutex_ != nullptr) {
146 mutex_->Unlock();
147 }
148 }
149
150 private:
151 explicit ConcurrentCallsGuard(absl::Mutex& mutex) : mutex_(&mutex) {
152 mutex_->AssertHeld();
153 }
154
155 // Reset to nullptr when the class is moved by the move constructor.
156 absl::Mutex* mutex_;
157};
158
159} // namespace
160
161absl::StatusOr<SolveResultProto> Solver::NonIncrementalSolve(
162 const ModelProto& model, const SolverTypeProto solver_type,
163 const InitArgs& init_args, const SolveArgs& solve_args) {
164 ASSIGN_OR_RETURN(std::unique_ptr<Solver> solver,
165 Solver::New(solver_type, model, init_args));
166 return solver->Solve(solve_args);
167}
168
169Solver::Solver(std::unique_ptr<SolverInterface> underlying_solver,
170 ModelSummary model_summary)
171 : underlying_solver_(std::move(underlying_solver)),
172 model_summary_(std::move(model_summary)) {
173 CHECK(underlying_solver_ != nullptr);
175}
176
178
179absl::StatusOr<std::unique_ptr<Solver>> Solver::New(
180 const SolverTypeProto solver_type, const ModelProto& model,
181 const InitArgs& arguments) {
182 RETURN_IF_ERROR(internal::ValidateInitArgs(arguments, solver_type));
185 auto underlying_solver,
186 AllSolversRegistry::Instance()->Create(solver_type, model, arguments));
187 auto result = absl::WrapUnique(
188 new Solver(std::move(underlying_solver), MakeSummary(model)));
189 return result;
190}
191
192absl::StatusOr<SolveResultProto> Solver::Solve(const SolveArgs& arguments) {
193 ASSIGN_OR_RETURN(const auto guard, ConcurrentCallsGuard::TryAcquire(mutex_));
194
195 // TODO(b/168037341): we should validate the result maths. Since the result
196 // can be filtered, this should be included in the solver_interface
197 // implementations.
198
200 << "invalid parameters";
202 ValidateModelSolveParameters(arguments.model_parameters, model_summary_))
203 << "invalid model_parameters";
204
205 SolverInterface::Callback cb = nullptr;
206 if (arguments.user_cb != nullptr) {
208 arguments.callback_registration, model_summary_));
209 cb = [&](const CallbackDataProto& callback_data)
210 -> absl::StatusOr<CallbackResultProto> {
212 callback_data, arguments.callback_registration, model_summary_));
213 auto callback_result = arguments.user_cb(callback_data);
215 callback_result, callback_data.event(),
216 arguments.callback_registration, model_summary_));
217 return callback_result;
218 };
219 }
220
221 ASSIGN_OR_RETURN(const SolveResultProto result,
222 underlying_solver_->Solve(arguments.parameters,
223 arguments.model_parameters,
224 arguments.message_callback,
225 arguments.callback_registration,
226 cb, arguments.interrupter));
227
228 // We consider errors in `result` to be internal errors, but
229 // `ValidateResult()` will return an InvalidArgumentError. So here we convert
230 // the error.
231 RETURN_IF_ERROR(ToInternalError(
232 ValidateResult(result, arguments.model_parameters, model_summary_)));
233
234 return result;
235}
236
237absl::StatusOr<bool> Solver::Update(const ModelUpdateProto& model_update) {
238 ASSIGN_OR_RETURN(const auto guard, ConcurrentCallsGuard::TryAcquire(mutex_));
239
240 RETURN_IF_ERROR(ValidateModelUpdateAndSummary(model_update, model_summary_));
241 if (!underlying_solver_->CanUpdate(model_update)) {
242 return false;
243 }
244 UpdateSummaryFromModelUpdate(model_update, model_summary_);
245 RETURN_IF_ERROR(underlying_solver_->Update(model_update));
246 return true;
247}
248
249namespace internal {
250
251absl::Status ValidateInitArgs(const Solver::InitArgs& init_args,
252 const SolverTypeProto solver_type) {
253 if (solver_type == SOLVER_TYPE_UNSPECIFIED) {
254 return absl::InvalidArgumentError(
255 "can't use SOLVER_TYPE_UNSPECIFIED as solver_type parameter");
256 }
257
258 if (init_args.non_streamable != nullptr &&
259 init_args.non_streamable->solver_type() != solver_type) {
260 return absl::InvalidArgumentError(
261 absl::StrCat("input non_streamable init arguments are for ",
263 " but solver_type is ", ProtoEnumToString(solver_type)));
264 }
265
266 return absl::OkStatus();
267}
268
269} // namespace internal
270} // namespace math_opt
271} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:495
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< SolveResultProto > NonIncrementalSolve(const ModelProto &model, SolverTypeProto solver_type, const InitArgs &init_args, const SolveArgs &solve_args)
Definition: solver.cc:161
absl::StatusOr< SolveResultProto > Solve(const SolveArgs &arguments)
Definition: solver.cc:192
static absl::StatusOr< std::unique_ptr< Solver > > New(SolverTypeProto solver_type, const ModelProto &model, const InitArgs &arguments)
Definition: solver.cc:179
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update)
Definition: solver.cc:237
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
const std::string name
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:251
absl::Status ValidateResult(const SolveResultProto &result, const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::Status ValidateModelUpdateAndSummary(const ModelUpdateProto &model_update, const ModelSummary &model_summary, const bool check_names)
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::Status ValidateModelSolveParameters(const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::Status ValidateModel(const ModelProto &model, const bool check_names)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
STL namespace.
CallbackRegistrationProto callback_registration
Definition: solver.h:96
ModelSolveParametersProto model_parameters
Definition: solver.h:87
const NonStreamableSolverInitArguments * non_streamable