summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMathias Preiner <mathias.preiner@gmail.com>2017-12-08 17:36:15 -0800
committerAina Niemetz <aina.niemetz@gmail.com>2017-12-08 17:36:15 -0800
commit99fb7d9e0b963222574c01e0362d3720c62b825f (patch)
treed0792c0d93e44db226c5c619f1b611cbfb2b67bb /src
parent707571c8b4a572b9554f9068df796f1257cb587d (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_options2
-rw-r--r--src/theory/bv/theory_bv_utils.cpp15
-rw-r--r--src/theory/bv/theory_bv_utils.h2
-rw-r--r--src/theory/quantifiers/ceg_t_instantiator.cpp469
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
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback