This commit adds the remaining changes for a working and integrated `pow2` solver.
In particular, it adds a rewrite and a lemma that identify `pow2(x)` with `0` whenever `x<0`.
Regressions are added as well, including `pow2-native-0.smt2` that shows the semantics of `pow2` on negative values.
The next steps are new rewrites and and more lemma schemas.
{PLUS, cvc5::Kind::PLUS},
{MULT, cvc5::Kind::MULT},
{IAND, cvc5::Kind::IAND},
+ {POW2, cvc5::Kind::POW2},
{MINUS, cvc5::Kind::MINUS},
{UMINUS, cvc5::Kind::UMINUS},
{DIVISION, cvc5::Kind::DIVISION},
{cvc5::Kind::PLUS, PLUS},
{cvc5::Kind::MULT, MULT},
{cvc5::Kind::IAND, IAND},
+ {cvc5::Kind::POW2, POW2},
{cvc5::Kind::MINUS, MINUS},
{cvc5::Kind::UMINUS, UMINUS},
{cvc5::Kind::DIVISION, DIVISION},
{
// integer version of AND
addIndexedOperator(api::IAND, api::IAND, "iand");
+ // pow2
+ addOperator(api::POW2, "pow2");
}
}
case kind::IS_INTEGER:
case kind::TO_INTEGER:
case kind::TO_REAL:
- case kind::POW:
- out << smtKindString(k, d_variant) << " ";
- break;
+ case kind::POW:
+ case kind::POW2: out << smtKindString(k, d_variant) << " "; break;
case kind::IAND:
out << "(_ iand " << n.getOperator().getConst<IntAnd>().d_size << ") ";
stillNeedToPrintParams = false;
case kind::MULT:
case kind::NONLINEAR_MULT: return "*";
case kind::IAND: return "iand";
+ case kind::POW2: return "POW2";
case kind::EXPONENTIAL: return "exp";
case kind::SINE: return "sin";
case kind::COSINE: return "cos";
// pow2 is only supported for integers
Assert(t[0].getType().isInteger());
Integer i = t[0].getConst<Rational>().getNumerator();
+ if (i < 0)
+ {
+ return RewriteResponse(
+ REWRITE_DONE,
+ nm->mkConst<Rational>(Rational(Integer(0), Integer(1))));
+ }
unsigned long k = i.getUnsignedLong();
Node ret = nm->mkConst<Rational>(Rational(Integer(2).pow(k), Integer(1)));
return RewriteResponse(REWRITE_DONE, ret);
d_ee->addFunctionKind(kind::EXPONENTIAL);
d_ee->addFunctionKind(kind::SINE);
d_ee->addFunctionKind(kind::IAND);
+ d_ee->addFunctionKind(kind::POW2);
// have proof equality engine only if proofs are enabled
Assert(isProofEnabled() == (pfee != nullptr));
d_pfee = pfee;
if (n != d_zero)
{
Kind k = n.getKind();
- if (k != NONLINEAR_MULT && !isTranscendentalKind(k) && k != IAND)
+ if (k != NONLINEAR_MULT && !isTranscendentalKind(k) && k != IAND
+ && k != POW2)
{
id = ExtReducedId::ARITH_SR_LINEAR;
return true;
d_tangentPlaneSlv(&d_extState),
d_cadSlv(d_im, d_model, state.getUserContext(), pnm),
d_icpSlv(d_im),
- d_iandSlv(d_im, state, d_model)
+ d_iandSlv(d_im, state, d_model),
+ d_pow2Slv(d_im, state, d_model)
{
d_extTheory.addFunctionKind(kind::NONLINEAR_MULT);
d_extTheory.addFunctionKind(kind::EXPONENTIAL);
d_extTheory.addFunctionKind(kind::SINE);
d_extTheory.addFunctionKind(kind::PI);
d_extTheory.addFunctionKind(kind::IAND);
+ d_extTheory.addFunctionKind(kind::POW2);
d_true = NodeManager::currentNM()->mkConst(true);
d_zero = NodeManager::currentNM()->mkConst(Rational(0));
d_one = NodeManager::currentNM()->mkConst(Rational(1));
break;
case InferStep::IAND_FULL: d_iandSlv.checkFullRefine(); break;
case InferStep::IAND_INITIAL: d_iandSlv.checkInitialRefine(); break;
+ case InferStep::POW2_INIT:
+ d_pow2Slv.initLastCall(assertions, false_asserts, xts);
+ break;
+ case InferStep::POW2_FULL: d_pow2Slv.checkFullRefine(); break;
+ case InferStep::POW2_INITIAL: d_pow2Slv.checkInitialRefine(); break;
case InferStep::ICP:
d_icpSlv.reset(assertions);
d_icpSlv.check();
#include "theory/arith/nl/iand_solver.h"
#include "theory/arith/nl/icp/icp_solver.h"
#include "theory/arith/nl/nl_model.h"
+#include "theory/arith/nl/pow2_solver.h"
#include "theory/arith/nl/stats.h"
#include "theory/arith/nl/strategy.h"
#include "theory/arith/nl/transcendental/transcendental_solver.h"
*/
IAndSolver d_iandSlv;
+ /** The pow2 solver
+ *
+ * This is the subsolver responsible for running the procedure for
+ * constraints involving powers of 2.
+ */
+ Pow2Solver d_pow2Slv;
+
/** The strategy for the nonlinear extension. */
Strategy d_strategy;
#include "theory/arith/nl/pow2_solver.h"
+#include "options/arith_options.h"
+#include "options/smt_options.h"
+#include "preprocessing/passes/bv_to_int.h"
#include "theory/arith/arith_msum.h"
#include "theory/arith/arith_state.h"
#include "theory/arith/arith_utilities.h"
#include "theory/arith/inference_manager.h"
#include "theory/arith/nl/nl_model.h"
#include "theory/rewriter.h"
+#include "util/bitvector.h"
using namespace cvc5::kind;
{
Trace("pow2-check") << "* " << i << ", value = " << valPow2xAbstract
<< std::endl;
- Trace("pow2-check") << " actual (" << valXConcrete << ", "
- << ") = " << valPow2xConcrete << std::endl;
+ Trace("pow2-check") << " actual " << valXConcrete << " = "
+ << valPow2xConcrete << std::endl;
}
if (valPow2xAbstract == valPow2xConcrete)
{
continue;
}
+ Integer x = valXConcrete.getConst<Rational>().getNumerator();
+ Integer pow2x = valPow2xAbstract.getConst<Rational>().getNumerator();
// add monotinicity lemmas
for (uint64_t j = i + 1; j < size; j++)
{
Node m = d_pow2s[j];
- Node valPow2yConcrete = d_model.computeConcreteModelValue(m);
+ Node valPow2yAbstract = d_model.computeAbstractModelValue(m);
Node valYConcrete = d_model.computeConcreteModelValue(m[0]);
- Integer x = valXConcrete.getConst<Rational>().getNumerator();
Integer y = valYConcrete.getConst<Rational>().getNumerator();
- Integer pow2x = valPow2xConcrete.getConst<Rational>().getNumerator();
- Integer pow2y = valPow2yConcrete.getConst<Rational>().getNumerator();
+ Integer pow2y = valPow2yAbstract.getConst<Rational>().getNumerator();
- if (x <= y && pow2x > pow2y)
+ if (x < y && pow2x >= pow2y)
{
Node assumption = nm->mkNode(LEQ, n[0], m[0]);
Node conclusion = nm->mkNode(LEQ, n, m);
}
}
+ // triviality lemmas: pow2(x) = 0 whenever x < 0
+ if (x < 0 && pow2x != 0)
+ {
+ Node assumption = nm->mkNode(LT, n[0], d_zero);
+ Node conclusion = nm->mkNode(EQUAL, n, d_zero);
+ Node lem = nm->mkNode(IMPLIES, assumption, conclusion);
+ d_im.addPendingLemma(
+ lem, InferenceId::ARITH_NL_POW2_TRIVIAL_CASE_REFINE, nullptr, true);
+ }
+
// Place holder for additional lemma schemas
// End of additional lemma schemas
lem, InferenceId::ARITH_NL_POW2_VALUE_REFINE, nullptr, true);
}
}
+
Node Pow2Solver::valueBasedLemma(Node i)
{
Assert(i.getKind() == POW2);
case InferStep::IAND_INIT: return os << "IAND_INIT";
case InferStep::IAND_FULL: return os << "IAND_FULL";
case InferStep::IAND_INITIAL: return os << "IAND_INITIAL";
+ case InferStep::POW2_INIT: return os << "POW2_INIT";
+ case InferStep::POW2_FULL: return os << "POW2_FULL";
+ case InferStep::POW2_INITIAL: return os << "POW2_INITIAL";
case InferStep::ICP: return os << "ICP";
case InferStep::NL_INIT: return os << "NL_INIT";
case InferStep::NL_MONOMIAL_INFER_BOUNDS:
}
one << InferStep::IAND_INIT;
one << InferStep::IAND_INITIAL << InferStep::BREAK;
+ one << InferStep::POW2_INIT;
+ one << InferStep::POW2_INITIAL << InferStep::BREAK;
if (options::nlExt() == options::NlExtMode::FULL
|| options::nlExt() == options::NlExtMode::LIGHT)
{
one << InferStep::BREAK;
}
one << InferStep::IAND_FULL << InferStep::BREAK;
+ one << InferStep::POW2_FULL << InferStep::BREAK;
if (options::nlCad())
{
one << InferStep::CAD_INIT;
/** An initial IAND check */
IAND_INITIAL,
+ /** Initialize the POW2 solver */
+ POW2_INIT,
+ /** A full POW2 check */
+ POW2_FULL,
+ /** An initial POW2 check */
+ POW2_INITIAL,
+
/** An ICP check */
+
ICP,
/** Initialize the NL solver */
&& Polynomial::isMember(n[1]);
}
+bool Variable::isPow2Member(Node n)
+{
+ return n.getKind() == kind::POW2 && Polynomial::isMember(n[0]);
+}
+
bool Variable::isDivMember(Node n){
switch(n.getKind()){
case kind::DIVISION:
case kind::INTS_MODULUS_TOTAL:
case kind::DIVISION_TOTAL: return isDivMember(n);
case kind::IAND: return isIAndMember(n);
+ case kind::POW2: return isPow2Member(n);
case kind::EXPONENTIAL:
case kind::SINE:
case kind::COSINE:
static bool isLeafMember(Node n);
static bool isIAndMember(Node n);
+ static bool isPow2Member(Node n);
static bool isDivMember(Node n);
bool isDivLike() const{
return isDivMember(getNode());
return "ARITH_NL_POW2_INIT_REFINE";
case InferenceId::ARITH_NL_POW2_VALUE_REFINE:
return "ARITH_NL_POW2_VALUE_REFINE";
+ case InferenceId::ARITH_NL_POW2_MONOTONE_REFINE:
+ return "ARITH_NL_POW2_MONOTONE_REFINE";
+ case InferenceId::ARITH_NL_POW2_TRIVIAL_CASE_REFINE:
+ return "ARITH_NL_POW2_TRIVIAL_CASE_REFINE";
case InferenceId::ARITH_NL_CAD_CONFLICT: return "ARITH_NL_CAD_CONFLICT";
case InferenceId::ARITH_NL_CAD_EXCLUDED_INTERVAL:
return "ARITH_NL_CAD_EXCLUDED_INTERVAL";
ARITH_NL_POW2_VALUE_REFINE,
// monotonicity refinements (Pow2Solver::checkFullRefine)
ARITH_NL_POW2_MONOTONE_REFINE,
+ // trivial refinements (Pow2Solver::checkFullRefine)
+ ARITH_NL_POW2_TRIVIAL_CASE_REFINE,
//-------------------- nonlinear cad solver
// conflict / infeasible subset obtained from cad
ARITH_NL_CAD_CONFLICT,
regress0/nl/nta/sin-sym.smt2
regress0/nl/nta/sqrt-simple.smt2
regress0/nl/nta/tan-rewrite.smt2
+ regress0/nl/pow2-native-0.smt2
+ regress0/nl/pow2-native-1.smt2
+ regress0/nl/pow2-native-2.smt2
+ regress0/nl/pow2-native-3.smt2
regress0/nl/real-as-int.smt2
regress0/nl/real-div-ufnra.smt2
regress0/nl/sin-cos-346-b-chunk-0169.smt2
--- /dev/null
+; EXPECT: unsat
+(set-logic QF_NIA)
+(declare-fun x () Int)
+(assert (< x 0))
+(assert (distinct (pow2 x) 0))
+(check-sat)
--- /dev/null
+; EXPECT: sat
+(set-logic QF_NIA)
+(set-info :status sat)
+(declare-fun x () Int)
+
+(assert (and (<= 0 x) (< x 16)))
+(assert (> (pow2 x) 0))
+
+(check-sat)
--- /dev/null
+; EXPECT: unsat
+(set-logic QF_NIA)
+(set-info :status unsat)
+(declare-fun x () Int)
+
+(assert (and (<= 0 x) (< x 16)))
+(assert (< (pow2 x) x))
+
+(check-sat)
--- /dev/null
+; EXPECT: unsat
+(set-logic QF_NIA)
+(set-info :status unsat)
+(declare-fun x () Int)
+(declare-fun y () Int)
+
+(assert (<= 0 x))
+(assert (<= 0 y))
+(assert (< x y))
+(assert (> (pow2 x) (pow2 y)))
+
+(check-sat)