OR-Tools  9.2
solve.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 <functional>
17#include <memory>
18#include <optional>
19#include <string>
20#include <utility>
21#include <vector>
22
24#include "absl/base/thread_annotations.h"
25#include "absl/container/flat_hash_set.h"
26#include "absl/memory/memory.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/string_view.h"
29#include "absl/synchronization/mutex.h"
31#include "ortools/math_opt/callback.pb.h"
37
38namespace operations_research {
39namespace math_opt {
40
41namespace {
42
43Solver::InitArgs ToSolverInitArgs(const SolverInitArguments& arguments) {
44 Solver::InitArgs solver_init_args;
45 solver_init_args.streamable = arguments.streamable.Proto();
46 if (arguments.non_streamable != nullptr) {
47 solver_init_args.non_streamable = arguments.non_streamable.get();
48 }
49
50 return solver_init_args;
51}
52
53// Asserts (with CHECK) that the input pointer is either nullptr or that it
54// points to the same model storage as storage_.
55void CheckModelStorage(const ModelStorage* const storage,
56 const ModelStorage* const expected_storage) {
57 if (storage != nullptr) {
58 CHECK_EQ(storage, expected_storage)
60 }
61}
62
63absl::StatusOr<SolveResult> CallSolve(
64 Solver& solver, const ModelStorage* const expected_storage,
65 const SolveArguments& arguments) {
66 CheckModelStorage(/*storage=*/arguments.model_parameters.storage(),
67 /*expected_storage=*/expected_storage);
68 CheckModelStorage(/*storage=*/arguments.callback_registration.storage(),
69 /*expected_storage=*/expected_storage);
70
71 if (arguments.callback == nullptr) {
72 CHECK(arguments.callback_registration.events.empty())
73 << "No callback was provided to run, but callback events were "
74 "registered.";
75 }
76
77 Solver::Callback cb = nullptr;
78 if (arguments.callback != nullptr) {
79 cb = [&](const CallbackDataProto& callback_data_proto) {
80 const CallbackData data(expected_storage, callback_data_proto);
81 const CallbackResult result = arguments.callback(data);
82 CheckModelStorage(/*storage=*/result.storage(),
83 /*expected_storage=*/expected_storage);
84 return result.Proto();
85 };
86 }
88 SolveResultProto solve_result,
89 solver.Solve(
90 {.parameters = arguments.parameters.Proto(),
91 .model_parameters = arguments.model_parameters.Proto(),
92 .message_callback = arguments.message_callback,
93 .callback_registration = arguments.callback_registration.Proto(),
94 .user_cb = std::move(cb),
95 .interrupter = arguments.interrupter}));
96 return SolveResult::FromProto(expected_storage, solve_result);
97}
98
99class PrinterMessageCallbackImpl {
100 public:
101 PrinterMessageCallbackImpl(std::ostream& output_stream,
102 const absl::string_view prefix)
103 : output_stream_(output_stream), prefix_(prefix) {}
104
105 void Call(const std::vector<std::string>& messages) {
106 const absl::MutexLock lock(&mutex_);
107 for (const std::string& message : messages) {
108 output_stream_ << prefix_ << message << '\n';
109 }
110 output_stream_.flush();
111 }
112
113 private:
114 absl::Mutex mutex_;
115 std::ostream& output_stream_ ABSL_GUARDED_BY(mutex_);
116 const std::string prefix_;
117};
118
119} // namespace
120
123 : streamable(std::move(streamable)) {}
124
126 const NonStreamableSolverInitArguments& non_streamable)
127 : non_streamable(non_streamable.Clone()) {}
128
131 const NonStreamableSolverInitArguments& non_streamable)
132 : streamable(std::move(streamable)),
133 non_streamable(non_streamable.Clone()) {}
134
136 : streamable(other.streamable),
137 non_streamable(other.non_streamable != nullptr
138 ? other.non_streamable->Clone()
139 : nullptr) {}
140
142 const SolverInitArguments& other) {
143 // Assignment to self is possible.
144 if (&other == this) {
145 return *this;
146 }
147
148 streamable = other.streamable;
150 other.non_streamable != nullptr ? other.non_streamable->Clone() : nullptr;
151
152 return *this;
153}
154
155absl::StatusOr<SolveResult> Solve(const Model& model,
156 const SolverType solver_type,
157 const SolveArguments& solve_args,
158 const SolverInitArguments& init_args) {
159 ASSIGN_OR_RETURN(const std::unique_ptr<Solver> solver,
160 Solver::New(EnumToProto(solver_type), model.ExportModel(),
161 ToSolverInitArgs(init_args)));
162 return CallSolve(*solver, model.storage(), solve_args);
163}
164
165absl::StatusOr<std::unique_ptr<IncrementalSolver>> IncrementalSolver::New(
166 Model& model, const SolverType solver_type, SolverInitArguments arguments) {
167 std::unique_ptr<UpdateTracker> update_tracker = model.NewUpdateTracker();
169 std::unique_ptr<Solver> solver,
170 Solver::New(EnumToProto(solver_type), update_tracker->ExportModel(),
171 ToSolverInitArgs(arguments)));
172 return absl::WrapUnique<IncrementalSolver>(
173 new IncrementalSolver(solver_type, std::move(arguments), model.storage(),
174 std::move(update_tracker), std::move(solver)));
175}
176
177IncrementalSolver::IncrementalSolver(
178 SolverType solver_type, SolverInitArguments init_args,
179 const ModelStorage* const expected_storage,
180 std::unique_ptr<UpdateTracker> update_tracker,
181 std::unique_ptr<Solver> solver)
182 : solver_type_(solver_type),
183 init_args_(std::move(init_args)),
184 expected_storage_(expected_storage),
185 update_tracker_(std::move(update_tracker)),
186 solver_(std::move(solver)) {}
187
188absl::StatusOr<SolveResult> IncrementalSolver::Solve(
189 const SolveArguments& arguments) {
191 return SolveWithoutUpdate(arguments);
192}
193
194absl::StatusOr<IncrementalSolver::UpdateResult> IncrementalSolver::Update() {
195 std::optional<ModelUpdateProto> model_update =
196 update_tracker_->ExportModelUpdate();
197 if (!model_update) {
198 return UpdateResult(true, std::move(model_update));
199 }
200
201 ASSIGN_OR_RETURN(const bool did_update, solver_->Update(*model_update));
202 update_tracker_->Checkpoint();
203
204 if (did_update) {
205 return UpdateResult(true, std::move(model_update));
206 }
207
208 ASSIGN_OR_RETURN(solver_, Solver::New(EnumToProto(solver_type_),
209 update_tracker_->ExportModel(),
210 ToSolverInitArgs(init_args_)));
211
212 return UpdateResult(false, std::move(model_update));
213}
214
215absl::StatusOr<SolveResult> IncrementalSolver::SolveWithoutUpdate(
216 const SolveArguments& arguments) const {
217 return CallSolve(*solver_, expected_storage_, arguments);
218}
219
220MessageCallback PrinterMessageCallback(std::ostream& output_stream,
221 const absl::string_view prefix) {
222 // Here we must use an std::shared_ptr since std::function requires that its
223 // input is copyable. And PrinterMessageCallbackImpl can't be copyable since
224 // it uses an absl::Mutex that is not.
225 const auto impl =
226 std::make_shared<PrinterMessageCallbackImpl>(output_stream, prefix);
227 return
228 [=](const std::vector<std::string>& messages) { impl->Call(messages); };
229}
230
231
232} // namespace math_opt
233} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
absl::StatusOr< SolveResult > Solve(const SolveArguments &arguments={})
Definition: solve.cc:188
absl::StatusOr< UpdateResult > Update()
Definition: solve.cc:194
absl::StatusOr< SolveResult > SolveWithoutUpdate(const SolveArguments &arguments={}) const
Definition: solve.cc:215
static absl::StatusOr< std::unique_ptr< IncrementalSolver > > New(Model &model, SolverType solver_type, SolverInitArguments arguments={})
Definition: solve.cc:165
SolverInterface::InitArgs InitArgs
Definition: solver.h:69
std::function< CallbackResultProto(const CallbackDataProto &)> Callback
Definition: solver.h:82
static absl::StatusOr< std::unique_ptr< Solver > > New(SolverTypeProto solver_type, const ModelProto &model, const InitArgs &arguments)
Definition: solver.cc:179
absl::Status status
Definition: g_gurobi.cc:35
GRBmodel * model
constexpr absl::string_view kObjectsFromOtherModelStorage
Definition: key_types.h:56
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Definition: solve.cc:155
MessageCallback PrinterMessageCallback(std::ostream &output_stream, const absl::string_view prefix)
Definition: solve.cc:220
std::function< void(const std::vector< std::string > &)> MessageCallback
Definition: solve.h:57
Enum< E >::Proto EnumToProto(const std::optional< E > value)
Definition: enums.h:264
Collection of objects used to extend the Constraint Solver library.
STL namespace.
#define ASSIGN_OR_RETURN(lhs, rexpr)
Definition: status_macros.h:48
#define RETURN_IF_ERROR(expr)
Definition: status_macros.h:29
static SolveResult FromProto(const ModelStorage *model, const SolveResultProto &solve_result_proto)
SolverInitArguments & operator=(const SolverInitArguments &other)
Definition: solve.cc:141
std::unique_ptr< const NonStreamableSolverInitArguments > non_streamable
Definition: solve.h:138
StreamableSolverInitArguments streamable
Definition: solve.h:132
std::string message
Definition: trace.cc:398