36 const double FeasibilityPump::kCpEpsilon = 1e-4;
39 : sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
61 VLOG(1) <<
"Feasibility Pump Total number of simplex iterations: "
62 << total_num_simplex_iterations_;
67 for (
const IntegerVariable
var :
ct.vars) {
71 integer_lp_.
push_back(LinearConstraintInternal());
72 LinearConstraintInternal& new_ct = integer_lp_.
back();
75 const int size =
ct.vars.size();
77 for (
int i = 0; i < size; ++i) {
79 IntegerVariable
var =
ct.vars[i];
80 IntegerValue coeff =
ct.coeffs[i];
85 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var), coeff});
88 std::sort(new_ct.terms.begin(), new_ct.terms.end());
93 objective_is_defined_ =
true;
94 const IntegerVariable pos_var =
96 if (ivar != pos_var) coeff = -coeff;
98 const auto it = mirror_lp_variable_.find(pos_var);
99 if (it == mirror_lp_variable_.end())
return;
100 const ColIndex
col = it->second;
101 integer_objective_.push_back({
col, coeff});
102 objective_infinity_norm_ =
106 ColIndex FeasibilityPump::GetOrCreateMirrorVariable(
107 IntegerVariable positive_variable) {
110 const auto it = mirror_lp_variable_.find(positive_variable);
111 if (it == mirror_lp_variable_.end()) {
112 const int model_var =
114 model_vars_size_ =
std::max(model_vars_size_, model_var + 1);
116 const ColIndex
col(integer_variables_.size());
117 mirror_lp_variable_[positive_variable] =
col;
118 integer_variables_.push_back(positive_variable);
119 var_is_binary_.push_back(
false);
120 lp_solution_.push_back(std::numeric_limits<double>::infinity());
121 integer_solution_.push_back(0);
128 void FeasibilityPump::PrintStats() {
129 if (lp_solution_is_set_) {
130 VLOG(2) <<
"Fractionality: " << lp_solution_fractionality_;
132 VLOG(2) <<
"Fractionality: NA";
136 if (integer_solution_is_set_) {
137 VLOG(2) <<
"#Infeasible const: " << num_infeasible_constraints_;
138 VLOG(2) <<
"Infeasibility: " << integer_solution_infeasibility_;
140 VLOG(2) <<
"Infeasibility: NA";
146 InitializeWorkingLP();
148 UpdateBoundsOfLpVariables();
149 lp_solution_is_set_ =
false;
150 integer_solution_is_set_ =
false;
156 for (
const auto& term : integer_objective_) {
160 mixing_factor_ = 1.0;
161 for (
int i = 0; i < max_fp_iterations_; ++i) {
163 L1DistanceMinimize();
164 if (!SolveLp())
break;
165 if (lp_solution_is_integer_)
break;
169 if (integer_solution_is_feasible_) MaybePushToRepo();
172 if (model_is_unsat_)
return false;
179 void FeasibilityPump::MaybePushToRepo() {
180 if (incomplete_solutions_ ==
nullptr)
return;
182 std::vector<double> lp_solution(model_vars_size_,
183 std::numeric_limits<double>::infinity());
185 if (lp_solution_is_integer_) {
187 for (
const IntegerVariable positive_var : integer_variables_) {
188 const int model_var =
190 if (model_var >= 0 && model_var < model_vars_size_) {
197 if (integer_solution_is_feasible_) {
199 for (
const IntegerVariable positive_var : integer_variables_) {
200 const int model_var =
202 if (model_var >= 0 && model_var < model_vars_size_) {
214 void FeasibilityPump::InitializeWorkingLP() {
217 for (
int i = 0; i < integer_variables_.size(); ++i) {
224 for (
const LinearConstraintInternal&
ct : integer_lp_) {
227 for (
const auto& term :
ct.terms) {
233 for (
const auto& term : integer_objective_) {
237 const int num_vars = integer_variables_.size();
238 for (
int i = 0; i < num_vars; i++) {
239 const IntegerVariable cp_var = integer_variables_[i];
245 objective_normalization_factor_ = 0.0;
250 if (!var_is_binary_[
col.value()]) {
251 integer_variables.push_back(
col);
256 objective_normalization_factor_ +=
260 objective_normalization_factor_ =
263 if (!integer_variables.empty()) {
265 norm_variables_.
assign(num_cols, ColIndex(-1));
266 norm_lhs_constraints_.
assign(num_cols, RowIndex(-1));
267 norm_rhs_constraints_.
assign(num_cols, RowIndex(-1));
284 for (
const ColIndex
col : integer_variables) {
286 norm_variables_[
col] = norm_variable;
289 norm_lhs_constraints_[
col] = row_a;
293 norm_rhs_constraints_[
col] = row_b;
299 scaler_.
Scale(&lp_data_);
304 void FeasibilityPump::L1DistanceMinimize() {
305 std::vector<double> new_obj_coeffs(lp_data_.
num_variables().value(), 0.0);
310 for (ColIndex
col(0);
col < num_cols; ++
col) {
311 new_obj_coeffs[
col.value()] =
318 if (var_is_binary_[
col.value()]) {
321 (1 - mixing_factor_) * objective_normalization_factor_ *
322 (1 - 2 * integer_solution_[
col.value()]);
323 new_obj_coeffs[
col.value()] = objective_coefficient;
335 (1 - mixing_factor_) * objective_normalization_factor_;
336 new_obj_coeffs[norm_variables_[
col].value()] = objective_coefficient;
340 const ColIndex norm_lhs_slack_variable =
342 const double lhs_scaling_factor =
346 lhs_scaling_factor * integer_solution_[
col.value()]);
347 const ColIndex norm_rhs_slack_variable =
349 const double rhs_scaling_factor =
353 -rhs_scaling_factor * integer_solution_[
col.value()]);
360 mixing_factor_ *= 0.8;
363 bool FeasibilityPump::SolveLp() {
364 const int num_vars = integer_variables_.size();
367 const auto status = simplex_.
Solve(lp_data_, time_limit_);
370 VLOG(1) <<
"The LP solver encountered an error: " << status.error_message();
383 lp_solution_fractionality_ = 0.0;
388 lp_solution_is_set_ =
true;
389 for (
int i = 0; i < num_vars; i++) {
390 const double value = GetVariableValueAtCpScale(ColIndex(i));
391 lp_solution_[i] =
value;
392 lp_solution_fractionality_ =
std::max(
393 lp_solution_fractionality_, std::abs(
value - std::round(
value)));
398 for (
const auto& term : integer_objective_) {
399 lp_objective_ += lp_solution_[term.first.value()] * term.second.value();
401 lp_solution_is_integer_ = lp_solution_fractionality_ < kCpEpsilon;
406 void FeasibilityPump::UpdateBoundsOfLpVariables() {
407 const int num_vars = integer_variables_.size();
408 for (
int i = 0; i < num_vars; i++) {
409 const IntegerVariable cp_var = integer_variables_[i];
418 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
421 double FeasibilityPump::GetVariableValueAtCpScale(ColIndex
var) {
430 IntegerVariable variable)
const {
431 return integer_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable)
435 bool FeasibilityPump::Round() {
436 bool rounding_successful =
true;
437 if (sat_parameters_.fp_rounding() == SatParameters::NEAREST_INTEGER) {
438 rounding_successful = NearestIntegerRounding();
439 }
else if (sat_parameters_.fp_rounding() == SatParameters::LOCK_BASED) {
440 rounding_successful = LockBasedRounding();
441 }
else if (sat_parameters_.fp_rounding() ==
442 SatParameters::ACTIVE_LOCK_BASED) {
443 rounding_successful = ActiveLockBasedRounding();
444 }
else if (sat_parameters_.fp_rounding() ==
445 SatParameters::PROPAGATION_ASSISTED) {
446 rounding_successful = PropagationRounding();
448 if (!rounding_successful)
return false;
449 FillIntegerSolutionStats();
453 bool FeasibilityPump::NearestIntegerRounding() {
454 if (!lp_solution_is_set_)
return false;
455 for (
int i = 0; i < lp_solution_.size(); ++i) {
456 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
458 integer_solution_is_set_ =
true;
462 bool FeasibilityPump::LockBasedRounding() {
463 if (!lp_solution_is_set_)
return false;
464 const int num_vars = integer_variables_.size();
468 if (var_up_locks_.empty()) {
469 var_up_locks_.resize(num_vars, 0);
470 var_down_locks_.resize(num_vars, 0);
471 for (
int i = 0; i < num_vars; ++i) {
474 const bool constraint_upper_bounded =
477 const bool constraint_lower_bounded =
480 if (entry.coefficient() > 0) {
481 var_up_locks_[i] += constraint_upper_bounded;
482 var_down_locks_[i] += constraint_lower_bounded;
484 var_up_locks_[i] += constraint_lower_bounded;
485 var_down_locks_[i] += constraint_upper_bounded;
491 for (
int i = 0; i < lp_solution_.size(); ++i) {
492 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1 ||
493 var_up_locks_[i] == var_down_locks_[i]) {
494 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
495 }
else if (var_up_locks_[i] > var_down_locks_[i]) {
496 integer_solution_[i] =
static_cast<int64_t
>(std::floor(lp_solution_[i]));
498 integer_solution_[i] =
static_cast<int64_t
>(std::ceil(lp_solution_[i]));
501 integer_solution_is_set_ =
true;
505 bool FeasibilityPump::ActiveLockBasedRounding() {
506 if (!lp_solution_is_set_)
return false;
507 const int num_vars = integer_variables_.size();
512 for (
int i = 0; i < num_vars; ++i) {
513 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1) {
514 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
522 if (row_status == ConstraintStatus::AT_LOWER_BOUND) {
523 if (entry.coefficient() > 0) {
528 }
else if (row_status == ConstraintStatus::AT_UPPER_BOUND) {
529 if (entry.coefficient() > 0) {
536 if (up_locks == down_locks) {
537 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
538 }
else if (up_locks > down_locks) {
539 integer_solution_[i] =
static_cast<int64_t
>(std::floor(lp_solution_[i]));
541 integer_solution_[i] =
static_cast<int64_t
>(std::ceil(lp_solution_[i]));
545 integer_solution_is_set_ =
true;
549 bool FeasibilityPump::PropagationRounding() {
550 if (!lp_solution_is_set_)
return false;
554 std::vector<int> rounding_order;
556 std::vector<std::pair<double, int>> binary_fractionality_vars;
557 std::vector<std::pair<double, int>> general_fractionality_vars;
558 for (
int i = 0; i < lp_solution_.size(); ++i) {
559 const double fractionality =
560 std::abs(std::round(lp_solution_[i]) - lp_solution_[i]);
561 if (var_is_binary_[i]) {
562 binary_fractionality_vars.push_back({fractionality, i});
564 general_fractionality_vars.push_back({fractionality, i});
567 std::sort(binary_fractionality_vars.begin(),
568 binary_fractionality_vars.end());
569 std::sort(general_fractionality_vars.begin(),
570 general_fractionality_vars.end());
572 for (
int i = 0; i < binary_fractionality_vars.size(); ++i) {
573 rounding_order.push_back(binary_fractionality_vars[i].second);
575 for (
int i = 0; i < general_fractionality_vars.size(); ++i) {
576 rounding_order.push_back(general_fractionality_vars[i].second);
580 for (
const int var_index : rounding_order) {
583 const IntegerVariable
var = integer_variables_[var_index];
584 const Domain& domain = (*domains_)[
var];
589 integer_solution_[var_index] = lb.value();
593 const int64_t rounded_value =
594 static_cast<int64_t
>(std::round(lp_solution_[var_index]));
595 const int64_t floor_value =
596 static_cast<int64_t
>(std::floor(lp_solution_[var_index]));
597 const int64_t ceil_value =
598 static_cast<int64_t
>(std::ceil(lp_solution_[var_index]));
600 const bool floor_is_in_domain =
601 (domain.Contains(floor_value) && lb.value() <= floor_value);
602 const bool ceil_is_in_domain =
603 (domain.Contains(ceil_value) && ub.value() >= ceil_value);
604 if (domain.IsEmpty()) {
605 integer_solution_[var_index] = rounded_value;
606 model_is_unsat_ =
true;
610 if (ceil_value < lb.value()) {
611 integer_solution_[var_index] = lb.value();
612 }
else if (floor_value > ub.value()) {
613 integer_solution_[var_index] = ub.value();
614 }
else if (ceil_is_in_domain && floor_is_in_domain) {
615 DCHECK(domain.Contains(rounded_value));
616 integer_solution_[var_index] = rounded_value;
617 }
else if (ceil_is_in_domain) {
618 integer_solution_[var_index] = ceil_value;
619 }
else if (floor_is_in_domain) {
620 integer_solution_[var_index] = floor_value;
622 const std::pair<IntegerLiteral, IntegerLiteral> values_in_domain =
625 const int64_t lower_value = values_in_domain.first.bound.value();
626 const int64_t higher_value = -values_in_domain.second.bound.value();
627 const int64_t distance_from_lower_value =
628 std::abs(lower_value - rounded_value);
629 const int64_t distance_from_higher_value =
630 std::abs(higher_value - rounded_value);
632 integer_solution_[var_index] =
633 (distance_from_lower_value < distance_from_higher_value)
638 CHECK(domain.Contains(integer_solution_[var_index]));
639 CHECK_GE(integer_solution_[var_index], lb);
640 CHECK_LE(integer_solution_[var_index], ub);
649 const IntegerValue
value(integer_solution_[var_index]);
653 }
else if (
value == ub) {
662 model_is_unsat_ =
true;
668 model_is_unsat_ =
true;
673 integer_solution_is_set_ =
true;
677 void FeasibilityPump::FillIntegerSolutionStats() {
679 integer_solution_objective_ = 0;
680 for (
const auto& term : integer_objective_) {
681 integer_solution_objective_ +=
682 integer_solution_[term.first.value()] * term.second.value();
685 integer_solution_is_feasible_ =
true;
686 num_infeasible_constraints_ = 0;
687 integer_solution_infeasibility_ = 0;
688 for (RowIndex i(0); i < integer_lp_.size(); ++i) {
689 int64_t activity = 0;
690 for (
const auto& term : integer_lp_[i].terms) {
692 CapProd(integer_solution_[term.first.value()], term.second.value());
698 activity =
CapAdd(activity, prod);
703 if (activity > integer_lp_[i].ub || activity < integer_lp_[i].lb) {
704 integer_solution_is_feasible_ =
false;
705 num_infeasible_constraints_++;
706 const int64_t ub_infeasibility =
707 activity > integer_lp_[i].ub.value()
708 ? activity - integer_lp_[i].ub.value()
710 const int64_t lb_infeasibility =
711 activity < integer_lp_[i].lb.value()
712 ? integer_lp_[i].lb.value() - activity
714 integer_solution_infeasibility_ =
715 std::max(integer_solution_infeasibility_,
716 std::max(ub_infeasibility, lb_infeasibility));
#define CHECK_EQ(val1, val2)
#define CHECK_GE(val1, val2)
#define CHECK_GT(val1, val2)
#define DCHECK(condition)
#define CHECK_LE(val1, val2)
#define VLOG(verboselevel)
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.
int EnqueueDecisionAndBacktrackOnConflict(Literal true_literal)
bool IsModelUnsat() const
void AddNewSolution(const std::vector< double > &lp_solution)
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
std::vector< ColIndex > ColIndexVector
IntType IntTypeAbs(IntType t)
IntegerVariable PositiveVariable(IntegerVariable i)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
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)