36 const 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 =
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;
438 rounding_successful = NearestIntegerRounding();
440 rounding_successful = LockBasedRounding();
443 rounding_successful = ActiveLockBasedRounding();
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));
bool IsVariableBinary(ColIndex col) const
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
FeasibilityPump(Model *model)
void SetObjectiveCoefficient(ColIndex col, Fractional value)
#define CHECK_GE(val1, val2)
Class that owns everything related to a particular optimization model.
void AddNewSolution(const std::vector< double > &lp_solution)
#define CHECK_GT(val1, val2)
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
ProblemStatus GetProblemStatus() const
#define VLOG(verboselevel)
Fractional GetVariableValue(ColIndex col) const
IntegerValue LowerBound(IntegerVariable i) const
void SetVariableType(ColIndex col, VariableType type)
void ClearStateForNextSolve()
int EnqueueDecisionAndBacktrackOnConflict(Literal true_literal)
int64_t CapProd(int64_t x, int64_t y)
ABSL_MUST_USE_RESULT Status Solve(const LinearProgram &lp, TimeLimit *time_limit)
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
void assign(IntType size, const T &v)
const DenseRow & objective_coefficients() const
double ToDouble(IntegerValue value)
IntegerVariable PositiveVariable(IntegerVariable i)
static constexpr FPRoundingMethod LOCK_BASED
int GetProtoVariableFromIntegerVariable(IntegerVariable var) const
::operations_research::sat::SatParameters_FPRoundingMethod fp_rounding() const
std::string GetDimensionString() const
Fractional VariableScalingFactor(ColIndex col) const
int64_t CapAdd(int64_t x, int64_t y)
Fractional UnscaleVariableValue(ColIndex col, Fractional value) const
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
static constexpr FPRoundingMethod NEAREST_INTEGER
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
void SetParameters(const GlopParameters ¶meters)
#define CHECK_LE(val1, val2)
Literal GetOrCreateLiteralAssociatedToEquality(IntegerVariable var, IntegerValue value)
std::vector< ColIndex > ColIndexVector
ColIndex CreateNewVariable()
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
void push_back(const value_type &x)
RowIndex CreateNewConstraint()
bool VariableIsPositive(IntegerVariable i)
double GetLPSolutionValue(IntegerVariable variable) const
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
ColIndex GetSlackVariable(RowIndex row) const
void AddLinearConstraint(const LinearConstraint &ct)
#define CHECK_EQ(val1, val2)
ColIndex num_variables() const
static constexpr FPRoundingMethod PROPAGATION_ASSISTED
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
#define DCHECK(condition)
int64_t GetNumberOfIterations() const
const DenseRow & variable_upper_bounds() const
ConstraintStatus GetConstraintStatus(RowIndex row) const
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
const DenseRow & variable_lower_bounds() const
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
static constexpr FPRoundingMethod ACTIVE_LOCK_BASED
IntegerValue UpperBound(IntegerVariable i) const
int64_t GetIntegerSolutionValue(IntegerVariable variable) const
bool IsModelUnsat() const
Collection of objects used to extend the Constraint Solver library.
glop::RowIndex ConstraintIndex
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
void SetObjectiveCoefficient(IntegerVariable ivar, IntegerValue coeff)
IntType IntTypeAbs(IntType t)
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
const std::vector< ColIndex > & IntegerVariablesList() const
const SparseColumn & GetSparseColumn(ColIndex col) const
void Scale(LinearProgram *lp)
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)