OR-Tools  9.2
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"
27
28namespace operations_research {
29namespace sat {
30
31using glop::ColIndex;
34using glop::RowIndex;
35
36const 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.
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)) {
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
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
106ColIndex 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
128void 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
179void 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
214void 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
304void 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
363bool 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
406void 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
417double FeasibilityPump::GetLPSolutionValue(IntegerVariable variable) const {
418 return lp_solution_[gtl::FindOrDie(mirror_lp_variable_, variable).value()];
419}
420
421double 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
435bool 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() ==
443 rounding_successful = ActiveLockBasedRounding();
444 } else if (sat_parameters_.fp_rounding() ==
446 rounding_successful = PropagationRounding();
447 }
448 if (!rounding_successful) return false;
449 FillIntegerSolutionStats();
450 return true;
451}
452
453bool 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
462bool 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
505bool 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
549bool 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
677void 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() ||
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:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
#define CHECK_GE(val1, val2)
Definition: base/logging.h:706
#define CHECK_GT(val1, val2)
Definition: base/logging.h:707
#define DCHECK(condition)
Definition: base/logging.h:889
#define CHECK_LE(val1, val2)
Definition: base/logging.h:704
#define VLOG(verboselevel)
Definition: base/logging.h:983
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:534
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:266
std::pair< IntegerLiteral, IntegerLiteral > Canonicalize(IntegerLiteral i_lit) const
Definition: integer.cc:202
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:220
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1439
IntegerValue LevelZeroUpperBound(IntegerVariable var) const
Definition: integer.h:1524
IntegerValue LevelZeroLowerBound(IntegerVariable var) const
Definition: integer.h:1519
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1435
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:38
::operations_research::sat::SatParameters_FPRoundingMethod fp_rounding() const
static constexpr FPRoundingMethod NEAREST_INTEGER
static constexpr FPRoundingMethod PROPAGATION_ASSISTED
static constexpr FPRoundingMethod LOCK_BASED
static constexpr FPRoundingMethod ACTIVE_LOCK_BASED
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
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:79
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:143
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:30
bool VariableIsPositive(IntegerVariable i)
Definition: integer.h:139
double ToDouble(IntegerValue value)
Definition: integer.h:71
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:1383
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1377
const double coeff