summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorGereon Kremer <nafur42@gmail.com>2021-10-20 18:23:40 -0700
committerGitHub <noreply@github.com>2021-10-20 18:23:40 -0700
commit578cda677d0cc62991f3ab38d0bc26074c8c28d6 (patch)
treea231ffb813653c1e2da5b38f24a9bd87a6f16b45 /src
parent2f903dcfff1eded7a75f71eede947719b72513d9 (diff)
parente590612dc4421d45cacc451a7b8a162acd9c7943 (diff)
Merge branch 'master' into fix1649fix1649
Diffstat (limited to 'src')
-rw-r--r--src/CMakeLists.txt7
-rw-r--r--src/api/cpp/cvc5.cpp31
-rw-r--r--src/api/cpp/cvc5.h27
-rw-r--r--src/api/cpp/cvc5_kind.h7
-rw-r--r--src/api/java/cvc5/Solver.java12
-rw-r--r--src/api/java/cvc5/Sort.java18
-rw-r--r--src/api/java/jni/cvc5_Solver.cpp18
-rw-r--r--src/api/java/jni/cvc5_Sort.cpp28
-rw-r--r--src/api/python/cvc5.pxd9
-rw-r--r--src/api/python/cvc5.pxi181
-rw-r--r--src/expr/CMakeLists.txt4
-rw-r--r--src/expr/codatatype_bound_variable.cpp113
-rw-r--r--src/expr/codatatype_bound_variable.h91
-rw-r--r--src/expr/uninterpreted_constant.cpp6
-rw-r--r--src/expr/variadic_trie.cpp55
-rw-r--r--src/expr/variadic_trie.h54
-rw-r--r--src/options/managed_streams.cpp32
-rw-r--r--src/options/managed_streams.h42
-rw-r--r--src/options/mkoptions.py34
-rw-r--r--src/options/module_template.cpp7
-rw-r--r--src/options/module_template.h2
-rw-r--r--src/options/options_handler.cpp5
-rw-r--r--src/options/smt_options.toml1
-rw-r--r--src/parser/parser.cpp30
-rw-r--r--src/parser/parser.h8
-rw-r--r--src/parser/smt2/Smt2.g28
-rw-r--r--src/parser/smt2/smt2.cpp34
-rw-r--r--src/parser/smt2/smt2.h2
-rw-r--r--src/printer/smt2/smt2_printer.cpp21
-rw-r--r--src/proof/alethe/alethe_post_processor.cpp73
-rw-r--r--src/proof/proof_node_manager.cpp7
-rw-r--r--src/proof/proof_node_manager.h8
-rw-r--r--src/smt/check_models.cpp11
-rw-r--r--src/smt/check_models.h10
-rw-r--r--src/smt/command.cpp17
-rw-r--r--src/smt/model_blocker.cpp2
-rw-r--r--src/smt/model_blocker.h6
-rw-r--r--src/smt/preprocessor.cpp2
-rw-r--r--src/smt/proof_manager.cpp2
-rw-r--r--src/smt/proof_post_processor.cpp4
-rw-r--r--src/smt/solver_engine.cpp18
-rw-r--r--src/smt/sygus_solver.cpp2
-rw-r--r--src/smt/witness_form.cpp18
-rw-r--r--src/smt/witness_form.h11
-rw-r--r--src/theory/arith/theory_arith.cpp23
-rw-r--r--src/theory/arrays/theory_arrays.cpp17
-rw-r--r--src/theory/arrays/theory_arrays_type_rules.cpp20
-rw-r--r--src/theory/datatypes/datatypes_rewriter.cpp10
-rw-r--r--src/theory/datatypes/kinds9
-rw-r--r--src/theory/datatypes/theory_datatypes.cpp8
-rw-r--r--src/theory/datatypes/theory_datatypes_type_rules.cpp8
-rw-r--r--src/theory/datatypes/theory_datatypes_type_rules.h6
-rw-r--r--src/theory/datatypes/type_enumerator.cpp5
-rw-r--r--src/theory/incomplete_id.cpp2
-rw-r--r--src/theory/incomplete_id.h5
-rw-r--r--src/theory/quantifiers/conjecture_generator.cpp6
-rw-r--r--src/theory/quantifiers/ematching/ho_trigger.cpp4
-rw-r--r--src/theory/quantifiers/ematching/inst_match_generator.cpp14
-rw-r--r--src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp41
-rw-r--r--src/theory/quantifiers/ematching/relational_match_generator.cpp125
-rw-r--r--src/theory/quantifiers/ematching/relational_match_generator.h92
-rw-r--r--src/theory/quantifiers/ematching/trigger_term_info.cpp42
-rw-r--r--src/theory/quantifiers/ematching/trigger_term_info.h16
-rw-r--r--src/theory/quantifiers/entailment_check.cpp411
-rw-r--r--src/theory/quantifiers/entailment_check.h156
-rw-r--r--src/theory/quantifiers/fmf/full_model_check.cpp11
-rw-r--r--src/theory/quantifiers/instantiate.cpp10
-rw-r--r--src/theory/quantifiers/quant_conflict_find.cpp69
-rw-r--r--src/theory/quantifiers/quant_conflict_find.h2
-rw-r--r--src/theory/quantifiers/quantifiers_rewriter.cpp20
-rw-r--r--src/theory/quantifiers/sygus/cegis.cpp28
-rw-r--r--src/theory/quantifiers/sygus/cegis.h7
-rw-r--r--src/theory/quantifiers/sygus/sygus_grammar_cons.cpp3
-rw-r--r--src/theory/quantifiers/term_database.cpp360
-rw-r--r--src/theory/quantifiers/term_database.h90
-rw-r--r--src/theory/quantifiers/term_registry.cpp7
-rw-r--r--src/theory/quantifiers/term_registry.h5
-rw-r--r--src/theory/rewriter.cpp22
-rw-r--r--src/theory/rewriter.h11
-rw-r--r--src/theory/sets/normal_form.h2
-rw-r--r--src/theory/strings/base_solver.cpp38
-rw-r--r--src/theory/strings/infer_proof_cons.cpp5
-rw-r--r--src/theory/strings/infer_proof_cons.h5
-rw-r--r--src/theory/strings/inference_manager.cpp15
-rw-r--r--src/theory/strings/inference_manager.h9
-rw-r--r--src/theory/strings/kinds4
-rw-r--r--src/theory/strings/regexp_enumerator.cpp49
-rw-r--r--src/theory/strings/regexp_enumerator.h59
-rw-r--r--src/theory/strings/regexp_solver.cpp5
-rw-r--r--src/theory/theory_model_builder.cpp2
-rw-r--r--src/theory/uf/ho_extension.cpp15
-rw-r--r--src/theory/uf/theory_uf.cpp2
-rw-r--r--src/util/smt2_quote_string.cpp12
-rw-r--r--src/util/smt2_quote_string.h5
94 files changed, 2174 insertions, 816 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 733186d16..a2b5966e1 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -795,6 +795,8 @@ libcvc5_add_sources(
theory/quantifiers/ematching/instantiation_engine.h
theory/quantifiers/ematching/pattern_term_selector.cpp
theory/quantifiers/ematching/pattern_term_selector.h
+ theory/quantifiers/ematching/relational_match_generator.cpp
+ theory/quantifiers/ematching/relational_match_generator.h
theory/quantifiers/ematching/trigger.cpp
theory/quantifiers/ematching/trigger.h
theory/quantifiers/ematching/trigger_database.cpp
@@ -805,6 +807,8 @@ libcvc5_add_sources(
theory/quantifiers/ematching/trigger_trie.h
theory/quantifiers/ematching/var_match_generator.cpp
theory/quantifiers/ematching/var_match_generator.h
+ theory/quantifiers/entailment_check.cpp
+ theory/quantifiers/entailment_check.h
theory/quantifiers/equality_query.cpp
theory/quantifiers/equality_query.h
theory/quantifiers/expr_miner.cpp
@@ -1079,6 +1083,8 @@ libcvc5_add_sources(
theory/strings/normal_form.h
theory/strings/proof_checker.cpp
theory/strings/proof_checker.h
+ theory/strings/regexp_enumerator.cpp
+ theory/strings/regexp_enumerator.h
theory/strings/regexp_elim.cpp
theory/strings/regexp_elim.h
theory/strings/regexp_entail.cpp
@@ -1402,7 +1408,6 @@ if(USE_GLPK)
if(ENABLE_STATIC_LIBRARY)
target_link_libraries(cvc5-static PUBLIC ${GLPK_LIBRARIES})
endif()
- target_link_libraries(cvc5-obj PUBLIC ${GLPK_LIBRARIES})
endif()
if(USE_POLY)
add_dependencies(cvc5-obj Polyxx_SHARED)
diff --git a/src/api/cpp/cvc5.cpp b/src/api/cpp/cvc5.cpp
index 778f700c0..bb39ae86d 100644
--- a/src/api/cpp/cvc5.cpp
+++ b/src/api/cpp/cvc5.cpp
@@ -1682,7 +1682,7 @@ size_t Sort::getSortConstructorArity() const
/* Bit-vector sort ----------------------------------------------------- */
-uint32_t Sort::getBVSize() const
+uint32_t Sort::getBitVectorSize() const
{
CVC5_API_TRY_CATCH_BEGIN;
CVC5_API_CHECK_NOT_NULL;
@@ -1695,7 +1695,7 @@ uint32_t Sort::getBVSize() const
/* Floating-point sort ------------------------------------------------- */
-uint32_t Sort::getFPExponentSize() const
+uint32_t Sort::getFloatingPointExponentSize() const
{
CVC5_API_TRY_CATCH_BEGIN;
CVC5_API_CHECK_NOT_NULL;
@@ -1706,7 +1706,7 @@ uint32_t Sort::getFPExponentSize() const
CVC5_API_TRY_CATCH_END;
}
-uint32_t Sort::getFPSignificandSize() const
+uint32_t Sort::getFloatingPointSignificandSize() const
{
CVC5_API_TRY_CATCH_BEGIN;
CVC5_API_CHECK_NOT_NULL;
@@ -5082,7 +5082,7 @@ Term Solver::mkTermFromKind(Kind kind) const
CVC5_API_KIND_CHECK_EXPECTED(kind == PI || kind == REGEXP_EMPTY
|| kind == REGEXP_SIGMA || kind == SEP_EMP,
kind)
- << "PI or REGEXP_EMPTY or REGEXP_SIGMA";
+ << "PI, REGEXP_EMPTY, REGEXP_SIGMA or SEP_EMP";
//////// all checks before this line
Node res;
cvc5::Kind k = extToIntKind(kind);
@@ -5822,6 +5822,18 @@ Term Solver::mkEmptyBag(const Sort& sort) const
CVC5_API_TRY_CATCH_END;
}
+Term Solver::mkSepEmp() const
+{
+ CVC5_API_TRY_CATCH_BEGIN;
+ //////// all checks before this line
+ Node res = getNodeManager()->mkNullaryOperator(d_nodeMgr->booleanType(),
+ cvc5::Kind::SEP_EMP);
+ (void)res.getType(true); /* kick off type checking */
+ return Term(this, res);
+ ////////
+ CVC5_API_TRY_CATCH_END;
+}
+
Term Solver::mkSepNil(const Sort& sort) const
{
CVC5_API_TRY_CATCH_BEGIN;
@@ -6030,10 +6042,11 @@ Term Solver::mkFloatingPoint(uint32_t exp, uint32_t sig, Term val) const
CVC5_API_ARG_CHECK_EXPECTED(exp > 0, exp) << "a value > 0";
CVC5_API_ARG_CHECK_EXPECTED(sig > 0, sig) << "a value > 0";
uint32_t bw = exp + sig;
- CVC5_API_ARG_CHECK_EXPECTED(bw == val.getSort().getBVSize(), val)
+ CVC5_API_ARG_CHECK_EXPECTED(bw == val.d_node->getType().getBitVectorSize(),
+ val)
<< "a bit-vector constant with bit-width '" << bw << "'";
CVC5_API_ARG_CHECK_EXPECTED(
- val.getSort().isBitVector() && val.d_node->isConst(), val)
+ val.d_node->getType().isBitVector() && val.d_node->isConst(), val)
<< "bit-vector constant";
//////// all checks before this line
return mkValHelper<cvc5::FloatingPoint>(
@@ -7204,7 +7217,7 @@ std::string Solver::getProof(void) const
{
CVC5_API_TRY_CATCH_BEGIN;
CVC5_API_CHECK(d_slv->getOptions().smt.produceProofs)
- << "Cannot get proof explicitly enabled (try --produce-proofs)";
+ << "Cannot get proof unless proofs are enabled (try --produce-proofs)";
CVC5_API_RECOVERABLE_CHECK(d_slv->getSmtMode() == SmtMode::UNSAT)
<< "Cannot get proof unless in unsat mode.";
return d_slv->getProof();
@@ -7455,6 +7468,8 @@ bool Solver::getAbduct(const Term& conj, Term& output) const
{
CVC5_API_TRY_CATCH_BEGIN;
CVC5_API_SOLVER_CHECK_TERM(conj);
+ CVC5_API_CHECK(d_slv->getOptions().smt.produceAbducts)
+ << "Cannot get abduct unless abducts are enabled (try --produce-abducts)";
//////// all checks before this line
Node result;
bool success = d_slv->getAbduct(*conj.d_node, result);
@@ -7471,6 +7486,8 @@ bool Solver::getAbduct(const Term& conj, Grammar& grammar, Term& output) const
{
CVC5_API_TRY_CATCH_BEGIN;
CVC5_API_SOLVER_CHECK_TERM(conj);
+ CVC5_API_CHECK(d_slv->getOptions().smt.produceAbducts)
+ << "Cannot get abduct unless abducts are enabled (try --produce-abducts)";
//////// all checks before this line
Node result;
bool success =
diff --git a/src/api/cpp/cvc5.h b/src/api/cpp/cvc5.h
index 3db581db6..c46b00d0e 100644
--- a/src/api/cpp/cvc5.h
+++ b/src/api/cpp/cvc5.h
@@ -700,19 +700,19 @@ class CVC5_EXPORT Sort
/**
* @return the bit-width of the bit-vector sort
*/
- uint32_t getBVSize() const;
+ uint32_t getBitVectorSize() const;
/* Floating-point sort ------------------------------------------------- */
/**
* @return the bit-width of the exponent of the floating-point sort
*/
- uint32_t getFPExponentSize() const;
+ uint32_t getFloatingPointExponentSize() const;
/**
* @return the width of the significand of the floating-point sort
*/
- uint32_t getFPSignificandSize() const;
+ uint32_t getFloatingPointSignificandSize() const;
/* Datatype sort ------------------------------------------------------- */
@@ -1353,6 +1353,8 @@ class CVC5_EXPORT Term
std::pair<int64_t, uint64_t> getReal64Value() const;
/**
* @return true if the term is a rational value.
+ *
+ * Note that a term of kind PI is not considered to be a real value.
*/
bool isRealValue() const;
/**
@@ -1448,6 +1450,17 @@ class CVC5_EXPORT Term
/**
* @return true if the term is a set value.
+ *
+ * A term is a set value if it is considered to be a (canonical) constant set
+ * value. A canonical set value is one whose AST is:
+ * ```
+ * (union (singleton c1) ... (union (singleton c_{n-1}) (singleton c_n))))
+ * ```
+ * where `c1 ... cn` are values ordered by id such that `c1 > ... > cn` (see
+ * also @ref Term::operator>(const Term&) const).
+ *
+ * Note that a universe set term (kind UNIVERSE_SET) is not considered to be
+ * a set value.
*/
bool isSetValue() const;
/**
@@ -2464,7 +2477,7 @@ class CVC5_EXPORT Grammar
/**
* Allow \p ntSymbol to be any input variable to corresponding
* synth-fun/synth-inv with the same sort as \p ntSymbol.
- * @param ntSymbol the non-terminal allowed to be any input constant
+ * @param ntSymbol the non-terminal allowed to be any input variable
*/
void addAnyVariable(const Term& ntSymbol);
@@ -3431,6 +3444,12 @@ class CVC5_EXPORT Solver
Term mkEmptyBag(const Sort& sort) const;
/**
+ * Create a separation logic empty term.
+ * @return the separation logic empty term
+ */
+ Term mkSepEmp() const;
+
+ /**
* Create a separation logic nil term.
* @param sort the sort of the nil term
* @return the separation logic nil term
diff --git a/src/api/cpp/cvc5_kind.h b/src/api/cpp/cvc5_kind.h
index 0ff05022f..fe87df934 100644
--- a/src/api/cpp/cvc5_kind.h
+++ b/src/api/cpp/cvc5_kind.h
@@ -766,6 +766,9 @@ enum CVC5_EXPORT Kind : int32_t
/**
* Pi constant.
*
+ * Note that PI is considered a special symbol of sort Real, but is not
+ * a real value, i.e., `Term::isRealValue() const` will return false.
+ *
* Create with:
* - `Solver::mkPi() const`
* - `Solver::mkTerm(Kind kind) const`
@@ -2228,6 +2231,10 @@ enum CVC5_EXPORT Kind : int32_t
* Finite universe set.
* All set variables must be interpreted as subsets of it.
*
+ * Note that UNIVERSE_SET is considered a special symbol of the theory of
+ * sets and is not considered as a set value,
+ * i.e., `Term::isSetValue() const` will return false.
+ *
* Create with:
* - `Solver::mkUniverseSet(const Sort& sort) const`
*/
diff --git a/src/api/java/cvc5/Solver.java b/src/api/java/cvc5/Solver.java
index b0bee500c..c46d60aee 100644
--- a/src/api/java/cvc5/Solver.java
+++ b/src/api/java/cvc5/Solver.java
@@ -882,6 +882,18 @@ public class Solver implements IPointer
private native long mkEmptyBag(long pointer, long sortPointer);
/**
+ * Create a separation logic empty term.
+ * @return the separation logic empty term
+ */
+ public Term mkSepEmp()
+ {
+ long termPointer = mkSepEmp(pointer);
+ return new Term(this, termPointer);
+ }
+
+ private native long mkSepEmp(long pointer);
+
+ /**
* Create a separation logic nil term.
* @param sort the sort of the nil term
* @return the separation logic nil term
diff --git a/src/api/java/cvc5/Sort.java b/src/api/java/cvc5/Sort.java
index f1f541e35..434c07424 100644
--- a/src/api/java/cvc5/Sort.java
+++ b/src/api/java/cvc5/Sort.java
@@ -725,34 +725,34 @@ public class Sort extends AbstractPointer implements Comparable<Sort>
/**
* @return the bit-width of the bit-vector sort
*/
- public int getBVSize()
+ public int getBitVectorSize()
{
- return getBVSize(pointer);
+ return getBitVectorSize(pointer);
}
- private native int getBVSize(long pointer);
+ private native int getBitVectorSize(long pointer);
/* Floating-point sort ------------------------------------------------- */
/**
* @return the bit-width of the exponent of the floating-point sort
*/
- public int getFPExponentSize()
+ public int getFloatingPointExponentSize()
{
- return getFPExponentSize(pointer);
+ return getFloatingPointExponentSize(pointer);
}
- private native int getFPExponentSize(long pointer);
+ private native int getFloatingPointExponentSize(long pointer);
/**
* @return the width of the significand of the floating-point sort
*/
- public int getFPSignificandSize()
+ public int getFloatingPointSignificandSize()
{
- return getFPSignificandSize(pointer);
+ return getFloatingPointSignificandSize(pointer);
}
- private native int getFPSignificandSize(long pointer);
+ private native int getFloatingPointSignificandSize(long pointer);
/* Datatype sort ------------------------------------------------------- */
diff --git a/src/api/java/jni/cvc5_Solver.cpp b/src/api/java/jni/cvc5_Solver.cpp
index c28ea412f..53c050c96 100644
--- a/src/api/java/jni/cvc5_Solver.cpp
+++ b/src/api/java/jni/cvc5_Solver.cpp
@@ -1061,6 +1061,22 @@ JNIEXPORT jlong JNICALL Java_cvc5_Solver_mkEmptyBag(JNIEnv* env,
/*
* Class: cvc5_Solver
+ * Method: mkSepEmp
+ * Signature: (JJ)J
+ */
+JNIEXPORT jlong JNICALL Java_cvc5_Solver_mkSepEmp(JNIEnv* env,
+ jobject,
+ jlong pointer)
+{
+ CVC5_JAVA_API_TRY_CATCH_BEGIN;
+ Solver* solver = reinterpret_cast<Solver*>(pointer);
+ Term* retPointer = new Term(solver->mkSepEmp());
+ return reinterpret_cast<jlong>(retPointer);
+ CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
+}
+
+/*
+ * Class: cvc5_Solver
* Method: mkSepNil
* Signature: (JJ)J
*/
@@ -2591,4 +2607,4 @@ JNIEXPORT jlong JNICALL Java_cvc5_Solver_getNullDatatypeDecl(JNIEnv* env,
DatatypeDecl* ret = new DatatypeDecl();
return reinterpret_cast<jlong>(ret);
CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
-} \ No newline at end of file
+}
diff --git a/src/api/java/jni/cvc5_Sort.cpp b/src/api/java/jni/cvc5_Sort.cpp
index a2754f032..36ba81249 100644
--- a/src/api/java/jni/cvc5_Sort.cpp
+++ b/src/api/java/jni/cvc5_Sort.cpp
@@ -978,46 +978,44 @@ JNIEXPORT jint JNICALL Java_cvc5_Sort_getSortConstructorArity(JNIEnv* env,
/*
* Class: cvc5_Sort
- * Method: getBVSize
+ * Method: getBitVectorSize
* Signature: (J)I
*/
-JNIEXPORT jint JNICALL Java_cvc5_Sort_getBVSize(JNIEnv* env,
- jobject,
- jlong pointer)
+JNIEXPORT jint JNICALL Java_cvc5_Sort_getBitVectorSize(JNIEnv* env,
+ jobject,
+ jlong pointer)
{
CVC5_JAVA_API_TRY_CATCH_BEGIN;
Sort* current = reinterpret_cast<Sort*>(pointer);
- return static_cast<jint>(current->getBVSize());
+ return static_cast<jint>(current->getBitVectorSize());
CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
}
/*
* Class: cvc5_Sort
- * Method: getFPExponentSize
+ * Method: getFloatingPointExponentSize
* Signature: (J)I
*/
-JNIEXPORT jint JNICALL Java_cvc5_Sort_getFPExponentSize(JNIEnv* env,
- jobject,
- jlong pointer)
+JNIEXPORT jint JNICALL
+Java_cvc5_Sort_getFloatingPointExponentSize(JNIEnv* env, jobject, jlong pointer)
{
CVC5_JAVA_API_TRY_CATCH_BEGIN;
Sort* current = reinterpret_cast<Sort*>(pointer);
- return static_cast<jint>(current->getFPExponentSize());
+ return static_cast<jint>(current->getFloatingPointExponentSize());
CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
}
/*
* Class: cvc5_Sort
- * Method: getFPSignificandSize
+ * Method: getFloatingPointSignificandSize
* Signature: (J)I
*/
-JNIEXPORT jint JNICALL Java_cvc5_Sort_getFPSignificandSize(JNIEnv* env,
- jobject,
- jlong pointer)
+JNIEXPORT jint JNICALL Java_cvc5_Sort_getFloatingPointSignificandSize(
+ JNIEnv* env, jobject, jlong pointer)
{
CVC5_JAVA_API_TRY_CATCH_BEGIN;
Sort* current = reinterpret_cast<Sort*>(pointer);
- return static_cast<jint>(current->getFPSignificandSize());
+ return static_cast<jint>(current->getFloatingPointSignificandSize());
CVC5_JAVA_API_TRY_CATCH_END_RETURN(env, 0);
}
diff --git a/src/api/python/cvc5.pxd b/src/api/python/cvc5.pxd
index ef9971c20..02b572120 100644
--- a/src/api/python/cvc5.pxd
+++ b/src/api/python/cvc5.pxd
@@ -219,6 +219,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api":
Term mkRegexpSigma() except +
Term mkEmptySet(Sort s) except +
Term mkEmptyBag(Sort s) except +
+ Term mkSepEmp() except +
Term mkSepNil(Sort sort) except +
Term mkString(const string& s) except +
Term mkString(const wstring& s) except +
@@ -308,6 +309,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api":
bint operator>(const Sort&) except +
bint operator<=(const Sort&) except +
bint operator>=(const Sort&) except +
+ bint isNull() except +
bint isBoolean() except +
bint isInteger() except +
bint isReal() except +
@@ -321,6 +323,7 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api":
bint isConstructor() except +
bint isSelector() except +
bint isTester() except +
+ bint isUpdater() except +
bint isFunction() except +
bint isPredicate() except +
bint isTuple() except +
@@ -357,9 +360,9 @@ cdef extern from "api/cpp/cvc5.h" namespace "cvc5::api":
vector[Sort] getUninterpretedSortParamSorts() except +
string getSortConstructorName() except +
size_t getSortConstructorArity() except +
- uint32_t getBVSize() except +
- uint32_t getFPExponentSize() except +
- uint32_t getFPSignificandSize() except +
+ uint32_t getBitVectorSize() except +
+ uint32_t getFloatingPointExponentSize() except +
+ uint32_t getFloatingPointSignificandSize() except +
vector[Sort] getDatatypeParamSorts() except +
size_t getDatatypeArity() except +
size_t getTupleLength() except +
diff --git a/src/api/python/cvc5.pxi b/src/api/python/cvc5.pxi
index 0f6b54dc6..4627859b9 100644
--- a/src/api/python/cvc5.pxi
+++ b/src/api/python/cvc5.pxi
@@ -95,7 +95,10 @@ cdef c_hash[c_Term] ctermhash = c_hash[c_Term]()
cdef class Datatype:
- """Wrapper class for :cpp:class:`cvc5::api::Datatype`."""
+ """
+ A cvc5 datatype.
+ Wrapper class for :cpp:class:`cvc5::api::Datatype`.
+ """
cdef c_Datatype cd
cdef Solver solver
def __cinit__(self, Solver solver):
@@ -114,7 +117,7 @@ cdef class Datatype:
def getConstructor(self, str name):
"""
:param name: the name of the constructor.
- :return: a constructor by name.
+ :return: a constructor by name.
"""
cdef DatatypeConstructor dc = DatatypeConstructor(self.solver)
dc.cdc = self.cd.getConstructor(name.encode())
@@ -151,34 +154,35 @@ cdef class Datatype:
return self.cd.getNumConstructors()
def isParametric(self):
- """:return: whether this datatype is parametric."""
+ """:return: True if this datatype is parametric."""
return self.cd.isParametric()
def isCodatatype(self):
- """:return: whether this datatype corresponds to a co-datatype."""
+ """:return: True if this datatype corresponds to a co-datatype."""
return self.cd.isCodatatype()
def isTuple(self):
- """:return: whether this datatype corresponds to a tuple."""
+ """:return: True if this datatype corresponds to a tuple."""
return self.cd.isTuple()
def isRecord(self):
- """:return: whether this datatype corresponds to a record."""
+ """:return: True if this datatype corresponds to a record."""
return self.cd.isRecord()
def isFinite(self):
- """:return: whether this datatype is finite."""
+ """:return: True if this datatype is finite."""
return self.cd.isFinite()
def isWellFounded(self):
- """:return: whether this datatype is well-founded (see :cpp:func:`Datatype::isWellFounded() <cvc5::api::Datatype::isWellFounded>`)."""
+ """:return: True if this datatype is well-founded (see :cpp:func:`Datatype::isWellFounded() <cvc5::api::Datatype::isWellFounded>`)."""
return self.cd.isWellFounded()
def hasNestedRecursion(self):
- """:return: whether this datatype has nested recursion (see :cpp:func:`Datatype::hasNestedRecursion() <cvc5::api::Datatype::hasNestedRecursion>`)."""
+ """:return: True if this datatype has nested recursion (see :cpp:func:`Datatype::hasNestedRecursion() <cvc5::api::Datatype::hasNestedRecursion>`)."""
return self.cd.hasNestedRecursion()
def isNull(self):
+ """:return: True if this Datatype is a null object."""
return self.cd.isNull()
def __str__(self):
@@ -195,7 +199,10 @@ cdef class Datatype:
cdef class DatatypeConstructor:
- """Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructor`."""
+ """
+ A cvc5 datatype constructor.
+ Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructor`.
+ """
cdef c_DatatypeConstructor cdc
cdef Solver solver
def __cinit__(self, Solver solver):
@@ -270,6 +277,7 @@ cdef class DatatypeConstructor:
return term
def isNull(self):
+ """:return: True if this DatatypeConstructor is a null object."""
return self.cdc.isNull()
def __str__(self):
@@ -286,7 +294,10 @@ cdef class DatatypeConstructor:
cdef class DatatypeConstructorDecl:
- """Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructorDecl`."""
+ """
+ A cvc5 datatype constructor declaration.
+ Wrapper class for :cpp:class:`cvc5::api::DatatypeConstructorDecl`.
+ """
cdef c_DatatypeConstructorDecl cddc
cdef Solver solver
@@ -311,6 +322,7 @@ cdef class DatatypeConstructorDecl:
self.cddc.addSelectorSelf(name.encode())
def isNull(self):
+ """:return: True if this DatatypeConstructorDecl is a null object."""
return self.cddc.isNull()
def __str__(self):
@@ -321,7 +333,10 @@ cdef class DatatypeConstructorDecl:
cdef class DatatypeDecl:
- """Wrapper class for :cpp:class:`cvc5::api::DatatypeDecl`."""
+ """
+ A cvc5 datatype declaration.
+ Wrapper class for :cpp:class:`cvc5::api::DatatypeDecl`.
+ """
cdef c_DatatypeDecl cdd
cdef Solver solver
def __cinit__(self, Solver solver):
@@ -354,6 +369,7 @@ cdef class DatatypeDecl:
return self.cdd.getName().decode()
def isNull(self):
+ """:return: True if this DatatypeDecl is a null object."""
return self.cdd.isNull()
def __str__(self):
@@ -364,7 +380,10 @@ cdef class DatatypeDecl:
cdef class DatatypeSelector:
- """Wrapper class for :cpp:class:`cvc5::api::DatatypeSelector`."""
+ """
+ A cvc5 datatype selector.
+ Wrapper class for :cpp:class:`cvc5::api::DatatypeSelector`.
+ """
cdef c_DatatypeSelector cds
cdef Solver solver
def __cinit__(self, Solver solver):
@@ -402,6 +421,7 @@ cdef class DatatypeSelector:
return sort
def isNull(self):
+ """:return: True if this DatatypeSelector is a null object."""
return self.cds.isNull()
def __str__(self):
@@ -412,6 +432,13 @@ cdef class DatatypeSelector:
cdef class Op:
+ """
+ A cvc5 operator.
+ An operator is a term that represents certain operators,
+ instantiated with its required parameters, e.g.,
+ a term of kind :cpp:enumerator:`BITVECTOR_EXTRACT`.
+ Wrapper class for :cpp:class:`cvc5::api::Op`.
+ """
cdef c_Op cop
cdef Solver solver
def __cinit__(self, Solver solver):
@@ -434,18 +461,33 @@ cdef class Op:
return cophash(self.cop)
def getKind(self):
+ """
+ :return: the kind of this operator.
+ """
return kind(<int> self.cop.getKind())
def isIndexed(self):
+ """
+ :return: True iff this operator is indexed.
+ """
return self.cop.isIndexed()
def isNull(self):
+ """
+ :return: True iff this operator is a null term.
+ """
return self.cop.isNull()
def getNumIndices(self):
+ """
+ :return: number of indices of this op.
+ """
return self.cop.getNumIndices()
def getIndices(self):
+ """
+ :return: the indices used to create this Op (see :cpp:func:`Op::getIndices() <cvc5::api::Op::getIndices>`).
+ """
indices = None
try:
indices = self.cop.getIndices[string]().decode()
@@ -468,6 +510,10 @@ cdef class Op:
return indices
cdef class Grammar:
+ """
+ A Sygus Grammar.
+ Wrapper class for :cpp:class:`cvc5::api::Grammar`.
+ """
cdef c_Grammar cgrammar
cdef Solver solver
def __cinit__(self, Solver solver):
@@ -475,46 +521,100 @@ cdef class Grammar:
self.cgrammar = c_Grammar()
def addRule(self, Term ntSymbol, Term rule):
+ """
+ Add ``rule`` to the set of rules corresponding to ``ntSymbol``.
+
+ :param ntSymbol: the non-terminal to which the rule is added.
+ :param rule: the rule to add.
+ """
self.cgrammar.addRule(ntSymbol.cterm, rule.cterm)
def addAnyConstant(self, Term ntSymbol):
+ """
+ Allow ``ntSymbol`` to be an arbitrary constant.
+
+ :param ntSymbol: the non-terminal allowed to be constant.
+ """
self.cgrammar.addAnyConstant(ntSymbol.cterm)
def addAnyVariable(self, Term ntSymbol):
+ """
+ Allow ``ntSymbol`` to be any input variable to corresponding synth-fun/synth-inv with the same sort as ``ntSymbol``.
+
+ :param ntSymbol: the non-terminal allowed to be any input variable.
+ """
self.cgrammar.addAnyVariable(ntSymbol.cterm)
def addRules(self, Term ntSymbol, rules):
+ """
+ Add ``ntSymbol`` to the set of rules corresponding to ``ntSymbol``.
+
+ :param ntSymbol: the non-terminal to which the rules are added.
+ :param rules: the rules to add.
+ """
cdef vector[c_Term] crules
for r in rules:
crules.push_back((<Term?> r).cterm)
self.cgrammar.addRules(ntSymbol.cterm, crules)
cdef class Result:
+ """
+ Encapsulation of a three-valued solver result, with explanations.
+ Wrapper class for :cpp:class:`cvc5::api::Result`.
+ """
cdef c_Result cr
def __cinit__(self):
# gets populated by solver
self.cr = c_Result()
def isNull(self):
+ """
+ :return: True if Result is empty, i.e., a nullary Result,
+ and not an actual result returned from a :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` (and friends) query.
+ """
return self.cr.isNull()
def isSat(self):
+ """
+ :return: True if query was a satisfiable :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query.
+ """
return self.cr.isSat()
def isUnsat(self):
+ """
+ :return: True if query was an usatisfiable :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query.
+ """
return self.cr.isUnsat()
def isSatUnknown(self):
+ """
+ :return: True if query was a :cpp:func:`Solver::checkSat() <cvc5::api::Solver::checkSat>` or :cpp:func:`Solver::checkSatAssuming() <cvc5::api::Solver::checkSatAssuming>` query and cvc5 was not able to determine (un)satisfiability.
+ """
return self.cr.isSatUnknown()
def isEntailed(self):
+ """
+ :return: True if corresponding query was an entailed :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query.
+ """
return self.cr.isEntailed()
def isNotEntailed(self):
+ """
+ :return: True if corresponding query was a :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query that is not entailed.
+ """
return self.cr.isNotEntailed()
def isEntailmentUnknown(self):
+ """
+ :return: True if query was a :cpp:func:`Solver::checkEntailed() <cvc5::api::Solver::checkEntailed>` query query and cvc5 was not able to determine if it is entailed.
+ """
return self.cr.isEntailmentUnknown()
+
+ def getUnknownExplanation(self):
+ """
+ :return: an explanation for an unknown query result.
+ """
+ return UnknownExplanation(<int> self.cr.getUnknownExplanation())
def __eq__(self, Result other):
return self.cr == other.cr
@@ -522,9 +622,6 @@ cdef class Result:
def __ne__(self, Result other):
return self.cr != other.cr
- def getUnknownExplanation(self):
- return UnknownExplanation(<int> self.cr.getUnknownExplanation())
-
def __str__(self):
return self.cr.toString().decode()
@@ -533,6 +630,20 @@ cdef class Result:
cdef class RoundingMode:
+ """
+ Rounding modes for floating-point numbers.
+
+ For many floating-point operations, infinitely precise results may not be
+ representable with the number of available bits. Thus, the results are
+ rounded in a certain way to one of the representable floating-point numbers.
+
+ These rounding modes directly follow the SMT-LIB theory for floating-point
+ arithmetic, which in turn is based on IEEE Standard 754 :cite:`IEEE754`.
+ The rounding modes are specified in Sections 4.3.1 and 4.3.2 of the IEEE
+ Standard 754.
+
+ Wrapper class for :cpp:enum:`cvc5::api::RoundingMode`.
+ """
cdef c_RoundingMode crm
cdef str name
def __cinit__(self, int rm):
@@ -557,6 +668,9 @@ cdef class RoundingMode:
cdef class UnknownExplanation:
+ """
+ Wrapper class for :cpp:enum:`cvc5::api::Result::UnknownExplanation`.
+ """
cdef c_UnknownExplanation cue
cdef str name
def __cinit__(self, int ue):
@@ -907,9 +1021,10 @@ cdef class Solver:
- ``Op mkOp(Kind kind, const string& arg)``
- ``Op mkOp(Kind kind, uint32_t arg)``
- ``Op mkOp(Kind kind, uint32_t arg0, uint32_t arg1)``
+ - ``Op mkOp(Kind kind, [uint32_t arg0, ...])`` (used for the TupleProject kind)
"""
cdef Op op = Op(self)
- cdef vector[int] v
+ cdef vector[uint32_t] v
if len(args) == 0:
op.cop = self.csolver.mkOp(k.k)
@@ -922,7 +1037,10 @@ cdef class Solver:
op.cop = self.csolver.mkOp(k.k, <int?> args[0])
elif isinstance(args[0], list):
for a in args[0]:
- v.push_back((<int?> a))
+ if a < 0 or a >= 2 ** 31:
+ raise ValueError("Argument {} must fit in a uint32_t".format(a))
+
+ v.push_back((<uint32_t?> a))
op.cop = self.csolver.mkOp(k.k, <const vector[uint32_t]&> v)
else:
raise ValueError("Unsupported signature"
@@ -1050,6 +1168,15 @@ cdef class Solver:
term.cterm = self.csolver.mkEmptyBag(s.csort)
return term
+ def mkSepEmp(self):
+ """Create a separation logic empty term.
+
+ :return: the separation logic empty term
+ """
+ cdef Term term = Term(self)
+ term.cterm = self.csolver.mkSepEmp()
+ return term
+
def mkSepNil(self, Sort sort):
"""Create a separation logic nil term.
@@ -2087,6 +2214,9 @@ cdef class Sort:
def __hash__(self):
return csorthash(self.csort)
+ def isNull(self):
+ return self.csort.isNull()
+
def isBoolean(self):
return self.csort.isBoolean()
@@ -2126,6 +2256,9 @@ cdef class Sort:
def isTester(self):
return self.csort.isTester()
+ def isUpdater(self):
+ return self.csort.isUpdater()
+
def isFunction(self):
return self.csort.isFunction()
@@ -2278,14 +2411,14 @@ cdef class Sort:
def getSortConstructorArity(self):
return self.csort.getSortConstructorArity()
- def getBVSize(self):
- return self.csort.getBVSize()
+ def getBitVectorSize(self):
+ return self.csort.getBitVectorSize()
- def getFPExponentSize(self):
- return self.csort.getFPExponentSize()
+ def getFloatingPointExponentSize(self):
+ return self.csort.getFloatingPointExponentSize()
- def getFPSignificandSize(self):
- return self.csort.getFPSignificandSize()
+ def getFloatingPointSignificandSize(self):
+ return self.csort.getFloatingPointSignificandSize()
def getDatatypeParamSorts(self):
param_sorts = []
diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt
index e73960676..45ce01edb 100644
--- a/src/expr/CMakeLists.txt
+++ b/src/expr/CMakeLists.txt
@@ -26,6 +26,8 @@ libcvc5_add_sources(
bound_var_manager.h
cardinality_constraint.cpp
cardinality_constraint.h
+ codatatype_bound_variable.cpp
+ codatatype_bound_variable.h
emptyset.cpp
emptyset.h
emptybag.cpp
@@ -95,6 +97,8 @@ libcvc5_add_sources(
sygus_datatype.h
uninterpreted_constant.cpp
uninterpreted_constant.h
+ variadic_trie.cpp
+ variadic_trie.h
)
libcvc5_add_sources(GENERATED
diff --git a/src/expr/codatatype_bound_variable.cpp b/src/expr/codatatype_bound_variable.cpp
new file mode 100644
index 000000000..a6a9d8e3e
--- /dev/null
+++ b/src/expr/codatatype_bound_variable.cpp
@@ -0,0 +1,113 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Representation of bound variables in codatatype values
+ */
+
+#include "expr/codatatype_bound_variable.h"
+
+#include <algorithm>
+#include <iostream>
+#include <sstream>
+#include <string>
+
+#include "base/check.h"
+#include "expr/type_node.h"
+
+using namespace std;
+
+namespace cvc5 {
+
+CodatatypeBoundVariable::CodatatypeBoundVariable(const TypeNode& type,
+ Integer index)
+ : d_type(new TypeNode(type)), d_index(index)
+{
+ PrettyCheckArgument(type.isCodatatype(),
+ type,
+ "codatatype bound variables can only be created for "
+ "codatatype sorts, not `%s'",
+ type.toString().c_str());
+ PrettyCheckArgument(
+ index >= 0,
+ index,
+ "index >= 0 required for codatatype bound variable index, not `%s'",
+ index.toString().c_str());
+}
+
+CodatatypeBoundVariable::~CodatatypeBoundVariable() {}
+
+CodatatypeBoundVariable::CodatatypeBoundVariable(
+ const CodatatypeBoundVariable& other)
+ : d_type(new TypeNode(other.getType())), d_index(other.getIndex())
+{
+}
+
+const TypeNode& CodatatypeBoundVariable::getType() const { return *d_type; }
+const Integer& CodatatypeBoundVariable::getIndex() const { return d_index; }
+bool CodatatypeBoundVariable::operator==(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return getType() == cbv.getType() && d_index == cbv.d_index;
+}
+bool CodatatypeBoundVariable::operator!=(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return !(*this == cbv);
+}
+
+bool CodatatypeBoundVariable::operator<(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return getType() < cbv.getType()
+ || (getType() == cbv.getType() && d_index < cbv.d_index);
+}
+bool CodatatypeBoundVariable::operator<=(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return getType() < cbv.getType()
+ || (getType() == cbv.getType() && d_index <= cbv.d_index);
+}
+bool CodatatypeBoundVariable::operator>(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return !(*this <= cbv);
+}
+bool CodatatypeBoundVariable::operator>=(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return !(*this < cbv);
+}
+
+std::ostream& operator<<(std::ostream& out, const CodatatypeBoundVariable& cbv)
+{
+ std::stringstream ss;
+ ss << cbv.getType();
+ std::string st(ss.str());
+ // must remove delimiting quotes from the name of the type
+ // this prevents us from printing symbols like |@cbv_|T|_n|
+ std::string q("|");
+ size_t pos;
+ while ((pos = st.find(q)) != std::string::npos)
+ {
+ st.replace(pos, 1, "");
+ }
+ return out << "cbv_" << st.c_str() << "_" << cbv.getIndex();
+}
+
+size_t CodatatypeBoundVariableHashFunction::operator()(
+ const CodatatypeBoundVariable& cbv) const
+{
+ return std::hash<TypeNode>()(cbv.getType())
+ * IntegerHashFunction()(cbv.getIndex());
+}
+
+} // namespace cvc5
diff --git a/src/expr/codatatype_bound_variable.h b/src/expr/codatatype_bound_variable.h
new file mode 100644
index 000000000..9af8476d5
--- /dev/null
+++ b/src/expr/codatatype_bound_variable.h
@@ -0,0 +1,91 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Representation of bound variables in codatatype values
+ */
+
+#include "cvc5_public.h"
+
+#ifndef CVC5__EXPR__CODATATYPE_BOUND_VARIABLE_H
+#define CVC5__EXPR__CODATATYPE_BOUND_VARIABLE_H
+
+#include <iosfwd>
+#include <memory>
+
+#include "util/integer.h"
+
+namespace cvc5 {
+
+class TypeNode;
+
+/**
+ * In theory, codatatype values are represented in using a MU-notation form,
+ * where recursive values may contain free constants indexed by their de Bruijn
+ * indices. This is sometimes written:
+ * MU x. (cons 0 x).
+ * where the MU binder is label for a term position, which x references.
+ * Instead of constructing an explicit MU binder (which is problematic for
+ * canonicity), we use de Bruijn indices for representing bound variables,
+ * whose index indicates the level in which it is nested. For example, we
+ * represent the above value as:
+ * (cons 0 @cbv_0)
+ * In the above value, @cbv_0 is a pointer to its direct parent, so the above
+ * value represents the infinite list (cons 0 (cons 0 (cons 0 ... ))).
+ * Another example, the value:
+ * (cons 0 (cons 1 @cbv_1))
+ * @cbv_1 is pointer to the top most node of this value, so this is value
+ * represents the infinite list (cons 0 (cons 1 (cons 0 (cons 1 ...)))). The
+ * value:
+ * (cons 0 (cons 1 @cbv_0))
+ * on the other hand represents the lasso:
+ * (cons 0 (cons 1 (cons 1 (cons 1 ... ))))
+ *
+ * This class is used for representing the indexed bound variables in the above
+ * values. It is considered a constant (isConst is true). However, constants
+ * of codatatype type are normalized by the rewriter (see datatypes rewriter
+ * normalizeCodatatypeConstant) to ensure their canonicity, via a variant
+ * of Hopcroft's algorithm.
+ */
+class CodatatypeBoundVariable
+{
+ public:
+ CodatatypeBoundVariable(const TypeNode& type, Integer index);
+ ~CodatatypeBoundVariable();
+
+ CodatatypeBoundVariable(const CodatatypeBoundVariable& other);
+
+ const TypeNode& getType() const;
+ const Integer& getIndex() const;
+ bool operator==(const CodatatypeBoundVariable& cbv) const;
+ bool operator!=(const CodatatypeBoundVariable& cbv) const;
+ bool operator<(const CodatatypeBoundVariable& cbv) const;
+ bool operator<=(const CodatatypeBoundVariable& cbv) const;
+ bool operator>(const CodatatypeBoundVariable& cbv) const;
+ bool operator>=(const CodatatypeBoundVariable& cbv) const;
+
+ private:
+ std::unique_ptr<TypeNode> d_type;
+ const Integer d_index;
+};
+std::ostream& operator<<(std::ostream& out, const CodatatypeBoundVariable& cbv);
+
+/**
+ * Hash function for codatatype bound variables.
+ */
+struct CodatatypeBoundVariableHashFunction
+{
+ size_t operator()(const CodatatypeBoundVariable& cbv) const;
+};
+
+} // namespace cvc5
+
+#endif /* CVC5__UNINTERPRETED_CONSTANT_H */
diff --git a/src/expr/uninterpreted_constant.cpp b/src/expr/uninterpreted_constant.cpp
index ef354568d..709ec112f 100644
--- a/src/expr/uninterpreted_constant.cpp
+++ b/src/expr/uninterpreted_constant.cpp
@@ -31,7 +31,11 @@ UninterpretedConstant::UninterpretedConstant(const TypeNode& type,
Integer index)
: d_type(new TypeNode(type)), d_index(index)
{
- //PrettyCheckArgument(type.isSort(), type, "uninterpreted constants can only be created for uninterpreted sorts, not `%s'", type.toString().c_str());
+ PrettyCheckArgument(type.isSort(),
+ type,
+ "uninterpreted constants can only be created for "
+ "uninterpreted sorts, not `%s'",
+ type.toString().c_str());
PrettyCheckArgument(index >= 0, index, "index >= 0 required for uninterpreted constant index, not `%s'", index.toString().c_str());
}
diff --git a/src/expr/variadic_trie.cpp b/src/expr/variadic_trie.cpp
new file mode 100644
index 000000000..bd27780e9
--- /dev/null
+++ b/src/expr/variadic_trie.cpp
@@ -0,0 +1,55 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Variadic trie utility
+ */
+
+#include "expr/variadic_trie.h"
+
+namespace cvc5 {
+
+bool VariadicTrie::add(Node n, const std::vector<Node>& i)
+{
+ VariadicTrie* curr = this;
+ for (const Node& ic : i)
+ {
+ curr = &(curr->d_children[ic]);
+ }
+ if (curr->d_data.isNull())
+ {
+ curr->d_data = n;
+ return true;
+ }
+ return false;
+}
+
+bool VariadicTrie::hasSubset(const std::vector<Node>& is) const
+{
+ if (!d_data.isNull())
+ {
+ return true;
+ }
+ for (const std::pair<const Node, VariadicTrie>& p : d_children)
+ {
+ Node n = p.first;
+ if (std::find(is.begin(), is.end(), n) != is.end())
+ {
+ if (p.second.hasSubset(is))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+} // namespace cvc5
diff --git a/src/expr/variadic_trie.h b/src/expr/variadic_trie.h
new file mode 100644
index 000000000..aa7ca1e37
--- /dev/null
+++ b/src/expr/variadic_trie.h
@@ -0,0 +1,54 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Variadic trie utility
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__EXPR__VARIADIC_TRIE_H
+#define CVC5__EXPR__VARIADIC_TRIE_H
+
+#include <map>
+#include <vector>
+
+#include "expr/node.h"
+
+namespace cvc5 {
+
+/**
+ * A trie that stores data at undetermined depth. Storing data at
+ * undetermined depth is in contrast to the NodeTrie (expr/node_trie.h), which
+ * assumes all data is stored at a fixed depth.
+ *
+ * Since data can be stored at any depth, we require both a d_children field
+ * and a d_data field.
+ */
+class VariadicTrie
+{
+ public:
+ /** the children of this node */
+ std::map<Node, VariadicTrie> d_children;
+ /** the data at this node */
+ Node d_data;
+ /**
+ * Add data with identifier n indexed by i, return true if data is not already
+ * stored at the node indexed by i.
+ */
+ bool add(Node n, const std::vector<Node>& i);
+ /** Is there any data in this trie that is indexed by any subset of is? */
+ bool hasSubset(const std::vector<Node>& is) const;
+};
+
+} // namespace cvc5
+
+#endif /* CVC5__EXPR__VARIADIC_TRIE_H */
diff --git a/src/options/managed_streams.cpp b/src/options/managed_streams.cpp
index 81bb242cf..90090df25 100644
--- a/src/options/managed_streams.cpp
+++ b/src/options/managed_streams.cpp
@@ -100,34 +100,54 @@ std::istream* openIStream(const std::string& filename)
}
} // namespace detail
-std::ostream* ManagedErr::defaultValue() const { return &std::cerr; }
+ManagedErr::ManagedErr() : ManagedStream(&std::cerr, "stderr") {}
bool ManagedErr::specialCases(const std::string& value)
{
if (value == "stderr" || value == "--")
{
- d_stream.reset();
+ d_nonowned = &std::cerr;
+ d_owned.reset();
+ d_description = "stderr";
+ return true;
+ }
+ else if (value == "stdout")
+ {
+ d_nonowned = &std::cout;
+ d_owned.reset();
+ d_description = "stdout";
return true;
}
return false;
}
-std::istream* ManagedIn::defaultValue() const { return &std::cin; }
+ManagedIn::ManagedIn() : ManagedStream(&std::cin, "stdin") {}
bool ManagedIn::specialCases(const std::string& value)
{
if (value == "stdin" || value == "--")
{
- d_stream.reset();
+ d_nonowned = &std::cin;
+ d_owned.reset();
+ d_description = "stdin";
return true;
}
return false;
}
-std::ostream* ManagedOut::defaultValue() const { return &std::cout; }
+ManagedOut::ManagedOut() : ManagedStream(&std::cout, "stdout") {}
bool ManagedOut::specialCases(const std::string& value)
{
if (value == "stdout" || value == "--")
{
- d_stream.reset();
+ d_nonowned = &std::cout;
+ d_owned.reset();
+ d_description = "stdout";
+ return true;
+ }
+ else if (value == "stderr")
+ {
+ d_nonowned = &std::cerr;
+ d_owned.reset();
+ d_description = "stderr";
return true;
}
return false;
diff --git a/src/options/managed_streams.h b/src/options/managed_streams.h
index 56bb21c2e..cf1820de6 100644
--- a/src/options/managed_streams.h
+++ b/src/options/managed_streams.h
@@ -50,7 +50,8 @@ template <typename Stream>
class ManagedStream
{
public:
- ManagedStream() {}
+ ManagedStream(Stream* nonowned, std::string description)
+ : d_nonowned(nonowned), d_description(std::move(description)) {}
virtual ~ManagedStream() {}
/**
@@ -62,11 +63,15 @@ class ManagedStream
if (specialCases(value)) return;
if constexpr (std::is_same<Stream, std::ostream>::value)
{
- d_stream.reset(detail::openOStream(value));
+ d_nonowned = nullptr;
+ d_owned.reset(detail::openOStream(value));
+ d_description = value;
}
else if constexpr (std::is_same<Stream, std::istream>::value)
{
- d_stream.reset(detail::openIStream(value));
+ d_nonowned = nullptr;
+ d_owned.reset(detail::openIStream(value));
+ d_description = value;
}
}
@@ -75,12 +80,14 @@ class ManagedStream
operator Stream&() const { return *getPtr(); }
operator Stream*() const { return getPtr(); }
+ const std::string& description() const { return d_description; }
+
protected:
- std::shared_ptr<Stream> d_stream;
+ Stream* d_nonowned;
+ std::shared_ptr<Stream> d_owned;
+ std::string d_description = "<null>";
private:
- /** Returns the value to be used if d_stream is not set. */
- virtual Stream* defaultValue() const = 0;
/**
* Check if there is a special case for this value. If so, the implementation
* should set d_stream appropriately and return true to skip the default
@@ -88,18 +95,18 @@ class ManagedStream
*/
virtual bool specialCases(const std::string& value) = 0;
- /** Return the pointer, either from d_stream of from defaultValue(). */
+ /** Return the pointer, either from d_nonowned or d_owned. */
Stream* getPtr() const
{
- if (d_stream) return d_stream.get();
- return defaultValue();
+ if (d_nonowned != nullptr) return d_nonowned;
+ return d_owned.get();
}
};
template <typename Stream>
std::ostream& operator<<(std::ostream& os, const ManagedStream<Stream>& ms)
{
- return os << "ManagedStream";
+ return os << ms.description();
}
/**
@@ -108,7 +115,10 @@ std::ostream& operator<<(std::ostream& os, const ManagedStream<Stream>& ms)
*/
class ManagedErr : public ManagedStream<std::ostream>
{
- std::ostream* defaultValue() const override final;
+ public:
+ ManagedErr();
+
+ private:
bool specialCases(const std::string& value) override final;
};
@@ -118,7 +128,10 @@ class ManagedErr : public ManagedStream<std::ostream>
*/
class ManagedIn : public ManagedStream<std::istream>
{
- std::istream* defaultValue() const override final;
+ public:
+ ManagedIn();
+
+ private:
bool specialCases(const std::string& value) override final;
};
@@ -128,7 +141,10 @@ class ManagedIn : public ManagedStream<std::istream>
*/
class ManagedOut : public ManagedStream<std::ostream>
{
- std::ostream* defaultValue() const override final;
+ public:
+ ManagedOut();
+
+ private:
bool specialCases(const std::string& value) override final;
};
diff --git a/src/options/mkoptions.py b/src/options/mkoptions.py
index a8f631de6..57f8b64e6 100644
--- a/src/options/mkoptions.py
+++ b/src/options/mkoptions.py
@@ -480,17 +480,6 @@ def generate_module_option_names(module):
'static constexpr const char* {name}__name = "{long_name}";', relevant)
-def generate_module_setdefaults_decl(module):
- res = []
- for option in module.options:
- if option.name is None:
- continue
- funcname = option.name[0].capitalize() + option.name[1:]
- res.append('void setDefault{}(Options& opts, {} value);'.format(
- funcname, option.type))
- return '\n'.join(res)
-
-
################################################################################
# for options/<module>.cpp
@@ -581,27 +570,6 @@ def generate_module_mode_impl(module):
return '\n'.join(res)
-TPL_SETDEFAULT_IMPL = '''void setDefault{capname}(Options& opts, {type} value)
-{{
- if (!opts.{module}.{name}WasSetByUser) opts.{module}.{name} = value;
-}}'''
-
-
-def generate_module_setdefaults_impl(module):
- res = []
- for option in module.options:
- if option.name is None:
- continue
- fmt = {
- 'capname': option.name[0].capitalize() + option.name[1:],
- 'type': option.type,
- 'module': module.id,
- 'name': option.name,
- }
- res.append(TPL_SETDEFAULT_IMPL.format(**fmt))
- return '\n'.join(res)
-
-
################################################################################
# for main/options.cpp
@@ -875,11 +843,9 @@ def codegen_module(module, dst_dir, tpls):
'holder_decl': generate_module_holder_decl(module),
'wrapper_functions': generate_module_wrapper_functions(module),
'option_names': generate_module_option_names(module),
- 'setdefaults_decl': generate_module_setdefaults_decl(module),
# module source
'header': module.header,
'modes_impl': generate_module_mode_impl(module),
- 'setdefaults_impl': generate_module_setdefaults_impl(module),
}
for tpl in tpls:
filename = tpl['output'].replace('module', module.filename)
diff --git a/src/options/module_template.cpp b/src/options/module_template.cpp
index ee5260f34..d2ece3f13 100644
--- a/src/options/module_template.cpp
+++ b/src/options/module_template.cpp
@@ -28,11 +28,4 @@ namespace cvc5::options {
${modes_impl}$
// clang-format on
-namespace ${id}$
-{
-// clang-format off
-${setdefaults_impl}$
-// clang-format on
-}
-
} // namespace cvc5::options
diff --git a/src/options/module_template.h b/src/options/module_template.h
index 61d689b72..d9b591f11 100644
--- a/src/options/module_template.h
+++ b/src/options/module_template.h
@@ -56,8 +56,6 @@ namespace ${id}$
{
// clang-format off
${option_names}$
-
-${setdefaults_decl}$
// clang-format on
}
diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp
index 93b08a858..ec6c831e4 100644
--- a/src/options/options_handler.cpp
+++ b/src/options/options_handler.cpp
@@ -387,7 +387,10 @@ void OptionsHandler::checkBvSatSolver(const std::string& option,
+ " does not support lazy bit-blasting.\n"
+ "Try --bv-sat-solver=minisat");
}
- options::bv::setDefaultBitvectorToBool(*d_options, true);
+ if (!d_options->bv.bitvectorToBoolWasSetByUser)
+ {
+ d_options->bv.bitvectorToBool = true;
+ }
}
}
diff --git a/src/options/smt_options.toml b/src/options/smt_options.toml
index 19862cab2..420496190 100644
--- a/src/options/smt_options.toml
+++ b/src/options/smt_options.toml
@@ -259,6 +259,7 @@ name = "SMT Layer"
category = "common"
long = "produce-assertions"
type = "bool"
+ alias = ["interactive-mode"]
help = "keep an assertions list (enables get-assertions command)"
[[option]]
diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp
index 84c0bd17c..9f34b5647 100644
--- a/src/parser/parser.cpp
+++ b/src/parser/parser.cpp
@@ -737,6 +737,36 @@ void Parser::pushScope(bool isUserContext)
d_symman->pushScope(isUserContext);
}
+void Parser::pushGetValueScope()
+{
+ pushScope();
+ // we must bind all relevant uninterpreted constants, which coincide with
+ // the set of uninterpreted constants that are printed in the definition
+ // of a model.
+ std::vector<api::Sort> declareSorts = d_symman->getModelDeclareSorts();
+ Trace("parser") << "Push get value scope, with " << declareSorts.size()
+ << " declared sorts" << std::endl;
+ for (const api::Sort& s : declareSorts)
+ {
+ std::vector<api::Term> elements = d_solver->getModelDomainElements(s);
+ for (const api::Term& e : elements)
+ {
+ // Uninterpreted constants are abstract values, which by SMT-LIB are
+ // required to be annotated with their type, e.g. (as @uc_Foo_0 Foo).
+ // Thus, the element is not printed simply as its name.
+ std::string en = e.toString();
+ size_t index = en.find("(as ");
+ if (index == 0)
+ {
+ index = en.find(" ", 4);
+ en = en.substr(4, index - 4);
+ }
+ Trace("parser") << "Get value scope : " << en << " -> " << e << std::endl;
+ defineVar(en, e);
+ }
+ }
+}
+
void Parser::popScope()
{
d_symman->popScope();
diff --git a/src/parser/parser.h b/src/parser/parser.h
index 4b04c77b7..19e5b8531 100644
--- a/src/parser/parser.h
+++ b/src/parser/parser.h
@@ -716,6 +716,14 @@ public:
*/
void pushScope(bool isUserContext = false);
+ /** Push scope for get-value
+ *
+ * This pushes a scope by a call to pushScope that binds all relevant bindings
+ * required for parsing the SMT-LIB command `get-value`. This includes
+ * all uninterpreted constant values in user-defined uninterpreted sorts.
+ */
+ void pushGetValueScope();
+
void popScope();
virtual void reset();
diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g
index 478edb651..e3aee250e 100644
--- a/src/parser/smt2/Smt2.g
+++ b/src/parser/smt2/Smt2.g
@@ -343,7 +343,13 @@ command [std::unique_ptr<cvc5::Command>* cmd]
| DECLARE_DATATYPE_TOK datatypeDefCommand[false, cmd]
| DECLARE_DATATYPES_TOK datatypesDefCommand[false, cmd]
| /* value query */
- GET_VALUE_TOK { PARSER_STATE->checkThatLogicIsSet(); }
+ GET_VALUE_TOK
+ {
+ PARSER_STATE->checkThatLogicIsSet();
+ // bind all symbols specific to the model, e.g. uninterpreted constant
+ // values
+ PARSER_STATE->pushGetValueScope();
+ }
( LPAREN_TOK termList[terms,expr] RPAREN_TOK
{ cmd->reset(new GetValueCommand(terms)); }
| ~LPAREN_TOK
@@ -352,6 +358,7 @@ command [std::unique_ptr<cvc5::Command>* cmd]
"parentheses?");
}
)
+ { PARSER_STATE->popScope(); }
| /* get-assignment */
GET_ASSIGNMENT_TOK { PARSER_STATE->checkThatLogicIsSet(); }
{ cmd->reset(new GetAssignmentCommand()); }
@@ -793,7 +800,7 @@ smt25Command[std::unique_ptr<cvc5::Command>* cmd]
/* echo */
| ECHO_TOK
- ( simpleSymbolicExpr[s]
+ ( str[s, true]
{ cmd->reset(new EchoCommand(s)); }
| { cmd->reset(new EchoCommand()); }
)
@@ -1648,7 +1655,7 @@ identifier[cvc5::ParseOp& p]
if (!f.getSort().isConstructor())
{
PARSER_STATE->parseError(
- "Bad syntax for test (_ is X), X must be a constructor.");
+ "Bad syntax for (_ is X), X must be a constructor.");
}
// get the datatype that f belongs to
api::Sort sf = f.getSort().getConstructorCodomainSort();
@@ -1662,7 +1669,7 @@ identifier[cvc5::ParseOp& p]
if (!f.getSort().isSelector())
{
PARSER_STATE->parseError(
- "Bad syntax for test (_ update X), X must be a selector.");
+ "Bad syntax for (_ update X), X must be a selector.");
}
std::string sname = f.toString();
// get the datatype that f belongs to
@@ -1673,13 +1680,6 @@ identifier[cvc5::ParseOp& p]
// get the updater term
p.d_expr = ds.getUpdaterTerm();
}
- | TUPLE_SEL_TOK m=INTEGER_LITERAL
- {
- // we adopt a special syntax (_ tupSel n)
- p.d_kind = api::APPLY_SELECTOR;
- // put m in expr so that the caller can deal with this case
- p.d_expr = SOLVER->mkInteger(AntlrInput::tokenToUnsigned($m));
- }
| TUPLE_PROJECT_TOK nonemptyNumeralList[numerals]
{
// we adopt a special syntax (_ tuple_project i_1 ... i_n) where
@@ -1697,9 +1697,10 @@ identifier[cvc5::ParseOp& p]
{
std::string opName = AntlrInput::tokenText($sym);
api::Kind k = PARSER_STATE->getIndexedOpKind(opName);
- if (k == api::APPLY_UPDATER)
+ if (k == api::APPLY_SELECTOR || k == api::APPLY_UPDATER)
{
- // we adopt a special syntax (_ tuple_update n) for tuple updaters
+ // we adopt a special syntax (_ tuple_select n) and (_ tuple_update n)
+ // for tuple selectors and updaters
if (numerals.size() != 1)
{
PARSER_STATE->parseError(
@@ -2314,7 +2315,6 @@ FORALL_TOK : 'forall';
CHAR_TOK : { PARSER_STATE->isTheoryEnabled(theory::THEORY_STRINGS) }? 'char';
TUPLE_CONST_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tuple';
-TUPLE_SEL_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tuple_select';
TUPLE_PROJECT_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tuple_project';
HO_ARROW_TOK : { PARSER_STATE->isHoEnabled() }? '->';
diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp
index b186c2b2a..be7bdcb0f 100644
--- a/src/parser/smt2/smt2.cpp
+++ b/src/parser/smt2/smt2.cpp
@@ -135,6 +135,11 @@ void Smt2::addDatatypesOperators()
{
Parser::addOperator(api::APPLY_UPDATER);
addOperator(api::DT_SIZE, "dt.size");
+ // Notice that tuple operators, we use the generic APPLY_SELECTOR and
+ // APPLY_UPDATER kinds. These are processed based on the context
+ // in which they are parsed, e.g. when parsing identifiers.
+ addIndexedOperator(
+ api::APPLY_SELECTOR, api::APPLY_SELECTOR, "tuple_select");
addIndexedOperator(api::APPLY_UPDATER, api::APPLY_UPDATER, "tuple_update");
}
}
@@ -258,14 +263,16 @@ void Smt2::addFloatingPointOperators() {
}
void Smt2::addSepOperators() {
+ defineVar("sep.emp", d_solver->mkSepEmp());
+ // the Boolean sort is a placeholder here since we don't have type info
+ // without type annotation
+ defineVar("sep.nil", d_solver->mkSepNil(d_solver->getBooleanSort()));
addOperator(api::SEP_STAR, "sep");
addOperator(api::SEP_PTO, "pto");
addOperator(api::SEP_WAND, "wand");
- addOperator(api::SEP_EMP, "emp");
Parser::addOperator(api::SEP_STAR);
Parser::addOperator(api::SEP_PTO);
Parser::addOperator(api::SEP_WAND);
- Parser::addOperator(api::SEP_EMP);
}
void Smt2::addCoreSymbols()
@@ -288,7 +295,7 @@ void Smt2::addOperator(api::Kind kind, const std::string& name)
Debug("parser") << "Smt2::addOperator( " << kind << ", " << name << " )"
<< std::endl;
Parser::addOperator(kind);
- operatorKindMap[name] = kind;
+ d_operatorKindMap[name] = kind;
}
void Smt2::addIndexedOperator(api::Kind tKind,
@@ -302,11 +309,11 @@ void Smt2::addIndexedOperator(api::Kind tKind,
api::Kind Smt2::getOperatorKind(const std::string& name) const
{
// precondition: isOperatorEnabled(name)
- return operatorKindMap.find(name)->second;
+ return d_operatorKindMap.find(name)->second;
}
bool Smt2::isOperatorEnabled(const std::string& name) const {
- return operatorKindMap.find(name) != operatorKindMap.end();
+ return d_operatorKindMap.find(name) != d_operatorKindMap.end();
}
bool Smt2::isTheoryEnabled(theory::TheoryId theory) const
@@ -437,7 +444,7 @@ void Smt2::reset() {
d_logicSet = false;
d_seenSetLogic = false;
d_logic = LogicInfo();
- operatorKindMap.clear();
+ d_operatorKindMap.clear();
d_lastNamedTerm = std::pair<api::Term, std::string>();
}
@@ -546,7 +553,7 @@ Command* Smt2::setLogic(std::string name, bool fromCommand)
if (d_logic.areTranscendentalsUsed())
{
- defineVar("real.pi", d_solver->mkTerm(api::PI));
+ defineVar("real.pi", d_solver->mkPi());
addTranscendentalOperators();
}
if (!strictModeEnabled())
@@ -672,12 +679,8 @@ Command* Smt2::setLogic(std::string name, bool fromCommand)
addFloatingPointOperators();
}
- if (d_logic.isTheoryEnabled(theory::THEORY_SEP)) {
- // the Boolean sort is a placeholder here since we don't have type info
- // without type annotation
- defineVar("sep.nil", d_solver->mkSepNil(d_solver->getBooleanSort()));
- defineVar("sep.emp", d_solver->mkTerm(api::SEP_EMP));
-
+ if (d_logic.isTheoryEnabled(theory::THEORY_SEP))
+ {
addSepOperators();
}
@@ -1119,11 +1122,6 @@ api::Term Smt2::applyParseOp(ParseOp& p, std::vector<api::Term>& args)
Debug("parser") << "applyParseOp: return uminus " << ret << std::endl;
return ret;
}
- if (kind == api::EQ_RANGE && d_solver->getOption("arrays-exp") != "true")
- {
- parseError(
- "eqrange predicate requires option --arrays-exp to be enabled.");
- }
if (kind == api::SINGLETON && args.size() == 1)
{
api::Term ret = d_solver->mkTerm(api::SINGLETON, args[0]);
diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h
index fd68732fe..8d8f8febe 100644
--- a/src/parser/smt2/smt2.h
+++ b/src/parser/smt2/smt2.h
@@ -50,7 +50,7 @@ class Smt2 : public Parser
bool d_seenSetLogic;
LogicInfo d_logic;
- std::unordered_map<std::string, api::Kind> operatorKindMap;
+ std::unordered_map<std::string, api::Kind> d_operatorKindMap;
/**
* Maps indexed symbols to the kind of the operator (e.g. "extract" to
* BITVECTOR_EXTRACT).
diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp
index 32b195be2..1da2a3c7b 100644
--- a/src/printer/smt2/smt2_printer.cpp
+++ b/src/printer/smt2/smt2_printer.cpp
@@ -1723,15 +1723,7 @@ void Smt2Printer::toStreamCmdEmpty(std::ostream& out,
void Smt2Printer::toStreamCmdEcho(std::ostream& out,
const std::string& output) const
{
- std::string s = output;
- // escape all double-quotes
- size_t pos = 0;
- while ((pos = s.find('"', pos)) != string::npos)
- {
- s.replace(pos, 1, "\"\"");
- pos += 2;
- }
- out << "(echo \"" << s << "\")" << std::endl;
+ out << "(echo " << cvc5::quoteString(output) << ')' << std::endl;
}
/*
@@ -1930,14 +1922,9 @@ static void toStream(std::ostream& out, const CommandUnsupported* s, Variant v)
#endif /* CVC5_COMPETITION_MODE */
}
-static void errorToStream(std::ostream& out, std::string message, Variant v) {
- // escape all double-quotes
- size_t pos = 0;
- while((pos = message.find('"', pos)) != string::npos) {
- message.replace(pos, 1, "\"\"");
- pos += 2;
- }
- out << "(error \"" << message << "\")" << endl;
+static void errorToStream(std::ostream& out, std::string message, Variant v)
+{
+ out << "(error " << cvc5::quoteString(message) << ')' << endl;
}
static void toStream(std::ostream& out, const CommandFailure* s, Variant v) {
diff --git a/src/proof/alethe/alethe_post_processor.cpp b/src/proof/alethe/alethe_post_processor.cpp
index 6312f3140..ef7735b44 100644
--- a/src/proof/alethe/alethe_post_processor.cpp
+++ b/src/proof/alethe/alethe_post_processor.cpp
@@ -55,6 +55,79 @@ bool AletheProofPostprocessCallback::update(Node res,
switch (id)
{
+ // To keep the original shape of the proof node it is necessary to rederive
+ // the original conclusion. However, the term that should be printed might
+ // be different from that conclusion. Thus, it is stored as an additional
+ // argument in the proof node. Usually, the only difference is an additional
+ // cl operator or the outmost or operator being replaced by a cl operator.
+ //
+ // When steps are added to the proof that have not been there previously,
+ // it is unwise to add them in the same manner. To illustrate this the
+ // following counterexample shows the pitfalls of this approach:
+ //
+ // (or a (or b c)) (not a)
+ // --------------------------- RESOLUTION
+ // (or b c)
+ //
+ // is converted into an Alethe proof that should be printed as
+ //
+ // (cl a (or b c)) (cl (not a))
+ // -------------------------------- RESOLUTION
+ // (cl (or b c))
+ // --------------- OR
+ // (cl b c)
+ //
+ // Here, (cl (or b c)) and (cl b c) cannot correspond to the same proof node
+ // (or b c). Thus, we build a new proof node using the kind SEXPR
+ // that is then printed as (cl (or b c)). We denote this wrapping of a proof
+ // node by using an extra pair of parenthesis, i.e. ((or b c)) is the proof
+ // node printed as (cl (or b c)).
+ //
+ //
+ // Some proof rules have a close correspondence in Alethe. There are two
+ // very frequent patterns that, to avoid repetition, are described here and
+ // referred to in the comments on the specific proof rules below.
+ //
+ // The first pattern, which will be called singleton pattern in the
+ // following, adds the original proof node F with the corresponding rule R'
+ // of the Alethe calculus and uses the same premises as the original proof
+ // node (P1:F1) ... (Pn:Fn). However, the conclusion is printed as (cl F).
+ //
+ // This means a cvc5 rule R that looks as follows:
+ //
+ // (P1:F1) ... (Pn:Fn)
+ // --------------------- R
+ // F
+ //
+ // is transformed into:
+ //
+ // (P1:F1) ... (Pn:Fn)
+ // --------------------- R'
+ // (cl F)*
+ //
+ // * the corresponding proof node is F
+ //
+ // The second pattern, which will be called clause pattern in the following,
+ // has a disjunction (or G1 ... Gn) as conclusion. It also adds the orignal
+ // proof node (or G1 ... Gn) with the corresponding rule R' of the Alethe
+ // calculus and uses the same premises as the original proof node (P1:F1)
+ // ... (Pn:Fn). However, the conclusion is printed as (cl G1 ... Gn), i.e.
+ // the or is replaced by the cl operator.
+ //
+ // This means a cvc5 rule R that looks as follows:
+ //
+ // (P1:F1) ... (Pn:Fn)
+ // --------------------- R
+ // (or G1 ... Gn)
+ //
+ // Is transformed into:
+ //
+ // (P1:F1) ... (Pn:Fn)
+ // --------------------- R'
+ // (cl G1 ... Gn)*
+ //
+ // * the corresponding proof node is (or G1 ... Gn)
+ //
//================================================= Core rules
//======================== Assume and Scope
case PfRule::ASSUME:
diff --git a/src/proof/proof_node_manager.cpp b/src/proof/proof_node_manager.cpp
index a3ef944e0..8b4b332a1 100644
--- a/src/proof/proof_node_manager.cpp
+++ b/src/proof/proof_node_manager.cpp
@@ -28,7 +28,8 @@ using namespace cvc5::kind;
namespace cvc5 {
-ProofNodeManager::ProofNodeManager(ProofChecker* pc) : d_checker(pc)
+ProofNodeManager::ProofNodeManager(theory::Rewriter* rr, ProofChecker* pc)
+ : d_rewriter(rr), d_checker(pc)
{
d_true = NodeManager::currentNM()->mkConst(true);
// we always allocate a proof checker, regardless of the proof checking mode
@@ -160,14 +161,14 @@ std::shared_ptr<ProofNode> ProofNodeManager::mkScope(
computedAcr = true;
for (const Node& acc : ac)
{
- Node accr = theory::Rewriter::rewrite(acc);
+ Node accr = d_rewriter->rewrite(acc);
if (accr != acc)
{
acr[accr] = acc;
}
}
}
- Node ar = theory::Rewriter::rewrite(a);
+ Node ar = d_rewriter->rewrite(a);
std::unordered_map<Node, Node>::iterator itr = acr.find(ar);
if (itr != acr.end())
{
diff --git a/src/proof/proof_node_manager.h b/src/proof/proof_node_manager.h
index 928aabb76..533f6d173 100644
--- a/src/proof/proof_node_manager.h
+++ b/src/proof/proof_node_manager.h
@@ -28,6 +28,10 @@ namespace cvc5 {
class ProofChecker;
class ProofNode;
+namespace theory {
+class Rewriter;
+}
+
/**
* A manager for proof node objects. This is a trusted interface for creating
* and updating ProofNode objects.
@@ -54,7 +58,7 @@ class ProofNode;
class ProofNodeManager
{
public:
- ProofNodeManager(ProofChecker* pc = nullptr);
+ ProofNodeManager(theory::Rewriter* rr, ProofChecker* pc = nullptr);
~ProofNodeManager() {}
/**
* This constructs a ProofNode with the given arguments. The expected
@@ -184,6 +188,8 @@ class ProofNodeManager
static ProofNode* cancelDoubleSymm(ProofNode* pn);
private:
+ /** The rewriter */
+ theory::Rewriter* d_rewriter;
/** The (optional) proof checker */
ProofChecker* d_checker;
/** the true node */
diff --git a/src/smt/check_models.cpp b/src/smt/check_models.cpp
index f148d1018..5d16c12ce 100644
--- a/src/smt/check_models.cpp
+++ b/src/smt/check_models.cpp
@@ -31,8 +31,7 @@ using namespace cvc5::theory;
namespace cvc5 {
namespace smt {
-CheckModels::CheckModels(Env& e) : d_env(e) {}
-CheckModels::~CheckModels() {}
+CheckModels::CheckModels(Env& e) : EnvObj(e) {}
void CheckModels::checkModel(TheoryModel* m,
const context::CDList<Node>& al,
@@ -50,6 +49,12 @@ void CheckModels::checkModel(TheoryModel* m,
throw RecoverableModalException(
"Cannot run check-model on a model with approximate values.");
}
+ Node sepHeap, sepNeq;
+ if (m->getHeapModel(sepHeap, sepNeq))
+ {
+ throw RecoverableModalException(
+ "Cannot run check-model on a model with a separation logic heap.");
+ }
theory::SubstitutionMap& sm = d_env.getTopLevelSubstitutions().get();
Trace("check-model") << "checkModel: Check assertions..." << std::endl;
@@ -71,7 +76,7 @@ void CheckModels::checkModel(TheoryModel* m,
Notice() << "SolverEngine::checkModel(): -- substitutes to " << n
<< std::endl;
- n = Rewriter::rewrite(n);
+ n = rewrite(n);
Notice() << "SolverEngine::checkModel(): -- rewrites to " << n << std::endl;
// We look up the value before simplifying. If n contains quantifiers,
diff --git a/src/smt/check_models.h b/src/smt/check_models.h
index d785b53d5..2b3447010 100644
--- a/src/smt/check_models.h
+++ b/src/smt/check_models.h
@@ -20,11 +20,10 @@
#include "context/cdlist.h"
#include "expr/node.h"
+#include "smt/env_obj.h"
namespace cvc5 {
-class Env;
-
namespace theory {
class TheoryModel;
}
@@ -34,11 +33,10 @@ namespace smt {
/**
* This utility is responsible for checking the current model.
*/
-class CheckModels
+class CheckModels : protected EnvObj
{
public:
CheckModels(Env& e);
- ~CheckModels();
/**
* Check model m against the current set of input assertions al.
*
@@ -48,10 +46,6 @@ class CheckModels
void checkModel(theory::TheoryModel* m,
const context::CDList<Node>& al,
bool hardFailure);
-
- private:
- /** Reference to the environment */
- Env& d_env;
};
} // namespace smt
diff --git a/src/smt/command.cpp b/src/smt/command.cpp
index 311d6713a..e8ee6d59c 100644
--- a/src/smt/command.cpp
+++ b/src/smt/command.cpp
@@ -38,6 +38,7 @@
#include "proof/unsat_core.h"
#include "smt/dump.h"
#include "smt/model.h"
+#include "util/smt2_quote_string.h"
#include "util/unsafe_interrupt_exception.h"
#include "util/utility.h"
@@ -302,19 +303,10 @@ void EmptyCommand::toStream(std::ostream& out,
/* class EchoCommand */
/* -------------------------------------------------------------------------- */
-EchoCommand::EchoCommand(std::string output)
-{
- // escape all double-quotes
- size_t pos = 0;
- while ((pos = output.find('"', pos)) != string::npos)
- {
- output.replace(pos, 1, "\"\"");
- pos += 2;
- }
- d_output = '"' + output + '"';
-}
+EchoCommand::EchoCommand(std::string output) : d_output(output) {}
std::string EchoCommand::getOutput() const { return d_output; }
+
void EchoCommand::invoke(api::Solver* solver, SymbolManager* sm)
{
/* we don't have an output stream here, nothing to do */
@@ -325,7 +317,7 @@ void EchoCommand::invoke(api::Solver* solver,
SymbolManager* sm,
std::ostream& out)
{
- out << d_output << std::endl;
+ out << cvc5::quoteString(d_output) << std::endl;
Trace("dtview::command") << "* ~COMMAND: echo |" << d_output << "|~"
<< std::endl;
d_commandStatus = CommandSuccess::instance();
@@ -335,6 +327,7 @@ void EchoCommand::invoke(api::Solver* solver,
}
Command* EchoCommand::clone() const { return new EchoCommand(d_output); }
+
std::string EchoCommand::getCommandName() const { return "echo"; }
void EchoCommand::toStream(std::ostream& out,
diff --git a/src/smt/model_blocker.cpp b/src/smt/model_blocker.cpp
index cbc388331..e8c1ff07f 100644
--- a/src/smt/model_blocker.cpp
+++ b/src/smt/model_blocker.cpp
@@ -25,6 +25,8 @@ using namespace cvc5::kind;
namespace cvc5 {
+ModelBlocker::ModelBlocker(Env& e) : EnvObj(e) {}
+
Node ModelBlocker::getModelBlocker(const std::vector<Node>& assertions,
theory::TheoryModel* m,
options::BlockModelsMode mode,
diff --git a/src/smt/model_blocker.h b/src/smt/model_blocker.h
index 42219e220..5e41de6a3 100644
--- a/src/smt/model_blocker.h
+++ b/src/smt/model_blocker.h
@@ -22,6 +22,7 @@
#include "expr/node.h"
#include "options/smt_options.h"
+#include "smt/env_obj.h"
namespace cvc5 {
@@ -32,9 +33,10 @@ class TheoryModel;
/**
* A utility for blocking the current model.
*/
-class ModelBlocker
+class ModelBlocker : protected EnvObj
{
public:
+ ModelBlocker(Env& e);
/** get model blocker
*
* This returns a disjunction of literals ~L1 V ... V ~Ln with the following
@@ -63,7 +65,7 @@ class ModelBlocker
* our input. In other words, we do not return ~(x < 0) V ~(w < 0) since the
* left disjunct is always false.
*/
- static Node getModelBlocker(
+ Node getModelBlocker(
const std::vector<Node>& assertions,
theory::TheoryModel* m,
options::BlockModelsMode mode,
diff --git a/src/smt/preprocessor.cpp b/src/smt/preprocessor.cpp
index 4b16b9391..3aed58b30 100644
--- a/src/smt/preprocessor.cpp
+++ b/src/smt/preprocessor.cpp
@@ -150,7 +150,7 @@ Node Preprocessor::simplify(const Node& node)
d_env.getPrinter().toStreamCmdSimplify(d_env.getDumpOut(), node);
}
Node ret = expandDefinitions(node);
- ret = theory::Rewriter::rewrite(ret);
+ ret = rewrite(ret);
return ret;
}
diff --git a/src/smt/proof_manager.cpp b/src/smt/proof_manager.cpp
index 2e08ab2df..4b4291075 100644
--- a/src/smt/proof_manager.cpp
+++ b/src/smt/proof_manager.cpp
@@ -41,7 +41,7 @@ PfManager::PfManager(Env& env)
d_pchecker(new ProofChecker(
options().proof.proofCheck == options::ProofCheckMode::EAGER,
options().proof.proofPedantic)),
- d_pnm(new ProofNodeManager(d_pchecker.get())),
+ d_pnm(new ProofNodeManager(env.getRewriter(), d_pchecker.get())),
d_pppg(new PreprocessProofGenerator(
d_pnm.get(), env.getUserContext(), "smt::PreprocessProofGenerator")),
d_pfpp(nullptr),
diff --git a/src/smt/proof_post_processor.cpp b/src/smt/proof_post_processor.cpp
index adc15ca2c..04a36c1c0 100644
--- a/src/smt/proof_post_processor.cpp
+++ b/src/smt/proof_post_processor.cpp
@@ -42,7 +42,7 @@ ProofPostprocessCallback::ProofPostprocessCallback(Env& env,
: d_env(env),
d_pnm(env.getProofNodeManager()),
d_pppg(pppg),
- d_wfpm(env.getProofNodeManager()),
+ d_wfpm(env),
d_updateScopedAssumptions(updateScopedAssumptions)
{
d_true = NodeManager::currentNM()->mkConst(true);
@@ -654,7 +654,7 @@ Node ProofPostprocessCallback::expandMacros(PfRule id,
// apply transitivity if necessary
Node eq = addProofForTrans(tchildren, cdp);
- cdp->addStep(args[0], PfRule::EQ_RESOLVE, {children[0], eq}, {});
+ cdp->addStep(eq[1], PfRule::EQ_RESOLVE, {children[0], eq}, {});
return args[0];
}
else if (id == PfRule::MACRO_RESOLUTION
diff --git a/src/smt/solver_engine.cpp b/src/smt/solver_engine.cpp
index 911e0a960..fc6ec3915 100644
--- a/src/smt/solver_engine.cpp
+++ b/src/smt/solver_engine.cpp
@@ -417,10 +417,10 @@ void SolverEngine::setInfo(const std::string& key, const std::string& value)
bool SolverEngine::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" || key == "time")
+ if (key == "all-statistics" || key == "error-behavior" || key == "filename"
+ || key == "name" || key == "version" || key == "authors"
+ || key == "status" || key == "time" || key == "reason-unknown"
+ || key == "assertion-stack-levels" || key == "all-options")
{
return true;
}
@@ -455,7 +455,7 @@ std::string SolverEngine::getInfo(const std::string& key) const
}
if (key == "authors")
{
- return toSExpr(Configuration::about());
+ return toSExpr("the " + Configuration::getName() + " authors");
}
if (key == "status")
{
@@ -1081,7 +1081,7 @@ Node SolverEngine::getValue(const Node& ex) const
// AJR : necessary?
if (!n.getType().isFunction())
{
- n = Rewriter::rewrite(n);
+ n = d_env->getRewriter()->rewrite(n);
}
Trace("smt") << "--- getting value of " << n << endl;
@@ -1224,7 +1224,8 @@ Result SolverEngine::blockModel()
// get expanded assertions
std::vector<Node> eassertsProc = getExpandedAssertions();
- Node eblocker = ModelBlocker::getModelBlocker(
+ ModelBlocker mb(*d_env.get());
+ Node eblocker = mb.getModelBlocker(
eassertsProc, m, d_env->getOptions().smt.blockModelsMode);
Trace("smt") << "Block formula: " << eblocker << std::endl;
return assertFormula(eblocker);
@@ -1247,7 +1248,8 @@ Result SolverEngine::blockModelValues(const std::vector<Node>& exprs)
// get expanded assertions
std::vector<Node> eassertsProc = getExpandedAssertions();
// we always do block model values mode here
- Node eblocker = ModelBlocker::getModelBlocker(
+ ModelBlocker mb(*d_env.get());
+ Node eblocker = mb.getModelBlocker(
eassertsProc, m, options::BlockModelsMode::VALUES, exprs);
return assertFormula(eblocker);
}
diff --git a/src/smt/sygus_solver.cpp b/src/smt/sygus_solver.cpp
index 2a1d4e6c6..5db9804c6 100644
--- a/src/smt/sygus_solver.cpp
+++ b/src/smt/sygus_solver.cpp
@@ -377,7 +377,7 @@ void SygusSolver::checkSynthSolution(Assertions& as)
// problem are rewritten to true. If this is not the case, then the
// assertions module of the subsolver will complain about assertions
// with free variables.
- Node ar = theory::Rewriter::rewrite(a);
+ Node ar = rewrite(a);
solChecker->assertFormula(ar);
}
Result r = solChecker->checkSat();
diff --git a/src/smt/witness_form.cpp b/src/smt/witness_form.cpp
index 8e998b9cf..16d297495 100644
--- a/src/smt/witness_form.cpp
+++ b/src/smt/witness_form.cpp
@@ -16,21 +16,27 @@
#include "smt/witness_form.h"
#include "expr/skolem_manager.h"
+#include "smt/env.h"
#include "theory/rewriter.h"
namespace cvc5 {
namespace smt {
-WitnessFormGenerator::WitnessFormGenerator(ProofNodeManager* pnm)
- : d_tcpg(pnm,
+WitnessFormGenerator::WitnessFormGenerator(Env& env)
+ : d_rewriter(env.getRewriter()),
+ d_tcpg(env.getProofNodeManager(),
nullptr,
TConvPolicy::FIXPOINT,
TConvCachePolicy::NEVER,
"WfGenerator::TConvProofGenerator",
nullptr,
true),
- d_wintroPf(pnm, nullptr, nullptr, "WfGenerator::LazyCDProof"),
- d_pskPf(pnm, nullptr, "WfGenerator::PurifySkolemProof")
+ d_wintroPf(env.getProofNodeManager(),
+ nullptr,
+ nullptr,
+ "WfGenerator::LazyCDProof"),
+ d_pskPf(
+ env.getProofNodeManager(), nullptr, "WfGenerator::PurifySkolemProof")
{
}
@@ -114,12 +120,12 @@ Node WitnessFormGenerator::convertToWitnessForm(Node t)
bool WitnessFormGenerator::requiresWitnessFormTransform(Node t, Node s) const
{
- return theory::Rewriter::rewrite(t) != theory::Rewriter::rewrite(s);
+ return d_rewriter->rewrite(t) != d_rewriter->rewrite(s);
}
bool WitnessFormGenerator::requiresWitnessFormIntro(Node t) const
{
- Node tr = theory::Rewriter::rewrite(t);
+ Node tr = d_rewriter->rewrite(t);
return !tr.isConst() || !tr.getConst<bool>();
}
diff --git a/src/smt/witness_form.h b/src/smt/witness_form.h
index 8522d41f0..06d60c1ed 100644
--- a/src/smt/witness_form.h
+++ b/src/smt/witness_form.h
@@ -25,6 +25,13 @@
#include "proof/proof_generator.h"
namespace cvc5 {
+
+class Env;
+
+namespace theory {
+class Rewriter;
+}
+
namespace smt {
/**
@@ -37,7 +44,7 @@ namespace smt {
class WitnessFormGenerator : public ProofGenerator
{
public:
- WitnessFormGenerator(ProofNodeManager* pnm);
+ WitnessFormGenerator(Env& env);
~WitnessFormGenerator() {}
/**
* Get proof for, which expects an equality of the form t = toWitness(t).
@@ -85,6 +92,8 @@ class WitnessFormGenerator : public ProofGenerator
* Return a proof generator that can prove the given axiom exists.
*/
ProofGenerator* convertExistsInternal(Node exists);
+ /** The rewriter we are using */
+ theory::Rewriter* d_rewriter;
/** The term conversion proof generator */
TConvProofGenerator d_tcpg;
/** The nodes we have already added rewrite steps for in d_tcpg */
diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp
index 76a0c363d..cf2373b9f 100644
--- a/src/theory/arith/theory_arith.cpp
+++ b/src/theory/arith/theory_arith.cpp
@@ -193,9 +193,9 @@ void TheoryArith::postCheck(Effort level)
if (Theory::fullEffort(level))
{
d_arithModelCache.clear();
+ std::set<Node> termSet;
if (d_nonlinearExtension != nullptr)
{
- std::set<Node> termSet;
updateModelCache(termSet);
d_nonlinearExtension->checkFullEffort(d_arithModelCache, termSet);
}
@@ -204,6 +204,15 @@ void TheoryArith::postCheck(Effort level)
// set incomplete
d_im.setIncomplete(IncompleteId::ARITH_NL_DISABLED);
}
+ // If we won't be doing a last call effort check (which implies that
+ // models will be computed), we must sanity check the integer model
+ // from the linear solver now. We also must update the model cache
+ // if we did not do so above.
+ if (d_nonlinearExtension == nullptr)
+ {
+ updateModelCache(termSet);
+ }
+ sanityCheckIntegerModel();
}
}
@@ -274,12 +283,6 @@ bool TheoryArith::collectModelValues(TheoryModel* m,
updateModelCache(termSet);
- if (sanityCheckIntegerModel())
- {
- // We added a lemma
- return false;
- }
-
// We are now ready to assert the model.
for (const std::pair<const Node, Node>& p : d_arithModelCache)
{
@@ -383,9 +386,9 @@ bool TheoryArith::sanityCheckIntegerModel()
Trace("arith-check") << p.first << " -> " << p.second << std::endl;
if (p.first.getType().isInteger() && !p.second.getType().isInteger())
{
- Assert(false) << "TheoryArithPrivate generated a bad model value for "
- "integer variable "
- << p.first << " : " << p.second;
+ Warning() << "TheoryArithPrivate generated a bad model value for "
+ "integer variable "
+ << p.first << " : " << p.second;
// must branch and bound
TrustNode lem =
d_bab.branchIntegerVariable(p.first, p.second.getConst<Rational>());
diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp
index 9e69c9f71..93eadde43 100644
--- a/src/theory/arrays/theory_arrays.cpp
+++ b/src/theory/arrays/theory_arrays.cpp
@@ -296,7 +296,19 @@ Node TheoryArrays::solveWrite(TNode term, bool solve1, bool solve2, bool ppCheck
TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems)
{
- // first, see if we need to expand definitions
+ // first, check for logic exceptions
+ Kind k = term.getKind();
+ if (!options().arrays.arraysExp)
+ {
+ if (k == kind::EQ_RANGE)
+ {
+ std::stringstream ss;
+ ss << "Term of kind " << k
+ << " not supported in default mode, try --arrays-exp";
+ throw LogicException(ss.str());
+ }
+ }
+ // see if we need to expand definitions
TrustNode texp = d_rewriter.expandDefinition(term);
if (!texp.isNull())
{
@@ -309,7 +321,8 @@ TrustNode TheoryArrays::ppRewrite(TNode term, std::vector<SkolemLemma>& lems)
d_ppEqualityEngine.addTerm(term);
NodeManager* nm = NodeManager::currentNM();
Node ret;
- switch (term.getKind()) {
+ switch (k)
+ {
case kind::SELECT: {
// select(store(a,i,v),j) = select(a,j)
// IF i != j
diff --git a/src/theory/arrays/theory_arrays_type_rules.cpp b/src/theory/arrays/theory_arrays_type_rules.cpp
index e1b4813ec..433768e5d 100644
--- a/src/theory/arrays/theory_arrays_type_rules.cpp
+++ b/src/theory/arrays/theory_arrays_type_rules.cpp
@@ -18,6 +18,7 @@
// for array-constant attributes
#include "expr/array_store_all.h"
#include "theory/arrays/theory_arrays_rewriter.h"
+#include "theory/builtin/theory_builtin_type_rules.h"
#include "theory/type_enumerator.h"
#include "util/cardinality.h"
@@ -249,7 +250,24 @@ bool ArraysProperties::isWellFounded(TypeNode type)
Node ArraysProperties::mkGroundTerm(TypeNode type)
{
- return *TypeEnumerator(type);
+ Assert(type.getKind() == kind::ARRAY_TYPE);
+ TypeNode elemType = type.getArrayConstituentType();
+ Node elem = elemType.mkGroundTerm();
+ if (elem.isConst())
+ {
+ return NodeManager::currentNM()->mkConst(ArrayStoreAll(type, elem));
+ }
+ // Note the distinction between mkGroundTerm and mkGroundValue. While
+ // an arbitrary value can be obtained by calling the type enumerator here,
+ // that is wrong for types that are not closed enumerable since it may
+ // return a term containing values that should not appear in e.g. assertions.
+ // For example, arrays whose element type is an uninterpreted sort will
+ // incorrectly introduce uninterpreted sort values if this is done.
+ // It is currently infeasible to construct an ArrayStoreAll with the element
+ // type's mkGroundTerm as an argument when that term is not constant.
+ // Thus, we must simply return a fresh Skolem here, using the same utility
+ // as that of uninterpreted sorts.
+ return builtin::SortProperties::mkGroundTerm(type);
}
TypeNode ArrayPartialSelectTypeRule::computeType(NodeManager* nodeManager,
diff --git a/src/theory/datatypes/datatypes_rewriter.cpp b/src/theory/datatypes/datatypes_rewriter.cpp
index c446504fd..fd957baaa 100644
--- a/src/theory/datatypes/datatypes_rewriter.cpp
+++ b/src/theory/datatypes/datatypes_rewriter.cpp
@@ -16,12 +16,12 @@
#include "theory/datatypes/datatypes_rewriter.h"
#include "expr/ascription_type.h"
+#include "expr/codatatype_bound_variable.h"
#include "expr/dtype.h"
#include "expr/dtype_cons.h"
#include "expr/node_algorithm.h"
#include "expr/skolem_manager.h"
#include "expr/sygus_datatype.h"
-#include "expr/uninterpreted_constant.h"
#include "options/datatypes_options.h"
#include "theory/datatypes/sygus_datatype_utils.h"
#include "theory/datatypes/theory_datatypes_utils.h"
@@ -729,7 +729,7 @@ Node DatatypesRewriter::collectRef(Node n,
else
{
// a loop
- const Integer& i = n.getConst<UninterpretedConstant>().getIndex();
+ const Integer& i = n.getConst<CodatatypeBoundVariable>().getIndex();
uint32_t index = i.toUnsignedInt();
if (index >= sk.size())
{
@@ -771,7 +771,7 @@ Node DatatypesRewriter::normalizeCodatatypeConstantEqc(
{
int debruijn = depth - it->second - 1;
return NodeManager::currentNM()->mkConst(
- UninterpretedConstant(n.getType(), debruijn));
+ CodatatypeBoundVariable(n.getType(), debruijn));
}
std::vector<Node> children;
bool childChanged = false;
@@ -798,10 +798,10 @@ Node DatatypesRewriter::replaceDebruijn(Node n,
TypeNode orig_tn,
unsigned depth)
{
- if (n.getKind() == kind::UNINTERPRETED_CONSTANT && n.getType() == orig_tn)
+ if (n.getKind() == kind::CODATATYPE_BOUND_VARIABLE && n.getType() == orig_tn)
{
unsigned index =
- n.getConst<UninterpretedConstant>().getIndex().toUnsignedInt();
+ n.getConst<CodatatypeBoundVariable>().getIndex().toUnsignedInt();
if (index == depth)
{
return orig;
diff --git a/src/theory/datatypes/kinds b/src/theory/datatypes/kinds
index 41d5ded76..cb3a78cf2 100644
--- a/src/theory/datatypes/kinds
+++ b/src/theory/datatypes/kinds
@@ -145,4 +145,13 @@ parameterized TUPLE_PROJECT TUPLE_PROJECT_OP 1 \
typerule TUPLE_PROJECT_OP "SimpleTypeRule<RBuiltinOperator>"
typerule TUPLE_PROJECT ::cvc5::theory::datatypes::TupleProjectTypeRule
+# For representing codatatype values
+constant CODATATYPE_BOUND_VARIABLE \
+ class \
+ CodatatypeBoundVariable \
+ ::cvc5::CodatatypeBoundVariableHashFunction \
+ "expr/codatatype_bound_variable.h" \
+ "the kind of expressions representing bound variables in codatatype constants, which are de Bruijn indexed variables; payload is an instance of the cvc5::CodatatypeBoundVariable class (used in models)"
+typerule CODATATYPE_BOUND_VARIABLE ::cvc5::theory::datatypes::CodatatypeBoundVariableTypeRule
+
endtheory
diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp
index c892ffc11..0cbeaa515 100644
--- a/src/theory/datatypes/theory_datatypes.cpp
+++ b/src/theory/datatypes/theory_datatypes.cpp
@@ -18,11 +18,11 @@
#include <sstream>
#include "base/check.h"
+#include "expr/codatatype_bound_variable.h"
#include "expr/dtype.h"
#include "expr/dtype_cons.h"
#include "expr/kind.h"
#include "expr/skolem_manager.h"
-#include "expr/uninterpreted_constant.h"
#include "options/datatypes_options.h"
#include "options/quantifiers_options.h"
#include "options/smt_options.h"
@@ -1290,10 +1290,10 @@ bool TheoryDatatypes::collectModelValues(TheoryModel* m,
Node TheoryDatatypes::getCodatatypesValue( Node n, std::map< Node, Node >& eqc_cons, std::map< Node, int >& vmap, int depth ){
std::map< Node, int >::iterator itv = vmap.find( n );
+ NodeManager* nm = NodeManager::currentNM();
if( itv!=vmap.end() ){
int debruijn = depth - 1 - itv->second;
- return NodeManager::currentNM()->mkConst(
- UninterpretedConstant(n.getType(), debruijn));
+ return nm->mkConst(CodatatypeBoundVariable(n.getType(), debruijn));
}else if( n.getType().isDatatype() ){
Node nc = eqc_cons[n];
if( !nc.isNull() ){
@@ -1308,7 +1308,7 @@ Node TheoryDatatypes::getCodatatypesValue( Node n, std::map< Node, Node >& eqc_c
children.push_back( rv );
}
vmap.erase( n );
- return NodeManager::currentNM()->mkNode( APPLY_CONSTRUCTOR, children );
+ return nm->mkNode(APPLY_CONSTRUCTOR, children);
}
}
return n;
diff --git a/src/theory/datatypes/theory_datatypes_type_rules.cpp b/src/theory/datatypes/theory_datatypes_type_rules.cpp
index 503eaf4df..86a11357b 100644
--- a/src/theory/datatypes/theory_datatypes_type_rules.cpp
+++ b/src/theory/datatypes/theory_datatypes_type_rules.cpp
@@ -18,6 +18,7 @@
#include <sstream>
#include "expr/ascription_type.h"
+#include "expr/codatatype_bound_variable.h"
#include "expr/dtype.h"
#include "expr/dtype_cons.h"
#include "expr/type_matcher.h"
@@ -597,6 +598,13 @@ TypeNode TupleProjectTypeRule::computeType(NodeManager* nm, TNode n, bool check)
return nm->mkTupleType(types);
}
+TypeNode CodatatypeBoundVariableTypeRule::computeType(NodeManager* nodeManager,
+ TNode n,
+ bool check)
+{
+ return n.getConst<CodatatypeBoundVariable>().getType();
+}
+
} // namespace datatypes
} // namespace theory
} // namespace cvc5
diff --git a/src/theory/datatypes/theory_datatypes_type_rules.h b/src/theory/datatypes/theory_datatypes_type_rules.h
index cf57a6c0d..7cf98aa74 100644
--- a/src/theory/datatypes/theory_datatypes_type_rules.h
+++ b/src/theory/datatypes/theory_datatypes_type_rules.h
@@ -99,6 +99,12 @@ class TupleProjectTypeRule
static TypeNode computeType(NodeManager* nm, TNode n, bool check);
};
+class CodatatypeBoundVariableTypeRule
+{
+ public:
+ static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check);
+};
+
} // namespace datatypes
} // namespace theory
} // namespace cvc5
diff --git a/src/theory/datatypes/type_enumerator.cpp b/src/theory/datatypes/type_enumerator.cpp
index 39b48ece9..6528f1052 100644
--- a/src/theory/datatypes/type_enumerator.cpp
+++ b/src/theory/datatypes/type_enumerator.cpp
@@ -16,6 +16,7 @@
#include "theory/datatypes/type_enumerator.h"
#include "expr/ascription_type.h"
+#include "expr/codatatype_bound_variable.h"
#include "expr/dtype_cons.h"
#include "theory/datatypes/datatypes_rewriter.h"
#include "theory/datatypes/theory_datatypes_utils.h"
@@ -108,8 +109,8 @@ Node DatatypesEnumerator::getTermEnum( TypeNode tn, unsigned i ){
{
if (d_child_enum)
{
- ret = NodeManager::currentNM()->mkConst(
- UninterpretedConstant(d_type, d_size_limit));
+ NodeManager* nm = NodeManager::currentNM();
+ ret = nm->mkConst(CodatatypeBoundVariable(d_type, d_size_limit));
}
else
{
diff --git a/src/theory/incomplete_id.cpp b/src/theory/incomplete_id.cpp
index a2a3baa0f..c763fb9a0 100644
--- a/src/theory/incomplete_id.cpp
+++ b/src/theory/incomplete_id.cpp
@@ -40,6 +40,8 @@ const char* toString(IncompleteId i)
case IncompleteId::STRINGS_LOOP_SKIP: return "STRINGS_LOOP_SKIP";
case IncompleteId::STRINGS_REGEXP_NO_SIMPLIFY:
return "STRINGS_REGEXP_NO_SIMPLIFY";
+ case IncompleteId::SEQ_FINITE_DYNAMIC_CARDINALITY:
+ return "SEQ_FINITE_DYNAMIC_CARDINALITY";
case IncompleteId::UF_HO_EXT_DISABLED: return "UF_HO_EXT_DISABLED";
case IncompleteId::UF_CARD_DISABLED: return "UF_CARD_DISABLED";
case IncompleteId::UF_CARD_MODE: return "UF_CARD_MODE";
diff --git a/src/theory/incomplete_id.h b/src/theory/incomplete_id.h
index 951c2a94f..aaa458d8e 100644
--- a/src/theory/incomplete_id.h
+++ b/src/theory/incomplete_id.h
@@ -52,6 +52,11 @@ enum class IncompleteId
STRINGS_LOOP_SKIP,
// we could not simplify a regular expression membership
STRINGS_REGEXP_NO_SIMPLIFY,
+ // incomplete due to sequence of a dynamic finite type (e.g. a type that
+ // we know is finite, but its exact cardinality is not fixed. For example,
+ // when finite model finding is enabled, uninterpreted sorts have a
+ // cardinality that depends on their interpretation in the current model).
+ SEQ_FINITE_DYNAMIC_CARDINALITY,
// HO extensionality axiom was disabled
UF_HO_EXT_DISABLED,
// UF+cardinality solver was disabled
diff --git a/src/theory/quantifiers/conjecture_generator.cpp b/src/theory/quantifiers/conjecture_generator.cpp
index b98088a71..d778a679e 100644
--- a/src/theory/quantifiers/conjecture_generator.cpp
+++ b/src/theory/quantifiers/conjecture_generator.cpp
@@ -245,6 +245,7 @@ Node ConjectureGenerator::getUniversalRepresentative(TNode n, bool add)
// now, do instantiation-based merging for each of these terms
Trace("thm-ee-debug") << "Merge equivalence classes based on instantiations of terms..." << std::endl;
//merge all pending equalities
+ EntailmentCheck* echeck = d_treg.getEntailmentCheck();
while( !d_upendingAdds.empty() ){
Trace("sg-pending") << "Add " << d_upendingAdds.size() << " pending terms..." << std::endl;
std::vector< Node > pending;
@@ -256,7 +257,7 @@ Node ConjectureGenerator::getUniversalRepresentative(TNode n, bool add)
Trace("thm-ee-add") << "UEE : Add universal term " << t << std::endl;
std::vector< Node > eq_terms;
//if occurs modulo equality at ground level, it is equivalent to representative of ground equality engine
- Node gt = getTermDatabase()->evaluateTerm(t);
+ Node gt = echeck->evaluateTerm(t);
if( !gt.isNull() && gt!=t ){
eq_terms.push_back( gt );
}
@@ -1362,7 +1363,8 @@ bool ConjectureGenerator::notifySubstitution( TNode glhs, std::map< TNode, TNode
}
Trace("sg-cconj-debug") << "Evaluate RHS : : " << rhs << std::endl;
//get the representative of rhs with substitution subs
- TNode grhs = getTermDatabase()->getEntailedTerm( rhs, subs, true );
+ EntailmentCheck* echeck = d_treg.getEntailmentCheck();
+ TNode grhs = echeck->getEntailedTerm(rhs, subs, true);
Trace("sg-cconj-debug") << "...done evaluating term, got : " << grhs << std::endl;
if( !grhs.isNull() ){
if( glhs!=grhs ){
diff --git a/src/theory/quantifiers/ematching/ho_trigger.cpp b/src/theory/quantifiers/ematching/ho_trigger.cpp
index a8ab760ce..d2b0b0542 100644
--- a/src/theory/quantifiers/ematching/ho_trigger.cpp
+++ b/src/theory/quantifiers/ematching/ho_trigger.cpp
@@ -238,6 +238,7 @@ bool HigherOrderTrigger::sendInstantiation(std::vector<Node>& m, InferenceId id)
d_lchildren.clear();
d_arg_to_arg_rep.clear();
d_arg_vector.clear();
+ EntailmentCheck* echeck = d_treg.getEntailmentCheck();
for (std::pair<const TNode, std::vector<Node> >& ha : ho_var_apps_subs)
{
TNode var = ha.first;
@@ -291,8 +292,7 @@ bool HigherOrderTrigger::sendInstantiation(std::vector<Node>& m, InferenceId id)
{
if (!d_qstate.areEqual(itf->second, args[k]))
{
- if (!d_treg.getTermDatabase()->isEntailed(
- itf->second.eqNode(args[k]), true))
+ if (!echeck->isEntailed(itf->second.eqNode(args[k]), true))
{
fixed_vals[k] = Node::null();
}
diff --git a/src/theory/quantifiers/ematching/inst_match_generator.cpp b/src/theory/quantifiers/ematching/inst_match_generator.cpp
index d8e3b7950..075299583 100644
--- a/src/theory/quantifiers/ematching/inst_match_generator.cpp
+++ b/src/theory/quantifiers/ematching/inst_match_generator.cpp
@@ -23,6 +23,7 @@
#include "theory/quantifiers/ematching/inst_match_generator_multi_linear.h"
#include "theory/quantifiers/ematching/inst_match_generator_simple.h"
#include "theory/quantifiers/ematching/pattern_term_selector.h"
+#include "theory/quantifiers/ematching/relational_match_generator.h"
#include "theory/quantifiers/ematching/var_match_generator.h"
#include "theory/quantifiers/instantiate.h"
#include "theory/quantifiers/quantifiers_state.h"
@@ -618,7 +619,7 @@ InstMatchGenerator* InstMatchGenerator::mkInstMatchGenerator(
InstMatchGenerator* init;
std::map< Node, InstMatchGenerator * >::iterator iti = pat_map_init.find( pats[pCounter] );
if( iti==pat_map_init.end() ){
- init = new InstMatchGenerator(tparent, pats[pCounter]);
+ init = getInstMatchGenerator(tparent, q, pats[pCounter]);
}else{
init = iti->second;
}
@@ -645,6 +646,7 @@ InstMatchGenerator* InstMatchGenerator::getInstMatchGenerator(Trigger* tparent,
Node q,
Node n)
{
+ // maybe variable match generator
if (n.getKind() != INST_CONSTANT)
{
Trace("var-trigger-debug")
@@ -672,6 +674,16 @@ InstMatchGenerator* InstMatchGenerator::getInstMatchGenerator(Trigger* tparent,
return vmg;
}
}
+ Trace("relational-trigger")
+ << "Is " << n << " a relational trigger?" << std::endl;
+ // relational triggers
+ bool hasPol, pol;
+ Node lit;
+ if (TriggerTermInfo::isUsableRelationTrigger(n, hasPol, pol, lit))
+ {
+ Trace("relational-trigger") << "...yes, for literal " << lit << std::endl;
+ return new RelationalMatchGenerator(tparent, lit, hasPol, pol);
+ }
return new InstMatchGenerator(tparent, n);
}
diff --git a/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp b/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp
index 30be83ecc..fdb0d0db3 100644
--- a/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp
+++ b/src/theory/quantifiers/ematching/inst_strategy_e_matching.cpp
@@ -339,20 +339,18 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){
TriggerDatabase::TR_GET_OLD,
d_num_trigger_vars[f]);
}
- if (tr == nullptr)
+ // if we generated a trigger above, add it
+ if (tr != nullptr)
{
- // did not generate a trigger
- continue;
- }
- addTrigger(tr, f);
- if (tr->isMultiTrigger())
- {
- // only add a single multi-trigger
- continue;
+ addTrigger(tr, f);
+ if (tr->isMultiTrigger())
+ {
+ // only add a single multi-trigger
+ continue;
+ }
}
// if we are generating additional triggers...
- size_t index = 0;
- if (index < patTerms.size())
+ if (patTerms.size() > 1)
{
// check if similar patterns exist, and if so, add them additionally
unsigned nqfs_curr = 0;
@@ -361,7 +359,7 @@ void InstStrategyAutoGenTriggers::generateTriggers( Node f ){
nqfs_curr =
d_quant_rel->getNumQuantifiersForSymbol(patTerms[0].getOperator());
}
- index++;
+ size_t index = 1;
bool success = true;
while (success && index < patTerms.size()
&& d_is_single_trigger[patTerms[index]])
@@ -527,16 +525,22 @@ bool InstStrategyAutoGenTriggers::generatePatternTerms(Node f)
Trace("auto-gen-trigger-debug")
<< "...required polarity for " << pat << " is " << rpol
<< ", eq=" << rpoleq << std::endl;
+ // Currently, we have ad-hoc treatment for relational triggers that
+ // are not handled by RelationalMatchGen.
+ bool isAdHocRelationalTrigger =
+ TriggerTermInfo::isRelationalTrigger(pat)
+ && !TriggerTermInfo::isUsableRelationTrigger(pat);
if (rpol != 0)
{
Assert(rpol == 1 || rpol == -1);
- if (TriggerTermInfo::isRelationalTrigger(pat))
+ if (isAdHocRelationalTrigger)
{
pat = rpol == -1 ? pat.negate() : pat;
}
else
{
- Assert(TriggerTermInfo::isAtomicTrigger(pat));
+ Assert(TriggerTermInfo::isAtomicTrigger(pat)
+ || TriggerTermInfo::isUsableRelationTrigger(pat));
if (pat.getType().isBoolean() && rpoleq.isNull())
{
if (options().quantifiers.literalMatchMode
@@ -575,13 +579,10 @@ bool InstStrategyAutoGenTriggers::generatePatternTerms(Node f)
}
Trace("auto-gen-trigger-debug") << "...got : " << pat << std::endl;
}
- else
+ else if (isAdHocRelationalTrigger)
{
- if (TriggerTermInfo::isRelationalTrigger(pat))
- {
- // consider both polarities
- addPatternToPool(f, pat.negate(), num_fv, mpat);
- }
+ // consider both polarities
+ addPatternToPool(f, pat.negate(), num_fv, mpat);
}
addPatternToPool(f, pat, num_fv, mpat);
}
diff --git a/src/theory/quantifiers/ematching/relational_match_generator.cpp b/src/theory/quantifiers/ematching/relational_match_generator.cpp
new file mode 100644
index 000000000..8981a7a2d
--- /dev/null
+++ b/src/theory/quantifiers/ematching/relational_match_generator.cpp
@@ -0,0 +1,125 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Relational match generator class.
+ */
+
+#include "theory/quantifiers/ematching/relational_match_generator.h"
+
+#include "theory/quantifiers/term_util.h"
+#include "util/rational.h"
+
+using namespace cvc5::kind;
+
+namespace cvc5 {
+namespace theory {
+namespace quantifiers {
+namespace inst {
+
+RelationalMatchGenerator::RelationalMatchGenerator(Trigger* tparent,
+ Node rtrigger,
+ bool hasPol,
+ bool pol)
+ : InstMatchGenerator(tparent, Node::null()),
+ d_vindex(-1),
+ d_hasPol(hasPol),
+ d_pol(pol),
+ d_counter(0)
+{
+ Assert((rtrigger.getKind() == EQUAL && rtrigger[0].getType().isReal())
+ || rtrigger.getKind() == GEQ);
+ Trace("relational-match-gen")
+ << "Relational trigger: " << rtrigger << ", hasPol/pol = " << hasPol
+ << "/" << pol << std::endl;
+ for (size_t i = 0; i < 2; i++)
+ {
+ if (rtrigger[i].getKind() == INST_CONSTANT)
+ {
+ d_var = rtrigger[i];
+ d_vindex = d_var.getAttribute(InstVarNumAttribute());
+ d_rhs = rtrigger[1 - i];
+ Assert(!quantifiers::TermUtil::hasInstConstAttr(d_rhs));
+ Kind k = rtrigger.getKind();
+ d_rel = (i == 0 ? k : (k == GEQ ? LEQ : k));
+ break;
+ }
+ }
+ Trace("relational-match-gen") << "...processed " << d_var << " (" << d_vindex
+ << ") " << d_rel << " " << d_rhs << std::endl;
+ AlwaysAssert(!d_var.isNull())
+ << "Failed to initialize RelationalMatchGenerator";
+}
+
+bool RelationalMatchGenerator::reset(Node eqc)
+{
+ d_counter = 0;
+ return true;
+}
+
+int RelationalMatchGenerator::getNextMatch(Node q, InstMatch& m)
+{
+ Trace("relational-match-gen") << "getNextMatch, rel match gen" << std::endl;
+ // try (up to) two different terms
+ Node s;
+ Node rhs = d_rhs;
+ bool rmPrev = m.get(d_vindex).isNull();
+ while (d_counter < 2)
+ {
+ bool checkPol = false;
+ if (d_counter == 0)
+ {
+ checkPol = d_pol;
+ }
+ else
+ {
+ Assert(d_counter == 1);
+ if (d_hasPol)
+ {
+ break;
+ }
+ // try the opposite polarity
+ checkPol = !d_pol;
+ }
+ NodeManager* nm = NodeManager::currentNM();
+ // falsify ( d_var <d_rel> d_rhs ) = checkPol
+ s = rhs;
+ if (!checkPol)
+ {
+ s = nm->mkNode(PLUS, s, nm->mkConst(Rational(d_rel == GEQ ? -1 : 1)));
+ }
+ d_counter++;
+ Trace("relational-match-gen")
+ << "...try set " << s << " for " << checkPol << std::endl;
+ if (m.set(d_qstate, d_vindex, s))
+ {
+ Trace("relational-match-gen") << "...success" << std::endl;
+ int ret = continueNextMatch(q, m, InferenceId::UNKNOWN);
+ if (ret > 0)
+ {
+ Trace("relational-match-gen") << "...returned " << ret << std::endl;
+ return ret;
+ }
+ Trace("relational-match-gen") << "...failed to gen inst" << std::endl;
+ // failed
+ if (rmPrev)
+ {
+ m.d_vals[d_vindex] = Node::null();
+ }
+ }
+ }
+ return -1;
+}
+
+} // namespace inst
+} // namespace quantifiers
+} // namespace theory
+} // namespace cvc5
diff --git a/src/theory/quantifiers/ematching/relational_match_generator.h b/src/theory/quantifiers/ematching/relational_match_generator.h
new file mode 100644
index 000000000..eead2876a
--- /dev/null
+++ b/src/theory/quantifiers/ematching/relational_match_generator.h
@@ -0,0 +1,92 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Relational match generator class.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__QUANTIFIERS__RELATIONAL_MATCH_GENERATOR_H
+#define CVC5__THEORY__QUANTIFIERS__RELATIONAL_MATCH_GENERATOR_H
+
+#include "expr/node.h"
+#include "theory/quantifiers/ematching/inst_match_generator.h"
+
+namespace cvc5 {
+namespace theory {
+namespace quantifiers {
+namespace inst {
+
+/**
+ * Match generator for relational triggers x ~ t where t is a ground term.
+ * This match generator tries a small fixed set of terms based on the kind of
+ * relation and the required polarity of the trigger in the quantified formula.
+ *
+ * For example, for quantified formula (forall ((x Int)) (=> (> x n) (P x))),
+ * we have that (> x n) is a relational trigger with required polarity "true".
+ * This generator will try the match `x -> n+1` only, where notice that n+1 is
+ * the canonical term chosen to satisfy x>n. Canonical terms for arithmetic
+ * relations (~ x n) are in set { n, n+1, n-1 }.
+ *
+ * If a relational trigger does not have a required polarity, then up to 2
+ * terms are tried, a term that satisfies the relation, and one that does not.
+ * If (>= x n) is a relational trigger with no polarity, then `x -> n` and
+ * `x -> n-1` will be generated.
+ *
+ * Currently this class handles only equality between real or integer valued
+ * terms, or inequalities (kind GEQ). It furthermore only considers ground terms
+ * t for the right hand side of relations.
+ */
+class RelationalMatchGenerator : public InstMatchGenerator
+{
+ public:
+ /**
+ * @param tparent The parent trigger that this match generator belongs to
+ * @param rtrigger The relational trigger
+ * @param hasPol Whether the trigger has an entailed polarity
+ * @param pol The entailed polarity of the relational trigger.
+ */
+ RelationalMatchGenerator(Trigger* tparent,
+ Node rtrigger,
+ bool hasPol,
+ bool pol);
+
+ /** Reset */
+ bool reset(Node eqc) override;
+ /** Get the next match. */
+ int getNextMatch(Node q, InstMatch& m) override;
+
+ private:
+ /** the variable */
+ Node d_var;
+ /** The index of the variable */
+ int64_t d_vindex;
+ /** the relation kind */
+ Kind d_rel;
+ /** the right hand side */
+ Node d_rhs;
+ /** whether we have a required polarity */
+ bool d_hasPol;
+ /** the required polarity, if it exists */
+ bool d_pol;
+ /**
+ * The current number of terms we have generated since the last call to reset
+ */
+ size_t d_counter;
+};
+
+} // namespace inst
+} // namespace quantifiers
+} // namespace theory
+} // namespace cvc5
+
+#endif
diff --git a/src/theory/quantifiers/ematching/trigger_term_info.cpp b/src/theory/quantifiers/ematching/trigger_term_info.cpp
index 600797f4e..f31ec088a 100644
--- a/src/theory/quantifiers/ematching/trigger_term_info.cpp
+++ b/src/theory/quantifiers/ematching/trigger_term_info.cpp
@@ -70,6 +70,46 @@ bool TriggerTermInfo::isRelationalTriggerKind(Kind k)
return k == EQUAL || k == GEQ;
}
+bool TriggerTermInfo::isUsableRelationTrigger(Node n)
+{
+ bool hasPol, pol;
+ Node lit;
+ return isUsableRelationTrigger(n, hasPol, pol, lit);
+}
+bool TriggerTermInfo::isUsableRelationTrigger(Node n,
+ bool& hasPol,
+ bool& pol,
+ Node& lit)
+{
+ // relational triggers (not) (= (~ x t) true|false), where ~ in { =, >= }.
+ hasPol = false;
+ pol = n.getKind() != NOT;
+ lit = pol ? n : n[0];
+ if (lit.getKind() == EQUAL && lit[1].getType().isBoolean()
+ && lit[1].isConst())
+ {
+ hasPol = true;
+ pol = lit[1].getConst<bool>() ? pol : !pol;
+ lit = lit[0];
+ }
+ // is it a relational trigger?
+ if ((lit.getKind() == EQUAL && lit[0].getType().isReal())
+ || lit.getKind() == GEQ)
+ {
+ // if one side of the relation is a variable and the other side is a ground
+ // term, we can treat this using the relational match generator
+ for (size_t i = 0; i < 2; i++)
+ {
+ if (lit[i].getKind() == INST_CONSTANT
+ && !quantifiers::TermUtil::hasInstConstAttr(lit[1 - i]))
+ {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
bool TriggerTermInfo::isSimpleTrigger(Node n)
{
Node t = n.getKind() == NOT ? n[0] : n;
@@ -105,7 +145,7 @@ int32_t TriggerTermInfo::getTriggerWeight(Node n)
{
return 0;
}
- if (isAtomicTrigger(n))
+ if (isAtomicTrigger(n) || isUsableRelationTrigger(n))
{
return 1;
}
diff --git a/src/theory/quantifiers/ematching/trigger_term_info.h b/src/theory/quantifiers/ematching/trigger_term_info.h
index 753d0850c..3816d0988 100644
--- a/src/theory/quantifiers/ematching/trigger_term_info.h
+++ b/src/theory/quantifiers/ematching/trigger_term_info.h
@@ -108,6 +108,19 @@ class TriggerTermInfo
static bool isRelationalTrigger(Node n);
/** Is k a relational trigger kind? */
static bool isRelationalTriggerKind(Kind k);
+ /**
+ * Is n a usable relational trigger, which is true if RelationalMatchGenerator
+ * can process n.
+ */
+ static bool isUsableRelationTrigger(Node n);
+ /**
+ * Same as above, but lit / hasPol / pol are updated to the required
+ * constructor arguments for RelationalMatchGenerator.
+ */
+ static bool isUsableRelationTrigger(Node n,
+ bool& hasPol,
+ bool& pol,
+ Node& lit);
/** is n a simple trigger (see inst_match_generator.h)? */
static bool isSimpleTrigger(Node n);
/** get trigger weight
@@ -116,7 +129,8 @@ class TriggerTermInfo
* trigger term n, where the smaller the value, the easier.
*
* Returns 0 for triggers that are APPLY_UF terms.
- * Returns 1 for other triggers whose kind is atomic.
+ * Returns 1 for other triggers whose kind is atomic, or are usable
+ * relational triggers.
* Returns 2 otherwise.
*/
static int32_t getTriggerWeight(Node n);
diff --git a/src/theory/quantifiers/entailment_check.cpp b/src/theory/quantifiers/entailment_check.cpp
new file mode 100644
index 000000000..543414a4e
--- /dev/null
+++ b/src/theory/quantifiers/entailment_check.cpp
@@ -0,0 +1,411 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Implementation of entailment check class.
+ */
+
+#include "theory/quantifiers/entailment_check.h"
+
+#include "theory/quantifiers/quantifiers_state.h"
+#include "theory/quantifiers/term_database.h"
+
+using namespace cvc5::kind;
+using namespace cvc5::context;
+
+namespace cvc5 {
+namespace theory {
+namespace quantifiers {
+
+EntailmentCheck::EntailmentCheck(Env& env, QuantifiersState& qs, TermDb& tdb)
+ : EnvObj(env), d_qstate(qs), d_tdb(tdb)
+{
+ d_true = NodeManager::currentNM()->mkConst(true);
+ d_false = NodeManager::currentNM()->mkConst(false);
+}
+
+EntailmentCheck::~EntailmentCheck() {}
+
+Node EntailmentCheck::evaluateTerm2(TNode n,
+ std::map<TNode, Node>& visited,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool useEntailmentTests,
+ bool reqHasTerm)
+{
+ std::map<TNode, Node>::iterator itv = visited.find(n);
+ if (itv != visited.end())
+ {
+ return itv->second;
+ }
+ Trace("term-db-eval") << "evaluate term : " << n << std::endl;
+ Node ret = n;
+ Kind k = n.getKind();
+ if (k == FORALL)
+ {
+ // do nothing
+ }
+ else if (k == BOUND_VARIABLE)
+ {
+ std::map<TNode, TNode>::iterator it = subs.find(n);
+ if (it != subs.end())
+ {
+ if (!subsRep)
+ {
+ Assert(d_qstate.hasTerm(it->second));
+ ret = d_qstate.getRepresentative(it->second);
+ }
+ else
+ {
+ ret = it->second;
+ }
+ }
+ }
+ else if (d_qstate.hasTerm(n))
+ {
+ Trace("term-db-eval") << "...exists in ee, return rep" << std::endl;
+ ret = d_qstate.getRepresentative(n);
+ reqHasTerm = false;
+ }
+ else if (n.hasOperator())
+ {
+ std::vector<TNode> args;
+ bool ret_set = false;
+ for (unsigned i = 0, nchild = n.getNumChildren(); i < nchild; i++)
+ {
+ TNode c = evaluateTerm2(
+ n[i], visited, subs, subsRep, useEntailmentTests, reqHasTerm);
+ if (c.isNull())
+ {
+ ret = Node::null();
+ ret_set = true;
+ break;
+ }
+ else if (c == d_true || c == d_false)
+ {
+ // short-circuiting
+ if ((k == AND && c == d_false) || (k == OR && c == d_true))
+ {
+ ret = c;
+ ret_set = true;
+ reqHasTerm = false;
+ break;
+ }
+ else if (k == ITE && i == 0)
+ {
+ ret = evaluateTerm2(n[c == d_true ? 1 : 2],
+ visited,
+ subs,
+ subsRep,
+ useEntailmentTests,
+ reqHasTerm);
+ ret_set = true;
+ reqHasTerm = false;
+ break;
+ }
+ }
+ Trace("term-db-eval") << " child " << i << " : " << c << std::endl;
+ args.push_back(c);
+ }
+ if (!ret_set)
+ {
+ // get the (indexed) operator of n, if it exists
+ TNode f = d_tdb.getMatchOperator(n);
+ // if it is an indexed term, return the congruent term
+ if (!f.isNull())
+ {
+ // if f is congruent to a term indexed by this class
+ TNode nn = d_tdb.getCongruentTerm(f, args);
+ Trace("term-db-eval") << " got congruent term " << nn
+ << " from DB for " << n << std::endl;
+ if (!nn.isNull())
+ {
+ ret = d_qstate.getRepresentative(nn);
+ Trace("term-db-eval") << "return rep" << std::endl;
+ ret_set = true;
+ reqHasTerm = false;
+ Assert(!ret.isNull());
+ }
+ }
+ if (!ret_set)
+ {
+ Trace("term-db-eval") << "return rewrite" << std::endl;
+ // a theory symbol or a new UF term
+ if (n.getMetaKind() == metakind::PARAMETERIZED)
+ {
+ args.insert(args.begin(), n.getOperator());
+ }
+ ret = NodeManager::currentNM()->mkNode(n.getKind(), args);
+ ret = rewrite(ret);
+ if (ret.getKind() == EQUAL)
+ {
+ if (d_qstate.areDisequal(ret[0], ret[1]))
+ {
+ ret = d_false;
+ }
+ }
+ if (useEntailmentTests)
+ {
+ if (ret.getKind() == EQUAL || ret.getKind() == GEQ)
+ {
+ Valuation& val = d_qstate.getValuation();
+ for (unsigned j = 0; j < 2; j++)
+ {
+ std::pair<bool, Node> et = val.entailmentCheck(
+ options::TheoryOfMode::THEORY_OF_TYPE_BASED,
+ j == 0 ? ret : ret.negate());
+ if (et.first)
+ {
+ ret = j == 0 ? d_true : d_false;
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ // must have the term
+ if (reqHasTerm && !ret.isNull())
+ {
+ Kind rk = ret.getKind();
+ if (rk != OR && rk != AND && rk != EQUAL && rk != ITE && rk != NOT
+ && rk != FORALL)
+ {
+ if (!d_qstate.hasTerm(ret))
+ {
+ ret = Node::null();
+ }
+ }
+ }
+ Trace("term-db-eval") << "evaluated term : " << n << ", got : " << ret
+ << ", reqHasTerm = " << reqHasTerm << std::endl;
+ visited[n] = ret;
+ return ret;
+}
+
+TNode EntailmentCheck::getEntailedTerm2(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep)
+{
+ Trace("term-db-entail") << "get entailed term : " << n << std::endl;
+ if (d_qstate.hasTerm(n))
+ {
+ Trace("term-db-entail") << "...exists in ee, return rep " << std::endl;
+ return n;
+ }
+ else if (n.getKind() == BOUND_VARIABLE)
+ {
+ std::map<TNode, TNode>::iterator it = subs.find(n);
+ if (it != subs.end())
+ {
+ Trace("term-db-entail")
+ << "...substitution is : " << it->second << std::endl;
+ if (subsRep)
+ {
+ Assert(d_qstate.hasTerm(it->second));
+ Assert(d_qstate.getRepresentative(it->second) == it->second);
+ return it->second;
+ }
+ return getEntailedTerm2(it->second, subs, subsRep);
+ }
+ }
+ else if (n.getKind() == ITE)
+ {
+ for (uint32_t i = 0; i < 2; i++)
+ {
+ if (isEntailed2(n[0], subs, subsRep, i == 0))
+ {
+ return getEntailedTerm2(n[i == 0 ? 1 : 2], subs, subsRep);
+ }
+ }
+ }
+ else
+ {
+ if (n.hasOperator())
+ {
+ TNode f = d_tdb.getMatchOperator(n);
+ if (!f.isNull())
+ {
+ std::vector<TNode> args;
+ for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++)
+ {
+ TNode c = getEntailedTerm2(n[i], subs, subsRep);
+ if (c.isNull())
+ {
+ return TNode::null();
+ }
+ c = d_qstate.getRepresentative(c);
+ Trace("term-db-entail") << " child " << i << " : " << c << std::endl;
+ args.push_back(c);
+ }
+ TNode nn = d_tdb.getCongruentTerm(f, args);
+ Trace("term-db-entail")
+ << " got congruent term " << nn << " for " << n << std::endl;
+ return nn;
+ }
+ }
+ }
+ return TNode::null();
+}
+
+Node EntailmentCheck::evaluateTerm(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool useEntailmentTests,
+ bool reqHasTerm)
+{
+ std::map<TNode, Node> visited;
+ return evaluateTerm2(
+ n, visited, subs, subsRep, useEntailmentTests, reqHasTerm);
+}
+
+Node EntailmentCheck::evaluateTerm(TNode n,
+ bool useEntailmentTests,
+ bool reqHasTerm)
+{
+ std::map<TNode, Node> visited;
+ std::map<TNode, TNode> subs;
+ return evaluateTerm2(n, visited, subs, false, useEntailmentTests, reqHasTerm);
+}
+
+TNode EntailmentCheck::getEntailedTerm(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep)
+{
+ return getEntailedTerm2(n, subs, subsRep);
+}
+
+TNode EntailmentCheck::getEntailedTerm(TNode n)
+{
+ std::map<TNode, TNode> subs;
+ return getEntailedTerm2(n, subs, false);
+}
+
+bool EntailmentCheck::isEntailed2(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool pol)
+{
+ Trace("term-db-entail") << "Check entailed : " << n << ", pol = " << pol
+ << std::endl;
+ Assert(n.getType().isBoolean());
+ if (n.getKind() == EQUAL && !n[0].getType().isBoolean())
+ {
+ TNode n1 = getEntailedTerm2(n[0], subs, subsRep);
+ if (!n1.isNull())
+ {
+ TNode n2 = getEntailedTerm2(n[1], subs, subsRep);
+ if (!n2.isNull())
+ {
+ if (n1 == n2)
+ {
+ return pol;
+ }
+ else
+ {
+ Assert(d_qstate.hasTerm(n1));
+ Assert(d_qstate.hasTerm(n2));
+ if (pol)
+ {
+ return d_qstate.areEqual(n1, n2);
+ }
+ else
+ {
+ return d_qstate.areDisequal(n1, n2);
+ }
+ }
+ }
+ }
+ }
+ else if (n.getKind() == NOT)
+ {
+ return isEntailed2(n[0], subs, subsRep, !pol);
+ }
+ else if (n.getKind() == OR || n.getKind() == AND)
+ {
+ bool simPol = (pol && n.getKind() == OR) || (!pol && n.getKind() == AND);
+ for (size_t i = 0, nchild = n.getNumChildren(); i < nchild; i++)
+ {
+ if (isEntailed2(n[i], subs, subsRep, pol))
+ {
+ if (simPol)
+ {
+ return true;
+ }
+ }
+ else
+ {
+ if (!simPol)
+ {
+ return false;
+ }
+ }
+ }
+ return !simPol;
+ // Boolean equality here
+ }
+ else if (n.getKind() == EQUAL || n.getKind() == ITE)
+ {
+ for (size_t i = 0; i < 2; i++)
+ {
+ if (isEntailed2(n[0], subs, subsRep, i == 0))
+ {
+ size_t ch = (n.getKind() == EQUAL || i == 0) ? 1 : 2;
+ bool reqPol = (n.getKind() == ITE || i == 0) ? pol : !pol;
+ return isEntailed2(n[ch], subs, subsRep, reqPol);
+ }
+ }
+ }
+ else if (n.getKind() == APPLY_UF)
+ {
+ TNode n1 = getEntailedTerm2(n, subs, subsRep);
+ if (!n1.isNull())
+ {
+ Assert(d_qstate.hasTerm(n1));
+ if (n1 == d_true)
+ {
+ return pol;
+ }
+ else if (n1 == d_false)
+ {
+ return !pol;
+ }
+ else
+ {
+ return d_qstate.getRepresentative(n1) == (pol ? d_true : d_false);
+ }
+ }
+ }
+ else if (n.getKind() == FORALL && !pol)
+ {
+ return isEntailed2(n[1], subs, subsRep, pol);
+ }
+ return false;
+}
+
+bool EntailmentCheck::isEntailed(TNode n, bool pol)
+{
+ std::map<TNode, TNode> subs;
+ return isEntailed2(n, subs, false, pol);
+}
+
+bool EntailmentCheck::isEntailed(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool pol)
+{
+ return isEntailed2(n, subs, subsRep, pol);
+}
+
+} // namespace quantifiers
+} // namespace theory
+} // namespace cvc5
diff --git a/src/theory/quantifiers/entailment_check.h b/src/theory/quantifiers/entailment_check.h
new file mode 100644
index 000000000..ecf74fe85
--- /dev/null
+++ b/src/theory/quantifiers/entailment_check.h
@@ -0,0 +1,156 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Entailment check class
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__QUANTIFIERS__ENTAILMENT_CHECK_H
+#define CVC5__THEORY__QUANTIFIERS__ENTAILMENT_CHECK_H
+
+#include <map>
+#include <vector>
+
+#include "expr/node.h"
+#include "smt/env_obj.h"
+
+namespace cvc5 {
+namespace theory {
+namespace quantifiers {
+
+class QuantifiersState;
+class TermDb;
+
+/**
+ * Entailment check utility, which determines whether formulas are entailed
+ * in the current context. The main focus of this class is on UF formulas.
+ * It works by looking at the term argument trie data structures in term
+ * database. For details, see e.g. Section 4.1 of Reynolds et al TACAS 2018.
+ */
+class EntailmentCheck : protected EnvObj
+{
+ public:
+ EntailmentCheck(Env& env, QuantifiersState& qs, TermDb& tdb);
+ ~EntailmentCheck();
+ /** evaluate term
+ *
+ * Returns a term n' such that n * subs = n' is entailed based on the current
+ * set of equalities, where ( n * subs ) is term n under the substitution
+ * subs.
+ *
+ * This function may generate new terms. In particular, we typically rewrite
+ * subterms of n of maximal size (in terms of the AST) to terms that exist
+ * in the equality engine.
+ *
+ * useEntailmentTests is whether to call the theory engine's entailmentTest
+ * on literals n for which this call fails to find a term n' that is
+ * equivalent to n, for increased precision. This is not frequently used.
+ *
+ * If reqHasTerm, then we require that the returned term is a Boolean
+ * combination of terms that exist in the equality engine used by this call.
+ * If no such term is constructable, this call returns null. The motivation
+ * for setting this to true is to "fail fast" if we require the return value
+ * of this function to only involve existing terms. This is used e.g. in
+ * the "propagating instances" portion of conflict-based instantiation
+ * (quant_conflict_find.h).
+ *
+ * @param n The term under consideration
+ * @param subs The substitution under consideration
+ * @param subsRep Whether the range of subs are representatives in the current
+ * equality engine
+ * @param useEntailmentTests Whether to use entailment tests to show
+ * n * subs is equivalent to true/false.
+ * @param reqHasTerm Whether we require the returned term to be a Boolean
+ * combination of terms known to the current equality engine
+ * @return the term n * subs evaluates to
+ */
+ Node evaluateTerm(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool useEntailmentTests = false,
+ bool reqHasTerm = false);
+ /** Same as above, without a substitution */
+ Node evaluateTerm(TNode n,
+ bool useEntailmentTests = false,
+ bool reqHasTerm = false);
+ /** get entailed term
+ *
+ * If possible, returns a term n' such that:
+ * (1) n' exists in the current equality engine (as specified by the state),
+ * (2) n = n' is entailed in the current context.
+ * It returns null if no such term can be found.
+ * Wrt evaluateTerm, this version does not construct new terms, and
+ * thus is less aggressive.
+ */
+ TNode getEntailedTerm(TNode n);
+ /** get entailed term
+ *
+ * If possible, returns a term n' such that:
+ * (1) n' exists in the current equality engine (as specified by the state),
+ * (2) n * subs = n' is entailed in the current context, where * denotes
+ * substitution application.
+ * It returns null if no such term can be found.
+ * subsRep is whether the substitution maps to terms that are representatives
+ * according to the quantifiers state.
+ * Wrt evaluateTerm, this version does not construct new terms, and
+ * thus is less aggressive.
+ */
+ TNode getEntailedTerm(TNode n, std::map<TNode, TNode>& subs, bool subsRep);
+ /** is entailed
+ * Checks whether the current context entails n with polarity pol, based on
+ * the equality information in the quantifiers state. Returns true if the
+ * entailment can be successfully shown.
+ */
+ bool isEntailed(TNode n, bool pol);
+ /** is entailed
+ *
+ * Checks whether the current context entails ( n * subs ) with polarity pol,
+ * based on the equality information in the quantifiers state,
+ * where * denotes substitution application.
+ * subsRep is whether the substitution maps to terms that are representatives
+ * according to in the quantifiers state.
+ */
+ bool isEntailed(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool pol);
+
+ protected:
+ /** helper for evaluate term */
+ Node evaluateTerm2(TNode n,
+ std::map<TNode, Node>& visited,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool useEntailmentTests,
+ bool reqHasTerm);
+ /** helper for get entailed term */
+ TNode getEntailedTerm2(TNode n, std::map<TNode, TNode>& subs, bool subsRep);
+ /** helper for is entailed */
+ bool isEntailed2(TNode n,
+ std::map<TNode, TNode>& subs,
+ bool subsRep,
+ bool pol);
+ /** The quantifiers state object */
+ QuantifiersState& d_qstate;
+ /** Reference to the term database */
+ TermDb& d_tdb;
+ /** boolean terms */
+ Node d_true;
+ Node d_false;
+}; /* class EntailmentCheck */
+
+} // namespace quantifiers
+} // namespace theory
+} // namespace cvc5
+
+#endif /* CVC5__THEORY__QUANTIFIERS__ENTAILMENT_CHECK_H */
diff --git a/src/theory/quantifiers/fmf/full_model_check.cpp b/src/theory/quantifiers/fmf/full_model_check.cpp
index 7c1d36ce8..94351274a 100644
--- a/src/theory/quantifiers/fmf/full_model_check.cpp
+++ b/src/theory/quantifiers/fmf/full_model_check.cpp
@@ -951,10 +951,15 @@ void FullModelChecker::doCheck(FirstOrderModelFmc * fm, Node f, Def & d, Node n
Node r = n;
if( !n.isConst() ){
TypeNode tn = n.getType();
- if( !fm->hasTerm(n) && tn.isFirstClass() ){
- r = getSomeDomainElement(fm, tn );
+ if (!fm->hasTerm(n) && tn.isFirstClass())
+ {
+ // if the term is unknown, we do not assume any value for it
+ r = Node::null();
+ }
+ else
+ {
+ r = fm->getRepresentative(r);
}
- r = fm->getRepresentative( r );
}
Trace("fmc-debug") << "Add constant entry..." << std::endl;
d.addEntry(fm, mkCondDefault(fm, f), r);
diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp
index f756fcfd1..be04f9404 100644
--- a/src/theory/quantifiers/instantiate.cpp
+++ b/src/theory/quantifiers/instantiate.cpp
@@ -25,6 +25,7 @@
#include "smt/logic_exception.h"
#include "smt/smt_statistics_registry.h"
#include "theory/quantifiers/cegqi/inst_strategy_cegqi.h"
+#include "theory/quantifiers/entailment_check.h"
#include "theory/quantifiers/first_order_model.h"
#include "theory/quantifiers/quantifiers_attributes.h"
#include "theory/quantifiers/quantifiers_preprocess.h"
@@ -183,7 +184,7 @@ bool Instantiate::addInstantiation(Node q,
#endif
}
- TermDb* tdb = d_treg.getTermDatabase();
+ EntailmentCheck* ec = d_treg.getEntailmentCheck();
// Note we check for entailment before checking for term vector duplication.
// Although checking for term vector duplication is a faster check, it is
// included automatically with recordInstantiationInternal, hence we prefer
@@ -206,7 +207,7 @@ bool Instantiate::addInstantiation(Node q,
{
subs[q[0][i]] = terms[i];
}
- if (tdb->isEntailed(q[1], subs, false, true))
+ if (ec->isEntailed(q[1], subs, false, true))
{
Trace("inst-add-debug") << " --> Currently entailed." << std::endl;
++(d_statistics.d_inst_duplicate_ent);
@@ -217,6 +218,7 @@ bool Instantiate::addInstantiation(Node q,
// check based on instantiation level
if (options::instMaxLevel() != -1)
{
+ TermDb* tdb = d_treg.getTermDatabase();
for (Node& t : terms)
{
if (!tdb->isTermEligibleForInstantiation(t, q))
@@ -409,7 +411,7 @@ bool Instantiate::addInstantiationExpFail(Node q,
// will never succeed with 1 variable
return false;
}
- TermDb* tdb = d_treg.getTermDatabase();
+ EntailmentCheck* echeck = d_treg.getEntailmentCheck();
Trace("inst-exp-fail") << "Explain inst failure..." << terms << std::endl;
// set up information for below
std::vector<Node>& vars = d_qreg.d_vars[q];
@@ -445,7 +447,7 @@ bool Instantiate::addInstantiationExpFail(Node q,
if (options::instNoEntail())
{
Trace("inst-exp-fail") << " check entailment" << std::endl;
- success = tdb->isEntailed(q[1], subs, false, true);
+ success = echeck->isEntailed(q[1], subs, false, true);
Trace("inst-exp-fail") << " entailed: " << success << std::endl;
}
// check whether the instantiation rewrites to the same thing
diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp
index 361adfd54..323398879 100644
--- a/src/theory/quantifiers/quant_conflict_find.cpp
+++ b/src/theory/quantifiers/quant_conflict_find.cpp
@@ -52,12 +52,6 @@ QuantInfo::~QuantInfo() {
d_var_mg.clear();
}
-QuantifiersInferenceManager& QuantInfo::getInferenceManager()
-{
- Assert(d_parent != nullptr);
- return d_parent->getInferenceManager();
-}
-
void QuantInfo::initialize( QuantConflictFind * p, Node q, Node qn ) {
d_parent = p;
d_q = q;
@@ -577,30 +571,33 @@ bool QuantInfo::isTConstraintSpurious(QuantConflictFind* p,
{
if( options::qcfEagerTest() ){
//check whether the instantiation evaluates as expected
+ EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck();
+ std::map<TNode, TNode> subs;
+ for (size_t i = 0, tsize = terms.size(); i < tsize; i++)
+ {
+ Trace("qcf-instance-check") << " " << terms[i] << std::endl;
+ subs[d_q[0][i]] = terms[i];
+ }
+ for (size_t i = 0, evsize = d_extra_var.size(); i < evsize; i++)
+ {
+ Node n = getCurrentExpValue(d_extra_var[i]);
+ Trace("qcf-instance-check")
+ << " " << d_extra_var[i] << " -> " << n << std::endl;
+ subs[d_extra_var[i]] = n;
+ }
if (p->atConflictEffort()) {
Trace("qcf-instance-check") << "Possible conflict instance for " << d_q << " : " << std::endl;
- std::map< TNode, TNode > subs;
- for( unsigned i=0; i<terms.size(); i++ ){
- Trace("qcf-instance-check") << " " << terms[i] << std::endl;
- subs[d_q[0][i]] = terms[i];
- }
- TermDb* tdb = p->getTermDatabase();
- for( unsigned i=0; i<d_extra_var.size(); i++ ){
- Node n = getCurrentExpValue( d_extra_var[i] );
- Trace("qcf-instance-check") << " " << d_extra_var[i] << " -> " << n << std::endl;
- subs[d_extra_var[i]] = n;
- }
- if (!tdb->isEntailed(d_q[1], subs, false, false))
+ if (!echeck->isEntailed(d_q[1], subs, false, false))
{
Trace("qcf-instance-check") << "...not entailed to be false." << std::endl;
return true;
}
}else{
- Node inst =
- getInferenceManager().getInstantiate()->getInstantiation(d_q, terms);
- inst = Rewriter::rewrite(inst);
- Node inst_eval = p->getTermDatabase()->evaluateTerm(
- inst, options::qcfTConstraint(), true);
+ // see if the body of the quantified formula evaluates to a Boolean
+ // combination of known terms under the current substitution. We use
+ // the helper method evaluateTerm from the entailment check utility.
+ Node inst_eval = echeck->evaluateTerm(
+ d_q[1], subs, false, options::qcfTConstraint(), true);
if( Trace.isOn("qcf-instance-check") ){
Trace("qcf-instance-check") << "Possible propagating instance for " << d_q << " : " << std::endl;
for( unsigned i=0; i<terms.size(); i++ ){
@@ -608,6 +605,10 @@ bool QuantInfo::isTConstraintSpurious(QuantConflictFind* p,
}
Trace("qcf-instance-check") << "...evaluates to " << inst_eval << std::endl;
}
+ // If it is the case that instantiation can be rewritten to a Boolean
+ // combination of terms that exist in the current context, then inst_eval
+ // is non-null. Moreover, we insist that inst_eval is not true, or else
+ // the instantiation is trivially entailed and hence is spurious.
if (inst_eval.isNull()
|| (inst_eval.isConst() && inst_eval.getConst<bool>()))
{
@@ -1219,11 +1220,11 @@ bool MatchGen::reset_round(QuantConflictFind* p)
// d_ground_eval[0] = p->d_false;
//}
// modified
- TermDb* tdb = p->getTermDatabase();
+ EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck();
QuantifiersState& qs = p->getState();
for (unsigned i = 0; i < 2; i++)
{
- if (tdb->isEntailed(d_n, i == 0))
+ if (echeck->isEntailed(d_n, i == 0))
{
d_ground_eval[0] = i==0 ? p->d_true : p->d_false;
}
@@ -1233,13 +1234,13 @@ bool MatchGen::reset_round(QuantConflictFind* p)
}
}
}else if( d_type==typ_eq ){
- TermDb* tdb = p->getTermDatabase();
+ EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck();
QuantifiersState& qs = p->getState();
for (unsigned i = 0, size = d_n.getNumChildren(); i < size; i++)
{
if (!expr::hasBoundVar(d_n[i]))
{
- TNode t = tdb->getEntailedTerm(d_n[i]);
+ TNode t = echeck->getEntailedTerm(d_n[i]);
if (qs.isInConflict())
{
return false;
@@ -1289,13 +1290,9 @@ void MatchGen::reset( QuantConflictFind * p, bool tgt, QuantInfo * qi ) {
TNode n = qi->getCurrentValue( d_n );
int vn = qi->getCurrentRepVar( qi->getVarNum( n ) );
if( vn==-1 ){
- //evaluate the value, see if it is compatible
- //int e = p->evaluate( n );
- //if( ( e==1 && d_tgt ) || ( e==0 && !d_tgt ) ){
- // d_child_counter = 0;
- //}
- //modified
- if( p->getTermDatabase()->isEntailed( n, d_tgt ) ){
+ EntailmentCheck* echeck = p->getTermRegistry().getEntailmentCheck();
+ if (echeck->isEntailed(n, d_tgt))
+ {
d_child_counter = 0;
}
}else{
@@ -2168,8 +2165,8 @@ void QuantConflictFind::checkQuantifiedFormula(Node q,
Node inst = qinst->getInstantiation(q, terms);
Debug("qcf-check-inst")
<< "Check instantiation " << inst << "..." << std::endl;
- Assert(!getTermDatabase()->isEntailed(inst, true));
- Assert(getTermDatabase()->isEntailed(inst, false)
+ Assert(!getTermRegistry().getEntailmentCheck()->isEntailed(inst, true));
+ Assert(getTermRegistry().getEntailmentCheck()->isEntailed(inst, false)
|| d_effort > EFFORT_CONFLICT);
}
// Process the lemma: either add an instantiation or specific lemmas
diff --git a/src/theory/quantifiers/quant_conflict_find.h b/src/theory/quantifiers/quant_conflict_find.h
index 927a74ff2..d14e281fb 100644
--- a/src/theory/quantifiers/quant_conflict_find.h
+++ b/src/theory/quantifiers/quant_conflict_find.h
@@ -132,8 +132,6 @@ public:
public:
QuantInfo();
~QuantInfo();
- /** Get quantifiers inference manager */
- QuantifiersInferenceManager& getInferenceManager();
std::vector< TNode > d_vars;
std::vector< TypeNode > d_var_types;
std::map< TNode, int > d_var_num;
diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp
index 7c7c7aade..52e90e26e 100644
--- a/src/theory/quantifiers/quantifiers_rewriter.cpp
+++ b/src/theory/quantifiers/quantifiers_rewriter.cpp
@@ -15,6 +15,7 @@
#include "theory/quantifiers/quantifiers_rewriter.h"
+#include "expr/ascription_type.h"
#include "expr/bound_var_manager.h"
#include "expr/dtype.h"
#include "expr/dtype_cons.h"
@@ -895,12 +896,25 @@ bool QuantifiersRewriter::getVarElimLit(Node body,
const DType& dt = datatypes::utils::datatypeOf(tester);
const DTypeConstructor& c = dt[index];
std::vector<Node> newChildren;
- newChildren.push_back(c.getConstructor());
+ Node cons = c.getConstructor();
+ TypeNode tspec;
+ // take into account if parametric
+ if (dt.isParametric())
+ {
+ tspec = c.getSpecializedConstructorType(lit[0].getType());
+ cons = nm->mkNode(
+ APPLY_TYPE_ASCRIPTION, nm->mkConst(AscriptionType(tspec)), cons);
+ }
+ else
+ {
+ tspec = cons.getType();
+ }
+ newChildren.push_back(cons);
std::vector<Node> newVars;
BoundVarManager* bvm = nm->getBoundVarManager();
- for (unsigned j = 0, nargs = c.getNumArgs(); j < nargs; j++)
+ for (size_t j = 0, nargs = c.getNumArgs(); j < nargs; j++)
{
- TypeNode tn = c[j].getRangeType();
+ TypeNode tn = tspec[j];
Node rn = nm->mkConst(Rational(j));
Node cacheVal = BoundVarManager::getCacheValue(body, lit, rn);
Node v = bvm->mkBoundVar<QRewDtExpandAttribute>(cacheVal, tn);
diff --git a/src/theory/quantifiers/sygus/cegis.cpp b/src/theory/quantifiers/sygus/cegis.cpp
index c8e14e4bd..fdc0b28e0 100644
--- a/src/theory/quantifiers/sygus/cegis.cpp
+++ b/src/theory/quantifiers/sygus/cegis.cpp
@@ -39,6 +39,7 @@ Cegis::Cegis(Env& env,
SynthConjecture* p)
: SygusModule(env, qs, qim, tds, p),
d_eval_unfold(tds->getEvalUnfold()),
+ d_cexClosedEnum(false),
d_cegis_sampler(env),
d_usingSymCons(false)
{
@@ -47,11 +48,18 @@ Cegis::Cegis(Env& env,
bool Cegis::initialize(Node conj, Node n, const std::vector<Node>& candidates)
{
d_base_body = n;
+ d_cexClosedEnum = true;
if (d_base_body.getKind() == NOT && d_base_body[0].getKind() == FORALL)
{
for (const Node& v : d_base_body[0][0])
{
d_base_vars.push_back(v);
+ if (!v.getType().isClosedEnumerable())
+ {
+ // not closed enumerable, refinement lemmas cannot be sent to the
+ // quantifier-free datatype solver
+ d_cexClosedEnum = false;
+ }
}
d_base_body = d_base_body[0][1];
}
@@ -467,14 +475,18 @@ void Cegis::addRefinementLemmaConjunct(unsigned wcounter,
void Cegis::registerRefinementLemma(const std::vector<Node>& vars, Node lem)
{
addRefinementLemma(lem);
- // Make the refinement lemma and add it to lems.
- // This lemma is guarded by the parent's guard, which has the semantics
- // "this conjecture has a solution", hence this lemma states:
- // if the parent conjecture has a solution, it satisfies the specification
- // for the given concrete point.
- Node rlem =
- NodeManager::currentNM()->mkNode(OR, d_parent->getGuard().negate(), lem);
- d_qim.addPendingLemma(rlem, InferenceId::QUANTIFIERS_SYGUS_CEGIS_REFINE);
+ // must be closed enumerable
+ if (d_cexClosedEnum && options().quantifiers.sygusEvalUnfold)
+ {
+ // Make the refinement lemma and add it to lems.
+ // This lemma is guarded by the parent's guard, which has the semantics
+ // "this conjecture has a solution", hence this lemma states:
+ // if the parent conjecture has a solution, it satisfies the specification
+ // for the given concrete point.
+ Node rlem = NodeManager::currentNM()->mkNode(
+ OR, d_parent->getGuard().negate(), lem);
+ d_qim.addPendingLemma(rlem, InferenceId::QUANTIFIERS_SYGUS_CEGIS_REFINE);
+ }
}
bool Cegis::usingRepairConst() { return true; }
diff --git a/src/theory/quantifiers/sygus/cegis.h b/src/theory/quantifiers/sygus/cegis.h
index 8e0fffdd1..8d1f0a1b2 100644
--- a/src/theory/quantifiers/sygus/cegis.h
+++ b/src/theory/quantifiers/sygus/cegis.h
@@ -119,6 +119,13 @@ class Cegis : public SygusModule
std::vector<Node> d_rl_vals;
/** all variables appearing in refinement lemmas */
std::unordered_set<Node> d_refinement_lemma_vars;
+ /**
+ * Are the counterexamples we are handling in this class of only closed
+ * enumerable types (see TypeNode::isClosedEnumerable). If this is false,
+ * then CEGIS refinement lemmas can contain terms that are unhandled by
+ * theory solvers, e.g. uninterpreted constants.
+ */
+ bool d_cexClosedEnum;
/** adds lem as a refinement lemma */
void addRefinementLemma(Node lem);
diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp
index 43c958ff9..9b4cfb9e1 100644
--- a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp
+++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp
@@ -20,6 +20,7 @@
#include "expr/ascription_type.h"
#include "expr/dtype_cons.h"
+#include "expr/node_algorithm.h"
#include "options/quantifiers_options.h"
#include "theory/bv/theory_bv_utils.h"
#include "theory/datatypes/sygus_datatype_utils.h"
@@ -420,6 +421,8 @@ void CegGrammarConstructor::mkSygusConstantsForType(TypeNode type,
{
// generate constant array over the first element of the constituent type
Node c = type.mkGroundTerm();
+ // note that c should never contain an uninterpreted constant
+ Assert(!expr::hasSubtermKind(UNINTERPRETED_CONSTANT, c));
ops.push_back(c);
}
else if (type.isRoundingMode())
diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp
index b1537a390..573cab7bf 100644
--- a/src/theory/quantifiers/term_database.cpp
+++ b/src/theory/quantifiers/term_database.cpp
@@ -451,366 +451,6 @@ bool TermDb::inRelevantDomain( TNode f, unsigned i, TNode r ) {
}
}
-Node TermDb::evaluateTerm2(TNode n,
- std::map<TNode, Node>& visited,
- std::vector<Node>& exp,
- bool useEntailmentTests,
- bool computeExp,
- bool reqHasTerm)
-{
- std::map< TNode, Node >::iterator itv = visited.find( n );
- if( itv != visited.end() ){
- return itv->second;
- }
- size_t prevSize = exp.size();
- Trace("term-db-eval") << "evaluate term : " << n << std::endl;
- Node ret = n;
- if( n.getKind()==FORALL || n.getKind()==BOUND_VARIABLE ){
- //do nothing
- }
- else if (d_qstate.hasTerm(n))
- {
- Trace("term-db-eval") << "...exists in ee, return rep" << std::endl;
- ret = d_qstate.getRepresentative(n);
- if (computeExp)
- {
- if (n != ret)
- {
- exp.push_back(n.eqNode(ret));
- }
- }
- reqHasTerm = false;
- }
- else if (n.hasOperator())
- {
- std::vector<TNode> args;
- bool ret_set = false;
- Kind k = n.getKind();
- std::vector<Node> tempExp;
- for (unsigned i = 0, nchild = n.getNumChildren(); i < nchild; i++)
- {
- TNode c = evaluateTerm2(n[i],
- visited,
- tempExp,
- useEntailmentTests,
- computeExp,
- reqHasTerm);
- if (c.isNull())
- {
- ret = Node::null();
- ret_set = true;
- break;
- }
- else if (c == d_true || c == d_false)
- {
- // short-circuiting
- if ((k == AND && c == d_false) || (k == OR && c == d_true))
- {
- ret = c;
- ret_set = true;
- reqHasTerm = false;
- break;
- }
- else if (k == ITE && i == 0)
- {
- ret = evaluateTerm2(n[c == d_true ? 1 : 2],
- visited,
- tempExp,
- useEntailmentTests,
- computeExp,
- reqHasTerm);
- ret_set = true;
- reqHasTerm = false;
- break;
- }
- }
- if (computeExp)
- {
- exp.insert(exp.end(), tempExp.begin(), tempExp.end());
- }
- Trace("term-db-eval") << " child " << i << " : " << c << std::endl;
- args.push_back(c);
- }
- if (ret_set)
- {
- // if we short circuited
- if (computeExp)
- {
- exp.clear();
- exp.insert(exp.end(), tempExp.begin(), tempExp.end());
- }
- }
- else
- {
- // get the (indexed) operator of n, if it exists
- TNode f = getMatchOperator(n);
- // if it is an indexed term, return the congruent term
- if (!f.isNull())
- {
- // if f is congruent to a term indexed by this class
- TNode nn = getCongruentTerm(f, args);
- Trace("term-db-eval") << " got congruent term " << nn
- << " from DB for " << n << std::endl;
- if (!nn.isNull())
- {
- if (computeExp)
- {
- Assert(nn.getNumChildren() == n.getNumChildren());
- for (unsigned i = 0, nchild = nn.getNumChildren(); i < nchild; i++)
- {
- if (nn[i] != n[i])
- {
- exp.push_back(nn[i].eqNode(n[i]));
- }
- }
- }
- ret = d_qstate.getRepresentative(nn);
- Trace("term-db-eval") << "return rep" << std::endl;
- ret_set = true;
- reqHasTerm = false;
- Assert(!ret.isNull());
- if (computeExp)
- {
- if (n != ret)
- {
- exp.push_back(nn.eqNode(ret));
- }
- }
- }
- }
- if( !ret_set ){
- Trace("term-db-eval") << "return rewrite" << std::endl;
- // a theory symbol or a new UF term
- if (n.getMetaKind() == metakind::PARAMETERIZED)
- {
- args.insert(args.begin(), n.getOperator());
- }
- ret = NodeManager::currentNM()->mkNode(n.getKind(), args);
- ret = rewrite(ret);
- if (ret.getKind() == EQUAL)
- {
- if (d_qstate.areDisequal(ret[0], ret[1]))
- {
- ret = d_false;
- }
- }
- if (useEntailmentTests)
- {
- if (ret.getKind() == EQUAL || ret.getKind() == GEQ)
- {
- Valuation& val = d_qstate.getValuation();
- for (unsigned j = 0; j < 2; j++)
- {
- std::pair<bool, Node> et = val.entailmentCheck(
- options::TheoryOfMode::THEORY_OF_TYPE_BASED,
- j == 0 ? ret : ret.negate());
- if (et.first)
- {
- ret = j == 0 ? d_true : d_false;
- if (computeExp)
- {
- exp.push_back(et.second);
- }
- break;
- }
- }
- }
- }
- }
- }
- }
- // must have the term
- if (reqHasTerm && !ret.isNull())
- {
- Kind k = ret.getKind();
- if (k != OR && k != AND && k != EQUAL && k != ITE && k != NOT
- && k != FORALL)
- {
- if (!d_qstate.hasTerm(ret))
- {
- ret = Node::null();
- }
- }
- }
- Trace("term-db-eval") << "evaluated term : " << n << ", got : " << ret
- << ", reqHasTerm = " << reqHasTerm << std::endl;
- // clear the explanation if failed
- if (computeExp && ret.isNull())
- {
- exp.resize(prevSize);
- }
- visited[n] = ret;
- return ret;
-}
-
-TNode TermDb::getEntailedTerm2(TNode n,
- std::map<TNode, TNode>& subs,
- bool subsRep,
- bool hasSubs)
-{
- Trace("term-db-entail") << "get entailed term : " << n << std::endl;
- if (d_qstate.hasTerm(n))
- {
- Trace("term-db-entail") << "...exists in ee, return rep " << std::endl;
- return n;
- }else if( n.getKind()==BOUND_VARIABLE ){
- if( hasSubs ){
- std::map< TNode, TNode >::iterator it = subs.find( n );
- if( it!=subs.end() ){
- Trace("term-db-entail") << "...substitution is : " << it->second << std::endl;
- if( subsRep ){
- Assert(d_qstate.hasTerm(it->second));
- Assert(d_qstate.getRepresentative(it->second) == it->second);
- return it->second;
- }
- return getEntailedTerm2(it->second, subs, subsRep, hasSubs);
- }
- }
- }else if( n.getKind()==ITE ){
- for( unsigned i=0; i<2; i++ ){
- if (isEntailed2(n[0], subs, subsRep, hasSubs, i == 0))
- {
- return getEntailedTerm2(n[i == 0 ? 1 : 2], subs, subsRep, hasSubs);
- }
- }
- }else{
- if( n.hasOperator() ){
- TNode f = getMatchOperator( n );
- if( !f.isNull() ){
- std::vector< TNode > args;
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- TNode c = getEntailedTerm2(n[i], subs, subsRep, hasSubs);
- if( c.isNull() ){
- return TNode::null();
- }
- c = d_qstate.getRepresentative(c);
- Trace("term-db-entail") << " child " << i << " : " << c << std::endl;
- args.push_back( c );
- }
- TNode nn = getCongruentTerm(f, args);
- Trace("term-db-entail") << " got congruent term " << nn << " for " << n << std::endl;
- return nn;
- }
- }
- }
- return TNode::null();
-}
-
-Node TermDb::evaluateTerm(TNode n,
- bool useEntailmentTests,
- bool reqHasTerm)
-{
- std::map< TNode, Node > visited;
- std::vector<Node> exp;
- return evaluateTerm2(n, visited, exp, useEntailmentTests, false, reqHasTerm);
-}
-
-Node TermDb::evaluateTerm(TNode n,
- std::vector<Node>& exp,
- bool useEntailmentTests,
- bool reqHasTerm)
-{
- std::map<TNode, Node> visited;
- return evaluateTerm2(n, visited, exp, useEntailmentTests, true, reqHasTerm);
-}
-
-TNode TermDb::getEntailedTerm(TNode n,
- std::map<TNode, TNode>& subs,
- bool subsRep)
-{
- return getEntailedTerm2(n, subs, subsRep, true);
-}
-
-TNode TermDb::getEntailedTerm(TNode n)
-{
- std::map< TNode, TNode > subs;
- return getEntailedTerm2(n, subs, false, false);
-}
-
-bool TermDb::isEntailed2(
- TNode n, std::map<TNode, TNode>& subs, bool subsRep, bool hasSubs, bool pol)
-{
- Trace("term-db-entail") << "Check entailed : " << n << ", pol = " << pol << std::endl;
- Assert(n.getType().isBoolean());
- if( n.getKind()==EQUAL && !n[0].getType().isBoolean() ){
- TNode n1 = getEntailedTerm2(n[0], subs, subsRep, hasSubs);
- if( !n1.isNull() ){
- TNode n2 = getEntailedTerm2(n[1], subs, subsRep, hasSubs);
- if( !n2.isNull() ){
- if( n1==n2 ){
- return pol;
- }else{
- Assert(d_qstate.hasTerm(n1));
- Assert(d_qstate.hasTerm(n2));
- if( pol ){
- return d_qstate.areEqual(n1, n2);
- }else{
- return d_qstate.areDisequal(n1, n2);
- }
- }
- }
- }
- }else if( n.getKind()==NOT ){
- return isEntailed2(n[0], subs, subsRep, hasSubs, !pol);
- }else if( n.getKind()==OR || n.getKind()==AND ){
- bool simPol = ( pol && n.getKind()==OR ) || ( !pol && n.getKind()==AND );
- for( unsigned i=0; i<n.getNumChildren(); i++ ){
- if (isEntailed2(n[i], subs, subsRep, hasSubs, pol))
- {
- if( simPol ){
- return true;
- }
- }else{
- if( !simPol ){
- return false;
- }
- }
- }
- return !simPol;
- //Boolean equality here
- }else if( n.getKind()==EQUAL || n.getKind()==ITE ){
- for( unsigned i=0; i<2; i++ ){
- if (isEntailed2(n[0], subs, subsRep, hasSubs, i == 0))
- {
- unsigned ch = ( n.getKind()==EQUAL || i==0 ) ? 1 : 2;
- bool reqPol = ( n.getKind()==ITE || i==0 ) ? pol : !pol;
- return isEntailed2(n[ch], subs, subsRep, hasSubs, reqPol);
- }
- }
- }else if( n.getKind()==APPLY_UF ){
- TNode n1 = getEntailedTerm2(n, subs, subsRep, hasSubs);
- if( !n1.isNull() ){
- Assert(d_qstate.hasTerm(n1));
- if( n1==d_true ){
- return pol;
- }else if( n1==d_false ){
- return !pol;
- }else{
- return d_qstate.getRepresentative(n1) == (pol ? d_true : d_false);
- }
- }
- }else if( n.getKind()==FORALL && !pol ){
- return isEntailed2(n[1], subs, subsRep, hasSubs, pol);
- }
- return false;
-}
-
-bool TermDb::isEntailed(TNode n, bool pol)
-{
- Assert(d_consistent_ee);
- std::map< TNode, TNode > subs;
- return isEntailed2(n, subs, false, false, pol);
-}
-
-bool TermDb::isEntailed(TNode n,
- std::map<TNode, TNode>& subs,
- bool subsRep,
- bool pol)
-{
- Assert(d_consistent_ee);
- return isEntailed2(n, subs, subsRep, true, pol);
-}
-
bool TermDb::isTermActive( Node n ) {
return d_inactive_map.find( n )==d_inactive_map.end();
//return !n.getAttribute(NoMatchAttribute());
diff --git a/src/theory/quantifiers/term_database.h b/src/theory/quantifiers/term_database.h
index af0b87bd8..0e5c7bc01 100644
--- a/src/theory/quantifiers/term_database.h
+++ b/src/theory/quantifiers/term_database.h
@@ -176,78 +176,6 @@ class TermDb : public QuantifiersUtil {
* equivalence class of r.
*/
bool inRelevantDomain(TNode f, unsigned i, TNode r);
- /** evaluate term
- *
- * Returns a term n' such that n = n' is entailed based on the equality
- * information ee. This function may generate new terms. In particular,
- * we typically rewrite subterms of n of maximal size to terms that exist in
- * the equality engine specified by ee.
- *
- * useEntailmentTests is whether to call the theory engine's entailmentTest
- * on literals n for which this call fails to find a term n' that is
- * equivalent to n, for increased precision. This is not frequently used.
- *
- * The vector exp stores the explanation for why n evaluates to that term,
- * that is, if this call returns a non-null node n', then:
- * exp => n = n'
- *
- * If reqHasTerm, then we require that the returned term is a Boolean
- * combination of terms that exist in the equality engine used by this call.
- * If no such term is constructable, this call returns null. The motivation
- * for setting this to true is to "fail fast" if we require the return value
- * of this function to only involve existing terms. This is used e.g. in
- * the "propagating instances" portion of conflict-based instantiation
- * (quant_conflict_find.h).
- */
- Node evaluateTerm(TNode n,
- std::vector<Node>& exp,
- bool useEntailmentTests = false,
- bool reqHasTerm = false);
- /** same as above, without exp */
- Node evaluateTerm(TNode n,
- bool useEntailmentTests = false,
- bool reqHasTerm = false);
- /** get entailed term
- *
- * If possible, returns a term n' such that:
- * (1) n' exists in the current equality engine (as specified by the state),
- * (2) n = n' is entailed in the current context.
- * It returns null if no such term can be found.
- * Wrt evaluateTerm, this version does not construct new terms, and
- * thus is less aggressive.
- */
- TNode getEntailedTerm(TNode n);
- /** get entailed term
- *
- * If possible, returns a term n' such that:
- * (1) n' exists in the current equality engine (as specified by the state),
- * (2) n * subs = n' is entailed in the current context, where * denotes
- * substitution application.
- * It returns null if no such term can be found.
- * subsRep is whether the substitution maps to terms that are representatives
- * according to the quantifiers state.
- * Wrt evaluateTerm, this version does not construct new terms, and
- * thus is less aggressive.
- */
- TNode getEntailedTerm(TNode n, std::map<TNode, TNode>& subs, bool subsRep);
- /** is entailed
- * Checks whether the current context entails n with polarity pol, based on
- * the equality information in the quantifiers state. Returns true if the
- * entailment can be successfully shown.
- */
- bool isEntailed(TNode n, bool pol);
- /** is entailed
- *
- * Checks whether the current context entails ( n * subs ) with polarity pol,
- * based on the equality information in the quantifiers state,
- * where * denotes substitution application.
- * subsRep is whether the substitution maps to terms that are representatives
- * according to in the quantifiers state.
- */
- bool isEntailed(TNode n,
- std::map<TNode, TNode>& subs,
- bool subsRep,
- bool pol);
/** is the term n active in the current context?
*
* By default, all terms are active. A term is inactive if:
@@ -355,24 +283,6 @@ class TermDb : public QuantifiersUtil {
//----------------------------- end implementation-specific
/** set has term */
void setHasTerm( Node n );
- /** helper for evaluate term */
- Node evaluateTerm2(TNode n,
- std::map<TNode, Node>& visited,
- std::vector<Node>& exp,
- bool useEntailmentTests,
- bool computeExp,
- bool reqHasTerm);
- /** helper for get entailed term */
- TNode getEntailedTerm2(TNode n,
- std::map<TNode, TNode>& subs,
- bool subsRep,
- bool hasSubs);
- /** helper for is entailed */
- bool isEntailed2(TNode n,
- std::map<TNode, TNode>& subs,
- bool subsRep,
- bool hasSubs,
- bool pol);
/** compute uf eqc terms :
* Ensure entries for f are in d_func_map_eqc_trie for all equivalence classes
*/
diff --git a/src/theory/quantifiers/term_registry.cpp b/src/theory/quantifiers/term_registry.cpp
index ab999ad9b..d11fb0b8d 100644
--- a/src/theory/quantifiers/term_registry.cpp
+++ b/src/theory/quantifiers/term_registry.cpp
@@ -18,6 +18,7 @@
#include "options/base_options.h"
#include "options/quantifiers_options.h"
#include "options/smt_options.h"
+#include "theory/quantifiers/entailment_check.h"
#include "theory/quantifiers/first_order_model.h"
#include "theory/quantifiers/fmf/first_order_model_fmc.h"
#include "theory/quantifiers/ho_term_database.h"
@@ -39,6 +40,7 @@ TermRegistry::TermRegistry(Env& env,
d_termPools(new TermPools(env, qs)),
d_termDb(logicInfo().isHigherOrder() ? new HoTermDb(env, qs, qr)
: new TermDb(env, qs, qr)),
+ d_echeck(new EntailmentCheck(env, qs, *d_termDb.get())),
d_sygusTdb(nullptr),
d_qmodel(nullptr)
{
@@ -132,6 +134,11 @@ TermDbSygus* TermRegistry::getTermDatabaseSygus() const
return d_sygusTdb.get();
}
+EntailmentCheck* TermRegistry::getEntailmentCheck() const
+{
+ return d_echeck.get();
+}
+
TermEnumeration* TermRegistry::getTermEnumeration() const
{
return d_termEnum.get();
diff --git a/src/theory/quantifiers/term_registry.h b/src/theory/quantifiers/term_registry.h
index 175d450df..60a87a91f 100644
--- a/src/theory/quantifiers/term_registry.h
+++ b/src/theory/quantifiers/term_registry.h
@@ -23,6 +23,7 @@
#include "context/cdhashset.h"
#include "smt/env_obj.h"
+#include "theory/quantifiers/entailment_check.h"
#include "theory/quantifiers/sygus/term_database_sygus.h"
#include "theory/quantifiers/term_database.h"
#include "theory/quantifiers/term_enumeration.h"
@@ -83,6 +84,8 @@ class TermRegistry : protected EnvObj
TermDb* getTermDatabase() const;
/** get term database sygus */
TermDbSygus* getTermDatabaseSygus() const;
+ /** get entailment check utility */
+ EntailmentCheck* getEntailmentCheck() const;
/** get term enumeration utility */
TermEnumeration* getTermEnumeration() const;
/** get the term pools utility */
@@ -103,6 +106,8 @@ class TermRegistry : protected EnvObj
std::unique_ptr<TermPools> d_termPools;
/** term database */
std::unique_ptr<TermDb> d_termDb;
+ /** entailment check */
+ std::unique_ptr<EntailmentCheck> d_echeck;
/** sygus term database */
std::unique_ptr<TermDbSygus> d_sygusTdb;
/** extended model object */
diff --git a/src/theory/rewriter.cpp b/src/theory/rewriter.cpp
index 6cb3bf3ff..361d90dcd 100644
--- a/src/theory/rewriter.cpp
+++ b/src/theory/rewriter.cpp
@@ -32,13 +32,6 @@ using namespace std;
namespace cvc5 {
namespace theory {
-/** Attribute true for nodes that have been rewritten with proofs enabled */
-struct RewriteWithProofsAttributeId
-{
-};
-typedef expr::Attribute<RewriteWithProofsAttributeId, bool>
- RewriteWithProofsAttribute;
-
// Note that this function is a simplified version of Theory::theoryOf for
// (type-based) theoryOfMode. We expand and simplify it here for the sake of
// efficiency.
@@ -173,7 +166,6 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId,
Node node,
TConvProofGenerator* tcpg)
{
- RewriteWithProofsAttribute rpfa;
#ifdef CVC5_ASSERTIONS
bool isEquality = node.getKind() == kind::EQUAL && (!node[0].getType().isBoolean());
@@ -187,7 +179,7 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId,
// Check if it's been cached already
Node cached = getPostRewriteCache(theoryId, node);
- if (!cached.isNull() && (tcpg == nullptr || node.getAttribute(rpfa)))
+ if (!cached.isNull() && (tcpg == nullptr || hasRewrittenWithProofs(node)))
{
return cached;
}
@@ -217,7 +209,8 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId,
cached = getPreRewriteCache(rewriteStackTop.getTheoryId(),
rewriteStackTop.d_node);
if (cached.isNull()
- || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
+ || (tcpg != nullptr
+ && !hasRewrittenWithProofs(rewriteStackTop.d_node)))
{
// Rewrite until fix-point is reached
for(;;) {
@@ -256,7 +249,7 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId,
rewriteStackTop.d_node);
// If not, go through the children
if (cached.isNull()
- || (tcpg != nullptr && !rewriteStackTop.d_node.getAttribute(rpfa)))
+ || (tcpg != nullptr && !hasRewrittenWithProofs(rewriteStackTop.d_node)))
{
// The child we need to rewrite
unsigned child = rewriteStackTop.d_nextChild++;
@@ -343,7 +336,7 @@ Node Rewriter::rewriteTo(theory::TheoryId theoryId,
if (tcpg != nullptr)
{
// if proofs are enabled, mark that we've rewritten with proofs
- rewriteStackTop.d_original.setAttribute(rpfa, true);
+ d_tpgNodes.insert(rewriteStackTop.d_original);
if (!cached.isNull())
{
// We may have gotten a different node, due to non-determinism in
@@ -474,5 +467,10 @@ void Rewriter::clearCaches()
clearCachesInternal();
}
+bool Rewriter::hasRewrittenWithProofs(TNode n) const
+{
+ return d_tpgNodes.find(n) != d_tpgNodes.end();
+}
+
} // namespace theory
} // namespace cvc5
diff --git a/src/theory/rewriter.h b/src/theory/rewriter.h
index f96062b61..d90af4a31 100644
--- a/src/theory/rewriter.h
+++ b/src/theory/rewriter.h
@@ -159,6 +159,11 @@ class Rewriter {
void clearCachesInternal();
+ /**
+ * Has n been rewritten with proofs? This checks if n is in d_tpgNodes.
+ */
+ bool hasRewrittenWithProofs(TNode n) const;
+
/** The resource manager, for tracking resource usage */
ResourceManager* d_resourceManager;
@@ -167,6 +172,12 @@ class Rewriter {
/** The proof generator */
std::unique_ptr<TConvProofGenerator> d_tpg;
+ /**
+ * Nodes rewritten with proofs. Since d_tpg contains a reference to all
+ * nodes that have been rewritten with proofs, we can keep only a TNode
+ * here.
+ */
+ std::unordered_set<TNode> d_tpgNodes;
#ifdef CVC5_ASSERTIONS
std::unique_ptr<std::unordered_set<Node>> d_rewriteStack = nullptr;
#endif /* CVC5_ASSERTIONS */
diff --git a/src/theory/sets/normal_form.h b/src/theory/sets/normal_form.h
index eb839e1c0..35d06a510 100644
--- a/src/theory/sets/normal_form.h
+++ b/src/theory/sets/normal_form.h
@@ -58,7 +58,7 @@ class NormalForm {
}
/**
- * Returns true if n is considered a to be a (canonical) constant set value.
+ * Returns true if n is considered to be a (canonical) constant set value.
* A canonical set value is one whose AST is:
* (union (singleton c1) ... (union (singleton c_{n-1}) (singleton c_n))))
* where c1 ... cn are constants and the node identifier of these constants
diff --git a/src/theory/strings/base_solver.cpp b/src/theory/strings/base_solver.cpp
index b675d2444..9396e3e87 100644
--- a/src/theory/strings/base_solver.cpp
+++ b/src/theory/strings/base_solver.cpp
@@ -17,6 +17,7 @@
#include "theory/strings/base_solver.h"
#include "expr/sequence.h"
+#include "options/quantifiers_options.h"
#include "options/strings_options.h"
#include "theory/rewriter.h"
#include "theory/strings/theory_strings_utils.h"
@@ -539,8 +540,41 @@ void BaseSolver::checkCardinalityType(TypeNode tn,
// infinite cardinality, we are fine
return;
}
- // TODO (cvc4-projects #23): how to handle sequence for finite types?
- return;
+ // we check the cardinality class of the type, assuming that FMF is
+ // disabled.
+ if (isCardinalityClassFinite(etn.getCardinalityClass(), false))
+ {
+ Cardinality c = etn.getCardinality();
+ bool smallCardinality = false;
+ if (!c.isLargeFinite())
+ {
+ Integer ci = c.getFiniteCardinality();
+ if (ci.fitsUnsignedInt())
+ {
+ smallCardinality = true;
+ typeCardSize = ci.toUnsignedInt();
+ }
+ }
+ if (!smallCardinality)
+ {
+ // if it is large finite, then there is no way we could have
+ // constructed that many terms in memory, hence there is nothing
+ // to do.
+ return;
+ }
+ }
+ else
+ {
+ Assert(options().quantifiers.finiteModelFind);
+ // we are in a case where the cardinality of the type is infinite
+ // if not FMF, and finite given the Env's option value for FMF. In this
+ // case, FMF must be true, and the cardinality is finite and dynamic
+ // (i.e. it depends on the model's finite interpretation for uninterpreted
+ // sorts). We do not know how to handle this case, we set incomplete.
+ // TODO (cvc4-projects #23): how to handle sequence for finite types?
+ d_im.setIncomplete(IncompleteId::SEQ_FINITE_DYNAMIC_CARDINALITY);
+ return;
+ }
}
// for each collection
for (unsigned i = 0, csize = cols.size(); i < csize; ++i)
diff --git a/src/theory/strings/infer_proof_cons.cpp b/src/theory/strings/infer_proof_cons.cpp
index 34597c8be..5eba8663a 100644
--- a/src/theory/strings/infer_proof_cons.cpp
+++ b/src/theory/strings/infer_proof_cons.cpp
@@ -59,6 +59,11 @@ void InferProofCons::notifyFact(const InferInfo& ii)
d_lazyFactMap.insert(ii.d_conc, iic);
}
+void InferProofCons::notifyLemma(const InferInfo& ii)
+{
+ d_lazyFactMap[ii.d_conc] = std::make_shared<InferInfo>(ii);
+}
+
bool InferProofCons::addProofTo(CDProof* pf,
Node conc,
InferenceId infer,
diff --git a/src/theory/strings/infer_proof_cons.h b/src/theory/strings/infer_proof_cons.h
index 110d231cf..02b266fd7 100644
--- a/src/theory/strings/infer_proof_cons.h
+++ b/src/theory/strings/infer_proof_cons.h
@@ -69,6 +69,11 @@ class InferProofCons : public ProofGenerator
* only for facts that are explained.
*/
void notifyFact(const InferInfo& ii);
+ /**
+ * Same as above, but always overwrites. This is used for lemmas and
+ * conflicts, which do not necessarily have unique conclusions.
+ */
+ void notifyLemma(const InferInfo& ii);
/**
* This returns the proof for fact. This is required for using this class as
diff --git a/src/theory/strings/inference_manager.cpp b/src/theory/strings/inference_manager.cpp
index 0e971fc54..1f531a97c 100644
--- a/src/theory/strings/inference_manager.cpp
+++ b/src/theory/strings/inference_manager.cpp
@@ -42,7 +42,8 @@ InferenceManager::InferenceManager(Env& env,
d_termReg(tr),
d_extt(e),
d_statistics(statistics),
- d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr)
+ d_ipc(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr),
+ d_ipcl(pnm ? new InferProofCons(context(), pnm, d_statistics) : nullptr)
{
NodeManager* nm = NodeManager::currentNM();
d_zero = nm->mkConst(Rational(0));
@@ -279,12 +280,12 @@ void InferenceManager::processConflict(const InferInfo& ii)
{
Assert(!d_state.isInConflict());
// setup the fact to reproduce the proof in the call below
- if (d_ipc != nullptr)
+ if (d_ipcl != nullptr)
{
- d_ipc->notifyFact(ii);
+ d_ipcl->notifyLemma(ii);
}
// make the trust node
- TrustNode tconf = mkConflictExp(ii.d_premises, d_ipc.get());
+ TrustNode tconf = mkConflictExp(ii.d_premises, d_ipcl.get());
Assert(tconf.getKind() == TrustNodeKind::CONFLICT);
Trace("strings-assert") << "(assert (not " << tconf.getNode()
<< ")) ; conflict " << ii.getId() << std::endl;
@@ -335,11 +336,11 @@ TrustNode InferenceManager::processLemma(InferInfo& ii, LemmaProperty& p)
}
// ensure that the proof generator is ready to explain the final conclusion
// of the lemma (ii.d_conc).
- if (d_ipc != nullptr)
+ if (d_ipcl != nullptr)
{
- d_ipc->notifyFact(ii);
+ d_ipcl->notifyLemma(ii);
}
- TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipc.get());
+ TrustNode tlem = mkLemmaExp(ii.d_conc, exp, noExplain, d_ipcl.get());
Trace("strings-pending") << "Process pending lemma : " << tlem.getNode()
<< std::endl;
diff --git a/src/theory/strings/inference_manager.h b/src/theory/strings/inference_manager.h
index 49f10d7cb..9f4cd1986 100644
--- a/src/theory/strings/inference_manager.h
+++ b/src/theory/strings/inference_manager.h
@@ -249,8 +249,15 @@ class InferenceManager : public InferenceManagerBuffered
ExtTheory& d_extt;
/** Reference to the statistics for the theory of strings/sequences. */
SequencesStatistics& d_statistics;
- /** Conversion from inferences to proofs */
+ /** Conversion from inferences to proofs for facts */
std::unique_ptr<InferProofCons> d_ipc;
+ /**
+ * Conversion from inferences to proofs for lemmas and conflicts. This is
+ * separate from the above proof generator to avoid rare cases where the
+ * conclusion of a lemma is a duplicate of the conclusion of another lemma,
+ * or is a fact in the current equality engine.
+ */
+ std::unique_ptr<InferProofCons> d_ipcl;
/** Common constants */
Node d_true;
Node d_false;
diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds
index aa95ef2f8..9faa935e1 100644
--- a/src/theory/strings/kinds
+++ b/src/theory/strings/kinds
@@ -56,6 +56,10 @@ enumerator STRING_TYPE \
"::cvc5::theory::strings::StringEnumerator" \
"theory/strings/type_enumerator.h"
+enumerator REGEXP_TYPE \
+ "::cvc5::theory::strings::RegExpEnumerator" \
+ "theory/strings/regexp_enumerator.h"
+
constant CONST_STRING \
class \
String \
diff --git a/src/theory/strings/regexp_enumerator.cpp b/src/theory/strings/regexp_enumerator.cpp
new file mode 100644
index 000000000..261d0008e
--- /dev/null
+++ b/src/theory/strings/regexp_enumerator.cpp
@@ -0,0 +1,49 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Implementation of enumerator for regular expressions.
+ */
+
+#include "theory/strings/regexp_enumerator.h"
+
+namespace cvc5 {
+namespace theory {
+namespace strings {
+
+RegExpEnumerator::RegExpEnumerator(TypeNode type, TypeEnumeratorProperties* tep)
+ : TypeEnumeratorBase<RegExpEnumerator>(type), d_senum(type, tep)
+{
+}
+
+RegExpEnumerator::RegExpEnumerator(const RegExpEnumerator& enumerator)
+ : TypeEnumeratorBase<RegExpEnumerator>(enumerator.getType()),
+ d_senum(enumerator.d_senum)
+{
+}
+
+Node RegExpEnumerator::operator*()
+{
+ NodeManager* nm = NodeManager::currentNM();
+ return nm->mkNode(kind::STRING_TO_REGEXP, *d_senum);
+}
+
+RegExpEnumerator& RegExpEnumerator::operator++()
+{
+ ++d_senum;
+ return *this;
+}
+
+bool RegExpEnumerator::isFinished() { return d_senum.isFinished(); }
+
+} // namespace strings
+} // namespace theory
+} // namespace cvc5
diff --git a/src/theory/strings/regexp_enumerator.h b/src/theory/strings/regexp_enumerator.h
new file mode 100644
index 000000000..289c8b046
--- /dev/null
+++ b/src/theory/strings/regexp_enumerator.h
@@ -0,0 +1,59 @@
+/******************************************************************************
+ * Top contributors (to current version):
+ * Andrew Reynolds
+ *
+ * This file is part of the cvc5 project.
+ *
+ * Copyright (c) 2009-2021 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.
+ * ****************************************************************************
+ *
+ * Enumerators for regular expressions.
+ */
+
+#include "cvc5_private.h"
+
+#ifndef CVC5__THEORY__STRINGS__REGEXP_ENUMERATOR_H
+#define CVC5__THEORY__STRINGS__REGEXP_ENUMERATOR_H
+
+#include <vector>
+
+#include "expr/node.h"
+#include "expr/type_node.h"
+#include "theory/strings/type_enumerator.h"
+
+namespace cvc5 {
+namespace theory {
+namespace strings {
+
+/**
+ * Simple regular expression enumerator, generates only singleton language
+ * regular expressions from a string enumeration, in other words:
+ * (str.to_re s1) ... (str.to_re sn) ....
+ * where s1 ... sn ... is the enumeration for strings.
+ */
+class RegExpEnumerator : public TypeEnumeratorBase<RegExpEnumerator>
+{
+ public:
+ RegExpEnumerator(TypeNode type, TypeEnumeratorProperties* tep = nullptr);
+ RegExpEnumerator(const RegExpEnumerator& enumerator);
+ ~RegExpEnumerator() {}
+ /** get the current term */
+ Node operator*() override;
+ /** increment */
+ RegExpEnumerator& operator++() override;
+ /** is this enumerator finished? */
+ bool isFinished() override;
+
+ private:
+ /** underlying string enumerator */
+ StringEnumerator d_senum;
+};
+
+} // namespace strings
+} // namespace theory
+} // namespace cvc5
+
+#endif /* CVC5__THEORY__STRINGS__TYPE_ENUMERATOR_H */
diff --git a/src/theory/strings/regexp_solver.cpp b/src/theory/strings/regexp_solver.cpp
index 614a5e6e0..898c0f1b7 100644
--- a/src/theory/strings/regexp_solver.cpp
+++ b/src/theory/strings/regexp_solver.cpp
@@ -546,7 +546,10 @@ bool RegExpSolver::checkPDerivative(
{
std::vector<Node> noExplain;
noExplain.push_back(atom);
- noExplain.push_back(x.eqNode(d_emptyString));
+ if (x != d_emptyString)
+ {
+ noExplain.push_back(x.eqNode(d_emptyString));
+ }
std::vector<Node> iexp = nf_exp;
iexp.insert(iexp.end(), noExplain.begin(), noExplain.end());
d_im.sendInference(iexp, noExplain, d_false, InferenceId::STRINGS_RE_DELTA_CONF);
diff --git a/src/theory/theory_model_builder.cpp b/src/theory/theory_model_builder.cpp
index 33dbe9ffa..1a0549a0a 100644
--- a/src/theory/theory_model_builder.cpp
+++ b/src/theory/theory_model_builder.cpp
@@ -224,7 +224,7 @@ bool TheoryEngineModelBuilder::isExcludedCdtValue(
{
Trace("model-builder-debug") << " ...matches with " << eqc << " -> "
<< eqc_m << std::endl;
- if (eqc_m.getKind() == kind::UNINTERPRETED_CONSTANT)
+ if (eqc_m.getKind() == kind::CODATATYPE_BOUND_VARIABLE)
{
Trace("model-builder-debug") << "*** " << val
<< " is excluded datatype for " << eqc
diff --git a/src/theory/uf/ho_extension.cpp b/src/theory/uf/ho_extension.cpp
index 096a47c86..fd7cd467e 100644
--- a/src/theory/uf/ho_extension.cpp
+++ b/src/theory/uf/ho_extension.cpp
@@ -43,13 +43,16 @@ HoExtension::HoExtension(Env& env,
Node HoExtension::ppRewrite(Node node)
{
// convert HO_APPLY to APPLY_UF if fully applied
- if (node[0].getType().getNumChildren() == 2)
+ if (node.getKind() == HO_APPLY)
{
- Trace("uf-ho") << "uf-ho : expanding definition : " << node << std::endl;
- Node ret = getApplyUfForHoApply(node);
- Trace("uf-ho") << "uf-ho : ppRewrite : " << node << " to " << ret
- << std::endl;
- return ret;
+ if (node[0].getType().getNumChildren() == 2)
+ {
+ Trace("uf-ho") << "uf-ho : expanding definition : " << node << std::endl;
+ Node ret = getApplyUfForHoApply(node);
+ Trace("uf-ho") << "uf-ho : ppRewrite : " << node << " to " << ret
+ << std::endl;
+ return ret;
+ }
}
return node;
}
diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp
index cd974d3e4..ca7dc6a73 100644
--- a/src/theory/uf/theory_uf.cpp
+++ b/src/theory/uf/theory_uf.cpp
@@ -212,7 +212,7 @@ TrustNode TheoryUF::ppRewrite(TNode node, std::vector<SkolemLemma>& lems)
Trace("uf-exp-def") << "TheoryUF::ppRewrite: expanding definition : " << node
<< std::endl;
Kind k = node.getKind();
- if (k == kind::HO_APPLY)
+ if (k == kind::HO_APPLY || (node.isVar() && node.getType().isFunction()))
{
if (!logicInfo().isHigherOrder())
{
diff --git a/src/util/smt2_quote_string.cpp b/src/util/smt2_quote_string.cpp
index 82750c48b..e2ab4a74c 100644
--- a/src/util/smt2_quote_string.cpp
+++ b/src/util/smt2_quote_string.cpp
@@ -42,4 +42,16 @@ std::string quoteSymbol(const std::string& s){
return s;
}
+std::string quoteString(const std::string& s) {
+ // escape all double-quotes
+ std::string output = s;
+ size_t pos = 0;
+ while ((pos = output.find('"', pos)) != std::string::npos)
+ {
+ output.replace(pos, 1, "\"\"");
+ pos += 2;
+ }
+ return '"' + output + '"';
+}
+
} // namespace cvc5
diff --git a/src/util/smt2_quote_string.h b/src/util/smt2_quote_string.h
index 98b2f89dd..f8be53c0c 100644
--- a/src/util/smt2_quote_string.h
+++ b/src/util/smt2_quote_string.h
@@ -27,6 +27,11 @@ namespace cvc5 {
*/
std::string quoteSymbol(const std::string& s);
+/**
+ * SMT-LIB 2 quoting for strings
+ */
+std::string quoteString(const std::string& s);
+
} // namespace cvc5
#endif /* CVC5__UTIL__SMT2_QUOTE_STRING_H */
generated by cgit on debian on lair
contact matthew@masot.net with questions or feedback