19#include "absl/status/statusor.h"
20#include "ortools/linear_solver/linear_solver.pb.h"
24#include "ortools/sat/cp_model.pb.h"
27#include "ortools/sat/sat_parameters.pb.h"
35#if defined(PROTOBUF_INTERNAL_IMPL)
36using google::protobuf::Message;
38using google::protobuf::Message;
45constexpr bool kProtoLiteSatParameters =
48MPSolverResponseStatus ToMPSolverResponseStatus(sat::CpSolverStatus
status,
51 case sat::CpSolverStatus::UNKNOWN:
52 return MPSOLVER_NOT_SOLVED;
53 case sat::CpSolverStatus::MODEL_INVALID:
54 return MPSOLVER_MODEL_INVALID;
55 case sat::CpSolverStatus::FEASIBLE:
56 return MPSOLVER_FEASIBLE;
57 case sat::CpSolverStatus::INFEASIBLE:
58 return MPSOLVER_INFEASIBLE;
59 case sat::CpSolverStatus::OPTIMAL:
60 return MPSOLVER_OPTIMAL;
64 return MPSOLVER_ABNORMAL;
67sat::CpSolverStatus FromMPSolverResponseStatus(MPSolverResponseStatus
status) {
69 case MPSolverResponseStatus::MPSOLVER_OPTIMAL:
71 case MPSolverResponseStatus::MPSOLVER_INFEASIBLE:
72 return sat::INFEASIBLE;
73 case MPSolverResponseStatus::MPSOLVER_MODEL_INVALID:
74 return sat::MODEL_INVALID;
81MPSolutionResponse InfeasibleResponse(SolverLogger& logger,
85 if (logger.LoggingIsEnabled()) {
86 sat::CpSolverResponse cp_response;
87 cp_response.set_status(sat::CpSolverStatus::INFEASIBLE);
90 response.set_status(MPSolverResponseStatus::MPSOLVER_INFEASIBLE);
95MPSolutionResponse ModelInvalidResponse(SolverLogger& logger,
99 if (logger.LoggingIsEnabled()) {
100 sat::CpSolverResponse cp_response;
101 cp_response.set_status(sat::CpSolverStatus::MODEL_INVALID);
104 response.set_status(MPSolverResponseStatus::MPSOLVER_MODEL_INVALID);
112 MPModelRequest request, std::atomic<bool>* interrupt_solve,
113 std::function<
void(
const std::string&)> logging_callback,
114 std::function<
void(
const MPSolution&)> solution_callback) {
115 sat::SatParameters params;
116 params.set_log_search_progress(request.enable_internal_solver_output());
118 if (request.has_solver_specific_parameters()) {
120 if (kProtoLiteSatParameters) {
121 if (!params.MergeFromString(request.solver_specific_parameters())) {
122 return absl::InvalidArgumentError(
123 "solver_specific_parameters is not a valid binary stream of the "
124 "SatParameters proto");
128 request.solver_specific_parameters(), ¶ms)) {
129 return absl::InvalidArgumentError(
130 "solver_specific_parameters is not a valid textual representation "
131 "of the SatParameters proto");
135 if (request.has_solver_time_limit_seconds()) {
136 params.set_max_time_in_seconds(request.solver_time_limit_seconds());
146 if (logging_callback !=
nullptr) {
163 sat::CpSolverResponse cp_response;
164 cp_response.set_status(FromMPSolverResponseStatus(
response.status()));
172 MPModelProto*
const mp_model = request.mutable_model();
175 return ModelInvalidResponse(logger,
"Extra CP-SAT validation failed.");
180 return InfeasibleResponse(logger,
181 "An integer variable has an empty domain");
186 const glop::GlopParameters glop_params;
187 std::vector<std::unique_ptr<glop::Preprocessor>> for_postsolve;
188 if (!params.enumerate_all_solutions()) {
196 return InfeasibleResponse(
197 logger,
"Problem proven infeasible during MIP presolve");
199 return ModelInvalidResponse(
200 logger,
"Problem detected invalid during MIP presolve");
204 if (params.log_search_progress()) {
206 sat::CpSolverResponse cp_response;
207 cp_response.set_status(sat::CpSolverStatus::UNKNOWN);
210 response.set_status(MPSolverResponseStatus::MPSOLVER_UNKNOWN_STATUS);
213 "Problem proven infeasible or unbounded during MIP presolve");
223 SOLVER_LOG(&logger,
"Scaling to pure integer problem.");
225 const int num_variables = mp_model->variable_size();
226 std::vector<double> var_scaling(num_variables, 1.0);
227 if (params.mip_automatically_scale_variables()) {
230 return InfeasibleResponse(
231 logger,
"A detected integer variable has an empty domain");
234 if (params.mip_var_scaling() != 1.0) {
236 params.mip_var_scaling(), params.mip_max_bound(), mp_model);
237 for (
int i = 0; i < var_scaling.size(); ++i) {
238 var_scaling[i] *= other_scaling[i];
242 sat::CpModelProto cp_model;
245 if (params.log_search_progress()) {
247 sat::CpSolverResponse cp_response;
248 cp_response.set_status(sat::CpSolverStatus::MODEL_INVALID);
251 response.set_status(MPSOLVER_MODEL_INVALID);
252 response.set_status_str(
"Failed to convert model into CP-SAT model");
255 DCHECK_EQ(cp_model.variables().size(), var_scaling.size());
256 DCHECK_EQ(cp_model.variables().size(), mp_model->variable().size());
259 if (request.model().has_solution_hint()) {
260 auto* cp_model_hint = cp_model.mutable_solution_hint();
261 const int size = request.model().solution_hint().var_index().size();
262 for (
int i = 0; i < size; ++i) {
263 const int var = request.model().solution_hint().var_index(i);
264 if (
var >= var_scaling.size())
continue;
270 request.model().solution_hint().var_value(i) * var_scaling[
var];
271 if (std::abs(
value) > params.mip_max_bound()) {
272 value =
value > 0 ? params.mip_max_bound() : -params.mip_max_bound();
275 cp_model_hint->add_vars(
var);
276 cp_model_hint->add_values(
static_cast<int64_t
>(std::round(
value)));
281 const int old_num_variables = mp_model->variable().size();
282 const int old_num_constraints = mp_model->constraint().size();
289 if (interrupt_solve !=
nullptr) {
294 auto post_solve = [&](
const sat::CpSolverResponse& cp_response) {
295 MPSolution mp_solution;
296 mp_solution.set_objective_value(cp_response.objective_value());
299 (glop::ColIndex(old_num_variables)));
302 static_cast<double>(cp_response.solution(v)) / var_scaling[v];
304 for (
int i = for_postsolve.size(); --i >= 0;) {
305 for_postsolve[i]->RecoverSolution(&glop_solution);
308 mp_solution.add_variable_value(
314 if (solution_callback !=
nullptr) {
316 [&](
const sat::CpSolverResponse& cp_response) {
317 solution_callback(post_solve(cp_response));
322 const sat::CpSolverResponse cp_response =
329 ToMPSolverResponseStatus(cp_response.status(), cp_model.has_objective()));
330 if (
response.status() == MPSOLVER_FEASIBLE ||
331 response.status() == MPSOLVER_OPTIMAL) {
332 response.set_objective_value(cp_response.objective_value());
333 response.set_best_objective_bound(cp_response.best_objective_bound());
334 MPSolution post_solved_solution = post_solve(cp_response);
335 *
response.mutable_variable_value() =
336 std::move(*post_solved_solution.mutable_variable_value());
342 for (
int i = 0; i < cp_response.additional_solutions().size(); ++i) {
343 sat::CpSolverResponse temp;
344 *temp.mutable_solution() = cp_response.additional_solutions(i).values();
345 MPSolution post_solved_solution = post_solve(temp);
346 *(
response.add_additional_solutions()->mutable_variable_value()) =
347 std::move(*post_solved_solution.mutable_variable_value());
354 if (kProtoLiteSatParameters) {
#define DCHECK_EQ(val1, val2)
void SetLogToStdOut(bool enable)
bool LoggingIsEnabled() const
void AddInfoLoggingCallback(std::function< void(const std::string &message)> callback)
void EnableLogging(bool enable)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Class that owns everything related to a particular optimization model.
T Add(std::function< T(Model *)> f)
This makes it possible to have a nicer API on the client side, and it allows both of these forms:
void Register(T *non_owned_class)
Register a non-owned class that will be "singleton" in the model.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
SharedResponseManager * response
@ INFEASIBLE_OR_UNBOUNDED
std::function< void(Model *)> NewFeasibleSolutionObserver(const std::function< void(const CpSolverResponse &response)> &observer)
Creates a solution observer with the model with model.Add(NewFeasibleSolutionObserver([](response){....
std::function< SatParameters(Model *)> NewSatParameters(const std::string ¶ms)
Creates parameters for the solver, which you can add to the model with.
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
Returns a string with some statistics on the solver response.
void RemoveNearZeroTerms(const SatParameters ¶ms, MPModelProto *mp_model, SolverLogger *logger)
bool ConvertMPModelProtoToCpModelProto(const SatParameters ¶ms, const MPModelProto &mp_model, CpModelProto *cp_model, SolverLogger *logger)
bool MPModelProtoValidationBeforeConversion(const SatParameters ¶ms, const MPModelProto &mp_model, SolverLogger *logger)
CpSolverResponse SolveCpModel(const CpModelProto &model_proto, Model *model)
Solves the given CpModelProto.
bool MakeBoundsOfIntegerVariablesInteger(const SatParameters ¶ms, MPModelProto *mp_model, SolverLogger *logger)
std::vector< double > ScaleContinuousVariables(double scaling, double max_bound, MPModelProto *mp_model)
std::vector< double > DetectImpliedIntegers(MPModelProto *mp_model, SolverLogger *logger)
Collection of objects used to extend the Constraint Solver library.
bool ExtractValidMPModelInPlaceOrPopulateResponseStatus(MPModelRequest *request, MPSolutionResponse *response)
Like ExtractValidMPModelOrPopulateResponseStatus(), but works in-place: if the MPModel needed extract...
bool ProtobufTextFormatMergeFromString(const std::string &proto_text_string, ProtoType *proto)
glop::ProblemStatus ApplyMipPresolveSteps(const glop::GlopParameters &glop_params, MPModelProto *model, std::vector< std::unique_ptr< glop::Preprocessor > > *for_postsolve, SolverLogger *logger)
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)
#define SOLVER_LOG(logger,...)