mirror of
https://github.com/Z3Prover/z3
synced 2026-06-22 08:30:28 +00:00
Parallel tactic (#9824)
Co-authored-by: Ilana Shapiro <ilanashapiro@Ilanas-MacBook-Pro.local> Co-authored-by: Ilana Shapiro <ilanashapiro@Ilanas-MBP.localdomain>
This commit is contained in:
parent
677abb589e
commit
6ef8660adb
20 changed files with 1889 additions and 280 deletions
|
|
@ -5,7 +5,7 @@ def_module_params('smt_parallel',
|
|||
('inprocessing', BOOL, False, 'integrate in-processing as a heuristic simplification'),
|
||||
('sls', BOOL, False, 'add sls-tactic as a separate worker thread outside the search tree parallelism'),
|
||||
('num_global_bb_fl_threads', UINT, 0, 'run failed-literal backbone worker threads; default is 0 (off), supported values are 1 (negative mode only) or 2 (negative and positive mode)'),
|
||||
('num_global_bb_batch_threads', UINT, 0, 'run Janota-style chunking backbone worker threads; default is 0 (off), supported values are 1 (negative mode only) or 2 (negative and positive mode)'),
|
||||
('num_global_bb_batch_threads', UINT, 2, 'run Janota-style chunking backbone worker threads; default is 2 (negative and positive mode), supported values are 0 (off), 1 (negative mode only) or 2 (negative and positive mode)'),
|
||||
('local_backbones', BOOL, False, 'enable local backbones experiment within the search tree parallelism'),
|
||||
('core_minimize', BOOL, True, 'minimize unsat cores used for parallel cube backtracking'),
|
||||
('ablate_backtracking', BOOL, False, 'ablation: pass entire cube as core instead of unsat core during backtracking'),
|
||||
|
|
|
|||
|
|
@ -132,6 +132,8 @@ namespace sat {
|
|||
m_best_phase.reset();
|
||||
m_phase.reset();
|
||||
m_prev_phase.reset();
|
||||
m_phase_birthdate.reset();
|
||||
m_best_phase_birthdate.reset();
|
||||
m_assigned_since_gc.reset();
|
||||
m_last_conflict.reset();
|
||||
m_last_propagation.reset();
|
||||
|
|
@ -161,6 +163,8 @@ namespace sat {
|
|||
m_phase[v] = src.m_phase[v];
|
||||
m_best_phase[v] = src.m_best_phase[v];
|
||||
m_prev_phase[v] = src.m_prev_phase[v];
|
||||
m_phase_birthdate[v] = src.m_phase_birthdate[v];
|
||||
m_best_phase_birthdate[v] = src.m_best_phase_birthdate[v];
|
||||
|
||||
// inherit activity:
|
||||
m_activity[v] = src.m_activity[v];
|
||||
|
|
@ -267,6 +271,8 @@ namespace sat {
|
|||
m_phase[v] = false;
|
||||
m_best_phase[v] = false;
|
||||
m_prev_phase[v] = false;
|
||||
m_phase_birthdate[v] = 0;
|
||||
m_best_phase_birthdate[v] = 0;
|
||||
m_assigned_since_gc[v] = false;
|
||||
m_last_conflict[v] = 0;
|
||||
m_last_propagation[v] = 0;
|
||||
|
|
@ -308,6 +314,8 @@ namespace sat {
|
|||
m_phase.push_back(false);
|
||||
m_best_phase.push_back(false);
|
||||
m_prev_phase.push_back(false);
|
||||
m_phase_birthdate.push_back(0);
|
||||
m_best_phase_birthdate.push_back(0);
|
||||
m_assigned_since_gc.push_back(false);
|
||||
m_last_conflict.push_back(0);
|
||||
m_last_propagation.push_back(0);
|
||||
|
|
@ -645,6 +653,17 @@ namespace sat {
|
|||
return 3*cls_allocator().get_allocation_size()/2 + memory::get_allocation_size() > memory::get_max_memory_size();
|
||||
}
|
||||
|
||||
void solver::set_phase(literal l) {
|
||||
if (l.var() >= num_vars())
|
||||
return;
|
||||
bool value = !l.sign();
|
||||
if (m_phase[l.var()] != value)
|
||||
m_phase_birthdate[l.var()] = m_stats.m_conflicts;
|
||||
if (m_best_phase[l.var()] != value)
|
||||
m_best_phase_birthdate[l.var()] = m_stats.m_conflicts;
|
||||
m_best_phase[l.var()] = m_phase[l.var()] = value;
|
||||
}
|
||||
|
||||
struct solver::cmp_activity {
|
||||
solver& s;
|
||||
cmp_activity(solver& s):s(s) {}
|
||||
|
|
@ -896,6 +915,8 @@ namespace sat {
|
|||
m_assignment[(~l).index()] = l_false;
|
||||
bool_var v = l.var();
|
||||
m_justification[v] = j;
|
||||
if (m_phase[v] != !l.sign())
|
||||
m_phase_birthdate[v] = m_stats.m_conflicts;
|
||||
m_phase[v] = !l.sign();
|
||||
m_assigned_since_gc[v] = true;
|
||||
m_trail.push_back(l);
|
||||
|
|
@ -904,17 +925,17 @@ namespace sat {
|
|||
case BH_VSIDS:
|
||||
break;
|
||||
case BH_CHB:
|
||||
m_last_propagation[v] = m_stats.m_conflict;
|
||||
m_last_propagation[v] = m_stats.m_conflicts;
|
||||
break;
|
||||
}
|
||||
|
||||
if (m_config.m_anti_exploration) {
|
||||
uint64_t age = m_stats.m_conflict - m_canceled[v];
|
||||
uint64_t age = m_stats.m_conflicts - m_canceled[v];
|
||||
if (age > 0) {
|
||||
double decay = pow(0.95, static_cast<double>(age));
|
||||
set_activity(v, static_cast<unsigned>(m_activity[v] * decay));
|
||||
// NB. MapleSAT does not update canceled.
|
||||
m_canceled[v] = m_stats.m_conflict;
|
||||
m_canceled[v] = m_stats.m_conflicts;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1378,8 +1399,12 @@ namespace sat {
|
|||
lbool r = m_local_search->check(_lits.size(), _lits.data(), nullptr);
|
||||
auto const& mdl = m_local_search->get_model();
|
||||
if (mdl.size() == m_best_phase.size()) {
|
||||
for (unsigned i = 0; i < m_best_phase.size(); ++i)
|
||||
m_best_phase[i] = l_true == mdl[i];
|
||||
for (unsigned i = 0; i < m_best_phase.size(); ++i) {
|
||||
bool is_true = l_true == mdl[i];
|
||||
if (m_best_phase[i] != is_true)
|
||||
m_best_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_best_phase[i] = is_true;
|
||||
}
|
||||
|
||||
if (r == l_true) {
|
||||
m_conflicts_since_restart = 0;
|
||||
|
|
@ -1671,12 +1696,12 @@ namespace sat {
|
|||
while (!m_case_split_queue.empty()) {
|
||||
if (m_config.m_anti_exploration) {
|
||||
next = m_case_split_queue.min_var();
|
||||
auto age = m_stats.m_conflict - m_canceled[next];
|
||||
auto age = m_stats.m_conflicts - m_canceled[next];
|
||||
while (age > 0) {
|
||||
set_activity(next, static_cast<unsigned>(m_activity[next] * pow(0.95, static_cast<double>(age))));
|
||||
m_canceled[next] = m_stats.m_conflict;
|
||||
m_canceled[next] = m_stats.m_conflicts;
|
||||
next = m_case_split_queue.min_var();
|
||||
age = m_stats.m_conflict - m_canceled[next];
|
||||
age = m_stats.m_conflicts - m_canceled[next];
|
||||
}
|
||||
}
|
||||
next = m_case_split_queue.next_var();
|
||||
|
|
@ -1714,6 +1739,32 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
|
||||
uint64_t solver::get_preferred_phase_birthdate(bool_var v) const {
|
||||
bool use_best =
|
||||
m_config.m_phase == PS_FROZEN ||
|
||||
((m_config.m_phase == PS_SAT_CACHING || m_config.m_phase == PS_LOCAL_SEARCH) && m_search_state == s_sat);
|
||||
return use_best ? m_best_phase_birthdate[v] : m_phase_birthdate[v];
|
||||
}
|
||||
|
||||
void solver::get_backbone_candidates(literal_vector& lits, unsigned max_num) {
|
||||
struct candidate {
|
||||
literal lit;
|
||||
uint64_t age;
|
||||
};
|
||||
svector<candidate> cands;
|
||||
uint64_t now = m_stats.m_conflicts;
|
||||
for (bool_var v = 0; v < num_vars(); ++v) {
|
||||
if (value(v) != l_undef || was_eliminated(v))
|
||||
continue;
|
||||
bool is_pos = guess(v);
|
||||
cands.push_back({ literal(v, !is_pos), now - get_preferred_phase_birthdate(v) });
|
||||
}
|
||||
std::stable_sort(cands.begin(), cands.end(),
|
||||
[](candidate const& a, candidate const& b) { return a.age > b.age; });
|
||||
for (unsigned i = 0; i < cands.size() && i < max_num; ++i)
|
||||
lits.push_back(cands[i].lit);
|
||||
}
|
||||
|
||||
bool solver::decide() {
|
||||
bool_var next;
|
||||
lbool phase = l_undef;
|
||||
|
|
@ -2145,8 +2196,13 @@ namespace sat {
|
|||
for (bool_var v = 0; v < num; ++v) {
|
||||
if (!was_eliminated(v)) {
|
||||
m_model[v] = value(v);
|
||||
m_phase[v] = value(v) == l_true;
|
||||
m_best_phase[v] = value(v) == l_true;
|
||||
bool is_true = value(v) == l_true;
|
||||
if (m_phase[v] != is_true)
|
||||
m_phase_birthdate[v] = m_stats.m_conflicts;
|
||||
if (m_best_phase[v] != is_true)
|
||||
m_best_phase_birthdate[v] = m_stats.m_conflicts;
|
||||
m_phase[v] = is_true;
|
||||
m_best_phase[v] = is_true;
|
||||
}
|
||||
}
|
||||
TRACE(sat_mc_bug, m_mc.display(tout););
|
||||
|
|
@ -2274,7 +2330,7 @@ namespace sat {
|
|||
m_restart_logs++;
|
||||
|
||||
std::stringstream strm;
|
||||
strm << "(sat.stats " << std::setw(6) << m_stats.m_conflict << " "
|
||||
strm << "(sat.stats " << std::setw(6) << m_stats.m_conflicts << " "
|
||||
<< std::setw(6) << m_stats.m_decision << " "
|
||||
<< std::setw(4) << m_stats.m_restart
|
||||
<< mk_stat(*this)
|
||||
|
|
@ -2432,7 +2488,7 @@ namespace sat {
|
|||
m_conflicts_since_init++;
|
||||
m_conflicts_since_restart++;
|
||||
m_conflicts_since_gc++;
|
||||
m_stats.m_conflict++;
|
||||
m_stats.m_conflicts++;
|
||||
if (m_step_size > m_config.m_step_size_min)
|
||||
m_step_size -= m_config.m_step_size_dec;
|
||||
|
||||
|
|
@ -2564,7 +2620,7 @@ namespace sat {
|
|||
tout << "missed " << lit << "@" << lvl(lit) << "\n";);
|
||||
CTRACE(sat, idx == 0, display(tout););
|
||||
if (idx == 0)
|
||||
IF_VERBOSE(0, verbose_stream() << "num-conflicts: " << m_stats.m_conflict << "\n");
|
||||
IF_VERBOSE(0, verbose_stream() << "num-conflicts: " << m_stats.m_conflicts << "\n");
|
||||
VERIFY(idx > 0);
|
||||
idx--;
|
||||
}
|
||||
|
|
@ -2874,7 +2930,7 @@ namespace sat {
|
|||
inc_activity(var);
|
||||
break;
|
||||
case BH_CHB:
|
||||
m_last_conflict[var] = m_stats.m_conflict;
|
||||
m_last_conflict[var] = m_stats.m_conflicts;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -2915,13 +2971,18 @@ namespace sat {
|
|||
for (unsigned i = head; i < sz; ++i) {
|
||||
bool_var v = m_trail[i].var();
|
||||
TRACE(forget_phase, tout << "forgetting phase of v" << v << "\n";);
|
||||
m_phase[v] = m_rand() % 2 == 0;
|
||||
bool value = m_rand() % 2 == 0;
|
||||
if (m_phase[v] != value)
|
||||
m_phase_birthdate[v] = m_stats.m_conflicts;
|
||||
m_phase[v] = value;
|
||||
}
|
||||
if (is_sat_phase() && head >= m_best_phase_size) {
|
||||
m_best_phase_size = head;
|
||||
IF_VERBOSE(12, verbose_stream() << "sticky trail: " << head << "\n");
|
||||
for (unsigned i = 0; i < head; ++i) {
|
||||
bool_var v = m_trail[i].var();
|
||||
if (m_best_phase[v] != m_phase[v])
|
||||
m_best_phase_birthdate[v] = m_stats.m_conflicts;
|
||||
m_best_phase[v] = m_phase[v];
|
||||
}
|
||||
set_has_new_best_phase(true);
|
||||
|
|
@ -2971,23 +3032,43 @@ namespace sat {
|
|||
void solver::do_rephase() {
|
||||
switch (m_config.m_phase) {
|
||||
case PS_ALWAYS_TRUE:
|
||||
for (auto& p : m_phase) p = true;
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
if (!m_phase[i])
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = true;
|
||||
}
|
||||
break;
|
||||
case PS_ALWAYS_FALSE:
|
||||
for (auto& p : m_phase) p = false;
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
if (m_phase[i])
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = false;
|
||||
}
|
||||
break;
|
||||
case PS_FROZEN:
|
||||
break;
|
||||
case PS_BASIC_CACHING:
|
||||
switch (m_rephase.count % 4) {
|
||||
case 0:
|
||||
for (auto& p : m_phase) p = (m_rand() % 2) == 0;
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
bool value = (m_rand() % 2) == 0;
|
||||
if (m_phase[i] != value)
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = value;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
for (auto& p : m_phase) p = false;
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
if (m_phase[i])
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = false;
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
for (auto& p : m_phase) p = !p;
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = !m_phase[i];
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
|
|
@ -2995,18 +3076,29 @@ namespace sat {
|
|||
break;
|
||||
case PS_SAT_CACHING:
|
||||
if (m_search_state == s_sat)
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i)
|
||||
m_phase[i] = m_best_phase[i];
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
if (m_phase[i] != m_best_phase[i])
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = m_best_phase[i];
|
||||
}
|
||||
break;
|
||||
case PS_RANDOM:
|
||||
for (auto& p : m_phase) p = (m_rand() % 2) == 0;
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
bool value = (m_rand() % 2) == 0;
|
||||
if (m_phase[i] != value)
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = value;
|
||||
}
|
||||
break;
|
||||
case PS_LOCAL_SEARCH:
|
||||
if (m_search_state == s_sat) {
|
||||
if (m_rand() % 2 == 0)
|
||||
bounded_local_search();
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i)
|
||||
m_phase[i] = m_best_phase[i];
|
||||
for (unsigned i = 0; i < m_phase.size(); ++i) {
|
||||
if (m_phase[i] != m_best_phase[i])
|
||||
m_phase_birthdate[i] = m_stats.m_conflicts;
|
||||
m_phase[i] = m_best_phase[i];
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
|
|
@ -3601,6 +3693,8 @@ namespace sat {
|
|||
m_phase.shrink(v);
|
||||
m_best_phase.shrink(v);
|
||||
m_prev_phase.shrink(v);
|
||||
m_phase_birthdate.shrink(v);
|
||||
m_best_phase_birthdate.shrink(v);
|
||||
m_assigned_since_gc.shrink(v);
|
||||
m_simplifier.reset_todos();
|
||||
}
|
||||
|
|
@ -3644,7 +3738,7 @@ namespace sat {
|
|||
SASSERT(value(v) == l_undef);
|
||||
m_case_split_queue.unassign_var_eh(v);
|
||||
if (m_config.m_anti_exploration) {
|
||||
m_canceled[v] = m_stats.m_conflict;
|
||||
m_canceled[v] = m_stats.m_conflicts;
|
||||
}
|
||||
}
|
||||
m_trail.shrink(old_sz);
|
||||
|
|
@ -3812,7 +3906,7 @@ namespace sat {
|
|||
double multiplier = m_config.m_reward_offset * (is_sat ? m_config.m_reward_multiplier : 1.0);
|
||||
for (unsigned i = qhead; i < m_trail.size(); ++i) {
|
||||
auto v = m_trail[i].var();
|
||||
auto d = m_stats.m_conflict - m_last_conflict[v] + 1;
|
||||
auto d = m_stats.m_conflicts - m_last_conflict[v] + 1;
|
||||
if (d == 0) d = 1;
|
||||
auto reward = multiplier / d;
|
||||
auto activity = m_activity[v];
|
||||
|
|
@ -4745,7 +4839,7 @@ namespace sat {
|
|||
st.update("sat mk var", m_mk_var);
|
||||
st.update("sat gc clause", m_gc_clause);
|
||||
st.update("sat del clause", m_del_clause);
|
||||
st.update("sat conflicts", m_conflict);
|
||||
st.update("sat conflicts", m_conflicts);
|
||||
st.update("sat decisions", m_decision);
|
||||
st.update("sat propagations 2ary", m_bin_propagate);
|
||||
st.update("sat propagations 3ary", m_ter_propagate);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ namespace sat {
|
|||
unsigned m_mk_bin_clause;
|
||||
unsigned m_mk_ter_clause;
|
||||
unsigned m_mk_clause;
|
||||
unsigned m_conflict;
|
||||
unsigned m_conflicts;
|
||||
unsigned m_propagate;
|
||||
unsigned m_bin_propagate;
|
||||
unsigned m_ter_propagate;
|
||||
|
|
@ -148,6 +148,8 @@ namespace sat {
|
|||
bool_vector m_phase;
|
||||
bool_vector m_best_phase;
|
||||
bool_vector m_prev_phase;
|
||||
svector<uint64_t> m_phase_birthdate;
|
||||
svector<uint64_t> m_best_phase_birthdate;
|
||||
bool m_new_best_phase = false;
|
||||
svector<char> m_assigned_since_gc;
|
||||
search_state m_search_state;
|
||||
|
|
@ -373,12 +375,17 @@ namespace sat {
|
|||
bool was_eliminated(bool_var v) const { return m_eliminated[v]; }
|
||||
void set_eliminated(bool_var v, bool f) override;
|
||||
bool was_eliminated(literal l) const { return was_eliminated(l.var()); }
|
||||
void set_phase(literal l) override { if (l.var() < num_vars()) m_best_phase[l.var()] = m_phase[l.var()] = !l.sign(); }
|
||||
void set_phase(literal l) override;
|
||||
bool get_phase(bool_var b) { return m_phase.get(b, false); }
|
||||
bool get_best_phase(bool_var b) { return m_best_phase.get(b, false); }
|
||||
uint64_t get_phase_birthdate(bool_var b) const { return m_phase_birthdate.get(b, 0); }
|
||||
uint64_t get_best_phase_birthdate(bool_var b) const { return m_best_phase_birthdate.get(b, 0); }
|
||||
uint64_t get_preferred_phase_birthdate(bool_var b) const;
|
||||
void set_has_new_best_phase(bool b) { m_new_best_phase = b; }
|
||||
bool has_new_best_phase() const { return m_new_best_phase; }
|
||||
void move_to_front(bool_var b);
|
||||
unsigned get_activity(bool_var v) const { return m_activity[v]; }
|
||||
void get_backbone_candidates(literal_vector& lits, unsigned max_num);
|
||||
unsigned scope_lvl() const { return m_scope_lvl; }
|
||||
unsigned search_lvl() const { return m_search_lvl; }
|
||||
bool at_search_lvl() const { return m_scope_lvl == m_search_lvl; }
|
||||
|
|
|
|||
|
|
@ -405,6 +405,50 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
unsigned get_assign_level(expr* e) const override {
|
||||
expr* atom = e;
|
||||
m.is_not(e, atom);
|
||||
sat::bool_var bv = m_map.to_bool_var(atom);
|
||||
return bv == sat::null_bool_var ? UINT_MAX : m_solver.lvl(bv);
|
||||
}
|
||||
|
||||
bool is_relevant(expr* e) const override {
|
||||
expr* atom = e;
|
||||
m.is_not(e, atom);
|
||||
sat::bool_var bv = m_map.to_bool_var(atom);
|
||||
if (bv == sat::null_bool_var)
|
||||
return true;
|
||||
auto* ext = dynamic_cast<euf::solver*>(m_solver.get_extension());
|
||||
return !ext || ext->is_relevant(bv);
|
||||
}
|
||||
|
||||
unsigned get_num_bool_vars() const override {
|
||||
return m_solver.num_vars();
|
||||
}
|
||||
|
||||
unsigned get_bool_var(expr* e) const override {
|
||||
expr* atom = e;
|
||||
m.is_not(e, atom);
|
||||
sat::bool_var bv = m_map.to_bool_var(atom);
|
||||
return bv == sat::null_bool_var ? UINT_MAX : bv;
|
||||
}
|
||||
|
||||
expr* bool_var2expr(unsigned v) const override {
|
||||
return v < m_solver.num_vars() ? m_map.bool_var2expr(v) : nullptr;
|
||||
}
|
||||
|
||||
lbool get_assignment(unsigned v) const override {
|
||||
return v < m_solver.num_vars() ? m_solver.value(v) : l_undef;
|
||||
}
|
||||
|
||||
double get_activity(unsigned v) const override {
|
||||
return v < m_solver.num_vars() ? static_cast<double>(m_solver.get_activity(v)) : 0.0;
|
||||
}
|
||||
|
||||
bool was_eliminated(unsigned v) const override {
|
||||
return v < m_solver.num_vars() && m_solver.was_eliminated(v);
|
||||
}
|
||||
|
||||
expr_ref_vector get_trail(unsigned max_level) override {
|
||||
expr_ref_vector result(m);
|
||||
unsigned sz = m_solver.trail_size();
|
||||
|
|
@ -451,8 +495,35 @@ public:
|
|||
vars.push_back(kv.m_value);
|
||||
}
|
||||
sat::literal_vector lits;
|
||||
lbool result = m_solver.cube(vars, lits, backtrack_level);
|
||||
expr_ref_vector fmls(m);
|
||||
if (!m_params.get_bool("cube.lookahead", false)) {
|
||||
sat::bool_var best = sat::null_bool_var;
|
||||
double best_activity = 0.0;
|
||||
unsigned n = 0;
|
||||
for (sat::bool_var v : vars) {
|
||||
if (get_assignment(v) != l_undef || was_eliminated(v))
|
||||
continue;
|
||||
double activity = get_activity(v);
|
||||
if (best == sat::null_bool_var || activity > best_activity || (activity == best_activity && m_solver.rand()(++n) == 0)) {
|
||||
best = v;
|
||||
best_activity = activity;
|
||||
}
|
||||
}
|
||||
if (best == sat::null_bool_var)
|
||||
return expr_ref_vector(m);
|
||||
expr* e = bool_var2expr(best);
|
||||
SASSERT(e);
|
||||
if (e)
|
||||
fmls.push_back(e);
|
||||
vs.reset();
|
||||
for (sat::bool_var v : vars) {
|
||||
expr* x = bool_var2expr(v);
|
||||
if (x)
|
||||
vs.push_back(x);
|
||||
}
|
||||
return fmls;
|
||||
}
|
||||
lbool result = m_solver.cube(vars, lits, backtrack_level);
|
||||
expr_ref_vector lit2expr(m);
|
||||
lit2expr.resize(m_solver.num_vars() * 2);
|
||||
m_map.mk_inv(lit2expr);
|
||||
|
|
@ -482,6 +553,27 @@ public:
|
|||
return fmls;
|
||||
}
|
||||
|
||||
void get_backbone_candidates(vector<solver::scored_literal>& candidates, unsigned max_num) override {
|
||||
if (!is_internalized()) {
|
||||
lbool r = internalize_formulas();
|
||||
if (r != l_true)
|
||||
return;
|
||||
}
|
||||
convert_internalized();
|
||||
sat::literal_vector lits;
|
||||
m_solver.get_backbone_candidates(lits, max_num);
|
||||
expr_ref_vector lit2expr(m);
|
||||
lit2expr.resize(m_solver.num_vars() * 2);
|
||||
m_map.mk_inv(lit2expr);
|
||||
uint64_t now = m_solver.get_stats().m_conflicts;
|
||||
for (sat::literal lit : lits) {
|
||||
expr* e = lit2expr.get(lit.index());
|
||||
if (!e)
|
||||
continue;
|
||||
candidates.push_back(scored_literal(m, e, static_cast<double>(now - m_solver.get_phase_birthdate(lit.var()))));
|
||||
}
|
||||
}
|
||||
|
||||
expr* congruence_next(expr* e) override { return e; }
|
||||
expr* congruence_root(expr* e) override { return e; }
|
||||
expr_ref congruence_explain(expr* a, expr* b) override { return expr_ref(m.mk_eq(a, b), m); }
|
||||
|
|
|
|||
|
|
@ -49,6 +49,13 @@ sat::bool_var atom2bool_var::to_bool_var(expr * n) const {
|
|||
return m_mapping[idx].m_value;
|
||||
}
|
||||
|
||||
expr* atom2bool_var::bool_var2expr(sat::bool_var v) const {
|
||||
for (auto const& kv : m_mapping)
|
||||
if (kv.m_value == v)
|
||||
return kv.m_key;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
struct collect_boolean_interface_proc {
|
||||
struct visitor {
|
||||
obj_hashtable<expr> & m_r;
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ public:
|
|||
atom2bool_var(ast_manager & m):expr2var(m) {}
|
||||
void insert(expr * n, sat::bool_var v) { expr2var::insert(n, v); }
|
||||
sat::bool_var to_bool_var(expr * n) const;
|
||||
expr* bool_var2expr(sat::bool_var v) const;
|
||||
void mk_inv(expr_ref_vector & lit2expr) const;
|
||||
void mk_var_inv(expr_ref_vector & var2expr) const;
|
||||
// return true if the mapping contains uninterpreted atoms.
|
||||
|
|
|
|||
|
|
@ -155,7 +155,7 @@ namespace bv {
|
|||
void ackerman::propagate() {
|
||||
auto* n = m_queue;
|
||||
vv* k = nullptr;
|
||||
unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflict * s.get_config().m_dack_factor);
|
||||
unsigned num_prop = static_cast<unsigned>(s.s().get_stats().m_conflicts * s.get_config().m_dack_factor);
|
||||
num_prop = std::min(num_prop, m_table.size());
|
||||
for (unsigned i = 0; i < num_prop; ++i, n = k) {
|
||||
k = n->next();
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ namespace euf {
|
|||
SASSERT(ctx.s().at_base_lvl());
|
||||
auto* n = m_queue;
|
||||
inference* k = nullptr;
|
||||
unsigned num_prop = static_cast<unsigned>(ctx.s().get_stats().m_conflict * ctx.m_config.m_dack_factor);
|
||||
unsigned num_prop = static_cast<unsigned>(ctx.s().get_stats().m_conflicts * ctx.m_config.m_dack_factor);
|
||||
num_prop = std::min(num_prop, m_table.size());
|
||||
for (unsigned i = 0; i < num_prop; ++i, n = k) {
|
||||
k = n->next();
|
||||
|
|
|
|||
|
|
@ -3652,6 +3652,13 @@ namespace smt {
|
|||
}
|
||||
}
|
||||
|
||||
void context::setup_for_parallel() {
|
||||
// Native SMT parallel configures the parent context before cloning workers.
|
||||
// context::copy then configures/internalizes each worker copy while
|
||||
// preprocessing is still enabled.
|
||||
setup_context(m_fparams.m_auto_config);
|
||||
}
|
||||
|
||||
config_mode context::get_config_mode(bool use_static_features) const {
|
||||
if (!m_fparams.m_auto_config)
|
||||
return CFG_BASIC;
|
||||
|
|
|
|||
|
|
@ -64,6 +64,7 @@ namespace smt {
|
|||
|
||||
class model_generator;
|
||||
class context;
|
||||
class kernel;
|
||||
|
||||
struct oom_exception : public z3_error {
|
||||
oom_exception() : z3_error(ERR_MEMOUT) {}
|
||||
|
|
@ -85,6 +86,7 @@ namespace smt {
|
|||
friend class model_generator;
|
||||
friend class lookahead;
|
||||
friend class parallel;
|
||||
friend class kernel;
|
||||
public:
|
||||
statistics m_stats;
|
||||
|
||||
|
|
@ -452,6 +454,8 @@ namespace smt {
|
|||
svector<double> const & get_activity_vector() const { return m_activity; }
|
||||
|
||||
double get_activity(bool_var v) const { return m_activity[v]; }
|
||||
unsigned get_num_assignments() const { return m_stats.m_num_assignments; }
|
||||
unsigned get_birthdate(bool_var v) const { return m_birthdate[v]; }
|
||||
|
||||
void set_activity(bool_var v, double act) { m_activity[v] = act; }
|
||||
|
||||
|
|
@ -538,6 +542,8 @@ namespace smt {
|
|||
return m_scope_lvl == m_search_lvl;
|
||||
}
|
||||
|
||||
void pop_to_search_level() { pop_to_search_lvl(); }
|
||||
|
||||
bool tracking_assumptions() const {
|
||||
return !m_assumptions.empty() && m_search_lvl > m_base_lvl;
|
||||
}
|
||||
|
|
@ -1697,6 +1703,8 @@ namespace smt {
|
|||
|
||||
lbool setup_and_check(bool reset_cancel = true);
|
||||
|
||||
void setup_for_parallel();
|
||||
|
||||
void reduce_assertions();
|
||||
|
||||
bool resource_limits_exceeded();
|
||||
|
|
@ -1914,4 +1922,3 @@ namespace smt {
|
|||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -280,6 +280,22 @@ namespace smt {
|
|||
smt_params_helper::collect_param_descrs(d);
|
||||
}
|
||||
|
||||
void kernel::pop_to_base_level() {
|
||||
m_imp->m_kernel.pop_to_base_lvl();
|
||||
}
|
||||
|
||||
void kernel::set_preprocess(bool f) {
|
||||
m_imp->m_kernel.get_fparams().m_preprocess = f;
|
||||
}
|
||||
|
||||
void kernel::reset_aux_statistics() {
|
||||
m_imp->m_kernel.m_aux_stats.reset();
|
||||
}
|
||||
|
||||
void kernel::add_aux_statistics(::statistics const& st) {
|
||||
m_imp->m_kernel.m_aux_stats.copy(st);
|
||||
}
|
||||
|
||||
context & kernel::get_context() {
|
||||
return m_imp->m_kernel;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -300,6 +300,14 @@ namespace smt {
|
|||
*/
|
||||
static void collect_param_descrs(param_descrs & d);
|
||||
|
||||
void pop_to_base_level();
|
||||
|
||||
void set_preprocess(bool f);
|
||||
|
||||
void reset_aux_statistics();
|
||||
|
||||
void add_aux_statistics(::statistics const& st);
|
||||
|
||||
void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause);
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -626,7 +626,7 @@ namespace smt {
|
|||
ctx->set_logic(p.ctx.m_setup.get_logic());
|
||||
context::copy(p.ctx, *ctx, true);
|
||||
ctx->pop_to_base_lvl();
|
||||
ctx->get_fparams().m_preprocess = false;
|
||||
ctx->get_fparams().m_preprocess = false; // avoid preprocessing lemmas that are exchanged
|
||||
}
|
||||
|
||||
void parallel::core_minimizer_worker::cancel() {
|
||||
|
|
@ -887,6 +887,7 @@ namespace smt {
|
|||
ctx->pop_to_base_lvl();
|
||||
m_shared_units_prefix = ctx->assigned_literals().size();
|
||||
m_num_initial_atoms = ctx->get_num_bool_vars();
|
||||
ctx->get_fparams().m_preprocess = false; // avoid preprocessing lemmas that are exchanged
|
||||
|
||||
smt_parallel_params pp(p.ctx.m_params);
|
||||
m_use_failed_literal_test = pp.num_global_bb_fl_threads() > 0;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@ namespace smt {
|
|||
struct cube_config {
|
||||
using literal = expr_ref;
|
||||
static bool literal_is_null(expr_ref const& l) { return l == nullptr; }
|
||||
static bool same_atom(expr_ref const& a, expr_ref const& b) {
|
||||
expr* atom_a = a.get();
|
||||
expr* atom_b = b.get();
|
||||
a.get_manager().is_not(atom_a, atom_a);
|
||||
b.get_manager().is_not(atom_b, atom_b);
|
||||
return atom_a == atom_b;
|
||||
}
|
||||
static std::ostream& display_literal(std::ostream& out, expr_ref const& l) { return out << mk_bounded_pp(l, l.get_manager()); }
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -22,12 +22,15 @@ Notes:
|
|||
#include "ast/for_each_expr.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/func_decl_dependencies.h"
|
||||
#include "smt/smt_context.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "params/smt_params.h"
|
||||
#include "params/smt_params_helper.hpp"
|
||||
#include "solver/solver_na2as.h"
|
||||
#include "solver/mus.h"
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
namespace {
|
||||
|
||||
class smt_solver : public solver_na2as {
|
||||
|
|
@ -61,6 +64,7 @@ namespace {
|
|||
smt_params m_smt_params;
|
||||
smt::kernel m_context;
|
||||
cuber* m_cuber;
|
||||
random_gen m_rand;
|
||||
symbol m_logic;
|
||||
bool m_minimizing_core;
|
||||
bool m_core_extend_patterns;
|
||||
|
|
@ -86,14 +90,17 @@ namespace {
|
|||
|
||||
solver * translate(ast_manager & m, params_ref const & p) override {
|
||||
ast_translation translator(get_manager(), m);
|
||||
params_ref init;
|
||||
init.copy(get_params());
|
||||
init.copy(p);
|
||||
|
||||
smt_solver * result = alloc(smt_solver, m, p, m_logic);
|
||||
smt_solver* result = alloc(smt_solver, m, init, m_logic);
|
||||
smt::kernel::copy(m_context, result->m_context, true);
|
||||
|
||||
if (mc0())
|
||||
if (mc0())
|
||||
result->set_model_converter(mc0()->translate(translator));
|
||||
|
||||
for (auto & [k, v] : m_name2assertion) {
|
||||
for (auto& [k, v] : m_name2assertion) {
|
||||
expr* val = translator(k);
|
||||
expr* key = translator(v);
|
||||
result->assert_expr(val, key);
|
||||
|
|
@ -212,6 +219,112 @@ namespace {
|
|||
return m_context.get_trail(max_level);
|
||||
}
|
||||
|
||||
expr_ref_vector get_assigned_literals() override {
|
||||
expr_ref_vector result(m);
|
||||
auto& ctx = const_cast<smt::kernel&>(m_context).get_context();
|
||||
for (auto lit : ctx.assigned_literals()) {
|
||||
expr* atom = ctx.bool_var2expr(lit.var());
|
||||
if (!atom)
|
||||
continue;
|
||||
result.push_back(lit.sign() ? m.mk_not(atom) : atom);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
unsigned get_assign_level(expr* e) const override {
|
||||
auto& ctx = const_cast<smt::kernel&>(m_context).get_context();
|
||||
expr* atom = e;
|
||||
get_manager().is_not(e, atom);
|
||||
if (!ctx.b_internalized(atom))
|
||||
return UINT_MAX;
|
||||
return ctx.get_assign_level(ctx.get_bool_var(atom));
|
||||
}
|
||||
|
||||
bool is_relevant(expr* e) const override {
|
||||
auto& ctx = const_cast<smt::kernel&>(m_context).get_context();
|
||||
expr* atom = e;
|
||||
get_manager().is_not(e, atom);
|
||||
return ctx.b_internalized(atom) && ctx.is_relevant(atom);
|
||||
}
|
||||
|
||||
unsigned get_num_bool_vars() const override {
|
||||
return const_cast<smt::kernel&>(m_context).get_context().get_num_bool_vars();
|
||||
}
|
||||
|
||||
unsigned get_bool_var(expr* e) const override {
|
||||
auto& ctx = const_cast<smt::kernel&>(m_context).get_context();
|
||||
expr* atom = e;
|
||||
get_manager().is_not(e, atom);
|
||||
return ctx.b_internalized(atom) ? ctx.get_bool_var(atom) : UINT_MAX;
|
||||
}
|
||||
|
||||
void pop_to_base_level() override {
|
||||
m_context.pop_to_base_level();
|
||||
}
|
||||
|
||||
void setup_for_parallel() override {
|
||||
const_cast<smt::kernel&>(m_context).get_context().setup_for_parallel();
|
||||
}
|
||||
|
||||
void set_preprocess(bool f) override {
|
||||
m_context.set_preprocess(f);
|
||||
}
|
||||
|
||||
void set_max_conflicts(unsigned max_conflicts) override {
|
||||
auto& ctx = const_cast<smt::kernel&>(m_context).get_context();
|
||||
ctx.get_fparams().m_max_conflicts = max_conflicts;
|
||||
}
|
||||
|
||||
unsigned get_max_conflicts() const override {
|
||||
return const_cast<smt::kernel&>(m_context).get_context().get_fparams().m_max_conflicts;
|
||||
}
|
||||
|
||||
void reset_parallel_statistics() override {
|
||||
m_context.reset_aux_statistics();
|
||||
}
|
||||
|
||||
void add_parallel_statistics(statistics const& st) override {
|
||||
m_context.add_aux_statistics(st);
|
||||
}
|
||||
|
||||
void collect_parallel_statistics(statistics& st) const override {
|
||||
m_context.collect_statistics(st);
|
||||
}
|
||||
|
||||
void get_backbone_candidates(vector<solver::scored_literal>& candidates, unsigned max_num) override {
|
||||
ast_manager& m = get_manager();
|
||||
auto& ctx = m_context.get_context();
|
||||
unsigned curr_time = ctx.get_num_assignments();
|
||||
vector<solver::scored_literal> all;
|
||||
|
||||
for (unsigned v = 0; v < ctx.get_num_bool_vars(); ++v) {
|
||||
if (ctx.get_assignment(v) != l_undef && ctx.get_assign_level(v) == ctx.get_base_level())
|
||||
continue;
|
||||
|
||||
expr* candidate = ctx.bool_var2expr(v);
|
||||
if (!candidate)
|
||||
continue;
|
||||
|
||||
auto const& d = ctx.get_bdata(v);
|
||||
if (d.m_phase_available && !d.m_phase)
|
||||
candidate = m.mk_not(candidate);
|
||||
|
||||
double age = static_cast<double>(curr_time - ctx.get_birthdate(v));
|
||||
all.push_back(solver::scored_literal(m, candidate, age));
|
||||
}
|
||||
|
||||
std::stable_sort(
|
||||
all.begin(),
|
||||
all.end(),
|
||||
[](solver::scored_literal const& a, solver::scored_literal const& b) {
|
||||
return a.score > b.score;
|
||||
});
|
||||
|
||||
unsigned n = std::min<unsigned>(max_num, all.size());
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
candidates.push_back(all[i]);
|
||||
}
|
||||
|
||||
void register_on_clause(void* ctx, user_propagator::on_clause_eh_t& on_clause) override {
|
||||
m_context.register_on_clause(ctx, on_clause);
|
||||
}
|
||||
|
|
@ -347,6 +460,44 @@ namespace {
|
|||
|
||||
expr_ref_vector cube(expr_ref_vector& vars, unsigned cutoff) override {
|
||||
ast_manager& m = get_manager();
|
||||
if (!get_params().get_bool("cube.lookahead", false)) {
|
||||
auto& ctx = m_context.get_context();
|
||||
obj_hashtable<expr> selected_vars;
|
||||
for (expr* v : vars)
|
||||
selected_vars.insert(v);
|
||||
expr_ref_vector candidates(m);
|
||||
expr_ref result(m);
|
||||
double score = 0.0;
|
||||
unsigned n = 0;
|
||||
|
||||
ctx.pop_to_search_level();
|
||||
for (unsigned v = 0; v < ctx.get_num_bool_vars(); ++v) {
|
||||
if (ctx.get_assignment(v) != l_undef)
|
||||
continue;
|
||||
expr* e = ctx.bool_var2expr(v);
|
||||
if (!e)
|
||||
continue;
|
||||
if (!selected_vars.empty() && !selected_vars.contains(e))
|
||||
continue;
|
||||
candidates.push_back(e);
|
||||
double new_score = ctx.get_activity(v);
|
||||
// Match smt_parallel: cube split tie-breaking is independent
|
||||
// of the per-worker SMT search seed.
|
||||
if (new_score > score || !result || (new_score == score && m_rand(++n) == 0)) {
|
||||
score = new_score;
|
||||
result = e;
|
||||
}
|
||||
}
|
||||
|
||||
vars.reset();
|
||||
vars.append(candidates);
|
||||
|
||||
expr_ref_vector lits(m);
|
||||
if (result)
|
||||
lits.push_back(result);
|
||||
return lits;
|
||||
}
|
||||
|
||||
if (!m_cuber) {
|
||||
m_cuber = alloc(cuber, *this);
|
||||
// force propagation
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@ def_module_params('parallel',
|
|||
('enable', BOOL, False, 'enable parallel solver by default on selected tactics (for QF_BV)'),
|
||||
('enable2', BOOL, False, 'enable (experimental) parallel solver by default on selected tactics (for QF_BV)'),
|
||||
('threads.max', UINT, 10000, 'caps maximal number of threads below the number of processors'),
|
||||
('num_bb_threads', UINT, 2, 'run Janota-style chunking backbone worker threads; default is 2 (negative and positive mode), supported values are 0 (off), 1 (negative mode only) or 2 (negative and positive mode)'),
|
||||
('core_minimize', BOOL, True, 'minimize unsat cores used for parallel cube backtracking'),
|
||||
('cube.lookahead', BOOL, False, 'use lookahead cubing in the parallel solver; when false, use VSIDS activity to select one split literal'),
|
||||
('conquer.batch_size', UINT, 100, 'number of cubes to batch together for fast conquer'),
|
||||
('conquer.restart.max', UINT, 5, 'maximal number of restarts during conquer phase'),
|
||||
('conquer.delay', UINT, 10, 'delay of cubes until applying conquer'),
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -57,7 +57,15 @@ solver* mk_smt2_solver(ast_manager& m, params_ref const& p, symbol const& logic
|
|||
class solver : public check_sat_result, public user_propagator::core {
|
||||
params_ref m_params;
|
||||
symbol m_cancel_backup_file;
|
||||
statistics m_parallel_stats;
|
||||
public:
|
||||
struct scored_literal {
|
||||
expr_ref lit;
|
||||
double score = 0.0;
|
||||
scored_literal(ast_manager& m, expr* e, double s): lit(e, m), score(s) {}
|
||||
scored_literal(expr_ref const& e, double s): lit(e), score(s) {}
|
||||
};
|
||||
|
||||
solver(ast_manager& m): check_sat_result(m) {}
|
||||
|
||||
/**
|
||||
|
|
@ -298,9 +306,47 @@ public:
|
|||
expr_ref_vector get_non_units();
|
||||
|
||||
virtual expr_ref_vector get_trail(unsigned max_level) = 0; // { return expr_ref_vector(get_manager()); }
|
||||
virtual expr_ref_vector get_assigned_literals() { return get_trail(UINT_MAX); }
|
||||
virtual unsigned get_assign_level(expr* e) const { return UINT_MAX; }
|
||||
virtual bool is_relevant(expr* e) const { return true; }
|
||||
virtual unsigned get_num_bool_vars() const { return UINT_MAX; }
|
||||
virtual unsigned get_bool_var(expr* e) const { return UINT_MAX; }
|
||||
virtual expr* bool_var2expr(unsigned) const { return nullptr; }
|
||||
virtual lbool get_assignment(unsigned) const { return l_undef; }
|
||||
virtual double get_activity(unsigned) const { return 0.0; }
|
||||
virtual bool was_eliminated(unsigned) const { return false; }
|
||||
|
||||
virtual void pop_to_base_level() {}
|
||||
|
||||
virtual void setup_for_parallel() {}
|
||||
|
||||
virtual void set_preprocess(bool) {}
|
||||
|
||||
virtual void set_max_conflicts(unsigned max_conflicts) {
|
||||
params_ref p;
|
||||
p.set_uint("max_conflicts", max_conflicts);
|
||||
updt_params(p);
|
||||
}
|
||||
|
||||
virtual unsigned get_max_conflicts() const { return UINT_MAX; }
|
||||
|
||||
virtual void reset_parallel_statistics() {
|
||||
m_parallel_stats.reset();
|
||||
}
|
||||
|
||||
virtual void add_parallel_statistics(statistics const& st) {
|
||||
m_parallel_stats.copy(st);
|
||||
}
|
||||
|
||||
virtual void collect_parallel_statistics(statistics& st) const {
|
||||
st.copy(m_parallel_stats);
|
||||
collect_statistics(st);
|
||||
}
|
||||
|
||||
virtual void get_levels(ptr_vector<expr> const& vars, unsigned_vector& depth) = 0;
|
||||
|
||||
virtual void get_backbone_candidates(vector<scored_literal>&, unsigned) {}
|
||||
|
||||
class scoped_push {
|
||||
solver& s;
|
||||
bool m_nopop;
|
||||
|
|
|
|||
|
|
@ -127,6 +127,10 @@ public:
|
|||
m_base->get_levels(vars, depth);
|
||||
}
|
||||
|
||||
void get_backbone_candidates(vector<solver::scored_literal>& candidates, unsigned max_num) override {
|
||||
m_base->get_backbone_candidates(candidates, max_num);
|
||||
}
|
||||
|
||||
expr_ref_vector get_trail(unsigned max_level) override {
|
||||
return m_base->get_trail(max_level);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,9 +68,17 @@ namespace search_tree {
|
|||
literal const &get_literal() const {
|
||||
return m_literal;
|
||||
}
|
||||
bool path_contains_atom(literal const& l) const {
|
||||
for (node const* n = this; n; n = n->parent())
|
||||
if (!Config::literal_is_null(n->get_literal()) && Config::same_atom(n->get_literal(), l))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
void split(literal const &a, literal const &b) {
|
||||
SASSERT(!Config::literal_is_null(a));
|
||||
SASSERT(!Config::literal_is_null(b));
|
||||
VERIFY(!path_contains_atom(a));
|
||||
VERIFY(!path_contains_atom(b));
|
||||
if (m_status != status::active)
|
||||
return;
|
||||
SASSERT(!m_left);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue