summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndrew Reynolds <andrew.j.reynolds@gmail.com>2018-09-10 08:39:50 -0500
committerGitHub <noreply@github.com>2018-09-10 08:39:50 -0500
commit4edb4a1841343dc91c9be0ae7458955cd5587cce (patch)
tree32376cb35a481c2701003c1ed2b501d8d3996ee9
parent5a86cca9ceaca1a7eb52a209dbab8d5f54cdd8ff (diff)
parent564f61f602b407e0598be762923853042a0e4aab (diff)
Merge branch 'master' into replreplRewreplreplRew
-rw-r--r--src/expr/type_node.cpp56
-rw-r--r--src/expr/type_node.h13
-rw-r--r--src/lib/clock_gettime.c44
-rw-r--r--src/lib/clock_gettime.h4
-rw-r--r--src/options/options_handler.cpp1
-rw-r--r--src/theory/quantifiers/cegqi/ceg_instantiator.cpp3
-rw-r--r--src/theory/quantifiers/conjecture_generator.cpp2
-rw-r--r--src/theory/quantifiers/first_order_model.cpp2
-rw-r--r--src/theory/quantifiers/instantiate.cpp2
-rw-r--r--src/theory/quantifiers/quantifiers_rewriter.cpp727
-rw-r--r--src/theory/quantifiers/quantifiers_rewriter.h100
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_conjecture.cpp3
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_conjecture.h2
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_instantiation.cpp64
-rw-r--r--src/theory/quantifiers/sygus/ce_guided_instantiation.h5
-rw-r--r--src/theory/quantifiers/term_enumeration.cpp88
-rw-r--r--src/theory/quantifiers/term_enumeration.h24
-rw-r--r--src/theory/rep_set.cpp10
-rw-r--r--src/theory/rep_set.h8
-rw-r--r--src/util/dense_map.h5
-rw-r--r--test/regress/Makefile.tests1
-rw-r--r--test/regress/regress0/quantifiers/cond-var-elim-binary.smt216
22 files changed, 717 insertions, 463 deletions
diff --git a/src/expr/type_node.cpp b/src/expr/type_node.cpp
index 6616f470f..b54290612 100644
--- a/src/expr/type_node.cpp
+++ b/src/expr/type_node.cpp
@@ -165,6 +165,62 @@ bool TypeNode::isInterpretedFinite()
return getAttribute(IsInterpretedFiniteAttr());
}
+/** Attribute true for types that are closed enumerable */
+struct IsClosedEnumerableTag
+{
+};
+struct IsClosedEnumerableComputedTag
+{
+};
+typedef expr::Attribute<IsClosedEnumerableTag, bool> IsClosedEnumerableAttr;
+typedef expr::Attribute<IsClosedEnumerableComputedTag, bool>
+ IsClosedEnumerableComputedAttr;
+
+bool TypeNode::isClosedEnumerable()
+{
+ // check it is already cached
+ if (!getAttribute(IsClosedEnumerableComputedAttr()))
+ {
+ bool ret = true;
+ if (isArray() || isSort() || isCodatatype() || isFunction())
+ {
+ ret = false;
+ }
+ else if (isSet())
+ {
+ ret = getSetElementType().isClosedEnumerable();
+ }
+ else if (isDatatype())
+ {
+ // avoid infinite loops: initially set to true
+ setAttribute(IsClosedEnumerableAttr(), ret);
+ setAttribute(IsClosedEnumerableComputedAttr(), true);
+ TypeNode tn = *this;
+ const Datatype& dt = getDatatype();
+ for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++)
+ {
+ for (unsigned j = 0, nargs = dt[i].getNumArgs(); j < nargs; j++)
+ {
+ TypeNode ctn = TypeNode::fromType(dt[i][j].getRangeType());
+ if (tn != ctn && !ctn.isClosedEnumerable())
+ {
+ ret = false;
+ break;
+ }
+ }
+ if (!ret)
+ {
+ break;
+ }
+ }
+ }
+ setAttribute(IsClosedEnumerableAttr(), ret);
+ setAttribute(IsClosedEnumerableComputedAttr(), true);
+ return ret;
+ }
+ return getAttribute(IsClosedEnumerableAttr());
+}
+
bool TypeNode::isFirstClass() const {
return ( getKind() != kind::FUNCTION_TYPE || options::ufHo() ) &&
getKind() != kind::CONSTRUCTOR_TYPE &&
diff --git a/src/expr/type_node.h b/src/expr/type_node.h
index 9cb6efc29..5b0caf659 100644
--- a/src/expr/type_node.h
+++ b/src/expr/type_node.h
@@ -429,6 +429,19 @@ public:
*/
bool isInterpretedFinite();
+ /** is closed enumerable type
+ *
+ * This returns true if this type has an enumerator that produces constants
+ * that are fully handled by CVC4's quantifier-free theory solvers. Examples
+ * of types that are not closed enumerable are:
+ * (1) uninterpreted sorts,
+ * (2) arrays,
+ * (3) codatatypes,
+ * (4) functions,
+ * (5) parametric sorts involving any of the above.
+ */
+ bool isClosedEnumerable();
+
/**
* Is this a first-class type?
* First-class types are types for which:
diff --git a/src/lib/clock_gettime.c b/src/lib/clock_gettime.c
index b440b8d28..71b2bf569 100644
--- a/src/lib/clock_gettime.c
+++ b/src/lib/clock_gettime.c
@@ -9,11 +9,9 @@
** All rights reserved. See the file COPYING in the top-level source
** directory for licensing information.\endverbatim
**
- ** \brief Replacement for clock_gettime() for systems without it (like
- ** Mac OS X)
+ ** \brief Replacement for clock_gettime() for systems without it (Windows)
**
- ** Replacement for clock_gettime() for systems without it (like Mac
- ** OS X).
+ ** Replacement for clock_gettime() for systems without it (Windows).
**/
// #warning "TODO(taking): Make lib/clock_gettime.h cvc4_private.h again."
@@ -26,43 +24,6 @@
extern "C" {
#endif /* __cplusplus */
-#ifdef __APPLE__
-
-#include <stdio.h>
-#include <errno.h>
-#include <mach/mach_time.h>
-
-static double s_clockconv = 0.0;
-
-long clock_gettime(clockid_t which_clock, struct timespec* tp) {
- if( s_clockconv == 0.0 ) {
- mach_timebase_info_data_t tb;
- kern_return_t err = mach_timebase_info(&tb);
- if(err == 0) {
- s_clockconv = ((double) tb.numer) / tb.denom;
- } else {
- return -EINVAL;
- }
- }
-
- switch(which_clock) {
- case CLOCK_REALTIME:
- case CLOCK_REALTIME_HR:
- case CLOCK_MONOTONIC:
- case CLOCK_MONOTONIC_HR: {
- uint64_t t = mach_absolute_time() * s_clockconv;
- tp->tv_sec = t / 1000000000ul;
- tp->tv_nsec = t % 1000000000ul;
- }
- break;
- default:
- return -EINVAL;
- }
-
- return 0;
-}
-
-#else /* not defined __APPLE__ */
#ifdef __WIN32__
#include <time.h>
@@ -80,7 +41,6 @@ long clock_gettime(clockid_t which_clock, struct timespec* tp) {
}
#endif /* closing #ifdef __WIN32__ */
-#endif /* closing #else for #ifdef __APPLE__ / __WIN32__ */
#ifdef __cplusplus
}/* extern "C" */
diff --git a/src/lib/clock_gettime.h b/src/lib/clock_gettime.h
index 2ad9442dd..db83da853 100644
--- a/src/lib/clock_gettime.h
+++ b/src/lib/clock_gettime.h
@@ -9,9 +9,9 @@
** All rights reserved. See the file COPYING in the top-level source
** directory for licensing information.\endverbatim
**
- ** \brief Replacement for clock_gettime() for systems without it (like Mac OS X)
+ ** \brief Replacement for clock_gettime() for systems without it (Windows)
**
- ** Replacement for clock_gettime() for systems without it (like Mac OS X).
+ ** Replacement for clock_gettime() for systems without it (Windows).
**/
#include "cvc4_private_library.h"
diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp
index 8abe8d541..22d944e4c 100644
--- a/src/options/options_handler.cpp
+++ b/src/options/options_handler.cpp
@@ -29,7 +29,6 @@
#include "base/modal_exception.h"
#include "base/output.h"
#include "lib/strtok_r.h"
-#include "gmp.h"
#include "options/arith_heuristic_pivot_rule.h"
#include "options/arith_propagation_mode.h"
#include "options/arith_unate_lemma_mode.h"
diff --git a/src/theory/quantifiers/cegqi/ceg_instantiator.cpp b/src/theory/quantifiers/cegqi/ceg_instantiator.cpp
index b90d98ca6..46649154e 100644
--- a/src/theory/quantifiers/cegqi/ceg_instantiator.cpp
+++ b/src/theory/quantifiers/cegqi/ceg_instantiator.cpp
@@ -29,7 +29,6 @@
#include "theory/quantifiers/quantifiers_attributes.h"
#include "theory/quantifiers/quantifiers_rewriter.h"
#include "theory/quantifiers/term_database.h"
-#include "theory/quantifiers/term_enumeration.h"
#include "theory/quantifiers/term_util.h"
#include "theory/theory_engine.h"
@@ -1645,7 +1644,7 @@ void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& lems, st
Instantiator::Instantiator( QuantifiersEngine * qe, TypeNode tn ) : d_type( tn ){
- d_closed_enum_type = qe->getTermEnumeration()->isClosedEnumerableType(tn);
+ d_closed_enum_type = tn.isClosedEnumerable();
}
bool Instantiator::processEqualTerm(CegInstantiator* ci,
diff --git a/src/theory/quantifiers/conjecture_generator.cpp b/src/theory/quantifiers/conjecture_generator.cpp
index 69f89021b..0dc704219 100644
--- a/src/theory/quantifiers/conjecture_generator.cpp
+++ b/src/theory/quantifiers/conjecture_generator.cpp
@@ -1088,7 +1088,7 @@ void ConjectureGenerator::getEnumerateUfTerm( Node n, unsigned num, std::vector<
for( unsigned i=0; i<n.getNumChildren(); i++ ){
vec.push_back( 0 );
TypeNode tn = n[i].getType();
- if (te->isClosedEnumerableType(tn))
+ if (tn.isClosedEnumerable())
{
types.push_back( tn );
}else{
diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp
index 1e3116f43..0eec40de2 100644
--- a/src/theory/quantifiers/first_order_model.cpp
+++ b/src/theory/quantifiers/first_order_model.cpp
@@ -317,7 +317,7 @@ Node FirstOrderModel::getModelBasisTerm(TypeNode tn)
if (d_model_basis_term.find(tn) == d_model_basis_term.end())
{
Node mbt;
- if (d_qe->getTermEnumeration()->isClosedEnumerableType(tn))
+ if (tn.isClosedEnumerable())
{
mbt = d_qe->getTermEnumeration()->getEnumerateTerm(tn, 0);
}
diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp
index eab05f454..24f418b0c 100644
--- a/src/theory/quantifiers/instantiate.cpp
+++ b/src/theory/quantifiers/instantiate.cpp
@@ -491,7 +491,7 @@ bool Instantiate::removeInstantiationInternal(Node q, std::vector<Node>& terms)
Node Instantiate::getTermForType(TypeNode tn)
{
- if (d_qe->getTermEnumeration()->isClosedEnumerableType(tn))
+ if (tn.isClosedEnumerable())
{
return d_qe->getTermEnumeration()->getEnumerateTerm(tn, 0);
}
diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp
index 1f6660f2f..1f94dd3df 100644
--- a/src/theory/quantifiers/quantifiers_rewriter.cpp
+++ b/src/theory/quantifiers/quantifiers_rewriter.cpp
@@ -735,36 +735,15 @@ Node QuantifiersRewriter::computeProcessTerms2( Node body, bool hasPol, bool pol
}
}
-
-bool QuantifiersRewriter::isConditionalVariableElim( Node n, int pol ){
- if( n.getKind()==NOT ){
- return isConditionalVariableElim( n[0], -pol );
- }else if( ( n.getKind()==AND && pol>=0 ) || ( n.getKind()==OR && pol<=0 ) ){
- pol = n.getKind()==AND ? 1 : -1;
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if( isConditionalVariableElim( n[i], pol ) ){
- return true;
- }
- }
- }else if( n.getKind()==EQUAL ){
- for (unsigned i = 0; i < 2; i++)
- {
- if (n[i].getKind() == BOUND_VARIABLE)
- {
- if (!expr::hasSubterm(n[1 - i], n[i]))
- {
- return true;
- }
- }
- }
- }else if( n.getKind()==BOUND_VARIABLE ){
- return true;
- }
- return false;
-}
-
-Node QuantifiersRewriter::computeCondSplit( Node body, QAttributes& qa ){
- if( options::iteDtTesterSplitQuant() && body.getKind()==ITE ){
+Node QuantifiersRewriter::computeCondSplit(Node body,
+ const std::vector<Node>& args,
+ QAttributes& qa)
+{
+ NodeManager* nm = NodeManager::currentNM();
+ Kind bk = body.getKind();
+ if (options::iteDtTesterSplitQuant() && bk == ITE
+ && body[0].getKind() == APPLY_TESTER)
+ {
Trace("quantifiers-rewrite-ite-debug") << "DTT split : " << body << std::endl;
std::map< Node, Node > pcons;
std::map< Node, std::map< int, Node > > ncons;
@@ -776,119 +755,131 @@ Node QuantifiersRewriter::computeCondSplit( Node body, QAttributes& qa ){
for( unsigned i=0; i<conj.size(); i++ ){
Trace("quantifiers-rewrite-ite") << " " << conj[i] << std::endl;
}
- return NodeManager::currentNM()->mkNode( AND, conj );
+ return nm->mkNode(AND, conj);
}
}
- if( options::condVarSplitQuant() ){
- if( body.getKind()==ITE || ( body.getKind()==EQUAL && body[0].getType().isBoolean() && options::condVarSplitQuantAgg() ) ){
- Assert( !qa.isFunDef() );
- Trace("quantifiers-rewrite-debug") << "Conditional var elim split " << body << "?" << std::endl;
- bool do_split = false;
- unsigned index_max = body.getKind()==ITE ? 0 : 1;
- for( unsigned index=0; index<=index_max; index++ ){
- if( isConditionalVariableElim( body[index] ) ){
- do_split = true;
- break;
- }
+ if (!options::condVarSplitQuant())
+ {
+ return body;
+ }
+ Trace("cond-var-split-debug")
+ << "Conditional var elim split " << body << "?" << std::endl;
+
+ if (bk == ITE
+ || (bk == EQUAL && body[0].getType().isBoolean()
+ && options::condVarSplitQuantAgg()))
+ {
+ Assert(!qa.isFunDef());
+ bool do_split = false;
+ unsigned index_max = bk == ITE ? 0 : 1;
+ std::vector<Node> tmpArgs = args;
+ for (unsigned index = 0; index <= index_max; index++)
+ {
+ if (hasVarElim(body[index], true, tmpArgs)
+ || hasVarElim(body[index], false, tmpArgs))
+ {
+ do_split = true;
+ break;
}
- if( do_split ){
- Node pos;
- Node neg;
- if( body.getKind()==ITE ){
- pos = NodeManager::currentNM()->mkNode( OR, body[0].negate(), body[1] );
- neg = NodeManager::currentNM()->mkNode( OR, body[0], body[2] );
- }else{
- pos = NodeManager::currentNM()->mkNode( OR, body[0].negate(), body[1] );
- neg = NodeManager::currentNM()->mkNode( OR, body[0], body[1].negate() );
- }
- Trace("quantifiers-rewrite-debug") << "*** Split (conditional variable eq) " << body << " into : " << std::endl;
- Trace("quantifiers-rewrite-debug") << " " << pos << std::endl;
- Trace("quantifiers-rewrite-debug") << " " << neg << std::endl;
- return NodeManager::currentNM()->mkNode( AND, pos, neg );
- }
- }else if( body.getKind()==OR && options::condVarSplitQuantAgg() ){
- std::vector< Node > bchildren;
- std::vector< Node > nchildren;
- for( unsigned i=0; i<body.getNumChildren(); i++ ){
- bool split = false;
- if( nchildren.empty() ){
- if( body[i].getKind()==AND ){
- std::vector< Node > children;
- for( unsigned j=0; j<body[i].getNumChildren(); j++ ){
- if( ( body[i][j].getKind()==NOT && body[i][j][0].getKind()==EQUAL && isConditionalVariableElim( body[i][j][0] ) ) ||
- body[i][j].getKind()==BOUND_VARIABLE ){
- nchildren.push_back( body[i][j] );
- }else{
- children.push_back( body[i][j] );
- }
+ }
+ if (do_split)
+ {
+ Node pos;
+ Node neg;
+ if (bk == ITE)
+ {
+ pos = nm->mkNode(OR, body[0].negate(), body[1]);
+ neg = nm->mkNode(OR, body[0], body[2]);
+ }
+ else
+ {
+ pos = nm->mkNode(OR, body[0].negate(), body[1]);
+ neg = nm->mkNode(OR, body[0], body[1].negate());
+ }
+ Trace("cond-var-split-debug") << "*** Split (conditional variable eq) "
+ << body << " into : " << std::endl;
+ Trace("cond-var-split-debug") << " " << pos << std::endl;
+ Trace("cond-var-split-debug") << " " << neg << std::endl;
+ return nm->mkNode(AND, pos, neg);
+ }
+ }
+
+ if (bk == OR)
+ {
+ unsigned size = body.getNumChildren();
+ bool do_split = false;
+ unsigned split_index = 0;
+ for (unsigned i = 0; i < size; i++)
+ {
+ // check if this child is a (conditional) variable elimination
+ Node b = body[i];
+ if (b.getKind() == AND)
+ {
+ std::vector<Node> vars;
+ std::vector<Node> subs;
+ std::vector<Node> tmpArgs = args;
+ for (unsigned j = 0, bsize = b.getNumChildren(); j < bsize; j++)
+ {
+ if (getVarElimLit(b[j], false, tmpArgs, vars, subs))
+ {
+ Trace("cond-var-split-debug") << "Variable elimination in child #"
+ << j << " under " << i << std::endl;
+ // Figure out if we should split
+ // Currently we split if the aggressive option is set, or
+ // if the top-level OR is binary.
+ if (options::condVarSplitQuantAgg() || size == 2)
+ {
+ do_split = true;
}
- if( !nchildren.empty() ){
- if( !children.empty() ){
- nchildren.push_back( children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( AND, children ) );
- }
- split = true;
+ // other splitting criteria go here
+
+ if (do_split)
+ {
+ split_index = i;
+ break;
}
+ vars.clear();
+ subs.clear();
+ tmpArgs = args;
}
}
- if( !split ){
- bchildren.push_back( body[i] );
- }
}
- if( !nchildren.empty() ){
- Trace("quantifiers-rewrite-debug") << "*** Split (conditional variable eq) " << body << " into : " << std::endl;
- for( unsigned i=0; i<nchildren.size(); i++ ){
- bchildren.push_back( nchildren[i] );
- nchildren[i] = NodeManager::currentNM()->mkNode( OR, bchildren );
- Trace("quantifiers-rewrite-debug") << " " << nchildren[i] << std::endl;
- bchildren.pop_back();
- }
- return NodeManager::currentNM()->mkNode( AND, nchildren );
+ if (do_split)
+ {
+ break;
+ }
+ }
+ if (do_split)
+ {
+ std::vector<Node> children;
+ for (TNode bc : body)
+ {
+ children.push_back(bc);
}
+ std::vector<Node> split_children;
+ for (TNode bci : body[split_index])
+ {
+ children[split_index] = bci;
+ split_children.push_back(nm->mkNode(OR, children));
+ }
+ // split the AND child, for example:
+ // ( x!=a ^ P(x) ) V Q(x) ---> ( x!=a V Q(x) ) ^ ( P(x) V Q(x) )
+ return nm->mkNode(AND, split_children);
}
}
+
return body;
}
-bool QuantifiersRewriter::isVariableElim(Node v, Node s)
+bool QuantifiersRewriter::isVarElim(Node v, Node s)
{
+ Assert(v.getKind() == BOUND_VARIABLE);
return !expr::hasSubterm(s, v) && s.getType().isSubtypeOf(v.getType());
}
-void QuantifiersRewriter::isVariableBoundElig( Node n, std::map< Node, int >& exclude, std::map< Node, std::map< int, bool > >& visited, bool hasPol, bool pol,
- std::map< Node, bool >& elig_vars ) {
- int vindex = hasPol ? ( pol ? 1 : -1 ) : 0;
- if( visited[n].find( vindex )==visited[n].end() ){
- visited[n][vindex] = true;
- if( elig_vars.find( n )!=elig_vars.end() ){
- //variable contained in a place apart from bounds, no longer eligible for elimination
- elig_vars.erase( n );
- Trace("var-elim-ineq-debug") << "...found occurrence of " << n << ", mark ineligible" << std::endl;
- }else{
- if( hasPol ){
- std::map< Node, int >::iterator itx = exclude.find( n );
- if( itx!=exclude.end() && itx->second==vindex ){
- //already processed this literal
- return;
- }
- }
- for( unsigned j=0; j<n.getNumChildren(); j++ ){
- bool newHasPol;
- bool newPol;
- QuantPhaseReq::getPolarity( n, j, hasPol, pol, newHasPol, newPol );
- isVariableBoundElig( n[j], exclude, visited, newHasPol, newPol, elig_vars );
- if( elig_vars.empty() ){
- break;
- }
- }
- }
- }else{
- //already visited
- }
-}
-
-Node QuantifiersRewriter::computeVariableElimLitBv(Node lit,
- std::vector<Node>& args,
- Node& var)
+Node QuantifiersRewriter::getVarElimLitBv(Node lit,
+ std::vector<Node>& args,
+ Node& var)
{
if (Trace.isOn("quant-velim-bv"))
{
@@ -922,7 +913,7 @@ Node QuantifiersRewriter::computeVariableElimLitBv(Node lit,
// if this is a proper variable elimination, that is, var = slv where
// var is not in the free variables of slv, then we can return this
// as the variable elimination for lit.
- if (isVariableElim(var, slv))
+ if (isVarElim(var, slv))
{
return slv;
}
@@ -937,17 +928,58 @@ Node QuantifiersRewriter::computeVariableElimLitBv(Node lit,
return Node::null();
}
-bool QuantifiersRewriter::computeVariableElimLit(
- Node lit,
- bool pol,
- std::vector<Node>& args,
- std::vector<Node>& vars,
- std::vector<Node>& subs,
- std::map<Node, std::map<bool, std::map<Node, bool> > >& num_bounds)
+bool QuantifiersRewriter::getVarElimLit(Node lit,
+ bool pol,
+ std::vector<Node>& args,
+ std::vector<Node>& vars,
+ std::vector<Node>& subs)
{
+ if (lit.getKind() == NOT)
+ {
+ return getVarElimLit(lit[0], !pol, args, vars, subs);
+ }
+ NodeManager* nm = NodeManager::currentNM();
Trace("var-elim-quant-debug")
<< "Eliminate : " << lit << ", pol = " << pol << "?" << std::endl;
- if (lit.getKind() == EQUAL && options::varElimQuant())
+ if (lit.getKind() == APPLY_TESTER && pol && lit[0].getKind() == BOUND_VARIABLE
+ && options::dtVarExpandQuant())
+ {
+ Trace("var-elim-dt") << "Expand datatype variable based on : " << lit
+ << std::endl;
+ std::vector<Node>::iterator ita =
+ std::find(args.begin(), args.end(), lit[0]);
+ if (ita != args.end())
+ {
+ vars.push_back(lit[0]);
+ Expr testerExpr = lit.getOperator().toExpr();
+ int index = Datatype::indexOf(testerExpr);
+ const Datatype& dt = Datatype::datatypeOf(testerExpr);
+ const DatatypeConstructor& c = dt[index];
+ std::vector<Node> newChildren;
+ newChildren.push_back(Node::fromExpr(c.getConstructor()));
+ std::vector<Node> newVars;
+ for (unsigned j = 0, nargs = c.getNumArgs(); j < nargs; j++)
+ {
+ TypeNode tn = TypeNode::fromType(c[j].getRangeType());
+ Node v = nm->mkBoundVar(tn);
+ newChildren.push_back(v);
+ newVars.push_back(v);
+ }
+ subs.push_back(nm->mkNode(APPLY_CONSTRUCTOR, newChildren));
+ Trace("var-elim-dt") << "...apply substitution " << subs[0] << "/"
+ << vars[0] << std::endl;
+ args.erase(ita);
+ args.insert(args.end(), newVars.begin(), newVars.end());
+ return true;
+ }
+ }
+ // all eliminations below guarded by varElimQuant()
+ if (!options::varElimQuant())
+ {
+ return false;
+ }
+
+ if (lit.getKind() == EQUAL)
{
if (pol || lit[0].getType().isBoolean())
{
@@ -964,7 +996,7 @@ bool QuantifiersRewriter::computeVariableElimLit(
std::find(args.begin(), args.end(), v_slv);
if (ita != args.end())
{
- if (isVariableElim(v_slv, lit[1 - i]))
+ if (isVarElim(v_slv, lit[1 - i]))
{
Node slv = lit[1 - i];
if (!tpol)
@@ -983,31 +1015,9 @@ bool QuantifiersRewriter::computeVariableElimLit(
}
}
}
- }else if( lit.getKind()==APPLY_TESTER && pol && lit[0].getKind()==BOUND_VARIABLE && options::dtVarExpandQuant() ){
- Trace("var-elim-dt") << "Expand datatype variable based on : " << lit << std::endl;
- std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), lit[0] );
- if( ita!=args.end() ){
- vars.push_back( lit[0] );
- Expr testerExpr = lit.getOperator().toExpr();
- int index = Datatype::indexOf( testerExpr );
- const Datatype& dt = Datatype::datatypeOf(testerExpr);
- const DatatypeConstructor& c = dt[index];
- std::vector< Node > newChildren;
- newChildren.push_back( Node::fromExpr( c.getConstructor() ) );
- std::vector< Node > newVars;
- for( unsigned j=0; j<c.getNumArgs(); j++ ){
- TypeNode tn = TypeNode::fromType( c[j].getRangeType() );
- Node v = NodeManager::currentNM()->mkBoundVar( tn );
- newChildren.push_back( v );
- newVars.push_back( v );
- }
- subs.push_back( NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, newChildren ) );
- Trace("var-elim-dt") << "...apply substitution " << subs[0] << "/" << vars[0] << std::endl;
- args.erase( ita );
- args.insert( args.end(), newVars.begin(), newVars.end() );
- return true;
- }
- }else if( lit.getKind()==BOUND_VARIABLE && options::varElimQuant() ){
+ }
+ if (lit.getKind() == BOUND_VARIABLE)
+ {
std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), lit );
if( ita!=args.end() ){
Trace("var-elim-bool") << "Variable eliminate : " << lit << std::endl;
@@ -1017,9 +1027,9 @@ bool QuantifiersRewriter::computeVariableElimLit(
return true;
}
}
- if( ( lit.getKind()==EQUAL && lit[0].getType().isReal() && pol && options::varElimQuant() ) ||
- ( ( lit.getKind()==GEQ || lit.getKind()==GT ) && options::varIneqElimQuant() ) ){
- //for arithmetic, solve the (in)equality
+ if (lit.getKind() == EQUAL && lit[0].getType().isReal() && pol)
+ {
+ // for arithmetic, solve the equality
std::map< Node, Node > msum;
if (ArithMSum::getMonomialSumLit(lit, msum))
{
@@ -1027,58 +1037,29 @@ bool QuantifiersRewriter::computeVariableElimLit(
if( !itm->first.isNull() ){
std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), itm->first );
if( ita!=args.end() ){
- if( lit.getKind()==EQUAL ){
- Assert( pol );
- Node veq_c;
- Node val;
- int ires =
- ArithMSum::isolate(itm->first, msum, veq_c, val, EQUAL);
- if( ires!=0 && veq_c.isNull() && isVariableElim( itm->first, val ) ){
- Trace("var-elim-quant") << "Variable eliminate based on solved equality : " << itm->first << " -> " << val << std::endl;
- vars.push_back( itm->first );
- subs.push_back( val );
- args.erase( ita );
- return true;
- }
- }else{
- Assert( lit.getKind()==GEQ || lit.getKind()==GT );
- //store that this literal is upper/lower bound for itm->first
- Node veq_c;
- Node val;
- int ires = ArithMSum::isolate(
- itm->first, msum, veq_c, val, lit.getKind());
- if( ires!=0 && veq_c.isNull() ){
- bool is_upper = pol!=( ires==1 );
- Trace("var-elim-ineq-debug") << lit << " is a " << ( is_upper ? "upper" : "lower" ) << " bound for " << itm->first << std::endl;
- Trace("var-elim-ineq-debug") << " pol/ires = " << pol << " " << ires << std::endl;
- num_bounds[itm->first][is_upper][lit] = pol;
- }else{
- Trace("var-elim-ineq-debug") << "...ineligible " << itm->first << " since it cannot be solved for (" << ires << ", " << veq_c << ")." << std::endl;
- num_bounds[itm->first][true].clear();
- num_bounds[itm->first][false].clear();
- }
- }
- }else{
- if( lit.getKind()==GEQ || lit.getKind()==GT ){
- //compute variables in itm->first, these are not eligible for elimination
- std::vector< Node > bvs;
- TermUtil::getBoundVars( itm->first, bvs );
- for( unsigned j=0; j<bvs.size(); j++ ){
- Trace("var-elim-ineq-debug") << "...ineligible " << bvs[j] << " since it is contained in monomial." << std::endl;
- num_bounds[bvs[j]][true].clear();
- num_bounds[bvs[j]][false].clear();
- }
+ Assert(pol);
+ Node veq_c;
+ Node val;
+ int ires = ArithMSum::isolate(itm->first, msum, veq_c, val, EQUAL);
+ if (ires != 0 && veq_c.isNull() && isVarElim(itm->first, val))
+ {
+ Trace("var-elim-quant")
+ << "Variable eliminate based on solved equality : "
+ << itm->first << " -> " << val << std::endl;
+ vars.push_back(itm->first);
+ subs.push_back(val);
+ args.erase(ita);
+ return true;
}
}
}
}
}
}
- else if (lit.getKind() == EQUAL && lit[0].getType().isBitVector() && pol
- && options::varElimQuant())
+ if (lit.getKind() == EQUAL && lit[0].getType().isBitVector() && pol)
{
Node var;
- Node slv = computeVariableElimLitBv(lit, args, var);
+ Node slv = getVarElimLitBv(lit, args, var);
if (!slv.isNull())
{
Assert(!var.isNull());
@@ -1096,92 +1077,282 @@ bool QuantifiersRewriter::computeVariableElimLit(
return true;
}
}
-
return false;
}
-Node QuantifiersRewriter::computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa ){
- Trace("var-elim-quant-debug") << "Compute var elimination for " << body << std::endl;
- std::map< Node, std::map< bool, std::map< Node, bool > > > num_bounds;
- QuantPhaseReq qpr( body );
+bool QuantifiersRewriter::getVarElim(Node n,
+ bool pol,
+ std::vector<Node>& args,
+ std::vector<Node>& vars,
+ std::vector<Node>& subs)
+{
+ Kind nk = n.getKind();
+ if (nk == NOT)
+ {
+ return getVarElim(n[0], !pol, args, vars, subs);
+ }
+ else if ((nk == AND && pol) || (nk == OR && !pol))
+ {
+ for (const Node& cn : n)
+ {
+ if (getVarElim(cn, pol, args, vars, subs))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+ return getVarElimLit(n, pol, args, vars, subs);
+}
+
+bool QuantifiersRewriter::hasVarElim(Node n, bool pol, std::vector<Node>& args)
+{
std::vector< Node > vars;
std::vector< Node > subs;
- for( std::map< Node, bool >::iterator it = qpr.d_phase_reqs.begin(); it != qpr.d_phase_reqs.end(); ++it ){
- Trace("var-elim-quant-debug") << " phase req : " << it->first << " -> " << ( it->second ? "true" : "false" ) << std::endl;
- if( computeVariableElimLit( it->first, it->second, args, vars, subs, num_bounds ) ){
- break;
+ return getVarElim(n, pol, args, vars, subs);
+}
+
+bool QuantifiersRewriter::getVarElimIneq(Node body,
+ std::vector<Node>& args,
+ std::vector<Node>& bounds,
+ std::vector<Node>& subs,
+ QAttributes& qa)
+{
+ // elimination based on inequalities
+ std::map<Node, std::map<bool, std::map<Node, bool> > > num_bounds;
+ QuantPhaseReq qpr(body);
+ for (const std::pair<const Node, bool>& pr : qpr.d_phase_reqs)
+ {
+ // an inequality that is entailed with a given polarity
+ Node lit = pr.first;
+ if (lit.getKind() != GEQ)
+ {
+ continue;
+ }
+ bool pol = pr.second;
+ Trace("var-elim-quant-debug") << "Process inequality bounds : " << lit
+ << ", pol = " << pol << "..." << std::endl;
+ // solve the inequality
+ std::map<Node, Node> msum;
+ if (!ArithMSum::getMonomialSumLit(lit, msum))
+ {
+ // not an inequality, cannot use
+ continue;
+ }
+ for (const std::pair<const Node, Node>& m : msum)
+ {
+ if (!m.first.isNull())
+ {
+ std::vector<Node>::iterator ita =
+ std::find(args.begin(), args.end(), m.first);
+ if (ita != args.end())
+ {
+ // store that this literal is upper/lower bound for itm->first
+ Node veq_c;
+ Node val;
+ int ires =
+ ArithMSum::isolate(m.first, msum, veq_c, val, lit.getKind());
+ if (ires != 0 && veq_c.isNull())
+ {
+ bool is_upper = pol != (ires == 1);
+ Trace("var-elim-ineq-debug")
+ << lit << " is a " << (is_upper ? "upper" : "lower")
+ << " bound for " << m.first << std::endl;
+ Trace("var-elim-ineq-debug")
+ << " pol/ires = " << pol << " " << ires << std::endl;
+ num_bounds[m.first][is_upper][lit] = pol;
+ }
+ else
+ {
+ Trace("var-elim-ineq-debug")
+ << "...ineligible " << m.first
+ << " since it cannot be solved for (" << ires << ", " << veq_c
+ << ")." << std::endl;
+ num_bounds[m.first][true].clear();
+ num_bounds[m.first][false].clear();
+ }
+ }
+ else
+ {
+ // compute variables in itm->first, these are not eligible for
+ // elimination
+ std::vector<Node> bvs;
+ TermUtil::getBoundVars(m.first, bvs);
+ for (TNode v : bvs)
+ {
+ Trace("var-elim-ineq-debug")
+ << "...ineligible " << v
+ << " since it is contained in monomial." << std::endl;
+ num_bounds[v][true].clear();
+ num_bounds[v][false].clear();
+ }
+ }
+ }
}
}
-
- if( !vars.empty() ){
- Trace("var-elim-quant-debug") << "VE " << vars.size() << "/" << args.size() << std::endl;
- //remake with eliminated nodes
- body = body.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
- body = Rewriter::rewrite( body );
- if( !qa.d_ipl.isNull() ){
- qa.d_ipl = qa.d_ipl.substitute( vars.begin(), vars.end(), subs.begin(), subs.end() );
+
+ // collect all variables that have only upper/lower bounds
+ std::map<Node, bool> elig_vars;
+ for (const std::pair<const Node, std::map<bool, std::map<Node, bool> > >& nb :
+ num_bounds)
+ {
+ if (nb.second.find(true) == nb.second.end())
+ {
+ Trace("var-elim-ineq-debug")
+ << "Variable " << nb.first << " has only lower bounds." << std::endl;
+ elig_vars[nb.first] = false;
}
- Trace("var-elim-quant") << "Return " << body << std::endl;
- }else{
- //collect all variables that have only upper/lower bounds
- std::map< Node, bool > elig_vars;
- for( std::map< Node, std::map< bool, std::map< Node, bool > > >::iterator it = num_bounds.begin(); it != num_bounds.end(); ++it ){
- if( it->second.find( true )==it->second.end() ){
- Trace("var-elim-ineq-debug") << "Variable " << it->first << " has only lower bounds." << std::endl;
- elig_vars[it->first] = false;
- }else if( it->second.find( false )==it->second.end() ){
- Trace("var-elim-ineq-debug") << "Variable " << it->first << " has only upper bounds." << std::endl;
- elig_vars[it->first] = true;
- }
- }
- if( !elig_vars.empty() ){
- std::vector< Node > inactive_vars;
- std::map< Node, std::map< int, bool > > visited;
- std::map< Node, int > exclude;
- for( std::map< Node, bool >::iterator it = qpr.d_phase_reqs.begin(); it != qpr.d_phase_reqs.end(); ++it ){
- if( it->first.getKind()==GEQ || it->first.getKind()==GT ){
- exclude[ it->first ] = it->second ? -1 : 1;
- Trace("var-elim-ineq-debug") << "...exclude " << it->first << " at polarity " << exclude[ it->first ] << std::endl;
- }
+ else if (nb.second.find(false) == nb.second.end())
+ {
+ Trace("var-elim-ineq-debug")
+ << "Variable " << nb.first << " has only upper bounds." << std::endl;
+ elig_vars[nb.first] = true;
+ }
+ }
+ if (elig_vars.empty())
+ {
+ return false;
+ }
+ std::vector<Node> inactive_vars;
+ std::map<Node, std::map<int, bool> > visited;
+ std::map<Node, int> exclude;
+ for (const std::pair<Node, bool>& pr : qpr.d_phase_reqs)
+ {
+ if (pr.first.getKind() == GEQ)
+ {
+ exclude[pr.first] = pr.second ? -1 : 1;
+ Trace("var-elim-ineq-debug")
+ << "...exclude " << pr.first << " at polarity " << exclude[pr.first]
+ << std::endl;
+ }
+ }
+ // traverse the body, invalidate variables if they occur in places other than
+ // the bounds they occur in
+ std::unordered_map<TNode, std::unordered_set<int>, TNodeHashFunction>
+ evisited;
+ std::vector<TNode> evisit;
+ std::vector<int> evisit_pol;
+ TNode ecur;
+ int ecur_pol;
+ evisit.push_back(body);
+ evisit_pol.push_back(1);
+ if (!qa.d_ipl.isNull())
+ {
+ // do not eliminate variables that occur in the annotation
+ evisit.push_back(qa.d_ipl);
+ evisit_pol.push_back(0);
+ }
+ do
+ {
+ ecur = evisit.back();
+ evisit.pop_back();
+ ecur_pol = evisit_pol.back();
+ evisit_pol.pop_back();
+ std::unordered_set<int>& epp = evisited[ecur];
+ if (epp.find(ecur_pol) == epp.end())
+ {
+ epp.insert(ecur_pol);
+ if (elig_vars.find(ecur) != elig_vars.end())
+ {
+ // variable contained in a place apart from bounds, no longer eligible
+ // for elimination
+ elig_vars.erase(ecur);
+ Trace("var-elim-ineq-debug") << "...found occurrence of " << ecur
+ << ", mark ineligible" << std::endl;
}
- //traverse the body, invalidate variables if they occur in places other than the bounds they occur in
- isVariableBoundElig( body, exclude, visited, true, true, elig_vars );
-
- if( !elig_vars.empty() ){
- if( !qa.d_ipl.isNull() ){
- isVariableBoundElig( qa.d_ipl, exclude, visited, false, true, elig_vars );
+ else
+ {
+ bool rec = true;
+ bool pol = ecur_pol >= 0;
+ bool hasPol = ecur_pol != 0;
+ if (hasPol)
+ {
+ std::map<Node, int>::iterator itx = exclude.find(ecur);
+ if (itx != exclude.end() && itx->second == ecur_pol)
+ {
+ // already processed this literal as a bound
+ rec = false;
+ }
}
- for( std::map< Node, bool >::iterator itev = elig_vars.begin(); itev != elig_vars.end(); ++itev ){
- Node v = itev->first;
- Trace("var-elim-ineq-debug") << v << " is eligible for elimination." << std::endl;
- //do substitution corresponding to infinite projection, all literals involving unbounded variable go to true/false
- std::vector< Node > terms;
- std::vector< Node > subs;
- for( std::map< Node, bool >::iterator itb = num_bounds[v][elig_vars[v]].begin(); itb != num_bounds[v][elig_vars[v]].end(); ++itb ){
- Trace("var-elim-ineq-debug") << " subs : " << itb->first << " -> " << itb->second << std::endl;
- terms.push_back( itb->first );
- subs.push_back( NodeManager::currentNM()->mkConst( itb->second ) );
+ if (rec)
+ {
+ for (unsigned j = 0, size = ecur.getNumChildren(); j < size; j++)
+ {
+ bool newHasPol;
+ bool newPol;
+ QuantPhaseReq::getPolarity(ecur, j, hasPol, pol, newHasPol, newPol);
+ evisit.push_back(ecur[j]);
+ evisit_pol.push_back(newHasPol ? (newPol ? 1 : -1) : 0);
}
- body = body.substitute( terms.begin(), terms.end(), subs.begin(), subs.end() );
- Trace("var-elim-ineq-debug") << "Return " << body << std::endl;
- //eliminate from args
- std::vector< Node >::iterator ita = std::find( args.begin(), args.end(), v );
- Assert( ita!=args.end() );
- args.erase( ita );
}
- }
+ }
}
- }
- return body;
+ } while (!evisit.empty() && !elig_vars.empty());
+
+ bool ret = false;
+ for (const std::pair<Node, bool>& ev : elig_vars)
+ {
+ Node v = ev.first;
+ Trace("var-elim-ineq-debug")
+ << v << " is eligible for elimination." << std::endl;
+ // do substitution corresponding to infinite projection, all literals
+ // involving unbounded variable go to true/false
+ for (const std::pair<const Node, bool>& nb : num_bounds[v][elig_vars[v]])
+ {
+ Trace("var-elim-ineq-debug")
+ << " subs : " << nb.first << " -> " << nb.second << std::endl;
+ bounds.push_back(nb.first);
+ subs.push_back(NodeManager::currentNM()->mkConst(nb.second));
+ }
+ // eliminate from args
+ std::vector<Node>::iterator ita = std::find(args.begin(), args.end(), v);
+ Assert(ita != args.end());
+ args.erase(ita);
+ ret = true;
+ }
+ return ret;
}
Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& args, QAttributes& qa ){
- if( options::varElimQuant() || options::dtVarExpandQuant() ){
- Node prev;
- do{
- prev = body;
- body = computeVarElimination2( body, args, qa );
- }while( prev!=body && !args.empty() );
+ if (!options::varElimQuant() && !options::dtVarExpandQuant()
+ && !options::varIneqElimQuant())
+ {
+ return body;
+ }
+ Node prev;
+ while (prev != body && !args.empty())
+ {
+ prev = body;
+
+ std::vector<Node> vars;
+ std::vector<Node> subs;
+ // standard variable elimination
+ if (options::varElimQuant())
+ {
+ getVarElim(body, false, args, vars, subs);
+ }
+ // variable elimination based on one-direction inequalities
+ if (vars.empty() && options::varIneqElimQuant())
+ {
+ getVarElimIneq(body, args, vars, subs, qa);
+ }
+ // if we eliminated a variable, update body and reprocess
+ if (!vars.empty())
+ {
+ Trace("var-elim-quant-debug")
+ << "VE " << vars.size() << "/" << args.size() << std::endl;
+ Assert(vars.size() == subs.size());
+ // remake with eliminated nodes
+ body =
+ body.substitute(vars.begin(), vars.end(), subs.begin(), subs.end());
+ body = Rewriter::rewrite(body);
+ if (!qa.d_ipl.isNull())
+ {
+ qa.d_ipl = qa.d_ipl.substitute(
+ vars.begin(), vars.end(), subs.begin(), subs.end());
+ }
+ Trace("var-elim-quant") << "Return " << body << std::endl;
+ }
}
return body;
}
@@ -1725,7 +1896,7 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption, QAttribut
n = NodeManager::currentNM()->mkNode( OR, new_conds );
}
}else if( computeOption==COMPUTE_COND_SPLIT ){
- n = computeCondSplit( n, qa );
+ n = computeCondSplit(n, args, qa);
}else if( computeOption==COMPUTE_PRENEX ){
if( options::prenexQuant()==PRENEX_QUANT_DISJ_NORMAL || options::prenexQuant()==PRENEX_QUANT_NORMAL ){
//will rewrite at preprocess time
diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h
index 963e73701..a1d6d25c3 100644
--- a/src/theory/quantifiers/quantifiers_rewriter.h
+++ b/src/theory/quantifiers/quantifiers_rewriter.h
@@ -43,33 +43,103 @@ private:
std::map< Node, Node >& cache, std::map< Node, Node >& icache,
std::vector< Node >& new_vars, std::vector< Node >& new_conds, bool elimExtArith );
static void computeDtTesterIteSplit( Node n, std::map< Node, Node >& pcons, std::map< Node, std::map< int, Node > >& ncons, std::vector< Node >& conj );
- static bool isConditionalVariableElim( Node n, int pol=0 );
- static bool isVariableElim( Node v, Node s );
- static void isVariableBoundElig( Node n, std::map< Node, int >& exclude, std::map< Node, std::map< int, bool > >& visited, bool hasPol, bool pol,
- std::map< Node, bool >& elig_vars );
- static bool computeVariableElimLit( Node n, bool pol, std::vector< Node >& args, std::vector< Node >& var, std::vector< Node >& subs,
- std::map< Node, std::map< bool, std::map< Node, bool > > >& num_bounds );
- static Node computeVarElimination2( Node body, std::vector< Node >& args, QAttributes& qa );
+ //-------------------------------------variable elimination
+ /** is variable elimination
+ *
+ * Returns true if v is not a subterm of s, and the type of s is a subtype of
+ * the type of v.
+ */
+ static bool isVarElim(Node v, Node s);
+ /** get variable elimination literal
+ *
+ * If n asserted with polarity pol is equivalent to an equality of the form
+ * v = s for some v in args, where isVariableElim( v, s ) holds, then this
+ * method removes v from args, adds v to vars, adds s to subs, and returns
+ * true. Otherwise, it returns false.
+ */
+ static bool getVarElimLit(Node n,
+ bool pol,
+ std::vector<Node>& args,
+ std::vector<Node>& vars,
+ std::vector<Node>& subs);
/** variable eliminate for bit-vector literals
*
* If this returns a non-null value ret, then var is updated to a member of
- * args, and lit is equivalent to ( var = ret ).
+ * args, lit is equivalent to ( var = ret ), and var is removed from args.
*/
- static Node computeVariableElimLitBv(Node lit,
- std::vector<Node>& args,
- Node& var);
-
+ static Node getVarElimLitBv(Node lit, std::vector<Node>& args, Node& var);
+ /** get variable elimination
+ *
+ * If n asserted with polarity pol entails a literal lit that corresponds
+ * to a variable elimination for some v via the above method, we return true.
+ * In this case, we update args/vars/subs based on eliminating v.
+ */
+ static bool getVarElim(Node n,
+ bool pol,
+ std::vector<Node>& args,
+ std::vector<Node>& vars,
+ std::vector<Node>& subs);
+ /** has variable elimination
+ *
+ * Returns true if n asserted with polarity pol entails a literal for
+ * which variable elimination is possible.
+ */
+ static bool hasVarElim(Node n, bool pol, std::vector<Node>& args);
+ /** compute variable elimination inequality
+ *
+ * This method eliminates variables from the body of quantified formula
+ * "body" using (global) reasoning about inequalities. In particular, if there
+ * exists a variable x that only occurs in body or annotation qa in literals
+ * of the form x>=t with a fixed polarity P, then we may replace all such
+ * literals with P. For example, note that:
+ * forall xy. x>y OR P(y) is equivalent to forall y. P(y).
+ *
+ * In the case that a variable x from args can be eliminated in this way,
+ * we remove x from args, add x >= t1, ..., x >= tn to bounds, add false, ...,
+ * false to subs, and return true.
+ */
+ static bool getVarElimIneq(Node body,
+ std::vector<Node>& args,
+ std::vector<Node>& bounds,
+ std::vector<Node>& subs,
+ QAttributes& qa);
+ /** compute variable elimination
+ *
+ * This computes variable elimination rewrites for a body of a quantified
+ * formula with bound variables args. This method updates args to args' and
+ * returns a node body' such that (forall args. body) is equivalent to
+ * (forall args'. body'). An example of a variable elimination rewrite is:
+ * forall xy. x != a V P( x,y ) ---> forall y. P( a, y )
+ */
+ static Node computeVarElimination(Node body,
+ std::vector<Node>& args,
+ QAttributes& qa);
+ //-------------------------------------end variable elimination
+ //-------------------------------------conditional splitting
+ /** compute conditional splitting
+ *
+ * This computes conditional splitting rewrites for a body of a quantified
+ * formula with bound variables args. It returns a body' that is equivalent
+ * to body. We split body into a conjunction if variable elimination can
+ * occur in one of the conjuncts. Examples of this are:
+ * ite( x=a, P(x), Q(x) ) ----> ( x!=a V P(x) ) ^ ( x=a V Q(x) )
+ * (x=a) = P(x) ----> ( x!=a V P(x) ) ^ ( x=a V ~P(x) )
+ * ( x!=a ^ P(x) ) V Q(x) ---> ( x!=a V Q(x) ) ^ ( P(x) V Q(x) )
+ * where in each case, x can be eliminated in the first conjunct.
+ */
+ static Node computeCondSplit(Node body,
+ const std::vector<Node>& args,
+ QAttributes& qa);
+ //-------------------------------------end conditional splitting
public:
static Node computeElimSymbols( Node body );
static Node computeMiniscoping( std::vector< Node >& args, Node body, QAttributes& qa );
static Node computeAggressiveMiniscoping( std::vector< Node >& args, Node body );
//cache is dependent upon currCond, icache is not, new_conds are negated conditions
static Node computeProcessTerms( Node body, std::vector< Node >& new_vars, std::vector< Node >& new_conds, Node q, QAttributes& qa );
- static Node computeCondSplit( Node body, QAttributes& qa );
static Node computePrenex( Node body, std::vector< Node >& args, std::vector< Node >& nargs, bool pol, bool prenexAgg );
static Node computePrenexAgg( Node n, bool topLevel, std::map< unsigned, std::map< Node, Node > >& visited );
static Node computeSplit( std::vector< Node >& args, Node body, QAttributes& qa );
- static Node computeVarElimination( Node body, std::vector< Node >& args, QAttributes& qa );
private:
enum{
COMPUTE_ELIM_SYMBOLS = 0,
@@ -79,8 +149,6 @@ private:
COMPUTE_PRENEX,
COMPUTE_VAR_ELIMINATION,
COMPUTE_COND_SPLIT,
- //COMPUTE_FLATTEN_ARGS_UF,
- //COMPUTE_CNF,
COMPUTE_LAST
};
static Node computeOperation( Node f, int computeOption, QAttributes& qa );
diff --git a/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp b/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp
index 1d070417e..0a212598b 100644
--- a/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp
+++ b/src/theory/quantifiers/sygus/ce_guided_conjecture.cpp
@@ -202,7 +202,8 @@ bool CegConjecture::isSingleInvocation() const {
return d_ceg_si->isSingleInvocation();
}
-bool CegConjecture::needsCheck( std::vector< Node >& lem ) {
+bool CegConjecture::needsCheck()
+{
if( isSingleInvocation() && !d_ceg_si->needsCheck() ){
return false;
}
diff --git a/src/theory/quantifiers/sygus/ce_guided_conjecture.h b/src/theory/quantifiers/sygus/ce_guided_conjecture.h
index 48d307f1e..612a2b4ce 100644
--- a/src/theory/quantifiers/sygus/ce_guided_conjecture.h
+++ b/src/theory/quantifiers/sygus/ce_guided_conjecture.h
@@ -58,7 +58,7 @@ public:
* refinement */
void incrementRefineCount() { d_refine_count++; }
/** whether the conjecture is waiting for a call to doCheck below */
- bool needsCheck( std::vector< Node >& lem );
+ bool needsCheck();
/** whether the conjecture is waiting for a call to doRefine below */
bool needsRefinement() const;
/** do single invocation check
diff --git a/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp b/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp
index 83a576d37..c59195746 100644
--- a/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp
+++ b/src/theory/quantifiers/sygus/ce_guided_instantiation.cpp
@@ -52,10 +52,20 @@ QuantifiersModule::QEffort CegInstantiation::needsModel(Theory::Effort e)
void CegInstantiation::check(Theory::Effort e, QEffort quant_e)
{
+ // are we at the proper effort level?
+ unsigned echeck =
+ d_conj->isSingleInvocation() ? QEFFORT_STANDARD : QEFFORT_MODEL;
+ if (quant_e != echeck)
+ {
+ return;
+ }
+
// if we are waiting to assign the conjecture, do it now
if (!d_waiting_conj.isNull())
{
Node q = d_waiting_conj;
+ Trace("cegqi-engine") << "--- Conjecture waiting to assign: " << q
+ << std::endl;
d_waiting_conj = Node::null();
if (!d_conj->isAssigned())
{
@@ -65,31 +75,29 @@ void CegInstantiation::check(Theory::Effort e, QEffort quant_e)
return;
}
}
- unsigned echeck =
- d_conj->isSingleInvocation() ? QEFFORT_STANDARD : QEFFORT_MODEL;
- if( quant_e==echeck ){
- Trace("cegqi-engine") << "---Counterexample Guided Instantiation Engine---" << std::endl;
- Trace("cegqi-engine-debug") << std::endl;
- bool active = false;
- bool value;
- if( d_quantEngine->getValuation().hasSatValue( d_conj->getConjecture(), value ) ) {
- active = value;
- }else{
- Trace("cegqi-engine-debug") << "...no value for quantified formula." << std::endl;
- }
- Trace("cegqi-engine-debug") << "Current conjecture status : active : " << active << std::endl;
- std::vector< Node > lem;
- if( active && d_conj->needsCheck( lem ) ){
- checkCegConjecture( d_conj );
- }else{
- Trace("cegqi-engine-debug") << "...does not need check." << std::endl;
- for( unsigned i=0; i<lem.size(); i++ ){
- Trace("cegqi-lemma") << "Cegqi::Lemma : check lemma : " << lem[i] << std::endl;
- d_quantEngine->addLemma( lem[i] );
- }
- }
- Trace("cegqi-engine") << "Finished Counterexample Guided Instantiation engine." << std::endl;
+
+ Trace("cegqi-engine") << "---Counterexample Guided Instantiation Engine---"
+ << std::endl;
+ Trace("cegqi-engine-debug") << std::endl;
+ bool active = false;
+ bool value;
+ if (d_quantEngine->getValuation().hasSatValue(d_conj->getConjecture(), value))
+ {
+ active = value;
+ }
+ else
+ {
+ Trace("cegqi-engine-debug")
+ << "...no value for quantified formula." << std::endl;
}
+ Trace("cegqi-engine-debug")
+ << "Current conjecture status : active : " << active << std::endl;
+ if (active && d_conj->needsCheck())
+ {
+ checkConjecture(d_conj);
+ }
+ Trace("cegqi-engine")
+ << "Finished Counterexample Guided Instantiation engine." << std::endl;
}
bool CegInstantiation::assignConjecture(Node q)
@@ -98,6 +106,7 @@ bool CegInstantiation::assignConjecture(Node q)
{
return false;
}
+ Trace("cegqi-engine") << "--- Assign conjecture " << q << std::endl;
if (options::sygusQePreproc())
{
// the following does quantifier elimination as a preprocess step
@@ -236,7 +245,7 @@ void CegInstantiation::registerQuantifier(Node q)
else
{
// assign it now
- d_conj->assign(q);
+ assignConjecture(q);
}
}else{
Assert( d_conj->getEmbeddedConjecture()==q );
@@ -257,7 +266,8 @@ Node CegInstantiation::getNextDecisionRequest( unsigned& priority ) {
return Node::null();
}
-void CegInstantiation::checkCegConjecture( CegConjecture * conj ) {
+void CegInstantiation::checkConjecture(CegConjecture* conj)
+{
Node q = conj->getEmbeddedConjecture();
Node aq = conj->getConjecture();
if( Trace.isOn("cegqi-engine-debug") ){
@@ -322,7 +332,7 @@ void CegInstantiation::checkCegConjecture( CegConjecture * conj ) {
if (conj->needsRefinement())
{
// immediately go to refine candidate
- checkCegConjecture(conj);
+ checkConjecture(conj);
return;
}
}
diff --git a/src/theory/quantifiers/sygus/ce_guided_instantiation.h b/src/theory/quantifiers/sygus/ce_guided_instantiation.h
index 03b4c4cc1..9c81b6978 100644
--- a/src/theory/quantifiers/sygus/ce_guided_instantiation.h
+++ b/src/theory/quantifiers/sygus/ce_guided_instantiation.h
@@ -48,8 +48,9 @@ private:
*/
bool assignConjecture(Node q);
/** check conjecture */
- void checkCegConjecture( CegConjecture * conj );
-public:
+ void checkConjecture(CegConjecture* conj);
+
+ public:
CegInstantiation( QuantifiersEngine * qe, context::Context* c );
~CegInstantiation();
public:
diff --git a/src/theory/quantifiers/term_enumeration.cpp b/src/theory/quantifiers/term_enumeration.cpp
index 34b9d7e81..8e3219768 100644
--- a/src/theory/quantifiers/term_enumeration.cpp
+++ b/src/theory/quantifiers/term_enumeration.cpp
@@ -53,84 +53,38 @@ Node TermEnumeration::getEnumerateTerm(TypeNode tn, unsigned index)
return d_enum_terms[tn][index];
}
-bool TermEnumeration::isClosedEnumerableType(TypeNode tn)
-{
- std::unordered_map<TypeNode, bool, TypeNodeHashFunction>::iterator it =
- d_typ_closed_enum.find(tn);
- if (it == d_typ_closed_enum.end())
- {
- d_typ_closed_enum[tn] = true;
- bool ret = true;
- if (tn.isArray() || tn.isSort() || tn.isCodatatype() || tn.isFunction())
- {
- ret = false;
- }
- else if (tn.isSet())
- {
- ret = isClosedEnumerableType(tn.getSetElementType());
- }
- else if (tn.isDatatype())
- {
- const Datatype& dt = ((DatatypeType)(tn).toType()).getDatatype();
- for (unsigned i = 0; i < dt.getNumConstructors(); i++)
- {
- for (unsigned j = 0; j < dt[i].getNumArgs(); j++)
- {
- TypeNode ctn = TypeNode::fromType(dt[i][j].getRangeType());
- if (tn != ctn && !isClosedEnumerableType(ctn))
- {
- ret = false;
- break;
- }
- }
- if (!ret)
- {
- break;
- }
- }
- }
-
- // other parametric sorts go here
-
- d_typ_closed_enum[tn] = ret;
- return ret;
- }
- else
- {
- return it->second;
- }
-}
-
-// checks whether a type is closed enumerable and is reasonably small enough (<1000)
-// such that all of its domain elements can be enumerated
bool TermEnumeration::mayComplete(TypeNode tn)
{
std::unordered_map<TypeNode, bool, TypeNodeHashFunction>::iterator it =
d_may_complete.find(tn);
if (it == d_may_complete.end())
{
- bool mc = false;
- if (isClosedEnumerableType(tn) && tn.isInterpretedFinite())
- {
- Cardinality c = tn.getCardinality();
- if (!c.isLargeFinite())
- {
- NodeManager* nm = NodeManager::currentNM();
- Node card = nm->mkConst(Rational(c.getFiniteCardinality()));
- // check if less than fixed upper bound, default 1000
- Node oth = nm->mkConst(Rational(options::fmfTypeCompletionThresh()));
- Node eq = nm->mkNode(LEQ, card, oth);
- eq = Rewriter::rewrite(eq);
- mc = eq.isConst() && eq.getConst<bool>();
- }
- }
+ // cache
+ bool mc = mayComplete(tn, options::fmfTypeCompletionThresh());
d_may_complete[tn] = mc;
return mc;
}
- else
+ return it->second;
+}
+
+bool TermEnumeration::mayComplete(TypeNode tn, unsigned maxCard)
+{
+ bool mc = false;
+ if (tn.isClosedEnumerable() && tn.isInterpretedFinite())
{
- return it->second;
+ Cardinality c = tn.getCardinality();
+ if (!c.isLargeFinite())
+ {
+ NodeManager* nm = NodeManager::currentNM();
+ Node card = nm->mkConst(Rational(c.getFiniteCardinality()));
+ // check if less than fixed upper bound
+ Node oth = nm->mkConst(Rational(maxCard));
+ Node eq = nm->mkNode(LEQ, card, oth);
+ eq = Rewriter::rewrite(eq);
+ mc = eq.isConst() && eq.getConst<bool>();
+ }
}
+ return mc;
}
} /* CVC4::theory::quantifiers namespace */
diff --git a/src/theory/quantifiers/term_enumeration.h b/src/theory/quantifiers/term_enumeration.h
index cede77dbe..cf25335f4 100644
--- a/src/theory/quantifiers/term_enumeration.h
+++ b/src/theory/quantifiers/term_enumeration.h
@@ -42,25 +42,19 @@ class TermEnumeration
~TermEnumeration() {}
/** get i^th term for type tn */
Node getEnumerateTerm(TypeNode tn, unsigned i);
- /** is closed enumerable type
- *
- * This returns true if this type has an enumerator that produces
- * constants that are handled by ground theory solvers.
- * Examples of types that are not closed enumerable are:
- * (1) uninterpreted sorts,
- * (2) arrays,
- * (3) codatatypes,
- * (4) parametric sorts involving any of the above.
- */
- bool isClosedEnumerableType(TypeNode tn);
/** may complete type
*
- * Returns true if the type tn is closed
- * enumerable, and is small enough
- * for finite model finding to enumerate it,
- * by some heuristic (current cardinality < 1000).
+ * Returns true if the type tn is closed enumerable, is interpreted as a
+ * finite type, and has cardinality less than some reasonable value
+ * (currently < 1000). This method caches the results of whether each type
+ * may be completed.
*/
bool mayComplete(TypeNode tn);
+ /**
+ * Static version of the above method where maximum cardinality is
+ * configurable.
+ */
+ static bool mayComplete(TypeNode tn, unsigned cardMax);
private:
/** ground terms enumerated for types */
diff --git a/src/theory/rep_set.cpp b/src/theory/rep_set.cpp
index 7fa22e418..dcd90c236 100644
--- a/src/theory/rep_set.cpp
+++ b/src/theory/rep_set.cpp
@@ -403,7 +403,7 @@ int RepSetIterator::increment(){
bool RepSetIterator::isFinished() const { return d_index.empty(); }
-Node RepSetIterator::getCurrentTerm(unsigned v, bool valTerm)
+Node RepSetIterator::getCurrentTerm(unsigned v, bool valTerm) const
{
unsigned ii = d_index_order[v];
unsigned curr = d_index[ii];
@@ -422,6 +422,14 @@ Node RepSetIterator::getCurrentTerm(unsigned v, bool valTerm)
return t;
}
+void RepSetIterator::getCurrentTerms(std::vector<Node>& terms) const
+{
+ for (unsigned i = 0, size = d_index_order.size(); i < size; i++)
+ {
+ terms.push_back(getCurrentTerm(i));
+ }
+}
+
void RepSetIterator::debugPrint( const char* c ){
for( unsigned v=0; v<d_index.size(); v++ ){
Debug( c ) << v << " : " << getCurrentTerm( v ) << std::endl;
diff --git a/src/theory/rep_set.h b/src/theory/rep_set.h
index 29cca462a..d5de1e520 100644
--- a/src/theory/rep_set.h
+++ b/src/theory/rep_set.h
@@ -148,7 +148,7 @@ public:
};
public:
- RepSetIterator(const RepSet* rs, RepBoundExt* rext);
+ RepSetIterator(const RepSet* rs, RepBoundExt* rext = nullptr);
~RepSetIterator() {}
/** set that this iterator will be iterating over instantiations for a
* quantifier */
@@ -168,9 +168,11 @@ public:
/** get domain size of the i^th field of this iterator */
unsigned domainSize(unsigned i);
/** get the i^th term in the tuple we are considering */
- Node getCurrentTerm(unsigned v, bool valTerm = false);
+ Node getCurrentTerm(unsigned v, bool valTerm = false) const;
/** get the number of terms in the tuple we are considering */
- unsigned getNumTerms() { return d_index_order.size(); }
+ unsigned getNumTerms() const { return d_index_order.size(); }
+ /** get current terms */
+ void getCurrentTerms(std::vector<Node>& terms) const;
/** get index order, returns var # */
unsigned getIndexOrder(unsigned v) { return d_index_order[v]; }
/** get variable order, returns index # */
diff --git a/src/util/dense_map.h b/src/util/dense_map.h
index a3a1573c0..410fcc8fa 100644
--- a/src/util/dense_map.h
+++ b/src/util/dense_map.h
@@ -26,7 +26,7 @@
#pragma once
-#include <boost/integer_traits.hpp>
+#include <limits>
#include <vector>
#include "base/cvc4_assert.h"
@@ -48,7 +48,8 @@ private:
typedef Index Position;
typedef std::vector<Position> PositionMap;
- static const Position POSITION_SENTINEL = boost::integer_traits<Position>::const_max;
+ static const Position POSITION_SENTINEL =
+ std::numeric_limits<Position>::max();
//Each Key in the set is mapped to its position in d_list.
//Each Key not in the set is mapped to KEY_SENTINEL
diff --git a/test/regress/Makefile.tests b/test/regress/Makefile.tests
index ae99caedd..59a5b5f64 100644
--- a/test/regress/Makefile.tests
+++ b/test/regress/Makefile.tests
@@ -610,6 +610,7 @@ REG0_TESTS = \
regress0/quantifiers/cegqi-nl-sq.smt2 \
regress0/quantifiers/clock-10.smt2 \
regress0/quantifiers/clock-3.smt2 \
+ regress0/quantifiers/cond-var-elim-binary.smt2 \
regress0/quantifiers/delta-simp.smt2 \
regress0/quantifiers/double-pattern.smt2 \
regress0/quantifiers/ex3.smt2 \
diff --git a/test/regress/regress0/quantifiers/cond-var-elim-binary.smt2 b/test/regress/regress0/quantifiers/cond-var-elim-binary.smt2
new file mode 100644
index 000000000..edf780b4e
--- /dev/null
+++ b/test/regress/regress0/quantifiers/cond-var-elim-binary.smt2
@@ -0,0 +1,16 @@
+(set-logic BV)
+(set-info :status unsat)
+(declare-fun k_42 () (_ BitVec 32))
+(declare-fun k_332 () (_ BitVec 32))
+(declare-fun k_28 () (_ BitVec 32))
+
+(assert (and
+(bvult k_332 k_42)
+
+(forall ((x (_ BitVec 32)) (y (_ BitVec 32))) (or
+ (and (not (bvult y (_ bv65539 32))) (not (= (_ bv1 32) x)))
+ (not (bvult k_332 (bvmul x k_42)))) )
+)
+)
+
+(check-sat)
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback