diff options
author | Mathias Preiner <mathias.preiner@gmail.com> | 2017-12-08 17:36:15 -0800 |
---|---|---|
committer | Aina Niemetz <aina.niemetz@gmail.com> | 2017-12-08 17:36:15 -0800 |
commit | 99fb7d9e0b963222574c01e0362d3720c62b825f (patch) | |
tree | d0792c0d93e44db226c5c619f1b611cbfb2b67bb /src | |
parent | 707571c8b4a572b9554f9068df796f1257cb587d (diff) |
Add CEGQI BV linearization of additions and equalities over additions. (#1417)
Adds support for linearizing additions w.r.t. to a variable.
For example,
a * x + b + c * d * -x = e + x
is rewritten to
x * (a - c * d - 1) = e - b.
This also adds an additional rewriting rule x * x = x --> x < 2.
Diffstat (limited to 'src')
-rw-r--r-- | src/options/quantifiers_options | 2 | ||||
-rw-r--r-- | src/theory/bv/theory_bv_utils.cpp | 15 | ||||
-rw-r--r-- | src/theory/bv/theory_bv_utils.h | 2 | ||||
-rw-r--r-- | src/theory/quantifiers/ceg_t_instantiator.cpp | 469 |
4 files changed, 476 insertions, 12 deletions
diff --git a/src/options/quantifiers_options b/src/options/quantifiers_options index ef20881db..c3091a131 100644 --- a/src/options/quantifiers_options +++ b/src/options/quantifiers_options @@ -349,6 +349,8 @@ option cbqiBvIneqMode --cbqi-bv-ineq=MODE CVC4::theory::quantifiers::CbqiBvIneqM choose mode for handling bit-vector inequalities with counterexample-guided instantiation option cbqiBvRmExtract --cbqi-bv-rm-extract bool :read-write :default true replaces extract terms with variables for counterexample-guided instantiation for bit-vectors +option cbqiBvLinearize --cbqi-bv-linear bool :read-write :default true + linearize adder chains for variables ### local theory extensions options diff --git a/src/theory/bv/theory_bv_utils.cpp b/src/theory/bv/theory_bv_utils.cpp index 089989e19..783d04492 100644 --- a/src/theory/bv/theory_bv_utils.cpp +++ b/src/theory/bv/theory_bv_utils.cpp @@ -26,6 +26,21 @@ namespace theory { namespace bv { namespace utils { +Node mkSum(std::vector<Node>& children, unsigned width) +{ + std::size_t nchildren = children.size(); + + if (nchildren == 0) + { + return mkZero(width); + } + else if (nchildren == 1) + { + return children[0]; + } + return NodeManager::currentNM()->mkNode(kind::BITVECTOR_PLUS, children); +} + Node mkInc(TNode t) { return NodeManager::currentNM()->mkNode( diff --git a/src/theory/bv/theory_bv_utils.h b/src/theory/bv/theory_bv_utils.h index fca0a3a47..ea2dd4fc8 100644 --- a/src/theory/bv/theory_bv_utils.h +++ b/src/theory/bv/theory_bv_utils.h @@ -156,9 +156,9 @@ inline Node mkExtract(TNode node, unsigned high, unsigned low) { inline Node mkBitOf(TNode node, unsigned index) { Node bitOfOp = NodeManager::currentNM()->mkConst<BitVectorBitOf>(BitVectorBitOf(index)); return NodeManager::currentNM()->mkNode(bitOfOp, node); - } +Node mkSum(std::vector<Node>& children, unsigned width); inline Node mkConcat(TNode node, unsigned repeat) { Assert (repeat); diff --git a/src/theory/quantifiers/ceg_t_instantiator.cpp b/src/theory/quantifiers/ceg_t_instantiator.cpp index 18086cbd5..55cc59c51 100644 --- a/src/theory/quantifiers/ceg_t_instantiator.cpp +++ b/src/theory/quantifiers/ceg_t_instantiator.cpp @@ -40,6 +40,9 @@ namespace CVC4 { namespace theory { namespace quantifiers { +struct BvLinearAttributeId {}; +using BvLinearAttribute = expr::Attribute<BvLinearAttributeId, bool>; + Node ArithInstantiator::getModelBasedProjectionValue( CegInstantiator * ci, Node e, Node t, bool isLower, Node c, Node me, Node mt, Node theta, Node inf_coeff, Node delta_coeff ) { Node val = t; Trace("cegqi-arith-bound2") << "Value : " << val << std::endl; @@ -1317,6 +1320,8 @@ Node BvInstantiator::rewriteAssertionForSolvePv(CegInstantiator* ci, children.push_back(it->second); contains_pv = contains_pv || visited_contains_pv[cur[i]]; } + // careful that rewrites above do not affect whether this term contains pv + visited_contains_pv[cur] = contains_pv; // rewrite the term ret = rewriteTermForSolvePv(pv, cur, children, visited_contains_pv); @@ -1329,8 +1334,19 @@ Node BvInstantiator::rewriteAssertionForSolvePv(CegInstantiator* ci, ret = cur; } } - // careful that rewrites above do not affect whether this term contains pv - visited_contains_pv[cur] = contains_pv; + + /* We need to update contains_pv also for rewritten nodes, since + * the normalizePv* functions rely on the information if pv occurs in a + * rewritten node or not. */ + if (ret != cur) + { + contains_pv = (ret == pv); + for (unsigned i = 0, size = ret.getNumChildren(); i < size; ++i) + { + contains_pv = contains_pv || visited_contains_pv[ret[i]]; + } + visited_contains_pv[ret] = contains_pv; + } // if was choice, pop context if (cur.getKind() == CHOICE) @@ -1345,19 +1361,391 @@ Node BvInstantiator::rewriteAssertionForSolvePv(CegInstantiator* ci, visited.top()[cur] = ret; } - else if (Trace.isOn("cegqi-bv-nl")) - { - if (cur == pv) - { - Trace("cegqi-bv-nl") << "NONLINEAR LITERAL for " << pv << " : " << lit - << std::endl; - } - } } while (!visit.top().empty()); Assert(visited.size() == 1); Assert(visited.top().find(lit) != visited.top().end()); Assert(!visited.top().find(lit)->second.isNull()); - return visited.top()[lit]; + + Node result = visited.top()[lit]; + + if (Trace.isOn("cegqi-bv-nl")) + { + std::vector<TNode> trace_visit; + std::unordered_set<TNode, TNodeHashFunction> trace_visited; + + trace_visit.push_back(result); + do + { + cur = trace_visit.back(); + trace_visit.pop_back(); + + if (trace_visited.find(cur) == trace_visited.end()) + { + trace_visited.insert(cur); + trace_visit.insert(trace_visit.end(), cur.begin(), cur.end()); + } + else if (cur == pv) + { + Trace("cegqi-bv-nl") + << "NONLINEAR LITERAL for " << pv << " : " << lit << std::endl; + } + } while (!trace_visit.empty()); + } + + return result; +} + +/** + * Determine coefficient of pv in term n, where n has the form pv, -pv, pv * t, + * or -pv * t. Extracting the coefficient of multiplications only succeeds if + * the multiplication are normalized with normalizePvMult. + * + * If sucessful it returns + * 1 if n == pv, + * -1 if n == -pv, + * t if n == pv * t, + * -t if n == -pv * t. + * If n is not a linear term, a null node is returned. + */ +static Node getPvCoeff(TNode pv, TNode n) +{ + bool neg = false; + Node coeff; + + if (n.getKind() == BITVECTOR_NEG) + { + neg = true; + n = n[0]; + } + + if (n == pv) + { + coeff = bv::utils::mkOne(bv::utils::getSize(pv)); + } + /* All multiplications are normalized to pv * (t1 * t2). */ + else if (n.getKind() == BITVECTOR_MULT && n.getAttribute(BvLinearAttribute())) + { + Assert(n.getNumChildren() == 2); + Assert(n[0] == pv); + coeff = n[1]; + } + else /* n is in no form to extract the coefficient for pv */ + { + Trace("cegqi-bv-nl") << "Cannot extract coefficient for " << pv << " in " + << n << std::endl; + return Node::null(); + } + Assert(!coeff.isNull()); + + if (neg) return NodeManager::currentNM()->mkNode(BITVECTOR_NEG, coeff); + return coeff; +} + +/** + * Normalizes the children of a BITVECTOR_MULT w.r.t. pv. contains_pv marks + * terms in which pv occurs. + * For example, + * + * a * -pv * b * c + * + * is rewritten to + * + * pv * -(a * b * c) + * + * Returns the normalized node if the resulting term is linear w.r.t. pv and + * a null node otherwise. + */ +static Node normalizePvMult( + TNode pv, + const std::vector<Node>& children, + std::unordered_map<TNode, bool, TNodeHashFunction>& contains_pv) +{ + bool neg, neg_coeff = false; + bool found_pv = false; + NodeManager* nm; + NodeBuilder<> nb(BITVECTOR_MULT); + BvLinearAttribute is_linear; + + nm = NodeManager::currentNM(); + for (TNode nc : children) + { + if (!contains_pv[nc]) + { + nb << nc; + continue; + } + + neg = false; + if (nc.getKind() == BITVECTOR_NEG) + { + neg = true; + nc = nc[0]; + } + + if (!found_pv && nc == pv) + { + found_pv = true; + neg_coeff = neg; + continue; + } + else if (!found_pv && nc.getKind() == BITVECTOR_MULT + && nc.getAttribute(is_linear)) + { + Assert(nc.getNumChildren() == 2); + Assert(nc[0] == pv); + Assert(!contains_pv[nc[1]]); + found_pv = true; + neg_coeff = neg; + nb << nc[1]; + continue; + } + return Node::null(); /* non-linear */ + } + Assert(nb.getNumChildren() > 0); + + Node coeff = (nb.getNumChildren() == 1) ? nb[0] : nb.constructNode(); + if (neg_coeff) + { + coeff = nm->mkNode(BITVECTOR_NEG, coeff); + } + coeff = Rewriter::rewrite(coeff); + unsigned size_coeff = bv::utils::getSize(coeff); + Node zero = bv::utils::mkZero(size_coeff); + if (coeff == zero) + { + return zero; + } + else if (coeff == bv::utils::mkOne(size_coeff)) + { + return pv; + } + Node result = nm->mkNode(BITVECTOR_MULT, pv, coeff); + contains_pv[result] = true; + result.setAttribute(is_linear, true); + return result; +} + +#ifdef CVC4_ASSERTIONS +static bool isLinearPlus( + TNode n, + TNode pv, + std::unordered_map<TNode, bool, TNodeHashFunction>& contains_pv) +{ + Node coeff; + Assert(n.getAttribute(BvLinearAttribute())); + Assert(n.getNumChildren() == 2); + if (n[0] != pv) + { + Assert(n[0].getKind() == BITVECTOR_MULT); + Assert(n[0].getNumChildren() == 2); + Assert(n[0][0] == pv); + Assert(!contains_pv[n[0][1]]); + } + Assert(!contains_pv[n[1]]); + coeff = getPvCoeff(pv, n[0]); + Assert(!coeff.isNull()); + Assert(!contains_pv[coeff]); + return true; +} +#endif + +/** + * Normalizes the children of a BITVECTOR_PLUS w.r.t. pv. contains_pv marks + * terms in which pv occurs. + * For example, + * + * a * pv + b + c * -pv + * + * is rewritten to + * + * pv * (a - c) + b + * + * Returns the normalized node if the resulting term is linear w.r.t. pv and + * a null node otherwise. + */ +static Node normalizePvPlus( + Node pv, + const std::vector<Node>& children, + std::unordered_map<TNode, bool, TNodeHashFunction>& contains_pv) +{ + NodeManager* nm; + NodeBuilder<> nb_c(BITVECTOR_PLUS); + NodeBuilder<> nb_l(BITVECTOR_PLUS); + BvLinearAttribute is_linear; + bool neg; + + nm = NodeManager::currentNM(); + for (TNode nc : children) + { + if (!contains_pv[nc]) + { + nb_l << nc; + continue; + } + + neg = false; + if (nc.getKind() == BITVECTOR_NEG) + { + neg = true; + nc = nc[0]; + } + + if (nc == pv + || (nc.getKind() == BITVECTOR_MULT && nc.getAttribute(is_linear))) + { + Node coeff = getPvCoeff(pv, nc); + Assert(!coeff.isNull()); + if (neg) + { + coeff = nm->mkNode(BITVECTOR_NEG, coeff); + } + nb_c << coeff; + continue; + } + else if (nc.getKind() == BITVECTOR_PLUS && nc.getAttribute(is_linear)) + { + Assert(isLinearPlus(nc, pv, contains_pv)); + Node coeff = getPvCoeff(pv, nc[0]); + Node leaf = nc[1]; + if (neg) + { + coeff = nm->mkNode(BITVECTOR_NEG, coeff); + leaf = nm->mkNode(BITVECTOR_NEG, leaf); + } + nb_c << coeff; + nb_l << leaf; + continue; + } + /* can't collect coefficients of 'pv' in 'cur' -> non-linear */ + return Node::null(); + } + Assert(nb_c.getNumChildren() > 0); + + Node coeffs = (nb_c.getNumChildren() == 1) ? nb_c[0] : nb_c.constructNode(); + coeffs = Rewriter::rewrite(coeffs); + + std::vector<Node> mult_children = {pv, coeffs}; + Node pv_mult_coeffs = normalizePvMult(pv, mult_children, contains_pv); + + if (nb_l.getNumChildren() > 0) + { + Node leafs = (nb_l.getNumChildren() == 1) ? nb_l[0] : nb_l.constructNode(); + leafs = Rewriter::rewrite(leafs); + Node zero = bv::utils::mkZero(bv::utils::getSize(pv)); + Node result; + /* pv * 0 + t --> t */ + if (pv_mult_coeffs == zero) + { + result = leafs; + } + else + { + result = nm->mkNode(BITVECTOR_PLUS, pv_mult_coeffs, leafs); + contains_pv[result] = true; + result.setAttribute(is_linear, true); + } + return result; + } + return pv_mult_coeffs; +} + +/** + * Linearize an equality w.r.t. pv such that pv only occurs once. contains_pv + * marks terms in which pv occurs. + * For example, equality + * + * -pv * a + b = c + pv + * + * rewrites to + * + * pv * (-a - 1) = c - b. + */ +static Node normalizePvEqual( + Node pv, + const std::vector<Node>& children, + std::unordered_map<TNode, bool, TNodeHashFunction>& contains_pv) +{ + Assert(children.size() == 2); + + NodeManager* nm = NodeManager::currentNM(); + BvLinearAttribute is_linear; + Node coeffs[2], leafs[2]; + bool neg; + TNode child; + + for (unsigned i = 0; i < 2; ++i) + { + child = children[i]; + neg = false; + if (child.getKind() == BITVECTOR_NEG) + { + neg = true; + child = child[0]; + } + if (child.getAttribute(is_linear) || child == pv) + { + if (child.getKind() == BITVECTOR_PLUS) + { + Assert(isLinearPlus(child, pv, contains_pv)); + coeffs[i] = getPvCoeff(pv, child[0]); + leafs[i] = child[1]; + } + else + { + Assert(child.getKind() == BITVECTOR_MULT || child == pv); + coeffs[i] = getPvCoeff(pv, child); + } + } + if (neg) + { + if (!coeffs[i].isNull()) + { + coeffs[i] = nm->mkNode(BITVECTOR_NEG, coeffs[i]); + } + if (!leafs[i].isNull()) + { + leafs[i] = nm->mkNode(BITVECTOR_NEG, leafs[i]); + } + } + } + + if (coeffs[0].isNull() || coeffs[1].isNull()) + { + return Node::null(); + } + + Node coeff = nm->mkNode(BITVECTOR_SUB, coeffs[0], coeffs[1]); + coeff = Rewriter::rewrite(coeff); + std::vector<Node> mult_children = {pv, coeff}; + Node lhs = normalizePvMult(pv, mult_children, contains_pv); + + Node rhs; + if (!leafs[0].isNull() && !leafs[1].isNull()) + { + rhs = nm->mkNode(BITVECTOR_SUB, leafs[1], leafs[0]); + } + else if (!leafs[0].isNull()) + { + rhs = nm->mkNode(BITVECTOR_NEG, leafs[0]); + } + else if (!leafs[1].isNull()) + { + rhs = leafs[1]; + } + else + { + rhs = bv::utils::mkZero(bv::utils::getSize(pv)); + } + rhs = Rewriter::rewrite(rhs); + + if (lhs == rhs) + { + return bv::utils::mkTrue(); + } + + Node result = lhs.eqNode(rhs); + contains_pv[result] = true; + return result; } Node BvInstantiator::rewriteTermForSolvePv( @@ -1384,6 +1772,65 @@ Node BvInstantiator::rewriteTermForSolvePv( children[1])); } } + else if (n.getKind() == EQUAL) + { + TNode lhs = children[0]; + TNode rhs = children[1]; + + /* rewrite: x * x = x -> x < 2 */ + if ((lhs == pv && rhs.getKind() == BITVECTOR_MULT && rhs[0] == pv + && rhs[1] == pv) + || (rhs == pv && lhs.getKind() == BITVECTOR_MULT && lhs[0] == pv + && lhs[1] == pv)) + { + return nm->mkNode( + BITVECTOR_ULT, + pv, + bv::utils::mkConst(BitVector(bv::utils::getSize(pv), Integer(2)))); + } + + if (options::cbqiBvLinearize() && contains_pv[lhs] && contains_pv[rhs]) + { + Node result = normalizePvEqual(pv, children, contains_pv); + if (!result.isNull()) + { + Trace("cegqi-bv-nl") + << "Normalize " << n << " to " << result << std::endl; + } + else + { + Trace("cegqi-bv-nl") + << "Nonlinear " << n.getKind() << " " << n << std::endl; + } + return result; + } + } + else if (n.getKind() == BITVECTOR_MULT || n.getKind() == BITVECTOR_PLUS) + { + if (options::cbqiBvLinearize() && contains_pv[n]) + { + Node result; + if (n.getKind() == BITVECTOR_MULT) + { + result = normalizePvMult(pv, children, contains_pv); + } + else + { + result = normalizePvPlus(pv, children, contains_pv); + } + if (!result.isNull()) + { + Trace("cegqi-bv-nl") + << "Normalize " << n << " to " << result << std::endl; + return result; + } + else + { + Trace("cegqi-bv-nl") + << "Nonlinear " << n.getKind() << " " << n << std::endl; + } + } + } // [2] try to rewrite non-linear literals -> linear literals |