/*++ Copyright (c) 2020 Microsoft Corporation Module Name: smt_relevant.cpp Abstract: Relevancy propagation Author: Nikolaj Bjorner (nbjorner) 2021-12-27 --*/ #include "sat/sat_solver.h" #include "sat/smt/euf_solver.h" #include "sat/smt/euf_relevancy.h" namespace euf { void relevancy::pop(unsigned n) { if (!m_enabled) return; if (n <= m_num_scopes) { m_num_scopes -= n; return; } else if (m_num_scopes > 0) { n -= m_num_scopes; m_num_scopes = 0; } SASSERT(n > 0); unsigned sz = m_lim[m_lim.size() - n]; for (unsigned i = m_trail.size(); i-- > sz; ) { auto const& [u, idx] = m_trail[i]; switch (u) { case update::relevant_var: m_relevant_var_ids[idx] = false; break; case update::add_queue: m_queue.pop_back(); break; case update::add_clause: { sat::clause* c = m_clauses.back(); for (sat::literal lit : *c) { SASSERT(m_occurs[lit.index()].back() == m_clauses.size() - 1); m_occurs[lit.index()].pop_back(); } m_clauses.pop_back(); m_roots.pop_back(); m_alloc.del_clause(c); break; } case update::set_root: m_roots[idx] = false; break; case update::set_qhead: m_qhead = idx; break; default: UNREACHABLE(); break; } } m_trail.shrink(sz); m_lim.shrink(m_lim.size() - n); } void relevancy::add_root(unsigned n, sat::literal const* lits) { if (!m_enabled) return; flush(); TRACE("relevancy", tout << "root " << sat::literal_vector(n, lits) << "\n"); sat::literal true_lit = sat::null_literal; for (unsigned i = 0; i < n; ++i) { if (ctx.s().value(lits[i]) == l_true) { if (is_relevant(lits[i])) return; true_lit = lits[i]; } } if (true_lit != sat::null_literal) { mark_relevant(true_lit); return; } sat::clause* cl = m_alloc.mk_clause(n, lits, false); unsigned sz = m_clauses.size(); m_clauses.push_back(cl); m_roots.push_back(true); m_trail.push_back(std::make_pair(update::add_clause, 0)); for (sat::literal lit : *cl) { ctx.s().set_external(lit.var()); occurs(lit).push_back(sz); } } void relevancy::add_def(unsigned n, sat::literal const* lits) { if (!m_enabled) return; flush(); TRACE("relevancy", tout << "def " << sat::literal_vector(n, lits) << "\n"); for (unsigned i = 0; i < n; ++i) { if (ctx.s().value(lits[i]) == l_false && is_relevant(lits[i])) { add_root(n, lits); return; } } sat::clause* cl = m_alloc.mk_clause(n, lits, false); unsigned sz = m_clauses.size(); m_clauses.push_back(cl); m_roots.push_back(false); m_trail.push_back(std::make_pair(update::add_clause, 0)); for (sat::literal lit : *cl) { ctx.s().set_external(lit.var()); occurs(lit).push_back(sz); } } void relevancy::add_to_propagation_queue(sat::literal lit) { m_trail.push_back(std::make_pair(update::add_queue, lit.var())); m_queue.push_back(std::make_pair(lit, nullptr)); } void relevancy::set_relevant(sat::literal lit) { euf::enode* n = ctx.bool_var2enode(lit.var()); if (n) mark_relevant(n); m_relevant_var_ids.setx(lit.var(), true, false); m_trail.push_back(std::make_pair(update::relevant_var, lit.var())); } void relevancy::set_asserted(sat::literal lit) { SASSERT(!is_relevant(lit)); SASSERT(ctx.s().value(lit) == l_true); set_relevant(lit); add_to_propagation_queue(lit); ctx.asserted(lit); } /** * Boolean variable is set relevant because an E-node is relevant. * */ void relevancy::relevant_eh(sat::bool_var v) { if (is_relevant(v)) return; sat::literal lit(v); switch (ctx.s().value(lit)) { case l_undef: set_relevant(lit); break; case l_true: set_asserted(lit); break; case l_false: set_asserted(~lit); break; } } void relevancy::asserted(sat::literal lit) { TRACE("relevancy", tout << "asserted " << lit << " relevant " << is_relevant(lit) << "\n"); if (!m_enabled) return; flush(); if (is_relevant(lit)) { add_to_propagation_queue(lit); return; } if (ctx.s().lvl(lit) <= ctx.s().search_lvl()) { set_relevant(lit); add_to_propagation_queue(lit); return; } for (auto idx : occurs(lit)) { if (!m_roots[idx]) continue; for (sat::literal lit2 : *m_clauses[idx]) if (lit2 != lit && ctx.s().value(lit2) == l_true && is_relevant(lit2)) goto next; set_relevant(lit); add_to_propagation_queue(lit); return; next: ; } } void relevancy::propagate() { if (!m_enabled) return; flush(); if (m_qhead == m_queue.size()) return; m_trail.push_back(std::make_pair(update::set_qhead, m_qhead)); while (m_qhead < m_queue.size() && !ctx.s().inconsistent() && ctx.get_manager().inc()) { auto const& [lit, n] = m_queue[m_qhead++]; SASSERT(n || lit != sat::null_literal); SASSERT(!n || lit == sat::null_literal); if (n) propagate_relevant(n); else propagate_relevant(lit); } } void relevancy::merge(euf::enode* root, euf::enode* other) { TRACE("relevancy", tout << "merge #" << ctx.bpp(root) << " " << is_relevant(root) << " #" << ctx.bpp(other) << " " << is_relevant(other) << "\n"); if (is_relevant(root)) mark_relevant(other); else if (is_relevant(other)) mark_relevant(root); } void relevancy::mark_relevant(euf::enode* n) { if (!m_enabled) return; flush(); if (is_relevant(n)) return; TRACE("relevancy", tout << "mark #" << ctx.bpp(n) << "\n"); m_trail.push_back(std::make_pair(update::add_queue, 0)); m_queue.push_back(std::make_pair(sat::null_literal, n)); } void relevancy::mark_relevant(sat::literal lit) { TRACE("relevancy", tout << "mark " << lit << " " << is_relevant(lit) << " " << ctx.s().value(lit) << " lim: " << m_lim.size() << "\n"); if (!m_enabled) return; flush(); if (is_relevant(lit)) return; set_relevant(lit); switch (ctx.s().value(lit)) { case l_true: break; case l_false: lit.neg(); break; default: return; } add_to_propagation_queue(lit); } void relevancy::propagate_relevant(sat::literal lit) { SASSERT(m_num_scopes == 0); TRACE("relevancy", tout << "propagate " << lit << " lim: " << m_lim.size() << "\n"); SASSERT(ctx.s().value(lit) == l_true); SASSERT(is_relevant(lit)); euf::enode* n = ctx.bool_var2enode(lit.var()); if (n && !ctx.get_si().is_bool_op(n->get_expr())) return; for (auto idx : occurs(~lit)) { if (m_roots[idx]) continue; sat::clause* cl = m_clauses[idx]; sat::literal true_lit = sat::null_literal; for (sat::literal lit2 : *cl) { if (ctx.s().value(lit2) == l_true) { if (is_relevant(lit2)) goto next; true_lit = lit2; } } if (true_lit != sat::null_literal) set_asserted(true_lit); else { m_trail.push_back(std::make_pair(update::set_root, idx)); m_roots[idx] = true; } next: TRACE("relevancy", tout << "propagate " << lit << " " << true_lit << " " << m_roots[idx] << "\n"); ; } } void relevancy::propagate_relevant(euf::enode* n) { m_todo.push_back(n); while (!m_todo.empty()) { n = m_todo.back(); m_todo.pop_back(); TRACE("relevancy", tout << "propagate #" << ctx.bpp(n) << " lim: " << m_lim.size() << "\n"); if (n->is_relevant()) continue; m_stack.push_back(n); while (!m_stack.empty()) { n = m_stack.back(); unsigned sz = m_stack.size(); bool is_bool_op = ctx.get_si().is_bool_op(n->get_expr()); if (!is_bool_op) for (euf::enode* arg : euf::enode_args(n)) if (!arg->is_relevant()) m_stack.push_back(arg); if (sz != m_stack.size()) continue; if (!n->is_relevant()) { ctx.get_egraph().set_relevant(n); ctx.relevant_eh(n); sat::bool_var v = n->bool_var(); if (v != sat::null_bool_var) relevant_eh(v); for (euf::enode* sib : euf::enode_class(n)) if (!sib->is_relevant()) m_todo.push_back(sib); } if (!ctx.get_manager().inc()) { m_todo.reset(); m_stack.reset(); return; } m_stack.pop_back(); } } } void relevancy::set_enabled(bool e) { m_enabled = e; ctx.get_egraph().set_default_relevant(!e); } }