24#include "absl/log/check.h"
25#include "absl/status/status.h"
26#include "absl/strings/str_cat.h"
27#include "absl/strings/string_view.h"
28#include "absl/types/span.h"
56using operations_research::MPConstraintProto;
57using operations_research::MPModelProto;
58using operations_research::MPVariableProto;
62void ScaleConstraint(absl::Span<const double> var_scaling,
65 for (
int i = 0;
i < num_terms; ++
i) {
66 const int var_index = mp_constraint->
var_index(
i);
72void ApplyVarScaling(absl::Span<const double> var_scaling,
75 for (
int i = 0;
i < num_variables; ++
i) {
76 const double scaling = var_scaling[
i];
88 ScaleConstraint(var_scaling, &mp_constraint);
90 for (MPGeneralConstraintProto& general_constraint :
92 switch (general_constraint.general_constraint_case()) {
94 ScaleConstraint(var_scaling,
95 general_constraint.mutable_indicator_constraint()
96 ->mutable_constraint());
104 LOG(FATAL) <<
"Scaling unsupported for general constraint of type "
105 << general_constraint.general_constraint_case();
115 std::vector<double> var_scaling(num_variables, 1.0);
116 for (
int i = 0;
i < num_variables; ++
i) {
118 if (max_bound == std::numeric_limits<double>::infinity()) {
119 var_scaling[
i] = scaling;
124 const double magnitude = std::max(std::abs(lb), std::abs(ub));
125 if (magnitude == 0 || magnitude > max_bound)
continue;
126 var_scaling[
i] = std::min(scaling, max_bound / magnitude);
128 ApplyVarScaling(var_scaling, mp_model);
136 const double initial_x = x;
139 int64_t current_q = 1;
141 while (current_q < limit) {
142 const double q =
static_cast<double>(current_q);
143 const double qx = q * initial_x;
144 const double qtolerance = q * tolerance;
145 if (std::abs(qx - std::round(qx)) < qtolerance) {
149 const double floored_x = std::floor(x);
150 if (floored_x >=
static_cast<double>(std::numeric_limits<int64_t>::max())) {
153 const int64_t new_q =
154 CapAdd(prev_q,
CapProd(
static_cast<int64_t
>(floored_x), current_q));
168double GetIntegralityMultiplier(
const MPModelProto& mp_model,
169 absl::Span<const double> var_scaling,
int var,
170 int ct_index,
double tolerance) {
173 double multiplier = 1.0;
174 double var_coeff = 0.0;
175 const double max_multiplier = 1e4;
189 if (multiplier == 0 || multiplier > max_multiplier)
return 0.0;
191 DCHECK_NE(var_coeff, 0.0);
195 if (!std::isfinite(bound))
continue;
197 const double scaled_bound = multiplier *
bound;
200 if (multiplier == 0 || multiplier > max_multiplier)
return 0.0;
202 return std::abs(multiplier * var_coeff);
212 int64_t num_changes = 0;
213 for (
int i = 0;
i < num_variables; ++
i) {
218 const double new_lb = std::isfinite(lb) ? std::ceil(lb - tolerance) : lb;
225 const double new_ub = std::isfinite(ub) ? std::floor(ub + tolerance) : ub;
231 if (new_ub < new_lb) {
232 SOLVER_LOG(logger,
"Empty domain for integer variable #",
i,
": [", lb,
243 int64_t num_variable_bounds_pushed_to_infinity = 0;
244 const double infinity = std::numeric_limits<double>::infinity();
245 for (
int i = 0;
i < num_variables; ++
i) {
248 if (std::isfinite(lb) && lb < -max_magnitude) {
249 ++num_variable_bounds_pushed_to_infinity;
254 if (std::isfinite(ub) && ub > max_magnitude) {
255 ++num_variable_bounds_pushed_to_infinity;
260 if (num_variable_bounds_pushed_to_infinity > 0) {
261 SOLVER_LOG(logger,
"Pushed ", num_variable_bounds_pushed_to_infinity,
262 " variable bounds to +/-infinity");
266 int64_t num_constraint_bounds_pushed_to_infinity = 0;
268 for (
int i = 0;
i < num_constraints; ++
i) {
271 if (std::isfinite(lb) && lb < -max_magnitude) {
272 ++num_constraint_bounds_pushed_to_infinity;
277 if (std::isfinite(ub) && ub > max_magnitude) {
278 ++num_constraint_bounds_pushed_to_infinity;
293 if (std::isfinite(lb) && lb < -max_magnitude) {
294 ++num_constraint_bounds_pushed_to_infinity;
299 if (std::isfinite(ub) && ub > max_magnitude) {
300 ++num_constraint_bounds_pushed_to_infinity;
305 if (num_constraint_bounds_pushed_to_infinity > 0) {
306 SOLVER_LOG(logger,
"Pushed ", num_constraint_bounds_pushed_to_infinity,
307 " constraint bounds to +/-infinity");
315 double max_dropped = 0.0;
318 for (
int i = 0;
i < num_variables; ++
i) {
322 max_dropped = std::max(max_dropped, std::abs(var->
lower_bound()));
327 max_dropped = std::max(max_dropped, std::abs(var->
upper_bound()));
332 for (
int i = 0;
i < num_constraints; ++
i) {
336 max_dropped = std::max(max_dropped, std::abs(ct->
lower_bound()));
341 max_dropped = std::max(max_dropped, std::abs(ct->
upper_bound()));
345 if (num_dropped > 0) {
346 SOLVER_LOG(logger,
"Set to zero ", num_dropped,
347 " variable or constraint bounds with largest magnitude ",
354 std::vector<double> max_bounds(num_variables);
355 for (
int i = 0;
i < num_variables; ++
i) {
359 max_bounds[
i] = value;
364 double largest_removed = 0.0;
371 int64_t num_removed = 0;
372 for (
int c = 0; c < num_constraints; ++c) {
376 if (size == 0)
continue;
377 const double threshold =
379 for (
int i = 0;
i < size; ++
i) {
382 if (std::abs(coeff) * max_bounds[var] < threshold) {
383 if (max_bounds[var] != 0) {
384 largest_removed = std::max(largest_removed, std::abs(coeff));
392 num_removed += size - new_size;
398 if (num_variables > 0) {
399 const double threshold =
401 for (
int var = 0; var < num_variables; ++var) {
403 if (coeff == 0.0)
continue;
404 if (std::abs(coeff) * max_bounds[var] < threshold) {
406 if (max_bounds[var] != 0) {
407 largest_removed = std::max(largest_removed, std::abs(coeff));
414 if (num_removed > 0) {
416 " near zero terms with largest magnitude of ", largest_removed,
427 switch (general_constraint.general_constraint_case()) {
435 SOLVER_LOG(logger,
"General constraints of type ",
436 general_constraint.general_constraint_case(),
437 " are not supported.");
445 for (
int i = 0;
i < num_variables; ++
i) {
456 SOLVER_LOG(logger,
"Objective coefficient is too large: ",
464 for (
int c = 0; c < num_constraints; ++c) {
475 if (std::abs(coeff) > threshold) {
476 SOLVER_LOG(logger,
"Constraint coefficient is too large: ", coeff);
488 std::vector<double> var_scaling(num_variables, 1.0);
490 int initial_num_integers = 0;
491 for (
int i = 0;
i < num_variables; ++
i) {
494 VLOG(1) <<
"Initial num integers: " << initial_num_integers;
497 const double tolerance = 1e-6;
498 std::vector<int> constraint_queue;
501 std::vector<int> constraint_to_num_non_integer(num_constraints, 0);
502 std::vector<std::vector<int>> var_to_constraints(num_variables);
503 for (
int i = 0;
i < num_constraints; ++
i) {
506 for (
const int var : mp_constraint.
var_index()) {
508 var_to_constraints[var].push_back(
i);
509 constraint_to_num_non_integer[
i]++;
512 if (constraint_to_num_non_integer[
i] == 1) {
513 constraint_queue.push_back(
i);
516 VLOG(1) <<
"Initial constraint queue: " << constraint_queue.size() <<
" / "
519 int num_detected = 0;
520 double max_scaling = 0.0;
521 auto scale_and_mark_as_integer = [&](
int var,
double scaling)
mutable {
524 CHECK_EQ(var_scaling[var], 1.0);
525 if (scaling != 1.0) {
526 VLOG(2) <<
"Scaled " << var <<
" by " << scaling;
530 max_scaling = std::max(max_scaling, scaling);
534 var_scaling[var] = scaling;
538 for (
const int ct_index : var_to_constraints[var]) {
539 constraint_to_num_non_integer[ct_index]--;
540 if (constraint_to_num_non_integer[ct_index] == 1) {
541 constraint_queue.push_back(ct_index);
546 int num_fail_due_to_rhs = 0;
547 int num_fail_due_to_large_multiplier = 0;
548 int num_processed_constraints = 0;
549 while (!constraint_queue.empty()) {
550 const int top_ct_index = constraint_queue.back();
551 constraint_queue.pop_back();
555 if (constraint_to_num_non_integer[top_ct_index] == 0)
continue;
561 ++num_processed_constraints;
573 double multiplier = 1.0;
574 const double max_multiplier = 1e4;
588 if (multiplier == 0 || multiplier > max_multiplier) {
594 if (multiplier == 0 || multiplier > max_multiplier) {
595 ++num_fail_due_to_large_multiplier;
601 if (std::abs(std::round(rhs * multiplier) - rhs * multiplier) >
602 tolerance * multiplier) {
603 ++num_fail_due_to_rhs;
613 double best_scaling = std::abs(var_coeff * multiplier);
614 for (
const int ct_index : var_to_constraints[var]) {
615 if (ct_index == top_ct_index)
continue;
616 if (constraint_to_num_non_integer[ct_index] != 1)
continue;
622 const double multiplier = GetIntegralityMultiplier(
623 *mp_model, var_scaling, var, ct_index, tolerance);
624 if (multiplier != 0.0 && multiplier < best_scaling) {
625 best_scaling = multiplier;
629 scale_and_mark_as_integer(var, best_scaling);
637 int num_in_inequalities = 0;
638 int num_to_be_handled = 0;
639 for (
int var = 0; var < num_variables; ++var) {
643 if (var_to_constraints[var].empty())
continue;
646 for (
const int ct_index : var_to_constraints[var]) {
647 if (constraint_to_num_non_integer[ct_index] != 1) {
654 std::vector<double> scaled_coeffs;
655 for (
const int ct_index : var_to_constraints[var]) {
656 const double multiplier = GetIntegralityMultiplier(
657 *mp_model, var_scaling, var, ct_index, tolerance);
658 if (multiplier == 0.0) {
662 scaled_coeffs.push_back(multiplier);
671 double scaling = scaled_coeffs[0];
672 for (
const double c : scaled_coeffs) {
673 scaling = std::min(scaling, c);
675 CHECK_GT(scaling, 0.0);
676 for (
const double c : scaled_coeffs) {
677 const double fraction = c / scaling;
678 if (std::abs(std::round(fraction) - fraction) > tolerance) {
692 if (!std::isfinite(bound))
continue;
693 if (std::abs(std::round(bound * scaling) - bound * scaling) >
694 tolerance * scaling) {
706 ++num_in_inequalities;
707 scale_and_mark_as_integer(var, scaling);
709 VLOG(1) <<
"num_new_integer: " << num_detected
710 <<
" num_processed_constraints: " << num_processed_constraints
711 <<
" num_rhs_fail: " << num_fail_due_to_rhs
712 <<
" num_multiplier_fail: " << num_fail_due_to_large_multiplier;
714 if (num_to_be_handled > 0) {
715 SOLVER_LOG(logger,
"Missed ", num_to_be_handled,
716 " potential implied integer.");
719 const int num_integers = initial_num_integers + num_detected;
720 SOLVER_LOG(logger,
"Num integers: ", num_integers,
"/", num_variables,
721 " (implied: ", num_detected,
722 " in_inequalities: ", num_in_inequalities,
723 " max_scaling: ", max_scaling,
")",
724 (num_integers == num_variables ?
" [IP] " :
" [MIP] "));
726 ApplyVarScaling(var_scaling, mp_model);
732 absl::Span<const IntegerVariableProto* const> var_domains =
735 absl::MakeConstSpan(mp_constraint.
var_index()),
740 return absl::InvalidArgumentError(
741 absl::StrCat(
"Scaling factor of zero while scaling constraint: ",
744 return absl::OkStatus();
748 absl::Span<const int> vars, absl::Span<const double> coeffs,
749 double lower_bound,
double upper_bound, absl::string_view name,
750 absl::Span<const IntegerVariableProto* const> var_domains,
761 const int num_coeffs = vars.size();
762 for (
int i = 0;
i < num_coeffs; ++
i) {
763 const auto& var_proto = var_domains[vars[
i]];
764 const int64_t lb = var_proto->domain(0);
765 const int64_t ub = var_proto->domain(var_proto->domain_size() - 1);
766 if (lb == 0 && ub == 0)
continue;
768 const double coeff = coeffs[
i];
769 if (coeff == 0.0)
continue;
777 double relative_coeff_error;
778 double scaled_sum_error;
782 if (scaling_factor == 0.0) {
783 return absl::InvalidArgumentError(
784 "Scaling factor of zero while scaling constraint");
794 const double scaled_value =
coefficients[
i] * scaling_factor;
795 const int64_t value =
static_cast<int64_t
>(std::round(scaled_value)) / gcd;
798 arg->add_coeffs(value);
818 const Fractional scaled_lb = std::ceil(lb * scaling_factor);
819 if (lb ==
kInfinity || scaled_lb >= std::numeric_limits<int64_t>::max()) {
821 arg->add_domain(std::numeric_limits<int64_t>::max());
823 scaled_lb <= std::numeric_limits<int64_t>::min()) {
824 arg->add_domain(std::numeric_limits<int64_t>::min());
826 arg->add_domain(
CeilRatio(IntegerValue(
static_cast<int64_t
>(scaled_lb)),
831 const Fractional scaled_ub = std::floor(ub * scaling_factor);
832 if (ub == -
kInfinity || scaled_ub <= std::numeric_limits<int64_t>::min()) {
834 arg->add_domain(std::numeric_limits<int64_t>::min());
836 scaled_ub >= std::numeric_limits<int64_t>::max()) {
837 arg->add_domain(std::numeric_limits<int64_t>::max());
839 arg->add_domain(
FloorRatio(IntegerValue(
static_cast<int64_t
>(scaled_ub)),
844 return absl::OkStatus();
855double FindFractionalScaling(absl::Span<const double> coefficients,
857 double multiplier = 1.0;
858 for (
const double coeff : coefficients) {
860 multiplier * tolerance);
861 if (multiplier == 0.0)
break;
869 absl::Span<const double> coefficients,
870 absl::Span<const double> lower_bounds,
871 absl::Span<const double> upper_bounds, int64_t max_absolute_activity,
872 double wanted_absolute_activity_precision,
double* relative_coeff_error,
873 double* scaled_sum_error) {
876 coefficients, lower_bounds, upper_bounds, max_absolute_activity);
877 if (scaling_factor == 0.0)
return scaling_factor;
887 double x = std::min(scaling_factor, 1.0);
888 for (; x <= scaling_factor; x *= 2) {
890 relative_coeff_error, scaled_sum_error);
891 if (*scaled_sum_error < wanted_absolute_activity_precision * x)
break;
894 if (x == scaling_factor)
break;
897 DCHECK(std::isfinite(scaling_factor));
907 const double integer_factor = FindFractionalScaling(coefficients, 1e-8);
908 DCHECK(std::isfinite(integer_factor));
909 if (integer_factor != 0 && integer_factor < scaling_factor) {
910 double local_relative_coeff_error;
911 double local_scaled_sum_error;
913 integer_factor, &local_relative_coeff_error,
914 &local_scaled_sum_error);
915 if (local_scaled_sum_error * scaling_factor <=
916 *scaled_sum_error * integer_factor ||
917 local_scaled_sum_error <
918 wanted_absolute_activity_precision * integer_factor) {
919 *relative_coeff_error = local_relative_coeff_error;
920 *scaled_sum_error = local_scaled_sum_error;
921 scaling_factor = integer_factor;
925 DCHECK(std::isfinite(scaling_factor));
926 return scaling_factor;
933 CHECK(cp_model !=
nullptr);
949 const int64_t kMaxVariableBound =
952 int num_truncated_bounds = 0;
953 int num_small_domains = 0;
954 const int64_t kSmallDomainSize = 1000;
960 for (
int i = 0;
i < num_variables; ++
i) {
971 if (mp_var.
lower_bound() >
static_cast<double>(kMaxVariableBound) ||
972 mp_var.
upper_bound() <
static_cast<double>(-kMaxVariableBound)) {
973 SOLVER_LOG(logger,
"Error: variable ", mp_var,
974 " is outside [-mip_max_bound..mip_max_bound]");
979 for (
const bool lower : {
true,
false}) {
981 if (std::abs(bound) + kWantedPrecision >=
982 static_cast<double>(kMaxVariableBound)) {
983 ++num_truncated_bounds;
984 cp_var->
add_domain(bound < 0 ? -kMaxVariableBound : kMaxVariableBound);
990 static_cast<int64_t
>(lower ? std::ceil(bound - kWantedPrecision)
991 : std::floor(bound + kWantedPrecision)));
995 LOG(WARNING) <<
"Variable #" <<
i <<
" cannot take integer value. "
1004 if (diff > kWantedPrecision && diff < kSmallDomainSize) {
1005 ++num_small_domains;
1010 if (num_truncated_bounds > 0) {
1011 SOLVER_LOG(logger,
"Warning: ", num_truncated_bounds,
1012 " bounds were truncated to ", kMaxVariableBound,
".");
1014 if (num_small_domains > 0) {
1015 SOLVER_LOG(logger,
"Warning: ", num_small_domains,
1016 " continuous variable domain with fewer than ", kSmallDomainSize,
1021 const int64_t kScalingTarget = int64_t{1}
1029 if (ConstraintIsAlwaysTrue(mp_constraint))
continue;
1031 const absl::Status status =
1034 SOLVER_LOG(logger,
"Error while scaling constraint. ", status.message());
1040 switch (general_constraint.general_constraint_case()) {
1042 const auto& indicator_constraint =
1043 general_constraint.indicator_constraint();
1045 indicator_constraint.constraint();
1046 if (ConstraintIsAlwaysTrue(mp_constraint))
continue;
1048 const int new_ct_index = cp_model->
constraints().size();
1049 const absl::Status status =
1052 SOLVER_LOG(logger,
"Error while scaling constraint. ",
1058 const int var = indicator_constraint.var_index();
1059 const int value = indicator_constraint.var_value();
1065 const auto& and_constraint = general_constraint.and_constraint();
1066 absl::string_view name = general_constraint.name();
1069 ct_pos->
set_name(name.empty() ?
"" : absl::StrCat(name,
"_pos"));
1072 and_constraint.var_index();
1075 ct_neg->
set_name(name.empty() ?
"" : absl::StrCat(name,
"_neg"));
1077 NegatedRef(and_constraint.resultant_var_index()));
1078 for (
const int var_index : and_constraint.var_index()) {
1084 const auto& or_constraint = general_constraint.or_constraint();
1085 absl::string_view name = general_constraint.name();
1088 ct_pos->
set_name(name.empty() ?
"" : absl::StrCat(name,
"_pos"));
1091 or_constraint.var_index();
1094 ct_neg->
set_name(name.empty() ?
"" : absl::StrCat(name,
"_neg"));
1096 NegatedRef(or_constraint.resultant_var_index()));
1097 for (
const int var_index : or_constraint.var_index()) {
1103 LOG(ERROR) <<
"Can't convert general constraints of type "
1104 << general_constraint.general_constraint_case()
1105 <<
" to CpModelProto.";
1111 SOLVER_LOG(logger,
"Maximum constraint coefficient relative error: ",
1113 SOLVER_LOG(logger,
"Maximum constraint worst-case activity error: ",
1116 ?
" [Potentially IMPRECISE]"
1118 SOLVER_LOG(logger,
"Constraint scaling factor range: [",
1127 for (
int i = 0;
i < num_variables; ++
i) {
1130 float_objective->add_vars(
i);
1136 if (float_objective->offset() == 0 && float_objective->vars().empty()) {
1146 for (
const int ref : literals) {
1163 CHECK(output !=
nullptr);
1167 const int num_vars =
input.variables().size();
1168 for (
int v = 0; v < num_vars; ++v) {
1169 if (
input.variables(v).domain().size() != 2) {
1170 VLOG(1) <<
"Cannot convert "
1182 if (
input.has_objective()) {
1183 double factor =
input.objective().scaling_factor();
1184 if (factor == 0.0) factor = 1.0;
1185 const int num_terms =
input.objective().vars().size();
1186 for (
int i = 0;
i < num_terms; ++
i) {
1187 const int var =
input.objective().vars(
i);
1188 if (var < 0)
return false;
1191 factor *
input.objective().coeffs(
i));
1197 }
else if (
input.has_floating_point_objective()) {
1198 const int num_terms =
input.floating_point_objective().vars().size();
1199 for (
int i = 0;
i < num_terms; ++
i) {
1200 const int var =
input.floating_point_objective().vars(
i);
1201 if (var < 0)
return false;
1204 input.floating_point_objective().coeffs(
i));
1213 const int num_constraints =
input.constraints().size();
1214 std::vector<int> tmp_literals;
1215 for (
int c = 0; c < num_constraints; ++c) {
1247 tmp_literals.clear();
1253 tmp_literals.push_back(ref);
1254 const int shift = AppendSumOfLiteral(tmp_literals, out);
1257 tmp_literals.pop_back();
1263 VLOG(1) <<
"Cannot convert constraint: "
1269 int64_t min_activity = 0;
1270 int64_t max_activity = 0;
1271 const int num_terms = ct.
linear().
vars().size();
1272 for (
int i = 0;
i < num_terms; ++
i) {
1274 if (var < 0)
return false;
1275 DCHECK_EQ(
input.variables(var).domain().size(), 2);
1278 min_activity += coeff *
input.variables(var).domain(0);
1279 max_activity += coeff *
input.variables(var).domain(1);
1281 min_activity += coeff *
input.variables(var).domain(1);
1282 max_activity += coeff *
input.variables(var).domain(0);
1298 for (
int i = 0;
i < num_terms; ++
i) {
1300 if (var < 0)
return false;
1307 std::vector<MPConstraintProto*> out_cts;
1312 const int64_t coeff = max_activity - ct.
linear().
domain(1);
1325 out_cts.push_back(high_out_ct);
1344 out_cts.push_back(low_out_ct);
1347 for (
int i = 0;
i < num_terms; ++
i) {
1349 if (var < 0)
return false;
1350 out_ct->add_var_index(var);
1366 absl::Span<
const std::pair<int, double>> objective,
1367 double objective_offset,
bool maximize,
1373 std::vector<int> var_indices;
1374 std::vector<double> coefficients;
1375 std::vector<double> lower_bounds;
1376 std::vector<double> upper_bounds;
1377 double min_magnitude = std::numeric_limits<double>::infinity();
1378 double max_magnitude = 0.0;
1379 double l1_norm = 0.0;
1380 for (
const auto& [var, coeff] : objective) {
1381 const auto& var_proto = cp_model->
variables(var);
1382 const int64_t lb = var_proto.
domain(0);
1383 const int64_t ub = var_proto.domain(var_proto.domain_size() - 1);
1385 if (lb != 0) objective_offset += lb * coeff;
1388 var_indices.push_back(var);
1389 coefficients.push_back(coeff);
1390 lower_bounds.push_back(lb);
1391 upper_bounds.push_back(ub);
1393 min_magnitude = std::min(min_magnitude, std::abs(coeff));
1394 max_magnitude = std::max(max_magnitude, std::abs(coeff));
1395 l1_norm += std::abs(coeff);
1398 if (coefficients.empty() && objective_offset == 0.0)
return true;
1400 if (!coefficients.empty()) {
1401 const double average_magnitude =
1402 l1_norm /
static_cast<double>(coefficients.size());
1403 SOLVER_LOG(logger,
"[Scaling] Floating point objective has ",
1404 coefficients.size(),
" terms with magnitude in [", min_magnitude,
1405 ", ", max_magnitude,
"] average = ", average_magnitude);
1409 const int64_t max_absolute_activity = int64_t{1}
1411 const double wanted_precision =
1414 double relative_coeff_error;
1415 double scaled_sum_error;
1417 coefficients, lower_bounds, upper_bounds, max_absolute_activity,
1418 wanted_precision, &relative_coeff_error, &scaled_sum_error);
1419 if (scaling_factor == 0.0) {
1420 LOG(ERROR) <<
"Scaling factor of zero while scaling objective! This "
1421 "likely indicate an infinite coefficient in the objective.";
1428 SOLVER_LOG(logger,
"[Scaling] Objective coefficient relative error: ",
1429 relative_coeff_error);
1430 SOLVER_LOG(logger,
"[Scaling] Objective worst-case absolute error: ",
1431 scaled_sum_error / scaling_factor);
1433 "[Scaling] Objective scaling factor: ", scaling_factor / gcd);
1435 if (scaled_sum_error / scaling_factor > wanted_precision) {
1437 "[Scaling] Warning: the worst-case absolute error is greater "
1438 "than the wanted precision (",
1440 "). Try to increase mip_max_activity_exponent (default = ",
1442 ") or reduced your variables range and/or objective "
1443 "coefficient. We will continue the solve, but the final "
1444 "objective value might be off.");
1451 const int64_t mult = maximize ? -1 : 1;
1452 objective_proto->
set_offset(objective_offset * scaling_factor / gcd * mult);
1453 objective_proto->set_scaling_factor(1.0 / scaling_factor * gcd * mult);
1454 for (
int i = 0;
i < coefficients.size(); ++
i) {
1455 const int64_t value =
1456 static_cast<int64_t
>(std::round(coefficients[
i] * scaling_factor)) /
1459 objective_proto->add_vars(var_indices[
i]);
1460 objective_proto->add_coeffs(value * mult);
1464 if (scaled_sum_error == 0.0) {
1465 objective_proto->set_scaling_was_exact(
true);
1473 CHECK(problem !=
nullptr);
1481 for (
int var_id(0); var_id < num_variables; ++var_id) {
1492 if (lb <= -1.0) is_binary =
false;
1493 if (ub >= 2.0) is_binary =
false;
1496 if (lb <= 0.0 && ub >= 1.0) {
1498 }
else if (lb <= 1.0 && ub >= 1.0) {
1505 }
else if (lb <= 0.0 && ub >= 0.0) {
1520 LOG(WARNING) <<
"The variable #" << var_id <<
" with name "
1521 << mp_var.
name() <<
" is not binary. "
1522 <<
"lb: " << lb <<
" ub: " << ub;
1528 const int64_t kInt64Max = std::numeric_limits<int64_t>::max();
1529 double max_relative_error = 0.0;
1530 double max_bound_error = 0.0;
1531 double max_scaling_factor = 0.0;
1532 double relative_error = 0.0;
1533 double scaling_factor = 0.0;
1534 std::vector<double> coefficients;
1542 coefficients.clear();
1544 for (
int i = 0;
i < num_coeffs; ++
i) {
1551 max_relative_error = std::max(relative_error, max_relative_error);
1552 max_scaling_factor = std::max(scaling_factor / gcd, max_scaling_factor);
1554 double bound_error = 0.0;
1555 for (
int i = 0;
i < num_coeffs; ++
i) {
1556 const double scaled_value = mp_constraint.
coefficient(
i) * scaling_factor;
1557 bound_error += std::abs(round(scaled_value) - scaled_value);
1558 const int64_t value =
static_cast<int64_t
>(round(scaled_value)) / gcd;
1564 max_bound_error = std::max(max_bound_error, bound_error);
1573 if (lb * scaling_factor >
static_cast<double>(kInt64Max)) {
1574 LOG(WARNING) <<
"A constraint is trivially unsatisfiable.";
1577 if (lb * scaling_factor > -
static_cast<double>(kInt64Max)) {
1580 static_cast<int64_t
>(round(lb * scaling_factor - bound_error)) /
1586 if (ub * scaling_factor < -
static_cast<double>(kInt64Max)) {
1587 LOG(WARNING) <<
"A constraint is trivially unsatisfiable.";
1590 if (ub * scaling_factor <
static_cast<double>(kInt64Max)) {
1593 static_cast<int64_t
>(round(ub * scaling_factor + bound_error)) /
1600 LOG(INFO) <<
"Maximum constraint relative error: " << max_relative_error;
1601 LOG(INFO) <<
"Maximum constraint bound error: " << max_bound_error;
1602 LOG(INFO) <<
"Maximum constraint scaling factor: " << max_scaling_factor;
1605 coefficients.clear();
1606 for (
int var_id = 0; var_id < num_variables; ++var_id) {
1613 max_relative_error = std::max(relative_error, max_relative_error);
1616 LOG(INFO) <<
"objective relative error: " << relative_error;
1617 LOG(INFO) <<
"objective scaling factor: " << scaling_factor / gcd;
1625 for (
int var_id = 0; var_id < num_variables; ++var_id) {
1627 const int64_t value =
1628 static_cast<int64_t
>(
1641 const double kRelativeTolerance = 1e-8;
1642 if (max_relative_error > kRelativeTolerance) {
1643 LOG(WARNING) <<
"The relative error during double -> int64_t conversion "
1671 for (
int i = 0;
i < constraint.literals_size(); ++
i) {
1672 const int literal = constraint.literals(
i);
1673 const double coeff = constraint.coefficients(
i);
1674 const ColIndex variable_index = ColIndex(abs(literal) - 1);
1684 constraint.has_lower_bound() ? constraint.lower_bound() - sum
1686 constraint.has_upper_bound() ? constraint.upper_bound() - sum
1696 const int literal = objective.
literals(
i);
1697 const double coeff =
1698 static_cast<double>(objective.
coefficients(
i)) * scaling_factor;
1699 const ColIndex variable_index = ColIndex(abs(literal) - 1);
1715 const CpModelProto& model_proto_with_floating_point_objective,
1717 const int64_t inner_integer_objective_lower_bound) {
1720 const CpModelProto& proto = model_proto_with_floating_point_objective;
1724 static_cast<double>(domain[domain.size() - 1]));
1732 for (
int i = 0;
i < float_obj.
vars().size(); ++
i) {
1733 const glop::ColIndex col(float_obj.
vars(
i));
1741 ct,
static_cast<double>(inner_integer_objective_lower_bound),
1742 std::numeric_limits<double>::infinity());
1743 for (
int i = 0;
i < integer_objective.
vars().size(); ++
i) {
1745 static_cast<double>(integer_objective.
coeffs(
i)));
1763 return float_obj.
maximize() ? std::numeric_limits<double>::infinity()
1764 : -std::numeric_limits<double>::infinity();
void set_var_index(int index, ::int32_t value)
::google::protobuf::RepeatedField< double > *PROTOBUF_NONNULL mutable_coefficient()
double lower_bound() const
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_var_index()
const ::std::string & name() const
void set_lower_bound(double value)
int coefficient_size() const
void add_coefficient(double value)
void set_upper_bound(double value)
double coefficient(int index) const
void set_coefficient(int index, double value)
void add_var_index(::int32_t value)
::int32_t var_index(int index) const
double upper_bound() const
GeneralConstraintCase general_constraint_case() const
::operations_research::MPIndicatorConstraint *PROTOBUF_NONNULL mutable_indicator_constraint()
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint()
const ::operations_research::MPVariableProto & variable(int index) const
int variable_size() const
void set_maximize(bool value)
const ::operations_research::MPGeneralConstraintProto & general_constraint(int index) const
::operations_research::MPConstraintProto *PROTOBUF_NONNULL add_constraint()
void clear_objective_offset()
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint(int index)
::operations_research::MPVariableProto *PROTOBUF_NONNULL add_variable()
const ::operations_research::MPConstraintProto & constraint(int index) const
void set_objective_offset(double value)
int general_constraint_size() const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
::operations_research::MPVariableProto *PROTOBUF_NONNULL mutable_variable(int index)
const ::std::string & name() const
::operations_research::MPGeneralConstraintProto *PROTOBUF_NONNULL mutable_general_constraint(int index)
int constraint_size() const
double objective_offset() const
void set_upper_bound(double value)
void set_is_integer(bool value)
void set_objective_coefficient(double value)
double lower_bound() const
double upper_bound() const
void clear_objective_coefficient()
void set_lower_bound(double value)
const ::std::string & name() const
double objective_coefficient() const
void set_change_status_to_imprecise(bool value)
void set_max_number_of_iterations(::int64_t value)
Fractional GetObjectiveValue() const
void SetParameters(const GlopParameters ¶meters)
ABSL_MUST_USE_RESULT ProblemStatus Solve(const LinearProgram &lp)
const DenseRow & objective_coefficients() const
void SetConstraintName(RowIndex row, absl::string_view name)
void SetObjectiveOffset(Fractional objective_offset)
void SetObjectiveCoefficient(ColIndex col, Fractional value)
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
void SetVariableType(ColIndex col, VariableType type)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
void SetVariableName(ColIndex col, absl::string_view name)
RowIndex CreateNewConstraint()
ColIndex CreateNewVariable()
void SetMaximizationProblem(bool maximize)
::int32_t literals(int index) const
void add_literals(::int32_t value)
::google::protobuf::RepeatedField<::int32_t > *PROTOBUF_NONNULL mutable_literals()
ConstraintCase constraint_case() const
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_and()
::int32_t enforcement_literal(int index) const
const ::operations_research::sat::BoolArgumentProto & exactly_one() const
const ::operations_research::sat::LinearConstraintProto & linear() const
const ::operations_research::sat::BoolArgumentProto & at_most_one() const
const ::operations_research::sat::BoolArgumentProto & bool_and() const
::operations_research::sat::BoolArgumentProto *PROTOBUF_NONNULL mutable_bool_or()
void add_enforcement_literal(::int32_t value)
const ::operations_research::sat::BoolArgumentProto & bool_or() const
::operations_research::sat::LinearConstraintProto *PROTOBUF_NONNULL mutable_linear()
void set_name(Arg_ &&arg, Args_... args)
void clear_floating_point_objective()
::operations_research::sat::FloatObjectiveProto *PROTOBUF_NONNULL mutable_floating_point_objective()
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL mutable_constraints(int index)
const ::operations_research::sat::IntegerVariableProto & variables(int index) const
const ::operations_research::sat::FloatObjectiveProto & floating_point_objective() const
const ::operations_research::sat::ConstraintProto & constraints(int index) const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::IntegerVariableProto *PROTOBUF_NONNULL add_variables()
::operations_research::sat::ConstraintProto *PROTOBUF_NONNULL add_constraints()
::operations_research::sat::CpObjectiveProto *PROTOBUF_NONNULL mutable_objective()
void set_offset(double value)
::int32_t vars(int index) const
::int64_t coeffs(int index) const
::int32_t vars(int index) const
void set_maximize(bool value)
double coeffs(int index) const
void add_domain(::int64_t value)
void set_name(Arg_ &&arg, Args_... args)
::int64_t domain(int index) const
void set_upper_bound(::int64_t value)
void set_name(Arg_ &&arg, Args_... args)
void set_lower_bound(::int64_t value)
void add_literals(::int32_t value)
void add_coefficients(::int64_t value)
void set_name(Arg_ &&arg, Args_... args)
::operations_research::sat::LinearBooleanConstraint *PROTOBUF_NONNULL add_constraints()
const ::operations_research::sat::LinearBooleanConstraint & constraints(int index) const
const ::operations_research::sat::LinearObjective & objective() const
void set_num_variables(::int32_t value)
::std::string *PROTOBUF_NONNULL add_var_names()
const ::std::string & var_names(int index) const
ABSL_ATTRIBUTE_REINITIALIZES void Clear() PROTOBUF_FINAL
::int32_t num_variables() const
int var_names_size() const
::operations_research::sat::LinearObjective *PROTOBUF_NONNULL mutable_objective()
::int32_t vars(int index) const
::int64_t coeffs(int index) const
::int64_t domain(int index) const
int literals_size() const
void add_literals(::int32_t value)
void add_coefficients(::int64_t value)
::int64_t coefficients(int index) const
void set_scaling_factor(double value)
double scaling_factor() const
::int32_t literals(int index) const
void set_offset(double value)
int coefficient_size() const
double coefficient(int index) const
void set_coefficient(int index, double value)
::int32_t var_index(int index) const
const ::operations_research::MPVariableProto & variable(int index) const
int variable_size() const
::operations_research::MPConstraintProto *PROTOBUF_NONNULL mutable_constraint(int index)
::operations_research::MPVariableProto *PROTOBUF_NONNULL mutable_variable(int index)
::operations_research::MPGeneralConstraintProto *PROTOBUF_NONNULL mutable_general_constraint(int index)
double lower_bound() const
double upper_bound() const
double objective_coefficient() const
double mip_check_precision() const
double mip_max_valid_magnitude() const
bool ignore_names() const
::int32_t mip_max_activity_exponent() const
double mip_wanted_precision() const
double mip_max_bound() const
double mip_drop_tolerance() const
double absolute_gap_limit() const
constexpr Fractional kInfinity
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
double FindBestScalingAndComputeErrors(absl::Span< const double > coefficients, absl::Span< const double > lower_bounds, absl::Span< const double > upper_bounds, int64_t max_absolute_activity, double wanted_absolute_activity_precision, double *relative_coeff_error, double *scaled_sum_error)
bool RefIsPositive(int ref)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
bool ConvertMPModelProtoToCpModelProto(const SatParameters ¶ms, const MPModelProto &mp_model, CpModelProto *cp_model, SolverLogger *logger)
int64_t FindRationalFactor(double x, int64_t limit, double tolerance)
void ConvertBooleanProblemToLinearProgram(const LinearBooleanProblem &problem, glop::LinearProgram *lp)
bool MakeBoundsOfIntegerVariablesInteger(const SatParameters ¶ms, MPModelProto *mp_model, SolverLogger *logger)
void ChangeLargeBoundsToInfinity(double max_magnitude, MPModelProto *mp_model, SolverLogger *logger)
bool ScaleAndSetObjective(const SatParameters ¶ms, absl::Span< const std::pair< int, double > > objective, double objective_offset, bool maximize, CpModelProto *cp_model, SolverLogger *logger)
void ChangeOptimizationDirection(LinearBooleanProblem *problem)
std::vector< double > DetectImpliedIntegers(MPModelProto *mp_model, SolverLogger *logger)
void RemoveNearZeroTerms(const SatParameters ¶ms, MPModelProto *mp_model, SolverLogger *logger)
bool ConvertBinaryMPModelProtoToBooleanProblem(const MPModelProto &mp_model, LinearBooleanProblem *problem)
double ComputeTrueObjectiveLowerBound(const CpModelProto &model_proto_with_floating_point_objective, const CpObjectiveProto &integer_objective, const int64_t inner_integer_objective_lower_bound)
bool ConvertCpModelProtoToMPModelProto(const CpModelProto &input, MPModelProto *output)
constexpr Fractional kInfinity
std::vector< double > ScaleContinuousVariables(double scaling, double max_bound, MPModelProto *mp_model)
bool MPModelProtoValidationBeforeConversion(const SatParameters ¶ms, const MPModelProto &mp_model, SolverLogger *logger)
int64_t CapAdd(int64_t x, int64_t y)
double GetBestScalingOfDoublesToInt64(absl::Span< const double > input, absl::Span< const double > lb, absl::Span< const double > ub, int64_t max_absolute_sum)
std::string ProtobufShortDebugString(const P &message)
int64_t CapProd(int64_t x, int64_t y)
void ComputeScalingErrors(absl::Span< const double > input, absl::Span< const double > lb, absl::Span< const double > ub, double scaling_factor, double *max_relative_coeff_error, double *max_scaled_sum_error)
std::string ProtobufDebugString(const P &message)
int64_t ComputeGcdOfRoundedDoubles(absl::Span< const double > x, double scaling_factor)
static int input(yyscan_t yyscanner)
std::vector< int > var_indices
double max_absolute_rhs_error
std::vector< double > coefficients
absl::Status ScaleAndAddConstraint(absl::Span< const int > vars, absl::Span< const double > coeffs, double lower_bound, double upper_bound, absl::string_view name, absl::Span< const IntegerVariableProto *const > var_domains, ConstraintProto *constraint)
double max_relative_coeff_error
double max_scaling_factor
std::vector< double > lower_bounds
double min_scaling_factor
std::vector< double > upper_bounds
#define SOLVER_LOG(logger,...)