OR-Tools  9.6
feasibility_pump.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <algorithm>
17 #include <cmath>
18 #include <cstdint>
19 #include <cstdlib>
20 #include <limits>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/container/flat_hash_map.h"
25 #include "absl/meta/type_traits.h"
26 #include "ortools/base/logging.h"
28 #include "ortools/glop/parameters.pb.h"
30 #include "ortools/glop/status.h"
36 #include "ortools/sat/integer.h"
38 #include "ortools/sat/model.h"
39 #include "ortools/sat/sat_base.h"
40 #include "ortools/sat/sat_parameters.pb.h"
41 #include "ortools/sat/sat_solver.h"
47 
48 namespace operations_research {
49 namespace sat {
50 
51 using glop::ColIndex;
53 using glop::Fractional;
54 using glop::RowIndex;
55 
56 const double FeasibilityPump::kCpEpsilon = 1e-4;
57 
59  : sat_parameters_(*(model->GetOrCreate<SatParameters>())),
60  time_limit_(model->GetOrCreate<TimeLimit>()),
61  integer_trail_(model->GetOrCreate<IntegerTrail>()),
62  trail_(model->GetOrCreate<Trail>()),
63  integer_encoder_(model->GetOrCreate<IntegerEncoder>()),
64  incomplete_solutions_(model->Mutable<SharedIncompleteSolutionManager>()),
65  sat_solver_(model->GetOrCreate<SatSolver>()),
66  domains_(model->GetOrCreate<IntegerDomains>()),
67  mapping_(model->Get<CpModelMapping>()) {
68  // Tweak the default parameters to make the solve incremental.
69  glop::GlopParameters parameters;
70  // Note(user): Primal simplex does better here since we have a limit on
71  // simplex iterations. So dual simplex sometimes fails to find a LP feasible
72  // solution.
73  parameters.set_use_dual_simplex(false);
74  parameters.set_max_number_of_iterations(2000);
75  simplex_.SetParameters(parameters);
76  lp_data_.Clear();
77  integer_lp_.clear();
78 }
79 
81  VLOG(1) << "Feasibility Pump Total number of simplex iterations: "
82  << total_num_simplex_iterations_;
83 }
84 
86  // We still create the mirror variable right away though.
87  for (const IntegerVariable var : ct.vars) {
88  GetOrCreateMirrorVariable(PositiveVariable(var));
89  }
90 
91  integer_lp_.push_back(LinearConstraintInternal());
92  LinearConstraintInternal& new_ct = integer_lp_.back();
93  new_ct.lb = ct.lb;
94  new_ct.ub = ct.ub;
95  const int size = ct.vars.size();
96  CHECK_LE(ct.lb, ct.ub);
97  for (int i = 0; i < size; ++i) {
98  // We only use positive variable inside this class.
99  IntegerVariable var = ct.vars[i];
100  IntegerValue coeff = ct.coeffs[i];
101  if (!VariableIsPositive(var)) {
102  var = NegationOf(var);
103  coeff = -coeff;
104  }
105  new_ct.terms.push_back({GetOrCreateMirrorVariable(var), coeff});
106  }
107  // Important to keep lp_data_ "clean".
108  std::sort(new_ct.terms.begin(), new_ct.terms.end());
109 }
110 
112  IntegerValue coeff) {
113  objective_is_defined_ = true;
114  const IntegerVariable pos_var =
115  VariableIsPositive(ivar) ? ivar : NegationOf(ivar);
116  if (ivar != pos_var) coeff = -coeff;
117 
118  const auto it = mirror_lp_variable_.find(pos_var);
119  if (it == mirror_lp_variable_.end()) return;
120  const ColIndex col = it->second;
121  integer_objective_.push_back({col, coeff});
122  objective_infinity_norm_ =
123  std::max(objective_infinity_norm_, IntTypeAbs(coeff));
124 }
125 
126 ColIndex FeasibilityPump::GetOrCreateMirrorVariable(
127  IntegerVariable positive_variable) {
128  DCHECK(VariableIsPositive(positive_variable));
129 
130  const auto it = mirror_lp_variable_.find(positive_variable);
131  if (it == mirror_lp_variable_.end()) {
132  const int model_var =
133  mapping_->GetProtoVariableFromIntegerVariable(positive_variable);
134  model_vars_size_ = std::max(model_vars_size_, model_var + 1);
135 
136  const ColIndex col(integer_variables_.size());
137  mirror_lp_variable_[positive_variable] = col;
138  integer_variables_.push_back(positive_variable);
139  var_is_binary_.push_back(false);
140  lp_solution_.push_back(std::numeric_limits<double>::infinity());
141  integer_solution_.push_back(0);
142 
143  return col;
144  }
145  return it->second;
146 }
147 
148 void FeasibilityPump::PrintStats() {
149  if (lp_solution_is_set_) {
150  VLOG(2) << "Fractionality: " << lp_solution_fractionality_;
151  } else {
152  VLOG(2) << "Fractionality: NA";
153  VLOG(2) << "simplex status: " << simplex_.GetProblemStatus();
154  }
155 
156  if (integer_solution_is_set_) {
157  VLOG(2) << "#Infeasible const: " << num_infeasible_constraints_;
158  VLOG(2) << "Infeasibility: " << integer_solution_infeasibility_;
159  } else {
160  VLOG(2) << "Infeasibility: NA";
161  }
162 }
163 
165  if (lp_data_.num_variables() == 0) {
166  InitializeWorkingLP();
167  }
168  UpdateBoundsOfLpVariables();
169  lp_solution_is_set_ = false;
170  integer_solution_is_set_ = false;
171 
172  // Restore the original objective
173  for (ColIndex col(0); col < lp_data_.num_variables(); ++col) {
174  lp_data_.SetObjectiveCoefficient(col, 0.0);
175  }
176  for (const auto& term : integer_objective_) {
177  lp_data_.SetObjectiveCoefficient(term.first, ToDouble(term.second));
178  }
179 
180  mixing_factor_ = 1.0;
181  for (int i = 0; i < max_fp_iterations_; ++i) {
182  if (time_limit_->LimitReached()) break;
183  L1DistanceMinimize();
184  if (!SolveLp()) break;
185  if (lp_solution_is_integer_) break;
186  if (!Round()) break;
187  // We don't end this loop if the integer solutions is feasible in hope to
188  // get better solution.
189  if (integer_solution_is_feasible_) MaybePushToRepo();
190  }
191 
192  if (model_is_unsat_) return false;
193 
194  PrintStats();
195  MaybePushToRepo();
196  return true;
197 }
198 
199 void FeasibilityPump::MaybePushToRepo() {
200  if (incomplete_solutions_ == nullptr) return;
201 
202  std::vector<double> lp_solution(model_vars_size_,
203  std::numeric_limits<double>::infinity());
204  // TODO(user): Consider adding solutions that have low fractionality.
205  if (lp_solution_is_integer_) {
206  // Fill the solution using LP solution values.
207  for (const IntegerVariable positive_var : integer_variables_) {
208  const int model_var =
209  mapping_->GetProtoVariableFromIntegerVariable(positive_var);
210  if (model_var >= 0 && model_var < model_vars_size_) {
211  lp_solution[model_var] = GetLPSolutionValue(positive_var);
212  }
213  }
214  incomplete_solutions_->AddNewSolution(lp_solution);
215  }
216 
217  if (integer_solution_is_feasible_) {
218  // Fill the solution using Integer solution values.
219  for (const IntegerVariable positive_var : integer_variables_) {
220  const int model_var =
221  mapping_->GetProtoVariableFromIntegerVariable(positive_var);
222  if (model_var >= 0 && model_var < model_vars_size_) {
223  lp_solution[model_var] = GetIntegerSolutionValue(positive_var);
224  }
225  }
226  incomplete_solutions_->AddNewSolution(lp_solution);
227  }
228 }
229 
230 // ----------------------------------------------------------------
231 // -------------------LPSolving------------------------------------
232 // ----------------------------------------------------------------
233 
234 void FeasibilityPump::InitializeWorkingLP() {
235  lp_data_.Clear();
236  // Create variables.
237  for (int i = 0; i < integer_variables_.size(); ++i) {
238  CHECK_EQ(ColIndex(i), lp_data_.CreateNewVariable());
239  lp_data_.SetVariableType(ColIndex(i),
241  }
242 
243  // Add constraints.
244  for (const LinearConstraintInternal& ct : integer_lp_) {
245  const ConstraintIndex row = lp_data_.CreateNewConstraint();
246  lp_data_.SetConstraintBounds(row, ToDouble(ct.lb), ToDouble(ct.ub));
247  for (const auto& term : ct.terms) {
248  lp_data_.SetCoefficient(row, term.first, ToDouble(term.second));
249  }
250  }
251 
252  // Add objective.
253  for (const auto& term : integer_objective_) {
254  lp_data_.SetObjectiveCoefficient(term.first, ToDouble(term.second));
255  }
256 
257  const int num_vars = integer_variables_.size();
258  for (int i = 0; i < num_vars; i++) {
259  const IntegerVariable cp_var = integer_variables_[i];
260  const double lb = ToDouble(integer_trail_->LevelZeroLowerBound(cp_var));
261  const double ub = ToDouble(integer_trail_->LevelZeroUpperBound(cp_var));
262  lp_data_.SetVariableBounds(ColIndex(i), lb, ub);
263  }
264 
265  objective_normalization_factor_ = 0.0;
266  glop::ColIndexVector integer_variables;
267  const ColIndex num_cols = lp_data_.num_variables();
268  for (ColIndex col : lp_data_.IntegerVariablesList()) {
269  var_is_binary_[col.value()] = lp_data_.IsVariableBinary(col);
270  if (!var_is_binary_[col.value()]) {
271  integer_variables.push_back(col);
272  }
273 
274  // The aim of this normalization value is to compute a coefficient of the
275  // d_i variables that should be minimized.
276  objective_normalization_factor_ +=
278  }
279  CHECK_GT(lp_data_.IntegerVariablesList().size(), 0);
280  objective_normalization_factor_ =
281  objective_normalization_factor_ / lp_data_.IntegerVariablesList().size();
282 
283  if (!integer_variables.empty()) {
284  // Update the LpProblem with norm variables and constraints.
285  norm_variables_.assign(num_cols, ColIndex(-1));
286  norm_lhs_constraints_.assign(num_cols, RowIndex(-1));
287  norm_rhs_constraints_.assign(num_cols, RowIndex(-1));
288  // For each integer non-binary variable x_i we introduce one new variable
289  // d_i subject to two new constraints:
290  // d_i - x_i >= -round(x'_i)
291  // d_i + x_i >= +round(x'_i)
292  // That's round(x'_i) - d_i <= x_i <= round(x'_i) + d_i, where d_i is an
293  // unbounded non-negative, and x'_i is the value of variable i from the
294  // previous solution obtained during the projection step. Consequently
295  // coefficients of the constraints are set here, but bounds of the
296  // constraints are updated at each iteration of the feasibility pump. Also
297  // coefficients of the objective are set here: x_i's are not present in the
298  // objective (i.e., coefficients set to 0.0), and d_i's are present in the
299  // objective with coefficients set to 1.0.
300  // Note that the treatment of integer non-binary variables is different
301  // from the treatment of binary variables. Binary variables do not impose
302  // any extra variables, nor extra constraints, but their objective
303  // coefficients are changed in the linear projection steps.
304  for (const ColIndex col : integer_variables) {
305  const ColIndex norm_variable = lp_data_.CreateNewVariable();
306  norm_variables_[col] = norm_variable;
307  lp_data_.SetVariableBounds(norm_variable, 0.0, glop::kInfinity);
308  const RowIndex row_a = lp_data_.CreateNewConstraint();
309  norm_lhs_constraints_[col] = row_a;
310  lp_data_.SetCoefficient(row_a, norm_variable, 1.0);
311  lp_data_.SetCoefficient(row_a, col, -1.0);
312  const RowIndex row_b = lp_data_.CreateNewConstraint();
313  norm_rhs_constraints_[col] = row_b;
314  lp_data_.SetCoefficient(row_b, norm_variable, 1.0);
315  lp_data_.SetCoefficient(row_b, col, 1.0);
316  }
317  }
318 
319  scaler_.Scale(&lp_data_);
321  /*detect_integer_constraints=*/false);
322 }
323 
324 void FeasibilityPump::L1DistanceMinimize() {
325  std::vector<double> new_obj_coeffs(lp_data_.num_variables().value(), 0.0);
326 
327  // Set the original subobjective. The coefficients are scaled by mixing factor
328  // and the offset remains at 0 (because it does not affect the solution).
329  const ColIndex num_cols(lp_data_.objective_coefficients().size());
330  for (ColIndex col(0); col < num_cols; ++col) {
331  new_obj_coeffs[col.value()] =
332  mixing_factor_ * lp_data_.objective_coefficients()[col];
333  }
334 
335  // Set the norm subobjective. The coefficients are scaled by 1 - mixing factor
336  // and the offset remains at 0 (because it does not affect the solution).
337  for (const ColIndex col : lp_data_.IntegerVariablesList()) {
338  if (var_is_binary_[col.value()]) {
339  const Fractional objective_coefficient =
340  mixing_factor_ * lp_data_.objective_coefficients()[col] +
341  (1 - mixing_factor_) * objective_normalization_factor_ *
342  (1 - 2 * integer_solution_[col.value()]);
343  new_obj_coeffs[col.value()] = objective_coefficient;
344  } else { // The variable is integer.
345  // Update the bounds of the constraints added in
346  // InitializeIntegerVariables() (see there for more details):
347  // d_i - x_i >= -round(x'_i)
348  // d_i + x_i >= +round(x'_i)
349 
350  // TODO(user): We change both the objective and the bounds, thus
351  // breaking the incrementality. Handle integer variables differently,
352  // e.g., intensify rounding, or use soft fixing from: Fischetti, Lodi,
353  // "Local Branching", Math Program Ser B 98:23-47 (2003).
354  const Fractional objective_coefficient =
355  (1 - mixing_factor_) * objective_normalization_factor_;
356  new_obj_coeffs[norm_variables_[col].value()] = objective_coefficient;
357  // At this point, constraint bounds have already been transformed into
358  // bounds of slack variables. Instead of updating the constraints, we need
359  // to update the slack variables corresponding to them.
360  const ColIndex norm_lhs_slack_variable =
361  lp_data_.GetSlackVariable(norm_lhs_constraints_[col]);
362  const double lhs_scaling_factor =
363  scaler_.VariableScalingFactor(norm_lhs_slack_variable);
364  lp_data_.SetVariableBounds(
365  norm_lhs_slack_variable, -glop::kInfinity,
366  lhs_scaling_factor * integer_solution_[col.value()]);
367  const ColIndex norm_rhs_slack_variable =
368  lp_data_.GetSlackVariable(norm_rhs_constraints_[col]);
369  const double rhs_scaling_factor =
370  scaler_.VariableScalingFactor(norm_rhs_slack_variable);
371  lp_data_.SetVariableBounds(
372  norm_rhs_slack_variable, -glop::kInfinity,
373  -rhs_scaling_factor * integer_solution_[col.value()]);
374  }
375  }
376  for (ColIndex col(0); col < lp_data_.num_variables(); ++col) {
377  lp_data_.SetObjectiveCoefficient(col, new_obj_coeffs[col.value()]);
378  }
379  // TODO(user): Tune this or expose as parameter.
380  mixing_factor_ *= 0.8;
381 }
382 
383 bool FeasibilityPump::SolveLp() {
384  const int num_vars = integer_variables_.size();
385  VLOG(3) << "LP relaxation: " << lp_data_.GetDimensionString() << ".";
386 
387  const auto status = simplex_.Solve(lp_data_, time_limit_);
388  total_num_simplex_iterations_ += simplex_.GetNumberOfIterations();
389  if (!status.ok()) {
390  VLOG(1) << "The LP solver encountered an error: " << status.error_message();
391  simplex_.ClearStateForNextSolve();
392  return false;
393  }
394 
395  // TODO(user): This shouldn't really happen except if the problem is UNSAT.
396  // But we can't just rely on a potentially imprecise LP to close the problem.
397  // The rest of the solver should do that with exact precision.
398  VLOG(3) << "simplex status: " << simplex_.GetProblemStatus();
400  return false;
401  }
402 
403  lp_solution_fractionality_ = 0.0;
408  lp_solution_is_set_ = true;
409  for (int i = 0; i < num_vars; i++) {
410  const double value = GetVariableValueAtCpScale(ColIndex(i));
411  lp_solution_[i] = value;
412  lp_solution_fractionality_ = std::max(
413  lp_solution_fractionality_, std::abs(value - std::round(value)));
414  }
415 
416  // Compute the objective value.
417  lp_objective_ = 0;
418  for (const auto& term : integer_objective_) {
419  lp_objective_ += lp_solution_[term.first.value()] * term.second.value();
420  }
421  lp_solution_is_integer_ = lp_solution_fractionality_ < kCpEpsilon;
422  }
423  return true;
424 }
425 
426 void FeasibilityPump::UpdateBoundsOfLpVariables() {
427  const int num_vars = integer_variables_.size();
428  for (int i = 0; i < num_vars; i++) {
429  const IntegerVariable cp_var = integer_variables_[i];
430  const double lb = ToDouble(integer_trail_->LevelZeroLowerBound(cp_var));
431  const double ub = ToDouble(integer_trail_->LevelZeroUpperBound(cp_var));
432  const double factor = scaler_.VariableScalingFactor(ColIndex(i));
433  lp_data_.SetVariableBounds(ColIndex(i), lb * factor, ub * factor);
434  }
435 }
436 
437 double FeasibilityPump::GetLPSolutionValue(IntegerVariable variable) const {
438  return lp_solution_[mirror_lp_variable_.at(variable).value()];
439 }
440 
441 double FeasibilityPump::GetVariableValueAtCpScale(ColIndex var) {
442  return scaler_.UnscaleVariableValue(var, simplex_.GetVariableValue(var));
443 }
444 
445 // ----------------------------------------------------------------
446 // -------------------Rounding-------------------------------------
447 // ----------------------------------------------------------------
448 
450  IntegerVariable variable) const {
451  return integer_solution_[mirror_lp_variable_.at(variable).value()];
452 }
453 
454 bool FeasibilityPump::Round() {
455  bool rounding_successful = true;
456  if (sat_parameters_.fp_rounding() == SatParameters::NEAREST_INTEGER) {
457  rounding_successful = NearestIntegerRounding();
458  } else if (sat_parameters_.fp_rounding() == SatParameters::LOCK_BASED) {
459  rounding_successful = LockBasedRounding();
460  } else if (sat_parameters_.fp_rounding() ==
461  SatParameters::ACTIVE_LOCK_BASED) {
462  rounding_successful = ActiveLockBasedRounding();
463  } else if (sat_parameters_.fp_rounding() ==
464  SatParameters::PROPAGATION_ASSISTED) {
465  rounding_successful = PropagationRounding();
466  }
467  if (!rounding_successful) return false;
468  FillIntegerSolutionStats();
469  return true;
470 }
471 
472 bool FeasibilityPump::NearestIntegerRounding() {
473  if (!lp_solution_is_set_) return false;
474  for (int i = 0; i < lp_solution_.size(); ++i) {
475  integer_solution_[i] = static_cast<int64_t>(std::round(lp_solution_[i]));
476  }
477  integer_solution_is_set_ = true;
478  return true;
479 }
480 
481 bool FeasibilityPump::LockBasedRounding() {
482  if (!lp_solution_is_set_) return false;
483  const int num_vars = integer_variables_.size();
484 
485  // We compute the number of locks based on variable coefficient in constraints
486  // and constraint bounds. This doesn't change over time so we cache it.
487  if (var_up_locks_.empty()) {
488  var_up_locks_.resize(num_vars, 0);
489  var_down_locks_.resize(num_vars, 0);
490  for (int i = 0; i < num_vars; ++i) {
491  for (const auto entry : lp_data_.GetSparseColumn(ColIndex(i))) {
492  ColIndex slack = lp_data_.GetSlackVariable(entry.row());
493  const bool constraint_upper_bounded =
494  lp_data_.variable_lower_bounds()[slack] > -glop::kInfinity;
495 
496  const bool constraint_lower_bounded =
497  lp_data_.variable_upper_bounds()[slack] < glop::kInfinity;
498 
499  if (entry.coefficient() > 0) {
500  var_up_locks_[i] += constraint_upper_bounded;
501  var_down_locks_[i] += constraint_lower_bounded;
502  } else {
503  var_up_locks_[i] += constraint_lower_bounded;
504  var_down_locks_[i] += constraint_upper_bounded;
505  }
506  }
507  }
508  }
509 
510  for (int i = 0; i < lp_solution_.size(); ++i) {
511  if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1 ||
512  var_up_locks_[i] == var_down_locks_[i]) {
513  integer_solution_[i] = static_cast<int64_t>(std::round(lp_solution_[i]));
514  } else if (var_up_locks_[i] > var_down_locks_[i]) {
515  integer_solution_[i] = static_cast<int64_t>(std::floor(lp_solution_[i]));
516  } else {
517  integer_solution_[i] = static_cast<int64_t>(std::ceil(lp_solution_[i]));
518  }
519  }
520  integer_solution_is_set_ = true;
521  return true;
522 }
523 
524 bool FeasibilityPump::ActiveLockBasedRounding() {
525  if (!lp_solution_is_set_) return false;
526  const int num_vars = integer_variables_.size();
527 
528  // We compute the number of locks based on variable coefficient in constraints
529  // and constraint bounds of active constraints. We consider the bound of the
530  // constraint that is tight for the current lp solution.
531  for (int i = 0; i < num_vars; ++i) {
532  if (std::abs(lp_solution_[i] - std::round(lp_solution_[i])) < 0.1) {
533  integer_solution_[i] = static_cast<int64_t>(std::round(lp_solution_[i]));
534  }
535 
536  int up_locks = 0;
537  int down_locks = 0;
538  for (const auto entry : lp_data_.GetSparseColumn(ColIndex(i))) {
539  const ConstraintStatus row_status =
540  simplex_.GetConstraintStatus(entry.row());
541  if (row_status == ConstraintStatus::AT_LOWER_BOUND) {
542  if (entry.coefficient() > 0) {
543  down_locks++;
544  } else {
545  up_locks++;
546  }
547  } else if (row_status == ConstraintStatus::AT_UPPER_BOUND) {
548  if (entry.coefficient() > 0) {
549  up_locks++;
550  } else {
551  down_locks++;
552  }
553  }
554  }
555  if (up_locks == down_locks) {
556  integer_solution_[i] = static_cast<int64_t>(std::round(lp_solution_[i]));
557  } else if (up_locks > down_locks) {
558  integer_solution_[i] = static_cast<int64_t>(std::floor(lp_solution_[i]));
559  } else {
560  integer_solution_[i] = static_cast<int64_t>(std::ceil(lp_solution_[i]));
561  }
562  }
563 
564  integer_solution_is_set_ = true;
565  return true;
566 }
567 
568 bool FeasibilityPump::PropagationRounding() {
569  if (!lp_solution_is_set_) return false;
570  sat_solver_->ResetToLevelZero();
571 
572  // Compute an order in which we will fix variables and do the propagation.
573  std::vector<int> rounding_order;
574  {
575  std::vector<std::pair<double, int>> binary_fractionality_vars;
576  std::vector<std::pair<double, int>> general_fractionality_vars;
577  for (int i = 0; i < lp_solution_.size(); ++i) {
578  const double fractionality =
579  std::abs(std::round(lp_solution_[i]) - lp_solution_[i]);
580  if (var_is_binary_[i]) {
581  binary_fractionality_vars.push_back({fractionality, i});
582  } else {
583  general_fractionality_vars.push_back({fractionality, i});
584  }
585  }
586  std::sort(binary_fractionality_vars.begin(),
587  binary_fractionality_vars.end());
588  std::sort(general_fractionality_vars.begin(),
589  general_fractionality_vars.end());
590 
591  for (int i = 0; i < binary_fractionality_vars.size(); ++i) {
592  rounding_order.push_back(binary_fractionality_vars[i].second);
593  }
594  for (int i = 0; i < general_fractionality_vars.size(); ++i) {
595  rounding_order.push_back(general_fractionality_vars[i].second);
596  }
597  }
598 
599  for (const int var_index : rounding_order) {
600  if (time_limit_->LimitReached()) return false;
601  // Get the bounds of the variable.
602  const IntegerVariable var = integer_variables_[var_index];
603  CHECK(VariableIsPositive(var));
604  const Domain& domain = (*domains_)[GetPositiveOnlyIndex(var)];
605 
606  const IntegerValue lb = integer_trail_->LowerBound(var);
607  const IntegerValue ub = integer_trail_->UpperBound(var);
608  if (lb == ub) {
609  integer_solution_[var_index] = lb.value();
610  continue;
611  }
612 
613  const int64_t rounded_value =
614  static_cast<int64_t>(std::round(lp_solution_[var_index]));
615  const int64_t floor_value =
616  static_cast<int64_t>(std::floor(lp_solution_[var_index]));
617  const int64_t ceil_value =
618  static_cast<int64_t>(std::ceil(lp_solution_[var_index]));
619 
620  const bool floor_is_in_domain =
621  (domain.Contains(floor_value) && lb.value() <= floor_value);
622  const bool ceil_is_in_domain =
623  (domain.Contains(ceil_value) && ub.value() >= ceil_value);
624  if (domain.IsEmpty()) {
625  integer_solution_[var_index] = rounded_value;
626  model_is_unsat_ = true;
627  return false;
628  }
629 
630  if (ceil_value < lb.value()) {
631  integer_solution_[var_index] = lb.value();
632  } else if (floor_value > ub.value()) {
633  integer_solution_[var_index] = ub.value();
634  } else if (ceil_is_in_domain && floor_is_in_domain) {
635  DCHECK(domain.Contains(rounded_value));
636  integer_solution_[var_index] = rounded_value;
637  } else if (ceil_is_in_domain) {
638  integer_solution_[var_index] = ceil_value;
639  } else if (floor_is_in_domain) {
640  integer_solution_[var_index] = floor_value;
641  } else {
642  const std::pair<IntegerLiteral, IntegerLiteral> values_in_domain =
643  integer_encoder_->Canonicalize(
644  IntegerLiteral::GreaterOrEqual(var, IntegerValue(rounded_value)));
645  const int64_t lower_value = values_in_domain.first.bound.value();
646  const int64_t higher_value = -values_in_domain.second.bound.value();
647  const int64_t distance_from_lower_value =
648  std::abs(lower_value - rounded_value);
649  const int64_t distance_from_higher_value =
650  std::abs(higher_value - rounded_value);
651 
652  integer_solution_[var_index] =
653  (distance_from_lower_value < distance_from_higher_value)
654  ? lower_value
655  : higher_value;
656  }
657 
658  CHECK(domain.Contains(integer_solution_[var_index]));
659  CHECK_GE(integer_solution_[var_index], lb);
660  CHECK_LE(integer_solution_[var_index], ub);
661 
662  // Propagate the value.
663  //
664  // When we want to fix the variable at its lb or ub, we do not create an
665  // equality literal to minimize the number of new literal we create. This
666  // is because creating an "== value" literal will implicitly also create
667  // a ">= value" and a "<= value" literals.
668  Literal to_enqueue;
669  const IntegerValue value(integer_solution_[var_index]);
670  if (value == lb) {
671  to_enqueue = integer_encoder_->GetOrCreateAssociatedLiteral(
673  } else if (value == ub) {
674  to_enqueue = integer_encoder_->GetOrCreateAssociatedLiteral(
676  } else {
677  to_enqueue =
679  }
680 
681  if (!sat_solver_->FinishPropagation()) {
682  model_is_unsat_ = true;
683  return false;
684  }
685  sat_solver_->EnqueueDecisionAndBacktrackOnConflict(to_enqueue);
686 
687  if (sat_solver_->ModelIsUnsat()) {
688  model_is_unsat_ = true;
689  return false;
690  }
691  }
692  sat_solver_->ResetToLevelZero();
693  integer_solution_is_set_ = true;
694  return true;
695 }
696 
697 void FeasibilityPump::FillIntegerSolutionStats() {
698  // Compute the objective value.
699  integer_solution_objective_ = 0;
700  for (const auto& term : integer_objective_) {
701  integer_solution_objective_ +=
702  integer_solution_[term.first.value()] * term.second.value();
703  }
704 
705  integer_solution_is_feasible_ = true;
706  num_infeasible_constraints_ = 0;
707  integer_solution_infeasibility_ = 0;
708  for (RowIndex i(0); i < integer_lp_.size(); ++i) {
709  int64_t activity = 0;
710  for (const auto& term : integer_lp_[i].terms) {
711  const int64_t prod =
712  CapProd(integer_solution_[term.first.value()], term.second.value());
713  if (prod <= std::numeric_limits<int64_t>::min() ||
715  activity = prod;
716  break;
717  }
718  activity = CapAdd(activity, prod);
719  if (activity <= std::numeric_limits<int64_t>::min() ||
720  activity >= std::numeric_limits<int64_t>::max())
721  break;
722  }
723  if (activity > integer_lp_[i].ub || activity < integer_lp_[i].lb) {
724  integer_solution_is_feasible_ = false;
725  num_infeasible_constraints_++;
726  const int64_t ub_infeasibility =
727  activity > integer_lp_[i].ub.value()
728  ? activity - integer_lp_[i].ub.value()
729  : 0;
730  const int64_t lb_infeasibility =
731  activity < integer_lp_[i].lb.value()
732  ? integer_lp_[i].lb.value() - activity
733  : 0;
734  integer_solution_infeasibility_ =
735  std::max(integer_solution_infeasibility_,
736  std::max(ub_infeasibility, lb_infeasibility));
737  }
738  }
739 }
740 
741 } // namespace sat
742 } // namespace operations_research
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
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:106
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:552
void SetVariableBounds(ColIndex col, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:250
void SetCoefficient(RowIndex row, ColIndex col, Fractional value)
Definition: lp_data.cc:318
ColIndex GetSlackVariable(RowIndex row) const
Definition: lp_data.cc:756
const DenseRow & variable_lower_bounds() const
Definition: lp_data.h:230
const DenseRow & objective_coefficients() const
Definition: lp_data.h:224
const std::vector< ColIndex > & IntegerVariablesList() const
Definition: lp_data.cc:281
Fractional GetObjectiveCoefficientForMinimizationVersion(ColIndex col) const
Definition: lp_data.cc:420
void SetConstraintBounds(RowIndex row, Fractional lower_bound, Fractional upper_bound)
Definition: lp_data.cc:310
void SetVariableType(ColIndex col, VariableType type)
Definition: lp_data.cc:237
void AddSlackVariablesWhereNecessary(bool detect_integer_constraints)
Definition: lp_data.cc:698
void SetObjectiveCoefficient(ColIndex col, Fractional value)
Definition: lp_data.cc:327
bool IsVariableBinary(ColIndex col) const
Definition: lp_data.cc:301
const DenseRow & variable_upper_bounds() const
Definition: lp_data.h:233
std::string GetDimensionString() const
Definition: lp_data.cc:426
const SparseColumn & GetSparseColumn(ColIndex col) const
Definition: lp_data.cc:410
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:312
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:308
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
Definition: integer.cc:227
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:254
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1561
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1646
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1641
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1557
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
Status EnqueueDecisionAndBacktrackOnConflict(Literal true_literal, int *first_propagation_index=nullptr)
Definition: sat_solver.cc:975
void AddNewSolution(const std::vector< double > &lp_solution)
SatParameters parameters
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Status status
Definition: g_gurobi.cc:41
GRBmodel * model
ColIndex col
Definition: markowitz.cc:186
RowIndex row
Definition: markowitz.cc:185
std::vector< ColIndex > ColIndexVector
Definition: lp_types.h:350
constexpr double kInfinity
Definition: lp_types.h:88
IntType IntTypeAbs(IntType t)
Definition: integer.h:85
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:149
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:46
PositiveOnlyIndex GetPositiveOnlyIndex(IntegerVariable var)
Definition: integer.h:155
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:145
double ToDouble(IntegerValue value)
Definition: integer.h:77
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:1505
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1499
#define VLOG(verboselevel)
Definition: vlog.h:39