28#include "absl/memory/memory.h"
29#include "absl/status/status.h"
30#include "absl/status/statusor.h"
31#include "absl/strings/match.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/str_join.h"
34#include "absl/strings/str_split.h"
35#include "absl/time/clock.h"
36#include "absl/time/time.h"
39#include "ortools/math_opt/callback.pb.h"
45#include "ortools/math_opt/model.pb.h"
46#include "ortools/math_opt/model_parameters.pb.h"
47#include "ortools/math_opt/model_update.pb.h"
48#include "ortools/math_opt/parameters.pb.h"
49#include "ortools/math_opt/result.pb.h"
50#include "ortools/math_opt/solution.pb.h"
51#include "ortools/math_opt/sparse_containers.pb.h"
55#include "absl/status/status.h"
64constexpr double kInf = std::numeric_limits<double>::infinity();
68std::vector<std::string> SetSolveParameters(
69 const SolveParametersProto&
parameters,
const bool has_message_callback,
70 MPModelRequest& request) {
71 std::vector<std::string> warnings;
73 request.set_solver_time_limit_seconds(absl::ToDoubleSeconds(
84 sat::SatParameters sat_parameters;
88 sat_parameters.set_catch_sigint_signal(
false);
91 sat_parameters.set_random_seed(
parameters.random_seed());
94 sat_parameters.set_num_search_workers(
parameters.threads());
97 sat_parameters.set_relative_gap_limit(
parameters.relative_gap_limit());
101 sat_parameters.set_absolute_gap_limit(
parameters.absolute_gap_limit());
105 "The cutoff_limit parameter is not supported for CP-SAT.");
109 "The best_bound_limit parameter is not supported for CP-SAT.");
113 "The objective_limit parameter is not supported for CP-SAT.");
117 sat_parameters.set_stop_after_first_solution(
true);
119 warnings.push_back(absl::StrCat(
120 "The CP-SAT solver only supports value 1 for solution_limit, found: ",
125 if (
parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
127 absl::StrCat(
"Setting the LP Algorithm (was set to ",
129 ") is not supported for CP_SAT solver"));
131 if (
parameters.presolve() != EMPHASIS_UNSPECIFIED) {
134 sat_parameters.set_cp_model_presolve(
false);
137 case EMPHASIS_MEDIUM:
139 case EMPHASIS_VERY_HIGH:
140 sat_parameters.set_cp_model_presolve(
true);
145 <<
" unknown, error setting CP-SAT parameters";
148 if (
parameters.scaling() != EMPHASIS_UNSPECIFIED) {
149 warnings.push_back(absl::StrCat(
"Setting the scaling (was set to ",
151 ") is not supported for CP_SAT solver"));
153 if (
parameters.cuts() != EMPHASIS_UNSPECIFIED) {
158 sat_parameters.set_add_cg_cuts(
false);
159 sat_parameters.set_add_mir_cuts(
false);
160 sat_parameters.set_add_zero_half_cuts(
false);
161 sat_parameters.set_add_clique_cuts(
false);
162 sat_parameters.set_max_all_diff_cut_size(0);
163 sat_parameters.set_add_lin_max_cuts(
false);
166 case EMPHASIS_MEDIUM:
168 case EMPHASIS_VERY_HIGH:
172 <<
" unknown, error setting CP-SAT parameters";
175 if (
parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
176 warnings.push_back(absl::StrCat(
"Setting the heuristics (was set to ",
178 ") is not supported for CP_SAT solver"));
180 sat_parameters.MergeFrom(
parameters.cp_sat());
185 if (has_message_callback) {
188 sat_parameters.set_log_search_progress(
true);
192 sat_parameters.set_log_to_stdout(
false);
196 request.set_enable_internal_solver_output(
parameters.enable_output());
199 request.set_solver_specific_parameters(
204absl::StatusOr<std::pair<SolveStatsProto, TerminationProto>>
205GetTerminationAndStats(
const bool is_interrupted,
const bool maximize,
206 const MPSolutionResponse&
response) {
207 SolveStatsProto solve_stats;
208 TerminationProto termination;
211 solve_stats.mutable_problem_status()->set_primal_status(
212 FEASIBILITY_STATUS_UNDETERMINED);
213 solve_stats.set_best_primal_bound(maximize ? -
kInf :
kInf);
214 solve_stats.mutable_problem_status()->set_dual_status(
215 FEASIBILITY_STATUS_UNDETERMINED);
216 solve_stats.set_best_dual_bound(maximize ?
kInf : -
kInf);
223 solve_stats.mutable_problem_status()->set_primal_status(
224 FEASIBILITY_STATUS_FEASIBLE);
225 solve_stats.set_best_primal_bound(
response.objective_value());
226 solve_stats.mutable_problem_status()->set_dual_status(
227 FEASIBILITY_STATUS_FEASIBLE);
228 solve_stats.set_best_dual_bound(
response.best_objective_bound());
233 solve_stats.mutable_problem_status()->set_primal_status(
234 FEASIBILITY_STATUS_INFEASIBLE);
250 if (absl::StrContains(
response.status_str(),
"infeasible or unbounded")) {
252 TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED,
response.status_str());
253 solve_stats.mutable_problem_status()->set_primal_or_dual_infeasible(
262 is_interrupted ? LIMIT_INTERRUPTED : LIMIT_UNDETERMINED,
264 solve_stats.mutable_problem_status()->set_primal_status(
265 FEASIBILITY_STATUS_FEASIBLE);
266 solve_stats.set_best_primal_bound(
response.objective_value());
267 solve_stats.set_best_dual_bound(
response.best_objective_bound());
268 if (std::isfinite(
response.best_objective_bound())) {
269 solve_stats.mutable_problem_status()->set_dual_status(
270 FEASIBILITY_STATUS_FEASIBLE);
275 is_interrupted ? LIMIT_INTERRUPTED : LIMIT_UNDETERMINED,
279 return absl::InternalError(
280 absl::StrCat(
"cp-sat solver returned MODEL_INVALID, details: ",
283 return absl::InternalError(
284 absl::StrCat(
"unexpected solve status: ",
response.status()));
286 return std::make_pair(std::move(solve_stats), std::move(termination));
295 model.variables().ids().end());
297 if (!
model.objective().quadratic_coefficients().row_ids().empty()) {
298 return absl::InvalidArgumentError(
299 "MathOpt does not currently support CP-SAT models with quadratic "
303 return absl::WrapUnique(
309 const ModelSolveParametersProto& model_parameters,
311 const CallbackRegistrationProto& callback_registration,
const Callback cb,
313 const absl::Time
start = absl::Now();
316 callback_registration,
317 {CALLBACK_EVENT_MIP_SOLUTION}));
318 if (callback_registration.add_lazy_constraints()) {
319 return absl::InvalidArgumentError(
320 "CallbackRegistrationProto.add_lazy_constraints=true is not supported "
326 SolveResultProto result;
333 std::vector<std::string> param_warnings =
335 message_cb !=
nullptr, req);
336 if (!param_warnings.empty()) {
337 if (
parameters.strictness().bad_parameter()) {
338 return absl::InvalidArgumentError(absl::StrJoin(param_warnings,
"; "));
340 for (std::string& warning : param_warnings) {
341 result.add_warnings(std::move(warning));
347 if (!model_parameters.solution_hints().empty()) {
349 for (
const auto [
id, val] :
350 MakeView(model_parameters.solution_hints(0).variable_values())) {
351 while (variable_ids_[i] <
id) {
363 std::atomic<bool> interrupt_solve =
false;
368 interrupter, [&]() { local_interrupter.
Interrupt(); });
370 std::function<void(
const std::string&)> logging_callback;
371 if (message_cb !=
nullptr) {
372 logging_callback = [&](
const std::string&
message) {
373 message_cb(absl::StrSplit(
message,
'\n'));
377 const absl::flat_hash_set<CallbackEventProto> events =
379 std::function<void(
const MPSolution&)> solution_callback;
380 absl::Status callback_error = absl::OkStatus();
381 if (events.contains(CALLBACK_EVENT_MIP_SOLUTION)) {
382 solution_callback = [
this, &cb, &callback_error, &local_interrupter,
383 &model_parameters](
const MPSolution& mp_solution) {
384 if (!callback_error.ok()) {
388 CallbackDataProto cb_data;
389 cb_data.set_event(CALLBACK_EVENT_MIP_SOLUTION);
390 *cb_data.mutable_primal_solution_vector() =
391 ExtractSolution(mp_solution.variable_value(), model_parameters);
392 const absl::StatusOr<CallbackResultProto> cb_result = cb(cb_data);
393 if (!cb_result.ok()) {
394 callback_error = cb_result.status();
397 local_interrupter.Interrupt();
398 }
else if (cb_result->terminate()) {
399 local_interrupter.Interrupt();
408 logging_callback, solution_callback));
413 *result.mutable_solve_stats() = std::move(solve_stats);
414 *result.mutable_termination() = std::move(termination);
417 PrimalSolutionProto& solution =
418 *result.add_solutions()->mutable_primal_solution();
419 *solution.mutable_variable_values() =
420 ExtractSolution(
response.variable_value(), model_parameters);
421 solution.set_objective_value(
response.objective_value());
422 solution.set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
426 absl::Now() -
start, result.mutable_solve_stats()->mutable_solve_time()));
438 return absl::InternalError(
"CP-SAT solver does not support incrementalism");
443 : cp_sat_model_(
std::move(cp_sat_model)),
446SparseDoubleVectorProto CpSatSolver::ExtractSolution(
447 const absl::Span<const double> cp_sat_variable_values,
448 const ModelSolveParametersProto& model_parameters)
const {
451 CHECK_EQ(cp_sat_variable_values.size(), variable_ids_.size());
453 SparseVectorFilterPredicate predicate(
454 model_parameters.variable_values_filter());
455 SparseDoubleVectorProto result;
456 for (
int i = 0; i < variable_ids_.size(); ++i) {
457 const int64_t
id = variable_ids_[i];
458 const double value = cp_sat_variable_values[i];
459 if (predicate.AcceptsAndUpdate(
id,
value)) {
461 result.add_values(
value);
#define CHECK_EQ(val1, val2)
::operations_research::PartialVariableAssignment * mutable_solution_hint()
::operations_research::MPModelProto * mutable_model()
void set_solver_type(::operations_research::MPModelRequest_SolverType value)
static constexpr SolverType SAT_INTEGER_PROGRAMMING
void add_var_index(int32_t value)
void add_var_value(double value)
bool CanUpdate(const ModelUpdateProto &model_update) override
absl::Status Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto ¶meters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, SolveInterrupter *interrupter) override
bool IsInterrupted() const
CallbackId AddInterruptionCallback(Callback callback)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
SharedResponseManager * response
absl::Span< const int64_t > variable_ids
TerminationProto FeasibleTermination(const LimitProto limit, const absl::string_view detail)
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_CP_SAT, CpSatSolver::New)
absl::StatusOr<::operations_research::MPModelProto > MathOptModelToMPModelProto(const ::operations_research::math_opt::ModelProto &model)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
TerminationProto NoSolutionFoundTermination(const LimitProto limit, const absl::string_view detail)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
absl::StatusOr< MPSolutionResponse > SatSolveProto(MPModelRequest request, std::atomic< bool > *interrupt_solve, std::function< void(const std::string &)> logging_callback, std::function< void(const MPSolution &)> solution_callback)
std::string EncodeSatParametersAsString(const sat::SatParameters ¶meters)
@ MPSOLVER_UNKNOWN_STATUS
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)