24 #include "absl/container/flat_hash_map.h"
25 #include "absl/meta/type_traits.h"
28 #include "ortools/glop/parameters.pb.h"
40 #include "ortools/sat/sat_parameters.pb.h"
56 const double FeasibilityPump::kCpEpsilon = 1e-4;
59 : sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
81 VLOG(1) <<
"Feasibility Pump Total number of simplex iterations: "
82 << total_num_simplex_iterations_;
87 for (
const IntegerVariable
var :
ct.vars) {
91 integer_lp_.
push_back(LinearConstraintInternal());
92 LinearConstraintInternal& new_ct = integer_lp_.
back();
95 const int size =
ct.vars.size();
96 CHECK_LE(
ct.lb,
ct.ub);
97 for (
int i = 0; i < size; ++i) {
99 IntegerVariable
var =
ct.vars[i];
100 IntegerValue coeff =
ct.coeffs[i];
105 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var), coeff});
108 std::sort(new_ct.terms.begin(), new_ct.terms.end());
112 IntegerValue coeff) {
113 objective_is_defined_ =
true;
114 const IntegerVariable pos_var =
116 if (ivar != pos_var) coeff = -coeff;
118 const auto it = mirror_lp_variable_.find(pos_var);
119 if (it == mirror_lp_variable_.end())
return;
120 const ColIndex
col = it->second;
121 integer_objective_.push_back({
col, coeff});
122 objective_infinity_norm_ =
126 ColIndex FeasibilityPump::GetOrCreateMirrorVariable(
127 IntegerVariable positive_variable) {
130 const auto it = mirror_lp_variable_.find(positive_variable);
131 if (it == mirror_lp_variable_.end()) {
132 const int model_var =
134 model_vars_size_ =
std::max(model_vars_size_, model_var + 1);
136 const ColIndex
col(integer_variables_.size());
137 mirror_lp_variable_[positive_variable] =
col;
138 integer_variables_.push_back(positive_variable);
139 var_is_binary_.push_back(
false);
140 lp_solution_.push_back(std::numeric_limits<double>::infinity());
141 integer_solution_.push_back(0);
148 void FeasibilityPump::PrintStats() {
149 if (lp_solution_is_set_) {
150 VLOG(2) <<
"Fractionality: " << lp_solution_fractionality_;
152 VLOG(2) <<
"Fractionality: NA";
156 if (integer_solution_is_set_) {
157 VLOG(2) <<
"#Infeasible const: " << num_infeasible_constraints_;
158 VLOG(2) <<
"Infeasibility: " << integer_solution_infeasibility_;
160 VLOG(2) <<
"Infeasibility: NA";
166 InitializeWorkingLP();
168 UpdateBoundsOfLpVariables();
169 lp_solution_is_set_ =
false;
170 integer_solution_is_set_ =
false;
176 for (
const auto& term : integer_objective_) {
180 mixing_factor_ = 1.0;
181 for (
int i = 0; i < max_fp_iterations_; ++i) {
183 L1DistanceMinimize();
184 if (!SolveLp())
break;
185 if (lp_solution_is_integer_)
break;
189 if (integer_solution_is_feasible_) MaybePushToRepo();
192 if (model_is_unsat_)
return false;
199 void FeasibilityPump::MaybePushToRepo() {
200 if (incomplete_solutions_ ==
nullptr)
return;
202 std::vector<double> lp_solution(model_vars_size_,
203 std::numeric_limits<double>::infinity());
205 if (lp_solution_is_integer_) {
207 for (
const IntegerVariable positive_var : integer_variables_) {
208 const int model_var =
210 if (model_var >= 0 && model_var < model_vars_size_) {
217 if (integer_solution_is_feasible_) {
219 for (
const IntegerVariable positive_var : integer_variables_) {
220 const int model_var =
222 if (model_var >= 0 && model_var < model_vars_size_) {
234 void FeasibilityPump::InitializeWorkingLP() {
237 for (
int i = 0; i < integer_variables_.size(); ++i) {
244 for (
const LinearConstraintInternal&
ct : integer_lp_) {
247 for (
const auto& term :
ct.terms) {
253 for (
const auto& term : integer_objective_) {
257 const int num_vars = integer_variables_.size();
258 for (
int i = 0; i < num_vars; i++) {
259 const IntegerVariable cp_var = integer_variables_[i];
265 objective_normalization_factor_ = 0.0;
270 if (!var_is_binary_[
col.value()]) {
271 integer_variables.push_back(
col);
276 objective_normalization_factor_ +=
280 objective_normalization_factor_ =
283 if (!integer_variables.empty()) {
285 norm_variables_.
assign(num_cols, ColIndex(-1));
286 norm_lhs_constraints_.
assign(num_cols, RowIndex(-1));
287 norm_rhs_constraints_.
assign(num_cols, RowIndex(-1));
304 for (
const ColIndex
col : integer_variables) {
306 norm_variables_[
col] = norm_variable;
309 norm_lhs_constraints_[
col] = row_a;
313 norm_rhs_constraints_[
col] = row_b;
319 scaler_.
Scale(&lp_data_);
324 void FeasibilityPump::L1DistanceMinimize() {
325 std::vector<double> new_obj_coeffs(lp_data_.
num_variables().value(), 0.0);
330 for (ColIndex
col(0);
col < num_cols; ++
col) {
331 new_obj_coeffs[
col.value()] =
338 if (var_is_binary_[
col.value()]) {
341 (1 - mixing_factor_) * objective_normalization_factor_ *
342 (1 - 2 * integer_solution_[
col.value()]);
343 new_obj_coeffs[
col.value()] = objective_coefficient;
355 (1 - mixing_factor_) * objective_normalization_factor_;
356 new_obj_coeffs[norm_variables_[
col].value()] = objective_coefficient;
360 const ColIndex norm_lhs_slack_variable =
362 const double lhs_scaling_factor =
366 lhs_scaling_factor * integer_solution_[
col.value()]);
367 const ColIndex norm_rhs_slack_variable =
369 const double rhs_scaling_factor =
373 -rhs_scaling_factor * integer_solution_[
col.value()]);
380 mixing_factor_ *= 0.8;
383 bool FeasibilityPump::SolveLp() {
384 const int num_vars = integer_variables_.size();
387 const auto status = simplex_.
Solve(lp_data_, time_limit_);
390 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
403 lp_solution_fractionality_ = 0.0;
408 lp_solution_is_set_ =
true;
409 for (
int i = 0; i < num_vars; i++) {
410 const double value = GetVariableValueAtCpScale(ColIndex(i));
411 lp_solution_[i] =
value;
412 lp_solution_fractionality_ =
std::max(
413 lp_solution_fractionality_, std::abs(
value - std::round(
value)));
418 for (
const auto& term : integer_objective_) {
419 lp_objective_ += lp_solution_[term.first.value()] * term.second.value();
421 lp_solution_is_integer_ = lp_solution_fractionality_ < kCpEpsilon;
426 void FeasibilityPump::UpdateBoundsOfLpVariables() {
427 const int num_vars = integer_variables_.size();
428 for (
int i = 0; i < num_vars; i++) {
429 const IntegerVariable cp_var = integer_variables_[i];
438 return lp_solution_[mirror_lp_variable_.at(variable).value()];
441 double FeasibilityPump::GetVariableValueAtCpScale(ColIndex
var) {
450 IntegerVariable variable)
const {
451 return integer_solution_[mirror_lp_variable_.at(variable).value()];
454 bool FeasibilityPump::Round() {
455 bool rounding_successful =
true;
456 if (sat_parameters_.fp_rounding() == SatParameters::NEAREST_INTEGER) {
457 rounding_successful = NearestIntegerRounding();
458 }
else if (sat_parameters_.fp_rounding() == SatParameters::LOCK_BASED) {
459 rounding_successful = LockBasedRounding();
460 }
else if (sat_parameters_.fp_rounding() ==
461 SatParameters::ACTIVE_LOCK_BASED) {
462 rounding_successful = ActiveLockBasedRounding();
463 }
else if (sat_parameters_.fp_rounding() ==
464 SatParameters::PROPAGATION_ASSISTED) {
465 rounding_successful = PropagationRounding();
467 if (!rounding_successful)
return false;
468 FillIntegerSolutionStats();
472 bool FeasibilityPump::NearestIntegerRounding() {
473 if (!lp_solution_is_set_)
return false;
474 for (
int i = 0; i < lp_solution_.size(); ++i) {
475 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
477 integer_solution_is_set_ =
true;
481 bool FeasibilityPump::LockBasedRounding() {
482 if (!lp_solution_is_set_)
return false;
483 const int num_vars = integer_variables_.size();
487 if (var_up_locks_.empty()) {
488 var_up_locks_.resize(num_vars, 0);
489 var_down_locks_.resize(num_vars, 0);
490 for (
int i = 0; i < num_vars; ++i) {
493 const bool constraint_upper_bounded =
496 const bool constraint_lower_bounded =
499 if (entry.coefficient() > 0) {
500 var_up_locks_[i] += constraint_upper_bounded;
501 var_down_locks_[i] += constraint_lower_bounded;
503 var_up_locks_[i] += constraint_lower_bounded;
504 var_down_locks_[i] += constraint_upper_bounded;
510 for (
int i = 0; i < lp_solution_.size(); ++i) {
511 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1 ||
512 var_up_locks_[i] == var_down_locks_[i]) {
513 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
514 }
else if (var_up_locks_[i] > var_down_locks_[i]) {
515 integer_solution_[i] =
static_cast<int64_t
>(std::floor(lp_solution_[i]));
517 integer_solution_[i] =
static_cast<int64_t
>(std::ceil(lp_solution_[i]));
520 integer_solution_is_set_ =
true;
524 bool FeasibilityPump::ActiveLockBasedRounding() {
525 if (!lp_solution_is_set_)
return false;
526 const int num_vars = integer_variables_.size();
531 for (
int i = 0; i < num_vars; ++i) {
532 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1) {
533 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
541 if (row_status == ConstraintStatus::AT_LOWER_BOUND) {
542 if (entry.coefficient() > 0) {
547 }
else if (row_status == ConstraintStatus::AT_UPPER_BOUND) {
548 if (entry.coefficient() > 0) {
555 if (up_locks == down_locks) {
556 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
557 }
else if (up_locks > down_locks) {
558 integer_solution_[i] =
static_cast<int64_t
>(std::floor(lp_solution_[i]));
560 integer_solution_[i] =
static_cast<int64_t
>(std::ceil(lp_solution_[i]));
564 integer_solution_is_set_ =
true;
568 bool FeasibilityPump::PropagationRounding() {
569 if (!lp_solution_is_set_)
return false;
573 std::vector<int> rounding_order;
575 std::vector<std::pair<double, int>> binary_fractionality_vars;
576 std::vector<std::pair<double, int>> general_fractionality_vars;
577 for (
int i = 0; i < lp_solution_.size(); ++i) {
578 const double fractionality =
579 std::abs(std::round(lp_solution_[i]) - lp_solution_[i]);
580 if (var_is_binary_[i]) {
581 binary_fractionality_vars.push_back({fractionality, i});
583 general_fractionality_vars.push_back({fractionality, i});
586 std::sort(binary_fractionality_vars.begin(),
587 binary_fractionality_vars.end());
588 std::sort(general_fractionality_vars.begin(),
589 general_fractionality_vars.end());
591 for (
int i = 0; i < binary_fractionality_vars.size(); ++i) {
592 rounding_order.push_back(binary_fractionality_vars[i].second);
594 for (
int i = 0; i < general_fractionality_vars.size(); ++i) {
595 rounding_order.push_back(general_fractionality_vars[i].second);
599 for (
const int var_index : rounding_order) {
602 const IntegerVariable
var = integer_variables_[var_index];
609 integer_solution_[var_index] = lb.value();
613 const int64_t rounded_value =
614 static_cast<int64_t
>(std::round(lp_solution_[var_index]));
615 const int64_t floor_value =
616 static_cast<int64_t
>(std::floor(lp_solution_[var_index]));
617 const int64_t ceil_value =
618 static_cast<int64_t
>(std::ceil(lp_solution_[var_index]));
620 const bool floor_is_in_domain =
621 (domain.Contains(floor_value) && lb.value() <= floor_value);
622 const bool ceil_is_in_domain =
623 (domain.Contains(ceil_value) && ub.value() >= ceil_value);
624 if (domain.IsEmpty()) {
625 integer_solution_[var_index] = rounded_value;
626 model_is_unsat_ =
true;
630 if (ceil_value < lb.value()) {
631 integer_solution_[var_index] = lb.value();
632 }
else if (floor_value > ub.value()) {
633 integer_solution_[var_index] = ub.value();
634 }
else if (ceil_is_in_domain && floor_is_in_domain) {
635 DCHECK(domain.Contains(rounded_value));
636 integer_solution_[var_index] = rounded_value;
637 }
else if (ceil_is_in_domain) {
638 integer_solution_[var_index] = ceil_value;
639 }
else if (floor_is_in_domain) {
640 integer_solution_[var_index] = floor_value;
642 const std::pair<IntegerLiteral, IntegerLiteral> values_in_domain =
645 const int64_t lower_value = values_in_domain.first.bound.value();
646 const int64_t higher_value = -values_in_domain.second.bound.value();
647 const int64_t distance_from_lower_value =
648 std::abs(lower_value - rounded_value);
649 const int64_t distance_from_higher_value =
650 std::abs(higher_value - rounded_value);
652 integer_solution_[var_index] =
653 (distance_from_lower_value < distance_from_higher_value)
658 CHECK(domain.Contains(integer_solution_[var_index]));
659 CHECK_GE(integer_solution_[var_index], lb);
660 CHECK_LE(integer_solution_[var_index], ub);
669 const IntegerValue
value(integer_solution_[var_index]);
673 }
else if (
value == ub) {
682 model_is_unsat_ =
true;
688 model_is_unsat_ =
true;
693 integer_solution_is_set_ =
true;
697 void FeasibilityPump::FillIntegerSolutionStats() {
699 integer_solution_objective_ = 0;
700 for (
const auto& term : integer_objective_) {
701 integer_solution_objective_ +=
702 integer_solution_[term.first.value()] * term.second.value();
705 integer_solution_is_feasible_ =
true;
706 num_infeasible_constraints_ = 0;
707 integer_solution_infeasibility_ = 0;
708 for (RowIndex i(0); i < integer_lp_.size(); ++i) {
709 int64_t activity = 0;
710 for (
const auto& term : integer_lp_[i].terms) {
712 CapProd(integer_solution_[term.first.value()], term.second.value());
718 activity =
CapAdd(activity, prod);
723 if (activity > integer_lp_[i].ub || activity < integer_lp_[i].lb) {
724 integer_solution_is_feasible_ =
false;
725 num_infeasible_constraints_++;
726 const int64_t ub_infeasibility =
727 activity > integer_lp_[i].ub.value()
728 ? activity - integer_lp_[i].ub.value()
730 const int64_t lb_infeasibility =
731 activity < integer_lp_[i].lb.value()
732 ? integer_lp_[i].lb.value() - activity
734 integer_solution_infeasibility_ =
735 std::max(integer_solution_infeasibility_,
736 std::max(ub_infeasibility, lb_infeasibility));
void push_back(const value_type &x)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
ColIndex GetSlackVariable(RowIndex row) const
const DenseRow & variable_lower_bounds() const
const DenseRow & objective_coefficients() const
const std::vector< ColIndex > & IntegerVariablesList() const
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
ColIndex CreateNewVariable()
void SetVariableType(ColIndex col, VariableType type)
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
void SetObjectiveCoefficient(ColIndex col, Fractional value)
bool IsVariableBinary(ColIndex col) const
const DenseRow & variable_upper_bounds() const
ColIndex num_variables() const
RowIndex CreateNewConstraint()
std::string GetDimensionString() const
const SparseColumn & GetSparseColumn(ColIndex col) const
void Scale(LinearProgram *lp)
Fractional VariableScalingFactor(ColIndex col) const
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Fractional GetVariableValue(ColIndex col) const
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
ProblemStatus GetProblemStatus() const
ConstraintStatus GetConstraintStatus(RowIndex row) const
void ClearStateForNextSolve()
int64_t GetNumberOfIterations() const
void SetParameters(const GlopParameters ¶meters)
void assign(IntType size, const T &v)
int GetProtoVariableFromIntegerVariable(IntegerVariable var) const
FeasibilityPump(Model *model)
glop::RowIndex ConstraintIndex
double GetLPSolutionValue(IntegerVariable variable) const
int64_t GetIntegerSolutionValue(IntegerVariable variable) const
void AddLinearConstraint(const LinearConstraint &ct)
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
IntegerValue LowerBound(IntegerVariable i) const
Class that owns everything related to a particular optimization model.
bool ModelIsUnsat() const
Status EnqueueDecisionAndBacktrackOnConflict(Literal true_literal, int *first_propagation_index=nullptr)
void AddNewSolution(const std::vector< double > &lp_solution)
std::vector< ColIndex > ColIndexVector
constexpr double kInfinity
IntType IntTypeAbs(IntType t)
IntegerVariable PositiveVariable(IntegerVariable i)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
bool VariableIsPositive(IntegerVariable i)
double ToDouble(IntegerValue value)
Collection of objects used to extend the Constraint Solver library.
int64_t CapAdd(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
#define VLOG(verboselevel)