/*++ Copyright (c) 2020 Microsoft Corporation Module Name: sat_lut_finder.cpp Abstract: lut finder Author: Nikolaj Bjorner 2020-01-02 Notes: --*/ #include "sat/sat_lut_finder.h" #include "sat/sat_solver.h" namespace sat { void lut_finder::operator()(clause_vector& clauses) { m_removed_clauses.reset(); unsigned max_size = m_max_lut_size; // we better have enough bits in the combination mask to // handle clauses up to max_size. // max_size = 5 -> 32 bits // max_size = 6 -> 64 bits SASSERT(sizeof(m_combination)*8 >= (1ull << static_cast(max_size))); init_clause_filter(); for (unsigned i = 0; i <= 6; ++i) { m_masks[i] = cut::effect_mask(i); } m_var_position.resize(s.num_vars()); for (clause* cp : clauses) { cp->unmark_used(); } for (; max_size > 2; --max_size) { for (clause* cp : clauses) { clause& c = *cp; if (c.size() == max_size && !c.was_removed() && !c.is_learned() && !c.was_used()) { check_lut(c); } } } m_clause_filters.clear(); for (clause* cp : clauses) cp->unmark_used(); for (clause* cp : m_removed_clauses) cp->mark_used(); std::function not_used = [](clause* cp) { return !cp->was_used(); }; clauses.filter_update(not_used); } void lut_finder::check_lut(clause& c) { SASSERT(c.size() > 2); unsigned filter = get_clause_filter(c); s.init_visited(); unsigned mask = 0, i = 0; m_vars.reset(); m_clause.reset(); for (literal l : c) { m_clause.push_back(l); } // ensure that variables in returned LUT are sorted std::sort(m_clause.begin(), m_clause.end()); for (literal l : m_clause) { m_vars.push_back(l.var()); m_var_position[l.var()] = i; s.mark_visited(l.var()); mask |= (l.sign() << (i++)); } m_clauses_to_remove.reset(); m_clauses_to_remove.push_back(&c); m_combination = 0; m_num_combinations = 0; set_combination(mask); c.mark_used(); for (literal l : c) { for (auto const& cf : m_clause_filters[l.var()]) { if ((filter == (filter | cf.m_filter)) && !cf.m_clause->was_used() && extract_lut(*cf.m_clause)) { add_lut(); return; } } // TBD: replace by BIG // loop over binary clauses in watch list for (watched const & w : s.get_wlist(l)) { if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_lut(~l, w.get_literal())) { add_lut(); return; } } } l.neg(); for (watched const & w : s.get_wlist(l)) { if (w.is_binary_clause() && s.is_visited(w.get_literal().var()) && w.get_literal().index() < l.index()) { if (extract_lut(~l, w.get_literal())) { add_lut(); return; } } } } } void lut_finder::add_lut() { DEBUG_CODE(for (clause* cp : m_clauses_to_remove) VERIFY(cp->was_used());); m_removed_clauses.append(m_clauses_to_remove); bool_var v; uint64_t lut = convert_combination(m_vars, v); TRACE("aig_simplifier", for (clause* cp : m_clauses_to_remove) { tout << *cp << "\n" << v << ": " << m_vars << "\n"; } display_mask(tout, lut, 1u << m_vars.size()) << "\n";); m_on_lut(lut, m_vars, v); } bool lut_finder::extract_lut(literal l1, literal l2) { SASSERT(s.is_visited(l1.var())); SASSERT(s.is_visited(l2.var())); m_missing.reset(); unsigned mask = 0; for (unsigned i = 0; i < m_vars.size(); ++i) { if (m_vars[i] == l1.var()) { mask |= (l1.sign() << i); } else if (m_vars[i] == l2.var()) { mask |= (l2.sign() << i); } else { m_missing.push_back(i); } } return update_combinations(mask); } bool lut_finder::extract_lut(clause& c2) { for (literal l : c2) { if (!s.is_visited(l.var())) return false; } if (c2.size() == m_vars.size()) { m_clauses_to_remove.push_back(&c2); c2.mark_used(); } // insert missing unsigned mask = 0; m_missing.reset(); SASSERT(c2.size() <= m_vars.size()); for (unsigned i = 0; i < m_vars.size(); ++i) { m_clause[i] = null_literal; } for (literal l : c2) { unsigned pos = m_var_position[l.var()]; m_clause[pos] = l; } for (unsigned j = 0; j < m_vars.size(); ++j) { literal lit = m_clause[j]; if (lit == null_literal) { m_missing.push_back(j); } else { mask |= (m_clause[j].sign() << j); } } return update_combinations(mask); } void lut_finder::set_combination(unsigned mask) { if (!get_combination(mask)) { m_combination |= (1ull << mask); m_num_combinations++; } } bool lut_finder::update_combinations(unsigned mask) { unsigned num_missing = m_missing.size(); for (unsigned k = 0; k < (1ul << num_missing); ++k) { unsigned mask2 = mask; for (unsigned i = 0; i < num_missing; ++i) { if ((k & (1 << i)) != 0) { mask2 |= 1ul << m_missing[i]; } } set_combination(mask2); } return lut_is_defined(m_vars.size()); } bool lut_finder::lut_is_defined(unsigned sz) { if (m_num_combinations < (1ull << (sz/2))) return false; for (unsigned i = sz; i-- > 0; ) { if (lut_is_defined(i, sz)) return true; } return false; } /** * \brief check if all output combinations for variable i are defined. */ bool lut_finder::lut_is_defined(unsigned i, unsigned sz) { uint64_t c = m_combination | (m_combination >> (1ull << (uint64_t)i)); uint64_t m = m_masks[i]; if (sz < 6) m &= ((1ull << (1ull << sz)) - 1); return (c & m) == m; } /** * find variable where it is defined * convert bit-mask to truth table for that variable. * remove variable from vars, * return truth table. */ uint64_t lut_finder::convert_combination(bool_var_vector& vars, bool_var& v) { SASSERT(lut_is_defined(vars.size())); unsigned i = 0; for (i = vars.size(); i-- > 0; ) { if (lut_is_defined(i, vars.size())) { break; } } SASSERT(i < vars.size()); v = vars[i]; vars.erase(v); uint64_t r = 0; uint64_t m = m_masks[i]; unsigned offset = 0; // example, if i = 2, then we are examining // how m_combination evaluates at position xy0uv // If it evaluates to 0, then it has to evaluate to 1 on position xy1uv // Offset keeps track of the value of xyuv // for (unsigned j = 0; j < 64; ++j) { if (0 != (m & (1ull << j))) { if (0 != (m_combination & (1ull << j))) { r |= 1ull << offset; } ++offset; } } return r; } void lut_finder::init_clause_filter() { m_clause_filters.reset(); m_clause_filters.resize(s.num_vars()); init_clause_filter(s.m_clauses); init_clause_filter(s.m_learned); } void lut_finder::init_clause_filter(clause_vector& clauses) { for (clause* cp : clauses) { clause& c = *cp; if (c.size() <= m_max_lut_size && s.all_distinct(c)) { clause_filter cf(get_clause_filter(c), cp); for (literal l : c) { m_clause_filters[l.var()].push_back(cf); } } } } unsigned lut_finder::get_clause_filter(clause const& c) { unsigned filter = 0; for (literal l : c) { filter |= 1 << ((l.var() % 32)); } return filter; } std::ostream& lut_finder::display_mask(std::ostream& out, uint64_t mask, unsigned sz) const { for (unsigned i = 0; i < sz; ++i) { out << ((0 != (((mask >> i)) & 0x1)) ? "1" : "0"); } return out; } }