diff options
117 files changed, 5718 insertions, 1464 deletions
diff --git a/contrib/get-authors b/contrib/get-authors index df147d10d..0fb3f6ae1 100755 --- a/contrib/get-authors +++ b/contrib/get-authors @@ -47,10 +47,8 @@ while [ $# -gt 0 ]; do '$2 !~ /^[ \t]*(#include|namespace|}.*namespace.*|[ \t]*$)/ {print $1}' | \ # Keep author names only, remove the last 4 columns in ( ... ) - awk 'NF{NF-=4};1' | \ - # Determine top three contributors - sort | uniq -c | sort -rn | head -n3 | \ + awk 'NF{NF-=4};1' | \ # Fix author names sed "s,Not Committed Yet <not.committed.yet>,$gituser," | \ @@ -68,6 +66,9 @@ while [ $# -gt 0 ]; do sed 's/yoni206/Yoni Zohar/' | \ sed 's/ayveejay/Andrew V. Jones/' | \ + # Determine top three contributors + sort | uniq -c | sort -rn | head -n3 | \ + # Remove first columns from uniq -c (number of lines) awk '{$1=""; print}' | \ diff --git a/examples/api/datatypes-new.cpp b/examples/api/datatypes-new.cpp index 9cfc7992c..500cb0710 100644 --- a/examples/api/datatypes-new.cpp +++ b/examples/api/datatypes-new.cpp @@ -91,8 +91,8 @@ void test(Solver& slv, Sort& consListSort) DatatypeDecl paramConsListSpec = slv.mkDatatypeDecl("paramlist", sort); // give the datatype a name - DatatypeConstructorDecl paramCons("cons"); - DatatypeConstructorDecl paramNil("nil"); + DatatypeConstructorDecl paramCons = slv.mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl paramNil = slv.mkDatatypeConstructorDecl("nil"); paramCons.addSelector("head", sort); paramCons.addSelectorSelf("tail"); paramConsListSpec.addConstructor(paramCons); @@ -144,11 +144,11 @@ int main() DatatypeDecl consListSpec = slv.mkDatatypeDecl("list"); // give the datatype a name - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = slv.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", slv.getIntegerSort()); cons.addSelectorSelf("tail"); consListSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = slv.mkDatatypeConstructorDecl("nil"); consListSpec.addConstructor(nil); std::cout << "spec is:" << std::endl << consListSpec << std::endl; @@ -167,10 +167,10 @@ int main() << ">>> Alternatively, use declareDatatype" << std::endl; std::cout << std::endl; - DatatypeConstructorDecl cons2("cons"); + DatatypeConstructorDecl cons2 = slv.mkDatatypeConstructorDecl("cons"); cons2.addSelector("head", slv.getIntegerSort()); cons2.addSelectorSelf("tail"); - DatatypeConstructorDecl nil2("nil"); + DatatypeConstructorDecl nil2 = slv.mkDatatypeConstructorDecl("nil"); std::vector<DatatypeConstructorDecl> ctors = {cons2, nil2}; Sort consListSort2 = slv.declareDatatype("list2", ctors); test(slv, consListSort2); diff --git a/examples/api/python/bitvectors.py b/examples/api/python/bitvectors.py index 8e4e1b682..f12e79541 100755 --- a/examples/api/python/bitvectors.py +++ b/examples/api/python/bitvectors.py @@ -84,7 +84,7 @@ if __name__ == "__main__": print("Checking entailment assuming:", new_x_eq_new_x_) print("Expect ENTAILED.") - print("CVC4:", slv.checkEntailment(new_x_eq_new_x_)) + print("CVC4:", slv.checkEntailed(new_x_eq_new_x_)) print("Popping context.") slv.pop() diff --git a/examples/api/python/datatypes.py b/examples/api/python/datatypes.py index ded268d69..1a67f5661 100755 --- a/examples/api/python/datatypes.py +++ b/examples/api/python/datatypes.py @@ -65,8 +65,8 @@ def test(slv, consListSort): # constructor "cons". sort = slv.mkParamSort("T") paramConsListSpec = slv.mkDatatypeDecl("paramlist", sort) - paramCons = pycvc4.DatatypeConstructorDecl("cons") - paramNil = pycvc4.DatatypeConstructorDecl("nil") + paramCons = slv.mkDatatypeConstructorDecl("cons") + paramNil = slv.mkDatatypeConstructorDecl("nil") paramCons.addSelector("head", sort) paramCons.addSelectorSelf("tail") paramConsListSpec.addConstructor(paramCons) @@ -102,11 +102,11 @@ if __name__ == "__main__": # symbols are assigned to its constructors, selectors, and testers. consListSpec = slv.mkDatatypeDecl("list") # give the datatype a name - cons = pycvc4.DatatypeConstructorDecl("cons") + cons = slv.mkDatatypeConstructorDecl("cons") cons.addSelector("head", slv.getIntegerSort()) cons.addSelectorSelf("tail") consListSpec.addConstructor(cons) - nil = pycvc4.DatatypeConstructorDecl("nil") + nil = slv.mkDatatypeConstructorDecl("nil") consListSpec.addConstructor(nil) print("spec is {}".format(consListSpec)) @@ -122,10 +122,10 @@ if __name__ == "__main__": print("### Alternatively, use declareDatatype") - cons2 = pycvc4.DatatypeConstructorDecl("cons") + cons2 = slv.mkDatatypeConstructorDecl("cons") cons2.addSelector("head", slv.getIntegerSort()) cons2.addSelectorSelf("tail") - nil2 = pycvc4.DatatypeConstructorDecl("nil") + nil2 = slv.mkDatatypeConstructorDecl("nil") ctors = [cons2, nil2] consListSort2 = slv.declareDatatype("list2", ctors) test(slv, consListSort2) diff --git a/examples/api/python/floating_point.py b/examples/api/python/floating_point.py new file mode 100755 index 000000000..c92666c0b --- /dev/null +++ b/examples/api/python/floating_point.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python + +##################### +#! \file floating_point.py + ## \verbatim + ## Top contributors (to current version): + ## Eva Darulova, Makai Mann + ## This file is part of the CVC4 project. + ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS + ## in the top-level source directory) and their institutional affiliations. + ## All rights reserved. See the file COPYING in the top-level source + ## directory for licensing information.\endverbatim + ## + ## \brief A simple demonstration of the solving capabilities of the CVC4 + ## floating point solver through the Python API contributed by Eva + ## Darulova. This requires building CVC4 with symfpu. + +import pycvc4 +from pycvc4 import kinds + +if __name__ == "__main__": + slv = pycvc4.Solver() + slv.setOption("produce-models", "true") + slv.setLogic("FP") + + # single 32-bit precision + fp32 = slv.mkFloatingPointSort(8, 24) + + # the standard rounding mode + rm = slv.mkRoundingMode(pycvc4.RoundNearestTiesToEven) + + # create a few single-precision variables + x = slv.mkConst(fp32, 'x') + y = slv.mkConst(fp32, 'y') + z = slv.mkConst(fp32, 'z') + + # check floating-point arithmetic is commutative, i.e. x + y == y + x + commutative = slv.mkTerm(kinds.FPEq, slv.mkTerm(kinds.FPPlus, rm, x, y), slv.mkTerm(kinds.FPPlus, rm, y, x)) + + slv.push() + slv.assertFormula(slv.mkTerm(kinds.Not, commutative)) + print("Checking floating-point commutativity") + print("Expect SAT (property does not hold for NaN and Infinities).") + print("CVC4:", slv.checkSat()) + print("Model for x:", slv.getValue(x)) + print("Model for y:", slv.getValue(y)) + + # disallow NaNs and Infinities + slv.assertFormula(slv.mkTerm(kinds.Not, slv.mkTerm(kinds.FPIsNan, x))) + slv.assertFormula(slv.mkTerm(kinds.Not, slv.mkTerm(kinds.FPIsInf, x))) + slv.assertFormula(slv.mkTerm(kinds.Not, slv.mkTerm(kinds.FPIsNan, y))) + slv.assertFormula(slv.mkTerm(kinds.Not, slv.mkTerm(kinds.FPIsInf, y))) + + print("Checking floating-point commutativity assuming x and y are not NaN or Infinity") + print("Expect UNSAT.") + print("CVC4:", slv.checkSat()) + + # check floating-point arithmetic is not associative + slv.pop() + + # constrain x, y, z between -3.14 and 3.14 (also disallows NaN and infinity) + a = slv.mkFloatingPoint(8, 24, slv.mkBitVector("11000000010010001111010111000011", 2)) # -3.14 + b = slv.mkFloatingPoint(8, 24, slv.mkBitVector("01000000010010001111010111000011", 2)) # 3.14 + + bounds_x = slv.mkTerm(kinds.And, slv.mkTerm(kinds.FPLeq, a, x), slv.mkTerm(kinds.FPLeq, x, b)) + bounds_y = slv.mkTerm(kinds.And, slv.mkTerm(kinds.FPLeq, a, y), slv.mkTerm(kinds.FPLeq, y, b)) + bounds_z = slv.mkTerm(kinds.And, slv.mkTerm(kinds.FPLeq, a, z), slv.mkTerm(kinds.FPLeq, z, b)) + slv.assertFormula(slv.mkTerm(kinds.And, slv.mkTerm(kinds.And, bounds_x, bounds_y), bounds_z)) + + # (x + y) + z == x + (y + z) + lhs = slv.mkTerm(kinds.FPPlus, rm, slv.mkTerm(kinds.FPPlus, rm, x, y), z) + rhs = slv.mkTerm(kinds.FPPlus, rm, x, slv.mkTerm(kinds.FPPlus, rm, y, z)) + associative = slv.mkTerm(kinds.Not, slv.mkTerm(kinds.FPEq, lhs, rhs)) + + slv.assertFormula(associative) + + print("Checking floating-point associativity") + print("Expect SAT.") + print("CVC4:", slv.checkSat()) + print("Model for x:", slv.getValue(x)) + print("Model for y:", slv.getValue(y)) + print("Model for z:", slv.getValue(z)) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff11897e9..44b5dfeaf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -300,18 +300,20 @@ libcvc4_add_sources( theory/arith/linear_equality.h theory/arith/matrix.cpp theory/arith/matrix.h - theory/arith/nl_constraint.cpp - theory/arith/nl_constraint.h - theory/arith/nl_lemma_utils.cpp - theory/arith/nl_lemma_utils.h - theory/arith/nl_model.cpp - theory/arith/nl_model.h - theory/arith/nl_monomial.cpp - theory/arith/nl_monomial.h - theory/arith/nl_solver.cpp - theory/arith/nl_solver.h - theory/arith/nonlinear_extension.cpp - theory/arith/nonlinear_extension.h + theory/arith/nl/nl_constraint.cpp + theory/arith/nl/nl_constraint.h + theory/arith/nl/nl_lemma_utils.cpp + theory/arith/nl/nl_lemma_utils.h + theory/arith/nl/nl_model.cpp + theory/arith/nl/nl_model.h + theory/arith/nl/nl_monomial.cpp + theory/arith/nl/nl_monomial.h + theory/arith/nl/nl_solver.cpp + theory/arith/nl/nl_solver.h + theory/arith/nl/nonlinear_extension.cpp + theory/arith/nl/nonlinear_extension.h + theory/arith/nl/transcendental_solver.cpp + theory/arith/nl/transcendental_solver.h theory/arith/normal_form.cpp theory/arith/normal_form.h theory/arith/partial_model.cpp @@ -332,8 +334,6 @@ libcvc4_add_sources( theory/arith/theory_arith_private.h theory/arith/theory_arith_private_forward.h theory/arith/theory_arith_type_rules.h - theory/arith/transcendental_solver.cpp - theory/arith/transcendental_solver.h theory/arith/type_enumerator.h theory/arrays/array_info.cpp theory/arrays/array_info.h @@ -355,6 +355,8 @@ libcvc4_add_sources( theory/atom_requests.h theory/booleans/circuit_propagator.cpp theory/booleans/circuit_propagator.h + theory/booleans/proof_checker.cpp + theory/booleans/proof_checker.h theory/booleans/theory_bool.cpp theory/booleans/theory_bool.h theory/booleans/theory_bool_rewriter.cpp @@ -366,6 +368,8 @@ libcvc4_add_sources( theory/builtin/theory_builtin_rewriter.cpp theory/builtin/theory_builtin_rewriter.h theory/builtin/theory_builtin_type_rules.h + theory/builtin/proof_checker.cpp + theory/builtin/proof_checker.h theory/builtin/type_enumerator.cpp theory/builtin/type_enumerator.h theory/bv/abstraction.cpp @@ -750,6 +754,8 @@ libcvc4_add_sources( theory/uf/equality_engine.cpp theory/uf/equality_engine.h theory/uf/equality_engine_types.h + theory/uf/proof_checker.cpp + theory/uf/proof_checker.h theory/uf/ho_extension.cpp theory/uf/ho_extension.h theory/uf/symmetry_breaker.cpp @@ -923,6 +929,7 @@ install(FILES expr/datatype.h expr/emptyset.h expr/expr_iomanip.h + expr/expr_sequence.h expr/record.h expr/symbol_table.h expr/type.h diff --git a/src/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp index 1ea421c4b..734fcddae 100644 --- a/src/api/cvc4cpp.cpp +++ b/src/api/cvc4cpp.cpp @@ -724,12 +724,12 @@ class CVC4ApiExceptionStream & CVC4ApiExceptionStream().ostream() \ << "Invalid size of argument '" << #arg << "', expected " -#define CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(cond, what, arg, idx) \ - CVC4_PREDICT_TRUE(cond) \ - ? (void)0 \ - : OstreamVoider() \ - & CVC4ApiExceptionStream().ostream() \ - << "Invalid " << what << " '" << arg << "' at index" << idx \ +#define CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(cond, what, arg, idx) \ + CVC4_PREDICT_TRUE(cond) \ + ? (void)0 \ + : OstreamVoider() \ + & CVC4ApiExceptionStream().ostream() \ + << "Invalid " << what << " '" << arg << "' at index " << idx \ << ", expected " #define CVC4_API_SOLVER_TRY_CATCH_BEGIN \ @@ -739,6 +739,19 @@ class CVC4ApiExceptionStream } \ catch (const CVC4::Exception& e) { throw CVC4ApiException(e.getMessage()); } \ catch (const std::invalid_argument& e) { throw CVC4ApiException(e.what()); } + +#define CVC4_API_SOLVER_CHECK_SORT(sort) \ + CVC4_API_CHECK(this == sort.d_solver) \ + << "Given sort is not associated with this solver"; + +#define CVC4_API_SOLVER_CHECK_TERM(term) \ + CVC4_API_CHECK(this == term.d_solver) \ + << "Given term is not associated with this solver"; + +#define CVC4_API_SOLVER_CHECK_OP(op) \ + CVC4_API_CHECK(this == op.d_solver) \ + << "Given operator is not associated with this solver"; + } // namespace /* -------------------------------------------------------------------------- */ @@ -823,9 +836,12 @@ std::ostream& operator<<(std::ostream& out, const Result& r) /* Sort */ /* -------------------------------------------------------------------------- */ -Sort::Sort(const CVC4::Type& t) : d_type(new CVC4::Type(t)) {} +Sort::Sort(const Solver* slv, const CVC4::Type& t) + : d_solver(slv), d_type(new CVC4::Type(t)) +{ +} -Sort::Sort() : d_type(new CVC4::Type()) {} +Sort::Sort() : d_solver(nullptr), d_type(new CVC4::Type()) {} Sort::~Sort() {} @@ -909,7 +925,7 @@ bool Sort::isComparableTo(Sort s) const Datatype Sort::getDatatype() const { CVC4_API_CHECK(isDatatype()) << "Expected datatype sort."; - return DatatypeType(*d_type).getDatatype(); + return Datatype(d_solver, DatatypeType(*d_type).getDatatype()); } Sort Sort::instantiate(const std::vector<Sort>& params) const @@ -923,10 +939,10 @@ Sort Sort::instantiate(const std::vector<Sort>& params) const } if (d_type->isDatatype()) { - return DatatypeType(*d_type).instantiate(tparams); + return Sort(d_solver, DatatypeType(*d_type).instantiate(tparams)); } Assert(d_type->isSortConstructor()); - return SortConstructorType(*d_type).instantiate(tparams); + return Sort(d_solver, SortConstructorType(*d_type).instantiate(tparams)); } std::string Sort::toString() const { return d_type->toString(); } @@ -947,13 +963,13 @@ std::vector<Sort> Sort::getConstructorDomainSorts() const { CVC4_API_CHECK(isConstructor()) << "Not a function sort."; std::vector<CVC4::Type> types = ConstructorType(*d_type).getArgTypes(); - return typeVectorToSorts(types); + return typeVectorToSorts(d_solver, types); } Sort Sort::getConstructorCodomainSort() const { CVC4_API_CHECK(isConstructor()) << "Not a function sort."; - return ConstructorType(*d_type).getRangeType(); + return Sort(d_solver, ConstructorType(*d_type).getRangeType()); } /* Function sort ------------------------------------------------------- */ @@ -968,13 +984,13 @@ std::vector<Sort> Sort::getFunctionDomainSorts() const { CVC4_API_CHECK(isFunction()) << "Not a function sort."; std::vector<CVC4::Type> types = FunctionType(*d_type).getArgTypes(); - return typeVectorToSorts(types); + return typeVectorToSorts(d_solver, types); } Sort Sort::getFunctionCodomainSort() const { CVC4_API_CHECK(isFunction()) << "Not a function sort."; - return FunctionType(*d_type).getRangeType(); + return Sort(d_solver, FunctionType(*d_type).getRangeType()); } /* Array sort ---------------------------------------------------------- */ @@ -982,13 +998,13 @@ Sort Sort::getFunctionCodomainSort() const Sort Sort::getArrayIndexSort() const { CVC4_API_CHECK(isArray()) << "Not an array sort."; - return ArrayType(*d_type).getIndexType(); + return Sort(d_solver, ArrayType(*d_type).getIndexType()); } Sort Sort::getArrayElementSort() const { CVC4_API_CHECK(isArray()) << "Not an array sort."; - return ArrayType(*d_type).getConstituentType(); + return Sort(d_solver, ArrayType(*d_type).getConstituentType()); } /* Set sort ------------------------------------------------------------ */ @@ -996,7 +1012,7 @@ Sort Sort::getArrayElementSort() const Sort Sort::getSetElementSort() const { CVC4_API_CHECK(isSet()) << "Not a set sort."; - return SetType(*d_type).getElementType(); + return Sort(d_solver, SetType(*d_type).getElementType()); } /* Uninterpreted sort -------------------------------------------------- */ @@ -1017,7 +1033,7 @@ std::vector<Sort> Sort::getUninterpretedSortParamSorts() const { CVC4_API_CHECK(isUninterpretedSort()) << "Not an uninterpreted sort."; std::vector<CVC4::Type> types = SortType(*d_type).getParamTypes(); - return typeVectorToSorts(types); + return typeVectorToSorts(d_solver, types); } /* Sort constructor sort ----------------------------------------------- */ @@ -1062,7 +1078,7 @@ std::vector<Sort> Sort::getDatatypeParamSorts() const { CVC4_API_CHECK(isParametricDatatype()) << "Not a parametric datatype sort."; std::vector<CVC4::Type> types = DatatypeType(*d_type).getParamTypes(); - return typeVectorToSorts(types); + return typeVectorToSorts(d_solver, types); } size_t Sort::getDatatypeArity() const @@ -1083,7 +1099,7 @@ std::vector<Sort> Sort::getTupleSorts() const { CVC4_API_CHECK(isTuple()) << "Not a tuple sort."; std::vector<CVC4::Type> types = DatatypeType(*d_type).getTupleTypes(); - return typeVectorToSorts(types); + return typeVectorToSorts(d_solver, types); } /* --------------------------------------------------------------------- */ @@ -1105,9 +1121,13 @@ size_t SortHashFunction::operator()(const Sort& s) const Op::Op() : d_kind(NULL_EXPR), d_expr(new CVC4::Expr()) {} -Op::Op(const Kind k) : d_kind(k), d_expr(new CVC4::Expr()) {} +Op::Op(const Solver* slv, const Kind k) + : d_solver(slv), d_kind(k), d_expr(new CVC4::Expr()) +{ +} -Op::Op(const Kind k, const CVC4::Expr& e) : d_kind(k), d_expr(new CVC4::Expr(e)) +Op::Op(const Solver* slv, const Kind k, const CVC4::Expr& e) + : d_solver(slv), d_kind(k), d_expr(new CVC4::Expr(e)) { } @@ -1323,19 +1343,20 @@ size_t OpHashFunction::operator()(const Op& t) const /* Term */ /* -------------------------------------------------------------------------- */ -Term::Term() : d_expr(new CVC4::Expr()) {} +Term::Term() : d_solver(nullptr), d_expr(new CVC4::Expr()) {} -Term::Term(const CVC4::Expr& e) : d_expr(new CVC4::Expr(e)) {} +Term::Term(const Solver* slv, const CVC4::Expr& e) + : d_solver(slv), d_expr(new CVC4::Expr(e)) +{ +} Term::~Term() {} -/* Helpers */ -/* -------------------------------------------------------------------------- */ - -/* Split out to avoid nested API calls (problematic with API tracing). */ -/* .......................................................................... */ - -bool Term::isNullHelper() const { return d_expr->isNull(); } +bool Term::isNullHelper() const +{ + /* Split out to avoid nested API calls (problematic with API tracing). */ + return d_expr->isNull(); +} bool Term::operator==(const Term& t) const { return *d_expr == *t.d_expr; } @@ -1371,12 +1392,12 @@ Term Term::operator[](size_t index) const if (index == 0) { // return the operator - return api::Term(d_expr->getOperator()); + return Term(d_solver, d_expr->getOperator()); } // otherwise we are looking up child at (index-1) index--; } - return api::Term((*d_expr)[index]); + return Term(d_solver, (*d_expr)[index]); } uint64_t Term::getId() const @@ -1394,7 +1415,7 @@ Kind Term::getKind() const Sort Term::getSort() const { CVC4_API_CHECK_NOT_NULL; - return Sort(d_expr->getType()); + return Sort(d_solver, d_expr->getType()); } Term Term::substitute(Term e, Term replacement) const @@ -1406,7 +1427,7 @@ Term Term::substitute(Term e, Term replacement) const << "Expected non-null term as replacement in substitute"; CVC4_API_CHECK(e.getSort().isComparableTo(replacement.getSort())) << "Expecting terms of comparable sort in substitute"; - return api::Term(d_expr->substitute(e.getExpr(), replacement.getExpr())); + return Term(d_solver, d_expr->substitute(e.getExpr(), replacement.getExpr())); } Term Term::substitute(const std::vector<Term> es, @@ -1424,8 +1445,9 @@ Term Term::substitute(const std::vector<Term> es, CVC4_API_CHECK(es[i].getSort().isComparableTo(replacements[i].getSort())) << "Expecting terms of comparable sort in substitute"; } - return api::Term(d_expr->substitute(termVectorToExprs(es), - termVectorToExprs(replacements))); + return Term(d_solver, + d_expr->substitute(termVectorToExprs(es), + termVectorToExprs(replacements))); } bool Term::hasOp() const @@ -1447,18 +1469,18 @@ Op Term::getOp() const // is one of the APPLY_* kinds if (isApplyKind(d_expr->getKind())) { - return Op(intToExtKind(d_expr->getKind())); + return Op(d_solver, intToExtKind(d_expr->getKind())); } else if (d_expr->isParameterized()) { // it's an indexed operator // so we should return the indexed op CVC4::Expr op = d_expr->getOperator(); - return Op(intToExtKind(d_expr->getKind()), op); + return Op(d_solver, intToExtKind(d_expr->getKind()), op); } else { - return Op(intToExtKind(d_expr->getKind())); + return Op(d_solver, intToExtKind(d_expr->getKind())); } } @@ -1475,9 +1497,9 @@ Term Term::notTerm() const CVC4_API_CHECK_NOT_NULL; try { - Term res = d_expr->notExpr(); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->notExpr(); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1491,9 +1513,9 @@ Term Term::andTerm(const Term& t) const CVC4_API_ARG_CHECK_NOT_NULL(t); try { - Term res = d_expr->andExpr(*t.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->andExpr(*t.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1507,9 +1529,9 @@ Term Term::orTerm(const Term& t) const CVC4_API_ARG_CHECK_NOT_NULL(t); try { - Term res = d_expr->orExpr(*t.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->orExpr(*t.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1523,9 +1545,9 @@ Term Term::xorTerm(const Term& t) const CVC4_API_ARG_CHECK_NOT_NULL(t); try { - Term res = d_expr->xorExpr(*t.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->xorExpr(*t.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1539,9 +1561,9 @@ Term Term::eqTerm(const Term& t) const CVC4_API_ARG_CHECK_NOT_NULL(t); try { - Term res = d_expr->eqExpr(*t.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->eqExpr(*t.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1555,9 +1577,9 @@ Term Term::impTerm(const Term& t) const CVC4_API_ARG_CHECK_NOT_NULL(t); try { - Term res = d_expr->impExpr(*t.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->impExpr(*t.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1572,9 +1594,9 @@ Term Term::iteTerm(const Term& then_t, const Term& else_t) const CVC4_API_ARG_CHECK_NOT_NULL(else_t); try { - Term res = d_expr->iteExpr(*then_t.d_expr, *else_t.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_expr->iteExpr(*then_t.d_expr, *else_t.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(d_solver, res); } catch (const CVC4::TypeCheckingException& e) { @@ -1584,11 +1606,15 @@ Term Term::iteTerm(const Term& then_t, const Term& else_t) const std::string Term::toString() const { return d_expr->toString(); } -Term::const_iterator::const_iterator() : d_orig_expr(nullptr), d_pos(0) {} +Term::const_iterator::const_iterator() + : d_solver(nullptr), d_orig_expr(nullptr), d_pos(0) +{ +} -Term::const_iterator::const_iterator(const std::shared_ptr<CVC4::Expr>& e, +Term::const_iterator::const_iterator(const Solver* slv, + const std::shared_ptr<CVC4::Expr>& e, uint32_t p) - : d_orig_expr(e), d_pos(p) + : d_solver(slv), d_orig_expr(e), d_pos(p) { } @@ -1597,6 +1623,7 @@ Term::const_iterator::const_iterator(const const_iterator& it) { if (it.d_orig_expr != nullptr) { + d_solver = it.d_solver; d_orig_expr = it.d_orig_expr; d_pos = it.d_pos; } @@ -1604,6 +1631,7 @@ Term::const_iterator::const_iterator(const const_iterator& it) Term::const_iterator& Term::const_iterator::operator=(const const_iterator& it) { + d_solver = it.d_solver; d_orig_expr = it.d_orig_expr; d_pos = it.d_pos; return *this; @@ -1615,7 +1643,8 @@ bool Term::const_iterator::operator==(const const_iterator& it) const { return false; } - return (*d_orig_expr == *it.d_orig_expr) && (d_pos == it.d_pos); + return (d_solver == it.d_solver && *d_orig_expr == *it.d_orig_expr) + && (d_pos == it.d_pos); } bool Term::const_iterator::operator!=(const const_iterator& it) const @@ -1647,7 +1676,7 @@ Term Term::const_iterator::operator*() const if (!d_pos && extra_child) { - return Term(d_orig_expr->getOperator()); + return Term(d_solver, d_orig_expr->getOperator()); } else { @@ -1658,13 +1687,13 @@ Term Term::const_iterator::operator*() const --idx; } Assert(idx >= 0); - return Term((*d_orig_expr)[idx]); + return Term(d_solver, (*d_orig_expr)[idx]); } } Term::const_iterator Term::begin() const { - return Term::const_iterator(d_expr, 0); + return Term::const_iterator(d_solver, d_expr, 0); } Term::const_iterator Term::end() const @@ -1681,7 +1710,7 @@ Term::const_iterator Term::end() const // one more child if this is a UF application (count the UF as a child) ++endpos; } - return Term::const_iterator(d_expr, endpos); + return Term::const_iterator(d_solver, d_expr, endpos); } // !!! This is only temporarily available until the parser is fully migrated @@ -1741,8 +1770,14 @@ size_t TermHashFunction::operator()(const Term& t) const /* DatatypeConstructorDecl -------------------------------------------------- */ -DatatypeConstructorDecl::DatatypeConstructorDecl(const std::string& name) - : d_ctor(new CVC4::DatatypeConstructor(name)) +DatatypeConstructorDecl::DatatypeConstructorDecl() + : d_solver(nullptr), d_ctor(nullptr) +{ +} + +DatatypeConstructorDecl::DatatypeConstructorDecl(const Solver* slv, + const std::string& name) + : d_solver(slv), d_ctor(new CVC4::DatatypeConstructor(name)) { } @@ -1789,28 +1824,33 @@ std::ostream& operator<<(std::ostream& out, /* DatatypeDecl ------------------------------------------------------------- */ -DatatypeDecl::DatatypeDecl(const Solver* s, +DatatypeDecl::DatatypeDecl() : d_solver(nullptr), d_dtype(nullptr) {} + +DatatypeDecl::DatatypeDecl(const Solver* slv, const std::string& name, bool isCoDatatype) - : d_dtype(new CVC4::Datatype(s->getExprManager(), name, isCoDatatype)) + : d_solver(slv), + d_dtype(new CVC4::Datatype(slv->getExprManager(), name, isCoDatatype)) { } -DatatypeDecl::DatatypeDecl(const Solver* s, +DatatypeDecl::DatatypeDecl(const Solver* slv, const std::string& name, Sort param, bool isCoDatatype) - : d_dtype(new CVC4::Datatype(s->getExprManager(), + : d_solver(slv), + d_dtype(new CVC4::Datatype(slv->getExprManager(), name, std::vector<Type>{*param.d_type}, isCoDatatype)) { } -DatatypeDecl::DatatypeDecl(const Solver* s, +DatatypeDecl::DatatypeDecl(const Solver* slv, const std::string& name, const std::vector<Sort>& params, bool isCoDatatype) + : d_solver(slv) { std::vector<Type> tparams; for (const Sort& p : params) @@ -1818,13 +1858,11 @@ DatatypeDecl::DatatypeDecl(const Solver* s, tparams.push_back(*p.d_type); } d_dtype = std::shared_ptr<CVC4::Datatype>( - new CVC4::Datatype(s->getExprManager(), name, tparams, isCoDatatype)); + new CVC4::Datatype(slv->getExprManager(), name, tparams, isCoDatatype)); } bool DatatypeDecl::isNullHelper() const { return !d_dtype; } -DatatypeDecl::DatatypeDecl() {} - DatatypeDecl::~DatatypeDecl() {} void DatatypeDecl::addConstructor(const DatatypeConstructorDecl& ctor) @@ -1875,8 +1913,9 @@ std::ostream& operator<<(std::ostream& out, const DatatypeDecl& dtdecl) DatatypeSelector::DatatypeSelector() { d_stor = nullptr; } -DatatypeSelector::DatatypeSelector(const CVC4::DatatypeConstructorArg& stor) - : d_stor(new CVC4::DatatypeConstructorArg(stor)) +DatatypeSelector::DatatypeSelector(const Solver* slv, + const CVC4::DatatypeConstructorArg& stor) + : d_solver(slv), d_stor(new CVC4::DatatypeConstructorArg(stor)) { CVC4_API_CHECK(d_stor->isResolved()) << "Expected resolved datatype selector"; } @@ -1887,13 +1926,13 @@ std::string DatatypeSelector::getName() const { return d_stor->getName(); } Term DatatypeSelector::getSelectorTerm() const { - Term sel = d_stor->getSelector(); + Term sel = Term(d_solver, d_stor->getSelector()); return sel; } Sort DatatypeSelector::getRangeSort() const { - return Sort(d_stor->getRangeType()); + return Sort(d_solver, d_stor->getRangeType()); } std::string DatatypeSelector::toString() const @@ -1919,10 +1958,13 @@ std::ostream& operator<<(std::ostream& out, const DatatypeSelector& stor) /* DatatypeConstructor ------------------------------------------------------ */ -DatatypeConstructor::DatatypeConstructor() { d_ctor = nullptr; } +DatatypeConstructor::DatatypeConstructor() : d_solver(nullptr), d_ctor(nullptr) +{ +} -DatatypeConstructor::DatatypeConstructor(const CVC4::DatatypeConstructor& ctor) - : d_ctor(new CVC4::DatatypeConstructor(ctor)) +DatatypeConstructor::DatatypeConstructor(const Solver* slv, + const CVC4::DatatypeConstructor& ctor) + : d_solver(slv), d_ctor(new CVC4::DatatypeConstructor(ctor)) { CVC4_API_CHECK(d_ctor->isResolved()) << "Expected resolved datatype constructor"; @@ -1934,13 +1976,13 @@ std::string DatatypeConstructor::getName() const { return d_ctor->getName(); } Term DatatypeConstructor::getConstructorTerm() const { - Term ctor = d_ctor->getConstructor(); + Term ctor = Term(d_solver, d_ctor->getConstructor()); return ctor; } Term DatatypeConstructor::getTesterTerm() const { - Term tst = d_ctor->getTester(); + Term tst = Term(d_solver, d_ctor->getTester()); return tst; } @@ -1951,7 +1993,7 @@ size_t DatatypeConstructor::getNumSelectors() const DatatypeSelector DatatypeConstructor::operator[](size_t index) const { - return (*d_ctor)[index]; + return DatatypeSelector(d_solver, (*d_ctor)[index]); } DatatypeSelector DatatypeConstructor::operator[](const std::string& name) const @@ -1972,36 +2014,41 @@ Term DatatypeConstructor::getSelectorTerm(const std::string& name) const DatatypeConstructor::const_iterator DatatypeConstructor::begin() const { - return DatatypeConstructor::const_iterator(*d_ctor, true); + return DatatypeConstructor::const_iterator(d_solver, *d_ctor, true); } DatatypeConstructor::const_iterator DatatypeConstructor::end() const { - return DatatypeConstructor::const_iterator(*d_ctor, false); + return DatatypeConstructor::const_iterator(d_solver, *d_ctor, false); } DatatypeConstructor::const_iterator::const_iterator( - const CVC4::DatatypeConstructor& ctor, bool begin) + const Solver* slv, const CVC4::DatatypeConstructor& ctor, bool begin) { + d_solver = slv; d_int_stors = ctor.getArgs(); + const std::vector<CVC4::DatatypeConstructorArg>* sels = static_cast<const std::vector<CVC4::DatatypeConstructorArg>*>( d_int_stors); for (const auto& s : *sels) { /* Can not use emplace_back here since constructor is private. */ - d_stors.push_back(DatatypeSelector(s)); + d_stors.push_back(DatatypeSelector(d_solver, s)); } d_idx = begin ? 0 : sels->size(); } -// Nullary constructor for Cython -DatatypeConstructor::const_iterator::const_iterator() {} +DatatypeConstructor::const_iterator::const_iterator() + : d_solver(nullptr), d_int_stors(nullptr), d_idx(0) +{ +} DatatypeConstructor::const_iterator& DatatypeConstructor::const_iterator::operator=( const DatatypeConstructor::const_iterator& it) { + d_solver = it.d_solver; d_int_stors = it.d_int_stors; d_stors = it.d_stors; d_idx = it.d_idx; @@ -2076,7 +2123,7 @@ DatatypeSelector DatatypeConstructor::getSelectorForName( } CVC4_API_CHECK(foundSel) << "No selector " << name << " for constructor " << getName() << " exists"; - return (*d_ctor)[index]; + return DatatypeSelector(d_solver, (*d_ctor)[index]); } std::ostream& operator<<(std::ostream& out, const DatatypeConstructor& ctor) @@ -2087,21 +2134,20 @@ std::ostream& operator<<(std::ostream& out, const DatatypeConstructor& ctor) /* Datatype ----------------------------------------------------------------- */ -Datatype::Datatype(const CVC4::Datatype& dtype) - : d_dtype(new CVC4::Datatype(dtype)) +Datatype::Datatype(const Solver* slv, const CVC4::Datatype& dtype) + : d_solver(slv), d_dtype(new CVC4::Datatype(dtype)) { CVC4_API_CHECK(d_dtype->isResolved()) << "Expected resolved datatype"; } -// Nullary constructor for Cython -Datatype::Datatype() {} +Datatype::Datatype() : d_solver(nullptr), d_dtype(nullptr) {} Datatype::~Datatype() {} DatatypeConstructor Datatype::operator[](size_t idx) const { CVC4_API_CHECK(idx < getNumConstructors()) << "Index out of bounds."; - return (*d_dtype)[idx]; + return DatatypeConstructor(d_solver, (*d_dtype)[idx]); } DatatypeConstructor Datatype::operator[](const std::string& name) const @@ -2141,12 +2187,12 @@ std::string Datatype::toString() const { return d_dtype->getName(); } Datatype::const_iterator Datatype::begin() const { - return Datatype::const_iterator(*d_dtype, true); + return Datatype::const_iterator(d_solver, *d_dtype, true); } Datatype::const_iterator Datatype::end() const { - return Datatype::const_iterator(*d_dtype, false); + return Datatype::const_iterator(d_solver, *d_dtype, false); } // !!! This is only temporarily available until the parser is fully migrated @@ -2169,28 +2215,33 @@ DatatypeConstructor Datatype::getConstructorForName( } CVC4_API_CHECK(foundCons) << "No constructor " << name << " for datatype " << getName() << " exists"; - return (*d_dtype)[index]; + return DatatypeConstructor(d_solver, (*d_dtype)[index]); } -Datatype::const_iterator::const_iterator(const CVC4::Datatype& dtype, +Datatype::const_iterator::const_iterator(const Solver* slv, + const CVC4::Datatype& dtype, bool begin) + : d_solver(slv), d_int_ctors(dtype.getConstructors()) { - d_int_ctors = dtype.getConstructors(); const std::vector<CVC4::DatatypeConstructor>* cons = static_cast<const std::vector<CVC4::DatatypeConstructor>*>(d_int_ctors); for (const auto& c : *cons) { /* Can not use emplace_back here since constructor is private. */ - d_ctors.push_back(DatatypeConstructor(c)); + d_ctors.push_back(DatatypeConstructor(d_solver, c)); } d_idx = begin ? 0 : cons->size(); } -Datatype::const_iterator::const_iterator() {} +Datatype::const_iterator::const_iterator() + : d_solver(nullptr), d_int_ctors(nullptr), d_idx(0) +{ +} Datatype::const_iterator& Datatype::const_iterator::operator=( const Datatype::const_iterator& it) { + d_solver = it.d_solver; d_int_ctors = it.d_int_ctors; d_ctors = it.d_ctors; d_idx = it.d_idx; @@ -2235,10 +2286,10 @@ bool Datatype::const_iterator::operator!=( /* -------------------------------------------------------------------------- */ /* Grammar */ /* -------------------------------------------------------------------------- */ -Grammar::Grammar(const Solver* s, +Grammar::Grammar(const Solver* slv, const std::vector<Term>& sygusVars, const std::vector<Term>& ntSymbols) - : d_s(s), + : d_solver(slv), d_sygusVars(sygusVars), d_ntSyms(ntSymbols), d_ntsToTerms(ntSymbols.size()), @@ -2326,8 +2377,9 @@ Sort Grammar::resolve() if (!d_sygusVars.empty()) { - bvl = d_s->getExprManager()->mkExpr(CVC4::kind::BOUND_VAR_LIST, - termVectorToExprs(d_sygusVars)); + bvl = Term(d_solver, + d_solver->getExprManager()->mkExpr( + CVC4::kind::BOUND_VAR_LIST, termVectorToExprs(d_sygusVars))); } std::unordered_map<Term, Sort, TermHashFunction> ntsToUnres(d_ntSyms.size()); @@ -2336,7 +2388,8 @@ Sort Grammar::resolve() { // make the unresolved type, used for referencing the final version of // the ntsymbol's datatype - ntsToUnres[ntsymbol] = d_s->getExprManager()->mkSort(ntsymbol.toString()); + ntsToUnres[ntsymbol] = + Sort(d_solver, d_solver->getExprManager()->mkSort(ntsymbol.toString())); } std::vector<CVC4::Datatype> datatypes; @@ -2347,7 +2400,7 @@ Sort Grammar::resolve() for (const Term& ntSym : d_ntSyms) { // make the datatype, which encodes terms generated by this non-terminal - DatatypeDecl dtDecl(d_s, ntSym.toString()); + DatatypeDecl dtDecl(d_solver, ntSym.toString()); for (const Term& consTerm : d_ntsToTerms[ntSym]) { @@ -2356,7 +2409,8 @@ Sort Grammar::resolve() if (d_allowVars.find(ntSym) != d_allowVars.cend()) { - addSygusConstructorVariables(dtDecl, ntSym.d_expr->getType()); + addSygusConstructorVariables(dtDecl, + Sort(d_solver, ntSym.d_expr->getType())); } bool aci = d_allowConst.find(ntSym) != d_allowConst.end(); @@ -2375,11 +2429,11 @@ Sort Grammar::resolve() } std::vector<DatatypeType> datatypeTypes = - d_s->getExprManager()->mkMutualDatatypeTypes( + d_solver->getExprManager()->mkMutualDatatypeTypes( datatypes, unresTypes, ExprManager::DATATYPE_FLAG_PLACEHOLDER); // return is the first datatype - return datatypeTypes[0]; + return Sort(d_solver, datatypeTypes[0]); } void Grammar::addSygusConstructorTerm( @@ -2406,11 +2460,13 @@ void Grammar::addSygusConstructorTerm( *op.d_expr, termVectorToExprs(args)); if (!args.empty()) { - Term lbvl = d_s->getExprManager()->mkExpr(CVC4::kind::BOUND_VAR_LIST, - termVectorToExprs(args)); + Term lbvl = Term(d_solver, + d_solver->getExprManager()->mkExpr( + CVC4::kind::BOUND_VAR_LIST, termVectorToExprs(args))); // its operator is a lambda - op = d_s->getExprManager()->mkExpr(CVC4::kind::LAMBDA, - {*lbvl.d_expr, *op.d_expr}); + op = Term(d_solver, + d_solver->getExprManager()->mkExpr(CVC4::kind::LAMBDA, + {*lbvl.d_expr, *op.d_expr})); } dt.d_dtype->addSygusConstructor( *op.d_expr, ssCName.str(), sortVectorToTypes(cargs), spc); @@ -2426,7 +2482,9 @@ Term Grammar::purifySygusGTerm( ntsToUnres.find(term); if (itn != ntsToUnres.cend()) { - Term ret = d_s->getExprManager()->mkBoundVar(term.d_expr->getType()); + Term ret = + Term(d_solver, + d_solver->getExprManager()->mkBoundVar(term.d_expr->getType())); args.push_back(ret); cargs.push_back(itn->second); return ret; @@ -2435,7 +2493,8 @@ Term Grammar::purifySygusGTerm( bool childChanged = false; for (unsigned i = 0, nchild = term.d_expr->getNumChildren(); i < nchild; i++) { - Term ptermc = purifySygusGTerm((*term.d_expr)[i], args, cargs, ntsToUnres); + Term ptermc = purifySygusGTerm( + Term(d_solver, (*term.d_expr)[i]), args, cargs, ntsToUnres); pchildren.push_back(ptermc); childChanged = childChanged || *ptermc.d_expr != (*term.d_expr)[i]; } @@ -2444,22 +2503,22 @@ Term Grammar::purifySygusGTerm( return term; } - Term nret; + Expr nret; if (term.d_expr->isParameterized()) { // it's an indexed operator so we should provide the op - nret = d_s->getExprManager()->mkExpr(term.d_expr->getKind(), - term.d_expr->getOperator(), - termVectorToExprs(pchildren)); + nret = d_solver->getExprManager()->mkExpr(term.d_expr->getKind(), + term.d_expr->getOperator(), + termVectorToExprs(pchildren)); } else { - nret = d_s->getExprManager()->mkExpr(term.d_expr->getKind(), - termVectorToExprs(pchildren)); + nret = d_solver->getExprManager()->mkExpr(term.d_expr->getKind(), + termVectorToExprs(pchildren)); } - return nret; + return Term(d_solver, nret); } void Grammar::addSygusConstructorVariables(DatatypeDecl& dt, Sort sort) const @@ -2538,9 +2597,9 @@ Solver::~Solver() {} template <typename T> Term Solver::mkValHelper(T t) const { - Term res = d_exprMgr->mkConst(t); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_exprMgr->mkConst(t); + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); } Term Solver::mkRealFromStrHelper(const std::string& s) const @@ -2623,7 +2682,7 @@ Term Solver::mkTermFromKind(Kind kind) const kind == PI || kind == REGEXP_EMPTY || kind == REGEXP_SIGMA, kind) << "PI or REGEXP_EMPTY or REGEXP_SIGMA"; - Term res; + Expr res; if (kind == REGEXP_EMPTY || kind == REGEXP_SIGMA) { CVC4::Kind k = extToIntKind(kind); @@ -2635,8 +2694,8 @@ Term Solver::mkTermFromKind(Kind kind) const Assert(kind == PI); res = d_exprMgr->mkNullaryOperator(d_exprMgr->realType(), CVC4::kind::PI); } - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2647,8 +2706,11 @@ Term Solver::mkTermHelper(Kind kind, const std::vector<Term>& children) const for (size_t i = 0, size = children.size(); i < size; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( - !children[i].isNull(), "parameter term", children[i], i) + !children[i].isNull(), "child term", children[i], i) << "non-null term"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == children[i].d_solver, "child term", children[i], i) + << "a child term associated to this solver object"; } std::vector<Expr> echildren = termVectorToExprs(children); @@ -2656,7 +2718,7 @@ Term Solver::mkTermHelper(Kind kind, const std::vector<Term>& children) const Assert(isDefinedIntKind(k)) << "Not a defined internal kind : " << k << " " << kind; - Term res; + Expr res; if (echildren.size() > 2) { if (kind == INTS_DIVISION || kind == XOR || kind == MINUS @@ -2701,8 +2763,8 @@ Term Solver::mkTermHelper(Kind kind, const std::vector<Term>& children) const res = d_exprMgr->mkExpr(k, echildren); } - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2713,20 +2775,31 @@ std::vector<Sort> Solver::mkDatatypeSortsInternal( CVC4_API_SOLVER_TRY_CATCH_BEGIN; std::vector<CVC4::Datatype> datatypes; - for (size_t i = 0, ndts = dtypedecls.size(); i < ndts; i++) - { - CVC4_API_ARG_CHECK_EXPECTED(dtypedecls[i].getNumConstructors() > 0, - dtypedecls[i]) + for (size_t i = 0, ndts = dtypedecls.size(); i < ndts; ++i) + { + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(this == dtypedecls[i].d_solver, + "datatype declaration", + dtypedecls[i], + i) + << "a datatype declaration associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(dtypedecls[i].getNumConstructors() > 0, + "datatype declaration", + dtypedecls[i], + i) << "a datatype declaration with at least one constructor"; datatypes.push_back(dtypedecls[i].getDatatype()); } + for (auto& sort : unresolvedSorts) + { + CVC4_API_SOLVER_CHECK_SORT(sort); + } std::set<Type> utypes = sortSetToTypes(unresolvedSorts); std::vector<CVC4::DatatypeType> dtypes = d_exprMgr->mkMutualDatatypeTypes(datatypes, utypes); std::vector<Sort> retTypes; for (CVC4::DatatypeType t : dtypes) { - retTypes.push_back(Sort(t)); + retTypes.push_back(Sort(this, t)); } return retTypes; @@ -2742,6 +2815,7 @@ std::vector<Type> Solver::sortVectorToTypes( std::vector<Type> res; for (const Sort& s : sorts) { + CVC4_API_SOLVER_CHECK_SORT(s); res.push_back(*s.d_type); } return res; @@ -2753,6 +2827,7 @@ std::vector<Expr> Solver::termVectorToExprs( std::vector<Expr> res; for (const Term& t : terms) { + CVC4_API_SOLVER_CHECK_TERM(t); res.push_back(*t.d_expr); } return res; @@ -2786,49 +2861,49 @@ void Solver::checkMkTerm(Kind kind, uint32_t nchildren) const Sort Solver::getNullSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return Type(); + return Sort(this, Type()); CVC4_API_SOLVER_TRY_CATCH_END; } Sort Solver::getBooleanSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->booleanType(); + return Sort(this, d_exprMgr->booleanType()); CVC4_API_SOLVER_TRY_CATCH_END; } Sort Solver::getIntegerSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->integerType(); + return Sort(this, d_exprMgr->integerType()); CVC4_API_SOLVER_TRY_CATCH_END; } Sort Solver::getRealSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->realType(); + return Sort(this, d_exprMgr->realType()); CVC4_API_SOLVER_TRY_CATCH_END; } Sort Solver::getRegExpSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->regExpType(); + return Sort(this, d_exprMgr->regExpType()); CVC4_API_SOLVER_TRY_CATCH_END; } Sort Solver::getStringSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->stringType(); + return Sort(this, d_exprMgr->stringType()); CVC4_API_SOLVER_TRY_CATCH_END; } Sort Solver::getRoundingmodeSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->roundingModeType(); + return Sort(this, d_exprMgr->roundingModeType()); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2841,8 +2916,11 @@ Sort Solver::mkArraySort(Sort indexSort, Sort elemSort) const << "non-null index sort"; CVC4_API_ARG_CHECK_EXPECTED(!elemSort.isNull(), elemSort) << "non-null element sort"; + CVC4_API_SOLVER_CHECK_SORT(indexSort); + CVC4_API_SOLVER_CHECK_SORT(elemSort); - return d_exprMgr->mkArrayType(*indexSort.d_type, *elemSort.d_type); + return Sort(this, + d_exprMgr->mkArrayType(*indexSort.d_type, *elemSort.d_type)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2852,7 +2930,7 @@ Sort Solver::mkBitVectorSort(uint32_t size) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(size > 0, size) << "size > 0"; - return d_exprMgr->mkBitVectorType(size); + return Sort(this, d_exprMgr->mkBitVectorType(size)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2863,7 +2941,7 @@ Sort Solver::mkFloatingPointSort(uint32_t exp, uint32_t sig) const CVC4_API_ARG_CHECK_EXPECTED(exp > 0, exp) << "exponent size > 0"; CVC4_API_ARG_CHECK_EXPECTED(sig > 0, sig) << "significand size > 0"; - return d_exprMgr->mkFloatingPointType(exp, sig); + return Sort(this, d_exprMgr->mkFloatingPointType(exp, sig)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2871,10 +2949,12 @@ Sort Solver::mkFloatingPointSort(uint32_t exp, uint32_t sig) const Sort Solver::mkDatatypeSort(DatatypeDecl dtypedecl) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_CHECK(this == dtypedecl.d_solver) + << "Given datatype declaration is not associated with this solver"; CVC4_API_ARG_CHECK_EXPECTED(dtypedecl.getNumConstructors() > 0, dtypedecl) << "a datatype declaration with at least one constructor"; - return d_exprMgr->mkDatatypeType(*dtypedecl.d_dtype); + return Sort(this, d_exprMgr->mkDatatypeType(*dtypedecl.d_dtype)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2897,13 +2977,16 @@ Sort Solver::mkFunctionSort(Sort domain, Sort codomain) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!codomain.isNull(), codomain) << "non-null codomain sort"; + CVC4_API_SOLVER_CHECK_SORT(domain); + CVC4_API_SOLVER_CHECK_SORT(codomain); CVC4_API_ARG_CHECK_EXPECTED(domain.isFirstClass(), domain) << "first-class sort as domain sort for function sort"; CVC4_API_ARG_CHECK_EXPECTED(codomain.isFirstClass(), codomain) << "first-class sort as codomain sort for function sort"; Assert(!codomain.isFunction()); /* A function sort is not first-class. */ - return d_exprMgr->mkFunctionType(*domain.d_type, *codomain.d_type); + return Sort(this, + d_exprMgr->mkFunctionType(*domain.d_type, *codomain.d_type)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2919,17 +3002,21 @@ Sort Solver::mkFunctionSort(const std::vector<Sort>& sorts, Sort codomain) const !sorts[i].isNull(), "parameter sort", sorts[i], i) << "non-null sort"; CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == sorts[i].d_solver, "parameter sort", sorts[i], i) + << "sort associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( sorts[i].isFirstClass(), "parameter sort", sorts[i], i) << "first-class sort as parameter sort for function sort"; } CVC4_API_ARG_CHECK_EXPECTED(!codomain.isNull(), codomain) << "non-null codomain sort"; + CVC4_API_SOLVER_CHECK_SORT(codomain); CVC4_API_ARG_CHECK_EXPECTED(codomain.isFirstClass(), codomain) << "first-class sort as codomain sort for function sort"; Assert(!codomain.isFunction()); /* A function sort is not first-class. */ std::vector<Type> argTypes = sortVectorToTypes(sorts); - return d_exprMgr->mkFunctionType(argTypes, *codomain.d_type); + return Sort(this, d_exprMgr->mkFunctionType(argTypes, *codomain.d_type)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2937,7 +3024,8 @@ Sort Solver::mkFunctionSort(const std::vector<Sort>& sorts, Sort codomain) const Sort Solver::mkParamSort(const std::string& symbol) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->mkSort(symbol, ExprManager::SORT_FLAG_PLACEHOLDER); + return Sort(this, + d_exprMgr->mkSort(symbol, ExprManager::SORT_FLAG_PLACEHOLDER)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2952,12 +3040,15 @@ Sort Solver::mkPredicateSort(const std::vector<Sort>& sorts) const !sorts[i].isNull(), "parameter sort", sorts[i], i) << "non-null sort"; CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == sorts[i].d_solver, "parameter sort", sorts[i], i) + << "sort associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( sorts[i].isFirstClass(), "parameter sort", sorts[i], i) << "first-class sort as parameter sort for predicate sort"; } std::vector<Type> types = sortVectorToTypes(sorts); - return d_exprMgr->mkPredicateType(types); + return Sort(this, d_exprMgr->mkPredicateType(types)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2973,11 +3064,14 @@ Sort Solver::mkRecordSort( CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( !p.second.isNull(), "parameter sort", p.second, i) << "non-null sort"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == p.second.d_solver, "parameter sort", p.second, i) + << "sort associated to this solver object"; i += 1; f.emplace_back(p.first, *p.second.d_type); } - return d_exprMgr->mkRecordType(Record(f)); + return Sort(this, d_exprMgr->mkRecordType(Record(f))); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2987,8 +3081,9 @@ Sort Solver::mkSetSort(Sort elemSort) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!elemSort.isNull(), elemSort) << "non-null element sort"; + CVC4_API_SOLVER_CHECK_SORT(elemSort); - return d_exprMgr->mkSetType(*elemSort.d_type); + return Sort(this, d_exprMgr->mkSetType(*elemSort.d_type)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -2996,7 +3091,7 @@ Sort Solver::mkSetSort(Sort elemSort) const Sort Solver::mkUninterpretedSort(const std::string& symbol) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->mkSort(symbol); + return Sort(this, d_exprMgr->mkSort(symbol)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3006,7 +3101,7 @@ Sort Solver::mkSortConstructorSort(const std::string& symbol, CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(arity > 0, arity) << "an arity > 0"; - return d_exprMgr->mkSortConstructor(symbol, arity); + return Sort(this, d_exprMgr->mkSortConstructor(symbol, arity)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3020,12 +3115,15 @@ Sort Solver::mkTupleSort(const std::vector<Sort>& sorts) const !sorts[i].isNull(), "parameter sort", sorts[i], i) << "non-null sort"; CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == sorts[i].d_solver, "parameter sort", sorts[i], i) + << "sort associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( !sorts[i].isFunctionLike(), "parameter sort", sorts[i], i) << "non-function-like sort as parameter sort for tuple sort"; } std::vector<Type> types = sortVectorToTypes(sorts); - return d_exprMgr->mkTupleType(types); + return Sort(this, d_exprMgr->mkTupleType(types)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3036,21 +3134,21 @@ Sort Solver::mkTupleSort(const std::vector<Sort>& sorts) const Term Solver::mkTrue(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->mkConst<bool>(true); + return Term(this, d_exprMgr->mkConst<bool>(true)); CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::mkFalse(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->mkConst<bool>(false); + return Term(this, d_exprMgr->mkConst<bool>(false)); CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::mkBoolean(bool val) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - return d_exprMgr->mkConst<bool>(val); + return Term(this, d_exprMgr->mkConst<bool>(val)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3058,10 +3156,10 @@ Term Solver::mkPi() const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - Term res = + Expr res = d_exprMgr->mkNullaryOperator(d_exprMgr->realType(), CVC4::kind::PI); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3143,10 +3241,10 @@ Term Solver::mkRegexpEmpty() const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - Term res = + Expr res = d_exprMgr->mkExpr(CVC4::kind::REGEXP_EMPTY, std::vector<CVC4::Expr>()); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3155,10 +3253,10 @@ Term Solver::mkRegexpSigma() const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - Term res = + Expr res = d_exprMgr->mkExpr(CVC4::kind::REGEXP_SIGMA, std::vector<CVC4::Expr>()); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3168,6 +3266,8 @@ Term Solver::mkEmptySet(Sort s) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(s.isNull() || s.isSet(), s) << "null sort or set sort"; + CVC4_API_ARG_CHECK_EXPECTED(s.isNull() || this == s.d_solver, s) + << "set sort associated to this solver object"; return mkValHelper<CVC4::EmptySet>(CVC4::EmptySet(*s.d_type)); @@ -3178,10 +3278,11 @@ Term Solver::mkSepNil(Sort sort) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!sort.isNull(), sort) << "non-null sort"; + CVC4_API_SOLVER_CHECK_SORT(sort); - Term res = d_exprMgr->mkNullaryOperator(*sort.d_type, CVC4::kind::SEP_NIL); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_exprMgr->mkNullaryOperator(*sort.d_type, CVC4::kind::SEP_NIL); + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3233,12 +3334,13 @@ Term Solver::mkUniverseSet(Sort sort) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!sort.isNull(), sort) << "non-null sort"; + CVC4_API_SOLVER_CHECK_SORT(sort); - Term res = + Expr res = d_exprMgr->mkNullaryOperator(*sort.d_type, CVC4::kind::UNIVERSE_SET); // TODO(#2771): Reenable? - // (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + // (void)res->getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3285,7 +3387,10 @@ Term Solver::mkBitVector(uint32_t size, std::string& s, uint32_t base) const Term Solver::mkConstArray(Sort sort, Term val) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_ARG_CHECK_NOT_NULL(sort); CVC4_API_ARG_CHECK_NOT_NULL(val); + CVC4_API_SOLVER_CHECK_SORT(sort); + CVC4_API_SOLVER_CHECK_TERM(val); CVC4_API_CHECK(sort.isArray()) << "Not an array sort."; CVC4_API_CHECK(sort.getArrayElementSort().isComparableTo(val.getSort())) << "Value does not match element sort."; @@ -3366,6 +3471,7 @@ Term Solver::mkUninterpretedConst(Sort sort, int32_t index) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!sort.isNull(), sort) << "non-null sort"; + CVC4_API_SOLVER_CHECK_SORT(sort); return mkValHelper<CVC4::UninterpretedConstant>( CVC4::UninterpretedConstant(*sort.d_type, index)); @@ -3381,7 +3487,7 @@ Term Solver::mkAbstractValue(const std::string& index) const CVC4::Integer idx(index, 10); CVC4_API_ARG_CHECK_EXPECTED(idx > 0, index) << "a string representing an integer > 0"; - return d_exprMgr->mkConst(CVC4::AbstractValue(idx)); + return Term(this, d_exprMgr->mkConst(CVC4::AbstractValue(idx))); // do not call getType(), for abstract values, type can not be computed // until it is substituted away CVC4_API_SOLVER_TRY_CATCH_END; @@ -3392,7 +3498,7 @@ Term Solver::mkAbstractValue(uint64_t index) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(index > 0, index) << "an integer > 0"; - return d_exprMgr->mkConst(CVC4::AbstractValue(Integer(index))); + return Term(this, d_exprMgr->mkConst(CVC4::AbstractValue(Integer(index)))); // do not call getType(), for abstract values, type can not be computed // until it is substituted away CVC4_API_SOLVER_TRY_CATCH_END; @@ -3409,6 +3515,7 @@ Term Solver::mkFloatingPoint(uint32_t exp, uint32_t sig, Term val) const CVC4_API_ARG_CHECK_EXPECTED(bw == val.getSort().getBVSize(), val) << "a bit-vector constant with bit-width '" << bw << "'"; CVC4_API_ARG_CHECK_EXPECTED(!val.isNull(), val) << "non-null term"; + CVC4_API_SOLVER_CHECK_TERM(val); CVC4_API_ARG_CHECK_EXPECTED( val.getSort().isBitVector() && val.d_expr->isConst(), val) << "bit-vector constant"; @@ -3426,11 +3533,12 @@ Term Solver::mkConst(Sort sort, const std::string& symbol) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!sort.isNull(), sort) << "non-null sort"; + CVC4_API_SOLVER_CHECK_SORT(sort); - Term res = symbol.empty() ? d_exprMgr->mkVar(*sort.d_type) + Expr res = symbol.empty() ? d_exprMgr->mkVar(*sort.d_type) : d_exprMgr->mkVar(symbol, *sort.d_type); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3442,15 +3550,25 @@ Term Solver::mkVar(Sort sort, const std::string& symbol) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!sort.isNull(), sort) << "non-null sort"; + CVC4_API_SOLVER_CHECK_SORT(sort); - Term res = symbol.empty() ? d_exprMgr->mkBoundVar(*sort.d_type) + Expr res = symbol.empty() ? d_exprMgr->mkBoundVar(*sort.d_type) : d_exprMgr->mkBoundVar(symbol, *sort.d_type); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } +/* Create datatype constructor declarations */ +/* -------------------------------------------------------------------------- */ + +DatatypeConstructorDecl Solver::mkDatatypeConstructorDecl( + const std::string& name) +{ + return DatatypeConstructorDecl(this, name); +} + /* Create datatype declarations */ /* -------------------------------------------------------------------------- */ @@ -3487,11 +3605,12 @@ Term Solver::mkTerm(Kind kind, Term child) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!child.isNull(), child) << "non-null term"; + CVC4_API_SOLVER_CHECK_TERM(child); checkMkTerm(kind, 1); - Term res = d_exprMgr->mkExpr(extToIntKind(kind), *child.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + Expr res = d_exprMgr->mkExpr(extToIntKind(kind), *child.d_expr); + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3501,12 +3620,14 @@ Term Solver::mkTerm(Kind kind, Term child1, Term child2) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(!child1.isNull(), child1) << "non-null term"; CVC4_API_ARG_CHECK_EXPECTED(!child2.isNull(), child2) << "non-null term"; + CVC4_API_SOLVER_CHECK_TERM(child1); + CVC4_API_SOLVER_CHECK_TERM(child2); checkMkTerm(kind, 2); - Term res = + Expr res = d_exprMgr->mkExpr(extToIntKind(kind), *child1.d_expr, *child2.d_expr); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3525,12 +3646,13 @@ Term Solver::mkTerm(Kind kind, const std::vector<Term>& children) const Term Solver::mkTerm(Op op) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_OP(op); Term res; if (op.isIndexedHelper()) { const CVC4::Kind int_kind = extToIntKind(op.d_kind); - res = d_exprMgr->mkExpr(int_kind, *op.d_expr); + res = Term(this, d_exprMgr->mkExpr(int_kind, *op.d_expr)); } else { @@ -3546,10 +3668,12 @@ Term Solver::mkTerm(Op op) const Term Solver::mkTerm(Op op, Term child) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_OP(op); CVC4_API_ARG_CHECK_EXPECTED(!child.isNull(), child) << "non-null term"; + CVC4_API_SOLVER_CHECK_TERM(child); const CVC4::Kind int_kind = extToIntKind(op.d_kind); - Term res; + Expr res; if (op.isIndexedHelper()) { res = d_exprMgr->mkExpr(int_kind, *op.d_expr, *child.d_expr); @@ -3559,8 +3683,8 @@ Term Solver::mkTerm(Op op, Term child) const res = d_exprMgr->mkExpr(int_kind, *child.d_expr); } - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3568,11 +3692,14 @@ Term Solver::mkTerm(Op op, Term child) const Term Solver::mkTerm(Op op, Term child1, Term child2) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_OP(op); CVC4_API_ARG_CHECK_EXPECTED(!child1.isNull(), child1) << "non-null term"; CVC4_API_ARG_CHECK_EXPECTED(!child2.isNull(), child2) << "non-null term"; + CVC4_API_SOLVER_CHECK_TERM(child1); + CVC4_API_SOLVER_CHECK_TERM(child2); const CVC4::Kind int_kind = extToIntKind(op.d_kind); - Term res; + Expr res; if (op.isIndexedHelper()) { res = @@ -3583,20 +3710,24 @@ Term Solver::mkTerm(Op op, Term child1, Term child2) const res = d_exprMgr->mkExpr(int_kind, *child1.d_expr, *child2.d_expr); } - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::mkTerm(Op op, Term child1, Term child2, Term child3) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_OP(op); CVC4_API_ARG_CHECK_EXPECTED(!child1.isNull(), child1) << "non-null term"; CVC4_API_ARG_CHECK_EXPECTED(!child2.isNull(), child2) << "non-null term"; CVC4_API_ARG_CHECK_EXPECTED(!child3.isNull(), child3) << "non-null term"; + CVC4_API_SOLVER_CHECK_TERM(child1); + CVC4_API_SOLVER_CHECK_TERM(child2); + CVC4_API_SOLVER_CHECK_TERM(child3); const CVC4::Kind int_kind = extToIntKind(op.d_kind); - Term res; + Expr res; if (op.isIndexedHelper()) { res = d_exprMgr->mkExpr( @@ -3608,8 +3739,8 @@ Term Solver::mkTerm(Op op, Term child1, Term child2, Term child3) const int_kind, *child1.d_expr, *child2.d_expr, *child3.d_expr); } - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3617,16 +3748,20 @@ Term Solver::mkTerm(Op op, Term child1, Term child2, Term child3) const Term Solver::mkTerm(Op op, const std::vector<Term>& children) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_OP(op); for (size_t i = 0, size = children.size(); i < size; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( - !children[i].isNull(), "parameter term", children[i], i) + !children[i].isNull(), "child term", children[i], i) << "non-null term"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == children[i].d_solver, "child term", children[i], i) + << "child term associated to this solver object"; } const CVC4::Kind int_kind = extToIntKind(op.d_kind); std::vector<Expr> echildren = termVectorToExprs(children); - Term res; + Expr res; if (op.isIndexedHelper()) { res = d_exprMgr->mkExpr(int_kind, *op.d_expr, echildren); @@ -3636,8 +3771,8 @@ Term Solver::mkTerm(Op op, const std::vector<Term>& children) const res = d_exprMgr->mkExpr(int_kind, echildren); } - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3651,16 +3786,22 @@ Term Solver::mkTuple(const std::vector<Sort>& sorts, std::vector<CVC4::Expr> args; for (size_t i = 0, size = sorts.size(); i < size; i++) { + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == terms[i].d_solver, "child term", terms[i], i) + << "child term associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == sorts[i].d_solver, "child sort", sorts[i], i) + << "child sort associated to this solver object"; args.push_back(*(ensureTermSort(terms[i], sorts[i])).d_expr); } Sort s = mkTupleSort(sorts); Datatype dt = s.getDatatype(); - Term res = d_exprMgr->mkExpr(extToIntKind(APPLY_CONSTRUCTOR), + Expr res = d_exprMgr->mkExpr(extToIntKind(APPLY_CONSTRUCTOR), *dt[0].getConstructorTerm().d_expr, args); - (void)res.d_expr->getType(true); /* kick off type checking */ - return res; + (void)res.getType(true); /* kick off type checking */ + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3673,7 +3814,7 @@ Op Solver::mkOp(Kind kind) const CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_CHECK(s_indexed_kinds.find(kind) == s_indexed_kinds.end()) << "Expected a kind for a non-indexed operator."; - return Op(kind); + return Op(this, kind); CVC4_API_SOLVER_TRY_CATCH_END } @@ -3687,6 +3828,7 @@ Op Solver::mkOp(Kind kind, const std::string& arg) const if (kind == RECORD_UPDATE) { res = Op( + this, kind, *mkValHelper<CVC4::RecordUpdate>(CVC4::RecordUpdate(arg)).d_expr.get()); } @@ -3697,7 +3839,8 @@ Op Solver::mkOp(Kind kind, const std::string& arg) const * as invalid. */ CVC4_API_ARG_CHECK_EXPECTED(arg != ".", arg) << "a string representing an integer, real or rational value."; - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::Divisible>(CVC4::Divisible(CVC4::Integer(arg))) .d_expr.get()); } @@ -3716,62 +3859,73 @@ Op Solver::mkOp(Kind kind, uint32_t arg) const { case DIVISIBLE: res = - Op(kind, + Op(this, + kind, *mkValHelper<CVC4::Divisible>(CVC4::Divisible(arg)).d_expr.get()); break; case BITVECTOR_REPEAT: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::BitVectorRepeat>(CVC4::BitVectorRepeat(arg)) .d_expr.get()); break; case BITVECTOR_ZERO_EXTEND: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::BitVectorZeroExtend>( CVC4::BitVectorZeroExtend(arg)) .d_expr.get()); break; case BITVECTOR_SIGN_EXTEND: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::BitVectorSignExtend>( CVC4::BitVectorSignExtend(arg)) .d_expr.get()); break; case BITVECTOR_ROTATE_LEFT: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::BitVectorRotateLeft>( CVC4::BitVectorRotateLeft(arg)) .d_expr.get()); break; case BITVECTOR_ROTATE_RIGHT: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::BitVectorRotateRight>( CVC4::BitVectorRotateRight(arg)) .d_expr.get()); break; case INT_TO_BITVECTOR: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::IntToBitVector>(CVC4::IntToBitVector(arg)) .d_expr.get()); break; case FLOATINGPOINT_TO_UBV: res = Op( + this, kind, *mkValHelper<CVC4::FloatingPointToUBV>(CVC4::FloatingPointToUBV(arg)) .d_expr.get()); break; case FLOATINGPOINT_TO_SBV: res = Op( + this, kind, *mkValHelper<CVC4::FloatingPointToSBV>(CVC4::FloatingPointToSBV(arg)) .d_expr.get()); break; case TUPLE_UPDATE: res = Op( + this, kind, *mkValHelper<CVC4::TupleUpdate>(CVC4::TupleUpdate(arg)).d_expr.get()); break; case REGEXP_REPEAT: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::RegExpRepeat>(CVC4::RegExpRepeat(arg)) .d_expr.get()); break; @@ -3794,49 +3948,57 @@ Op Solver::mkOp(Kind kind, uint32_t arg1, uint32_t arg2) const switch (kind) { case BITVECTOR_EXTRACT: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::BitVectorExtract>( CVC4::BitVectorExtract(arg1, arg2)) .d_expr.get()); break; case FLOATINGPOINT_TO_FP_IEEE_BITVECTOR: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::FloatingPointToFPIEEEBitVector>( CVC4::FloatingPointToFPIEEEBitVector(arg1, arg2)) .d_expr.get()); break; case FLOATINGPOINT_TO_FP_FLOATINGPOINT: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::FloatingPointToFPFloatingPoint>( CVC4::FloatingPointToFPFloatingPoint(arg1, arg2)) .d_expr.get()); break; case FLOATINGPOINT_TO_FP_REAL: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::FloatingPointToFPReal>( CVC4::FloatingPointToFPReal(arg1, arg2)) .d_expr.get()); break; case FLOATINGPOINT_TO_FP_SIGNED_BITVECTOR: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::FloatingPointToFPSignedBitVector>( CVC4::FloatingPointToFPSignedBitVector(arg1, arg2)) .d_expr.get()); break; case FLOATINGPOINT_TO_FP_UNSIGNED_BITVECTOR: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::FloatingPointToFPUnsignedBitVector>( CVC4::FloatingPointToFPUnsignedBitVector(arg1, arg2)) .d_expr.get()); break; case FLOATINGPOINT_TO_FP_GENERIC: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::FloatingPointToFPGeneric>( CVC4::FloatingPointToFPGeneric(arg1, arg2)) .d_expr.get()); break; case REGEXP_LOOP: - res = Op(kind, + res = Op(this, + kind, *mkValHelper<CVC4::RegExpLoop>(CVC4::RegExpLoop(arg1, arg2)) .d_expr.get()); break; @@ -3853,12 +4015,13 @@ Op Solver::mkOp(Kind kind, uint32_t arg1, uint32_t arg2) const /* Non-SMT-LIB commands */ /* -------------------------------------------------------------------------- */ -Term Solver::simplify(const Term& t) +Term Solver::simplify(const Term& term) { CVC4_API_SOLVER_TRY_CATCH_BEGIN; - CVC4_API_ARG_CHECK_NOT_NULL(t); + CVC4_API_ARG_CHECK_NOT_NULL(term); + CVC4_API_SOLVER_CHECK_TERM(term); - return d_smtEngine->simplify(*t.d_expr); + return Term(this, d_smtEngine->simplify(*term.d_expr)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3872,6 +4035,7 @@ Result Solver::checkEntailed(Term term) const << "Cannot make multiple queries unless incremental solving is enabled " "(try --incremental)"; CVC4_API_ARG_CHECK_NOT_NULL(term); + CVC4_API_SOLVER_CHECK_TERM(term); CVC4::Result r = d_smtEngine->checkEntailed(*term.d_expr); return Result(r); @@ -3889,6 +4053,7 @@ Result Solver::checkEntailed(const std::vector<Term>& terms) const "(try --incremental)"; for (const Term& term : terms) { + CVC4_API_SOLVER_CHECK_TERM(term); CVC4_API_ARG_CHECK_NOT_NULL(term); } @@ -3907,10 +4072,11 @@ Result Solver::checkEntailed(const std::vector<Term>& terms) const */ void Solver::assertFormula(Term term) const { - // CHECK: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_TERM(term); + CVC4_API_ARG_CHECK_NOT_NULL(term); d_smtEngine->assertFormula(*term.d_expr); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -3918,10 +4084,15 @@ void Solver::assertFormula(Term term) const */ Result Solver::checkSat(void) const { - // CHECK: - // if d_queryMade -> incremental enabled + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(!d_smtEngine->isQueryMade() + || CVC4::options::incrementalSolving()) + << "Cannot make multiple queries unless incremental solving is enabled " + "(try --incremental)"; CVC4::Result r = d_smtEngine->checkSat(); return Result(r); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -3929,10 +4100,16 @@ Result Solver::checkSat(void) const */ Result Solver::checkSatAssuming(Term assumption) const { - // CHECK: - // if assumptions.size() > 0: incremental enabled? + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(!d_smtEngine->isQueryMade() + || CVC4::options::incrementalSolving()) + << "Cannot make multiple queries unless incremental solving is enabled " + "(try --incremental)"; + CVC4_API_SOLVER_CHECK_TERM(assumption); CVC4::Result r = d_smtEngine->checkSat(*assumption.d_expr); return Result(r); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -3940,11 +4117,21 @@ Result Solver::checkSatAssuming(Term assumption) const */ Result Solver::checkSatAssuming(const std::vector<Term>& assumptions) const { - // CHECK: - // if assumptions.size() > 0: incremental enabled? + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(!d_smtEngine->isQueryMade() || assumptions.size() == 0 + || CVC4::options::incrementalSolving()) + << "Cannot make multiple queries unless incremental solving is enabled " + "(try --incremental)"; + for (const Term& term : assumptions) + { + CVC4_API_SOLVER_CHECK_TERM(term); + CVC4_API_ARG_CHECK_NOT_NULL(term); + } std::vector<Expr> eassumptions = termVectorToExprs(assumptions); CVC4::Result r = d_smtEngine->checkSat(eassumptions); return Result(r); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -3954,14 +4141,21 @@ Sort Solver::declareDatatype( const std::string& symbol, const std::vector<DatatypeConstructorDecl>& ctors) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(ctors.size() > 0, ctors) << "a datatype declaration with at least one constructor"; DatatypeDecl dtdecl(this, symbol); - for (const DatatypeConstructorDecl& ctor : ctors) + for (size_t i = 0, size = ctors.size(); i < size; i++) { - dtdecl.addConstructor(ctor); + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED(this == ctors[i].d_solver, + "datatype constructor declaration", + ctors[i], + i) + << "datatype constructor declaration associated to this solver object"; + dtdecl.addConstructor(ctors[i]); } - return d_exprMgr->mkDatatypeType(*dtdecl.d_dtype); + return Sort(this, d_exprMgr->mkDatatypeType(*dtdecl.d_dtype)); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -3971,14 +4165,19 @@ Term Solver::declareFun(const std::string& symbol, const std::vector<Sort>& sorts, Sort sort) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; for (size_t i = 0, size = sorts.size(); i < size; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == sorts[i].d_solver, "parameter sort", sorts[i], i) + << "parameter sort associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( sorts[i].isFirstClass(), "parameter sort", sorts[i], i) << "first-class sort as parameter sort for function sort"; } CVC4_API_ARG_CHECK_EXPECTED(sort.isFirstClass(), sort) << "first-class sort as function codomain sort"; + CVC4_API_SOLVER_CHECK_SORT(sort); Assert(!sort.isFunction()); /* A function sort is not first-class. */ Type type = *sort.d_type; if (!sorts.empty()) @@ -3986,7 +4185,8 @@ Term Solver::declareFun(const std::string& symbol, std::vector<Type> types = sortVectorToTypes(sorts); type = d_exprMgr->mkFunctionType(types, type); } - return d_exprMgr->mkVar(symbol, type); + return Term(this, d_exprMgr->mkVar(symbol, type)); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -3994,8 +4194,10 @@ Term Solver::declareFun(const std::string& symbol, */ Sort Solver::declareSort(const std::string& symbol, uint32_t arity) const { - if (arity == 0) return d_exprMgr->mkSort(symbol); - return d_exprMgr->mkSortConstructor(symbol, arity); + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + if (arity == 0) return Sort(this, d_exprMgr->mkSort(symbol)); + return Sort(this, d_exprMgr->mkSortConstructor(symbol, arity)); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4006,26 +4208,28 @@ Term Solver::defineFun(const std::string& symbol, Sort sort, Term term) const { - // CHECK: - // for bv in bound_vars: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(bv.getExprManager()) - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) - // CHECK: not recursive + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(sort.isFirstClass(), sort) << "first-class sort as codomain sort for function sort"; - // CHECK: - // for v in bound_vars: is bound var std::vector<Type> domain_types; for (size_t i = 0, size = bound_vars.size(); i < size; ++i) { + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == bound_vars[i].d_solver, "bound variable", bound_vars[i], i) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + bound_vars[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + bound_vars[i], + i) + << "a bound variable"; CVC4::Type t = bound_vars[i].d_expr->getType(); CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( t.isFirstClass(), "sort of parameter", bound_vars[i], i) << "first-class sort of parameter of defined function"; domain_types.push_back(t); } + CVC4_API_SOLVER_CHECK_SORT(sort); CVC4_API_CHECK(sort == term.getSort()) << "Invalid sort of function body '" << term << "', expected '" << sort << "'"; @@ -4037,18 +4241,15 @@ Term Solver::defineFun(const std::string& symbol, Expr fun = d_exprMgr->mkVar(symbol, type); std::vector<Expr> ebound_vars = termVectorToExprs(bound_vars); d_smtEngine->defineFunction(fun, ebound_vars, *term.d_expr); - return fun; + return Term(this, fun); + CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::defineFun(Term fun, const std::vector<Term>& bound_vars, Term term) const { - // CHECK: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(bv.getExprManager()) - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(fun.getSort().isFunction(), fun) << "function"; std::vector<Sort> domain_sorts = fun.getSort().getFunctionDomainSorts(); size_t size = bound_vars.size(); @@ -4057,6 +4258,15 @@ Term Solver::defineFun(Term fun, for (size_t i = 0; i < size; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == bound_vars[i].d_solver, "bound variable", bound_vars[i], i) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + bound_vars[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + bound_vars[i], + i) + << "a bound variable"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( domain_sorts[i] == bound_vars[i].getSort(), "sort of parameter", bound_vars[i], @@ -4064,16 +4274,15 @@ Term Solver::defineFun(Term fun, << "'" << domain_sorts[i] << "'"; } Sort codomain = fun.getSort().getFunctionCodomainSort(); + CVC4_API_SOLVER_CHECK_TERM(term); CVC4_API_CHECK(codomain == term.getSort()) << "Invalid sort of function body '" << term << "', expected '" << codomain << "'"; - // CHECK: not recursive - // CHECK: - // for v in bound_vars: is bound var std::vector<Expr> ebound_vars = termVectorToExprs(bound_vars); d_smtEngine->defineFunction(*fun.d_expr, ebound_vars, *term.d_expr); return fun; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4084,29 +4293,33 @@ Term Solver::defineFunRec(const std::string& symbol, Sort sort, Term term) const { - // CHECK: - // for bv in bound_vars: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(bv.getExprManager()) - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(sort.isFirstClass(), sort) << "first-class sort as function codomain sort"; Assert(!sort.isFunction()); /* A function sort is not first-class. */ - // CHECK: - // for v in bound_vars: is bound var std::vector<Type> domain_types; for (size_t i = 0, size = bound_vars.size(); i < size; ++i) { + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == bound_vars[i].d_solver, "bound variable", bound_vars[i], i) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + bound_vars[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + bound_vars[i], + i) + << "a bound variable"; CVC4::Type t = bound_vars[i].d_expr->getType(); CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( t.isFirstClass(), "sort of parameter", bound_vars[i], i) << "first-class sort of parameter of defined function"; domain_types.push_back(t); } + CVC4_API_SOLVER_CHECK_SORT(sort); CVC4_API_CHECK(sort == term.getSort()) << "Invalid sort of function body '" << term << "', expected '" << sort << "'"; + CVC4_API_SOLVER_CHECK_TERM(term); Type type = *sort.d_type; if (!domain_types.empty()) { @@ -4115,19 +4328,15 @@ Term Solver::defineFunRec(const std::string& symbol, Expr fun = d_exprMgr->mkVar(symbol, type); std::vector<Expr> ebound_vars = termVectorToExprs(bound_vars); d_smtEngine->defineFunctionRec(fun, ebound_vars, *term.d_expr); - return fun; + return Term(this, fun); + CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::defineFunRec(Term fun, const std::vector<Term>& bound_vars, Term term) const { - // CHECK: - // for bv in bound_vars: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(bv.getExprManager()) - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED(fun.getSort().isFunction(), fun) << "function"; std::vector<Sort> domain_sorts = fun.getSort().getFunctionDomainSorts(); size_t size = bound_vars.size(); @@ -4136,21 +4345,30 @@ Term Solver::defineFunRec(Term fun, for (size_t i = 0; i < size; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == bound_vars[i].d_solver, "bound variable", bound_vars[i], i) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + bound_vars[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + bound_vars[i], + i) + << "a bound variable"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( domain_sorts[i] == bound_vars[i].getSort(), "sort of parameter", bound_vars[i], i) << "'" << domain_sorts[i] << "'"; } + CVC4_API_SOLVER_CHECK_TERM(term); Sort codomain = fun.getSort().getFunctionCodomainSort(); CVC4_API_CHECK(codomain == term.getSort()) << "Invalid sort of function body '" << term << "', expected '" << codomain << "'"; - // CHECK: - // for v in bound_vars: is bound var std::vector<Expr> ebound_vars = termVectorToExprs(bound_vars); d_smtEngine->defineFunctionRec(*fun.d_expr, ebound_vars, *term.d_expr); return fun; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4160,15 +4378,7 @@ void Solver::defineFunsRec(const std::vector<Term>& funs, const std::vector<std::vector<Term>>& bound_vars, const std::vector<Term>& terms) const { - // CHECK: - // for f in funs: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(f.getExprManager()) - // for bv in bound_vars: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(bv.getExprManager()) - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) + CVC4_API_SOLVER_TRY_CATCH_BEGIN; size_t funs_size = funs.size(); CVC4_API_ARG_SIZE_CHECK_EXPECTED(funs_size == bound_vars.size(), bound_vars) << "'" << funs_size << "'"; @@ -4178,13 +4388,30 @@ void Solver::defineFunsRec(const std::vector<Term>& funs, const std::vector<Term>& bvars = bound_vars[j]; const Term& term = terms[j]; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == fun.d_solver, "function", fun, j) + << "function associated to this solver object"; CVC4_API_ARG_CHECK_EXPECTED(fun.getSort().isFunction(), fun) << "function"; + CVC4_API_SOLVER_CHECK_TERM(term); + std::vector<Sort> domain_sorts = fun.getSort().getFunctionDomainSorts(); size_t size = bvars.size(); CVC4_API_ARG_SIZE_CHECK_EXPECTED(size == domain_sorts.size(), bvars) << "'" << domain_sorts.size() << "'"; for (size_t i = 0; i < size; ++i) { + for (size_t k = 0, nbvars = bvars.size(); k < nbvars; ++k) + { + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == bvars[k].d_solver, "bound variable", bvars[k], k) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + bvars[k].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + bvars[k], + k) + << "a bound variable"; + } CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( domain_sorts[i] == bvars[i].getSort(), "sort of parameter", @@ -4197,8 +4424,6 @@ void Solver::defineFunsRec(const std::vector<Term>& funs, codomain == term.getSort(), "sort of function body", term, j) << "'" << codomain << "'"; } - // CHECK: - // for bv in bound_vars (for v in bv): is bound var std::vector<Expr> efuns = termVectorToExprs(funs); std::vector<std::vector<Expr>> ebound_vars; for (const auto& v : bound_vars) @@ -4207,6 +4432,7 @@ void Solver::defineFunsRec(const std::vector<Term>& funs, } std::vector<Expr> exprs = termVectorToExprs(terms); d_smtEngine->defineFunctionsRec(efuns, ebound_vars, exprs); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4222,6 +4448,7 @@ void Solver::echo(std::ostream& out, const std::string& str) const */ std::vector<Term> Solver::getAssertions(void) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; std::vector<Expr> assertions = d_smtEngine->getAssertions(); /* Can not use * return std::vector<Term>(assertions.begin(), assertions.end()); @@ -4229,9 +4456,10 @@ std::vector<Term> Solver::getAssertions(void) const std::vector<Term> res; for (const Expr& e : assertions) { - res.push_back(Term(e)); + res.push_back(Term(this, e)); } return res; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4239,15 +4467,19 @@ std::vector<Term> Solver::getAssertions(void) const */ std::vector<std::pair<Term, Term>> Solver::getAssignment(void) const { - // CHECK: produce-models set - // CHECK: result sat + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(CVC4::options::produceAssignments()) + << "Cannot get assignment unless assignment generation is enabled " + "(try --produce-assignments)"; std::vector<std::pair<Expr, Expr>> assignment = d_smtEngine->getAssignment(); std::vector<std::pair<Term, Term>> res; for (const auto& p : assignment) { - res.emplace_back(Term(p.first), Term(p.second)); + res.emplace_back(Term(this, p.first), Term(this, p.second)); } return res; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4255,8 +4487,12 @@ std::vector<std::pair<Term, Term>> Solver::getAssignment(void) const */ std::string Solver::getInfo(const std::string& flag) const { - // CHECK: flag valid? + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_CHECK(d_smtEngine->isValidGetInfoFlag(flag)) + << "Unrecognized flag for getInfo."; + return d_smtEngine->getInfo(flag).toString(); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4264,9 +4500,10 @@ std::string Solver::getInfo(const std::string& flag) const */ std::string Solver::getOption(const std::string& option) const { - // CHECK: option exists? + CVC4_API_SOLVER_TRY_CATCH_BEGIN; SExpr res = d_smtEngine->getOption(option); return res.toString(); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4274,9 +4511,18 @@ std::string Solver::getOption(const std::string& option) const */ std::vector<Term> Solver::getUnsatAssumptions(void) const { - // CHECK: incremental? - // CHECK: option produce-unsat-assumptions set? - // CHECK: last check sat/valid result is unsat/invalid + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(CVC4::options::incrementalSolving()) + << "Cannot get unsat assumptions unless incremental solving is enabled " + "(try --incremental)"; + CVC4_API_CHECK(CVC4::options::unsatAssumptions()) + << "Cannot get unsat assumptions unless explicitly enabled " + "(try --produce-unsat-assumptions)"; + CVC4_API_CHECK(d_smtEngine->getSmtMode() + == SmtEngine::SmtMode::SMT_MODE_UNSAT) + << "Cannot get unsat assumptions unless in unsat mode."; + std::vector<Expr> uassumptions = d_smtEngine->getUnsatAssumptions(); /* Can not use * return std::vector<Term>(uassumptions.begin(), uassumptions.end()); @@ -4284,9 +4530,10 @@ std::vector<Term> Solver::getUnsatAssumptions(void) const std::vector<Term> res; for (const Expr& e : uassumptions) { - res.push_back(Term(e)); + res.push_back(Term(this, e)); } return res; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4294,7 +4541,14 @@ std::vector<Term> Solver::getUnsatAssumptions(void) const */ std::vector<Term> Solver::getUnsatCore(void) const { - // CHECK: result unsat? + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(CVC4::options::unsatCores()) + << "Cannot get unsat core unless explicitly enabled " + "(try --produce-unsat-cores)"; + CVC4_API_CHECK(d_smtEngine->getSmtMode() + == SmtEngine::SmtMode::SMT_MODE_UNSAT) + << "Cannot get unsat core unless in unsat mode."; UnsatCore core = d_smtEngine->getUnsatCore(); /* Can not use * return std::vector<Term>(core.begin(), core.end()); @@ -4302,9 +4556,10 @@ std::vector<Term> Solver::getUnsatCore(void) const std::vector<Term> res; for (const Expr& e : core) { - res.push_back(Term(e)); + res.push_back(Term(this, e)); } return res; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4312,10 +4567,10 @@ std::vector<Term> Solver::getUnsatCore(void) const */ Term Solver::getValue(Term term) const { - // CHECK: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(expr.getExprManager()) - return d_smtEngine->getValue(*term.d_expr); + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_SOLVER_CHECK_TERM(term); + return Term(this, d_smtEngine->getValue(*term.d_expr)); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4323,17 +4578,25 @@ Term Solver::getValue(Term term) const */ std::vector<Term> Solver::getValue(const std::vector<Term>& terms) const { - // CHECK: - // for e in exprs: - // NodeManager::fromExprManager(d_exprMgr) - // == NodeManager::fromExprManager(e.getExprManager()) + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(CVC4::options::produceModels()) + << "Cannot get value unless model generation is enabled " + "(try --produce-models)"; + CVC4_API_CHECK(d_smtEngine->getSmtMode() + != SmtEngine::SmtMode::SMT_MODE_UNSAT) + << "Cannot get value when in unsat mode."; std::vector<Term> res; - for (const Term& t : terms) + for (size_t i = 0, n = terms.size(); i < n; ++i) { + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == terms[i].d_solver, "term", terms[i], i) + << "term associated to this solver object"; /* Can not use emplace_back here since constructor is private. */ - res.push_back(Term(d_smtEngine->getValue(*t.d_expr))); + res.push_back(Term(this, d_smtEngine->getValue(*terms[i].d_expr))); } return res; + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4358,8 +4621,16 @@ void Solver::pop(uint32_t nscopes) const void Solver::printModel(std::ostream& out) const { - // CHECK: produce-models? + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); + CVC4_API_CHECK(CVC4::options::produceModels()) + << "Cannot get value unless model generation is enabled " + "(try --produce-models)"; + CVC4_API_CHECK(d_smtEngine->getSmtMode() + != SmtEngine::SmtMode::SMT_MODE_UNSAT) + << "Cannot get value when in unsat mode."; out << *d_smtEngine->getModel(); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4383,22 +4654,11 @@ void Solver::push(uint32_t nscopes) const /** * ( reset-assertions ) */ -void Solver::resetAssertions(void) const { d_smtEngine->resetAssertions(); } - -// TODO: issue #2781 -void Solver::setLogicHelper(const std::string& logic) const +void Solver::resetAssertions(void) const { - CVC4_API_CHECK(!d_smtEngine->isFullyInited()) - << "Invalid call to 'setLogic', solver is already fully initialized"; - try - { - CVC4::LogicInfo logic_info(logic); - d_smtEngine->setLogic(logic_info); - } - catch (CVC4::IllegalArgumentException& e) - { - throw CVC4ApiException(e.getMessage()); - } + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + d_smtEngine->resetAssertions(); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4406,6 +4666,7 @@ void Solver::setLogicHelper(const std::string& logic) const */ void Solver::setInfo(const std::string& keyword, const std::string& value) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_EXPECTED( keyword == "source" || keyword == "category" || keyword == "difficulty" || keyword == "filename" || keyword == "license" || keyword == "name" @@ -4425,12 +4686,21 @@ void Solver::setInfo(const std::string& keyword, const std::string& value) const << "'sat', 'unsat' or 'unknown'"; d_smtEngine->setInfo(keyword, value); + CVC4_API_SOLVER_TRY_CATCH_END; } /** * ( set-logic <symbol> ) */ -void Solver::setLogic(const std::string& logic) const { setLogicHelper(logic); } +void Solver::setLogic(const std::string& logic) const +{ + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_CHECK(!d_smtEngine->isFullyInited()) + << "Invalid call to 'setLogic', solver is already fully initialized"; + CVC4::LogicInfo logic_info(logic); + d_smtEngine->setLogic(logic_info); + CVC4_API_SOLVER_TRY_CATCH_END; +} /** * ( set-option <option> ) @@ -4438,16 +4708,11 @@ void Solver::setLogic(const std::string& logic) const { setLogicHelper(logic); } void Solver::setOption(const std::string& option, const std::string& value) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_CHECK(!d_smtEngine->isFullyInited()) << "Invalid call to 'setOption', solver is already fully initialized"; - try - { - d_smtEngine->setOption(option, value); - } - catch (CVC4::OptionException& e) - { - throw CVC4ApiException(e.getMessage()); - } + d_smtEngine->setOption(option, value); + CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::ensureTermSort(const Term& term, const Sort& sort) const @@ -4471,7 +4736,8 @@ Term Solver::ensureTermSort(const Term& term, const Sort& sort) const // constructors. We do this cast using division with 1. This has the // advantage wrt using TO_REAL since (constant) division is always included // in the theory. - res = Term(d_exprMgr->mkExpr(extToIntKind(DIVISION), + res = Term(this, + d_exprMgr->mkExpr(extToIntKind(DIVISION), *res.d_expr, d_exprMgr->mkConst(CVC4::Rational(1)))); } @@ -4483,13 +4749,14 @@ Term Solver::mkSygusVar(Sort sort, const std::string& symbol) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_NOT_NULL(sort); + CVC4_API_SOLVER_CHECK_SORT(sort); Expr res = d_exprMgr->mkBoundVar(symbol, *sort.d_type); (void)res.getType(true); /* kick off type checking */ d_smtEngine->declareSygusVar(symbol, res, *sort.d_type); - return res; + return Term(this, res); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -4497,12 +4764,22 @@ Term Solver::mkSygusVar(Sort sort, const std::string& symbol) const Grammar Solver::mkSygusGrammar(const std::vector<Term>& boundVars, const std::vector<Term>& ntSymbols) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_SIZE_CHECK_EXPECTED(!ntSymbols.empty(), ntSymbols) << "non-empty vector"; for (size_t i = 0, n = boundVars.size(); i < n; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == boundVars[i].d_solver, "bound variable", boundVars[i], i) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + boundVars[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + boundVars[i], + i) + << "a bound variable"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( !boundVars[i].isNull(), "parameter term", boundVars[i], i) << "non-null term"; } @@ -4510,11 +4787,21 @@ Grammar Solver::mkSygusGrammar(const std::vector<Term>& boundVars, for (size_t i = 0, n = ntSymbols.size(); i < n; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == ntSymbols[i].d_solver, "term", ntSymbols[i], i) + << "term associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + ntSymbols[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + ntSymbols[i], + i) + << "a bound variable"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( !ntSymbols[i].isNull(), "parameter term", ntSymbols[i], i) << "non-null term"; } return Grammar(this, boundVars, ntSymbols); + CVC4_API_SOLVER_TRY_CATCH_END; } Term Solver::synthFun(const std::string& symbol, @@ -4535,14 +4822,16 @@ Term Solver::synthFun(const std::string& symbol, Term Solver::synthInv(const std::string& symbol, const std::vector<Term>& boundVars) const { - return synthFunHelper(symbol, boundVars, d_exprMgr->booleanType(), true); + return synthFunHelper( + symbol, boundVars, Sort(this, d_exprMgr->booleanType()), true); } Term Solver::synthInv(const std::string& symbol, const std::vector<Term>& boundVars, Grammar& g) const { - return synthFunHelper(symbol, boundVars, d_exprMgr->booleanType(), true, &g); + return synthFunHelper( + symbol, boundVars, Sort(this, d_exprMgr->booleanType()), true, &g); } Term Solver::synthFunHelper(const std::string& symbol, @@ -4561,10 +4850,20 @@ Term Solver::synthFunHelper(const std::string& symbol, for (size_t i = 0, n = boundVars.size(); i < n; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == boundVars[i].d_solver, "bound variable", boundVars[i], i) + << "bound variable associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + boundVars[i].d_expr->getKind() == CVC4::Kind::BOUND_VARIABLE, + "bound variable", + boundVars[i], + i) + << "a bound variable"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( !boundVars[i].isNull(), "parameter term", boundVars[i], i) << "non-null term"; varTypes.push_back(boundVars[i].d_expr->getType()); } + CVC4_API_SOLVER_CHECK_SORT(sort); if (g != nullptr) { @@ -4586,19 +4885,22 @@ Term Solver::synthFunHelper(const std::string& symbol, isInv, termVectorToExprs(boundVars)); - return fun; + return Term(this, fun); CVC4_API_SOLVER_TRY_CATCH_END; } void Solver::addSygusConstraint(Term term) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_NOT_NULL(term); + CVC4_API_SOLVER_CHECK_TERM(term); CVC4_API_ARG_CHECK_EXPECTED( term.d_expr->getType() == d_exprMgr->booleanType(), term) << "boolean term"; d_smtEngine->assertSygusConstraint(*term.d_expr); + CVC4_API_SOLVER_TRY_CATCH_END; } void Solver::addSygusInvConstraint(Term inv, @@ -4606,10 +4908,15 @@ void Solver::addSygusInvConstraint(Term inv, Term trans, Term post) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_NOT_NULL(inv); + CVC4_API_SOLVER_CHECK_TERM(inv); CVC4_API_ARG_CHECK_NOT_NULL(pre); + CVC4_API_SOLVER_CHECK_TERM(pre); CVC4_API_ARG_CHECK_NOT_NULL(trans); + CVC4_API_SOLVER_CHECK_TERM(trans); CVC4_API_ARG_CHECK_NOT_NULL(post); + CVC4_API_SOLVER_CHECK_TERM(post); CVC4_API_ARG_CHECK_EXPECTED(inv.d_expr->getType().isFunction(), inv) << "a function"; @@ -4644,13 +4951,21 @@ void Solver::addSygusInvConstraint(Term inv, d_smtEngine->assertSygusInvConstraint( *inv.d_expr, *pre.d_expr, *trans.d_expr, *post.d_expr); + CVC4_API_SOLVER_TRY_CATCH_END; } -Result Solver::checkSynth() const { return d_smtEngine->checkSynth(); } +Result Solver::checkSynth() const +{ + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + return d_smtEngine->checkSynth(); + CVC4_API_SOLVER_TRY_CATCH_END; +} Term Solver::getSynthSolution(Term term) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_CHECK_NOT_NULL(term); + CVC4_API_SOLVER_CHECK_TERM(term); std::map<CVC4::Expr, CVC4::Expr> map; CVC4_API_CHECK(d_smtEngine->getSynthSolutions(map)) @@ -4661,17 +4976,22 @@ Term Solver::getSynthSolution(Term term) const CVC4_API_CHECK(it != map.cend()) << "Synth solution not found for given term"; - return it->second; + return Term(this, it->second); + CVC4_API_SOLVER_TRY_CATCH_END; } std::vector<Term> Solver::getSynthSolutions( const std::vector<Term>& terms) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4_API_ARG_SIZE_CHECK_EXPECTED(!terms.empty(), terms) << "non-empty vector"; for (size_t i = 0, n = terms.size(); i < n; ++i) { CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( + this == terms[i].d_solver, "parameter term", terms[i], i) + << "parameter term associated to this solver object"; + CVC4_API_ARG_AT_INDEX_CHECK_EXPECTED( !terms[i].isNull(), "parameter term", terms[i], i) << "non-null term"; } @@ -4692,15 +5012,18 @@ std::vector<Term> Solver::getSynthSolutions( CVC4_API_CHECK(it != map.cend()) << "Synth solution not found for term at index " << i; - synthSolution.push_back(it->second); + synthSolution.push_back(Term(this, it->second)); } return synthSolution; + CVC4_API_SOLVER_TRY_CATCH_END; } void Solver::printSynthSolution(std::ostream& out) const { + CVC4_API_SOLVER_TRY_CATCH_BEGIN; d_smtEngine->printSynthSolution(out); + CVC4_API_SOLVER_TRY_CATCH_END; } /** @@ -4749,22 +5072,24 @@ std::set<Type> sortSetToTypes(const std::set<Sort>& sorts) return types; } -std::vector<Term> exprVectorToTerms(const std::vector<Expr>& exprs) +std::vector<Term> exprVectorToTerms(const Solver* slv, + const std::vector<Expr>& exprs) { std::vector<Term> terms; for (size_t i = 0, esize = exprs.size(); i < esize; i++) { - terms.push_back(Term(exprs[i])); + terms.push_back(Term(slv, exprs[i])); } return terms; } -std::vector<Sort> typeVectorToSorts(const std::vector<Type>& types) +std::vector<Sort> typeVectorToSorts(const Solver* slv, + const std::vector<Type>& types) { std::vector<Sort> sorts; for (size_t i = 0, tsize = types.size(); i < tsize; i++) { - sorts.push_back(Sort(types[i])); + sorts.push_back(Sort(slv, types[i])); } return sorts; } diff --git a/src/api/cvc4cpp.h b/src/api/cvc4cpp.h index 279453747..855ba4400 100644 --- a/src/api/cvc4cpp.h +++ b/src/api/cvc4cpp.h @@ -49,6 +49,8 @@ class Result; namespace api { +class Solver; + /* -------------------------------------------------------------------------- */ /* Exception */ /* -------------------------------------------------------------------------- */ @@ -199,10 +201,11 @@ class CVC4_PUBLIC Sort // migrated to the new API. !!! /** * Constructor. + * @param slv the associated solver object * @param t the internal type that is to be wrapped by this sort * @return the Sort */ - Sort(const CVC4::Type& t); + Sort(const Solver* slv, const CVC4::Type& t); /** * Constructor. @@ -589,6 +592,11 @@ class CVC4_PUBLIC Sort bool isNullHelper() const; /** + * The associated solver object. + */ + const Solver* d_solver; + + /** * The interal type wrapped by this sort. * This is a shared_ptr rather than a unique_ptr to avoid overhead due to * memory allocation (CVC4::Type is already ref counted, so this could be @@ -637,19 +645,21 @@ class CVC4_PUBLIC Op // migrated to the new API. !!! /** * Constructor for a single kind (non-indexed operator). + * @param slv the associated solver object * @param k the kind of this Op */ - Op(const Kind k); + Op(const Solver* slv, const Kind k); // !!! This constructor is only temporarily public until the parser is fully // migrated to the new API. !!! /** * Constructor. + * @param slv the associated solver object * @param k the kind of this Op * @param e the internal expression that is to be wrapped by this term * @return the Term */ - Op(const Kind k, const CVC4::Expr& e); + Op(const Solver* slv, const Kind k, const CVC4::Expr& e); /** * Destructor. @@ -726,6 +736,11 @@ class CVC4_PUBLIC Op */ bool isIndexedHelper() const; + /** + * The associated solver object. + */ + const Solver* d_solver; + /* The kind of this operator. */ Kind d_kind; @@ -758,10 +773,11 @@ class CVC4_PUBLIC Term // migrated to the new API. !!! /** * Constructor. + * @param slv the associated solver object * @param e the internal expression that is to be wrapped by this term * @return the Term */ - Term(const CVC4::Expr& e); + Term(const Solver* slv, const CVC4::Expr& e); /** * Constructor. @@ -955,10 +971,13 @@ class CVC4_PUBLIC Term /** * Constructor + * @param slv the associated solver object * @param e a shared pointer to the expression that we're iterating over * @param p the position of the iterator (e.g. which child it's on) */ - const_iterator(const std::shared_ptr<CVC4::Expr>& e, uint32_t p); + const_iterator(const Solver* slv, + const std::shared_ptr<CVC4::Expr>& e, + uint32_t p); /** * Copy constructor. @@ -1005,6 +1024,10 @@ class CVC4_PUBLIC Term Term operator*() const; private: + /** + * The associated solver object. + */ + const Solver* d_solver; /* The original expression to be iterated over */ std::shared_ptr<CVC4::Expr> d_orig_expr; /* Keeps track of the iteration position */ @@ -1025,6 +1048,12 @@ class CVC4_PUBLIC Term // to the new API. !!! CVC4::Expr getExpr(void) const; + protected: + /** + * The associated solver object. + */ + const Solver* d_solver; + private: /** * Helper for isNull checks. This prevents calling an API function with @@ -1138,14 +1167,13 @@ class DatatypeIterator; class CVC4_PUBLIC DatatypeConstructorDecl { friend class DatatypeDecl; + friend class Solver; public: /** - * Constructor. - * @param name the name of the datatype constructor - * @return the DatatypeConstructorDecl + * Nullary constructor for Cython. */ - DatatypeConstructorDecl(const std::string& name); + DatatypeConstructorDecl(); /** * Add datatype selector declaration. @@ -1170,6 +1198,19 @@ class CVC4_PUBLIC DatatypeConstructorDecl private: /** + * Constructor. + * @param slv the associated solver object + * @param name the name of the datatype constructor + * @return the DatatypeConstructorDecl + */ + DatatypeConstructorDecl(const Solver* slv, const std::string& name); + + /** + * The associated solver object. + */ + const Solver* d_solver; + + /** * The internal (intermediate) datatype constructor wrapped by this * datatype constructor declaration. * This is a shared_ptr rather than a unique_ptr since @@ -1190,7 +1231,7 @@ class CVC4_PUBLIC DatatypeDecl public: /** - * Nullary constructor for Cython + * Nullary constructor for Cython. */ DatatypeDecl(); @@ -1211,6 +1252,9 @@ class CVC4_PUBLIC DatatypeDecl /** Is this Datatype declaration parametric? */ bool isParametric() const; + /** + * @return true if this DatatypeDecl is a null object + */ bool isNull() const; /** @@ -1228,24 +1272,24 @@ class CVC4_PUBLIC DatatypeDecl private: /** * Constructor. - * @param s the solver that created this datatype declaration + * @param slv the associated solver object * @param name the name of the datatype * @param isCoDatatype true if a codatatype is to be constructed * @return the DatatypeDecl */ - DatatypeDecl(const Solver* s, + DatatypeDecl(const Solver* slv, const std::string& name, bool isCoDatatype = false); /** * Constructor for parameterized datatype declaration. * Create sorts parameter with Solver::mkParamSort(). - * @param s the solver that created this datatype declaration + * @param slv the associated solver object * @param name the name of the datatype * @param param the sort parameter * @param isCoDatatype true if a codatatype is to be constructed */ - DatatypeDecl(const Solver* s, + DatatypeDecl(const Solver* slv, const std::string& name, Sort param, bool isCoDatatype = false); @@ -1253,19 +1297,27 @@ class CVC4_PUBLIC DatatypeDecl /** * Constructor for parameterized datatype declaration. * Create sorts parameter with Solver::mkParamSort(). - * @param s the solver that created this datatype declaration + * @param slv the associated solver object * @param name the name of the datatype * @param params a list of sort parameters * @param isCoDatatype true if a codatatype is to be constructed */ - DatatypeDecl(const Solver* s, + DatatypeDecl(const Solver* slv, const std::string& name, const std::vector<Sort>& params, bool isCoDatatype = false); - // helper for isNull() to avoid calling API functions from other API functions + /** + * Helper for isNull checks. This prevents calling an API function with + * CVC4_API_CHECK_NOT_NULL + */ bool isNullHelper() const; + /** + * The associated solver object. + */ + const Solver* d_solver; + /* The internal (intermediate) datatype wrapped by this datatype * declaration * This is a shared_ptr rather than a unique_ptr since CVC4::Datatype is @@ -1292,10 +1344,11 @@ class CVC4_PUBLIC DatatypeSelector // migrated to the new API. !!! /** * Constructor. + * @param slv the associated solver object * @param stor the internal datatype selector to be wrapped * @return the DatatypeSelector */ - DatatypeSelector(const CVC4::DatatypeConstructorArg& stor); + DatatypeSelector(const Solver* slv, const CVC4::DatatypeConstructorArg& stor); /** * Destructor. @@ -1325,6 +1378,11 @@ class CVC4_PUBLIC DatatypeSelector private: /** + * The associated solver object. + */ + const Solver* d_solver; + + /** * The internal datatype selector wrapped by this datatype selector. * This is a shared_ptr rather than a unique_ptr since CVC4::Datatype is * not ref counted. @@ -1353,7 +1411,7 @@ class CVC4_PUBLIC DatatypeConstructor * @param ctor the internal datatype constructor to be wrapped * @return the DatatypeConstructor */ - DatatypeConstructor(const CVC4::DatatypeConstructor& ctor); + DatatypeConstructor(const Solver* slv, const CVC4::DatatypeConstructor& ctor); /** * Destructor. @@ -1466,16 +1524,27 @@ class CVC4_PUBLIC DatatypeConstructor private: /** * Constructor. + * @param slv the associated Solver object * @param ctor the internal datatype constructor to iterate over * @param true if this is a begin() iterator */ - const_iterator(const CVC4::DatatypeConstructor& ctor, bool begin); + const_iterator(const Solver* slv, + const CVC4::DatatypeConstructor& ctor, + bool begin); + + /** + * The associated solver object. + */ + const Solver* d_solver; + /* A pointer to the list of selectors of the internal datatype * constructor to iterate over. * This pointer is maintained for operators == and != only. */ const void* d_int_stors; + /* The list of datatype selector (wrappers) to iterate over. */ std::vector<DatatypeSelector> d_stors; + /* The current index of the iterator. */ size_t d_idx; }; @@ -1501,6 +1570,12 @@ class CVC4_PUBLIC DatatypeConstructor * @return the selector object for the name */ DatatypeSelector getSelectorForName(const std::string& name) const; + + /** + * The associated solver object. + */ + const Solver* d_solver; + /** * The internal datatype constructor wrapped by this datatype constructor. * This is a shared_ptr rather than a unique_ptr since CVC4::Datatype is @@ -1525,7 +1600,7 @@ class CVC4_PUBLIC Datatype * @param dtype the internal datatype to be wrapped * @return the Datatype */ - Datatype(const CVC4::Datatype& dtype); + Datatype(const Solver* slv, const CVC4::Datatype& dtype); // Nullary constructor for Cython Datatype(); @@ -1654,16 +1729,25 @@ class CVC4_PUBLIC Datatype private: /** * Constructor. + * @param slv the associated Solver object * @param dtype the internal datatype to iterate over * @param true if this is a begin() iterator */ - const_iterator(const CVC4::Datatype& dtype, bool begin); + const_iterator(const Solver* slv, const CVC4::Datatype& dtype, bool begin); + + /** + * The associated solver object. + */ + const Solver* d_solver; + /* A pointer to the list of constructors of the internal datatype * to iterate over. * This pointer is maintained for operators == and != only. */ const void* d_int_ctors; + /* The list of datatype constructor (wrappers) to iterate over. */ std::vector<DatatypeConstructor> d_ctors; + /* The current index of the iterator. */ size_t d_idx; }; @@ -1689,6 +1773,12 @@ class CVC4_PUBLIC Datatype * @return the constructor object for the name */ DatatypeConstructor getConstructorForName(const std::string& name) const; + + /** + * The associated solver object. + */ + const Solver* d_solver; + /** * The internal datatype wrapped by this datatype. * This is a shared_ptr rather than a unique_ptr since CVC4::Datatype is @@ -1793,11 +1883,11 @@ class CVC4_PUBLIC Grammar private: /** * Constructor. - * @param s the solver that created this grammar + * @param slv the solver that created this grammar * @param sygusVars the input variables to synth-fun/synth-var * @param ntSymbols the non-terminals of this grammar */ - Grammar(const Solver* s, + Grammar(const Solver* slv, const std::vector<Term>& sygusVars, const std::vector<Term>& ntSymbols); @@ -1863,7 +1953,7 @@ class CVC4_PUBLIC Grammar void addSygusConstructorVariables(DatatypeDecl& dt, Sort sort) const; /** The solver that created this grammar. */ - const Solver* d_s; + const Solver* d_solver; /** Input variables to the corresponding function/invariant to synthesize.*/ std::vector<Term> d_sygusVars; /** The non-terminal symbols of this grammar. */ @@ -2613,6 +2703,12 @@ class CVC4_PUBLIC Solver Term mkVar(Sort sort, const std::string& symbol = std::string()) const; /* .................................................................... */ + /* Create datatype constructor declarations */ + /* .................................................................... */ + + DatatypeConstructorDecl mkDatatypeConstructorDecl(const std::string& name); + + /* .................................................................... */ /* Create datatype declarations */ /* .................................................................... */ @@ -3142,9 +3238,11 @@ class CVC4_PUBLIC Solver // new API. !!! std::vector<Expr> termVectorToExprs(const std::vector<Term>& terms); std::vector<Type> sortVectorToTypes(const std::vector<Sort>& sorts); -std::vector<Term> exprVectorToTerms(const std::vector<Expr>& terms); -std::vector<Sort> typeVectorToSorts(const std::vector<Type>& sorts); std::set<Type> sortSetToTypes(const std::set<Sort>& sorts); +std::vector<Term> exprVectorToTerms(const Solver* slv, + const std::vector<Expr>& terms); +std::vector<Sort> typeVectorToSorts(const Solver* slv, + const std::vector<Type>& sorts); } // namespace api diff --git a/src/api/python/cvc4.pxd b/src/api/python/cvc4.pxd index d81d0c0bf..1e0b9893b 100644 --- a/src/api/python/cvc4.pxd +++ b/src/api/python/cvc4.pxd @@ -54,7 +54,6 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": cdef cppclass DatatypeConstructorDecl: - DatatypeConstructorDecl(const string& name) except + void addSelector(const string& name, Sort sort) except + void addSelectorSelf(const string& name) except + string toString() except + @@ -81,15 +80,22 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": T getIndices[T]() except + string toString() except + + cdef cppclass OpHashFunction: + OpHashFunction() except + + size_t operator()(const Op & o) except + + cdef cppclass Result: - # Note: don't even need constructor + Result() except+ + bint isNull() except + bint isSat() except + bint isUnsat() except + bint isSatUnknown() except + bint isEntailed() except + bint isNotEntailed() except + bint isEntailmentUnknown() except + + bint operator==(const Result& r) except + + bint operator!=(const Result& r) except + string getUnknownExplanation() except + string toString() except + @@ -156,6 +162,7 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": # default value for symbol defined in cvc4cpp.h Term mkConst(Sort sort) except + Term mkVar(Sort sort, const string& symbol) except + + DatatypeConstructorDecl mkDatatypeConstructorDecl(const string& name) except + DatatypeDecl mkDatatypeDecl(const string& name) except + DatatypeDecl mkDatatypeDecl(const string& name, bint isCoDatatype) except + DatatypeDecl mkDatatypeDecl(const string& name, Sort param) except + @@ -229,12 +236,17 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": bint isUninterpretedSortParameterized() except + string toString() except + + cdef cppclass SortHashFunction: + SortHashFunction() except + + size_t operator()(const Sort & s) except + + cdef cppclass Term: Term() bint operator==(const Term&) except + bint operator!=(const Term&) except + Kind getKind() except + Sort getSort() except + + Term substitute(const vector[Term] es, const vector[Term] & reps) except + bint hasOp() except + Op getOp() except + bint isNull() except + @@ -255,6 +267,10 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": const_iterator begin() except + const_iterator end() except + + cdef cppclass TermHashFunction: + TermHashFunction() except + + size_t operator()(const Term & t) except + + cdef extern from "api/cvc4cpp.h" namespace "CVC4::api::RoundingMode": cdef RoundingMode ROUND_NEAREST_TIES_TO_EVEN, diff --git a/src/api/python/cvc4.pxi b/src/api/python/cvc4.pxi index 1489b34a6..827c53ef4 100644 --- a/src/api/python/cvc4.pxi +++ b/src/api/python/cvc4.pxi @@ -15,11 +15,14 @@ from cvc4 cimport DatatypeSelector as c_DatatypeSelector from cvc4 cimport Result as c_Result from cvc4 cimport RoundingMode as c_RoundingMode from cvc4 cimport Op as c_Op +from cvc4 cimport OpHashFunction as c_OpHashFunction from cvc4 cimport Solver as c_Solver from cvc4 cimport Sort as c_Sort +from cvc4 cimport SortHashFunction as c_SortHashFunction from cvc4 cimport ROUND_NEAREST_TIES_TO_EVEN, ROUND_TOWARD_POSITIVE from cvc4 cimport ROUND_TOWARD_ZERO, ROUND_NEAREST_TIES_TO_AWAY from cvc4 cimport Term as c_Term +from cvc4 cimport TermHashFunction as c_TermHashFunction from cvc4kinds cimport Kind as c_Kind @@ -49,7 +52,12 @@ def expand_list_arg(num_req_args=0): ### can omit spaces between unrelated oneliners ### always use c++ default arguments #### only use default args of None at python level -#### Result class can have default because it's pure python + + +## Objects for hashing +cdef c_OpHashFunction cophash = c_OpHashFunction() +cdef c_SortHashFunction csorthash = c_SortHashFunction() +cdef c_TermHashFunction ctermhash = c_TermHashFunction() cdef class Datatype: @@ -125,12 +133,14 @@ cdef class DatatypeConstructor: cdef class DatatypeConstructorDecl: - cdef c_DatatypeConstructorDecl* cddc - def __cinit__(self, str name): - self.cddc = new c_DatatypeConstructorDecl(name.encode()) + cdef c_DatatypeConstructorDecl cddc + + def __cinit__(self): + pass def addSelector(self, str name, Sort sort): self.cddc.addSelector(name.encode(), sort.csort) + def addSelectorSelf(self, str name): self.cddc.addSelectorSelf(name.encode()) @@ -147,7 +157,7 @@ cdef class DatatypeDecl: pass def addConstructor(self, DatatypeConstructorDecl ctor): - self.cdd.addConstructor(ctor.cddc[0]) + self.cdd.addConstructor(ctor.cddc) def isParametric(self): return self.cdd.isParametric() @@ -188,6 +198,9 @@ cdef class Op: def __repr__(self): return self.cop.toString().decode() + def __hash__(self): + return cophash(self.cop) + def getKind(self): return kind(<int> self.cop.getKind()) @@ -217,54 +230,47 @@ cdef class Op: return indices -class Result: - def __init__(self, name, explanation=""): - name = name.lower() - incomplete = False - if "(incomplete)" in name: - incomplete = True - name = name.replace("(incomplete)", "").strip() - assert name in {"sat", "unsat", "valid", "invalid", "unknown"}, \ - "can't interpret result = {}".format(name) - - self._name = name - self._explanation = explanation - self._incomplete = incomplete - - def __bool__(self): - if self._name in {"sat", "valid"}: - return True - elif self._name in {"unsat", "invalid"}: - return False - elif self._name == "unknown": - raise RuntimeError("Cannot interpret 'unknown' result as a Boolean") - else: - assert False, "Unhandled result=%s"%self._name +cdef class Result: + cdef c_Result cr + def __cinit__(self): + # gets populated by solver + self.cr = c_Result() - def __eq__(self, other): - if not isinstance(other, Result): - return False + def isNull(self): + return self.cr.isNull() - return self._name == other._name + def isSat(self): + return self.cr.isSat() - def __ne__(self, other): - return not self.__eq__(other) + def isUnsat(self): + return self.cr.isUnsat() - def __str__(self): - return self._name + def isSatUnknown(self): + return self.cr.isSatUnknown() - def __repr__(self): - return self._name + def isEntailed(self): + return self.cr.isEntailed() + + def isNotEntailed(self): + return self.cr.isNotEntailed() + + def isEntailmentUnknown(self): + return self.cr.isEntailmentUnknown() - def isUnknown(self): - return self._name == "unknown" + def __eq__(self, Result other): + return self.cr == other.cr - def isIncomplete(self): - return self._incomplete + def __ne__(self, Result other): + return self.cr != other.cr - @property - def explanation(self): - return self._explanation + def getUnknownExplanation(self): + return self.cr.getUnknownExplanation().decode() + + def __str__(self): + return self.cr.toString().decode() + + def __repr__(self): + return self.cr.toString().decode() cdef class RoundingMode: @@ -663,6 +669,11 @@ cdef class Solver: (<str?> symbol).encode()) return term + def mkDatatypeConstructorDecl(self, str name): + cdef DatatypeConstructorDecl ddc = DatatypeConstructorDecl() + ddc.cddc = self.csolver.mkDatatypeConstructorDecl(name.encode()) + return ddc + def mkDatatypeDecl(self, str name, sorts_or_bool=None, isCoDatatype=None): cdef DatatypeDecl dd = DatatypeDecl() cdef vector[c_Sort] v @@ -716,12 +727,9 @@ cdef class Solver: self.csolver.assertFormula(term.cterm) def checkSat(self): - cdef c_Result r = self.csolver.checkSat() - name = r.toString().decode() - explanation = "" - if r.isSatUnknown(): - explanation = r.getUnknownExplanation().decode() - return Result(name, explanation) + cdef Result r = Result() + r.cr = self.csolver.checkSat() + return r @expand_list_arg(num_req_args=0) def checkSatAssuming(self, *assumptions): @@ -732,17 +740,13 @@ cdef class Solver: where assumptions can also be comma-separated arguments of type (boolean) Term ''' - cdef c_Result r + cdef Result r = Result() # used if assumptions is a list of terms cdef vector[c_Term] v for a in assumptions: v.push_back((<Term?> a).cterm) - r = self.csolver.checkSatAssuming(<const vector[c_Term]&> v) - name = r.toString().decode() - explanation = "" - if r.isSatUnknown(): - explanation = r.getUnknownExplanation().decode() - return Result(name, explanation) + r.cr = self.csolver.checkSatAssuming(<const vector[c_Term]&> v) + return r @expand_list_arg(num_req_args=0) def checkEntailed(self, *assumptions): @@ -753,17 +757,13 @@ cdef class Solver: where assumptions can also be comma-separated arguments of type (boolean) Term ''' - cdef c_Result r + cdef Result r = Result() # used if assumptions is a list of terms cdef vector[c_Term] v for a in assumptions: v.push_back((<Term?> a).cterm) - r = self.csolver.checkEntailed(<const vector[c_Term]&> v) - name = r.toString().decode() - explanation = "" - if r.isEntailmentUnknown(): - explanation = r.getUnknownExplanation().decode() - return Result(name, explanation) + r.cr = self.csolver.checkEntailed(<const vector[c_Term]&> v) + return r @expand_list_arg(num_req_args=1) def declareDatatype(self, str symbol, *ctors): @@ -778,7 +778,7 @@ cdef class Solver: cdef vector[c_DatatypeConstructorDecl] v for c in ctors: - v.push_back((<DatatypeConstructorDecl?> c).cddc[0]) + v.push_back((<DatatypeConstructorDecl?> c).cddc) sort.csort = self.csolver.declareDatatype(symbol.encode(), v) return sort @@ -953,6 +953,9 @@ cdef class Sort: def __repr__(self): return self.csort.toString().decode() + def __hash__(self): + return csorthash(self.csort) + def isBoolean(self): return self.csort.isBoolean() @@ -1054,6 +1057,9 @@ cdef class Term: term.cterm = ci yield term + def __hash__(self): + return ctermhash(self.cterm) + def getKind(self): return kind(<int> self.cterm.getKind()) @@ -1062,6 +1068,23 @@ cdef class Term: sort.csort = self.cterm.getSort() return sort + def substitute(self, list es, list replacements): + cdef vector[c_Term] ces + cdef vector[c_Term] creplacements + cdef Term term = Term() + + if len(es) != len(replacements): + raise RuntimeError("Expecting list inputs to substitute to " + "have the same length but got: " + "{} and {}".format(len(es), len(replacements))) + + for e, r in zip(es, replacements): + ces.push_back((<Term?> e).cterm) + creplacements.push_back((<Term?> r).cterm) + + term.cterm = self.cterm.substitute(ces, creplacements) + return term + def hasOp(self): return self.cterm.hasOp() diff --git a/src/bindings/java/CMakeLists.txt b/src/bindings/java/CMakeLists.txt index c5abf9b27..4e1b96af9 100644 --- a/src/bindings/java/CMakeLists.txt +++ b/src/bindings/java/CMakeLists.txt @@ -65,6 +65,7 @@ set(gen_java_files ${CMAKE_CURRENT_BINARY_DIR}/ExprHashFunction.java ${CMAKE_CURRENT_BINARY_DIR}/ExprManager.java ${CMAKE_CURRENT_BINARY_DIR}/ExprManagerMapCollection.java + ${CMAKE_CURRENT_BINARY_DIR}/ExprSequence.java ${CMAKE_CURRENT_BINARY_DIR}/FloatingPoint.java ${CMAKE_CURRENT_BINARY_DIR}/FloatingPointConvertSort.java ${CMAKE_CURRENT_BINARY_DIR}/FloatingPointSize.java @@ -137,6 +138,7 @@ set(gen_java_files ${CMAKE_CURRENT_BINARY_DIR}/SWIGTYPE_p_std__vectorT_std__pairT_CVC4__Expr_CVC4__Expr_t_t.java ${CMAKE_CURRENT_BINARY_DIR}/SWIGTYPE_p_std__vectorT_std__vectorT_std__string_t_t.java ${CMAKE_CURRENT_BINARY_DIR}/SelectorType.java + ${CMAKE_CURRENT_BINARY_DIR}/SequenceType.java ${CMAKE_CURRENT_BINARY_DIR}/SetType.java ${CMAKE_CURRENT_BINARY_DIR}/SmtEngine.java ${CMAKE_CURRENT_BINARY_DIR}/SortConstructorType.java diff --git a/src/cvc4.i b/src/cvc4.i index 01fd088a8..6b3598a2f 100644 --- a/src/cvc4.i +++ b/src/cvc4.i @@ -286,6 +286,7 @@ std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; %include "expr/array_store_all.i" %include "expr/ascription_type.i" %include "expr/emptyset.i" +%include "expr/expr_sequence.i" %include "expr/datatype.i" %include "expr/record.i" %include "proof/unsat_core.i" diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt index f2a4377d0..3d41b7a72 100644 --- a/src/expr/CMakeLists.txt +++ b/src/expr/CMakeLists.txt @@ -12,6 +12,8 @@ libcvc4_add_sources( expr_iomanip.cpp expr_iomanip.h expr_manager_scope.h + expr_sequence.cpp + expr_sequence.h kind_map.h match_trie.cpp match_trie.h @@ -32,6 +34,8 @@ libcvc4_add_sources( node_traversal.h node_value.cpp node_value.h + sequence.cpp + sequence.h node_visitor.h proof.cpp proof.h @@ -47,6 +51,8 @@ libcvc4_add_sources( proof_rule.h proof_skolem_cache.cpp proof_skolem_cache.h + proof_step_buffer.cpp + proof_step_buffer.h symbol_table.cpp symbol_table.h term_canonize.cpp diff --git a/src/expr/expr.i b/src/expr/expr.i index 14ccf213c..14228d7c5 100644 --- a/src/expr/expr.i +++ b/src/expr/expr.i @@ -146,6 +146,7 @@ namespace CVC4 { %template(getConstBoolean) CVC4::Expr::getConst<bool>; %template(getConstDatatypeIndexConstant) CVC4::Expr::getConst<CVC4::DatatypeIndexConstant>; %template(getConstEmptySet) CVC4::Expr::getConst<CVC4::EmptySet>; +%template(getConstExprSequence) CVC4::Expr::getConst<CVC4::ExprSequence>; %template(getConstFloatingPoint) CVC4::Expr::getConst<CVC4::FloatingPoint>; %template(getConstKind) CVC4::Expr::getConst<CVC4::kind::Kind_t>; %template(getConstRational) CVC4::Expr::getConst<CVC4::Rational>; diff --git a/src/expr/expr_manager.i b/src/expr/expr_manager.i index f8251e752..5a5e7a9d4 100644 --- a/src/expr/expr_manager.i +++ b/src/expr/expr_manager.i @@ -57,6 +57,7 @@ %template(mkConst) CVC4::ExprManager::mkConst<CVC4::Rational>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::BitVector>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::EmptySet>; +%template(mkConst) CVC4::ExprManager::mkConst<CVC4::ExprSequence>; %template(mkConst) CVC4::ExprManager::mkConst<CVC4::String>; #ifdef SWIGPYTHON /* The python bindings cannot differentiate between bool and other basic diff --git a/src/expr/expr_sequence.cpp b/src/expr/expr_sequence.cpp new file mode 100644 index 000000000..4f761c8f7 --- /dev/null +++ b/src/expr/expr_sequence.cpp @@ -0,0 +1,98 @@ +/********************* */ +/*! \file expr_sequence.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of the sequence data type. + **/ + +#include "expr/expr_sequence.h" + +#include "expr/expr.h" +#include "expr/node.h" +#include "expr/sequence.h" +#include "expr/type.h" +#include "expr/type_node.h" + +namespace CVC4 { + +ExprSequence::ExprSequence(const Type& t, const std::vector<Expr>& seq) +{ + d_type.reset(new Type(t)); + std::vector<Node> nseq; + for (const Expr& e : seq) + { + nseq.push_back(Node::fromExpr(e)); + } + d_sequence.reset(new Sequence(TypeNode::fromType(t), nseq)); +} +ExprSequence::~ExprSequence() {} + +ExprSequence::ExprSequence(const ExprSequence& other) + : d_type(new Type(other.getType())), + d_sequence(new Sequence(other.getSequence())) +{ +} + +ExprSequence& ExprSequence::operator=(const ExprSequence& other) +{ + (*d_type) = other.getType(); + (*d_sequence) = other.getSequence(); + return *this; +} + +const Type& ExprSequence::getType() const { return *d_type; } + +const Sequence& ExprSequence::getSequence() const { return *d_sequence; } + +bool ExprSequence::operator==(const ExprSequence& es) const +{ + return getType() == es.getType() && getSequence() == es.getSequence(); +} + +bool ExprSequence::operator!=(const ExprSequence& es) const +{ + return !(*this == es); +} + +bool ExprSequence::operator<(const ExprSequence& es) const +{ + return (getType() < es.getType()) + || (getType() == es.getType() && getSequence() < es.getSequence()); +} + +bool ExprSequence::operator<=(const ExprSequence& es) const +{ + return (getType() < es.getType()) + || (getType() == es.getType() && getSequence() <= es.getSequence()); +} + +bool ExprSequence::operator>(const ExprSequence& es) const +{ + return !(*this <= es); +} + +bool ExprSequence::operator>=(const ExprSequence& es) const +{ + return !(*this < es); +} + +std::ostream& operator<<(std::ostream& os, const ExprSequence& s) +{ + return os << "__expr_sequence__(" << s.getType() << ", " << s.getSequence() + << ")"; +} + +size_t ExprSequenceHashFunction::operator()(const ExprSequence& es) const +{ + uint64_t hash = fnv1a::fnv1a_64(TypeHashFunction()(es.getType())); + return static_cast<size_t>(SequenceHashFunction()(es.getSequence()), hash); +} + +} // namespace CVC4 diff --git a/src/expr/expr_sequence.h b/src/expr/expr_sequence.h new file mode 100644 index 000000000..9515a9244 --- /dev/null +++ b/src/expr/expr_sequence.h @@ -0,0 +1,76 @@ +/********************* */ +/*! \file expr_sequence.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief The sequence data type. + **/ + +#include "cvc4_public.h" + +#ifndef CVC4__EXPR__EXPR_SEQUENCE_H +#define CVC4__EXPR__EXPR_SEQUENCE_H + +#include <iosfwd> +#include <memory> +#include <vector> + +namespace CVC4 { + +// messy; Expr needs ExprSequence (because it's the payload of a +// CONSTANT-kinded expression), and ExprSequence needs Expr. +class Type; +class Expr; +class Sequence; + +/** The CVC4 sequence class + * + * This data structure is the domain of values for the sequence type. + */ +class CVC4_PUBLIC ExprSequence +{ + public: + /** constructors for ExprSequence + * + * Internally, a CVC4::ExprSequence is represented by a vector of Nodes + * (d_seq), where each Node in this vector must be a constant. + */ + ExprSequence(const Type& type, const std::vector<Expr>& seq); + ~ExprSequence(); + + ExprSequence(const ExprSequence& other); + ExprSequence& operator=(const ExprSequence& other); + + bool operator==(const ExprSequence& es) const; + bool operator!=(const ExprSequence& es) const; + bool operator<(const ExprSequence& es) const; + bool operator<=(const ExprSequence& es) const; + bool operator>(const ExprSequence& es) const; + bool operator>=(const ExprSequence& es) const; + + const Type& getType() const; + const Sequence& getSequence() const; + + private: + /** The element type of the sequence */ + std::unique_ptr<Type> d_type; + /** The data of the sequence */ + std::unique_ptr<Sequence> d_sequence; +}; /* class ExprSequence */ + +struct CVC4_PUBLIC ExprSequenceHashFunction +{ + size_t operator()(const ::CVC4::ExprSequence& s) const; +}; /* struct ExprSequenceHashFunction */ + +std::ostream& operator<<(std::ostream& os, const ExprSequence& s) CVC4_PUBLIC; + +} // namespace CVC4 + +#endif /* CVC4__EXPR__SEQUENCE_H */ diff --git a/src/expr/expr_sequence.i b/src/expr/expr_sequence.i new file mode 100644 index 000000000..42e130466 --- /dev/null +++ b/src/expr/expr_sequence.i @@ -0,0 +1,18 @@ +%{ +#include "expr/expr_sequence.h" +%} + +%rename(equals) CVC4::ExprSequence::operator==(const ExprSequence&) const; +%ignore CVC4::ExprSequence::operator!=(const ExprSequence&) const; +%ignore CVC4::ExprSequence::getSequence() const; + +%rename(less) CVC4::ExprSequence::operator<(const ExprSequence&) const; +%rename(lessEqual) CVC4::ExprSequence::operator<=(const ExprSequence&) const; +%rename(greater) CVC4::ExprSequence::operator>(const ExprSequence&) const; +%rename(greaterEqual) CVC4::ExprSequence::operator>=(const ExprSequence&) const; + +%rename(apply) CVC4::ExprSequenceHashFunction::operator()(const ExprSequence&) const; + +%ignore CVC4::operator<<(std::ostream& out, const ExprSequence& es); + +%include "expr/expr_sequence.h" diff --git a/src/expr/node_manager.cpp b/src/expr/node_manager.cpp index feec9b782..427afd5af 100644 --- a/src/expr/node_manager.cpp +++ b/src/expr/node_manager.cpp @@ -495,6 +495,17 @@ Node NodeManager::mkSkolem(const std::string& prefix, const TypeNode& type, cons return n; } +TypeNode NodeManager::mkSequenceType(TypeNode elementType) +{ + CheckArgument( + !elementType.isNull(), elementType, "unexpected NULL element type"); + CheckArgument(elementType.isFirstClass(), + elementType, + "cannot store types that are not first-class in sequences. Try " + "option --uf-ho."); + return mkTypeNode(kind::SEQUENCE_TYPE, elementType); +} + TypeNode NodeManager::mkConstructorType(const DatatypeConstructor& constructor, TypeNode range) { vector<TypeNode> sorts; @@ -854,4 +865,26 @@ void NodeManager::debugHook(int debugFlag){ // For debugging purposes only, DO NOT CHECK IN ANY CODE! } +Kind NodeManager::getKindForFunction(TNode fun) +{ + TypeNode tn = fun.getType(); + if (tn.isFunction()) + { + return kind::APPLY_UF; + } + else if (tn.isConstructor()) + { + return kind::APPLY_CONSTRUCTOR; + } + else if (tn.isSelector()) + { + return kind::APPLY_SELECTOR; + } + else if (tn.isTester()) + { + return kind::APPLY_TESTER; + } + return kind::UNDEFINED_KIND; +} + }/* CVC4 namespace */ diff --git a/src/expr/node_manager.h b/src/expr/node_manager.h index aea49d979..1fab328e9 100644 --- a/src/expr/node_manager.h +++ b/src/expr/node_manager.h @@ -444,6 +444,20 @@ public: /** Get a Kind from an operator expression */ static inline Kind operatorToKind(TNode n); + /** Get corresponding application kind for function + * + * Different functional nodes are applied differently, according to their + * type. For example, uninterpreted functions (of FUNCTION_TYPE) are applied + * via APPLY_UF, while constructors (of CONSTRUCTOR_TYPE) via + * APPLY_CONSTRUCTOR. This method provides the correct application according + * to which functional type fun has. + * + * @param fun The functional node + * @return the correct application kind for fun. If fun's type is not function + * like (see TypeNode::isFunctionLike), then UNDEFINED_KIND is returned. + */ + static Kind getKindForFunction(TNode fun); + // general expression-builders /** Create a node with one child. */ @@ -875,9 +889,12 @@ public: /** Make the type of arrays with the given parameterization */ inline TypeNode mkArrayType(TypeNode indexType, TypeNode constituentType); - /** Make the type of arrays with the given parameterization */ + /** Make the type of set with the given parameterization */ inline TypeNode mkSetType(TypeNode elementType); + /** Make the type of sequences with the given parameterization */ + TypeNode mkSequenceType(TypeNode elementType); + /** Make a type representing a constructor with the given parameterization */ TypeNode mkConstructorType(const DatatypeConstructor& constructor, TypeNode range); /** diff --git a/src/expr/proof_rule.cpp b/src/expr/proof_rule.cpp index e555f5691..595e1d5f7 100644 --- a/src/expr/proof_rule.cpp +++ b/src/expr/proof_rule.cpp @@ -25,7 +25,66 @@ const char* toString(PfRule id) //================================================= Core rules case PfRule::ASSUME: return "ASSUME"; case PfRule::SCOPE: return "SCOPE"; - + case PfRule::SUBS: return "SUBS"; + case PfRule::REWRITE: return "REWRITE"; + case PfRule::MACRO_SR_EQ_INTRO: return "MACRO_SR_EQ_INTRO"; + case PfRule::MACRO_SR_PRED_INTRO: return "MACRO_SR_PRED_INTRO"; + case PfRule::MACRO_SR_PRED_ELIM: return "MACRO_SR_PRED_ELIM"; + case PfRule::MACRO_SR_PRED_TRANSFORM: return "MACRO_SR_PRED_TRANSFORM"; + //================================================= Equality rules + case PfRule::REFL: return "REFL"; + case PfRule::SYMM: return "SYMM"; + case PfRule::TRANS: return "TRANS"; + case PfRule::CONG: return "CONG"; + case PfRule::TRUE_INTRO: return "TRUE_INTRO"; + case PfRule::TRUE_ELIM: return "TRUE_ELIM"; + case PfRule::FALSE_INTRO: return "FALSE_INTRO"; + case PfRule::FALSE_ELIM: return "FALSE_ELIM"; + //================================================= Boolean rules + case PfRule::SPLIT: return "SPLIT"; + case PfRule::AND_ELIM: return "AND_ELIM"; + case PfRule::AND_INTRO: return "AND_INTRO"; + case PfRule::NOT_OR_ELIM: return "NOT_OR_ELIM"; + case PfRule::IMPLIES_ELIM: return "IMPLIES_ELIM"; + case PfRule::NOT_IMPLIES_ELIM1: return "NOT_IMPLIES_ELIM1"; + case PfRule::NOT_IMPLIES_ELIM2: return "NOT_IMPLIES_ELIM2"; + case PfRule::EQUIV_ELIM1: return "EQUIV_ELIM1"; + case PfRule::EQUIV_ELIM2: return "EQUIV_ELIM2"; + case PfRule::NOT_EQUIV_ELIM1: return "NOT_EQUIV_ELIM1"; + case PfRule::NOT_EQUIV_ELIM2: return "NOT_EQUIV_ELIM2"; + case PfRule::XOR_ELIM1: return "XOR_ELIM1"; + case PfRule::XOR_ELIM2: return "XOR_ELIM2"; + case PfRule::NOT_XOR_ELIM1: return "NOT_XOR_ELIM1"; + case PfRule::NOT_XOR_ELIM2: return "NOT_XOR_ELIM2"; + case PfRule::ITE_ELIM1: return "ITE_ELIM1"; + case PfRule::ITE_ELIM2: return "ITE_ELIM2"; + case PfRule::NOT_ITE_ELIM1: return "NOT_ITE_ELIM1"; + case PfRule::NOT_ITE_ELIM2: return "NOT_ITE_ELIM2"; + case PfRule::CONTRA: return "CONTRA"; + //================================================= De Morgan rules + case PfRule::NOT_AND: return "NOT_AND"; + //================================================= CNF rules + case PfRule::CNF_AND_POS: return "CNF_AND_POS"; + case PfRule::CNF_AND_NEG: return "CNF_AND_NEG"; + case PfRule::CNF_OR_POS: return "CNF_OR_POS"; + case PfRule::CNF_OR_NEG: return "CNF_OR_NEG"; + case PfRule::CNF_IMPLIES_POS: return "CNF_IMPLIES_POS"; + case PfRule::CNF_IMPLIES_NEG1: return "CNF_IMPLIES_NEG1"; + case PfRule::CNF_IMPLIES_NEG2: return "CNF_IMPLIES_NEG2"; + case PfRule::CNF_EQUIV_POS1: return "CNF_EQUIV_POS1"; + case PfRule::CNF_EQUIV_POS2: return "CNF_EQUIV_POS2"; + case PfRule::CNF_EQUIV_NEG1: return "CNF_EQUIV_NEG1"; + case PfRule::CNF_EQUIV_NEG2: return "CNF_EQUIV_NEG2"; + case PfRule::CNF_XOR_POS1: return "CNF_XOR_POS1"; + case PfRule::CNF_XOR_POS2: return "CNF_XOR_POS2"; + case PfRule::CNF_XOR_NEG1: return "CNF_XOR_NEG1"; + case PfRule::CNF_XOR_NEG2: return "CNF_XOR_NEG2"; + case PfRule::CNF_ITE_POS1: return "CNF_ITE_POS1"; + case PfRule::CNF_ITE_POS2: return "CNF_ITE_POS2"; + case PfRule::CNF_ITE_POS3: return "CNF_ITE_POS3"; + case PfRule::CNF_ITE_NEG1: return "CNF_ITE_NEG1"; + case PfRule::CNF_ITE_NEG2: return "CNF_ITE_NEG2"; + case PfRule::CNF_ITE_NEG3: return "CNF_ITE_NEG3"; //================================================= Unknown rule case PfRule::UNKNOWN: return "UNKNOWN"; default: return "?"; diff --git a/src/expr/proof_rule.h b/src/expr/proof_rule.h index 0d03bb347..6acccfffd 100644 --- a/src/expr/proof_rule.h +++ b/src/expr/proof_rule.h @@ -71,6 +71,408 @@ enum class PfRule : uint32_t // has the conclusion (=> F F) and has no free assumptions. More generally, a // proof with no free assumptions always concludes a valid formula. SCOPE, + //================================================= Equality rules + // ======== Reflexive + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= t t) + REFL, + // ======== Symmetric + // Children: (P:(= t1 t2)) or (P:(not (= t1 t2))) + // Arguments: none + // ----------------------- + // Conclusion: (= t2 t1) or (not (= t2 t1)) + SYMM, + // ======== Transitivity + // Children: (P1:(= t1 t2), ..., Pn:(= t{n-1} tn)) + // Arguments: none + // ----------------------- + // Conclusion: (= t1 tn) + TRANS, + // ======== Congruence (subsumed by Substitute?) + // Children: (P1:(= t1 s1), ..., Pn:(= tn sn)) + // Arguments: (f) + // --------------------------------------------- + // Conclusion: (= (f t1 ... tn) (f s1 ... sn)) + CONG, + // ======== True intro + // Children: (P:F) + // Arguments: none + // ---------------------------------------- + // Conclusion: (= F true) + TRUE_INTRO, + // ======== True elim + // Children: (P:(= F true) + // Arguments: none + // ---------------------------------------- + // Conclusion: F + TRUE_ELIM, + // ======== False intro + // Children: (P:(not F)) + // Arguments: none + // ---------------------------------------- + // Conclusion: (= F false) + FALSE_INTRO, + // ======== False elim + // Children: (P:(= F false) + // Arguments: none + // ---------------------------------------- + // Conclusion: (not F) + FALSE_ELIM, + + //================================================= Boolean rules + // ======== Split + // Children: none + // Arguments: (F) + // --------------------- + // Conclusion: (or F (not F)) + SPLIT, + // ======== And elimination + // Children: (P:(and F1 ... Fn)) + // Arguments: (i) + // --------------------- + // Conclusion: (Fi) + AND_ELIM, + // ======== And introduction + // Children: (P1:F1 ... Pn:Fn)) + // Arguments: () + // --------------------- + // Conclusion: (and P1 ... Pn) + AND_INTRO, + // ======== Not Or elimination + // Children: (P:(not (or F1 ... Fn))) + // Arguments: (i) + // --------------------- + // Conclusion: (not Fi) + NOT_OR_ELIM, + // ======== Implication elimination + // Children: (P:(=> F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) F2) + IMPLIES_ELIM, + // ======== Not Implication elimination version 1 + // Children: (P:(not (=> F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (F1) + NOT_IMPLIES_ELIM1, + // ======== Not Implication elimination version 2 + // Children: (P:(not (=> F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (not F2) + NOT_IMPLIES_ELIM2, + // ======== Equivalence elimination version 1 + // Children: (P:(= F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) F2) + EQUIV_ELIM1, + // ======== Equivalence elimination version 2 + // Children: (P:(= F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or F1 (not F2)) + EQUIV_ELIM2, + // ======== Not Equivalence elimination version 1 + // Children: (P:(not (= F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or F1 F2) + NOT_EQUIV_ELIM1, + // ======== Not Equivalence elimination version 2 + // Children: (P:(not (= F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) (not F2)) + NOT_EQUIV_ELIM2, + // ======== XOR elimination version 1 + // Children: (P:(xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or F1 F2) + XOR_ELIM1, + // ======== XOR elimination version 2 + // Children: (P:(xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) (not F2)) + XOR_ELIM2, + // ======== Not XOR elimination version 1 + // Children: (P:(not (xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or F1 (not F2)) + NOT_XOR_ELIM1, + // ======== Not XOR elimination version 2 + // Children: (P:(not (xor F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) F2) + NOT_XOR_ELIM2, + // ======== ITE elimination version 1 + // Children: (P:(ite C F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or (not C) F1) + ITE_ELIM1, + // ======== ITE elimination version 2 + // Children: (P:(ite C F1 F2)) + // Arguments: () + // --------------------- + // Conclusion: (or C F2) + ITE_ELIM2, + // ======== Not ITE elimination version 1 + // Children: (P:(not (ite C F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or (not C) (not F1)) + NOT_ITE_ELIM1, + // ======== Not ITE elimination version 1 + // Children: (P:(not (ite C F1 F2))) + // Arguments: () + // --------------------- + // Conclusion: (or C (not F2)) + NOT_ITE_ELIM2, + // ======== Not ITE elimination version 1 + // Children: (P1:P P2:(not P)) + // Arguments: () + // --------------------- + // Conclusion: (false) + CONTRA, + + //================================================= De Morgan rules + // ======== Not And + // Children: (P:(not (and F1 ... Fn)) + // Arguments: () + // --------------------- + // Conclusion: (or (not F1) ... (not Fn)) + NOT_AND, + //================================================= CNF rules + // ======== CNF And Pos + // Children: () + // Arguments: ((and F1 ... Fn), i) + // --------------------- + // Conclusion: (or (not (and F1 ... Fn)) Fi) + CNF_AND_POS, + // ======== CNF And Neg + // Children: () + // Arguments: ((and F1 ... Fn)) + // --------------------- + // Conclusion: (or (and F1 ... Fn) (not F1) ... (not Fn)) + CNF_AND_NEG, + // ======== CNF Or Pos + // Children: () + // Arguments: ((or F1 ... Fn)) + // --------------------- + // Conclusion: (or (not (or F1 ... Fn)) F1 ... Fn) + CNF_OR_POS, + // ======== CNF Or Neg + // Children: () + // Arguments: ((or F1 ... Fn), i) + // --------------------- + // Conclusion: (or (or F1 ... Fn) (not Fi)) + CNF_OR_NEG, + // ======== CNF Implies Pos + // Children: () + // Arguments: ((implies F1 F2)) + // --------------------- + // Conclusion: (or (not (implies F1 F2)) (not F1) F2) + CNF_IMPLIES_POS, + // ======== CNF Implies Neg version 1 + // Children: () + // Arguments: ((implies F1 F2)) + // --------------------- + // Conclusion: (or (implies F1 F2) F1) + CNF_IMPLIES_NEG1, + // ======== CNF Implies Neg version 2 + // Children: () + // Arguments: ((implies F1 F2)) + // --------------------- + // Conclusion: (or (implies F1 F2) (not F2)) + CNF_IMPLIES_NEG2, + // ======== CNF Equiv Pos version 1 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (not (= F1 F2)) (not F1) F2) + CNF_EQUIV_POS1, + // ======== CNF Equiv Pos version 2 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (not (= F1 F2)) F1 (not F2)) + CNF_EQUIV_POS2, + // ======== CNF Equiv Neg version 1 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (= F1 F2) F1 F2) + CNF_EQUIV_NEG1, + // ======== CNF Equiv Neg version 2 + // Children: () + // Arguments: ((= F1 F2)) + // --------------------- + // Conclusion: (or (= F1 F2) (not F1) (not F2)) + CNF_EQUIV_NEG2, + // ======== CNF Xor Pos version 1 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (not (xor F1 F2)) F1 F2) + CNF_XOR_POS1, + // ======== CNF Xor Pos version 2 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (not (xor F1 F2)) (not F1) (not F2)) + CNF_XOR_POS2, + // ======== CNF Xor Neg version 1 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (xor F1 F2) (not F1) F2) + CNF_XOR_NEG1, + // ======== CNF Xor Neg version 2 + // Children: () + // Arguments: ((xor F1 F2)) + // --------------------- + // Conclusion: (or (xor F1 F2) F1 (not F2)) + CNF_XOR_NEG2, + // ======== CNF ITE Pos version 1 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (not (ite C F1 F2)) (not C) F1) + CNF_ITE_POS1, + // ======== CNF ITE Pos version 2 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (not (ite C F1 F2)) C F2) + CNF_ITE_POS2, + // ======== CNF ITE Pos version 3 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (not (ite C F1 F2)) F1 F2) + CNF_ITE_POS3, + // ======== CNF ITE Neg version 1 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (ite C F1 F2) (not C) (not F1)) + CNF_ITE_NEG1, + // ======== CNF ITE Neg version 2 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (ite C F1 F2) C (not F2)) + CNF_ITE_NEG2, + // ======== CNF ITE Neg version 3 + // Children: () + // Arguments: ((ite C F1 F2)) + // --------------------- + // Conclusion: (or (ite C F1 F2) (not F1) (not F2)) + CNF_ITE_NEG3, + + //======================== Builtin theory (common node operations) + // ======== Substitution + // Children: (P1:F1, ..., Pn:Fn) + // Arguments: (t, (ids)?) + // --------------------------------------------------------------- + // Conclusion: (= t t*sigma{ids}(Fn)*...*sigma{ids}(F1)) + // where sigma{ids}(Fi) are substitutions, which notice are applied in + // reverse order. + // Notice that ids is a MethodId identifier, which determines how to convert + // the formulas F1, ..., Fn into substitutions. + SUBS, + // ======== Rewrite + // Children: none + // Arguments: (t, (idr)?) + // ---------------------------------------- + // Conclusion: (= t Rewriter{idr}(t)) + // where idr is a MethodId identifier, which determines the kind of rewriter + // to apply, e.g. Rewriter::rewrite. + REWRITE, + // ======== Substitution + Rewriting equality introduction + // + // In this rule, we provide a term t and conclude that it is equal to its + // rewritten form under a (proven) substitution. + // + // Children: (P1:F1, ..., Pn:Fn) + // Arguments: (t, (ids (idr)?)?) + // --------------------------------------------------------------- + // Conclusion: (= t t') + // where + // t' is + // toWitness(Rewriter{idr}(toSkolem(t)*sigma{ids}(Fn)*...*sigma{ids}(F1))) + // toSkolem(...) converts terms from witness form to Skolem form, + // toWitness(...) converts terms from Skolem form to witness form. + // + // Notice that: + // toSkolem(t')=Rewriter{idr}(toSkolem(t)*sigma{ids}(Fn)*...*sigma{ids}(F1)) + // In other words, from the point of view of Skolem forms, this rule + // transforms t to t' by standard substitution + rewriting. + // + // The argument ids and idr is optional and specify the identifier of the + // substitution and rewriter respectively to be used. For details, see + // theory/builtin/proof_checker.h. + MACRO_SR_EQ_INTRO, + // ======== Substitution + Rewriting predicate introduction + // + // In this rule, we provide a formula F and conclude it, under the condition + // that it rewrites to true under a proven substitution. + // + // Children: (P1:F1, ..., Pn:Fn) + // Arguments: (F, (ids (idr)?)?) + // --------------------------------------------------------------- + // Conclusion: F + // where + // Rewriter{idr}(F*sigma{ids}(Fn)*...*sigma{ids}(F1)) == true + // where ids and idr are method identifiers. + // + // Notice that we apply rewriting on the witness form of F, meaning that this + // rule may conclude an F whose Skolem form is justified by the definition of + // its (fresh) Skolem variables. Furthermore, notice that the rewriting and + // substitution is applied only within the side condition, meaning the + // rewritten form of the witness form of F does not escape this rule. + MACRO_SR_PRED_INTRO, + // ======== Substitution + Rewriting predicate elimination + // + // In this rule, if we have proven a formula F, then we may conclude its + // rewritten form under a proven substitution. + // + // Children: (P1:F, P2:F1, ..., P_{n+1}:Fn) + // Arguments: ((ids (idr)?)?) + // ---------------------------------------- + // Conclusion: F' + // where + // F' is + // toWitness(Rewriter{idr}(toSkolem(F)*sigma{ids}(Fn)*...*sigma{ids}(F1)). + // where ids and idr are method identifiers. + // + // We rewrite only on the Skolem form of F, similar to MACRO_SR_EQ_INTRO. + MACRO_SR_PRED_ELIM, + // ======== Substitution + Rewriting predicate transform + // + // In this rule, if we have proven a formula F, then we may provide a formula + // G and conclude it if F and G are equivalent after rewriting under a proven + // substitution. + // + // Children: (P1:F, P2:F1, ..., P_{n+1}:Fn) + // Arguments: (G, (ids (idr)?)?) + // ---------------------------------------- + // Conclusion: G + // where + // Rewriter{idr}(F*sigma{ids}(Fn)*...*sigma{ids}(F1)) == + // Rewriter{idr}(G*sigma{ids}(Fn)*...*sigma{ids}(F1)) + // + // Notice that we apply rewriting on the witness form of F and G, similar to + // MACRO_SR_PRED_INTRO. + MACRO_SR_PRED_TRANSFORM, //================================================= Unknown rule UNKNOWN, diff --git a/src/expr/proof_step_buffer.cpp b/src/expr/proof_step_buffer.cpp new file mode 100644 index 000000000..800efa2c0 --- /dev/null +++ b/src/expr/proof_step_buffer.cpp @@ -0,0 +1,109 @@ +/********************* */ +/*! \file proof_step_buffer.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of proof step and proof step buffer utilities. + **/ + +#include "expr/proof_step_buffer.h" + +using namespace CVC4::kind; + +namespace CVC4 { + +ProofStep::ProofStep() : d_rule(PfRule::UNKNOWN) {} +ProofStep::ProofStep(PfRule r, + const std::vector<Node>& children, + const std::vector<Node>& args) + : d_rule(r), d_children(children), d_args(args) +{ +} +std::ostream& operator<<(std::ostream& out, ProofStep step) +{ + out << "(step " << step.d_rule; + for (const Node& c : step.d_children) + { + out << " " << c; + } + if (!step.d_args.empty()) + { + out << " :args"; + for (const Node& a : step.d_args) + { + out << " " << a; + } + } + out << ")"; + return out; +} + +ProofStepBuffer::ProofStepBuffer(ProofChecker* pc) : d_checker(pc) {} + +Node ProofStepBuffer::tryStep(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args, + Node expected) +{ + if (d_checker == nullptr) + { + Assert(false) << "ProofStepBuffer::ProofStepBuffer: no proof checker."; + return Node::null(); + } + Node res = + d_checker->checkDebug(id, children, args, expected, "pf-step-buffer"); + if (!res.isNull()) + { + // add proof step + d_steps.push_back( + std::pair<Node, ProofStep>(res, ProofStep(id, children, args))); + } + return res; +} + +void ProofStepBuffer::addStep(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args, + Node expected) +{ + d_steps.push_back( + std::pair<Node, ProofStep>(expected, ProofStep(id, children, args))); +} + +void ProofStepBuffer::addSteps(ProofStepBuffer& psb) +{ + const std::vector<std::pair<Node, ProofStep>>& steps = psb.getSteps(); + for (const std::pair<Node, ProofStep>& step : steps) + { + addStep(step.second.d_rule, + step.second.d_children, + step.second.d_args, + step.first); + } +} + +void ProofStepBuffer::popStep() +{ + Assert(!d_steps.empty()); + if (!d_steps.empty()) + { + d_steps.pop_back(); + } +} + +size_t ProofStepBuffer::getNumSteps() const { return d_steps.size(); } + +const std::vector<std::pair<Node, ProofStep>>& ProofStepBuffer::getSteps() const +{ + return d_steps; +} + +void ProofStepBuffer::clear() { d_steps.clear(); } + +} // namespace CVC4 diff --git a/src/expr/proof_step_buffer.h b/src/expr/proof_step_buffer.h new file mode 100644 index 000000000..005aee399 --- /dev/null +++ b/src/expr/proof_step_buffer.h @@ -0,0 +1,96 @@ +/********************* */ +/*! \file proof_step_buffer.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Proof step and proof step buffer utilities. + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__EXPR__PROOF_STEP_BUFFER_H +#define CVC4__EXPR__PROOF_STEP_BUFFER_H + +#include <vector> + +#include "expr/node.h" +#include "expr/proof_checker.h" +#include "expr/proof_rule.h" + +namespace CVC4 { + +/** + * Information for constructing a step in a CDProof. Notice that the conclusion + * of the proof step is intentionally not included in this data structure. + * Instead, it is intended that conclusions may be associated with proof steps + * based on e.g. the result of proof checking. + */ +class ProofStep +{ + public: + ProofStep(); + ProofStep(PfRule r, + const std::vector<Node>& children, + const std::vector<Node>& args); + /** The proof rule */ + PfRule d_rule; + /** The proof children */ + std::vector<Node> d_children; + /** The proof arguments */ + std::vector<Node> d_args; +}; +std::ostream& operator<<(std::ostream& out, ProofStep step); + +/** + * Class used to speculatively try and buffer a set of proof steps before + * sending them to a proof object. + */ +class ProofStepBuffer +{ + public: + ProofStepBuffer(ProofChecker* pc = nullptr); + ~ProofStepBuffer() {} + /** + * Returns the conclusion of the proof step, as determined by the proof + * checker of the given proof. If this is non-null, then the given step + * is added to the buffer maintained by this class. + * + * If expected is non-null, then this method returns null if the result of + * checking is not equal to expected. + */ + Node tryStep(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args, + Node expected = Node::null()); + /** Same as above, without checking */ + void addStep(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args, + Node expected); + /** Multi-step version */ + void addSteps(ProofStepBuffer& psb); + /** pop step */ + void popStep(); + /** Get num steps */ + size_t getNumSteps() const; + /** Get steps */ + const std::vector<std::pair<Node, ProofStep>>& getSteps() const; + /** Clear */ + void clear(); + + private: + /** The proof checker*/ + ProofChecker* d_checker; + /** the queued proof steps */ + std::vector<std::pair<Node, ProofStep>> d_steps; +}; + +} // namespace CVC4 + +#endif /* CVC4__EXPR__PROOF_STEP_BUFFER_H */ diff --git a/src/expr/sequence.cpp b/src/expr/sequence.cpp index e42a67bbe..f70a70027 100644 --- a/src/expr/sequence.cpp +++ b/src/expr/sequence.cpp @@ -14,6 +14,8 @@ #include "expr/sequence.h" +#include "expr/expr_sequence.h" + using namespace std; namespace CVC4 { @@ -297,6 +299,16 @@ bool Sequence::noOverlapWith(const Sequence& y) const size_t Sequence::maxSize() { return std::numeric_limits<uint32_t>::max(); } +ExprSequence Sequence::toExprSequence() +{ + std::vector<Expr> seq; + for (const Node& n : d_seq) + { + seq.push_back(n.toExpr()); + } + return ExprSequence(d_type.toType(), seq); +} + std::ostream& operator<<(std::ostream& os, const Sequence& s) { const std::vector<Node>& vec = s.getVec(); diff --git a/src/expr/sequence.h b/src/expr/sequence.h index 833e79441..2e0721b4a 100644 --- a/src/expr/sequence.h +++ b/src/expr/sequence.h @@ -144,6 +144,10 @@ class Sequence */ static size_t maxSize(); + //!!!!!!!!!!!!!!! temporary + ExprSequence toExprSequence(); + //!!!!!!!!!!!!!!! end temporary + private: /** * Returns a negative number if *this < y, 0 if *this and y are equal and a diff --git a/src/expr/type.cpp b/src/expr/type.cpp index 031dcb3f0..2067beef5 100644 --- a/src/expr/type.cpp +++ b/src/expr/type.cpp @@ -353,6 +353,12 @@ bool Type::isSet() const { return d_typeNode->isSet(); } +bool Type::isSequence() const +{ + NodeManagerScope nms(d_nodeManager); + return d_typeNode->isSequence(); +} + /** Is this a sort kind */ bool Type::isSort() const { NodeManagerScope nms(d_nodeManager); @@ -516,6 +522,11 @@ SetType::SetType(const Type& t) : Type(t) PrettyCheckArgument(isNull() || isSet(), this); } +SequenceType::SequenceType(const Type& t) : Type(t) +{ + PrettyCheckArgument(isNull() || isSequence(), this); +} + SortType::SortType(const Type& t) : Type(t) { PrettyCheckArgument(isNull() || isSort(), this); @@ -550,6 +561,11 @@ Type SetType::getElementType() const { return makeType(d_typeNode->getSetElementType()); } +Type SequenceType::getElementType() const +{ + return makeType(d_typeNode->getSequenceElementType()); +} + DatatypeType ConstructorType::getRangeType() const { return DatatypeType(makeType(d_typeNode->getConstructorRangeType())); } diff --git a/src/expr/type.h b/src/expr/type.h index 529c40930..0cdf55626 100644 --- a/src/expr/type.h +++ b/src/expr/type.h @@ -373,7 +373,13 @@ protected: */ bool isSet() const; - /** + /** + * Is this a Sequence type? + * @return true if the type is a Sequence type + */ + bool isSequence() const; + + /** * Is this a datatype type? * @return true if the type is a datatype type */ @@ -515,15 +521,26 @@ class CVC4_PUBLIC ArrayType : public Type { Type getConstituentType() const; };/* class ArrayType */ -/** Class encapsulating an set type. */ +/** Class encapsulating a set type. */ class CVC4_PUBLIC SetType : public Type { public: /** Construct from the base type */ SetType(const Type& type = Type()); - /** Get the index type */ + /** Get the element type */ + Type getElementType() const; +}; /* class SetType */ + +/** Class encapsulating a sequence type. */ +class CVC4_PUBLIC SequenceType : public Type +{ + public: + /** Construct from the base type */ + SequenceType(const Type& type = Type()); + + /** Get the element type */ Type getElementType() const; -};/* class SetType */ +}; /* class SetType */ /** Class encapsulating a user-defined sort. */ class CVC4_PUBLIC SortType : public Type { diff --git a/src/expr/type_node.cpp b/src/expr/type_node.cpp index 110db6162..e191be0c2 100644 --- a/src/expr/type_node.cpp +++ b/src/expr/type_node.cpp @@ -122,7 +122,7 @@ bool TypeNode::isFiniteInternal(bool usortFinite) { ret = true; } - else if (isString() || isRegExp() || isReal()) + else if (isString() || isRegExp() || isSequence() || isReal()) { ret = false; } @@ -245,6 +245,10 @@ bool TypeNode::isClosedEnumerable() { ret = getSetElementType().isClosedEnumerable(); } + else if (isSequence()) + { + ret = getSequenceElementType().isClosedEnumerable(); + } else if (isDatatype()) { // avoid infinite loops: initially set to true @@ -353,6 +357,12 @@ bool TypeNode::isComparableTo(TypeNode t) const { return false; } +TypeNode TypeNode::getSequenceElementType() const +{ + Assert(isSequence()); + return (*this)[0]; +} + TypeNode TypeNode::getBaseType() const { TypeNode realt = NodeManager::currentNM()->realType(); if (isSubtypeOf(realt)) { diff --git a/src/expr/type_node.h b/src/expr/type_node.h index 70392fb01..c9771bd3d 100644 --- a/src/expr/type_node.h +++ b/src/expr/type_node.h @@ -518,6 +518,9 @@ public: /** Is this a Set type? */ bool isSet() const; + /** Is this a Sequence type? */ + bool isSequence() const; + /** Get the index type (for array types) */ TypeNode getArrayIndexType() const; @@ -536,6 +539,8 @@ public: /** Get the element type (for set types) */ TypeNode getSetElementType() const; + /** Get the element type (for sequence types) */ + TypeNode getSequenceElementType() const; /** * Is this a function type? Function-like things (e.g. datatype * selectors) that aren't actually functions are NOT considered @@ -964,6 +969,11 @@ inline bool TypeNode::isSet() const { return getKind() == kind::SET_TYPE; } +inline bool TypeNode::isSequence() const +{ + return getKind() == kind::SEQUENCE_TYPE; +} + inline TypeNode TypeNode::getSetElementType() const { Assert(isSet()); return (*this)[0]; diff --git a/src/options/quantifiers_options.toml b/src/options/quantifiers_options.toml index 1834c90c4..8ae6ea89a 100644 --- a/src/options/quantifiers_options.toml +++ b/src/options/quantifiers_options.toml @@ -46,9 +46,6 @@ header = "options/quantifiers_options.h" [[option.mode.SIMPLE]] name = "simple" help = "Do simple prenexing of same sign quantifiers." -[[option.mode.DISJ_NORMAL]] - name = "dnorm" - help = "Prenex to disjunctive prenex normal form." [[option.mode.NORMAL]] name = "norm" help = "Prenex to prenex normal form." diff --git a/src/options/smt_options.toml b/src/options/smt_options.toml index 08e6f317c..449c0c31e 100644 --- a/src/options/smt_options.toml +++ b/src/options/smt_options.toml @@ -346,7 +346,7 @@ header = "options/smt_options.h" long = "unconstrained-simp" type = "bool" default = "false" - help = "turn on unconstrained simplification (see Bruttomesso/Brummayer PhD thesis)" + help = "turn on unconstrained simplification (see Bruttomesso/Brummayer PhD thesis). Fully supported only in (subsets of) the logic QF_ABV." [[option]] name = "repeatSimp" diff --git a/src/options/strings_options.toml b/src/options/strings_options.toml index 3cf8f5852..32c4c64c7 100644 --- a/src/options/strings_options.toml +++ b/src/options/strings_options.toml @@ -55,15 +55,6 @@ header = "options/strings_options.h" help = "perform string preprocessing lazily" [[option]] - name = "stringLenGeqZ" - category = "regular" - long = "strings-len-geqz" - type = "bool" - default = "false" - read_only = true - help = "strings length greater than zero lemmas" - -[[option]] name = "stringLenNorm" category = "regular" long = "strings-len-norm" diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 8e4152e2e..e604c7769 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -1159,7 +1159,7 @@ declareVariables[std::unique_ptr<CVC4::Command>* cmd, CVC4::api::Sort& t, PARSER_STATE->checkDeclaration(*i, CHECK_UNDECLARED, SYM_VARIABLE); api::Term func = PARSER_STATE->mkVar( *i, - t.getType(), + api::Sort(SOLVER, t.getType()), ExprManager::VAR_FLAG_GLOBAL | ExprManager::VAR_FLAG_DEFINED); PARSER_STATE->defineVar(*i, f); Command* decl = @@ -1654,7 +1654,7 @@ tupleStore[CVC4::api::Term& f] } const Datatype & dt = ((DatatypeType)t.getType()).getDatatype(); f2 = SOLVER->mkTerm( - api::APPLY_SELECTOR, api::Term(dt[0][k].getSelector()), f); + api::APPLY_SELECTOR, api::Term(SOLVER, dt[0][k].getSelector()), f); } ( ( arrayStore[f2] | DOT ( tupleStore[f2] @@ -1687,7 +1687,7 @@ recordStore[CVC4::api::Term& f] } const Datatype & dt = ((DatatypeType)t.getType()).getDatatype(); f2 = SOLVER->mkTerm( - api::APPLY_SELECTOR, api::Term(dt[0][id].getSelector()), f); + api::APPLY_SELECTOR, api::Term(SOLVER, dt[0][id].getSelector()), f); } ( ( arrayStore[f2] | DOT ( tupleStore[f2] @@ -1831,7 +1831,9 @@ postfixTerm[CVC4::api::Term& f] PARSER_STATE->parseError(std::string("no such field `") + id + "' in record"); } const Datatype & dt = ((DatatypeType)type.getType()).getDatatype(); - f = SOLVER->mkTerm(api::APPLY_SELECTOR,api::Term(dt[0][id].getSelector()), f); + f = SOLVER->mkTerm(api::APPLY_SELECTOR, + api::Term(SOLVER, dt[0][id].getSelector()), + f); } | k=numeral { @@ -1846,7 +1848,9 @@ postfixTerm[CVC4::api::Term& f] PARSER_STATE->parseError(ss.str()); } const Datatype & dt = ((DatatypeType)type.getType()).getDatatype(); - f = SOLVER->mkTerm(api::APPLY_SELECTOR,api::Term(dt[0][k].getSelector()), f); + f = SOLVER->mkTerm(api::APPLY_SELECTOR, + api::Term(SOLVER, dt[0][k].getSelector()), + f); } ) )* @@ -1857,7 +1861,7 @@ postfixTerm[CVC4::api::Term& f] | ABS_TOK LPAREN formula[f] RPAREN { f = MK_TERM(CVC4::api::ABS, f); } | DIVISIBLE_TOK LPAREN formula[f] COMMA n=numeral RPAREN - { f = MK_TERM(SOLVER->mkOp(CVC4::api::DIVISIBLE,n), f); } + { f = MK_TERM(SOLVER->mkOp(CVC4::api::DIVISIBLE, n), f); } | DISTINCT_TOK LPAREN formula[f] { args.push_back(f); } ( COMMA formula[f] { args.push_back(f); } )* RPAREN @@ -1868,7 +1872,7 @@ postfixTerm[CVC4::api::Term& f] ) ( typeAscription[f, t] { - f = PARSER_STATE->applyTypeAscription(f,t).getExpr(); + f = PARSER_STATE->applyTypeAscription(f,t); } )? ; @@ -1885,8 +1889,8 @@ relationTerm[CVC4::api::Term& f] args.push_back(f); types.push_back(f.getSort()); api::Sort t = SOLVER->mkTupleSort(types); - const Datatype& dt = ((DatatypeType)t.getType()).getDatatype(); - args.insert( args.begin(), api::Term(dt[0].getConstructor()) ); + const Datatype& dt = Datatype(((DatatypeType)t.getType()).getDatatype()); + args.insert(args.begin(), api::Term(SOLVER, dt[0].getConstructor())); f = MK_TERM(api::APPLY_CONSTRUCTOR, args); } | IDEN_TOK LPAREN formula[f] RPAREN @@ -2136,7 +2140,7 @@ simpleTerm[CVC4::api::Term& f] } api::Sort dtype = SOLVER->mkTupleSort(types); const Datatype& dt = ((DatatypeType)dtype.getType()).getDatatype(); - args.insert( args.begin(), dt[0].getConstructor() ); + args.insert(args.begin(), api::Term(SOLVER, dt[0].getConstructor())); f = MK_TERM(api::APPLY_CONSTRUCTOR, args); } } @@ -2146,7 +2150,9 @@ simpleTerm[CVC4::api::Term& f] { std::vector<api::Sort> types; api::Sort dtype = SOLVER->mkTupleSort(types); const Datatype& dt = ((DatatypeType)dtype.getType()).getDatatype(); - f = MK_TERM(api::APPLY_CONSTRUCTOR, api::Term(dt[0].getConstructor())); } + f = MK_TERM(api::APPLY_CONSTRUCTOR, + api::Term(SOLVER, dt[0].getConstructor())); + } /* empty record literal */ | PARENHASH HASHPAREN @@ -2154,7 +2160,8 @@ simpleTerm[CVC4::api::Term& f] api::Sort dtype = SOLVER->mkRecordSort( std::vector<std::pair<std::string, api::Sort>>()); const Datatype& dt = ((DatatypeType)dtype.getType()).getDatatype(); - f = MK_TERM(api::APPLY_CONSTRUCTOR, api::Term(dt[0].getConstructor())); + f = MK_TERM(api::APPLY_CONSTRUCTOR, + api::Term(SOLVER, dt[0].getConstructor())); } /* empty set literal */ | LBRACE RBRACE @@ -2252,7 +2259,7 @@ simpleTerm[CVC4::api::Term& f] } api::Sort dtype = SOLVER->mkRecordSort(typeIds); const Datatype& dt = ((DatatypeType)dtype.getType()).getDatatype(); - args.insert( args.begin(), dt[0].getConstructor() ); + args.insert(args.begin(), api::Term(SOLVER, dt[0].getConstructor())); f = MK_TERM(api::APPLY_CONSTRUCTOR, args); } @@ -2360,8 +2367,9 @@ constructorDef[CVC4::api::DatatypeDecl& type] std::unique_ptr<CVC4::api::DatatypeConstructorDecl> ctor; } : identifier[id,CHECK_UNDECLARED,SYM_SORT] - { - ctor.reset(new CVC4::api::DatatypeConstructorDecl(id)); + { + ctor.reset(new CVC4::api::DatatypeConstructorDecl( + SOLVER->mkDatatypeConstructorDecl(id))); } ( LPAREN selector[&ctor] diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index c860d14c7..b24f9ae9d 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -82,14 +82,9 @@ api::Term Parser::getSymbol(const std::string& name, SymbolType type) { checkDeclaration(name, CHECK_DECLARED, type); assert(isDeclared(name, type)); - - if (type == SYM_VARIABLE) { - // Functions share var namespace - return d_symtab->lookup(name); - } - - assert(false); // Unhandled(type); - return Expr(); + assert(type == SYM_VARIABLE); + // Functions share var namespace + return api::Term(d_solver, d_symtab->lookup(name)); } api::Term Parser::getVariable(const std::string& name) @@ -166,7 +161,7 @@ api::Sort Parser::getSort(const std::string& name) { checkDeclaration(name, CHECK_DECLARED, SYM_SORT); assert(isDeclared(name, SYM_SORT)); - api::Sort t = api::Sort(d_symtab->lookupType(name)); + api::Sort t = api::Sort(d_solver, d_symtab->lookupType(name)); return t; } @@ -175,8 +170,8 @@ api::Sort Parser::getSort(const std::string& name, { checkDeclaration(name, CHECK_DECLARED, SYM_SORT); assert(isDeclared(name, SYM_SORT)); - api::Sort t = - api::Sort(d_symtab->lookupType(name, api::sortVectorToTypes(params))); + api::Sort t = api::Sort( + d_solver, d_symtab->lookupType(name, api::sortVectorToTypes(params))); return t; } @@ -237,7 +232,8 @@ std::vector<api::Term> Parser::bindBoundVars( std::vector<api::Term> vars; for (std::pair<std::string, api::Sort>& i : sortedVarNames) { - vars.push_back(bindBoundVar(i.first, i.second.getType())); + vars.push_back( + bindBoundVar(i.first, api::Sort(d_solver, i.second.getType()))); } return vars; } @@ -251,7 +247,7 @@ api::Term Parser::mkAnonymousFunction(const std::string& prefix, } stringstream name; name << prefix << "_anon_" << ++d_anonymousFunctionCount; - return mkVar(name.str(), type.getType(), flags); + return mkVar(name.str(), api::Sort(d_solver, type.getType()), flags); } std::vector<api::Term> Parser::bindVars(const std::vector<std::string> names, @@ -334,7 +330,8 @@ void Parser::defineParameterizedType(const std::string& name, api::Sort Parser::mkSort(const std::string& name, uint32_t flags) { Debug("parser") << "newSort(" << name << ")" << std::endl; - api::Sort type = d_solver->getExprManager()->mkSort(name, flags); + api::Sort type = + api::Sort(d_solver, d_solver->getExprManager()->mkSort(name, flags)); defineType( name, type, @@ -348,8 +345,9 @@ api::Sort Parser::mkSortConstructor(const std::string& name, { Debug("parser") << "newSortConstructor(" << name << ", " << arity << ")" << std::endl; - api::Sort type = - d_solver->getExprManager()->mkSortConstructor(name, arity, flags); + api::Sort type = api::Sort( + d_solver, + d_solver->getExprManager()->mkSortConstructor(name, arity, flags)); defineType( name, vector<api::Sort>(arity), @@ -379,8 +377,10 @@ api::Sort Parser::mkUnresolvedTypeConstructor( { Debug("parser") << "newSortConstructor(P)(" << name << ", " << params.size() << ")" << std::endl; - api::Sort unresolved = d_solver->getExprManager()->mkSortConstructor( - name, params.size(), ExprManager::SORT_FLAG_PLACEHOLDER); + api::Sort unresolved = + api::Sort(d_solver, + d_solver->getExprManager()->mkSortConstructor( + name, params.size(), ExprManager::SORT_FLAG_PLACEHOLDER)); defineType(name, params, unresolved); api::Sort t = getSort(name, params); d_unresolved.insert(unresolved); @@ -588,11 +588,12 @@ api::Term Parser::applyTypeAscription(api::Term t, api::Sort s) Expr e = t.getExpr(); const DatatypeConstructor& dtc = Datatype::datatypeOf(e)[Datatype::indexOf(e)]; - t = api::Term(em->mkExpr( - kind::APPLY_TYPE_ASCRIPTION, - em->mkConst( - AscriptionType(dtc.getSpecializedConstructorType(s.getType()))), - e)); + t = api::Term( + d_solver, + em->mkExpr(kind::APPLY_TYPE_ASCRIPTION, + em->mkConst(AscriptionType( + dtc.getSpecializedConstructorType(s.getType()))), + e)); } // the type of t does not match the sort s by design (constructor type // vs datatype type), thus we use an alternative check here. @@ -624,7 +625,7 @@ api::Term Parser::mkVar(const std::string& name, uint32_t flags) { return api::Term( - d_solver->getExprManager()->mkVar(name, type.getType(), flags)); + d_solver, d_solver->getExprManager()->mkVar(name, type.getType(), flags)); } //!!!!!!!!!!! temporary @@ -892,16 +893,16 @@ std::vector<unsigned> Parser::processAdHocStringEsc(const std::string& s) return str; } -Expr Parser::mkStringConstant(const std::string& s) +api::Term Parser::mkStringConstant(const std::string& s) { ExprManager* em = d_solver->getExprManager(); if (language::isInputLang_smt2_6(em->getOptions().getInputLanguage())) { - return d_solver->mkString(s, true).getExpr(); + return api::Term(d_solver, d_solver->mkString(s, true).getExpr()); } // otherwise, we must process ad-hoc escape sequences std::vector<unsigned> str = processAdHocStringEsc(s); - return d_solver->mkString(str).getExpr(); + return api::Term(d_solver, d_solver->mkString(str).getExpr()); } } /* CVC4::parser namespace */ diff --git a/src/parser/parser.h b/src/parser/parser.h index 7941cfdd5..b5dc83902 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -808,7 +808,7 @@ public: inline SymbolTable* getSymbolTable() const { return d_symtab; } - + //------------------------ operator overloading /** is this function overloaded? */ bool isOverloadedFunction(api::Term fun) @@ -822,7 +822,8 @@ public: */ api::Term getOverloadedConstantForType(const std::string& name, api::Sort t) { - return d_symtab->getOverloadedConstantForType(name, t.getType()); + return api::Term(d_solver, + d_symtab->getOverloadedConstantForType(name, t.getType())); } /** @@ -833,8 +834,9 @@ public: api::Term getOverloadedFunctionForTypes(const std::string& name, std::vector<api::Sort>& argTypes) { - return d_symtab->getOverloadedFunctionForTypes( - name, api::sortVectorToTypes(argTypes)); + return api::Term(d_solver, + d_symtab->getOverloadedFunctionForTypes( + name, api::sortVectorToTypes(argTypes))); } //------------------------ end operator overloading /** @@ -845,7 +847,7 @@ public: * SMT-LIB 2.6 or higher), or otherwise calling the solver to construct * the string. */ - Expr mkStringConstant(const std::string& s); + api::Term mkStringConstant(const std::string& s); private: /** ad-hoc string escaping diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index d591c29de..62bf7e974 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -101,7 +101,7 @@ namespace CVC4 { struct myExpr : public CVC4::api::Term { myExpr() : CVC4::api::Term() {} myExpr(void*) : CVC4::api::Term() {} - myExpr(const Expr& e) : CVC4::api::Term(e) {} + myExpr(const Expr& e) : CVC4::api::Term(d_solver, e) {} myExpr(const myExpr& e) : CVC4::api::Term(e) {} };/* struct myExpr */ }/* CVC4::parser::smt2 namespace */ @@ -286,7 +286,7 @@ command [std::unique_ptr<CVC4::Command>* cmd] { PARSER_STATE->popScope(); // Do NOT call mkSort, since that creates a new sort! // This name is not its own distinct sort, it's an alias. - PARSER_STATE->defineParameterizedType(name, sorts, t.getType()); + PARSER_STATE->defineParameterizedType(name, sorts, t); cmd->reset(new DefineTypeCommand( name, api::sortVectorToTypes(sorts), t.getType())); } @@ -800,7 +800,7 @@ sygusGrammarV1[CVC4::api::Sort & ret, PARSER_STATE->getUnresolvedSorts().clear(); - ret = datatypeTypes[0]; + ret = api::Sort(SOLVER, datatypeTypes[0]); }; // SyGuS grammar term. @@ -893,7 +893,7 @@ sygusGTerm[CVC4::SygusGTerm& sgt, const std::string& fun] << "expression " << atomTerm << std::endl; std::stringstream ss; ss << atomTerm; - sgt.d_op.d_expr = atomTerm.getExpr(); + sgt.d_op.d_expr = atomTerm; sgt.d_name = ss.str(); sgt.d_gterm_type = SygusGTerm::gterm_op; } @@ -1692,7 +1692,13 @@ termNonVariable[CVC4::api::Term& expr, CVC4::api::Term& expr2] std::vector<api::Sort> argTypes; } : LPAREN_TOK quantOp[kind] - { PARSER_STATE->pushScope(true); } + { + if (!PARSER_STATE->isTheoryEnabled(theory::THEORY_QUANTIFIERS)) + { + PARSER_STATE->parseError("Quantifier used in non-quantified logic."); + } + PARSER_STATE->pushScope(true); + } boundVarList[bvl] term[f, f2] RPAREN_TOK { @@ -1791,8 +1797,10 @@ termNonVariable[CVC4::api::Term& expr, CVC4::api::Term& expr2] Expr ef = f.getExpr(); if (Datatype::datatypeOf(ef).isParametric()) { - type = Datatype::datatypeOf(ef)[Datatype::indexOf(ef)] - .getSpecializedConstructorType(expr.getSort().getType()); + type = api::Sort( + SOLVER, + Datatype::datatypeOf(ef)[Datatype::indexOf(ef)] + .getSpecializedConstructorType(expr.getSort().getType())); } argTypes = type.getConstructorDomainSorts(); } @@ -1914,10 +1922,10 @@ termNonVariable[CVC4::api::Term& expr, CVC4::api::Term& expr2] sorts.emplace_back(arg.getSort()); terms.emplace_back(arg); } - expr = SOLVER->mkTuple(sorts, terms).getExpr(); + expr = SOLVER->mkTuple(sorts, terms); } | /* an atomic term (a term with no subterms) */ - termAtomic[atomTerm] { expr = atomTerm.getExpr(); } + termAtomic[atomTerm] { expr = atomTerm; } ; @@ -2513,7 +2521,8 @@ constructorDef[CVC4::api::DatatypeDecl& type] } : symbol[id,CHECK_NONE,SYM_VARIABLE] { - ctor = new api::DatatypeConstructorDecl(id); + ctor = new api::DatatypeConstructorDecl( + SOLVER->mkDatatypeConstructorDecl(id)); } ( LPAREN_TOK selector[*ctor] RPAREN_TOK )* { // make the constructor @@ -2630,8 +2639,8 @@ CHAR_TOK : { PARSER_STATE->isTheoryEnabled(theory::THEORY_STRINGS) }? 'char'; TUPLE_CONST_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'mkTuple'; TUPLE_SEL_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tupSel'; -HO_ARROW_TOK : { PARSER_STATE->getLogic().isHigherOrder() }? '->'; -HO_LAMBDA_TOK : { PARSER_STATE->getLogic().isHigherOrder() }? 'lambda'; +HO_ARROW_TOK : { PARSER_STATE->isHoEnabled() }? '->'; +HO_LAMBDA_TOK : { PARSER_STATE->isHoEnabled() }? 'lambda'; /** * A sequence of printable ASCII characters (except backslash) that starts diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index 91260d1db..608e47a6b 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -315,6 +315,12 @@ bool Smt2::isTheoryEnabled(theory::TheoryId theory) const return d_logic.isTheoryEnabled(theory); } +bool Smt2::isHoEnabled() const +{ + return getLogic().isHigherOrder() + && d_solver->getExprManager()->getOptions().getUfHo(); +} + bool Smt2::logicIsSet() { return d_logicSet; } @@ -1371,7 +1377,7 @@ void Smt2::mkSygusDatatype(api::DatatypeDecl& dt, if( std::find( types.begin(), types.end(), t )==types.end() ){ types.push_back( t ); //identity element - api::Sort bt = dt.getDatatype().getSygusType(); + api::Sort bt = api::Sort(d_solver, dt.getDatatype().getSygusType()); Debug("parser-sygus") << ": make identity function for " << bt << ", argument type " << t << std::endl; std::stringstream ss; @@ -1475,7 +1481,7 @@ api::Term Smt2::purifySygusGTerm(api::Term term, api::Term ret = d_solver->mkVar(term.getSort()); Trace("parser-sygus2-debug") << "...unresolved non-terminal, intro " << ret << std::endl; - args.push_back(ret.getExpr()); + args.push_back(api::Term(d_solver, ret.getExpr())); cargs.push_back(itn->second); return ret; } @@ -1565,8 +1571,7 @@ void Smt2::parseOpApplyTypeAscription(ParseOp& p, api::Sort type) Trace("parser-qid") << " " << p.d_expr.getKind() << " " << p.d_expr.getSort(); Trace("parser-qid") << std::endl; // otherwise, we process the type ascription - p.d_expr = - applyTypeAscription(api::Term(p.d_expr), api::Sort(type)).getExpr(); + p.d_expr = applyTypeAscription(p.d_expr, type); } api::Term Smt2::parseOpToExpr(ParseOp& p) @@ -1770,8 +1775,10 @@ api::Term Smt2::applyParseOp(ParseOp& p, std::vector<api::Term>& args) parseError(ss.str()); } const Datatype& dt = ((DatatypeType)t.getType()).getDatatype(); - api::Term ret = d_solver->mkTerm( - api::APPLY_SELECTOR, api::Term(dt[0][n].getSelector()), args[0]); + api::Term ret = + d_solver->mkTerm(api::APPLY_SELECTOR, + api::Term(d_solver, dt[0][n].getSelector()), + args[0]); Debug("parser") << "applyParseOp: return selector " << ret << std::endl; return ret; } diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index 35d088601..af1e36795 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -98,6 +98,13 @@ class Smt2 : public Parser bool isTheoryEnabled(theory::TheoryId theory) const; + /** + * Checks if higher-order support is enabled. + * + * @return true if higher-order support is enabled, false otherwise + */ + bool isHoEnabled() const; + bool logicIsSet() override; /** diff --git a/src/parser/tptp/Tptp.g b/src/parser/tptp/Tptp.g index c2f4675b1..e26709595 100644 --- a/src/parser/tptp/Tptp.g +++ b/src/parser/tptp/Tptp.g @@ -1441,7 +1441,7 @@ tffLetTermBinding[std::vector<CVC4::api::Term> & bvlist, PARSER_STATE->checkLetBinding(bvlist, lhs, rhs, false); std::vector<api::Term> lchildren(++lhs.begin(), lhs.end()); rhs = MK_TERM(api::LAMBDA, MK_TERM(api::BOUND_VAR_LIST, lchildren), rhs); - lhs = api::Term(lhs.getExpr().getOperator()); + lhs = api::Term(SOLVER, lhs.getExpr().getOperator()); } | LPAREN_TOK tffLetTermBinding[bvlist, lhs, rhs] RPAREN_TOK ; @@ -1463,7 +1463,7 @@ tffLetFormulaBinding[std::vector<CVC4::api::Term> & bvlist, PARSER_STATE->checkLetBinding(bvlist, lhs, rhs, true); std::vector<api::Term> lchildren(++lhs.begin(), lhs.end()); rhs = MK_TERM(api::LAMBDA, MK_TERM(api::BOUND_VAR_LIST, lchildren), rhs); - lhs = api::Term(lhs.getExpr().getOperator()); + lhs = api::Term(SOLVER, lhs.getExpr().getOperator()); } | LPAREN_TOK tffLetFormulaBinding[bvlist, lhs, rhs] RPAREN_TOK ; diff --git a/src/preprocessing/passes/unconstrained_simplifier.cpp b/src/preprocessing/passes/unconstrained_simplifier.cpp index 5d544ae57..b74909824 100644 --- a/src/preprocessing/passes/unconstrained_simplifier.cpp +++ b/src/preprocessing/passes/unconstrained_simplifier.cpp @@ -91,6 +91,15 @@ void UnconstrainedSimplifier::visitAll(TNode assertion) d_unconstrained.insert(current); } } + else if (current.isClosure()) + { + // Throw an exception. This should never happen in practice unless the + // user specifically enabled unconstrained simplification in an illegal + // logic. + throw LogicException( + "Cannot use unconstrained simplification in this logic, due to " + "(possibly internally introduced) quantified formula."); + } else { for (TNode childNode : current) diff --git a/src/preprocessing/passes/unconstrained_simplifier.h b/src/preprocessing/passes/unconstrained_simplifier.h index ac4fd0a03..7fc13e17d 100644 --- a/src/preprocessing/passes/unconstrained_simplifier.h +++ b/src/preprocessing/passes/unconstrained_simplifier.h @@ -62,7 +62,11 @@ class UnconstrainedSimplifier : public PreprocessingPass theory::SubstitutionMap d_substitutions; const LogicInfo& d_logicInfo; - + /** + * Visit all subterms in assertion. This method throws a LogicException if + * there is a subterm that is unhandled by this preprocessing pass (e.g. a + * quantified formula). + */ void visitAll(TNode assertion); Node newUnconstrainedVar(TypeNode t, TNode var); void processUnconstrained(); diff --git a/src/smt/set_defaults.cpp b/src/smt/set_defaults.cpp index e06363883..bae7fbe68 100644 --- a/src/smt/set_defaults.cpp +++ b/src/smt/set_defaults.cpp @@ -538,18 +538,6 @@ void setDefaults(SmtEngine& smte, LogicInfo& logic) smte.setOption("produce-models", SExpr("true")); } - // Set the options for the theoryOf - if (!options::theoryOfMode.wasSetByUser()) - { - if (logic.isSharingEnabled() && !logic.isTheoryEnabled(THEORY_BV) - && !logic.isTheoryEnabled(THEORY_STRINGS) - && !logic.isTheoryEnabled(THEORY_SETS)) - { - Trace("smt") << "setting theoryof-mode to term-based" << std::endl; - options::theoryOfMode.set(options::TheoryOfMode::THEORY_OF_TERM_BASED); - } - } - ///////////////////////////////////////////////////////////////////////////// // Theory widening // @@ -609,6 +597,18 @@ void setDefaults(SmtEngine& smte, LogicInfo& logic) } ///////////////////////////////////////////////////////////////////////////// + // Set the options for the theoryOf + if (!options::theoryOfMode.wasSetByUser()) + { + if (logic.isSharingEnabled() && !logic.isTheoryEnabled(THEORY_BV) + && !logic.isTheoryEnabled(THEORY_STRINGS) + && !logic.isTheoryEnabled(THEORY_SETS)) + { + Trace("smt") << "setting theoryof-mode to term-based" << std::endl; + options::theoryOfMode.set(options::TheoryOfMode::THEORY_OF_TERM_BASED); + } + } + // by default, symmetry breaker is on only for non-incremental QF_UF if (!options::ufSymmetryBreaker.wasSetByUser()) { @@ -1161,11 +1161,8 @@ void setDefaults(SmtEngine& smte, LogicInfo& logic) // prenexing if (options::cegqiNestedQE()) { - // only complete with prenex = disj_normal or normal - if (options::prenexQuant() <= options::PrenexQuantMode::DISJ_NORMAL) - { - options::prenexQuant.set(options::PrenexQuantMode::DISJ_NORMAL); - } + // only complete with prenex = normal + options::prenexQuant.set(options::PrenexQuantMode::NORMAL); } else if (options::globalNegate()) { diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 3c0a2cd8f..9e382cdcf 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -1022,72 +1022,110 @@ void SmtEngine::setInfo(const std::string& key, const CVC4::SExpr& value) throw UnrecognizedOptionException(); } -CVC4::SExpr SmtEngine::getInfo(const std::string& key) const { +bool SmtEngine::isValidGetInfoFlag(const std::string& key) const +{ + if (key == "all-statistics" || key == "error-behavior" || key == "name" + || key == "version" || key == "authors" || key == "status" + || key == "reason-unknown" || key == "assertion-stack-levels" + || key == "all-options") + { + return true; + } + return false; +} +CVC4::SExpr SmtEngine::getInfo(const std::string& key) const +{ SmtScope smts(this); Trace("smt") << "SMT getInfo(" << key << ")" << endl; - if(key == "all-statistics") { + if (!isValidGetInfoFlag(key)) + { + throw UnrecognizedOptionException(); + } + if (key == "all-statistics") + { vector<SExpr> stats; - for(StatisticsRegistry::const_iterator i = NodeManager::fromExprManager(d_exprManager)->getStatisticsRegistry()->begin(); - i != NodeManager::fromExprManager(d_exprManager)->getStatisticsRegistry()->end(); - ++i) { + for (StatisticsRegistry::const_iterator i = + NodeManager::fromExprManager(d_exprManager) + ->getStatisticsRegistry() + ->begin(); + i + != NodeManager::fromExprManager(d_exprManager) + ->getStatisticsRegistry() + ->end(); + ++i) + { vector<SExpr> v; v.push_back((*i).first); v.push_back((*i).second); stats.push_back(v); } - for(StatisticsRegistry::const_iterator i = d_statisticsRegistry->begin(); - i != d_statisticsRegistry->end(); - ++i) { + for (StatisticsRegistry::const_iterator i = d_statisticsRegistry->begin(); + i != d_statisticsRegistry->end(); + ++i) + { vector<SExpr> v; v.push_back((*i).first); v.push_back((*i).second); stats.push_back(v); } return SExpr(stats); - } else if(key == "error-behavior") { + } + if (key == "error-behavior") + { return SExpr(SExpr::Keyword("immediate-exit")); - } else if(key == "name") { + } + if (key == "name") + { return SExpr(Configuration::getName()); - } else if(key == "version") { + } + if (key == "version") + { return SExpr(Configuration::getVersionString()); - } else if(key == "authors") { + } + if (key == "authors") + { return SExpr(Configuration::about()); - } else if(key == "status") { + } + if (key == "status") + { // sat | unsat | unknown - switch(d_status.asSatisfiabilityResult().isSat()) { - case Result::SAT: - return SExpr(SExpr::Keyword("sat")); - case Result::UNSAT: - return SExpr(SExpr::Keyword("unsat")); - default: - return SExpr(SExpr::Keyword("unknown")); - } - } else if(key == "reason-unknown") { - if(!d_status.isNull() && d_status.isUnknown()) { + switch (d_status.asSatisfiabilityResult().isSat()) + { + case Result::SAT: return SExpr(SExpr::Keyword("sat")); + case Result::UNSAT: return SExpr(SExpr::Keyword("unsat")); + default: return SExpr(SExpr::Keyword("unknown")); + } + } + if (key == "reason-unknown") + { + if (!d_status.isNull() && d_status.isUnknown()) + { stringstream ss; ss << d_status.whyUnknown(); string s = ss.str(); transform(s.begin(), s.end(), s.begin(), ::tolower); return SExpr(SExpr::Keyword(s)); - } else { + } + else + { throw RecoverableModalException( "Can't get-info :reason-unknown when the " "last result wasn't unknown!"); } - } else if(key == "assertion-stack-levels") { + } + if (key == "assertion-stack-levels") + { AlwaysAssert(d_userLevels.size() <= std::numeric_limits<unsigned long int>::max()); return SExpr(static_cast<unsigned long int>(d_userLevels.size())); - } else if(key == "all-options") { - // get the options, like all-statistics - std::vector< std::vector<std::string> > current_options = - Options::current()->getOptions(); - return SExpr::parseListOfListOfAtoms(current_options); - } else { - throw UnrecognizedOptionException(); } + Assert(key == "all-options"); + // get the options, like all-statistics + std::vector<std::vector<std::string>> current_options = + Options::current()->getOptions(); + return SExpr::parseListOfListOfAtoms(current_options); } void SmtEngine::debugCheckFormals(const std::vector<Expr>& formals, Expr func) diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index 672bec821..75737b603 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -141,6 +141,27 @@ class CVC4_PUBLIC SmtEngine public: /* ....................................................................... */ + /** + * The current mode of the solver, which is an extension of Figure 4.1 on + * page 52 of the SMT-LIB version 2.6 standard + * http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf + */ + enum SmtMode + { + // the initial state of the solver + SMT_MODE_START, + // normal state of the solver, after assert/push/pop/declare/define + SMT_MODE_ASSERT, + // immediately after a check-sat returning "sat" + SMT_MODE_SAT, + // immediately after a check-sat returning "unknown" + SMT_MODE_SAT_UNKNOWN, + // immediately after a check-sat returning "unsat" + SMT_MODE_UNSAT, + // immediately after a successful call to get-abduct + SMT_MODE_ABDUCT + }; + /** Construct an SmtEngine with the given expression manager. */ SmtEngine(ExprManager* em); /** Destruct the SMT engine. */ @@ -162,6 +183,9 @@ class CVC4_PUBLIC SmtEngine /** Return the user context level. */ size_t getNumUserLevels() { return d_userLevels.size(); } + /** Return the current mode of the solver. */ + SmtMode getSmtMode() { return d_smtMode; } + /** * Set the logic of the script. * @throw ModalException, LogicException @@ -189,6 +213,9 @@ class CVC4_PUBLIC SmtEngine */ void setInfo(const std::string& key, const CVC4::SExpr& value); + /** Return true if given keyword is a valid SMT-LIB v2 get-info flag. */ + bool isValidGetInfoFlag(const std::string& key) const; + /** Query information about the SMT environment. */ CVC4::SExpr getInfo(const std::string& key) const; @@ -838,27 +865,6 @@ class CVC4_PUBLIC SmtEngine /** The types for the recursive function definitions */ typedef context::CDList<Node> NodeList; - /** - * The current mode of the solver, which is an extension of Figure 4.1 on - * page 52 of the SMT-LIB version 2.6 standard - * http://smtlib.cs.uiowa.edu/papers/smt-lib-reference-v2.6-r2017-07-18.pdf - */ - enum SmtMode - { - // the initial state of the solver - SMT_MODE_START, - // normal state of the solver, after assert/push/pop/declare/define - SMT_MODE_ASSERT, - // immediately after a check-sat returning "sat" - SMT_MODE_SAT, - // immediately after a check-sat returning "unknown" - SMT_MODE_SAT_UNKNOWN, - // immediately after a check-sat returning "unsat" - SMT_MODE_UNSAT, - // immediately after a successful call to get-abduct - SMT_MODE_ABDUCT - }; - // disallow copy/assignment SmtEngine(const SmtEngine&) = delete; SmtEngine& operator=(const SmtEngine&) = delete; diff --git a/src/theory/arith/nl_constraint.cpp b/src/theory/arith/nl/nl_constraint.cpp index 8fb4535ea..c4c4dfbe7 100644 --- a/src/theory/arith/nl_constraint.cpp +++ b/src/theory/arith/nl/nl_constraint.cpp @@ -12,7 +12,7 @@ ** \brief Implementation of utilities for non-linear constraints **/ -#include "theory/arith/nl_constraint.h" +#include "theory/arith/nl/nl_constraint.h" #include "theory/arith/arith_msum.h" #include "theory/arith/arith_utilities.h" @@ -22,6 +22,7 @@ using namespace CVC4::kind; namespace CVC4 { namespace theory { namespace arith { +namespace nl { ConstraintDb::ConstraintDb(MonomialDb& mdb) : d_mdb(mdb) {} @@ -118,6 +119,7 @@ bool ConstraintDb::isMaximal(Node atom, Node x) const return itcm->second.find(x) != itcm->second.end(); } +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_constraint.h b/src/theory/arith/nl/nl_constraint.h index faa3cc812..e86ac4b66 100644 --- a/src/theory/arith/nl_constraint.h +++ b/src/theory/arith/nl/nl_constraint.h @@ -12,19 +12,20 @@ ** \brief Utilities for non-linear constraints **/ -#ifndef CVC4__THEORY__ARITH__NL_CONSTRAINT_H -#define CVC4__THEORY__ARITH__NL_CONSTRAINT_H +#ifndef CVC4__THEORY__ARITH__NL__NL_CONSTRAINT_H +#define CVC4__THEORY__ARITH__NL__NL_CONSTRAINT_H #include <map> #include <vector> #include "expr/kind.h" #include "expr/node.h" -#include "theory/arith/nl_monomial.h" +#include "theory/arith/nl/nl_monomial.h" namespace CVC4 { namespace theory { namespace arith { +namespace nl { /** constraint information * @@ -79,6 +80,7 @@ class ConstraintDb std::map<Node, std::map<Node, ConstraintInfo> > d_c_info; }; +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_lemma_utils.cpp b/src/theory/arith/nl/nl_lemma_utils.cpp index e43a77b06..ca34d91a9 100644 --- a/src/theory/arith/nl_lemma_utils.cpp +++ b/src/theory/arith/nl/nl_lemma_utils.cpp @@ -12,13 +12,14 @@ ** \brief Implementation of utilities for the non-linear solver **/ -#include "theory/arith/nl_lemma_utils.h" +#include "theory/arith/nl/nl_lemma_utils.h" -#include "theory/arith/nl_model.h" +#include "theory/arith/nl/nl_model.h" namespace CVC4 { namespace theory { namespace arith { +namespace nl { bool SortNlModel::operator()(Node i, Node j) { @@ -58,6 +59,7 @@ Node ArgTrie::add(Node d, const std::vector<Node>& args) return at->d_data; } +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_lemma_utils.h b/src/theory/arith/nl/nl_lemma_utils.h index bd729dad9..64a4deb17 100644 --- a/src/theory/arith/nl_lemma_utils.h +++ b/src/theory/arith/nl/nl_lemma_utils.h @@ -12,8 +12,8 @@ ** \brief Utilities for processing lemmas from the non-linear solver **/ -#ifndef CVC4__THEORY__ARITH__NL_LEMMA_UTILS_H -#define CVC4__THEORY__ARITH__NL_LEMMA_UTILS_H +#ifndef CVC4__THEORY__ARITH__NL__NL_LEMMA_UTILS_H +#define CVC4__THEORY__ARITH__NL__NL_LEMMA_UTILS_H #include <tuple> #include <vector> @@ -22,6 +22,7 @@ namespace CVC4 { namespace theory { namespace arith { +namespace nl { class NlModel; @@ -98,6 +99,7 @@ class ArgTrie Node add(Node d, const std::vector<Node>& args); }; +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_model.cpp b/src/theory/arith/nl/nl_model.cpp index 0d47c8874..d5df96bd8 100644 --- a/src/theory/arith/nl_model.cpp +++ b/src/theory/arith/nl/nl_model.cpp @@ -12,7 +12,7 @@ ** \brief Model object for the non-linear extension class **/ -#include "theory/arith/nl_model.h" +#include "theory/arith/nl/nl_model.h" #include "expr/node_algorithm.h" #include "options/arith_options.h" @@ -25,6 +25,7 @@ using namespace CVC4::kind; namespace CVC4 { namespace theory { namespace arith { +namespace nl { NlModel::NlModel(context::Context* c) : d_used_approx(false) { @@ -320,7 +321,7 @@ bool NlModel::checkModel(const std::vector<Node>& assertions, Node mg = nm->mkSkolem("model", nm->booleanType()); gs.push_back(mg); // assert the constructed model as assertions - for (const std::pair<const Node, std::pair<Node, Node> > cb : + for (const std::pair<const Node, std::pair<Node, Node>> cb : d_check_model_bounds) { Node l = cb.second.first; @@ -350,7 +351,7 @@ bool NlModel::addCheckModelSubstitution(TNode v, TNode s) } // if we previously had an approximate bound, the exact bound should be in its // range - std::map<Node, std::pair<Node, Node> >::iterator itb = + std::map<Node, std::pair<Node, Node>>::iterator itb = d_check_model_bounds.find(v); if (itb != d_check_model_bounds.end()) { @@ -852,7 +853,7 @@ bool NlModel::simpleCheckModelLit(Node lit) for (const Node& v : vs) { // is it a valid variable? - std::map<Node, std::pair<Node, Node> >::iterator bit = + std::map<Node, std::pair<Node, Node>>::iterator bit = d_check_model_bounds.find(v); if (!expr::hasSubterm(invalid_vsum, v) && bit != d_check_model_bounds.end()) { @@ -1041,7 +1042,7 @@ bool NlModel::simpleCheckModelMsum(const std::map<Node, Node>& msum, bool pol) } Trace("nl-ext-cms-debug") << " "; } - std::map<Node, std::pair<Node, Node> >::iterator bit = + std::map<Node, std::pair<Node, Node>>::iterator bit = d_check_model_bounds.find(vc); // if there is a model bound for this term if (bit != d_check_model_bounds.end()) @@ -1284,7 +1285,7 @@ void NlModel::getModelValueRepair( // values for variables that we solved for, using techniques specific to // this class. NodeManager* nm = NodeManager::currentNM(); - for (const std::pair<const Node, std::pair<Node, Node> >& cb : + for (const std::pair<const Node, std::pair<Node, Node>>& cb : d_check_model_bounds) { Node l = cb.second.first; @@ -1342,6 +1343,7 @@ void NlModel::getModelValueRepair( } } +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_model.h b/src/theory/arith/nl/nl_model.h index 5129a7a32..61193fc12 100644 --- a/src/theory/arith/nl_model.h +++ b/src/theory/arith/nl/nl_model.h @@ -12,8 +12,8 @@ ** \brief Model object for the non-linear extension class **/ -#ifndef CVC4__THEORY__ARITH__NL_MODEL_H -#define CVC4__THEORY__ARITH__NL_MODEL_H +#ifndef CVC4__THEORY__ARITH__NL__NL_MODEL_H +#define CVC4__THEORY__ARITH__NL__NL_MODEL_H #include <map> #include <unordered_map> @@ -28,6 +28,7 @@ namespace CVC4 { namespace theory { namespace arith { +namespace nl { class NonlinearExtension; @@ -307,7 +308,7 @@ class NlModel * (2) variables we have solved quadratic equations for, whose value * involves approximations of square roots. */ - std::map<Node, std::pair<Node, Node> > d_check_model_bounds; + std::map<Node, std::pair<Node, Node>> d_check_model_bounds; /** * The map from literals that our model construction solved, to the variable * that was solved for. Examples of such literals are: @@ -326,6 +327,7 @@ class NlModel std::unordered_set<Node, NodeHashFunction> d_tautology; }; /* class NlModel */ +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_monomial.cpp b/src/theory/arith/nl/nl_monomial.cpp index be472217d..e8e7aceba 100644 --- a/src/theory/arith/nl_monomial.cpp +++ b/src/theory/arith/nl/nl_monomial.cpp @@ -12,10 +12,10 @@ ** \brief Implementation of utilities for monomials **/ -#include "theory/arith/nl_monomial.h" +#include "theory/arith/nl/nl_monomial.h" #include "theory/arith/arith_utilities.h" -#include "theory/arith/nl_lemma_utils.h" +#include "theory/arith/nl/nl_lemma_utils.h" #include "theory/rewriter.h" using namespace CVC4::kind; @@ -23,6 +23,7 @@ using namespace CVC4::kind; namespace CVC4 { namespace theory { namespace arith { +namespace nl { // Returns a[key] if key is in a or value otherwise. unsigned getCountWithDefault(const NodeMultiset& a, Node key, unsigned value) @@ -329,6 +330,7 @@ Node MonomialDb::mkMonomialRemFactor(Node n, return ret; } +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_monomial.h b/src/theory/arith/nl/nl_monomial.h index 81665c4d9..b226730ac 100644 --- a/src/theory/arith/nl_monomial.h +++ b/src/theory/arith/nl/nl_monomial.h @@ -12,8 +12,8 @@ ** \brief Utilities for monomials **/ -#ifndef CVC4__THEORY__ARITH__NL_MONOMIAL_H -#define CVC4__THEORY__ARITH__NL_MONOMIAL_H +#ifndef CVC4__THEORY__ARITH__NL__NL_MONOMIAL_H +#define CVC4__THEORY__ARITH__NL__NL_MONOMIAL_H #include <map> #include <vector> @@ -23,6 +23,7 @@ namespace CVC4 { namespace theory { namespace arith { +namespace nl { class MonomialDb; class NlModel; @@ -140,6 +141,7 @@ class MonomialDb std::map<Node, std::map<Node, Node> > d_m_contain_umult; }; +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_solver.cpp b/src/theory/arith/nl/nl_solver.cpp index 893d3dbd7..12cb02c70 100644 --- a/src/theory/arith/nl_solver.cpp +++ b/src/theory/arith/nl/nl_solver.cpp @@ -12,7 +12,7 @@ ** \brief Implementation of non-linear solver **/ -#include "theory/arith/nl_solver.h" +#include "theory/arith/nl/nl_solver.h" #include "options/arith_options.h" #include "theory/arith/arith_msum.h" @@ -25,6 +25,7 @@ using namespace CVC4::kind; namespace CVC4 { namespace theory { namespace arith { +namespace nl { void debugPrintBound(const char* c, Node coeff, Node x, Kind type, Node rhs) { @@ -1580,6 +1581,7 @@ std::vector<Node> NlSolver::checkMonomialInferResBounds() return lemmas; } +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nl_solver.h b/src/theory/arith/nl/nl_solver.h index 73ca97f00..35c51569b 100644 --- a/src/theory/arith/nl_solver.h +++ b/src/theory/arith/nl/nl_solver.h @@ -27,15 +27,16 @@ #include "context/context.h" #include "expr/kind.h" #include "expr/node.h" -#include "theory/arith/nl_constraint.h" -#include "theory/arith/nl_lemma_utils.h" -#include "theory/arith/nl_model.h" -#include "theory/arith/nl_monomial.h" +#include "theory/arith/nl/nl_constraint.h" +#include "theory/arith/nl/nl_lemma_utils.h" +#include "theory/arith/nl/nl_model.h" +#include "theory/arith/nl/nl_monomial.h" #include "theory/arith/theory_arith.h" namespace CVC4 { namespace theory { namespace arith { +namespace nl { typedef std::map<Node, unsigned> NodeMultiset; @@ -361,6 +362,7 @@ class NlSolver Node getFactorSkolem(Node n, std::vector<Node>& lemmas); }; /* class NlSolver */ +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nonlinear_extension.cpp b/src/theory/arith/nl/nonlinear_extension.cpp index b638d8a59..4b2d2fd37 100644 --- a/src/theory/arith/nonlinear_extension.cpp +++ b/src/theory/arith/nl/nonlinear_extension.cpp @@ -15,7 +15,7 @@ ** \todo document this file **/ -#include "theory/arith/nonlinear_extension.h" +#include "theory/arith/nl/nonlinear_extension.h" #include "options/arith_options.h" #include "theory/arith/arith_utilities.h" @@ -28,6 +28,7 @@ using namespace CVC4::kind; namespace CVC4 { namespace theory { namespace arith { +namespace nl { NonlinearExtension::NonlinearExtension(TheoryArith& containing, eq::EqualityEngine* ee) @@ -49,27 +50,37 @@ NonlinearExtension::NonlinearExtension(TheoryArith& containing, NonlinearExtension::~NonlinearExtension() {} bool NonlinearExtension::getCurrentSubstitution( - int effort, const std::vector<Node>& vars, std::vector<Node>& subs, - std::map<Node, std::vector<Node> >& exp) { + int effort, + const std::vector<Node>& vars, + std::vector<Node>& subs, + std::map<Node, std::vector<Node>>& exp) +{ // get the constant equivalence classes - std::map<Node, std::vector<int> > rep_to_subs_index; + std::map<Node, std::vector<int>> rep_to_subs_index; bool retVal = false; - for (unsigned i = 0; i < vars.size(); i++) { + for (unsigned i = 0; i < vars.size(); i++) + { Node n = vars[i]; - if (d_ee->hasTerm(n)) { + if (d_ee->hasTerm(n)) + { Node nr = d_ee->getRepresentative(n); - if (nr.isConst()) { + if (nr.isConst()) + { subs.push_back(nr); Trace("nl-subs") << "Basic substitution : " << n << " -> " << nr << std::endl; exp[n].push_back(n.eqNode(nr)); retVal = true; - } else { + } + else + { rep_to_subs_index[nr].push_back(i); subs.push_back(n); } - } else { + } + else + { subs.push_back(n); } } @@ -79,8 +90,10 @@ bool NonlinearExtension::getCurrentSubstitution( } std::pair<bool, Node> NonlinearExtension::isExtfReduced( - int effort, Node n, Node on, const std::vector<Node>& exp) const { - if (n != d_zero) { + int effort, Node n, Node on, const std::vector<Node>& exp) const +{ + if (n != d_zero) + { Kind k = n.getKind(); return std::make_pair(k != NONLINEAR_MULT && !isTranscendentalKind(k), Node::null()); @@ -88,15 +101,15 @@ std::pair<bool, Node> NonlinearExtension::isExtfReduced( Assert(n == d_zero); if (on.getKind() == NONLINEAR_MULT) { - Trace("nl-ext-zero-exp") << "Infer zero : " << on << " == " << n - << std::endl; + Trace("nl-ext-zero-exp") + << "Infer zero : " << on << " == " << n << std::endl; // minimize explanation if a substitution+rewrite results in zero const std::set<Node> vars(on.begin(), on.end()); for (unsigned i = 0, size = exp.size(); i < size; i++) { - Trace("nl-ext-zero-exp") << " exp[" << i << "] = " << exp[i] - << std::endl; + Trace("nl-ext-zero-exp") + << " exp[" << i << "] = " << exp[i] << std::endl; std::vector<Node> eqs; if (exp[i].getKind() == EQUAL) { @@ -119,8 +132,8 @@ std::pair<bool, Node> NonlinearExtension::isExtfReduced( { if (eqs[j][r] == d_zero && vars.find(eqs[j][1 - r]) != vars.end()) { - Trace("nl-ext-zero-exp") << "...single exp : " << eqs[j] - << std::endl; + Trace("nl-ext-zero-exp") + << "...single exp : " << eqs[j] << std::endl; return std::make_pair(true, eqs[j]); } } @@ -337,9 +350,10 @@ std::vector<Node> NonlinearExtension::checkModelEval( const std::vector<Node>& assertions) { std::vector<Node> false_asserts; - for (size_t i = 0; i < assertions.size(); ++i) { + for (size_t i = 0; i < assertions.size(); ++i) + { Node lit = assertions[i]; - Node atom = lit.getKind()==NOT ? lit[0] : lit; + Node atom = lit.getKind() == NOT ? lit[0] : lit; Node litv = d_model.computeConcreteModelValue(lit); Trace("nl-ext-mv-assert") << "M[[ " << lit << " ]] -> " << litv; if (litv != d_true) @@ -403,7 +417,8 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, } //----------------------------------- possibly split on zero - if (options::nlExtSplitZero()) { + if (options::nlExtSplitZero()) + { Trace("nl-ext") << "Get zero split lemmas..." << std::endl; lemmas = d_nlSlv.checkSplitZero(); filterLemmas(lemmas, lems); @@ -415,7 +430,8 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, } } - //-----------------------------------initial lemmas for transcendental functions + //-----------------------------------initial lemmas for transcendental + //functions lemmas = d_trSlv.checkTranscendentalInitialRefine(); filterLemmas(lemmas, lems); if (!lems.empty()) @@ -445,8 +461,10 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, return lems.size(); } - //-----------------------------------lemmas based on magnitude of non-zero monomials - for (unsigned c = 0; c < 3; c++) { + //-----------------------------------lemmas based on magnitude of non-zero + //monomials + for (unsigned c = 0; c < 3; c++) + { // c is effort level lemmas = d_nlSlv.checkMonomialMagnitude(c); unsigned nlem = lemmas.size(); @@ -462,7 +480,7 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, //-----------------------------------inferred bounds lemmas // e.g. x >= t => y*x >= y*t - std::vector< Node > nt_lemmas; + std::vector<Node> nt_lemmas; lemmas = d_nlSlv.checkMonomialInferBounds(nt_lemmas, assertions, false_asserts); // Trace("nl-ext") << "Bound lemmas : " << lemmas.size() << ", " << @@ -494,7 +512,8 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, //------------------------------------factoring lemmas // x*y + x*z >= t => exists k. k = y + z ^ x*k >= t - if( options::nlExtFactor() ){ + if (options::nlExtFactor()) + { lemmas = d_nlSlv.checkFactoring(assertions, false_asserts); filterLemmas(lemmas, lems); if (!lems.empty()) @@ -507,7 +526,8 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, //------------------------------------resolution bound inferences // e.g. ( y>=0 ^ s <= x*z ^ x*y <= t ) => y*s <= z*t - if (options::nlExtResBound()) { + if (options::nlExtResBound()) + { lemmas = d_nlSlv.checkMonomialInferResBounds(); filterLemmas(lemmas, lems); if (!lems.empty()) @@ -517,7 +537,7 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, return lems.size(); } } - + //------------------------------------tangent planes if (options::nlExtTangentPlanes() && !options::nlExtTangentPlanesInterleave()) { @@ -535,7 +555,8 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, return 0; } -void NonlinearExtension::check(Theory::Effort e) { +void NonlinearExtension::check(Theory::Effort e) +{ Trace("nl-ext") << std::endl; Trace("nl-ext") << "NonlinearExtension::check, effort = " << e << ", built model = " << d_builtModel.get() << std::endl; @@ -543,15 +564,20 @@ void NonlinearExtension::check(Theory::Effort e) { { d_containing.getExtTheory()->clearCache(); d_needsLastCall = true; - if (options::nlExtRewrites()) { + if (options::nlExtRewrites()) + { std::vector<Node> nred; - if (!d_containing.getExtTheory()->doInferences(0, nred)) { + if (!d_containing.getExtTheory()->doInferences(0, nred)) + { Trace("nl-ext") << "...sent no lemmas, # extf to reduce = " << nred.size() << std::endl; - if (nred.empty()) { + if (nred.empty()) + { d_needsLastCall = false; } - } else { + } + else + { Trace("nl-ext") << "...sent lemmas." << std::endl; } } @@ -809,7 +835,7 @@ void NonlinearExtension::presolve() Trace("nl-ext") << "NonlinearExtension::presolve" << std::endl; } - +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nonlinear_extension.h b/src/theory/arith/nl/nonlinear_extension.h index 5aa2070a6..855310daa 100644 --- a/src/theory/arith/nonlinear_extension.h +++ b/src/theory/arith/nl/nonlinear_extension.h @@ -15,8 +15,8 @@ ** multiplication via axiom instantiations. **/ -#ifndef CVC4__THEORY__ARITH__NONLINEAR_EXTENSION_H -#define CVC4__THEORY__ARITH__NONLINEAR_EXTENSION_H +#ifndef CVC4__THEORY__ARITH__NL__NONLINEAR_EXTENSION_H +#define CVC4__THEORY__ARITH__NL__NONLINEAR_EXTENSION_H #include <stdint.h> #include <map> @@ -25,16 +25,17 @@ #include "context/cdlist.h" #include "expr/kind.h" #include "expr/node.h" -#include "theory/arith/nl_lemma_utils.h" -#include "theory/arith/nl_model.h" -#include "theory/arith/nl_solver.h" +#include "theory/arith/nl/nl_lemma_utils.h" +#include "theory/arith/nl/nl_model.h" +#include "theory/arith/nl/nl_solver.h" +#include "theory/arith/nl/transcendental_solver.h" #include "theory/arith/theory_arith.h" -#include "theory/arith/transcendental_solver.h" #include "theory/uf/equality_engine.h" namespace CVC4 { namespace theory { namespace arith { +namespace nl { /** Non-linear extension class * @@ -60,7 +61,8 @@ namespace arith { * for valid arithmetic theory lemmas, based on the current set of assertions, * where d_out is the output channel of TheoryArith. */ -class NonlinearExtension { +class NonlinearExtension +{ typedef context::CDHashSet<Node, NodeHashFunction> NodeSet; public: @@ -84,9 +86,10 @@ class NonlinearExtension { * that hold in the current context. We call { vars -> subs } a "derivable * substituion" (see Reynolds et al. FroCoS 2017). */ - bool getCurrentSubstitution(int effort, const std::vector<Node>& vars, + bool getCurrentSubstitution(int effort, + const std::vector<Node>& vars, std::vector<Node>& subs, - std::map<Node, std::vector<Node> >& exp); + std::map<Node, std::vector<Node>>& exp); /** Is the term n in reduced form? * * Used for context-dependent simplification. @@ -103,7 +106,9 @@ class NonlinearExtension { * The second part of the pair is used for constructing * minimal explanations for context-dependent simplifications. */ - std::pair<bool, Node> isExtfReduced(int effort, Node n, Node on, + std::pair<bool, Node> isExtfReduced(int effort, + Node n, + Node on, const std::vector<Node>& exp) const; /** Check at effort level e. * @@ -157,6 +162,7 @@ class NonlinearExtension { * on the output channel of TheoryArith in this function. */ void presolve(); + private: /** Model-based refinement * @@ -179,7 +185,6 @@ class NonlinearExtension { std::vector<Node>& mlemsPp, std::map<Node, NlLemmaSideEffect>& lemSE); - /** check last call * * Check assertions for consistency in the effort LAST_CALL with a subset of @@ -328,6 +333,7 @@ class NonlinearExtension { context::CDO<bool> d_builtModel; }; /* class NonlinearExtension */ +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/transcendental_solver.cpp b/src/theory/arith/nl/transcendental_solver.cpp index 665accc0a..3e10f6397 100644 --- a/src/theory/arith/transcendental_solver.cpp +++ b/src/theory/arith/nl/transcendental_solver.cpp @@ -12,7 +12,7 @@ ** \brief Implementation of solver for handling transcendental functions. **/ -#include "theory/arith/transcendental_solver.h" +#include "theory/arith/nl/transcendental_solver.h" #include <cmath> #include <set> @@ -29,6 +29,7 @@ using namespace CVC4::kind; namespace CVC4 { namespace theory { namespace arith { +namespace nl { TranscendentalSolver::TranscendentalSolver(NlModel& m) : d_model(m) { @@ -1470,6 +1471,7 @@ Node TranscendentalSolver::mkValidPhase(Node a, Node pi) NodeManager::currentNM()->mkNode(MULT, mkRationalNode(-1), pi), a, pi); } +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/transcendental_solver.h b/src/theory/arith/nl/transcendental_solver.h index 88de49af3..5cd57d8fa 100644 --- a/src/theory/arith/transcendental_solver.h +++ b/src/theory/arith/nl/transcendental_solver.h @@ -12,8 +12,8 @@ ** \brief Solving for handling transcendental functions. **/ -#ifndef CVC4__THEORY__ARITH__TRANSCENDENTAL_SOLVER_H -#define CVC4__THEORY__ARITH__TRANSCENDENTAL_SOLVER_H +#ifndef CVC4__THEORY__ARITH__NL__TRANSCENDENTAL_SOLVER_H +#define CVC4__THEORY__ARITH__NL__TRANSCENDENTAL_SOLVER_H #include <map> #include <unordered_map> @@ -21,12 +21,13 @@ #include <vector> #include "expr/node.h" -#include "theory/arith/nl_lemma_utils.h" -#include "theory/arith/nl_model.h" +#include "theory/arith/nl/nl_lemma_utils.h" +#include "theory/arith/nl/nl_model.h" namespace CVC4 { namespace theory { namespace arith { +namespace nl { /** Transcendental solver class * @@ -421,6 +422,7 @@ class TranscendentalSolver Node d_pi_bound[2]; }; /* class TranscendentalSolver */ +} // namespace nl } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 09b6d742a..3ff3966cc 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -55,7 +55,7 @@ #include "theory/arith/dio_solver.h" #include "theory/arith/linear_equality.h" #include "theory/arith/matrix.h" -#include "theory/arith/nonlinear_extension.h" +#include "theory/arith/nl/nonlinear_extension.h" #include "theory/arith/normal_form.h" #include "theory/arith/partial_model.h" #include "theory/arith/simplex.h" @@ -162,7 +162,7 @@ TheoryArithPrivate::TheoryArithPrivate(TheoryArith& containing, d_nlin_inverse_skolem(u) { if( options::nlExt() ){ - d_nonlinearExtension = new NonlinearExtension( + d_nonlinearExtension = new nl::NonlinearExtension( containing, d_congruenceManager.getEqualityEngine()); } } @@ -1112,14 +1112,8 @@ void TheoryArithPrivate::checkNonLinearLogic(Node term) } } -struct ArithElimOpAttributeId -{ -}; -typedef expr::Attribute<ArithElimOpAttributeId, Node> ArithElimOpAttribute; - Node TheoryArithPrivate::eliminateOperatorsRec(Node n) { - ArithElimOpAttribute aeoa; Trace("arith-elim") << "Begin elim: " << n << std::endl; NodeManager* nm = NodeManager::currentNM(); std::unordered_map<Node, Node, TNodeHashFunction> visited; @@ -1138,18 +1132,11 @@ Node TheoryArithPrivate::eliminateOperatorsRec(Node n) } else if (it == visited.end()) { - if (cur.hasAttribute(aeoa)) - { - visited[cur] = cur.getAttribute(aeoa); - } - else + visited[cur] = Node::null(); + visit.push_back(cur); + for (const Node& cn : cur) { - visited[cur] = Node::null(); - visit.push_back(cur); - for (const Node& cn : cur) - { - visit.push_back(cn); - } + visit.push_back(cn); } } else if (it->second.isNull()) @@ -1180,7 +1167,6 @@ Node TheoryArithPrivate::eliminateOperatorsRec(Node n) // are defined in terms of other non-standard operators. ret = eliminateOperatorsRec(retElim); } - cur.setAttribute(aeoa, ret); visited[cur] = ret; } } while (!visit.empty()); diff --git a/src/theory/arith/theory_arith_private.h b/src/theory/arith/theory_arith_private.h index 8198dbcf1..822b38f69 100644 --- a/src/theory/arith/theory_arith_private.h +++ b/src/theory/arith/theory_arith_private.h @@ -83,7 +83,9 @@ namespace inferbounds { } class InferBoundsResult; +namespace nl { class NonlinearExtension; +} /** * Implementation of QF_LRA. @@ -372,7 +374,7 @@ private: AttemptSolutionSDP d_attemptSolSimplex; /** non-linear algebraic approach */ - NonlinearExtension * d_nonlinearExtension; + nl::NonlinearExtension* d_nonlinearExtension; bool solveRealRelaxation(Theory::Effort effortLevel); diff --git a/src/theory/booleans/proof_checker.cpp b/src/theory/booleans/proof_checker.cpp new file mode 100644 index 000000000..8fdbe9f6b --- /dev/null +++ b/src/theory/booleans/proof_checker.cpp @@ -0,0 +1,568 @@ +/********************* */ +/*! \file proof_checker.cpp + ** \verbatim + ** Top contributors (to current version): + ** Haniel Barbosa, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of equality proof checker + **/ + +#include "theory/booleans/proof_checker.h" + +namespace CVC4 { +namespace theory { +namespace booleans { + +void BoolProofRuleChecker::registerTo(ProofChecker* pc) +{ + pc->registerChecker(PfRule::SPLIT, this); + pc->registerChecker(PfRule::AND_ELIM, this); + pc->registerChecker(PfRule::AND_INTRO, this); + pc->registerChecker(PfRule::NOT_OR_ELIM, this); + pc->registerChecker(PfRule::IMPLIES_ELIM, this); + pc->registerChecker(PfRule::NOT_IMPLIES_ELIM1, this); + pc->registerChecker(PfRule::NOT_IMPLIES_ELIM2, this); + pc->registerChecker(PfRule::EQUIV_ELIM1, this); + pc->registerChecker(PfRule::EQUIV_ELIM2, this); + pc->registerChecker(PfRule::NOT_EQUIV_ELIM1, this); + pc->registerChecker(PfRule::NOT_EQUIV_ELIM2, this); + pc->registerChecker(PfRule::XOR_ELIM1, this); + pc->registerChecker(PfRule::XOR_ELIM2, this); + pc->registerChecker(PfRule::NOT_XOR_ELIM1, this); + pc->registerChecker(PfRule::NOT_XOR_ELIM2, this); + pc->registerChecker(PfRule::ITE_ELIM1, this); + pc->registerChecker(PfRule::ITE_ELIM2, this); + pc->registerChecker(PfRule::NOT_ITE_ELIM1, this); + pc->registerChecker(PfRule::NOT_ITE_ELIM2, this); + pc->registerChecker(PfRule::NOT_AND, this); + pc->registerChecker(PfRule::CNF_AND_POS, this); + pc->registerChecker(PfRule::CNF_AND_NEG, this); + pc->registerChecker(PfRule::CNF_OR_POS, this); + pc->registerChecker(PfRule::CNF_OR_NEG, this); + pc->registerChecker(PfRule::CNF_IMPLIES_POS, this); + pc->registerChecker(PfRule::CNF_IMPLIES_NEG1, this); + pc->registerChecker(PfRule::CNF_IMPLIES_NEG2, this); + pc->registerChecker(PfRule::CNF_EQUIV_POS1, this); + pc->registerChecker(PfRule::CNF_EQUIV_POS2, this); + pc->registerChecker(PfRule::CNF_EQUIV_NEG1, this); + pc->registerChecker(PfRule::CNF_EQUIV_NEG2, this); + pc->registerChecker(PfRule::CNF_XOR_POS1, this); + pc->registerChecker(PfRule::CNF_XOR_POS2, this); + pc->registerChecker(PfRule::CNF_XOR_NEG1, this); + pc->registerChecker(PfRule::CNF_XOR_NEG2, this); + pc->registerChecker(PfRule::CNF_ITE_POS1, this); + pc->registerChecker(PfRule::CNF_ITE_POS2, this); + pc->registerChecker(PfRule::CNF_ITE_POS3, this); + pc->registerChecker(PfRule::CNF_ITE_NEG1, this); + pc->registerChecker(PfRule::CNF_ITE_NEG2, this); + pc->registerChecker(PfRule::CNF_ITE_NEG3, this); +} + +Node BoolProofRuleChecker::checkInternal(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args) +{ + if (id == PfRule::SPLIT) + { + Assert(children.empty()); + Assert(args.size() == 1); + return NodeManager::currentNM()->mkNode( + kind::OR, args[0], args[0].notNode()); + } + // natural deduction rules + if (id == PfRule::AND_ELIM) + { + Assert(children.size() == 1); + Assert(args.size() == 1); + uint32_t i; + if (children[0].getKind() != kind::AND || !getUInt32(args[0], i)) + { + return Node::null(); + } + if (i >= children[0].getNumChildren()) + { + return Node::null(); + } + return children[0][i]; + } + if (id == PfRule::AND_INTRO) + { + Assert(children.size() >= 1); + return children.size() == 1 + ? children[0] + : NodeManager::currentNM()->mkNode(kind::AND, children); + } + if (id == PfRule::NOT_OR_ELIM) + { + Assert(children.size() == 1); + Assert(args.size() == 1); + uint32_t i; + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::OR || !getUInt32(args[0], i)) + { + return Node::null(); + } + if (i >= children[0][0].getNumChildren()) + { + return Node::null(); + } + return children[0][0][i].notNode(); + } + if (id == PfRule::IMPLIES_ELIM) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::IMPLIES) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0].notNode(), children[0][1]); + } + if (id == PfRule::NOT_IMPLIES_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::IMPLIES) + { + return Node::null(); + } + return children[0][0][0]; + } + if (id == PfRule::NOT_IMPLIES_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::IMPLIES) + { + return Node::null(); + } + return children[0][0][1].notNode(); + } + if (id == PfRule::EQUIV_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::EQUAL) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0].notNode(), children[0][1]); + } + if (id == PfRule::EQUIV_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::EQUAL) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0], children[0][1].notNode()); + } + if (id == PfRule::NOT_EQUIV_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::EQUAL) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0][0], children[0][0][1]); + } + if (id == PfRule::NOT_EQUIV_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::EQUAL) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0][0].notNode(), children[0][0][1].notNode()); + } + if (id == PfRule::XOR_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::XOR) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0], children[0][1]); + } + if (id == PfRule::XOR_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::XOR) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0].notNode(), children[0][1].notNode()); + } + if (id == PfRule::NOT_XOR_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::XOR) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0][0], children[0][0][1].notNode()); + } + if (id == PfRule::NOT_XOR_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::XOR) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0][0].notNode(), children[0][0][1]); + } + if (id == PfRule::ITE_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::ITE) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0].notNode(), children[0][1]); + } + if (id == PfRule::ITE_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::ITE) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0], children[0][2]); + } + if (id == PfRule::NOT_ITE_ELIM1) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::ITE) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0][0].notNode(), children[0][0][1].notNode()); + } + if (id == PfRule::NOT_ITE_ELIM2) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::ITE) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, children[0][0][0], children[0][0][2].notNode()); + } + // De Morgan + if (id == PfRule::NOT_AND) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT + || children[0][0].getKind() != kind::AND) + { + return Node::null(); + } + std::vector<Node> disjuncts; + for (unsigned i = 0, size = children[0][0].getNumChildren(); i < size; ++i) + { + disjuncts.push_back(children[0][0][i].notNode()); + } + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + // valid clauses rules for Tseitin CNF transformation + if (id == PfRule::CNF_AND_POS) + { + Assert(children.empty()); + Assert(args.size() == 2); + uint32_t i; + if (args[0].getKind() != kind::AND || !getUInt32(args[1], i)) + { + return Node::null(); + } + if (i >= args[0].getNumChildren()) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, args[0].notNode(), args[0][i]); + } + if (id == PfRule::CNF_AND_NEG) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::AND) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0]}; + for (unsigned i = 0, size = args[0].getNumChildren(); i < size; ++i) + { + disjuncts.push_back(args[0][i].notNode()); + } + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_OR_POS) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::OR) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0].notNode()}; + for (unsigned i = 0, size = args[0].getNumChildren(); i < size; ++i) + { + disjuncts.push_back(args[0][i]); + } + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_OR_NEG) + { + Assert(children.empty()); + Assert(args.size() == 2); + uint32_t i; + if (args[0].getKind() != kind::OR || !getUInt32(args[1], i)) + { + return Node::null(); + } + if (i >= args[0].getNumChildren()) + { + return Node::null(); + } + return NodeManager::currentNM()->mkNode( + kind::OR, args[0], args[0][i].notNode()); + } + if (id == PfRule::CNF_IMPLIES_POS) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::IMPLIES) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0].notNode(), args[0][0].notNode(), args[0][1]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_IMPLIES_NEG1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::IMPLIES) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0], args[0][0]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_IMPLIES_NEG2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::IMPLIES) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0], args[0][1].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_EQUIV_POS1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::EQUAL) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0].notNode(), args[0][0].notNode(), args[0][1]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_EQUIV_POS2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::EQUAL) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0].notNode(), args[0][0], args[0][1].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_EQUIV_NEG1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::EQUAL) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0], args[0][0], args[0][1]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_EQUIV_NEG2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::EQUAL) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0], args[0][0].notNode(), args[0][1].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_XOR_POS1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::XOR) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0].notNode(), args[0][0], args[0][1]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_XOR_POS2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::XOR) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0].notNode(), args[0][0].notNode(), args[0][1].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_XOR_NEG1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::XOR) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0], args[0][0].notNode(), args[0][1]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_XOR_NEG2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::XOR) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0], args[0][0], args[0][1].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_ITE_POS1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::ITE) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0].notNode(), args[0][0].notNode(), args[0][1]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_ITE_POS2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::ITE) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0].notNode(), args[0][0], args[0][2]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_ITE_POS3) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::ITE) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0].notNode(), args[0][1], args[0][2]}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_ITE_NEG1) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::ITE) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0], args[0][0].notNode(), args[0][1].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_ITE_NEG2) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::ITE) + { + return Node::null(); + } + std::vector<Node> disjuncts{args[0], args[0][0], args[0][2].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + if (id == PfRule::CNF_ITE_NEG3) + { + Assert(children.empty()); + Assert(args.size() == 1); + if (args[0].getKind() != kind::ITE) + { + return Node::null(); + } + std::vector<Node> disjuncts{ + args[0], args[0][1].notNode(), args[0][2].notNode()}; + return NodeManager::currentNM()->mkNode(kind::OR, disjuncts); + } + // no rule + return Node::null(); +} + +} // namespace booleans +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/booleans/proof_checker.h b/src/theory/booleans/proof_checker.h new file mode 100644 index 000000000..56414ff91 --- /dev/null +++ b/src/theory/booleans/proof_checker.h @@ -0,0 +1,49 @@ +/********************* */ +/*! \file proof_checker.h + ** \verbatim + ** Top contributors (to current version): + ** Haniel Barbosa, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Boolean proof checker utility + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__BOOLEANS__PROOF_CHECKER_H +#define CVC4__THEORY__BOOLEANS__PROOF_CHECKER_H + +#include "expr/node.h" +#include "expr/proof_checker.h" +#include "expr/proof_node.h" + +namespace CVC4 { +namespace theory { +namespace booleans { + +/** A checker for boolean reasoning in proofs */ +class BoolProofRuleChecker : public ProofRuleChecker +{ + public: + BoolProofRuleChecker() {} + ~BoolProofRuleChecker() {} + + /** Register all rules owned by this rule checker into pc. */ + void registerTo(ProofChecker* pc) override; + + protected: + /** Return the conclusion of the given proof step, or null if it is invalid */ + Node checkInternal(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args) override; +}; + +} // namespace booleans +} // namespace theory +} // namespace CVC4 + +#endif /* CVC4__THEORY__BOOLEANS__PROOF_CHECKER_H */ diff --git a/src/theory/builtin/proof_checker.cpp b/src/theory/builtin/proof_checker.cpp new file mode 100644 index 000000000..8d2a1540c --- /dev/null +++ b/src/theory/builtin/proof_checker.cpp @@ -0,0 +1,359 @@ +/********************* */ +/*! \file proof_checker.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of equality proof checker + **/ + +#include "theory/builtin/proof_checker.h" + +#include "expr/proof_skolem_cache.h" +#include "theory/rewriter.h" +#include "theory/theory.h" + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { + +const char* toString(MethodId id) +{ + switch (id) + { + case MethodId::RW_REWRITE: return "RW_REWRITE"; + case MethodId::RW_IDENTITY: return "RW_IDENTITY"; + case MethodId::SB_DEFAULT: return "SB_DEFAULT"; + case MethodId::SB_LITERAL: return "SB_LITERAL"; + case MethodId::SB_FORMULA: return "SB_FORMULA"; + default: return "MethodId::Unknown"; + }; +} + +std::ostream& operator<<(std::ostream& out, MethodId id) +{ + out << toString(id); + return out; +} + +Node mkMethodId(MethodId id) +{ + return NodeManager::currentNM()->mkConst(Rational(static_cast<uint32_t>(id))); +} + +namespace builtin { + +void BuiltinProofRuleChecker::registerTo(ProofChecker* pc) +{ + pc->registerChecker(PfRule::ASSUME, this); + pc->registerChecker(PfRule::SCOPE, this); + pc->registerChecker(PfRule::SUBS, this); + pc->registerChecker(PfRule::REWRITE, this); + pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this); + pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this); + pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this); + pc->registerChecker(PfRule::MACRO_SR_PRED_TRANSFORM, this); +} + +Node BuiltinProofRuleChecker::applyRewrite(Node n, MethodId idr) +{ + Node nk = ProofSkolemCache::getSkolemForm(n); + Node nkr = applyRewriteExternal(nk, idr); + return ProofSkolemCache::getWitnessForm(nkr); +} + +Node BuiltinProofRuleChecker::applySubstitution(Node n, Node exp, MethodId ids) +{ + if (exp.isNull() || exp.getKind() != EQUAL) + { + return Node::null(); + } + Node nk = ProofSkolemCache::getSkolemForm(n); + Node nks = applySubstitutionExternal(nk, exp, ids); + return ProofSkolemCache::getWitnessForm(nks); +} + +Node BuiltinProofRuleChecker::applySubstitution(Node n, + const std::vector<Node>& exp, + MethodId ids) +{ + Node nk = ProofSkolemCache::getSkolemForm(n); + Node nks = applySubstitutionExternal(nk, exp, ids); + return ProofSkolemCache::getWitnessForm(nks); +} + +Node BuiltinProofRuleChecker::applySubstitutionRewrite( + Node n, const std::vector<Node>& exp, MethodId ids, MethodId idr) +{ + Node nk = ProofSkolemCache::getSkolemForm(n); + Node nks = applySubstitutionExternal(nk, exp, ids); + Node nksr = applyRewriteExternal(nks, idr); + return ProofSkolemCache::getWitnessForm(nksr); +} + +Node BuiltinProofRuleChecker::applyRewriteExternal(Node n, MethodId idr) +{ + Trace("builtin-pfcheck-debug") + << "applyRewriteExternal (" << idr << "): " << n << std::endl; + if (idr == MethodId::RW_REWRITE) + { + return Rewriter::rewrite(n); + } + else if (idr == MethodId::RW_IDENTITY) + { + // does nothing + return n; + } + // unknown rewriter + Assert(false) + << "BuiltinProofRuleChecker::applyRewriteExternal: no rewriter for " + << idr << std::endl; + return n; +} + +Node BuiltinProofRuleChecker::applySubstitutionExternal(Node n, + Node exp, + MethodId ids) +{ + Assert(!exp.isNull()); + Node expk = ProofSkolemCache::getSkolemForm(exp); + TNode var, subs; + if (ids == MethodId::SB_DEFAULT) + { + if (expk.getKind() != EQUAL) + { + return Node::null(); + } + var = expk[0]; + subs = expk[1]; + } + else if (ids == MethodId::SB_LITERAL) + { + bool polarity = expk.getKind() != NOT; + var = polarity ? expk : expk[0]; + subs = NodeManager::currentNM()->mkConst(polarity); + } + else if (ids == MethodId::SB_FORMULA) + { + var = expk; + subs = NodeManager::currentNM()->mkConst(true); + } + else + { + Assert(false) << "BuiltinProofRuleChecker::applySubstitutionExternal: no " + "substitution for " + << ids << std::endl; + } + return n.substitute(var, subs); +} + +Node BuiltinProofRuleChecker::applySubstitutionExternal( + Node n, const std::vector<Node>& exp, MethodId ids) +{ + Node curr = n; + // apply substitution one at a time, in reverse order + for (size_t i = 0, nexp = exp.size(); i < nexp; i++) + { + if (exp[nexp - 1 - i].isNull()) + { + return Node::null(); + } + curr = applySubstitutionExternal(curr, exp[nexp - 1 - i], ids); + if (curr.isNull()) + { + break; + } + } + return curr; +} + +bool BuiltinProofRuleChecker::getMethodId(TNode n, MethodId& i) +{ + uint32_t index; + if (!getUInt32(n, index)) + { + return false; + } + i = static_cast<MethodId>(index); + return true; +} + +Node BuiltinProofRuleChecker::checkInternal(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args) +{ + // compute what was proven + if (id == PfRule::ASSUME) + { + Assert(children.empty()); + Assert(args.size() == 1); + Assert(args[0].getType().isBoolean()); + return args[0]; + } + else if (id == PfRule::SCOPE) + { + Assert(children.size() == 1); + if (args.empty()) + { + // no antecedant + return children[0]; + } + Node ant = mkAnd(args); + // if the conclusion is false, its the negated antencedant only + if (children[0].isConst() && !children[0].getConst<bool>()) + { + return ant.notNode(); + } + return NodeManager::currentNM()->mkNode(IMPLIES, ant, children[0]); + } + else if (id == PfRule::SUBS) + { + Assert(children.size() > 0); + Assert(1 <= args.size() && args.size() <= 2); + MethodId ids = MethodId::SB_DEFAULT; + if (args.size() == 2 && !getMethodId(args[1], ids)) + { + return Node::null(); + } + std::vector<Node> exp; + for (size_t i = 0, nchild = children.size(); i < nchild; i++) + { + exp.push_back(children[i]); + } + Node res = applySubstitution(args[0], exp); + return args[0].eqNode(res); + } + else if (id == PfRule::REWRITE) + { + Assert(children.empty()); + Assert(1 <= args.size() && args.size() <= 2); + MethodId ids = MethodId::RW_REWRITE; + if (args.size() == 2 && !getMethodId(args[1], ids)) + { + return Node::null(); + } + Node res = applyRewrite(args[0]); + return args[0].eqNode(res); + } + else if (id == PfRule::MACRO_SR_EQ_INTRO) + { + Assert(1 <= args.size() && args.size() <= 3); + MethodId ids, idr; + if (!getMethodIds(args, ids, idr, 1)) + { + return Node::null(); + } + Node res = applySubstitutionRewrite(args[0], children, idr); + return args[0].eqNode(res); + } + else if (id == PfRule::MACRO_SR_PRED_INTRO) + { + Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " " + << args.size() << std::endl; + Assert(1 <= args.size() && args.size() <= 3); + MethodId ids, idr; + if (!getMethodIds(args, ids, idr, 1)) + { + return Node::null(); + } + Node res = applySubstitutionRewrite(args[0], children, ids, idr); + if (res.isNull()) + { + return Node::null(); + } + // **** NOTE: can rewrite the witness form here. This enables certain lemmas + // to be provable, e.g. (= k t) where k is a purification Skolem for t. + res = Rewriter::rewrite(res); + if (!res.isConst() || !res.getConst<bool>()) + { + Trace("builtin-pfcheck") + << "Failed to rewrite to true, res=" << res << std::endl; + return Node::null(); + } + return args[0]; + } + else if (id == PfRule::MACRO_SR_PRED_ELIM) + { + Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " " + << args.size() << std::endl; + Assert(children.size() >= 1); + Assert(args.size() <= 2); + std::vector<Node> exp; + exp.insert(exp.end(), children.begin() + 1, children.end()); + MethodId ids, idr; + if (!getMethodIds(args, ids, idr, 0)) + { + return Node::null(); + } + Node res1 = applySubstitutionRewrite(children[0], exp, ids, idr); + Trace("builtin-pfcheck") << "Returned " << res1 << std::endl; + return res1; + } + else if (id == PfRule::MACRO_SR_PRED_TRANSFORM) + { + Trace("builtin-pfcheck") << "Check " << id << " " << children.size() << " " + << args.size() << std::endl; + Assert(children.size() >= 1); + Assert(1 <= args.size() && args.size() <= 3); + Assert(args[0].getType().isBoolean()); + MethodId ids, idr; + if (!getMethodIds(args, ids, idr, 1)) + { + return Node::null(); + } + std::vector<Node> exp; + exp.insert(exp.end(), children.begin() + 1, children.end()); + Node res1 = applySubstitutionRewrite(children[0], exp, ids, idr); + Node res2 = applySubstitutionRewrite(args[0], exp, ids, idr); + // can rewrite the witness forms + res1 = Rewriter::rewrite(res1); + res2 = Rewriter::rewrite(res2); + if (res1.isNull() || res1 != res2) + { + Trace("builtin-pfcheck") << "Failed to match results" << std::endl; + Trace("builtin-pfcheck-debug") << res1 << " vs " << res2 << std::endl; + return Node::null(); + } + return args[0]; + } + // no rule + return Node::null(); +} + +bool BuiltinProofRuleChecker::getMethodIds(const std::vector<Node>& args, + MethodId& ids, + MethodId& idr, + size_t index) +{ + ids = MethodId::SB_DEFAULT; + idr = MethodId::RW_REWRITE; + if (args.size() > index) + { + if (!getMethodId(args[index], ids)) + { + Trace("builtin-pfcheck") + << "Failed to get id from " << args[index] << std::endl; + return false; + } + } + if (args.size() > index + 1) + { + if (!getMethodId(args[index + 1], idr)) + { + Trace("builtin-pfcheck") + << "Failed to get id from " << args[index + 1] << std::endl; + return false; + } + } + return true; +} + +} // namespace builtin +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/builtin/proof_checker.h b/src/theory/builtin/proof_checker.h new file mode 100644 index 000000000..f4424b107 --- /dev/null +++ b/src/theory/builtin/proof_checker.h @@ -0,0 +1,150 @@ +/********************* */ +/*! \file proof_checker.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Equality proof checker utility + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__BUILTIN__PROOF_CHECKER_H +#define CVC4__THEORY__BUILTIN__PROOF_CHECKER_H + +#include "expr/node.h" +#include "expr/proof_checker.h" +#include "expr/proof_node.h" + +namespace CVC4 { +namespace theory { + +/** + * Identifiers for rewriters and substitutions, which we abstractly + * classify as "methods". Methods have a unique identifier in the internal + * proof calculus implemented by the checker below. + * + * A "rewriter" is abstractly a method from Node to Node, where the output + * is semantically equivalent to the input. The identifiers below list + * various methods that have this contract. This identifier is used + * in a number of the builtin rules. + * + * A substitution is a method for turning a formula into a substitution. + */ +enum class MethodId : uint32_t +{ + //---------------------------- Rewriters + // Rewriter::rewrite(n) + RW_REWRITE, + // identity + RW_IDENTITY, + //---------------------------- Substitutions + // (= x y) is interpreted as x -> y, using Node::substitute + SB_DEFAULT, + // P, (not P) are interpreted as P -> true, P -> false using Node::substitute + SB_LITERAL, + // P is interpreted as P -> true using Node::substitute + SB_FORMULA, +}; +/** Converts a rewriter id to a string. */ +const char* toString(MethodId id); +/** Write a rewriter id to out */ +std::ostream& operator<<(std::ostream& out, MethodId id); +/** Make a method id node */ +Node mkMethodId(MethodId id); + +namespace builtin { + +/** A checker for builtin proofs */ +class BuiltinProofRuleChecker : public ProofRuleChecker +{ + public: + BuiltinProofRuleChecker() {} + ~BuiltinProofRuleChecker() {} + /** + * Apply rewrite on n (in witness form). This encapsulates the exact behavior + * of a REWRITE step in a proof. Rewriting is performed on the Skolem form of + * n. + * + * @param n The node (in witness form) to rewrite, + * @param idr The method identifier of the rewriter, by default RW_REWRITE + * specifying a call to Rewriter::rewrite. + * @return The rewritten form of n. + */ + static Node applyRewrite(Node n, MethodId idr = MethodId::RW_REWRITE); + /** + * Apply substitution on n (in witness form). This encapsulates the exact + * behavior of a SUBS step in a proof. Substitution is on the Skolem form of + * n. + * + * @param n The node (in witness form) to substitute, + * @param exp The (set of) equalities (in witness form) corresponding to the + * substitution + * @param ids The method identifier of the substitution, by default SB_DEFAULT + * specifying that lhs/rhs of equalities are interpreted as a substitution. + * @return The substituted form of n. + */ + static Node applySubstitution(Node n, + Node exp, + MethodId ids = MethodId::SB_DEFAULT); + static Node applySubstitution(Node n, + const std::vector<Node>& exp, + MethodId ids = MethodId::SB_DEFAULT); + /** Apply substitution + rewriting + * + * Combines the above two steps. + * + * @param n The node (in witness form) to substitute and rewrite, + * @param exp The (set of) equalities (in witness form) corresponding to the + * substitution + * @param ids The method identifier of the substitution. + * @param idr The method identifier of the rewriter. + * @return The substituted, rewritten form of n. + */ + static Node applySubstitutionRewrite(Node n, + const std::vector<Node>& exp, + MethodId ids = MethodId::SB_DEFAULT, + MethodId idr = MethodId::RW_REWRITE); + /** get a rewriter Id from a node, return false if we fail */ + static bool getMethodId(TNode n, MethodId& i); + + /** Register all rules owned by this rule checker into pc. */ + void registerTo(ProofChecker* pc) override; + + protected: + /** Return the conclusion of the given proof step, or null if it is invalid */ + Node checkInternal(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args) override; + /** get method identifiers */ + bool getMethodIds(const std::vector<Node>& args, + MethodId& ids, + MethodId& idr, + size_t index); + /** + * Apply rewrite (on Skolem form). id is the identifier of the rewriter. + */ + static Node applyRewriteExternal(Node n, MethodId idr = MethodId::RW_REWRITE); + /** + * Apply substitution for n (on Skolem form), where exp is an equality + * (or set of equalities) in Witness form. Returns the result of + * n * sigma{ids}(exp), where sigma{ids} is a substitution based on method + * identifier ids. + */ + static Node applySubstitutionExternal(Node n, Node exp, MethodId ids); + /** Same as above, for a list of substitutions in exp */ + static Node applySubstitutionExternal(Node n, + const std::vector<Node>& exp, + MethodId ids); +}; + +} // namespace builtin +} // namespace theory +} // namespace CVC4 + +#endif /* CVC4__THEORY__BUILTIN__PROOF_CHECKER_H */ diff --git a/src/theory/datatypes/theory_datatypes_utils.cpp b/src/theory/datatypes/theory_datatypes_utils.cpp index ee0fd814e..ea67ab79d 100644 --- a/src/theory/datatypes/theory_datatypes_utils.cpp +++ b/src/theory/datatypes/theory_datatypes_utils.cpp @@ -23,6 +23,7 @@ #include "smt/smt_engine_scope.h" #include "theory/evaluator.h" #include "theory/rewriter.h" +#include "printer/sygus_print_callback.h" using namespace CVC4; using namespace CVC4::kind; @@ -711,6 +712,223 @@ Node sygusToBuiltinEval(Node n, const std::vector<Node>& args) return visited[n]; } +void getFreeSymbolsSygusType(TypeNode sdt, + std::unordered_set<Node, NodeHashFunction>& syms) +{ + // datatype types we need to process + std::vector<TypeNode> typeToProcess; + // datatype types we have processed + std::map<TypeNode, TypeNode> typesProcessed; + typeToProcess.push_back(sdt); + while (!typeToProcess.empty()) + { + std::vector<TypeNode> typeNextToProcess; + for (const TypeNode& curr : typeToProcess) + { + Assert(curr.isDatatype() && curr.getDType().isSygus()); + const DType& dtc = curr.getDType(); + for (unsigned j = 0, ncons = dtc.getNumConstructors(); j < ncons; j++) + { + // collect the symbols from the operator + Node op = dtc[j].getSygusOp(); + expr::getSymbols(op, syms); + // traverse the argument types + for (unsigned k = 0, nargs = dtc[j].getNumArgs(); k < nargs; k++) + { + TypeNode argt = dtc[j].getArgType(k); + if (!argt.isDatatype() || !argt.getDType().isSygus()) + { + // not a sygus datatype + continue; + } + if (typesProcessed.find(argt) == typesProcessed.end()) + { + typeNextToProcess.push_back(argt); + } + } + } + } + typeToProcess.clear(); + typeToProcess.insert(typeToProcess.end(), + typeNextToProcess.begin(), + typeNextToProcess.end()); + } +} + +TypeNode substituteAndGeneralizeSygusType(TypeNode sdt, + const std::vector<Node>& syms, + const std::vector<Node>& vars) +{ + NodeManager* nm = NodeManager::currentNM(); + const DType& sdtd = sdt.getDType(); + // compute the new formal argument list + std::vector<Node> formalVars; + Node prevVarList = sdtd.getSygusVarList(); + if (!prevVarList.isNull()) + { + for (const Node& v : prevVarList) + { + // if it is not being replaced + if (std::find(syms.begin(), syms.end(), v) != syms.end()) + { + formalVars.push_back(v); + } + } + } + for (const Node& v : vars) + { + if (v.getKind() == BOUND_VARIABLE) + { + formalVars.push_back(v); + } + } + // make the sygus variable list for the formal argument list + Node abvl = nm->mkNode(BOUND_VAR_LIST, formalVars); + Trace("sygus-abduct-debug") << "...finish" << std::endl; + + // must convert all constructors to version with variables in "vars" + std::vector<SygusDatatype> sdts; + std::set<Type> unres; + + Trace("dtsygus-gen-debug") << "Process sygus type:" << std::endl; + Trace("dtsygus-gen-debug") << sdtd.getName() << std::endl; + + // datatype types we need to process + std::vector<TypeNode> dtToProcess; + // datatype types we have processed + std::map<TypeNode, TypeNode> dtProcessed; + dtToProcess.push_back(sdt); + std::stringstream ssutn0; + ssutn0 << sdtd.getName() << "_s"; + TypeNode abdTNew = + nm->mkSort(ssutn0.str(), ExprManager::SORT_FLAG_PLACEHOLDER); + unres.insert(abdTNew.toType()); + dtProcessed[sdt] = abdTNew; + + // We must convert all symbols in the sygus datatype type sdt to + // apply the substitution { syms -> vars }, where syms is the free + // variables of the input problem, and vars is the formal argument list + // of the function-to-synthesize. + + // We are traversing over the subfield types of the datatype to convert + // them into the form described above. + while (!dtToProcess.empty()) + { + std::vector<TypeNode> dtNextToProcess; + for (const TypeNode& curr : dtToProcess) + { + Assert(curr.isDatatype() && curr.getDType().isSygus()); + const DType& dtc = curr.getDType(); + std::stringstream ssdtn; + ssdtn << dtc.getName() << "_s"; + sdts.push_back(SygusDatatype(ssdtn.str())); + Trace("dtsygus-gen-debug") + << "Process datatype " << sdts.back().getName() << "..." << std::endl; + for (unsigned j = 0, ncons = dtc.getNumConstructors(); j < ncons; j++) + { + Node op = dtc[j].getSygusOp(); + // apply the substitution to the argument + Node ops = + op.substitute(syms.begin(), syms.end(), vars.begin(), vars.end()); + Trace("dtsygus-gen-debug") << " Process constructor " << op << " / " + << ops << "..." << std::endl; + std::vector<TypeNode> cargs; + for (unsigned k = 0, nargs = dtc[j].getNumArgs(); k < nargs; k++) + { + TypeNode argt = dtc[j].getArgType(k); + std::map<TypeNode, TypeNode>::iterator itdp = dtProcessed.find(argt); + TypeNode argtNew; + if (itdp == dtProcessed.end()) + { + std::stringstream ssutn; + ssutn << argt.getDType().getName() << "_s"; + argtNew = + nm->mkSort(ssutn.str(), ExprManager::SORT_FLAG_PLACEHOLDER); + Trace("dtsygus-gen-debug") << " ...unresolved type " << argtNew + << " for " << argt << std::endl; + unres.insert(argtNew.toType()); + dtProcessed[argt] = argtNew; + dtNextToProcess.push_back(argt); + } + else + { + argtNew = itdp->second; + } + Trace("dtsygus-gen-debug") + << " Arg #" << k << ": " << argtNew << std::endl; + cargs.push_back(argtNew); + } + // callback prints as the expression + std::shared_ptr<SygusPrintCallback> spc; + std::vector<Expr> args; + if (op.getKind() == LAMBDA) + { + Node opBody = op[1]; + for (const Node& v : op[0]) + { + args.push_back(v.toExpr()); + } + spc = std::make_shared<printer::SygusExprPrintCallback>( + opBody.toExpr(), args); + } + else if (cargs.empty()) + { + spc = std::make_shared<printer::SygusExprPrintCallback>(op.toExpr(), + args); + } + std::stringstream ss; + ss << ops.getKind(); + Trace("dtsygus-gen-debug") << "Add constructor : " << ops << std::endl; + sdts.back().addConstructor(ops, ss.str(), cargs, spc); + } + Trace("dtsygus-gen-debug") + << "Set sygus : " << dtc.getSygusType() << " " << abvl << std::endl; + TypeNode stn = dtc.getSygusType(); + sdts.back().initializeDatatype( + stn, abvl, dtc.getSygusAllowConst(), dtc.getSygusAllowAll()); + } + dtToProcess.clear(); + dtToProcess.insert( + dtToProcess.end(), dtNextToProcess.begin(), dtNextToProcess.end()); + } + Trace("dtsygus-gen-debug") + << "Make " << sdts.size() << " datatype types..." << std::endl; + // extract the datatypes + std::vector<Datatype> datatypes; + for (unsigned i = 0, ndts = sdts.size(); i < ndts; i++) + { + datatypes.push_back(sdts[i].getDatatype()); + } + // make the datatype types + std::vector<DatatypeType> datatypeTypes = + nm->toExprManager()->mkMutualDatatypeTypes( + datatypes, unres, ExprManager::DATATYPE_FLAG_PLACEHOLDER); + TypeNode sdtS = TypeNode::fromType(datatypeTypes[0]); + if (Trace.isOn("dtsygus-gen-debug")) + { + Trace("dtsygus-gen-debug") << "Made datatype types:" << std::endl; + for (unsigned j = 0, ndts = datatypeTypes.size(); j < ndts; j++) + { + const DType& dtj = TypeNode::fromType(datatypeTypes[j]).getDType(); + Trace("dtsygus-gen-debug") << "#" << j << ": " << dtj << std::endl; + for (unsigned k = 0, ncons = dtj.getNumConstructors(); k < ncons; k++) + { + for (unsigned l = 0, nargs = dtj[k].getNumArgs(); l < nargs; l++) + { + if (!dtj[k].getArgType(l).isDatatype()) + { + Trace("dtsygus-gen-debug") + << "Argument " << l << " of " << dtj[k] + << " is not datatype : " << dtj[k].getArgType(l) << std::endl; + AlwaysAssert(false); + } + } + } + } + } + return sdtS; +} + } // namespace utils } // namespace datatypes } // namespace theory diff --git a/src/theory/datatypes/theory_datatypes_utils.h b/src/theory/datatypes/theory_datatypes_utils.h index 58f719910..038922f37 100644 --- a/src/theory/datatypes/theory_datatypes_utils.h +++ b/src/theory/datatypes/theory_datatypes_utils.h @@ -245,6 +245,46 @@ Node sygusToBuiltin(Node c, bool isExternal = false); */ Node sygusToBuiltinEval(Node n, const std::vector<Node>& args); +/** Get free symbols in a sygus datatype type + * + * Add the free symbols (expr::getSymbols) in terms that can be generated by + * sygus datatype sdt to the set syms. For example, given sdt encodes the + * grammar: + * G -> a | +( b, G ) | c | e + * We have that { a, b, c, e } are added to syms. Notice that expr::getSymbols + * excludes variables whose kind is BOUND_VARIABLE. + */ +void getFreeSymbolsSygusType(TypeNode sdt, + std::unordered_set<Node, NodeHashFunction>& syms); + +/** Substitute and generalize a sygus datatype type + * + * This transforms a sygus datatype sdt into another one sdt' that generates + * terms t such that t * { vars -> syms } is generated by sdt. + * + * The arguments syms and vars should be vectors of the same size and types. + * It is recommended that the arguments in syms and vars should be variables + * (return true for .isVar()) but this is not required. + * + * The variables in vars of type BOUND_VARIABLE are added to the + * formal argument list of t. Other symbols are not. + * + * For example, given sdt encodes the grammar: + * G -> a | +( b, G ) | c | e + * Let syms = { a, b, c } and vars = { x_a, x_b, d }, where x_a and x_b have + * type BOUND_VARIABLE and d does not. + * The returned type encodes the grammar: + * G' -> x_a | +( x_b, G' ) | d | e + * Additionally, x_a and x_b are treated as formal arguments of a function + * to synthesize whose syntax restrictions are specified by G'. + * + * This method traverses the type definition of the datatype corresponding to + * the argument sdt. + */ +TypeNode substituteAndGeneralizeSygusType(TypeNode sdt, + const std::vector<Node>& syms, + const std::vector<Node>& vars); + // ------------------------ end sygus utils } // namespace utils diff --git a/src/theory/quantifiers/cegqi/ceg_bv_instantiator.cpp b/src/theory/quantifiers/cegqi/ceg_bv_instantiator.cpp index f94fee66b..472dabf68 100644 --- a/src/theory/quantifiers/cegqi/ceg_bv_instantiator.cpp +++ b/src/theory/quantifiers/cegqi/ceg_bv_instantiator.cpp @@ -633,7 +633,7 @@ struct SortBvExtractInterval }; void BvInstantiatorPreprocess::registerCounterexampleLemma( - std::vector<Node>& lems, std::vector<Node>& ce_vars) + Node lem, std::vector<Node>& ceVars, std::vector<Node>& auxLems) { // new variables std::vector<Node> vars; @@ -647,12 +647,8 @@ void BvInstantiatorPreprocess::registerCounterexampleLemma( // map from terms to bitvector extracts applied to that term std::map<Node, std::vector<Node> > extract_map; std::unordered_set<TNode, TNodeHashFunction> visited; - for (unsigned i = 0, size = lems.size(); i < size; i++) - { - Trace("cegqi-bv-pp-debug2") - << "Register ce lemma # " << i << " : " << lems[i] << std::endl; - collectExtracts(lems[i], extract_map, visited); - } + Trace("cegqi-bv-pp-debug2") << "Register ce lemma " << lem << std::endl; + collectExtracts(lem, extract_map, visited); for (std::pair<const Node, std::vector<Node> >& es : extract_map) { // sort based on the extract start position @@ -721,10 +717,10 @@ void BvInstantiatorPreprocess::registerCounterexampleLemma( Trace("cegqi-bv-pp") << "Adding " << new_lems.size() << " lemmas..." << std::endl; - lems.insert(lems.end(), new_lems.begin(), new_lems.end()); + auxLems.insert(auxLems.end(), new_lems.begin(), new_lems.end()); Trace("cegqi-bv-pp") << "Adding " << vars.size() << " variables..." << std::endl; - ce_vars.insert(ce_vars.end(), vars.begin(), vars.end()); + ceVars.insert(ceVars.end(), vars.begin(), vars.end()); } } diff --git a/src/theory/quantifiers/cegqi/ceg_bv_instantiator.h b/src/theory/quantifiers/cegqi/ceg_bv_instantiator.h index 3ad45d5be..6f6c216f6 100644 --- a/src/theory/quantifiers/cegqi/ceg_bv_instantiator.h +++ b/src/theory/quantifiers/cegqi/ceg_bv_instantiator.h @@ -168,32 +168,32 @@ class BvInstantiatorPreprocess : public InstantiatorPreprocess ~BvInstantiatorPreprocess() override {} /** register counterexample lemma * - * This method modifies the contents of lems based on the extract terms - * it contains when the option --cbqi-bv-rm-extract is enabled. It introduces + * This method adds to auxLems based on the extract terms that lem + * contains when the option --cbqi-bv-rm-extract is enabled. It introduces * a dummy equality so that segments of terms t under extracts can be solved * independently. * - * For example: + * For example, if lem is: * P[ ((extract 7 4) t), ((extract 3 0) t)] - * becomes: - * P[((extract 7 4) t), ((extract 3 0) t)] ^ + * then we add: * t = concat( x74, x30 ) - * where x74 and x30 are fresh variables of type BV_4. + * to auxLems, where x74 and x30 are fresh variables of type BV_4, which are + * added to ceVars. * - * Another example: + * Another example, for: * P[ ((extract 7 3) t), ((extract 4 0) t)] - * becomes: - * P[((extract 7 4) t), ((extract 3 0) t)] ^ + * we add: * t = concat( x75, x44, x30 ) - * where x75, x44 and x30 are fresh variables of type BV_3, BV_1, and BV_4 - * respectively. + * to auxLems where x75, x44 and x30 are fresh variables of type BV_3, BV_1, + * and BV_4 respectively, which are added to ceVars. * - * Notice we leave the original conjecture alone. This is done for performance + * Notice we leave the original lem alone. This is done for performance * since the added equalities ensure we are able to construct the proper * solved forms for variables in t and for the intermediate variables above. */ - void registerCounterexampleLemma(std::vector<Node>& lems, - std::vector<Node>& ce_vars) override; + void registerCounterexampleLemma(Node lem, + std::vector<Node>& ceVars, + std::vector<Node>& auxLems) override; private: /** collect extracts diff --git a/src/theory/quantifiers/cegqi/ceg_instantiator.cpp b/src/theory/quantifiers/cegqi/ceg_instantiator.cpp index 186024219..95a4037fc 100644 --- a/src/theory/quantifiers/cegqi/ceg_instantiator.cpp +++ b/src/theory/quantifiers/cegqi/ceg_instantiator.cpp @@ -21,7 +21,6 @@ #include "expr/node_algorithm.h" #include "options/quantifiers_options.h" -#include "smt/term_formula_removal.h" #include "theory/arith/arith_msum.h" #include "theory/quantifiers/cegqi/inst_strategy_cegqi.h" #include "theory/quantifiers/first_order_model.h" @@ -1571,18 +1570,21 @@ void CegInstantiator::collectCeAtoms( Node n, std::map< Node, bool >& visited ) } } -void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& lems, std::vector< Node >& ce_vars ) { +void CegInstantiator::registerCounterexampleLemma(Node lem, + std::vector<Node>& ceVars, + std::vector<Node>& auxLems) +{ Trace("cegqi-reg") << "Register counterexample lemma..." << std::endl; d_input_vars.clear(); - d_input_vars.insert(d_input_vars.end(), ce_vars.begin(), ce_vars.end()); + d_input_vars.insert(d_input_vars.end(), ceVars.begin(), ceVars.end()); //Assert( d_vars.empty() ); d_vars.clear(); registerTheoryId(THEORY_UF); - for (unsigned i = 0; i < ce_vars.size(); i++) + for (const Node& cv : ceVars) { - Trace("cegqi-reg") << " register input variable : " << ce_vars[i] << std::endl; - registerVariable(ce_vars[i]); + Trace("cegqi-reg") << " register input variable : " << cv << std::endl; + registerVariable(cv); } // preprocess with all relevant instantiator preprocessors @@ -1592,7 +1594,7 @@ void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& lems, st pvars.insert(pvars.end(), d_vars.begin(), d_vars.end()); for (std::pair<const TheoryId, InstantiatorPreprocess*>& p : d_tipp) { - p.second->registerCounterexampleLemma(lems, pvars); + p.second->registerCounterexampleLemma(lem, pvars, auxLems); } // must register variables generated by preprocessors Trace("cegqi-debug") << "Register variables from theory-specific methods " @@ -1600,28 +1602,43 @@ void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& lems, st << std::endl; for (unsigned i = d_input_vars.size(), size = pvars.size(); i < size; ++i) { - Trace("cegqi-reg") << " register theory preprocess variable : " << pvars[i] - << std::endl; + Trace("cegqi-reg") << " register inst preprocess variable : " << pvars[i] + << std::endl; registerVariable(pvars[i]); } - //remove ITEs - IteSkolemMap iteSkolemMap; - d_qe->getTheoryEngine()->getTermFormulaRemover()->run(lems, iteSkolemMap); - for(IteSkolemMap::iterator i = iteSkolemMap.begin(); i != iteSkolemMap.end(); ++i) { - Trace("cegqi-reg") << " register aux variable : " << i->first << std::endl; - registerVariable(i->first); - } - for( unsigned i=0; i<lems.size(); i++ ){ - Trace("cegqi-debug") << "Counterexample lemma (pre-rewrite) " << i << " : " << lems[i] << std::endl; - Node rlem = lems[i]; - rlem = Rewriter::rewrite( rlem ); - // also must preprocess to ensure that the counterexample atoms we - // collect below are identical to the atoms that we add to the CNF stream - rlem = d_qe->getTheoryEngine()->preprocess(rlem); - Trace("cegqi-debug") << "Counterexample lemma (post-rewrite) " << i << " : " << rlem << std::endl; - lems[i] = rlem; + // register variables that were introduced during TheoryEngine preprocessing + std::unordered_set<Node, NodeHashFunction> ceSyms; + expr::getSymbols(lem, ceSyms); + std::unordered_set<Node, NodeHashFunction> qSyms; + expr::getSymbols(d_quant, qSyms); + // all variables that are in counterexample lemma but not in quantified + // formula + for (const Node& ces : ceSyms) + { + if (qSyms.find(ces) != qSyms.end()) + { + // a free symbol of the quantified formula. + continue; + } + if (std::find(d_vars.begin(), d_vars.end(), ces) != d_vars.end()) + { + // already processed variable + continue; + } + if (ces.getType().isBoolean()) + { + // Boolean variables, including the counterexample literal, don't matter + // since they are always assigned a model value. + continue; + } + Trace("cegqi-reg") << " register theory preprocess variable : " << ces + << std::endl; + // register the variable, which was introduced by TheoryEngine's preprocess + // method, e.g. an ITE skolem. + registerVariable(ces); } + // determine variable order: must do Reals before Ints Trace("cegqi-debug") << "Determine variable order..." << std::endl; if (!d_vars.empty()) @@ -1673,8 +1690,10 @@ void CegInstantiator::registerCounterexampleLemma( std::vector< Node >& lems, st // the original body d_is_nested_quant = false; std::map< Node, bool > visited; - for( unsigned i=0; i<lems.size(); i++ ){ - collectCeAtoms( lems[i], visited ); + collectCeAtoms(lem, visited); + for (const Node& alem : auxLems) + { + collectCeAtoms(alem, visited); } } diff --git a/src/theory/quantifiers/cegqi/ceg_instantiator.h b/src/theory/quantifiers/cegqi/ceg_instantiator.h index 7351e60f0..08f7a1262 100644 --- a/src/theory/quantifiers/cegqi/ceg_instantiator.h +++ b/src/theory/quantifiers/cegqi/ceg_instantiator.h @@ -224,21 +224,20 @@ class CegInstantiator { void presolve(Node q); /** Register the counterexample lemma * - * lems : contains the conjuncts of the counterexample lemma of the - * quantified formula we are processing. The counterexample - * lemma is the formula { ~phi[e/x] } in Figure 1 of Reynolds - * et al. FMSD 2017. - * ce_vars : contains the variables e. Notice these are variables of - * INST_CONSTANT kind, since we do not permit bound - * variables in assertions. - * - * This method may modify the set of lemmas lems based on: - * - ITE removal, - * - Theory-specific preprocessing of instantiation lemmas. - * It may also introduce new variables to ce_vars if necessary. - */ - void registerCounterexampleLemma(std::vector<Node>& lems, - std::vector<Node>& ce_vars); + * @param lem contains the counterexample lemma of the quantified formula we + * are processing. The counterexample lemma is the formula { ~phi[e/x] } in + * Figure 1 of Reynolds et al. FMSD 2017. + * @param ce_vars contains the variables e. Notice these are variables of + * INST_CONSTANT kind, since we do not permit bound variables in assertions. + * This method may add additional variables to this vector if it decides there + * are additional auxiliary variables to solve for. + * @param auxLems : if this method decides that additional lemmas should be + * sent on the output channel, they are added to this vector, and sent out by + * the caller of this method. + */ + void registerCounterexampleLemma(Node lem, + std::vector<Node>& ce_vars, + std::vector<Node>& auxLems); //------------------------------interface for instantiators /** get quantifiers engine */ QuantifiersEngine* getQuantifiersEngine() { return d_qe; } @@ -829,8 +828,9 @@ class InstantiatorPreprocess * of counterexample lemmas, with the same contract as * CegInstantiation::registerCounterexampleLemma. */ - virtual void registerCounterexampleLemma(std::vector<Node>& lems, - std::vector<Node>& ce_vars) + virtual void registerCounterexampleLemma(Node lem, + std::vector<Node>& ceVars, + std::vector<Node>& auxLems) { } }; diff --git a/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp b/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp index 208eb0bf8..8693f97f4 100644 --- a/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp +++ b/src/theory/quantifiers/cegqi/inst_strategy_cegqi.cpp @@ -593,15 +593,18 @@ void InstStrategyCegqi::registerCounterexampleLemma(Node q, Node lem) { ce_vars.push_back(tutil->getInstantiationConstant(q, i)); } - std::vector<Node> lems; - lems.push_back(lem); CegInstantiator* cinst = getInstantiator(q); - cinst->registerCounterexampleLemma(lems, ce_vars); - for (unsigned i = 0, size = lems.size(); i < size; i++) + LemmaStatus status = d_quantEngine->getOutputChannel().lemma(lem); + Node ppLem = status.getRewrittenLemma(); + Trace("cegqi-debug") << "Counterexample lemma (post-preprocess): " << ppLem + << std::endl; + std::vector<Node> auxLems; + cinst->registerCounterexampleLemma(ppLem, ce_vars, auxLems); + for (unsigned i = 0, size = auxLems.size(); i < size; i++) { - Trace("cegqi-debug") << "Counterexample lemma " << i << " : " << lems[i] - << std::endl; - d_quantEngine->addLemma(lems[i], false); + Trace("cegqi-debug") << "Auxiliary CE lemma " << i << " : " << auxLems[i] + << std::endl; + d_quantEngine->addLemma(auxLems[i], false); } } diff --git a/src/theory/quantifiers/extended_rewrite.cpp b/src/theory/quantifiers/extended_rewrite.cpp index 1f42c384f..8803a9df8 100644 --- a/src/theory/quantifiers/extended_rewrite.cpp +++ b/src/theory/quantifiers/extended_rewrite.cpp @@ -586,6 +586,11 @@ Node ExtendedRewriter::extendedRewriteAndOr(Node n) Node ExtendedRewriter::extendedRewritePullIte(Kind itek, Node n) { Assert(n.getKind() != ITE); + if (n.isClosure()) + { + // don't pull ITE out of quantifiers + return n; + } NodeManager* nm = NodeManager::currentNM(); TypeNode tn = n.getType(); std::vector<Node> children; diff --git a/src/theory/quantifiers/fmf/full_model_check.cpp b/src/theory/quantifiers/fmf/full_model_check.cpp index af3a94d96..91cacdc2e 100644 --- a/src/theory/quantifiers/fmf/full_model_check.cpp +++ b/src/theory/quantifiers/fmf/full_model_check.cpp @@ -592,139 +592,168 @@ void FullModelChecker::debugPrint(const char * tr, Node n, bool dispStar) { int FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { Trace("fmc") << "Full model check " << f << ", effort = " << effort << "..." << std::endl; + // register the quantifier + registerQuantifiedFormula(f); Assert(!d_qe->inConflict()); - if( optUseModel() ){ - FirstOrderModelFmc * fmfmc = fm->asFirstOrderModelFmc(); - if (effort==0) { - //register the quantifier - if (d_quant_cond.find(f)==d_quant_cond.end()) { - std::vector< TypeNode > types; - for(unsigned i=0; i<f[0].getNumChildren(); i++){ - types.push_back(f[0][i].getType()); + // we do not do model-based quantifier instantiation if the option + // disables it, or if the quantified formula has an unhandled type. + if (!optUseModel() || !isHandled(f)) + { + return 0; + } + FirstOrderModelFmc* fmfmc = fm->asFirstOrderModelFmc(); + if (effort == 0) + { + if (options::mbqiMode() == options::MbqiMode::NONE) + { + // just exhaustive instantiate + Node c = mkCondDefault(fmfmc, f); + d_quant_models[f].addEntry(fmfmc, c, d_false); + if (!exhaustiveInstantiate(fmfmc, f, c, -1)) + { + return 0; + } + return 1; + } + // model check the quantifier + doCheck(fmfmc, f, d_quant_models[f], f[1]); + std::vector<Node>& mcond = d_quant_models[f].d_cond; + Trace("fmc") << "Definition for quantifier " << f << " is : " << std::endl; + Assert(!mcond.empty()); + d_quant_models[f].debugPrint("fmc", Node::null(), this); + Trace("fmc") << std::endl; + + // consider all entries going to non-true + Instantiate* instq = d_qe->getInstantiate(); + for (unsigned i = 0, msize = mcond.size(); i < msize; i++) + { + if (d_quant_models[f].d_value[i] == d_true) + { + // already satisfied + continue; + } + Trace("fmc-inst") << "Instantiate based on " << mcond[i] << "..." + << std::endl; + bool hasStar = false; + std::vector<Node> inst; + for (unsigned j = 0, nchild = mcond[i].getNumChildren(); j < nchild; j++) + { + if (fmfmc->isStar(mcond[i][j])) + { + hasStar = true; + inst.push_back(fmfmc->getModelBasisTerm(mcond[i][j].getType())); + } + else + { + inst.push_back(mcond[i][j]); } - TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->booleanType() ); - Node op = NodeManager::currentNM()->mkSkolem( "qfmc", typ, "op created for full-model checking" ); - d_quant_cond[f] = op; } - - if (options::mbqiMode() == options::MbqiMode::NONE) + bool addInst = true; + if (hasStar) { - //just exhaustive instantiate - Node c = mkCondDefault( fmfmc, f ); - d_quant_models[f].addEntry( fmfmc, c, d_false ); - return exhaustiveInstantiate( fmfmc, f, c, -1); + // try obvious (specified by inst) + Node ev = d_quant_models[f].evaluate(fmfmc, inst); + if (ev == d_true) + { + addInst = false; + Trace("fmc-debug") + << "...do not instantiate, evaluation was " << ev << std::endl; + } } else { - //model check the quantifier - doCheck(fmfmc, f, d_quant_models[f], f[1]); - Trace("fmc") << "Definition for quantifier " << f << " is : " << std::endl; - Assert(!d_quant_models[f].d_cond.empty()); - d_quant_models[f].debugPrint("fmc", Node::null(), this); - Trace("fmc") << std::endl; - - //consider all entries going to non-true - for (unsigned i=0; i<d_quant_models[f].d_cond.size(); i++) { - if( d_quant_models[f].d_value[i]!=d_true ) { - Trace("fmc-inst") << "Instantiate based on " << d_quant_models[f].d_cond[i] << "..." << std::endl; - bool hasStar = false; - std::vector< Node > inst; - for (unsigned j=0; j<d_quant_models[f].d_cond[i].getNumChildren(); j++) { - if (fmfmc->isStar(d_quant_models[f].d_cond[i][j])) { - hasStar = true; - inst.push_back(fmfmc->getModelBasisTerm(d_quant_models[f].d_cond[i][j].getType())); - }else{ - inst.push_back(d_quant_models[f].d_cond[i][j]); - } - } - bool addInst = true; - if( hasStar ){ - //try obvious (specified by inst) - Node ev = d_quant_models[f].evaluate(fmfmc, inst); - if (ev==d_true) { - addInst = false; - Trace("fmc-debug") << "...do not instantiate, evaluation was " << ev << std::endl; - } - }else{ - //for debugging - if (Trace.isOn("fmc-test-inst")) { - Node ev = d_quant_models[f].evaluate(fmfmc, inst); - if( ev==d_true ){ - Message() << "WARNING: instantiation was true! " << f << " " - << d_quant_models[f].d_cond[i] << std::endl; - AlwaysAssert(false); - }else{ - Trace("fmc-test-inst") << "...instantiation evaluated to false." << std::endl; - } - } - } - if( addInst ){ - if( options::fmfBound() ){ - std::vector< Node > cond; - cond.push_back(d_quant_cond[f]); - cond.insert( cond.end(), inst.begin(), inst.end() ); - //need to do exhaustive instantiate algorithm to set things properly (should only add one instance) - Node c = mkCond( cond ); - unsigned prevInst = d_addedLemmas; - exhaustiveInstantiate( fmfmc, f, c, -1 ); - if( d_addedLemmas==prevInst ){ - d_star_insts[f].push_back(i); - } - }else{ - //just add the instance - d_triedLemmas++; - if (d_qe->getInstantiate()->addInstantiation(f, inst, true)) - { - Trace("fmc-debug-inst") << "** Added instantiation." << std::endl; - d_addedLemmas++; - if( d_qe->inConflict() || options::fmfOneInstPerRound() ){ - break; - } - }else{ - Trace("fmc-debug-inst") << "** Instantiation was duplicate." << std::endl; - //this can happen if evaluation is unknown, or if we are generalizing a star that already has a value - //if( !hasStar && d_quant_models[f].d_value[i]==d_false ){ - // Trace("fmc-warn") << "**** FMC warning: inconsistent duplicate instantiation." << std::endl; - //} - //this assertion can happen if two instantiations from this round are identical - // (0,1)->false (1,0)->false for forall xy. f( x, y ) = f( y, x ) - //Assert( hasStar || d_quant_models[f].d_value[i]!=d_false ); - //might try it next effort level - d_star_insts[f].push_back(i); - } - } - }else{ - Trace("fmc-debug-inst") << "** Instantiation was already true." << std::endl; - //might try it next effort level - d_star_insts[f].push_back(i); - } + // for debugging + if (Trace.isOn("fmc-test-inst")) + { + Node ev = d_quant_models[f].evaluate(fmfmc, inst); + if (ev == d_true) + { + Message() << "WARNING: instantiation was true! " << f << " " + << mcond[i] << std::endl; + AlwaysAssert(false); + } + else + { + Trace("fmc-test-inst") + << "...instantiation evaluated to false." << std::endl; } } } - }else{ - if (!d_star_insts[f].empty()) { - Trace("fmc-exh") << "Exhaustive instantiate " << f << std::endl; - Trace("fmc-exh") << "Definition was : " << std::endl; - d_quant_models[f].debugPrint("fmc-exh", Node::null(), this); - Trace("fmc-exh") << std::endl; - Def temp; - //simplify the exceptions? - for( int i=(d_star_insts[f].size()-1); i>=0; i--) { - //get witness for d_star_insts[f][i] - int j = d_star_insts[f][i]; - if( temp.addEntry(fmfmc, d_quant_models[f].d_cond[j], d_quant_models[f].d_value[j] ) ){ - if( !exhaustiveInstantiate(fmfmc, f, d_quant_models[f].d_cond[j], j ) ){ - //something went wrong, resort to exhaustive instantiation - return 0; - } - } + if (!addInst) + { + Trace("fmc-debug-inst") + << "** Instantiation was already true." << std::endl; + // might try it next effort level + d_star_insts[f].push_back(i); + continue; + } + if (options::fmfBound()) + { + std::vector<Node> cond; + cond.push_back(d_quant_cond[f]); + cond.insert(cond.end(), inst.begin(), inst.end()); + // need to do exhaustive instantiate algorithm to set things properly + // (should only add one instance) + Node c = mkCond(cond); + unsigned prevInst = d_addedLemmas; + exhaustiveInstantiate(fmfmc, f, c, -1); + if (d_addedLemmas == prevInst) + { + d_star_insts[f].push_back(i); } + continue; + } + // just add the instance + d_triedLemmas++; + if (instq->addInstantiation(f, inst, true)) + { + Trace("fmc-debug-inst") << "** Added instantiation." << std::endl; + d_addedLemmas++; + if (d_qe->inConflict() || options::fmfOneInstPerRound()) + { + break; + } + } + else + { + Trace("fmc-debug-inst") + << "** Instantiation was duplicate." << std::endl; + // might try it next effort level + d_star_insts[f].push_back(i); } } return 1; - }else{ - return 0; } + // Get the list of instantiation regions (described by "star entries" in the + // definition) that were not tried at the previous effort level. For each + // of these, we add one instantiation. + std::vector<Node>& mcond = d_quant_models[f].d_cond; + if (!d_star_insts[f].empty()) + { + if (Trace.isOn("fmc-exh")) + { + Trace("fmc-exh") << "Exhaustive instantiate " << f << std::endl; + Trace("fmc-exh") << "Definition was : " << std::endl; + d_quant_models[f].debugPrint("fmc-exh", Node::null(), this); + Trace("fmc-exh") << std::endl; + } + Def temp; + // simplify the exceptions? + for (int i = (d_star_insts[f].size() - 1); i >= 0; i--) + { + // get witness for d_star_insts[f][i] + int j = d_star_insts[f][i]; + if (temp.addEntry(fmfmc, mcond[j], d_quant_models[f].d_value[j])) + { + if (!exhaustiveInstantiate(fmfmc, f, mcond[j], j)) + { + // something went wrong, resort to exhaustive instantiation + return 0; + } + } + } + } + return 1; } /** Representative bound fmc entry @@ -1290,3 +1319,33 @@ Node FullModelChecker::getFunctionValue(FirstOrderModelFmc * fm, Node op, const bool FullModelChecker::useSimpleModels() { return options::fmfFmcSimple(); } + +void FullModelChecker::registerQuantifiedFormula(Node q) +{ + if (d_quant_cond.find(q) != d_quant_cond.end()) + { + return; + } + NodeManager* nm = NodeManager::currentNM(); + std::vector<TypeNode> types; + for (const Node& v : q[0]) + { + TypeNode tn = v.getType(); + if (tn.isFunction()) + { + // we will not use model-based quantifier instantiation for q, since + // the model-based instantiation algorithm does not handle (universally + // quantified) functions + d_unhandledQuant.insert(q); + } + types.push_back(tn); + } + TypeNode typ = nm->mkFunctionType(types, nm->booleanType()); + Node op = nm->mkSkolem("qfmc", typ, "op for full-model checking"); + d_quant_cond[q] = op; +} + +bool FullModelChecker::isHandled(Node q) const +{ + return d_unhandledQuant.find(q) == d_unhandledQuant.end(); +} diff --git a/src/theory/quantifiers/fmf/full_model_check.h b/src/theory/quantifiers/fmf/full_model_check.h index 7dd1991f5..60de5d1eb 100644 --- a/src/theory/quantifiers/fmf/full_model_check.h +++ b/src/theory/quantifiers/fmf/full_model_check.h @@ -86,7 +86,16 @@ protected: Node d_false; std::map<TypeNode, std::map< Node, int > > d_rep_ids; std::map<Node, Def > d_quant_models; + /** + * The predicate for the quantified formula. This is used to express + * conditions under which the quantified formula is false in the model. + * For example, for quantified formula (forall x:Int, y:U. P), this is + * a predicate of type (Int x U) -> Bool. + */ std::map<Node, Node > d_quant_cond; + /** A set of quantified formulas that cannot be handled by model-based + * quantifier instantiation */ + std::unordered_set<Node, NodeHashFunction> d_unhandledQuant; std::map< TypeNode, Node > d_array_cond; std::map< Node, Node > d_array_term_cond; std::map< Node, std::vector< int > > d_star_insts; @@ -155,6 +164,16 @@ public: bool processBuildModel(TheoryModel* m) override; bool useSimpleModels(); + + private: + /** + * Register quantified formula. + * This checks whether q can be handled by model-based instantiation and + * initializes the necessary information if so. + */ + void registerQuantifiedFormula(Node q); + /** Is quantified formula q handled by model-based instantiation? */ + bool isHandled(Node q) const; };/* class FullModelChecker */ }/* CVC4::theory::quantifiers::fmcheck namespace */ diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index df86922bc..43c6e73bc 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -1342,15 +1342,19 @@ Node QuantifiersRewriter::computeVarElimination( Node body, std::vector< Node >& } Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, std::vector< Node >& nargs, bool pol, bool prenexAgg ){ - if( body.getKind()==FORALL ){ + NodeManager* nm = NodeManager::currentNM(); + Kind k = body.getKind(); + if (k == FORALL) + { if( ( pol || prenexAgg ) && ( options::prenexQuantUser() || body.getNumChildren()==2 ) ){ std::vector< Node > terms; std::vector< Node > subs; //for doing prenexing of same-signed quantifiers //must rename each variable that already exists - for( unsigned i=0; i<body[0].getNumChildren(); i++ ){ - terms.push_back( body[0][i] ); - subs.push_back( NodeManager::currentNM()->mkBoundVar( body[0][i].getType() ) ); + for (const Node& v : body[0]) + { + terms.push_back(v); + subs.push_back(nm->mkBoundVar(v.getType())); } if( pol ){ args.insert( args.end(), subs.begin(), subs.end() ); @@ -1362,161 +1366,134 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, s return newBody; } //must remove structure - }else if( prenexAgg && body.getKind()==kind::ITE && body.getType().isBoolean() ){ - Node nn = NodeManager::currentNM()->mkNode( kind::AND, - NodeManager::currentNM()->mkNode( kind::OR, body[0].notNode(), body[1] ), - NodeManager::currentNM()->mkNode( kind::OR, body[0], body[2] ) ); + } + else if (prenexAgg && k == ITE && body.getType().isBoolean()) + { + Node nn = nm->mkNode(AND, + nm->mkNode(OR, body[0].notNode(), body[1]), + nm->mkNode(OR, body[0], body[2])); return computePrenex( nn, args, nargs, pol, prenexAgg ); - }else if( prenexAgg && body.getKind()==kind::EQUAL && body[0].getType().isBoolean() ){ - Node nn = NodeManager::currentNM()->mkNode( kind::AND, - NodeManager::currentNM()->mkNode( kind::OR, body[0].notNode(), body[1] ), - NodeManager::currentNM()->mkNode( kind::OR, body[0], body[1].notNode() ) ); + } + else if (prenexAgg && k == EQUAL && body[0].getType().isBoolean()) + { + Node nn = nm->mkNode(AND, + nm->mkNode(OR, body[0].notNode(), body[1]), + nm->mkNode(OR, body[0], body[1].notNode())); return computePrenex( nn, args, nargs, pol, prenexAgg ); }else if( body.getType().isBoolean() ){ - Assert(body.getKind() != EXISTS); + Assert(k != EXISTS); bool childrenChanged = false; std::vector< Node > newChildren; - for( unsigned i=0; i<body.getNumChildren(); i++ ){ + for (size_t i = 0, nchild = body.getNumChildren(); i < nchild; i++) + { bool newHasPol; bool newPol; QuantPhaseReq::getPolarity( body, i, true, pol, newHasPol, newPol ); - if( newHasPol ){ - Node n = computePrenex( body[i], args, nargs, newPol, prenexAgg ); - newChildren.push_back( n ); - if( n!=body[i] ){ - childrenChanged = true; - } - }else{ + if (!newHasPol) + { newChildren.push_back( body[i] ); + continue; } + Node n = computePrenex(body[i], args, nargs, newPol, prenexAgg); + newChildren.push_back(n); + childrenChanged = n != body[i] || childrenChanged; } if( childrenChanged ){ - if( body.getKind()==NOT && newChildren[0].getKind()==NOT ){ + if (k == NOT && newChildren[0].getKind() == NOT) + { return newChildren[0][0]; - }else{ - return NodeManager::currentNM()->mkNode( body.getKind(), newChildren ); } + return nm->mkNode(k, newChildren); } } return body; } -Node QuantifiersRewriter::computePrenexAgg( Node n, bool topLevel, std::map< unsigned, std::map< Node, Node > >& visited ){ - unsigned tindex = topLevel ? 0 : 1; - std::map< Node, Node >::iterator itv = visited[tindex].find( n ); - if( itv!=visited[tindex].end() ){ +Node QuantifiersRewriter::computePrenexAgg(Node n, + std::map<Node, Node>& visited) +{ + std::map< Node, Node >::iterator itv = visited.find( n ); + if( itv!=visited.end() ){ return itv->second; } - if (expr::hasClosure(n)) + if (!expr::hasClosure(n)) + { + // trivial + return n; + } + NodeManager* nm = NodeManager::currentNM(); + Node ret = n; + if (n.getKind() == NOT) + { + ret = computePrenexAgg(n[0], visited).negate(); + } + else if (n.getKind() == FORALL) { - Node ret = n; - if (topLevel - && options::prenexQuant() == options::PrenexQuantMode::DISJ_NORMAL - && (n.getKind() == AND || (n.getKind() == NOT && n[0].getKind() == OR))) + std::vector<Node> children; + children.push_back(computePrenexAgg(n[1], visited)); + std::vector<Node> args; + args.insert(args.end(), n[0].begin(), n[0].end()); + // for each child, strip top level quant + for (unsigned i = 0; i < children.size(); i++) { - std::vector< Node > children; - Node nc = n.getKind()==NOT ? n[0] : n; - for( unsigned i=0; i<nc.getNumChildren(); i++ ){ - Node ncc = computePrenexAgg( nc[i], true, visited ); - if( n.getKind()==NOT ){ - ncc = ncc.negate(); - } - children.push_back( ncc ); + if (children[i].getKind() == FORALL) + { + args.insert(args.end(), children[i][0].begin(), children[i][0].end()); + children[i] = children[i][1]; } - ret = NodeManager::currentNM()->mkNode( AND, children ); } - else if (n.getKind() == NOT) + // keep the pattern + std::vector<Node> iplc; + if (n.getNumChildren() == 3) { - ret = computePrenexAgg( n[0], false, visited ).negate(); + iplc.insert(iplc.end(), n[2].begin(), n[2].end()); } - else if (n.getKind() == FORALL) + Node nb = children.size() == 1 ? children[0] : nm->mkNode(OR, children); + ret = mkForall(args, nb, iplc, true); + } + else + { + std::vector<Node> args; + std::vector<Node> nargs; + Node nn = computePrenex(n, args, nargs, true, true); + if (n != nn) { - /* - Node nn = computePrenexAgg( n[1], false ); - if( nn!=n[1] ){ - if( n.getNumChildren()==2 ){ - return NodeManager::currentNM()->mkNode( FORALL, n[0], nn ); - }else{ - return NodeManager::currentNM()->mkNode( FORALL, n[0], nn, n[2] ); - } - } - */ - std::vector< Node > children; - if (n[1].getKind() == OR - && options::prenexQuant() == options::PrenexQuantMode::DISJ_NORMAL) + Node nnn = computePrenexAgg(nn, visited); + // merge prenex + if (nnn.getKind() == FORALL) { - for( unsigned i=0; i<n[1].getNumChildren(); i++ ){ - children.push_back( computePrenexAgg( n[1][i], false, visited ) ); + args.insert(args.end(), nnn[0].begin(), nnn[0].end()); + nnn = nnn[1]; + // pos polarity variables are inner + if (!args.empty()) + { + nnn = mkForall(args, nnn, true); } + args.clear(); } - else + else if (nnn.getKind() == NOT && nnn[0].getKind() == FORALL) { - children.push_back( computePrenexAgg( n[1], false, visited ) ); + nargs.insert(nargs.end(), nnn[0][0].begin(), nnn[0][0].end()); + nnn = nnn[0][1].negate(); } - std::vector< Node > args; - for( unsigned i=0; i<n[0].getNumChildren(); i++ ){ - args.push_back( n[0][i] ); + if (!nargs.empty()) + { + nnn = mkForall(nargs, nnn.negate(), true).negate(); } - std::vector< Node > nargs; - //for each child, strip top level quant - for( unsigned i=0; i<children.size(); i++ ){ - if( children[i].getKind()==FORALL ){ - for( unsigned j=0; j<children[i][0].getNumChildren(); j++ ){ - args.push_back( children[i][0][j] ); - } - children[i] = children[i][1]; - } + if (!args.empty()) + { + nnn = mkForall(args, nnn, true); } - // keep the pattern - std::vector< Node > iplc; - if( n.getNumChildren()==3 ){ - for( unsigned i=0; i<n[2].getNumChildren(); i++ ){ - iplc.push_back( n[2][i] ); - } - } - Node nb = children.size()==1 ? children[0] : NodeManager::currentNM()->mkNode( OR, children ); - ret = mkForall( args, nb, iplc, true ); + ret = nnn; } else { - std::vector< Node > args; - std::vector< Node > nargs; - Node nn = computePrenex( n, args, nargs, true, true ); - if( n!=nn ){ - Node nnn = computePrenexAgg( nn, false, visited ); - //merge prenex - if( nnn.getKind()==FORALL ){ - for( unsigned i=0; i<nnn[0].getNumChildren(); i++ ){ - args.push_back( nnn[0][i] ); - } - nnn = nnn[1]; - //pos polarity variables are inner - if( !args.empty() ){ - nnn = mkForall( args, nnn, true ); - } - args.clear(); - }else if( nnn.getKind()==NOT && nnn[0].getKind()==FORALL ){ - for( unsigned i=0; i<nnn[0][0].getNumChildren(); i++ ){ - nargs.push_back( nnn[0][0][i] ); - } - nnn = nnn[0][1].negate(); - } - if( !nargs.empty() ){ - nnn = mkForall( nargs, nnn.negate(), true ).negate(); - } - if( !args.empty() ){ - nnn = mkForall( args, nnn, true ); - } - ret = nnn; - }else{ - Assert(args.empty()); - Assert(nargs.empty()); - } + Assert(args.empty()); + Assert(nargs.empty()); } - visited[tindex][n] = ret; - return ret; } - return n; + visited[n] = ret; + return ret; } Node QuantifiersRewriter::computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ) { @@ -1925,8 +1902,7 @@ Node QuantifiersRewriter::computeOperation(Node f, if( computeOption==COMPUTE_ELIM_SYMBOLS ){ n = computeElimSymbols( n ); }else if( computeOption==COMPUTE_MINISCOPING ){ - if (options::prenexQuant() == options::PrenexQuantMode::DISJ_NORMAL - || options::prenexQuant() == options::PrenexQuantMode::NORMAL) + if (options::prenexQuant() == options::PrenexQuantMode::NORMAL) { if( !qa.d_qid_num.isNull() ){ //already processed this, return self @@ -1957,8 +1933,7 @@ Node QuantifiersRewriter::computeOperation(Node f, } else if (computeOption == COMPUTE_PRENEX) { - if (options::prenexQuant() == options::PrenexQuantMode::DISJ_NORMAL - || options::prenexQuant() == options::PrenexQuantMode::NORMAL) + if (options::prenexQuant() == options::PrenexQuantMode::NORMAL) { //will rewrite at preprocess time return f; @@ -2091,12 +2066,11 @@ Node QuantifiersRewriter::preprocess( Node n, bool isInst ) { } } //pull all quantifiers globally - if (options::prenexQuant() == options::PrenexQuantMode::DISJ_NORMAL - || options::prenexQuant() == options::PrenexQuantMode::NORMAL) + if (options::prenexQuant() == options::PrenexQuantMode::NORMAL) { Trace("quantifiers-prenex") << "Prenexing : " << n << std::endl; - std::map< unsigned, std::map< Node, Node > > visited; - n = computePrenexAgg( n, true, visited ); + std::map<Node, Node> visited; + n = computePrenexAgg(n, visited); n = Rewriter::rewrite( n ); Trace("quantifiers-prenex") << "Prenexing returned : " << n << std::endl; //Assert( isPrenexNormalForm( n ) ); diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 2a3180e78..c8995ef4e 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -234,8 +234,27 @@ class QuantifiersRewriter : public TheoryRewriter 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 ); + /** + * This function removes top-level quantifiers from subformulas of body + * appearing with overall polarity pol. It adds quantified variables that + * appear in positive polarity positions into args, and those at negative + * polarity positions in nargs. + * + * If prenexAgg is true, we ensure that all top-level quantifiers are + * eliminated from subformulas. This means that we must expand ITE and + * Boolean equalities to ensure that quantifiers are at fixed polarities. + * + * For example, calling this function on: + * (or (forall ((x Int)) (P x z)) (not (forall ((y Int)) (Q y z)))) + * would return: + * (or (P x z) (not (Q y z))) + * and add {x} to args, and {y} to nargs. + */ 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 ); + /** + * Apply prenexing aggressively. Returns the prenex normal form of n. + */ + static Node computePrenexAgg(Node n, std::map<Node, Node>& visited); static Node computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ); private: static Node computeOperation(Node f, diff --git a/src/theory/quantifiers/sygus/sygus_abduct.cpp b/src/theory/quantifiers/sygus/sygus_abduct.cpp index a58c5d841..ef2e7e445 100644 --- a/src/theory/quantifiers/sygus/sygus_abduct.cpp +++ b/src/theory/quantifiers/sygus/sygus_abduct.cpp @@ -19,7 +19,6 @@ #include "expr/dtype.h" #include "expr/node_algorithm.h" #include "expr/sygus_datatype.h" -#include "printer/sygus_print_callback.h" #include "theory/datatypes/theory_datatypes_utils.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/quantifiers_rewriter.h" @@ -58,6 +57,12 @@ Node SygusAbduct::mkAbductionConjecture(const std::string& name, for (const Node& s : symset) { TypeNode tn = s.getType(); + if (tn.isConstructor() || tn.isSelector() || tn.isTester()) + { + // datatype symbols should be considered interpreted symbols here, not + // (higher-order) variables. + continue; + } // Notice that we allow for non-first class (e.g. function) variables here. // This is applicable to the case where we are doing get-abduct in a logic // with UF. @@ -73,8 +78,6 @@ Node SygusAbduct::mkAbductionConjecture(const std::string& name, SygusVarToTermAttribute sta; vlv.setAttribute(sta, s); } - // make the sygus variable list - Node abvl = nm->mkNode(BOUND_VAR_LIST, varlist); Trace("sygus-abduct-debug") << "...finish" << std::endl; Trace("sygus-abduct-debug") << "Make abduction predicate..." << std::endl; @@ -84,163 +87,23 @@ Node SygusAbduct::mkAbductionConjecture(const std::string& name, Node abd = nm->mkBoundVar(name.c_str(), abdType); Trace("sygus-abduct-debug") << "...finish" << std::endl; - // if provided, we will associate it with the function-to-synthesize + // the sygus variable list + Node abvl; + // if provided, we will associate the provide sygus datatype type with the + // function-to-synthesize. However, we must convert it so that its + // free symbols are universally quantified. if (!abdGType.isNull()) { Assert(abdGType.isDatatype() && abdGType.getDType().isSygus()); - // must convert all constructors to version with bound variables in "vars" - std::vector<SygusDatatype> sdts; - std::set<Type> unres; - Trace("sygus-abduct-debug") << "Process abduction type:" << std::endl; Trace("sygus-abduct-debug") << abdGType.getDType().getName() << std::endl; - // datatype types we need to process - std::vector<TypeNode> dtToProcess; - // datatype types we have processed - std::map<TypeNode, TypeNode> dtProcessed; - dtToProcess.push_back(abdGType); - std::stringstream ssutn0; - ssutn0 << abdGType.getDType().getName() << "_s"; - TypeNode abdTNew = - nm->mkSort(ssutn0.str(), ExprManager::SORT_FLAG_PLACEHOLDER); - unres.insert(abdTNew.toType()); - dtProcessed[abdGType] = abdTNew; - - // We must convert all symbols in the sygus datatype type abdGType to - // apply the substitution { syms -> varlist }, where syms is the free - // variables of the input problem, and varlist is the formal argument list - // of the abduct-to-synthesize. For example, given user-provided sygus - // grammar: - // G -> a | +( b, G ) - // we synthesize a abduct A with two arguments x_a and x_b corresponding to - // a and b, and reconstruct the grammar: - // G' -> x_a | +( x_b, G' ) - // In this way, x_a and x_b are treated as bound variables and handled as - // arguments of the abduct-to-synthesize instead of as free variables with - // no relation to A. We additionally require that x_a, when printed, prints - // "a", which we do with a custom sygus callback below. + // substitute the free symbols of the grammar with variables corresponding + // to the formal argument list of the new sygus datatype type. + TypeNode abdGTypeS = datatypes::utils::substituteAndGeneralizeSygusType( + abdGType, syms, varlist); - // We are traversing over the subfield types of the datatype to convert - // them into the form described above. - while (!dtToProcess.empty()) - { - std::vector<TypeNode> dtNextToProcess; - for (const TypeNode& curr : dtToProcess) - { - Assert(curr.isDatatype() && curr.getDType().isSygus()); - const DType& dtc = curr.getDType(); - std::stringstream ssdtn; - ssdtn << dtc.getName() << "_s"; - sdts.push_back(SygusDatatype(ssdtn.str())); - Trace("sygus-abduct-debug") - << "Process datatype " << sdts.back().getName() << "..." - << std::endl; - for (unsigned j = 0, ncons = dtc.getNumConstructors(); j < ncons; j++) - { - Node op = dtc[j].getSygusOp(); - // apply the substitution to the argument - Node ops = op.substitute( - syms.begin(), syms.end(), varlist.begin(), varlist.end()); - Trace("sygus-abduct-debug") << " Process constructor " << op << " / " - << ops << "..." << std::endl; - std::vector<TypeNode> cargs; - for (unsigned k = 0, nargs = dtc[j].getNumArgs(); k < nargs; k++) - { - TypeNode argt = dtc[j].getArgType(k); - std::map<TypeNode, TypeNode>::iterator itdp = - dtProcessed.find(argt); - TypeNode argtNew; - if (itdp == dtProcessed.end()) - { - std::stringstream ssutn; - ssutn << argt.getDType().getName() << "_s"; - argtNew = - nm->mkSort(ssutn.str(), ExprManager::SORT_FLAG_PLACEHOLDER); - Trace("sygus-abduct-debug") - << " ...unresolved type " << argtNew << " for " << argt - << std::endl; - unres.insert(argtNew.toType()); - dtProcessed[argt] = argtNew; - dtNextToProcess.push_back(argt); - } - else - { - argtNew = itdp->second; - } - Trace("sygus-abduct-debug") - << " Arg #" << k << ": " << argtNew << std::endl; - cargs.push_back(argtNew); - } - // callback prints as the expression - std::shared_ptr<SygusPrintCallback> spc; - std::vector<Expr> args; - if (op.getKind() == LAMBDA) - { - Node opBody = op[1]; - for (const Node& v : op[0]) - { - args.push_back(v.toExpr()); - } - spc = std::make_shared<printer::SygusExprPrintCallback>( - opBody.toExpr(), args); - } - else if (cargs.empty()) - { - spc = std::make_shared<printer::SygusExprPrintCallback>(op.toExpr(), - args); - } - std::stringstream ss; - ss << ops.getKind(); - Trace("sygus-abduct-debug") - << "Add constructor : " << ops << std::endl; - sdts.back().addConstructor(ops, ss.str(), cargs, spc); - } - Trace("sygus-abduct-debug") - << "Set sygus : " << dtc.getSygusType() << " " << abvl << std::endl; - TypeNode stn = dtc.getSygusType(); - sdts.back().initializeDatatype( - stn, abvl, dtc.getSygusAllowConst(), dtc.getSygusAllowAll()); - } - dtToProcess.clear(); - dtToProcess.insert( - dtToProcess.end(), dtNextToProcess.begin(), dtNextToProcess.end()); - } - Trace("sygus-abduct-debug") - << "Make " << sdts.size() << " datatype types..." << std::endl; - // extract the datatypes - std::vector<Datatype> datatypes; - for (unsigned i = 0, ndts = sdts.size(); i < ndts; i++) - { - datatypes.push_back(sdts[i].getDatatype()); - } - // make the datatype types - std::vector<DatatypeType> datatypeTypes = - nm->toExprManager()->mkMutualDatatypeTypes( - datatypes, unres, ExprManager::DATATYPE_FLAG_PLACEHOLDER); - TypeNode abdGTypeS = TypeNode::fromType(datatypeTypes[0]); - if (Trace.isOn("sygus-abduct-debug")) - { - Trace("sygus-abduct-debug") << "Made datatype types:" << std::endl; - for (unsigned j = 0, ndts = datatypeTypes.size(); j < ndts; j++) - { - const DType& dtj = TypeNode::fromType(datatypeTypes[j]).getDType(); - Trace("sygus-abduct-debug") << "#" << j << ": " << dtj << std::endl; - for (unsigned k = 0, ncons = dtj.getNumConstructors(); k < ncons; k++) - { - for (unsigned l = 0, nargs = dtj[k].getNumArgs(); l < nargs; l++) - { - if (!dtj[k].getArgType(l).isDatatype()) - { - Trace("sygus-abduct-debug") - << "Argument " << l << " of " << dtj[k] - << " is not datatype : " << dtj[k].getArgType(l) << std::endl; - AlwaysAssert(false); - } - } - } - } - } + Assert(abdGTypeS.isDatatype() && abdGTypeS.getDType().isSygus()); Trace("sygus-abduct-debug") << "Make sygus grammar attribute..." << std::endl; @@ -250,6 +113,19 @@ Node SygusAbduct::mkAbductionConjecture(const std::string& name, theory::SygusSynthGrammarAttribute ssg; abd.setAttribute(ssg, sym); Trace("sygus-abduct-debug") << "Finished setting up grammar." << std::endl; + + // use the bound variable list from the new substituted grammar type + const DType& agtsd = abdGTypeS.getDType(); + abvl = agtsd.getSygusVarList(); + Assert(!abvl.isNull() && abvl.getKind() == BOUND_VAR_LIST); + } + else + { + // the bound variable list of the abduct-to-synthesize is determined by + // the variable list above + abvl = nm->mkNode(BOUND_VAR_LIST, varlist); + // We do not set a grammar type for abd (SygusSynthGrammarAttribute). + // Its grammar will be constructed internally in the default way } Trace("sygus-abduct-debug") << "Make abduction predicate app..." << std::endl; diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds index 06f05a8af..800847ffe 100644 --- a/src/theory/strings/kinds +++ b/src/theory/strings/kinds @@ -58,6 +58,27 @@ constant CONST_STRING \ "util/string.h" \ "a string of characters" +# the type +operator SEQUENCE_TYPE 1 "seuence type, takes as parameter the type of the elements" +cardinality SEQUENCE_TYPE \ + "::CVC4::theory::strings::SequenceProperties::computeCardinality(%TYPE%)" \ + "theory/strings/theory_strings_type_rules.h" +well-founded SEQUENCE_TYPE \ + "::CVC4::theory::strings::SequenceProperties::isWellFounded(%TYPE%)" \ + "::CVC4::theory::strings::SequenceProperties::mkGroundTerm(%TYPE%)" \ + "theory/strings/theory_strings_type_rules.h" +enumerator SEQUENCE_TYPE \ + "::CVC4::theory::strings::SequenceEnumerator" \ + "theory/strings/type_enumerator.h" + +constant CONST_SEQUENCE \ + ::CVC4::ExprSequence \ + ::CVC4::ExprSequenceHashFunction \ + "expr/expr_sequence.h" \ + "a sequence of characters" + +operator SEQ_UNIT 1 "a sequence of length one" + # equal equal / less than / output operator STRING_TO_REGEXP 1 "convert string to regexp" operator REGEXP_CONCAT 2: "regexp concat" @@ -138,4 +159,9 @@ typerule STRING_FROM_CODE "SimpleTypeRule<RString, AInteger>" typerule STRING_TOUPPER "SimpleTypeRule<RString, AString>" typerule STRING_TOLOWER "SimpleTypeRule<RString, AString>" +### sequence specific operators + +typerule CONST_SEQUENCE ::CVC4::theory::strings::ConstSequenceTypeRule +typerule SEQ_UNIT ::CVC4::theory::strings::SeqUnitTypeRule + endtheory diff --git a/src/theory/strings/rewrites.cpp b/src/theory/strings/rewrites.cpp index 2953a2b3c..a4055c4f9 100644 --- a/src/theory/strings/rewrites.cpp +++ b/src/theory/strings/rewrites.cpp @@ -200,6 +200,7 @@ const char* toString(Rewrite r) case Rewrite::LEN_REPL_INV: return "LEN_REPL_INV"; case Rewrite::LEN_CONV_INV: return "LEN_CONV_INV"; case Rewrite::CHARAT_ELIM: return "CHARAT_ELIM"; + case Rewrite::SEQ_UNIT_EVAL: return "SEQ_UNIT_EVAL"; default: return "?"; } } diff --git a/src/theory/strings/rewrites.h b/src/theory/strings/rewrites.h index 7a315ebd3..96a3b65fd 100644 --- a/src/theory/strings/rewrites.h +++ b/src/theory/strings/rewrites.h @@ -202,7 +202,8 @@ enum class Rewrite : uint32_t LEN_CONCAT, LEN_REPL_INV, LEN_CONV_INV, - CHARAT_ELIM + CHARAT_ELIM, + SEQ_UNIT_EVAL }; /** diff --git a/src/theory/strings/sequences_rewriter.cpp b/src/theory/strings/sequences_rewriter.cpp index 2d2ec0af0..4f74d7c15 100644 --- a/src/theory/strings/sequences_rewriter.cpp +++ b/src/theory/strings/sequences_rewriter.cpp @@ -264,7 +264,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) // Add a constant string to the side with more `cn`s to restore // the difference in number of `cn`s std::vector<Node> vec(diff, cn); - trimmed[j].push_back(Word::mkWord(vec)); + trimmed[j].push_back(Word::mkWordFlatten(vec)); } } @@ -602,7 +602,7 @@ Node SequencesRewriter::rewriteConcat(Node node) std::vector<Node> wvec; wvec.push_back(preNode); wvec.push_back(tmpNode[0]); - preNode = Word::mkWord(wvec); + preNode = Word::mkWordFlatten(wvec); node_vec.push_back(preNode); } else @@ -644,7 +644,7 @@ Node SequencesRewriter::rewriteConcat(Node node) std::vector<Node> vec; vec.push_back(preNode); vec.push_back(tmpNode); - preNode = Word::mkWord(vec); + preNode = Word::mkWordFlatten(vec); } } } @@ -1461,6 +1461,10 @@ RewriteResponse SequencesRewriter::postRewrite(TNode node) { retNode = rewriteRepeatRegExp(node); } + else if (nk == SEQ_UNIT) + { + retNode = rewriteSeqUnit(node); + } Trace("sequences-postrewrite") << "Strings::SequencesRewriter::postRewrite returning " << retNode @@ -3095,6 +3099,19 @@ Node SequencesRewriter::canonicalStrForSymbolicLength(Node len, TypeNode stype) return res; } +Node SequencesRewriter::rewriteSeqUnit(Node node) +{ + NodeManager* nm = NodeManager::currentNM(); + if (node[0].isConst()) + { + std::vector<Expr> seq; + seq.push_back(node[0].toExpr()); + TypeNode stype = nm->mkSequenceType(node[0].getType()); + Node ret = nm->mkConst(ExprSequence(stype.toType(), seq)); + return returnRewrite(node, ret, Rewrite::SEQ_UNIT_EVAL); + } + return node; +} Node SequencesRewriter::returnRewrite(Node node, Node ret, Rewrite r) { diff --git a/src/theory/strings/sequences_rewriter.h b/src/theory/strings/sequences_rewriter.h index 56b74f536..490dd8b3c 100644 --- a/src/theory/strings/sequences_rewriter.h +++ b/src/theory/strings/sequences_rewriter.h @@ -224,6 +224,12 @@ class SequencesRewriter : public TheoryRewriter * Returns the rewritten form of node. */ Node rewriteStringToCode(Node node); + /** rewrite seq.unit + * This is the entry point for post-rewriting terms n of the form + * seq.unit( t ) + * Returns the rewritten form of node. + */ + Node rewriteSeqUnit(Node node); /** length preserving rewrite * diff --git a/src/theory/strings/term_registry.cpp b/src/theory/strings/term_registry.cpp index ec034b0c9..6330d7c10 100644 --- a/src/theory/strings/term_registry.cpp +++ b/src/theory/strings/term_registry.cpp @@ -374,6 +374,13 @@ Node TermRegistry::getRegisterTermAtomicLemma(Node n, LengthStatus s, std::map<Node, bool>& reqPhase) { + if (n.isConst()) + { + // No need to send length for constant terms. This case may be triggered + // for cases where the skolem cache automatically replaces a skolem by + // a constant. + return Node::null(); + } Assert(n.getType().isStringLike()); NodeManager* nm = NodeManager::currentNM(); Node n_len = nm->mkNode(kind::STRING_LENGTH, n); @@ -433,14 +440,6 @@ Node TermRegistry::getRegisterTermAtomicLemma(Node n, Assert(false); } - // additionally add len( x ) >= 0 ? - if (options::stringLenGeqZ()) - { - Node n_len_geq = nm->mkNode(kind::GEQ, n_len, d_zero); - n_len_geq = Rewriter::rewrite(n_len_geq); - lems.push_back(n_len_geq); - } - if (lems.empty()) { return Node::null(); diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index 939146a3d..50c6ede62 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -38,48 +38,45 @@ StringsPreprocess::StringsPreprocess(SkolemCache* sc, SequencesStatistics& stats) : d_sc(sc), d_statistics(stats) { - //Constants - d_zero = NodeManager::currentNM()->mkConst(Rational(0)); - d_one = NodeManager::currentNM()->mkConst(Rational(1)); - d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1)); } StringsPreprocess::~StringsPreprocess(){ } -Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { - unsigned prev_new_nodes = new_nodes.size(); - Trace("strings-preprocess-debug") << "StringsPreprocess::simplify: " << t << std::endl; +Node StringsPreprocess::reduce(Node t, + std::vector<Node>& asserts, + SkolemCache* sc) +{ + Trace("strings-preprocess-debug") + << "StringsPreprocess::reduce: " << t << std::endl; Node retNode = t; - NodeManager *nm = NodeManager::currentNM(); + NodeManager* nm = NodeManager::currentNM(); + Node zero = nm->mkConst(Rational(0)); + Node one = nm->mkConst(Rational(1)); + Node negOne = nm->mkConst(Rational(-1)); if( t.getKind() == kind::STRING_SUBSTR ) { // processing term: substr( s, n, m ) Node s = t[0]; Node n = t[1]; Node m = t[2]; - Node skt = d_sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "sst"); + Node skt = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "sst"); Node t12 = nm->mkNode(PLUS, n, m); t12 = Rewriter::rewrite(t12); Node lt0 = nm->mkNode(STRING_LENGTH, s); //start point is greater than or equal zero - Node c1 = nm->mkNode(GEQ, n, d_zero); + Node c1 = nm->mkNode(GEQ, n, zero); //start point is less than end of string Node c2 = nm->mkNode(GT, lt0, n); //length is positive - Node c3 = nm->mkNode(GT, m, d_zero); + Node c3 = nm->mkNode(GT, m, zero); Node cond = nm->mkNode(AND, c1, c2, c3); Node emp = Word::mkEmptyWord(t.getType()); - Node sk1 = n == d_zero ? emp - : d_sc->mkSkolemCached( - s, n, SkolemCache::SK_PREFIX, "sspre"); - Node sk2 = ArithEntail::check(t12, lt0) - ? emp - : d_sc->mkSkolemCached( - s, t12, SkolemCache::SK_SUFFIX_REM, "sssufr"); + Node sk1 = sc->mkSkolemCached(s, n, SkolemCache::SK_PREFIX, "sspre"); + Node sk2 = sc->mkSkolemCached(s, t12, SkolemCache::SK_SUFFIX_REM, "sssufr"); Node b11 = s.eqNode(nm->mkNode(STRING_CONCAT, sk1, skt, sk2)); //length of first skolem is second argument Node b12 = nm->mkNode(STRING_LENGTH, sk1).eqNode(n); @@ -89,7 +86,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node b13 = nm->mkNode( OR, nm->mkNode(EQUAL, lsk2, nm->mkNode(MINUS, lt0, nm->mkNode(PLUS, n, m))), - nm->mkNode(EQUAL, lsk2, d_zero)); + nm->mkNode(EQUAL, lsk2, zero)); // Length of the result is at most m Node b14 = nm->mkNode(LEQ, nm->mkNode(STRING_LENGTH, skt), m); @@ -112,7 +109,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // satisfied. If n + m is less than the length of s, then len(sk2) = 0 // cannot be satisfied because we have the constraint that len(skt) <= m, // so sk2 must be greater than 0. - new_nodes.push_back( lemma ); + asserts.push_back(lemma); // Thus, substr( s, n, m ) = skt retNode = skt; @@ -123,15 +120,16 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node x = t[0]; Node y = t[1]; Node n = t[2]; - Node skk = nm->mkSkolem("iok", nm->integerType(), "created for indexof"); + Node skk = sc->mkTypedSkolemCached( + nm->integerType(), t, SkolemCache::SK_PURIFY, "iok"); Node negone = nm->mkConst(Rational(-1)); Node krange = nm->mkNode(GEQ, skk, negone); // assert: indexof( x, y, n ) >= -1 - new_nodes.push_back( krange ); + asserts.push_back(krange); krange = nm->mkNode(GEQ, nm->mkNode(STRING_LENGTH, x), skk); // assert: len( x ) >= indexof( x, y, z ) - new_nodes.push_back( krange ); + asserts.push_back(krange); // substr( x, n, len( x ) - n ) Node st = nm->mkNode(STRING_SUBSTR, @@ -139,16 +137,16 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { n, nm->mkNode(MINUS, nm->mkNode(STRING_LENGTH, x), n)); Node io2 = - d_sc->mkSkolemCached(st, y, SkolemCache::SK_FIRST_CTN_PRE, "iopre"); + sc->mkSkolemCached(st, y, SkolemCache::SK_FIRST_CTN_PRE, "iopre"); Node io4 = - d_sc->mkSkolemCached(st, y, SkolemCache::SK_FIRST_CTN_POST, "iopost"); + sc->mkSkolemCached(st, y, SkolemCache::SK_FIRST_CTN_POST, "iopost"); // ~contains( substr( x, n, len( x ) - n ), y ) Node c11 = nm->mkNode(STRING_STRCTN, st, y).negate(); // n > len( x ) Node c12 = nm->mkNode(GT, n, nm->mkNode(STRING_LENGTH, x)); // 0 > n - Node c13 = nm->mkNode(GT, d_zero, n); + Node c13 = nm->mkNode(GT, zero, n); Node cond1 = nm->mkNode(OR, c11, c12, c13); // skk = -1 Node cc1 = skk.eqNode(negone); @@ -171,8 +169,8 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { nm->mkNode( STRING_SUBSTR, y, - d_zero, - nm->mkNode(MINUS, nm->mkNode(STRING_LENGTH, y), d_one))), + zero, + nm->mkNode(MINUS, nm->mkNode(STRING_LENGTH, y), one))), y) .negate(); // skk = n + len( io2 ) @@ -189,7 +187,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // skk = n + len( io2 ) // for fresh io2, io4. Node rr = nm->mkNode(ITE, cond1, cc1, nm->mkNode(ITE, cond2, cc2, cc3)); - new_nodes.push_back( rr ); + asserts.push_back(rr); // Thus, indexof( x, y, n ) = skk. retNode = skk; @@ -198,7 +196,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { { // processing term: int.to.str( n ) Node n = t[0]; - Node itost = d_sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "itost"); + Node itost = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "itost"); Node leni = nm->mkNode(STRING_LENGTH, itost); std::vector<Node> conc; @@ -206,21 +204,20 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { argTypes.push_back(nm->integerType()); Node u = nm->mkSkolem("U", nm->mkFunctionType(argTypes, nm->integerType())); - Node lem = nm->mkNode(GEQ, leni, d_one); + Node lem = nm->mkNode(GEQ, leni, one); conc.push_back(lem); lem = n.eqNode(nm->mkNode(APPLY_UF, u, leni)); conc.push_back(lem); - lem = d_zero.eqNode(nm->mkNode(APPLY_UF, u, d_zero)); + lem = zero.eqNode(nm->mkNode(APPLY_UF, u, zero)); conc.push_back(lem); - Node x = nm->mkBoundVar(nm->integerType()); - Node xPlusOne = nm->mkNode(PLUS, x, d_one); + Node x = SkolemCache::mkIndexVar(t); + Node xPlusOne = nm->mkNode(PLUS, x, one); Node xbv = nm->mkNode(BOUND_VAR_LIST, x); - Node g = - nm->mkNode(AND, nm->mkNode(GEQ, x, d_zero), nm->mkNode(LT, x, leni)); - Node sx = nm->mkNode(STRING_SUBSTR, itost, x, d_one); + Node g = nm->mkNode(AND, nm->mkNode(GEQ, x, zero), nm->mkNode(LT, x, leni)); + Node sx = nm->mkNode(STRING_SUBSTR, itost, x, one); Node ux = nm->mkNode(APPLY_UF, u, x); Node ux1 = nm->mkNode(APPLY_UF, u, xPlusOne); Node c0 = nm->mkNode(STRING_TO_CODE, nm->mkConst(String("0"))); @@ -229,10 +226,10 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node ten = nm->mkConst(Rational(10)); Node eq = ux1.eqNode(nm->mkNode(PLUS, c, nm->mkNode(MULT, ten, ux))); Node leadingZeroPos = - nm->mkNode(AND, x.eqNode(d_zero), nm->mkNode(GT, leni, d_one)); + nm->mkNode(AND, x.eqNode(zero), nm->mkNode(GT, leni, one)); Node cb = nm->mkNode( AND, - nm->mkNode(GEQ, c, nm->mkNode(ITE, leadingZeroPos, d_one, d_zero)), + nm->mkNode(GEQ, c, nm->mkNode(ITE, leadingZeroPos, one, zero)), nm->mkNode(LT, c, ten)); Node ux1lem = nm->mkNode(GEQ, n, ux1); @@ -241,11 +238,11 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { lem = nm->mkNode(FORALL, xbv, lem); conc.push_back(lem); - Node nonneg = nm->mkNode(GEQ, n, d_zero); + Node nonneg = nm->mkNode(GEQ, n, zero); Node emp = Word::mkEmptyWord(t.getType()); lem = nm->mkNode(ITE, nonneg, nm->mkNode(AND, conc), itost.eqNode(emp)); - new_nodes.push_back(lem); + asserts.push_back(lem); // assert: // IF n>=0 // THEN: @@ -274,26 +271,27 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { retNode = itost; } else if( t.getKind() == kind::STRING_STOI ) { Node s = t[0]; - Node stoit = nm->mkSkolem("stoit", nm->integerType(), "created for stoi"); + Node stoit = sc->mkTypedSkolemCached( + nm->integerType(), t, SkolemCache::SK_PURIFY, "stoit"); Node lens = nm->mkNode(STRING_LENGTH, s); std::vector<Node> conc1; - Node lem = stoit.eqNode(d_neg_one); + Node lem = stoit.eqNode(negOne); conc1.push_back(lem); Node emp = Word::mkEmptyWord(s.getType()); Node sEmpty = s.eqNode(emp); Node k = nm->mkSkolem("k", nm->integerType()); - Node kc1 = nm->mkNode(GEQ, k, d_zero); + Node kc1 = nm->mkNode(GEQ, k, zero); Node kc2 = nm->mkNode(LT, k, lens); Node c0 = nm->mkNode(STRING_TO_CODE, nm->mkConst(String("0"))); Node codeSk = nm->mkNode( MINUS, - nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, s, k, d_one)), + nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, s, k, one)), c0); Node ten = nm->mkConst(Rational(10)); Node kc3 = nm->mkNode( - OR, nm->mkNode(LT, codeSk, d_zero), nm->mkNode(GEQ, codeSk, ten)); + OR, nm->mkNode(LT, codeSk, zero), nm->mkNode(GEQ, codeSk, ten)); conc1.push_back(nm->mkNode(OR, sEmpty, nm->mkNode(AND, kc1, kc2, kc3))); std::vector<Node> conc2; @@ -304,24 +302,22 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { lem = stoit.eqNode(nm->mkNode(APPLY_UF, u, lens)); conc2.push_back(lem); - lem = d_zero.eqNode(nm->mkNode(APPLY_UF, u, d_zero)); + lem = zero.eqNode(nm->mkNode(APPLY_UF, u, zero)); conc2.push_back(lem); - lem = nm->mkNode(GT, lens, d_zero); + lem = nm->mkNode(GT, lens, zero); conc2.push_back(lem); - Node x = nm->mkBoundVar(nm->integerType()); + Node x = SkolemCache::mkIndexVar(t); Node xbv = nm->mkNode(BOUND_VAR_LIST, x); - Node g = - nm->mkNode(AND, nm->mkNode(GEQ, x, d_zero), nm->mkNode(LT, x, lens)); - Node sx = nm->mkNode(STRING_SUBSTR, s, x, d_one); + Node g = nm->mkNode(AND, nm->mkNode(GEQ, x, zero), nm->mkNode(LT, x, lens)); + Node sx = nm->mkNode(STRING_SUBSTR, s, x, one); Node ux = nm->mkNode(APPLY_UF, u, x); - Node ux1 = nm->mkNode(APPLY_UF, u, nm->mkNode(PLUS, x, d_one)); + Node ux1 = nm->mkNode(APPLY_UF, u, nm->mkNode(PLUS, x, one)); Node c = nm->mkNode(MINUS, nm->mkNode(STRING_TO_CODE, sx), c0); Node eq = ux1.eqNode(nm->mkNode(PLUS, c, nm->mkNode(MULT, ten, ux))); - Node cb = - nm->mkNode(AND, nm->mkNode(GEQ, c, d_zero), nm->mkNode(LT, c, ten)); + Node cb = nm->mkNode(AND, nm->mkNode(GEQ, c, zero), nm->mkNode(LT, c, ten)); Node ux1lem = nm->mkNode(GEQ, stoit, ux1); @@ -329,9 +325,9 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { lem = nm->mkNode(FORALL, xbv, lem); conc2.push_back(lem); - Node sneg = nm->mkNode(LT, stoit, d_zero); + Node sneg = nm->mkNode(LT, stoit, zero); lem = nm->mkNode(ITE, sneg, nm->mkNode(AND, conc1), nm->mkNode(AND, conc2)); - new_nodes.push_back(lem); + asserts.push_back(lem); // assert: // IF stoit < 0 @@ -362,10 +358,10 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node z = t[2]; TypeNode tn = t[0].getType(); Node rp1 = - d_sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_CTN_PRE, "rfcpre"); + sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_CTN_PRE, "rfcpre"); Node rp2 = - d_sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_CTN_POST, "rfcpost"); - Node rpw = d_sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "rpw"); + sc->mkSkolemCached(x, y, SkolemCache::SK_FIRST_CTN_POST, "rfcpost"); + Node rpw = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "rpw"); // y = "" Node emp = Word::mkEmptyWord(tn); @@ -387,10 +383,10 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { rp1, nm->mkNode(kind::STRING_SUBSTR, y, - d_zero, + zero, nm->mkNode(kind::MINUS, nm->mkNode(kind::STRING_LENGTH, y), - d_one))), + one))), y) .negate(); @@ -410,7 +406,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { cond2, nm->mkNode(kind::AND, c21, c22, c23), rpw.eqNode(x))); - new_nodes.push_back( rr ); + asserts.push_back(rr); // Thus, replace( x, y, z ) = rpw. retNode = rpw; @@ -421,16 +417,16 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node x = t[0]; Node y = t[1]; Node z = t[2]; - Node rpaw = d_sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "rpaw"); + Node rpaw = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "rpaw"); - Node numOcc = d_sc->mkTypedSkolemCached( + Node numOcc = sc->mkTypedSkolemCached( nm->integerType(), x, y, SkolemCache::SK_NUM_OCCUR, "numOcc"); std::vector<TypeNode> argTypes; argTypes.push_back(nm->integerType()); Node us = nm->mkSkolem("Us", nm->mkFunctionType(argTypes, nm->stringType())); TypeNode ufType = nm->mkFunctionType(argTypes, nm->integerType()); - Node uf = d_sc->mkTypedSkolemCached( + Node uf = sc->mkTypedSkolemCached( ufType, x, y, SkolemCache::SK_OCCUR_INDEX, "Uf"); Node ufno = nm->mkNode(APPLY_UF, uf, numOcc); @@ -438,27 +434,27 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node rem = nm->mkNode(STRING_SUBSTR, x, ufno, nm->mkNode(STRING_LENGTH, x)); std::vector<Node> lem; - lem.push_back(nm->mkNode(GEQ, numOcc, d_zero)); - lem.push_back(rpaw.eqNode(nm->mkNode(APPLY_UF, us, d_zero))); + lem.push_back(nm->mkNode(GEQ, numOcc, zero)); + lem.push_back(rpaw.eqNode(nm->mkNode(APPLY_UF, us, zero))); lem.push_back(usno.eqNode(rem)); - lem.push_back(nm->mkNode(APPLY_UF, uf, d_zero).eqNode(d_zero)); - lem.push_back(nm->mkNode(STRING_STRIDOF, x, y, ufno).eqNode(d_neg_one)); + lem.push_back(nm->mkNode(APPLY_UF, uf, zero).eqNode(zero)); + lem.push_back(nm->mkNode(STRING_STRIDOF, x, y, ufno).eqNode(negOne)); - Node i = nm->mkBoundVar(nm->integerType()); + Node i = SkolemCache::mkIndexVar(t); Node bvli = nm->mkNode(BOUND_VAR_LIST, i); Node bound = - nm->mkNode(AND, nm->mkNode(GEQ, i, d_zero), nm->mkNode(LT, i, numOcc)); + nm->mkNode(AND, nm->mkNode(GEQ, i, zero), nm->mkNode(LT, i, numOcc)); Node ufi = nm->mkNode(APPLY_UF, uf, i); - Node ufip1 = nm->mkNode(APPLY_UF, uf, nm->mkNode(PLUS, i, d_one)); + Node ufip1 = nm->mkNode(APPLY_UF, uf, nm->mkNode(PLUS, i, one)); Node ii = nm->mkNode(STRING_STRIDOF, x, y, ufi); Node cc = nm->mkNode( STRING_CONCAT, nm->mkNode(STRING_SUBSTR, x, ufi, nm->mkNode(MINUS, ii, ufi)), z, - nm->mkNode(APPLY_UF, us, nm->mkNode(PLUS, i, d_one))); + nm->mkNode(APPLY_UF, us, nm->mkNode(PLUS, i, one))); std::vector<Node> flem; - flem.push_back(ii.eqNode(d_neg_one).negate()); + flem.push_back(ii.eqNode(negOne).negate()); flem.push_back(nm->mkNode(APPLY_UF, us, i).eqNode(cc)); flem.push_back( ufip1.eqNode(nm->mkNode(PLUS, ii, nm->mkNode(STRING_LENGTH, y)))); @@ -487,7 +483,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node emp = Word::mkEmptyWord(t.getType()); Node assert = nm->mkNode(ITE, y.eqNode(emp), rpaw.eqNode(x), nm->mkNode(AND, lem)); - new_nodes.push_back(assert); + asserts.push_back(assert); // Thus, replaceall( x, y, z ) = rpaw retNode = rpaw; @@ -495,19 +491,17 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { else if (t.getKind() == STRING_TOLOWER || t.getKind() == STRING_TOUPPER) { Node x = t[0]; - Node r = d_sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "r"); + Node r = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "r"); Node lenx = nm->mkNode(STRING_LENGTH, x); Node lenr = nm->mkNode(STRING_LENGTH, r); Node eqLenA = lenx.eqNode(lenr); - Node i = nm->mkBoundVar(nm->integerType()); + Node i = SkolemCache::mkIndexVar(t); Node bvi = nm->mkNode(BOUND_VAR_LIST, i); - Node ci = - nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, x, i, d_one)); - Node ri = - nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, r, i, d_one)); + Node ci = nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, x, i, one)); + Node ri = nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, r, i, one)); Node lb = nm->mkConst(Rational(t.getKind() == STRING_TOUPPER ? 97 : 65)); Node ub = nm->mkConst(Rational(t.getKind() == STRING_TOUPPER ? 122 : 90)); @@ -521,7 +515,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { ci); Node bound = - nm->mkNode(AND, nm->mkNode(LEQ, d_zero, i), nm->mkNode(LT, i, lenr)); + nm->mkNode(AND, nm->mkNode(LEQ, zero, i), nm->mkNode(LT, i, lenr)); Node rangeA = nm->mkNode(FORALL, bvi, nm->mkNode(OR, bound.negate(), ri.eqNode(res))); @@ -533,7 +527,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // str.code( str.substr(r,i,1) ) = ite( 97 <= ci <= 122, ci-32, ci) // where ci = str.code( str.substr(x,i,1) ) Node assert = nm->mkNode(AND, eqLenA, rangeA); - new_nodes.push_back(assert); + asserts.push_back(assert); // Thus, toLower( x ) = r retNode = r; @@ -541,22 +535,22 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { else if (t.getKind() == STRING_REV) { Node x = t[0]; - Node r = d_sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "r"); + Node r = sc->mkSkolemCached(t, SkolemCache::SK_PURIFY, "r"); Node lenx = nm->mkNode(STRING_LENGTH, x); Node lenr = nm->mkNode(STRING_LENGTH, r); Node eqLenA = lenx.eqNode(lenr); - Node i = nm->mkBoundVar(nm->integerType()); + Node i = SkolemCache::mkIndexVar(t); Node bvi = nm->mkNode(BOUND_VAR_LIST, i); Node revi = nm->mkNode( - MINUS, nm->mkNode(STRING_LENGTH, x), nm->mkNode(PLUS, i, d_one)); - Node ssr = nm->mkNode(STRING_SUBSTR, r, i, d_one); - Node ssx = nm->mkNode(STRING_SUBSTR, x, revi, d_one); + MINUS, nm->mkNode(STRING_LENGTH, x), nm->mkNode(PLUS, i, one)); + Node ssr = nm->mkNode(STRING_SUBSTR, r, i, one); + Node ssx = nm->mkNode(STRING_SUBSTR, x, revi, one); Node bound = - nm->mkNode(AND, nm->mkNode(LEQ, d_zero, i), nm->mkNode(LT, i, lenr)); + nm->mkNode(AND, nm->mkNode(LEQ, zero, i), nm->mkNode(LT, i, lenr)); Node rangeA = nm->mkNode( FORALL, bvi, nm->mkNode(OR, bound.negate(), ssr.eqNode(ssx))); // assert: @@ -564,7 +558,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // forall i. 0 <= i < len(r) => // substr(r,i,1) = substr(x,len(x)-(i+1),1) Node assert = nm->mkNode(AND, eqLenA, rangeA); - new_nodes.push_back(assert); + asserts.push_back(assert); // Thus, (str.rev x) = r retNode = r; @@ -576,31 +570,38 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { //negative contains reduces to existential Node lenx = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, x); Node lens = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, s); - Node b1 = NodeManager::currentNM()->mkBoundVar(NodeManager::currentNM()->integerType()); + Node b1 = SkolemCache::mkIndexVar(t); Node b1v = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, b1); - Node body = NodeManager::currentNM()->mkNode( kind::AND, - NodeManager::currentNM()->mkNode( kind::LEQ, d_zero, b1 ), - NodeManager::currentNM()->mkNode( kind::LEQ, b1, NodeManager::currentNM()->mkNode( kind::MINUS, lenx, lens ) ), - NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, x, b1, lens), s ) - ); + Node body = NodeManager::currentNM()->mkNode( + kind::AND, + NodeManager::currentNM()->mkNode(kind::LEQ, zero, b1), + NodeManager::currentNM()->mkNode( + kind::LEQ, + b1, + NodeManager::currentNM()->mkNode(kind::MINUS, lenx, lens)), + NodeManager::currentNM()->mkNode( + kind::EQUAL, + NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, x, b1, lens), + s)); retNode = NodeManager::currentNM()->mkNode( kind::EXISTS, b1v, body ); } else if (t.getKind() == kind::STRING_LEQ) { - Node ltp = nm->mkSkolem("ltp", nm->booleanType()); + Node ltp = sc->mkTypedSkolemCached( + nm->booleanType(), t, SkolemCache::SK_PURIFY, "ltp"); Node k = nm->mkSkolem("k", nm->integerType()); std::vector<Node> conj; - conj.push_back(nm->mkNode(GEQ, k, d_zero)); + conj.push_back(nm->mkNode(GEQ, k, zero)); Node substr[2]; Node code[2]; for (unsigned r = 0; r < 2; r++) { Node ta = t[r]; Node tb = t[1 - r]; - substr[r] = nm->mkNode(STRING_SUBSTR, ta, d_zero, k); + substr[r] = nm->mkNode(STRING_SUBSTR, ta, zero, k); code[r] = - nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, ta, k, d_one)); + nm->mkNode(STRING_TO_CODE, nm->mkNode(STRING_SUBSTR, ta, k, one)); conj.push_back(nm->mkNode(LEQ, k, nm->mkNode(STRING_LENGTH, ta))); } conj.push_back(substr[0].eqNode(substr[1])); @@ -632,18 +633,29 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // ELSE: str.code(substr( x, k, 1 )) > str.code(substr( y, k, 1 )) Node assert = nm->mkNode(ITE, t[0].eqNode(t[1]), ltp, nm->mkNode(AND, conj)); - new_nodes.push_back(assert); + asserts.push_back(assert); // Thus, str.<=( x, y ) = ltp retNode = ltp; } + return retNode; +} +Node StringsPreprocess::simplify(Node t, std::vector<Node>& asserts) +{ + size_t prev_asserts = asserts.size(); + // call the static reduce routine + Node retNode = reduce(t, asserts, d_sc); if( t!=retNode ){ Trace("strings-preprocess") << "StringsPreprocess::simplify: " << t << " -> " << retNode << std::endl; - if(!new_nodes.empty()) { - Trace("strings-preprocess") << " ... new nodes (" << (new_nodes.size()-prev_new_nodes) << "):" << std::endl; - for(unsigned int i=prev_new_nodes; i<new_nodes.size(); ++i) { - Trace("strings-preprocess") << " " << new_nodes[i] << std::endl; + if (!asserts.empty()) + { + Trace("strings-preprocess") + << " ... new nodes (" << (asserts.size() - prev_asserts) + << "):" << std::endl; + for (size_t i = prev_asserts; i < asserts.size(); ++i) + { + Trace("strings-preprocess") << " " << asserts[i] << std::endl; } } d_statistics.d_reductions << t.getKind(); @@ -656,14 +668,17 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { return retNode; } -Node StringsPreprocess::simplifyRec( Node t, std::vector< Node > & new_nodes, std::map< Node, Node >& visited ){ +Node StringsPreprocess::simplifyRec(Node t, + std::vector<Node>& asserts, + std::map<Node, Node>& visited) +{ std::map< Node, Node >::iterator it = visited.find(t); if( it!=visited.end() ){ return it->second; }else{ Node retNode = t; if( t.getNumChildren()==0 ){ - retNode = simplify( t, new_nodes ); + retNode = simplify(t, asserts); }else if( t.getKind()!=kind::FORALL ){ bool changed = false; std::vector< Node > cc; @@ -671,7 +686,7 @@ Node StringsPreprocess::simplifyRec( Node t, std::vector< Node > & new_nodes, st cc.push_back( t.getOperator() ); } for(unsigned i=0; i<t.getNumChildren(); i++) { - Node s = simplifyRec( t[i], new_nodes, visited ); + Node s = simplifyRec(t[i], asserts, visited); cc.push_back( s ); if( s!=t[i] ) { changed = true; @@ -681,24 +696,27 @@ Node StringsPreprocess::simplifyRec( Node t, std::vector< Node > & new_nodes, st if( changed ){ tmp = NodeManager::currentNM()->mkNode( t.getKind(), cc ); } - retNode = simplify( tmp, new_nodes ); + retNode = simplify(tmp, asserts); } visited[t] = retNode; return retNode; } } -Node StringsPreprocess::processAssertion( Node n, std::vector< Node > &new_nodes ) { +Node StringsPreprocess::processAssertion(Node n, std::vector<Node>& asserts) +{ std::map< Node, Node > visited; - std::vector< Node > new_nodes_curr; - Node ret = simplifyRec( n, new_nodes_curr, visited ); - while( !new_nodes_curr.empty() ){ - Node curr = new_nodes_curr.back(); - new_nodes_curr.pop_back(); - std::vector< Node > new_nodes_tmp; - curr = simplifyRec( curr, new_nodes_tmp, visited ); - new_nodes_curr.insert( new_nodes_curr.end(), new_nodes_tmp.begin(), new_nodes_tmp.end() ); - new_nodes.push_back( curr ); + std::vector<Node> asserts_curr; + Node ret = simplifyRec(n, asserts_curr, visited); + while (!asserts_curr.empty()) + { + Node curr = asserts_curr.back(); + asserts_curr.pop_back(); + std::vector<Node> asserts_tmp; + curr = simplifyRec(curr, asserts_tmp, visited); + asserts_curr.insert( + asserts_curr.end(), asserts_tmp.begin(), asserts_tmp.end()); + asserts.push_back(curr); } return ret; } @@ -708,18 +726,22 @@ void StringsPreprocess::processAssertions( std::vector< Node > &vec_node ){ for( unsigned i=0; i<vec_node.size(); i++ ){ Trace("strings-preprocess-debug") << "Preprocessing assertion " << vec_node[i] << std::endl; //preprocess until fixed point - std::vector< Node > new_nodes; - std::vector< Node > new_nodes_curr; - new_nodes_curr.push_back( vec_node[i] ); - while( !new_nodes_curr.empty() ){ - Node curr = new_nodes_curr.back(); - new_nodes_curr.pop_back(); - std::vector< Node > new_nodes_tmp; - curr = simplifyRec( curr, new_nodes_tmp, visited ); - new_nodes_curr.insert( new_nodes_curr.end(), new_nodes_tmp.begin(), new_nodes_tmp.end() ); - new_nodes.push_back( curr ); + std::vector<Node> asserts; + std::vector<Node> asserts_curr; + asserts_curr.push_back(vec_node[i]); + while (!asserts_curr.empty()) + { + Node curr = asserts_curr.back(); + asserts_curr.pop_back(); + std::vector<Node> asserts_tmp; + curr = simplifyRec(curr, asserts_tmp, visited); + asserts_curr.insert( + asserts_curr.end(), asserts_tmp.begin(), asserts_tmp.end()); + asserts.push_back(curr); } - Node res = new_nodes.size()==1 ? new_nodes[0] : NodeManager::currentNM()->mkNode( kind::AND, new_nodes ); + Node res = asserts.size() == 1 + ? asserts[0] + : NodeManager::currentNM()->mkNode(kind::AND, asserts); if( res!=vec_node[i] ){ res = Rewriter::rewrite( res ); PROOF( ProofManager::currentPM()->addDependence( res, vec_node[i] ); ); diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h index fb6404aa6..1392b4ea1 100644 --- a/src/theory/strings/theory_strings_preprocess.h +++ b/src/theory/strings/theory_strings_preprocess.h @@ -44,21 +44,41 @@ class StringsPreprocess { context::UserContext* u, SequencesStatistics& stats); ~StringsPreprocess(); + /** The reduce routine + * + * This is the main routine for constructing the reduction lemma for + * an extended function t. It returns the simplified form of t, as well + * as assertions for t, interpeted conjunctively. The reduction lemma + * for t is: + * asserts[0] ^ ... ^ asserts[n] ^ t = t' + * where t' is the term returned by this method. + * The argument sc defines the methods for generating new Skolem variables. + * The return value is t itself if it is not reduced by this class. + * + * The reduction lemma for t is a way of specifying the complete semantics + * of t. In other words, any model satisfying the reduction lemma of t + * correctly interprets t. + * + * @param t The node to reduce, + * @param asserts The vector for storing the assertions that correspond to + * the reduction of t, + * @param sc The skolem cache for generating new variables, + * @return The reduced form of t. + */ + static Node reduce(Node t, std::vector<Node>& asserts, SkolemCache* sc); /** - * Returns a node t' such that - * (exists k) new_nodes => t = t' - * is valid, where k are the free skolems introduced when constructing - * new_nodes. + * Calls the above method for the skolem cache owned by this class, and + * records statistics. */ - Node simplify(Node t, std::vector<Node>& new_nodes); + Node simplify(Node t, std::vector<Node>& asserts); /** * Applies simplifyRec on t until a fixed point is reached, and returns * the resulting term t', which is such that - * (exists k) new_nodes => t = t' + * (exists k) asserts => t = t' * is valid, where k are the free skolems introduced when constructing - * new_nodes. + * asserts. */ - Node processAssertion(Node t, std::vector<Node>& new_nodes); + Node processAssertion(Node t, std::vector<Node>& asserts); /** * Replaces all formulas t in vec_node with an equivalent formula t' that * contains no free instances of extended functions (that is, extended @@ -68,21 +88,17 @@ class StringsPreprocess { void processAssertions(std::vector<Node>& vec_node); private: - /** commonly used constants */ - Node d_zero; - Node d_one; - Node d_neg_one; /** pointer to the skolem cache used by this class */ SkolemCache* d_sc; /** Reference to the statistics for the theory of strings/sequences. */ SequencesStatistics& d_statistics; /** * Applies simplify to all top-level extended function subterms of t. New - * assertions created in this reduction are added to new_nodes. The argument + * assertions created in this reduction are added to asserts. The argument * visited stores a cache of previous results. */ Node simplifyRec(Node t, - std::vector<Node>& new_nodes, + std::vector<Node>& asserts, std::map<Node, Node>& visited); }; diff --git a/src/theory/strings/theory_strings_type_rules.h b/src/theory/strings/theory_strings_type_rules.h index 93a32f26e..3229e6631 100644 --- a/src/theory/strings/theory_strings_type_rules.h +++ b/src/theory/strings/theory_strings_type_rules.h @@ -20,6 +20,9 @@ #ifndef CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H #define CVC4__THEORY__STRINGS__THEORY_STRINGS_TYPE_RULES_H +#include "expr/expr_sequence.h" +#include "expr/sequence.h" + namespace CVC4 { namespace theory { namespace strings { @@ -318,6 +321,53 @@ public: } }; +class ConstSequenceTypeRule +{ + public: + static TypeNode computeType(NodeManager* nodeManager, + TNode n, + bool check) + { + Assert(n.getKind() == kind::CONST_SEQUENCE); + return n.getConst<ExprSequence>().getSequence().getType(); + } +}; + +class SeqUnitTypeRule +{ + public: + static TypeNode computeType(NodeManager* nodeManager, + TNode n, + bool check) + { + return nodeManager->mkSequenceType(n[0].getType(check)); + } +}; + +/** Properties of the sequence type */ +struct SequenceProperties +{ + static Cardinality computeCardinality(TypeNode type) + { + Assert(type.getKind() == kind::SEQUENCE_TYPE); + return Cardinality::INTEGERS; + } + /** A sequence is well-founded if its element type is */ + static bool isWellFounded(TypeNode type) + { + return type[0].isWellFounded(); + } + /** Make ground term for sequence type (return the empty sequence) */ + static Node mkGroundTerm(TypeNode type) + { + Assert(type.isSequence()); + // empty sequence + std::vector<Expr> seq; + return NodeManager::currentNM()->mkConst( + ExprSequence(SequenceType(type.toType()), seq)); + } +}; /* struct SequenceProperties */ + }/* CVC4::theory::strings namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/strings/type_enumerator.cpp b/src/theory/strings/type_enumerator.cpp index d24206860..c25363e65 100644 --- a/src/theory/strings/type_enumerator.cpp +++ b/src/theory/strings/type_enumerator.cpp @@ -158,6 +158,67 @@ void StringEnumLen::mkCurr() d_curr = makeStandardModelConstant(d_witer->getData(), d_cardinality); } +SeqEnumLen::SeqEnumLen(TypeNode tn, + TypeEnumeratorProperties* tep, + uint32_t startLength) + : SEnumLen(tn, startLength) +{ + d_elementEnumerator.reset( + new TypeEnumerator(d_type.getSequenceElementType(), tep)); + mkCurr(); +} + +SeqEnumLen::SeqEnumLen(TypeNode tn, + TypeEnumeratorProperties* tep, + uint32_t startLength, + uint32_t endLength) + : SEnumLen(tn, startLength, endLength) +{ + d_elementEnumerator.reset( + new TypeEnumerator(d_type.getSequenceElementType(), tep)); + mkCurr(); +} + +SeqEnumLen::SeqEnumLen(const SeqEnumLen& wenum) + : SEnumLen(wenum), + d_elementEnumerator(new TypeEnumerator(*wenum.d_elementEnumerator)), + d_elementDomain(wenum.d_elementDomain) +{ +} + +bool SeqEnumLen::increment() +{ + if (!d_elementEnumerator->isFinished()) + { + // yet to establish domain + Assert(d_elementEnumerator != nullptr); + d_elementDomain.push_back((**d_elementEnumerator).toExpr()); + ++(*d_elementEnumerator); + } + // the current cardinality is the domain size of the element + if (!d_witer->increment(d_elementDomain.size())) + { + Assert(d_elementEnumerator->isFinished()); + d_curr = Node::null(); + return false; + } + mkCurr(); + return true; +} + +void SeqEnumLen::mkCurr() +{ + std::vector<Expr> seq; + const std::vector<unsigned>& data = d_witer->getData(); + for (unsigned i : data) + { + seq.push_back(d_elementDomain[i]); + } + // make sequence from seq + d_curr = + NodeManager::currentNM()->mkConst(ExprSequence(d_type.toType(), seq)); +} + StringEnumerator::StringEnumerator(TypeNode type, TypeEnumeratorProperties* tep) : TypeEnumeratorBase<StringEnumerator>(type), d_wenum(0, utils::getAlphabetCardinality()) @@ -182,6 +243,28 @@ StringEnumerator& StringEnumerator::operator++() bool StringEnumerator::isFinished() { return d_wenum.isFinished(); } +SequenceEnumerator::SequenceEnumerator(TypeNode type, + TypeEnumeratorProperties* tep) + : TypeEnumeratorBase<SequenceEnumerator>(type), d_wenum(type, tep, 0) +{ +} + +SequenceEnumerator::SequenceEnumerator(const SequenceEnumerator& enumerator) + : TypeEnumeratorBase<SequenceEnumerator>(enumerator.getType()), + d_wenum(enumerator.d_wenum) +{ +} + +Node SequenceEnumerator::operator*() { return d_wenum.getCurrent(); } + +SequenceEnumerator& SequenceEnumerator::operator++() +{ + d_wenum.increment(); + return *this; +} + +bool SequenceEnumerator::isFinished() { return d_wenum.isFinished(); } + } // namespace strings } // namespace theory } // namespace CVC4 diff --git a/src/theory/strings/type_enumerator.h b/src/theory/strings/type_enumerator.h index b379ce5c3..c82892624 100644 --- a/src/theory/strings/type_enumerator.h +++ b/src/theory/strings/type_enumerator.h @@ -136,6 +136,34 @@ class StringEnumLen : public SEnumLen void mkCurr(); }; +/** + * Enumerates sequence values for a given length. + */ +class SeqEnumLen : public SEnumLen +{ + public: + /** For sequences */ + SeqEnumLen(TypeNode tn, TypeEnumeratorProperties* tep, uint32_t startLength); + SeqEnumLen(TypeNode tn, + TypeEnumeratorProperties* tep, + uint32_t startLength, + uint32_t endLength); + /** copy constructor */ + SeqEnumLen(const SeqEnumLen& wenum); + /** destructor */ + ~SeqEnumLen() {} + /** increment */ + bool increment() override; + + private: + /** an enumerator for the elements' type */ + std::unique_ptr<TypeEnumerator> d_elementEnumerator; + /** The domain */ + std::vector<Expr> d_elementDomain; + /** Make the current term from d_data */ + void mkCurr(); +}; + class StringEnumerator : public TypeEnumeratorBase<StringEnumerator> { public: @@ -154,6 +182,21 @@ class StringEnumerator : public TypeEnumeratorBase<StringEnumerator> StringEnumLen d_wenum; }; /* class StringEnumerator */ +class SequenceEnumerator : public TypeEnumeratorBase<SequenceEnumerator> +{ + public: + SequenceEnumerator(TypeNode type, TypeEnumeratorProperties* tep = nullptr); + SequenceEnumerator(const SequenceEnumerator& enumerator); + ~SequenceEnumerator() {} + Node operator*() override; + SequenceEnumerator& operator++() override; + bool isFinished() override; + + private: + /** underlying sequence enumerator */ + SeqEnumLen d_wenum; +}; /* class SequenceEnumerator */ + }/* CVC4::theory::strings namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/strings/word.cpp b/src/theory/strings/word.cpp index e9ab2652e..3f6a9de32 100644 --- a/src/theory/strings/word.cpp +++ b/src/theory/strings/word.cpp @@ -14,6 +14,7 @@ #include "theory/strings/word.h" +#include "expr/sequence.h" #include "util/string.h" using namespace CVC4::kind; @@ -28,23 +29,28 @@ Node Word::mkEmptyWord(TypeNode tn) { return mkEmptyWord(CONST_STRING); } + else if (tn.isSequence()) + { + std::vector<Expr> seq; + return NodeManager::currentNM()->mkConst( + ExprSequence(tn.getSequenceElementType().toType(), seq)); + } Unimplemented(); return Node::null(); } Node Word::mkEmptyWord(Kind k) { - NodeManager* nm = NodeManager::currentNM(); if (k == CONST_STRING) { std::vector<unsigned> vec; - return nm->mkConst(String(vec)); + return NodeManager::currentNM()->mkConst(String(vec)); } Unimplemented(); return Node::null(); } -Node Word::mkWord(const std::vector<Node>& xs) +Node Word::mkWordFlatten(const std::vector<Node>& xs) { Assert(!xs.empty()); NodeManager* nm = NodeManager::currentNM(); @@ -61,6 +67,22 @@ Node Word::mkWord(const std::vector<Node>& xs) } return nm->mkConst(String(vec)); } + else if (k == CONST_SEQUENCE) + { + std::vector<Expr> seq; + TypeNode tn = xs[0].getType(); + for (TNode x : xs) + { + Assert(x.getType() == tn); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const std::vector<Node>& vecc = sx.getVec(); + for (const Node& c : vecc) + { + seq.push_back(c.toExpr()); + } + } + return NodeManager::currentNM()->mkConst(ExprSequence(tn.toType(), seq)); + } Unimplemented(); return Node::null(); } @@ -72,6 +94,10 @@ size_t Word::getLength(TNode x) { return x.getConst<String>().size(); } + else if (k == CONST_SEQUENCE) + { + return x.getConst<ExprSequence>().getSequence().size(); + } Unimplemented(); return 0; } @@ -111,6 +137,13 @@ bool Word::strncmp(TNode x, TNode y, std::size_t n) String sy = y.getConst<String>(); return sx.strncmp(sy, n); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.strncmp(sy, n); + } Unimplemented(); return false; } @@ -125,6 +158,13 @@ bool Word::rstrncmp(TNode x, TNode y, std::size_t n) String sy = y.getConst<String>(); return sx.rstrncmp(sy, n); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.rstrncmp(sy, n); + } Unimplemented(); return false; } @@ -139,6 +179,13 @@ std::size_t Word::find(TNode x, TNode y, std::size_t start) String sy = y.getConst<String>(); return sx.find(sy, start); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.find(sy, start); + } Unimplemented(); return 0; } @@ -153,6 +200,13 @@ std::size_t Word::rfind(TNode x, TNode y, std::size_t start) String sy = y.getConst<String>(); return sx.rfind(sy, start); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.rfind(sy, start); + } Unimplemented(); return 0; } @@ -167,6 +221,13 @@ bool Word::hasPrefix(TNode x, TNode y) String sy = y.getConst<String>(); return sx.hasPrefix(sy); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.hasPrefix(sy); + } Unimplemented(); return false; } @@ -181,6 +242,13 @@ bool Word::hasSuffix(TNode x, TNode y) String sy = y.getConst<String>(); return sx.hasSuffix(sy); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.hasSuffix(sy); + } Unimplemented(); return false; } @@ -198,6 +266,16 @@ Node Word::replace(TNode x, TNode y, TNode t) String st = t.getConst<String>(); return nm->mkConst(String(sx.replace(sy, st))); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + Assert(t.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + const Sequence& st = t.getConst<ExprSequence>().getSequence(); + Sequence res = sx.replace(sy, st); + return nm->mkConst(res.toExprSequence()); + } Unimplemented(); return Node::null(); } @@ -210,6 +288,12 @@ Node Word::substr(TNode x, std::size_t i) String sx = x.getConst<String>(); return nm->mkConst(String(sx.substr(i))); } + else if (k == CONST_SEQUENCE) + { + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + Sequence res = sx.substr(i); + return nm->mkConst(res.toExprSequence()); + } Unimplemented(); return Node::null(); } @@ -222,6 +306,12 @@ Node Word::substr(TNode x, std::size_t i, std::size_t j) String sx = x.getConst<String>(); return nm->mkConst(String(sx.substr(i, j))); } + else if (k == CONST_SEQUENCE) + { + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + Sequence res = sx.substr(i, j); + return nm->mkConst(res.toExprSequence()); + } Unimplemented(); return Node::null(); } @@ -237,6 +327,12 @@ Node Word::suffix(TNode x, std::size_t i) String sx = x.getConst<String>(); return nm->mkConst(String(sx.suffix(i))); } + else if (k == CONST_SEQUENCE) + { + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + Sequence res = sx.suffix(i); + return nm->mkConst(res.toExprSequence()); + } Unimplemented(); return Node::null(); } @@ -251,6 +347,13 @@ bool Word::noOverlapWith(TNode x, TNode y) String sy = y.getConst<String>(); return sx.noOverlapWith(sy); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.noOverlapWith(sy); + } Unimplemented(); return false; } @@ -265,6 +368,13 @@ std::size_t Word::overlap(TNode x, TNode y) String sy = y.getConst<String>(); return sx.overlap(sy); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.overlap(sy); + } Unimplemented(); return 0; } @@ -279,10 +389,32 @@ std::size_t Word::roverlap(TNode x, TNode y) String sy = y.getConst<String>(); return sx.roverlap(sy); } + else if (k == CONST_SEQUENCE) + { + Assert(y.getKind() == CONST_SEQUENCE); + const Sequence& sx = x.getConst<ExprSequence>().getSequence(); + const Sequence& sy = y.getConst<ExprSequence>().getSequence(); + return sx.roverlap(sy); + } Unimplemented(); return 0; } +bool Word::isRepeated(TNode x) +{ + Kind k = x.getKind(); + if (k == CONST_STRING) + { + return x.getConst<String>().isRepeated(); + } + else if (k == CONST_SEQUENCE) + { + return x.getConst<ExprSequence>().getSequence().isRepeated(); + } + Unimplemented(); + return false; +} + Node Word::splitConstant(TNode x, TNode y, size_t& index, bool isRev) { Assert(x.isConst() && y.isConst()); diff --git a/src/theory/strings/word.h b/src/theory/strings/word.h index b84ea6874..ad1d9bc74 100644 --- a/src/theory/strings/word.h +++ b/src/theory/strings/word.h @@ -37,7 +37,7 @@ class Word static Node mkEmptyWord(Kind k); /** make word from constants in (non-empty) vector vec */ - static Node mkWord(const std::vector<Node>& xs); + static Node mkWordFlatten(const std::vector<Node>& xs); /** Return the length of word x */ static size_t getLength(TNode x); @@ -139,6 +139,8 @@ class Word * Notice that x.overlap(y) = y.roverlap(x) */ static std::size_t roverlap(TNode x, TNode y); + /** Return true if word x is a repetition of the same character */ + static bool isRepeated(TNode x); /** Split constant * * This returns the suffix remainder (resp. prefix remainder when isRev is diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 2c27c6054..71c144daa 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -1931,7 +1931,14 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, // Lemma analysis isn't online yet; this lemma may only live for this // user level. - return theory::LemmaStatus(additionalLemmas[0], d_userContext->getLevel()); + Node retLemma = additionalLemmas[0]; + if (additionalLemmas.size() > 1) + { + // the returned lemma is the conjunction of all additional lemmas. + retLemma = + NodeManager::currentNM()->mkNode(kind::AND, additionalLemmas.ref()); + } + return theory::LemmaStatus(retLemma, d_userContext->getLevel()); } void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 9c631ca60..233047321 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -890,8 +890,6 @@ public: theory::eq::EqualityEngine* getMasterEqualityEngine() { return d_masterEqualityEngine; } - RemoveTermFormulas* getTermFormulaRemover() { return &d_tform_remover; } - SortInference* getSortInference() { return &d_sortInfer; } /** Prints the assertions to the debug stream */ diff --git a/src/theory/uf/proof_checker.cpp b/src/theory/uf/proof_checker.cpp new file mode 100644 index 000000000..28ae34b7b --- /dev/null +++ b/src/theory/uf/proof_checker.cpp @@ -0,0 +1,172 @@ +/********************* */ +/*! \file proof_checker.cpp + ** \verbatim + ** Top contributors (to current version): + ** Haniel Barbosa, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of equality proof checker + **/ + +#include "theory/uf/proof_checker.h" + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { +namespace uf { + +void UfProofRuleChecker::registerTo(ProofChecker* pc) +{ + // add checkers + pc->registerChecker(PfRule::REFL, this); + pc->registerChecker(PfRule::SYMM, this); + pc->registerChecker(PfRule::TRANS, this); + pc->registerChecker(PfRule::CONG, this); + pc->registerChecker(PfRule::TRUE_INTRO, this); + pc->registerChecker(PfRule::TRUE_ELIM, this); + pc->registerChecker(PfRule::FALSE_INTRO, this); + pc->registerChecker(PfRule::FALSE_ELIM, this); +} + +Node UfProofRuleChecker::checkInternal(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args) +{ + // compute what was proven + if (id == PfRule::REFL) + { + Assert(children.empty()); + Assert(args.size() == 1); + return args[0].eqNode(args[0]); + } + else if (id == PfRule::SYMM) + { + Assert(children.size() == 1); + Assert(args.empty()); + bool polarity = children[0].getKind() != NOT; + Node eqp = polarity ? children[0] : children[0][0]; + if (eqp.getKind() != EQUAL) + { + // not a (dis)equality + return Node::null(); + } + Node conc = eqp[1].eqNode(eqp[0]); + return polarity ? conc : conc.notNode(); + } + else if (id == PfRule::TRANS) + { + Assert(children.size() > 0); + Assert(args.empty()); + Node first; + Node curr; + for (size_t i = 0, nchild = children.size(); i < nchild; i++) + { + Node eqp = children[i]; + if (eqp.getKind() != EQUAL) + { + return Node::null(); + } + if (first.isNull()) + { + first = eqp[0]; + } + else if (eqp[0] != curr) + { + return Node::null(); + } + curr = eqp[1]; + } + return first.eqNode(curr); + } + else if (id == PfRule::CONG) + { + Assert(children.size() > 0); + Assert(args.size() == 1); + // We do congruence over builtin kinds using operatorToKind + std::vector<Node> lchildren; + std::vector<Node> rchildren; + // get the expected kind for args[0] + Kind k = NodeManager::getKindForFunction(args[0]); + if (k == kind::UNDEFINED_KIND) + { + k = NodeManager::operatorToKind(args[0]); + } + if (k == kind::UNDEFINED_KIND) + { + return Node::null(); + } + Trace("uf-pfcheck") << "congruence for " << args[0] << " uses kind " << k + << ", metakind=" << kind::metaKindOf(k) << std::endl; + if (kind::metaKindOf(k) == kind::metakind::PARAMETERIZED) + { + // parameterized kinds require the operator + lchildren.push_back(args[0]); + rchildren.push_back(args[0]); + } + for (size_t i = 0, nchild = children.size(); i < nchild; i++) + { + Node eqp = children[i]; + if (eqp.getKind() != EQUAL) + { + return Node::null(); + } + lchildren.push_back(eqp[0]); + rchildren.push_back(eqp[1]); + } + NodeManager* nm = NodeManager::currentNM(); + Node l = nm->mkNode(k, lchildren); + Node r = nm->mkNode(k, rchildren); + return l.eqNode(r); + } + else if (id == PfRule::TRUE_INTRO) + { + Assert(children.size() == 1); + Assert(args.empty()); + Node trueNode = NodeManager::currentNM()->mkConst(true); + return children[0].eqNode(trueNode); + } + else if (id == PfRule::TRUE_ELIM) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != EQUAL || !children[0][1].isConst() + || !children[0][1].getConst<bool>()) + { + return Node::null(); + } + return children[0][0]; + } + else if (id == PfRule::FALSE_INTRO) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != kind::NOT) + { + return Node::null(); + } + Node falseNode = NodeManager::currentNM()->mkConst(false); + return children[0][0].eqNode(falseNode); + } + else if (id == PfRule::FALSE_ELIM) + { + Assert(children.size() == 1); + Assert(args.empty()); + if (children[0].getKind() != EQUAL || !children[0][1].isConst() + || children[0][1].getConst<bool>()) + { + return Node::null(); + } + return children[0][0].notNode(); + } + // no rule + return Node::null(); +} + +} // namespace uf +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/uf/proof_checker.h b/src/theory/uf/proof_checker.h new file mode 100644 index 000000000..022574eab --- /dev/null +++ b/src/theory/uf/proof_checker.h @@ -0,0 +1,49 @@ +/********************* */ +/*! \file proof_checker.h + ** \verbatim + ** Top contributors (to current version): + ** Haniel Barbosa, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Equality proof checker utility + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__UF__PROOF_CHECKER_H +#define CVC4__THEORY__UF__PROOF_CHECKER_H + +#include "expr/node.h" +#include "expr/proof_checker.h" +#include "expr/proof_node.h" + +namespace CVC4 { +namespace theory { +namespace uf { + +/** A checker for builtin proofs */ +class UfProofRuleChecker : public ProofRuleChecker +{ + public: + UfProofRuleChecker() {} + ~UfProofRuleChecker() {} + + /** Register all rules owned by this rule checker into pc. */ + void registerTo(ProofChecker* pc) override; + + protected: + /** Return the conclusion of the given proof step, or null if it is invalid */ + Node checkInternal(PfRule id, + const std::vector<Node>& children, + const std::vector<Node>& args) override; +}; + +} // namespace uf +} // namespace theory +} // namespace CVC4 + +#endif /* CVC4__THEORY__UF__PROOF_CHECKER_H */ diff --git a/src/util/floatingpoint.cpp b/src/util/floatingpoint.cpp index 3bcf2b0de..f5545f73c 100644 --- a/src/util/floatingpoint.cpp +++ b/src/util/floatingpoint.cpp @@ -925,7 +925,8 @@ static FloatingPointLiteral constructorHelperBitVector( // We only have multiplyByPow(uint32_t) so we can't convert all numbers. // As we convert Integer -> unsigned int -> uint32_t we need that // unsigned int is not smaller than uint32_t - static_assert(sizeof(unsigned int) >= sizeof(uint32_t)); + static_assert(sizeof(unsigned int) >= sizeof(uint32_t), + "Conversion float -> real could loose data"); #ifdef CVC4_ASSERTIONS // Note that multipling by 2^n requires n bits of space (worst case) // so, in effect, these tests limit us to cases where the resultant diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index fbf249e7f..290fca6bc 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -37,6 +37,8 @@ set(regress_0_tests regress0/arith/issue3413.smt2 regress0/arith/issue3480.smt2 regress0/arith/issue3683.smt2 + regress0/arith/issue4367.smt2 + regress0/arith/issue4525.smt2 regress0/arith/ite-lift.smt2 regress0/arith/leq.01.smtv1.smt2 regress0/arith/miplib.cvc @@ -538,6 +540,7 @@ set(regress_0_tests regress0/ho/ho-matching-nested-app.smt2 regress0/ho/ho-std-fmf.smt2 regress0/ho/hoa0008.smt2 + regress0/ho/issue4477.smt2 regress0/ho/ite-apply-eq.smt2 regress0/ho/lambda-equality-non-canon.smt2 regress0/ho/match-middle.smt2 @@ -737,6 +740,8 @@ set(regress_0_tests regress0/quantifiers/issue3655.smt2 regress0/quantifiers/issue4086-infs.smt2 regress0/quantifiers/issue4275-qcf-cegqi-rep.smt2 + regress0/quantifiers/issue4437-unc-quant.smt2 + regress0/quantifiers/issue4476-ext-rew.smt2 regress0/quantifiers/lra-triv-gn.smt2 regress0/quantifiers/macros-int-real.smt2 regress0/quantifiers/macros-real-arg.smt2 @@ -1123,8 +1128,6 @@ set(regress_0_tests regress0/uflra/simple.03.cvc regress0/uflra/simple.04.cvc regress0/unconstrained/4481.smt2 - regress0/unconstrained/4484.smt2 - regress0/unconstrained/4486.smt2 regress0/unconstrained/arith.smt2 regress0/unconstrained/arith3.smt2 regress0/unconstrained/arith4.smt2 @@ -1197,6 +1200,7 @@ set(regress_0_tests # Regression level 1 tests set(regress_1_tests + regress1/abduct-dt.smt2 regress1/arith/arith-int-004.cvc regress1/arith/arith-int-011.cvc regress1/arith/arith-int-012.cvc @@ -1312,6 +1316,7 @@ set(regress_1_tests regress1/fmf/issue3626.smt2 regress1/fmf/issue3689.smt2 regress1/fmf/issue4068-si-qf.smt2 + regress1/fmf/issue4225-univ-fun.smt2 regress1/fmf/issue916-fmf-or.smt2 regress1/fmf/jasmin-cdt-crash.smt2 regress1/fmf/ko-bound-set.cvc diff --git a/test/regress/README.md b/test/regress/README.md index 28ccfb96b..0dc1d4eb8 100644 --- a/test/regress/README.md +++ b/test/regress/README.md @@ -12,7 +12,7 @@ By default, each invocation of CVC4 is done with a 10 minute timeout. To use a different timeout, set the `TEST_TIMEOUT` environment variable: ``` -TEST_TIMEOUT=0.5 make regress0 +TEST_TIMEOUT=0.5 ctest -L regress0 ``` This runs regression tests from level 0 with a 0.5 second timeout. diff --git a/test/regress/regress0/arith/issue3480.smt2 b/test/regress/regress0/arith/issue3480.smt2 index 7609ad3e7..74ce8d32b 100644 --- a/test/regress/regress0/arith/issue3480.smt2 +++ b/test/regress/regress0/arith/issue3480.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --quiet +; COMMAND-LINE: --theoryof-mode=type --quiet (set-logic QF_NIA) (declare-fun a () Int) (declare-fun b () Int) diff --git a/test/regress/regress0/arith/issue4367.smt2 b/test/regress/regress0/arith/issue4367.smt2 new file mode 100644 index 000000000..abe5b09fd --- /dev/null +++ b/test/regress/regress0/arith/issue4367.smt2 @@ -0,0 +1,11 @@ +; COMMAND-LINE: --incremental --check-unsat-cores +; EXPECT: unsat +; EXPECT: unsat +(set-logic NRA) +(declare-const r0 Real) +(assert (! (forall ((q0 Bool) (q1 Real)) (= (* r0 r0) r0 r0)) :named IP_2)) +(assert (! (not (forall ((q2 Real)) (not (<= 55.033442 r0 55.033442 q2)))) :named IP_5)) +(push 1) +(check-sat) +(pop 1) +(check-sat) diff --git a/test/regress/regress0/arith/issue4525.smt2 b/test/regress/regress0/arith/issue4525.smt2 new file mode 100644 index 000000000..ae7e00990 --- /dev/null +++ b/test/regress/regress0/arith/issue4525.smt2 @@ -0,0 +1,4 @@ +(set-logic QF_NRAT) +(assert (> (cot 0.0) (/ 1 0))) +(set-info :status unsat) +(check-sat) diff --git a/test/regress/regress0/ho/issue4477.smt2 b/test/regress/regress0/ho/issue4477.smt2 new file mode 100644 index 000000000..7162d260c --- /dev/null +++ b/test/regress/regress0/ho/issue4477.smt2 @@ -0,0 +1,11 @@ +; REQUIRES: no-competition +; SCRUBBER: grep -o "Symbol '->' not declared" +; EXPECT: Symbol '->' not declared +; EXIT: 1 +(set-logic ALL) +(declare-sort s 0) +(declare-fun a () s) +(declare-fun b () s) +(declare-fun c (s) s) +(assert (forall ((d (-> s s))) (distinct (d a) (c a) b))) +(check-sat) diff --git a/test/regress/regress0/quantifiers/issue4437-unc-quant.smt2 b/test/regress/regress0/quantifiers/issue4437-unc-quant.smt2 new file mode 100644 index 000000000..61f792999 --- /dev/null +++ b/test/regress/regress0/quantifiers/issue4437-unc-quant.smt2 @@ -0,0 +1,7 @@ +; EXPECT: (error "Parse Error: issue4437-unc-quant.smt2:6.15: Quantifier used in non-quantified logic.") +; EXIT: 1 +(set-logic QF_AUFBVLIA) +(declare-fun a () (_ BitVec 8)) +(declare-fun b () (_ BitVec 8)) +(assert (forall ((c (_ BitVec 8))) (= (bvashr c a) b))) +(check-sat) diff --git a/test/regress/regress0/quantifiers/issue4476-ext-rew.smt2 b/test/regress/regress0/quantifiers/issue4476-ext-rew.smt2 new file mode 100644 index 000000000..c54254e67 --- /dev/null +++ b/test/regress/regress0/quantifiers/issue4476-ext-rew.smt2 @@ -0,0 +1,5 @@ +(set-logic NRA) +(set-info :status sat) +(set-option :ext-rewrite-quant true) +(assert (exists ((a Real) (b Real)) (forall ((c Real)) (= (/ b (/ 1 c)) 0)))) +(check-sat) diff --git a/test/regress/regress0/unconstrained/4481.smt2 b/test/regress/regress0/unconstrained/4481.smt2 index 028607093..19179f4d7 100644 --- a/test/regress/regress0/unconstrained/4481.smt2 +++ b/test/regress/regress0/unconstrained/4481.smt2 @@ -1,5 +1,6 @@ ; COMMAND-LINE: --unconstrained-simp -; EXPECT: unsat +; EXPECT: (error "Cannot use unconstrained simplification in this logic, due to (possibly internally introduced) quantified formula.") +; EXIT: 1 (set-logic ALL) (set-info :status unsat) (declare-fun a () Int) diff --git a/test/regress/regress0/unconstrained/4484.smt2 b/test/regress/regress0/unconstrained/4484.smt2 deleted file mode 100644 index f2f11295b..000000000 --- a/test/regress/regress0/unconstrained/4484.smt2 +++ /dev/null @@ -1,8 +0,0 @@ -; COMMAND-LINE: --unconstrained-simp -; EXPECT: unsat -(set-logic QF_NIRA) -(set-info :status unsat) -(declare-fun a () Real) -(assert (= (to_int a) 2)) -(assert (= (to_int (/ a 2.0)) 2)) -(check-sat) diff --git a/test/regress/regress0/unconstrained/4486.smt2 b/test/regress/regress0/unconstrained/4486.smt2 deleted file mode 100644 index 01771ce66..000000000 --- a/test/regress/regress0/unconstrained/4486.smt2 +++ /dev/null @@ -1,8 +0,0 @@ -; COMMAND-LINE: --unconstrained-simp -; EXPECT: sat -(set-logic ALL) -(set-info :status sat) -(declare-fun x () Real) -(assert (is_int x)) -(assert (is_int (+ x 1))) -(check-sat) diff --git a/test/regress/regress1/abduct-dt.smt2 b/test/regress/regress1/abduct-dt.smt2 new file mode 100644 index 000000000..d72d15a21 --- /dev/null +++ b/test/regress/regress1/abduct-dt.smt2 @@ -0,0 +1,8 @@ +; COMMAND-LINE: --produce-abducts +; SCRUBBER: grep -v -E '(\(define-fun)' +; EXIT: 0 +(set-logic ALL) +(declare-datatypes ((List 0)) (((nil) (cons (head Int) (tail List))))) +(declare-fun x () List) +(assert (distinct x nil)) +(get-abduct A (= x (cons (head x) (tail x)))) diff --git a/test/regress/regress1/fmf/issue4225-univ-fun.smt2 b/test/regress/regress1/fmf/issue4225-univ-fun.smt2 new file mode 100644 index 000000000..9946a4567 --- /dev/null +++ b/test/regress/regress1/fmf/issue4225-univ-fun.smt2 @@ -0,0 +1,6 @@ +; COMMAND-LINE: --finite-model-find --uf-ho +; EXPECT: unknown +(set-logic ALL) +; this is not handled by fmf +(assert (forall ((a (-> Int Int)) (b Int)) (not (= (a b) 0)))) +(check-sat) diff --git a/test/regress/regress1/strings/norn-ab.smt2 b/test/regress/regress1/strings/norn-ab.smt2 index 63a95bb78..6109a01dd 100644 --- a/test/regress/regress1/strings/norn-ab.smt2 +++ b/test/regress/regress1/strings/norn-ab.smt2 @@ -1,5 +1,5 @@ (set-info :smt-lib-version 2.6) -(set-logic QF_SLIA) +(set-logic SLIA) (set-info :status unsat) (set-option :strings-exp true) diff --git a/test/unit/api/datatype_api_black.h b/test/unit/api/datatype_api_black.h index b404dfb13..c9eaf103e 100644 --- a/test/unit/api/datatype_api_black.h +++ b/test/unit/api/datatype_api_black.h @@ -43,10 +43,10 @@ void DatatypeBlack::tearDown() {} void DatatypeBlack::testMkDatatypeSort() { DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver.getIntegerSort()); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort listSort = d_solver.mkDatatypeSort(dtypeSpec); Datatype d = listSort.getDatatype(); @@ -75,22 +75,22 @@ void DatatypeBlack::testMkDatatypeSorts() unresTypes.insert(unresList); DatatypeDecl tree = d_solver.mkDatatypeDecl("tree"); - DatatypeConstructorDecl node("node"); + DatatypeConstructorDecl node = d_solver.mkDatatypeConstructorDecl("node"); node.addSelector("left", unresTree); node.addSelector("right", unresTree); tree.addConstructor(node); - DatatypeConstructorDecl leaf("leaf"); + DatatypeConstructorDecl leaf = d_solver.mkDatatypeConstructorDecl("leaf"); leaf.addSelector("data", unresList); tree.addConstructor(leaf); DatatypeDecl list = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("car", unresTree); cons.addSelector("cdr", unresTree); list.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); list.addConstructor(nil); std::vector<DatatypeDecl> dtdecls; @@ -130,13 +130,13 @@ void DatatypeBlack::testDatatypeStructs() // create datatype sort to test DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", intSort); cons.addSelectorSelf("tail"); Sort nullSort; TS_ASSERT_THROWS(cons.addSelector("null", nullSort), CVC4ApiException&); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); Datatype dt = dtypeSort.getDatatype(); @@ -152,11 +152,11 @@ void DatatypeBlack::testDatatypeStructs() // create datatype sort to test DatatypeDecl dtypeSpecEnum = d_solver.mkDatatypeDecl("enum"); - DatatypeConstructorDecl ca("A"); + DatatypeConstructorDecl ca = d_solver.mkDatatypeConstructorDecl("A"); dtypeSpecEnum.addConstructor(ca); - DatatypeConstructorDecl cb("B"); + DatatypeConstructorDecl cb = d_solver.mkDatatypeConstructorDecl("B"); dtypeSpecEnum.addConstructor(cb); - DatatypeConstructorDecl cc("C"); + DatatypeConstructorDecl cc = d_solver.mkDatatypeConstructorDecl("C"); dtypeSpecEnum.addConstructor(cc); Sort dtypeSortEnum = d_solver.mkDatatypeSort(dtypeSpecEnum); Datatype dtEnum = dtypeSortEnum.getDatatype(); @@ -165,7 +165,8 @@ void DatatypeBlack::testDatatypeStructs() // create codatatype DatatypeDecl dtypeSpecStream = d_solver.mkDatatypeDecl("stream", true); - DatatypeConstructorDecl consStream("cons"); + DatatypeConstructorDecl consStream = + d_solver.mkDatatypeConstructorDecl("cons"); consStream.addSelector("head", intSort); consStream.addSelectorSelf("tail"); dtypeSpecStream.addConstructor(consStream); @@ -204,11 +205,11 @@ void DatatypeBlack::testDatatypeNames() DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); TS_ASSERT_THROWS_NOTHING(dtypeSpec.getName()); TS_ASSERT(dtypeSpec.getName() == std::string("list")); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", intSort); cons.addSelectorSelf("tail"); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); Datatype dt = dtypeSort.getDatatype(); diff --git a/test/unit/api/op_black.h b/test/unit/api/op_black.h index e99e8daf2..27ca7bb88 100644 --- a/test/unit/api/op_black.h +++ b/test/unit/api/op_black.h @@ -52,7 +52,7 @@ void OpBlack::testIsNull() void OpBlack::testOpFromKind() { - Op plus(PLUS); + Op plus(&d_solver, PLUS); TS_ASSERT(!plus.isIndexed()); TS_ASSERT_THROWS(plus.getIndices<uint32_t>(), CVC4ApiException&); diff --git a/test/unit/api/solver_black.h b/test/unit/api/solver_black.h index 07b5c5aec..43554088f 100644 --- a/test/unit/api/solver_black.h +++ b/test/unit/api/solver_black.h @@ -39,6 +39,7 @@ class SolverBlack : public CxxTest::TestSuite void testMkBitVectorSort(); void testMkFloatingPointSort(); void testMkDatatypeSort(); + void testMkDatatypeSorts(); void testMkFunctionSort(); void testMkOp(); void testMkParamSort(); @@ -87,18 +88,39 @@ class SolverBlack : public CxxTest::TestSuite void testDefineFunsRec(); void testUFIteration(); + + void testGetInfo(); void testGetOp(); + void testGetOption(); + void testGetUnsatAssumptions1(); + void testGetUnsatAssumptions2(); + void testGetUnsatAssumptions3(); + void testGetUnsatCore1(); + void testGetUnsatCore2(); + void testGetUnsatCore3(); + void testGetValue1(); + void testGetValue2(); + void testGetValue3(); void testPush1(); void testPush2(); void testPop1(); void testPop2(); void testPop3(); + void testPrintModel1(); + void testPrintModel2(); + void testPrintModel3(); void testSimplify(); + + void testAssertFormula(); void testCheckEntailed(); void testCheckEntailed1(); void testCheckEntailed2(); + void testCheckSat(); + void testCheckSatAssuming(); + void testCheckSatAssuming1(); + void testCheckSatAssuming2(); void testSetInfo(); void testSetLogic(); @@ -173,6 +195,8 @@ void SolverBlack::testMkArraySort() TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(boolSort, intSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(realSort, bvSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(bvSort, fpSort)); + Solver slv; + TS_ASSERT_THROWS(slv.mkArraySort(boolSort, boolSort), CVC4ApiException&); } void SolverBlack::testMkBitVectorSort() @@ -191,17 +215,64 @@ void SolverBlack::testMkFloatingPointSort() void SolverBlack::testMkDatatypeSort() { DatatypeDecl dtypeSpec = d_solver->mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver->mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver->getIntegerSort()); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver->mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); TS_ASSERT_THROWS_NOTHING(d_solver->mkDatatypeSort(dtypeSpec)); + + Solver slv; + TS_ASSERT_THROWS(slv.mkDatatypeSort(dtypeSpec), CVC4ApiException&); + DatatypeDecl throwsDtypeSpec = d_solver->mkDatatypeDecl("list"); TS_ASSERT_THROWS(d_solver->mkDatatypeSort(throwsDtypeSpec), CVC4ApiException&); } +void SolverBlack::testMkDatatypeSorts() +{ + Solver slv; + + DatatypeDecl dtypeSpec1 = d_solver->mkDatatypeDecl("list1"); + DatatypeConstructorDecl cons1 = d_solver->mkDatatypeConstructorDecl("cons1"); + cons1.addSelector("head1", d_solver->getIntegerSort()); + dtypeSpec1.addConstructor(cons1); + DatatypeConstructorDecl nil1 = d_solver->mkDatatypeConstructorDecl("nil1"); + dtypeSpec1.addConstructor(nil1); + DatatypeDecl dtypeSpec2 = d_solver->mkDatatypeDecl("list2"); + DatatypeConstructorDecl cons2 = d_solver->mkDatatypeConstructorDecl("cons2"); + cons2.addSelector("head2", d_solver->getIntegerSort()); + dtypeSpec2.addConstructor(cons2); + DatatypeConstructorDecl nil2 = d_solver->mkDatatypeConstructorDecl("nil2"); + dtypeSpec2.addConstructor(nil2); + std::vector<DatatypeDecl> decls = {dtypeSpec1, dtypeSpec2}; + TS_ASSERT_THROWS_NOTHING(d_solver->mkDatatypeSorts(decls)); + + TS_ASSERT_THROWS(slv.mkDatatypeSorts(decls), CVC4ApiException&); + + DatatypeDecl throwsDtypeSpec = d_solver->mkDatatypeDecl("list"); + std::vector<DatatypeDecl> throwsDecls = {throwsDtypeSpec}; + TS_ASSERT_THROWS(d_solver->mkDatatypeSorts(throwsDecls), CVC4ApiException&); + + /* with unresolved sorts */ + Sort unresList = d_solver->mkUninterpretedSort("ulist"); + std::set<Sort> unresSorts = {unresList}; + DatatypeDecl ulist = d_solver->mkDatatypeDecl("ulist"); + DatatypeConstructorDecl ucons = d_solver->mkDatatypeConstructorDecl("ucons"); + ucons.addSelector("car", unresList); + ucons.addSelector("cdr", unresList); + ulist.addConstructor(ucons); + DatatypeConstructorDecl unil = d_solver->mkDatatypeConstructorDecl("unil"); + ulist.addConstructor(unil); + std::vector<DatatypeDecl> udecls = {ulist}; + TS_ASSERT_THROWS_NOTHING(d_solver->mkDatatypeSorts(udecls, unresSorts)); + + TS_ASSERT_THROWS(slv.mkDatatypeSorts(udecls, unresSorts), CVC4ApiException&); + + /* Note: More tests are in datatype_api_black. */ +} + void SolverBlack::testMkFunctionSort() { TS_ASSERT_THROWS_NOTHING(d_solver->mkFunctionSort( @@ -228,6 +299,23 @@ void SolverBlack::testMkFunctionSort() {d_solver->getIntegerSort(), d_solver->mkUninterpretedSort("u")}, funSort2), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.mkFunctionSort(d_solver->mkUninterpretedSort("u"), + d_solver->getIntegerSort()), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkFunctionSort(slv.mkUninterpretedSort("u"), + d_solver->getIntegerSort()), + CVC4ApiException&); + std::vector<Sort> sorts1 = {d_solver->getBooleanSort(), + slv.getIntegerSort(), + d_solver->getIntegerSort()}; + std::vector<Sort> sorts2 = {slv.getBooleanSort(), slv.getIntegerSort()}; + TS_ASSERT_THROWS_NOTHING(slv.mkFunctionSort(sorts2, slv.getIntegerSort())); + TS_ASSERT_THROWS(slv.mkFunctionSort(sorts1, slv.getIntegerSort()), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkFunctionSort(sorts2, d_solver->getIntegerSort()), + CVC4ApiException&); } void SolverBlack::testMkParamSort() @@ -246,6 +334,10 @@ void SolverBlack::testMkPredicateSort() TS_ASSERT_THROWS( d_solver->mkPredicateSort({d_solver->getIntegerSort(), funSort}), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.mkPredicateSort({d_solver->getIntegerSort()}), + CVC4ApiException&); } void SolverBlack::testMkRecordSort() @@ -259,6 +351,9 @@ void SolverBlack::testMkRecordSort() TS_ASSERT_THROWS_NOTHING(d_solver->mkRecordSort(empty)); Sort recSort = d_solver->mkRecordSort(fields); TS_ASSERT_THROWS_NOTHING(recSort.getDatatype()); + + Solver slv; + TS_ASSERT_THROWS(slv.mkRecordSort(fields), CVC4ApiException&); } void SolverBlack::testMkSetSort() @@ -266,6 +361,9 @@ void SolverBlack::testMkSetSort() TS_ASSERT_THROWS_NOTHING(d_solver->mkSetSort(d_solver->getBooleanSort())); TS_ASSERT_THROWS_NOTHING(d_solver->mkSetSort(d_solver->getIntegerSort())); TS_ASSERT_THROWS_NOTHING(d_solver->mkSetSort(d_solver->mkBitVectorSort(4))); + Solver slv; + TS_ASSERT_THROWS(slv.mkSetSort(d_solver->mkBitVectorSort(4)), + CVC4ApiException&); } void SolverBlack::testMkUninterpretedSort() @@ -288,6 +386,10 @@ void SolverBlack::testMkTupleSort() d_solver->getIntegerSort()); TS_ASSERT_THROWS(d_solver->mkTupleSort({d_solver->getIntegerSort(), funSort}), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.mkTupleSort({d_solver->getIntegerSort()}), + CVC4ApiException&); } void SolverBlack::testMkBitVector() @@ -332,6 +434,8 @@ void SolverBlack::testMkVar() TS_ASSERT_THROWS_NOTHING(d_solver->mkVar(funSort, "")); TS_ASSERT_THROWS(d_solver->mkVar(Sort()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkVar(Sort(), "a"), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.mkVar(boolSort, "x"), CVC4ApiException&); } void SolverBlack::testMkBoolean() @@ -352,6 +456,9 @@ void SolverBlack::testMkUninterpretedConst() d_solver->mkUninterpretedConst(d_solver->getBooleanSort(), 1)); TS_ASSERT_THROWS(d_solver->mkUninterpretedConst(Sort(), 1), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.mkUninterpretedConst(d_solver->getBooleanSort(), 1), + CVC4ApiException&); } void SolverBlack::testMkAbstractValue() @@ -393,13 +500,23 @@ void SolverBlack::testMkFloatingPoint() TS_ASSERT_THROWS(d_solver->mkFloatingPoint(3, 0, t1), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkFloatingPoint(3, 5, t2), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkFloatingPoint(3, 5, t2), CVC4ApiException&); + + if (CVC4::Configuration::isBuiltWithSymFPU()) + { + Solver slv; + TS_ASSERT_THROWS(slv.mkFloatingPoint(3, 5, t1), CVC4ApiException&); + } } void SolverBlack::testMkEmptySet() { + Solver slv; + Sort s = d_solver->mkSetSort(d_solver->getBooleanSort()); + TS_ASSERT_THROWS_NOTHING(d_solver->mkEmptySet(Sort())); + TS_ASSERT_THROWS_NOTHING(d_solver->mkEmptySet(s)); TS_ASSERT_THROWS(d_solver->mkEmptySet(d_solver->getBooleanSort()), CVC4ApiException&); - TS_ASSERT_THROWS_NOTHING(d_solver->mkEmptySet(Sort())); + TS_ASSERT_THROWS(slv.mkEmptySet(s), CVC4ApiException&); } void SolverBlack::testMkFalse() @@ -558,6 +675,8 @@ void SolverBlack::testMkSepNil() { TS_ASSERT_THROWS_NOTHING(d_solver->mkSepNil(d_solver->getBooleanSort())); TS_ASSERT_THROWS(d_solver->mkSepNil(Sort()), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.mkSepNil(d_solver->getIntegerSort()), CVC4ApiException&); } void SolverBlack::testMkString() @@ -592,6 +711,7 @@ void SolverBlack::testMkTerm() std::vector<Term> v4 = {d_solver->mkReal(1), d_solver->mkReal(2)}; std::vector<Term> v5 = {d_solver->mkReal(1), Term()}; std::vector<Term> v6 = {}; + Solver slv; // mkTerm(Kind kind) const TS_ASSERT_THROWS_NOTHING(d_solver->mkTerm(PI)); @@ -603,6 +723,7 @@ void SolverBlack::testMkTerm() TS_ASSERT_THROWS_NOTHING(d_solver->mkTerm(NOT, d_solver->mkTrue())); TS_ASSERT_THROWS(d_solver->mkTerm(NOT, Term()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(NOT, a), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkTerm(NOT, d_solver->mkTrue()), CVC4ApiException&); // mkTerm(Kind kind, Term child1, Term child2) const TS_ASSERT_THROWS_NOTHING(d_solver->mkTerm(EQUAL, a, b)); @@ -610,6 +731,7 @@ void SolverBlack::testMkTerm() TS_ASSERT_THROWS(d_solver->mkTerm(EQUAL, a, Term()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(EQUAL, a, d_solver->mkTrue()), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkTerm(EQUAL, a, b), CVC4ApiException&); // mkTerm(Kind kind, Term child1, Term child2, Term child3) const TS_ASSERT_THROWS_NOTHING(d_solver->mkTerm( @@ -626,6 +748,10 @@ void SolverBlack::testMkTerm() TS_ASSERT_THROWS( d_solver->mkTerm(ITE, d_solver->mkTrue(), d_solver->mkTrue(), b), CVC4ApiException&); + TS_ASSERT_THROWS( + slv.mkTerm( + ITE, d_solver->mkTrue(), d_solver->mkTrue(), d_solver->mkTrue()), + CVC4ApiException&); // mkTerm(Kind kind, const std::vector<Term>& children) const TS_ASSERT_THROWS_NOTHING(d_solver->mkTerm(EQUAL, v1)); @@ -643,15 +769,17 @@ void SolverBlack::testMkTermFromOp() std::vector<Term> v2 = {d_solver->mkReal(1), Term()}; std::vector<Term> v3 = {}; std::vector<Term> v4 = {d_solver->mkReal(5)}; + Solver slv; + // simple operator terms Op opterm1 = d_solver->mkOp(BITVECTOR_EXTRACT, 2, 1); Op opterm2 = d_solver->mkOp(DIVISIBLE, 1); - // list datatype + // list datatype Sort sort = d_solver->mkParamSort("T"); DatatypeDecl listDecl = d_solver->mkDatatypeDecl("paramlist", sort); - DatatypeConstructorDecl cons("cons"); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl cons = d_solver->mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl nil = d_solver->mkDatatypeConstructorDecl("nil"); cons.addSelector("head", sort); cons.addSelectorSelf("tail"); listDecl.addConstructor(cons); @@ -661,6 +789,7 @@ void SolverBlack::testMkTermFromOp() listSort.instantiate(std::vector<Sort>{d_solver->getIntegerSort()}); Term c = d_solver->mkConst(intListSort, "c"); Datatype list = listSort.getDatatype(); + // list datatype constructor and selector operator terms Term consTerm1 = list.getConstructorTerm("cons"); Term consTerm2 = list.getConstructor("cons").getConstructorTerm(); @@ -684,6 +813,7 @@ void SolverBlack::testMkTermFromOp() TS_ASSERT_THROWS(d_solver->mkTerm(APPLY_SELECTOR, headTerm1), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(opterm1), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkTerm(APPLY_CONSTRUCTOR, nilTerm1), CVC4ApiException&); // mkTerm(Op op, Term child) const TS_ASSERT_THROWS_NOTHING(d_solver->mkTerm(opterm1, a)); @@ -695,24 +825,29 @@ void SolverBlack::testMkTermFromOp() TS_ASSERT_THROWS( d_solver->mkTerm(APPLY_CONSTRUCTOR, consTerm1, d_solver->mkReal(0)), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkTerm(opterm1, a), CVC4ApiException&); // mkTerm(Op op, Term child1, Term child2) const - TS_ASSERT_THROWS( - d_solver->mkTerm(opterm2, d_solver->mkReal(1), d_solver->mkReal(2)), - CVC4ApiException&); TS_ASSERT_THROWS_NOTHING( d_solver->mkTerm(APPLY_CONSTRUCTOR, consTerm1, d_solver->mkReal(0), d_solver->mkTerm(APPLY_CONSTRUCTOR, nilTerm1))); + TS_ASSERT_THROWS( + d_solver->mkTerm(opterm2, d_solver->mkReal(1), d_solver->mkReal(2)), + CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(opterm1, a, b), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(opterm2, d_solver->mkReal(1), Term()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(opterm2, Term(), d_solver->mkReal(1)), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkTerm(APPLY_CONSTRUCTOR, + consTerm1, + d_solver->mkReal(0), + d_solver->mkTerm(APPLY_CONSTRUCTOR, nilTerm1)), + CVC4ApiException&); - // mkTerm(Op op, Term child1, Term child2, Term child3) - // const + // mkTerm(Op op, Term child1, Term child2, Term child3) const TS_ASSERT_THROWS(d_solver->mkTerm(opterm1, a, b, a), CVC4ApiException&); TS_ASSERT_THROWS( d_solver->mkTerm( @@ -724,6 +859,7 @@ void SolverBlack::testMkTermFromOp() TS_ASSERT_THROWS(d_solver->mkTerm(opterm2, v1), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(opterm2, v2), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkTerm(opterm2, v3), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkTerm(opterm2, v4), CVC4ApiException&); } void SolverBlack::testMkTrue() @@ -747,12 +883,22 @@ void SolverBlack::testMkTuple() TS_ASSERT_THROWS(d_solver->mkTuple({d_solver->getIntegerSort()}, {d_solver->mkReal("5.3")}), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS( + slv.mkTuple({d_solver->mkBitVectorSort(3)}, {slv.mkBitVector("101", 2)}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.mkTuple({slv.mkBitVectorSort(3)}, {d_solver->mkBitVector("101", 2)}), + CVC4ApiException&); } void SolverBlack::testMkUniverseSet() { - TS_ASSERT_THROWS(d_solver->mkUniverseSet(Sort()), CVC4ApiException&); TS_ASSERT_THROWS_NOTHING(d_solver->mkUniverseSet(d_solver->getBooleanSort())); + TS_ASSERT_THROWS(d_solver->mkUniverseSet(Sort()), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.mkUniverseSet(d_solver->getBooleanSort()), + CVC4ApiException&); } void SolverBlack::testMkConst() @@ -768,6 +914,9 @@ void SolverBlack::testMkConst() TS_ASSERT_THROWS_NOTHING(d_solver->mkConst(funSort, "")); TS_ASSERT_THROWS(d_solver->mkConst(Sort()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkConst(Sort(), "a"), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.mkConst(boolSort), CVC4ApiException&); } void SolverBlack::testMkConstArray() @@ -778,23 +927,29 @@ void SolverBlack::testMkConstArray() Term constArr = d_solver->mkConstArray(arrSort, zero); TS_ASSERT_THROWS_NOTHING(d_solver->mkConstArray(arrSort, zero)); + TS_ASSERT_THROWS(d_solver->mkConstArray(Sort(), zero), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkConstArray(arrSort, Term()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkConstArray(arrSort, d_solver->mkBitVector(1, 1)), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkConstArray(intSort, zero), CVC4ApiException&); + Solver slv; + Term zero2 = slv.mkReal(0); + Sort arrSort2 = slv.mkArraySort(slv.getIntegerSort(), slv.getIntegerSort()); + TS_ASSERT_THROWS(slv.mkConstArray(arrSort2, zero), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkConstArray(arrSort, zero2), CVC4ApiException&); } void SolverBlack::testDeclareDatatype() { - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver->mkDatatypeConstructorDecl("nil"); std::vector<DatatypeConstructorDecl> ctors1 = {nil}; TS_ASSERT_THROWS_NOTHING(d_solver->declareDatatype(std::string("a"), ctors1)); - DatatypeConstructorDecl cons("cons"); - DatatypeConstructorDecl nil2("nil"); + DatatypeConstructorDecl cons = d_solver->mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl nil2 = d_solver->mkDatatypeConstructorDecl("nil"); std::vector<DatatypeConstructorDecl> ctors2 = {cons, nil2}; TS_ASSERT_THROWS_NOTHING(d_solver->declareDatatype(std::string("b"), ctors2)); - DatatypeConstructorDecl cons2("cons"); - DatatypeConstructorDecl nil3("nil"); + DatatypeConstructorDecl cons2 = d_solver->mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl nil3 = d_solver->mkDatatypeConstructorDecl("nil"); std::vector<DatatypeConstructorDecl> ctors3 = {cons2, nil3}; TS_ASSERT_THROWS_NOTHING(d_solver->declareDatatype(std::string(""), ctors3)); std::vector<DatatypeConstructorDecl> ctors4; @@ -802,6 +957,9 @@ void SolverBlack::testDeclareDatatype() CVC4ApiException&); TS_ASSERT_THROWS(d_solver->declareDatatype(std::string(""), ctors4), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.declareDatatype(std::string("a"), ctors1), + CVC4ApiException&); } void SolverBlack::testDeclareFun() @@ -817,6 +975,8 @@ void SolverBlack::testDeclareFun() CVC4ApiException&); TS_ASSERT_THROWS(d_solver->declareFun("f5", {bvSort, bvSort}, funSort), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.declareFun("f1", {}, bvSort), CVC4ApiException&); } void SolverBlack::testDeclareSort() @@ -845,17 +1005,36 @@ void SolverBlack::testDefineFun() TS_ASSERT_THROWS_NOTHING(d_solver->defineFun("f", {}, bvSort, v1)); TS_ASSERT_THROWS_NOTHING(d_solver->defineFun("ff", {b1, b2}, bvSort, v1)); TS_ASSERT_THROWS_NOTHING(d_solver->defineFun(f1, {b1, b11}, v1)); + TS_ASSERT_THROWS(d_solver->defineFun("ff", {v1, b2}, bvSort, v1), + CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun("fff", {b1}, bvSort, v3), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun("ffff", {b1}, funSort2, v3), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun("fffff", {b1, b3}, bvSort, v1), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->defineFun(f1, {v1, b11}, v1), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun(f1, {b1}, v1), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun(f1, {b1, b11}, v2), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun(f1, {b1, b11}, v3), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun(f2, {b1}, v2), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFun(f3, {b1}, v1), CVC4ApiException&); + + Solver slv; + Sort bvSort2 = slv.mkBitVectorSort(32); + Term v12 = slv.mkConst(bvSort2, "v1"); + Term b12 = slv.mkVar(bvSort2, "b1"); + Term b22 = slv.mkVar(slv.getIntegerSort(), "b2"); + TS_ASSERT_THROWS(slv.defineFun("f", {}, bvSort, v12), CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFun("f", {}, bvSort2, v1), CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFun("ff", {b1, b22}, bvSort2, v12), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFun("ff", {b12, b2}, bvSort2, v12), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFun("ff", {b12, b22}, bvSort, v12), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFun("ff", {b12, b22}, bvSort2, v1), + CVC4ApiException&); } void SolverBlack::testDefineFunRec() @@ -879,6 +1058,8 @@ void SolverBlack::testDefineFunRec() TS_ASSERT_THROWS_NOTHING(d_solver->defineFunRec(f1, {b1, b11}, v1)); TS_ASSERT_THROWS(d_solver->defineFunRec("fff", {b1}, bvSort, v3), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->defineFunRec("ff", {b1, v2}, bvSort, v1), + CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFunRec("ffff", {b1}, funSort2, v3), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFunRec("fffff", {b1, b3}, bvSort, v1), @@ -890,6 +1071,24 @@ void SolverBlack::testDefineFunRec() CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFunRec(f2, {b1}, v2), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFunRec(f3, {b1}, v1), CVC4ApiException&); + + Solver slv; + Sort bvSort2 = slv.mkBitVectorSort(32); + Term v12 = slv.mkConst(bvSort2, "v1"); + Term b12 = slv.mkVar(bvSort2, "b1"); + Term b22 = slv.mkVar(slv.getIntegerSort(), "b2"); + TS_ASSERT_THROWS_NOTHING(slv.defineFunRec("f", {}, bvSort2, v12)); + TS_ASSERT_THROWS_NOTHING(slv.defineFunRec("ff", {b12, b22}, bvSort2, v12)); + TS_ASSERT_THROWS(slv.defineFunRec("f", {}, bvSort, v12), CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFunRec("f", {}, bvSort2, v1), CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFunRec("ff", {b1, b22}, bvSort2, v12), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFunRec("ff", {b12, b2}, bvSort2, v12), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFunRec("ff", {b12, b22}, bvSort, v12), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.defineFunRec("ff", {b12, b22}, bvSort2, v1), + CVC4ApiException&); } void SolverBlack::testDefineFunsRec() @@ -913,6 +1112,9 @@ void SolverBlack::testDefineFunsRec() TS_ASSERT_THROWS_NOTHING( d_solver->defineFunsRec({f1, f2}, {{b1, b11}, {b4}}, {v1, v2})); TS_ASSERT_THROWS( + d_solver->defineFunsRec({f1, f2}, {{v1, b11}, {b4}}, {v1, v2}), + CVC4ApiException&); + TS_ASSERT_THROWS( d_solver->defineFunsRec({f1, f3}, {{b1, b11}, {b4}}, {v1, v2}), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->defineFunsRec({f1, f2}, {{b1}, {b4}}, {v1, v2}), @@ -923,6 +1125,42 @@ void SolverBlack::testDefineFunsRec() TS_ASSERT_THROWS( d_solver->defineFunsRec({f1, f2}, {{b1, b11}, {b4}}, {v1, v4}), CVC4ApiException&); + + Solver slv; + Sort uSort2 = slv.mkUninterpretedSort("u"); + Sort bvSort2 = slv.mkBitVectorSort(32); + Sort funSort12 = slv.mkFunctionSort({bvSort2, bvSort2}, bvSort2); + Sort funSort22 = slv.mkFunctionSort(uSort2, slv.getIntegerSort()); + Term b12 = slv.mkVar(bvSort2, "b1"); + Term b112 = slv.mkVar(bvSort2, "b1"); + Term b42 = slv.mkVar(uSort2, "b4"); + Term v12 = slv.mkConst(bvSort2, "v1"); + Term v22 = slv.mkConst(slv.getIntegerSort(), "v2"); + Term f12 = slv.mkConst(funSort12, "f1"); + Term f22 = slv.mkConst(funSort22, "f2"); + TS_ASSERT_THROWS_NOTHING( + slv.defineFunsRec({f12, f22}, {{b12, b112}, {b42}}, {v12, v22})); + TS_ASSERT_THROWS( + slv.defineFunsRec({f1, f22}, {{b12, b112}, {b42}}, {v12, v22}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.defineFunsRec({f12, f2}, {{b12, b112}, {b42}}, {v12, v22}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.defineFunsRec({f12, f22}, {{b1, b112}, {b42}}, {v12, v22}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.defineFunsRec({f12, f22}, {{b12, b11}, {b42}}, {v12, v22}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.defineFunsRec({f12, f22}, {{b12, b112}, {b4}}, {v12, v22}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.defineFunsRec({f12, f22}, {{b12, b112}, {b42}}, {v1, v22}), + CVC4ApiException&); + TS_ASSERT_THROWS( + slv.defineFunsRec({f12, f22}, {{b12, b112}, {b42}}, {v12, v2}), + CVC4ApiException&); } void SolverBlack::testUFIteration() @@ -945,6 +1183,12 @@ void SolverBlack::testUFIteration() } } +void SolverBlack::testGetInfo() +{ + TS_ASSERT_THROWS_NOTHING(d_solver->getInfo("name")); + TS_ASSERT_THROWS(d_solver->getInfo("asdf"), CVC4ApiException&); +} + void SolverBlack::testGetOp() { Sort bv32 = d_solver->mkBitVectorSort(32); @@ -959,11 +1203,11 @@ void SolverBlack::testGetOp() // Test Datatypes -- more complicated DatatypeDecl consListSpec = d_solver->mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver->mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver->getIntegerSort()); cons.addSelectorSelf("tail"); consListSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver->mkDatatypeConstructorDecl("nil"); consListSpec.addConstructor(nil); Sort consListSort = d_solver->mkDatatypeSort(consListSpec); Datatype consList = consListSort.getDatatype(); @@ -978,13 +1222,171 @@ void SolverBlack::testGetOp() Term listhead = d_solver->mkTerm(APPLY_SELECTOR, headTerm, listcons1); TS_ASSERT(listnil.hasOp()); - TS_ASSERT_EQUALS(listnil.getOp(), APPLY_CONSTRUCTOR); + TS_ASSERT_EQUALS(listnil.getOp(), Op(d_solver.get(), APPLY_CONSTRUCTOR)); TS_ASSERT(listcons1.hasOp()); - TS_ASSERT_EQUALS(listcons1.getOp(), APPLY_CONSTRUCTOR); + TS_ASSERT_EQUALS(listcons1.getOp(), Op(d_solver.get(), APPLY_CONSTRUCTOR)); TS_ASSERT(listhead.hasOp()); - TS_ASSERT_EQUALS(listhead.getOp(), APPLY_SELECTOR); + TS_ASSERT_EQUALS(listhead.getOp(), Op(d_solver.get(), APPLY_SELECTOR)); +} + +void SolverBlack::testGetOption() +{ + TS_ASSERT_THROWS_NOTHING(d_solver->getOption("incremental")); + TS_ASSERT_THROWS(d_solver->getOption("asdf"), CVC4ApiException&); +} + +void SolverBlack::testGetUnsatAssumptions1() +{ +#if IS_PROOFS_BUILD + d_solver->setOption("incremental", "false"); + d_solver->checkSatAssuming(d_solver->mkFalse()); + TS_ASSERT_THROWS(d_solver->getUnsatAssumptions(), CVC4ApiException&); +#endif +} + +void SolverBlack::testGetUnsatAssumptions2() +{ +#if IS_PROOFS_BUILD + d_solver->setOption("incremental", "true"); + d_solver->setOption("produce-unsat-assumptions", "false"); + d_solver->checkSatAssuming(d_solver->mkFalse()); + TS_ASSERT_THROWS(d_solver->getUnsatAssumptions(), CVC4ApiException&); +#endif +} + +void SolverBlack::testGetUnsatAssumptions3() +{ +#if IS_PROOFS_BUILD + d_solver->setOption("incremental", "true"); + d_solver->setOption("produce-unsat-assumptions", "true"); + d_solver->checkSatAssuming(d_solver->mkFalse()); + TS_ASSERT_THROWS_NOTHING(d_solver->getUnsatAssumptions()); + d_solver->checkSatAssuming(d_solver->mkTrue()); + TS_ASSERT_THROWS(d_solver->getUnsatAssumptions(), CVC4ApiException&); +#endif +} + +void SolverBlack::testGetUnsatCore1() +{ +#if IS_PROOFS_BUILD + d_solver->setOption("incremental", "false"); + d_solver->assertFormula(d_solver->mkFalse()); + d_solver->checkSat(); + TS_ASSERT_THROWS(d_solver->getUnsatCore(), CVC4ApiException&); +#endif +} + +void SolverBlack::testGetUnsatCore2() +{ +#if IS_PROOFS_BUILD + d_solver->setOption("incremental", "false"); + d_solver->setOption("produce-unsat-cores", "false"); + d_solver->assertFormula(d_solver->mkFalse()); + d_solver->checkSat(); + TS_ASSERT_THROWS(d_solver->getUnsatCore(), CVC4ApiException&); +#endif +} + +void SolverBlack::testGetUnsatCore3() +{ +#if IS_PROOFS_BUILD + d_solver->setOption("incremental", "true"); + d_solver->setOption("produce-unsat-cores", "true"); + + Sort uSort = d_solver->mkUninterpretedSort("u"); + Sort intSort = d_solver->getIntegerSort(); + Sort boolSort = d_solver->getBooleanSort(); + Sort uToIntSort = d_solver->mkFunctionSort(uSort, intSort); + Sort intPredSort = d_solver->mkFunctionSort(intSort, boolSort); + std::vector<Term> unsat_core; + + Term x = d_solver->mkConst(uSort, "x"); + Term y = d_solver->mkConst(uSort, "y"); + Term f = d_solver->mkConst(uToIntSort, "f"); + Term p = d_solver->mkConst(intPredSort, "p"); + Term zero = d_solver->mkReal(0); + Term one = d_solver->mkReal(1); + Term f_x = d_solver->mkTerm(APPLY_UF, f, x); + Term f_y = d_solver->mkTerm(APPLY_UF, f, y); + Term sum = d_solver->mkTerm(PLUS, f_x, f_y); + Term p_0 = d_solver->mkTerm(APPLY_UF, p, zero); + Term p_f_y = d_solver->mkTerm(APPLY_UF, p, f_y); + d_solver->assertFormula(d_solver->mkTerm(GT, zero, f_x)); + d_solver->assertFormula(d_solver->mkTerm(GT, zero, f_y)); + d_solver->assertFormula(d_solver->mkTerm(GT, sum, one)); + d_solver->assertFormula(p_0); + d_solver->assertFormula(p_f_y.notTerm()); + TS_ASSERT(d_solver->checkSat().isUnsat()); + + TS_ASSERT_THROWS_NOTHING(unsat_core = d_solver->getUnsatCore()); + + d_solver->resetAssertions(); + for (const auto& t : unsat_core) + { + d_solver->assertFormula(t); + } + Result res = d_solver->checkSat(); + TS_ASSERT(res.isUnsat()); +#endif +} + +void SolverBlack::testGetValue1() +{ + d_solver->setOption("produce-models", "false"); + Term t = d_solver->mkTrue(); + d_solver->assertFormula(t); + d_solver->checkSat(); + TS_ASSERT_THROWS(d_solver->getValue(t), CVC4ApiException&); +} + +void SolverBlack::testGetValue2() +{ + d_solver->setOption("produce-models", "true"); + Term t = d_solver->mkFalse(); + d_solver->assertFormula(t); + d_solver->checkSat(); + TS_ASSERT_THROWS(d_solver->getValue(t), CVC4ApiException&); +} + +void SolverBlack::testGetValue3() +{ + d_solver->setOption("produce-models", "true"); + Sort uSort = d_solver->mkUninterpretedSort("u"); + Sort intSort = d_solver->getIntegerSort(); + Sort boolSort = d_solver->getBooleanSort(); + Sort uToIntSort = d_solver->mkFunctionSort(uSort, intSort); + Sort intPredSort = d_solver->mkFunctionSort(intSort, boolSort); + std::vector<Term> unsat_core; + + Term x = d_solver->mkConst(uSort, "x"); + Term y = d_solver->mkConst(uSort, "y"); + Term z = d_solver->mkConst(uSort, "z"); + Term f = d_solver->mkConst(uToIntSort, "f"); + Term p = d_solver->mkConst(intPredSort, "p"); + Term zero = d_solver->mkReal(0); + Term one = d_solver->mkReal(1); + Term f_x = d_solver->mkTerm(APPLY_UF, f, x); + Term f_y = d_solver->mkTerm(APPLY_UF, f, y); + Term sum = d_solver->mkTerm(PLUS, f_x, f_y); + Term p_0 = d_solver->mkTerm(APPLY_UF, p, zero); + Term p_f_y = d_solver->mkTerm(APPLY_UF, p, f_y); + + d_solver->assertFormula(d_solver->mkTerm(LEQ, zero, f_x)); + d_solver->assertFormula(d_solver->mkTerm(LEQ, zero, f_y)); + d_solver->assertFormula(d_solver->mkTerm(LEQ, sum, one)); + d_solver->assertFormula(p_0.notTerm()); + d_solver->assertFormula(p_f_y); + TS_ASSERT(d_solver->checkSat().isSat()); + TS_ASSERT_THROWS_NOTHING(d_solver->getValue(x)); + TS_ASSERT_THROWS_NOTHING(d_solver->getValue(y)); + TS_ASSERT_THROWS_NOTHING(d_solver->getValue(z)); + TS_ASSERT_THROWS_NOTHING(d_solver->getValue(sum)); + TS_ASSERT_THROWS_NOTHING(d_solver->getValue(p_f_y)); + + Solver slv; + TS_ASSERT_THROWS(slv.getValue(x), CVC4ApiException&); } void SolverBlack::testPush1() @@ -1023,6 +1425,32 @@ void SolverBlack::testPop3() TS_ASSERT_THROWS(d_solver->pop(1), CVC4ApiException&); } +void SolverBlack::testPrintModel1() +{ + d_solver->setOption("produce-models", "false"); + Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x"); + d_solver->assertFormula(x.eqTerm(x)); + TS_ASSERT_THROWS(d_solver->printModel(std::cout), CVC4ApiException&); +} + +void SolverBlack::testPrintModel2() +{ + d_solver->setOption("produce-models", "true"); + Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x"); + d_solver->assertFormula(x.eqTerm(x).notTerm()); + d_solver->checkSat(); + TS_ASSERT_THROWS(d_solver->printModel(std::cout), CVC4ApiException&); +} + +void SolverBlack::testPrintModel3() +{ + d_solver->setOption("produce-models", "true"); + Term x = d_solver->mkConst(d_solver->getBooleanSort(), "x"); + d_solver->assertFormula(x.eqTerm(x)); + d_solver->checkSat(); + TS_ASSERT_THROWS_NOTHING(d_solver->printModel(std::cout)); +} + void SolverBlack::testSetInfo() { TS_ASSERT_THROWS(d_solver->setInfo("cvc4-lagic", "QF_BV"), CVC4ApiException&); @@ -1059,11 +1487,11 @@ void SolverBlack::testSimplify() Sort funSort1 = d_solver->mkFunctionSort({bvSort, bvSort}, bvSort); Sort funSort2 = d_solver->mkFunctionSort(uSort, d_solver->getIntegerSort()); DatatypeDecl consListSpec = d_solver->mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver->mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver->getIntegerSort()); cons.addSelectorSelf("tail"); consListSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver->mkDatatypeConstructorDecl("nil"); consListSpec.addConstructor(nil); Sort consListSort = d_solver->mkDatatypeSort(consListSpec); @@ -1081,6 +1509,8 @@ void SolverBlack::testSimplify() TS_ASSERT_THROWS_NOTHING(d_solver->simplify(x_eq_b)); TS_ASSERT(d_solver->mkTrue() != x_eq_b); TS_ASSERT(d_solver->mkTrue() != d_solver->simplify(x_eq_b)); + Solver slv; + TS_ASSERT_THROWS(slv.simplify(x), CVC4ApiException&); Term i1 = d_solver->mkConst(d_solver->getIntegerSort(), "i1"); TS_ASSERT_THROWS_NOTHING(d_solver->simplify(i1)); @@ -1123,12 +1553,22 @@ void SolverBlack::testSimplify() TS_ASSERT_THROWS_NOTHING(d_solver->simplify(f2)); } +void SolverBlack::testAssertFormula() +{ + TS_ASSERT_THROWS_NOTHING(d_solver->assertFormula(d_solver->mkTrue())); + TS_ASSERT_THROWS(d_solver->assertFormula(Term()), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.assertFormula(d_solver->mkTrue()), CVC4ApiException&); +} + void SolverBlack::testCheckEntailed() { d_solver->setOption("incremental", "false"); TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(d_solver->mkTrue())); TS_ASSERT_THROWS(d_solver->checkEntailed(d_solver->mkTrue()), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.checkEntailed(d_solver->mkTrue()), CVC4ApiException&); } void SolverBlack::testCheckEntailed1() @@ -1142,6 +1582,8 @@ void SolverBlack::testCheckEntailed1() TS_ASSERT_THROWS(d_solver->checkEntailed(Term()), CVC4ApiException&); TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(d_solver->mkTrue())); TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(z)); + Solver slv; + TS_ASSERT_THROWS(slv.checkEntailed(d_solver->mkTrue()), CVC4ApiException&); } void SolverBlack::testCheckEntailed2() @@ -1191,6 +1633,91 @@ void SolverBlack::testCheckEntailed2() TS_ASSERT_THROWS( d_solver->checkEntailed({n, d_solver->mkTerm(DISTINCT, x, y)}), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.checkEntailed(d_solver->mkTrue()), CVC4ApiException&); +} + +void SolverBlack::testCheckSat() +{ + d_solver->setOption("incremental", "false"); + TS_ASSERT_THROWS_NOTHING(d_solver->checkSat()); + TS_ASSERT_THROWS(d_solver->checkSat(), CVC4ApiException&); +} + +void SolverBlack::testCheckSatAssuming() +{ + d_solver->setOption("incremental", "false"); + TS_ASSERT_THROWS_NOTHING(d_solver->checkSatAssuming(d_solver->mkTrue())); + TS_ASSERT_THROWS(d_solver->checkSatAssuming(d_solver->mkTrue()), + CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.checkSatAssuming(d_solver->mkTrue()), CVC4ApiException&); +} + +void SolverBlack::testCheckSatAssuming1() +{ + Sort boolSort = d_solver->getBooleanSort(); + Term x = d_solver->mkConst(boolSort, "x"); + Term y = d_solver->mkConst(boolSort, "y"); + Term z = d_solver->mkTerm(AND, x, y); + d_solver->setOption("incremental", "true"); + TS_ASSERT_THROWS_NOTHING(d_solver->checkSatAssuming(d_solver->mkTrue())); + TS_ASSERT_THROWS(d_solver->checkSatAssuming(Term()), CVC4ApiException&); + TS_ASSERT_THROWS_NOTHING(d_solver->checkSatAssuming(d_solver->mkTrue())); + TS_ASSERT_THROWS_NOTHING(d_solver->checkSatAssuming(z)); + Solver slv; + TS_ASSERT_THROWS(slv.checkSatAssuming(d_solver->mkTrue()), CVC4ApiException&); +} + +void SolverBlack::testCheckSatAssuming2() +{ + d_solver->setOption("incremental", "true"); + + Sort uSort = d_solver->mkUninterpretedSort("u"); + Sort intSort = d_solver->getIntegerSort(); + Sort boolSort = d_solver->getBooleanSort(); + Sort uToIntSort = d_solver->mkFunctionSort(uSort, intSort); + Sort intPredSort = d_solver->mkFunctionSort(intSort, boolSort); + + Term n = Term(); + // Constants + Term x = d_solver->mkConst(uSort, "x"); + Term y = d_solver->mkConst(uSort, "y"); + // Functions + Term f = d_solver->mkConst(uToIntSort, "f"); + Term p = d_solver->mkConst(intPredSort, "p"); + // Values + Term zero = d_solver->mkReal(0); + Term one = d_solver->mkReal(1); + // Terms + Term f_x = d_solver->mkTerm(APPLY_UF, f, x); + Term f_y = d_solver->mkTerm(APPLY_UF, f, y); + Term sum = d_solver->mkTerm(PLUS, f_x, f_y); + Term p_0 = d_solver->mkTerm(APPLY_UF, p, zero); + Term p_f_y = d_solver->mkTerm(APPLY_UF, p, f_y); + // Assertions + Term assertions = + d_solver->mkTerm(AND, + std::vector<Term>{ + d_solver->mkTerm(LEQ, zero, f_x), // 0 <= f(x) + d_solver->mkTerm(LEQ, zero, f_y), // 0 <= f(y) + d_solver->mkTerm(LEQ, sum, one), // f(x) + f(y) <= 1 + p_0.notTerm(), // not p(0) + p_f_y // p(f(y)) + }); + + TS_ASSERT_THROWS_NOTHING(d_solver->checkSatAssuming(d_solver->mkTrue())); + d_solver->assertFormula(assertions); + TS_ASSERT_THROWS_NOTHING( + d_solver->checkSatAssuming(d_solver->mkTerm(DISTINCT, x, y))); + TS_ASSERT_THROWS_NOTHING(d_solver->checkSatAssuming( + {d_solver->mkFalse(), d_solver->mkTerm(DISTINCT, x, y)})); + TS_ASSERT_THROWS(d_solver->checkSatAssuming(n), CVC4ApiException&); + TS_ASSERT_THROWS( + d_solver->checkSatAssuming({n, d_solver->mkTerm(DISTINCT, x, y)}), + CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.checkSatAssuming(d_solver->mkTrue()), CVC4ApiException&); } void SolverBlack::testSetLogic() @@ -1239,20 +1766,30 @@ void SolverBlack::testMkSygusVar() TS_ASSERT_THROWS(d_solver->mkSygusVar(Sort()), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkSygusVar(d_solver->getNullSort(), "a"), CVC4ApiException&); + Solver slv; + TS_ASSERT_THROWS(slv.mkSygusVar(boolSort), CVC4ApiException&); } void SolverBlack::testMkSygusGrammar() { Term nullTerm; Term boolTerm = d_solver->mkBoolean(true); - Term intTerm = d_solver->mkReal(1); + Term boolVar = d_solver->mkVar(d_solver->getBooleanSort()); + Term intVar = d_solver->mkVar(d_solver->getIntegerSort()); - TS_ASSERT_THROWS_NOTHING(d_solver->mkSygusGrammar({}, {intTerm})); - TS_ASSERT_THROWS_NOTHING(d_solver->mkSygusGrammar({boolTerm}, {intTerm})); + TS_ASSERT_THROWS_NOTHING(d_solver->mkSygusGrammar({}, {intVar})); + TS_ASSERT_THROWS_NOTHING(d_solver->mkSygusGrammar({boolVar}, {intVar})); TS_ASSERT_THROWS(d_solver->mkSygusGrammar({}, {}), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->mkSygusGrammar({}, {nullTerm}), CVC4ApiException&); - TS_ASSERT_THROWS(d_solver->mkSygusGrammar({nullTerm}, {intTerm}), + TS_ASSERT_THROWS(d_solver->mkSygusGrammar({}, {boolTerm}), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->mkSygusGrammar({boolTerm}, {intVar}), CVC4ApiException&); + Solver slv; + Term boolVar2 = slv.mkVar(slv.getBooleanSort()); + Term intVar2 = slv.mkVar(slv.getIntegerSort()); + TS_ASSERT_THROWS_NOTHING(slv.mkSygusGrammar({boolVar2}, {intVar2})); + TS_ASSERT_THROWS(slv.mkSygusGrammar({boolVar}, {intVar2}), CVC4ApiException&); + TS_ASSERT_THROWS(slv.mkSygusGrammar({boolVar2}, {intVar}), CVC4ApiException&); } void SolverBlack::testSynthFun() @@ -1284,6 +1821,13 @@ void SolverBlack::testSynthFun() TS_ASSERT_THROWS(d_solver->synthFun("f5", {}, boolToBool), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->synthFun("f6", {x}, boolean, g2), CVC4ApiException&); + Solver slv; + Term x2 = slv.mkVar(slv.getBooleanSort()); + TS_ASSERT_THROWS_NOTHING(slv.synthFun("f1", {x2}, slv.getBooleanSort())); + TS_ASSERT_THROWS(slv.synthFun("", {}, d_solver->getBooleanSort()), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.synthFun("f1", {x}, d_solver->getBooleanSort()), + CVC4ApiException&); } void SolverBlack::testSynthInv() @@ -1320,6 +1864,9 @@ void SolverBlack::testAddSygusConstraint() TS_ASSERT_THROWS_NOTHING(d_solver->addSygusConstraint(boolTerm)); TS_ASSERT_THROWS(d_solver->addSygusConstraint(nullTerm), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->addSygusConstraint(intTerm), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.addSygusConstraint(boolTerm), CVC4ApiException&); } void SolverBlack::testAddSygusInvConstraint() @@ -1375,6 +1922,23 @@ void SolverBlack::testAddSygusInvConstraint() TS_ASSERT_THROWS(d_solver->addSygusInvConstraint(inv, pre, trans, trans), CVC4ApiException&); + Solver slv; + Sort boolean2 = slv.getBooleanSort(); + Sort real2 = slv.getRealSort(); + Term inv22 = slv.declareFun("inv", {real2}, boolean2); + Term pre22 = slv.declareFun("pre", {real2}, boolean2); + Term trans22 = slv.declareFun("trans", {real2, real2}, boolean2); + Term post22 = slv.declareFun("post", {real2}, boolean2); + TS_ASSERT_THROWS_NOTHING( + slv.addSygusInvConstraint(inv22, pre22, trans22, post22)); + TS_ASSERT_THROWS(slv.addSygusInvConstraint(inv, pre22, trans22, post22), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.addSygusInvConstraint(inv22, pre, trans22, post22), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.addSygusInvConstraint(inv22, pre22, trans, post22), + CVC4ApiException&); + TS_ASSERT_THROWS(slv.addSygusInvConstraint(inv22, pre22, trans22, post), + CVC4ApiException&); } void SolverBlack::testGetSynthSolution() @@ -1395,6 +1959,9 @@ void SolverBlack::testGetSynthSolution() TS_ASSERT_THROWS(d_solver->getSynthSolution(nullTerm), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->getSynthSolution(x), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.getSynthSolution(f), CVC4ApiException&); } void SolverBlack::testGetSynthSolutions() @@ -1417,4 +1984,7 @@ void SolverBlack::testGetSynthSolutions() TS_ASSERT_THROWS(d_solver->getSynthSolutions({}), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->getSynthSolutions({nullTerm}), CVC4ApiException&); TS_ASSERT_THROWS(d_solver->getSynthSolutions({x}), CVC4ApiException&); + + Solver slv; + TS_ASSERT_THROWS(slv.getSynthSolutions({x}), CVC4ApiException&); } diff --git a/test/unit/api/sort_black.h b/test/unit/api/sort_black.h index 42d2dcb25..437b5cf26 100644 --- a/test/unit/api/sort_black.h +++ b/test/unit/api/sort_black.h @@ -63,10 +63,10 @@ void SortBlack::testGetDatatype() { // create datatype sort, check should not fail DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver.getIntegerSort()); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); TS_ASSERT_THROWS_NOTHING(dtypeSort.getDatatype()); @@ -80,11 +80,11 @@ void SortBlack::testDatatypeSorts() Sort intSort = d_solver.getIntegerSort(); // create datatype sort to test DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", intSort); cons.addSelectorSelf("tail"); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); Datatype dt = dtypeSort.getDatatype(); @@ -121,8 +121,9 @@ void SortBlack::testInstantiate() // instantiate parametric datatype, check should not fail Sort sort = d_solver.mkParamSort("T"); DatatypeDecl paramDtypeSpec = d_solver.mkDatatypeDecl("paramlist", sort); - DatatypeConstructorDecl paramCons("cons"); - DatatypeConstructorDecl paramNil("nil"); + DatatypeConstructorDecl paramCons = + d_solver.mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl paramNil = d_solver.mkDatatypeConstructorDecl("nil"); paramCons.addSelector("head", sort); paramDtypeSpec.addConstructor(paramCons); paramDtypeSpec.addConstructor(paramNil); @@ -131,10 +132,10 @@ void SortBlack::testInstantiate() paramDtypeSort.instantiate(std::vector<Sort>{d_solver.getIntegerSort()})); // instantiate non-parametric datatype sort, check should fail DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver.getIntegerSort()); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); TS_ASSERT_THROWS( @@ -265,8 +266,9 @@ void SortBlack::testGetDatatypeParamSorts() // create parametric datatype, check should not fail Sort sort = d_solver.mkParamSort("T"); DatatypeDecl paramDtypeSpec = d_solver.mkDatatypeDecl("paramlist", sort); - DatatypeConstructorDecl paramCons("cons"); - DatatypeConstructorDecl paramNil("nil"); + DatatypeConstructorDecl paramCons = + d_solver.mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl paramNil = d_solver.mkDatatypeConstructorDecl("nil"); paramCons.addSelector("head", sort); paramDtypeSpec.addConstructor(paramCons); paramDtypeSpec.addConstructor(paramNil); @@ -274,10 +276,10 @@ void SortBlack::testGetDatatypeParamSorts() TS_ASSERT_THROWS_NOTHING(paramDtypeSort.getDatatypeParamSorts()); // create non-parametric datatype sort, check should fail DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver.getIntegerSort()); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); TS_ASSERT_THROWS(dtypeSort.getDatatypeParamSorts(), CVC4ApiException&); @@ -287,10 +289,10 @@ void SortBlack::testGetDatatypeArity() { // create datatype sort, check should not fail DatatypeDecl dtypeSpec = d_solver.mkDatatypeDecl("list"); - DatatypeConstructorDecl cons("cons"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); cons.addSelector("head", d_solver.getIntegerSort()); dtypeSpec.addConstructor(cons); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); dtypeSpec.addConstructor(nil); Sort dtypeSort = d_solver.mkDatatypeSort(dtypeSpec); TS_ASSERT_THROWS_NOTHING(dtypeSort.getDatatypeArity()); diff --git a/test/unit/api/term_black.h b/test/unit/api/term_black.h index 78d6ee5cc..a3cbd028f 100644 --- a/test/unit/api/term_black.h +++ b/test/unit/api/term_black.h @@ -175,10 +175,10 @@ void TermBlack::testGetOp() Term extb = d_solver.mkTerm(ext, b); TS_ASSERT(ab.hasOp()); - TS_ASSERT_EQUALS(ab.getOp(), Op(SELECT)); + TS_ASSERT_EQUALS(ab.getOp(), Op(&d_solver, SELECT)); TS_ASSERT(!ab.getOp().isIndexed()); // can compare directly to a Kind (will invoke Op constructor) - TS_ASSERT_EQUALS(ab.getOp(), SELECT); + TS_ASSERT_EQUALS(ab.getOp(), Op(&d_solver, SELECT)); TS_ASSERT(extb.hasOp()); TS_ASSERT(extb.getOp().isIndexed()); TS_ASSERT_EQUALS(extb.getOp(), ext); @@ -189,7 +189,7 @@ void TermBlack::testGetOp() TS_ASSERT(!f.hasOp()); TS_ASSERT_THROWS(f.getOp(), CVC4ApiException&); TS_ASSERT(fx.hasOp()); - TS_ASSERT_EQUALS(fx.getOp(), APPLY_UF); + TS_ASSERT_EQUALS(fx.getOp(), Op(&d_solver, APPLY_UF)); std::vector<Term> children(fx.begin(), fx.end()); // testing rebuild from op and children TS_ASSERT_EQUALS(fx, d_solver.mkTerm(fx.getOp(), children)); @@ -197,8 +197,8 @@ void TermBlack::testGetOp() // Test Datatypes Ops Sort sort = d_solver.mkParamSort("T"); DatatypeDecl listDecl = d_solver.mkDatatypeDecl("paramlist", sort); - DatatypeConstructorDecl cons("cons"); - DatatypeConstructorDecl nil("nil"); + DatatypeConstructorDecl cons = d_solver.mkDatatypeConstructorDecl("cons"); + DatatypeConstructorDecl nil = d_solver.mkDatatypeConstructorDecl("nil"); cons.addSelector("head", sort); cons.addSelectorSelf("tail"); listDecl.addConstructor(cons); @@ -225,10 +225,10 @@ void TermBlack::testGetOp() TS_ASSERT(headTerm.hasOp()); TS_ASSERT(tailTerm.hasOp()); - TS_ASSERT_EQUALS(nilTerm.getOp(), APPLY_CONSTRUCTOR); - TS_ASSERT_EQUALS(consTerm.getOp(), APPLY_CONSTRUCTOR); - TS_ASSERT_EQUALS(headTerm.getOp(), APPLY_SELECTOR); - TS_ASSERT_EQUALS(tailTerm.getOp(), APPLY_SELECTOR); + TS_ASSERT_EQUALS(nilTerm.getOp(), Op(&d_solver, APPLY_CONSTRUCTOR)); + TS_ASSERT_EQUALS(consTerm.getOp(), Op(&d_solver, APPLY_CONSTRUCTOR)); + TS_ASSERT_EQUALS(headTerm.getOp(), Op(&d_solver, APPLY_SELECTOR)); + TS_ASSERT_EQUALS(tailTerm.getOp(), Op(&d_solver, APPLY_SELECTOR)); // Test rebuilding children.clear(); diff --git a/test/unit/parser/parser_builder_black.h b/test/unit/parser/parser_builder_black.h index 44bb9293b..2962439ff 100644 --- a/test/unit/parser/parser_builder_black.h +++ b/test/unit/parser/parser_builder_black.h @@ -111,7 +111,7 @@ class ParserBuilderBlack : public CxxTest::TestSuite TS_ASSERT(parser != NULL); api::Term e = parser->nextExpression(); - TS_ASSERT_EQUALS(e, d_solver->getExprManager()->mkConst(true)); + TS_ASSERT_EQUALS(e, d_solver->mkTrue()); e = parser->nextExpression(); TS_ASSERT(e.isNull()); diff --git a/test/unit/theory/theory_strings_word_white.h b/test/unit/theory/theory_strings_word_white.h index d84df7836..6b594d904 100644 --- a/test/unit/theory/theory_strings_word_white.h +++ b/test/unit/theory/theory_strings_word_white.h @@ -67,10 +67,10 @@ class TheoryStringsWordWhite : public CxxTest::TestSuite std::vector<Node> vec; vec.push_back(abc); - Node abcMk = Word::mkWord(vec); + Node abcMk = Word::mkWordFlatten(vec); TS_ASSERT_EQUALS(abc, abcMk); vec.push_back(a); - Node abcaMk = Word::mkWord(vec); + Node abcaMk = Word::mkWordFlatten(vec); TS_ASSERT_EQUALS(abca, abcaMk); TS_ASSERT(Word::getLength(empty) == 0); |