/*++ 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_elim_eqs.h" #include"sat_solver.h" #include"trace.h" namespace sat { elim_eqs::elim_eqs(solver & s): m_solver(s) { } 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) { vector::iterator it = m_solver.m_watches.begin(); vector::iterator end = m_solver.m_watches.end(); for (unsigned l_idx = 0; it != end; ++it, ++l_idx) { watch_list & wlist = *it; literal l1 = ~to_literal(l_idx); literal r1 = norm(roots, l1); watch_list::iterator it2 = wlist.begin(); watch_list::iterator itprev = it2; watch_list::iterator end2 = wlist.end(); for (; it2 != end2; ++it2) { if (it2->is_binary_clause()) { literal l2 = it2->get_literal(); literal r2 = norm(roots, l2); if (r1 == r2) { m_solver.assign(r1, justification()); if (m_solver.inconsistent()) return; // consume unit continue; } if (r1 == ~r2) { // consume tautology continue; } if (l1 != r1) { // add half r1 => r2, the other half ~r2 => ~r1 is added when traversing l2 m_solver.m_watches[(~r1).index()].push_back(watched(r2, it2->is_learned())); continue; } it2->set_literal(r2); // keep it } *itprev = *it2; itprev++; } wlist.set_end(itprev); } } 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("elim_eqs", 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.dettach_clause(c); // apply substitution for (i = 0; i < sz; i++) { SASSERT(!m_solver.was_eliminated(c[i].var())); c[i] = norm(roots, c[i]); } std::sort(c.begin(), c.end()); TRACE("elim_eqs", tout << "after normalization/sorting: " << c << "\n";); // remove duplicates, and check if it is a tautology literal l_prev = null_literal; unsigned j = 0; for (i = 0; i < sz; i++) { literal l = c[i]; if (l == l_prev) continue; if (l == ~l_prev) break; l_prev = l; lbool val = m_solver.value(l); if (val == l_true) break; // clause was satisfied if (val == l_false) continue; // skip c[j] = l; j++; } if (i < sz) { // clause is a tautology or was simplified m_solver.del_clause(c); continue; } if (j == 0) { // empty clause m_solver.set_conflict(justification()); return; } TRACE("elim_eqs", tout << "after removing duplicates: " << c << " j: " << j << "\n";); if (j < sz) c.shrink(j); else c.update_approx(); SASSERT(c.size() == j); DEBUG_CODE({ for (unsigned i = 0; i < c.size(); i++) { SASSERT(c[i] == norm(roots, c[i])); } }); SASSERT(j >= 1); switch (j) { case 1: m_solver.assign(c[0], justification()); m_solver.del_clause(c); break; case 2: m_solver.mk_bin_clause(c[0], c[1], c.is_learned()); m_solver.del_clause(c); break; default: SASSERT(*it == &c); *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; bool_var_vector::const_iterator it = to_elim.begin(); bool_var_vector::const_iterator end = to_elim.end(); for (; it != end; ++it) { bool_var v = *it; literal l(v, false); literal r = roots[v]; SASSERT(v != r.var()); if (m_solver.is_external(v)) { // cannot really eliminate v, since we have to notify extension of future assignments 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.m_eliminated[v] = true; mc.insert(e, ~l, r); mc.insert(e, l, ~r); } } } bool elim_eqs::check_clauses(literal_vector const & roots) const { clause_vector * vs[2] = { &m_solver.m_clauses, &m_solver.m_learned }; for (unsigned i = 0; i < 2; i++) { clause_vector & cs = *(vs[i]); clause_vector::iterator it = cs.begin(); clause_vector::iterator end = cs.end(); for (; it != end; ++it) { clause & c = *(*it); unsigned sz = c.size(); for (unsigned i = 0; i < sz; i++) { CTRACE("elim_eqs_bug", m_solver.was_eliminated(c[i].var()), tout << "lit: " << c[i] << " " << norm(roots, c[i]) << "\n"; tout << c << "\n";); SASSERT(!m_solver.was_eliminated(c[i].var())); } } } return true; } void elim_eqs::operator()(literal_vector const & roots, bool_var_vector const & to_elim) { 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)); } };