/*++ Copyright (c) 2020 Microsoft Corporation Module Name: sat_aig_finder.cpp Author: Nikolaj Bjorner 2020-01-02 Notes: AIG finder --*/ #include "sat/sat_aig_finder.h" #include "sat/sat_solver.h" namespace sat { aig_finder::aig_finder(solver& s): s(s), m_big(s.rand()) {} void aig_finder::operator()(clause_vector& clauses) { m_big.init(s, true); find_aigs(clauses); find_ifs(clauses); } bool aig_finder::implies(literal a, literal b) { if (m_big.connected(a, b)) return true; for (auto const& w : s.get_wlist(a)) { if (w.is_binary_clause() && b == w.get_literal()) return true; } return false; } void aig_finder::find_aigs(clause_vector& clauses) { if (!m_on_aig) { return; } unsigned j = 0; for (clause* cp : clauses) { if (!find_aig(*cp)) { clauses[j++] = cp; } } clauses.shrink(j); } // a = ~b & ~c // if (~a | ~b) (~a | ~c), (b | c | a) bool aig_finder::find_aig(clause& c) { bool is_aig = false; if (c.size() <= 2) { return false; } for (literal head : c) { is_aig = true; for (literal tail : c) { if (head != tail && !implies(head, ~tail)) { is_aig = false; break; } } if (is_aig) { m_ands.reset(); for (literal tail : c) if (tail != head) m_ands.push_back(~tail); // DEBUG_CODE(validate_and(head, m_ands, c);); m_on_aig(head, m_ands); break; } } return is_aig; } // // x = if y then z else u // if x, y -> z // x, ~y -> u // y, z -> x // ~y, u -> x // // So there are clauses // y -> (x = z) // u -> (x = ~y) // // from clause x, y, z // then ~x, ~y -> z // look for ~y, z -> ~x - contains ternary(y, ~z, ~x) // look for ~x, y -> u - u is used in a ternary claues (~y, x) // look for y, u -> ~x - contains ternary(~u, ~x, ~y) // then ~x = if ~y then z else u void aig_finder::find_ifs(clause_vector& clauses) { if (!m_on_if) { return; } for (clause* cp : clauses) cp->unmark_used(); typedef svector> use_list_t; struct binary { literal x, y; use_list_t* use_list; binary(literal _x, literal _y, use_list_t* u): x(_x), y(_y), use_list(u) { if (x.index() > y.index()) std::swap(x, y); } binary():x(null_literal), y(null_literal), use_list(nullptr) {} struct hash { unsigned operator()(binary const& t) const { return mk_mix(t.x.index(), t.y.index(), 3); } }; struct eq { bool operator()(binary const& a, binary const& b) const { return a.x == b.x && a.y == b.y; } }; }; hashtable binaries; scoped_ptr_vector use_lists; auto insert_binary = [&](literal x, literal y, literal z, clause* c) { binary b(x, y, nullptr); auto* e = binaries.insert_if_not_there2(b); if (e->get_data().use_list == nullptr) { use_list_t* use_list = alloc(use_list_t); use_lists.push_back(use_list); e->get_data().use_list = use_list; } e->get_data().use_list->push_back(std::make_pair(z, c)); }; struct ternary { literal x, y, z; clause* orig; ternary(literal _x, literal _y, literal _z, clause* c): x(_x), y(_y), z(_z), orig(c) { if (x.index() > y.index()) std::swap(x, y); if (y.index() > z.index()) std::swap(y, z); if (x.index() > y.index()) std::swap(x, y); } ternary():x(null_literal), y(null_literal), z(null_literal), orig(nullptr) {} struct hash { unsigned operator()(ternary const& t) const { return mk_mix(t.x.hash(), t.y.hash(), t.z.hash()); } }; struct eq { bool operator()(ternary const& a, ternary const& b) const { return a.x == b.x && a.y == b.y && a.z == b.z; } }; }; hashtable ternaries; auto has_ternary = [&](literal x, literal y, literal z, clause*& c) { ternary t(x, y, z, nullptr); if (ternaries.find(t, t)) { c = t.orig; return true; } if (implies(~y, z) || implies(~x, y) || implies(~x, z)) { c = nullptr; return true; } return false; }; auto insert_ternary = [&](clause& c) { if (c.size() == 3) { ternaries.insert(ternary(c[0], c[1], c[2], &c)); insert_binary(c[0], c[1], c[2], &c); insert_binary(c[0], c[2], c[1], &c); insert_binary(c[2], c[1], c[0], &c); } }; for (clause* cp : s.learned()) { insert_ternary(*cp); } for (clause* cp : s.clauses()) { insert_ternary(*cp); } auto try_ite = [&,this](literal x, literal y, literal z, clause& c) { literal u; clause* c1, *c2, *c3; if (!has_ternary(y, ~z, ~x, c1)) { return false; } binary b(~y, x, nullptr); if (!binaries.find(b, b)) { return false; } for (auto p : *b.use_list) { u = p.first; c2 = p.second; if (!has_ternary(~u, ~x, ~y, c3)) { continue; } c.mark_used(); if (c1) c1->mark_used(); if (c2) c2->mark_used(); if (c3) c3->mark_used(); // DEBUG_CODE(validate_if(~x, ~y, z, u, c, c1, c2, c3);); m_on_if(~x, ~y, z, u); return true; } return false; }; for (clause* cp : clauses) { clause& c = *cp; if (c.size() != 3 || c.was_used()) continue; literal x = c[0], y = c[1], z = c[2]; if (try_ite(x, z, y, c)) continue; if (try_ite(x, y, z, c)) continue; if (try_ite(y, x, z, c)) continue; if (try_ite(z, x, y, c)) continue; if (try_ite(z, y, x, c)) continue; if (try_ite(y, z, x, c)) continue; } std::function not_used = [](clause* cp) { return !cp->was_used(); }; clauses.filter_update(not_used); } void aig_finder::validate_clause(literal_vector const& clause, vector const& clauses) { solver vs(s.params(), s.rlimit()); for (unsigned i = 0; i < s.num_vars(); ++i) { vs.mk_var(); } svector bins; s.collect_bin_clauses(bins, true, false); for (auto b : bins) { vs.mk_clause(b.first, b.second); } for (auto const& cl : clauses) { vs.mk_clause(cl); } for (literal l : clause) { literal nl = ~l; vs.mk_clause(1, &nl); } lbool r = vs.check(); if (r != l_false) { vs.display(verbose_stream()); UNREACHABLE(); } } void aig_finder::validate_clause(literal x, literal y, literal z, vector const& clauses) { literal_vector clause; clause.push_back(x); clause.push_back(y); clause.push_back(z); validate_clause(clause, clauses); } void aig_finder::validate_and(literal head, literal_vector const& ands, clause const& c) { IF_VERBOSE(2, verbose_stream() << "validate and: " << head << " == " << ands << "\n"); vector clauses; clauses.push_back(literal_vector(c.size(), c.begin())); literal_vector clause; clause.push_back(head); for (literal l : ands) clause.push_back(~l); validate_clause(clause, clauses); for (literal l : ands) { clause.reset(); clause.push_back(~head); clause.push_back(l); validate_clause(clause, clauses); } } void aig_finder::validate_if(literal x, literal c, literal t, literal e, clause const& c0, clause const* c1, clause const* c2, clause const* c3) { IF_VERBOSE(2, verbose_stream() << "validate if: " << x << " == " << c << " ? " << t << " : " << e << "\n"); vector clauses; clauses.push_back(literal_vector(c0.size(), c0.begin())); if (c1) clauses.push_back(literal_vector(c1->size(), c1->begin())); if (c2) clauses.push_back(literal_vector(c2->size(), c2->begin())); if (c3) clauses.push_back(literal_vector(c3->size(), c3->begin())); literal_vector clause; validate_clause(~x, ~c, t, clauses); validate_clause(~x, c, e, clauses); validate_clause(~t, ~c, x, clauses); validate_clause(~e, c, x, clauses); } }