Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solve_impl.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 <optional>
18#include <utility>
19
20#include "absl/base/nullability.h"
21#include "absl/functional/any_invocable.h"
22#include "absl/log/check.h"
23#include "absl/memory/memory.h"
24#include "absl/status/status.h"
25#include "absl/status/statusor.h"
26#include "absl/synchronization/mutex.h"
40
42namespace {
43
44absl::StatusOr<SolveResult> CallSolve(BaseSolver& solver,
45 const ModelStorageCPtr expected_storage,
46 const SolveArguments& arguments,
47 SolveInterrupter& local_canceller) {
48 RETURN_IF_ERROR(arguments.CheckModelStorage(expected_storage));
49
50 BaseSolver::Callback cb = nullptr;
51 absl::Mutex mutex;
52 absl::Status cb_status; // Guarded by `mutex`.
53 if (arguments.callback != nullptr) {
54 cb = [&](const CallbackDataProto& callback_data_proto) {
55 const CallbackData data(expected_storage, callback_data_proto);
56 const CallbackResult result = arguments.callback(data);
57 if (const absl::Status status =
58 result.CheckModelStorage(expected_storage);
59 !status.ok()) {
60 // Note that we use util::StatusBuilder() here as util::Annotate() is
61 // not available in open-source code.
62 util::StatusBuilder builder(status);
63 builder << "invalid CallbackResult returned by user callback";
64
65 { // Limit `lock` scope.
66 const absl::MutexLock lock(mutex);
67 cb_status.Update(builder);
68 }
69
70 // Trigger subprocess cancellation.
71 local_canceller.Interrupt();
72
73 // Trigger early termination of the solve if cancellation is not
74 // supported (i.e. in in-process solve).
75 CallbackResultProto result_proto;
76 result_proto.set_terminate(true);
77 return result_proto;
78 }
79 return result.Proto();
80 };
81 }
82
83 ASSIGN_OR_RETURN(ModelSolveParametersProto model_parameters,
84 arguments.model_parameters.Proto());
85 const absl::StatusOr<SolveResultProto> solve_result_proto = solver.Solve(
86 {.parameters = arguments.parameters.Proto(),
87 .model_parameters = std::move(model_parameters),
88 .message_callback = arguments.message_callback,
89 .callback_registration = arguments.callback_registration.Proto(),
90 .user_cb = std::move(cb),
91 .interrupter = arguments.interrupter});
92
93 // solver.Solve() returns an error on cancellation by local_canceller but in
94 // that case we want to ignore this error and return status generated in the
95 // callback instead.
96 { // Limit `lock` scope.
97 const absl::MutexLock lock(mutex);
98 RETURN_IF_ERROR(cb_status);
99 }
100
101 if (!solve_result_proto.ok()) {
102 return solve_result_proto.status();
103 }
104
105 return SolveResult::FromProto(expected_storage, solve_result_proto.value());
106}
107
108absl::StatusOr<ComputeInfeasibleSubsystemResult> CallComputeInfeasibleSubsystem(
109 BaseSolver& solver, const ModelStorageCPtr expected_storage,
110 const ComputeInfeasibleSubsystemArguments& arguments,
111 SolveInterrupter& local_canceller) {
113 const ComputeInfeasibleSubsystemResultProto compute_result_proto,
114 solver.ComputeInfeasibleSubsystem(
115 {.parameters = arguments.parameters.Proto(),
116 .message_callback = arguments.message_callback,
117 .interrupter = arguments.interrupter}));
118
119 return ComputeInfeasibleSubsystemResult::FromProto(expected_storage,
120 compute_result_proto);
121}
122
123} // namespace
124
125absl::StatusOr<SolveResult> SolveImpl(
126 const BaseSolverFactory solver_factory, const Model& model,
127 const SolverType solver_type, const SolveArguments& solve_args,
128 const SolveInterrupter* absl_nullable const user_canceller,
129 const bool remove_names) {
130 SolveInterrupter local_canceller;
131 const ScopedSolveInterrupterCallback user_canceller_cb(
132 user_canceller, [&]() { local_canceller.Interrupt(); });
134 const std::unique_ptr<BaseSolver> solver,
135 solver_factory(EnumToProto(solver_type), model.ExportModel(remove_names),
136 &local_canceller));
137 return CallSolve(*solver, model.storage(), solve_args, local_canceller);
138}
139
140absl::StatusOr<ComputeInfeasibleSubsystemResult> ComputeInfeasibleSubsystemImpl(
141 const BaseSolverFactory solver_factory, const Model& model,
142 const SolverType solver_type,
143 const ComputeInfeasibleSubsystemArguments& compute_args,
144 const SolveInterrupter* absl_nullable const user_canceller,
145 const bool remove_names) {
146 SolveInterrupter local_canceller;
147 const ScopedSolveInterrupterCallback user_canceller_cb(
148 user_canceller, [&]() { local_canceller.Interrupt(); });
150 const std::unique_ptr<BaseSolver> subprocess_solver,
151 solver_factory(EnumToProto(solver_type), model.ExportModel(remove_names),
152 &local_canceller));
153 return CallComputeInfeasibleSubsystem(*subprocess_solver, model.storage(),
154 compute_args, local_canceller);
155}
156
157absl::StatusOr<std::unique_ptr<IncrementalSolverImpl>>
159 BaseSolverFactory solver_factory, Model* const model,
161 const SolveInterrupter* absl_nullable const user_canceller,
162 const bool remove_names) {
163 if (model == nullptr) {
164 return absl::InvalidArgumentError("input model can't be null");
165 }
166 auto local_canceller = std::make_shared<SolveInterrupter>();
167 auto user_canceller_cb =
168 std::make_unique<const ScopedSolveInterrupterCallback>(
169 user_canceller,
170 [local_canceller]() { local_canceller->Interrupt(); });
171 std::unique_ptr<UpdateTracker> update_tracker = model->NewUpdateTracker();
172 ASSIGN_OR_RETURN(ModelProto model_proto,
173 update_tracker->ExportModel(remove_names));
175 std::unique_ptr<BaseSolver> solver,
176 solver_factory(EnumToProto(solver_type), std::move(model_proto),
177 local_canceller.get()));
178 return absl::WrapUnique<IncrementalSolverImpl>(new IncrementalSolverImpl(
179 std::move(solver_factory), solver_type, remove_names,
180 std::move(local_canceller), std::move(user_canceller_cb),
181 model->storage(), std::move(update_tracker), std::move(solver)));
182}
183
184IncrementalSolverImpl::IncrementalSolverImpl(
185 BaseSolverFactory solver_factory, SolverType solver_type,
186 const bool remove_names, std::shared_ptr<SolveInterrupter> local_canceller,
187 std::unique_ptr<const ScopedSolveInterrupterCallback> user_canceller_cb,
188 const ModelStorageCPtr expected_storage,
189 std::unique_ptr<UpdateTracker> update_tracker,
190 std::unique_ptr<BaseSolver> solver)
191 : solver_factory_(std::move(solver_factory)),
192 solver_type_(solver_type),
193 remove_names_(remove_names),
194 local_canceller_(std::move(local_canceller)),
195 user_canceller_cb_(std::move(user_canceller_cb)),
196 expected_storage_(expected_storage),
197 update_tracker_(std::move(update_tracker)),
198 solver_(std::move(solver)) {}
199
200absl::StatusOr<UpdateResult> IncrementalSolverImpl::Update() {
201 // TODO: b/260337466 - Add permanent errors and concurrency protection.
202 ASSIGN_OR_RETURN(std::optional<ModelUpdateProto> model_update,
203 update_tracker_->ExportModelUpdate(remove_names_));
204 if (!model_update.has_value()) {
205 return UpdateResult(true);
206 }
207
208 OR_ASSIGN_OR_RETURN3(const bool did_update,
209 solver_->Update(*std::move(model_update)),
210 _ << "update failed");
211 RETURN_IF_ERROR(update_tracker_->AdvanceCheckpoint());
212
213 if (did_update) {
214 return UpdateResult(true);
215 }
216
217 ASSIGN_OR_RETURN(ModelProto model_proto,
218 update_tracker_->ExportModel(remove_names_));
220 solver_,
221 solver_factory_(EnumToProto(solver_type_), std::move(model_proto),
222 local_canceller_.get()),
223 _ << "solver re-creation failed");
224
225 return UpdateResult(false);
226}
227
229 const SolveArguments& arguments) const {
230 // TODO: b/260337466 - Add permanent errors and concurrency protection.
231 return CallSolve(*solver_, expected_storage_, arguments, *local_canceller_);
232}
233
234absl::StatusOr<ComputeInfeasibleSubsystemResult>
236 const ComputeInfeasibleSubsystemArguments& arguments) const {
237 // TODO: b/260337466 - Add permanent errors and concurrency protection.
238 return CallComputeInfeasibleSubsystem(*solver_, expected_storage_, arguments,
239 *local_canceller_);
240}
241
242} // namespace operations_research::math_opt::internal
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
std::function< CallbackResultProto(const CallbackDataProto &)> Callback
Definition base_solver.h:56
absl::StatusOr< SolveResult > SolveWithoutUpdate() const
absl::StatusOr< ComputeInfeasibleSubsystemResult > ComputeInfeasibleSubsystemWithoutUpdate() const
ModelProto ExportModel(bool remove_names=false) const
Definition model.cc:300
std::unique_ptr< UpdateTracker > NewUpdateTracker()
Definition model.cc:304
ModelStorageCPtr storage() const
Definition model.h:921
absl::StatusOr< UpdateResult > Update() override
static absl::StatusOr< std::unique_ptr< IncrementalSolverImpl > > New(BaseSolverFactory solver_factory, Model *model, SolverType solver_type, const SolveInterrupter *absl_nullable user_canceller, bool remove_names)
absl::StatusOr< ComputeInfeasibleSubsystemResult > ComputeInfeasibleSubsystemImpl(const BaseSolverFactory solver_factory, const Model &model, const SolverType solver_type, const ComputeInfeasibleSubsystemArguments &compute_args, const SolveInterrupter *absl_nullable const user_canceller, const bool remove_names)
absl::AnyInvocable< absl::StatusOr< std::unique_ptr< BaseSolver > >( SolverTypeProto solver_type, ModelProto model, SolveInterrupter *local_canceller) const > BaseSolverFactory
Definition solve_impl.h:51
absl::StatusOr< SolveResult > SolveImpl(const BaseSolverFactory solver_factory, const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolveInterrupter *absl_nullable const user_canceller, const bool remove_names)
const ModelStorage *absl_nonnull ModelStorageCPtr
Enum< E >::Proto EnumToProto(std::optional< E > value)
Definition enums.h:270
STL namespace.
static absl::StatusOr< ComputeInfeasibleSubsystemResult > FromProto(ModelStorageCPtr model, const ComputeInfeasibleSubsystemResultProto &result_proto)
static absl::StatusOr< SolveResult > FromProto(ModelStorageCPtr model, const SolveResultProto &solve_result_proto)
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)