OR-Tools  9.6
math_opt/cpp/solve.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <utility>
20 
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/memory/memory.h"
23 #include "absl/status/status.h"
24 #include "absl/status/statusor.h"
25 #include "absl/synchronization/mutex.h"
27 #include "ortools/math_opt/callback.pb.h"
32 
33 namespace operations_research {
34 namespace math_opt {
35 
36 namespace {
37 
38 Solver::InitArgs ToSolverInitArgs(const SolverInitArguments& arguments) {
39  Solver::InitArgs solver_init_args;
40  solver_init_args.streamable = arguments.streamable.Proto();
41  if (arguments.non_streamable != nullptr) {
42  solver_init_args.non_streamable = arguments.non_streamable.get();
43  }
44 
45  return solver_init_args;
46 }
47 
48 absl::StatusOr<SolveResult> CallSolve(
49  Solver& solver, const ModelStorage* const expected_storage,
50  const SolveArguments& arguments) {
51  RETURN_IF_ERROR(arguments.CheckModelStorageAndCallback(expected_storage));
52 
53  Solver::Callback cb = nullptr;
54  absl::Mutex mutex;
55  absl::Status cb_status; // Guarded by `mutex`.
56  if (arguments.callback != nullptr) {
57  cb = [&](const CallbackDataProto& callback_data_proto) {
58  const CallbackData data(expected_storage, callback_data_proto);
59  // TODO(b/249995436): offer a way for user-callback to return a Status as
60  // well and somehow label it as "user error" (annotation + payload?).
61  const CallbackResult result = arguments.callback(data);
62 
63  if (const absl::Status status =
64  result.CheckModelStorage(expected_storage);
65  !status.ok()) {
66  // Note that we use util::StatusBuilder() here as util::Annotate() is
67  // not available in open-source code.
68  util::StatusBuilder builder(status);
69  builder << "invalid CallbackResult returned by user callback";
70 
71  const absl::MutexLock lock(&mutex);
72  cb_status.Update(builder);
73 
74  // Trigger early termination of the solve.
75  CallbackResultProto result_proto;
76  result_proto.set_terminate(true);
77  return result_proto;
78  }
79 
80  return result.Proto();
81  };
82  }
84  const SolveResultProto solve_result,
85  solver.Solve(
86  {.parameters = arguments.parameters.Proto(),
87  .model_parameters = arguments.model_parameters.Proto(),
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  const absl::MutexLock lock(&mutex);
94  RETURN_IF_ERROR(cb_status);
95 
96  return SolveResult::FromProto(expected_storage, solve_result);
97 }
98 
99 } // namespace
100 
101 absl::StatusOr<SolveResult> Solve(const Model& model,
102  const SolverType solver_type,
103  const SolveArguments& solve_args,
104  const SolverInitArguments& init_args) {
105  ASSIGN_OR_RETURN(const std::unique_ptr<Solver> solver,
106  Solver::New(EnumToProto(solver_type), model.ExportModel(),
107  ToSolverInitArgs(init_args)));
108  return CallSolve(*solver, model.storage(), solve_args);
109 }
110 
111 absl::StatusOr<std::unique_ptr<IncrementalSolver>> IncrementalSolver::New(
112  Model* const model, const SolverType solver_type,
113  SolverInitArguments arguments) {
114  if (model == nullptr) {
115  return absl::InvalidArgumentError("input model can't be null");
116  }
117  std::unique_ptr<UpdateTracker> update_tracker = model->NewUpdateTracker();
118  ASSIGN_OR_RETURN(const ModelProto model_proto, update_tracker->ExportModel());
119  ASSIGN_OR_RETURN(std::unique_ptr<Solver> solver,
120  Solver::New(EnumToProto(solver_type), model_proto,
121  ToSolverInitArgs(arguments)));
122  return absl::WrapUnique<IncrementalSolver>(
123  new IncrementalSolver(solver_type, std::move(arguments), model->storage(),
124  std::move(update_tracker), std::move(solver)));
125 }
126 
127 IncrementalSolver::IncrementalSolver(
128  SolverType solver_type, SolverInitArguments init_args,
129  const ModelStorage* const expected_storage,
130  std::unique_ptr<UpdateTracker> update_tracker,
131  std::unique_ptr<Solver> solver)
132  : solver_type_(solver_type),
133  init_args_(std::move(init_args)),
134  expected_storage_(expected_storage),
135  update_tracker_(std::move(update_tracker)),
136  solver_(std::move(solver)) {}
137 
138 absl::StatusOr<SolveResult> IncrementalSolver::Solve(
139  const SolveArguments& arguments) {
141  return SolveWithoutUpdate(arguments);
142 }
143 
144 absl::StatusOr<UpdateResult> IncrementalSolver::Update() {
145  ASSIGN_OR_RETURN(std::optional<ModelUpdateProto> model_update,
146  update_tracker_->ExportModelUpdate());
147  if (!model_update) {
148  return UpdateResult(true, std::move(model_update));
149  }
150 
151  OR_ASSIGN_OR_RETURN3(const bool did_update, solver_->Update(*model_update),
152  _ << "update failed");
153  RETURN_IF_ERROR(update_tracker_->AdvanceCheckpoint());
154 
155  if (did_update) {
156  return UpdateResult(true, std::move(model_update));
157  }
158 
159  ASSIGN_OR_RETURN(const ModelProto model_proto,
160  update_tracker_->ExportModel());
161  OR_ASSIGN_OR_RETURN3(solver_,
162  Solver::New(EnumToProto(solver_type_), model_proto,
163  ToSolverInitArgs(init_args_)),
164  _ << "solver re-creation failed");
165 
166  return UpdateResult(false, std::move(model_update));
167 }
168 
169 absl::StatusOr<SolveResult> IncrementalSolver::SolveWithoutUpdate(
170  const SolveArguments& arguments) const {
171  return CallSolve(*solver_, expected_storage_, arguments);
172 }
173 
174 } // namespace math_opt
175 } // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
absl::StatusOr< SolveResult > Solve(const SolveArguments &arguments={})
absl::StatusOr< SolveResult > SolveWithoutUpdate(const SolveArguments &arguments={}) const
static absl::StatusOr< std::unique_ptr< IncrementalSolver > > New(Model *model, SolverType solver_type, SolverInitArguments arguments={})
SolverInterface::InitArgs InitArgs
Definition: solver.h:70
std::function< CallbackResultProto(const CallbackDataProto &)> Callback
Definition: solver.h:83
static absl::StatusOr< std::unique_ptr< Solver > > New(SolverTypeProto solver_type, const ModelProto &model, const InitArgs &arguments)
Definition: solver.cc:93
CpModelProto const * model_proto
absl::Status status
Definition: g_gurobi.cc:41
GRBmodel * model
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Enum< E >::Proto EnumToProto(const std::optional< E > value)
Definition: enums.h:268
Collection of objects used to extend the Constraint Solver library.
static absl::StatusOr< SolveResult > FromProto(const ModelStorage *model, const SolveResultProto &solve_result_proto)
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)