25#include "absl/strings/str_cat.h"
29#include "ortools/glop/parameters.pb.h"
30#include "ortools/linear_solver/linear_solver.pb.h"
34#include "ortools/sat/boolean_problem.pb.h"
35#include "ortools/sat/cp_model.pb.h"
38#include "ortools/sat/sat_parameters.pb.h"
51using operations_research::MPConstraintProto;
52using operations_research::MPModelProto;
53using operations_research::MPVariableProto;
57void ScaleConstraint(
const std::vector<double>& var_scaling,
58 MPConstraintProto* mp_constraint) {
59 const int num_terms = mp_constraint->coefficient_size();
60 for (
int i = 0; i < num_terms; ++i) {
61 const int var_index = mp_constraint->var_index(i);
62 mp_constraint->set_coefficient(
63 i, mp_constraint->coefficient(i) / var_scaling[var_index]);
67void ApplyVarScaling(
const std::vector<double>& var_scaling,
68 MPModelProto* mp_model) {
69 const int num_variables = mp_model->variable_size();
70 for (
int i = 0; i < num_variables; ++i) {
71 const double scaling = var_scaling[i];
72 const MPVariableProto& mp_var = mp_model->variable(i);
73 const double old_lb = mp_var.lower_bound();
74 const double old_ub = mp_var.upper_bound();
75 const double old_obj = mp_var.objective_coefficient();
76 mp_model->mutable_variable(i)->set_lower_bound(old_lb * scaling);
77 mp_model->mutable_variable(i)->set_upper_bound(old_ub * scaling);
78 mp_model->mutable_variable(i)->set_objective_coefficient(old_obj / scaling);
82 for (MPConstraintProto& mp_constraint : *mp_model->mutable_constraint()) {
83 ScaleConstraint(var_scaling, &mp_constraint);
85 for (MPGeneralConstraintProto& general_constraint :
86 *mp_model->mutable_general_constraint()) {
87 switch (general_constraint.general_constraint_case()) {
88 case MPGeneralConstraintProto::kIndicatorConstraint:
89 ScaleConstraint(var_scaling,
90 general_constraint.mutable_indicator_constraint()
91 ->mutable_constraint());
93 case MPGeneralConstraintProto::kAndConstraint:
94 case MPGeneralConstraintProto::kOrConstraint:
99 LOG(
FATAL) <<
"Scaling unsupported for general constraint of type "
100 << general_constraint.general_constraint_case();
108 MPModelProto* mp_model) {
109 const int num_variables = mp_model->variable_size();
110 std::vector<double> var_scaling(num_variables, 1.0);
111 for (
int i = 0; i < num_variables; ++i) {
112 if (mp_model->variable(i).is_integer())
continue;
113 const double lb = mp_model->variable(i).lower_bound();
114 const double ub = mp_model->variable(i).upper_bound();
115 const double magnitude =
std::max(std::abs(lb), std::abs(ub));
116 if (magnitude == 0 || magnitude > max_bound)
continue;
117 var_scaling[i] =
std::min(scaling, max_bound / magnitude);
119 ApplyVarScaling(var_scaling, mp_model);
127 const double initial_x = x;
133 if (std::abs(q * initial_x - std::round(q * initial_x)) < q * tolerance) {
137 const int new_q = prev_q +
static_cast<int>(std::floor(x)) * q;
151double GetIntegralityMultiplier(
const MPModelProto& mp_model,
152 const std::vector<double>& var_scaling,
int var,
153 int ct_index,
double tolerance) {
154 DCHECK(!mp_model.variable(
var).is_integer());
155 const MPConstraintProto&
ct = mp_model.constraint(ct_index);
156 double multiplier = 1.0;
157 double var_coeff = 0.0;
158 const double max_multiplier = 1e4;
159 for (
int i = 0; i <
ct.var_index().size(); ++i) {
160 if (
var ==
ct.var_index(i)) {
161 var_coeff =
ct.coefficient(i);
165 DCHECK(mp_model.variable(
ct.var_index(i)).is_integer());
169 multiplier *
ct.coefficient(i) / var_scaling[
ct.var_index(i)];
172 if (multiplier == 0 || multiplier > max_multiplier)
return 0.0;
177 for (
const double bound : {
ct.lower_bound(),
ct.upper_bound()}) {
178 if (!std::isfinite(
bound))
continue;
179 if (std::abs(std::round(
bound * multiplier) -
bound * multiplier) >
180 tolerance * multiplier) {
184 return std::abs(multiplier * var_coeff);
190 MPModelProto* mp_model,
192 const int num_variables = mp_model->variable_size();
193 const double tolerance = params.mip_wanted_precision();
194 int64_t num_changes = 0;
195 for (
int i = 0; i < num_variables; ++i) {
196 const MPVariableProto& mp_var = mp_model->variable(i);
197 if (!mp_var.is_integer())
continue;
199 const double lb = mp_var.lower_bound();
200 const double new_lb = std::isfinite(lb) ? std::ceil(lb - tolerance) : lb;
203 mp_model->mutable_variable(i)->set_lower_bound(new_lb);
206 const double ub = mp_var.upper_bound();
207 const double new_ub = std::isfinite(ub) ? std::floor(ub + tolerance) : ub;
210 mp_model->mutable_variable(i)->set_upper_bound(new_ub);
213 if (new_ub < new_lb) {
214 SOLVER_LOG(logger,
"Empty domain for integer variable #", i,
": [", lb,
220 if (num_changes > 0) {
222 " bounds of integer variables to integer values");
229 const int num_variables = mp_model->variable_size();
234 std::vector<double> max_bounds(num_variables);
235 for (
int i = 0; i < num_variables; ++i) {
236 double value = std::abs(mp_model->variable(i).lower_bound());
239 max_bounds[i] =
value;
247 int64_t num_removed = 0;
248 double largest_removed = 0.0;
249 const int num_constraints = mp_model->constraint_size();
250 for (
int c = 0; c < num_constraints; ++c) {
251 MPConstraintProto*
ct = mp_model->mutable_constraint(c);
253 const int size =
ct->var_index().size();
254 if (size == 0)
continue;
255 const double threshold =
256 params.mip_wanted_precision() /
static_cast<double>(size);
257 for (
int i = 0; i < size; ++i) {
258 const int var =
ct->var_index(i);
259 const double coeff =
ct->coefficient(i);
260 if (std::abs(
coeff) * max_bounds[
var] < threshold) {
261 largest_removed =
std::max(largest_removed, std::abs(
coeff));
264 ct->set_var_index(new_size,
var);
265 ct->set_coefficient(new_size,
coeff);
268 num_removed += size - new_size;
269 ct->mutable_var_index()->Truncate(new_size);
270 ct->mutable_coefficient()->Truncate(new_size);
273 if (num_removed > 0) {
275 " near zero terms with largest magnitude of ", largest_removed,
281 const MPModelProto& mp_model,
284 for (
const MPGeneralConstraintProto& general_constraint :
285 mp_model.general_constraint()) {
286 switch (general_constraint.general_constraint_case()) {
287 case MPGeneralConstraintProto::kIndicatorConstraint:
289 case MPGeneralConstraintProto::kAndConstraint:
291 case MPGeneralConstraintProto::kOrConstraint:
294 SOLVER_LOG(logger,
"General constraints of type ",
295 general_constraint.general_constraint_case(),
296 " are not supported.");
302 const double threshold = params.mip_max_valid_magnitude();
303 const int num_variables = mp_model.variable_size();
304 for (
int i = 0; i < num_variables; ++i) {
305 const MPVariableProto&
var = mp_model.variable(i);
306 if ((std::isfinite(
var.lower_bound()) &&
307 std::abs(
var.lower_bound()) > threshold) ||
308 (std::isfinite(
var.upper_bound()) &&
309 std::abs(
var.upper_bound()) > threshold)) {
310 SOLVER_LOG(logger,
"Variable bounds are too large [",
var.lower_bound(),
311 ",",
var.upper_bound(),
"]");
314 if (std::abs(
var.objective_coefficient()) > threshold) {
315 SOLVER_LOG(logger,
"Objective coefficient is too large: ",
316 var.objective_coefficient());
322 const int num_constraints = mp_model.constraint_size();
323 for (
int c = 0; c < num_constraints; ++c) {
324 const MPConstraintProto&
ct = mp_model.constraint(c);
325 if ((std::isfinite(
ct.lower_bound()) &&
326 std::abs(
ct.lower_bound()) > threshold) ||
327 (std::isfinite(
ct.upper_bound()) &&
328 std::abs(
ct.upper_bound()) > threshold)) {
329 SOLVER_LOG(logger,
"Constraint bounds are too large [",
ct.lower_bound(),
330 ",",
ct.upper_bound(),
"]");
333 for (
const double coeff :
ct.coefficient()) {
334 if (std::abs(
coeff) > threshold) {
346 const int num_variables = mp_model->variable_size();
347 std::vector<double> var_scaling(num_variables, 1.0);
349 int initial_num_integers = 0;
350 for (
int i = 0; i < num_variables; ++i) {
351 if (mp_model->variable(i).is_integer()) ++initial_num_integers;
353 VLOG(1) <<
"Initial num integers: " << initial_num_integers;
356 const double tolerance = 1e-6;
357 std::vector<int> constraint_queue;
359 const int num_constraints = mp_model->constraint_size();
360 std::vector<int> constraint_to_num_non_integer(num_constraints, 0);
361 std::vector<std::vector<int>> var_to_constraints(num_variables);
362 for (
int i = 0; i < num_constraints; ++i) {
363 const MPConstraintProto& mp_constraint = mp_model->constraint(i);
365 for (
const int var : mp_constraint.var_index()) {
366 if (!mp_model->variable(
var).is_integer()) {
367 var_to_constraints[
var].push_back(i);
368 constraint_to_num_non_integer[i]++;
371 if (constraint_to_num_non_integer[i] == 1) {
372 constraint_queue.push_back(i);
375 VLOG(1) <<
"Initial constraint queue: " << constraint_queue.size() <<
" / "
378 int num_detected = 0;
379 double max_scaling = 0.0;
380 auto scale_and_mark_as_integer = [&](
int var,
double scaling)
mutable {
382 CHECK(!mp_model->variable(
var).is_integer());
384 if (scaling != 1.0) {
385 VLOG(2) <<
"Scaled " <<
var <<
" by " << scaling;
389 max_scaling =
std::max(max_scaling, scaling);
393 var_scaling[
var] = scaling;
394 mp_model->mutable_variable(
var)->set_is_integer(
true);
397 for (
const int ct_index : var_to_constraints[
var]) {
398 constraint_to_num_non_integer[ct_index]--;
399 if (constraint_to_num_non_integer[ct_index] == 1) {
400 constraint_queue.push_back(ct_index);
405 int num_fail_due_to_rhs = 0;
406 int num_fail_due_to_large_multiplier = 0;
407 int num_processed_constraints = 0;
408 while (!constraint_queue.empty()) {
409 const int top_ct_index = constraint_queue.back();
410 constraint_queue.pop_back();
414 if (constraint_to_num_non_integer[top_ct_index] == 0)
continue;
417 const MPConstraintProto&
ct = mp_model->constraint(top_ct_index);
418 if (
ct.lower_bound() + tolerance <
ct.upper_bound())
continue;
420 ++num_processed_constraints;
432 double multiplier = 1.0;
433 const double max_multiplier = 1e4;
435 for (
int i = 0; i <
ct.var_index().size(); ++i) {
436 if (!mp_model->variable(
ct.var_index(i)).is_integer()) {
438 var =
ct.var_index(i);
439 var_coeff =
ct.coefficient(i);
444 multiplier *
ct.coefficient(i) / var_scaling[
ct.var_index(i)];
447 if (multiplier == 0 || multiplier > max_multiplier) {
453 if (multiplier == 0 || multiplier > max_multiplier) {
454 ++num_fail_due_to_large_multiplier;
459 const double rhs =
ct.lower_bound();
460 if (std::abs(std::round(rhs * multiplier) - rhs * multiplier) >
461 tolerance * multiplier) {
462 ++num_fail_due_to_rhs;
472 double best_scaling = std::abs(var_coeff * multiplier);
473 for (
const int ct_index : var_to_constraints[
var]) {
474 if (ct_index == top_ct_index)
continue;
475 if (constraint_to_num_non_integer[ct_index] != 1)
continue;
478 const MPConstraintProto&
ct = mp_model->constraint(top_ct_index);
479 if (
ct.lower_bound() + tolerance <
ct.upper_bound())
continue;
481 const double multiplier = GetIntegralityMultiplier(
482 *mp_model, var_scaling,
var, ct_index, tolerance);
483 if (multiplier != 0.0 && multiplier < best_scaling) {
484 best_scaling = multiplier;
488 scale_and_mark_as_integer(
var, best_scaling);
496 int num_in_inequalities = 0;
497 int num_to_be_handled = 0;
498 for (
int var = 0;
var < num_variables; ++
var) {
499 if (mp_model->variable(
var).is_integer())
continue;
502 if (var_to_constraints[
var].empty())
continue;
505 for (
const int ct_index : var_to_constraints[
var]) {
506 if (constraint_to_num_non_integer[ct_index] != 1) {
513 std::vector<double> scaled_coeffs;
514 for (
const int ct_index : var_to_constraints[
var]) {
515 const double multiplier = GetIntegralityMultiplier(
516 *mp_model, var_scaling,
var, ct_index, tolerance);
517 if (multiplier == 0.0) {
521 scaled_coeffs.push_back(multiplier);
530 double scaling = scaled_coeffs[0];
531 for (
const double c : scaled_coeffs) {
535 for (
const double c : scaled_coeffs) {
536 const double fraction = c / scaling;
537 if (std::abs(std::round(fraction) - fraction) > tolerance) {
550 mp_model->variable(
var).upper_bound()}) {
551 if (!std::isfinite(
bound))
continue;
552 if (std::abs(std::round(
bound * scaling) -
bound * scaling) >
553 tolerance * scaling) {
565 ++num_in_inequalities;
566 scale_and_mark_as_integer(
var, scaling);
568 VLOG(1) <<
"num_new_integer: " << num_detected
569 <<
" num_processed_constraints: " << num_processed_constraints
570 <<
" num_rhs_fail: " << num_fail_due_to_rhs
571 <<
" num_multiplier_fail: " << num_fail_due_to_large_multiplier;
573 if (num_to_be_handled > 0) {
574 SOLVER_LOG(logger,
"Missed ", num_to_be_handled,
575 " potential implied integer.");
578 const int num_integers = initial_num_integers + num_detected;
579 SOLVER_LOG(logger,
"Num integers: ", num_integers,
"/", num_variables,
580 " (implied: ", num_detected,
581 " in_inequalities: ", num_in_inequalities,
582 " max_scaling: ", max_scaling,
")",
583 (num_integers == num_variables ?
" [IP] " :
" [MIP] "));
585 ApplyVarScaling(var_scaling, mp_model);
592struct ConstraintScaler {
594 ConstraintProto* AddConstraint(
const MPModelProto& mp_model,
595 const MPConstraintProto& mp_constraint,
596 CpModelProto* cp_model);
613double FindFractionalScaling(
const std::vector<double>&
coefficients,
615 double multiplier = 1.0;
618 multiplier * tolerance);
619 if (multiplier == 0.0)
break;
625double FindBestScalingAndComputeErrors(
628 const std::vector<double>&
upper_bounds, int64_t max_absolute_activity,
629 double wanted_absolute_activity_precision,
double* relative_coeff_error,
630 double* scaled_sum_error) {
634 if (scaling_factor == 0.0)
return scaling_factor;
641 double x =
std::min(scaling_factor, 1.0);
642 for (; x <= scaling_factor; x *= 2) {
644 relative_coeff_error, scaled_sum_error);
645 if (*scaled_sum_error < wanted_absolute_activity_precision * x)
break;
657 const double integer_factor = FindFractionalScaling(
coefficients, 1e-8);
658 if (integer_factor != 0 && integer_factor < scaling_factor) {
659 double local_relative_coeff_error;
660 double local_scaled_sum_error;
662 integer_factor, &local_relative_coeff_error,
663 &local_scaled_sum_error);
664 if (local_scaled_sum_error * scaling_factor <=
665 *scaled_sum_error * integer_factor ||
666 local_scaled_sum_error <
667 wanted_absolute_activity_precision * integer_factor) {
668 *relative_coeff_error = local_relative_coeff_error;
669 *scaled_sum_error = local_scaled_sum_error;
670 scaling_factor = integer_factor;
674 return scaling_factor;
679ConstraintProto* ConstraintScaler::AddConstraint(
680 const MPModelProto& mp_model,
const MPConstraintProto& mp_constraint,
681 CpModelProto* cp_model) {
682 if (mp_constraint.lower_bound() == -
kInfinity &&
683 mp_constraint.upper_bound() ==
kInfinity) {
687 auto* constraint = cp_model->add_constraints();
688 constraint->set_name(mp_constraint.name());
689 auto* arg = constraint->mutable_linear();
697 const int num_coeffs = mp_constraint.coefficient_size();
698 for (
int i = 0; i < num_coeffs; ++i) {
699 const auto& var_proto = cp_model->variables(mp_constraint.var_index(i));
700 const int64_t lb = var_proto.domain(0);
701 const int64_t ub = var_proto.domain(var_proto.domain_size() - 1);
702 if (lb == 0 && ub == 0)
continue;
704 const double coeff = mp_constraint.coefficient(i);
705 if (
coeff == 0.0)
continue;
713 double relative_coeff_error;
714 double scaled_sum_error;
715 const double scaling_factor = FindBestScalingAndComputeErrors(
718 if (scaling_factor == 0.0) {
722 LOG(DFATAL) <<
"Scaling factor of zero while scaling constraint: "
723 << mp_constraint.ShortDebugString();
733 const double scaled_value =
coefficients[i] * scaling_factor;
734 const int64_t
value =
static_cast<int64_t
>(std::round(scaled_value)) / gcd;
737 arg->add_coeffs(
value);
757 const Fractional scaled_lb = std::ceil(lb * scaling_factor);
765 arg->add_domain(
CeilRatio(IntegerValue(
static_cast<int64_t
>(scaled_lb)),
770 const Fractional scaled_ub = std::floor(ub * scaling_factor);
778 arg->add_domain(
FloorRatio(IntegerValue(
static_cast<int64_t
>(scaled_ub)),
789 const MPModelProto& mp_model,
790 CpModelProto* cp_model,
792 CHECK(cp_model !=
nullptr);
794 cp_model->set_name(mp_model.name());
808 const int64_t kMaxVariableBound =
809 static_cast<int64_t
>(params.mip_max_bound());
811 int num_truncated_bounds = 0;
812 int num_small_domains = 0;
813 const int64_t kSmallDomainSize = 1000;
814 const double kWantedPrecision = params.mip_wanted_precision();
817 const int num_variables = mp_model.variable_size();
818 for (
int i = 0; i < num_variables; ++i) {
819 const MPVariableProto& mp_var = mp_model.variable(i);
820 IntegerVariableProto* cp_var = cp_model->add_variables();
821 cp_var->set_name(mp_var.name());
829 if (mp_var.lower_bound() >
static_cast<double>(kMaxVariableBound) ||
830 mp_var.upper_bound() <
static_cast<double>(-kMaxVariableBound)) {
831 SOLVER_LOG(logger,
"Error: variable ", mp_var.DebugString(),
832 " is outside [-mip_max_bound..mip_max_bound]");
837 for (
const bool lower : {
true,
false}) {
838 const double bound =
lower ? mp_var.lower_bound() : mp_var.upper_bound();
839 if (std::abs(
bound) >=
static_cast<double>(kMaxVariableBound)) {
840 ++num_truncated_bounds;
841 cp_var->add_domain(
bound < 0 ? -kMaxVariableBound : kMaxVariableBound);
847 static_cast<int64_t
>(
lower ? std::ceil(
bound - kWantedPrecision)
848 : std::floor(
bound + kWantedPrecision)));
851 if (cp_var->domain(0) > cp_var->domain(1)) {
852 LOG(
WARNING) <<
"Variable #" << i <<
" cannot take integer value. "
853 << mp_var.ShortDebugString();
859 if (!mp_var.is_integer() && cp_var->domain(0) != cp_var->domain(1) &&
860 cp_var->domain(1) - cp_var->domain(0) < kSmallDomainSize) {
865 if (num_truncated_bounds > 0) {
866 SOLVER_LOG(logger,
"Warning: ", num_truncated_bounds,
867 " bounds were truncated to ", kMaxVariableBound,
".");
869 if (num_small_domains > 0) {
870 SOLVER_LOG(logger,
"Warning: ", num_small_domains,
871 " continuous variable domain with fewer than ", kSmallDomainSize,
875 ConstraintScaler scaler;
876 const int64_t kScalingTarget = int64_t{1}
877 << params.mip_max_activity_exponent();
878 scaler.wanted_precision = kWantedPrecision;
879 scaler.scaling_target = kScalingTarget;
882 for (
const MPConstraintProto& mp_constraint : mp_model.constraint()) {
883 scaler.AddConstraint(mp_model, mp_constraint, cp_model);
885 for (
const MPGeneralConstraintProto& general_constraint :
886 mp_model.general_constraint()) {
887 switch (general_constraint.general_constraint_case()) {
888 case MPGeneralConstraintProto::kIndicatorConstraint: {
889 const auto& indicator_constraint =
890 general_constraint.indicator_constraint();
891 const MPConstraintProto& mp_constraint =
892 indicator_constraint.constraint();
893 ConstraintProto*
ct =
894 scaler.AddConstraint(mp_model, mp_constraint, cp_model);
895 if (
ct ==
nullptr)
continue;
898 const int var = indicator_constraint.var_index();
899 const int value = indicator_constraint.var_value();
903 case MPGeneralConstraintProto::kAndConstraint: {
904 const auto& and_constraint = general_constraint.and_constraint();
905 const std::string&
name = general_constraint.name();
907 ConstraintProto* ct_pos = cp_model->add_constraints();
908 ct_pos->set_name(
name.empty() ?
"" : absl::StrCat(
name,
"_pos"));
909 ct_pos->add_enforcement_literal(and_constraint.resultant_var_index());
910 *ct_pos->mutable_bool_and()->mutable_literals() =
911 and_constraint.var_index();
913 ConstraintProto* ct_neg = cp_model->add_constraints();
914 ct_neg->set_name(
name.empty() ?
"" : absl::StrCat(
name,
"_neg"));
915 ct_neg->add_enforcement_literal(
916 NegatedRef(and_constraint.resultant_var_index()));
917 for (
const int var_index : and_constraint.var_index()) {
918 ct_neg->mutable_bool_or()->add_literals(
NegatedRef(var_index));
922 case MPGeneralConstraintProto::kOrConstraint: {
923 const auto& or_constraint = general_constraint.or_constraint();
924 const std::string&
name = general_constraint.name();
926 ConstraintProto* ct_pos = cp_model->add_constraints();
927 ct_pos->set_name(
name.empty() ?
"" : absl::StrCat(
name,
"_pos"));
928 ct_pos->add_enforcement_literal(or_constraint.resultant_var_index());
929 *ct_pos->mutable_bool_or()->mutable_literals() =
930 or_constraint.var_index();
932 ConstraintProto* ct_neg = cp_model->add_constraints();
933 ct_neg->set_name(
name.empty() ?
"" : absl::StrCat(
name,
"_neg"));
934 ct_neg->add_enforcement_literal(
935 NegatedRef(or_constraint.resultant_var_index()));
936 for (
const int var_index : or_constraint.var_index()) {
937 ct_neg->mutable_bool_and()->add_literals(
NegatedRef(var_index));
942 LOG(
ERROR) <<
"Can't convert general constraints of type "
943 << general_constraint.general_constraint_case()
944 <<
" to CpModelProto.";
950 SOLVER_LOG(logger,
"Maximum constraint coefficient relative error: ",
951 scaler.max_relative_coeff_error);
952 SOLVER_LOG(logger,
"Maximum constraint worst-case activity error: ",
953 scaler.max_absolute_rhs_error,
954 (scaler.max_absolute_rhs_error > params.mip_check_precision()
955 ?
" [Potentially IMPRECISE]"
958 "Maximum constraint scaling factor: ", scaler.max_scaling_factor);
963 auto* float_objective = cp_model->mutable_floating_point_objective();
964 float_objective->set_maximize(mp_model.maximize());
965 float_objective->set_offset(mp_model.objective_offset());
966 for (
int i = 0; i < num_variables; ++i) {
967 const MPVariableProto& mp_var = mp_model.variable(i);
968 if (mp_var.objective_coefficient() != 0.0) {
969 float_objective->add_vars(i);
970 float_objective->add_coeffs(mp_var.objective_coefficient());
975 if (float_objective->offset() == 0 && float_objective->vars().empty()) {
976 cp_model->clear_floating_point_objective();
982 MPModelProto* output) {
983 CHECK(output !=
nullptr);
987 const int num_vars =
input.variables().size();
988 for (
int v = 0; v < num_vars; ++v) {
989 if (
input.variables(v).domain().size() != 2) {
990 VLOG(1) <<
"Cannot convert " <<
input.variables(v).ShortDebugString();
994 MPVariableProto*
var = output->add_variable();
995 var->set_is_integer(
true);
996 var->set_lower_bound(
input.variables(v).domain(0));
997 var->set_upper_bound(
input.variables(v).domain(1));
1001 if (
input.has_objective()) {
1002 double factor =
input.objective().scaling_factor();
1003 if (factor == 0.0) factor = 1.0;
1004 const int num_terms =
input.objective().vars().size();
1005 for (
int i = 0; i < num_terms; ++i) {
1006 const int var =
input.objective().vars(i);
1007 if (
var < 0)
return false;
1008 output->mutable_variable(
var)->set_objective_coefficient(
1009 factor *
input.objective().coeffs(i));
1011 output->set_objective_offset(factor *
input.objective().offset());
1012 }
else if (
input.has_floating_point_objective()) {
1013 const int num_terms =
input.floating_point_objective().vars().size();
1014 for (
int i = 0; i < num_terms; ++i) {
1015 const int var =
input.floating_point_objective().vars(i);
1016 if (
var < 0)
return false;
1017 output->mutable_variable(
var)->set_objective_coefficient(
1018 input.floating_point_objective().coeffs(i));
1020 output->set_objective_offset(
input.floating_point_objective().offset());
1022 if (output->objective_offset() == 0.0) {
1023 output->clear_objective_offset();
1027 const int num_constraints =
input.constraints().size();
1028 for (
int c = 0; c < num_constraints; ++c) {
1029 const ConstraintProto&
ct =
input.constraints(c);
1030 switch (
ct.constraint_case()) {
1031 case ConstraintProto::kLinear: {
1032 if (
ct.linear().domain().size() != 2) {
1033 VLOG(1) <<
"Cannot convert constraint: " <<
ct.DebugString();
1037 MPConstraintProto* out_ct = output->add_constraint();
1038 out_ct->set_lower_bound(
ct.linear().domain(0));
1039 out_ct->set_upper_bound(
ct.linear().domain(1));
1041 const int num_terms =
ct.linear().vars().size();
1042 for (
int i = 0; i < num_terms; ++i) {
1043 const int var =
ct.linear().vars(i);
1044 if (
var < 0)
return false;
1045 out_ct->add_var_index(
var);
1046 out_ct->add_coefficient(
ct.linear().coeffs(i));
1051 VLOG(1) <<
"Cannot convert constraint: " <<
ct.DebugString();
1060 const std::vector<std::pair<int, double>>& objective,
1061 double objective_offset,
bool maximize,
1064 cp_model->clear_objective();
1071 double min_magnitude = std::numeric_limits<double>::infinity();
1072 double max_magnitude = 0.0;
1073 double l1_norm = 0.0;
1074 for (
const auto& [
var,
coeff] : objective) {
1075 const auto& var_proto = cp_model->variables(
var);
1076 const int64_t lb = var_proto.domain(0);
1077 const int64_t ub = var_proto.domain(var_proto.domain_size() - 1);
1079 if (lb != 0) objective_offset += lb *
coeff;
1089 l1_norm += std::abs(
coeff);
1092 if (
coefficients.empty() && objective_offset == 0.0)
return true;
1095 const double average_magnitude =
1097 SOLVER_LOG(logger,
"[Scaling] Floating point objective has ",
1098 coefficients.size(),
" terms with magnitude in [", min_magnitude,
1099 ", ", max_magnitude,
"] average = ", average_magnitude);
1103 const int64_t max_absolute_activity = int64_t{1}
1104 << params.mip_max_activity_exponent();
1106 std::max(params.mip_wanted_precision(), params.absolute_gap_limit());
1108 double relative_coeff_error;
1109 double scaled_sum_error;
1110 const double scaling_factor = FindBestScalingAndComputeErrors(
1113 if (scaling_factor == 0.0) {
1114 LOG(
ERROR) <<
"Scaling factor of zero while scaling objective! This "
1115 "likely indicate an infinite coefficient in the objective.";
1122 SOLVER_LOG(logger,
"[Scaling] Objective coefficient relative error: ",
1123 relative_coeff_error);
1124 SOLVER_LOG(logger,
"[Scaling] Objective worst-case absolute error: ",
1125 scaled_sum_error / scaling_factor);
1127 "[Scaling] Objective scaling factor: ", scaling_factor / gcd);
1132 auto* objective_proto = cp_model->mutable_objective();
1133 const int64_t mult = maximize ? -1 : 1;
1134 objective_proto->set_offset(objective_offset * scaling_factor / gcd * mult);
1135 objective_proto->set_scaling_factor(1.0 / scaling_factor * gcd * mult);
1137 const int64_t
value =
1138 static_cast<int64_t
>(std::round(
coefficients[i] * scaling_factor)) /
1142 objective_proto->add_coeffs(
value * mult);
1146 if (scaled_sum_error == 0.0) {
1147 objective_proto->set_scaling_was_exact(
true);
1154 LinearBooleanProblem* problem) {
1155 CHECK(problem !=
nullptr);
1157 problem->set_name(mp_model.name());
1158 const int num_variables = mp_model.variable_size();
1159 problem->set_num_variables(num_variables);
1163 for (
int var_id(0); var_id < num_variables; ++var_id) {
1164 const MPVariableProto& mp_var = mp_model.variable(var_id);
1165 problem->add_var_names(mp_var.name());
1170 bool is_binary = mp_var.is_integer();
1174 if (lb <= -1.0) is_binary =
false;
1175 if (ub >= 2.0) is_binary =
false;
1178 if (lb <= 0.0 && ub >= 1.0) {
1180 }
else if (lb <= 1.0 && ub >= 1.0) {
1182 LinearBooleanConstraint* constraint = problem->add_constraints();
1183 constraint->set_lower_bound(1);
1184 constraint->set_upper_bound(1);
1185 constraint->add_literals(var_id + 1);
1186 constraint->add_coefficients(1);
1187 }
else if (lb <= 0.0 && ub >= 0.0) {
1189 LinearBooleanConstraint* constraint = problem->add_constraints();
1190 constraint->set_lower_bound(0);
1191 constraint->set_upper_bound(0);
1192 constraint->add_literals(var_id + 1);
1193 constraint->add_coefficients(1);
1202 LOG(
WARNING) <<
"The variable #" << var_id <<
" with name "
1203 << mp_var.name() <<
" is not binary. "
1204 <<
"lb: " << lb <<
" ub: " << ub;
1211 double max_relative_error = 0.0;
1212 double max_bound_error = 0.0;
1214 double relative_error = 0.0;
1215 double scaling_factor = 0.0;
1219 for (
const MPConstraintProto& mp_constraint : mp_model.constraint()) {
1220 LinearBooleanConstraint* constraint = problem->add_constraints();
1221 constraint->set_name(mp_constraint.name());
1225 const int num_coeffs = mp_constraint.coefficient_size();
1226 for (
int i = 0; i < num_coeffs; ++i) {
1233 max_relative_error =
std::max(relative_error, max_relative_error);
1236 double bound_error = 0.0;
1237 for (
int i = 0; i < num_coeffs; ++i) {
1238 const double scaled_value = mp_constraint.coefficient(i) * scaling_factor;
1239 bound_error += std::abs(round(scaled_value) - scaled_value);
1240 const int64_t
value =
static_cast<int64_t
>(round(scaled_value)) / gcd;
1242 constraint->add_literals(mp_constraint.var_index(i) + 1);
1243 constraint->add_coefficients(
value);
1246 max_bound_error =
std::max(max_bound_error, bound_error);
1253 const Fractional lb = mp_constraint.lower_bound();
1255 if (lb * scaling_factor >
static_cast<double>(kInt64Max)) {
1256 LOG(
WARNING) <<
"A constraint is trivially unsatisfiable.";
1259 if (lb * scaling_factor > -
static_cast<double>(kInt64Max)) {
1261 constraint->set_lower_bound(
1262 static_cast<int64_t
>(round(lb * scaling_factor - bound_error)) /
1266 const Fractional ub = mp_constraint.upper_bound();
1268 if (ub * scaling_factor < -
static_cast<double>(kInt64Max)) {
1269 LOG(
WARNING) <<
"A constraint is trivially unsatisfiable.";
1272 if (ub * scaling_factor <
static_cast<double>(kInt64Max)) {
1274 constraint->set_upper_bound(
1275 static_cast<int64_t
>(round(ub * scaling_factor + bound_error)) /
1282 LOG(
INFO) <<
"Maximum constraint relative error: " << max_relative_error;
1283 LOG(
INFO) <<
"Maximum constraint bound error: " << max_bound_error;
1288 for (
int var_id = 0; var_id < num_variables; ++var_id) {
1289 const MPVariableProto& mp_var = mp_model.variable(var_id);
1290 coefficients.push_back(mp_var.objective_coefficient());
1295 max_relative_error =
std::max(relative_error, max_relative_error);
1298 LOG(
INFO) <<
"objective relative error: " << relative_error;
1299 LOG(
INFO) <<
"objective scaling factor: " << scaling_factor / gcd;
1301 LinearObjective* objective = problem->mutable_objective();
1302 objective->set_offset(mp_model.objective_offset() * scaling_factor / gcd);
1306 objective->set_scaling_factor(1.0 / scaling_factor * gcd);
1307 for (
int var_id = 0; var_id < num_variables; ++var_id) {
1308 const MPVariableProto& mp_var = mp_model.variable(var_id);
1309 const int64_t
value =
1310 static_cast<int64_t
>(
1311 round(mp_var.objective_coefficient() * scaling_factor)) /
1314 objective->add_literals(var_id + 1);
1315 objective->add_coefficients(
value);
1323 const double kRelativeTolerance = 1e-8;
1324 if (max_relative_error > kRelativeTolerance) {
1325 LOG(
WARNING) <<
"The relative error during double -> int64_t conversion "
1335 for (
int i = 0; i < problem.num_variables(); ++i) {
1342 if (problem.var_names_size() != 0) {
1343 CHECK_EQ(problem.var_names_size(), problem.num_variables());
1344 for (
int i = 0; i < problem.num_variables(); ++i) {
1349 for (
const LinearBooleanConstraint& constraint : problem.constraints()) {
1353 for (
int i = 0; i < constraint.literals_size(); ++i) {
1354 const int literal = constraint.literals(i);
1355 const double coeff = constraint.coefficients(i);
1356 const ColIndex variable_index = ColIndex(abs(
literal) - 1);
1366 constraint.has_lower_bound() ? constraint.lower_bound() - sum
1368 constraint.has_upper_bound() ? constraint.upper_bound() - sum
1375 const LinearObjective& objective = problem.objective();
1376 const double scaling_factor = objective.scaling_factor();
1377 for (
int i = 0; i < objective.literals_size(); ++i) {
1378 const int literal = objective.literals(i);
1379 const double coeff =
1380 static_cast<double>(objective.coefficients(i)) * scaling_factor;
1381 const ColIndex variable_index = ColIndex(abs(
literal) - 1);
1397 const CpModelProto& model_proto_with_floating_point_objective,
1398 const CpObjectiveProto& integer_objective,
1399 const int64_t inner_integer_objective_lower_bound) {
1402 const CpModelProto&
proto = model_proto_with_floating_point_objective;
1403 for (
int i = 0; i <
proto.variables().size(); ++i) {
1404 const auto& domain =
proto.variables(i).domain();
1406 static_cast<double>(domain[domain.size() - 1]));
1411 const FloatObjectiveProto& float_obj =
proto.floating_point_objective();
1414 for (
int i = 0; i < float_obj.vars().size(); ++i) {
1415 const glop::ColIndex
col(float_obj.vars(i));
1423 ct,
static_cast<double>(inner_integer_objective_lower_bound),
1424 std::numeric_limits<double>::infinity());
1425 for (
int i = 0; i < integer_objective.vars().size(); ++i) {
1427 static_cast<double>(integer_objective.coeffs(i)));
1434 glop::GlopParameters glop_parameters;
1435 glop_parameters.set_max_deterministic_time(10);
1436 glop_parameters.set_change_status_to_imprecise(
false);
1444 return float_obj.maximize() ? std::numeric_limits<double>::infinity()
1445 : -std::numeric_limits<double>::infinity();
#define DCHECK_NE(val1, val2)
#define CHECK_EQ(val1, val2)
#define CHECK_GT(val1, val2)
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
#define VLOG(verboselevel)
Fractional GetObjectiveValue() const
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
void SetParameters(const GlopParameters ¶meters)
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
void SetConstraintName(RowIndex row, absl::string_view name)
void SetObjectiveOffset(Fractional objective_offset)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
void SetVariableName(ColIndex col, absl::string_view name)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
ColIndex CreateNewVariable()
void SetVariableType(ColIndex col, VariableType type)
const DenseRow & objective_coefficients() const
void SetObjectiveCoefficient(ColIndex col, Fractional value)
RowIndex CreateNewConstraint()
void SetMaximizationProblem(bool maximize)
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool ScaleAndSetObjective(const SatParameters ¶ms, const std::vector< std::pair< int, double > > &objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
bool ConvertCpModelProtoToMPModelProto(const CpModelProto &input, MPModelProto *output)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
void ConvertBooleanProblemToLinearProgram(const LinearBooleanProblem &problem, glop::LinearProgram *lp)
bool ConvertBinaryMPModelProtoToBooleanProblem(const MPModelProto &mp_model, LinearBooleanProblem *problem)
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)
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
bool MakeBoundsOfIntegerVariablesInteger(const SatParameters ¶ms, MPModelProto *mp_model, SolverLogger *logger)
double ComputeTrueObjectiveLowerBound(const CpModelProto &model_proto_with_floating_point_objective, const CpObjectiveProto &integer_objective, const int64_t inner_integer_objective_lower_bound)
std::vector< double > ScaleContinuousVariables(double scaling, double max_bound, MPModelProto *mp_model)
std::vector< double > DetectImpliedIntegers(MPModelProto *mp_model, SolverLogger *logger)
int FindRationalFactor(double x, int limit, double tolerance)
Collection of objects used to extend the Constraint Solver library.
void ComputeScalingErrors(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, double scaling_factor, double *max_relative_coeff_error, double *max_scaled_sum_error)
int64_t ComputeGcdOfRoundedDoubles(const std::vector< double > &x, double scaling_factor)
double GetBestScalingOfDoublesToInt64(const std::vector< double > &input, const std::vector< double > &lb, const std::vector< double > &ub, int64_t max_absolute_sum)
static int input(yyscan_t yyscanner)
double max_scaling_factor
double max_relative_coeff_error
std::vector< double > lower_bounds
std::vector< int > var_indices
std::vector< double > upper_bounds
std::vector< double > coefficients
double max_absolute_rhs_error
#define SOLVER_LOG(logger,...)