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