/*++ Copyright (c) 2011 Microsoft Corporation Module Name: sat_elim_eqs.cpp Abstract: Helper class for eliminating eqs. Author: Leonardo de Moura (leonardo) 2011-05-27. Revision History: --*/ #include "sat/sat_elim_eqs.h" #include "sat/sat_solver.h" #include "util/trace.h" namespace sat { elim_eqs::elim_eqs(solver & s): m_solver(s), m_to_delete(nullptr) { } elim_eqs::~elim_eqs() { dealloc(m_to_delete); } inline literal norm(literal_vector const & roots, literal l) { if (l.sign()) return ~roots[l.var()]; else return roots[l.var()]; } void elim_eqs::cleanup_bin_watches(literal_vector const & roots) { unsigned l_idx = 0; m_new_bin.reset(); for (watch_list & wlist : m_solver.m_watches) { literal l1 = ~to_literal(l_idx++); literal r1 = norm(roots, l1); watch_list::iterator it = wlist.begin(); watch_list::iterator itprev = it; watch_list::iterator end = wlist.end(); for (; it != end; ++it) { if (it->is_binary_clause()) { literal l2 = it->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { m_solver.assign_unit(r1); if (m_solver.inconsistent()) { ++it; for (; it != end; ++it, ++itprev) *itprev = *it; wlist.set_end(itprev); return; } // consume unit continue; } if (r1 == ~r2) { // consume tautology continue; } if (l1 != r1 || l2 != r2) { if (r1.index() < r2.index()) { TRACE("elim_eqs", tout << l1 << " " << l2 << " " << r1 << " " << r2 << "\n";); m_new_bin.push_back(bin(r1, r2, it->is_learned())); } continue; } // keep it } *itprev = *it; itprev++; } wlist.set_end(itprev); } for (auto const& b : m_new_bin) { m_solver.mk_bin_clause(b.l1, b.l2, b.learned); } m_new_bin.reset(); } void elim_eqs::drat_delete_clause() { if (m_solver.m_config.m_drat) { m_solver.m_drat.del(*m_to_delete->get()); } } void elim_eqs::cleanup_clauses(literal_vector const & roots, clause_vector & cs) { clause_vector::iterator it = cs.begin(); clause_vector::iterator it2 = it; clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); TRACE("sats", tout << "processing: " << c << "\n";); unsigned sz = c.size(); unsigned i; for (i = 0; i < sz; i++) { literal l = c[i]; literal r = norm(roots, l); if (l != r) break; } if (i == sz) { // clause was not affected *it2 = *it; it2++; continue; } if (!c.frozen()) { m_solver.detach_clause(c); } // save clause to be deleted for drat if (m_solver.m_config.m_drat) { if (!m_to_delete) m_to_delete = alloc(tmp_clause); m_to_delete->set(sz, c.begin(), c.is_learned()); } // apply substitution for (i = 0; i < sz; i++) { literal lit = c[i]; c[i] = norm(roots, lit); VERIFY(c[i] == norm(roots, c[i])); VERIFY(!m_solver.was_eliminated(c[i].var()) || lit == c[i]); } std::sort(c.begin(), c.end()); for (literal l : c) VERIFY(l == norm(roots, l)); TRACE("sats", tout << "after normalization/sorting: " << c << "\n"; tout.flush();); DEBUG_CODE({ for (literal l : c) { CTRACE("sat", l != norm(roots, l), tout << l << " " << norm(roots, l) << "\n"; tout.flush();); SASSERT(l == norm(roots, l)); } }); // remove duplicates, and check if it is a tautology unsigned j = 0; literal l_prev = null_literal; for (i = 0; i < sz; i++) { literal l = c[i]; if (l == ~l_prev) { break; } if (l == l_prev) { continue; } SASSERT(l != ~l_prev); l_prev = l; lbool val = m_solver.value(l); if (val == l_true) { break; } if (val == l_false) { continue; // skip } c[j] = l; j++; } TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); if (i < sz) { drat_delete_clause(); c.set_removed(true); m_solver.del_clause(c); continue; } switch (j) { case 0: m_solver.set_conflict(); for (; it != end; ++it) { *it2 = *it; it2++; } cs.set_end(it2); return; case 1: m_solver.assign_unit(c[0]); drat_delete_clause(); c.set_removed(true); m_solver.del_clause(c); break; case 2: m_solver.mk_bin_clause(c[0], c[1], c.is_learned()); drat_delete_clause(); c.set_removed(true); m_solver.del_clause(c); break; default: SASSERT(*it == &c); if (j < sz) { c.shrink(j); } else { c.update_approx(); } if (m_solver.m_config.m_drat) { m_solver.m_drat.add(c, status::redundant()); drat_delete_clause(); } DEBUG_CODE(for (literal l : c) VERIFY(l == norm(roots, l));); *it2 = *it; it2++; if (!c.frozen()) { m_solver.attach_clause(c); } break; } } cs.set_end(it2); } void elim_eqs::save_elim(literal_vector const & roots, bool_var_vector const & to_elim) { model_converter & mc = m_solver.m_mc; for (bool_var v : to_elim) { literal l(v, false); literal r = roots[v]; SASSERT(v != r.var()); if (m_solver.m_cut_simplifier) m_solver.m_cut_simplifier->set_root(v, r); bool set_root = m_solver.set_root(l, r); TRACE("elim_eqs", tout << l << " " << r << "\n";); if (m_solver.is_assumption(v) || (m_solver.is_external(v) && (m_solver.is_incremental() || !set_root))) { // cannot really eliminate v, since we have to notify extension of future assignments if (m_solver.m_config.m_drat) { m_solver.m_drat.add(~l, r, sat::status::redundant()); m_solver.m_drat.add(l, ~r, sat::status::redundant()); } m_solver.mk_bin_clause(~l, r, false); m_solver.mk_bin_clause(l, ~r, false); } else { model_converter::entry & e = mc.mk(model_converter::ELIM_VAR, v); TRACE("save_elim", tout << "marking as deleted: " << v << " l: " << l << " r: " << r << "\n";); m_solver.set_eliminated(v, true); mc.insert(e, ~l, r); mc.insert(e, l, ~r); } } m_solver.flush_roots(); } bool elim_eqs::check_clause(clause const& c, literal_vector const& roots) const { for (literal l : c) { CTRACE("elim_eqs_bug", m_solver.was_eliminated(l.var()), tout << "lit: " << l << " " << norm(roots, l) << "\n"; tout << c << "\n";); if (m_solver.was_eliminated(l.var())) { IF_VERBOSE(0, verbose_stream() << c << " contains eliminated literal " << l << " " << norm(roots, l) << "\n";); UNREACHABLE(); } } return true; } bool elim_eqs::check_clauses(literal_vector const & roots) const { for (clause * cp : m_solver.m_clauses) if (!check_clause(*cp, roots)) return false; for (clause * cp : m_solver.m_learned) if (!check_clause(*cp, roots)) return false; return true; } void elim_eqs::operator()(literal_vector const & roots, bool_var_vector const & to_elim) { TRACE("elim_eqs", tout << "before bin cleanup\n"; m_solver.display(tout);); cleanup_bin_watches(roots); TRACE("elim_eqs", tout << "after bin cleanup\n"; m_solver.display(tout);); cleanup_clauses(roots, m_solver.m_clauses); if (m_solver.inconsistent()) return; cleanup_clauses(roots, m_solver.m_learned); if (m_solver.inconsistent()) return; save_elim(roots, to_elim); m_solver.propagate(false); SASSERT(check_clauses(roots)); TRACE("elim_eqs", tout << "after full cleanup\n"; m_solver.display(tout);); } void elim_eqs::operator()(union_find<>& uf) { TRACE("elim_eqs", tout << "before union-find bin\n";); literal_vector roots(m_solver.num_vars(), null_literal); bool_var_vector to_elim; for (unsigned i = m_solver.num_vars(); i-- > 0; ) { literal l1(i, false); unsigned idx = uf.find(l1.index()); if (idx != l1.index()) { roots[i] = to_literal(idx); to_elim.push_back(i); TRACE("elim_eqs", tout << "remove " << roots[i] << "\n";); } else { roots[i] = l1; } } (*this)(roots, to_elim); } };