/*++ Copyright (c) 2017 Microsoft Corporation Module Name: sat_elim_vars.cpp Abstract: Helper class for eliminating variables Author: Nikolaj Bjorner (nbjorner) 2017-10-14 Revision History: --*/ #include "sat/sat_simplifier.h" #include "sat/sat_elim_vars.h" #include "sat/sat_solver.h" namespace sat{ elim_vars::elim_vars(simplifier& s) : simp(s), s(s.s), m(20,10000) { m_mark_lim = 0; m_max_literals = 11; // p.resolution_occ_cutoff(); } bool elim_vars::operator()(bool_var v) { if (s.value(v) != l_undef) return false; literal pos_l(v, false); literal neg_l(v, true); unsigned num_bin_pos = simp.get_num_non_learned_bin(pos_l); if (num_bin_pos > m_max_literals) return false; unsigned num_bin_neg = simp.get_num_non_learned_bin(neg_l); if (num_bin_neg > m_max_literals) return false; clause_use_list & pos_occs = simp.m_use_list.get(pos_l); clause_use_list & neg_occs = simp.m_use_list.get(neg_l); unsigned clause_size = num_bin_pos + num_bin_neg + pos_occs.size() + neg_occs.size(); reset_mark(); mark_var(v); if (!mark_literals(pos_occs)) return false; if (!mark_literals(neg_occs)) return false; if (!mark_literals(pos_l)) return false; if (!mark_literals(neg_l)) return false; // associate index with each variable. unsigned index = 0; for (bool_var w : m_vars) { m_var2index[w] = index++; } bdd b1 = make_clauses(pos_l); bdd b2 = make_clauses(neg_l); bdd b3 = make_clauses(pos_occs); bdd b4 = make_clauses(neg_occs); bdd b0 = b1 && b2 && b3 && b4; bdd b = m.mk_exists(m_var2index[v], b0); TRACE("elim_vars", tout << "eliminate " << v << "\n"; tout << "clauses : " << clause_size << "\n"; for (watched const& w : simp.get_wlist(~pos_l)) { if (w.is_binary_non_learned_clause()) { tout << pos_l << " " << w.get_literal() << "\n"; } } m.display(tout, b1); for (watched const& w : simp.get_wlist(~neg_l)) { if (w.is_binary_non_learned_clause()) { tout << neg_l << " " << w.get_literal() << "\n"; } } m.display(tout, b2); clause_use_list::iterator itp = pos_occs.mk_iterator(); while (!itp.at_end()) { clause const& c = itp.curr(); tout << c << "\n"; itp.next(); } m.display(tout, b3); clause_use_list::iterator itn = neg_occs.mk_iterator(); while (!itn.at_end()) { clause const& c = itn.curr(); tout << c << "\n"; itn.next(); } m.display(tout, b4); tout << "eliminated:\n"; tout << b << "\n"; tout << m.cnf_size(b) << "\n"; ); std::cout << "num clauses: " << clause_size << " cnf size: " << m.cnf_size(b) << "\n"; if (clause_size < m.cnf_size(b)) return false; literal_vector lits; TRACE("elim_vars", clause_vector clauses; literal_vector units; get_clauses(b0, lits, clauses, units); for (clause* c : clauses) tout << *c << "\n"; for (literal l : units) tout << l << "\n"; for (clause* c : clauses) s.m_cls_allocator.del_clause(c); SASSERT(lits.empty()); clauses.reset(); units.reset(); tout << "after elim:\n"; get_clauses(b, lits, clauses, units); for (clause* c : clauses) tout << *c << "\n"; for (literal l : units) tout << l << "\n"; for (clause* c : clauses) s.m_cls_allocator.del_clause(c); SASSERT(lits.empty()); clauses.reset(); ); return false; // eliminate variable simp.m_pos_cls.reset(); simp.m_neg_cls.reset(); simp.collect_clauses(pos_l, simp.m_pos_cls); simp.collect_clauses(neg_l, simp.m_neg_cls); model_converter::entry & mc_entry = s.m_mc.mk(model_converter::ELIM_VAR, v); simp.save_clauses(mc_entry, simp.m_pos_cls); simp.save_clauses(mc_entry, simp.m_neg_cls); s.m_eliminated[v] = true; simp.remove_bin_clauses(pos_l); simp.remove_bin_clauses(neg_l); simp.remove_clauses(pos_occs, pos_l); simp.remove_clauses(neg_occs, neg_l); pos_occs.reset(); neg_occs.reset(); add_clauses(b, lits); return true; } void elim_vars::add_clauses(bdd const& b, literal_vector& lits) { if (b.is_true()) { // no-op } else if (b.is_false()) { SASSERT(lits.size() > 0); std::cout << lits << "\n"; literal_vector c(lits); if (simp.cleanup_clause(c)) return; std::cout << c << "\n"; switch (c.size()) { case 0: UNREACHABLE(); break; case 1: simp.propagate_unit(c[0]); break; case 2: simp.add_non_learned_binary_clause(c[0],c[1]); break; default: { clause* cp = s.m_cls_allocator.mk_clause(c.size(), c.c_ptr(), false); s.m_clauses.push_back(cp); simp.m_use_list.insert(*cp); break; } } } else { unsigned v = m_vars[b.var()]; lits.push_back(literal(v, false)); add_clauses(b.lo(), lits); lits.pop_back(); lits.push_back(literal(v, true)); add_clauses(b.hi(), lits); lits.pop_back(); } } void elim_vars::get_clauses(bdd const& b, literal_vector & lits, clause_vector& clauses, literal_vector& units) { if (b.is_true()) { return; } if (b.is_false()) { if (lits.size() > 1) { clause* c = s.m_cls_allocator.mk_clause(lits.size(), lits.c_ptr(), false); clauses.push_back(c); } else { units.push_back(lits.back()); } return; } // if (v hi lo) // (v | lo) & (!v | hi) // if (v T lo) -> (v | lo) unsigned v = m_vars[b.var()]; lits.push_back(literal(v, false)); get_clauses(b.lo(), lits, clauses, units); lits.pop_back(); lits.push_back(literal(v, true)); get_clauses(b.hi(), lits, clauses, units); lits.pop_back(); } void elim_vars::reset_mark() { m_vars.reset(); m_mark.resize(s.num_vars()); m_var2index.resize(s.num_vars()); ++m_mark_lim; if (m_mark_lim == 0) { ++m_mark_lim; m_mark.fill(0); } } void elim_vars::mark_var(bool_var v) { if (m_mark[v] != m_mark_lim) { m_mark[v] = m_mark_lim; m_vars.push_back(v); } } bool elim_vars::mark_literals(clause_use_list & occs) { clause_use_list::iterator it = occs.mk_iterator(); while (!it.at_end()) { clause const& c = it.curr(); for (literal l : c) { mark_var(l.var()); } if (num_vars() > m_max_literals) return false; it.next(); } return true; } bool elim_vars::mark_literals(literal lit) { watch_list& wl = simp.get_wlist(lit); for (watched const& w : wl) { if (w.is_binary_non_learned_clause()) { mark_var(w.get_literal().var()); } } return num_vars() <= m_max_literals; } bdd elim_vars::make_clauses(clause_use_list & occs) { bdd result = m.mk_true(); clause_use_list::iterator it = occs.mk_iterator(); while (!it.at_end()) { clause const& c = it.curr(); bdd cl = m.mk_false(); for (literal l : c) { cl |= mk_literal(l); } it.next(); result &= cl; } return result; } bdd elim_vars::make_clauses(literal lit) { bdd result = m.mk_true(); watch_list& wl = simp.get_wlist(~lit); for (watched const& w : wl) { if (w.is_binary_non_learned_clause()) { result &= (mk_literal(lit) || mk_literal(w.get_literal())); } } return result; } bdd elim_vars::mk_literal(literal l) { return l.sign() ? m.mk_nvar(m_var2index[l.var()]) : m.mk_var(m_var2index[l.var()]); } };