36const double FeasibilityPump::kCpEpsilon = 1e-4;
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 =
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_ =
106ColIndex 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);
128void 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;
179void 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_) {
214void 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_);
304void 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;
363bool 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;
406void 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()];
421double FeasibilityPump::GetVariableValueAtCpScale(ColIndex
var) {
430 IntegerVariable variable)
const {
431 return integer_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable)
435bool FeasibilityPump::Round() {
436 bool rounding_successful =
true;
438 rounding_successful = NearestIntegerRounding();
440 rounding_successful = LockBasedRounding();
443 rounding_successful = ActiveLockBasedRounding();
446 rounding_successful = PropagationRounding();
448 if (!rounding_successful)
return false;
449 FillIntegerSolutionStats();
453bool 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;
462bool 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;
505bool 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;
549bool 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;
677void 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 std::vector< ColIndex > & IntegerVariablesList() const
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
ColIndex CreateNewVariable()
const DenseRow & variable_upper_bounds() const
void SetVariableType(ColIndex col, VariableType type)
const DenseRow & objective_coefficients() const
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
void SetObjectiveCoefficient(ColIndex col, Fractional value)
bool IsVariableBinary(ColIndex col) const
ColIndex num_variables() const
RowIndex CreateNewConstraint()
const DenseRow & variable_lower_bounds() const
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.
::operations_research::sat::SatParameters_FPRoundingMethod fp_rounding() const
static constexpr FPRoundingMethod NEAREST_INTEGER
static constexpr FPRoundingMethod PROPAGATION_ASSISTED
static constexpr FPRoundingMethod LOCK_BASED
static constexpr FPRoundingMethod ACTIVE_LOCK_BASED
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)