Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solver_interface.h
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
14#ifndef ORTOOLS_MATH_OPT_CORE_SOLVER_INTERFACE_H_
15#define ORTOOLS_MATH_OPT_CORE_SOLVER_INTERFACE_H_
16
17#include <functional>
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "absl/base/attributes.h"
23#include "absl/base/nullability.h"
24#include "absl/base/thread_annotations.h"
25#include "absl/container/flat_hash_map.h"
26#include "absl/container/flat_hash_set.h"
27#include "absl/status/statusor.h"
28#include "absl/synchronization/mutex.h"
38
39namespace operations_research {
40namespace math_opt {
41
42// Interface implemented by actual solvers.
43//
44// This interface is not meant to be used directly. The actual API is the one of
45// the Solver class. The Solver class validates the models before calling this
46// interface. It makes sure no concurrent calls happen on Solve(),
47// ComputeInfeasibleSubsystem(), CanUpdate() and Update(). It makes sure no
48// other function is called after Solve(), ComputeInfeasibleSubsystem(),
49// Update() or a callback have failed.
50//
51// Implementations of this interface should not have public constructors but
52// instead have a static `New` function with the signature of Factory function
53// as defined below. They should register this factory using the macro
54// MATH_OPT_REGISTER_SOLVER().
56 public:
57 // Initialization arguments.
58 struct InitArgs {
59 // All parameters that can be stored in a proto and exchange with other
60 // processes.
62
63 // All parameters that can't be exchanged with another process. The caller
64 // keeps ownership of non_streamable.
66 nullptr;
67 };
68
69 // A callback function (if non null) for messages emitted by the solver.
70 //
71 // See Solver::MessageCallback documentation for details.
72 using MessageCallback = std::function<void(const std::vector<std::string>&)>;
73
74 // A callback function (if non null) provided by the Solver class to its
75 // SolverInterface that wraps the user callback function
76 // (BaseSolver::Callback) and validates its inputs (provided by the
77 // SolverInterface implementation) and outputs (provided by the user). A
78 // failing status is returned if those inputs or outputs are invalid.
79 //
80 // To be clear the SolverInterface::Callback is implemented by the Solver
81 // class and looks like:
82 //
83 // absl::Status Callback(const CallbackDataProto& callback_data) {
84 // RETURN_IF_ERROR(ValidateCallbackDataProto(callback_data, ...));
85 // CallbackResultProto result = user_cb(callback_data);
86 // RETURN_IF_ERROR(ValidateCallbackResultProto(result));
87 // return result;
88 // }
89 //
90 // As a consequence SolverInterface implementations can rely on receiving a
91 // valid CallbackResultProto.
92 //
93 // When the SolverInterface::Callback returns an error the SolverInterface
94 // implementation must interrupt the Solve() as soon as possible and return
95 // this error.
96 using Callback = std::function<absl::StatusOr<CallbackResultProto>(
97 const CallbackDataProto&)>;
98
99 // A factory builds a solver based on the input model and parameters.
100 //
101 // Implementation should have a static `New()` function with this signature
102 // and no public constructors.
103 //
104 // The implementation should assume the input ModelProto is valid and is free
105 // to CHECK-fail if this is not the case. It should also assume that the input
106 // init_args.streamable and init_args.non_streamable are also either not set
107 // of set to the arguments of the correct solver.
108 using Factory =
109 std::function<absl::StatusOr<std::unique_ptr<SolverInterface>>(
110 const ModelProto& model, const InitArgs& init_args)>;
111
112 SolverInterface() = default;
115 virtual ~SolverInterface() = default;
116
117 // Solves the current model (included all updates).
118 //
119 // All input arguments are ensured (by solver.cc) to be valid. Furthermore,
120 // since all parameters are references or functions (which could be a lambda
121 // expression), the implementation should not keep a reference or copy of
122 // them, as they may become invalid reference after the invocation if this
123 // function.
124 //
125 // Parameters `message_cb`, `cb` and `interrupter` are optional. They are
126 // nullptr when not set.
127 //
128 // When parameter `message_cb` is not null the value of
129 // parameters.enable_output should be ignored the solver should behave as it
130 // is was false (i.e. not print anything).
131 //
132 // When parameter `message_cb` is not null and the underlying solver does not
133 // supports message callbacks, it should ignore it.
134 //
135 // The parameter `cb` won't be null when
136 // callback_registration.request_registration is not empty (solver.cc will
137 // return an error in that case before calling SolverInterface::Solve()).
138 //
139 // Solvers should return an InvalidArgumentError when called with events on
140 // callback_registration that are not supported by the solver for the type of
141 // model being solved (for example MIP events if the model is an LP, or events
142 // that are not emitted by the solver). Solvers should use
143 // CheckRegisteredCallbackEvents() to implement that.
144 virtual absl::StatusOr<SolveResultProto> Solve(
145 const SolveParametersProto& parameters,
146 const ModelSolveParametersProto& model_parameters,
147 MessageCallback message_cb,
148 const CallbackRegistrationProto& callback_registration, Callback cb,
149 const SolveInterrupter* absl_nullable interrupter) = 0;
150
151 // Updates the model to solve and returns true, or returns false if this
152 // update is not supported.
153 //
154 // The implementation should assume the input ModelUpdate is valid and is free
155 // to assert if this is not the case.
156 virtual absl::StatusOr<bool> Update(const ModelUpdateProto& model_update) = 0;
157
158 // Computes a infeasible subsystem of the model (including all updates).
159 //
160 // All input arguments are ensured (by solver.cc) to be valid. Furthermore,
161 // since all parameters are references or functions (which could be a lambda
162 // expression), the implementation should not keep a reference or copy of
163 // them, as they may become invalid reference after the invocation if this
164 // function.
165 //
166 // The parameters `message_cb` and `interrupter` are optional. They are
167 // nullptr when not set.
168 //
169 // When parameter `message_cb` is not null the value of
170 // parameters.enable_output should be ignored the solver should behave as it
171 // is was false (i.e. not print anything).
172 //
173 // When parameter `message_cb` is not null and the underlying solver does not
174 // supports message callbacks, it should ignore it.
175 virtual absl::StatusOr<ComputeInfeasibleSubsystemResultProto>
177 const SolveParametersProto& parameters, MessageCallback message_cb,
178 const SolveInterrupter* absl_nullable interrupter) = 0;
179};
180
182 public:
185
186 static AllSolversRegistry* absl_nonnull Instance();
187
188 // Maps the given factory to the given solver type. Calling this twice will
189 // result in an error, using static initialization is recommended, e.g. see
190 // MATH_OPT_REGISTER_SOLVER defined below.
191 //
192 // Required: factory must be threadsafe.
193 void Register(SolverTypeProto solver_type, SolverInterface::Factory factory);
194
195 // Invokes the factory associated to the solver type with the provided
196 // arguments.
197 absl::StatusOr<std::unique_ptr<SolverInterface>> Create(
198 SolverTypeProto solver_type, const ModelProto& model,
199 const SolverInterface::InitArgs& init_args) const;
200
201 // Whether a solver type is supported.
202 bool IsRegistered(SolverTypeProto solver_type) const;
203
204 // List all supported solver types.
205 std::vector<SolverTypeProto> RegisteredSolvers() const;
206
207 // Returns a human-readable list of supported solver types.
208 std::string RegisteredSolversToString() const;
209
210 private:
211 // Friendship to be able to build new instances and use
212 // SetTemporaryTestInstance().
214
215 AllSolversRegistry() = default;
216
217 // Makes a copy of the registry, keeping the factories of the `kept` solver
218 // types.
219 //
220 // If a solver type is listed but not registered, it will generate a
221 // LOG(FATAL).
222 //
223 // See WithAlternateAllSolversRegistry.
225 const absl::flat_hash_set<SolverTypeProto>& kept);
226
227 // Sets or resets a temporary replacement returned by Instance().
228 //
229 // It LOG(FATAL) if called twice without a reset (i.e., without calling
230 // SetTemporaryTestInstance(nullptr)). It also LOG(FATAL) if a reset happens
231 // without a previous set.
232 //
233 // This is for testing only. The user is responsible to make sure that no
234 // threads are using the temp_instance after it has been reset.
235 //
236 // See WithAlternateAllSolversRegistry.
237 static void SetTemporaryTestInstance(
238 AllSolversRegistry* absl_nullable temp_instance);
239
240 mutable absl::Mutex mutex_;
241 absl::flat_hash_map<SolverTypeProto, SolverInterface::Factory>
242 registered_solvers_ ABSL_GUARDED_BY(mutex_);
243
244 // Temporary replacement of Instance() for tests; installed and removed by
245 // WithAlternateAllSolversRegistry via
246 // AllSolversRegistry::SetTemporaryTestInstance().
247 static AllSolversRegistry* absl_nullable temporary_test_instance_;
248};
249
250// Use to ensure that a solver is registered exactly one time. Invoke in each cc
251// file implementing a SolverInterface. Example use:
252//
253// MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_GSCIP, GScipSolver::New)
254//
255// Can only be used once per cc file.
256//
257// Arguments:
258// solver_type: A SolverTypeProto proto enum.
259// solver_factory: A SolverInterface::Factory for solver_type.
260#define MATH_OPT_REGISTER_SOLVER(solver_type, solver_factory) \
261 namespace { \
262 const void* const kRegisterSolver ABSL_ATTRIBUTE_UNUSED = [] { \
263 AllSolversRegistry::Instance()->Register(solver_type, solver_factory); \
264 return nullptr; \
265 }(); \
266 } // namespace
267
268} // namespace math_opt
269} // namespace operations_research
270
271#endif // ORTOOLS_MATH_OPT_CORE_SOLVER_INTERFACE_H_
bool IsRegistered(SolverTypeProto solver_type) const
AllSolversRegistry & operator=(const AllSolversRegistry &)=delete
std::vector< SolverTypeProto > RegisteredSolvers() const
void Register(SolverTypeProto solver_type, SolverInterface::Factory factory)
absl::StatusOr< std::unique_ptr< SolverInterface > > Create(SolverTypeProto solver_type, const ModelProto &model, const SolverInterface::InitArgs &init_args) const
static AllSolversRegistry *absl_nonnull Instance()
AllSolversRegistry(const AllSolversRegistry &)=delete
SolverInterface(const SolverInterface &)=delete
std::function< void(const std::vector< std::string > &)> MessageCallback
virtual absl::StatusOr< bool > Update(const ModelUpdateProto &model_update)=0
virtual absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto &parameters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, const SolveInterrupter *absl_nullable interrupter)=0
virtual absl::StatusOr< ComputeInfeasibleSubsystemResultProto > ComputeInfeasibleSubsystem(const SolveParametersProto &parameters, MessageCallback message_cb, const SolveInterrupter *absl_nullable interrupter)=0
std::function< absl::StatusOr< CallbackResultProto >( const CallbackDataProto &)> Callback
SolverInterface & operator=(const SolverInterface &)=delete
std::function< absl::StatusOr< std::unique_ptr< SolverInterface > >( const ModelProto &model, const InitArgs &init_args)> Factory
OR-Tools root namespace.
const NonStreamableSolverInitArguments *absl_nullable non_streamable