OR-Tools  9.3
integer_search.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <functional>
20#include <random>
21#include <vector>
22
23#include "absl/container/flat_hash_set.h"
24#include "absl/strings/str_cat.h"
25#include "absl/time/clock.h"
26#include "absl/time/time.h"
29#include "ortools/sat/cp_model.pb.h"
32#include "ortools/sat/integer.h"
35#include "ortools/sat/model.h"
36#include "ortools/sat/probing.h"
38#include "ortools/sat/restart.h"
39#include "ortools/sat/rins.h"
43#include "ortools/sat/sat_parameters.pb.h"
46#include "ortools/sat/util.h"
49
50namespace operations_research {
51namespace sat {
52
53IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail* integer_trail) {
54 DCHECK(!integer_trail->IsCurrentlyIgnored(var));
55 const IntegerValue lb = integer_trail->LowerBound(var);
56 DCHECK_LE(lb, integer_trail->UpperBound(var));
57 if (lb == integer_trail->UpperBound(var)) return IntegerLiteral();
59}
60
62 const auto& variables =
63 model->GetOrCreate<ObjectiveDefinition>()->objective_impacting_variables;
64 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
65 if (variables.contains(var)) {
66 return AtMinValue(var, integer_trail);
67 } else if (variables.contains(NegationOf(var))) {
68 return AtMinValue(NegationOf(var), integer_trail);
69 }
70 return IntegerLiteral();
71}
72
74 IntegerTrail* integer_trail) {
75 const IntegerValue var_lb = integer_trail->LowerBound(var);
76 const IntegerValue var_ub = integer_trail->UpperBound(var);
77 CHECK_LT(var_lb, var_ub);
78
79 const IntegerValue chosen_value =
80 var_lb + std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
81 return IntegerLiteral::GreaterOrEqual(var, chosen_value);
82}
83
84IntegerLiteral SplitAroundGivenValue(IntegerVariable var, IntegerValue value,
85 Model* model) {
86 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
87 const IntegerValue lb = integer_trail->LowerBound(var);
88 const IntegerValue ub = integer_trail->UpperBound(var);
89
90 const absl::flat_hash_set<IntegerVariable>& variables =
91 model->GetOrCreate<ObjectiveDefinition>()->objective_impacting_variables;
92
93 // Heuristic: Prefer the objective direction first. Reference: Conflict-Driven
94 // Heuristics for Mixed Integer Programming (2019) by Jakob Witzig and Ambros
95 // Gleixner.
96 // NOTE: The value might be out of bounds. In that case we return
97 // kNoLiteralIndex.
98 const bool branch_down_feasible = value >= lb && value < ub;
99 const bool branch_up_feasible = value > lb && value <= ub;
100 if (variables.contains(var) && branch_down_feasible) {
102 } else if (variables.contains(NegationOf(var)) && branch_up_feasible) {
104 } else if (branch_down_feasible) {
106 } else if (branch_up_feasible) {
108 }
109 return IntegerLiteral();
110}
111
113 auto* parameters = model->GetOrCreate<SatParameters>();
114 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
115 auto* lp_dispatcher = model->GetOrCreate<LinearProgrammingDispatcher>();
116 DCHECK(!integer_trail->IsCurrentlyIgnored(var));
117
118 const IntegerVariable positive_var = PositiveVariable(var);
120 gtl::FindWithDefault(*lp_dispatcher, positive_var, nullptr);
121
122 // We only use this if the sub-lp has a solution, and depending on the value
123 // of exploit_all_lp_solution() if it is a pure-integer solution.
124 if (lp == nullptr || !lp->HasSolution()) return IntegerLiteral();
125 if (!parameters->exploit_all_lp_solution() && !lp->SolutionIsInteger()) {
126 return IntegerLiteral();
127 }
128
129 // TODO(user): Depending if we branch up or down, this might not exclude the
130 // LP value, which is potentially a bad thing.
131 //
132 // TODO(user): Why is the reduced cost doing things differently?
133 const IntegerValue value = IntegerValue(
134 static_cast<int64_t>(std::round(lp->GetSolutionValue(positive_var))));
135
136 // Because our lp solution might be from higher up in the tree, it
137 // is possible that value is now outside the domain of positive_var.
138 // In this case, this function will return an invalid literal.
139 return SplitAroundGivenValue(positive_var, value, model);
140}
141
143 IntegerVariable var, const SharedSolutionRepository<int64_t>& solution_repo,
144 Model* model) {
145 if (solution_repo.NumSolutions() == 0) {
146 return IntegerLiteral();
147 }
148
149 const IntegerVariable positive_var = PositiveVariable(var);
150 const int proto_var =
151 model->Get<CpModelMapping>()->GetProtoVariableFromIntegerVariable(
152 positive_var);
153
154 if (proto_var < 0) {
155 return IntegerLiteral();
156 }
157
158 const IntegerValue value(solution_repo.GetVariableValueInSolution(
159 proto_var, /*solution_index=*/0));
160 return SplitAroundGivenValue(positive_var, value, model);
161}
162
163// TODO(user): the complexity caused by the linear scan in this heuristic and
164// the one below is ok when search_branching is set to SAT_SEARCH because it is
165// not executed often, but otherwise it is done for each search decision,
166// which seems expensive. Improve.
168 const std::vector<IntegerVariable>& vars, Model* model) {
169 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
170 return [/*copy*/ vars, integer_trail]() {
171 for (const IntegerVariable var : vars) {
172 // Note that there is no point trying to fix a currently ignored variable.
173 if (integer_trail->IsCurrentlyIgnored(var)) continue;
174 const IntegerLiteral decision = AtMinValue(var, integer_trail);
175 if (decision.IsValid()) return BooleanOrIntegerLiteral(decision);
176 }
178 };
179}
180
181std::function<BooleanOrIntegerLiteral()>
183 const std::vector<IntegerVariable>& vars, Model* model) {
184 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
185 return [/*copy */ vars, integer_trail]() {
186 IntegerVariable candidate = kNoIntegerVariable;
187 IntegerValue candidate_lb;
188 for (const IntegerVariable var : vars) {
189 if (integer_trail->IsCurrentlyIgnored(var)) continue;
190 const IntegerValue lb = integer_trail->LowerBound(var);
191 if (lb < integer_trail->UpperBound(var) &&
192 (candidate == kNoIntegerVariable || lb < candidate_lb)) {
193 candidate = var;
194 candidate_lb = lb;
195 }
196 }
197 if (candidate == kNoIntegerVariable) return BooleanOrIntegerLiteral();
198 return BooleanOrIntegerLiteral(AtMinValue(candidate, integer_trail));
199 };
200}
201
203 std::vector<std::function<BooleanOrIntegerLiteral()>> heuristics) {
204 return [heuristics]() {
205 for (const auto& h : heuristics) {
206 const BooleanOrIntegerLiteral decision = h();
207 if (decision.HasValue()) return decision;
208 }
210 };
211}
212
214 std::vector<std::function<IntegerLiteral(IntegerVariable)>>
215 value_selection_heuristics,
216 std::function<BooleanOrIntegerLiteral()> var_selection_heuristic,
217 Model* model) {
218 auto* encoder = model->GetOrCreate<IntegerEncoder>();
219 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
220 auto* sat_policy = model->GetOrCreate<SatDecisionPolicy>();
221 return [=]() {
222 // Get the current decision.
223 const BooleanOrIntegerLiteral current_decision = var_selection_heuristic();
224 if (!current_decision.HasValue()) return current_decision;
225
226 // When we are in the "stable" phase, we prefer to follow the SAT polarity
227 // heuristic.
228 if (current_decision.boolean_literal_index != kNoLiteralIndex &&
229 sat_policy->InStablePhase()) {
230 return current_decision;
231 }
232
233 // IntegerLiteral case.
234 if (current_decision.boolean_literal_index == kNoLiteralIndex) {
235 for (const auto& value_heuristic : value_selection_heuristics) {
236 const IntegerLiteral decision =
237 value_heuristic(current_decision.integer_literal.var);
238 if (decision.IsValid()) return BooleanOrIntegerLiteral(decision);
239 }
240 return current_decision;
241 }
242
243 // Boolean case. We try to decode the Boolean decision to see if it is
244 // associated with an integer variable.
245 for (const IntegerLiteral l : encoder->GetAllIntegerLiterals(
246 Literal(current_decision.boolean_literal_index))) {
247 if (integer_trail->IsCurrentlyIgnored(l.var)) continue;
248
249 // Sequentially try the value selection heuristics.
250 for (const auto& value_heuristic : value_selection_heuristics) {
251 const IntegerLiteral decision = value_heuristic(l.var);
252 if (decision.IsValid()) return BooleanOrIntegerLiteral(decision);
253 }
254 }
255
256 return current_decision;
257 };
258}
259
261 auto* lp_constraints =
263 int num_lp_variables = 0;
264 for (LinearProgrammingConstraint* lp : *lp_constraints) {
265 num_lp_variables += lp->NumVariables();
266 }
267 const int num_integer_variables =
268 model->GetOrCreate<IntegerTrail>()->NumIntegerVariables().value() / 2;
269 return (num_integer_variables <= 2 * num_lp_variables);
270}
271
272// TODO(user): Experiment more with value selection heuristics.
274 std::function<BooleanOrIntegerLiteral()> var_selection_heuristic,
275 Model* model) {
276 const SatParameters& parameters = *(model->GetOrCreate<SatParameters>());
277 std::vector<std::function<IntegerLiteral(IntegerVariable)>>
278 value_selection_heuristics;
279
280 // LP based value.
281 //
282 // Note that we only do this if a big enough percentage of the problem
283 // variables appear in the LP relaxation.
285 (parameters.exploit_integer_lp_solution() ||
286 parameters.exploit_all_lp_solution())) {
287 value_selection_heuristics.push_back([model](IntegerVariable var) {
289 });
290 }
291
292 // Solution based value.
293 if (parameters.exploit_best_solution()) {
294 auto* response_manager = model->Get<SharedResponseManager>();
295 if (response_manager != nullptr) {
296 VLOG(3) << "Using best solution value selection heuristic.";
297 value_selection_heuristics.push_back(
298 [model, response_manager](IntegerVariable var) {
300 var, response_manager->SolutionsRepository(), model);
301 });
302 }
303 }
304
305 // Relaxation Solution based value.
306 if (parameters.exploit_relaxation_solution()) {
309 if (relaxation_solutions != nullptr) {
310 value_selection_heuristics.push_back(
311 [model, relaxation_solutions](IntegerVariable var) {
312 VLOG(3) << "Using relaxation solution value selection heuristic.";
315 });
316 }
317 }
318
319 // Objective based value.
320 if (parameters.exploit_objective()) {
321 value_selection_heuristics.push_back([model](IntegerVariable var) {
323 });
324 }
325
326 return SequentialValueSelection(value_selection_heuristics,
327 var_selection_heuristic, model);
328}
329
331 SatSolver* sat_solver = model->GetOrCreate<SatSolver>();
332 Trail* trail = model->GetOrCreate<Trail>();
333 SatDecisionPolicy* decision_policy = model->GetOrCreate<SatDecisionPolicy>();
334 return [sat_solver, trail, decision_policy] {
335 const bool all_assigned = trail->Index() == sat_solver->NumVariables();
336 if (all_assigned) return BooleanOrIntegerLiteral();
337 const Literal result = decision_policy->NextBranch();
338 CHECK(!sat_solver->Assignment().LiteralIsAssigned(result));
339 return BooleanOrIntegerLiteral(result.Index());
340 };
341}
342
344 auto* objective = model->Get<ObjectiveDefinition>();
345 const bool has_objective =
346 objective != nullptr && objective->objective_var != kNoIntegerVariable;
347 if (!has_objective) {
348 return []() { return BooleanOrIntegerLiteral(); };
349 }
350
351 auto* pseudo_costs = model->GetOrCreate<PseudoCosts>();
352 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
353 return [pseudo_costs, integer_trail]() {
354 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
355 if (chosen_var == kNoIntegerVariable) return BooleanOrIntegerLiteral();
356
357 // TODO(user): This will be overidden by the value decision heuristic in
358 // almost all cases.
360 GreaterOrEqualToMiddleValue(chosen_var, integer_trail));
361 };
362}
363
364// A simple heuristic for scheduling models.
366 Model* model) {
367 auto* repo = model->GetOrCreate<IntervalsRepository>();
368 auto* heuristic = model->GetOrCreate<SearchHeuristics>();
369 auto* trail = model->GetOrCreate<Trail>();
370 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
371 return [repo, heuristic, trail, integer_trail]() {
372 struct ToSchedule {
373 // Variable to fix.
374 LiteralIndex presence = kNoLiteralIndex;
377
378 // Information to select best.
379 IntegerValue size_min = kMaxIntegerValue;
380 IntegerValue time = kMaxIntegerValue;
381 };
382 ToSchedule best;
383
384 // TODO(user): we should also precompute fixed precedences and only fix
385 // interval that have all their predecessors fixed.
386 const int num_intervals = repo->NumIntervals();
387 for (IntervalVariable i(0); i < num_intervals; ++i) {
388 if (repo->IsAbsent(i)) continue;
389 if (!repo->IsPresent(i) || !integer_trail->IsFixed(repo->Start(i)) ||
390 !integer_trail->IsFixed(repo->End(i))) {
391 IntegerValue time = integer_trail->LowerBound(repo->Start(i));
392 if (repo->IsOptional(i)) {
393 // For task whose presence is still unknown, our propagators should
394 // have propagated the minimium time as if it was present. So this
395 // should reflect the earliest time at which this interval can be
396 // scheduled.
397 time = std::max(time, integer_trail->ConditionalLowerBound(
398 repo->PresenceLiteral(i), repo->Start(i)));
399 }
400
401 // For variable size, we compute the min size once the start is fixed
402 // to time. This is needed to never pick the "artificial" makespan
403 // interval at the end in priority compared to intervals that still
404 // need to be scheduled.
405 const IntegerValue size_min =
406 std::max(integer_trail->LowerBound(repo->Size(i)),
407 integer_trail->LowerBound(repo->End(i)) - time);
408 if (time < best.time ||
409 (time == best.time && size_min < best.size_min)) {
410 best.presence = repo->IsOptional(i) ? repo->PresenceLiteral(i).Index()
412 best.start = repo->Start(i);
413 best.end = repo->End(i);
414 best.time = time;
415 best.size_min = size_min;
416 }
417 }
418 }
419 if (best.time == kMaxIntegerValue) return BooleanOrIntegerLiteral();
420
421 // Use the next_decision_override to fix in turn all the variables from
422 // the selected interval.
423 int num_times = 0;
424 heuristic->next_decision_override = [trail, integer_trail, best,
425 num_times]() mutable {
426 if (++num_times > 5) {
427 // We have been trying to fix this interval for a while. Do we miss
428 // some propagation? In any case, try to see if the heuristic above
429 // would select something else.
430 VLOG(3) << "Skipping ... ";
432 }
433
434 // First make sure the interval is present.
435 if (best.presence != kNoLiteralIndex) {
436 if (!trail->Assignment().LiteralIsAssigned(Literal(best.presence))) {
437 VLOG(3) << "assign " << best.presence;
438 return BooleanOrIntegerLiteral(best.presence);
439 }
440 if (trail->Assignment().LiteralIsFalse(Literal(best.presence))) {
441 VLOG(2) << "unperformed.";
443 }
444 }
445
446 // We assume that start_min is propagated by now.
447 if (!integer_trail->IsFixed(best.start)) {
448 const IntegerValue start_min = integer_trail->LowerBound(best.start);
449 VLOG(3) << "start == " << start_min;
450 return BooleanOrIntegerLiteral(best.start.LowerOrEqual(start_min));
451 }
452
453 // We assume that end_min is propagated by now.
454 if (!integer_trail->IsFixed(best.end)) {
455 const IntegerValue end_min = integer_trail->LowerBound(best.end);
456 VLOG(3) << "end == " << end_min;
457 return BooleanOrIntegerLiteral(best.end.LowerOrEqual(end_min));
458 }
459
460 // Everything is fixed, dettach the override.
461 const IntegerValue start = integer_trail->LowerBound(best.start);
462 VLOG(2) << "Fixed @[" << start << ","
463 << integer_trail->LowerBound(best.end) << "]"
464 << (best.presence != kNoLiteralIndex
465 ? absl::StrCat(" presence=",
466 Literal(best.presence).DebugString())
467 : "")
468 << (best.time < start
469 ? absl::StrCat(" start_at_selection=", best.time.value())
470 : "");
472 };
473
474 return heuristic->next_decision_override();
475 };
476}
477
479 Model* model) {
480 SatSolver* sat_solver = model->GetOrCreate<SatSolver>();
481 SatDecisionPolicy* decision_policy = model->GetOrCreate<SatDecisionPolicy>();
482
483 // TODO(user): Add other policy and perform more experiments.
484 std::function<BooleanOrIntegerLiteral()> sat_policy =
486 std::vector<std::function<BooleanOrIntegerLiteral()>> policies{
487 sat_policy, SequentialSearch({PseudoCost(model), sat_policy})};
488 // The higher weight for the sat policy is because this policy actually
489 // contains a lot of variation as we randomize the sat parameters.
490 // TODO(user): Do more experiments to find better distribution.
491 std::discrete_distribution<int> var_dist{3 /*sat_policy*/, 1 /*Pseudo cost*/};
492
493 // Value selection.
494 std::vector<std::function<IntegerLiteral(IntegerVariable)>>
495 value_selection_heuristics;
496 std::vector<int> value_selection_weight;
497
498 // LP Based value.
499 value_selection_heuristics.push_back([model](IntegerVariable var) {
501 });
502 value_selection_weight.push_back(8);
503
504 // Solution based value.
505 auto* response_manager = model->Get<SharedResponseManager>();
506 if (response_manager != nullptr) {
507 value_selection_heuristics.push_back(
508 [model, response_manager](IntegerVariable var) {
510 var, response_manager->SolutionsRepository(), model);
511 });
512 value_selection_weight.push_back(5);
513 }
514
515 // Relaxation solution based value.
517 if (relaxation_solutions != nullptr) {
518 value_selection_heuristics.push_back(
519 [model, relaxation_solutions](IntegerVariable var) {
522 });
523 value_selection_weight.push_back(3);
524 }
525
526 // Middle value.
527 auto* integer_trail = model->GetOrCreate<IntegerTrail>();
528 value_selection_heuristics.push_back([integer_trail](IntegerVariable var) {
529 return GreaterOrEqualToMiddleValue(var, integer_trail);
530 });
531 value_selection_weight.push_back(1);
532
533 // Min value.
534 value_selection_heuristics.push_back([integer_trail](IntegerVariable var) {
535 return AtMinValue(var, integer_trail);
536 });
537 value_selection_weight.push_back(1);
538
539 // Special case: Don't change the decision value.
540 value_selection_weight.push_back(10);
541
542 // TODO(user): These distribution values are just guessed values. They need
543 // to be tuned.
544 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
545 value_selection_weight.end());
546
547 int policy_index = 0;
548 int val_policy_index = 0;
549 auto* encoder = model->GetOrCreate<IntegerEncoder>();
550 return [=]() mutable {
551 if (sat_solver->CurrentDecisionLevel() == 0) {
552 auto* random = model->GetOrCreate<ModelRandomGenerator>();
553 RandomizeDecisionHeuristic(*random, model->GetOrCreate<SatParameters>());
554 decision_policy->ResetDecisionHeuristic();
555
556 // Select the variable selection heuristic.
557 policy_index = var_dist(*(random));
558
559 // Select the value selection heuristic.
560 val_policy_index = val_dist(*(random));
561 }
562
563 // Get the current decision.
564 const BooleanOrIntegerLiteral current_decision = policies[policy_index]();
565 if (!current_decision.HasValue()) return current_decision;
566
567 // Special case: Don't override the decision value.
568 if (val_policy_index >= value_selection_heuristics.size()) {
569 return current_decision;
570 }
571
572 if (current_decision.boolean_literal_index == kNoLiteralIndex) {
573 const IntegerLiteral new_decision =
574 value_selection_heuristics[val_policy_index](
575 current_decision.integer_literal.var);
576 if (new_decision.IsValid()) return BooleanOrIntegerLiteral(new_decision);
577 return current_decision;
578 }
579
580 // Decode the decision and get the variable.
581 for (const IntegerLiteral l : encoder->GetAllIntegerLiterals(
582 Literal(current_decision.boolean_literal_index))) {
583 if (integer_trail->IsCurrentlyIgnored(l.var)) continue;
584
585 // Try the selected policy.
586 const IntegerLiteral new_decision =
587 value_selection_heuristics[val_policy_index](l.var);
588 if (new_decision.IsValid()) return BooleanOrIntegerLiteral(new_decision);
589 }
590
591 // Selected policy failed. Revert back to original decision.
592 return current_decision;
593 };
594}
595
596// TODO(user): Avoid the quadratic algorithm!!
598 const std::vector<BooleanOrIntegerVariable>& vars,
599 const std::vector<IntegerValue>& values, Model* model) {
600 const Trail* trail = model->GetOrCreate<Trail>();
601 const IntegerTrail* integer_trail = model->GetOrCreate<IntegerTrail>();
602 return [=] { // copy
603 for (int i = 0; i < vars.size(); ++i) {
604 const IntegerValue value = values[i];
605 if (vars[i].bool_var != kNoBooleanVariable) {
606 if (trail->Assignment().VariableIsAssigned(vars[i].bool_var)) continue;
608 Literal(vars[i].bool_var, value == 1).Index());
609 } else {
610 const IntegerVariable integer_var = vars[i].int_var;
611 if (integer_trail->IsCurrentlyIgnored(integer_var)) continue;
612 if (integer_trail->IsFixed(integer_var)) continue;
613
614 const IntegerVariable positive_var = PositiveVariable(integer_var);
615 const IntegerLiteral decision = SplitAroundGivenValue(
616 positive_var, positive_var != integer_var ? -value : value, model);
617 if (decision.IsValid()) return BooleanOrIntegerLiteral(decision);
618
619 // If the value is outside the current possible domain, we skip it.
620 continue;
621 }
622 }
624 };
625}
626
627std::function<bool()> RestartEveryKFailures(int k, SatSolver* solver) {
628 bool reset_at_next_call = true;
629 int next_num_failures = 0;
630 return [=]() mutable {
631 if (reset_at_next_call) {
632 next_num_failures = solver->num_failures() + k;
633 reset_at_next_call = false;
634 } else if (solver->num_failures() >= next_num_failures) {
635 reset_at_next_call = true;
636 }
637 return reset_at_next_call;
638 };
639}
640
641std::function<bool()> SatSolverRestartPolicy(Model* model) {
642 auto policy = model->GetOrCreate<RestartPolicy>();
643 return [policy]() { return policy->ShouldRestart(); };
644}
645
646namespace {
647
648std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
649 std::function<IntegerLiteral()> f) {
650 return [f]() { return BooleanOrIntegerLiteral(f()); };
651}
652
653} // namespace
654
656 SearchHeuristics& heuristics = *model->GetOrCreate<SearchHeuristics>();
657 CHECK(heuristics.fixed_search != nullptr);
658 heuristics.policy_index = 0;
659 heuristics.decision_policies.clear();
660 heuristics.restart_policies.clear();
661
662 const SatParameters& parameters = *(model->GetOrCreate<SatParameters>());
663 switch (parameters.search_branching()) {
664 case SatParameters::AUTOMATIC_SEARCH: {
665 std::function<BooleanOrIntegerLiteral()> decision_policy;
666 if (parameters.randomize_search()) {
667 decision_policy = RandomizeOnRestartHeuristic(model);
668 } else {
669 decision_policy = SatSolverHeuristic(model);
670 }
671 decision_policy =
672 SequentialSearch({decision_policy, heuristics.fixed_search});
673 decision_policy = IntegerValueSelectionHeuristic(decision_policy, model);
674 heuristics.decision_policies = {decision_policy};
676 return;
677 }
678 case SatParameters::FIXED_SEARCH: {
679 // Not all Boolean might appear in fixed_search(), so once there is no
680 // decision left, we fix all Booleans that are still undecided.
682 {heuristics.fixed_search, SatSolverHeuristic(model)})};
683
684 if (parameters.randomize_search()) {
686 return;
687 }
688
689 // TODO(user): We might want to restart if external info is available.
690 // Code a custom restart for this?
691 auto no_restart = []() { return false; };
692 heuristics.restart_policies = {no_restart};
693 return;
694 }
695 case SatParameters::HINT_SEARCH: {
696 CHECK(heuristics.hint_search != nullptr);
697 heuristics.decision_policies = {
699 heuristics.fixed_search})};
700 auto no_restart = []() { return false; };
701 heuristics.restart_policies = {no_restart};
702 return;
703 }
704 case SatParameters::PORTFOLIO_SEARCH: {
705 // TODO(user): This is not used in any of our default config. remove?
706 // It make also no sense to choose a value in the LP heuristic and then
707 // override it with IntegerValueSelectionHeuristic(), clean that up.
708 std::vector<std::function<BooleanOrIntegerLiteral()>> base_heuristics;
709 base_heuristics.push_back(heuristics.fixed_search);
710 for (const auto& ct :
711 *(model->GetOrCreate<LinearProgrammingConstraintCollection>())) {
712 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
713 ct->HeuristicLpReducedCostBinary(model)));
714 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
715 ct->HeuristicLpMostInfeasibleBinary(model)));
716 }
718 base_heuristics, SequentialSearch({SatSolverHeuristic(model),
719 heuristics.fixed_search}));
720 for (auto& ref : heuristics.decision_policies) {
722 }
723 heuristics.restart_policies.assign(heuristics.decision_policies.size(),
725 return;
726 }
727 case SatParameters::LP_SEARCH: {
728 std::vector<std::function<BooleanOrIntegerLiteral()>> lp_heuristics;
729 for (const auto& ct :
730 *(model->GetOrCreate<LinearProgrammingConstraintCollection>())) {
731 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
732 ct->HeuristicLpReducedCostAverageBranching()));
733 }
734 if (lp_heuristics.empty()) { // Revert to fixed search.
736 {heuristics.fixed_search, SatSolverHeuristic(model)})},
738 return;
739 }
742 heuristics.fixed_search}));
743 heuristics.restart_policies.assign(heuristics.decision_policies.size(),
745 return;
746 }
747 case SatParameters::PSEUDO_COST_SEARCH: {
748 std::function<BooleanOrIntegerLiteral()> search =
750 heuristics.fixed_search});
751 heuristics.decision_policies = {
754 return;
755 }
756 case SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH: {
757 std::function<BooleanOrIntegerLiteral()> search = SequentialSearch(
759 heuristics.decision_policies = {search};
760 heuristics.restart_policies = {
761 RestartEveryKFailures(10, model->GetOrCreate<SatSolver>())};
762 return;
763 }
764 }
765}
766
767std::vector<std::function<BooleanOrIntegerLiteral()>> CompleteHeuristics(
768 const std::vector<std::function<BooleanOrIntegerLiteral()>>&
769 incomplete_heuristics,
770 const std::function<BooleanOrIntegerLiteral()>& completion_heuristic) {
771 std::vector<std::function<BooleanOrIntegerLiteral()>> complete_heuristics;
772 complete_heuristics.reserve(incomplete_heuristics.size());
773 for (const auto& incomplete : incomplete_heuristics) {
774 complete_heuristics.push_back(
775 SequentialSearch({incomplete, completion_heuristic}));
776 }
777 return complete_heuristics;
778}
779
781 : model_(model),
782 sat_solver_(model->GetOrCreate<SatSolver>()),
783 integer_trail_(model->GetOrCreate<IntegerTrail>()),
784 encoder_(model->GetOrCreate<IntegerEncoder>()),
785 implied_bounds_(model->GetOrCreate<ImpliedBounds>()),
786 time_limit_(model->GetOrCreate<TimeLimit>()),
787 pseudo_costs_(model->GetOrCreate<PseudoCosts>()) {
788 // This is needed for recording the pseudo-costs.
789 const ObjectiveDefinition* objective = model->Get<ObjectiveDefinition>();
790 if (objective != nullptr) objective_var_ = objective->objective_var;
791}
792
794 // If we pushed root level deductions, we restart to incorporate them.
795 // Note that in the present of assumptions, it is important to return to
796 // the level zero first ! otherwise, the new deductions will not be
797 // incorporated and the solver will loop forever.
798 if (integer_trail_->HasPendingRootLevelDeduction()) {
799 sat_solver_->Backtrack(0);
800 if (!sat_solver_->RestoreSolverToAssumptionLevel()) {
801 return false;
802 }
803 }
804
805 if (sat_solver_->CurrentDecisionLevel() == 0) {
806 if (!implied_bounds_->EnqueueNewDeductions()) {
807 sat_solver_->NotifyThatModelIsUnsat();
808 return false;
809 }
810
811 auto* level_zero_callbacks = model_->GetOrCreate<LevelZeroCallbackHelper>();
812 for (const auto& cb : level_zero_callbacks->callbacks) {
813 if (!cb()) {
814 sat_solver_->NotifyThatModelIsUnsat();
815 return false;
816 }
817 }
818
819 if (model_->GetOrCreate<SatParameters>()->use_sat_inprocessing() &&
820 !model_->GetOrCreate<Inprocessing>()->InprocessingRound()) {
821 sat_solver_->NotifyThatModelIsUnsat();
822 return false;
823 }
824 }
825 return true;
826}
827
829 const std::function<BooleanOrIntegerLiteral()>& f) {
830 LiteralIndex decision = kNoLiteralIndex;
831 while (!time_limit_->LimitReached()) {
832 BooleanOrIntegerLiteral new_decision;
833 if (integer_trail_->InPropagationLoop()) {
834 const IntegerVariable var =
836 if (var != kNoIntegerVariable) {
837 new_decision.integer_literal =
838 GreaterOrEqualToMiddleValue(var, integer_trail_);
839 }
840 }
841 if (!new_decision.HasValue()) {
842 new_decision = f();
843 }
844 if (!new_decision.HasValue() &&
845 integer_trail_->CurrentBranchHadAnIncompletePropagation()) {
846 const IntegerVariable var = integer_trail_->FirstUnassignedVariable();
847 if (var != kNoIntegerVariable) {
848 new_decision.integer_literal = AtMinValue(var, integer_trail_);
849 }
850 }
851 if (!new_decision.HasValue()) break;
852
853 // Convert integer decision to literal one if needed.
854 //
855 // TODO(user): Ideally it would be cool to delay the creation even more
856 // until we have a conflict with these decisions, but it is currrently
857 // hard to do so.
858 if (new_decision.boolean_literal_index != kNoLiteralIndex) {
859 decision = new_decision.boolean_literal_index;
860 } else {
861 decision =
862 encoder_->GetOrCreateAssociatedLiteral(new_decision.integer_literal)
863 .Index();
864 }
865 if (sat_solver_->Assignment().LiteralIsAssigned(Literal(decision))) {
866 // TODO(user): It would be nicer if this can never happen. For now, it
867 // does because of the Propagate() not reaching the fixed point as
868 // mentionned in a TODO above. As a work-around, we display a message
869 // but do not crash and recall the decision heuristic.
870 VLOG(1) << "Trying to take a decision that is already assigned!"
871 << " Fix this. Continuing for now...";
872 continue;
873 }
874 break;
875 }
876 return decision;
877}
878
880 // Record the changelist and objective bounds for updating pseudo costs.
881 const std::vector<PseudoCosts::VariableBoundChange> bound_changes =
882 GetBoundChanges(decision.Index(), model_);
883 IntegerValue old_obj_lb = kMinIntegerValue;
884 IntegerValue old_obj_ub = kMaxIntegerValue;
885 if (objective_var_ != kNoIntegerVariable) {
886 old_obj_lb = integer_trail_->LowerBound(objective_var_);
887 old_obj_ub = integer_trail_->UpperBound(objective_var_);
888 }
889 const int old_level = sat_solver_->CurrentDecisionLevel();
890
891 // Note that kUnsatTrailIndex might also mean ASSUMPTIONS_UNSAT.
892 //
893 // TODO(user): on some problems, this function can be quite long. Expand
894 // so that we can check the time limit at each step?
895 const int index = sat_solver_->EnqueueDecisionAndBackjumpOnConflict(decision);
896 if (index == kUnsatTrailIndex) return false;
897
898 // Update the implied bounds each time we enqueue a literal at level zero.
899 // This is "almost free", so we might as well do it.
900 if (old_level == 0 && sat_solver_->CurrentDecisionLevel() == 1) {
901 implied_bounds_->ProcessIntegerTrail(decision);
902 }
903
904 // Update the pseudo costs.
905 if (sat_solver_->CurrentDecisionLevel() > old_level &&
906 objective_var_ != kNoIntegerVariable) {
907 const IntegerValue new_obj_lb = integer_trail_->LowerBound(objective_var_);
908 const IntegerValue new_obj_ub = integer_trail_->UpperBound(objective_var_);
909 const IntegerValue objective_bound_change =
910 (new_obj_lb - old_obj_lb) + (old_obj_ub - new_obj_ub);
911 pseudo_costs_->UpdateCost(bound_changes, objective_bound_change);
912 }
913
914 sat_solver_->AdvanceDeterministicTime(time_limit_);
915 return sat_solver_->ReapplyAssumptionsIfNeeded();
916}
917
919 TimeLimit* time_limit = model->GetOrCreate<TimeLimit>();
921
922 SearchHeuristics& heuristics = *model->GetOrCreate<SearchHeuristics>();
923 const int num_policies = heuristics.decision_policies.size();
924 CHECK_NE(num_policies, 0);
925 CHECK_EQ(num_policies, heuristics.restart_policies.size());
926
927 auto* helper = model->GetOrCreate<IntegerSearchHelper>();
928
929 // This is needed for recording the pseudo-costs.
930 IntegerVariable objective_var = kNoIntegerVariable;
931 {
932 const ObjectiveDefinition* objective = model->Get<ObjectiveDefinition>();
933 if (objective != nullptr) objective_var = objective->objective_var;
934 }
935
936 // Note that it is important to do the level-zero propagation if it wasn't
937 // already done because EnqueueDecisionAndBackjumpOnConflict() assumes that
938 // the solver is in a "propagated" state.
939 SatSolver* const sat_solver = model->GetOrCreate<SatSolver>();
940
941 // TODO(user): We have the issue that at level zero. calling the propagation
942 // loop more than once can propagate more! This is because we call the LP
943 // again and again on each level zero propagation. This is causing some
944 // CHECKs() to fail in multithread (rarely) because when we associate new
945 // literals to integer ones, Propagate() is indirectly called. Not sure yet
946 // how to fix.
947 if (!sat_solver->FinishPropagation()) return sat_solver->UnsatStatus();
948
949 auto* prober = model->GetOrCreate<Prober>();
950
951 const SatParameters& sat_parameters = *(model->GetOrCreate<SatParameters>());
952
953 // Main search loop.
954 const int64_t old_num_conflicts = sat_solver->num_failures();
955 const int64_t conflict_limit = sat_parameters.max_number_of_conflicts();
956 int64_t num_decisions_since_last_lp_record_ = 0;
957 int64_t num_decisions_without_probing = 0;
958 while (!time_limit->LimitReached() &&
959 (sat_solver->num_failures() - old_num_conflicts < conflict_limit)) {
960 // If needed, restart and switch decision_policy.
961 if (heuristics.restart_policies[heuristics.policy_index]()) {
962 if (!sat_solver->RestoreSolverToAssumptionLevel()) {
963 return sat_solver->UnsatStatus();
964 }
965 heuristics.policy_index = (heuristics.policy_index + 1) % num_policies;
966 }
967
968 if (!helper->BeforeTakingDecision()) return sat_solver->UnsatStatus();
969
970 LiteralIndex decision = kNoLiteralIndex;
971 while (true) {
972 if (heuristics.next_decision_override != nullptr) {
973 // Note that to properly count the num_times, we do not want to move
974 // this function, but actually call that copy.
975 decision = helper->GetDecision(heuristics.next_decision_override);
976 if (decision == kNoLiteralIndex) {
977 heuristics.next_decision_override = nullptr;
978 }
979 }
980 if (decision == kNoLiteralIndex) {
981 decision = helper->GetDecision(
982 heuristics.decision_policies[heuristics.policy_index]);
983 }
984
985 // Probing?
986 //
987 // TODO(user): Be smarter about what variables we probe, we can
988 // also do more than one.
989 if (decision != kNoLiteralIndex &&
990 sat_solver->CurrentDecisionLevel() == 0 &&
991 sat_parameters.probing_period_at_root() > 0 &&
992 ++num_decisions_without_probing >=
993 sat_parameters.probing_period_at_root()) {
994 num_decisions_without_probing = 0;
995 if (!prober->ProbeOneVariable(Literal(decision).Variable())) {
997 }
998 DCHECK_EQ(sat_solver->CurrentDecisionLevel(), 0);
999
1000 // We need to check after the probing that the literal is not fixed,
1001 // otherwise we just go to the next decision.
1002 if (sat_solver->Assignment().LiteralIsAssigned(Literal(decision))) {
1003 continue;
1004 }
1005 }
1006 break;
1007 }
1008
1009 // No decision means that we reached a leave of the search tree and that
1010 // we have a feasible solution.
1011 //
1012 // Tricky: If the time limit is reached during the final propagation when
1013 // all variables are fixed, there is no guarantee that the propagation
1014 // responsible for testing the validity of the solution was run to
1015 // completion. So we cannot report a feasible solution.
1017 if (decision == kNoLiteralIndex) {
1018 // Save the current polarity of all Booleans in the solution. It will be
1019 // followed for the next SAT decisions. This is known to be a good policy
1020 // for optimization problem. Note that for decision problem we don't care
1021 // since we are just done as soon as a solution is found.
1022 //
1023 // This idea is kind of "well known", see for instance the "LinSBPS"
1024 // submission to the maxSAT 2018 competition by Emir Demirovic and Peter
1025 // Stuckey where they show it is a good idea and provide more references.
1026 if (model->GetOrCreate<SatParameters>()->use_optimization_hints()) {
1027 auto* sat_decision = model->GetOrCreate<SatDecisionPolicy>();
1028 const auto& trail = *model->GetOrCreate<Trail>();
1029 for (int i = 0; i < trail.Index(); ++i) {
1030 sat_decision->SetAssignmentPreference(trail[i], 0.0);
1031 }
1032 }
1033 return SatSolver::FEASIBLE;
1034 }
1035
1036 if (!helper->TakeDecision(Literal(decision))) {
1037 return sat_solver->UnsatStatus();
1038 }
1039
1040 // TODO(user): Experiment more around dynamically changing the
1041 // threshold for storing LP solutions in the pool. Alternatively expose
1042 // this as parameter so this can be tuned later.
1043 //
1044 // TODO(user): Avoid adding the same solution many time if the LP didn't
1045 // change. Avoid adding solution that are too deep in the tree (most
1046 // variable fixed). Also use a callback rather than having this here, we
1047 // don't want this file to depend on cp_model.proto.
1048 if (model->Get<SharedLPSolutionRepository>() != nullptr) {
1049 num_decisions_since_last_lp_record_++;
1050 if (num_decisions_since_last_lp_record_ >= 100) {
1051 // NOTE: We can actually record LP solutions more frequently. However
1052 // this process is time consuming and workers waste a lot of time doing
1053 // this. To avoid this we don't record solutions after each decision.
1055 num_decisions_since_last_lp_record_ = 0;
1056 }
1057 }
1058 }
1059 return SatSolver::Status::LIMIT_REACHED;
1060}
1061
1063 const std::vector<Literal>& assumptions, Model* model) {
1064 SatSolver* const solver = model->GetOrCreate<SatSolver>();
1065
1066 // Sync the bound first.
1067 if (!solver->ResetToLevelZero()) return solver->UnsatStatus();
1068 auto* level_zero_callbacks = model->GetOrCreate<LevelZeroCallbackHelper>();
1069 for (const auto& cb : level_zero_callbacks->callbacks) {
1070 if (!cb()) {
1071 solver->NotifyThatModelIsUnsat();
1072 return solver->UnsatStatus();
1073 }
1074 }
1075
1076 // Add the assumptions if any and solve.
1077 if (!solver->ResetWithGivenAssumptions(assumptions)) {
1078 return solver->UnsatStatus();
1079 }
1080 return SolveIntegerProblem(model);
1081}
1082
1084 const IntegerVariable num_vars =
1085 model->GetOrCreate<IntegerTrail>()->NumIntegerVariables();
1086 std::vector<IntegerVariable> all_variables;
1087 for (IntegerVariable var(0); var < num_vars; ++var) {
1088 all_variables.push_back(var);
1089 }
1090
1091 SearchHeuristics& heuristics = *model->GetOrCreate<SearchHeuristics>();
1092 heuristics.policy_index = 0;
1093 heuristics.decision_policies = {SequentialSearch(
1095 FirstUnassignedVarAtItsMinHeuristic(all_variables, model)})};
1097 return ResetAndSolveIntegerProblem(/*assumptions=*/{}, model);
1098}
1099
1101 Model* model)
1102 : model_(model),
1103 sat_solver_(model->GetOrCreate<SatSolver>()),
1104 time_limit_(model->GetOrCreate<TimeLimit>()),
1105 trail_(model->GetOrCreate<Trail>()),
1106 integer_trail_(model->GetOrCreate<IntegerTrail>()),
1107 encoder_(model->GetOrCreate<IntegerEncoder>()),
1108 parameters_(*(model->GetOrCreate<SatParameters>())),
1109 level_zero_callbacks_(model->GetOrCreate<LevelZeroCallbackHelper>()),
1110 prober_(model->GetOrCreate<Prober>()),
1111 shared_response_manager_(model->Mutable<SharedResponseManager>()),
1112 shared_bounds_manager_(model->Mutable<SharedBoundsManager>()),
1113 active_limit_(parameters_.shaving_search_deterministic_time()) {
1114 auto* mapping = model_->GetOrCreate<CpModelMapping>();
1115 absl::flat_hash_set<BooleanVariable> visited;
1116 for (int v = 0; v < model_proto.variables_size(); ++v) {
1117 if (mapping->IsBoolean(v)) {
1118 const BooleanVariable bool_var = mapping->Literal(v).Variable();
1119 const auto [_, inserted] = visited.insert(bool_var);
1120 if (inserted) {
1121 bool_vars_.push_back(bool_var);
1122 }
1123 } else {
1124 IntegerVariable var = mapping->Integer(v);
1125 if (integer_trail_->IsFixed(var)) continue;
1126 int_vars_.push_back(var);
1127 }
1128 }
1129 VLOG(2) << "Start continuous probing with " << bool_vars_.size()
1130 << " Boolean variables, and " << int_vars_.size()
1131 << " integer variables"
1132 << ", deterministic time limit = "
1133 << time_limit_->GetDeterministicLimit() << " on " << model_->Name();
1134 last_logging_time_ = absl::Now();
1135}
1136
1137// Continuous probing procedure.
1138// TODO(user):
1139// - sort variables before the iteration (statically or dynamically)
1140// - compress clause databases regularly (especially the implication graph)
1141// - better interleaving of the probing and shaving phases
1142// - move the shaving code directly in the probing class
1143// - probe all variables and not just the model ones
1145 // Backtrack to level 0 in case we are not there.
1146 if (!sat_solver_->ResetToLevelZero()) return SatSolver::INFEASIBLE;
1147
1148 while (!time_limit_->LimitReached()) {
1149 // Run sat in-processing to reduce the size of the clause database.
1150 if (parameters_.use_sat_inprocessing() &&
1152 return SatSolver::INFEASIBLE;
1153 }
1154
1155 // Probe each Boolean variable at most once per loop.
1156 probed_bool_vars_.clear();
1157 probed_literals_.clear();
1158
1159 // Store current statistics to detect an iteration without any improvement.
1160 const int64_t initial_num_literals_fixed =
1161 prober_->num_new_literals_fixed();
1162 const int64_t initial_num_bounds_shaved = num_bounds_shaved_;
1163
1164 // Probe variable bounds.
1165 // TODO(user): Probe optional variables.
1166 for (; current_int_var_ < int_vars_.size(); ++current_int_var_) {
1167 const IntegerVariable int_var = int_vars_[current_int_var_];
1168 if (integer_trail_->IsFixed(int_var) ||
1169 integer_trail_->IsOptional(int_var)) {
1170 continue;
1171 }
1172
1173 if (!ImportFromSharedClasses()) {
1174 return SatSolver::INFEASIBLE;
1175 }
1176
1177 if (time_limit_->LimitReached()) {
1179 }
1180
1181 const BooleanVariable shave_lb =
1182 encoder_
1184 int_var, integer_trail_->LowerBound(int_var)))
1185 .Variable();
1186 const auto [_lb, lb_inserted] = probed_bool_vars_.insert(shave_lb);
1187 if (lb_inserted) {
1188 if (!prober_->ProbeOneVariable(shave_lb)) {
1189 return SatSolver::INFEASIBLE;
1190 }
1191 num_literals_probed_++;
1192 }
1193
1194 const BooleanVariable shave_ub =
1195 encoder_
1197 int_var, integer_trail_->UpperBound(int_var)))
1198 .Variable();
1199 const auto [_ub, ub_inserted] = probed_bool_vars_.insert(shave_ub);
1200 if (ub_inserted) {
1201 if (!prober_->ProbeOneVariable(shave_ub)) {
1202 return SatSolver::INFEASIBLE;
1203 }
1204 num_literals_probed_++;
1205 }
1206
1207 if (parameters_.use_shaving_in_probing_search()) {
1208 const SatSolver::Status lb_status =
1209 ShaveLiteral(Literal(shave_lb, true));
1210 if (ReportStatus(lb_status)) return lb_status;
1211
1212 const SatSolver::Status ub_status =
1213 ShaveLiteral(Literal(shave_ub, true));
1214 if (ReportStatus(ub_status)) return ub_status;
1215 }
1216
1217 LogStatistics();
1218 }
1219
1220 // Probe Boolean variables from the model.
1221 for (; current_bool_var_ < bool_vars_.size(); ++current_bool_var_) {
1222 const BooleanVariable& bool_var = bool_vars_[current_bool_var_];
1223
1224 if (sat_solver_->Assignment().VariableIsAssigned(bool_var)) continue;
1225
1226 if (!ImportFromSharedClasses()) {
1227 return SatSolver::INFEASIBLE;
1228 }
1229
1230 if (time_limit_->LimitReached()) {
1232 }
1233
1234 const auto [_, inserted] = probed_bool_vars_.insert(bool_var);
1235 if (inserted) {
1236 if (!prober_->ProbeOneVariable(bool_var)) {
1237 return SatSolver::INFEASIBLE;
1238 }
1239 num_literals_probed_++;
1240 }
1241
1242 const Literal literal(bool_var, true);
1243 if (parameters_.use_shaving_in_probing_search() &&
1244 !sat_solver_->Assignment().LiteralIsAssigned(literal)) {
1245 const SatSolver::Status true_status = ShaveLiteral(literal);
1246 if (ReportStatus(true_status)) return true_status;
1247 if (true_status == SatSolver::ASSUMPTIONS_UNSAT) continue;
1248
1249 const SatSolver::Status false_status = ShaveLiteral(literal.Negated());
1250 if (ReportStatus(false_status)) return false_status;
1251 }
1252
1253 LogStatistics();
1254 }
1255
1256 // Adjust the active_limit.
1257 {
1258 const double deterministic_time =
1259 parameters_.shaving_search_deterministic_time();
1260 const bool something_has_been_detected =
1261 num_bounds_shaved_ != initial_num_bounds_shaved ||
1262 prober_->num_new_literals_fixed() != initial_num_literals_fixed;
1263 if (something_has_been_detected) { // Reset the limit.
1264 active_limit_ = deterministic_time;
1265 } else if (active_limit_ < 25 * deterministic_time) { // Bump the limit.
1266 active_limit_ += deterministic_time;
1267 }
1268 }
1269
1270 ++iteration_;
1271 current_bool_var_ = 0;
1272 current_int_var_ = 0;
1273 }
1275}
1276
1277bool ContinuousProber::ImportFromSharedClasses() {
1278 if (!sat_solver_->ResetToLevelZero()) return false;
1279 for (const auto& cb : level_zero_callbacks_->callbacks) {
1280 if (!cb()) {
1281 sat_solver_->NotifyThatModelIsUnsat();
1282 return false;
1283 }
1284 }
1285 return true;
1286}
1287
1288SatSolver::Status ContinuousProber::ShaveLiteral(Literal literal) {
1289 const auto [_, inserted] = probed_literals_.insert(literal.Index());
1290 if (trail_->Assignment().LiteralIsAssigned(literal) || !inserted) {
1292 }
1293 num_bounds_tried_++;
1294
1295 const double original_dtime_limit = time_limit_->GetDeterministicLimit();
1296 time_limit_->ChangeDeterministicLimit(
1297 std::min(original_dtime_limit,
1298 time_limit_->GetElapsedDeterministicTime() + active_limit_));
1301 time_limit_->ChangeDeterministicLimit(original_dtime_limit);
1302
1304 num_bounds_shaved_++;
1305 }
1306
1307 return status;
1308}
1309
1310bool ContinuousProber::ReportStatus(const SatSolver::Status status) {
1312}
1313
1314void ContinuousProber::LogStatistics() {
1315 if (shared_response_manager_ == nullptr ||
1316 shared_bounds_manager_ == nullptr) {
1317 return;
1318 }
1319 shared_response_manager_->LogPeriodicMessage(
1320 "Probe",
1321 absl::StrCat("#iterations:", iteration_, " #literals fixed/probed:",
1322 prober_->num_new_literals_fixed(), "/", num_literals_probed_,
1323 " #bounds shaved/tried:", num_bounds_shaved_, "/",
1324 num_bounds_tried_, " #new_integer_bounds:",
1325 shared_bounds_manager_->NumBoundsExported("probing"),
1326 ", #new_binary_clauses:", prober_->num_new_binary_clauses()),
1327 &last_logging_time_);
1328}
1329
1330} // namespace sat
1331} // 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 DCHECK_LE(val1, val2)
Definition: base/logging.h:893
#define CHECK_LT(val1, val2)
Definition: base/logging.h:706
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define DCHECK(condition)
Definition: base/logging.h:890
#define DCHECK_EQ(val1, val2)
Definition: base/logging.h:891
#define VLOG(verboselevel)
Definition: base/logging.h:984
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
ContinuousProber(const CpModelProto &model_proto, Model *model)
sat::Literal Literal(int ref) const
void ProcessIntegerTrail(Literal first_decision)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
Definition: integer.cc:238
LiteralIndex GetDecision(const std::function< BooleanOrIntegerLiteral()> &f)
IntegerVariable FirstUnassignedVariable() const
Definition: integer.cc:1265
bool IsCurrentlyIgnored(IntegerVariable i) const
Definition: integer.h:705
bool IsFixed(IntegerVariable i) const
Definition: integer.h:1453
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
Definition: integer.cc:1232
IntegerValue UpperBound(IntegerVariable i) const
Definition: integer.h:1449
IntegerValue LowerBound(IntegerVariable i) const
Definition: integer.h:1445
bool IsOptional(IntegerVariable i) const
Definition: integer.h:702
LiteralIndex Index() const
Definition: sat_base.h:87
BooleanVariable Variable() const
Definition: sat_base.h:83
std::string DebugString() const
Definition: sat_base.h:96
Class that owns everything related to a particular optimization model.
Definition: sat/model.h:42
const std::string & Name() const
Definition: sat/model.h:179
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
Definition: sat/model.h:110
int num_new_binary_clauses() const
Definition: probing.h:87
bool ProbeOneVariable(BooleanVariable b)
Definition: probing.cc:190
int num_new_literals_fixed() const
Definition: probing.h:86
void UpdateCost(const std::vector< VariableBoundChange > &bound_changes, IntegerValue obj_bound_improvement)
Definition: pseudo_costs.cc:49
const VariablesAssignment & Assignment() const
Definition: sat_solver.h:378
void AdvanceDeterministicTime(TimeLimit *limit)
Definition: sat_solver.h:444
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
Definition: sat_solver.cc:536
void Backtrack(int target_level)
Definition: sat_solver.cc:991
bool ResetWithGivenAssumptions(const std::vector< Literal > &assumptions)
Definition: sat_solver.cc:587
int NumBoundsExported(const std::string &worker_name)
void LogPeriodicMessage(const std::string &prefix, const std::string &message, absl::Time *last_logging_time)
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
const VariablesAssignment & Assignment() const
Definition: sat_base.h:383
bool LiteralIsAssigned(Literal literal) const
Definition: sat_base.h:156
bool VariableIsAssigned(BooleanVariable var) const
Definition: sat_base.h:161
SatParameters parameters
SharedRelaxationSolutionRepository * relaxation_solutions
CpModelProto const * model_proto
ModelSharedTimeLimit * time_limit
const Constraint * ct
int64_t value
IntVar * var
Definition: expr_array.cc:1874
absl::Status status
Definition: g_gurobi.cc:35
GRBmodel * model
int index
const Collection::value_type::second_type & FindWithDefault(const Collection &collection, const typename Collection::value_type::first_type &key, const typename Collection::value_type::second_type &value)
Definition: map_util.h:29
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
Definition: sat/util.cc:59
std::vector< std::function< BooleanOrIntegerLiteral()> > CompleteHeuristics(const std::vector< std::function< BooleanOrIntegerLiteral()> > &incomplete_heuristics, const std::function< BooleanOrIntegerLiteral()> &completion_heuristic)
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
std::function< BooleanOrIntegerLiteral()> SequentialValueSelection(std::vector< std::function< IntegerLiteral(IntegerVariable)> > value_selection_heuristics, std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
const LiteralIndex kNoLiteralIndex(-1)
IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail *integer_trail)
void RecordLPRelaxationValues(Model *model)
Definition: rins.cc:33
IntegerLiteral GreaterOrEqualToMiddleValue(IntegerVariable var, IntegerTrail *integer_trail)
IntegerLiteral SplitAroundGivenValue(IntegerVariable var, IntegerValue value, Model *model)
std::function< BooleanOrIntegerLiteral()> UnassignedVarWithLowestMinAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
SatSolver::Status SolveIntegerProblemWithLazyEncoding(Model *model)
std::function< bool()> SatSolverRestartPolicy(Model *model)
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> FollowHint(const std::vector< BooleanOrIntegerVariable > &vars, const std::vector< IntegerValue > &values, Model *model)
std::function< bool()> RestartEveryKFailures(int k, SatSolver *solver)
std::function< BooleanOrIntegerLiteral()> SchedulingSearchHeuristic(Model *model)
IntegerLiteral ChooseBestObjectiveValue(IntegerVariable var, Model *model)
void ConfigureSearchHeuristics(Model *model)
IntegerVariable PositiveVariable(IntegerVariable i)
Definition: integer.h:149
std::function< BooleanOrIntegerLiteral()> IntegerValueSelectionHeuristic(std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
SatSolver::Status SolveIntegerProblem(Model *model)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
std::vector< PseudoCosts::VariableBoundChange > GetBoundChanges(LiteralIndex decision, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
Definition: integer.h:1669
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
Definition: integer.cc:47
IntegerLiteral SplitAroundLpValue(IntegerVariable var, Model *model)
IntegerLiteral SplitUsingBestSolutionValueInRepository(IntegerVariable var, const SharedSolutionRepository< int64_t > &solution_repo, Model *model)
const BooleanVariable kNoBooleanVariable(-1)
bool LinearizedPartIsLarge(Model *model)
const int kUnsatTrailIndex
Definition: sat_solver.h:56
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(Model *model)
Collection of objects used to extend the Constraint Solver library.
Literal literal
Definition: optimization.cc:89
int64_t time
Definition: resource.cc:1693
Rev< int64_t > start_min
Rev< int64_t > end_min
std::optional< int64_t > end
int64_t start
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::function< bool()> > callbacks
std::vector< std::function< bool()> > restart_policies
std::function< BooleanOrIntegerLiteral()> hint_search
std::function< BooleanOrIntegerLiteral()> fixed_search
std::function< BooleanOrIntegerLiteral()> next_decision_override
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies