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/random/distributions.h"
28#include "absl/strings/str_cat.h"
29#include "absl/strings/string_view.h"
31#include "ortools/sat/cp_model.pb.h"
38#include "ortools/sat/sat_parameters.pb.h"
44ABSL_FLAG(
bool, cp_model_use_max_hs,
false,
"Use max_hs in search portfolio.");
93 int64_t
value)
const {
109 int64_t
value)
const {
119 IntegerValue(
value));
130 const IntegerVariable variable = mapping_.
Integer(
var);
133 std::vector<ValueLiteralPair> encoding =
135 std::sort(encoding.begin(), encoding.end(),
137 std::vector<Literal> unassigned_sorted_literals;
138 for (
const auto& p : encoding) {
140 unassigned_sorted_literals.push_back(p.literal);
146 const int target = (unassigned_sorted_literals.size() + 1) / 2 - 1;
161bool ModelHasSchedulingConstraints(
const CpModelProto& cp_model_proto) {
162 for (
const ConstraintProto&
ct : cp_model_proto.constraints()) {
163 if (
ct.constraint_case() == ConstraintProto::kNoOverlap)
return true;
164 if (
ct.constraint_case() == ConstraintProto::kCumulative)
return true;
172 const std::vector<DecisionStrategyProto>& strategies,
Model*
model) {
179 return [&view, &
parameters, random, strategies]() {
180 for (
const DecisionStrategyProto& strategy : strategies) {
188 std::vector<VarValue> active_refs;
191 for (
int i = 0; i < strategy.variables().size(); ++i) {
192 const int ref = strategy.variables(i);
194 if (view.IsFixed(
var) || view.IsCurrentlyFree(
var))
continue;
198 while (t_index < strategy.transformations().size() &&
199 strategy.transformations(t_index).index() < i) {
202 if (t_index < strategy.transformations_size() &&
203 strategy.transformations(t_index).index() == i) {
204 coeff = strategy.transformations(t_index).positive_coeff();
205 offset = strategy.transformations(t_index).offset();
213 int64_t lb = view.Min(
var);
214 int64_t ub = view.Max(
var);
219 switch (strategy.variable_selection_strategy()) {
220 case DecisionStrategyProto::CHOOSE_FIRST:
222 case DecisionStrategyProto::CHOOSE_LOWEST_MIN:
225 case DecisionStrategyProto::CHOOSE_HIGHEST_MAX:
228 case DecisionStrategyProto::CHOOSE_MIN_DOMAIN_SIZE:
231 case DecisionStrategyProto::CHOOSE_MAX_DOMAIN_SIZE:
235 LOG(
FATAL) <<
"Unknown VariableSelectionStrategy "
236 << strategy.variable_selection_strategy();
238 if (
value < candidate_value) {
240 candidate_value =
value;
242 if (strategy.variable_selection_strategy() ==
243 DecisionStrategyProto::CHOOSE_FIRST &&
248 candidate_value +
parameters.search_randomization_tolerance()) {
249 active_refs.push_back({ref,
value});
256 CHECK(!active_refs.empty());
257 const IntegerValue threshold(
258 candidate_value +
parameters.search_randomization_tolerance());
259 auto is_above_tolerance = [threshold](
const VarValue& entry) {
260 return entry.value > threshold;
263 active_refs.erase(std::remove_if(active_refs.begin(), active_refs.end(),
266 const int winner = absl::Uniform<int>(*random, 0, active_refs.size());
267 candidate = active_refs[winner].ref;
270 DecisionStrategyProto::DomainReductionStrategy selection =
271 strategy.domain_reduction_strategy();
274 case DecisionStrategyProto::SELECT_MIN_VALUE:
275 selection = DecisionStrategyProto::SELECT_MAX_VALUE;
277 case DecisionStrategyProto::SELECT_MAX_VALUE:
278 selection = DecisionStrategyProto::SELECT_MIN_VALUE;
280 case DecisionStrategyProto::SELECT_LOWER_HALF:
281 selection = DecisionStrategyProto::SELECT_UPPER_HALF;
283 case DecisionStrategyProto::SELECT_UPPER_HALF:
284 selection = DecisionStrategyProto::SELECT_LOWER_HALF;
292 const int64_t lb = view.Min(
var);
293 const int64_t ub = view.Max(
var);
295 case DecisionStrategyProto::SELECT_MIN_VALUE:
296 return view.LowerOrEqual(
var, lb);
297 case DecisionStrategyProto::SELECT_MAX_VALUE:
298 return view.GreaterOrEqual(
var, ub);
299 case DecisionStrategyProto::SELECT_LOWER_HALF:
300 return view.LowerOrEqual(
var, lb + (ub - lb) / 2);
301 case DecisionStrategyProto::SELECT_UPPER_HALF:
302 return view.GreaterOrEqual(
var, ub - (ub - lb) / 2);
303 case DecisionStrategyProto::SELECT_MEDIAN_VALUE:
304 return view.MedianValue(
var);
306 LOG(
FATAL) <<
"Unknown DomainReductionStrategy "
307 << strategy.domain_reduction_strategy();
315 const CpModelProto& cp_model_proto,
316 const std::vector<IntegerVariable>& variable_mapping,
317 IntegerVariable objective_var,
Model*
model) {
322 std::vector<DecisionStrategyProto> strategies;
323 for (
const DecisionStrategyProto&
proto :
324 cp_model_proto.search_strategy()) {
325 strategies.push_back(
proto);
332 if (ModelHasSchedulingConstraints(cp_model_proto)) {
337 if (
model->GetOrCreate<SatParameters>()->instantiate_all_variables()) {
338 std::vector<IntegerVariable> decisions;
339 for (
const IntegerVariable
var : variable_mapping) {
344 decisions.push_back(objective_var);
346 decisions.push_back(
var);
356 const CpModelProto& cp_model_proto,
357 const std::vector<IntegerVariable>& variable_mapping,
360 std::vector<int> ref_to_display;
361 for (
int i = 0; i < cp_model_proto.variables_size(); ++i) {
363 if (cp_model_proto.variables(i).name().empty())
continue;
364 ref_to_display.push_back(i);
366 std::sort(ref_to_display.begin(), ref_to_display.end(), [&](
int i,
int j) {
367 return cp_model_proto.variables(i).name() <
368 cp_model_proto.variables(j).name();
371 std::vector<std::pair<int64_t, int64_t>> old_domains(variable_mapping.size());
372 return [instrumented_strategy,
model, variable_mapping, cp_model_proto,
373 old_domains, ref_to_display]()
mutable {
375 if (!decision.
HasValue())
return decision;
379 LOG(
INFO) <<
"Boolean decision " << l;
382 LOG(
INFO) <<
" - associated with " << i_lit;
387 const int level =
model->Get<
Trail>()->CurrentDecisionLevel();
388 std::string to_display =
389 absl::StrCat(
"Diff since last call, level=", level,
"\n");
391 for (
const int ref : ref_to_display) {
392 const IntegerVariable
var = variable_mapping[ref];
393 const std::pair<int64_t, int64_t> new_domain(
396 if (new_domain != old_domains[ref]) {
397 absl::StrAppend(&to_display, cp_model_proto.variables(ref).name(),
" [",
398 old_domains[ref].first,
",", old_domains[ref].second,
399 "] -> [", new_domain.first,
",", new_domain.second,
401 old_domains[ref] = new_domain;
417 const SatParameters& base_params,
const CpModelProto& cp_model) {
420 absl::flat_hash_map<std::string, SatParameters> strategies;
423 strategies[
"default"] = base_params;
427 SatParameters new_params = base_params;
428 new_params.set_linearization_level(0);
429 strategies[
"no_lp"] = new_params;
430 new_params.set_linearization_level(1);
431 strategies[
"default_lp"] = new_params;
432 new_params.set_linearization_level(2);
433 new_params.set_add_lp_constraints_lazily(
false);
434 strategies[
"max_lp"] = new_params;
444 SatParameters new_params = base_params;
445 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
446 new_params.set_optimize_with_core(
true);
447 new_params.set_linearization_level(0);
448 strategies[
"core"] = new_params;
453 SatParameters new_params = base_params;
454 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
455 new_params.set_optimize_with_core(
true);
456 new_params.set_linearization_level(1);
457 strategies[
"core_default_lp"] = new_params;
461 SatParameters new_params = base_params;
462 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
463 new_params.set_optimize_with_core(
true);
464 new_params.set_linearization_level(2);
465 strategies[
"core_max_lp"] = new_params;
469 SatParameters new_params = base_params;
470 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
471 new_params.set_optimize_with_core(
true);
472 new_params.set_optimize_with_max_hs(
true);
473 strategies[
"max_hs"] = new_params;
477 SatParameters new_params = base_params;
478 new_params.set_optimize_with_lb_tree_search(
true);
479 new_params.set_linearization_level(2);
483 new_params.set_share_objective_bounds(
false);
484 strategies[
"lb_tree_search"] = new_params;
488 SatParameters new_params = base_params;
489 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
490 new_params.set_use_probing_search(
true);
491 strategies[
"probing"] = new_params;
496 SatParameters new_params = base_params;
497 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
498 strategies[
"auto"] = new_params;
500 new_params.set_search_branching(SatParameters::FIXED_SEARCH);
501 strategies[
"fixed"] = new_params;
503 new_params.set_search_branching(
504 SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH);
505 strategies[
"quick_restart"] = new_params;
507 new_params.set_search_branching(
508 SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH);
509 new_params.set_linearization_level(0);
510 strategies[
"quick_restart_no_lp"] = new_params;
513 new_params.set_linearization_level(2);
514 new_params.set_search_branching(SatParameters::LP_SEARCH);
515 strategies[
"reduced_costs"] = new_params;
518 new_params.set_linearization_level(2);
519 new_params.set_search_branching(SatParameters::PSEUDO_COST_SEARCH);
520 new_params.set_exploit_best_solution(
true);
521 strategies[
"pseudo_costs"] = new_params;
526 SatParameters new_params = base_params;
527 new_params.set_boolean_encoding_level(0);
528 strategies[
"less_encoding"] = new_params;
532 for (
const SatParameters& params : base_params.subsolver_params()) {
533 strategies[params.name()] = params;
542 const bool use_fixed_strategy = !cp_model.search_strategy().empty() ||
543 ModelHasSchedulingConstraints(cp_model);
549 std::vector<std::string> names;
551 if (base_params.reduce_memory_usage_in_interleave_mode() &&
552 base_params.interleave_search()) {
554 if (cp_model.has_objective()) {
555 names.push_back(
"default_lp");
556 names.push_back(use_fixed_strategy ?
"fixed" :
"pseudo_costs");
557 names.push_back(cp_model.objective().vars_size() > 1 ?
"core" :
"no_lp");
558 names.push_back(
"max_lp");
560 names.push_back(
"default_lp");
561 names.push_back(use_fixed_strategy ?
"fixed" :
"no_lp");
562 names.push_back(
"less_encoding");
563 names.push_back(
"max_lp");
564 names.push_back(
"quick_restart");
568 if (base_params.subsolvers().empty()) {
569 names.push_back(
"default_lp");
570 names.push_back(
"fixed");
571 names.push_back(
"less_encoding");
573 names.push_back(
"no_lp");
574 names.push_back(
"max_lp");
575 names.push_back(
"core");
577 names.push_back(
"reduced_costs");
578 names.push_back(
"pseudo_costs");
580 names.push_back(
"quick_restart");
581 names.push_back(
"quick_restart_no_lp");
582 names.push_back(
"lb_tree_search");
583 names.push_back(
"probing");
584#if !defined(__PORTABLE_PLATFORM__) && defined(USE_SCIP)
585 if (absl::GetFlag(FLAGS_cp_model_use_max_hs)) names.push_back(
"max_hs");
588 for (
const std::string&
name : base_params.subsolvers()) {
589 names.push_back(
name);
594 absl::flat_hash_set<std::string> to_ignore;
595 for (
const std::string&
name : base_params.ignore_subsolvers()) {
596 to_ignore.insert(
name);
599 for (
const std::string&
name : names) {
600 if (to_ignore.contains(
name))
continue;
601 names[new_size++] =
name;
603 names.resize(new_size);
607 std::vector<SatParameters> result;
608 for (
const std::string&
name : names) {
609 if (!strategies.contains(
name)) {
615 SatParameters params = strategies.at(
name);
618 if (!use_fixed_strategy &&
619 params.search_branching() == SatParameters::FIXED_SEARCH) {
622 if (cp_model.has_objective()) {
623 if (cp_model.objective().vars_size() <= 1 &&
624 params.optimize_with_core()) {
627 if (
name ==
"less_encoding")
continue;
629 if (params.optimize_with_core())
continue;
630 if (params.search_branching() == SatParameters::LP_SEARCH)
continue;
631 if (params.search_branching() == SatParameters::PSEUDO_COST_SEARCH) {
640 params.set_name(
name);
641 params.set_random_seed(base_params.random_seed() + result.size() + 1);
642 result.push_back(params);
645 if (cp_model.has_objective()) {
649 1, base_params.num_workers() - base_params.min_num_lns_workers());
650 if (!base_params.interleave_search() && result.size() > target) {
651 result.resize(target);
658 int target = base_params.num_workers();
659 if (!base_params.interleave_search() &&
660 (base_params.use_rins_lns() || base_params.use_relaxation_lns() ||
661 base_params.use_feasibility_pump())) {
662 target =
std::max(1, base_params.num_workers() - 1);
664 if (!base_params.interleave_search() && result.size() > target) {
665 result.resize(target);
669 while (result.size() < target) {
671 SatParameters new_params = base_params;
672 new_params.set_search_branching(SatParameters::FIXED_SEARCH);
673 new_params.set_randomize_search(
true);
674 new_params.set_search_randomization_tolerance(
index);
675 new_params.set_random_seed(base_params.random_seed() + result.size() + 1);
676 new_params.set_name(absl::StrCat(
"random_",
index));
677 result.push_back(new_params);
#define CHECK_NE(val1, val2)
#define DCHECK(condition)
An Assignment is a variable -> domains mapping, used to report solutions to the user.
bool IsBoolean(int ref) const
bool IsInteger(int ref) const
IntegerVariable Integer(int ref) const
sat::Literal Literal(int ref) const
int NumProtoVariables() const
bool IsFixed(int var) const
BooleanOrIntegerLiteral GreaterOrEqual(int var, int64_t value) const
bool IsCurrentlyFree(int var) const
BooleanOrIntegerLiteral MedianValue(int var) const
CpModelView(Model *model)
int64_t Max(int var) const
BooleanOrIntegerLiteral LowerOrEqual(int var, int64_t value) const
int64_t Min(int var) const
const InlinedIntegerLiteralVector & GetAllIntegerLiterals(Literal lit) const
bool VariableIsFullyEncoded(IntegerVariable var) const
std::vector< ValueLiteralPair > RawDomainEncoding(IntegerVariable var) const
bool IsCurrentlyIgnored(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LowerBound(IntegerVariable i) const
LiteralIndex NegatedIndex() const
LiteralIndex Index() const
BooleanVariable Variable() const
Class that owns everything related to a particular optimization model.
bool LiteralIsAssigned(Literal literal) const
bool VariableIsAssigned(BooleanVariable var) const
bool LiteralIsTrue(Literal literal) const
bool LiteralIsFalse(Literal literal) const
ABSL_FLAG(bool, cp_model_use_max_hs, false, "Use max_hs in search portfolio.")
const std::function< BooleanOrIntegerLiteral()> ConstructSearchStrategyInternal(const std::vector< DecisionStrategyProto > &strategies, Model *model)
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
bool RefIsPositive(int ref)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()> > heuristics)
const LiteralIndex kNoLiteralIndex(-1)
const IntegerVariable kNoIntegerVariable(-1)
std::function< BooleanOrIntegerLiteral()> SchedulingSearchHeuristic(Model *model)
std::vector< SatParameters > GetDiverseSetOfParameters(const SatParameters &base_params, const CpModelProto &cp_model)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< BooleanOrIntegerLiteral()> ConstructSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, IntegerVariable objective_var, Model *model)
std::function< BooleanOrIntegerLiteral()> InstrumentSearchStrategy(const CpModelProto &cp_model_proto, const std::vector< IntegerVariable > &variable_mapping, const std::function< BooleanOrIntegerLiteral()> &instrumented_strategy, Model *model)
Collection of objects used to extend the Constraint Solver library.
LiteralIndex boolean_literal_index
IntegerLiteral integer_literal
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)