OR-Tools  9.0
feasibility_pump.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
15 
16 #include <cstdint>
17 #include <limits>
18 #include <vector>
19 
23 #include "ortools/sat/integer.h"
24 #include "ortools/sat/sat_base.h"
25 #include "ortools/sat/sat_solver.h"
27 
28 namespace operations_research {
29 namespace sat {
30 
31 using glop::ColIndex;
33 using glop::Fractional;
34 using glop::RowIndex;
35 
36 const double FeasibilityPump::kCpEpsilon = 1e-4;
37 
39  : sat_parameters_(*(model->GetOrCreate<SatParameters>())),
40  time_limit_(model->GetOrCreate<TimeLimit>()),
41  integer_trail_(model->GetOrCreate<IntegerTrail>()),
42  trail_(model->GetOrCreate<Trail>()),
43  integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
44  incomplete_solutions_(model->Mutable<SharedIncompleteSolutionManager>()),
45  sat_solver_(model->GetOrCreate<SatSolver>()),
46  domains_(model->GetOrCreate<IntegerDomains>()),
47  mapping_(model->Get<CpModelMapping>()) {
48  // Tweak the default parameters to make the solve incremental.
49  glop::GlopParameters parameters;
50  // Note(user): Primal simplex does better here since we have a limit on
51  // simplex iterations. So dual simplex sometimes fails to find a LP feasible
52  // solution.
53  parameters.set_use_dual_simplex(false);
54  parameters.set_max_number_of_iterations(2000);
55  simplex_.SetParameters(parameters);
56  lp_data_.Clear();
57  integer_lp_.clear();
58 }
59 
61  VLOG(1) << "Feasibility Pump Total number of simplex iterations: "
62  << total_num_simplex_iterations_;
63 }
64 
66  // We still create the mirror variable right away though.
67  for (const IntegerVariable var : ct.vars) {
68  GetOrCreateMirrorVariable(PositiveVariable(var));
69  }
70 
71  integer_lp_.push_back(LinearConstraintInternal());
72  LinearConstraintInternal& new_ct = integer_lp_.back();
73  new_ct.lb = ct.lb;
74  new_ct.ub = ct.ub;
75  const int size = ct.vars.size();
76  CHECK_LE(ct.lb, ct.ub);
77  for (int i = 0; i < size; ++i) {
78  // We only use positive variable inside this class.
79  IntegerVariable var = ct.vars[i];
80  IntegerValue coeff = ct.coeffs[i];
81  if (!VariableIsPositive(var)) {
82  var = NegationOf(var);
83  coeff = -coeff;
84  }
85  new_ct.terms.push_back({GetOrCreateMirrorVariable(var), coeff});
86  }
87  // Important to keep lp_data_ "clean".
88  std::sort(new_ct.terms.begin(), new_ct.terms.end());
89 }
90 
91 void FeasibilityPump::SetObjectiveCoefficient(IntegerVariable ivar,
92  IntegerValue coeff) {
93  objective_is_defined_ = true;
94  const IntegerVariable pos_var =
95  VariableIsPositive(ivar) ? ivar : NegationOf(ivar);
96  if (ivar != pos_var) coeff = -coeff;
97 
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_ =
103  std::max(objective_infinity_norm_, IntTypeAbs(coeff));
104 }
105 
106 ColIndex FeasibilityPump::GetOrCreateMirrorVariable(
107  IntegerVariable positive_variable) {
108  DCHECK(VariableIsPositive(positive_variable));
109 
110  const auto it = mirror_lp_variable_.find(positive_variable);
111  if (it == mirror_lp_variable_.end()) {
112  const int model_var =
113  mapping_->GetProtoVariableFromIntegerVariable(positive_variable);
114  model_vars_size_ = std::max(model_vars_size_, model_var + 1);
115 
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);
122 
123  return col;
124  }
125  return it->second;
126 }
127 
128 void FeasibilityPump::PrintStats() {
129  if (lp_solution_is_set_) {
130  VLOG(2) << "Fractionality: " << lp_solution_fractionality_;
131  } else {
132  VLOG(2) << "Fractionality: NA";
133  VLOG(2) << "simplex status: " << simplex_.GetProblemStatus();
134  }
135 
136  if (integer_solution_is_set_) {
137  VLOG(2) << "#Infeasible const: " << num_infeasible_constraints_;
138  VLOG(2) << "Infeasibility: " << integer_solution_infeasibility_;
139  } else {
140  VLOG(2) << "Infeasibility: NA";
141  }
142 }
143 
145  if (lp_data_.num_variables() == 0) {
146  InitializeWorkingLP();
147  }
148  UpdateBoundsOfLpVariables();
149  lp_solution_is_set_ = false;
150  integer_solution_is_set_ = false;
151 
152  // Restore the original objective
153  for (ColIndex col(0); col < lp_data_.num_variables(); ++col) {
154  lp_data_.SetObjectiveCoefficient(col, 0.0);
155  }
156  for (const auto& term : integer_objective_) {
157  lp_data_.SetObjectiveCoefficient(term.first, ToDouble(term.second));
158  }
159 
160  mixing_factor_ = 1.0;
161  for (int i = 0; i < max_fp_iterations_; ++i) {
162  if (time_limit_->LimitReached()) break;
163  L1DistanceMinimize();
164  if (!SolveLp()) break;
165  if (lp_solution_is_integer_) break;
166  if (!Round()) break;
167  // We don't end this loop if the integer solutions is feasible in hope to
168  // get better solution.
169  if (integer_solution_is_feasible_) MaybePushToRepo();
170  }
171 
172  if (model_is_unsat_) return false;
173 
174  PrintStats();
175  MaybePushToRepo();
176  return true;
177 }
178 
179 void FeasibilityPump::MaybePushToRepo() {
180  if (incomplete_solutions_ == nullptr) return;
181 
182  std::vector<double> lp_solution(model_vars_size_,
183  std::numeric_limits<double>::infinity());
184  // TODO(user): Consider adding solutions that have low fractionality.
185  if (lp_solution_is_integer_) {
186  // Fill the solution using LP solution values.
187  for (const IntegerVariable positive_var : integer_variables_) {
188  const int model_var =
189  mapping_->GetProtoVariableFromIntegerVariable(positive_var);
190  if (model_var >= 0 && model_var < model_vars_size_) {
191  lp_solution[model_var] = GetLPSolutionValue(positive_var);
192  }
193  }
194  incomplete_solutions_->AddNewSolution(lp_solution);
195  }
196 
197  if (integer_solution_is_feasible_) {
198  // Fill the solution using Integer solution values.
199  for (const IntegerVariable positive_var : integer_variables_) {
200  const int model_var =
201  mapping_->GetProtoVariableFromIntegerVariable(positive_var);
202  if (model_var >= 0 && model_var < model_vars_size_) {
203  lp_solution[model_var] = GetIntegerSolutionValue(positive_var);
204  }
205  }
206  incomplete_solutions_->AddNewSolution(lp_solution);
207  }
208 }
209 
210 // ----------------------------------------------------------------
211 // -------------------LPSolving------------------------------------
212 // ----------------------------------------------------------------
213 
214 void FeasibilityPump::InitializeWorkingLP() {
215  lp_data_.Clear();
216  // Create variables.
217  for (int i = 0; i < integer_variables_.size(); ++i) {
218  CHECK_EQ(ColIndex(i), lp_data_.CreateNewVariable());
219  lp_data_.SetVariableType(ColIndex(i),
221  }
222 
223  // Add constraints.
224  for (const LinearConstraintInternal& ct : integer_lp_) {
225  const ConstraintIndex row = lp_data_.CreateNewConstraint();
226  lp_data_.SetConstraintBounds(row, ToDouble(ct.lb), ToDouble(ct.ub));
227  for (const auto& term : ct.terms) {
228  lp_data_.SetCoefficient(row, term.first, ToDouble(term.second));
229  }
230  }
231 
232  // Add objective.
233  for (const auto& term : integer_objective_) {
234  lp_data_.SetObjectiveCoefficient(term.first, ToDouble(term.second));
235  }
236 
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];
240  const double lb = ToDouble(integer_trail_->LevelZeroLowerBound(cp_var));
241  const double ub = ToDouble(integer_trail_->LevelZeroUpperBound(cp_var));
242  lp_data_.SetVariableBounds(ColIndex(i), lb, ub);
243  }
244 
245  objective_normalization_factor_ = 0.0;
246  glop::ColIndexVector integer_variables;
247  const ColIndex num_cols = lp_data_.num_variables();
248  for (ColIndex col : lp_data_.IntegerVariablesList()) {
249  var_is_binary_[col.value()] = lp_data_.IsVariableBinary(col);
250  if (!var_is_binary_[col.value()]) {
251  integer_variables.push_back(col);
252  }
253 
254  // The aim of this normalization value is to compute a coefficient of the
255  // d_i variables that should be minimized.
256  objective_normalization_factor_ +=
258  }
259  CHECK_GT(lp_data_.IntegerVariablesList().size(), 0);
260  objective_normalization_factor_ =
261  objective_normalization_factor_ / lp_data_.IntegerVariablesList().size();
262 
263  if (!integer_variables.empty()) {
264  // Update the LpProblem with norm variables and constraints.
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));
268  // For each integer non-binary variable x_i we introduce one new variable
269  // d_i subject to two new constraints:
270  // d_i - x_i >= -round(x'_i)
271  // d_i + x_i >= +round(x'_i)
272  // That's round(x'_i) - d_i <= x_i <= round(x'_i) + d_i, where d_i is an
273  // unbounded non-negative, and x'_i is the value of variable i from the
274  // previous solution obtained during the projection step. Consequently
275  // coefficients of the constraints are set here, but bounds of the
276  // constraints are updated at each iteration of the feasibility pump. Also
277  // coefficients of the objective are set here: x_i's are not present in the
278  // objective (i.e., coefficients set to 0.0), and d_i's are present in the
279  // objective with coefficients set to 1.0.
280  // Note that the treatment of integer non-binary variables is different
281  // from the treatment of binary variables. Binary variables do not impose
282  // any extra variables, nor extra constraints, but their objective
283  // coefficients are changed in the linear projection steps.
284  for (const ColIndex col : integer_variables) {
285  const ColIndex norm_variable = lp_data_.CreateNewVariable();
286  norm_variables_[col] = norm_variable;
287  lp_data_.SetVariableBounds(norm_variable, 0.0, glop::kInfinity);
288  const RowIndex row_a = lp_data_.CreateNewConstraint();
289  norm_lhs_constraints_[col] = row_a;
290  lp_data_.SetCoefficient(row_a, norm_variable, 1.0);
291  lp_data_.SetCoefficient(row_a, col, -1.0);
292  const RowIndex row_b = lp_data_.CreateNewConstraint();
293  norm_rhs_constraints_[col] = row_b;
294  lp_data_.SetCoefficient(row_b, norm_variable, 1.0);
295  lp_data_.SetCoefficient(row_b, col, 1.0);
296  }
297  }
298 
299  scaler_.Scale(&lp_data_);
301  /*detect_integer_constraints=*/false);
302 }
303 
304 void FeasibilityPump::L1DistanceMinimize() {
305  std::vector<double> new_obj_coeffs(lp_data_.num_variables().value(), 0.0);
306 
307  // Set the original subobjective. The coefficients are scaled by mixing factor
308  // and the offset remains at 0 (because it does not affect the solution).
309  const ColIndex num_cols(lp_data_.objective_coefficients().size());
310  for (ColIndex col(0); col < num_cols; ++col) {
311  new_obj_coeffs[col.value()] =
312  mixing_factor_ * lp_data_.objective_coefficients()[col];
313  }
314 
315  // Set the norm subobjective. The coefficients are scaled by 1 - mixing factor
316  // and the offset remains at 0 (because it does not affect the solution).
317  for (const ColIndex col : lp_data_.IntegerVariablesList()) {
318  if (var_is_binary_[col.value()]) {
319  const Fractional objective_coefficient =
320  mixing_factor_ * lp_data_.objective_coefficients()[col] +
321  (1 - mixing_factor_) * objective_normalization_factor_ *
322  (1 - 2 * integer_solution_[col.value()]);
323  new_obj_coeffs[col.value()] = objective_coefficient;
324  } else { // The variable is integer.
325  // Update the bounds of the constraints added in
326  // InitializeIntegerVariables() (see there for more details):
327  // d_i - x_i >= -round(x'_i)
328  // d_i + x_i >= +round(x'_i)
329 
330  // TODO(user): We change both the objective and the bounds, thus
331  // breaking the incrementality. Handle integer variables differently,
332  // e.g., intensify rounding, or use soft fixing from: Fischetti, Lodi,
333  // "Local Branching", Math Program Ser B 98:23-47 (2003).
334  const Fractional objective_coefficient =
335  (1 - mixing_factor_) * objective_normalization_factor_;
336  new_obj_coeffs[norm_variables_[col].value()] = objective_coefficient;
337  // At this point, constraint bounds have already been transformed into
338  // bounds of slack variables. Instead of updating the constraints, we need
339  // to update the slack variables corresponding to them.
340  const ColIndex norm_lhs_slack_variable =
341  lp_data_.GetSlackVariable(norm_lhs_constraints_[col]);
342  const double lhs_scaling_factor =
343  scaler_.VariableScalingFactor(norm_lhs_slack_variable);
344  lp_data_.SetVariableBounds(
345  norm_lhs_slack_variable, -glop::kInfinity,
346  lhs_scaling_factor * integer_solution_[col.value()]);
347  const ColIndex norm_rhs_slack_variable =
348  lp_data_.GetSlackVariable(norm_rhs_constraints_[col]);
349  const double rhs_scaling_factor =
350  scaler_.VariableScalingFactor(norm_rhs_slack_variable);
351  lp_data_.SetVariableBounds(
352  norm_rhs_slack_variable, -glop::kInfinity,
353  -rhs_scaling_factor * integer_solution_[col.value()]);
354  }
355  }
356  for (ColIndex col(0); col < lp_data_.num_variables(); ++col) {
357  lp_data_.SetObjectiveCoefficient(col, new_obj_coeffs[col.value()]);
358  }
359  // TODO(user): Tune this or expose as parameter.
360  mixing_factor_ *= 0.8;
361 }
362 
363 bool FeasibilityPump::SolveLp() {
364  const int num_vars = integer_variables_.size();
365  VLOG(3) << "LP relaxation: " << lp_data_.GetDimensionString() << ".";
366 
367  const auto status = simplex_.Solve(lp_data_, time_limit_);
368  total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
369  if (!status.ok()) {
370  VLOG(1) << "The LP solver encountered an error: " << status.error_message();
371  simplex_.ClearStateForNextSolve();
372  return false;
373  }
374 
375  // TODO(user): This shouldn't really happen except if the problem is UNSAT.
376  // But we can't just rely on a potentially imprecise LP to close the problem.
377  // The rest of the solver should do that with exact precision.
378  VLOG(3) << "simplex status: " << simplex_.GetProblemStatus();
380  return false;
381  }
382 
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)));
394  }
395 
396  // Compute the objective value.
397  lp_objective_ = 0;
398  for (const auto& term : integer_objective_) {
399  lp_objective_ += lp_solution_[term.first.value()] * term.second.value();
400  }
401  lp_solution_is_integer_ = lp_solution_fractionality_ < kCpEpsilon;
402  }
403  return true;
404 }
405 
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];
410  const double lb = ToDouble(integer_trail_->LevelZeroLowerBound(cp_var));
411  const double ub = ToDouble(integer_trail_->LevelZeroUpperBound(cp_var));
412  const double factor = scaler_.VariableScalingFactor(ColIndex(i));
413  lp_data_.SetVariableBounds(ColIndex(i), lb * factor, ub * factor);
414  }
415 }
416 
417 double FeasibilityPump::GetLPSolutionValue(IntegerVariable variable) const {
418  return lp_solution_[gtl::FindOrDie(mirror_lp_variable_, variable).value()];
419 }
420 
421 double FeasibilityPump::GetVariableValueAtCpScale(ColIndex var) {
422  return scaler_.UnscaleVariableValue(var, simplex_.GetVariableValue(var));
423 }
424 
425 // ----------------------------------------------------------------
426 // -------------------Rounding-------------------------------------
427 // ----------------------------------------------------------------
428 
430  IntegerVariable variable) const {
431  return integer_solution_[gtl::FindOrDie(mirror_lp_variable_, variable)
432  .value()];
433 }
434 
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();
447  }
448  if (!rounding_successful) return false;
449  FillIntegerSolutionStats();
450  return true;
451 }
452 
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]));
457  }
458  integer_solution_is_set_ = true;
459  return true;
460 }
461 
462 bool FeasibilityPump::LockBasedRounding() {
463  if (!lp_solution_is_set_) return false;
464  const int num_vars = integer_variables_.size();
465 
466  // We compute the number of locks based on variable coefficient in constraints
467  // and constraint bounds. This doesn't change over time so we cache it.
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) {
472  for (const auto entry : lp_data_.GetSparseColumn(ColIndex(i))) {
473  ColIndex slack = lp_data_.GetSlackVariable(entry.row());
474  const bool constraint_upper_bounded =
475  lp_data_.variable_lower_bounds()[slack] > -glop::kInfinity;
476 
477  const bool constraint_lower_bounded =
478  lp_data_.variable_upper_bounds()[slack] < glop::kInfinity;
479 
480  if (entry.coefficient() > 0) {
481  var_up_locks_[i] += constraint_upper_bounded;
482  var_down_locks_[i] += constraint_lower_bounded;
483  } else {
484  var_up_locks_[i] += constraint_lower_bounded;
485  var_down_locks_[i] += constraint_upper_bounded;
486  }
487  }
488  }
489  }
490 
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]));
497  } else {
498  integer_solution_[i] = static_cast<int64_t>(std::ceil(lp_solution_[i]));
499  }
500  }
501  integer_solution_is_set_ = true;
502  return true;
503 }
504 
505 bool FeasibilityPump::ActiveLockBasedRounding() {
506  if (!lp_solution_is_set_) return false;
507  const int num_vars = integer_variables_.size();
508 
509  // We compute the number of locks based on variable coefficient in constraints
510  // and constraint bounds of active constraints. We consider the bound of the
511  // constraint that is tight for the current lp solution.
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]));
515  }
516 
517  int up_locks = 0;
518  int down_locks = 0;
519  for (const auto entry : lp_data_.GetSparseColumn(ColIndex(i))) {
520  const ConstraintStatus row_status =
521  simplex_.GetConstraintStatus(entry.row());
522  if (row_status == ConstraintStatus::AT_LOWER_BOUND) {
523  if (entry.coefficient() > 0) {
524  down_locks++;
525  } else {
526  up_locks++;
527  }
528  } else if (row_status == ConstraintStatus::AT_UPPER_BOUND) {
529  if (entry.coefficient() > 0) {
530  up_locks++;
531  } else {
532  down_locks++;
533  }
534  }
535  }
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]));
540  } else {
541  integer_solution_[i] = static_cast<int64_t>(std::ceil(lp_solution_[i]));
542  }
543  }
544 
545  integer_solution_is_set_ = true;
546  return true;
547 }
548 
549 bool FeasibilityPump::PropagationRounding() {
550  if (!lp_solution_is_set_) return false;
551  sat_solver_->ResetToLevelZero();
552 
553  // Compute an order in which we will fix variables and do the propagation.
554  std::vector<int> rounding_order;
555  {
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});
563  } else {
564  general_fractionality_vars.push_back({fractionality, i});
565  }
566  }
567  std::sort(binary_fractionality_vars.begin(),
568  binary_fractionality_vars.end());
569  std::sort(general_fractionality_vars.begin(),
570  general_fractionality_vars.end());
571 
572  for (int i = 0; i < binary_fractionality_vars.size(); ++i) {
573  rounding_order.push_back(binary_fractionality_vars[i].second);
574  }
575  for (int i = 0; i < general_fractionality_vars.size(); ++i) {
576  rounding_order.push_back(general_fractionality_vars[i].second);
577  }
578  }
579 
580  for (const int var_index : rounding_order) {
581  if (time_limit_->LimitReached()) return false;
582  // Get the bounds of the variable.
583  const IntegerVariable var = integer_variables_[var_index];
584  const Domain& domain = (*domains_)[var];
585 
586  const IntegerValue lb = integer_trail_->LowerBound(var);
587  const IntegerValue ub = integer_trail_->UpperBound(var);
588  if (lb == ub) {
589  integer_solution_[var_index] = lb.value();
590  continue;
591  }
592 
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]));
599 
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;
607  return false;
608  }
609 
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;
621  } else {
622  const std::pair<IntegerLiteral, IntegerLiteral> values_in_domain =
623  integer_encoder_->Canonicalize(
624  IntegerLiteral::GreaterOrEqual(var, IntegerValue(rounded_value)));
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);
631 
632  integer_solution_[var_index] =
633  (distance_from_lower_value < distance_from_higher_value)
634  ? lower_value
635  : higher_value;
636  }
637 
638  CHECK(domain.Contains(integer_solution_[var_index]));
639  CHECK_GE(integer_solution_[var_index], lb);
640  CHECK_LE(integer_solution_[var_index], ub);
641 
642  // Propagate the value.
643  //
644  // When we want to fix the variable at its lb or ub, we do not create an
645  // equality literal to minimize the number of new literal we create. This
646  // is because creating an "== value" literal will implicitly also create
647  // a ">= value" and a "<= value" literals.
648  Literal to_enqueue;
649  const IntegerValue value(integer_solution_[var_index]);
650  if (value == lb) {
651  to_enqueue = integer_encoder_->GetOrCreateAssociatedLiteral(
653  } else if (value == ub) {
654  to_enqueue = integer_encoder_->GetOrCreateAssociatedLiteral(
656  } else {
657  to_enqueue =
659  }
660 
661  if (!sat_solver_->FinishPropagation()) {
662  model_is_unsat_ = true;
663  return false;
664  }
665  sat_solver_->EnqueueDecisionAndBacktrackOnConflict(to_enqueue);
666 
667  if (sat_solver_->IsModelUnsat()) {
668  model_is_unsat_ = true;
669  return false;
670  }
671  }
672  sat_solver_->ResetToLevelZero();
673  integer_solution_is_set_ = true;
674  return true;
675 }
676 
677 void FeasibilityPump::FillIntegerSolutionStats() {
678  // Compute the objective value.
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();
683  }
684 
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) {
691  const int64_t prod =
692  CapProd(integer_solution_[term.first.value()], term.second.value());
693  if (prod <= std::numeric_limits<int64_t>::min() ||
695  activity = prod;
696  break;
697  }
698  activity = CapAdd(activity, prod);
699  if (activity <= std::numeric_limits<int64_t>::min() ||
700  activity >= std::numeric_limits<int64_t>::max())
701  break;
702  }
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()
709  : 0;
710  const int64_t lb_infeasibility =
711  activity < integer_lp_[i].lb.value()
712  ? integer_lp_[i].lb.value() - activity
713  : 0;
714  integer_solution_infeasibility_ =
715  std::max(integer_solution_infeasibility_,
716  std::max(ub_infeasibility, lb_infeasibility));
717  }
718  }
719 }
720 
721 } // namespace sat
722 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
#define CHECK(condition)
Definition: base/logging.h:498
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:705
#define CHECK_GE(val1, val2)
Definition: base/logging.h:709
#define CHECK_GT(val1, val2)
Definition: base/logging.h:710
#define DCHECK(condition)
Definition: base/logging.h:892
#define CHECK_LE(val1, val2)
Definition: base/logging.h:707
#define VLOG(verboselevel)
Definition: base/logging.h:986
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...
Definition: time_limit.h:105
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:532
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:249
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:317
ColIndex GetSlackVariable(RowIndex row) const
Definition: lp_data.cc:755
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:229
const DenseRow & objective_coefficients() const
Definition: lp_data.h:223
const std::vector< ColIndex > & IntegerVariablesList() const
Definition: lp_data.cc:280
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
Definition: lp_data.cc:419
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:309
void SetVariableType(ColIndex col, VariableType type)
Definition: lp_data.cc:236
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
Definition: lp_data.cc:697
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:326
bool IsVariableBinary(ColIndex col) const
Definition: lp_data.cc:300
const DenseRow & variable_upper_bounds() const
Definition: lp_data.h:232
std::string GetDimensionString() const
Definition: lp_data.cc:425
const SparseColumn & GetSparseColumn(ColIndex col) const
Definition: lp_data.cc:409
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)
ConstraintStatus GetConstraintStatus(RowIndex row) const
void SetParameters(const GlopParameters &parameters)
void assign(IntType size, const T &v)
Definition: lp_types.h:275
int GetProtoVariableFromIntegerVariable(IntegerVariable var) const
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)
Definition: integer.cc:250
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
Definition: integer.cc:186
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:204
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1309
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1360
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1355
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1305
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
int EnqueueDecisionAndBacktrackOnConflict(Literal true_literal)
Definition: sat_solver.cc:862
void AddNewSolution(const std::vector< double > &lp_solution)
SatParameters parameters
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
GRBmodel * model
ColIndex col
Definition: markowitz.cc:183
RowIndex row
Definition: markowitz.cc:182
const Collection::value_type::second_type & FindOrDie(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:206
std::vector< ColIndex > ColIndexVector
Definition: lp_types.h:309
const double kInfinity
Definition: lp_types.h:84
IntType IntTypeAbs(IntType t)
Definition: integer.h:78
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:139
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:29
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:135
double ToDouble(IntegerValue value)
Definition: integer.h:70
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)
Definition: integer.h:1275
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1269