OR-Tools  9.3
max_hs.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
14#include "ortools/sat/max_hs.h"
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <functional>
20#include <limits>
21#include <utility>
22#include <vector>
23
24#include "absl/container/flat_hash_map.h"
25#include "absl/container/flat_hash_set.h"
26#include "absl/flags/flag.h"
27#include "absl/meta/type_traits.h"
28#include "absl/random/random.h"
29#include "absl/strings/string_view.h"
33#if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
35#endif // __PORTABLE_PLATFORM__
36#include "ortools/linear_solver/linear_solver.pb.h"
37#include "ortools/sat/cp_model.pb.h"
39#include "ortools/sat/integer.h"
43#include "ortools/sat/model.h"
46#include "ortools/sat/sat_parameters.pb.h"
49#include "ortools/sat/util.h"
52
53// TODO(user): Remove this flag when experiments are stable.
55 int, max_hs_strategy, 0,
56 "MaxHsStrategy: 0 extract only objective variable, 1 extract all variables "
57 "colocated with objective variables, 2 extract all variables in the "
58 "linearization");
59
60namespace operations_research {
61namespace sat {
62
64 const CpModelProto& model_proto,
65 const ObjectiveDefinition& objective_definition,
66 const std::function<void()>& feasible_solution_observer, Model* model)
67 : model_proto_(model_proto),
68 objective_definition_(objective_definition),
69 feasible_solution_observer_(feasible_solution_observer),
70 model_(model),
71 sat_solver_(model->GetOrCreate<SatSolver>()),
72 time_limit_(model->GetOrCreate<TimeLimit>()),
73 parameters_(*model->GetOrCreate<SatParameters>()),
74 random_(model->GetOrCreate<ModelRandomGenerator>()),
75 shared_response_(model->GetOrCreate<SharedResponseManager>()),
76 integer_trail_(model->GetOrCreate<IntegerTrail>()),
77 integer_encoder_(model_->GetOrCreate<IntegerEncoder>()) {
78 request_.set_solver_specific_parameters("limits/gap = 0");
79 request_.set_solver_type(MPModelRequest::SCIP_MIXED_INTEGER_PROGRAMMING);
80}
81
82bool HittingSetOptimizer::ImportFromOtherWorkers() {
83 auto* level_zero_callbacks = model_->GetOrCreate<LevelZeroCallbackHelper>();
84 for (const auto& cb : level_zero_callbacks->callbacks) {
85 if (!cb()) {
86 sat_solver_->NotifyThatModelIsUnsat();
87 return false;
88 }
89 }
90 return true;
91}
92
93// Slightly different algo than FindCores() which aim to extract more cores, but
94// not necessarily non-overlaping ones.
95SatSolver::Status HittingSetOptimizer::FindMultipleCoresForMaxHs(
96 std::vector<Literal> assumptions,
97 std::vector<std::vector<Literal>>* cores) {
98 cores->clear();
99 const double saved_dlimit = time_limit_->GetDeterministicLimit();
100 auto cleanup = ::absl::MakeCleanup([this, saved_dlimit]() {
101 time_limit_->ChangeDeterministicLimit(saved_dlimit);
102 });
103
104 bool first_loop = true;
105 do {
106 if (time_limit_->LimitReached()) return SatSolver::LIMIT_REACHED;
107
108 // The order of assumptions do not matter.
109 // Randomizing it should improve diversity.
110 std::shuffle(assumptions.begin(), assumptions.end(), *random_);
111
112 const SatSolver::Status result =
113 ResetAndSolveIntegerProblem(assumptions, model_);
114 if (result != SatSolver::ASSUMPTIONS_UNSAT) return result;
115 std::vector<Literal> core = sat_solver_->GetLastIncompatibleDecisions();
116 if (sat_solver_->parameters().minimize_core()) {
117 MinimizeCoreWithPropagation(time_limit_, sat_solver_, &core);
118 }
119 CHECK(!core.empty());
120 cores->push_back(core);
121 if (!parameters_.find_multiple_cores()) break;
122
123 // Pick a random literal from the core and remove it from the set of
124 // assumptions.
125 CHECK(!core.empty());
126 const Literal random_literal =
127 core[absl::Uniform<int>(*random_, 0, core.size())];
128 for (int i = 0; i < assumptions.size(); ++i) {
129 if (assumptions[i] == random_literal) {
130 std::swap(assumptions[i], assumptions.back());
131 assumptions.pop_back();
132 break;
133 }
134 }
135
136 // Once we found at least one core, we impose a time limit to not spend
137 // too much time finding more.
138 if (first_loop) {
140 saved_dlimit, time_limit_->GetElapsedDeterministicTime() + 1.0));
141 first_loop = false;
142 }
143 } while (!assumptions.empty());
144
146}
147
148int HittingSetOptimizer::GetExtractedIndex(IntegerVariable var) const {
149 if (var.value() >= sat_var_to_mp_var_.size()) return kUnextracted;
150 return sat_var_to_mp_var_[var];
151}
152
153void HittingSetOptimizer::ExtractObjectiveVariables() {
154 const std::vector<IntegerVariable>& variables = objective_definition_.vars;
155 const std::vector<IntegerValue>& coefficients = objective_definition_.coeffs;
156 MPModelProto* hs_model = request_.mutable_model();
157
158 // Create the initial objective constraint.
159 // It is used to constraint the objective during search.
160 if (obj_constraint_ == nullptr) {
161 obj_constraint_ = hs_model->add_constraint();
162 obj_constraint_->set_lower_bound(-std::numeric_limits<double>::infinity());
163 obj_constraint_->set_upper_bound(std::numeric_limits<double>::infinity());
164 }
165
166 // Extract the objective variables.
167 for (int i = 0; i < variables.size(); ++i) {
168 IntegerVariable var = variables[i];
169 IntegerValue coeff = coefficients[i];
170
171 // Link the extracted variable to the positive variable.
172 if (!VariableIsPositive(var)) {
173 var = NegationOf(var);
174 coeff = -coeff;
175 }
176
177 // Normalized objective variables expects positive coefficients.
178 if (coeff > 0) {
179 normalized_objective_variables_.push_back(var);
180 normalized_objective_coefficients_.push_back(coeff);
181 } else {
182 normalized_objective_variables_.push_back(NegationOf(var));
183 normalized_objective_coefficients_.push_back(-coeff);
184 }
185
186 // Extract.
187 const int index = hs_model->variable_size();
188 obj_constraint_->add_var_index(index);
189 obj_constraint_->add_coefficient(ToDouble(coeff));
190
191 MPVariableProto* var_proto = hs_model->add_variable();
192 var_proto->set_lower_bound(ToDouble(integer_trail_->LowerBound(var)));
193 var_proto->set_upper_bound(ToDouble(integer_trail_->UpperBound(var)));
194 var_proto->set_objective_coefficient(ToDouble(coeff));
195 var_proto->set_is_integer(true);
196
197 // Store extraction info.
198 const int max_index = std::max(var.value(), NegationOf(var).value());
199 if (max_index >= sat_var_to_mp_var_.size()) {
200 sat_var_to_mp_var_.resize(max_index + 1, -1);
201 }
202 sat_var_to_mp_var_[var] = index;
203 sat_var_to_mp_var_[NegationOf(var)] = index;
204 extracted_variables_info_.push_back({var, var_proto});
205 }
206}
207
208void HittingSetOptimizer::ExtractAdditionalVariables(
209 const std::vector<IntegerVariable>& to_extract) {
210 MPModelProto* hs_model = request_.mutable_model();
211
212 VLOG(2) << "Extract " << to_extract.size() << " additional variables";
213 for (IntegerVariable tmp_var : to_extract) {
214 if (GetExtractedIndex(tmp_var) != kUnextracted) continue;
215
216 // Use the positive variable for the domain.
217 const IntegerVariable var = PositiveVariable(tmp_var);
218
219 const int index = hs_model->variable_size();
220 MPVariableProto* var_proto = hs_model->add_variable();
221 var_proto->set_lower_bound(ToDouble(integer_trail_->LowerBound(var)));
222 var_proto->set_upper_bound(ToDouble(integer_trail_->UpperBound(var)));
223 var_proto->set_is_integer(true);
224
225 // Store extraction info.
226 const int max_index = std::max(var.value(), NegationOf(var).value());
227 if (max_index >= sat_var_to_mp_var_.size()) {
228 sat_var_to_mp_var_.resize(max_index + 1, -1);
229 }
230 sat_var_to_mp_var_[var] = index;
231 sat_var_to_mp_var_[NegationOf(var)] = index;
232 extracted_variables_info_.push_back({var, var_proto});
233 }
234}
235
236// This code will use heuristics to decide which non-objective variables to
237// extract:
238// 0: no additional variables.
239// 1: any variable appearing in the same constraint as an objective variable.
240// 2: all variables appearing in the linear relaxation.
241//
242// TODO(user): We could also decide to extract all if small enough.
243std::vector<IntegerVariable>
244HittingSetOptimizer::ComputeAdditionalVariablesToExtract() {
245 absl::flat_hash_set<IntegerVariable> result_set;
246 if (absl::GetFlag(FLAGS_max_hs_strategy) == 0) return {};
247 const bool extract_all = absl::GetFlag(FLAGS_max_hs_strategy) == 2;
248
249 for (const std::vector<Literal>& literals : relaxation_.at_most_ones) {
250 bool found_at_least_one = extract_all;
251 for (const Literal literal : literals) {
252 if (GetExtractedIndex(integer_encoder_->GetLiteralView(literal)) !=
253 kUnextracted) {
254 found_at_least_one = true;
255 }
256 if (found_at_least_one) break;
257 }
258 if (!found_at_least_one) continue;
259 for (const Literal literal : literals) {
260 const IntegerVariable var = integer_encoder_->GetLiteralView(literal);
261 if (GetExtractedIndex(var) == kUnextracted) {
262 result_set.insert(PositiveVariable(var));
263 }
264 }
265 }
266
267 for (const LinearConstraint& linear : relaxation_.linear_constraints) {
268 bool found_at_least_one = extract_all;
269 for (const IntegerVariable var : linear.vars) {
270 if (GetExtractedIndex(var) != kUnextracted) {
271 found_at_least_one = true;
272 }
273 if (found_at_least_one) break;
274 }
275 if (!found_at_least_one) continue;
276 for (const IntegerVariable var : linear.vars) {
277 if (GetExtractedIndex(var) == kUnextracted) {
278 result_set.insert(PositiveVariable(var));
279 }
280 }
281 }
282
283 std::vector<IntegerVariable> result(result_set.begin(), result_set.end());
284 std::sort(result.begin(), result.end());
285
286 return result;
287}
288
289void HittingSetOptimizer::ProjectAndAddAtMostOne(
290 const std::vector<Literal>& literals) {
291 LinearConstraintBuilder builder(model_, 0, 1);
292 for (const Literal& literal : literals) {
293 if (!builder.AddLiteralTerm(literal, 1)) {
294 VLOG(3) << "Could not extract literal " << literal;
295 }
296 }
297
298 if (ProjectAndAddLinear(builder.Build()) != nullptr) {
299 num_extracted_at_most_ones_++;
300 }
301}
302
303MPConstraintProto* HittingSetOptimizer::ProjectAndAddLinear(
304 const LinearConstraint& linear) {
305 int num_extracted_variables = 0;
306 for (int i = 0; i < linear.vars.size(); ++i) {
307 if (GetExtractedIndex(PositiveVariable(linear.vars[i])) != kUnextracted) {
308 num_extracted_variables++;
309 }
310 }
311 if (num_extracted_variables <= 1) return nullptr;
312
313 MPConstraintProto* ct = request_.mutable_model()->add_constraint();
314 ProjectLinear(linear, ct);
315 return ct;
316}
317
318void HittingSetOptimizer::ProjectLinear(const LinearConstraint& linear,
319 MPConstraintProto* ct) {
320 IntegerValue lb = linear.lb;
321 IntegerValue ub = linear.ub;
322
323 for (int i = 0; i < linear.vars.size(); ++i) {
324 const IntegerVariable var = linear.vars[i];
325 const IntegerValue coeff = linear.coeffs[i];
326 const int index = GetExtractedIndex(PositiveVariable(var));
327 const bool negated = !VariableIsPositive(var);
328 if (index != kUnextracted) {
329 ct->add_var_index(index);
330 ct->add_coefficient(negated ? -ToDouble(coeff) : ToDouble(coeff));
331 } else {
332 const IntegerValue var_lb = integer_trail_->LevelZeroLowerBound(var);
333 const IntegerValue var_ub = integer_trail_->LevelZeroUpperBound(var);
334
335 if (coeff > 0) {
336 if (lb != kMinIntegerValue) lb -= coeff * var_ub;
337 if (ub != kMaxIntegerValue) ub -= coeff * var_lb;
338 } else {
339 if (lb != kMinIntegerValue) lb -= coeff * var_lb;
340 if (ub != kMaxIntegerValue) ub -= coeff * var_ub;
341 }
342 }
343 }
344
345 ct->set_lower_bound(ToDouble(lb));
346 ct->set_upper_bound(ToDouble(ub));
347}
348
349bool HittingSetOptimizer::ComputeInitialMpModel() {
350 if (!ImportFromOtherWorkers()) return false;
351
352 ExtractObjectiveVariables();
353
354 // Linearize the constraints from the model.
355 for (const auto& ct : model_proto_.constraints()) {
356 TryToLinearizeConstraint(model_proto_, ct, /*linearization_level=*/2,
357 model_, &relaxation_);
358 }
359
360 ExtractAdditionalVariables(ComputeAdditionalVariablesToExtract());
361
362 // Build the MPModel from the linear relaxation.
363 for (const auto& literals : relaxation_.at_most_ones) {
364 ProjectAndAddAtMostOne(literals);
365 }
366 if (num_extracted_at_most_ones_ > 0) {
367 VLOG(2) << "Projected " << num_extracted_at_most_ones_ << "/"
368 << relaxation_.at_most_ones.size() << " at_most_ones constraints";
369 }
370
371 for (int i = 0; i < relaxation_.linear_constraints.size(); ++i) {
372 MPConstraintProto* ct =
373 ProjectAndAddLinear(relaxation_.linear_constraints[i]);
374 if (ct != nullptr) linear_extract_info_.push_back({i, ct});
375 }
376 if (!linear_extract_info_.empty()) {
377 VLOG(2) << "Projected " << linear_extract_info_.size() << "/"
378 << relaxation_.linear_constraints.size() << " linear constraints";
379 }
380 return true;
381}
382
383void HittingSetOptimizer::TightenMpModel() {
384 // Update the MP variables bounds from the SAT level 0 bounds.
385 for (const auto& [var, var_proto] : extracted_variables_info_) {
386 var_proto->set_lower_bound(ToDouble(integer_trail_->LowerBound(var)));
387 var_proto->set_upper_bound(ToDouble(integer_trail_->UpperBound(var)));
388 }
389
390 int tightened = 0;
391 for (const auto& [index, ct] : linear_extract_info_) {
392 const double original_lb = ct->lower_bound();
393 const double original_ub = ct->upper_bound();
394 ct->Clear();
395 ProjectLinear(relaxation_.linear_constraints[index], ct);
396 if (original_lb != ct->lower_bound() || original_ub != ct->upper_bound()) {
397 tightened++;
398 }
399 }
400 if (tightened > 0) {
401 VLOG(2) << "Tightened " << tightened << " linear constraints";
402 }
403}
404
405bool HittingSetOptimizer::ProcessSolution() {
406 const std::vector<IntegerVariable>& variables = objective_definition_.vars;
407 const std::vector<IntegerValue>& coefficients = objective_definition_.coeffs;
408
409 // We don't assume that objective_var is linked with its linear term, so
410 // we recompute the objective here.
411 IntegerValue objective(0);
412 for (int i = 0; i < variables.size(); ++i) {
413 objective +=
414 coefficients[i] * IntegerValue(model_->Get(Value(variables[i])));
415 }
416 if (objective >
417 integer_trail_->UpperBound(objective_definition_.objective_var)) {
418 return true;
419 }
420
421 if (feasible_solution_observer_ != nullptr) {
422 feasible_solution_observer_();
423 }
424
425 // Constrain objective_var. This has a better result when objective_var is
426 // used in an LP relaxation for instance.
427 sat_solver_->Backtrack(0);
428 sat_solver_->SetAssumptionLevel(0);
429 if (!integer_trail_->Enqueue(
430 IntegerLiteral::LowerOrEqual(objective_definition_.objective_var,
431 objective - 1),
432 {}, {})) {
433 return false;
434 }
435 return true;
436}
437
438void HittingSetOptimizer::AddCoresToTheMpModel(
439 const std::vector<std::vector<Literal>>& cores) {
440 MPModelProto* hs_model = request_.mutable_model();
441
442 for (const std::vector<Literal>& core : cores) {
443 // For cores of size 1, we can just constrain the bound of the variable.
444 if (core.size() == 1) {
445 for (const int index : assumption_to_indices_.at(core.front().Index())) {
446 const IntegerVariable var = normalized_objective_variables_[index];
447 const double new_bound = ToDouble(integer_trail_->LowerBound(var));
448 if (VariableIsPositive(var)) {
449 hs_model->mutable_variable(index)->set_lower_bound(new_bound);
450 } else {
451 hs_model->mutable_variable(index)->set_upper_bound(-new_bound);
452 }
453 }
454 continue;
455 }
456
457 // Add the corresponding constraint to hs_model.
458 MPConstraintProto* at_least_one = hs_model->add_constraint();
459 at_least_one->set_lower_bound(1.0);
460 for (const Literal lit : core) {
461 for (const int index : assumption_to_indices_.at(lit.Index())) {
462 const IntegerVariable var = normalized_objective_variables_[index];
463 const double sat_lb = ToDouble(integer_trail_->LowerBound(var));
464 // normalized_objective_variables_[index] is mapped onto
465 // hs_model.variable[index] * sign.
466 const double sign = VariableIsPositive(var) ? 1.0 : -1.0;
467 // We round hs_value to the nearest integer. This should help in the
468 // hash_map part.
469 const double hs_value =
470 std::round(response_.variable_value(index)) * sign;
471
472 if (hs_value == sat_lb) {
473 at_least_one->add_var_index(index);
474 at_least_one->add_coefficient(sign);
475 at_least_one->set_lower_bound(at_least_one->lower_bound() + hs_value);
476 } else {
477 // The operation type (< or >) is consistent for the same variable,
478 // so we do not need this information in the key.
479 const std::pair<int, int64_t> key = {index,
480 static_cast<int64_t>(hs_value)};
481 const int new_bool_var_index = hs_model->variable_size();
482 const auto [it, inserted] =
483 mp_integer_literals_.insert({key, new_bool_var_index});
484
485 at_least_one->add_var_index(it->second);
486 at_least_one->add_coefficient(1.0);
487
488 if (inserted) {
489 // Creates the implied bound constraint.
490 MPVariableProto* bool_var = hs_model->add_variable();
491 bool_var->set_lower_bound(0);
492 bool_var->set_upper_bound(1);
493 bool_var->set_is_integer(true);
494
495 // (bool_var == 1) => x * sign > hs_value.
496 // (x * sign - sat_lb) - (hs_value - sat_lb + 1) * bool_var >= 0.
497 MPConstraintProto* implied_bound = hs_model->add_constraint();
498 implied_bound->set_lower_bound(sat_lb);
499 implied_bound->add_var_index(index);
500 implied_bound->add_coefficient(sign);
501 implied_bound->add_var_index(new_bool_var_index);
502 implied_bound->add_coefficient(sat_lb - hs_value - 1.0);
503 }
504 }
505 }
506 }
507 }
508}
509
510std::vector<Literal> HittingSetOptimizer::BuildAssumptions(
511 IntegerValue stratified_threshold,
512 IntegerValue* next_stratified_threshold) {
513 std::vector<Literal> assumptions;
514 // This code assumes that the variables from the objective are extracted
515 // first, and in the order of the objective definition.
516 for (int i = 0; i < normalized_objective_variables_.size(); ++i) {
517 const IntegerVariable var = normalized_objective_variables_[i];
518 const IntegerValue coeff = normalized_objective_coefficients_[i];
519
520 // Correct the sign of the value queried from the MP solution.
521 // Note that normalized_objective_variables_[i] is mapped onto
522 // hs_model.variable[i] * sign.
523 const IntegerValue hs_value(
524 static_cast<int64_t>(std::round(response_.variable_value(i))) *
525 (VariableIsPositive(var) ? 1 : -1));
526
527 // Non binding, ignoring.
528 if (hs_value == integer_trail_->UpperBound(var)) continue;
529
530 // Only consider the terms above the threshold.
531 if (coeff < stratified_threshold) {
532 *next_stratified_threshold = std::max(*next_stratified_threshold, coeff);
533 } else {
534 // It is possible that different variables have the same associated
535 // literal. So we do need to consider this case.
536 assumptions.push_back(integer_encoder_->GetOrCreateAssociatedLiteral(
538 assumption_to_indices_[assumptions.back().Index()].push_back(i);
539 }
540 }
541 return assumptions;
542}
543
544// This is the "generalized" hitting set problem we will solve. Each time
545// we find a core, a new constraint will be added to this problem.
546//
547// TODO(user): remove code duplication with MinimizeWithCoreAndLazyEncoding();
549#if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
550 if (!ComputeInitialMpModel()) return SatSolver::INFEASIBLE;
551
552 // This is used by the "stratified" approach. We will only consider terms with
553 // a weight not lower than this threshold. The threshold will decrease as the
554 // algorithm progress.
555 IntegerValue stratified_threshold = kMaxIntegerValue;
556
557 // Start the algorithm.
558 SatSolver::Status result;
559 for (int iter = 0;; ++iter) {
560 // TODO(user): Even though we keep the same solver, currently the solve is
561 // not really done incrementally. It might be hard to improve though.
562 //
563 // TODO(user): deal with time limit.
564
565 // Get the best external bound and constraint the objective of the MPModel.
566 if (shared_response_ != nullptr) {
567 const IntegerValue best_lower_bound =
568 shared_response_->GetInnerObjectiveLowerBound();
569 obj_constraint_->set_lower_bound(ToDouble(best_lower_bound));
570 }
571
572 if (!ImportFromOtherWorkers()) return SatSolver::INFEASIBLE;
573 TightenMpModel();
574
575 // TODO(user): C^c is broken when using SCIP.
576 MPSolver::SolveWithProto(request_, &response_);
577 if (response_.status() != MPSolverResponseStatus::MPSOLVER_OPTIMAL) {
578 // We currently abort if we have a non-optimal result.
579 // This is correct if we had a limit reached, but not in the other
580 // cases.
581 //
582 // TODO(user): It is actually easy to use a FEASIBLE result. If when
583 // passing it to SAT it is no feasbile, we can still create cores. If it
584 // is feasible, we have a solution, but we cannot increase the lower
585 // bound.
587 }
588 if (response_.status() != MPSolverResponseStatus::MPSOLVER_OPTIMAL) {
589 continue;
590 }
591
592 const IntegerValue mip_objective(
593 static_cast<int64_t>(std::round(response_.objective_value())));
594 VLOG(2) << "--" << iter
595 << "-- constraints:" << request_.mutable_model()->constraint_size()
596 << " variables:" << request_.mutable_model()->variable_size()
597 << " hs_lower_bound:"
598 << objective_definition_.ScaleIntegerObjective(mip_objective)
599 << " strat:" << stratified_threshold;
600
601 // Update the objective lower bound with our current bound.
602 //
603 // Note(user): This is not needed for correctness, but it might cause
604 // more propagation and is nice to have for reporting/logging purpose.
605 if (!integer_trail_->Enqueue(
606 IntegerLiteral::GreaterOrEqual(objective_definition_.objective_var,
607 mip_objective),
608 {}, {})) {
609 result = SatSolver::INFEASIBLE;
610 break;
611 }
612
613 sat_solver_->Backtrack(0);
614 sat_solver_->SetAssumptionLevel(0);
615 assumption_to_indices_.clear();
616 IntegerValue next_stratified_threshold(0);
617 const std::vector<Literal> assumptions =
618 BuildAssumptions(stratified_threshold, &next_stratified_threshold);
619
620 // No assumptions with the current stratified_threshold? use the new one.
621 if (assumptions.empty() && next_stratified_threshold > 0) {
622 CHECK_LT(next_stratified_threshold, stratified_threshold);
623 stratified_threshold = next_stratified_threshold;
624 --iter; // "false" iteration, the lower bound does not increase.
625 continue;
626 }
627
628 // TODO(user): Use the real weights and exploit the extra cores.
629 // TODO(user): If we extract more than the objective variables, we could
630 // use the solution values from the MPModel as hints to the SAT model.
631 result = FindMultipleCoresForMaxHs(assumptions, &temp_cores_);
632 if (result == SatSolver::FEASIBLE) {
633 if (!ProcessSolution()) return SatSolver::INFEASIBLE;
634 if (parameters_.stop_after_first_solution()) {
636 }
637 if (temp_cores_.empty()) {
638 // If not all assumptions were taken, continue with a lower stratified
639 // bound. Otherwise we have an optimal solution.
640 stratified_threshold = next_stratified_threshold;
641 if (stratified_threshold == 0) break;
642 --iter; // "false" iteration, the lower bound does not increase.
643 continue;
644 }
645 } else if (result == SatSolver::LIMIT_REACHED) {
646 // Hack: we use a local limit internally that we restore at the end.
647 // However we still return LIMIT_REACHED in this case...
648 if (time_limit_->LimitReached()) break;
649 } else if (result != SatSolver::ASSUMPTIONS_UNSAT) {
650 break;
651 }
652
653 sat_solver_->Backtrack(0);
654 sat_solver_->SetAssumptionLevel(0);
655 AddCoresToTheMpModel(temp_cores_);
656 }
657
658 return result;
659#else // !__PORTABLE_PLATFORM__ && USE_SCIP
660 LOG(FATAL) << "Not supported.";
661#endif // !__PORTABLE_PLATFORM__ && USE_SCIP
662}
663
664} // namespace sat
665} // 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_LT(val1, val2)
Definition: base/logging.h:706
#define LOG(severity)
Definition: base/logging.h:420
#define VLOG(verboselevel)
Definition: base/logging.h:984
void resize(size_type new_size)
size_type size() const
static void SolveWithProto(const MPModelRequest &model_request, MPSolutionResponse *response, std::atomic< bool > *interrupt=nullptr)
Solves the model encoded by a MPModelRequest protocol buffer and fills the solution encoded as a MPSo...
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
Definition: time_limit.h:106
double GetDeterministicLimit() const
Queries the deterministic time limit.
Definition: time_limit.h:304
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
Definition: time_limit.h:546
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
Definition: time_limit.h:261
void ChangeDeterministicLimit(double new_limit)
Overwrites the deterministic time limit with the new value.
Definition: time_limit.h:297
HittingSetOptimizer(const CpModelProto &model_proto, const ObjectiveDefinition &objective_definition, const std::function< void()> &feasible_solution_observer, Model *model)
Definition: max_hs.cc:63
const IntegerVariable GetLiteralView(Literal lit) const
Definition: integer.h:499
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:238
ABSL_MUST_USE_RESULT bool Enqueue(IntegerLiteral i_lit, absl::Span< const Literal > literal_reason, absl::Span< const IntegerLiteral > integer_reason)
Definition: integer.cc:1048
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
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
Definition: sat/model.h:91
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:110
const SatParameters & parameters() const
Definition: sat_solver.cc:131
void SetAssumptionLevel(int assumption_level)
Definition: sat_solver.cc:1060
void Backtrack(int target_level)
Definition: sat_solver.cc:991
std::vector< Literal > GetLastIncompatibleDecisions()
Definition: sat_solver.cc:1375
CpModelProto const * model_proto
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Span< const double > coefficients
GRBmodel * model
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
int index
const int FATAL
Definition: log_severity.h:32
ABSL_FLAG(int, max_hs_strategy, 0, "MaxHsStrategy: 0 extract only objective variable, 1 extract all variables " "colocated with objective variables, 2 extract all variables in the " "linearization")
absl::Cleanup< absl::decay_t< Callback > > MakeCleanup(Callback &&callback)
Definition: cleanup.h:125
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
Definition: id_map.h:262
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
void TryToLinearizeConstraint(const CpModelProto &model_proto, const ConstraintProto &ct, int linearization_level, Model *model, LinearRelaxation *relaxation)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:149
std::function< int64_t(const Model &)> Value(IntegerVariable v)
Definition: integer.h:1683
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:47
void MinimizeCoreWithPropagation(TimeLimit *limit, SatSolver *solver, std::vector< Literal > *core)
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.
Literal literal
Definition: optimization.cc:89
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1393
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
Definition: integer.h:1387
std::vector< std::vector< Literal > > at_most_ones
std::vector< LinearConstraint > linear_constraints
double ScaleIntegerObjective(IntegerValue value) const
const double coeff