24#include "absl/container/flat_hash_map.h"
25#include "absl/meta/type_traits.h"
29#include "ortools/glop/parameters.pb.h"
41#include "ortools/sat/sat_parameters.pb.h"
57const double FeasibilityPump::kCpEpsilon = 1e-4;
60 : sat_parameters_(*(
model->GetOrCreate<SatParameters>())),
82 VLOG(1) <<
"Feasibility Pump Total number of simplex iterations: "
83 << total_num_simplex_iterations_;
88 for (
const IntegerVariable
var :
ct.vars) {
92 integer_lp_.
push_back(LinearConstraintInternal());
93 LinearConstraintInternal& new_ct = integer_lp_.
back();
96 const int size =
ct.vars.size();
98 for (
int i = 0; i < size; ++i) {
100 IntegerVariable
var =
ct.vars[i];
101 IntegerValue
coeff =
ct.coeffs[i];
106 new_ct.terms.push_back({GetOrCreateMirrorVariable(
var),
coeff});
109 std::sort(new_ct.terms.begin(), new_ct.terms.end());
113 IntegerValue
coeff) {
114 objective_is_defined_ =
true;
115 const IntegerVariable pos_var =
119 const auto it = mirror_lp_variable_.find(pos_var);
120 if (it == mirror_lp_variable_.end())
return;
121 const ColIndex
col = it->second;
122 integer_objective_.push_back({
col,
coeff});
123 objective_infinity_norm_ =
127ColIndex FeasibilityPump::GetOrCreateMirrorVariable(
128 IntegerVariable positive_variable) {
131 const auto it = mirror_lp_variable_.find(positive_variable);
132 if (it == mirror_lp_variable_.end()) {
133 const int model_var =
135 model_vars_size_ =
std::max(model_vars_size_, model_var + 1);
137 const ColIndex
col(integer_variables_.size());
138 mirror_lp_variable_[positive_variable] =
col;
139 integer_variables_.push_back(positive_variable);
140 var_is_binary_.push_back(
false);
141 lp_solution_.push_back(std::numeric_limits<double>::infinity());
142 integer_solution_.push_back(0);
149void FeasibilityPump::PrintStats() {
150 if (lp_solution_is_set_) {
151 VLOG(2) <<
"Fractionality: " << lp_solution_fractionality_;
153 VLOG(2) <<
"Fractionality: NA";
157 if (integer_solution_is_set_) {
158 VLOG(2) <<
"#Infeasible const: " << num_infeasible_constraints_;
159 VLOG(2) <<
"Infeasibility: " << integer_solution_infeasibility_;
161 VLOG(2) <<
"Infeasibility: NA";
167 InitializeWorkingLP();
169 UpdateBoundsOfLpVariables();
170 lp_solution_is_set_ =
false;
171 integer_solution_is_set_ =
false;
177 for (
const auto& term : integer_objective_) {
181 mixing_factor_ = 1.0;
182 for (
int i = 0; i < max_fp_iterations_; ++i) {
184 L1DistanceMinimize();
185 if (!SolveLp())
break;
186 if (lp_solution_is_integer_)
break;
190 if (integer_solution_is_feasible_) MaybePushToRepo();
193 if (model_is_unsat_)
return false;
200void FeasibilityPump::MaybePushToRepo() {
201 if (incomplete_solutions_ ==
nullptr)
return;
203 std::vector<double> lp_solution(model_vars_size_,
204 std::numeric_limits<double>::infinity());
206 if (lp_solution_is_integer_) {
208 for (
const IntegerVariable positive_var : integer_variables_) {
209 const int model_var =
211 if (model_var >= 0 && model_var < model_vars_size_) {
218 if (integer_solution_is_feasible_) {
220 for (
const IntegerVariable positive_var : integer_variables_) {
221 const int model_var =
223 if (model_var >= 0 && model_var < model_vars_size_) {
235void FeasibilityPump::InitializeWorkingLP() {
238 for (
int i = 0; i < integer_variables_.size(); ++i) {
245 for (
const LinearConstraintInternal&
ct : integer_lp_) {
248 for (
const auto& term :
ct.terms) {
254 for (
const auto& term : integer_objective_) {
258 const int num_vars = integer_variables_.size();
259 for (
int i = 0; i < num_vars; i++) {
260 const IntegerVariable cp_var = integer_variables_[i];
266 objective_normalization_factor_ = 0.0;
271 if (!var_is_binary_[
col.value()]) {
272 integer_variables.push_back(
col);
277 objective_normalization_factor_ +=
281 objective_normalization_factor_ =
284 if (!integer_variables.empty()) {
286 norm_variables_.
assign(num_cols, ColIndex(-1));
287 norm_lhs_constraints_.
assign(num_cols, RowIndex(-1));
288 norm_rhs_constraints_.
assign(num_cols, RowIndex(-1));
305 for (
const ColIndex
col : integer_variables) {
307 norm_variables_[
col] = norm_variable;
310 norm_lhs_constraints_[
col] = row_a;
314 norm_rhs_constraints_[
col] = row_b;
320 scaler_.
Scale(&lp_data_);
325void FeasibilityPump::L1DistanceMinimize() {
326 std::vector<double> new_obj_coeffs(lp_data_.
num_variables().value(), 0.0);
331 for (ColIndex
col(0);
col < num_cols; ++
col) {
332 new_obj_coeffs[
col.value()] =
339 if (var_is_binary_[
col.value()]) {
342 (1 - mixing_factor_) * objective_normalization_factor_ *
343 (1 - 2 * integer_solution_[
col.value()]);
344 new_obj_coeffs[
col.value()] = objective_coefficient;
356 (1 - mixing_factor_) * objective_normalization_factor_;
357 new_obj_coeffs[norm_variables_[
col].value()] = objective_coefficient;
361 const ColIndex norm_lhs_slack_variable =
363 const double lhs_scaling_factor =
367 lhs_scaling_factor * integer_solution_[
col.value()]);
368 const ColIndex norm_rhs_slack_variable =
370 const double rhs_scaling_factor =
374 -rhs_scaling_factor * integer_solution_[
col.value()]);
381 mixing_factor_ *= 0.8;
384bool FeasibilityPump::SolveLp() {
385 const int num_vars = integer_variables_.size();
388 const auto status = simplex_.
Solve(lp_data_, time_limit_);
391 VLOG(1) <<
"The LP solver encountered an error: " <<
status.error_message();
404 lp_solution_fractionality_ = 0.0;
409 lp_solution_is_set_ =
true;
410 for (
int i = 0; i < num_vars; i++) {
411 const double value = GetVariableValueAtCpScale(ColIndex(i));
412 lp_solution_[i] =
value;
413 lp_solution_fractionality_ =
std::max(
414 lp_solution_fractionality_, std::abs(
value - std::round(
value)));
419 for (
const auto& term : integer_objective_) {
420 lp_objective_ += lp_solution_[term.first.value()] * term.second.value();
422 lp_solution_is_integer_ = lp_solution_fractionality_ < kCpEpsilon;
427void FeasibilityPump::UpdateBoundsOfLpVariables() {
428 const int num_vars = integer_variables_.size();
429 for (
int i = 0; i < num_vars; i++) {
430 const IntegerVariable cp_var = integer_variables_[i];
439 return lp_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable).value()];
442double FeasibilityPump::GetVariableValueAtCpScale(ColIndex
var) {
451 IntegerVariable variable)
const {
452 return integer_solution_[
gtl::FindOrDie(mirror_lp_variable_, variable)
456bool FeasibilityPump::Round() {
457 bool rounding_successful =
true;
458 if (sat_parameters_.fp_rounding() == SatParameters::NEAREST_INTEGER) {
459 rounding_successful = NearestIntegerRounding();
460 }
else if (sat_parameters_.fp_rounding() == SatParameters::LOCK_BASED) {
461 rounding_successful = LockBasedRounding();
462 }
else if (sat_parameters_.fp_rounding() ==
463 SatParameters::ACTIVE_LOCK_BASED) {
464 rounding_successful = ActiveLockBasedRounding();
465 }
else if (sat_parameters_.fp_rounding() ==
466 SatParameters::PROPAGATION_ASSISTED) {
467 rounding_successful = PropagationRounding();
469 if (!rounding_successful)
return false;
470 FillIntegerSolutionStats();
474bool FeasibilityPump::NearestIntegerRounding() {
475 if (!lp_solution_is_set_)
return false;
476 for (
int i = 0; i < lp_solution_.size(); ++i) {
477 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
479 integer_solution_is_set_ =
true;
483bool FeasibilityPump::LockBasedRounding() {
484 if (!lp_solution_is_set_)
return false;
485 const int num_vars = integer_variables_.size();
489 if (var_up_locks_.empty()) {
490 var_up_locks_.resize(num_vars, 0);
491 var_down_locks_.resize(num_vars, 0);
492 for (
int i = 0; i < num_vars; ++i) {
495 const bool constraint_upper_bounded =
498 const bool constraint_lower_bounded =
501 if (entry.coefficient() > 0) {
502 var_up_locks_[i] += constraint_upper_bounded;
503 var_down_locks_[i] += constraint_lower_bounded;
505 var_up_locks_[i] += constraint_lower_bounded;
506 var_down_locks_[i] += constraint_upper_bounded;
512 for (
int i = 0; i < lp_solution_.size(); ++i) {
513 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1 ||
514 var_up_locks_[i] == var_down_locks_[i]) {
515 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
516 }
else if (var_up_locks_[i] > var_down_locks_[i]) {
517 integer_solution_[i] =
static_cast<int64_t
>(std::floor(lp_solution_[i]));
519 integer_solution_[i] =
static_cast<int64_t
>(std::ceil(lp_solution_[i]));
522 integer_solution_is_set_ =
true;
526bool FeasibilityPump::ActiveLockBasedRounding() {
527 if (!lp_solution_is_set_)
return false;
528 const int num_vars = integer_variables_.size();
533 for (
int i = 0; i < num_vars; ++i) {
534 if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1) {
535 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
543 if (row_status == ConstraintStatus::AT_LOWER_BOUND) {
544 if (entry.coefficient() > 0) {
549 }
else if (row_status == ConstraintStatus::AT_UPPER_BOUND) {
550 if (entry.coefficient() > 0) {
557 if (up_locks == down_locks) {
558 integer_solution_[i] =
static_cast<int64_t
>(std::round(lp_solution_[i]));
559 }
else if (up_locks > down_locks) {
560 integer_solution_[i] =
static_cast<int64_t
>(std::floor(lp_solution_[i]));
562 integer_solution_[i] =
static_cast<int64_t
>(std::ceil(lp_solution_[i]));
566 integer_solution_is_set_ =
true;
570bool FeasibilityPump::PropagationRounding() {
571 if (!lp_solution_is_set_)
return false;
575 std::vector<int> rounding_order;
577 std::vector<std::pair<double, int>> binary_fractionality_vars;
578 std::vector<std::pair<double, int>> general_fractionality_vars;
579 for (
int i = 0; i < lp_solution_.size(); ++i) {
580 const double fractionality =
581 std::abs(std::round(lp_solution_[i]) - lp_solution_[i]);
582 if (var_is_binary_[i]) {
583 binary_fractionality_vars.push_back({fractionality, i});
585 general_fractionality_vars.push_back({fractionality, i});
588 std::sort(binary_fractionality_vars.begin(),
589 binary_fractionality_vars.end());
590 std::sort(general_fractionality_vars.begin(),
591 general_fractionality_vars.end());
593 for (
int i = 0; i < binary_fractionality_vars.size(); ++i) {
594 rounding_order.push_back(binary_fractionality_vars[i].second);
596 for (
int i = 0; i < general_fractionality_vars.size(); ++i) {
597 rounding_order.push_back(general_fractionality_vars[i].second);
601 for (
const int var_index : rounding_order) {
604 const IntegerVariable
var = integer_variables_[var_index];
605 const Domain& domain = (*domains_)[
var];
610 integer_solution_[var_index] = lb.value();
614 const int64_t rounded_value =
615 static_cast<int64_t
>(std::round(lp_solution_[var_index]));
616 const int64_t floor_value =
617 static_cast<int64_t
>(std::floor(lp_solution_[var_index]));
618 const int64_t ceil_value =
619 static_cast<int64_t
>(std::ceil(lp_solution_[var_index]));
621 const bool floor_is_in_domain =
622 (domain.Contains(floor_value) && lb.value() <= floor_value);
623 const bool ceil_is_in_domain =
624 (domain.Contains(ceil_value) && ub.value() >= ceil_value);
625 if (domain.IsEmpty()) {
626 integer_solution_[var_index] = rounded_value;
627 model_is_unsat_ =
true;
631 if (ceil_value < lb.value()) {
632 integer_solution_[var_index] = lb.value();
633 }
else if (floor_value > ub.value()) {
634 integer_solution_[var_index] = ub.value();
635 }
else if (ceil_is_in_domain && floor_is_in_domain) {
636 DCHECK(domain.Contains(rounded_value));
637 integer_solution_[var_index] = rounded_value;
638 }
else if (ceil_is_in_domain) {
639 integer_solution_[var_index] = ceil_value;
640 }
else if (floor_is_in_domain) {
641 integer_solution_[var_index] = floor_value;
643 const std::pair<IntegerLiteral, IntegerLiteral> values_in_domain =
646 const int64_t lower_value = values_in_domain.first.bound.value();
647 const int64_t higher_value = -values_in_domain.second.bound.value();
648 const int64_t distance_from_lower_value =
649 std::abs(lower_value - rounded_value);
650 const int64_t distance_from_higher_value =
651 std::abs(higher_value - rounded_value);
653 integer_solution_[var_index] =
654 (distance_from_lower_value < distance_from_higher_value)
659 CHECK(domain.Contains(integer_solution_[var_index]));
660 CHECK_GE(integer_solution_[var_index], lb);
661 CHECK_LE(integer_solution_[var_index], ub);
670 const IntegerValue
value(integer_solution_[var_index]);
674 }
else if (
value == ub) {
683 model_is_unsat_ =
true;
689 model_is_unsat_ =
true;
694 integer_solution_is_set_ =
true;
698void FeasibilityPump::FillIntegerSolutionStats() {
700 integer_solution_objective_ = 0;
701 for (
const auto& term : integer_objective_) {
702 integer_solution_objective_ +=
703 integer_solution_[term.first.value()] * term.second.value();
706 integer_solution_is_feasible_ =
true;
707 num_infeasible_constraints_ = 0;
708 integer_solution_infeasibility_ = 0;
709 for (RowIndex i(0); i < integer_lp_.size(); ++i) {
710 int64_t activity = 0;
711 for (
const auto& term : integer_lp_[i].terms) {
713 CapProd(integer_solution_[term.first.value()], term.second.value());
719 activity =
CapAdd(activity, prod);
724 if (activity > integer_lp_[i].ub || activity < integer_lp_[i].lb) {
725 integer_solution_is_feasible_ =
false;
726 num_infeasible_constraints_++;
727 const int64_t ub_infeasibility =
728 activity > integer_lp_[i].ub.value()
729 ? activity - integer_lp_[i].ub.value()
731 const int64_t lb_infeasibility =
732 activity < integer_lp_[i].lb.value()
733 ? integer_lp_[i].lb.value() - activity
735 integer_solution_infeasibility_ =
736 std::max(integer_solution_infeasibility_,
737 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.
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)