163 std::function<
void(
const MPSolution&)> solution_callback) {
170 std::optional<LazyMutableCopy<MPModelProto>> optional_model =
172 if (!optional_model) {
181 cp_response.
set_status(FromMPSolverResponseStatus(response.status()));
182 SOLVER_LOG(logger, CpSolverResponseStats(cp_response));
189 std::unique_ptr<MPModelProto> mp_model =
190 std::move(optional_model).value().copy_or_move_as_unique_ptr();
194 std::move(request).dispose();
198 if (params.mip_treat_high_magnitude_bounds_as_infinity()) {
200 mp_model.get(), logger);
203 return InvalidModelResponse(*logger,
"Extra CP-SAT validation failed.");
209 return InfeasibleResponse(*logger,
210 "An integer variable has an empty domain");
215 RemoveNearZeroTerms(params, mp_model.get(), logger);
220 std::vector<std::unique_ptr<glop::Preprocessor>> for_postsolve;
221 if (!params.enumerate_all_solutions() && params.mip_presolve_level() > 0) {
223 glop_params, mp_model.get(), &for_postsolve, logger);
229 return InfeasibleResponse(
230 *logger,
"Problem proven infeasible during MIP presolve");
232 return InvalidModelResponse(
233 *logger,
"Problem detected invalid during MIP presolve");
237 if (params.log_search_progress()) {
241 SOLVER_LOG(logger,
"MIP presolve: problem infeasible or unbounded.");
242 LOG(INFO) << CpSolverResponseStats(cp_response);
246 response.set_status_str(
247 "Problem proven infeasible or unbounded during MIP presolve");
253 if (time_limit->LimitReached()) {
254 return TimeLimitResponse(*logger);
257 RemoveNearZeroTerms(params, mp_model.get(), logger);
260 SOLVER_LOG(logger,
"Scaling to pure integer problem.");
262 const int num_variables = mp_model->variable_size();
263 std::vector<double> var_scaling(num_variables, 1.0);
264 if (params.mip_automatically_scale_variables()) {
268 return InfeasibleResponse(
269 *logger,
"A detected integer variable has an empty domain");
272 if (params.mip_var_scaling() != 1.0) {
273 const double max_bound = params.mip_scale_large_domain()
274 ? std::numeric_limits<double>::infinity()
275 : params.mip_max_bound();
277 params.mip_var_scaling(), max_bound, mp_model.get());
278 for (
int i = 0; i < var_scaling.size(); ++i) {
279 var_scaling[i] *= other_scaling[i];
284 if (params.only_solve_ip()) {
285 bool all_integer =
true;
287 if (!var.is_integer()) {
293 return InvalidModelResponse(
295 "The model contains non-integer variables but the parameter "
296 "'only_solve_ip' was set. Change this parameter if you "
297 "still want to solve a more constrained version of the original MIP "
298 "where non-integer variables can only take a finite set of values.");
303 if (!ConvertMPModelProtoToCpModelProto(params, *mp_model, &cp_model,
305 return InvalidModelResponse(*logger,
306 "Failed to convert model into CP-SAT model");
308 DCHECK_EQ(cp_model.
variables().size(), var_scaling.size());
309 DCHECK_EQ(cp_model.
variables().size(), mp_model->variable().size());
312 if (mp_model->has_solution_hint()) {
314 const int size = mp_model->solution_hint().var_index().size();
315 for (
int i = 0; i < size; ++i) {
316 const int var = mp_model->solution_hint().var_index(i);
317 if (var >= var_scaling.size())
continue;
322 double value = mp_model->solution_hint().var_value(i) * var_scaling[var];
323 if (std::abs(value) > params.mip_max_bound()) {
324 value = value > 0 ? params.mip_max_bound() : -params.mip_max_bound();
327 cp_model_hint->add_vars(var);
328 cp_model_hint->add_values(
static_cast<int64_t
>(std::round(value)));
333 const int old_num_variables = mp_model->variable().size();
334 const int old_num_constraints = mp_model->constraint().size();
335 const bool is_maximize = mp_model->maximize();
344 (glop::ColIndex(old_num_variables)));
347 static_cast<double>(sat_response.solution(v)) / var_scaling[v];
349 for (
int i = for_postsolve.size(); --i >= 0;) {
350 for_postsolve[i]->RecoverSolution(&glop_solution);
359 if (solution_callback !=
nullptr) {
362 solution_callback(post_solve(sat_response));
372 response.mutable_solve_info()->set_solve_wall_time_seconds(
374 response.mutable_solve_info()->set_solve_user_time_seconds(
376 response.set_status(ToMPSolverResponseStatus(cp_response->
status(),
382 MPSolution post_solved_solution = post_solve(*cp_response);
383 *response.mutable_variable_value() =
392 if (absl::MakeConstSpan(additional_solution.values()) ==
393 absl::MakeConstSpan(cp_response->
solution())) {
400 obj += additional_solution.values(var) * obj_coef;
409 *response.add_additional_solutions() = post_solve(temp);
411 std::sort(response.mutable_additional_solutions()->pointer_begin(),
412 response.mutable_additional_solutions()->pointer_end(),
415 return left->objective_value() > right->objective_value();
417 return left->objective_value() < right->objective_value();
425 std::function<
void(
const std::string&)> logging_callback,
426 std::function<
void(
const MPSolution&)> solution_callback,
427 std::function<
void(
const double)> best_bound_callback) {
439 if (logging_callback !=
nullptr) {
446 if (request->has_solver_specific_parameters()) {
448 if constexpr (!std::is_base_of<Message, sat::SatParameters>::value) {
449 if (!params.MergeFromString(request->solver_specific_parameters())) {
450 return InvalidParametersResponse(
452 "solver_specific_parameters is not a valid binary stream of the "
453 "SatParameters proto");
457 request->solver_specific_parameters(), ¶ms)) {
458 return InvalidParametersResponse(
460 "solver_specific_parameters is not a valid textual representation "
461 "of the SatParameters proto");
469 if (!error.empty()) {
470 return InvalidParametersResponse(
471 *logger, absl::StrCat(
"Invalid CP-SAT parameters: ", error));
481 if (request->has_solver_time_limit_seconds()) {
487 if (interrupt_solve !=
nullptr) {
492 if (best_bound_callback !=
nullptr) {