185 const CpModelProto& cp_model_proto,
Model*
model) {
186 if (cp_model_proto.search_strategy().empty())
return nullptr;
188 std::vector<DecisionStrategyProto> strategies;
189 for (
const DecisionStrategyProto&
proto : cp_model_proto.search_strategy()) {
190 strategies.push_back(
proto);
198 return [&view, &
parameters, random, strategies]() {
199 for (
const DecisionStrategyProto& strategy : strategies) {
200 int candidate_ref = -1;
201 int64_t candidate_value = std::numeric_limits<int64_t>::max();
207 const bool randomize_decision =
208 parameters.search_random_variable_pool_size() > 1;
210 randomize_decision ?
parameters.search_random_variable_pool_size()
213 for (
const LinearExpressionProto& expr : strategy.exprs()) {
214 const int var = expr.vars(0);
215 if (view.IsFixed(
var))
continue;
217 int64_t coeff = expr.coeffs(0);
218 int64_t offset = expr.offset();
219 int64_t lb = view.Min(
var);
220 int64_t ub = view.Max(
var);
230 switch (strategy.variable_selection_strategy()) {
231 case DecisionStrategyProto::CHOOSE_FIRST:
233 case DecisionStrategyProto::CHOOSE_LOWEST_MIN:
234 value = coeff * lb + offset;
236 case DecisionStrategyProto::CHOOSE_HIGHEST_MAX:
237 value = -(coeff * ub + offset);
239 case DecisionStrategyProto::CHOOSE_MIN_DOMAIN_SIZE:
243 case DecisionStrategyProto::CHOOSE_MAX_DOMAIN_SIZE:
245 value = -(ub - lb + 1);
248 LOG(FATAL) <<
"Unknown VariableSelectionStrategy "
249 << strategy.variable_selection_strategy();
252 if (randomize_decision) {
255 const double noise = absl::Uniform(*random, 0., 1.0);
257 candidate_value = std::min(candidate_value,
value);
258 }
else if (
value < candidate_value) {
260 candidate_value =
value;
265 if (strategy.variable_selection_strategy() ==
266 DecisionStrategyProto::CHOOSE_FIRST &&
267 !randomize_decision) {
273 if (candidate_value == std::numeric_limits<int64_t>::max())
continue;
276 if (randomize_decision) {
278 candidate_ref = candidates[absl::Uniform(
279 *random, 0,
static_cast<int>(candidates.size()))];
282 DecisionStrategyProto::DomainReductionStrategy selection =
283 strategy.domain_reduction_strategy();
286 case DecisionStrategyProto::SELECT_MIN_VALUE:
287 selection = DecisionStrategyProto::SELECT_MAX_VALUE;
289 case DecisionStrategyProto::SELECT_MAX_VALUE:
290 selection = DecisionStrategyProto::SELECT_MIN_VALUE;
292 case DecisionStrategyProto::SELECT_LOWER_HALF:
293 selection = DecisionStrategyProto::SELECT_UPPER_HALF;
295 case DecisionStrategyProto::SELECT_UPPER_HALF:
296 selection = DecisionStrategyProto::SELECT_LOWER_HALF;
304 const int64_t lb = view.Min(
var);
305 const int64_t ub = view.Max(
var);
307 case DecisionStrategyProto::SELECT_MIN_VALUE:
308 return view.LowerOrEqual(
var, lb);
309 case DecisionStrategyProto::SELECT_MAX_VALUE:
310 return view.GreaterOrEqual(
var, ub);
311 case DecisionStrategyProto::SELECT_LOWER_HALF:
312 return view.LowerOrEqual(
var, lb + (ub - lb) / 2);
313 case DecisionStrategyProto::SELECT_UPPER_HALF:
314 return view.GreaterOrEqual(
var, ub - (ub - lb) / 2);
315 case DecisionStrategyProto::SELECT_MEDIAN_VALUE:
316 return view.MedianValue(
var);
318 LOG(FATAL) <<
"Unknown DomainReductionStrategy "
319 << strategy.domain_reduction_strategy();
422 const CpModelProto& cp_model_proto,
423 const std::vector<IntegerVariable>& variable_mapping,
426 std::vector<int> ref_to_display;
427 for (
int i = 0;
i < cp_model_proto.variables_size(); ++
i) {
429 if (cp_model_proto.variables(
i).name().empty())
continue;
430 ref_to_display.push_back(
i);
432 std::sort(ref_to_display.begin(), ref_to_display.end(), [&](
int i,
int j) {
433 return cp_model_proto.variables(i).name() <
434 cp_model_proto.variables(j).name();
437 std::vector<std::pair<int64_t, int64_t>> old_domains(variable_mapping.size());
438 return [instrumented_strategy,
model, &variable_mapping, &cp_model_proto,
439 old_domains, ref_to_display]()
mutable {
441 if (!decision.
HasValue())
return decision;
445 LOG(INFO) <<
"Boolean decision " << l;
447 for (
const IntegerLiteral i_lit : encoder->GetIntegerLiterals(l)) {
448 LOG(INFO) <<
" - associated with " << i_lit;
450 for (
const auto [
var,
value] : encoder->GetEqualityLiterals(l)) {
451 LOG(INFO) <<
" - associated with " <<
var <<
" == " <<
value;
456 const int level =
model->Get<
Trail>()->CurrentDecisionLevel();
457 std::string to_display =
458 absl::StrCat(
"Diff since last call, level=", level,
"\n");
460 for (
const int ref : ref_to_display) {
461 const IntegerVariable
var = variable_mapping[ref];
462 const std::pair<int64_t, int64_t> new_domain(
465 if (new_domain != old_domains[ref]) {
466 absl::StrAppend(&to_display, cp_model_proto.variables(ref).name(),
" [",
467 old_domains[ref].first,
",", old_domains[ref].second,
468 "] -> [", new_domain.first,
",", new_domain.second,
470 old_domains[ref] = new_domain;
473 LOG(INFO) << to_display;
479 SatParameters base_params) {
480 absl::flat_hash_map<std::string, SatParameters> strategies;
485 base_params.set_log_search_progress(
false);
488 strategies[
"default"] = base_params;
492 SatParameters new_params = base_params;
493 new_params.set_linearization_level(0);
494 strategies[
"no_lp"] = new_params;
495 new_params.set_linearization_level(1);
496 strategies[
"default_lp"] = new_params;
497 new_params.set_linearization_level(2);
498 new_params.set_add_lp_constraints_lazily(
false);
499 strategies[
"max_lp"] = new_params;
509 SatParameters new_params = base_params;
510 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
511 new_params.set_optimize_with_core(
true);
512 new_params.set_linearization_level(0);
513 strategies[
"core"] = new_params;
518 SatParameters new_params = base_params;
519 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
520 new_params.set_optimize_with_core(
true);
521 new_params.set_linearization_level(1);
522 strategies[
"core_default_lp"] = new_params;
526 SatParameters new_params = base_params;
527 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
528 new_params.set_optimize_with_core(
true);
529 new_params.set_linearization_level(2);
530 strategies[
"core_max_lp"] = new_params;
534 SatParameters new_params = base_params;
535 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
536 new_params.set_optimize_with_core(
true);
537 new_params.set_optimize_with_max_hs(
true);
538 strategies[
"max_hs"] = new_params;
542 SatParameters new_params = base_params;
543 new_params.set_optimize_with_lb_tree_search(
true);
546 new_params.set_share_objective_bounds(
false);
548 new_params.set_linearization_level(0);
549 strategies[
"lb_tree_search_no_lp"] = new_params;
551 new_params.set_linearization_level(2);
552 if (base_params.use_dual_scheduling_heuristics()) {
553 AddDualSchedulingHeuristics(new_params);
556 new_params.set_add_lp_constraints_lazily(
false);
557 new_params.set_root_lp_iterations(100'000);
558 strategies[
"lb_tree_search"] = new_params;
562 SatParameters new_params = base_params;
563 new_params.set_use_objective_lb_search(
true);
565 new_params.set_linearization_level(0);
566 strategies[
"objective_lb_search_no_lp"] = new_params;
568 new_params.set_linearization_level(1);
569 strategies[
"objective_lb_search"] = new_params;
571 if (base_params.use_dual_scheduling_heuristics()) {
572 AddDualSchedulingHeuristics(new_params);
574 new_params.set_linearization_level(2);
575 strategies[
"objective_lb_search_max_lp"] = new_params;
579 SatParameters new_params = base_params;
580 new_params.set_use_objective_shaving_search(
true);
581 new_params.set_cp_model_presolve(
true);
582 new_params.set_cp_model_probing_level(0);
583 new_params.set_symmetry_level(0);
584 if (base_params.use_dual_scheduling_heuristics()) {
585 AddDualSchedulingHeuristics(new_params);
588 strategies[
"objective_shaving"] = new_params;
590 new_params.set_linearization_level(0);
591 strategies[
"objective_shaving_no_lp"] = new_params;
593 new_params.set_linearization_level(2);
594 strategies[
"objective_shaving_max_lp"] = new_params;
598 SatParameters new_params = base_params;
599 new_params.set_use_variables_shaving_search(
true);
600 new_params.set_cp_model_presolve(
true);
601 new_params.set_cp_model_probing_level(0);
602 new_params.set_symmetry_level(0);
603 new_params.set_share_objective_bounds(
false);
604 new_params.set_share_level_zero_bounds(
false);
606 strategies[
"variables_shaving"] = new_params;
608 new_params.set_linearization_level(0);
609 strategies[
"variables_shaving_no_lp"] = new_params;
611 if (base_params.use_dual_scheduling_heuristics()) {
612 AddDualSchedulingHeuristics(new_params);
614 new_params.set_linearization_level(2);
615 strategies[
"variables_shaving_max_lp"] = new_params;
619 SatParameters new_params = base_params;
620 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
621 new_params.set_use_probing_search(
true);
622 new_params.set_at_most_one_max_expansion_size(2);
623 if (base_params.use_dual_scheduling_heuristics()) {
624 AddDualSchedulingHeuristics(new_params);
626 strategies[
"probing"] = new_params;
628 new_params.set_linearization_level(0);
629 strategies[
"probing_no_lp"] = new_params;
631 new_params.set_linearization_level(2);
633 new_params.set_add_lp_constraints_lazily(
false);
634 new_params.set_root_lp_iterations(100'000);
635 strategies[
"probing_max_lp"] = new_params;
640 SatParameters new_params = base_params;
641 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
642 strategies[
"auto"] = new_params;
644 new_params.set_search_branching(SatParameters::FIXED_SEARCH);
645 new_params.set_use_dynamic_precedence_in_disjunctive(
false);
646 new_params.set_use_dynamic_precedence_in_cumulative(
false);
647 strategies[
"fixed"] = new_params;
653 SatParameters new_params = base_params;
654 new_params.set_search_branching(
655 SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH);
656 strategies[
"quick_restart"] = new_params;
658 new_params.set_linearization_level(0);
659 strategies[
"quick_restart_no_lp"] = new_params;
661 new_params.set_linearization_level(2);
662 strategies[
"quick_restart_max_lp"] = new_params;
666 SatParameters new_params = base_params;
667 new_params.set_linearization_level(2);
668 new_params.set_search_branching(SatParameters::LP_SEARCH);
669 if (base_params.use_dual_scheduling_heuristics()) {
670 AddDualSchedulingHeuristics(new_params);
672 strategies[
"reduced_costs"] = new_params;
677 SatParameters new_params = base_params;
678 new_params.set_linearization_level(2);
679 new_params.set_search_branching(SatParameters::PSEUDO_COST_SEARCH);
680 new_params.set_exploit_best_solution(
true);
681 strategies[
"pseudo_costs"] = new_params;
686 SatParameters new_params = base_params;
687 new_params.set_boolean_encoding_level(0);
688 strategies[
"less_encoding"] = new_params;
693 SatParameters new_params = base_params;
694 new_params.set_use_shared_tree_search(
true);
695 new_params.set_search_branching(SatParameters::AUTOMATIC_SEARCH);
699 new_params.set_optimize_with_core(
false);
700 new_params.set_optimize_with_lb_tree_search(
false);
701 new_params.set_optimize_with_max_hs(
false);
703 strategies[
"shared_tree"] = new_params;
708 SatParameters new_params = base_params;
709 new_params.set_stop_after_first_solution(
false);
710 new_params.set_cp_model_presolve(
true);
713 new_params.set_use_sat_inprocessing(
false);
714 new_params.set_cp_model_probing_level(0);
715 new_params.set_symmetry_level(0);
716 new_params.set_find_big_linear_overlap(
false);
718 new_params.set_log_search_progress(
false);
719 new_params.set_debug_crash_on_bad_hint(
false);
720 new_params.set_solution_pool_size(1);
721 strategies[
"lns"] = new_params;
726 for (
const SatParameters& params : base_params.subsolver_params()) {
727 auto it = strategies.find(params.name());
728 if (it != strategies.end()) {
729 it->second.MergeFrom(params);
733 SatParameters new_params = base_params;
734 new_params.MergeFrom(params);
735 strategies[params.name()] = new_params;
740 for (
auto& [
name, params] : strategies) {
741 params.set_name(
name);
755 const SatParameters& base_params,
const CpModelProto& cp_model,
767 const bool use_fixed_strategy = !cp_model.search_strategy().empty() ||
768 ModelHasSchedulingConstraints(cp_model);
774 std::vector<std::string> names;
777 for (
const std::string&
name : base_params.extra_subsolvers()) {
778 names.push_back(
name);
782 if (base_params.subsolvers().empty()) {
784 names.push_back(
"default_lp");
785 names.push_back(
"fixed");
786 names.push_back(
"core");
787 names.push_back(
"no_lp");
788 names.push_back(
"max_lp");
789 names.push_back(
"quick_restart");
790 names.push_back(
"reduced_costs");
791 names.push_back(
"quick_restart_no_lp");
792 names.push_back(
"pseudo_costs");
793 names.push_back(
"lb_tree_search");
794 names.push_back(
"probing");
795 names.push_back(
"objective_lb_search");
796 names.push_back(
"objective_shaving_no_lp");
797 names.push_back(
"objective_shaving_max_lp");
798 names.push_back(
"probing_max_lp");
799 names.push_back(
"probing_no_lp");
800 names.push_back(
"objective_lb_search_no_lp");
801 names.push_back(
"objective_lb_search_max_lp");
803 for (
const std::string&
name : base_params.subsolvers()) {
806 if (
name ==
"core_or_no_lp") {
807 if (!cp_model.has_objective() ||
808 cp_model.objective().vars_size() <= 1) {
809 names.push_back(
"no_lp");
811 names.push_back(
"core");
814 names.push_back(
name);
821 for (
const std::string&
name : names) {
823 names[new_size++] =
name;
826 names.resize(new_size);
829 std::vector<SatParameters> result;
830 for (
const std::string&
name : names) {
831 SatParameters params = strategies.at(
name);
834 if (!use_fixed_strategy &&
835 params.search_branching() == SatParameters::FIXED_SEARCH) {
842 if (params.use_probing_search() && params.interleave_search())
continue;
846 if ((params.use_objective_shaving_search() ||
847 params.use_variables_shaving_search()) &&
848 params.interleave_search()) {
854 if (params.use_probing_search() && cp_model.variables().empty())
continue;
856 if (cp_model.has_objective() && !cp_model.objective().vars().empty()) {
858 if (cp_model.objective().vars().size() == 1 &&
859 params.optimize_with_core()) {
863 if (
name ==
"less_encoding")
continue;
868 if (params.interleave_search() &&
869 (params.optimize_with_lb_tree_search() ||
870 params.use_objective_lb_search())) {
875 if (params.optimize_with_lb_tree_search())
continue;
876 if (params.optimize_with_core())
continue;
877 if (params.use_objective_lb_search())
continue;
878 if (params.use_objective_shaving_search())
continue;
879 if (params.search_branching() == SatParameters::LP_SEARCH)
continue;
880 if (params.search_branching() == SatParameters::PSEUDO_COST_SEARCH) {
886 params.set_name(
name);
888 base_params.random_seed(),
static_cast<int64_t
>(result.size()) + 1));
889 result.push_back(params);
896 if (base_params.interleave_search())
return result;
899 int num_to_keep = base_params.num_full_subsolvers();
900 if (num_to_keep == 0) {
903 const int num_available =
904 std::max(0, base_params.num_workers() - num_already_present);
906 const auto heuristic_num_workers = [](
int num_workers) {
907 DCHECK_GE(num_workers, 0);
908 if (num_workers == 1)
return 1;
909 if (num_workers <= 4)
return num_workers - 1;
910 if (num_workers <= 8)
return num_workers - 2;
911 if (num_workers <= 16)
return num_workers - (num_workers / 4 + 1);
912 return num_workers - (num_workers / 2 - 3);
915 num_to_keep = heuristic_num_workers(num_available);
918 if (result.size() > num_to_keep) {
919 result.resize(std::max(0, num_to_keep));
925 const SatParameters& base_params) {
926 std::vector<SatParameters> result;
928 const auto get_base = [&result, &base_params](
bool fj) {
929 SatParameters new_params = base_params;
930 new_params.set_log_search_progress(
false);
931 new_params.set_use_feasibility_jump(fj);
933 const int base_seed = base_params.random_seed();
934 new_params.set_random_seed(
CombineSeed(base_seed, result.size()));
939 if (base_params.use_feasibility_jump()) {
940 SatParameters new_params = get_base(
true);
941 new_params.set_name(
"fj");
942 new_params.set_feasibility_jump_linearization_level(0);
943 result.push_back(new_params);
947 for (
int i = 0;
i < 2; ++
i) {
948 SatParameters new_params = get_base(
false);
949 new_params.set_search_random_variable_pool_size(5);
950 new_params.set_search_branching(SatParameters::RANDOMIZED_SEARCH);
952 new_params.set_name(
"fs_random_no_lp");
953 new_params.set_linearization_level(0);
955 new_params.set_name(
"fs_random");
957 result.push_back(new_params);
961 if (base_params.use_feasibility_jump()) {
962 SatParameters new_params = get_base(
true);
963 new_params.set_name(
"fj");
964 new_params.set_feasibility_jump_linearization_level(0);
965 result.push_back(new_params);
969 for (
int i = 0;
i < 2; ++
i) {
970 SatParameters new_params = get_base(
false);
971 new_params.set_search_random_variable_pool_size(5);
972 new_params.set_search_branching(
973 SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH);
975 new_params.set_name(
"fs_random_quick_restart_no_lp");
976 new_params.set_linearization_level(0);
978 new_params.set_name(
"fs_random_quick_restart");
980 result.push_back(new_params);
986 if (base_params.use_feasibility_jump()) {
987 SatParameters new_params = get_base(
true);
988 new_params.set_name(
"fj_lin");
989 new_params.set_feasibility_jump_linearization_level(2);
990 result.push_back(new_params);