162 std::function<
void(
const std::string&)> logging_callback,
163 std::function<
void(
const MPSolution&)> solution_callback) {
164 sat::SatParameters params;
165 params.set_log_search_progress(request->enable_internal_solver_output());
174 if (logging_callback !=
nullptr) {
181 if (request->has_solver_specific_parameters()) {
183 if constexpr (!std::is_base_of<Message, sat::SatParameters>::value) {
184 if (!params.MergeFromString(request->solver_specific_parameters())) {
185 return InvalidParametersResponse(
187 "solver_specific_parameters is not a valid binary stream of the "
188 "SatParameters proto");
192 request->solver_specific_parameters(), ¶ms)) {
193 return InvalidParametersResponse(
195 "solver_specific_parameters is not a valid textual representation "
196 "of the SatParameters proto");
204 if (!error.empty()) {
205 return InvalidParametersResponse(
206 logger, absl::StrCat(
"Invalid CP-SAT parameters: ", error));
216 if (request->has_solver_time_limit_seconds()) {
217 params.set_max_time_in_seconds(request->solver_time_limit_seconds());
223 MPSolutionResponse response;
224 std::optional<LazyMutableCopy<MPModelProto>> optional_model =
226 if (!optional_model) {
234 sat::CpSolverResponse cp_response;
235 cp_response.set_status(FromMPSolverResponseStatus(response.status()));
236 SOLVER_LOG(&logger, CpSolverResponseStats(cp_response));
243 std::unique_ptr<MPModelProto> mp_model =
244 std::move(optional_model).value().copy_or_move_as_unique_ptr();
248 std::move(request).dispose();
252 if (params.mip_treat_high_magnitude_bounds_as_infinity()) {
254 mp_model.get(), &logger);
258 return InvalidModelResponse(logger,
"Extra CP-SAT validation failed.");
264 return InfeasibleResponse(logger,
265 "An integer variable has an empty domain");
270 RemoveNearZeroTerms(params, mp_model.get(), &logger);
274 const glop::GlopParameters glop_params;
275 std::vector<std::unique_ptr<glop::Preprocessor>> for_postsolve;
276 if (!params.enumerate_all_solutions() && params.mip_presolve_level() > 0) {
278 glop_params, mp_model.get(), &for_postsolve, &logger);
284 return InfeasibleResponse(
285 logger,
"Problem proven infeasible during MIP presolve");
287 return InvalidModelResponse(
288 logger,
"Problem detected invalid during MIP presolve");
292 if (params.log_search_progress()) {
294 sat::CpSolverResponse cp_response;
295 cp_response.set_status(sat::CpSolverStatus::UNKNOWN);
296 SOLVER_LOG(&logger,
"MIP presolve: problem infeasible or unbounded.");
297 LOG(INFO) << CpSolverResponseStats(cp_response);
299 response.set_status(MPSolverResponseStatus::MPSOLVER_UNKNOWN_STATUS);
301 response.set_status_str(
302 "Problem proven infeasible or unbounded during MIP presolve");
309 return TimeLimitResponse(logger);
312 RemoveNearZeroTerms(params, mp_model.get(), &logger);
315 SOLVER_LOG(&logger,
"Scaling to pure integer problem.");
317 const int num_variables = mp_model->variable_size();
318 std::vector<double> var_scaling(num_variables, 1.0);
319 if (params.mip_automatically_scale_variables()) {
323 return InfeasibleResponse(
324 logger,
"A detected integer variable has an empty domain");
327 if (params.mip_var_scaling() != 1.0) {
328 const double max_bound = params.mip_scale_large_domain()
329 ? std::numeric_limits<double>::infinity()
330 : params.mip_max_bound();
332 params.mip_var_scaling(), max_bound, mp_model.get());
333 for (
int i = 0; i < var_scaling.size(); ++i) {
334 var_scaling[i] *= other_scaling[i];
339 if (params.only_solve_ip()) {
340 bool all_integer =
true;
341 for (
const MPVariableProto&
var : mp_model->variable()) {
342 if (!
var.is_integer()) {
348 return InvalidModelResponse(
350 "The model contains non-integer variables but the parameter "
351 "'only_solve_ip' was set. Change this parameter if you "
352 "still want to solve a more constrained version of the original MIP "
353 "where non-integer variables can only take a finite set of values.");
357 sat::CpModelProto cp_model;
358 if (!ConvertMPModelProtoToCpModelProto(params, *mp_model, &cp_model,
360 return InvalidModelResponse(logger,
361 "Failed to convert model into CP-SAT model");
363 DCHECK_EQ(cp_model.variables().size(), var_scaling.size());
364 DCHECK_EQ(cp_model.variables().size(), mp_model->variable().size());
367 if (mp_model->has_solution_hint()) {
368 auto* cp_model_hint = cp_model.mutable_solution_hint();
369 const int size = mp_model->solution_hint().var_index().size();
370 for (
int i = 0; i <
size; ++i) {
371 const int var = mp_model->solution_hint().var_index(i);
372 if (
var >= var_scaling.size())
continue;
377 double value = mp_model->solution_hint().var_value(i) * var_scaling[
var];
378 if (std::abs(
value) > params.mip_max_bound()) {
379 value =
value > 0 ? params.mip_max_bound() : -params.mip_max_bound();
382 cp_model_hint->add_vars(
var);
383 cp_model_hint->add_values(
static_cast<int64_t
>(std::round(
value)));
388 const int old_num_variables = mp_model->variable().size();
389 const int old_num_constraints = mp_model->constraint().size();
390 const bool is_maximize = mp_model->maximize();
393 params.set_max_time_in_seconds(
time_limit->GetTimeLeft());
395 std::numeric_limits<double>::infinity()) {
396 params.set_max_deterministic_time(
time_limit->GetDeterministicTimeLeft());
402 sat_model.
Add(NewSatParameters(params));
403 if (interrupt_solve !=
nullptr) {
408 auto post_solve = [&](
const sat::CpSolverResponse& cp_response) {
409 MPSolution mp_solution;
410 mp_solution.set_objective_value(cp_response.objective_value());
413 (glop::ColIndex(old_num_variables)));
416 static_cast<double>(cp_response.solution(v)) / var_scaling[v];
418 for (
int i = for_postsolve.size(); --i >= 0;) {
419 for_postsolve[i]->RecoverSolution(&glop_solution);
422 mp_solution.add_variable_value(
428 if (solution_callback !=
nullptr) {
430 [&](
const sat::CpSolverResponse& cp_response) {
431 solution_callback(post_solve(cp_response));
436 const sat::CpSolverResponse cp_response =
442 response.mutable_solve_info()->set_solve_wall_time_seconds(
443 cp_response.wall_time());
444 response.mutable_solve_info()->set_solve_user_time_seconds(
445 cp_response.user_time());
447 ToMPSolverResponseStatus(cp_response.status(), cp_model.has_objective()));
448 if (response.status() == MPSOLVER_FEASIBLE ||
449 response.status() == MPSOLVER_OPTIMAL) {
450 response.set_objective_value(cp_response.objective_value());
451 response.set_best_objective_bound(cp_response.best_objective_bound());
452 MPSolution post_solved_solution = post_solve(cp_response);
453 *response.mutable_variable_value() =
454 std::move(*post_solved_solution.mutable_variable_value());
460 for (
const sat::CpSolverSolution& additional_solution :
461 cp_response.additional_solutions()) {
462 if (absl::MakeConstSpan(additional_solution.values()) ==
463 absl::MakeConstSpan(cp_response.solution())) {
466 double obj = cp_model.floating_point_objective().offset();
467 for (
int i = 0; i < cp_model.floating_point_objective().vars_size(); ++i) {
468 const int32_t
var = cp_model.floating_point_objective().vars(i);
469 const double obj_coef = cp_model.floating_point_objective().coeffs(i);
470 obj += additional_solution.values(
var) * obj_coef;
473 if (cp_model.objective().scaling_factor() != 0.0) {
474 obj *= cp_model.objective().scaling_factor();
476 sat::CpSolverResponse temp;
477 *temp.mutable_solution() = additional_solution.values();
478 temp.set_objective_value(obj);
479 *response.add_additional_solutions() = post_solve(temp);
481 std::sort(response.mutable_additional_solutions()->pointer_begin(),
482 response.mutable_additional_solutions()->pointer_end(),
483 [is_maximize](
const MPSolution* left,
const MPSolution* right) {
485 return left->objective_value() > right->objective_value();
487 return left->objective_value() < right->objective_value();