23 #include "absl/container/flat_hash_set.h"
24 #include "absl/log/check.h"
25 #include "absl/strings/str_cat.h"
26 #include "absl/time/clock.h"
27 #include "absl/time/time.h"
29 #include "ortools/sat/cp_model.pb.h"
43 #include "ortools/sat/sat_parameters.pb.h"
62 const auto& variables =
65 if (variables.contains(
var)) {
77 CHECK_LT(var_lb, var_ub);
79 const IntegerValue chosen_value =
80 var_lb +
std::max(IntegerValue(1), (var_ub - var_lb) / IntegerValue(2));
88 const IntegerValue ub = integer_trail->UpperBound(
var);
90 const absl::flat_hash_set<IntegerVariable>& variables =
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) {
116 DCHECK(!integer_trail->IsCurrentlyIgnored(
var));
119 const auto& it = lp_dispatcher->find(positive_var);
121 it == lp_dispatcher->end() ? nullptr : it->second;
134 const IntegerValue
value = IntegerValue(
151 const int proto_var =
169 const std::vector<IntegerVariable>& vars,
Model*
model) {
171 return [ vars, integer_trail]() {
172 for (
const IntegerVariable
var : vars) {
174 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
182 std::function<BooleanOrIntegerLiteral()>
184 const std::vector<IntegerVariable>& vars,
Model*
model) {
186 return [ vars, integer_trail]() {
188 IntegerValue candidate_lb;
189 for (
const IntegerVariable
var : vars) {
190 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
191 const IntegerValue lb = integer_trail->LowerBound(
var);
205 return [heuristics]() {
206 for (
const auto& h : heuristics) {
208 if (decision.
HasValue())
return decision;
216 value_selection_heuristics,
225 if (!current_decision.
HasValue())
return current_decision;
230 sat_policy->InStablePhase()) {
231 return current_decision;
236 for (
const auto& value_heuristic : value_selection_heuristics) {
241 return current_decision;
248 for (
const IntegerVariable
var : encoder->GetAllAssociatedVariables(
250 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
253 for (
const auto& value_heuristic : value_selection_heuristics) {
259 return current_decision;
264 auto* lp_constraints =
266 int num_lp_variables = 0;
268 num_lp_variables += lp->NumVariables();
270 const int num_integer_variables =
272 return (num_integer_variables <= 2 * num_lp_variables);
282 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
284 value_selection_heuristics;
293 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
301 if (response_manager !=
nullptr) {
302 VLOG(3) <<
"Using best solution value selection heuristic.";
303 value_selection_heuristics.push_back(
304 [
model, response_manager](IntegerVariable
var) {
306 var, response_manager->SolutionsRepository(),
model);
312 if (
parameters.exploit_relaxation_solution()) {
316 value_selection_heuristics.push_back(
318 VLOG(3) <<
"Using relaxation solution value selection heuristic.";
327 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
333 var_selection_heuristic,
model);
340 return [sat_solver, trail, decision_policy] {
341 const bool all_assigned = trail->Index() == sat_solver->
NumVariables();
343 const Literal result = decision_policy->NextBranch();
353 const IntegerVariable obj_var = objective_definition->
objective_var;
358 return [obj_var, integer_trail, sat_solver, random]() {
360 const int level = sat_solver->CurrentDecisionLevel();
363 const IntegerValue obj_lb = integer_trail->LowerBound(obj_var);
364 const IntegerValue obj_ub = integer_trail->UpperBound(obj_var);
365 const IntegerValue mid = (obj_ub - obj_lb) / 2;
366 const IntegerValue new_ub =
367 obj_lb + absl::LogUniform<int64_t>(*random, 0, mid.value());
376 const bool has_objective =
378 if (!has_objective) {
384 return [pseudo_costs, integer_trail]() {
385 const IntegerVariable chosen_var = pseudo_costs->GetBestDecisionVar();
402 return [repo, heuristic, trail, integer_trail]() {
417 const int num_intervals = repo->NumIntervals();
418 for (IntervalVariable i(0); i < num_intervals; ++i) {
419 if (repo->IsAbsent(i))
continue;
420 if (!repo->IsPresent(i) || !integer_trail->IsFixed(repo->Start(i)) ||
421 !integer_trail->IsFixed(repo->End(i))) {
422 IntegerValue
time = integer_trail->LowerBound(repo->Start(i));
429 repo->PresenceLiteral(i), repo->Start(i)));
436 const IntegerValue size_min =
437 std::max(integer_trail->LowerBound(repo->Size(i)),
438 integer_trail->LowerBound(repo->End(i)) -
time);
439 if (
time < best.time ||
440 (
time == best.time && size_min < best.size_min)) {
443 best.start = repo->Start(i);
444 best.end = repo->End(i);
446 best.size_min = size_min;
455 heuristic->next_decision_override = [trail, integer_trail, best,
456 num_times]()
mutable {
457 if (++num_times > 5) {
461 VLOG(3) <<
"Skipping ... ";
467 if (!trail->Assignment().LiteralIsAssigned(
Literal(best.presence))) {
468 VLOG(3) <<
"assign " << best.presence;
471 if (trail->Assignment().LiteralIsFalse(
Literal(best.presence))) {
472 VLOG(2) <<
"unperformed.";
478 if (!integer_trail->IsFixed(best.start)) {
479 const IntegerValue
start_min = integer_trail->LowerBound(best.start);
485 if (!integer_trail->IsFixed(best.end)) {
486 const IntegerValue
end_min = integer_trail->LowerBound(best.end);
492 const IntegerValue
start = integer_trail->LowerBound(best.start);
494 << integer_trail->LowerBound(best.end) <<
"]"
496 ? absl::StrCat(
" presence=",
499 << (best.time <
start
500 ? absl::StrCat(
" start_at_selection=", best.time.value())
505 return heuristic->next_decision_override();
522 std::discrete_distribution<int> var_dist{3 , 1 };
526 value_selection_heuristics;
527 std::vector<int> value_selection_weight;
530 value_selection_heuristics.push_back([
model](IntegerVariable
var) {
533 value_selection_weight.push_back(8);
537 if (response_manager !=
nullptr) {
538 value_selection_heuristics.push_back(
539 [
model, response_manager](IntegerVariable
var) {
541 var, response_manager->SolutionsRepository(),
model);
543 value_selection_weight.push_back(5);
549 value_selection_heuristics.push_back(
554 value_selection_weight.push_back(3);
559 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
562 value_selection_weight.push_back(1);
565 value_selection_heuristics.push_back([integer_trail](IntegerVariable
var) {
568 value_selection_weight.push_back(1);
571 value_selection_weight.push_back(10);
575 std::discrete_distribution<int> val_dist(value_selection_weight.begin(),
576 value_selection_weight.end());
578 int policy_index = 0;
579 int val_policy_index = 0;
581 return [=]()
mutable {
585 decision_policy->ResetDecisionHeuristic();
588 policy_index = var_dist(*(random));
591 val_policy_index = val_dist(*(random));
596 if (!current_decision.
HasValue())
return current_decision;
599 if (val_policy_index >= value_selection_heuristics.size()) {
600 return current_decision;
605 value_selection_heuristics[val_policy_index](
608 return current_decision;
612 for (
const IntegerVariable
var : encoder->GetAllAssociatedVariables(
614 if (integer_trail->IsCurrentlyIgnored(
var))
continue;
618 value_selection_heuristics[val_policy_index](
var);
623 return current_decision;
628 const std::vector<BooleanOrIntegerVariable>& vars,
629 const std::vector<IntegerValue>& values,
Model*
model) {
639 int* rev_start_index =
model->TakeOwnership(
new int);
640 *rev_start_index = 0;
643 rev_int_repo->SaveState(rev_start_index);
644 for (
int i = *rev_start_index; i < vars.size(); ++i) {
645 const IntegerValue
value = values[i];
647 if (trail->Assignment().VariableIsAssigned(vars[i].bool_var))
continue;
650 *rev_start_index = i;
654 const IntegerVariable integer_var = vars[i].int_var;
655 if (integer_trail->IsCurrentlyIgnored(integer_var))
continue;
656 if (integer_trail->IsFixed(integer_var))
continue;
663 *rev_start_index = i;
676 bool reset_at_next_call =
true;
677 int next_num_failures = 0;
678 return [=]()
mutable {
679 if (reset_at_next_call) {
681 reset_at_next_call =
false;
682 }
else if (solver->
num_failures() >= next_num_failures) {
683 reset_at_next_call =
true;
685 return reset_at_next_call;
696 std::function<BooleanOrIntegerLiteral()> WrapIntegerLiteralHeuristic(
697 std::function<IntegerLiteral()> f) {
698 return [f]() {
return BooleanOrIntegerLiteral(f()); };
710 const SatParameters&
parameters = *(
model->GetOrCreate<SatParameters>());
712 case SatParameters::AUTOMATIC_SEARCH: {
731 case SatParameters::FIXED_SEARCH: {
744 auto no_restart = []() {
return false; };
748 case SatParameters::PARTIAL_FIXED_SEARCH: {
752 auto no_restart = []() {
return false; };
756 case SatParameters::HINT_SEARCH: {
761 auto no_restart = []() {
return false; };
765 case SatParameters::PORTFOLIO_SEARCH: {
771 for (
const auto&
ct :
773 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
774 ct->HeuristicLpReducedCostBinary(
model)));
775 base_heuristics.push_back(WrapIntegerLiteralHeuristic(
776 ct->HeuristicLpMostInfeasibleBinary(
model)));
788 case SatParameters::LP_SEARCH: {
790 for (
const auto&
ct :
792 lp_heuristics.push_back(WrapIntegerLiteralHeuristic(
793 ct->HeuristicLpReducedCostAverageBranching()));
795 if (lp_heuristics.empty()) {
810 case SatParameters::PSEUDO_COST_SEARCH: {
819 case SatParameters::PORTFOLIO_WITH_QUICK_RESTART_SEARCH: {
832 incomplete_heuristics,
835 complete_heuristics.reserve(incomplete_heuristics.size());
836 for (
const auto& incomplete : incomplete_heuristics) {
837 complete_heuristics.push_back(
840 return complete_heuristics;
844 : parameters_(*
model->GetOrCreate<SatParameters>()),
856 if (objective !=
nullptr) objective_var_ = objective->
objective_var;
873 for (
const auto& cb : level_zero_callbacks->callbacks) {
880 if (parameters_.use_sat_inprocessing() &&
895 const IntegerVariable
var =
912 if (!new_decision.
HasValue())
break;
931 VLOG(1) <<
"Trying to take a decision that is already assigned!"
932 <<
" Fix this. Continuing for now...";
942 const std::vector<PseudoCosts::VariableBoundChange> bound_changes =
947 old_obj_lb = integer_trail_->
LowerBound(objective_var_);
948 old_obj_ub = integer_trail_->
UpperBound(objective_var_);
969 const IntegerValue new_obj_lb = integer_trail_->
LowerBound(objective_var_);
970 const IntegerValue new_obj_ub = integer_trail_->
UpperBound(objective_var_);
971 const IntegerValue objective_bound_change =
972 (new_obj_lb - old_obj_lb) + (old_obj_ub - new_obj_ub);
973 pseudo_costs_->
UpdateCost(bound_changes, objective_bound_change);
985 CHECK_NE(num_policies, 0);
1001 const int64_t old_num_conflicts = sat_solver_->
num_failures();
1002 const int64_t conflict_limit = parameters_.max_number_of_conflicts();
1003 int64_t num_decisions_since_last_lp_record_ = 0;
1004 int64_t num_decisions_without_probing = 0;
1006 (sat_solver_->
num_failures() - old_num_conflicts < conflict_limit)) {
1038 parameters_.probing_period_at_root() > 0 &&
1039 ++num_decisions_without_probing >=
1040 parameters_.probing_period_at_root()) {
1041 num_decisions_without_probing = 0;
1073 if (parameters_.use_optimization_hints()) {
1076 for (
int i = 0; i < trail.Index(); ++i) {
1077 sat_decision->SetAssignmentPreference(trail[i], 0.0);
1100 parameters_.linearization_level() >= 2) {
1101 num_decisions_since_last_lp_record_++;
1102 if (num_decisions_since_last_lp_record_ >= 100) {
1107 num_decisions_since_last_lp_record_ = 0;
1111 return SatSolver::Status::LIMIT_REACHED;
1115 const std::vector<Literal>& assumptions,
Model*
model) {
1121 for (
const auto& cb : level_zero_callbacks->callbacks) {
1136 const IntegerVariable num_vars =
1138 std::vector<IntegerVariable> all_variables;
1139 for (IntegerVariable
var(0);
var < num_vars; ++
var) {
1140 all_variables.push_back(
var);
1160 parameters_(*(
model->GetOrCreate<SatParameters>())),
1165 active_limit_(parameters_.shaving_search_deterministic_time()) {
1167 absl::flat_hash_set<BooleanVariable> visited;
1168 for (
int v = 0; v <
model_proto.variables_size(); ++v) {
1169 if (mapping->IsBoolean(v)) {
1171 const auto [_, inserted] = visited.insert(bool_var);
1173 bool_vars_.push_back(bool_var);
1176 IntegerVariable
var = mapping->Integer(v);
1178 int_vars_.push_back(
var);
1181 VLOG(2) <<
"Start continuous probing with " << bool_vars_.size()
1182 <<
" Boolean variables, and " << int_vars_.size()
1183 <<
" integer variables"
1184 <<
", deterministic time limit = "
1186 last_logging_time_ = absl::Now();
1202 if (parameters_.use_sat_inprocessing() &&
1208 probed_bool_vars_.clear();
1209 probed_literals_.clear();
1212 const int64_t initial_num_literals_fixed =
1214 const int64_t initial_num_bounds_shaved = num_bounds_shaved_;
1218 for (; current_int_var_ < int_vars_.size(); ++current_int_var_) {
1219 const IntegerVariable int_var = int_vars_[current_int_var_];
1220 if (integer_trail_->
IsFixed(int_var) ||
1225 if (!ImportFromSharedClasses()) {
1233 const BooleanVariable shave_lb =
1236 int_var, integer_trail_->
LowerBound(int_var)))
1238 const auto [_lb, lb_inserted] = probed_bool_vars_.insert(shave_lb);
1243 num_literals_probed_++;
1246 const BooleanVariable shave_ub =
1249 int_var, integer_trail_->
UpperBound(int_var)))
1251 const auto [_ub, ub_inserted] = probed_bool_vars_.insert(shave_ub);
1256 num_literals_probed_++;
1259 if (parameters_.use_shaving_in_probing_search()) {
1261 ShaveLiteral(
Literal(shave_lb,
true));
1262 if (ReportStatus(lb_status))
return lb_status;
1265 ShaveLiteral(
Literal(shave_ub,
true));
1266 if (ReportStatus(ub_status))
return ub_status;
1273 for (; current_bool_var_ < bool_vars_.size(); ++current_bool_var_) {
1274 const BooleanVariable& bool_var = bool_vars_[current_bool_var_];
1278 if (!ImportFromSharedClasses()) {
1286 const auto [_, inserted] = probed_bool_vars_.insert(bool_var);
1291 num_literals_probed_++;
1295 if (parameters_.use_shaving_in_probing_search() &&
1298 if (ReportStatus(true_status))
return true_status;
1302 if (ReportStatus(false_status))
return false_status;
1310 const double deterministic_time =
1311 parameters_.shaving_search_deterministic_time();
1312 const bool something_has_been_detected =
1313 num_bounds_shaved_ != initial_num_bounds_shaved ||
1315 if (something_has_been_detected) {
1316 active_limit_ = deterministic_time;
1317 }
else if (active_limit_ < 25 * deterministic_time) {
1318 active_limit_ += deterministic_time;
1323 current_bool_var_ = 0;
1324 current_int_var_ = 0;
1329 bool ContinuousProber::ImportFromSharedClasses() {
1331 for (
const auto& cb : level_zero_callbacks_->
callbacks) {
1341 const auto [_, inserted] = probed_literals_.insert(
literal.Index());
1345 num_bounds_tried_++;
1357 num_bounds_shaved_++;
1370 void ContinuousProber::LogStatistics() {
1371 if (shared_response_manager_ ==
nullptr ||
1372 shared_bounds_manager_ ==
nullptr) {
1377 absl::StrCat(
"#iterations:", iteration_,
" #literals fixed/probed:",
1379 " #bounds shaved/tried:", num_bounds_shaved_,
"/",
1380 num_bounds_tried_,
" #new_integer_bounds:",
1383 parameters_.log_frequency_in_seconds(), &last_logging_time_);
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
double GetDeterministicLimit() const
Queries the deterministic time limit.
bool LimitReached()
Returns true when the external limit is true, or the deterministic time is over the deterministic lim...
double GetElapsedDeterministicTime() const
Returns the elapsed deterministic time since the construction of this object.
void ChangeDeterministicLimit(double new_limit)
Overwrites the deterministic time limit with the new value.
SatSolver::Status Probe()
ContinuousProber(const CpModelProto &model_proto, Model *model)
sat::Literal Literal(int ref) const
bool ProcessIntegerTrail(Literal first_decision)
Literal GetOrCreateAssociatedLiteral(IntegerLiteral i_lit)
SatSolver::Status SolveIntegerProblem()
IntegerSearchHelper(Model *model)
bool BeforeTakingDecision()
bool TakeDecision(Literal decision)
LiteralIndex GetDecision(const std::function< BooleanOrIntegerLiteral()> &f)
IntegerVariable FirstUnassignedVariable() const
bool IsCurrentlyIgnored(IntegerVariable i) const
bool IsFixed(IntegerVariable i) const
bool CurrentBranchHadAnIncompletePropagation()
bool InPropagationLoop() const
IntegerVariable NextVariableToBranchOnInPropagationLoop() const
IntegerValue UpperBound(IntegerVariable i) const
IntegerValue LowerBound(IntegerVariable i) const
bool HasPendingRootLevelDeduction() const
bool IsOptional(IntegerVariable i) const
double GetSolutionValue(IntegerVariable variable) const
bool SolutionIsInteger() const
LiteralIndex Index() const
BooleanVariable Variable() const
std::string DebugString() const
Class that owns everything related to a particular optimization model.
const std::string & Name() const
T Get(std::function< T(const Model &)> f) const
Similar to Add() but this is const.
T * GetOrCreate()
Returns an object of type T that is unique to this model (like a "local" singleton).
int num_new_binary_clauses() const
bool ProbeOneVariable(BooleanVariable b)
int num_new_literals_fixed() const
void ProcessTrailAtLevelOne()
std::vector< VariableBoundChange > GetBoundChanges(Literal decision)
void UpdateCost(const std::vector< VariableBoundChange > &bound_changes, IntegerValue obj_bound_improvement)
void NotifyThatModelIsUnsat()
Status UnsatStatus() const
void AdvanceDeterministicTime(TimeLimit *limit)
const VariablesAssignment & Assignment() const
int EnqueueDecisionAndBackjumpOnConflict(Literal true_literal)
void Backtrack(int target_level)
bool RestoreSolverToAssumptionLevel()
int64_t num_failures() const
bool ReapplyAssumptionsIfNeeded()
bool ResetWithGivenAssumptions(const std::vector< Literal > &assumptions)
int CurrentDecisionLevel() const
int NumBoundsExported(const std::string &worker_name)
void LogPeriodicMessage(const std::string &prefix, const std::string &message, double frequency_seconds, absl::Time *last_logging_time)
ValueType GetVariableValueInSolution(int var_index, int solution_index) const
const VariablesAssignment & Assignment() const
bool LiteralIsAssigned(Literal literal) const
bool VariableIsAssigned(BooleanVariable var) const
SharedRelaxationSolutionRepository * relaxation_solutions
CpModelProto const * model_proto
void RandomizeDecisionHeuristic(absl::BitGenRef random, SatParameters *parameters)
std::function< BooleanOrIntegerLiteral()> FirstUnassignedVarAtItsMinHeuristic(const std::vector< IntegerVariable > &vars, Model *model)
std::function< int64_t(const Model &)> UpperBound(IntegerVariable v)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
SatSolver::Status ResetAndSolveIntegerProblem(const std::vector< Literal > &assumptions, Model *model)
const LiteralIndex kNoLiteralIndex(-1)
IntegerLiteral AtMinValue(IntegerVariable var, IntegerTrail *integer_trail)
void RecordLPRelaxationValues(Model *model)
std::function< BooleanOrIntegerLiteral()> ShaveObjectiveLb(Model *model)
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)
std::function< BooleanOrIntegerLiteral()> RandomizeOnRestartHeuristic(Model *model)
void ConfigureSearchHeuristics(Model *model)
std::vector< std::function< BooleanOrIntegerLiteral()> > CompleteHeuristics(const std::vector< std::function< BooleanOrIntegerLiteral()>> &incomplete_heuristics, const std::function< BooleanOrIntegerLiteral()> &completion_heuristic)
IntegerVariable PositiveVariable(IntegerVariable i)
std::function< BooleanOrIntegerLiteral()> IntegerValueSelectionHeuristic(std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
std::function< BooleanOrIntegerLiteral()> SatSolverHeuristic(Model *model)
std::vector< IntegerVariable > NegationOf(const std::vector< IntegerVariable > &vars)
std::function< BooleanOrIntegerLiteral()> SequentialSearch(std::vector< std::function< BooleanOrIntegerLiteral()>> heuristics)
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
std::function< BooleanOrIntegerLiteral()> PseudoCost(Model *model)
std::function< BooleanOrIntegerLiteral()> SequentialValueSelection(std::vector< std::function< IntegerLiteral(IntegerVariable)>> value_selection_heuristics, std::function< BooleanOrIntegerLiteral()> var_selection_heuristic, Model *model)
Collection of objects used to extend the Constraint Solver library.
std::optional< int64_t > end
LiteralIndex boolean_literal_index
IntegerLiteral integer_literal
static IntegerLiteral LowerOrEqual(IntegerVariable i, IntegerValue bound)
static IntegerLiteral GreaterOrEqual(IntegerVariable i, IntegerValue bound)
std::vector< std::function< bool()> > callbacks
IntegerVariable objective_var
std::vector< std::function< bool()> > restart_policies
std::function< BooleanOrIntegerLiteral()> hint_search
std::function< BooleanOrIntegerLiteral()> fixed_search
std::function< BooleanOrIntegerLiteral()> next_decision_override
std::function< BooleanOrIntegerLiteral()> user_search
std::vector< std::function< BooleanOrIntegerLiteral()> > decision_policies
#define VLOG(verboselevel)