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