diff options
author | Andrew Reynolds <andrew.j.reynolds@gmail.com> | 2020-04-02 09:46:15 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-02 09:46:15 -0500 |
commit | 9720e341d9cda3de7e7b2b0c25afe190cc2021e4 (patch) | |
tree | cb44e6cd092956a05ac00d4104d19bd0e1f36eb4 | |
parent | 796703fc72cfd67dc05357b10a5f0311200f2865 (diff) | |
parent | aa6fb6fa3df0b1519e6763e72541c022396ab1dc (diff) |
Merge branch 'master' into rmAliasesrmAliases
481 files changed, 8749 insertions, 7266 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..705e0679d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,168 @@ +on: [push, pull_request] +name: CI + +jobs: + build: + + strategy: + matrix: + os: [ubuntu-latest, macos-latest] + name: [ + production, + production-clang, + debug, + debug-cln + ] + + include: + - name: production + config: production --language-bindings=java --lfsc --python-bindings + cache-key: production + python-bindings: true + check-examples: true + + - name: production-clang + config: production + cache-key: production-clang + check-examples: true + env: CC=clang CXX=clang++ + + - name: debug + config: debug --symfpu --lfsc --no-debug-symbols + cache-key: debug + + - name: debug-cln + config: debug --symfpu --cln --gpl --no-debug-symbols --no-proofs + cache-key: debug-cln + + name: ${{ matrix.os }}:${{ matrix.name }} + runs-on: ${{ matrix.os }} + + steps: + + - uses: actions/checkout@v2 + + - name: Install Packages + if: runner.os == 'Linux' + run: | + sudo apt-get update + sudo apt-get install -y \ + ccache \ + cxxtest \ + libcln-dev \ + libgmp-dev \ + swig3.0 + python3 -m pip install toml + python3 -m pip install setuptools + echo "::add-path::/usr/lib/ccache" + + - name: Install Packages (macOS) + if: runner.os == 'macOS' + run: | + brew install \ + ccache \ + cxxtest \ + cln \ + gmp \ + swig + python3 -m pip install toml + python3 -m pip install setuptools + echo "::add-path::/usr/local/opt/ccache/libexec" + + # Note: We install Cython with sudo since cmake can't find Cython otherwise. + - name: Install Cython + if: matrix.python-bindings && runner.os == 'Linux' + run: | + sudo python3 -m pip install \ + Cython==0.29 --install-option="--no-cython-compile" + + - name: Install Cython (macOS) + if: matrix.python-bindings && runner.os == 'macOS' + run: | + python3 -m pip install \ + Cython==0.29 --install-option="--no-cython-compile" + + - name: Restore Dependencies + id: restore-deps + uses: actions/cache@v1 + with: + path: deps/install + key: ${{ runner.os }}-deps-${{ hashFiles('contrib/get-**') }}-${{ hashFiles('.github/workflows/ci.yml') }} + + - name: Setup Dependencies + if: steps.restore-deps.outputs.cache-hit != 'true' + run: | + ./contrib/get-antlr-3.4 + ./contrib/get-symfpu + ./contrib/get-cadical + ./contrib/get-cryptominisat + ./contrib/get-lfsc-checker + + # GitHub actions currently does not support modifying an already existing + # cache. Hence, we create a new cache for each commit with key + # cache-${{ runner.os }}-${{ matrix.cache-key }}-${{ github.sha }}. This + # will result in an initial cache miss. However, restore-keys will search + # for the most recent cache with prefix + # cache-${{ runner.os }}-${{ matrix.cache-key }}-, and if found uses it as + # a base for the new cache. + - name: Restore ccache + id: cache + uses: actions/cache@v1 + with: + path: ccache-dir + key: cache-${{ runner.os }}-${{ matrix.cache-key }}-${{ github.sha }} + restore-keys: cache-${{ runner.os }}-${{ matrix.cache-key }}- + + - name: Configure ccache + run: | + ccache --set-config=cache_dir=${{ github.workspace }}/ccache-dir + ccache --set-config=compression=true + ccache --set-config=compression_level=6 + ccache -M 500M + ccache -z + + - name: Configure + run: | + ${{ matrix.env }} ./configure.sh ${{ matrix.config }} \ + --python3 \ + --prefix=$(pwd)/build/install \ + --unit-testing + + - name: Build + run: make -j2 + working-directory: build + + - name: ccache Statistics + run: ccache -s + + - name: Run CTest + run: make -j2 check + env: + ARGS: --output-on-failure -LE regress[1-4] + CVC4_REGRESSION_ARGS: --no-early-exit + working-directory: build + + - name: Install Check + run: | + make -j2 install + echo -e "#include <cvc4/api/cvc4cpp.h>\nint main() { CVC4::api::Solver s; return 0; }" > /tmp/test.cpp + g++ -std=c++11 /tmp/test.cpp -I install/include -L install/lib -lcvc4 + working-directory: build + + - name: Python Install Check + if: matrix.python-bindings + run: | + export PYTHONPATH="$PYTHONPATH:$(dirname $(find build/install/ -name "pycvc4" -type d))" + python3 -c "import pycvc4" + + # Examples are built for non-symfpu builds + - name: Check Examples + if: matrix.check-examples && runner.os == 'Linux' + run: | + mkdir build + cd build + cmake .. -DCMAKE_PREFIX_PATH=$(pwd)/../../build/install/lib/cmake + make -j2 + ctest -j2 --output-on-failure + working-directory: examples + diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 8297813da..000000000 --- a/.travis.yml +++ /dev/null @@ -1,153 +0,0 @@ -language: cpp -cache: - - apt - - ccache - -sudo: false -dist: xenial - -env: - global: - - CCACHE_COMPRESS=1 -addons: - apt: - sources: - - ubuntu-toolchain-r-test - packages: &common_deps - - antlr3 - - cmake - - cxxtest - - junit4 - - libantlr3c-dev - - libcln-dev - - libgmp-dev - - libhamcrest-java - - openjdk-8-jdk - - python3 - - python3-pip - - python3-setuptools - - swig3.0 -before_install: - # Clang does not play nice with ccache (at least the versions offered by - # Travis), use a workaround: - # https://github.com/travis-ci/travis-ci/issues/5383#issuecomment-224630584 - - | - if [ "$TRAVIS_OS_NAME" == "linux" ] && [ "$CXX" == "clang++" ]; then - export CFLAGS="-Qunused-arguments" - export CXXFLAGS="-Qunused-arguments" - sudo ln -s $(which ccache) /usr/lib/ccache/clang - sudo ln -s $(which ccache) /usr/lib/ccache/clang++ - fi -before_script: - export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 -script: - - ccache -M 1G - - ccache -z - - ${CC} --version - - ${CXX} --version - - sudo ${TRAVIS_PYTHON} -m pip install toml - - sudo ${TRAVIS_PYTHON} -m pip install Cython==0.29 --install-option="--no-cython-compile" - - | - echo "travis_fold:start:load_script" - normal="$(echo -e '\033[0m')" red="$normal$(echo -e '\033[01;31m')" green="$normal$(echo -e '\033[01;32m')" - configureCVC4() { - echo "CVC4 config - $TRAVIS_CVC4_CONFIG"; - ./configure.sh --name=build --prefix=$(pwd)/build/install --unit-testing $TRAVIS_CVC4_CONFIG - } - error() { - echo; - echo "${red}${1}${normal}"; - echo; - exit 1; - } - makeCheck() { - ( - cd build - make -j2 check ARGS='-LE regress[1-4]' CVC4_REGRESSION_ARGS='--no-early-exit' || error "BUILD/UNIT/SYSTEM/REGRESSION TEST FAILED" - ) - } - makeExamples() { - ( - cd examples - mkdir build - cd build - cmake .. -DCMAKE_PREFIX_PATH=$(pwd)/../../build/install/lib/cmake - make -j2 - ctest -j2 --output-on-failure || error "RUNNING EXAMPLES FAILED" - ) - } - makeInstallCheck() { - ( - cd build - make install -j2 - echo -e "#include <cvc4/cvc4.h>\nint main() { CVC4::ExprManager em; return 0; }" > /tmp/test.cpp - $CXX -std=c++11 /tmp/test.cpp -I install/include -L install/lib -lcvc4 -lcln || exit 1 - # set PYTHONPATH to include the directory containing pycvc4 module - export PYTHONPATH=$PYTHONPATH:$(dirname $(find ./install/ -name "pycvc4" -type d)) - if [[ "$TRAVIS_CVC4_PYTHON_BINDINGS" == "yes" ]]; then - $TRAVIS_PYTHON -c "import pycvc4" || exit 1 - fi - ) - } - run() { - echo "travis_fold:start:$1" - echo "Running $1" - $1 || exit 1 - echo "travis_fold:end:$1" - } - [[ "$TRAVIS_CVC4_CONFIG" == *"symfpu"* ]] && CVC4_SYMFPU_BUILD="yes" - [ -n "$CVC4_SYMFPU_BUILD" ] && run contrib/get-symfpu - [ -n "$TRAVIS_CVC4" ] && [ -n "$TRAVIS_WITH_LFSC" ] && run contrib/get-lfsc-checker - [ -n "$TRAVIS_CVC4" ] && run configureCVC4 - [ -n "$TRAVIS_CVC4" ] && run makeCheck - [ -z "$CVC4_SYMFPU_BUILD" ] && run makeInstallCheck && run makeExamples - [ -z "$TRAVIS_CVC4" ] && error "Unknown Travis-CI configuration" - echo "travis_fold:end:load_script" - - echo; echo "${green}EVERYTHING SEEMED TO PASS!${normal}" - - ccache -s -matrix: - fast_finish: true - include: - # Test with GCC - - compiler: gcc - env: - - TRAVIS_CVC4=yes - - TRAVIS_WITH_LFSC=yes - - TRAVIS_CVC4_PYTHON_BINDINGS=no - - TRAVIS_CVC4_CONFIG="production --language-bindings=java --lfsc" - - TRAVIS_PYTHON=python - - compiler: gcc - env: - - TRAVIS_CVC4=yes - - TRAVIS_WITH_LFSC=yes - - TRAVIS_CVC4_PYTHON_BINDINGS=no - - TRAVIS_CVC4_CONFIG="debug --symfpu --lfsc --no-debug-symbols" - - TRAVIS_PYTHON=python - # Test python bindings - - compiler: gcc - env: - - TRAVIS_CVC4=yes - - TRAVIS_WITH_LFSC=yes - - TRAVIS_CVC4_PYTHON_BINDINGS=yes - - TRAVIS_CVC4_CONFIG="production --python-bindings --python2" - - TRAVIS_PYTHON=python - - compiler: gcc - env: - - TRAVIS_CVC4=yes - - TRAVIS_WITH_LFSC=yes - - TRAVIS_CVC4_PYTHON_BINDINGS=yes - - TRAVIS_CVC4_CONFIG="production --python-bindings --python3" - - TRAVIS_PYTHON=python3 - # - # Test with Clang - - compiler: clang - env: - - TRAVIS_CVC4=yes - - TRAVIS_WITH_LFSC=yes - - TRAVIS_CVC4_PYTHON_BINDINGS=no - - TRAVIS_CVC4_CONFIG="debug --symfpu --cln --gpl --no-debug-symbols --no-proofs" - - TRAVIS_PYTHON=python -notifications: - email: - on_success: change - on_failure: always diff --git a/CMakeLists.txt b/CMakeLists.txt index 945f71d36..c535890e1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -121,7 +121,6 @@ cvc4_option(ENABLE_DEBUG_SYMBOLS "Enable debug symbols") cvc4_option(ENABLE_DUMPING "Enable dumping") cvc4_option(ENABLE_MUZZLE "Suppress ALL non-result output") cvc4_option(ENABLE_PROOFS "Enable proof support") -cvc4_option(ENABLE_REPLAY "Enable the replay feature") cvc4_option(ENABLE_STATISTICS "Enable statistics") cvc4_option(ENABLE_TRACING "Enable tracing") cvc4_option(ENABLE_UNIT_TESTING "Enable unit testing") @@ -429,10 +428,6 @@ if(ENABLE_PROOFS) add_definitions(-DCVC4_PROOF) endif() -if(ENABLE_REPLAY) - add_definitions(-DCVC4_REPLAY) -endif() - if(ENABLE_TRACING) add_definitions(-DCVC4_TRACING) endif() @@ -608,7 +603,6 @@ message("") print_config("Dumping :" ENABLE_DUMPING) print_config("Muzzle :" ENABLE_MUZZLE) print_config("Proofs :" ENABLE_PROOFS) -print_config("Replay :" ENABLE_REPLAY) print_config("Statistics :" ENABLE_STATISTICS) print_config("Tracing :" ENABLE_TRACING) message("") @@ -1,9 +1,10 @@ [![License: BSD]( https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)]( https://opensource.org/licenses/BSD-3-Clause) -[![Build Status]( - https://travis-ci.org/CVC4/CVC4.svg?branch=master)]( - https://travis-ci.org/CVC4/CVC4) +![CI](https://github.com/CVC4/CVC4/workflows/CI/badge.svg) +[![Coverage]( + https://img.shields.io/endpoint?url=https://cvc4.cs.stanford.edu/downloads/builds/coverage/nightly-coverage.json)]( + https://cvc4.cs.stanford.edu/downloads/builds/coverage) CVC4 =============================================================================== diff --git a/cmake/ConfigCompetition.cmake b/cmake/ConfigCompetition.cmake index 6bd846d0c..e18d2b2f1 100644 --- a/cmake/ConfigCompetition.cmake +++ b/cmake/ConfigCompetition.cmake @@ -8,8 +8,6 @@ set(OPTIMIZATION_LEVEL 9) cvc4_set_option(ENABLE_DEBUG_SYMBOLS OFF) # enable_statistics=no cvc4_set_option(ENABLE_STATISTICS OFF) -# enable_replay=no -cvc4_set_option(ENABLE_REPLAY OFF) # enable_assertions=no cvc4_set_option(ENABLE_ASSERTIONS OFF) # enable_proof=no diff --git a/cmake/ConfigDebug.cmake b/cmake/ConfigDebug.cmake index 31b142ffc..1ee78a602 100644 --- a/cmake/ConfigDebug.cmake +++ b/cmake/ConfigDebug.cmake @@ -7,8 +7,6 @@ add_c_cxx_flag("-Og") cvc4_set_option(ENABLE_DEBUG_SYMBOLS ON) # enable_statistics=yes cvc4_set_option(ENABLE_STATISTICS ON) -# enable_replay=yes -cvc4_set_option(ENABLE_REPLAY ON) # enable_assertions=yes cvc4_set_option(ENABLE_ASSERTIONS ON) # enable_proof=yes diff --git a/cmake/ConfigProduction.cmake b/cmake/ConfigProduction.cmake index 49e338abf..503f5d58f 100644 --- a/cmake/ConfigProduction.cmake +++ b/cmake/ConfigProduction.cmake @@ -4,8 +4,6 @@ set(OPTIMIZATION_LEVEL 3) cvc4_set_option(ENABLE_DEBUG_SYMBOLS OFF) # enable_statistics=yes cvc4_set_option(ENABLE_STATISTICS ON) -# enable_replay=no -cvc4_set_option(ENABLE_REPLAY OFF) # enable_assertions=no cvc4_set_option(ENABLE_ASSERTIONS OFF) # enable_proof=yes diff --git a/cmake/ConfigTesting.cmake b/cmake/ConfigTesting.cmake index 40366495d..cdc9e3af8 100644 --- a/cmake/ConfigTesting.cmake +++ b/cmake/ConfigTesting.cmake @@ -4,8 +4,6 @@ set(OPTIMIZATION_LEVEL 2) cvc4_set_option(ENABLE_DEBUG_SYMBOLS ON) # enable_statistics=yes cvc4_set_option(ENABLE_STATISTICS ON) -# enable_replay=yes -cvc4_set_option(ENABLE_REPLAY ON) # enable_assertions=yes cvc4_set_option(ENABLE_ASSERTIONS ON) # enable_proof=yes diff --git a/configure.sh b/configure.sh index ae9b275aa..070e2c230 100755 --- a/configure.sh +++ b/configure.sh @@ -1,6 +1,8 @@ -#!/bin/sh +#!/bin/bash #--------------------------------------------------------------------------# +set -e -o pipefail + usage () { cat <<EOF Usage: $0 [<build type>] [<option> ...] @@ -34,7 +36,6 @@ The following flags enable optional features (disable with --no-<option name>). --valgrind Valgrind instrumentation --debug-context-mm use the debug context memory manager --statistics include statistics - --replay turn on the replay feature --assertions turn on assertions --tracing include tracing code --dumping include dumping code @@ -130,7 +131,6 @@ lfsc=default muzzle=default optimized=default proofs=default -replay=default shared=default static_binary=default statistics=default @@ -248,9 +248,6 @@ do --proofs) proofs=ON;; --no-proofs) proofs=OFF;; - --replay) replay=ON;; - --no-replay) replay=OFF;; - --static) shared=OFF; static_binary=ON;; --no-static) shared=ON;; @@ -387,8 +384,6 @@ cmake_opts="" && cmake_opts="$cmake_opts -DENABLE_OPTIMIZED=$optimized" [ $proofs != default ] \ && cmake_opts="$cmake_opts -DENABLE_PROOFS=$proofs" -[ $replay != default ] \ - && cmake_opts="$cmake_opts -DENABLE_REPLAY=$replay" [ $shared != default ] \ && cmake_opts="$cmake_opts -DENABLE_SHARED=$shared" [ $static_binary != default ] \ @@ -465,7 +460,7 @@ root_dir=$(pwd) [ $win64 = ON ] && [ -e "$build_dir" ] && rm -r "$build_dir" mkdir -p "$build_dir" -cd "$build_dir" || exit 1 +cd "$build_dir" [ -e CMakeCache.txt ] && rm CMakeCache.txt build_dir_escaped=$(echo "$build_dir" | sed 's/\//\\\//g') diff --git a/contrib/get-antlr-3.4 b/contrib/get-antlr-3.4 index 9ab0695b7..bc75e8339 100755 --- a/contrib/get-antlr-3.4 +++ b/contrib/get-antlr-3.4 @@ -26,15 +26,15 @@ if [ -z "${MACHINE_TYPE}" ]; then MACHINE_TYPE=$(${CONFIG_GUESS_SCRIPT} | sed 's,-.*,,') fi -mkdir -p "$ANTLR_HOME_DIR/share/java" +mkdir -p "$INSTALL_DIR/share/java" webget \ "https://www.antlr3.org/download/antlr-3.4-complete.jar" \ - "$ANTLR_HOME_DIR/share/java/antlr-3.4-complete.jar" + "$INSTALL_DIR/share/java/antlr-3.4-complete.jar" mkdir -p "$ANTLR_HOME_DIR/bin" tee "$ANTLR_HOME_DIR/bin/antlr3" <<EOF #!/usr/bin/env bash -export CLASSPATH=$ANTLR_HOME_DIR/share/java/antlr-3.4-complete.jar:\$CLASSPATH +export CLASSPATH=$INSTALL_DIR/share/java/antlr-3.4-complete.jar:\$CLASSPATH exec java org.antlr.Tool "\$@" EOF chmod a+x "$ANTLR_HOME_DIR/bin/antlr3" diff --git a/examples/SimpleVC.java b/examples/SimpleVC.java index e77a5e99f..798dc08af 100644 --- a/examples/SimpleVC.java +++ b/examples/SimpleVC.java @@ -55,8 +55,9 @@ public class SimpleVC { new Expr(em.mkExpr(Kind.AND, x_positive, y_positive)). impExpr(new Expr(twox_plus_y_geq_3)); - System.out.println("Checking validity of formula " + formula + " with CVC4."); - System.out.println("CVC4 should report VALID."); - System.out.println("Result from CVC4 is: " + smt.query(formula)); + System.out.println( + "Checking entailment of formula " + formula + " with CVC4."); + System.out.println("CVC4 should report ENTAILED."); + System.out.println("Result from CVC4 is: " + smt.checkEntailed(formula)); } } diff --git a/examples/SimpleVC.ml b/examples/SimpleVC.ml index c89341dc8..d9d78586b 100644 --- a/examples/SimpleVC.ml +++ b/examples/SimpleVC.ml @@ -79,13 +79,13 @@ let formula = em->mkExpr(implies, lhs, twox_plus_y_geq_3) let bformula = new_Expr(formula) in -print_string "Checking validity of formula " ; +print_string "Checking entailment of formula " ; print_string (get_string (formula->toString ())) ; print_string " with CVC4." ; print_newline () ; -print_string "CVC4 should report VALID." ; +print_string "CVC4 should report ENTAILED." ; print_newline () ; print_string "Result from CVC4 is: " ; -print_string (get_string (smt->query(bformula)->toString ())) ; +print_string (get_string (smt->checkEntailed(bformula)->toString ())) ; print_newline () ;; diff --git a/examples/SimpleVC.php b/examples/SimpleVC.php index 329446703..031c0a1c5 100755 --- a/examples/SimpleVC.php +++ b/examples/SimpleVC.php @@ -50,7 +50,7 @@ my $twox_plus_y_geq_3 = $em->mkExpr($CVC4::GEQ, $twox_plus_y, $three); my $formula = new CVC4::Expr($em->mkExpr($CVC4::AND, $x_positive, $y_positive))->impExpr(new CVC4::Expr($twox_plus_y_geq_3)); -print "Checking validity of formula " . $formula->toString() . " with CVC4.\n"; -print "CVC4 should report VALID.\n"; -print "Result from CVC4 is: " . $smt->query($formula)->toString() . "\n"; +print "Checking entailment of formula " . $formula->toString() . " with CVC4.\n"; +print "CVC4 should report ENTAILED.\n"; +print "Result from CVC4 is: " . $smt->checkEntailed($formula)->toString() . "\n"; diff --git a/examples/SimpleVC.pl b/examples/SimpleVC.pl index 998c28bb0..20ad6c737 100755 --- a/examples/SimpleVC.pl +++ b/examples/SimpleVC.pl @@ -50,7 +50,7 @@ my $twox_plus_y_geq_3 = $em->mkExpr($CVC4::GEQ, $twox_plus_y, $three); my $formula = new CVC4::Expr($em->mkExpr($CVC4::AND, $x_positive, $y_positive))->impExpr(new CVC4::Expr($twox_plus_y_geq_3)); -print "Checking validity of formula " . $formula->toString() . " with CVC4.\n"; -print "CVC4 should report VALID.\n"; -print "Result from CVC4 is: " . $smt->query($formula)->toString() . "\n"; +print "Checking entailment of formula " . $formula->toString() . " with CVC4.\n"; +print "CVC4 should report ENTAILED.\n"; +print "Result from CVC4 is: " . $smt->checkEntailed($formula)->toString() . "\n"; diff --git a/examples/SimpleVC.py b/examples/SimpleVC.py index 4c21d35c0..5550974c9 100755 --- a/examples/SimpleVC.py +++ b/examples/SimpleVC.py @@ -53,9 +53,9 @@ def main(): formula = Expr(em.mkExpr(CVC4.AND, x_positive, y_positive)).impExpr(Expr(twox_plus_y_geq_3)) - print("Checking validity of formula " + formula.toString() + " with CVC4.") - print("CVC4 should report VALID.") - print("Result from CVC4 is: " + smt.query(formula).toString()) + print("Checking entailment of formula " + formula.toString() + " with CVC4.") + print("CVC4 should report ENTAILED .") + print("Result from CVC4 is: " + smt.checkEntailed(formula).toString()) return 0 diff --git a/examples/SimpleVC.rb b/examples/SimpleVC.rb index 0d19ef49f..4f289d34f 100755 --- a/examples/SimpleVC.rb +++ b/examples/SimpleVC.rb @@ -49,9 +49,9 @@ twox_plus_y_geq_3 = em.mkExpr(GEQ, twox_plus_y, three) formula = Expr.new(em.mkExpr(AND, x_positive, y_positive)).impExpr(Expr.new(twox_plus_y_geq_3)) -puts "Checking validity of formula " + formula.toString + " with CVC4." -puts "CVC4 should report VALID." -puts "Result from CVC4 is: " + smt.query(formula).toString +puts "Checking entailment of formula " + formula.toString + " with CVC4." +puts "CVC4 should report ENTAILED." +puts "Result from CVC4 is: " + smt.checkEntailed(formula).toString exit diff --git a/examples/SimpleVC.tcl b/examples/SimpleVC.tcl index ed0decb26..4e1c76c5a 100755 --- a/examples/SimpleVC.tcl +++ b/examples/SimpleVC.tcl @@ -48,7 +48,7 @@ set twox_plus_y_geq_3 [ExprManager_mkExpr em $GEQ $twox_plus_y $three] set formula [Expr_impExpr [Expr _1 [ExprManager_mkExpr em $AND $x_positive $y_positive]] [Expr _2 $twox_plus_y_geq_3]] -puts "Checking validity of formula [Expr_toString $formula] with CVC4." -puts "CVC4 should report VALID." -puts "Result from CVC4 is: [Result_toString [SmtEngine_query smt $formula]]" +puts "Checking entailment of formula [Expr_toString $formula] with CVC4." +puts "CVC4 should report ENTAILED." +puts "Result from CVC4 is: [Result_toString [SmtEngine_checkEntailed smt $formula]]" diff --git a/examples/api/bitvectors-new.cpp b/examples/api/bitvectors-new.cpp index 4578da733..ebb8ee7ee 100644 --- a/examples/api/bitvectors-new.cpp +++ b/examples/api/bitvectors-new.cpp @@ -87,9 +87,9 @@ int main() slv.assertFormula(assignment1); Term new_x_eq_new_x_ = slv.mkTerm(EQUAL, new_x, new_x_); - cout << " Check validity assuming: " << new_x_eq_new_x_ << endl; - cout << " Expect valid. " << endl; - cout << " CVC4: " << slv.checkValidAssuming(new_x_eq_new_x_) << endl; + cout << " Check entailment assuming: " << new_x_eq_new_x_ << endl; + cout << " Expect ENTAILED. " << endl; + cout << " CVC4: " << slv.checkEntailed(new_x_eq_new_x_) << endl; cout << " Popping context. " << endl; slv.pop(); @@ -103,15 +103,15 @@ int main() cout << "Asserting " << assignment2 << " to CVC4 " << endl; slv.assertFormula(assignment2); - cout << " Check validity assuming: " << new_x_eq_new_x_ << endl; - cout << " Expect valid. " << endl; - cout << " CVC4: " << slv.checkValidAssuming(new_x_eq_new_x_) << endl; + cout << " Check entailment assuming: " << new_x_eq_new_x_ << endl; + cout << " Expect ENTAILED. " << endl; + cout << " CVC4: " << slv.checkEntailed(new_x_eq_new_x_) << endl; Term x_neq_x = slv.mkTerm(EQUAL, x, x).notTerm(); std::vector<Term> v{new_x_eq_new_x_, x_neq_x}; - cout << " Check Validity Assuming: " << v << endl; - cout << " Expect invalid. " << endl; - cout << " CVC4: " << slv.checkValidAssuming(v) << endl; + cout << " Check entailment assuming: " << v << endl; + cout << " Expect NOT_ENTAILED. " << endl; + cout << " CVC4: " << slv.checkEntailed(v) << endl; // Assert that a is odd Op extract_op = slv.mkOp(BITVECTOR_EXTRACT, 0, 0); diff --git a/examples/api/bitvectors.cpp b/examples/api/bitvectors.cpp index 259eb48ff..494a45abc 100644 --- a/examples/api/bitvectors.cpp +++ b/examples/api/bitvectors.cpp @@ -87,8 +87,8 @@ int main() { Expr new_x_eq_new_x_ = em.mkExpr(kind::EQUAL, new_x, new_x_); cout << " Querying: " << new_x_eq_new_x_ << endl; - cout << " Expect valid. " << endl; - cout << " CVC4: " << smt.query(new_x_eq_new_x_) << endl; + cout << " Expect entailed. " << endl; + cout << " CVC4: " << smt.checkEntailed(new_x_eq_new_x_) << endl; cout << " Popping context. " << endl; smt.pop(); @@ -103,14 +103,14 @@ int main() { smt.assertFormula(assignment2); cout << " Querying: " << new_x_eq_new_x_ << endl; - cout << " Expect valid. " << endl; - cout << " CVC4: " << smt.query(new_x_eq_new_x_) << endl; + cout << " Expect ENTAILED. " << endl; + cout << " CVC4: " << smt.checkEntailed(new_x_eq_new_x_) << endl; Expr x_neq_x = em.mkExpr(kind::EQUAL, x, x).notExpr(); std::vector<Expr> v{new_x_eq_new_x_, x_neq_x}; cout << " Querying: " << v << endl; - cout << " Expect invalid. " << endl; - cout << " CVC4: " << smt.query(v) << endl; + cout << " Expect NOT_ENTAILED. " << endl; + cout << " CVC4: " << smt.checkEntailed(v) << endl; // Assert that a is odd Expr extract_op = em.mkConst(BitVectorExtract(0, 0)); diff --git a/examples/api/combination-new.cpp b/examples/api/combination-new.cpp index 5284a0119..546d9ee9c 100644 --- a/examples/api/combination-new.cpp +++ b/examples/api/combination-new.cpp @@ -82,9 +82,10 @@ int main() cout << "Given the following assertions:" << endl << assertions << endl << endl; - cout << "Prove x /= y is valid. " << endl - << "CVC4: " << slv.checkValidAssuming(slv.mkTerm(DISTINCT, x, y)) - << "." << endl << endl; + cout << "Prove x /= y is entailed. " << endl + << "CVC4: " << slv.checkEntailed(slv.mkTerm(DISTINCT, x, y)) << "." + << endl + << endl; cout << "Call checkSat to show that the assertions are satisfiable. " << endl diff --git a/examples/api/combination.cpp b/examples/api/combination.cpp index 646e6b17a..2e972a543 100644 --- a/examples/api/combination.cpp +++ b/examples/api/combination.cpp @@ -86,8 +86,8 @@ int main() { cout << "Given the following assumptions:" << endl << assumptions << endl - << "Prove x /= y is valid. " - << "CVC4 says: " << smt.query(em.mkExpr(kind::DISTINCT, x, y)) + << "Prove x /= y is entailed. " + << "CVC4 says: " << smt.checkEntailed(em.mkExpr(kind::DISTINCT, x, y)) << "." << endl; cout << "Now we call checksat on a trivial query to show that" << endl diff --git a/examples/api/extract-new.cpp b/examples/api/extract-new.cpp index 0f0f8243a..705cdd90f 100644 --- a/examples/api/extract-new.cpp +++ b/examples/api/extract-new.cpp @@ -47,9 +47,9 @@ int main() slv.assertFormula(eq); Term eq2 = slv.mkTerm(EQUAL, x_31_31, x_0_0); - cout << " Check validity assuming: " << eq2 << endl; - cout << " Expect valid. " << endl; - cout << " CVC4: " << slv.checkValidAssuming(eq2) << endl; + cout << " Check entailment assuming: " << eq2 << endl; + cout << " Expect ENTAILED. " << endl; + cout << " CVC4: " << slv.checkEntailed(eq2) << endl; return 0; } diff --git a/examples/api/extract.cpp b/examples/api/extract.cpp index a008ec718..e2558df28 100644 --- a/examples/api/extract.cpp +++ b/examples/api/extract.cpp @@ -48,8 +48,8 @@ int main() { Expr eq2 = em.mkExpr(kind::EQUAL, x_31_31, x_0_0); cout << " Querying: " << eq2 << endl; - cout << " Expect valid. " << endl; - cout << " CVC4: " << smt.query(eq2) << endl; + cout << " Expect entailed. " << endl; + cout << " CVC4: " << smt.checkEntailed(eq2) << endl; return 0; } diff --git a/examples/api/helloworld-new.cpp b/examples/api/helloworld-new.cpp index ab869f03c..f442c1412 100644 --- a/examples/api/helloworld-new.cpp +++ b/examples/api/helloworld-new.cpp @@ -24,7 +24,7 @@ int main() { Solver slv; Term helloworld = slv.mkVar(slv.getBooleanSort(), "Hello World!"); - std::cout << helloworld << " is " << slv.checkValidAssuming(helloworld) + std::cout << helloworld << " is " << slv.checkEntailed(helloworld) << std::endl; return 0; } diff --git a/examples/api/helloworld.cpp b/examples/api/helloworld.cpp index befeaa7bd..49a334504 100644 --- a/examples/api/helloworld.cpp +++ b/examples/api/helloworld.cpp @@ -23,6 +23,7 @@ int main() { ExprManager em; Expr helloworld = em.mkVar("Hello World!", em.booleanType()); SmtEngine smt(&em); - std::cout << helloworld << " is " << smt.query(helloworld) << std::endl; + std::cout << helloworld << " is " << smt.checkEntailed(helloworld) + << std::endl; return 0; } diff --git a/examples/api/java/BitVectors.java b/examples/api/java/BitVectors.java index 17111b63a..bb2245a6f 100644 --- a/examples/api/java/BitVectors.java +++ b/examples/api/java/BitVectors.java @@ -86,8 +86,8 @@ public class BitVectors { Expr new_x_eq_new_x_ = em.mkExpr(Kind.EQUAL, new_x, new_x_); System.out.println(" Querying: " + new_x_eq_new_x_); - System.out.println(" Expect valid. "); - System.out.println(" CVC4: " + smt.query(new_x_eq_new_x_)); + System.out.println(" Expect entailed. "); + System.out.println(" CVC4: " + smt.checkEntailed(new_x_eq_new_x_)); System.out.println(" Popping context. "); smt.pop(); @@ -102,7 +102,7 @@ public class BitVectors { smt.assertFormula(assignment2); System.out.println(" Querying: " + new_x_eq_new_x_); - System.out.println(" Expect valid. "); - System.out.println(" CVC4: " + smt.query(new_x_eq_new_x_)); + System.out.println(" Expect entailed. "); + System.out.println(" CVC4: " + smt.checkEntailed(new_x_eq_new_x_)); } } diff --git a/examples/api/java/Combination.java b/examples/api/java/Combination.java index 0c9ca84d6..53b1fa92d 100644 --- a/examples/api/java/Combination.java +++ b/examples/api/java/Combination.java @@ -83,10 +83,9 @@ public class Combination { System.out.println("Given the following assumptions:"); System.out.println(assumptions); - System.out.println("Prove x /= y is valid. " + - "CVC4 says: " + smt.query(em.mkExpr(Kind.DISTINCT, x, y)) + - "."); - + System.out.println("Prove x /= y is entailed. " + + "CVC4 says: " + smt.checkEntailed(em.mkExpr(Kind.DISTINCT, x, y)) + + "."); System.out.println("Now we call checksat on a trivial query to show that"); System.out.println("the assumptions are satisfiable: " + diff --git a/examples/api/java/HelloWorld.java b/examples/api/java/HelloWorld.java index 393ce40f0..a08f3d452 100644 --- a/examples/api/java/HelloWorld.java +++ b/examples/api/java/HelloWorld.java @@ -30,6 +30,6 @@ public class HelloWorld { Expr helloworld = em.mkVar("Hello World!", em.booleanType()); SmtEngine smt = new SmtEngine(em); - System.out.println(helloworld + " is " + smt.query(helloworld)); + System.out.println(helloworld + " is " + smt.checkEntailed(helloworld)); } } diff --git a/examples/api/java/LinearArith.java b/examples/api/java/LinearArith.java index 2ddf92584..e060263b4 100644 --- a/examples/api/java/LinearArith.java +++ b/examples/api/java/LinearArith.java @@ -61,8 +61,9 @@ public class LinearArith { smt.push(); Expr diff_leq_two_thirds = em.mkExpr(Kind.LEQ, diff, two_thirds); System.out.println("Prove that " + diff_leq_two_thirds + " with CVC4."); - System.out.println("CVC4 should report VALID."); - System.out.println("Result from CVC4 is: " + smt.query(diff_leq_two_thirds)); + System.out.println("CVC4 should report ENTAILED."); + System.out.println( + "Result from CVC4 is: " + smt.checkEntailed(diff_leq_two_thirds)); smt.pop(); System.out.println(); diff --git a/examples/api/linear_arith-new.cpp b/examples/api/linear_arith-new.cpp index a4ff9a2cc..887c35d24 100644 --- a/examples/api/linear_arith-new.cpp +++ b/examples/api/linear_arith-new.cpp @@ -62,9 +62,9 @@ int main() slv.push(); Term diff_leq_two_thirds = slv.mkTerm(LEQ, diff, two_thirds); cout << "Prove that " << diff_leq_two_thirds << " with CVC4." << endl; - cout << "CVC4 should report VALID." << endl; - cout << "Result from CVC4 is: " - << slv.checkValidAssuming(diff_leq_two_thirds) << endl; + cout << "CVC4 should report ENTAILED." << endl; + cout << "Result from CVC4 is: " << slv.checkEntailed(diff_leq_two_thirds) + << endl; slv.pop(); cout << endl; diff --git a/examples/api/linear_arith.cpp b/examples/api/linear_arith.cpp index f1c8b861c..9e605f85c 100644 --- a/examples/api/linear_arith.cpp +++ b/examples/api/linear_arith.cpp @@ -62,8 +62,9 @@ int main() { smt.push(); Expr diff_leq_two_thirds = em.mkExpr(kind::LEQ, diff, two_thirds); cout << "Prove that " << diff_leq_two_thirds << " with CVC4." << endl; - cout << "CVC4 should report VALID." << endl; - cout << "Result from CVC4 is: " << smt.query(diff_leq_two_thirds) << endl; + cout << "CVC4 should report ENTAILED." << endl; + cout << "Result from CVC4 is: " << smt.checkEntailed(diff_leq_two_thirds) + << endl; smt.pop(); cout << endl; diff --git a/examples/api/python/bitvectors.py b/examples/api/python/bitvectors.py index 773974cc7..8e4e1b682 100755 --- a/examples/api/python/bitvectors.py +++ b/examples/api/python/bitvectors.py @@ -4,7 +4,7 @@ #! \file bitvectors.py ## \verbatim ## Top contributors (to current version): - ## Makai Mann + ## Makai Mann, Aina Niemetz ## This file is part of the CVC4 project. ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ## in the top-level source directory) and their institutional affiliations. @@ -82,9 +82,9 @@ if __name__ == "__main__": slv.assertFormula(assignment1) new_x_eq_new_x_ = slv.mkTerm(kinds.Equal, new_x, new_x_) - print("Checking validity assuming:", new_x_eq_new_x_) - print("Expect valid.") - print("CVC4:", slv.checkValidAssuming(new_x_eq_new_x_)) + print("Checking entailment assuming:", new_x_eq_new_x_) + print("Expect ENTAILED.") + print("CVC4:", slv.checkEntailment(new_x_eq_new_x_)) print("Popping context.") slv.pop() @@ -98,16 +98,16 @@ if __name__ == "__main__": print("Asserting {} to CVC4".format(assignment2)) slv.assertFormula(assignment2) - print("Checking validity assuming:", new_x_eq_new_x_) - print("Expect valid.") - print("CVC4:", slv.checkValidAssuming(new_x_eq_new_x_)) + print("Checking entailment assuming:", new_x_eq_new_x_) + print("Expect ENTAILED.") + print("CVC4:", slv.checkEntailed(new_x_eq_new_x_)) x_neq_x = slv.mkTerm(kinds.Equal, x, x).notTerm() v = [new_x_eq_new_x_, x_neq_x] - print("Check Validity Assuming: ", v) - print("Expect invalid.") - print("CVC4:", slv.checkValidAssuming(v)) + print("Check entailment assuming: ", v) + print("Expect NOT_ENTAILED.") + print("CVC4:", slv.checkEntailed(v)) # Assert that a is odd extract_op = slv.mkOp(kinds.BVExtract, 0, 0) diff --git a/examples/api/python/combination.py b/examples/api/python/combination.py index 1b488d7d5..7a8055bdf 100755 --- a/examples/api/python/combination.py +++ b/examples/api/python/combination.py @@ -4,7 +4,7 @@ #! \file combination.py ## \verbatim ## Top contributors (to current version): - ## Makai Mann + ## Makai Mann, Aina Niemetz ## This file is part of the CVC4 project. ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ## in the top-level source directory) and their institutional affiliations. @@ -69,8 +69,8 @@ if __name__ == "__main__": slv.assertFormula(assertions) print("Given the following assertions:", assertions, "\n") - print("Prove x /= y is valid.\nCVC4: ", - slv.checkValidAssuming(slv.mkTerm(kinds.Distinct, x, y)), "\n") + print("Prove x /= y is entailed.\nCVC4: ", + slv.checkEntailed(slv.mkTerm(kinds.Distinct, x, y)), "\n") print("Call checkSat to show that the assertions are satisfiable") print("CVC4:", slv.checkSat(), "\n") diff --git a/examples/api/python/extract.py b/examples/api/python/extract.py index 1bfdf652a..2b8714739 100755 --- a/examples/api/python/extract.py +++ b/examples/api/python/extract.py @@ -4,7 +4,7 @@ #! \file extract.py ## \verbatim ## Top contributors (to current version): - ## Makai Mann + ## Makai Mann, Aina Niemetz ## This file is part of the CVC4 project. ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ## in the top-level source directory) and their institutional affiliations. @@ -46,6 +46,6 @@ if __name__ == "__main__": slv.assertFormula(eq) eq2 = slv.mkTerm(Equal, x_31_31, x_0_0) - print("Check validity assuming:", eq2) - print("Expect valid") - print("CVC4:", slv.checkValidAssuming(eq2)) + print("Check entailment assuming:", eq2) + print("Expect ENTAILED") + print("CVC4:", slv.checkEntailed(eq2)) diff --git a/examples/api/python/helloworld.py b/examples/api/python/helloworld.py index 472cf945b..5607d7f83 100755 --- a/examples/api/python/helloworld.py +++ b/examples/api/python/helloworld.py @@ -4,7 +4,7 @@ #! \file helloworld.py ## \verbatim ## Top contributors (to current version): -## Makai Mann +## Makai Mann, Aina Niemetz ## This file is part of the CVC4 project. ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ## in the top-level source directory) and their institutional affiliations. @@ -18,4 +18,4 @@ from pycvc4 import kinds if __name__ == "__main__": slv = pycvc4.Solver() helloworld = slv.mkConst(slv.getBooleanSort(), "Hello World!") - print(helloworld, "is", slv.checkValidAssuming(helloworld)) + print(helloworld, "is", slv.checkEntailed(helloworld)) diff --git a/examples/api/python/linear_arith.py b/examples/api/python/linear_arith.py index 6ae6427b1..bab9680b3 100755 --- a/examples/api/python/linear_arith.py +++ b/examples/api/python/linear_arith.py @@ -4,7 +4,7 @@ #! \file linear_arith.py ## \verbatim ## Top contributors (to current version): -## Makai Mann +## Makai Mann, Aina Niemetz ## This file is part of the CVC4 project. ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ## in the top-level source directory) and their institutional affiliations. @@ -54,9 +54,9 @@ if __name__ == "__main__": slv.push() diff_leq_two_thirds = slv.mkTerm(kinds.Leq, diff, two_thirds) print("Prove that", diff_leq_two_thirds, "with CVC4") - print("CVC4 should report VALID") + print("CVC4 should report ENTAILED") print("Result from CVC4 is:", - slv.checkValidAssuming(diff_leq_two_thirds)) + slv.checkEntailed(diff_leq_two_thirds)) slv.pop() print() diff --git a/examples/api/python/sets.py b/examples/api/python/sets.py index 584880b2b..b69c56b56 100755 --- a/examples/api/python/sets.py +++ b/examples/api/python/sets.py @@ -4,7 +4,7 @@ #! \file sets.py ## \verbatim ## Top contributors (to current version): -## Makai Mann +## Makai Mann, Aina Niemetz ## This file is part of the CVC4 project. ## Copyright (c) 2009-2018 by the authors listed in the file AUTHORS ## in the top-level source directory) and their institutional affiliations. @@ -48,7 +48,7 @@ if __name__ == "__main__": theorem = slv.mkTerm(kinds.Equal, lhs, rhs) print("CVC4 reports: {} is {}".format(theorem, - slv.checkValidAssuming(theorem))) + slv.checkEntailed(theorem))) # Verify emptset is a subset of any set @@ -58,7 +58,7 @@ if __name__ == "__main__": theorem = slv.mkTerm(kinds.Subset, emptyset, A) print("CVC4 reports: {} is {}".format(theorem, - slv.checkValidAssuming(theorem))) + slv.checkEntailed(theorem))) # Find me an element in 1, 2 intersection 2, 3, if there is one. diff --git a/examples/api/sets-new.cpp b/examples/api/sets-new.cpp index 2ca3db9d2..21ef925df 100644 --- a/examples/api/sets-new.cpp +++ b/examples/api/sets-new.cpp @@ -52,8 +52,8 @@ int main() Term theorem = slv.mkTerm(EQUAL, lhs, rhs); - cout << "CVC4 reports: " << theorem << " is " - << slv.checkValidAssuming(theorem) << "." << endl; + cout << "CVC4 reports: " << theorem << " is " << slv.checkEntailed(theorem) + << "." << endl; } // Verify emptset is a subset of any set @@ -63,8 +63,8 @@ int main() Term theorem = slv.mkTerm(SUBSET, emptyset, A); - cout << "CVC4 reports: " << theorem << " is " - << slv.checkValidAssuming(theorem) << "." << endl; + cout << "CVC4 reports: " << theorem << " is " << slv.checkEntailed(theorem) + << "." << endl; } // Find me an element in {1, 2} intersection {2, 3}, if there is one. diff --git a/examples/api/sets.cpp b/examples/api/sets.cpp index 9fb342431..eb6a5a350 100644 --- a/examples/api/sets.cpp +++ b/examples/api/sets.cpp @@ -55,7 +55,8 @@ int main() { Expr theorem = em.mkExpr(kind::EQUAL, lhs, rhs); - cout << "CVC4 reports: " << theorem << " is " << smt.query(theorem) << "." << endl; + cout << "CVC4 reports: " << theorem << " is " << smt.checkEntailed(theorem) + << "." << endl; } // Verify emptset is a subset of any set @@ -65,7 +66,8 @@ int main() { Expr theorem = em.mkExpr(kind::SUBSET, emptyset, A); - cout << "CVC4 reports: " << theorem << " is " << smt.query(theorem) << "." << endl; + cout << "CVC4 reports: " << theorem << " is " << smt.checkEntailed(theorem) + << "." << endl; } // Find me an element in {1, 2} intersection {2, 3}, if there is one. diff --git a/examples/simple_vc_cxx.cpp b/examples/simple_vc_cxx.cpp index 25a05a1a7..cfd14b089 100644 --- a/examples/simple_vc_cxx.cpp +++ b/examples/simple_vc_cxx.cpp @@ -50,9 +50,9 @@ int main() { em.mkExpr(kind::AND, x_positive, y_positive). impExpr(twox_plus_y_geq_3); - cout << "Checking validity of formula " << formula << " with CVC4." << endl; - cout << "CVC4 should report VALID." << endl; - cout << "Result from CVC4 is: " << smt.query(formula) << endl; + cout << "Checking entailment of formula " << formula << " with CVC4." << endl; + cout << "CVC4 should report ENTAILED." << endl; + cout << "Result from CVC4 is: " << smt.checkEntailed(formula) << endl; return 0; } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 4520ee421..c1c8c15e0 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -236,6 +236,8 @@ libcvc4_add_sources( smt/model_core_builder.h smt/model_blocker.cpp smt/model_blocker.h + smt/set_defaults.cpp + smt/set_defaults.h smt/smt_engine.cpp smt/smt_engine.h smt/smt_engine_scope.cpp @@ -291,6 +293,7 @@ libcvc4_add_sources( theory/arith/linear_equality.h theory/arith/matrix.cpp theory/arith/matrix.h + theory/arith/nl_lemma_utils.cpp theory/arith/nl_lemma_utils.h theory/arith/nl_model.cpp theory/arith/nl_model.h @@ -316,6 +319,8 @@ libcvc4_add_sources( theory/arith/theory_arith_private.h theory/arith/theory_arith_private_forward.h theory/arith/theory_arith_type_rules.h + theory/arith/transcendental_solver.cpp + theory/arith/transcendental_solver.h theory/arith/type_enumerator.h theory/arrays/array_info.cpp theory/arrays/array_info.h @@ -422,14 +427,6 @@ libcvc4_add_sources( theory/fp/theory_fp_rewriter.h theory/fp/theory_fp_type_rules.h theory/fp/type_enumerator.h - theory/idl/idl_assertion.cpp - theory/idl/idl_assertion.h - theory/idl/idl_assertion_db.cpp - theory/idl/idl_assertion_db.h - theory/idl/idl_model.cpp - theory/idl/idl_model.h - theory/idl/theory_idl.cpp - theory/idl/theory_idl.h theory/interrupted.h theory/logic_info.cpp theory/logic_info.h @@ -679,6 +676,8 @@ libcvc4_add_sources( theory/strings/regexp_operation.h theory/strings/regexp_solver.cpp theory/strings/regexp_solver.h + theory/strings/rewrites.cpp + theory/strings/rewrites.h theory/strings/sequences_rewriter.cpp theory/strings/sequences_rewriter.h theory/strings/sequences_stats.cpp @@ -762,8 +761,7 @@ set(KINDS_FILES ${PROJECT_SOURCE_DIR}/src/theory/sep/kinds ${PROJECT_SOURCE_DIR}/src/theory/sets/kinds ${PROJECT_SOURCE_DIR}/src/theory/strings/kinds - ${PROJECT_SOURCE_DIR}/src/theory/quantifiers/kinds - ${PROJECT_SOURCE_DIR}/src/theory/idl/kinds) + ${PROJECT_SOURCE_DIR}/src/theory/quantifiers/kinds) #-----------------------------------------------------------------------------# # Add subdirectories @@ -898,7 +896,6 @@ install(FILES expr/datatype.h expr/emptyset.h expr/expr_iomanip.h - expr/expr_stream.h expr/record.h expr/symbol_table.h expr/type.h @@ -964,6 +961,7 @@ install(FILES util/result.h util/sexpr.h util/statistics.h + util/string.h util/tuple.h util/unsafe_interrupt_exception.h ${CMAKE_CURRENT_BINARY_DIR}/util/floatingpoint.h diff --git a/src/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp index f563e83f5..2e6e70d6b 100644 --- a/src/api/cvc4cpp.cpp +++ b/src/api/cvc4cpp.cpp @@ -277,6 +277,7 @@ const static std::unordered_map<Kind, CVC4::Kind, KindHashFunction> s_kinds{ {REGEXP_PLUS, CVC4::Kind::REGEXP_PLUS}, {REGEXP_OPT, CVC4::Kind::REGEXP_OPT}, {REGEXP_RANGE, CVC4::Kind::REGEXP_RANGE}, + {REGEXP_REPEAT, CVC4::Kind::REGEXP_REPEAT}, {REGEXP_LOOP, CVC4::Kind::REGEXP_LOOP}, {REGEXP_EMPTY, CVC4::Kind::REGEXP_EMPTY}, {REGEXP_SIGMA, CVC4::Kind::REGEXP_SIGMA}, @@ -544,6 +545,7 @@ const static std::unordered_map<CVC4::Kind, Kind, CVC4::kind::KindHashFunction> {CVC4::Kind::REGEXP_PLUS, REGEXP_PLUS}, {CVC4::Kind::REGEXP_OPT, REGEXP_OPT}, {CVC4::Kind::REGEXP_RANGE, REGEXP_RANGE}, + {CVC4::Kind::REGEXP_REPEAT, REGEXP_REPEAT}, {CVC4::Kind::REGEXP_LOOP, REGEXP_LOOP}, {CVC4::Kind::REGEXP_EMPTY, REGEXP_EMPTY}, {CVC4::Kind::REGEXP_SIGMA, REGEXP_SIGMA}, @@ -766,22 +768,22 @@ bool Result::isSatUnknown(void) const && d_result->isSat() == CVC4::Result::SAT_UNKNOWN; } -bool Result::isValid(void) const +bool Result::isEntailed(void) const { - return d_result->getType() == CVC4::Result::TYPE_VALIDITY - && d_result->isValid() == CVC4::Result::VALID; + return d_result->getType() == CVC4::Result::TYPE_ENTAILMENT + && d_result->isEntailed() == CVC4::Result::ENTAILED; } -bool Result::isInvalid(void) const +bool Result::isNotEntailed(void) const { - return d_result->getType() == CVC4::Result::TYPE_VALIDITY - && d_result->isValid() == CVC4::Result::INVALID; + return d_result->getType() == CVC4::Result::TYPE_ENTAILMENT + && d_result->isEntailed() == CVC4::Result::NOT_ENTAILED; } -bool Result::isValidUnknown(void) const +bool Result::isEntailmentUnknown(void) const { - return d_result->getType() == CVC4::Result::TYPE_VALIDITY - && d_result->isValid() == CVC4::Result::VALIDITY_UNKNOWN; + return d_result->getType() == CVC4::Result::TYPE_ENTAILMENT + && d_result->isEntailed() == CVC4::Result::ENTAILMENT_UNKNOWN; } bool Result::operator==(const Result& r) const @@ -2292,7 +2294,7 @@ Term Solver::mkValHelper(T t) const return res; } -Term Solver::mkRealFromStrHelper(std::string s) const +Term Solver::mkRealFromStrHelper(const std::string& s) const { /* CLN and GMP handle this case differently, CLN interprets it as 0, GMP * throws an std::invalid_argument exception. For consistency, we treat it @@ -2316,7 +2318,7 @@ Term Solver::mkBVFromIntHelper(uint32_t size, uint64_t val) const CVC4_API_SOLVER_TRY_CATCH_END; } -Term Solver::mkBVFromStrHelper(std::string s, uint32_t base) const +Term Solver::mkBVFromStrHelper(const std::string& s, uint32_t base) const { CVC4_API_ARG_CHECK_EXPECTED(!s.empty(), s) << "a non-empty string"; CVC4_API_ARG_CHECK_EXPECTED(base == 2 || base == 10 || base == 16, s) @@ -2326,7 +2328,7 @@ Term Solver::mkBVFromStrHelper(std::string s, uint32_t base) const } Term Solver::mkBVFromStrHelper(uint32_t size, - std::string s, + const std::string& s, uint32_t base) const { CVC4_API_ARG_CHECK_EXPECTED(!s.empty(), s) << "a non-empty string"; @@ -2351,6 +2353,20 @@ Term Solver::mkBVFromStrHelper(uint32_t size, return mkValHelper<CVC4::BitVector>(CVC4::BitVector(size, val)); } +Term Solver::mkCharFromStrHelper(const std::string& s) const +{ + CVC4_API_CHECK(s.find_first_not_of("0123456789abcdefABCDEF", 0) + == std::string::npos + && s.size() <= 5 && s.size() > 0) + << "Unexpected string for hexidecimal character " << s; + uint32_t val = static_cast<uint32_t>(std::stoul(s, 0, 16)); + CVC4_API_CHECK(val < String::num_codes()) + << "Not a valid code point for hexidecimal character " << s; + std::vector<unsigned> cpts; + cpts.push_back(val); + return mkValHelper<CVC4::String>(CVC4::String(cpts)); +} + Term Solver::mkTermFromKind(Kind kind) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; @@ -2949,6 +2965,21 @@ Term Solver::mkString(const std::vector<unsigned>& s) const CVC4_API_SOLVER_TRY_CATCH_END; } +Term Solver::mkChar(const std::string& s) const +{ + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + return mkCharFromStrHelper(s); + CVC4_API_SOLVER_TRY_CATCH_END; +} + +Term Solver::mkChar(const char* s) const +{ + CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_ARG_CHECK_NOT_NULLPTR(s); + return mkCharFromStrHelper(std::string(s)); + CVC4_API_SOLVER_TRY_CATCH_END; +} + Term Solver::mkUniverseSet(Sort sort) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; @@ -3490,6 +3521,11 @@ Op Solver::mkOp(Kind kind, uint32_t arg) const kind, *mkValHelper<CVC4::TupleUpdate>(CVC4::TupleUpdate(arg)).d_expr.get()); break; + case REGEXP_REPEAT: + res = Op(kind, + *mkValHelper<CVC4::RegExpRepeat>(CVC4::RegExpRepeat(arg)) + .d_expr.get()); + break; default: CVC4_API_KIND_CHECK_EXPECTED(false, kind) << "operator kind with uint32_t argument"; @@ -3550,6 +3586,11 @@ Op Solver::mkOp(Kind kind, uint32_t arg1, uint32_t arg2) const CVC4::FloatingPointToFPGeneric(arg1, arg2)) .d_expr.get()); break; + case REGEXP_LOOP: + res = Op(kind, + *mkValHelper<CVC4::RegExpLoop>(CVC4::RegExpLoop(arg1, arg2)) + .d_expr.get()); + break; default: CVC4_API_KIND_CHECK_EXPECTED(false, kind) << "operator kind with two uint32_t arguments"; @@ -3573,7 +3614,7 @@ Term Solver::simplify(const Term& t) CVC4_API_SOLVER_TRY_CATCH_END; } -Result Solver::checkValid(void) const +Result Solver::checkEntailed(Term term) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); @@ -3581,14 +3622,15 @@ Result Solver::checkValid(void) const || CVC4::options::incrementalSolving()) << "Cannot make multiple queries unless incremental solving is enabled " "(try --incremental)"; + CVC4_API_ARG_CHECK_NOT_NULL(term); - CVC4::Result r = d_smtEngine->query(); + CVC4::Result r = d_smtEngine->checkEntailed(*term.d_expr); return Result(r); CVC4_API_SOLVER_TRY_CATCH_END; } -Result Solver::checkValidAssuming(Term assumption) const +Result Solver::checkEntailed(const std::vector<Term>& terms) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); @@ -3596,29 +3638,13 @@ Result Solver::checkValidAssuming(Term assumption) const || CVC4::options::incrementalSolving()) << "Cannot make multiple queries unless incremental solving is enabled " "(try --incremental)"; - CVC4_API_ARG_CHECK_NOT_NULL(assumption); - - CVC4::Result r = d_smtEngine->query(*assumption.d_expr); - return Result(r); - - CVC4_API_SOLVER_TRY_CATCH_END; -} - -Result Solver::checkValidAssuming(const std::vector<Term>& assumptions) const -{ - CVC4_API_SOLVER_TRY_CATCH_BEGIN; - CVC4::ExprManagerScope exmgrs(*(d_exprMgr.get())); - CVC4_API_CHECK(!d_smtEngine->isQueryMade() - || CVC4::options::incrementalSolving()) - << "Cannot make multiple queries unless incremental solving is enabled " - "(try --incremental)"; - for (const Term& assumption : assumptions) + for (const Term& term : terms) { - CVC4_API_ARG_CHECK_NOT_NULL(assumption); + CVC4_API_ARG_CHECK_NOT_NULL(term); } - std::vector<Expr> eassumptions = termVectorToExprs(assumptions); - CVC4::Result r = d_smtEngine->query(eassumptions); + std::vector<Expr> exprs = termVectorToExprs(terms); + CVC4::Result r = d_smtEngine->checkEntailed(exprs); return Result(r); CVC4_API_SOLVER_TRY_CATCH_END; diff --git a/src/api/cvc4cpp.h b/src/api/cvc4cpp.h index 3c99d2480..edff95a2f 100644 --- a/src/api/cvc4cpp.h +++ b/src/api/cvc4cpp.h @@ -114,22 +114,21 @@ class CVC4_PUBLIC Result bool isSatUnknown() const; /** - * Return true if corresponding query was a valid checkValid() or - * checkValidAssuming() query. + * Return true if corresponding query was an entailed checkEntailed() query. */ - bool isValid() const; + bool isEntailed() const; /** - * Return true if corresponding query was an invalid checkValid() or - * checkValidAssuming() query. + * Return true if corresponding query was a checkEntailed() query that is + * not entailed. */ - bool isInvalid() const; + bool isNotEntailed() const; /** - * Return true if query was a checkValid() or checkValidAssuming() query - * and CVC4 was not able to determine (in)validity. + * Return true if query was a checkEntailed() () query and CVC4 was not able + * to determine if it is entailed. */ - bool isValidUnknown() const; + bool isEntailmentUnknown() const; /** * Operator overloading for equality of two results. @@ -2304,6 +2303,20 @@ class CVC4_PUBLIC Solver Term mkString(const std::vector<unsigned>& s) const; /** + * Create a character constant from a given string. + * @param s the string denoting the code point of the character (in base 16) + * @return the character constant + */ + Term mkChar(const std::string& s) const; + + /** + * Create a character constant from a given string. + * @param s the string denoting the code point of the character (in base 16) + * @return the character constant + */ + Term mkChar(const char* s) const; + + /** * Create a universe set of the given sort. * @param sort the sort of the set elements * @return the universe set constant @@ -2555,24 +2568,19 @@ class CVC4_PUBLIC Solver Result checkSatAssuming(const std::vector<Term>& assumptions) const; /** - * Check validity. - * @return the result of the validity check. + * Check entailment of the given formula w.r.t. the current set of assertions. + * @param term the formula to check entailment for + * @return the result of the entailment check. */ - Result checkValid() const; + Result checkEntailed(Term term) const; /** - * Check validity assuming the given formula. - * @param assumption the formula to assume - * @return the result of the validity check. - */ - Result checkValidAssuming(Term assumption) const; - - /** - * Check validity assuming the given formulas. - * @param assumptions the formulas to assume - * @return the result of the validity check. + * Check entailment of the given set of given formulas w.r.t. the current + * set of assertions. + * @param terms the terms to check entailment for + * @return the result of the entailmentcheck. */ - Result checkValidAssuming(const std::vector<Term>& assumptions) const; + Result checkEntailed(const std::vector<Term>& terms) const; /** * Create datatype sort. @@ -2818,18 +2826,20 @@ class CVC4_PUBLIC Solver template <typename T> Term mkValHelper(T t) const; /* Helper for mkReal functions that take a string as argument. */ - Term mkRealFromStrHelper(std::string s) const; + Term mkRealFromStrHelper(const std::string& s) const; /* Helper for mkBitVector functions that take a string as argument. */ - Term mkBVFromStrHelper(std::string s, uint32_t base) const; + Term mkBVFromStrHelper(const std::string& s, uint32_t base) const; /* Helper for mkBitVector functions that take a string and a size as * arguments. */ - Term mkBVFromStrHelper(uint32_t size, std::string s, uint32_t base) const; + Term mkBVFromStrHelper(uint32_t size, const std::string& s, uint32_t base) const; /* Helper for mkBitVector functions that take an integer as argument. */ Term mkBVFromIntHelper(uint32_t size, uint64_t val) const; /* Helper for setLogic. */ void setLogicHelper(const std::string& logic) const; /* Helper for mkTerm functions that create Term from a Kind */ Term mkTermFromKind(Kind kind) const; + /* Helper for mkChar functions that take a string as argument. */ + Term mkCharFromStrHelper(const std::string& s) const; /** * Helper function that ensures that a given term is of sort real (as opposed diff --git a/src/api/cvc4cppkind.h b/src/api/cvc4cppkind.h index d399ad616..f8e1fb90c 100644 --- a/src/api/cvc4cppkind.h +++ b/src/api/cvc4cppkind.h @@ -2175,15 +2175,37 @@ enum CVC4_PUBLIC Kind : int32_t */ REGEXP_RANGE, /** - * Regexp loop. - * Parameters: 2 (3) - * -[1]: Term of sort RegExp - * -[2]: Lower bound for the number of repetitions of the first argument - * -[3]: Upper bound for the number of repetitions of the first argument + * Operator for regular expression repeat. + * Parameters: 1 + * -[1]: The number of repetitions * Create with: - * mkTerm(Kind kind, Term child1, Term child2) - * mkTerm(Kind kind, Term child1, Term child2, Term child3) - * mkTerm(Kind kind, const std::vector<Term>& children) + * mkOp(Kind kind, uint32_t param) + * + * Apply regular expression loop. + * Parameters: 2 + * -[1]: Op of kind REGEXP_REPEAT + * -[2]: Term of regular expression sort + * Create with: + * mkTerm(Op op, Term child) + * mkTerm(Op op, const std::vector<Term>& children) + */ + REGEXP_REPEAT, + /** + * Operator for regular expression loop, from lower bound to upper bound + * number of repetitions. + * Parameters: 2 + * -[1]: The lower bound + * -[2]: The upper bound + * Create with: + * mkOp(Kind kind, uint32_t param, uint32_t param) + * + * Apply regular expression loop. + * Parameters: 2 + * -[1]: Op of kind REGEXP_LOOP + * -[2]: Term of regular expression sort + * Create with: + * mkTerm(Op op, Term child) + * mkTerm(Op op, const std::vector<Term>& children) */ REGEXP_LOOP, /** diff --git a/src/api/python/cvc4.pxd b/src/api/python/cvc4.pxd index bbff6f58b..d81d0c0bf 100644 --- a/src/api/python/cvc4.pxd +++ b/src/api/python/cvc4.pxd @@ -87,9 +87,9 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": bint isSat() except + bint isUnsat() except + bint isSatUnknown() except + - bint isValid() except + - bint isInvalid() except + - bint isValidUnknown() except + + bint isEntailed() except + + bint isNotEntailed() except + + bint isEntailmentUnknown() except + string getUnknownExplanation() except + string toString() except + @@ -168,8 +168,7 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": void assertFormula(Term term) except + Result checkSat() except + Result checkSatAssuming(const vector[Term]& assumptions) except + - Result checkValid() except + - Result checkValidAssuming(const vector[Term]& assumptions) except + + Result checkEntailed(const vector[Term]& assumptions) except + Sort declareDatatype(const string& symbol, const vector[DatatypeConstructorDecl]& ctors) Term declareFun(const string& symbol, Sort sort) except + Term declareFun(const string& symbol, const vector[Sort]& sorts, Sort sort) except + diff --git a/src/api/python/cvc4.pxi b/src/api/python/cvc4.pxi index 188753122..60bd89cbd 100644 --- a/src/api/python/cvc4.pxi +++ b/src/api/python/cvc4.pxi @@ -749,19 +749,11 @@ cdef class Solver: explanation = r.getUnknownExplanation().decode() return Result(name, explanation) - def checkValid(self): - cdef c_Result r = self.csolver.checkValid() - name = r.toString().decode() - explanation = "" - if r.isValidUnknown(): - explanation = r.getUnknownExplanation().decode() - return Result(name, explanation) - @expand_list_arg(num_req_args=0) - def checkValidAssuming(self, *assumptions): + def checkEntailed(self, *assumptions): ''' Supports the following arguments: - Result checkValidAssuming(List[Term] assumptions) + Result checkEntailed(List[Term] assumptions) where assumptions can also be comma-separated arguments of type (boolean) Term @@ -771,10 +763,10 @@ cdef class Solver: cdef vector[c_Term] v for a in assumptions: v.push_back((<Term?> a).cterm) - r = self.csolver.checkValidAssuming(<const vector[c_Term]&> v) + r = self.csolver.checkEntailed(<const vector[c_Term]&> v) name = r.toString().decode() explanation = "" - if r.isValidUnknown(): + if r.isEntailmentUnknown(): explanation = r.getUnknownExplanation().decode() return Result(name, explanation) diff --git a/src/base/configuration.cpp b/src/base/configuration.cpp index f907f212f..aed835f3f 100644 --- a/src/base/configuration.cpp +++ b/src/base/configuration.cpp @@ -50,10 +50,6 @@ bool Configuration::isStatisticsBuild() { return IS_STATISTICS_BUILD; } -bool Configuration::isReplayBuild() { - return IS_REPLAY_BUILD; -} - bool Configuration::isTracingBuild() { return IS_TRACING_BUILD; } diff --git a/src/base/configuration.h b/src/base/configuration.h index 60cdd5a9c..72ccb2301 100644 --- a/src/base/configuration.h +++ b/src/base/configuration.h @@ -47,8 +47,6 @@ public: static bool isStatisticsBuild(); - static bool isReplayBuild(); - static bool isTracingBuild(); static bool isDumpingBuild(); diff --git a/src/base/configuration_private.h b/src/base/configuration_private.h index f3e76d53b..77db0b51c 100644 --- a/src/base/configuration_private.h +++ b/src/base/configuration_private.h @@ -36,12 +36,6 @@ namespace CVC4 { # define IS_STATISTICS_BUILD false #endif /* CVC4_STATISTICS_ON */ -#ifdef CVC4_REPLAY -# define IS_REPLAY_BUILD true -#else /* CVC4_REPLAY */ -# define IS_REPLAY_BUILD false -#endif /* CVC4_REPLAY */ - #ifdef CVC4_TRACING # define IS_TRACING_BUILD true #else /* CVC4_TRACING */ diff --git a/src/bindings/java/CMakeLists.txt b/src/bindings/java/CMakeLists.txt index 344387ed9..c5abf9b27 100644 --- a/src/bindings/java/CMakeLists.txt +++ b/src/bindings/java/CMakeLists.txt @@ -65,7 +65,6 @@ set(gen_java_files ${CMAKE_CURRENT_BINARY_DIR}/ExprHashFunction.java ${CMAKE_CURRENT_BINARY_DIR}/ExprManager.java ${CMAKE_CURRENT_BINARY_DIR}/ExprManagerMapCollection.java - ${CMAKE_CURRENT_BINARY_DIR}/ExprStream.java ${CMAKE_CURRENT_BINARY_DIR}/FloatingPoint.java ${CMAKE_CURRENT_BINARY_DIR}/FloatingPointConvertSort.java ${CMAKE_CURRENT_BINARY_DIR}/FloatingPointSize.java @@ -112,6 +111,8 @@ set(gen_java_files ${CMAKE_CURRENT_BINARY_DIR}/RecordUpdate.java ${CMAKE_CURRENT_BINARY_DIR}/RecordUpdateHashFunction.java ${CMAKE_CURRENT_BINARY_DIR}/RecoverableModalException.java + ${CMAKE_CURRENT_BINARY_DIR}/RegExpLoop.java + ${CMAKE_CURRENT_BINARY_DIR}/RegExpRepeat.java ${CMAKE_CURRENT_BINARY_DIR}/RegExpType.java ${CMAKE_CURRENT_BINARY_DIR}/Result.java ${CMAKE_CURRENT_BINARY_DIR}/RoundingMode.java diff --git a/src/cvc4.i b/src/cvc4.i index f9f8f5743..01fd088a8 100644 --- a/src/cvc4.i +++ b/src/cvc4.i @@ -278,6 +278,7 @@ std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; %include "util/result.i" %include "util/sexpr.i" %include "util/statistics.i" +%include "util/string.i" %include "util/tuple.i" %include "util/unsafe_interrupt_exception.i" @@ -300,7 +301,6 @@ std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; // The remainder of the includes: %include "expr/expr.i" %include "expr/expr_manager.i" -%include "expr/expr_stream.i" %include "expr/variable_type_map.i" %include "options/option_exception.i" %include "options/options.i" diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt index d1faa8ffb..a308e536c 100644 --- a/src/expr/CMakeLists.txt +++ b/src/expr/CMakeLists.txt @@ -12,7 +12,6 @@ libcvc4_add_sources( expr_iomanip.cpp expr_iomanip.h expr_manager_scope.h - expr_stream.h kind_map.h match_trie.cpp match_trie.h @@ -29,6 +28,8 @@ libcvc4_add_sources( node_self_iterator.h node_trie.cpp node_trie.h + node_traversal.cpp + node_traversal.h node_value.cpp node_value.h node_visitor.h diff --git a/src/expr/expr_stream.h b/src/expr/expr_stream.h deleted file mode 100644 index d31e8e4fc..000000000 --- a/src/expr/expr_stream.h +++ /dev/null @@ -1,45 +0,0 @@ -/********************* */ -/*! \file expr_stream.h - ** \verbatim - ** Top contributors (to current version): - ** Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief A stream interface for expressions - ** - ** A stream interface for expressions. - **/ - -#include "cvc4_public.h" - -#ifndef CVC4__EXPR_STREAM_H -#define CVC4__EXPR_STREAM_H - -#include "expr/expr.h" - -namespace CVC4 { - -/** - * A pure-virtual stream interface for expressions. Can be used to - * communicate streams of expressions between different parts of CVC4. - */ -class CVC4_PUBLIC ExprStream { -public: - /** Virtual destructor; this implementation does nothing. */ - virtual ~ExprStream() {} - - /** - * Get the next expression in the stream (advancing the stream - * pointer as a side effect.) - */ - virtual Expr nextExpr() = 0; -};/* class ExprStream */ - -}/* CVC4 namespace */ - -#endif /* CVC4__EXPR_STREAM_H */ - diff --git a/src/expr/expr_stream.i b/src/expr/expr_stream.i deleted file mode 100644 index f1144623b..000000000 --- a/src/expr/expr_stream.i +++ /dev/null @@ -1,5 +0,0 @@ -%{ -#include "expr/expr_stream.h" -%} - -%include "expr/expr_stream.h" diff --git a/src/expr/metakind_template.h b/src/expr/metakind_template.h index c4d35b7dc..fb1626adb 100644 --- a/src/expr/metakind_template.h +++ b/src/expr/metakind_template.h @@ -203,20 +203,6 @@ Kind operatorToKind(::CVC4::expr::NodeValue* nv); #line 205 "${template}" -namespace theory { - -static inline bool useTheoryValidate(std::string theory) { -${use_theory_validations} - return false; -} - -static const char *const useTheoryHelp = "\ -The following options are valid alternate implementations for use with\n\ -the --use-theory option:\n\ -\n\ -${theory_alternate_doc}"; - -}/* CVC4::theory namespace */ }/* CVC4 namespace */ #endif /* CVC4__NODE_MANAGER_NEEDS_CONSTANT_MAP */ diff --git a/src/expr/mkmetakind b/src/expr/mkmetakind index fe8a152df..e2a733ec8 100755 --- a/src/expr/mkmetakind +++ b/src/expr/mkmetakind @@ -50,9 +50,6 @@ metakind_ubchildren= metakind_lbchildren= metakind_operatorKinds= -use_theory_validations= -theory_alternate_doc= - seen_theory=false seen_theory_builtin=false @@ -108,13 +105,6 @@ function alternate { theory_header="$4" theory_includes="${theory_includes}#include \"$theory_header\" " - - use_theory_validations="${use_theory_validations} - if(theory == \"$name\") { - return true; - }" - theory_alternate_doc="$theory_alternate_doc$name - alternate implementation for $theory_id\\n\\ -" } function properties { @@ -410,10 +400,6 @@ check_builtin_theory_seen nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 -if [ -z "$theory_alternate_doc" ]; then - theory_alternate_doc="[none defined]" -fi - text=$(cat "$template") for var in \ metakind_includes \ @@ -428,8 +414,6 @@ for var in \ metakind_ubchildren \ metakind_lbchildren \ metakind_operatorKinds \ - use_theory_validations \ - theory_alternate_doc \ template \ ; do eval text="\${text//\\\$\\{$var\\}/\${$var}}" diff --git a/src/expr/node_manager.cpp b/src/expr/node_manager.cpp index 5d409f748..16ffd8306 100644 --- a/src/expr/node_manager.cpp +++ b/src/expr/node_manager.cpp @@ -575,6 +575,42 @@ TypeNode NodeManager::RecTypeCache::getRecordType( NodeManager * nm, const Recor } } +TypeNode NodeManager::mkFunctionType(const std::vector<TypeNode>& sorts) +{ + Assert(sorts.size() >= 2); + CheckArgument(!sorts[sorts.size() - 1].isFunction(), + sorts[sorts.size() - 1], + "must flatten function types"); + return mkTypeNode(kind::FUNCTION_TYPE, sorts); +} + +TypeNode NodeManager::mkPredicateType(const std::vector<TypeNode>& sorts) +{ + Assert(sorts.size() >= 1); + std::vector<TypeNode> sortNodes; + sortNodes.insert(sortNodes.end(), sorts.begin(), sorts.end()); + sortNodes.push_back(booleanType()); + return mkFunctionType(sortNodes); +} + +TypeNode NodeManager::mkFunctionType(const TypeNode& domain, + const TypeNode& range) +{ + std::vector<TypeNode> sorts; + sorts.push_back(domain); + sorts.push_back(range); + return mkFunctionType(sorts); +} + +TypeNode NodeManager::mkFunctionType(const std::vector<TypeNode>& argTypes, + const TypeNode& range) +{ + Assert(argTypes.size() >= 1); + std::vector<TypeNode> sorts(argTypes); + sorts.push_back(range); + return mkFunctionType(sorts); +} + TypeNode NodeManager::mkTupleType(const std::vector<TypeNode>& types) { std::vector< TypeNode > ts; Debug("tuprec-debug") << "Make tuple type : "; diff --git a/src/expr/node_manager.h b/src/expr/node_manager.h index eced00c48..2e8f40fff 100644 --- a/src/expr/node_manager.h +++ b/src/expr/node_manager.h @@ -807,7 +807,7 @@ public: * @param range the range type * @returns the functional type domain -> range */ - inline TypeNode mkFunctionType(const TypeNode& domain, const TypeNode& range); + TypeNode mkFunctionType(const TypeNode& domain, const TypeNode& range); /** * Make a function type with input types from @@ -817,8 +817,8 @@ public: * @param range the range type * @returns the functional type (argTypes[0], ..., argTypes[n]) -> range */ - inline TypeNode mkFunctionType(const std::vector<TypeNode>& argTypes, - const TypeNode& range); + TypeNode mkFunctionType(const std::vector<TypeNode>& argTypes, + const TypeNode& range); /** * Make a function type with input types from @@ -826,7 +826,7 @@ public: * <code>sorts[sorts.size()-1]</code>. <code>sorts</code> must have * at least 2 elements. */ - inline TypeNode mkFunctionType(const std::vector<TypeNode>& sorts); + TypeNode mkFunctionType(const std::vector<TypeNode>& sorts); /** * Make a predicate type with input types from @@ -834,7 +834,7 @@ public: * <code>BOOLEAN</code>. <code>sorts</code> must have at least one * element. */ - inline TypeNode mkPredicateType(const std::vector<TypeNode>& sorts); + TypeNode mkPredicateType(const std::vector<TypeNode>& sorts); /** * Make a tuple type with types from @@ -1086,52 +1086,6 @@ inline TypeNode NodeManager::builtinOperatorType() { return TypeNode(mkTypeConst<TypeConstant>(BUILTIN_OPERATOR_TYPE)); } -/** Make a function type from domain to range. */ -inline TypeNode NodeManager::mkFunctionType(const TypeNode& domain, const TypeNode& range) { - std::vector<TypeNode> sorts; - sorts.push_back(domain); - sorts.push_back(range); - return mkFunctionType(sorts); -} - -inline TypeNode NodeManager::mkFunctionType(const std::vector<TypeNode>& argTypes, const TypeNode& range) { - Assert(argTypes.size() >= 1); - std::vector<TypeNode> sorts(argTypes); - sorts.push_back(range); - return mkFunctionType(sorts); -} - -inline TypeNode -NodeManager::mkFunctionType(const std::vector<TypeNode>& sorts) { - Assert(sorts.size() >= 2); - std::vector<TypeNode> sortNodes; - for (unsigned i = 0; i < sorts.size(); ++ i) { - CheckArgument(sorts[i].isFirstClass(), - sorts, - "cannot create function types for argument types that are " - "not first-class. Try option --uf-ho."); - sortNodes.push_back(sorts[i]); - } - CheckArgument(!sorts[sorts.size()-1].isFunction(), sorts[sorts.size()-1], - "must flatten function types"); - return mkTypeNode(kind::FUNCTION_TYPE, sortNodes); -} - -inline TypeNode -NodeManager::mkPredicateType(const std::vector<TypeNode>& sorts) { - Assert(sorts.size() >= 1); - std::vector<TypeNode> sortNodes; - for (unsigned i = 0; i < sorts.size(); ++ i) { - CheckArgument(sorts[i].isFirstClass(), - sorts, - "cannot create predicate types for argument types that are " - "not first-class. Try option --uf-ho."); - sortNodes.push_back(sorts[i]); - } - sortNodes.push_back(booleanType()); - return mkTypeNode(kind::FUNCTION_TYPE, sortNodes); -} - inline TypeNode NodeManager::mkSExprType(const std::vector<TypeNode>& types) { std::vector<TypeNode> typeNodes; for (unsigned i = 0; i < types.size(); ++ i) { diff --git a/src/expr/node_traversal.cpp b/src/expr/node_traversal.cpp new file mode 100644 index 000000000..9e7a82c24 --- /dev/null +++ b/src/expr/node_traversal.cpp @@ -0,0 +1,150 @@ +/********************* */ +/*! \file node_traversal.cpp + ** \verbatim + ** Top contributors (to current version): + ** Alex Ozdemir + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Iterators for traversing nodes. + **/ + +#include "node_traversal.h" + +namespace CVC4 { + +NodeDfsIterator::NodeDfsIterator(TNode n, bool postorder) + : d_stack{n}, + d_visited(), + d_postorder(postorder), + d_current(TNode()) +{ +} + +NodeDfsIterator::NodeDfsIterator(bool postorder) + : d_stack(), + d_visited(), + d_postorder(postorder), + d_current(TNode()) +{ +} + +NodeDfsIterator& NodeDfsIterator::operator++() +{ + // If we were just constructed, advance to first visit, **before** + // advancing past it to the next visit (below). + initializeIfUninitialized(); + + // Advance to the next visit + advanceToNextVisit(); + return *this; +} + +NodeDfsIterator NodeDfsIterator::operator++(int) +{ + NodeDfsIterator copyOfOld(*this); + ++*this; + return copyOfOld; +} + +TNode& NodeDfsIterator::operator*() +{ + // If we were just constructed, advance to first visit + initializeIfUninitialized(); + Assert(!d_current.isNull()); + + return d_current; +} + +bool NodeDfsIterator::operator==(const NodeDfsIterator& other) const +{ + // The stack and current node uniquely represent traversal state. We need not + // use the scheduled node set. + // + // Users should not compare iterators for traversals of different nodes. + Assert(d_postorder == other.d_postorder); + return d_stack == other.d_stack && d_current == other.d_current; +} + +bool NodeDfsIterator::operator!=(const NodeDfsIterator& other) const +{ + return !(*this == other); +} + +void NodeDfsIterator::advanceToNextVisit() +{ + // While a node is enqueued and we're not at the right visit type + while (!d_stack.empty()) + { + TNode back = d_stack.back(); + auto visitEntry = d_visited.find(back); + if (visitEntry == d_visited.end()) + { + // if we haven't pre-visited this node, pre-visit it + d_visited[back] = false; + d_current = back; + // Use integer underflow to reverse-iterate + for (size_t n = back.getNumChildren(), i = n - 1; i < n; --i) + { + d_stack.push_back(back[i]); + } + if (!d_postorder) + { + return; + } + } + else if (!d_postorder || visitEntry->second) + { + // if we're previsiting or we've already post-visited this node: skip it + d_stack.pop_back(); + } + else + { + // otherwise, this is a post-visit + visitEntry->second = true; + d_current = back; + d_stack.pop_back(); + return; + } + } + // We're at the end of the traversal: nullify the current node to agree + // with the "end" iterator. + d_current = TNode(); +} + +void NodeDfsIterator::initializeIfUninitialized() +{ + if (d_current.isNull()) + { + advanceToNextVisit(); + } +} + +NodeDfsIterable::NodeDfsIterable(TNode n) : d_node(n), d_postorder(true) {} + +NodeDfsIterable& NodeDfsIterable::inPostorder() +{ + d_postorder = true; + return *this; +} + +NodeDfsIterable& NodeDfsIterable::inPreorder() +{ + d_postorder = false; + return *this; +} + +NodeDfsIterator NodeDfsIterable::begin() const +{ + return NodeDfsIterator(d_node, d_postorder); +} + +NodeDfsIterator NodeDfsIterable::end() const +{ + return NodeDfsIterator(d_postorder); +} + +} // namespace CVC4 diff --git a/src/expr/node_traversal.h b/src/expr/node_traversal.h new file mode 100644 index 000000000..fffc1d746 --- /dev/null +++ b/src/expr/node_traversal.h @@ -0,0 +1,131 @@ +/********************* */ +/*! \file node_traversal.h + ** \verbatim + ** Top contributors (to current version): + ** Alex Ozdemir + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Iterators for traversing nodes. + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__EXPR__NODE_TRAVERSAL_H +#define CVC4__EXPR__NODE_TRAVERSAL_H + +#include <cstddef> +#include <iterator> +#include <unordered_map> +#include <vector> + +#include "expr/node.h" + +namespace CVC4 { + +// Iterator for traversing a node in post-order +// It does DAG-traversal, so indentical sub-nodes will be visited once only. +class NodeDfsIterator +{ + public: + // STL type definitions for an iterator + using value_type = TNode; + using pointer = TNode*; + using reference = TNode&; + using iterator_category = std::forward_iterator_tag; + using difference_type = std::ptrdiff_t; + + // Construct a traversal iterator beginning at `n` + NodeDfsIterator(TNode n, bool postorder); + // Construct an end-of-traversal iterator + NodeDfsIterator(bool postorder); + + // Move/copy construction and assignment. Destructor. + NodeDfsIterator(NodeDfsIterator&&) = default; + NodeDfsIterator& operator=(NodeDfsIterator&&) = default; + NodeDfsIterator(NodeDfsIterator&) = default; + NodeDfsIterator& operator=(NodeDfsIterator&) = default; + ~NodeDfsIterator() = default; + + // Preincrement + NodeDfsIterator& operator++(); + // Postincrement + NodeDfsIterator operator++(int); + // Dereference + reference operator*(); + // Equals + bool operator==(const NodeDfsIterator&) const; + // Not equals + bool operator!=(const NodeDfsIterator&) const; + + private: + // While we're not at an appropriate visit (see d_postorder), advance. + // In each step: + // * enqueue children of a not-yet-pre-visited node (and mark it + // previsited) + // * pop a not-yet-post-visited node (and mark it post-visited) + // * pop an already post-visited node. + // After calling this, `d_current` will be changed to the next node, if there + // is another node in the traversal. + void advanceToNextVisit(); + + // If this iterator hasn't been dereferenced or incremented yet, advance to + // first visit. + // Necessary because we are lazy and don't find our first visit node at + // construction time. + void initializeIfUninitialized(); + + // Stack of nodes to visit. + std::vector<TNode> d_stack; + + // Whether (and how) we've visited a node. + // Absent if we haven't visited it. + // Set to `false` if we've already pre-visited it (enqueued its children). + // Set to `true` if we've also already post-visited it. + std::unordered_map<TNode, bool, TNodeHashFunction> d_visited; + + // Whether this is a post-order iterator (the alternative is pre-order) + bool d_postorder; + + // Whether this iterator has been initialized (advanced to its first + // visit) + bool d_initialized; + + // Current referent node. A valid node to visit if non-null. + // Null after construction (but before first access) and at the end. + TNode d_current; +}; + +// Node wrapper that is iterable in DAG post-order +class NodeDfsIterable +{ + public: + NodeDfsIterable(TNode n); + + // Modifying the traversal order + // Modify this iterable to be in post-order (default) + NodeDfsIterable& inPostorder(); + // Modify this iterable to be in pre-order + NodeDfsIterable& inPreorder(); + + // Move/copy construction and assignment. Destructor. + NodeDfsIterable(NodeDfsIterable&&) = default; + NodeDfsIterable& operator=(NodeDfsIterable&&) = default; + NodeDfsIterable(NodeDfsIterable&) = default; + NodeDfsIterable& operator=(NodeDfsIterable&) = default; + ~NodeDfsIterable() = default; + + NodeDfsIterator begin() const; + NodeDfsIterator end() const; + + private: + TNode d_node; + bool d_postorder; +}; + +} // namespace CVC4 + +#endif // CVC4__EXPR__NODE_TRAVERSAL_H diff --git a/src/main/command_executor.cpp b/src/main/command_executor.cpp index 9db704621..323f24492 100644 --- a/src/main/command_executor.cpp +++ b/src/main/command_executor.cpp @@ -53,8 +53,7 @@ CommandExecutor::CommandExecutor(Options& options) d_smtEngine(d_solver->getSmtEngine()), d_options(options), d_stats("driver"), - d_result(), - d_replayStream(nullptr) + d_result() { } @@ -72,12 +71,6 @@ void CommandExecutor::safeFlushStatistics(int fd) const d_stats.safeFlushInformation(fd); } -void CommandExecutor::setReplayStream(ExprStream* replayStream) { - assert(d_replayStream == NULL); - d_replayStream = replayStream; - d_smtEngine->setReplayStream(d_replayStream); -} - bool CommandExecutor::doCommand(Command* cmd) { if( d_options.getParseOnly() ) { diff --git a/src/main/command_executor.h b/src/main/command_executor.h index 3fc971f5b..a9b6d9077 100644 --- a/src/main/command_executor.h +++ b/src/main/command_executor.h @@ -44,17 +44,12 @@ class CommandExecutor Options& d_options; StatisticsRegistry d_stats; Result d_result; - ExprStream* d_replayStream; public: CommandExecutor(Options& options); virtual ~CommandExecutor() { - if (d_replayStream != NULL) - { - delete d_replayStream; - } } /** @@ -92,8 +87,6 @@ class CommandExecutor void flushOutputStreams(); - void setReplayStream(ExprStream* replayStream); - protected: /** Executes treating cmd as a singleton */ virtual bool doCommandSingleton(CVC4::Command* cmd); diff --git a/src/main/driver_unified.cpp b/src/main/driver_unified.cpp index be2d0a0f8..92368148b 100644 --- a/src/main/driver_unified.cpp +++ b/src/main/driver_unified.cpp @@ -184,23 +184,6 @@ int runCvc4(int argc, char* argv[], Options& opts) { // Create the command executor to execute the parsed commands pExecutor = new CommandExecutor(opts); - std::unique_ptr<Parser> replayParser; - if (opts.getReplayInputFilename() != "") - { - std::string replayFilename = opts.getReplayInputFilename(); - ParserBuilder replayParserBuilder( - pExecutor->getSolver(), replayFilename, opts); - - if( replayFilename == "-") { - if( inputFromStdin ) { - throw OptionException("Replay file and input file can't both be stdin."); - } - replayParserBuilder.withStreamInput(cin); - } - replayParser.reset(replayParserBuilder.build()); - pExecutor->setReplayStream(new Parser::ExprStream(replayParser.get())); - } - int returnValue = 0; { // Timer statistic @@ -241,10 +224,6 @@ int runCvc4(int argc, char* argv[], Options& opts) { << endl << endl; Message() << Configuration::copyright() << endl; } - if(replayParser) { - // have the replay parser use the declarations input interactively - replayParser->useDeclarationsFrom(shell.getParser()); - } while(true) { try { @@ -294,10 +273,6 @@ int runCvc4(int argc, char* argv[], Options& opts) { vector< vector<Command*> > allCommands; allCommands.push_back(vector<Command*>()); std::unique_ptr<Parser> parser(parserBuilder.build()); - if(replayParser) { - // have the replay parser use the file's declarations - replayParser->useDeclarationsFrom(parser.get()); - } int needReset = 0; // true if one of the commands was interrupted bool interrupted = false; @@ -453,10 +428,6 @@ int runCvc4(int argc, char* argv[], Options& opts) { } std::unique_ptr<Parser> parser(parserBuilder.build()); - if(replayParser) { - // have the replay parser use the file's declarations - replayParser->useDeclarationsFrom(parser.get()); - } bool interrupted = false; while (status) { diff --git a/src/options/CMakeLists.txt b/src/options/CMakeLists.txt index 4fb331e50..4909d7d43 100644 --- a/src/options/CMakeLists.txt +++ b/src/options/CMakeLists.txt @@ -47,7 +47,6 @@ set(options_toml_files decision_options.toml expr_options.toml fp_options.toml - idl_options.toml main_options.toml parser_options.toml printer_options.toml diff --git a/src/options/arith_options.toml b/src/options/arith_options.toml index ab8164130..1c0351bcb 100644 --- a/src/options/arith_options.toml +++ b/src/options/arith_options.toml @@ -551,3 +551,12 @@ header = "options/arith_options.h" default = "true" read_only = true help = "whether to increment the precision for irrational function constraints" + +[[option]] + name = "brabTest" + category = "regular" + long = "arith-brab" + type = "bool" + default = "true" + read_only = true + help = "whether to use simple rounding, similar to a unit-cube test, for integers" diff --git a/src/options/datatypes_options.toml b/src/options/datatypes_options.toml index 82e833506..ac371efeb 100644 --- a/src/options/datatypes_options.toml +++ b/src/options/datatypes_options.toml @@ -32,15 +32,6 @@ header = "options/datatypes_options.h" help = "do binary splits for datatype constructor types" [[option]] - name = "dtRefIntro" - category = "regular" - long = "dt-ref-sk-intro" - type = "bool" - default = "false" - read_only = true - help = "introduce reference skolems for shorter explanations" - -[[option]] name = "cdtBisimilar" category = "regular" long = "cdt-bisimilar" diff --git a/src/options/idl_options.toml b/src/options/idl_options.toml deleted file mode 100644 index d3bee7018..000000000 --- a/src/options/idl_options.toml +++ /dev/null @@ -1,11 +0,0 @@ -id = "IDL" -name = "Idl" -header = "options/idl_options.h" - -[[option]] - name = "idlRewriteEq" - category = "regular" - long = "idl-rewrite-equalities" - type = "bool" - default = "false" - help = "enable rewriting equalities into two inequalities in IDL solver (default is disabled)" diff --git a/src/options/options.h b/src/options/options.h index ad2729205..3d1e67aba 100644 --- a/src/options/options.h +++ b/src/options/options.h @@ -67,9 +67,6 @@ class CVC4_PUBLIC Options { /** Listeners for options::tlimit-per. */ ListenerCollection d_rlimitPerListeners; - /** Listeners for options::useTheoryList. */ - ListenerCollection d_useTheoryListListeners; - /** Listeners for options::defaultExprDepth. */ ListenerCollection d_setDefaultExprDepthListeners; @@ -94,10 +91,6 @@ class CVC4_PUBLIC Options { /** Listeners for options::diagnosticChannelName. */ ListenerCollection d_setDiagnosticChannelListeners; - /** Listeners for options::replayFilename. */ - ListenerCollection d_setReplayFilenameListeners; - - static ListenerCollection::Registration* registerAndNotify( ListenerCollection& collection, Listener* listener, bool notify); @@ -231,7 +224,6 @@ public: std::ostream* getOut(); std::ostream* getOutConst() const; // TODO: Remove this. std::string getBinaryName() const; - std::string getReplayInputFilename() const; unsigned getParseStep() const; // TODO: Document these. @@ -382,19 +374,6 @@ public: Listener* listener, bool notifyIfSet); /** - * Registers a listener for options::useTheoryList being set. - * - * If notifyIfSet is true, this calls notify on the listener - * if the option was set by the user. - * - * The memory for the Registration is controlled by the user and must - * be destroyed before the Options object is. - */ - ListenerCollection::Registration* registerUseTheoryListListener( - Listener* listener, bool notifyIfSet); - - - /** * Registers a listener for options::defaultExprDepth being set. * * If notifyIfSet is true, this calls notify on the listener @@ -490,18 +469,6 @@ public: ListenerCollection::Registration* registerSetDiagnosticOutputChannelListener( Listener* listener, bool notifyIfSet); - /** - * Registers a listener for options::replayLogFilename being set. - * - * If notifyIfSet is true, this calls notify on the listener - * if the option was set by the user. - * - * The memory for the Registration is controlled by the user and must - * be destroyed before the Options object is. - */ - ListenerCollection::Registration* registerSetReplayLogFilename( - Listener* listener, bool notifyIfSet); - /** Sends a std::flush to getErr(). */ void flushErr(); diff --git a/src/options/options_handler.cpp b/src/options/options_handler.cpp index 3e6c4da3c..7fcc8f2ae 100644 --- a/src/options/options_handler.cpp +++ b/src/options/options_handler.cpp @@ -244,20 +244,6 @@ void OptionsHandler::setBitblastAig(std::string option, bool arg) } } -// theory/options_handlers.h -std::string OptionsHandler::handleUseTheoryList(std::string option, std::string optarg) { - std::string currentList = options::useTheoryList(); - if(currentList.empty()){ - return optarg; - } else { - return currentList +','+ optarg; - } -} - -void OptionsHandler::notifyUseTheoryList(std::string option) { - d_options->d_useTheoryListListeners.notify(); -} - // printer/options_handlers.h const std::string OptionsHandler::s_instFormatHelp = "\ Inst format modes currently supported by the --inst-format option:\n\ @@ -340,23 +326,6 @@ void OptionsHandler::notifySetDiagnosticOutputChannel(std::string option) { d_options->d_setDiagnosticChannelListeners.notify(); } - -std::string OptionsHandler::checkReplayFilename(std::string option, std::string optarg) { -#ifdef CVC4_REPLAY - if(optarg == "") { - throw OptionException (std::string("Bad file name for --replay")); - } else { - return optarg; - } -#else /* CVC4_REPLAY */ - throw OptionException("The replay feature was disabled in this build of CVC4."); -#endif /* CVC4_REPLAY */ -} - -void OptionsHandler::notifySetReplayLogFilename(std::string option) { - d_options->d_setReplayFilenameListeners.notify(); -} - void OptionsHandler::statsEnabledBuild(std::string option, bool value) { #ifndef CVC4_STATISTICS_ON @@ -453,7 +422,6 @@ void OptionsHandler::showConfiguration(std::string option) { print_config_cond("debug code", Configuration::isDebugBuild()); print_config_cond("statistics", Configuration::isStatisticsBuild()); - print_config_cond("replay", Configuration::isReplayBuild()); print_config_cond("tracing", Configuration::isTracingBuild()); print_config_cond("dumping", Configuration::isDumpingBuild()); print_config_cond("muzzled", Configuration::isMuzzledBuild()); diff --git a/src/options/options_handler.h b/src/options/options_handler.h index a395bb453..396b2c8ea 100644 --- a/src/options/options_handler.h +++ b/src/options/options_handler.h @@ -74,10 +74,6 @@ public: void setBitblastAig(std::string option, bool arg); - // theory/options_handlers.h - void notifyUseTheoryList(std::string option); - std::string handleUseTheoryList(std::string option, std::string optarg); - // printer/options_handlers.h InstFormatMode stringToInstFormatMode(std::string option, std::string optarg); @@ -96,8 +92,6 @@ public: void notifyDumpToFile(std::string option); void notifySetRegularOutputChannel(std::string option); void notifySetDiagnosticOutputChannel(std::string option); - std::string checkReplayFilename(std::string option, std::string optarg); - void notifySetReplayLogFilename(std::string option); void statsEnabledBuild(std::string option, bool value); diff --git a/src/options/options_public_functions.cpp b/src/options/options_public_functions.cpp index d1022c51c..bae60a374 100644 --- a/src/options/options_public_functions.cpp +++ b/src/options/options_public_functions.cpp @@ -187,10 +187,6 @@ std::string Options::getBinaryName() const{ return (*this)[options::binary_name]; } -std::string Options::getReplayInputFilename() const{ - return (*this)[options::replayInputFilename]; -} - unsigned Options::getParseStep() const{ return (*this)[options::parseStep]; } diff --git a/src/options/options_template.cpp b/src/options/options_template.cpp index dad4f13a1..48b6a66dd 100644 --- a/src/options/options_template.cpp +++ b/src/options/options_template.cpp @@ -317,13 +317,6 @@ ListenerCollection::Registration* Options::registerRlimitPerListener( return registerAndNotify(d_rlimitPerListeners, listener, notify); } -ListenerCollection::Registration* Options::registerUseTheoryListListener( - Listener* listener, bool notifyIfSet) -{ - bool notify = notifyIfSet && wasSetByUser(options::useTheoryList); - return registerAndNotify(d_useTheoryListListeners, listener, notify); -} - ListenerCollection::Registration* Options::registerSetDefaultExprDepthListener( Listener* listener, bool notifyIfSet) { @@ -382,14 +375,6 @@ Options::registerSetDiagnosticOutputChannelListener( return registerAndNotify(d_setDiagnosticChannelListeners, listener, notify); } -ListenerCollection::Registration* -Options::registerSetReplayLogFilename( - Listener* listener, bool notifyIfSet) -{ - bool notify = notifyIfSet && wasSetByUser(options::replayLogFilename); - return registerAndNotify(d_setReplayFilenameListeners, listener, notify); -} - ${custom_handlers}$ diff --git a/src/options/smt_options.toml b/src/options/smt_options.toml index 51df591d7..a0e3014b0 100644 --- a/src/options/smt_options.toml +++ b/src/options/smt_options.toml @@ -620,27 +620,6 @@ header = "options/smt_options.h" read_only = true help = "amount of resources spent for each sat conflict (bitvectors)" -# --replay is currently broken; don't document it for 1.0 -[[option]] - name = "replayInputFilename" - category = "undocumented" - long = "replay=FILE" - type = "std::string" - handler = "checkReplayFilename" - read_only = true - help = "replay decisions from file" - -# --replay is currently broken; don't document it for 1.0 -[[option]] - name = "replayLogFilename" - category = "undocumented" - long = "replay-log=FILE" - type = "std::string" - handler = "checkReplayFilename" - notifies = ["notifySetReplayLogFilename", "notifyBeforeSearch"] - read_only = true - help = "replay decisions from file" - [[option]] name = "forceNoLimitCpuWhileDump" category = "regular" diff --git a/src/options/theory_options.toml b/src/options/theory_options.toml index 13c3d5cfb..84c994c3f 100644 --- a/src/options/theory_options.toml +++ b/src/options/theory_options.toml @@ -19,17 +19,6 @@ header = "options/theory_options.h" help = "Type variables as uninterpreted, type constants by theory, equalities by the parametric theory." [[option]] - name = "useTheoryList" - smt_name = "use-theory" - category = "regular" - long = "use-theory=NAME" - type = "std::string" - handler = "handleUseTheoryList" - notifies = ["notifyUseTheoryList"] - read_only = true - help = "use alternate theory implementation NAME (--use-theory=help for a list). This option may be repeated or a comma separated list." - -[[option]] name = "assignFunctionValues" category = "regular" long = "assign-function-values" diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 82c0581ce..32604d03f 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -114,6 +114,7 @@ tokens { FORALL_TOK = 'FORALL'; EXISTS_TOK = 'EXISTS'; + CHOICE_TOK = 'CHOICE'; PATTERN_TOK = 'PATTERN'; LAMBDA_TOK = 'LAMBDA'; @@ -343,7 +344,8 @@ int getOperatorPrecedence(int type) { case IMPLIES_TOK: return 30;// right-to-left case IFF_TOK: return 31; case FORALL_TOK: - case EXISTS_TOK: return 32; + case EXISTS_TOK: + case CHOICE_TOK: return 32; case ASSIGN_TOK: case IN_TOK: return 33; @@ -1322,8 +1324,6 @@ restrictedTypePossiblyFunctionLHS[CVC4::api::Sort& t, * declared in the outer context. What follows isn't quite right, * though, since type aliases and function definitions should be * retained in the set of current declarations. */ - { /*symtab = PARSER_STATE->getSymbolTable(); - PARSER_STATE->useDeclarationsFrom(new SymbolTable());*/ } formula[f] ( COMMA formula[f2] )? RPAREN { PARSER_STATE->unimplementedFeature("predicate subtyping not supported in this release"); @@ -1465,7 +1465,7 @@ prefixFormula[CVC4::api::Term& f] api::Term ipl; } /* quantifiers */ - : ( FORALL_TOK { k = api::FORALL; } | EXISTS_TOK { k = api::EXISTS; } ) + : ( FORALL_TOK { k = api::FORALL; } | EXISTS_TOK { k = api::EXISTS; } | CHOICE_TOK { k = api::CHOICE; } ) { PARSER_STATE->pushScope(); } LPAREN boundVarDecl[ids,t] { for(std::vector<std::string>::const_iterator i = ids.begin(); i != ids.end(); ++i) { @@ -2072,8 +2072,11 @@ stringTerm[CVC4::api::Term& f] { f = MK_TERM(CVC4::api::REGEXP_OPT, f); } | REGEXP_RANGE_TOK LPAREN formula[f] COMMA formula[f2] RPAREN { f = MK_TERM(CVC4::api::REGEXP_RANGE, f, f2); } - | REGEXP_LOOP_TOK LPAREN formula[f] COMMA formula[f2] COMMA formula[f3] RPAREN - { f = MK_TERM(CVC4::api::REGEXP_LOOP, f, f2, f3); } + | REGEXP_LOOP_TOK LPAREN formula[f] COMMA lo=numeral COMMA hi=numeral RPAREN + { + api::Op lop = SOLVER->mkOp(CVC4::api::REGEXP_LOOP, lo, hi); + f = MK_TERM(lop, f); + } | REGEXP_COMPLEMENT_TOK LPAREN formula[f] RPAREN { f = MK_TERM(CVC4::api::REGEXP_COMPLEMENT, f); } | REGEXP_EMPTY_TOK @@ -2083,7 +2086,7 @@ stringTerm[CVC4::api::Term& f] /* string literal */ | str[s] - { f = SOLVER->mkString(s, true); } + { f = PARSER_STATE->mkStringConstant(s); } | setsTerm[f] ; diff --git a/src/parser/parser.cpp b/src/parser/parser.cpp index b36f36a93..5dca92370 100644 --- a/src/parser/parser.cpp +++ b/src/parser/parser.cpp @@ -757,5 +757,153 @@ void Parser::attributeNotSupported(const std::string& attr) { } } +std::vector<unsigned> Parser::processAdHocStringEsc(const std::string& s) +{ + std::vector<unsigned> str; + unsigned i = 0; + while (i < s.size()) + { + // get the current character + if (s[i] != '\\') + { + // don't worry about printable here + str.push_back(static_cast<unsigned>(s[i])); + ++i; + continue; + } + // slash is always escaped + ++i; + if (i >= s.size()) + { + // slash cannot be the last character if we are parsing escape sequences + std::stringstream serr; + serr << "Escape sequence at the end of string: \"" << s + << "\" should be handled by lexer"; + parseError(serr.str()); + } + switch (s[i]) + { + case 'n': + { + str.push_back(static_cast<unsigned>('\n')); + i++; + } + break; + case 't': + { + str.push_back(static_cast<unsigned>('\t')); + i++; + } + break; + case 'v': + { + str.push_back(static_cast<unsigned>('\v')); + i++; + } + break; + case 'b': + { + str.push_back(static_cast<unsigned>('\b')); + i++; + } + break; + case 'r': + { + str.push_back(static_cast<unsigned>('\r')); + i++; + } + break; + case 'f': + { + str.push_back(static_cast<unsigned>('\f')); + i++; + } + break; + case 'a': + { + str.push_back(static_cast<unsigned>('\a')); + i++; + } + break; + case '\\': + { + str.push_back(static_cast<unsigned>('\\')); + i++; + } + break; + case 'x': + { + bool isValid = false; + if (i + 2 < s.size()) + { + if (std::isxdigit(s[i + 1]) && std::isxdigit(s[i + 2])) + { + std::stringstream shex; + shex << s[i + 1] << s[i + 2]; + unsigned val; + shex >> std::hex >> val; + str.push_back(val); + i += 3; + isValid = true; + } + } + if (!isValid) + { + std::stringstream serr; + serr << "Illegal String Literal: \"" << s + << "\", must have two digits after \\x"; + parseError(serr.str()); + } + } + break; + default: + { + if (std::isdigit(s[i])) + { + // octal escape sequences TODO : revisit (issue #1251). + unsigned num = static_cast<unsigned>(s[i]) - 48; + bool flag = num < 4; + if (i + 1 < s.size() && num < 8 && std::isdigit(s[i + 1]) + && s[i + 1] < '8') + { + num = num * 8 + static_cast<unsigned>(s[i + 1]) - 48; + if (flag && i + 2 < s.size() && std::isdigit(s[i + 2]) + && s[i + 2] < '8') + { + num = num * 8 + static_cast<unsigned>(s[i + 2]) - 48; + str.push_back(num); + i += 3; + } + else + { + str.push_back(num); + i += 2; + } + } + else + { + str.push_back(num); + i++; + } + } + } + } + } + return str; +} + +Expr Parser::mkStringConstant(const std::string& s) +{ + ExprManager* em = d_solver->getExprManager(); + if (em->getOptions().getInputLanguage() + == language::input::LANG_SMTLIB_V2_6_1) + { + return d_solver->mkString(s, true).getExpr(); + } + // otherwise, we must process ad-hoc escape sequences + std::vector<unsigned> str = processAdHocStringEsc(s); + return d_solver->mkString(str).getExpr(); +} + } /* CVC4::parser namespace */ } /* CVC4 namespace */ diff --git a/src/parser/parser.h b/src/parser/parser.h index ecea4d3bd..cd4105cd0 100644 --- a/src/parser/parser.h +++ b/src/parser/parser.h @@ -26,7 +26,6 @@ #include "api/cvc4cpp.h" #include "expr/expr.h" -#include "expr/expr_stream.h" #include "expr/kind.h" #include "expr/symbol_table.h" #include "parser/input.h" @@ -273,6 +272,9 @@ public: return d_input; } + /** Get unresolved sorts */ + inline std::set<api::Sort>& getUnresolvedSorts() { return d_unresolved; } + /** Deletes and replaces the current parser input. */ void setInput(Input* input) { delete d_input; @@ -803,63 +805,9 @@ public: d_globalDeclarations = flag; } - /** - * Set the current symbol table used by this parser. - * From now on, this parser will perform its definitions and - * lookups in the declaration scope of the "parser" argument - * (but doesn't re-delegate if the other parser's declaration scope - * changes later). A NULL argument restores this parser's - * "primordial" declaration scope assigned at its creation. Calling - * p->useDeclarationsFrom(p) is a no-op. - * - * This feature is useful when e.g. reading out-of-band expression data: - * 1. Parsing --replay log files produced with --replay-log. - * 2. Perhaps a multi-query benchmark file is being single-stepped - * with intervening queries on stdin that must reference the same - * declaration scope(s). - * - * However, the feature must be used carefully. Pushes and pops - * should be performed with the correct current declaration scope. - * Care must be taken to match up declaration scopes, of course; - * If variables in the deferred-to parser go out of scope, the - * secondary parser will give errors that they are undeclared. - * Also, an outer-scope variable shadowed by an inner-scope one of - * the same name may be temporarily inaccessible. - * - * In short, caveat emptor. - */ - inline void useDeclarationsFrom(Parser* parser) { - if(parser == NULL) { - d_symtab = &d_symtabAllocated; - } else { - d_symtab = parser->d_symtab; - } - } - - inline void useDeclarationsFrom(SymbolTable* symtab) { - d_symtab = symtab; - } - inline SymbolTable* getSymbolTable() const { return d_symtab; } - - /** - * An expression stream interface for a parser. This stream simply - * pulls expressions from the given Parser object. - * - * Here, the ExprStream base class allows a Parser (from the parser - * library) and core components of CVC4 (in the core library) to - * communicate without polluting the public interface or having them - * reach into private (undocumented) interfaces. - */ - class ExprStream : public CVC4::ExprStream { - Parser* d_parser; - public: - ExprStream(Parser* parser) : d_parser(parser) {} - ~ExprStream() { delete d_parser; } - Expr nextExpr() override { return d_parser->nextExpression().getExpr(); } - };/* class Parser::ExprStream */ //------------------------ operator overloading /** is this function overloaded? */ @@ -889,6 +837,28 @@ public: name, api::sortVectorToTypes(argTypes)); } //------------------------ end operator overloading + /** + * Make string constant + * + * This makes the string constant based on the string s. This may involve + * processing ad-hoc escape sequences (if the language is not + * SMT-LIB 2.6.1 or higher), or otherwise calling the solver to construct + * the string. + */ + Expr mkStringConstant(const std::string& s); + + private: + /** ad-hoc string escaping + * + * Returns the (internal) vector of code points corresponding to processing + * the escape sequences in string s. This is to support string inputs that + * do no comply with the SMT-LIB standard. + * + * This method handles escape sequences, including \n, \t, \v, \b, \r, \f, \a, + * \\, \x[N] and octal escape sequences of the form \[c1]([c2]([c3])?)? where + * c1, c2, c3 are digits from 0 to 7. + */ + std::vector<unsigned> processAdHocStringEsc(const std::string& s); };/* class Parser */ }/* CVC4::parser namespace */ diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index ec1eae7da..1d0fb71cb 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -782,8 +782,24 @@ sygusGrammarV1[CVC4::api::Sort & ret, Debug("parser-sygus") << " " << i << " : " << datatypes[i].getName() << std::endl; } - std::vector<api::Sort> datatypeTypes = - PARSER_STATE->bindMutualDatatypeTypes(datatypes, false); + + std::vector<CVC4::Datatype> dtypes; + dtypes.reserve(ndatatypes); + + for (api::DatatypeDecl i : datatypes) + { + dtypes.push_back(i.getDatatype()); + } + + std::set<Type> tset = + api::sortSetToTypes(PARSER_STATE->getUnresolvedSorts()); + + std::vector<DatatypeType> datatypeTypes = + SOLVER->getExprManager()->mkMutualDatatypeTypes( + dtypes, tset, ExprManager::DATATYPE_FLAG_PLACEHOLDER); + + PARSER_STATE->getUnresolvedSorts().clear(); + ret = datatypeTypes[0]; }; @@ -2067,6 +2083,11 @@ termAtomic[CVC4::api::Term& atomTerm] api::Term v2 = SOLVER->mkConst(api::Sort(type2), "_emp2"); atomTerm = SOLVER->mkTerm(api::SEP_EMP, v1, v2); } + | CHAR_TOK HEX_LITERAL + { + std::string hexStr = AntlrInput::tokenTextSubstr($HEX_LITERAL, 2); + atomTerm = SOLVER->mkChar(hexStr); + } | sym=SIMPLE_SYMBOL nonemptyNumeralList[numerals] { atomTerm = @@ -2078,10 +2099,10 @@ termAtomic[CVC4::api::Term& atomTerm] // Bit-vector constants | HEX_LITERAL - { - assert(AntlrInput::tokenText($HEX_LITERAL).find("#x") == 0); - std::string hexStr = AntlrInput::tokenTextSubstr($HEX_LITERAL, 2); - atomTerm = SOLVER->mkBitVector(hexStr, 16); + { + assert(AntlrInput::tokenText($HEX_LITERAL).find("#x") == 0); + std::string hexStr = AntlrInput::tokenTextSubstr($HEX_LITERAL, 2); + atomTerm = SOLVER->mkBitVector(hexStr, 16); } | BINARY_LITERAL { @@ -2091,7 +2112,7 @@ termAtomic[CVC4::api::Term& atomTerm] } // String constant - | str[s,false] { atomTerm = SOLVER->mkString(s, true); } + | str[s,false] { atomTerm = PARSER_STATE->mkStringConstant(s); } // NOTE: Theory constants go here @@ -2296,6 +2317,7 @@ quantOp[CVC4::api::Kind& kind] } : EXISTS_TOK { $kind = api::EXISTS; } | FORALL_TOK { $kind = api::FORALL; } + | CHOICE_TOK { $kind = api::CHOICE; } ; /** @@ -2666,8 +2688,10 @@ ATTRIBUTE_INST_LEVEL : ':quant-inst-max-level'; // operators (NOTE: theory symbols go here) EXISTS_TOK : 'exists'; FORALL_TOK : 'forall'; +CHOICE_TOK : { !PARSER_STATE->strictModeEnabled() }? 'choice'; EMP_TOK : { PARSER_STATE->isTheoryEnabled(theory::THEORY_SEP) }? 'emp'; +CHAR_TOK : { PARSER_STATE->isTheoryEnabled(theory::THEORY_STRINGS) }? 'char'; TUPLE_CONST_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'mkTuple'; TUPLE_SEL_TOK: { PARSER_STATE->isTheoryEnabled(theory::THEORY_DATATYPES) }? 'tupSel'; diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index 81ddae6d6..3233ee7e8 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -193,8 +193,9 @@ void Smt2::addStringOperators() { addOperator(api::REGEXP_STAR, "re.*"); addOperator(api::REGEXP_PLUS, "re.+"); addOperator(api::REGEXP_OPT, "re.opt"); + addIndexedOperator(api::REGEXP_REPEAT, api::REGEXP_REPEAT, "re.^"); + addIndexedOperator(api::REGEXP_LOOP, api::REGEXP_LOOP, "re.loop"); addOperator(api::REGEXP_RANGE, "re.range"); - addOperator(api::REGEXP_LOOP, "re.loop"); addOperator(api::REGEXP_COMPLEMENT, "re.comp"); addOperator(api::REGEXP_DIFF, "re.diff"); addOperator(api::STRING_LT, "str.<"); @@ -330,7 +331,7 @@ api::Term Smt2::getExpressionForNameAndType(const std::string& name, bool Smt2::getTesterName(api::Term cons, std::string& name) { - if (v2_6() && strictModeEnabled()) + if ((v2_6() || sygus_v2()) && strictModeEnabled()) { // 2.6 or above uses indexed tester symbols, if we are in strict mode, // we do not automatically define is-cons for constructor cons. @@ -744,11 +745,17 @@ bool Smt2::sygus() const return ilang == language::input::LANG_SYGUS || ilang == language::input::LANG_SYGUS_V2; } + bool Smt2::sygus_v1() const { return getLanguage() == language::input::LANG_SYGUS; } +bool Smt2::sygus_v2() const +{ + return getLanguage() == language::input::LANG_SYGUS_V2; +} + void Smt2::setInfo(const std::string& flag, const SExpr& sexpr) { // TODO: ??? } @@ -1261,7 +1268,8 @@ void Smt2::mkSygusDatatype(api::DatatypeDecl& dt, api::Term lbvl = makeSygusBoundVarList(dt, i, ltypes, largs); // make the let_body - api::Term body = applyParseOp(ops[i], largs); + std::vector<api::Term> largsApply = largs; + api::Term body = applyParseOp(ops[i], largsApply); // replace by lambda ParseOp pLam; pLam.d_expr = d_solver->mkTerm(api::LAMBDA, lbvl, body); diff --git a/src/parser/smt2/smt2.h b/src/parser/smt2/smt2.h index 0400c680f..35d088601 100644 --- a/src/parser/smt2/smt2.h +++ b/src/parser/smt2/smt2.h @@ -277,6 +277,8 @@ class Smt2 : public Parser bool sygus() const; /** Are we using the sygus version 1.0 format? */ bool sygus_v1() const; + /** Are we using the sygus version 2.0 format? */ + bool sygus_v2() const; /** * Returns true if the language that we are parsing (SMT-LIB version >=2.5 diff --git a/src/preprocessing/passes/bv_gauss.cpp b/src/preprocessing/passes/bv_gauss.cpp index 683716410..b08f69b50 100644 --- a/src/preprocessing/passes/bv_gauss.cpp +++ b/src/preprocessing/passes/bv_gauss.cpp @@ -687,8 +687,9 @@ BVGauss::Result BVGauss::gaussElimRewriteForUrem( return ret; } -BVGauss::BVGauss(PreprocessingPassContext* preprocContext) - : PreprocessingPass(preprocContext, "bv-gauss") +BVGauss::BVGauss(PreprocessingPassContext* preprocContext, + const std::string& name) + : PreprocessingPass(preprocContext, name) { } diff --git a/src/preprocessing/passes/bv_gauss.h b/src/preprocessing/passes/bv_gauss.h index 93d61be9e..7fb23814a 100644 --- a/src/preprocessing/passes/bv_gauss.h +++ b/src/preprocessing/passes/bv_gauss.h @@ -30,7 +30,8 @@ namespace passes { class BVGauss : public PreprocessingPass { public: - BVGauss(PreprocessingPassContext* preprocContext); + BVGauss(PreprocessingPassContext* preprocContext, + const std::string& name = "bv-gauss"); protected: /** diff --git a/src/preprocessing/passes/bv_to_int.cpp b/src/preprocessing/passes/bv_to_int.cpp index cb78b0897..0e4bc41c0 100644 --- a/src/preprocessing/passes/bv_to_int.cpp +++ b/src/preprocessing/passes/bv_to_int.cpp @@ -401,7 +401,28 @@ Node BVToInt::bvToInt(Node n) d_nm->mkNode(kind::MINUS, mult, multSig); d_rangeAssertions.insert( mkRangeConstraint(d_bvToIntCache[current], bvsize)); - d_rangeAssertions.insert(mkRangeConstraint(sigma, bvsize)); + if (translated_children[0].isConst() + || translated_children[1].isConst()) + { + /* + * based on equation (23), section 3.2.3 of: + * Bozzano et al. + * Encoding RTL Constructs for MathSAT: a Preliminary Report. + */ + // this is an optimization when one of the children is constant + Node c = translated_children[0].isConst() + ? translated_children[0] + : translated_children[1]; + d_rangeAssertions.insert( + d_nm->mkNode(kind::LEQ, d_zero, sigma)); + // the value of sigma is bounded by (c - 1) + // where c is the constant multiplicand + d_rangeAssertions.insert(d_nm->mkNode(kind::LT, sigma, c)); + } + else + { + d_rangeAssertions.insert(mkRangeConstraint(sigma, bvsize)); + } break; } case kind::BITVECTOR_UDIV_TOTAL: diff --git a/src/preprocessing/passes/synth_rew_rules.cpp b/src/preprocessing/passes/synth_rew_rules.cpp index 7b8e61359..f1e9e39c5 100644 --- a/src/preprocessing/passes/synth_rew_rules.cpp +++ b/src/preprocessing/passes/synth_rew_rules.cpp @@ -169,7 +169,7 @@ PreprocessingPassResult SynthRewRulesPass::applyInternal( std::stringstream ssv; if (varCounter < 26) { - ssv << String::convertUnsignedIntToChar(varCounter + 32); + ssv << static_cast<char>(varCounter + 61); } else { diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index cad3c4640..1178c7299 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -160,6 +160,11 @@ void CvcPrinter::toStream( toStreamRational(out, n, false); break; } + case kind::CONST_STRING: + { + out << '"' << n.getConst<String>().toString() << '"'; + break; + } case kind::TYPE_CONSTANT: switch(TypeConstant tc = n.getConst<TypeConstant>()) { case REAL_TYPE: diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 541827f89..6e4fcb63a 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -202,7 +202,7 @@ void Smt2Printer::toStream(std::ostream& out, } case kind::CONST_STRING: { - std::string s = n.getConst<String>().toString(true); + std::string s = n.getConst<String>().toString(); out << '"'; for(size_t i = 0; i < s.size(); ++i) { char c = s[i]; diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index 14556708b..f9e3293fa 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -60,9 +60,9 @@ std::string append(const std::string& str, uint64_t num) { ProofManager::ProofManager(context::Context* context, ProofFormat format) : d_context(context), - d_satProof(NULL), - d_cnfProof(NULL), - d_theoryProof(NULL), + d_satProof(nullptr), + d_cnfProof(nullptr), + d_theoryProof(nullptr), d_inputFormulas(), d_inputCoreFormulas(context), d_outputCoreFormulas(context), @@ -73,11 +73,7 @@ ProofManager::ProofManager(context::Context* context, ProofFormat format) { } -ProofManager::~ProofManager() { - if (d_satProof) delete d_satProof; - if (d_cnfProof) delete d_cnfProof; - if (d_theoryProof) delete d_theoryProof; -} +ProofManager::~ProofManager() {} ProofManager* ProofManager::currentPM() { return smt::currentProofManager(); @@ -89,26 +85,29 @@ const Proof& ProofManager::getProof(SmtEngine* smt) Assert(currentPM()->d_format == LFSC); currentPM()->d_fullProof.reset(new LFSCProof( smt, - static_cast<CoreSatProof*>(getSatProof()), + getSatProof(), static_cast<LFSCCnfProof*>(getCnfProof()), static_cast<LFSCTheoryProofEngine*>(getTheoryProofEngine()))); } return *(currentPM()->d_fullProof); } -CoreSatProof* ProofManager::getSatProof() { +CoreSatProof* ProofManager::getSatProof() +{ Assert(currentPM()->d_satProof); - return currentPM()->d_satProof; + return currentPM()->d_satProof.get(); } -CnfProof* ProofManager::getCnfProof() { +CnfProof* ProofManager::getCnfProof() +{ Assert(currentPM()->d_cnfProof); - return currentPM()->d_cnfProof; + return currentPM()->d_cnfProof.get(); } -TheoryProofEngine* ProofManager::getTheoryProofEngine() { +TheoryProofEngine* ProofManager::getTheoryProofEngine() +{ Assert(currentPM()->d_theoryProof != NULL); - return currentPM()->d_theoryProof; + return currentPM()->d_theoryProof.get(); } UFProof* ProofManager::getUfProof() { @@ -141,43 +140,45 @@ SkolemizationManager* ProofManager::getSkolemizationManager() { return &(currentPM()->d_skolemizationManager); } -void ProofManager::initSatProof(Minisat::Solver* solver) { - Assert(currentPM()->d_satProof == NULL); - Assert(currentPM()->d_format == LFSC); - currentPM()->d_satProof = new CoreSatProof(solver, d_context, ""); +void ProofManager::initSatProof(Minisat::Solver* solver) +{ + Assert(d_format == LFSC); + // Destroy old instance before initializing new one to avoid issues with + // registering stats + d_satProof.reset(); + d_satProof.reset(new CoreSatProof(solver, d_context, "")); } void ProofManager::initCnfProof(prop::CnfStream* cnfStream, - context::Context* ctx) { - ProofManager* pm = currentPM(); - Assert(pm->d_satProof != NULL); - Assert(pm->d_cnfProof == NULL); - Assert(pm->d_format == LFSC); - CnfProof* cnf = new LFSCCnfProof(cnfStream, ctx, ""); - pm->d_cnfProof = cnf; + context::Context* ctx) +{ + Assert(d_satProof != nullptr); + Assert(d_format == LFSC); + + d_cnfProof.reset(new LFSCCnfProof(cnfStream, ctx, "")); // true and false have to be setup in a special way Node true_node = NodeManager::currentNM()->mkConst<bool>(true); Node false_node = NodeManager::currentNM()->mkConst<bool>(false).notNode(); - pm->d_cnfProof->pushCurrentAssertion(true_node); - pm->d_cnfProof->pushCurrentDefinition(true_node); - pm->d_cnfProof->registerConvertedClause(pm->d_satProof->getTrueUnit()); - pm->d_cnfProof->popCurrentAssertion(); - pm->d_cnfProof->popCurrentDefinition(); - - pm->d_cnfProof->pushCurrentAssertion(false_node); - pm->d_cnfProof->pushCurrentDefinition(false_node); - pm->d_cnfProof->registerConvertedClause(pm->d_satProof->getFalseUnit()); - pm->d_cnfProof->popCurrentAssertion(); - pm->d_cnfProof->popCurrentDefinition(); + d_cnfProof->pushCurrentAssertion(true_node); + d_cnfProof->pushCurrentDefinition(true_node); + d_cnfProof->registerConvertedClause(d_satProof->getTrueUnit()); + d_cnfProof->popCurrentAssertion(); + d_cnfProof->popCurrentDefinition(); + d_cnfProof->pushCurrentAssertion(false_node); + d_cnfProof->pushCurrentDefinition(false_node); + d_cnfProof->registerConvertedClause(d_satProof->getFalseUnit()); + d_cnfProof->popCurrentAssertion(); + d_cnfProof->popCurrentDefinition(); } -void ProofManager::initTheoryProofEngine() { - Assert(currentPM()->d_theoryProof == NULL); - Assert(currentPM()->d_format == LFSC); - currentPM()->d_theoryProof = new LFSCTheoryProofEngine(); +void ProofManager::initTheoryProofEngine() +{ + Assert(d_theoryProof == NULL); + Assert(d_format == LFSC); + d_theoryProof.reset(new LFSCTheoryProofEngine()); } std::string ProofManager::getInputClauseName(ClauseId id, diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index ec845e41d..a59f36858 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -143,9 +143,9 @@ private: class ProofManager { context::Context* d_context; - CoreSatProof* d_satProof; - CnfProof* d_cnfProof; - TheoryProofEngine* d_theoryProof; + std::unique_ptr<CoreSatProof> d_satProof; + std::unique_ptr<CnfProof> d_cnfProof; + std::unique_ptr<TheoryProofEngine> d_theoryProof; // information that will need to be shared across proofs ExprSet d_inputFormulas; @@ -179,10 +179,9 @@ public: static ProofManager* currentPM(); // initialization - void initSatProof(Minisat::Solver* solver); - static void initCnfProof(CVC4::prop::CnfStream* cnfStream, - context::Context* ctx); - static void initTheoryProofEngine(); + void initSatProof(Minisat::Solver* solver); + void initCnfProof(CVC4::prop::CnfStream* cnfStream, context::Context* ctx); + void initTheoryProofEngine(); // getting various proofs static const Proof& getProof(SmtEngine* smt); diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index 80cce599f..f56f6a447 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -662,15 +662,6 @@ Lit Solver::pickBranchLit() { Lit nextLit; -#ifdef CVC4_REPLAY - - nextLit = MinisatSatSolver::toMinisatLit(d_proxy->getNextReplayDecision()); - - if (nextLit != lit_Undef) { - return nextLit; - } -#endif /* CVC4_REPLAY */ - // Theory requests nextLit = MinisatSatSolver::toMinisatLit(d_proxy->getNextTheoryDecisionRequest()); @@ -1547,10 +1538,6 @@ lbool Solver::search(int nof_conflicts) check_type = CHECK_FINAL; continue; } - -#ifdef CVC4_REPLAY - d_proxy->logDecision(MinisatSatSolver::toSatLiteral(next)); -#endif /* CVC4_REPLAY */ } // Increase decision level and enqueue 'next' diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index 19ee29191..89b919109 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -44,13 +44,6 @@ using namespace std; using namespace CVC4::context; - -#ifdef CVC4_REPLAY -# define CVC4_USE_REPLAY true -#else /* CVC4_REPLAY */ -# define CVC4_USE_REPLAY false -#endif /* CVC4_REPLAY */ - namespace CVC4 { namespace prop { @@ -76,9 +69,7 @@ public: PropEngine::PropEngine(TheoryEngine* te, Context* satContext, - UserContext* userContext, - std::ostream* replayLog, - ExprStream* replayStream) + UserContext* userContext) : d_inCheckSat(false), d_theoryEngine(te), d_context(satContext), @@ -101,13 +92,8 @@ PropEngine::PropEngine(TheoryEngine* te, d_cnfStream = new CVC4::prop::TseitinCnfStream( d_satSolver, d_registrar, userContext, true); - d_theoryProxy = new TheoryProxy(this, - d_theoryEngine, - d_decisionEngine.get(), - d_context, - d_cnfStream, - replayLog, - replayStream); + d_theoryProxy = new TheoryProxy( + this, d_theoryEngine, d_decisionEngine.get(), d_context, d_cnfStream); d_satSolver->initialize(d_context, d_theoryProxy); d_decisionEngine->setSatSolver(d_satSolver); @@ -115,6 +101,11 @@ PropEngine::PropEngine(TheoryEngine* te, PROOF ( ProofManager::currentPM()->initCnfProof(d_cnfStream, userContext); ); + + NodeManager* nm = NodeManager::currentNM(); + d_cnfStream->convertAndAssert(nm->mkConst(true), false, false, RULE_GIVEN); + d_cnfStream->convertAndAssert( + nm->mkConst(false).notNode(), false, false, RULE_GIVEN); } PropEngine::~PropEngine() { diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index 707244ff5..f1d73fc92 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -24,7 +24,6 @@ #include <sys/time.h> #include "base/modal_exception.h" -#include "expr/expr_stream.h" #include "expr/node.h" #include "options/options.h" #include "preprocessing/assertion_pipeline.h" @@ -62,9 +61,7 @@ class PropEngine */ PropEngine(TheoryEngine*, context::Context* satContext, - context::UserContext* userContext, - std::ostream* replayLog, - ExprStream* replayStream); + context::UserContext* userContext); /** * Destructor. diff --git a/src/prop/theory_proxy.cpp b/src/prop/theory_proxy.cpp index 557dcc413..38c99f551 100644 --- a/src/prop/theory_proxy.cpp +++ b/src/prop/theory_proxy.cpp @@ -18,7 +18,6 @@ #include "context/context.h" #include "decision/decision_engine.h" -#include "expr/expr_stream.h" #include "options/decision_options.h" #include "prop/cnf_stream.h" #include "prop/prop_engine.h" @@ -37,24 +36,17 @@ TheoryProxy::TheoryProxy(PropEngine* propEngine, TheoryEngine* theoryEngine, DecisionEngine* decisionEngine, context::Context* context, - CnfStream* cnfStream, - std::ostream* replayLog, - ExprStream* replayStream) + CnfStream* cnfStream) : d_propEngine(propEngine), d_cnfStream(cnfStream), d_decisionEngine(decisionEngine), d_theoryEngine(theoryEngine), - d_replayLog(replayLog), - d_replayStream(replayStream), - d_queue(context), - d_replayedDecisions("prop::theoryproxy::replayedDecisions", 0) + d_queue(context) { - smtStatisticsRegistry()->registerStat(&d_replayedDecisions); } TheoryProxy::~TheoryProxy() { /* nothing to do for now */ - smtStatisticsRegistry()->unregisterStat(&d_replayedDecisions); } void TheoryProxy::variableNotify(SatVariable var) { @@ -150,29 +142,6 @@ void TheoryProxy::notifyRestart() { d_theoryEngine->notifyRestart(); } -SatLiteral TheoryProxy::getNextReplayDecision() { -#ifdef CVC4_REPLAY - if(d_replayStream != NULL) { - Expr e = d_replayStream->nextExpr(); - if(!e.isNull()) { // we get null node when out of decisions to replay - // convert & return - ++d_replayedDecisions; - return d_cnfStream->getLiteral(e); - } - } -#endif /* CVC4_REPLAY */ - return undefSatLiteral; -} - -void TheoryProxy::logDecision(SatLiteral lit) { -#ifdef CVC4_REPLAY - if(d_replayLog != NULL) { - Assert(lit != undefSatLiteral) << "logging an `undef' decision ?!"; - (*d_replayLog) << d_cnfStream->getNode(lit) << std::endl; - } -#endif /* CVC4_REPLAY */ -} - void TheoryProxy::spendResource(ResourceManager::Resource r) { d_theoryEngine->spendResource(r); diff --git a/src/prop/theory_proxy.h b/src/prop/theory_proxy.h index 0d76b473f..089d2082d 100644 --- a/src/prop/theory_proxy.h +++ b/src/prop/theory_proxy.h @@ -27,7 +27,6 @@ #include <unordered_set> #include "context/cdqueue.h" -#include "expr/expr_stream.h" #include "expr/node.h" #include "prop/sat_solver.h" #include "theory/theory.h" @@ -54,9 +53,7 @@ class TheoryProxy TheoryEngine* theoryEngine, DecisionEngine* decisionEngine, context::Context* context, - CnfStream* cnfStream, - std::ostream* replayLog, - ExprStream* replayStream); + CnfStream* cnfStream); ~TheoryProxy(); @@ -83,10 +80,6 @@ class TheoryProxy void notifyRestart(); - SatLiteral getNextReplayDecision(); - - void logDecision(SatLiteral lit); - void spendResource(ResourceManager::Resource r); bool isDecisionEngineDone(); @@ -111,12 +104,6 @@ class TheoryProxy /** The theory engine we are using. */ TheoryEngine* d_theoryEngine; - /** Stream on which to log replay events. */ - std::ostream* d_replayLog; - - /** Stream for replaying decisions. */ - ExprStream* d_replayStream; - /** Queue of asserted facts */ context::CDQueue<TNode> d_queue; @@ -126,11 +113,6 @@ class TheoryProxy */ std::unordered_set<Node, NodeHashFunction> d_shared; - /** - * Statistic: the number of replayed decisions (via --replay). - */ - IntStat d_replayedDecisions; - }; /* class SatSolver */ }/* CVC4::prop namespace */ diff --git a/src/smt/command.cpp b/src/smt/command.cpp index 79cc465ac..20f2dcff9 100644 --- a/src/smt/command.cpp +++ b/src/smt/command.cpp @@ -513,7 +513,7 @@ void QueryCommand::invoke(SmtEngine* smtEngine) { try { - d_result = smtEngine->query(d_expr); + d_result = smtEngine->checkEntailed(d_expr); d_commandStatus = CommandSuccess::instance(); } catch (exception& e) diff --git a/src/smt/managed_ostreams.cpp b/src/smt/managed_ostreams.cpp index a73ec44f4..7615325b7 100644 --- a/src/smt/managed_ostreams.cpp +++ b/src/smt/managed_ostreams.cpp @@ -163,31 +163,4 @@ void ManagedDiagnosticOutputChannel::addSpecialCases(OstreamOpener* opener) opener->addSpecialCase("stderr", &std::cerr); } - -ManagedReplayLogOstream::ManagedReplayLogOstream() : d_replayLog(NULL) {} -ManagedReplayLogOstream::~ManagedReplayLogOstream(){ - if(d_replayLog != NULL) { - (*d_replayLog) << std::flush; - } -} - -std::string ManagedReplayLogOstream::defaultSource() const { - return options::replayLogFilename(); -} - -void ManagedReplayLogOstream::initialize(std::ostream* outStream) { - if(outStream != NULL){ - *outStream << language::SetLanguage(options::outputLanguage()) - << expr::ExprSetDepth(-1); - } - /* Do this regardless of managing the memory. */ - d_replayLog = outStream; -} - -/** Adds special cases to an ostreamopener. */ -void ManagedReplayLogOstream::addSpecialCases(OstreamOpener* opener) const { - opener->addSpecialCase("-", &std::cout); -} - - }/* CVC4 namespace */ diff --git a/src/smt/managed_ostreams.h b/src/smt/managed_ostreams.h index f495f8e72..bc12bbe39 100644 --- a/src/smt/managed_ostreams.h +++ b/src/smt/managed_ostreams.h @@ -156,27 +156,6 @@ class ManagedDiagnosticOutputChannel : public ManagedOstream { void addSpecialCases(OstreamOpener* opener) const override; };/* class ManagedRegularOutputChannel */ -/** This controls the memory associated with replay-log. */ -class ManagedReplayLogOstream : public ManagedOstream { - public: - ManagedReplayLogOstream(); - ~ManagedReplayLogOstream(); - - std::ostream* getReplayLog() const { return d_replayLog; } - const char* getName() const override { return "replay-log"; } - std::string defaultSource() const override; - - protected: - /** Initializes an output stream. Not necessarily managed. */ - void initialize(std::ostream* outStream) override; - - /** Adds special cases to an ostreamopener. */ - void addSpecialCases(OstreamOpener* opener) const override; - - private: - std::ostream* d_replayLog; -};/* class ManagedRegularOutputChannel */ - }/* CVC4 namespace */ #endif /* CVC4__MANAGED_OSTREAMS_H */ diff --git a/src/smt/set_defaults.cpp b/src/smt/set_defaults.cpp new file mode 100644 index 000000000..e0493b180 --- /dev/null +++ b/src/smt/set_defaults.cpp @@ -0,0 +1,1353 @@ +/********************* */ +/*! \file set_defaults.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of setting default options. + **/ + +#include "smt/set_defaults.h" + +#include "base/output.h" +#include "options/arith_options.h" +#include "options/arrays_options.h" +#include "options/base_options.h" +#include "options/booleans_options.h" +#include "options/bv_options.h" +#include "options/datatypes_options.h" +#include "options/decision_options.h" +#include "options/language.h" +#include "options/main_options.h" +#include "options/open_ostream.h" +#include "options/option_exception.h" +#include "options/printer_options.h" +#include "options/proof_options.h" +#include "options/prop_options.h" +#include "options/quantifiers_options.h" +#include "options/sep_options.h" +#include "options/set_language.h" +#include "options/smt_options.h" +#include "options/strings_options.h" +#include "options/theory_options.h" +#include "options/uf_options.h" +#include "theory/theory.h" + +using namespace CVC4::theory; + +namespace CVC4 { +namespace smt { + +void setDefaults(SmtEngine& smte, LogicInfo& logic) +{ + // Language-based defaults + if (!options::bitvectorDivByZeroConst.wasSetByUser()) + { + // Bitvector-divide-by-zero changed semantics in SMT LIB 2.6, thus we + // set this option if the input format is SMT LIB 2.6. We also set this + // option if we are sygus, since we assume SMT LIB 2.6 semantics for sygus. + options::bitvectorDivByZeroConst.set( + language::isInputLang_smt2_6(options::inputLanguage()) + || language::isInputLangSygus(options::inputLanguage())); + } + bool is_sygus = language::isInputLangSygus(options::inputLanguage()); + + if (options::bitblastMode() == options::BitblastMode::EAGER) + { + if (options::produceModels() + && (logic.isTheoryEnabled(THEORY_ARRAYS) + || logic.isTheoryEnabled(THEORY_UF))) + { + if (options::bitblastMode.wasSetByUser() + || options::produceModels.wasSetByUser()) + { + throw OptionException(std::string( + "Eager bit-blasting currently does not support model generation " + "for the combination of bit-vectors with arrays or uinterpreted " + "functions. Try --bitblast=lazy")); + } + Notice() << "SmtEngine: setting bit-blast mode to lazy to support model" + << "generation" << std::endl; + smte.setOption("bitblastMode", SExpr("lazy")); + } + else if (!options::incrementalSolving()) + { + options::ackermann.set(true); + } + + if (options::incrementalSolving() && !logic.isPure(THEORY_BV)) + { + throw OptionException( + "Incremental eager bit-blasting is currently " + "only supported for QF_BV. Try --bitblast=lazy."); + } + } + + if (options::solveIntAsBV() > 0) + { + logic = logic.getUnlockedCopy(); + logic.enableTheory(THEORY_BV); + logic.lock(); + } + + if (options::solveBVAsInt() > 0) + { + if (logic.isTheoryEnabled(THEORY_BV)) + { + logic = logic.getUnlockedCopy(); + logic.enableTheory(THEORY_ARITH); + logic.arithNonLinear(); + logic.lock(); + } + } + + // set options about ackermannization + if (options::ackermann() && options::produceModels() + && (logic.isTheoryEnabled(THEORY_ARRAYS) + || logic.isTheoryEnabled(THEORY_UF))) + { + if (options::produceModels.wasSetByUser()) + { + throw OptionException(std::string( + "Ackermannization currently does not support model generation.")); + } + Notice() << "SmtEngine: turn off ackermannization to support model" + << "generation" << std::endl; + options::ackermann.set(false); + } + + if (options::ackermann()) + { + if (options::incrementalSolving()) + { + throw OptionException( + "Incremental Ackermannization is currently not supported."); + } + + if (logic.isQuantified()) + { + throw LogicException("Cannot use Ackermannization on quantified formula"); + } + + if (logic.isTheoryEnabled(THEORY_UF)) + { + logic = logic.getUnlockedCopy(); + logic.disableTheory(THEORY_UF); + logic.lock(); + } + if (logic.isTheoryEnabled(THEORY_ARRAYS)) + { + logic = logic.getUnlockedCopy(); + logic.disableTheory(THEORY_ARRAYS); + logic.lock(); + } + } + + // Set default options associated with strings-exp. We also set these options + // if we are using eager string preprocessing, which may introduce quantified + // formulas at preprocess time. + if (options::stringExp() || !options::stringLazyPreproc()) + { + // We require quantifiers since extended functions reduce using them. + if (!logic.isQuantified()) + { + logic = logic.getUnlockedCopy(); + logic.enableQuantifiers(); + logic.lock(); + Trace("smt") << "turning on quantifier logic, for strings-exp" + << std::endl; + } + // We require bounded quantifier handling. + if (!options::fmfBound.wasSetByUser()) + { + options::fmfBound.set(true); + Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; + } + // Turn off E-matching, since some bounded quantifiers introduced by strings + // (e.g. for replaceall) admit matching loops. + if (!options::eMatching.wasSetByUser()) + { + options::eMatching.set(false); + Trace("smt") << "turning off E-matching, for strings-exp" << std::endl; + } + // Do not eliminate extended arithmetic symbols from quantified formulas, + // since some strategies, e.g. --re-elim-agg, introduce them. + if (!options::elimExtArithQuant.wasSetByUser()) + { + options::elimExtArithQuant.set(false); + Trace("smt") << "turning off elim-ext-arith-quant, for strings-exp" + << std::endl; + } + } + + // sygus inference may require datatypes + if (!smte.isInternalSubsolver()) + { + if (options::produceAbducts() || options::sygusInference() + || options::sygusRewSynthInput()) + { + // since we are trying to recast as sygus, we assume the input is sygus + is_sygus = true; + } + } + + // We now know whether the input is sygus. Update the logic to incorporate + // the theories we need internally for handling sygus problems. + if (is_sygus) + { + logic = logic.getUnlockedCopy(); + logic.enableSygus(); + logic.lock(); + } + + // sygus core connective requires unsat cores + if (options::sygusCoreConnective()) + { + options::unsatCores.set(true); + } + + if ((options::checkModels() || options::checkSynthSol() + || options::produceAbducts() + || options::modelCoresMode() != options::ModelCoresMode::NONE + || options::blockModelsMode() != options::BlockModelsMode::NONE) + && !options::produceAssertions()) + { + Notice() << "SmtEngine: turning on produce-assertions to support " + << "option requiring assertions." << std::endl; + smte.setOption("produce-assertions", SExpr("true")); + } + + // Disable options incompatible with incremental solving, unsat cores, and + // proofs or output an error if enabled explicitly + if (options::incrementalSolving() || options::unsatCores() + || options::proof()) + { + if (options::unconstrainedSimp()) + { + if (options::unconstrainedSimp.wasSetByUser()) + { + throw OptionException( + "unconstrained simplification not supported with unsat " + "cores/proofs/incremental solving"); + } + Notice() << "SmtEngine: turning off unconstrained simplification to " + "support unsat cores/proofs/incremental solving" + << std::endl; + options::unconstrainedSimp.set(false); + } + } + else + { + // Turn on unconstrained simplification for QF_AUFBV + if (!options::unconstrainedSimp.wasSetByUser()) + { + bool uncSimp = !logic.isQuantified() && !options::produceModels() + && !options::produceAssignments() + && !options::checkModels() + && (logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_BV)); + Trace("smt") << "setting unconstrained simplification to " << uncSimp + << std::endl; + options::unconstrainedSimp.set(uncSimp); + } + } + + if (options::incrementalSolving() || options::proof()) + { + if (options::sygusInference()) + { + if (options::sygusInference.wasSetByUser()) + { + throw OptionException( + "sygus inference not supported with proofs/incremental solving"); + } + Notice() << "SmtEngine: turning off sygus inference to support " + "proofs/incremental solving" + << std::endl; + options::sygusInference.set(false); + } + } + + // Disable options incompatible with unsat cores and proofs or output an + // error if enabled explicitly + if (options::unsatCores() || options::proof()) + { + if (options::simplificationMode() != options::SimplificationMode::NONE) + { + if (options::simplificationMode.wasSetByUser()) + { + throw OptionException( + "simplification not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off simplification to support unsat " + "cores/proofs" + << std::endl; + options::simplificationMode.set(options::SimplificationMode::NONE); + } + + if (options::pbRewrites()) + { + if (options::pbRewrites.wasSetByUser()) + { + throw OptionException( + "pseudoboolean rewrites not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off pseudoboolean rewrites to support " + "unsat cores/proofs" + << std::endl; + smte.setOption("pb-rewrites", false); + } + + if (options::sortInference()) + { + if (options::sortInference.wasSetByUser()) + { + throw OptionException( + "sort inference not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off sort inference to support unsat " + "cores/proofs" + << std::endl; + options::sortInference.set(false); + } + + if (options::preSkolemQuant()) + { + if (options::preSkolemQuant.wasSetByUser()) + { + throw OptionException( + "pre-skolemization not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off pre-skolemization to support unsat " + "cores/proofs" + << std::endl; + options::preSkolemQuant.set(false); + } + + if (options::solveBVAsInt() > 0) + { + /** + * Operations on 1 bits are better handled as Boolean operations + * than as integer operations. + * Therefore, we enable bv-to-bool, which runs before + * the translation to integers. + */ + options::bitvectorToBool.set(true); + } + + if (options::bitvectorToBool()) + { + if (options::bitvectorToBool.wasSetByUser()) + { + throw OptionException( + "bv-to-bool not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off bitvector-to-bool to support unsat " + "cores/proofs" + << std::endl; + options::bitvectorToBool.set(false); + } + + if (options::boolToBitvector() != options::BoolToBVMode::OFF) + { + if (options::boolToBitvector.wasSetByUser()) + { + throw OptionException( + "bool-to-bv != off not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off bool-to-bv to support unsat " + "cores/proofs" + << std::endl; + smte.setOption("boolToBitvector", SExpr("off")); + } + + if (options::bvIntroducePow2()) + { + if (options::bvIntroducePow2.wasSetByUser()) + { + throw OptionException( + "bv-intro-pow2 not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off bv-intro-pow2 to support " + "unsat-cores/proofs" + << std::endl; + smte.setOption("bv-intro-pow2", false); + } + + if (options::repeatSimp()) + { + if (options::repeatSimp.wasSetByUser()) + { + throw OptionException( + "repeat-simp not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off repeat-simp to support unsat " + "cores/proofs" + << std::endl; + smte.setOption("repeat-simp", false); + } + + if (options::globalNegate()) + { + if (options::globalNegate.wasSetByUser()) + { + throw OptionException( + "global-negate not supported with unsat cores/proofs"); + } + Notice() << "SmtEngine: turning off global-negate to support unsat " + "cores/proofs" + << std::endl; + smte.setOption("global-negate", false); + } + + if (options::bitvectorAig()) + { + throw OptionException( + "bitblast-aig not supported with unsat cores/proofs"); + } + } + else + { + // by default, nonclausal simplification is off for QF_SAT + if (!options::simplificationMode.wasSetByUser()) + { + bool qf_sat = logic.isPure(THEORY_BOOL) && !logic.isQuantified(); + Trace("smt") << "setting simplification mode to <" + << logic.getLogicString() << "> " << (!qf_sat) << std::endl; + // simplification=none works better for SMT LIB benchmarks with + // quantifiers, not others options::simplificationMode.set(qf_sat || + // quantifiers ? options::SimplificationMode::NONE : + // options::SimplificationMode::BATCH); + options::simplificationMode.set(qf_sat + ? options::SimplificationMode::NONE + : options::SimplificationMode::BATCH); + } + } + + if (options::cbqiBv() && logic.isQuantified()) + { + if (options::boolToBitvector() != options::BoolToBVMode::OFF) + { + if (options::boolToBitvector.wasSetByUser()) + { + throw OptionException( + "bool-to-bv != off not supported with CBQI BV for quantified " + "logics"); + } + Notice() << "SmtEngine: turning off bool-to-bitvector to support CBQI BV" + << std::endl; + smte.setOption("boolToBitvector", SExpr("off")); + } + } + + // cases where we need produce models + if (!options::produceModels() + && (options::produceAssignments() || options::sygusRewSynthCheck() + || is_sygus)) + { + Notice() << "SmtEngine: turning on produce-models" << std::endl; + smte.setOption("produce-models", SExpr("true")); + } + + // Set the options for the theoryOf + if (!options::theoryOfMode.wasSetByUser()) + { + if (logic.isSharingEnabled() && !logic.isTheoryEnabled(THEORY_BV) + && !logic.isTheoryEnabled(THEORY_STRINGS) + && !logic.isTheoryEnabled(THEORY_SETS)) + { + Trace("smt") << "setting theoryof-mode to term-based" << std::endl; + options::theoryOfMode.set(options::TheoryOfMode::THEORY_OF_TERM_BASED); + } + } + + // strings require LIA, UF; widen the logic + if (logic.isTheoryEnabled(THEORY_STRINGS)) + { + LogicInfo log(logic.getUnlockedCopy()); + // Strings requires arith for length constraints, and also UF + if (!logic.isTheoryEnabled(THEORY_UF)) + { + Trace("smt") << "because strings are enabled, also enabling UF" + << std::endl; + log.enableTheory(THEORY_UF); + } + if (!logic.isTheoryEnabled(THEORY_ARITH) || logic.isDifferenceLogic() + || !logic.areIntegersUsed()) + { + Trace("smt") << "because strings are enabled, also enabling linear " + "integer arithmetic" + << std::endl; + log.enableTheory(THEORY_ARITH); + log.enableIntegers(); + log.arithOnlyLinear(); + } + logic = log; + logic.lock(); + } + if (logic.isTheoryEnabled(THEORY_ARRAYS) + || logic.isTheoryEnabled(THEORY_DATATYPES) + || logic.isTheoryEnabled(THEORY_SETS)) + { + if (!logic.isTheoryEnabled(THEORY_UF)) + { + LogicInfo log(logic.getUnlockedCopy()); + Trace("smt") << "because a theory that permits Boolean terms is enabled, " + "also enabling UF" + << std::endl; + log.enableTheory(THEORY_UF); + logic = log; + logic.lock(); + } + } + + // by default, symmetry breaker is on only for non-incremental QF_UF + if (!options::ufSymmetryBreaker.wasSetByUser()) + { + bool qf_uf_noinc = logic.isPure(THEORY_UF) && !logic.isQuantified() + && !options::incrementalSolving() && !options::proof() + && !options::unsatCores(); + Trace("smt") << "setting uf symmetry breaker to " << qf_uf_noinc + << std::endl; + options::ufSymmetryBreaker.set(qf_uf_noinc); + } + + // If in arrays, set the UF handler to arrays + if (logic.isTheoryEnabled(THEORY_ARRAYS) + && (!logic.isQuantified() + || (logic.isQuantified() && !logic.isTheoryEnabled(THEORY_UF)))) + { + Theory::setUninterpretedSortOwner(THEORY_ARRAYS); + } + else + { + Theory::setUninterpretedSortOwner(THEORY_UF); + } + + if (!options::simplifyWithCareEnabled.wasSetByUser()) + { + bool qf_aufbv = + !logic.isQuantified() && logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_UF) && logic.isTheoryEnabled(THEORY_BV); + + bool withCare = qf_aufbv; + Trace("smt") << "setting ite simplify with care to " << withCare + << std::endl; + options::simplifyWithCareEnabled.set(withCare); + } + // Turn off array eager index splitting for QF_AUFLIA + if (!options::arraysEagerIndexSplitting.wasSetByUser()) + { + if (not logic.isQuantified() && logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_UF) + && logic.isTheoryEnabled(THEORY_ARITH)) + { + Trace("smt") << "setting array eager index splitting to false" + << std::endl; + options::arraysEagerIndexSplitting.set(false); + } + } + // Turn on multiple-pass non-clausal simplification for QF_AUFBV + if (!options::repeatSimp.wasSetByUser()) + { + bool repeatSimp = !logic.isQuantified() + && (logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_UF) + && logic.isTheoryEnabled(THEORY_BV)) + && !options::unsatCores(); + Trace("smt") << "setting repeat simplification to " << repeatSimp + << std::endl; + options::repeatSimp.set(repeatSimp); + } + + if (options::boolToBitvector() == options::BoolToBVMode::ALL + && !logic.isTheoryEnabled(THEORY_BV)) + { + if (options::boolToBitvector.wasSetByUser()) + { + throw OptionException( + "bool-to-bv=all not supported for non-bitvector logics."); + } + Notice() << "SmtEngine: turning off bool-to-bv for non-bv logic: " + << logic.getLogicString() << std::endl; + smte.setOption("boolToBitvector", SExpr("off")); + } + + if (!options::bvEagerExplanations.wasSetByUser() + && logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_BV)) + { + Trace("smt") << "enabling eager bit-vector explanations " << std::endl; + options::bvEagerExplanations.set(true); + } + + // Turn on arith rewrite equalities only for pure arithmetic + if (!options::arithRewriteEq.wasSetByUser()) + { + bool arithRewriteEq = + logic.isPure(THEORY_ARITH) && logic.isLinear() && !logic.isQuantified(); + Trace("smt") << "setting arith rewrite equalities " << arithRewriteEq + << std::endl; + options::arithRewriteEq.set(arithRewriteEq); + } + if (!options::arithHeuristicPivots.wasSetByUser()) + { + int16_t heuristicPivots = 5; + if (logic.isPure(THEORY_ARITH) && !logic.isQuantified()) + { + if (logic.isDifferenceLogic()) + { + heuristicPivots = -1; + } + else if (!logic.areIntegersUsed()) + { + heuristicPivots = 0; + } + } + Trace("smt") << "setting arithHeuristicPivots " << heuristicPivots + << std::endl; + options::arithHeuristicPivots.set(heuristicPivots); + } + if (!options::arithPivotThreshold.wasSetByUser()) + { + uint16_t pivotThreshold = 2; + if (logic.isPure(THEORY_ARITH) && !logic.isQuantified()) + { + if (logic.isDifferenceLogic()) + { + pivotThreshold = 16; + } + } + Trace("smt") << "setting arith arithPivotThreshold " << pivotThreshold + << std::endl; + options::arithPivotThreshold.set(pivotThreshold); + } + if (!options::arithStandardCheckVarOrderPivots.wasSetByUser()) + { + int16_t varOrderPivots = -1; + if (logic.isPure(THEORY_ARITH) && !logic.isQuantified()) + { + varOrderPivots = 200; + } + Trace("smt") << "setting arithStandardCheckVarOrderPivots " + << varOrderPivots << std::endl; + options::arithStandardCheckVarOrderPivots.set(varOrderPivots); + } + if (logic.isPure(THEORY_ARITH) && !logic.areRealsUsed()) + { + if (!options::nlExtTangentPlanesInterleave.wasSetByUser()) + { + Trace("smt") << "setting nlExtTangentPlanesInterleave to true" + << std::endl; + options::nlExtTangentPlanesInterleave.set(true); + } + } + + // Set decision mode based on logic (if not set by user) + if (!options::decisionMode.wasSetByUser()) + { + options::DecisionMode decMode = + // sygus uses internal + is_sygus ? options::DecisionMode::INTERNAL : + // ALL + logic.hasEverything() + ? options::DecisionMode::JUSTIFICATION + : ( // QF_BV + (not logic.isQuantified() && logic.isPure(THEORY_BV)) || + // QF_AUFBV or QF_ABV or QF_UFBV + (not logic.isQuantified() + && (logic.isTheoryEnabled(THEORY_ARRAYS) + || logic.isTheoryEnabled(THEORY_UF)) + && logic.isTheoryEnabled(THEORY_BV)) + || + // QF_AUFLIA (and may be ends up enabling + // QF_AUFLRA?) + (not logic.isQuantified() + && logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_UF) + && logic.isTheoryEnabled(THEORY_ARITH)) + || + // QF_LRA + (not logic.isQuantified() + && logic.isPure(THEORY_ARITH) && logic.isLinear() + && !logic.isDifferenceLogic() + && !logic.areIntegersUsed()) + || + // Quantifiers + logic.isQuantified() || + // Strings + logic.isTheoryEnabled(THEORY_STRINGS) + ? options::DecisionMode::JUSTIFICATION + : options::DecisionMode::INTERNAL); + + bool stoponly = + // ALL + logic.hasEverything() || logic.isTheoryEnabled(THEORY_STRINGS) + ? false + : ( // QF_AUFLIA + (not logic.isQuantified() + && logic.isTheoryEnabled(THEORY_ARRAYS) + && logic.isTheoryEnabled(THEORY_UF) + && logic.isTheoryEnabled(THEORY_ARITH)) + || + // QF_LRA + (not logic.isQuantified() + && logic.isPure(THEORY_ARITH) && logic.isLinear() + && !logic.isDifferenceLogic() + && !logic.areIntegersUsed()) + ? true + : false); + + Trace("smt") << "setting decision mode to " << decMode << std::endl; + options::decisionMode.set(decMode); + options::decisionStopOnly.set(stoponly); + } + if (options::incrementalSolving()) + { + // disable modes not supported by incremental + options::sortInference.set(false); + options::ufssFairnessMonotone.set(false); + options::quantEpr.set(false); + options::globalNegate.set(false); + } + if (logic.hasCardinalityConstraints()) + { + // must have finite model finding on + options::finiteModelFind.set(true); + } + + // if it contains a theory with non-termination, do not strictly enforce that + // quantifiers and theory combination must be interleaved + if (logic.isTheoryEnabled(THEORY_STRINGS) + || (logic.isTheoryEnabled(THEORY_ARITH) && !logic.isLinear())) + { + if (!options::instWhenStrictInterleave.wasSetByUser()) + { + options::instWhenStrictInterleave.set(false); + } + } + + if (options::instMaxLevel() != -1) + { + Notice() << "SmtEngine: turning off cbqi to support instMaxLevel" + << std::endl; + options::cbqi.set(false); + } + // Do we need to track instantiations? + // Needed for sygus due to single invocation techniques. + if (options::cbqiNestedQE() + || (options::proof() && !options::trackInstLemmas.wasSetByUser()) + || is_sygus) + { + options::trackInstLemmas.set(true); + } + + if ((options::fmfBoundLazy.wasSetByUser() && options::fmfBoundLazy()) + || (options::fmfBoundInt.wasSetByUser() && options::fmfBoundInt())) + { + options::fmfBound.set(true); + } + // now have determined whether fmfBoundInt is on/off + // apply fmfBoundInt options + if (options::fmfBound()) + { + if (!options::mbqiMode.wasSetByUser() + || (options::mbqiMode() != options::MbqiMode::NONE + && options::mbqiMode() != options::MbqiMode::FMC)) + { + // if bounded integers are set, use no MBQI by default + options::mbqiMode.set(options::MbqiMode::NONE); + } + if (!options::prenexQuant.wasSetByUser()) + { + options::prenexQuant.set(options::PrenexQuantMode::NONE); + } + } + if (options::ufHo()) + { + // if higher-order, then current variants of model-based instantiation + // cannot be used + if (options::mbqiMode() != options::MbqiMode::NONE) + { + options::mbqiMode.set(options::MbqiMode::NONE); + } + if (!options::hoElimStoreAx.wasSetByUser()) + { + // by default, use store axioms only if --ho-elim is set + options::hoElimStoreAx.set(options::hoElim()); + } + } + if (options::fmfFunWellDefinedRelevant()) + { + if (!options::fmfFunWellDefined.wasSetByUser()) + { + options::fmfFunWellDefined.set(true); + } + } + if (options::fmfFunWellDefined()) + { + if (!options::finiteModelFind.wasSetByUser()) + { + options::finiteModelFind.set(true); + } + } + // EPR + if (options::quantEpr()) + { + if (!options::preSkolemQuant.wasSetByUser()) + { + options::preSkolemQuant.set(true); + } + } + + // now, have determined whether finite model find is on/off + // apply finite model finding options + if (options::finiteModelFind()) + { + // apply conservative quantifiers splitting + if (!options::quantDynamicSplit.wasSetByUser()) + { + options::quantDynamicSplit.set(options::QuantDSplitMode::DEFAULT); + } + // do not eliminate extended arithmetic symbols from quantified formulas + if (!options::elimExtArithQuant.wasSetByUser()) + { + options::elimExtArithQuant.set(false); + } + if (!options::eMatching.wasSetByUser()) + { + options::eMatching.set(options::fmfInstEngine()); + } + if (!options::instWhenMode.wasSetByUser()) + { + // instantiate only on last call + if (options::eMatching()) + { + options::instWhenMode.set(options::InstWhenMode::LAST_CALL); + } + } + } + + // apply sygus options + // if we are attempting to rewrite everything to SyGuS, use sygus() + if (is_sygus) + { + if (!options::sygus()) + { + Trace("smt") << "turning on sygus" << std::endl; + } + options::sygus.set(true); + // must use Ferrante/Rackoff for real arithmetic + if (!options::cbqiMidpoint.wasSetByUser()) + { + options::cbqiMidpoint.set(true); + } + if (options::sygusRepairConst()) + { + if (!options::cbqi.wasSetByUser()) + { + options::cbqi.set(true); + } + } + if (options::sygusInference()) + { + // optimization: apply preskolemization, makes it succeed more often + if (!options::preSkolemQuant.wasSetByUser()) + { + options::preSkolemQuant.set(true); + } + if (!options::preSkolemQuantNested.wasSetByUser()) + { + options::preSkolemQuantNested.set(true); + } + } + // counterexample-guided instantiation for sygus + if (!options::cegqiSingleInvMode.wasSetByUser()) + { + options::cegqiSingleInvMode.set(options::CegqiSingleInvMode::USE); + } + if (!options::quantConflictFind.wasSetByUser()) + { + options::quantConflictFind.set(false); + } + if (!options::instNoEntail.wasSetByUser()) + { + options::instNoEntail.set(false); + } + if (!options::cbqiFullEffort.wasSetByUser()) + { + // should use full effort cbqi for single invocation and repair const + options::cbqiFullEffort.set(true); + } + if (options::sygusRew()) + { + options::sygusRewSynth.set(true); + options::sygusRewVerify.set(true); + } + if (options::sygusRewSynthInput()) + { + // If we are using synthesis rewrite rules from input, we use + // sygusRewSynth after preprocessing. See passes/synth_rew_rules.h for + // details on this technique. + options::sygusRewSynth.set(true); + // we should not use the extended rewriter, since we are interested + // in rewrites that are not in the main rewriter + if (!options::sygusExtRew.wasSetByUser()) + { + options::sygusExtRew.set(false); + } + } + // Whether we must use "basic" sygus algorithms. A non-basic sygus algorithm + // is one that is specialized for returning a single solution. Non-basic + // sygus algorithms currently include the PBE solver, UNIF+PI, static + // template inference for invariant synthesis, and single invocation + // techniques. + bool reqBasicSygus = false; + if (options::produceAbducts()) + { + // if doing abduction, we should filter strong solutions + if (!options::sygusFilterSolMode.wasSetByUser()) + { + options::sygusFilterSolMode.set(options::SygusFilterSolMode::STRONG); + } + // we must use basic sygus algorithms, since e.g. we require checking + // a sygus side condition for consistency with axioms. + reqBasicSygus = true; + } + if (options::sygusRewSynth() || options::sygusRewVerify() + || options::sygusQueryGen()) + { + // rewrite rule synthesis implies that sygus stream must be true + options::sygusStream.set(true); + } + if (options::sygusStream() || options::incrementalSolving()) + { + // Streaming and incremental mode are incompatible with techniques that + // focus the search towards finding a single solution. + reqBasicSygus = true; + } + // Now, disable options for non-basic sygus algorithms, if necessary. + if (reqBasicSygus) + { + if (!options::sygusUnifPbe.wasSetByUser()) + { + options::sygusUnifPbe.set(false); + } + if (options::sygusUnifPi.wasSetByUser()) + { + options::sygusUnifPi.set(options::SygusUnifPiMode::NONE); + } + if (!options::sygusInvTemplMode.wasSetByUser()) + { + options::sygusInvTemplMode.set(options::SygusInvTemplMode::NONE); + } + if (!options::cegqiSingleInvMode.wasSetByUser()) + { + options::cegqiSingleInvMode.set(options::CegqiSingleInvMode::NONE); + } + } + // do not allow partial functions + if (!options::bitvectorDivByZeroConst()) + { + if (options::bitvectorDivByZeroConst.wasSetByUser()) + { + throw OptionException( + "--no-bv-div-zero-const is not supported with SyGuS"); + } + Notice() + << "SmtEngine: setting bv-div-zero-const to true to support SyGuS" + << std::endl; + options::bitvectorDivByZeroConst.set(true); + } + if (!options::dtRewriteErrorSel.wasSetByUser()) + { + options::dtRewriteErrorSel.set(true); + } + // do not miniscope + if (!options::miniscopeQuant.wasSetByUser()) + { + options::miniscopeQuant.set(false); + } + if (!options::miniscopeQuantFreeVar.wasSetByUser()) + { + options::miniscopeQuantFreeVar.set(false); + } + if (!options::quantSplit.wasSetByUser()) + { + options::quantSplit.set(false); + } + // rewrite divk + if (!options::rewriteDivk.wasSetByUser()) + { + options::rewriteDivk.set(true); + } + // do not do macros + if (!options::macrosQuant.wasSetByUser()) + { + options::macrosQuant.set(false); + } + if (!options::cbqiPreRegInst.wasSetByUser()) + { + options::cbqiPreRegInst.set(true); + } + } + // counterexample-guided instantiation for non-sygus + // enable if any possible quantifiers with arithmetic, datatypes or bitvectors + if ((logic.isQuantified() + && (logic.isTheoryEnabled(THEORY_ARITH) + || logic.isTheoryEnabled(THEORY_DATATYPES) + || logic.isTheoryEnabled(THEORY_BV) + || logic.isTheoryEnabled(THEORY_FP))) + || options::cbqiAll()) + { + if (!options::cbqi.wasSetByUser()) + { + options::cbqi.set(true); + } + // check whether we should apply full cbqi + if (logic.isPure(THEORY_BV)) + { + if (!options::cbqiFullEffort.wasSetByUser()) + { + options::cbqiFullEffort.set(true); + } + } + } + if (options::cbqi()) + { + // must rewrite divk + if (!options::rewriteDivk.wasSetByUser()) + { + options::rewriteDivk.set(true); + } + if (options::incrementalSolving()) + { + // cannot do nested quantifier elimination in incremental mode + options::cbqiNestedQE.set(false); + } + if (logic.isPure(THEORY_ARITH) || logic.isPure(THEORY_BV)) + { + if (!options::quantConflictFind.wasSetByUser()) + { + options::quantConflictFind.set(false); + } + if (!options::instNoEntail.wasSetByUser()) + { + options::instNoEntail.set(false); + } + if (!options::instWhenMode.wasSetByUser() && options::cbqiModel()) + { + // only instantiation should happen at last call when model is avaiable + options::instWhenMode.set(options::InstWhenMode::LAST_CALL); + } + } + else + { + // only supported in pure arithmetic or pure BV + options::cbqiNestedQE.set(false); + } + // prenexing + if (options::cbqiNestedQE()) + { + // only complete with prenex = disj_normal or normal + if (options::prenexQuant() <= options::PrenexQuantMode::DISJ_NORMAL) + { + options::prenexQuant.set(options::PrenexQuantMode::DISJ_NORMAL); + } + } + else if (options::globalNegate()) + { + if (!options::prenexQuant.wasSetByUser()) + { + options::prenexQuant.set(options::PrenexQuantMode::NONE); + } + } + } + // implied options... + if (options::strictTriggers()) + { + if (!options::userPatternsQuant.wasSetByUser()) + { + options::userPatternsQuant.set(options::UserPatMode::TRUST); + } + } + if (options::qcfMode.wasSetByUser() || options::qcfTConstraint()) + { + options::quantConflictFind.set(true); + } + if (options::cbqiNestedQE()) + { + options::prenexQuantUser.set(true); + if (!options::preSkolemQuant.wasSetByUser()) + { + options::preSkolemQuant.set(true); + } + } + // for induction techniques + if (options::quantInduction()) + { + if (!options::dtStcInduction.wasSetByUser()) + { + options::dtStcInduction.set(true); + } + if (!options::intWfInduction.wasSetByUser()) + { + options::intWfInduction.set(true); + } + } + if (options::dtStcInduction()) + { + // try to remove ITEs from quantified formulas + if (!options::iteDtTesterSplitQuant.wasSetByUser()) + { + options::iteDtTesterSplitQuant.set(true); + } + if (!options::iteLiftQuant.wasSetByUser()) + { + options::iteLiftQuant.set(options::IteLiftQuantMode::ALL); + } + } + if (options::intWfInduction()) + { + if (!options::purifyTriggers.wasSetByUser()) + { + options::purifyTriggers.set(true); + } + } + if (options::conjectureNoFilter()) + { + if (!options::conjectureFilterActiveTerms.wasSetByUser()) + { + options::conjectureFilterActiveTerms.set(false); + } + if (!options::conjectureFilterCanonical.wasSetByUser()) + { + options::conjectureFilterCanonical.set(false); + } + if (!options::conjectureFilterModel.wasSetByUser()) + { + options::conjectureFilterModel.set(false); + } + } + if (options::conjectureGenPerRound.wasSetByUser()) + { + if (options::conjectureGenPerRound() > 0) + { + options::conjectureGen.set(true); + } + else + { + options::conjectureGen.set(false); + } + } + // can't pre-skolemize nested quantifiers without UF theory + if (!logic.isTheoryEnabled(THEORY_UF) && options::preSkolemQuant()) + { + if (!options::preSkolemQuantNested.wasSetByUser()) + { + options::preSkolemQuantNested.set(false); + } + } + if (!logic.isTheoryEnabled(THEORY_DATATYPES)) + { + options::quantDynamicSplit.set(options::QuantDSplitMode::NONE); + } + + // until bugs 371,431 are fixed + if (!options::minisatUseElim.wasSetByUser()) + { + // cannot use minisat elimination for logics where a theory solver + // introduces new literals into the search. This includes quantifiers + // (quantifier instantiation), and the lemma schemas used in non-linear + // and sets. We also can't use it if models are enabled. + if (logic.isTheoryEnabled(THEORY_SETS) || logic.isQuantified() + || options::produceModels() || options::produceAssignments() + || options::checkModels() + || (logic.isTheoryEnabled(THEORY_ARITH) && !logic.isLinear())) + { + options::minisatUseElim.set(false); + } + } + + // For now, these array theory optimizations do not support model-building + if (options::produceModels() || options::produceAssignments() + || options::checkModels()) + { + options::arraysOptimizeLinear.set(false); + options::arraysLazyRIntro1.set(false); + } + + if (options::proof()) + { + if (options::incrementalSolving()) + { + if (options::incrementalSolving.wasSetByUser()) + { + throw OptionException("--incremental is not supported with proofs"); + } + Warning() + << "SmtEngine: turning off incremental solving mode (not yet " + "supported with --proof, try --tear-down-incremental instead)" + << std::endl; + smte.setOption("incremental", SExpr("false")); + } + if (logic > LogicInfo("QF_AUFBVLRA")) + { + throw OptionException( + "Proofs are only supported for sub-logics of QF_AUFBVLIA."); + } + if (options::bitvectorAlgebraicSolver()) + { + if (options::bitvectorAlgebraicSolver.wasSetByUser()) + { + throw OptionException( + "--bv-algebraic-solver is not supported with proofs"); + } + Notice() << "SmtEngine: turning off bv algebraic solver to support proofs" + << std::endl; + options::bitvectorAlgebraicSolver.set(false); + } + if (options::bitvectorEqualitySolver()) + { + if (options::bitvectorEqualitySolver.wasSetByUser()) + { + throw OptionException("--bv-eq-solver is not supported with proofs"); + } + Notice() << "SmtEngine: turning off bv eq solver to support proofs" + << std::endl; + options::bitvectorEqualitySolver.set(false); + } + if (options::bitvectorInequalitySolver()) + { + if (options::bitvectorInequalitySolver.wasSetByUser()) + { + throw OptionException( + "--bv-inequality-solver is not supported with proofs"); + } + Notice() << "SmtEngine: turning off bv ineq solver to support proofs" + << std::endl; + options::bitvectorInequalitySolver.set(false); + } + } + + if (!options::bitvectorEqualitySolver()) + { + if (options::bvLazyRewriteExtf()) + { + if (options::bvLazyRewriteExtf.wasSetByUser()) + { + throw OptionException( + "--bv-lazy-rewrite-extf requires --bv-eq-solver to be set"); + } + } + Trace("smt") + << "disabling bvLazyRewriteExtf since equality solver is disabled" + << std::endl; + options::bvLazyRewriteExtf.set(false); + } + + if (!options::sygusExprMinerCheckUseExport()) + { + if (options::sygusExprMinerCheckTimeout.wasSetByUser()) + { + throw OptionException( + "--sygus-expr-miner-check-timeout=N requires " + "--sygus-expr-miner-check-use-export"); + } + if (options::sygusRewSynthInput() || options::produceAbducts()) + { + std::stringstream ss; + ss << (options::sygusRewSynthInput() ? "--sygus-rr-synth-input" + : "--produce-abducts"); + ss << "requires --sygus-expr-miner-check-use-export"; + throw OptionException(ss.str()); + } + } + + if (options::stringFMF() && !options::stringProcessLoopMode.wasSetByUser()) + { + Trace("smt") << "settting stringProcessLoopMode to 'simple' since " + "--strings-fmf enabled" + << std::endl; + options::stringProcessLoopMode.set(options::ProcessLoopMode::SIMPLE); + } + + // !!! All options that require disabling models go here + bool disableModels = false; + std::string sOptNoModel; + if (options::unconstrainedSimp.wasSetByUser() && options::unconstrainedSimp()) + { + disableModels = true; + sOptNoModel = "unconstrained-simp"; + } + else if (options::sortInference()) + { + disableModels = true; + sOptNoModel = "sort-inference"; + } + else if (options::minisatUseElim()) + { + disableModels = true; + sOptNoModel = "minisat-elimination"; + } + else if (logic.isTheoryEnabled(THEORY_ARITH) && !logic.isLinear() + && !options::nlExt()) + { + disableModels = true; + sOptNoModel = "nonlinear arithmetic without nl-ext"; + } + else if (options::globalNegate()) + { + disableModels = true; + sOptNoModel = "global-negate"; + } + if (disableModels) + { + if (options::produceModels()) + { + if (options::produceModels.wasSetByUser()) + { + std::stringstream ss; + ss << "Cannot use " << sOptNoModel << " with model generation."; + throw OptionException(ss.str()); + } + Notice() << "SmtEngine: turning off produce-models to support " + << sOptNoModel << std::endl; + smte.setOption("produce-models", SExpr("false")); + } + if (options::produceAssignments()) + { + if (options::produceAssignments.wasSetByUser()) + { + std::stringstream ss; + ss << "Cannot use " << sOptNoModel + << " with model generation (produce-assignments)."; + throw OptionException(ss.str()); + } + Notice() << "SmtEngine: turning off produce-assignments to support " + << sOptNoModel << std::endl; + smte.setOption("produce-assignments", SExpr("false")); + } + if (options::checkModels()) + { + if (options::checkModels.wasSetByUser()) + { + std::stringstream ss; + ss << "Cannot use " << sOptNoModel + << " with model generation (check-models)."; + throw OptionException(ss.str()); + } + Notice() << "SmtEngine: turning off check-models to support " + << sOptNoModel << std::endl; + smte.setOption("check-models", SExpr("false")); + } + } +} + +} // namespace smt +} // namespace CVC4 diff --git a/src/smt/set_defaults.h b/src/smt/set_defaults.h new file mode 100644 index 000000000..8871b0b38 --- /dev/null +++ b/src/smt/set_defaults.h @@ -0,0 +1,42 @@ +/********************* */ +/*! \file set_defaults.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Method for setting the default options of an SMT engine. + **/ + +#ifndef CVC4__SMT__SET_DEFAULTS_H +#define CVC4__SMT__SET_DEFAULTS_H + +#include "smt/smt_engine.h" +#include "theory/logic_info.h" + +namespace CVC4 { +namespace smt { + +/** + * The purpose of this method is to set the default options and update the logic + * info for SMT engine smte. + * + * The argument logic is a reference to the logic of SmtEngine, which can be + * updated by this method based on the current options and the logic itself. + * + * Note that currently, options are associated with the ExprManager. Thus, this + * call updates the options associated with the current ExprManager. + * If this designed is updated in the future so that SmtEngine has its own + * copy of options, this method should be updated accordingly so that it + * is responsible for updating this copy. + */ +void setDefaults(SmtEngine& smte, LogicInfo& logic); + +} // namespace smt +} // namespace CVC4 + +#endif /* CVC4__SMT__SET_DEFAULTS_H */ diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 7d78e93f9..2e1716543 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -86,6 +86,7 @@ #include "smt/managed_ostreams.h" #include "smt/model_blocker.h" #include "smt/model_core_builder.h" +#include "smt/set_defaults.h" #include "smt/smt_engine_scope.h" #include "smt/term_formula_removal.h" #include "smt/update_ostream.h" @@ -293,40 +294,6 @@ class BeforeSearchListener : public Listener { SmtEngine* d_smt; }; /* class BeforeSearchListener */ -class UseTheoryListListener : public Listener { - public: - UseTheoryListListener(TheoryEngine* theoryEngine) - : d_theoryEngine(theoryEngine) - {} - - void notify() override - { - std::stringstream commaList(options::useTheoryList()); - std::string token; - - Debug("UseTheoryListListener") << "UseTheoryListListener::notify() " - << options::useTheoryList() << std::endl; - - while(std::getline(commaList, token, ',')){ - if(token == "help") { - puts(theory::useTheoryHelp); - exit(1); - } - if(theory::useTheoryValidate(token)) { - d_theoryEngine->enableTheoryAlternative(token); - } else { - throw OptionException( - std::string("unknown option for --use-theory : `") + token + - "'. Try --use-theory=help."); - } - } - } - - private: - TheoryEngine* d_theoryEngine; -}; /* class UseTheoryListListener */ - - class SetDefaultExprDepthListener : public Listener { public: void notify() override @@ -432,15 +399,11 @@ class SmtEnginePrivate : public NodeManagerListener { /** Manager for the memory of --dump-to. */ ManagedDumpOStream d_managedDumpChannel; - /** Manager for --replay-log. */ - ManagedReplayLogOstream d_managedReplayLog; - /** * This list contains: * softResourceOut * hardResourceOut * beforeSearchListener - * UseTheoryListListener * * This needs to be deleted before both NodeManager's Options, * SmtEngine, d_resourceManager, and TheoryEngine. @@ -553,7 +516,6 @@ class SmtEnginePrivate : public NodeManagerListener { d_managedRegularChannel(), d_managedDiagnosticChannel(), d_managedDumpChannel(), - d_managedReplayLog(), d_listenerRegistrations(new ListenerRegistrationList()), d_propagator(true, true), d_assertions(), @@ -617,9 +579,6 @@ class SmtEnginePrivate : public NodeManagerListener { d_listenerRegistrations->add( nodeManagerOptions.registerDumpToFileNameListener( new SetToDefaultSourceListener(&d_managedDumpChannel), true)); - d_listenerRegistrations->add( - nodeManagerOptions.registerSetReplayLogFilename( - new SetToDefaultSourceListener(&d_managedReplayLog), true)); } catch (OptionException& e) { @@ -813,17 +772,6 @@ class SmtEnginePrivate : public NodeManagerListener { return retval; } - void addUseTheoryListListener(TheoryEngine* theoryEngine){ - Options& nodeManagerOptions = NodeManager::currentNM()->getOptions(); - d_listenerRegistrations->add( - nodeManagerOptions.registerUseTheoryListListener( - new UseTheoryListListener(theoryEngine), true)); - } - - std::ostream* getReplayLog() const { - return d_managedReplayLog.getReplayLog(); - } - //------------------------------- expression names // implements setExpressionName, as described in smt_engine.h void setExpressionName(Expr e, std::string name) { @@ -869,12 +817,10 @@ SmtEngine::SmtEngine(ExprManager* em) d_fullyInited(false), d_queryMade(false), d_needPostsolve(false), - d_earlyTheoryPP(true), d_globalNegation(false), d_status(), d_expectedStatus(), d_smtMode(SMT_MODE_START), - d_replayStream(nullptr), d_private(nullptr), d_statisticsRegistry(nullptr), d_stats(nullptr) @@ -923,10 +869,11 @@ void SmtEngine::finishInit() #endif } - d_private->addUseTheoryListListener(getTheoryEngine()); + // set the random seed + Random::getRandom().setSeed(options::seed()); // ensure that our heuristics are properly set up - setDefaults(); + setDefaults(*this, d_logic); Trace("smt-debug") << "Making decision engine..." << std::endl; @@ -935,11 +882,8 @@ void SmtEngine::finishInit() * are unregistered by the obsolete PropEngine object before registered * again by the new PropEngine object */ d_propEngine.reset(nullptr); - d_propEngine.reset(new PropEngine(getTheoryEngine(), - getContext(), - getUserContext(), - d_private->getReplayLog(), - d_replayStream)); + d_propEngine.reset( + new PropEngine(getTheoryEngine(), getContext(), getUserContext())); Trace("smt-debug") << "Setting up theory engine..." << std::endl; d_theoryEngine->setPropEngine(getPropEngine()); @@ -1014,9 +958,6 @@ void SmtEngine::finalOptionsAreSet() { d_fullyInited = true; Assert(d_logic.isLocked()); - - d_propEngine->assertFormula(NodeManager::currentNM()->mkConst<bool>(true)); - d_propEngine->assertFormula(NodeManager::currentNM()->mkConst<bool>(false).notNode()); } void SmtEngine::shutdown() { @@ -1145,1216 +1086,6 @@ void SmtEngine::setLogicInternal() d_logic.lock(); } -void SmtEngine::setDefaults() { - Random::getRandom().setSeed(options::seed()); - // Language-based defaults - if (!options::bitvectorDivByZeroConst.wasSetByUser()) - { - // Bitvector-divide-by-zero changed semantics in SMT LIB 2.6, thus we - // set this option if the input format is SMT LIB 2.6. We also set this - // option if we are sygus, since we assume SMT LIB 2.6 semantics for sygus. - options::bitvectorDivByZeroConst.set( - language::isInputLang_smt2_6(options::inputLanguage()) - || language::isInputLangSygus(options::inputLanguage())); - } - bool is_sygus = language::isInputLangSygus(options::inputLanguage()); - - if (options::bitblastMode() == options::BitblastMode::EAGER) - { - if (options::produceModels() - && (d_logic.isTheoryEnabled(THEORY_ARRAYS) - || d_logic.isTheoryEnabled(THEORY_UF))) - { - if (options::bitblastMode.wasSetByUser() - || options::produceModels.wasSetByUser()) - { - throw OptionException(std::string( - "Eager bit-blasting currently does not support model generation " - "for the combination of bit-vectors with arrays or uinterpreted " - "functions. Try --bitblast=lazy")); - } - Notice() << "SmtEngine: setting bit-blast mode to lazy to support model" - << "generation" << endl; - setOption("bitblastMode", SExpr("lazy")); - } - else if (!options::incrementalSolving()) - { - options::ackermann.set(true); - } - - if (options::incrementalSolving() && !d_logic.isPure(THEORY_BV)) - { - throw OptionException( - "Incremental eager bit-blasting is currently " - "only supported for QF_BV. Try --bitblast=lazy."); - } - } - - if (options::solveIntAsBV() > 0) - { - d_logic = d_logic.getUnlockedCopy(); - d_logic.enableTheory(THEORY_BV); - d_logic.lock(); - } - - if (options::solveBVAsInt() > 0) - { - if (d_logic.isTheoryEnabled(THEORY_BV)) - { - d_logic = d_logic.getUnlockedCopy(); - d_logic.enableTheory(THEORY_ARITH); - d_logic.arithNonLinear(); - d_logic.lock(); - } - } - - // set options about ackermannization - if (options::ackermann() && options::produceModels() - && (d_logic.isTheoryEnabled(THEORY_ARRAYS) - || d_logic.isTheoryEnabled(THEORY_UF))) - { - if (options::produceModels.wasSetByUser()) - { - throw OptionException(std::string( - "Ackermannization currently does not support model generation.")); - } - Notice() << "SmtEngine: turn off ackermannization to support model" - << "generation" << endl; - options::ackermann.set(false); - } - - if (options::ackermann()) - { - if (options::incrementalSolving()) - { - throw OptionException( - "Incremental Ackermannization is currently not supported."); - } - - if (d_logic.isQuantified()) - { - throw LogicException("Cannot use Ackermannization on quantified formula"); - } - - if (d_logic.isTheoryEnabled(THEORY_UF)) - { - d_logic = d_logic.getUnlockedCopy(); - d_logic.disableTheory(THEORY_UF); - d_logic.lock(); - } - if (d_logic.isTheoryEnabled(THEORY_ARRAYS)) - { - d_logic = d_logic.getUnlockedCopy(); - d_logic.disableTheory(THEORY_ARRAYS); - d_logic.lock(); - } - } - - // Set default options associated with strings-exp. We also set these options - // if we are using eager string preprocessing, which may introduce quantified - // formulas at preprocess time. - if (options::stringExp() || !options::stringLazyPreproc()) - { - // We require quantifiers since extended functions reduce using them. - if (!d_logic.isQuantified()) - { - d_logic = d_logic.getUnlockedCopy(); - d_logic.enableQuantifiers(); - d_logic.lock(); - Trace("smt") << "turning on quantifier logic, for strings-exp" - << std::endl; - } - // We require bounded quantifier handling. - if (!options::fmfBound.wasSetByUser()) - { - options::fmfBound.set( true ); - Trace("smt") << "turning on fmf-bound-int, for strings-exp" << std::endl; - } - // Turn off E-matching, since some bounded quantifiers introduced by strings - // (e.g. for replaceall) admit matching loops. - if (!options::eMatching.wasSetByUser()) - { - options::eMatching.set(false); - Trace("smt") << "turning off E-matching, for strings-exp" << std::endl; - } - // Do not eliminate extended arithmetic symbols from quantified formulas, - // since some strategies, e.g. --re-elim-agg, introduce them. - if (!options::elimExtArithQuant.wasSetByUser()) - { - options::elimExtArithQuant.set(false); - Trace("smt") << "turning off elim-ext-arith-quant, for strings-exp" - << std::endl; - } - } - - // sygus inference may require datatypes - if (!d_isInternalSubsolver) - { - if (options::produceAbducts() || options::sygusInference() - || options::sygusRewSynthInput()) - { - // since we are trying to recast as sygus, we assume the input is sygus - is_sygus = true; - } - } - - // We now know whether the input is sygus. Update the logic to incorporate - // the theories we need internally for handling sygus problems. - if (is_sygus) - { - d_logic = d_logic.getUnlockedCopy(); - d_logic.enableSygus(); - d_logic.lock(); - } - - // sygus core connective requires unsat cores - if (options::sygusCoreConnective()) - { - options::unsatCores.set(true); - } - - if ((options::checkModels() || options::checkSynthSol() - || options::produceAbducts() - || options::modelCoresMode() != options::ModelCoresMode::NONE - || options::blockModelsMode() != options::BlockModelsMode::NONE) - && !options::produceAssertions()) - { - Notice() << "SmtEngine: turning on produce-assertions to support " - << "option requiring assertions." << endl; - setOption("produce-assertions", SExpr("true")); - } - - // Disable options incompatible with incremental solving, unsat cores, and - // proofs or output an error if enabled explicitly - if (options::incrementalSolving() || options::unsatCores() - || options::proof()) - { - if (options::unconstrainedSimp()) - { - if (options::unconstrainedSimp.wasSetByUser()) - { - throw OptionException( - "unconstrained simplification not supported with unsat " - "cores/proofs/incremental solving"); - } - Notice() << "SmtEngine: turning off unconstrained simplification to " - "support unsat cores/proofs/incremental solving" - << endl; - options::unconstrainedSimp.set(false); - } - } - else - { - // Turn on unconstrained simplification for QF_AUFBV - if (!options::unconstrainedSimp.wasSetByUser()) - { - // bool qf_sat = d_logic.isPure(THEORY_BOOL) && - // !d_logic.isQuantified(); bool uncSimp = false && !qf_sat && - // !options::incrementalSolving(); - bool uncSimp = !d_logic.isQuantified() && !options::produceModels() - && !options::produceAssignments() - && !options::checkModels() - && (d_logic.isTheoryEnabled(THEORY_ARRAYS) - && d_logic.isTheoryEnabled(THEORY_BV)); - Trace("smt") << "setting unconstrained simplification to " << uncSimp - << endl; - options::unconstrainedSimp.set(uncSimp); - } - } - - if (options::incrementalSolving() || options::proof()) - { - if (options::sygusInference()) - { - if (options::sygusInference.wasSetByUser()) - { - throw OptionException( - "sygus inference not supported with proofs/incremental solving"); - } - Notice() << "SmtEngine: turning off sygus inference to support " - "proofs/incremental solving" - << std::endl; - options::sygusInference.set(false); - } - } - - // Disable options incompatible with unsat cores and proofs or output an - // error if enabled explicitly - if (options::unsatCores() || options::proof()) - { - if (options::simplificationMode() != options::SimplificationMode::NONE) - { - if (options::simplificationMode.wasSetByUser()) - { - throw OptionException( - "simplification not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off simplification to support unsat " - "cores/proofs" - << endl; - options::simplificationMode.set(options::SimplificationMode::NONE); - } - - if (options::pbRewrites()) - { - if (options::pbRewrites.wasSetByUser()) - { - throw OptionException( - "pseudoboolean rewrites not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off pseudoboolean rewrites to support " - "unsat cores/proofs" - << endl; - setOption("pb-rewrites", false); - } - - if (options::sortInference()) - { - if (options::sortInference.wasSetByUser()) - { - throw OptionException( - "sort inference not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off sort inference to support unsat " - "cores/proofs" - << endl; - options::sortInference.set(false); - } - - if (options::preSkolemQuant()) - { - if (options::preSkolemQuant.wasSetByUser()) - { - throw OptionException( - "pre-skolemization not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off pre-skolemization to support unsat " - "cores/proofs" - << endl; - options::preSkolemQuant.set(false); - } - - if (options::solveBVAsInt() > 0) - { - /** - * Operations on 1 bits are better handled as Boolean operations - * than as integer operations. - * Therefore, we enable bv-to-bool, which runs before - * the translation to integers. - */ - options::bitvectorToBool.set(true); - } - - if (options::bitvectorToBool()) - { - if (options::bitvectorToBool.wasSetByUser()) - { - throw OptionException( - "bv-to-bool not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off bitvector-to-bool to support unsat " - "cores/proofs" - << endl; - options::bitvectorToBool.set(false); - } - - if (options::boolToBitvector() != options::BoolToBVMode::OFF) - { - if (options::boolToBitvector.wasSetByUser()) - { - throw OptionException( - "bool-to-bv != off not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off bool-to-bv to support unsat " - "cores/proofs" - << endl; - setOption("boolToBitvector", SExpr("off")); - } - - if (options::bvIntroducePow2()) - { - if (options::bvIntroducePow2.wasSetByUser()) - { - throw OptionException( - "bv-intro-pow2 not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off bv-intro-pow2 to support " - "unsat-cores/proofs" - << endl; - setOption("bv-intro-pow2", false); - } - - if (options::repeatSimp()) - { - if (options::repeatSimp.wasSetByUser()) - { - throw OptionException( - "repeat-simp not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off repeat-simp to support unsat " - "cores/proofs" - << endl; - setOption("repeat-simp", false); - } - - if (options::globalNegate()) - { - if (options::globalNegate.wasSetByUser()) - { - throw OptionException( - "global-negate not supported with unsat cores/proofs"); - } - Notice() << "SmtEngine: turning off global-negate to support unsat " - "cores/proofs" - << endl; - setOption("global-negate", false); - } - - if (options::bitvectorAig()) - { - throw OptionException( - "bitblast-aig not supported with unsat cores/proofs"); - } - } - else - { - // by default, nonclausal simplification is off for QF_SAT - if (!options::simplificationMode.wasSetByUser()) - { - bool qf_sat = d_logic.isPure(THEORY_BOOL) && !d_logic.isQuantified(); - Trace("smt") << "setting simplification mode to <" - << d_logic.getLogicString() << "> " << (!qf_sat) << endl; - // simplification=none works better for SMT LIB benchmarks with - // quantifiers, not others options::simplificationMode.set(qf_sat || - // quantifiers ? options::SimplificationMode::NONE : - // options::SimplificationMode::BATCH); - options::simplificationMode.set(qf_sat - ? options::SimplificationMode::NONE - : options::SimplificationMode::BATCH); - } - } - - if (options::cbqiBv() && d_logic.isQuantified()) - { - if (options::boolToBitvector() != options::BoolToBVMode::OFF) - { - if (options::boolToBitvector.wasSetByUser()) - { - throw OptionException( - "bool-to-bv != off not supported with CBQI BV for quantified " - "logics"); - } - Notice() << "SmtEngine: turning off bool-to-bitvector to support CBQI BV" - << endl; - setOption("boolToBitvector", SExpr("off")); - } - } - - // cases where we need produce models - if (!options::produceModels() - && (options::produceAssignments() || options::sygusRewSynthCheck() - || is_sygus)) - { - Notice() << "SmtEngine: turning on produce-models" << endl; - setOption("produce-models", SExpr("true")); - } - - // Set the options for the theoryOf - if(!options::theoryOfMode.wasSetByUser()) { - if(d_logic.isSharingEnabled() && - !d_logic.isTheoryEnabled(THEORY_BV) && - !d_logic.isTheoryEnabled(THEORY_STRINGS) && - !d_logic.isTheoryEnabled(THEORY_SETS) ) { - Trace("smt") << "setting theoryof-mode to term-based" << endl; - options::theoryOfMode.set(options::TheoryOfMode::THEORY_OF_TERM_BASED); - } - } - - // strings require LIA, UF; widen the logic - if(d_logic.isTheoryEnabled(THEORY_STRINGS)) { - LogicInfo log(d_logic.getUnlockedCopy()); - // Strings requires arith for length constraints, and also UF - if(!d_logic.isTheoryEnabled(THEORY_UF)) { - Trace("smt") << "because strings are enabled, also enabling UF" << endl; - log.enableTheory(THEORY_UF); - } - if(!d_logic.isTheoryEnabled(THEORY_ARITH) || d_logic.isDifferenceLogic() || !d_logic.areIntegersUsed()) { - Trace("smt") << "because strings are enabled, also enabling linear integer arithmetic" << endl; - log.enableTheory(THEORY_ARITH); - log.enableIntegers(); - log.arithOnlyLinear(); - } - d_logic = log; - d_logic.lock(); - } - if(d_logic.isTheoryEnabled(THEORY_ARRAYS) || d_logic.isTheoryEnabled(THEORY_DATATYPES) || d_logic.isTheoryEnabled(THEORY_SETS)) { - if(!d_logic.isTheoryEnabled(THEORY_UF)) { - LogicInfo log(d_logic.getUnlockedCopy()); - Trace("smt") << "because a theory that permits Boolean terms is enabled, also enabling UF" << endl; - log.enableTheory(THEORY_UF); - d_logic = log; - d_logic.lock(); - } - } - - // by default, symmetry breaker is on only for non-incremental QF_UF - if(! options::ufSymmetryBreaker.wasSetByUser()) { - bool qf_uf_noinc = d_logic.isPure(THEORY_UF) && !d_logic.isQuantified() - && !options::incrementalSolving() && !options::proof() - && !options::unsatCores(); - Trace("smt") << "setting uf symmetry breaker to " << qf_uf_noinc << endl; - options::ufSymmetryBreaker.set(qf_uf_noinc); - } - - // If in arrays, set the UF handler to arrays - if(d_logic.isTheoryEnabled(THEORY_ARRAYS) && ( !d_logic.isQuantified() || - (d_logic.isQuantified() && !d_logic.isTheoryEnabled(THEORY_UF)))) { - Theory::setUninterpretedSortOwner(THEORY_ARRAYS); - } else { - Theory::setUninterpretedSortOwner(THEORY_UF); - } - - if(! options::simplifyWithCareEnabled.wasSetByUser() ){ - bool qf_aufbv = !d_logic.isQuantified() && - d_logic.isTheoryEnabled(THEORY_ARRAYS) && - d_logic.isTheoryEnabled(THEORY_UF) && - d_logic.isTheoryEnabled(THEORY_BV); - - bool withCare = qf_aufbv; - Trace("smt") << "setting ite simplify with care to " << withCare << endl; - options::simplifyWithCareEnabled.set(withCare); - } - // Turn off array eager index splitting for QF_AUFLIA - if(! options::arraysEagerIndexSplitting.wasSetByUser()) { - if (not d_logic.isQuantified() && - d_logic.isTheoryEnabled(THEORY_ARRAYS) && - d_logic.isTheoryEnabled(THEORY_UF) && - d_logic.isTheoryEnabled(THEORY_ARITH)) { - Trace("smt") << "setting array eager index splitting to false" << endl; - options::arraysEagerIndexSplitting.set(false); - } - } - // Turn on model-based arrays for QF_AX (unless models are enabled) - // if(! options::arraysModelBased.wasSetByUser()) { - // if (not d_logic.isQuantified() && - // d_logic.isTheoryEnabled(THEORY_ARRAYS) && - // d_logic.isPure(THEORY_ARRAYS) && - // !options::produceModels() && - // !options::checkModels()) { - // Trace("smt") << "turning on model-based array solver" << endl; - // options::arraysModelBased.set(true); - // } - // } - // Turn on multiple-pass non-clausal simplification for QF_AUFBV - if(! options::repeatSimp.wasSetByUser()) { - bool repeatSimp = !d_logic.isQuantified() && - (d_logic.isTheoryEnabled(THEORY_ARRAYS) && - d_logic.isTheoryEnabled(THEORY_UF) && - d_logic.isTheoryEnabled(THEORY_BV)) && - !options::unsatCores(); - Trace("smt") << "setting repeat simplification to " << repeatSimp << endl; - options::repeatSimp.set(repeatSimp); - } - - if (options::boolToBitvector() == options::BoolToBVMode::ALL - && !d_logic.isTheoryEnabled(THEORY_BV)) - { - if (options::boolToBitvector.wasSetByUser()) - { - throw OptionException( - "bool-to-bv=all not supported for non-bitvector logics."); - } - Notice() << "SmtEngine: turning off bool-to-bv for non-bv logic: " - << d_logic.getLogicString() << std::endl; - setOption("boolToBitvector", SExpr("off")); - } - - if (! options::bvEagerExplanations.wasSetByUser() && - d_logic.isTheoryEnabled(THEORY_ARRAYS) && - d_logic.isTheoryEnabled(THEORY_BV)) { - Trace("smt") << "enabling eager bit-vector explanations " << endl; - options::bvEagerExplanations.set(true); - } - - // Turn on arith rewrite equalities only for pure arithmetic - if(! options::arithRewriteEq.wasSetByUser()) { - bool arithRewriteEq = d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isQuantified(); - Trace("smt") << "setting arith rewrite equalities " << arithRewriteEq << endl; - options::arithRewriteEq.set(arithRewriteEq); - } - if(! options::arithHeuristicPivots.wasSetByUser()) { - int16_t heuristicPivots = 5; - if(d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified()) { - if(d_logic.isDifferenceLogic()) { - heuristicPivots = -1; - } else if(!d_logic.areIntegersUsed()) { - heuristicPivots = 0; - } - } - Trace("smt") << "setting arithHeuristicPivots " << heuristicPivots << endl; - options::arithHeuristicPivots.set(heuristicPivots); - } - if(! options::arithPivotThreshold.wasSetByUser()){ - uint16_t pivotThreshold = 2; - if(d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified()){ - if(d_logic.isDifferenceLogic()){ - pivotThreshold = 16; - } - } - Trace("smt") << "setting arith arithPivotThreshold " << pivotThreshold << endl; - options::arithPivotThreshold.set(pivotThreshold); - } - if(! options::arithStandardCheckVarOrderPivots.wasSetByUser()){ - int16_t varOrderPivots = -1; - if(d_logic.isPure(THEORY_ARITH) && !d_logic.isQuantified()){ - varOrderPivots = 200; - } - Trace("smt") << "setting arithStandardCheckVarOrderPivots " << varOrderPivots << endl; - options::arithStandardCheckVarOrderPivots.set(varOrderPivots); - } - // Turn off early theory preprocessing if arithRewriteEq is on - if (options::arithRewriteEq()) { - d_earlyTheoryPP = false; - } - if (d_logic.isPure(THEORY_ARITH) && !d_logic.areRealsUsed()) - { - if (!options::nlExtTangentPlanesInterleave.wasSetByUser()) - { - Trace("smt") << "setting nlExtTangentPlanesInterleave to true" << endl; - options::nlExtTangentPlanesInterleave.set(true); - } - } - - // Set decision mode based on logic (if not set by user) - if(!options::decisionMode.wasSetByUser()) { - options::DecisionMode decMode = - // sygus uses internal - is_sygus ? options::DecisionMode::INTERNAL : - // ALL - d_logic.hasEverything() - ? options::DecisionMode::JUSTIFICATION - : ( // QF_BV - (not d_logic.isQuantified() && d_logic.isPure(THEORY_BV)) || - // QF_AUFBV or QF_ABV or QF_UFBV - (not d_logic.isQuantified() - && (d_logic.isTheoryEnabled(THEORY_ARRAYS) - || d_logic.isTheoryEnabled(THEORY_UF)) - && d_logic.isTheoryEnabled(THEORY_BV)) - || - // QF_AUFLIA (and may be ends up enabling - // QF_AUFLRA?) - (not d_logic.isQuantified() - && d_logic.isTheoryEnabled(THEORY_ARRAYS) - && d_logic.isTheoryEnabled(THEORY_UF) - && d_logic.isTheoryEnabled(THEORY_ARITH)) - || - // QF_LRA - (not d_logic.isQuantified() - && d_logic.isPure(THEORY_ARITH) - && d_logic.isLinear() - && !d_logic.isDifferenceLogic() - && !d_logic.areIntegersUsed()) - || - // Quantifiers - d_logic.isQuantified() || - // Strings - d_logic.isTheoryEnabled(THEORY_STRINGS) - ? options::DecisionMode::JUSTIFICATION - : options::DecisionMode::INTERNAL); - - bool stoponly = - // ALL - d_logic.hasEverything() || d_logic.isTheoryEnabled(THEORY_STRINGS) ? false : - ( // QF_AUFLIA - (not d_logic.isQuantified() && - d_logic.isTheoryEnabled(THEORY_ARRAYS) && - d_logic.isTheoryEnabled(THEORY_UF) && - d_logic.isTheoryEnabled(THEORY_ARITH) - ) || - // QF_LRA - (not d_logic.isQuantified() && - d_logic.isPure(THEORY_ARITH) && d_logic.isLinear() && !d_logic.isDifferenceLogic() && !d_logic.areIntegersUsed() - ) - ? true : false - ); - - Trace("smt") << "setting decision mode to " << decMode << endl; - options::decisionMode.set(decMode); - options::decisionStopOnly.set(stoponly); - } - if( options::incrementalSolving() ){ - //disable modes not supported by incremental - options::sortInference.set( false ); - options::ufssFairnessMonotone.set( false ); - options::quantEpr.set( false ); - options::globalNegate.set(false); - } - if( d_logic.hasCardinalityConstraints() ){ - //must have finite model finding on - options::finiteModelFind.set( true ); - } - - //if it contains a theory with non-termination, do not strictly enforce that quantifiers and theory combination must be interleaved - if( d_logic.isTheoryEnabled(THEORY_STRINGS) || (d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear()) ){ - if( !options::instWhenStrictInterleave.wasSetByUser() ){ - options::instWhenStrictInterleave.set( false ); - } - } - - if( options::instMaxLevel()!=-1 ){ - Notice() << "SmtEngine: turning off cbqi to support instMaxLevel" << endl; - options::cbqi.set(false); - } - // Do we need to track instantiations? - // Needed for sygus due to single invocation techniques. - if (options::cbqiNestedQE() - || (options::proof() && !options::trackInstLemmas.wasSetByUser()) - || is_sygus) - { - options::trackInstLemmas.set( true ); - } - - if( ( options::fmfBoundLazy.wasSetByUser() && options::fmfBoundLazy() ) || - ( options::fmfBoundInt.wasSetByUser() && options::fmfBoundInt() ) ) { - options::fmfBound.set( true ); - } - //now have determined whether fmfBoundInt is on/off - //apply fmfBoundInt options - if( options::fmfBound() ){ - if (!options::mbqiMode.wasSetByUser() - || (options::mbqiMode() != options::MbqiMode::NONE - && options::mbqiMode() != options::MbqiMode::FMC)) - { - //if bounded integers are set, use no MBQI by default - options::mbqiMode.set(options::MbqiMode::NONE); - } - if( ! options::prenexQuant.wasSetByUser() ){ - options::prenexQuant.set(options::PrenexQuantMode::NONE); - } - } - if( options::ufHo() ){ - //if higher-order, then current variants of model-based instantiation cannot be used - if (options::mbqiMode() != options::MbqiMode::NONE) - { - options::mbqiMode.set(options::MbqiMode::NONE); - } - if (!options::hoElimStoreAx.wasSetByUser()) - { - // by default, use store axioms only if --ho-elim is set - options::hoElimStoreAx.set(options::hoElim()); - } - } - if( options::fmfFunWellDefinedRelevant() ){ - if( !options::fmfFunWellDefined.wasSetByUser() ){ - options::fmfFunWellDefined.set( true ); - } - } - if( options::fmfFunWellDefined() ){ - if( !options::finiteModelFind.wasSetByUser() ){ - options::finiteModelFind.set( true ); - } - } - //EPR - if( options::quantEpr() ){ - if( !options::preSkolemQuant.wasSetByUser() ){ - options::preSkolemQuant.set( true ); - } - } - - //now, have determined whether finite model find is on/off - //apply finite model finding options - if( options::finiteModelFind() ){ - //apply conservative quantifiers splitting - if( !options::quantDynamicSplit.wasSetByUser() ){ - options::quantDynamicSplit.set(options::QuantDSplitMode::DEFAULT); - } - //do not eliminate extended arithmetic symbols from quantified formulas - if( !options::elimExtArithQuant.wasSetByUser() ){ - options::elimExtArithQuant.set( false ); - } - if( !options::eMatching.wasSetByUser() ){ - options::eMatching.set( options::fmfInstEngine() ); - } - if( !options::instWhenMode.wasSetByUser() ){ - //instantiate only on last call - if( options::eMatching() ){ - options::instWhenMode.set(options::InstWhenMode::LAST_CALL); - } - } - } - - // apply sygus options - // if we are attempting to rewrite everything to SyGuS, use sygus() - if (is_sygus) - { - if (!options::sygus()) - { - Trace("smt") << "turning on sygus" << std::endl; - } - options::sygus.set(true); - // must use Ferrante/Rackoff for real arithmetic - if (!options::cbqiMidpoint.wasSetByUser()) - { - options::cbqiMidpoint.set(true); - } - if (options::sygusRepairConst()) - { - if (!options::cbqi.wasSetByUser()) - { - options::cbqi.set(true); - } - } - if (options::sygusInference()) - { - // optimization: apply preskolemization, makes it succeed more often - if (!options::preSkolemQuant.wasSetByUser()) - { - options::preSkolemQuant.set(true); - } - if (!options::preSkolemQuantNested.wasSetByUser()) - { - options::preSkolemQuantNested.set(true); - } - } - //counterexample-guided instantiation for sygus - if( !options::cegqiSingleInvMode.wasSetByUser() ){ - options::cegqiSingleInvMode.set(options::CegqiSingleInvMode::USE); - } - if( !options::quantConflictFind.wasSetByUser() ){ - options::quantConflictFind.set( false ); - } - if( !options::instNoEntail.wasSetByUser() ){ - options::instNoEntail.set( false ); - } - if (!options::cbqiFullEffort.wasSetByUser()) - { - // should use full effort cbqi for single invocation and repair const - options::cbqiFullEffort.set(true); - } - if (options::sygusRew()) - { - options::sygusRewSynth.set(true); - options::sygusRewVerify.set(true); - } - if (options::sygusRewSynthInput()) - { - // If we are using synthesis rewrite rules from input, we use - // sygusRewSynth after preprocessing. See passes/synth_rew_rules.h for - // details on this technique. - options::sygusRewSynth.set(true); - // we should not use the extended rewriter, since we are interested - // in rewrites that are not in the main rewriter - if (!options::sygusExtRew.wasSetByUser()) - { - options::sygusExtRew.set(false); - } - } - // Whether we must use "basic" sygus algorithms. A non-basic sygus algorithm - // is one that is specialized for returning a single solution. Non-basic - // sygus algorithms currently include the PBE solver, UNIF+PI, static - // template inference for invariant synthesis, and single invocation - // techniques. - bool reqBasicSygus = false; - if (options::produceAbducts()) - { - // if doing abduction, we should filter strong solutions - if (!options::sygusFilterSolMode.wasSetByUser()) - { - options::sygusFilterSolMode.set(options::SygusFilterSolMode::STRONG); - } - // we must use basic sygus algorithms, since e.g. we require checking - // a sygus side condition for consistency with axioms. - reqBasicSygus = true; - } - if (options::sygusRewSynth() || options::sygusRewVerify() - || options::sygusQueryGen()) - { - // rewrite rule synthesis implies that sygus stream must be true - options::sygusStream.set(true); - } - if (options::sygusStream() || options::incrementalSolving()) - { - // Streaming and incremental mode are incompatible with techniques that - // focus the search towards finding a single solution. - reqBasicSygus = true; - } - // Now, disable options for non-basic sygus algorithms, if necessary. - if (reqBasicSygus) - { - if (!options::sygusUnifPbe.wasSetByUser()) - { - options::sygusUnifPbe.set(false); - } - if (options::sygusUnifPi.wasSetByUser()) - { - options::sygusUnifPi.set(options::SygusUnifPiMode::NONE); - } - if (!options::sygusInvTemplMode.wasSetByUser()) - { - options::sygusInvTemplMode.set(options::SygusInvTemplMode::NONE); - } - if (!options::cegqiSingleInvMode.wasSetByUser()) - { - options::cegqiSingleInvMode.set(options::CegqiSingleInvMode::NONE); - } - } - //do not allow partial functions - if (!options::bitvectorDivByZeroConst()) - { - if (options::bitvectorDivByZeroConst.wasSetByUser()) - { - throw OptionException( - "--no-bv-div-zero-const is not supported with SyGuS"); - } - Notice() - << "SmtEngine: setting bv-div-zero-const to true to support SyGuS" - << std::endl; - options::bitvectorDivByZeroConst.set( true ); - } - if( !options::dtRewriteErrorSel.wasSetByUser() ){ - options::dtRewriteErrorSel.set( true ); - } - //do not miniscope - if( !options::miniscopeQuant.wasSetByUser() ){ - options::miniscopeQuant.set( false ); - } - if( !options::miniscopeQuantFreeVar.wasSetByUser() ){ - options::miniscopeQuantFreeVar.set( false ); - } - if (!options::quantSplit.wasSetByUser()) - { - options::quantSplit.set(false); - } - //rewrite divk - if( !options::rewriteDivk.wasSetByUser()) { - options::rewriteDivk.set( true ); - } - //do not do macros - if( !options::macrosQuant.wasSetByUser()) { - options::macrosQuant.set( false ); - } - if( !options::cbqiPreRegInst.wasSetByUser()) { - options::cbqiPreRegInst.set( true ); - } - } - //counterexample-guided instantiation for non-sygus - // enable if any possible quantifiers with arithmetic, datatypes or bitvectors - if ((d_logic.isQuantified() - && (d_logic.isTheoryEnabled(THEORY_ARITH) - || d_logic.isTheoryEnabled(THEORY_DATATYPES) - || d_logic.isTheoryEnabled(THEORY_BV) - || d_logic.isTheoryEnabled(THEORY_FP))) - || options::cbqiAll()) - { - if( !options::cbqi.wasSetByUser() ){ - options::cbqi.set( true ); - } - // check whether we should apply full cbqi - if (d_logic.isPure(THEORY_BV)) - { - if (!options::cbqiFullEffort.wasSetByUser()) - { - options::cbqiFullEffort.set(true); - } - } - } - if( options::cbqi() ){ - //must rewrite divk - if( !options::rewriteDivk.wasSetByUser()) { - options::rewriteDivk.set( true ); - } - if (options::incrementalSolving()) - { - // cannot do nested quantifier elimination in incremental mode - options::cbqiNestedQE.set(false); - } - if (d_logic.isPure(THEORY_ARITH) || d_logic.isPure(THEORY_BV)) - { - if( !options::quantConflictFind.wasSetByUser() ){ - options::quantConflictFind.set( false ); - } - if( !options::instNoEntail.wasSetByUser() ){ - options::instNoEntail.set( false ); - } - if( !options::instWhenMode.wasSetByUser() && options::cbqiModel() ){ - //only instantiation should happen at last call when model is avaiable - options::instWhenMode.set(options::InstWhenMode::LAST_CALL); - } - }else{ - // only supported in pure arithmetic or pure BV - options::cbqiNestedQE.set(false); - } - // prenexing - if (options::cbqiNestedQE()) - { - // only complete with prenex = disj_normal or normal - if (options::prenexQuant() <= options::PrenexQuantMode::DISJ_NORMAL) - { - options::prenexQuant.set(options::PrenexQuantMode::DISJ_NORMAL); - } - } - else if (options::globalNegate()) - { - if (!options::prenexQuant.wasSetByUser()) - { - options::prenexQuant.set(options::PrenexQuantMode::NONE); - } - } - } - //implied options... - if( options::strictTriggers() ){ - if( !options::userPatternsQuant.wasSetByUser() ){ - options::userPatternsQuant.set(options::UserPatMode::TRUST); - } - } - if( options::qcfMode.wasSetByUser() || options::qcfTConstraint() ){ - options::quantConflictFind.set( true ); - } - if( options::cbqiNestedQE() ){ - options::prenexQuantUser.set( true ); - if( !options::preSkolemQuant.wasSetByUser() ){ - options::preSkolemQuant.set( true ); - } - } - //for induction techniques - if( options::quantInduction() ){ - if( !options::dtStcInduction.wasSetByUser() ){ - options::dtStcInduction.set( true ); - } - if( !options::intWfInduction.wasSetByUser() ){ - options::intWfInduction.set( true ); - } - } - if( options::dtStcInduction() ){ - //try to remove ITEs from quantified formulas - if( !options::iteDtTesterSplitQuant.wasSetByUser() ){ - options::iteDtTesterSplitQuant.set( true ); - } - if( !options::iteLiftQuant.wasSetByUser() ){ - options::iteLiftQuant.set(options::IteLiftQuantMode::ALL); - } - } - if( options::intWfInduction() ){ - if( !options::purifyTriggers.wasSetByUser() ){ - options::purifyTriggers.set( true ); - } - } - if( options::conjectureNoFilter() ){ - if( !options::conjectureFilterActiveTerms.wasSetByUser() ){ - options::conjectureFilterActiveTerms.set( false ); - } - if( !options::conjectureFilterCanonical.wasSetByUser() ){ - options::conjectureFilterCanonical.set( false ); - } - if( !options::conjectureFilterModel.wasSetByUser() ){ - options::conjectureFilterModel.set( false ); - } - } - if( options::conjectureGenPerRound.wasSetByUser() ){ - if( options::conjectureGenPerRound()>0 ){ - options::conjectureGen.set( true ); - }else{ - options::conjectureGen.set( false ); - } - } - //can't pre-skolemize nested quantifiers without UF theory - if( !d_logic.isTheoryEnabled(THEORY_UF) && options::preSkolemQuant() ){ - if( !options::preSkolemQuantNested.wasSetByUser() ){ - options::preSkolemQuantNested.set( false ); - } - } - if( !d_logic.isTheoryEnabled(THEORY_DATATYPES) ){ - options::quantDynamicSplit.set(options::QuantDSplitMode::NONE); - } - - //until bugs 371,431 are fixed - if( ! options::minisatUseElim.wasSetByUser()){ - // cannot use minisat elimination for logics where a theory solver - // introduces new literals into the search. This includes quantifiers - // (quantifier instantiation), and the lemma schemas used in non-linear - // and sets. We also can't use it if models are enabled. - if (d_logic.isTheoryEnabled(THEORY_SETS) || d_logic.isQuantified() - || options::produceModels() || options::produceAssignments() - || options::checkModels() - || (d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear())) - { - options::minisatUseElim.set( false ); - } - } - - // For now, these array theory optimizations do not support model-building - if (options::produceModels() || options::produceAssignments() || options::checkModels()) { - options::arraysOptimizeLinear.set(false); - options::arraysLazyRIntro1.set(false); - } - - if (options::proof()) - { - if (options::incrementalSolving()) - { - if (options::incrementalSolving.wasSetByUser()) - { - throw OptionException("--incremental is not supported with proofs"); - } - Warning() - << "SmtEngine: turning off incremental solving mode (not yet " - "supported with --proof, try --tear-down-incremental instead)" - << endl; - setOption("incremental", SExpr("false")); - } - if (d_logic > LogicInfo("QF_AUFBVLRA")) - { - throw OptionException( - "Proofs are only supported for sub-logics of QF_AUFBVLIA."); - } - if (options::bitvectorAlgebraicSolver()) - { - if (options::bitvectorAlgebraicSolver.wasSetByUser()) - { - throw OptionException( - "--bv-algebraic-solver is not supported with proofs"); - } - Notice() << "SmtEngine: turning off bv algebraic solver to support proofs" - << std::endl; - options::bitvectorAlgebraicSolver.set(false); - } - if (options::bitvectorEqualitySolver()) - { - if (options::bitvectorEqualitySolver.wasSetByUser()) - { - throw OptionException("--bv-eq-solver is not supported with proofs"); - } - Notice() << "SmtEngine: turning off bv eq solver to support proofs" - << std::endl; - options::bitvectorEqualitySolver.set(false); - } - if (options::bitvectorInequalitySolver()) - { - if (options::bitvectorInequalitySolver.wasSetByUser()) - { - throw OptionException( - "--bv-inequality-solver is not supported with proofs"); - } - Notice() << "SmtEngine: turning off bv ineq solver to support proofs" - << std::endl; - options::bitvectorInequalitySolver.set(false); - } - } - - if (!options::bitvectorEqualitySolver()) - { - if (options::bvLazyRewriteExtf()) - { - if (options::bvLazyRewriteExtf.wasSetByUser()) - { - throw OptionException( - "--bv-lazy-rewrite-extf requires --bv-eq-solver to be set"); - } - } - Trace("smt") - << "disabling bvLazyRewriteExtf since equality solver is disabled" - << endl; - options::bvLazyRewriteExtf.set(false); - } - - if (!options::sygusExprMinerCheckUseExport()) - { - if (options::sygusExprMinerCheckTimeout.wasSetByUser()) - { - throw OptionException( - "--sygus-expr-miner-check-timeout=N requires " - "--sygus-expr-miner-check-use-export"); - } - if (options::sygusRewSynthInput() || options::produceAbducts()) - { - std::stringstream ss; - ss << (options::sygusRewSynthInput() ? "--sygus-rr-synth-input" - : "--produce-abducts"); - ss << "requires --sygus-expr-miner-check-use-export"; - throw OptionException(ss.str()); - } - } - - if (options::stringFMF() && !options::stringProcessLoopMode.wasSetByUser()) - { - Trace("smt") << "settting stringProcessLoopMode to 'simple' since " - "--strings-fmf enabled" - << endl; - options::stringProcessLoopMode.set(options::ProcessLoopMode::SIMPLE); - } - - // !!! All options that require disabling models go here - bool disableModels = false; - std::string sOptNoModel; - if (options::unconstrainedSimp.wasSetByUser() && options::unconstrainedSimp()) - { - disableModels = true; - sOptNoModel = "unconstrained-simp"; - } - else if (options::sortInference()) - { - disableModels = true; - sOptNoModel = "sort-inference"; - } - else if (options::minisatUseElim()) - { - disableModels = true; - sOptNoModel = "minisat-elimination"; - } - else if (d_logic.isTheoryEnabled(THEORY_ARITH) && !d_logic.isLinear() - && !options::nlExt()) - { - disableModels = true; - sOptNoModel = "nonlinear arithmetic without nl-ext"; - } - else if (options::globalNegate()) - { - disableModels = true; - sOptNoModel = "global-negate"; - } - if (disableModels) - { - if (options::produceModels()) - { - if (options::produceModels.wasSetByUser()) - { - std::stringstream ss; - ss << "Cannot use " << sOptNoModel << " with model generation."; - throw OptionException(ss.str()); - } - Notice() << "SmtEngine: turning off produce-models to support " - << sOptNoModel << endl; - setOption("produce-models", SExpr("false")); - } - if (options::produceAssignments()) - { - if (options::produceAssignments.wasSetByUser()) - { - std::stringstream ss; - ss << "Cannot use " << sOptNoModel - << " with model generation (produce-assignments)."; - throw OptionException(ss.str()); - } - Notice() << "SmtEngine: turning off produce-assignments to support " - << sOptNoModel << endl; - setOption("produce-assignments", SExpr("false")); - } - if (options::checkModels()) - { - if (options::checkModels.wasSetByUser()) - { - std::stringstream ss; - ss << "Cannot use " << sOptNoModel - << " with model generation (check-models)."; - throw OptionException(ss.str()); - } - Notice() << "SmtEngine: turning off check-models to support " - << sOptNoModel << endl; - setOption("check-models", SExpr("false")); - } - } -} - void SmtEngine::setProblemExtended() { d_smtMode = SMT_MODE_ASSERT; @@ -2966,7 +1697,8 @@ bool SmtEnginePrivate::simplifyAssertions() d_smt.d_theoryEngine->staticInitializeBVOptions(d_assertions.ref()); // Theory preprocessing - if (d_smt.d_earlyTheoryPP) + bool doEarlyTheoryPp = !options::arithRewriteEq(); + if (doEarlyTheoryPp) { d_passes["theory-preprocess"]->apply(&d_assertions); } @@ -3034,7 +1766,7 @@ Result SmtEngine::check() { resourceManager->out()) { Result::UnknownExplanation why = resourceManager->outOfResources() ? Result::RESOURCEOUT : Result::TIMEOUT; - return Result(Result::VALIDITY_UNKNOWN, why, d_filename); + return Result(Result::ENTAILMENT_UNKNOWN, why, d_filename); } // Make sure the prop layer has all of the assertions @@ -3059,7 +1791,8 @@ Result SmtEngine::check() { Result SmtEngine::quickCheck() { Assert(d_fullyInited); Trace("smt") << "SMT quickCheck()" << endl; - return Result(Result::VALIDITY_UNKNOWN, Result::REQUIRES_FULL_CHECK, d_filename); + return Result( + Result::ENTAILMENT_UNKNOWN, Result::REQUIRES_FULL_CHECK, d_filename); } theory::TheoryModel* SmtEngine::getAvailableModel(const char* c) const @@ -3075,7 +1808,8 @@ theory::TheoryModel* SmtEngine::getAvailableModel(const char* c) const { std::stringstream ss; ss << "Cannot " << c - << " unless immediately preceded by SAT/INVALID or UNKNOWN response."; + << " unless immediately preceded by SAT/NOT_ENTAILED or UNKNOWN " + "response."; throw RecoverableModalException(ss.str().c_str()); } @@ -3648,35 +2382,34 @@ Result SmtEngine::checkSat(const vector<Expr>& assumptions, bool inUnsatCore) return checkSatisfiability(assumptions, inUnsatCore, false); } -Result SmtEngine::query(const Expr& assumption, bool inUnsatCore) +Result SmtEngine::checkEntailed(const Expr& expr, bool inUnsatCore) { - Dump("benchmark") << QueryCommand(assumption, inUnsatCore); - return checkSatisfiability(assumption.isNull() - ? std::vector<Expr>() - : std::vector<Expr>{assumption}, - inUnsatCore, - true) - .asValidityResult(); + Dump("benchmark") << QueryCommand(expr, inUnsatCore); + return checkSatisfiability( + expr.isNull() ? std::vector<Expr>() : std::vector<Expr>{expr}, + inUnsatCore, + true) + .asEntailmentResult(); } -Result SmtEngine::query(const vector<Expr>& assumptions, bool inUnsatCore) +Result SmtEngine::checkEntailed(const vector<Expr>& exprs, bool inUnsatCore) { - return checkSatisfiability(assumptions, inUnsatCore, true).asValidityResult(); + return checkSatisfiability(exprs, inUnsatCore, true).asEntailmentResult(); } Result SmtEngine::checkSatisfiability(const Expr& expr, bool inUnsatCore, - bool isQuery) + bool isEntailmentCheck) { return checkSatisfiability( expr.isNull() ? std::vector<Expr>() : std::vector<Expr>{expr}, inUnsatCore, - isQuery); + isEntailmentCheck); } Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, bool inUnsatCore, - bool isQuery) + bool isEntailmentCheck) { try { @@ -3684,7 +2417,8 @@ Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, finalOptionsAreSet(); doPendingPops(); - Trace("smt") << "SmtEngine::" << (isQuery ? "query" : "checkSat") << "(" + Trace("smt") << "SmtEngine::" + << (isEntailmentCheck ? "checkEntailed" : "checkSat") << "(" << assumptions << ")" << endl; if(d_queryMade && !options::incrementalSolving()) { @@ -3702,7 +2436,7 @@ Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, setProblemExtended(); - if (isQuery) + if (isEntailmentCheck) { size_t size = assumptions.size(); if (size > 1) @@ -3808,8 +2542,8 @@ Result SmtEngine::checkSatisfiability(const vector<Expr>& assumptions, d_smtMode = SMT_MODE_SAT_UNKNOWN; } - Trace("smt") << "SmtEngine::" << (isQuery ? "query" : "checkSat") << "(" - << assumptions << ") => " << r << endl; + Trace("smt") << "SmtEngine::" << (isEntailmentCheck ? "query" : "checkSat") + << "(" << assumptions << ") => " << r << endl; // Check that SAT results generate a model correctly. if(options::checkModels()) { @@ -3855,7 +2589,7 @@ vector<Expr> SmtEngine::getUnsatAssumptions(void) { throw RecoverableModalException( "Cannot get unsat assumptions unless immediately preceded by " - "UNSAT/VALID response."); + "UNSAT/ENTAILED."); } finalOptionsAreSet(); if (Dump.isOn("benchmark")) @@ -3893,7 +2627,7 @@ Result SmtEngine::assertFormula(const Expr& ex, bool inUnsatCore) } bool maybeHasFv = language::isInputLangSygus(options::inputLanguage()); d_private->addFormula(e.getNode(), inUnsatCore, true, false, maybeHasFv); - return quickCheck().asValidityResult(); + return quickCheck().asEntailmentResult(); }/* SmtEngine::assertFormula() */ /* @@ -4486,13 +3220,13 @@ std::pair<Expr, Expr> SmtEngine::getSepHeapAndNilExpr(void) Expr heap; Expr nil; Model* m = getAvailableModel("get separation logic heap and nil"); - if (m->getHeapModel(heap, nil)) + if (!m->getHeapModel(heap, nil)) { - return std::make_pair(heap, nil); + InternalError() + << "SmtEngine::getSepHeapAndNilExpr(): failed to obtain heap/nil " + "expressions from theory model."; } - InternalError() - << "SmtEngine::getSepHeapAndNilExpr(): failed to obtain heap/nil " - "expressions from theory model."; + return std::make_pair(heap, nil); } std::vector<Expr> SmtEngine::getExpandedAssertions() @@ -4562,8 +3296,8 @@ UnsatCore SmtEngine::getUnsatCoreInternal() if (d_smtMode != SMT_MODE_UNSAT) { throw RecoverableModalException( - "Cannot get an unsat core unless immediately preceded by UNSAT/VALID " - "response."); + "Cannot get an unsat core unless immediately preceded by " + "UNSAT/ENTAILED response."); } d_proofManager->traceUnsatCore(); // just to trigger core creation @@ -5068,7 +3802,7 @@ const Proof& SmtEngine::getProof() if (d_smtMode != SMT_MODE_UNSAT) { throw RecoverableModalException( - "Cannot get a proof unless immediately preceded by UNSAT/VALID " + "Cannot get a proof unless immediately preceded by UNSAT/ENTAILED " "response."); } @@ -5546,11 +4280,8 @@ void SmtEngine::resetAssertions() * statistics are unregistered by the obsolete PropEngine object before * registered again by the new PropEngine object */ d_propEngine.reset(nullptr); - d_propEngine.reset(new PropEngine(getTheoryEngine(), - getContext(), - getUserContext(), - d_private->getReplayLog(), - d_replayStream)); + d_propEngine.reset( + new PropEngine(getTheoryEngine(), getContext(), getUserContext())); d_theoryEngine->setPropEngine(getPropEngine()); } @@ -5684,6 +4415,9 @@ void SmtEngine::setOption(const std::string& key, const CVC4::SExpr& value) } void SmtEngine::setIsInternalSubsolver() { d_isInternalSubsolver = true; } + +bool SmtEngine::isInternalSubsolver() const { return d_isInternalSubsolver; } + CVC4::SExpr SmtEngine::getOption(const std::string& key) const { NodeManagerScope nms(d_nodeManager); @@ -5740,12 +4474,6 @@ CVC4::SExpr SmtEngine::getOption(const std::string& key) const return SExpr::parseAtom(nodeManagerOptions.getOption(key)); } -void SmtEngine::setReplayStream(ExprStream* replayStream) { - AlwaysAssert(!d_fullyInited) - << "Cannot set replay stream once fully initialized"; - d_replayStream = replayStream; -} - bool SmtEngine::getExpressionName(Expr e, std::string& name) const { return d_private->getExpressionName(e, name); } diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index fbd92bcf2..37b89cfb7 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -28,7 +28,6 @@ #include "context/cdlist_forward.h" #include "expr/expr.h" #include "expr/expr_manager.h" -#include "expr/expr_stream.h" #include "options/options.h" #include "proof/unsat_core.h" #include "smt/logic_exception.h" @@ -153,7 +152,9 @@ class CVC4_PUBLIC SmtEngine */ bool isFullyInited() { return d_fullyInited; } - /** Return true if a query() or checkSat() has already been made. */ + /** + * Return true if a checkEntailed() or checkSatisfiability() has been made. + */ bool isQueryMade() { return d_queryMade; } /** Return the user context level. */ @@ -203,6 +204,8 @@ class CVC4_PUBLIC SmtEngine * --sygus-abduct. */ void setIsInternalSubsolver(); + /** Is this an internal subsolver? */ + bool isInternalSubsolver() const; /** set the input name */ void setFilename(std::string filename); @@ -210,8 +213,8 @@ class CVC4_PUBLIC SmtEngine std::string getFilename() const; /** - * Get the model (only if immediately preceded by a SAT - * or INVALID query). Only permitted if produce-models is on. + * Get the model (only if immediately preceded by a SAT or NOT_ENTAILED + * query). Only permitted if produce-models is on. */ Model* getModel(); @@ -229,9 +232,9 @@ class CVC4_PUBLIC SmtEngine /** * Block the current model values of (at least) the values in exprs. - * Can be called only if immediately preceded by a SAT or INVALID query. Only - * permitted if produce-models is on, and the block-models option is set to a - * mode other than "none". + * Can be called only if immediately preceded by a SAT or NOT_ENTAILED query. + * Only permitted if produce-models is on, and the block-models option is set + * to a mode other than "none". * * This adds an assertion to the assertion stack of the form: * (or (not (= exprs[0] M0)) ... (not (= exprs[n] Mn))) @@ -309,18 +312,21 @@ class CVC4_PUBLIC SmtEngine Result assertFormula(const Expr& e, bool inUnsatCore = true); /** - * Check validity of an expression with respect to the current set - * of assertions by asserting the query expression's negation and - * calling check(). Returns valid, invalid, or unknown result. + * Check if a given (set of) expression(s) is entailed with respect to the + * current set of assertions. We check this by asserting the negation of + * the (big AND over the) given (set of) expression(s). + * Returns ENTAILED, NOT_ENTAILED, or ENTAILMENT_UNKNOWN result. * * @throw Exception */ - Result query(const Expr& assumption = Expr(), bool inUnsatCore = true); - Result query(const std::vector<Expr>& assumptions, bool inUnsatCore = true); + Result checkEntailed(const Expr& assumption = Expr(), + bool inUnsatCore = true); + Result checkEntailed(const std::vector<Expr>& assumptions, + bool inUnsatCore = true); /** * Assert a formula (if provided) to the current context and call - * check(). Returns sat, unsat, or unknown result. + * check(). Returns SAT, UNSAT, or SAT_UNKNOWN result. * * @throw Exception */ @@ -455,9 +461,9 @@ class CVC4_PUBLIC SmtEngine Expr expandDefinitions(const Expr& e); /** - * Get the assigned value of an expr (only if immediately preceded - * by a SAT or INVALID query). Only permitted if the SmtEngine is - * set to operate interactively and produce-models is on. + * Get the assigned value of an expr (only if immediately preceded by a SAT + * or NOT_ENTAILED query). Only permitted if the SmtEngine is set to operate + * interactively and produce-models is on. * * @throw ModalException, TypeCheckingException, LogicException, * UnsafeInterruptException @@ -482,15 +488,15 @@ class CVC4_PUBLIC SmtEngine /** * Get the assignment (only if immediately preceded by a SAT or - * INVALID query). Only permitted if the SmtEngine is set to + * NOT_ENTAILED query). Only permitted if the SmtEngine is set to * operate interactively and produce-assignments is on. */ std::vector<std::pair<Expr, Expr> > getAssignment(); /** - * Get the last proof (only if immediately preceded by an UNSAT - * or VALID query). Only permitted if CVC4 was built with proof - * support and produce-proofs is on. + * Get the last proof (only if immediately preceded by an UNSAT or ENTAILED + * query). Only permitted if CVC4 was built with proof support and + * produce-proofs is on. * * The Proof object is owned by this SmtEngine until the SmtEngine is * destroyed. @@ -623,9 +629,9 @@ class CVC4_PUBLIC SmtEngine std::vector<std::vector<Expr> >& tvecs); /** - * Get an unsatisfiable core (only if immediately preceded by an - * UNSAT or VALID query). Only permitted if CVC4 was built with - * unsat-core support and produce-unsat-cores is on. + * Get an unsatisfiable core (only if immediately preceded by an UNSAT or + * ENTAILED query). Only permitted if CVC4 was built with unsat-core support + * and produce-unsat-cores is on. */ UnsatCore getUnsatCore(); @@ -713,8 +719,8 @@ class CVC4_PUBLIC SmtEngine * * Note that the cumulative timer only ticks away when one of the * SmtEngine's workhorse functions (things like assertFormula(), - * query(), checkSat(), and simplify()) are running. Between calls, - * the timer is still. + * checkEntailed(), checkSat(), and simplify()) are running. + * Between calls, the timer is still. * * When an SmtEngine is first created, it has no time or resource * limits. @@ -773,7 +779,10 @@ class CVC4_PUBLIC SmtEngine /** Flush statistic from this SmtEngine. Safe to use in a signal handler. */ void safeFlushStatistics(int fd) const; - /** Returns the most recent result of checkSat/query or (set-info :status). */ + /** + * Returns the most recent result of checkSat/checkEntailed or + * (set-info :status). + */ Result getStatusOfLastCommand() const { return d_status; } /** @@ -798,13 +807,6 @@ class CVC4_PUBLIC SmtEngine void beforeSearch(); /** - * Expermintal feature: Sets the sequence of decisions. - * This currently requires very fine grained knowledge about literal - * translation. - */ - void setReplayStream(ExprStream* exprStream); - - /** * Get expression name. * * Return true if given expressoion has a name in the current context. @@ -887,7 +889,7 @@ class CVC4_PUBLIC SmtEngine /** * Internal method to get an unsatisfiable core (only if immediately preceded - * by an UNSAT or VALID query). Only permitted if CVC4 was built with + * by an UNSAT or ENTAILED query). Only permitted if CVC4 was built with * unsat-core support and produce-unsat-cores is on. Does not dump the * command. */ @@ -939,12 +941,6 @@ class CVC4_PUBLIC SmtEngine void finalOptionsAreSet(); /** - * Apply heuristics settings and other defaults. Done once, at - * finishInit() time. - */ - void setDefaults(); - - /** * Sets that the problem has been extended. This sets the smt mode of the * solver to SMT_MODE_ASSERT, and clears the list of assumptions from the * previous call to checkSatisfiability. @@ -1019,13 +1015,13 @@ class CVC4_PUBLIC SmtEngine bool userVisible = true, const char* dumpTag = "declarations"); - /* Check satisfiability (used for query and check-sat). */ + /* Check satisfiability (used to check satisfiability and entailment). */ Result checkSatisfiability(const Expr& assumption, bool inUnsatCore, - bool isQuery); + bool isEntailmentCheck); Result checkSatisfiability(const std::vector<Expr>& assumptions, bool inUnsatCore, - bool isQuery); + bool isEntailmentCheck); /** * Check that all Expr in formals are of BOUND_VARIABLE kind, where func is @@ -1135,9 +1131,9 @@ class CVC4_PUBLIC SmtEngine /** * The list of assumptions from the previous call to checkSatisfiability. - * Note that if the last call to checkSatisfiability was a validity check, - * i.e., a call to query(a1, ..., an), then d_assumptions contains one single - * assumption ~(a1 AND ... AND an). + * Note that if the last call to checkSatisfiability was an entailment check, + * i.e., a call to checkEntailed(a1, ..., an), then d_assumptions contains + * one single assumption ~(a1 AND ... AND an). */ std::vector<Expr> d_assumptions; @@ -1201,10 +1197,10 @@ class CVC4_PUBLIC SmtEngine bool d_fullyInited; /** - * Whether or not a query() or checkSat() has already been made through - * this SmtEngine. If true, and incrementalSolving is false, then - * attempting an additional query() or checkSat() will fail with a - * ModalException. + * Whether or not a checkEntailed() or checkSatisfiability() has already been + * made through this SmtEngine. If true, and incrementalSolving is false, + * then attempting an additional checkEntailed() or checkSat() will fail with + * a ModalException. */ bool d_queryMade; @@ -1226,7 +1222,8 @@ class CVC4_PUBLIC SmtEngine bool d_globalNegation; /** - * Most recent result of last checkSat/query or (set-info :status). + * Most recent result of last checkSatisfiability/checkEntailed or + * (set-info :status). */ Result d_status; @@ -1247,9 +1244,6 @@ class CVC4_PUBLIC SmtEngine */ std::map<std::string, Integer> d_commandVerbosity; - /** ReplayStream for the solver. */ - ExprStream* d_replayStream; - /** * A private utility class to SmtEngine. */ diff --git a/src/theory/arith/arith_utilities.cpp b/src/theory/arith/arith_utilities.cpp index cbb27197f..cb8524a58 100644 --- a/src/theory/arith/arith_utilities.cpp +++ b/src/theory/arith/arith_utilities.cpp @@ -272,6 +272,12 @@ Node arithSubstitute(Node n, std::vector<Node>& vars, std::vector<Node>& subs) return visited[n]; } +Node mkBounded(Node l, Node a, Node u) +{ + NodeManager* nm = NodeManager::currentNM(); + return nm->mkNode(AND, nm->mkNode(GEQ, a, l), nm->mkNode(LEQ, a, u)); +} + } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/arith_utilities.h b/src/theory/arith/arith_utilities.h index f87a908b4..2d466f52f 100644 --- a/src/theory/arith/arith_utilities.h +++ b/src/theory/arith/arith_utilities.h @@ -335,6 +335,9 @@ void printRationalApprox(const char* c, Node cr, unsigned prec = 5); */ Node arithSubstitute(Node n, std::vector<Node>& vars, std::vector<Node>& subs); +/** Make the node u >= a ^ a >= l */ +Node mkBounded(Node l, Node a, Node u); + }/* CVC4::theory::arith namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/arith/nl_lemma_utils.cpp b/src/theory/arith/nl_lemma_utils.cpp new file mode 100644 index 000000000..e43a77b06 --- /dev/null +++ b/src/theory/arith/nl_lemma_utils.cpp @@ -0,0 +1,63 @@ +/********************* */ +/*! \file nl_lemma_utils.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of utilities for the non-linear solver + **/ + +#include "theory/arith/nl_lemma_utils.h" + +#include "theory/arith/nl_model.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +bool SortNlModel::operator()(Node i, Node j) +{ + int cv = d_nlm->compare(i, j, d_isConcrete, d_isAbsolute); + if (cv == 0) + { + return i < j; + } + return d_reverse_order ? cv < 0 : cv > 0; +} + +bool SortNonlinearDegree::operator()(Node i, Node j) +{ + unsigned i_count = getDegree(i); + unsigned j_count = getDegree(j); + return i_count == j_count ? (i < j) : (i_count < j_count ? true : false); +} + +unsigned SortNonlinearDegree::getDegree(Node n) const +{ + std::map<Node, unsigned>::const_iterator it = d_mdegree.find(n); + Assert(it != d_mdegree.end()); + return it->second; +} + +Node ArgTrie::add(Node d, const std::vector<Node>& args) +{ + ArgTrie* at = this; + for (const Node& a : args) + { + at = &(at->d_children[a]); + } + if (at->d_data.isNull()) + { + at->d_data = d; + } + return at->d_data; +} + +} // namespace arith +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/arith/nl_lemma_utils.h b/src/theory/arith/nl_lemma_utils.h index 74886a1fb..9ad9f2ca5 100644 --- a/src/theory/arith/nl_lemma_utils.h +++ b/src/theory/arith/nl_lemma_utils.h @@ -23,6 +23,8 @@ namespace CVC4 { namespace theory { namespace arith { +class NlModel; + /** * A side effect of adding a lemma in the non-linear solver. This is used * to specify how the state of the non-linear solver should update. This @@ -46,6 +48,56 @@ struct NlLemmaSideEffect std::vector<std::tuple<Node, unsigned, Node> > d_secantPoint; }; +struct SortNlModel +{ + SortNlModel() + : d_nlm(nullptr), + d_isConcrete(true), + d_isAbsolute(false), + d_reverse_order(false) + { + } + /** pointer to the model */ + NlModel* d_nlm; + /** are we comparing concrete model values? */ + bool d_isConcrete; + /** are we comparing absolute values? */ + bool d_isAbsolute; + /** are we in reverse order? */ + bool d_reverse_order; + /** the comparison */ + bool operator()(Node i, Node j); +}; + +struct SortNonlinearDegree +{ + SortNonlinearDegree(std::map<Node, unsigned>& m) : d_mdegree(m) {} + /** pointer to the non-linear extension */ + std::map<Node, unsigned>& d_mdegree; + /** Get the degree of n in d_mdegree */ + unsigned getDegree(Node n) const; + /** + * Sorts by degree of the monomials, where lower degree monomials come + * first. + */ + bool operator()(Node i, Node j); +}; + +/** An argument trie, for computing congruent terms */ +class ArgTrie +{ + public: + /** children of this node */ + std::map<Node, ArgTrie> d_children; + /** the data of this node */ + Node d_data; + /** + * Set d as the data on the node whose path is [args], return either d if + * that node has no data, or the data that already occurs there. + */ + Node add(Node d, const std::vector<Node>& args); +}; + } // namespace arith } // namespace theory } // namespace CVC4 diff --git a/src/theory/arith/nonlinear_extension.cpp b/src/theory/arith/nonlinear_extension.cpp index f11d55855..61a8b18b0 100644 --- a/src/theory/arith/nonlinear_extension.cpp +++ b/src/theory/arith/nonlinear_extension.cpp @@ -113,49 +113,6 @@ void debugPrintBound(const char* c, Node coeff, Node x, Kind type, Node rhs) { Trace(c) << t << " " << type << " " << rhs; } -struct SortNlModel -{ - SortNlModel() - : d_nlm(nullptr), - d_isConcrete(true), - d_isAbsolute(false), - d_reverse_order(false) - { - } - /** pointer to the model */ - NlModel* d_nlm; - /** are we comparing concrete model values? */ - bool d_isConcrete; - /** are we comparing absolute values? */ - bool d_isAbsolute; - /** are we in reverse order? */ - bool d_reverse_order; - /** the comparison */ - bool operator()(Node i, Node j) { - int cv = d_nlm->compare(i, j, d_isConcrete, d_isAbsolute); - if (cv == 0) { - return i < j; - } - return d_reverse_order ? cv < 0 : cv > 0; - } -}; -struct SortNonlinearDegree -{ - SortNonlinearDegree(NodeMultiset& m) : d_mdegree(m) {} - /** pointer to the non-linear extension */ - NodeMultiset& d_mdegree; - /** - * Sorts by degree of the monomials, where lower degree monomials come - * first. - */ - bool operator()(Node i, Node j) - { - unsigned i_count = getCount(d_mdegree, i); - unsigned j_count = getCount(d_mdegree, j); - return i_count == j_count ? (i < j) : (i_count < j_count ? true : false); - } -}; - bool hasNewMonomials(Node n, const std::vector<Node>& existing) { std::set<Node> visited; @@ -189,6 +146,7 @@ NonlinearExtension::NonlinearExtension(TheoryArith& containing, d_ee(ee), d_needsLastCall(false), d_model(containing.getSatContext()), + d_trSlv(d_model), d_builtModel(containing.getSatContext(), false) { d_true = NodeManager::currentNM()->mkConst(true); @@ -200,13 +158,6 @@ NonlinearExtension::NonlinearExtension(TheoryArith& containing, d_order_points.push_back(d_neg_one); d_order_points.push_back(d_zero); d_order_points.push_back(d_one); - d_taylor_real_fv = NodeManager::currentNM()->mkBoundVar( - "x", NodeManager::currentNM()->realType()); - d_taylor_real_fv_base = NodeManager::currentNM()->mkBoundVar( - "a", NodeManager::currentNM()->realType()); - d_taylor_real_fv_base_rem = NodeManager::currentNM()->mkBoundVar( - "b", NodeManager::currentNM()->realType()); - d_taylor_degree = options::nlExtTfTaylorDegree(); } NonlinearExtension::~NonlinearExtension() {} @@ -509,13 +460,6 @@ Node NonlinearExtension::mkValidPhase(Node a, Node pi) { NodeManager::currentNM()->mkNode(MULT, mkRationalNode(-1), pi), a, pi); } -Node NonlinearExtension::mkBounded( Node l, Node a, Node u ) { - return NodeManager::currentNM()->mkNode( - AND, - NodeManager::currentNM()->mkNode(GEQ, a, l), - NodeManager::currentNM()->mkNode(LEQ, a, u)); -} - Node NonlinearExtension::mkMonomialRemFactor( Node n, const NodeMultiset& n_exp_rem) const { std::vector<Node> children; @@ -566,13 +510,7 @@ void NonlinearExtension::sendLemmas(const std::vector<Node>& out, void NonlinearExtension::processSideEffect(const NlLemmaSideEffect& se) { - for (const std::tuple<Node, unsigned, Node>& sp : se.d_secantPoint) - { - Node tf = std::get<0>(sp); - unsigned d = std::get<1>(sp); - Node c = std::get<2>(sp); - d_secant_points[tf][d].push_back(c); - } + d_trSlv.processSideEffect(se); } unsigned NonlinearExtension::filterLemma(Node lem, std::vector<Node>& out) @@ -779,90 +717,18 @@ bool NonlinearExtension::checkModel(const std::vector<Node>& assertions, // get the presubstitution Trace("nl-ext-cm-debug") << " apply pre-substitution..." << std::endl; - std::vector<Node> pvars; - std::vector<Node> psubs; - for (std::pair<const Node, Node>& tb : d_trMaster) - { - pvars.push_back(tb.first); - psubs.push_back(tb.second); - } - // initialize representation of assertions - std::vector<Node> passertions; - for (const Node& a : assertions) - { - Node pa = a; - if (!pvars.empty()) - { - pa = arithSubstitute(pa, pvars, psubs); - pa = Rewriter::rewrite(pa); - } - if (!pa.isConst() || !pa.getConst<bool>()) - { - Trace("nl-ext-cm-assert") << "- assert : " << pa << std::endl; - passertions.push_back(pa); - } - } + std::vector<Node> passertions = assertions; - // get model bounds for all transcendental functions - Trace("nl-ext-cm") << "----- Get bounds for transcendental functions..." - << std::endl; - for (std::pair<const Kind, std::vector<Node> >& tfs : d_funcMap) + // preprocess the assertions with the trancendental solver + if (!d_trSlv.preprocessAssertionsCheckModel(passertions)) { - Kind k = tfs.first; - for (const Node& tf : tfs.second) - { - Trace("nl-ext-cm") << "- Term: " << tf << std::endl; - bool success = true; - // tf is Figure 3 : tf( x ) - Node bl; - Node bu; - if (k == PI) - { - bl = d_pi_bound[0]; - bu = d_pi_bound[1]; - } - else - { - std::pair<Node, Node> bounds = getTfModelBounds(tf, d_taylor_degree); - bl = bounds.first; - bu = bounds.second; - if (bl != bu) - { - d_model.setUsedApproximate(); - } - } - if (!bl.isNull() && !bu.isNull()) - { - // for each function in the congruence classe - for (const Node& ctf : d_funcCongClass[tf]) - { - // each term in congruence classes should be master terms - Assert(d_trSlaves.find(ctf) != d_trSlaves.end()); - // we set the bounds for each slave of tf - for (const Node& stf : d_trSlaves[ctf]) - { - Trace("nl-ext-cm") << "...bound for " << stf << " : [" << bl << ", " - << bu << "]" << std::endl; - success = d_model.addCheckModelBound(stf, bl, bu); - } - } - } - else - { - Trace("nl-ext-cm") << "...no bound for " << tf << std::endl; - } - if (!success) - { - // a bound was conflicting - Trace("nl-ext-cm") << "...failed to set bound for " << tf << std::endl; - Trace("nl-ext-cm") << "-----" << std::endl; - return false; - } - } + return false; } + Trace("nl-ext-cm") << "-----" << std::endl; - bool ret = d_model.checkModel( - passertions, false_asserts, d_taylor_degree, lemmas, gs); + unsigned tdegree = d_trSlv.getTaylorDegree(); + bool ret = + d_model.checkModel(passertions, false_asserts, tdegree, lemmas, gs); return ret; } @@ -882,33 +748,6 @@ std::vector<Node> NonlinearExtension::checkSplitZero() { return lemmas; } -/** An argument trie, for computing congruent terms */ -class ArgTrie -{ - public: - /** children of this node */ - std::map<Node, ArgTrie> d_children; - /** the data of this node */ - Node d_data; - /** - * Set d as the data on the node whose path is [args], return either d if - * that node has no data, or the data that already occurs there. - */ - Node add(Node d, const std::vector<Node>& args) - { - ArgTrie* at = this; - for (const Node& a : args) - { - at = &(at->d_children[a]); - } - if (at->d_data.isNull()) - { - at->d_data = d; - } - return at->d_data; - } -}; - int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, const std::vector<Node>& false_asserts, const std::vector<Node>& xts, @@ -926,18 +765,8 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, d_ci.clear(); d_ci_exp.clear(); d_ci_max.clear(); - d_funcCongClass.clear(); - d_funcMap.clear(); - d_tf_region.clear(); - - std::vector<Node> lemmas; - NodeManager* nm = NodeManager::currentNM(); Trace("nl-ext-mv") << "Extended terms : " << std::endl; - // register the extended function terms - std::map< Node, Node > mvarg_to_term; - std::vector<Node> trNeedsMaster; - bool needPi = false; // for computing congruence std::map<Kind, ArgTrie> argTrie; for (unsigned i = 0, xsize = xts.size(); i < xsize; i++) @@ -947,47 +776,6 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, d_model.computeAbstractModelValue(a); d_model.printModelValue("nl-ext-mv", a); Kind ak = a.getKind(); - bool consider = true; - // if is an unpurified application of SINE, or it is a transcendental - // applied to a trancendental, purify. - if (isTranscendentalKind(ak)) - { - // if we've already computed master for a - if (d_trMaster.find(a) != d_trMaster.end()) - { - // a master has at least one slave - consider = (d_trSlaves.find(a) != d_trSlaves.end()); - } - else - { - if (ak == SINE) - { - // always not a master - consider = false; - } - else - { - for (const Node& ac : a) - { - if (isTranscendentalKind(ac.getKind())) - { - consider = false; - break; - } - } - } - if (!consider) - { - // wait to assign a master below - trNeedsMaster.push_back(a); - } - else - { - d_trMaster[a] = a; - d_trSlaves[a].insert(a); - } - } - } if (ak == NONLINEAR_MULT) { d_ms.push_back( a ); @@ -1008,126 +796,19 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, } // mark processed if has a "one" factor (will look at reduced monomial)? } - else if (a.getNumChildren() > 0) - { - if (ak == SINE) - { - needPi = true; - } - // if we didn't indicate that it should be purified above - if( consider ){ - std::vector<Node> repList; - for (const Node& ac : a) - { - Node r = d_model.computeConcreteModelValue(ac); - repList.push_back(r); - } - Node aa = argTrie[ak].add(a, repList); - if (aa != a) - { - // apply congruence to pairs of terms that are disequal and congruent - Assert(aa.getNumChildren() == a.getNumChildren()); - Node mvaa = d_model.computeAbstractModelValue(a); - Node mvaaa = d_model.computeAbstractModelValue(aa); - if (mvaa != mvaaa) - { - std::vector<Node> exp; - for (unsigned j = 0, size = a.getNumChildren(); j < size; j++) - { - exp.push_back(a[j].eqNode(aa[j])); - } - Node expn = exp.size() == 1 ? exp[0] : nm->mkNode(AND, exp); - Node cong_lemma = nm->mkNode(OR, expn.negate(), a.eqNode(aa)); - lemmas.push_back( cong_lemma ); - } - } - else - { - // new representative of congruence class - d_funcMap[ak].push_back(a); - } - // add to congruence class - d_funcCongClass[aa].push_back(a); - } - } - else if (ak == PI) - { - Assert(consider); - needPi = true; - d_funcMap[ak].push_back(a); - d_funcCongClass[a].push_back(a); - } - else - { - Assert(false); - } - } - // initialize pi if necessary - if (needPi && d_pi.isNull()) - { - mkPi(); - getCurrentPiBounds(lemmas); } + // initialize the trancendental function solver + std::vector<Node> lemmas; + d_trSlv.initLastCall(assertions, false_asserts, xts, lemmas, lemsPp); + + // process lemmas that may have been generated by the transcendental solver filterLemmas(lemmas, lems); - if (!lems.empty()) + if (!lems.empty() || !lemsPp.empty()) { Trace("nl-ext") << " ...finished with " << lems.size() << " new lemmas during registration." << std::endl; - return lems.size(); - } - - // process SINE phase shifting - for (const Node& a : trNeedsMaster) - { - // should not have processed this already - Assert(d_trMaster.find(a) == d_trMaster.end()); - Kind k = a.getKind(); - Assert(k == SINE || k == EXPONENTIAL); - Node y = - nm->mkSkolem("y", nm->realType(), "phase shifted trigonometric arg"); - Node new_a = nm->mkNode(k, y); - d_trSlaves[new_a].insert(new_a); - d_trSlaves[new_a].insert(a); - d_trMaster[a] = new_a; - d_trMaster[new_a] = new_a; - Node lem; - if (k == SINE) - { - Trace("nl-ext-tf") << "Basis sine : " << new_a << " for " << a - << std::endl; - Assert(!d_pi.isNull()); - Node shift = nm->mkSkolem("s", nm->integerType(), "number of shifts"); - // TODO : do not introduce shift here, instead needs model-based - // refinement for constant shifts (cvc4-projects #1284) - lem = nm->mkNode( - AND, - mkValidPhase(y, d_pi), - nm->mkNode( - ITE, - mkValidPhase(a[0], d_pi), - a[0].eqNode(y), - a[0].eqNode(nm->mkNode( - PLUS, - y, - nm->mkNode(MULT, nm->mkConst(Rational(2)), shift, d_pi)))), - new_a.eqNode(a)); - } - else - { - // do both equalities to ensure that new_a becomes a preregistered term - lem = nm->mkNode(AND, a.eqNode(new_a), a[0].eqNode(y)); - } - // note we must do preprocess on this lemma - Trace("nl-ext-lemma") << "NonlinearExtension::Lemma : purify : " << lem - << std::endl; - lemsPp.push_back(lem); - } - if (!lemsPp.empty()) - { - Trace("nl-ext") << " ...finished with " << lemsPp.size() - << " new lemmas SINE phase shifting." << std::endl; - return lemsPp.size(); + return lems.size() + lemsPp.size(); } Trace("nl-ext") << "We have " << d_ms.size() << " monomials." << std::endl; @@ -1148,25 +829,6 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, d_model.computeAbstractModelValue(v); d_model.printModelValue("nl-ext-mv", v); } - if (Trace.isOn("nl-ext-mv")) - { - Trace("nl-ext-mv") << "Arguments of trancendental functions : " - << std::endl; - for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) - { - Kind k = tfl.first; - if (k == SINE || k == EXPONENTIAL) - { - for (const Node& tf : tfl.second) - { - Node v = tf[0]; - d_model.computeConcreteModelValue(v); - d_model.computeAbstractModelValue(v); - d_model.printModelValue("nl-ext-mv", v); - } - } - } - } //----------------------------------- possibly split on zero if (options::nlExtSplitZero()) { @@ -1182,7 +844,7 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, } //-----------------------------------initial lemmas for transcendental functions - lemmas = checkTranscendentalInitialRefine(); + lemmas = d_trSlv.checkTranscendentalInitialRefine(); filterLemmas(lemmas, lems); if (!lems.empty()) { @@ -1202,7 +864,7 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, } //-----------------------------------monotonicity of transdental functions - lemmas = checkTranscendentalMonotonic(); + lemmas = d_trSlv.checkTranscendentalMonotonic(); filterLemmas(lemmas, lems); if (!lems.empty()) { @@ -1313,7 +975,7 @@ int NonlinearExtension::checkLastCall(const std::vector<Node>& assertions, } if (options::nlExtTfTangentPlanes()) { - lemmas = checkTranscendentalTangentPlanes(lemSE); + lemmas = d_trSlv.checkTranscendentalTangentPlanes(lemSE); filterLemmas(lemmas, wlems); } Trace("nl-ext") << " ...finished with " << wlems.size() << " waiting lemmas." @@ -1543,12 +1205,12 @@ bool NonlinearExtension::modelBasedRefinement( // we are incomplete if (options::nlExtIncPrecision() && d_model.usedApproximate()) { - d_taylor_degree++; + d_trSlv.incrementTaylorDegree(); needsRecheck = true; // increase precision for PI? // Difficult since Taylor series is very slow to converge - Trace("nl-ext") << "...increment Taylor degree to " << d_taylor_degree - << std::endl; + Trace("nl-ext") << "...increment Taylor degree to " + << d_trSlv.getTaylorDegree() << std::endl; } else { @@ -1660,36 +1322,6 @@ void NonlinearExtension::assignOrderIds(std::vector<Node>& vars, } } -void NonlinearExtension::mkPi(){ - if( d_pi.isNull() ){ - d_pi = NodeManager::currentNM()->mkNullaryOperator( - NodeManager::currentNM()->realType(), PI); - d_pi_2 = Rewriter::rewrite(NodeManager::currentNM()->mkNode( - MULT, - d_pi, - NodeManager::currentNM()->mkConst(Rational(1) / Rational(2)))); - d_pi_neg_2 = Rewriter::rewrite(NodeManager::currentNM()->mkNode( - MULT, - d_pi, - NodeManager::currentNM()->mkConst(Rational(-1) / Rational(2)))); - d_pi_neg = Rewriter::rewrite(NodeManager::currentNM()->mkNode( - MULT, d_pi, NodeManager::currentNM()->mkConst(Rational(-1)))); - //initialize bounds - d_pi_bound[0] = - NodeManager::currentNM()->mkConst(Rational(103993) / Rational(33102)); - d_pi_bound[1] = - NodeManager::currentNM()->mkConst(Rational(104348) / Rational(33215)); - } -} - -void NonlinearExtension::getCurrentPiBounds( std::vector< Node >& lemmas ) { - Node pi_lem = NodeManager::currentNM()->mkNode( - AND, - NodeManager::currentNM()->mkNode(GEQ, d_pi, d_pi_bound[0]), - NodeManager::currentNM()->mkNode(LEQ, d_pi, d_pi_bound[1])); - lemmas.push_back( pi_lem ); -} - bool NonlinearExtension::getApproximateSqrt(Node c, Node& l, Node& u, @@ -2807,1098 +2439,6 @@ std::vector<Node> NonlinearExtension::checkMonomialInferResBounds() { } return lemmas; } - -std::vector<Node> NonlinearExtension::checkTranscendentalInitialRefine() { - std::vector< Node > lemmas; - Trace("nl-ext") << "Get initial refinement lemmas for transcendental functions..." << std::endl; - for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) - { - Kind k = tfl.first; - for (const Node& t : tfl.second) - { - //initial refinements - if( d_tf_initial_refine.find( t )==d_tf_initial_refine.end() ){ - d_tf_initial_refine[t] = true; - Node lem; - if (k == SINE) - { - Node symn = NodeManager::currentNM()->mkNode( - SINE, NodeManager::currentNM()->mkNode(MULT, d_neg_one, t[0])); - symn = Rewriter::rewrite( symn ); - // Can assume it is its own master since phase is split over 0, - // hence -pi <= t[0] <= pi implies -pi <= -t[0] <= pi. - d_trMaster[symn] = symn; - d_trSlaves[symn].insert(symn); - Assert(d_trSlaves.find(t) != d_trSlaves.end()); - std::vector< Node > children; - - lem = NodeManager::currentNM()->mkNode( - AND, - // bounds - NodeManager::currentNM()->mkNode( - AND, - NodeManager::currentNM()->mkNode(LEQ, t, d_one), - NodeManager::currentNM()->mkNode(GEQ, t, d_neg_one)), - // symmetry - NodeManager::currentNM()->mkNode(PLUS, t, symn).eqNode(d_zero), - // sign - NodeManager::currentNM()->mkNode( - EQUAL, - NodeManager::currentNM()->mkNode(LT, t[0], d_zero), - NodeManager::currentNM()->mkNode(LT, t, d_zero)), - // zero val - NodeManager::currentNM()->mkNode( - EQUAL, - NodeManager::currentNM()->mkNode(GT, t[0], d_zero), - NodeManager::currentNM()->mkNode(GT, t, d_zero))); - lem = NodeManager::currentNM()->mkNode( - AND, - lem, - // zero tangent - NodeManager::currentNM()->mkNode( - AND, - NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode(GT, t[0], d_zero), - NodeManager::currentNM()->mkNode(LT, t, t[0])), - NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode(LT, t[0], d_zero), - NodeManager::currentNM()->mkNode(GT, t, t[0]))), - // pi tangent - NodeManager::currentNM()->mkNode( - AND, - NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode(LT, t[0], d_pi), - NodeManager::currentNM()->mkNode( - LT, - t, - NodeManager::currentNM()->mkNode(MINUS, d_pi, t[0]))), - NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode(GT, t[0], d_pi_neg), - NodeManager::currentNM()->mkNode( - GT, - t, - NodeManager::currentNM()->mkNode( - MINUS, d_pi_neg, t[0]))))); - } - else if (k == EXPONENTIAL) - { - // ( exp(x) > 0 ) ^ ( x=0 <=> exp( x ) = 1 ) ^ ( x < 0 <=> exp( x ) < - // 1 ) ^ ( x <= 0 V exp( x ) > x + 1 ) - lem = NodeManager::currentNM()->mkNode( - AND, - NodeManager::currentNM()->mkNode(GT, t, d_zero), - NodeManager::currentNM()->mkNode( - EQUAL, t[0].eqNode(d_zero), t.eqNode(d_one)), - NodeManager::currentNM()->mkNode( - EQUAL, - NodeManager::currentNM()->mkNode(LT, t[0], d_zero), - NodeManager::currentNM()->mkNode(LT, t, d_one)), - NodeManager::currentNM()->mkNode( - OR, - NodeManager::currentNM()->mkNode(LEQ, t[0], d_zero), - NodeManager::currentNM()->mkNode( - GT, - t, - NodeManager::currentNM()->mkNode(PLUS, t[0], d_one)))); - } - if( !lem.isNull() ){ - lemmas.push_back( lem ); - } - } - } - } - - return lemmas; -} - -std::vector<Node> NonlinearExtension::checkTranscendentalMonotonic() { - std::vector< Node > lemmas; - Trace("nl-ext") << "Get monotonicity lemmas for transcendental functions..." << std::endl; - - //sort arguments of all transcendentals - std::map< Kind, std::vector< Node > > sorted_tf_args; - std::map< Kind, std::map< Node, Node > > tf_arg_to_term; - - for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) - { - Kind k = tfl.first; - if (k == EXPONENTIAL || k == SINE) - { - for (const Node& tf : tfl.second) - { - Node a = tf[0]; - Node mvaa = d_model.computeAbstractModelValue(a); - if (mvaa.isConst()) - { - Trace("nl-ext-tf-mono-debug") << "...tf term : " << a << std::endl; - sorted_tf_args[k].push_back(a); - tf_arg_to_term[k][a] = tf; - } - } - } - } - - SortNlModel smv; - smv.d_nlm = &d_model; - //sort by concrete values - smv.d_isConcrete = true; - smv.d_reverse_order = true; - for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) - { - Kind k = tfl.first; - if( !sorted_tf_args[k].empty() ){ - std::sort( sorted_tf_args[k].begin(), sorted_tf_args[k].end(), smv ); - Trace("nl-ext-tf-mono") << "Sorted transcendental function list for " << k << " : " << std::endl; - for (unsigned i = 0; i < sorted_tf_args[k].size(); i++) - { - Node targ = sorted_tf_args[k][i]; - Node mvatarg = d_model.computeAbstractModelValue(targ); - Trace("nl-ext-tf-mono") - << " " << targ << " -> " << mvatarg << std::endl; - Node t = tf_arg_to_term[k][targ]; - Node mvat = d_model.computeAbstractModelValue(t); - Trace("nl-ext-tf-mono") << " f-val : " << mvat << std::endl; - } - std::vector< Node > mpoints; - std::vector< Node > mpoints_vals; - if (k == SINE) - { - mpoints.push_back( d_pi ); - mpoints.push_back( d_pi_2 ); - mpoints.push_back(d_zero); - mpoints.push_back( d_pi_neg_2 ); - mpoints.push_back( d_pi_neg ); - } - else if (k == EXPONENTIAL) - { - mpoints.push_back( Node::null() ); - } - if( !mpoints.empty() ){ - //get model values for points - for( unsigned i=0; i<mpoints.size(); i++ ){ - Node mpv; - if( !mpoints[i].isNull() ){ - mpv = d_model.computeAbstractModelValue(mpoints[i]); - Assert(mpv.isConst()); - } - mpoints_vals.push_back( mpv ); - } - - unsigned mdir_index = 0; - int monotonic_dir = -1; - Node mono_bounds[2]; - Node targ, targval, t, tval; - for (unsigned i = 0, size = sorted_tf_args[k].size(); i < size; i++) - { - Node sarg = sorted_tf_args[k][i]; - Node sargval = d_model.computeAbstractModelValue(sarg); - Assert(sargval.isConst()); - Node s = tf_arg_to_term[k][ sarg ]; - Node sval = d_model.computeAbstractModelValue(s); - Assert(sval.isConst()); - - //increment to the proper monotonicity region - bool increment = true; - while (increment && mdir_index < mpoints.size()) - { - increment = false; - if( mpoints[mdir_index].isNull() ){ - increment = true; - }else{ - Node pval = mpoints_vals[mdir_index]; - Assert(pval.isConst()); - if( sargval.getConst<Rational>() < pval.getConst<Rational>() ){ - increment = true; - Trace("nl-ext-tf-mono") << "...increment at " << sarg << " since model value is less than " << mpoints[mdir_index] << std::endl; - } - } - if( increment ){ - tval = Node::null(); - mono_bounds[1] = mpoints[mdir_index]; - mdir_index++; - monotonic_dir = regionToMonotonicityDir(k, mdir_index); - if (mdir_index < mpoints.size()) - { - mono_bounds[0] = mpoints[mdir_index]; - }else{ - mono_bounds[0] = Node::null(); - } - } - } - // store the concavity region - d_tf_region[s] = mdir_index; - Trace("nl-ext-concavity") << "Transcendental function " << s - << " is in region #" << mdir_index; - Trace("nl-ext-concavity") << ", arg model value = " << sargval - << std::endl; - - if( !tval.isNull() ){ - Node mono_lem; - if( monotonic_dir==1 && sval.getConst<Rational>() > tval.getConst<Rational>() ){ - mono_lem = NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode(GEQ, targ, sarg), - NodeManager::currentNM()->mkNode(GEQ, t, s)); - }else if( monotonic_dir==-1 && sval.getConst<Rational>() < tval.getConst<Rational>() ){ - mono_lem = NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode(LEQ, targ, sarg), - NodeManager::currentNM()->mkNode(LEQ, t, s)); - } - if( !mono_lem.isNull() ){ - if( !mono_bounds[0].isNull() ){ - Assert(!mono_bounds[1].isNull()); - mono_lem = NodeManager::currentNM()->mkNode( - IMPLIES, - NodeManager::currentNM()->mkNode( - AND, - mkBounded(mono_bounds[0], targ, mono_bounds[1]), - mkBounded(mono_bounds[0], sarg, mono_bounds[1])), - mono_lem); - } - Trace("nl-ext-tf-mono") << "Monotonicity lemma : " << mono_lem << std::endl; - lemmas.push_back( mono_lem ); - } - } - // store the previous values - targ = sarg; - targval = sargval; - t = s; - tval = sval; - } - } - } - } - return lemmas; -} - -std::vector<Node> NonlinearExtension::checkTranscendentalTangentPlanes( - std::map<Node, NlLemmaSideEffect>& lemSE) -{ - std::vector<Node> lemmas; - Trace("nl-ext") << "Get tangent plane lemmas for transcendental functions..." - << std::endl; - // this implements Figure 3 of "Satisfiaility Modulo Transcendental Functions - // via Incremental Linearization" by Cimatti et al - for (std::pair<const Kind, std::vector<Node> >& tfs : d_funcMap) - { - Kind k = tfs.first; - if (k == PI) - { - // We do not use Taylor approximation for PI currently. - // This is because the convergence is extremely slow, and hence an - // initial approximation is superior. - continue; - } - Trace("nl-ext-tftp-debug2") << "Taylor variables: " << std::endl; - Trace("nl-ext-tftp-debug2") - << " taylor_real_fv : " << d_taylor_real_fv << std::endl; - Trace("nl-ext-tftp-debug2") - << " taylor_real_fv_base : " << d_taylor_real_fv_base << std::endl; - Trace("nl-ext-tftp-debug2") - << " taylor_real_fv_base_rem : " << d_taylor_real_fv_base_rem - << std::endl; - Trace("nl-ext-tftp-debug2") << std::endl; - - // we substitute into the Taylor sum P_{n,f(0)}( x ) - - for (const Node& tf : tfs.second) - { - // tf is Figure 3 : tf( x ) - Trace("nl-ext-tftp") << "Compute tangent planes " << tf << std::endl; - // go until max degree is reached, or we don't meet bound criteria - for (unsigned d = 1; d <= d_taylor_degree; d++) - { - Trace("nl-ext-tftp") << "- run at degree " << d << "..." << std::endl; - unsigned prev = lemmas.size(); - if (checkTfTangentPlanesFun(tf, d, lemmas, lemSE)) - { - Trace("nl-ext-tftp") - << "...fail, #lemmas = " << (lemmas.size() - prev) << std::endl; - break; - } - else - { - Trace("nl-ext-tftp") << "...success" << std::endl; - } - } - } - } - - return lemmas; -} - -bool NonlinearExtension::checkTfTangentPlanesFun( - Node tf, - unsigned d, - std::vector<Node>& lemmas, - std::map<Node, NlLemmaSideEffect>& lemSE) -{ - NodeManager* nm = NodeManager::currentNM(); - Kind k = tf.getKind(); - // this should only be run on master applications - Assert(d_trSlaves.find(tf) != d_trSlaves.end()); - - // Figure 3 : c - Node c = d_model.computeAbstractModelValue(tf[0]); - int csign = c.getConst<Rational>().sgn(); - if (csign == 0) - { - // no secant/tangent plane is necessary - return true; - } - Assert(csign == 1 || csign == -1); - - // Figure 3: P_l, P_u - // mapped to for signs of c - std::map<int, Node> poly_approx_bounds[2]; - std::vector<Node> pbounds; - getPolynomialApproximationBoundForArg(k, c, d, pbounds); - poly_approx_bounds[0][1] = pbounds[0]; - poly_approx_bounds[0][-1] = pbounds[1]; - poly_approx_bounds[1][1] = pbounds[2]; - poly_approx_bounds[1][-1] = pbounds[3]; - - // Figure 3 : v - Node v = d_model.computeAbstractModelValue(tf); - - // check value of tf - Trace("nl-ext-tftp-debug") << "Process tangent plane refinement for " << tf - << ", degree " << d << "..." << std::endl; - Trace("nl-ext-tftp-debug") << " value in model : " << v << std::endl; - Trace("nl-ext-tftp-debug") << " arg value in model : " << c << std::endl; - - std::vector<Node> taylor_vars; - taylor_vars.push_back(d_taylor_real_fv); - - // compute the concavity - int region = -1; - std::unordered_map<Node, int, NodeHashFunction>::iterator itr = - d_tf_region.find(tf); - if (itr != d_tf_region.end()) - { - region = itr->second; - Trace("nl-ext-tftp-debug") << " region is : " << region << std::endl; - } - // Figure 3 : conc - int concavity = regionToConcavity(k, itr->second); - Trace("nl-ext-tftp-debug") << " concavity is : " << concavity << std::endl; - if (concavity == 0) - { - // no secant/tangent plane is necessary - return true; - } - // bounds for which we are this concavity - // Figure 3: < l, u > - Node bounds[2]; - if (k == SINE) - { - bounds[0] = regionToLowerBound(k, region); - Assert(!bounds[0].isNull()); - bounds[1] = regionToUpperBound(k, region); - Assert(!bounds[1].isNull()); - } - - // Figure 3: P - Node poly_approx; - - // compute whether this is a tangent refinement or a secant refinement - bool is_tangent = false; - bool is_secant = false; - std::pair<Node, Node> mvb = getTfModelBounds(tf, d); - for (unsigned r = 0; r < 2; r++) - { - Node pab = poly_approx_bounds[r][csign]; - Node v_pab = r == 0 ? mvb.first : mvb.second; - if (!v_pab.isNull()) - { - Trace("nl-ext-tftp-debug2") << "...model value of " << pab << " is " - << v_pab << std::endl; - - Assert(v_pab.isConst()); - Node comp = nm->mkNode(r == 0 ? LT : GT, v, v_pab); - Trace("nl-ext-tftp-debug2") << "...compare : " << comp << std::endl; - Node compr = Rewriter::rewrite(comp); - Trace("nl-ext-tftp-debug2") << "...got : " << compr << std::endl; - if (compr == d_true) - { - // beyond the bounds - if (r == 0) - { - poly_approx = poly_approx_bounds[r][csign]; - is_tangent = concavity == 1; - is_secant = concavity == -1; - } - else - { - poly_approx = poly_approx_bounds[r][csign]; - is_tangent = concavity == -1; - is_secant = concavity == 1; - } - if (Trace.isOn("nl-ext-tftp")) - { - Trace("nl-ext-tftp") << "*** Outside boundary point ("; - Trace("nl-ext-tftp") << (r == 0 ? "low" : "high") << ") "; - printRationalApprox("nl-ext-tftp", v_pab); - Trace("nl-ext-tftp") << ", will refine..." << std::endl; - Trace("nl-ext-tftp") << " poly_approx = " << poly_approx - << std::endl; - Trace("nl-ext-tftp") << " is_tangent = " << is_tangent - << std::endl; - Trace("nl-ext-tftp") << " is_secant = " << is_secant << std::endl; - } - break; - } - else - { - Trace("nl-ext-tftp") << " ...within " << (r == 0 ? "low" : "high") - << " bound : "; - printRationalApprox("nl-ext-tftp", v_pab); - Trace("nl-ext-tftp") << std::endl; - } - } - } - - // Figure 3: P( c ) - Node poly_approx_c; - if (is_tangent || is_secant) - { - Assert(!poly_approx.isNull()); - std::vector<Node> taylor_subs; - taylor_subs.push_back(c); - Assert(taylor_vars.size() == taylor_subs.size()); - poly_approx_c = poly_approx.substitute(taylor_vars.begin(), - taylor_vars.end(), - taylor_subs.begin(), - taylor_subs.end()); - Trace("nl-ext-tftp-debug2") << "...poly approximation at c is " - << poly_approx_c << std::endl; - } - else - { - // we may want to continue getting better bounds - return false; - } - - if (is_tangent) - { - // compute tangent plane - // Figure 3: T( x ) - // We use zero slope tangent planes, since the concavity of the Taylor - // approximation cannot be easily established. - Node tplane = poly_approx_c; - - Node lem = nm->mkNode(concavity == 1 ? GEQ : LEQ, tf, tplane); - std::vector<Node> antec; - int mdir = regionToMonotonicityDir(k, region); - for (unsigned i = 0; i < 2; i++) - { - // Tangent plane is valid in the interval [c,u) if the slope of the - // function matches its concavity, and is valid in (l, c] otherwise. - Node use_bound = (mdir == concavity) == (i == 0) ? c : bounds[i]; - if (!use_bound.isNull()) - { - Node ant = nm->mkNode(i == 0 ? GEQ : LEQ, tf[0], use_bound); - antec.push_back(ant); - } - } - if (!antec.empty()) - { - Node antec_n = antec.size() == 1 ? antec[0] : nm->mkNode(AND, antec); - lem = nm->mkNode(IMPLIES, antec_n, lem); - } - Trace("nl-ext-tftp-debug2") - << "*** Tangent plane lemma (pre-rewrite): " << lem << std::endl; - lem = Rewriter::rewrite(lem); - Trace("nl-ext-tftp-lemma") << "*** Tangent plane lemma : " << lem - << std::endl; - Assert(d_model.computeAbstractModelValue(lem) == d_false); - // Figure 3 : line 9 - lemmas.push_back(lem); - } - else if (is_secant) - { - // bounds are the minimum and maximum previous secant points - // should not repeat secant points: secant lemmas should suffice to - // rule out previous assignment - Assert(std::find( - d_secant_points[tf][d].begin(), d_secant_points[tf][d].end(), c) - == d_secant_points[tf][d].end()); - // Insert into the (temporary) vector. We do not update this vector - // until we are sure this secant plane lemma has been processed. We do - // this by mapping the lemma to a side effect below. - std::vector<Node> spoints = d_secant_points[tf][d]; - spoints.push_back(c); - - // sort - SortNlModel smv; - smv.d_nlm = &d_model; - smv.d_isConcrete = true; - std::sort(spoints.begin(), spoints.end(), smv); - // get the resulting index of c - unsigned index = - std::find(spoints.begin(), spoints.end(), c) - spoints.begin(); - // bounds are the next closest upper/lower bound values - if (index > 0) - { - bounds[0] = spoints[index - 1]; - } - else - { - // otherwise, we use the lower boundary point for this concavity - // region - if (k == SINE) - { - Assert(!bounds[0].isNull()); - } - else if (k == EXPONENTIAL) - { - // pick c-1 - bounds[0] = Rewriter::rewrite(nm->mkNode(MINUS, c, d_one)); - } - } - if (index < spoints.size() - 1) - { - bounds[1] = spoints[index + 1]; - } - else - { - // otherwise, we use the upper boundary point for this concavity - // region - if (k == SINE) - { - Assert(!bounds[1].isNull()); - } - else if (k == EXPONENTIAL) - { - // pick c+1 - bounds[1] = Rewriter::rewrite(nm->mkNode(PLUS, c, d_one)); - } - } - Trace("nl-ext-tftp-debug2") << "...secant bounds are : " << bounds[0] - << " ... " << bounds[1] << std::endl; - - // the secant plane may be conjunction of 1-2 guarded inequalities - std::vector<Node> lemmaConj; - for (unsigned s = 0; s < 2; s++) - { - // compute secant plane - Assert(!poly_approx.isNull()); - Assert(!bounds[s].isNull()); - // take the model value of l or u (since may contain PI) - Node b = d_model.computeAbstractModelValue(bounds[s]); - Trace("nl-ext-tftp-debug2") << "...model value of bound " << bounds[s] - << " is " << b << std::endl; - Assert(b.isConst()); - if (c != b) - { - // Figure 3 : P(l), P(u), for s = 0,1 - Node poly_approx_b; - std::vector<Node> taylor_subs; - taylor_subs.push_back(b); - Assert(taylor_vars.size() == taylor_subs.size()); - poly_approx_b = poly_approx.substitute(taylor_vars.begin(), - taylor_vars.end(), - taylor_subs.begin(), - taylor_subs.end()); - // Figure 3: S_l( x ), S_u( x ) for s = 0,1 - Node splane; - Node rcoeff_n = Rewriter::rewrite(nm->mkNode(MINUS, b, c)); - Assert(rcoeff_n.isConst()); - Rational rcoeff = rcoeff_n.getConst<Rational>(); - Assert(rcoeff.sgn() != 0); - poly_approx_b = Rewriter::rewrite(poly_approx_b); - poly_approx_c = Rewriter::rewrite(poly_approx_c); - splane = nm->mkNode( - PLUS, - poly_approx_b, - nm->mkNode(MULT, - nm->mkNode(MINUS, poly_approx_b, poly_approx_c), - nm->mkConst(Rational(1) / rcoeff), - nm->mkNode(MINUS, tf[0], b))); - - Node lem = nm->mkNode(concavity == 1 ? LEQ : GEQ, tf, splane); - // With respect to Figure 3, this is slightly different. - // In particular, we chose b to be the model value of bounds[s], - // which is a constant although bounds[s] may not be (e.g. if it - // contains PI). - // To ensure that c...b does not cross an inflection point, - // we guard with the symbolic version of bounds[s]. - // This leads to lemmas e.g. of this form: - // ( c <= x <= PI/2 ) => ( sin(x) < ( P( b ) - P( c ) )*( x - - // b ) + P( b ) ) - // where b = (PI/2)^M, the current value of PI/2 in the model. - // This is sound since we are guarded by the symbolic - // representation of PI/2. - Node antec_n = - nm->mkNode(AND, - nm->mkNode(GEQ, tf[0], s == 0 ? bounds[s] : c), - nm->mkNode(LEQ, tf[0], s == 0 ? c : bounds[s])); - lem = nm->mkNode(IMPLIES, antec_n, lem); - Trace("nl-ext-tftp-debug2") - << "*** Secant plane lemma (pre-rewrite) : " << lem << std::endl; - lem = Rewriter::rewrite(lem); - Trace("nl-ext-tftp-lemma") << "*** Secant plane lemma : " << lem - << std::endl; - lemmaConj.push_back(lem); - Assert(d_model.computeAbstractModelValue(lem) == d_false); - } - } - // Figure 3 : line 22 - Assert(!lemmaConj.empty()); - Node lem = - lemmaConj.size() == 1 ? lemmaConj[0] : nm->mkNode(AND, lemmaConj); - lemmas.push_back(lem); - // The side effect says that if lem is added, then we should add the - // secant point c for (tf,d). - lemSE[lem].d_secantPoint.push_back(std::make_tuple(tf, d, c)); - } - return true; -} - -int NonlinearExtension::regionToMonotonicityDir(Kind k, int region) -{ - if (k == EXPONENTIAL) - { - if (region == 1) - { - return 1; - } - } - else if (k == SINE) - { - if (region == 1 || region == 4) - { - return -1; - } - else if (region == 2 || region == 3) - { - return 1; - } - } - return 0; -} - -int NonlinearExtension::regionToConcavity(Kind k, int region) -{ - if (k == EXPONENTIAL) - { - if (region == 1) - { - return 1; - } - } - else if (k == SINE) - { - if (region == 1 || region == 2) - { - return -1; - } - else if (region == 3 || region == 4) - { - return 1; - } - } - return 0; -} - -Node NonlinearExtension::regionToLowerBound(Kind k, int region) -{ - if (k == SINE) - { - if (region == 1) - { - return d_pi_2; - } - else if (region == 2) - { - return d_zero; - } - else if (region == 3) - { - return d_pi_neg_2; - } - else if (region == 4) - { - return d_pi_neg; - } - } - return Node::null(); -} - -Node NonlinearExtension::regionToUpperBound(Kind k, int region) -{ - if (k == SINE) - { - if (region == 1) - { - return d_pi; - } - else if (region == 2) - { - return d_pi_2; - } - else if (region == 3) - { - return d_zero; - } - else if (region == 4) - { - return d_pi_neg_2; - } - } - return Node::null(); -} - -Node NonlinearExtension::getDerivative(Node n, Node x) -{ - Assert(x.isVar()); - // only handle the cases of the taylor expansion of d - if (n.getKind() == EXPONENTIAL) - { - if (n[0] == x) - { - return n; - } - } - else if (n.getKind() == SINE) - { - if (n[0] == x) - { - Node na = NodeManager::currentNM()->mkNode(MINUS, d_pi_2, n[0]); - Node ret = NodeManager::currentNM()->mkNode(SINE, na); - ret = Rewriter::rewrite(ret); - return ret; - } - } - else if (n.getKind() == PLUS) - { - std::vector<Node> dchildren; - for (unsigned i = 0; i < n.getNumChildren(); i++) - { - // PLUS is flattened in rewriter, recursion depth is bounded by 1 - Node dc = getDerivative(n[i], x); - if (dc.isNull()) - { - return dc; - }else{ - dchildren.push_back(dc); - } - } - return NodeManager::currentNM()->mkNode(PLUS, dchildren); - } - else if (n.getKind() == MULT) - { - Assert(n[0].isConst()); - Node dc = getDerivative(n[1], x); - if (!dc.isNull()) - { - return NodeManager::currentNM()->mkNode(MULT, n[0], dc); - } - } - else if (n.getKind() == NONLINEAR_MULT) - { - unsigned xcount = 0; - std::vector<Node> children; - unsigned xindex = 0; - for (unsigned i = 0, size = n.getNumChildren(); i < size; i++) - { - if (n[i] == x) - { - xcount++; - xindex = i; - } - children.push_back(n[i]); - } - if (xcount == 0) - { - return d_zero; - } - else - { - children[xindex] = NodeManager::currentNM()->mkConst(Rational(xcount)); - } - return NodeManager::currentNM()->mkNode(MULT, children); - } - else if (n.isVar()) - { - return n == x ? d_one : d_zero; - } - else if (n.isConst()) - { - return d_zero; - } - Trace("nl-ext-debug") << "No derivative computed for " << n; - Trace("nl-ext-debug") << " for d/d{" << x << "}" << std::endl; - return Node::null(); -} - -std::pair<Node, Node> NonlinearExtension::getTaylor(Node fa, unsigned n) -{ - Assert(n > 0); - Node fac; // what term we cache for fa - if (fa[0] == d_zero) - { - // optimization : simpler to compute (x-fa[0])^n if we are centered around 0 - fac = fa; - } - else - { - // otherwise we use a standard factor a in (x-a)^n - fac = NodeManager::currentNM()->mkNode(fa.getKind(), d_taylor_real_fv_base); - } - Node taylor_rem; - Node taylor_sum; - // check if we have already computed this Taylor series - std::unordered_map<unsigned, Node>::iterator itt = d_taylor_sum[fac].find(n); - if (itt == d_taylor_sum[fac].end()) - { - Node i_exp_base; - if (fa[0] == d_zero) - { - i_exp_base = d_taylor_real_fv; - } - else - { - i_exp_base = Rewriter::rewrite(NodeManager::currentNM()->mkNode( - MINUS, d_taylor_real_fv, d_taylor_real_fv_base)); - } - Node i_derv = fac; - Node i_fact = d_one; - Node i_exp = d_one; - int i_derv_status = 0; - unsigned counter = 0; - std::vector<Node> sum; - do - { - counter++; - if (fa.getKind() == EXPONENTIAL) - { - // unchanged - } - else if (fa.getKind() == SINE) - { - if (i_derv_status % 2 == 1) - { - Node arg = NodeManager::currentNM()->mkNode( - PLUS, d_pi_2, d_taylor_real_fv_base); - i_derv = NodeManager::currentNM()->mkNode(SINE, arg); - } - else - { - i_derv = fa; - } - if (i_derv_status >= 2) - { - i_derv = NodeManager::currentNM()->mkNode(MINUS, d_zero, i_derv); - } - i_derv = Rewriter::rewrite(i_derv); - i_derv_status = i_derv_status == 3 ? 0 : i_derv_status + 1; - } - if (counter == (n + 1)) - { - TNode x = d_taylor_real_fv_base; - i_derv = i_derv.substitute(x, d_taylor_real_fv_base_rem); - } - Node curr = NodeManager::currentNM()->mkNode( - MULT, - NodeManager::currentNM()->mkNode(DIVISION, i_derv, i_fact), - i_exp); - if (counter == (n + 1)) - { - taylor_rem = curr; - } - else - { - sum.push_back(curr); - i_fact = Rewriter::rewrite(NodeManager::currentNM()->mkNode( - MULT, - NodeManager::currentNM()->mkConst(Rational(counter)), - i_fact)); - i_exp = Rewriter::rewrite( - NodeManager::currentNM()->mkNode(MULT, i_exp_base, i_exp)); - } - } while (counter <= n); - taylor_sum = - sum.size() == 1 ? sum[0] : NodeManager::currentNM()->mkNode(PLUS, sum); - - if (fac[0] != d_taylor_real_fv_base) - { - TNode x = d_taylor_real_fv_base; - taylor_sum = taylor_sum.substitute(x, fac[0]); - } - - // cache - d_taylor_sum[fac][n] = taylor_sum; - d_taylor_rem[fac][n] = taylor_rem; - } - else - { - taylor_sum = itt->second; - Assert(d_taylor_rem[fac].find(n) != d_taylor_rem[fac].end()); - taylor_rem = d_taylor_rem[fac][n]; - } - - // must substitute for the argument if we were using a different lookup - if (fa[0] != fac[0]) - { - TNode x = d_taylor_real_fv_base; - taylor_sum = taylor_sum.substitute(x, fa[0]); - } - return std::pair<Node, Node>(taylor_sum, taylor_rem); -} - -void NonlinearExtension::getPolynomialApproximationBounds( - Kind k, unsigned d, std::vector<Node>& pbounds) -{ - if (d_poly_bounds[k][d].empty()) - { - NodeManager* nm = NodeManager::currentNM(); - Node tft = nm->mkNode(k, d_zero); - // n is the Taylor degree we are currently considering - unsigned n = 2 * d; - // n must be even - std::pair<Node, Node> taylor = getTaylor(tft, n); - Trace("nl-ext-tftp-debug2") << "Taylor for " << k - << " is : " << taylor.first << std::endl; - Node taylor_sum = Rewriter::rewrite(taylor.first); - Trace("nl-ext-tftp-debug2") << "Taylor for " << k - << " is (post-rewrite) : " << taylor_sum - << std::endl; - Assert(taylor.second.getKind() == MULT); - Assert(taylor.second.getNumChildren() == 2); - Assert(taylor.second[0].getKind() == DIVISION); - Trace("nl-ext-tftp-debug2") << "Taylor remainder for " << k << " is " - << taylor.second << std::endl; - // ru is x^{n+1}/(n+1)! - Node ru = nm->mkNode(DIVISION, taylor.second[1], taylor.second[0][1]); - ru = Rewriter::rewrite(ru); - Trace("nl-ext-tftp-debug2") - << "Taylor remainder factor is (post-rewrite) : " << ru << std::endl; - if (k == EXPONENTIAL) - { - pbounds.push_back(taylor_sum); - pbounds.push_back(taylor_sum); - pbounds.push_back(Rewriter::rewrite( - nm->mkNode(MULT, taylor_sum, nm->mkNode(PLUS, d_one, ru)))); - pbounds.push_back(Rewriter::rewrite(nm->mkNode(PLUS, taylor_sum, ru))); - } - else - { - Assert(k == SINE); - Node l = Rewriter::rewrite(nm->mkNode(MINUS, taylor_sum, ru)); - Node u = Rewriter::rewrite(nm->mkNode(PLUS, taylor_sum, ru)); - pbounds.push_back(l); - pbounds.push_back(l); - pbounds.push_back(u); - pbounds.push_back(u); - } - Trace("nl-ext-tf-tplanes") << "Polynomial approximation for " << k - << " is: " << std::endl; - Trace("nl-ext-tf-tplanes") << " Lower (pos): " << pbounds[0] << std::endl; - Trace("nl-ext-tf-tplanes") << " Upper (pos): " << pbounds[2] << std::endl; - Trace("nl-ext-tf-tplanes") << " Lower (neg): " << pbounds[1] << std::endl; - Trace("nl-ext-tf-tplanes") << " Upper (neg): " << pbounds[3] << std::endl; - d_poly_bounds[k][d].insert( - d_poly_bounds[k][d].end(), pbounds.begin(), pbounds.end()); - } - else - { - pbounds.insert( - pbounds.end(), d_poly_bounds[k][d].begin(), d_poly_bounds[k][d].end()); - } -} - -void NonlinearExtension::getPolynomialApproximationBoundForArg( - Kind k, Node c, unsigned d, std::vector<Node>& pbounds) -{ - getPolynomialApproximationBounds(k, d, pbounds); - Assert(c.isConst()); - if (k == EXPONENTIAL && c.getConst<Rational>().sgn() == 1) - { - NodeManager* nm = NodeManager::currentNM(); - Node tft = nm->mkNode(k, d_zero); - bool success = false; - unsigned ds = d; - TNode ttrf = d_taylor_real_fv; - TNode tc = c; - do - { - success = true; - unsigned n = 2 * ds; - std::pair<Node, Node> taylor = getTaylor(tft, n); - // check that 1-c^{n+1}/(n+1)! > 0 - Node ru = nm->mkNode(DIVISION, taylor.second[1], taylor.second[0][1]); - Node rus = ru.substitute(ttrf, tc); - rus = Rewriter::rewrite(rus); - Assert(rus.isConst()); - if (rus.getConst<Rational>() > d_one.getConst<Rational>()) - { - success = false; - ds = ds + 1; - } - } while (!success); - if (ds > d) - { - Trace("nl-ext-exp-taylor") - << "*** Increase Taylor bound to " << ds << " > " << d << " for (" - << k << " " << c << ")" << std::endl; - // must use sound upper bound - std::vector<Node> pboundss; - getPolynomialApproximationBounds(k, ds, pboundss); - pbounds[2] = pboundss[2]; - } - } -} - -std::pair<Node, Node> NonlinearExtension::getTfModelBounds(Node tf, unsigned d) -{ - // compute the model value of the argument - Node c = d_model.computeAbstractModelValue(tf[0]); - Assert(c.isConst()); - int csign = c.getConst<Rational>().sgn(); - Kind k = tf.getKind(); - if (csign == 0) - { - // at zero, its trivial - if (k == SINE) - { - return std::pair<Node, Node>(d_zero, d_zero); - } - Assert(k == EXPONENTIAL); - return std::pair<Node, Node>(d_one, d_one); - } - bool isNeg = csign == -1; - - std::vector<Node> pbounds; - getPolynomialApproximationBoundForArg(k, c, d, pbounds); - - std::vector<Node> bounds; - TNode tfv = d_taylor_real_fv; - TNode tfs = tf[0]; - for (unsigned d2 = 0; d2 < 2; d2++) - { - int index = d2 == 0 ? (isNeg ? 1 : 0) : (isNeg ? 3 : 2); - Node pab = pbounds[index]; - if (!pab.isNull()) - { - // { x -> tf[0] } - pab = pab.substitute(tfv, tfs); - pab = Rewriter::rewrite(pab); - Node v_pab = d_model.computeAbstractModelValue(pab); - bounds.push_back(v_pab); - } - else - { - bounds.push_back(Node::null()); - } - } - return std::pair<Node, Node>(bounds[0], bounds[1]); -} } // namespace arith } // namespace theory diff --git a/src/theory/arith/nonlinear_extension.h b/src/theory/arith/nonlinear_extension.h index 19810730f..bfc713b12 100644 --- a/src/theory/arith/nonlinear_extension.h +++ b/src/theory/arith/nonlinear_extension.h @@ -37,6 +37,7 @@ #include "theory/arith/nl_lemma_utils.h" #include "theory/arith/nl_model.h" #include "theory/arith/theory_arith.h" +#include "theory/arith/transcendental_solver.h" #include "theory/uf/equality_engine.h" namespace CVC4 { @@ -253,7 +254,6 @@ class NonlinearExtension { static Node mkLit(Node a, Node b, int status, bool isAbsolute = false); static Node mkAbs(Node a); static Node mkValidPhase(Node a, Node pi); - static Node mkBounded( Node l, Node a, Node u ); Node mkMonomialRemFactor(Node n, const NodeMultiset& n_exp_rem) const; //---------------------------------------end term utilities @@ -449,21 +449,6 @@ class NonlinearExtension { Node d_two; Node d_true; Node d_false; - /** PI - * - * Note that PI is a (symbolic, non-constant) nullary operator. This is - * because its value cannot be computed exactly. We constraint PI to concrete - * lower and upper bounds stored in d_pi_bound below. - */ - Node d_pi; - /** PI/2 */ - Node d_pi_2; - /** -PI/2 */ - Node d_pi_neg_2; - /** -PI */ - Node d_pi_neg; - /** the concrete lower and upper bounds for PI */ - Node d_pi_bound[2]; // The theory of arithmetic containing this extension. TheoryArith& d_containing; @@ -488,6 +473,12 @@ class NonlinearExtension { * and for establishing when we are able to answer "SAT". */ NlModel d_model; + /** The transcendental extension object + * + * This is the subsolver responsible for running the procedure for + * transcendental functions. + */ + TranscendentalSolver d_trSlv; /** * The lemmas we computed during collectModelInfo. We store two vectors of * lemmas to be sent out on the output channel of TheoryArith. The first @@ -509,27 +500,6 @@ class NonlinearExtension { std::map<Node, unsigned> d_order_vars; std::vector<Node> d_order_points; - //transcendental functions - /** - * Some transcendental functions f(t) are "purified", e.g. we add - * t = y ^ f(t) = f(y) where y is a fresh variable. Those that are not - * purified we call "master terms". - * - * The maps below maintain a master/slave relationship over - * transcendental functions (SINE, EXPONENTIAL, PI), where above - * f(y) is the master of itself and of f(t). - * - * This is used for ensuring that the argument y of SINE we process is on the - * interval [-pi .. pi], and that exponentials are not applied to arguments - * that contain transcendental functions. - */ - std::map<Node, Node> d_trMaster; - std::map<Node, std::unordered_set<Node, NodeHashFunction>> d_trSlaves; - /** The transcendental functions we have done initial refinements on */ - std::map< Node, bool > d_tf_initial_refine; - - void mkPi(); - void getCurrentPiBounds( std::vector< Node >& lemmas ); private: //per last-call effort check @@ -552,25 +522,6 @@ class NonlinearExtension { std::map<Node, std::map<Node, std::map<Node, Node> > > d_ci_exp; std::map<Node, std::map<Node, std::map<Node, bool> > > d_ci_max; - /** - * Maps representives of a congruence class to the members of that class. - * - * In detail, a congruence class is a set of terms of the form - * { f(t1), ..., f(tn) } - * such that t1 = ... = tn in the current context. We choose an arbitrary - * term among these to be the repesentative of this congruence class. - * - * Moreover, notice we compute congruence classes only over terms that - * are transcendental function applications that are "master terms", - * see d_trMaster/d_trSlave. - */ - std::map<Node, std::vector<Node> > d_funcCongClass; - /** - * A list of all functions for each kind in { EXPONENTIAL, SINE, POW, PI } - * that are representives of their congruence class. - */ - std::map<Kind, std::vector<Node> > d_funcMap; - // factor skolems std::map< Node, Node > d_factor_skolem; Node getFactorSkolem( Node n, std::vector< Node >& lemmas ); @@ -578,96 +529,6 @@ class NonlinearExtension { // tangent plane bounds std::map< Node, std::map< Node, Node > > d_tangent_val_bound[4]; - /** secant points (sorted list) for transcendental functions - * - * This is used for tangent plane refinements for - * transcendental functions. This is the set - * "get-previous-secant-points" in "Satisfiability - * Modulo Transcendental Functions via Incremental - * Linearization" by Cimatti et al., CADE 2017, for - * each transcendental function application. We store this set for each - * Taylor degree. - */ - std::unordered_map<Node, - std::map<unsigned, std::vector<Node> >, - NodeHashFunction> - d_secant_points; - - /** get Taylor series of degree n for function fa centered around point fa[0]. - * - * Return value is ( P_{n,f(a)}( x ), R_{n+1,f(a)}( x ) ) where - * the first part of the pair is the Taylor series expansion : - * P_{n,f(a)}( x ) = sum_{i=0}^n (f^i( a )/i!)*(x-a)^i - * and the second part of the pair is the Taylor series remainder : - * R_{n+1,f(a),b}( x ) = (f^{n+1}( b )/(n+1)!)*(x-a)^{n+1} - * - * The above values are cached for each (f,n) for a fixed variable "a". - * To compute the Taylor series for fa, we compute the Taylor series - * for ( fa.getKind(), n ) then substitute { a -> fa[0] } if fa[0]!=0. - * We compute P_{n,f(0)}( x )/R_{n+1,f(0),b}( x ) for ( fa.getKind(), n ) - * if fa[0]=0. - * In the latter case, note we compute the exponential x^{n+1} - * instead of (x-a)^{n+1}, which can be done faster. - */ - std::pair<Node, Node> getTaylor(Node fa, unsigned n); - - /** internal variables used for constructing (cached) versions of the Taylor - * series above. - */ - Node d_taylor_real_fv; // x above - Node d_taylor_real_fv_base; // a above - Node d_taylor_real_fv_base_rem; // b above - - /** cache of sum and remainder terms for getTaylor */ - std::unordered_map<Node, std::unordered_map<unsigned, Node>, NodeHashFunction> - d_taylor_sum; - std::unordered_map<Node, std::unordered_map<unsigned, Node>, NodeHashFunction> - d_taylor_rem; - /** taylor degree - * - * Indicates that the degree of the polynomials in the Taylor approximation of - * all transcendental functions is 2*d_taylor_degree. This value is set - * initially to options::nlExtTfTaylorDegree() and may be incremented - * if the option options::nlExtTfIncPrecision() is enabled. - */ - unsigned d_taylor_degree; - /** polynomial approximation bounds - * - * This adds P_l+[x], P_l-[x], P_u+[x], P_u-[x] to pbounds, where x is - * d_taylor_real_fv. These are polynomial approximations of the Taylor series - * of <k>( 0 ) for degree 2*d where k is SINE or EXPONENTIAL. - * These correspond to P_l and P_u from Figure 3 of Cimatti et al., CADE 2017, - * for positive/negative (+/-) values of the argument of <k>( 0 ). - * - * Notice that for certain bounds (e.g. upper bounds for exponential), the - * Taylor approximation for a fixed degree is only sound up to a given - * upper bound on the argument. To obtain sound lower/upper bounds for a - * given <k>( c ), use the function below. - */ - void getPolynomialApproximationBounds(Kind k, - unsigned d, - std::vector<Node>& pbounds); - /** polynomial approximation bounds - * - * This computes polynomial approximations P_l+[x], P_l-[x], P_u+[x], P_u-[x] - * that are sound (lower, upper) bounds for <k>( c ). Notice that these - * polynomials may depend on c. In particular, for P_u+[x] for <k>( c ) where - * c>0, we return the P_u+[x] from the function above for the minimum degree - * d' >= d such that (1-c^{2*d'+1}/(2*d'+1)!) is positive. - */ - void getPolynomialApproximationBoundForArg(Kind k, - Node c, - unsigned d, - std::vector<Node>& pbounds); - /** cache of the above function */ - std::map<Kind, std::map<unsigned, std::vector<Node> > > d_poly_bounds; - /** get transcendental function model bounds - * - * This returns the current lower and upper bounds of transcendental - * function application tf based on Taylor of degree 2*d, which is dependent - * on the model value of its argument. - */ - std::pair<Node, Node> getTfModelBounds(Node tf, unsigned d); /** get approximate sqrt * * This approximates the square root of positive constant c. If this method @@ -680,70 +541,6 @@ class NonlinearExtension { */ bool getApproximateSqrt(Node c, Node& l, Node& u, unsigned iter = 15) const; - /** concavity region for transcendental functions - * - * This stores an integer that identifies an interval in - * which the current model value for an argument of an - * application of a transcendental function resides. - * - * For exp( x ): - * region #1 is -infty < x < infty - * For sin( x ): - * region #0 is pi < x < infty (this is an invalid region) - * region #1 is pi/2 < x <= pi - * region #2 is 0 < x <= pi/2 - * region #3 is -pi/2 < x <= 0 - * region #4 is -pi < x <= -pi/2 - * region #5 is -infty < x <= -pi (this is an invalid region) - * All regions not listed above, as well as regions 0 and 5 - * for SINE are "invalid". We only process applications - * of transcendental functions whose arguments have model - * values that reside in valid regions. - */ - std::unordered_map<Node, int, NodeHashFunction> d_tf_region; - /** get monotonicity direction - * - * Returns whether the slope is positive (+1) or negative(-1) - * in region of transcendental function with kind k. - * Returns 0 if region is invalid. - */ - int regionToMonotonicityDir(Kind k, int region); - /** get concavity - * - * Returns whether we are concave (+1) or convex (-1) - * in region of transcendental function with kind k, - * where region is defined above. - * Returns 0 if region is invalid. - */ - int regionToConcavity(Kind k, int region); - /** region to lower bound - * - * Returns the term corresponding to the lower - * bound of the region of transcendental function - * with kind k. Returns Node::null if the region - * is invalid, or there is no lower bound for the - * region. - */ - Node regionToLowerBound(Kind k, int region); - /** region to upper bound - * - * Returns the term corresponding to the upper - * bound of the region of transcendental function - * with kind k. Returns Node::null if the region - * is invalid, or there is no upper bound for the - * region. - */ - Node regionToUpperBound(Kind k, int region); - /** get derivative - * - * Returns d/dx n. Supports cases of n - * for transcendental functions applied to x, - * multiplication, addition, constants and variables. - * Returns Node::null() if derivative is an - * unhandled case. - */ - Node getDerivative(Node n, Node x); - private: //-------------------------------------------- lemma schemas /** check split zero @@ -873,122 +670,6 @@ class NonlinearExtension { */ std::vector<Node> checkTangentPlanes(); - /** check transcendental initial refine - * - * Returns a set of valid theory lemmas, based on - * simple facts about transcendental functions. - * This mostly follows the initial axioms described in - * Section 4 of "Satisfiability - * Modulo Transcendental Functions via Incremental - * Linearization" by Cimatti et al., CADE 2017. - * - * Examples: - * - * sin( x ) = -sin( -x ) - * ( PI > x > 0 ) => 0 < sin( x ) < 1 - * exp( x )>0 - * x<0 => exp( x )<1 - */ - std::vector<Node> checkTranscendentalInitialRefine(); - - /** check transcendental monotonic - * - * Returns a set of valid theory lemmas, based on a - * lemma scheme that ensures that applications - * of transcendental functions respect monotonicity. - * - * Examples: - * - * x > y => exp( x ) > exp( y ) - * PI/2 > x > y > 0 => sin( x ) > sin( y ) - * PI > x > y > PI/2 => sin( x ) < sin( y ) - */ - std::vector<Node> checkTranscendentalMonotonic(); - - /** check transcendental tangent planes - * - * Returns a set of valid theory lemmas, based on - * computing an "incremental linearization" of - * transcendental functions based on the model values - * of transcendental functions and their arguments. - * It is based on Figure 3 of "Satisfiability - * Modulo Transcendental Functions via Incremental - * Linearization" by Cimatti et al., CADE 2017. - * This schema is not terminating in general. - * It is not enabled by default, and can - * be enabled by --nl-ext-tf-tplanes. - * - * Example: - * - * Assume we have a term sin(y) where M( y ) = 1 where M is the current model. - * Note that: - * sin(1) ~= .841471 - * - * The Taylor series and remainder of sin(y) of degree 7 is - * P_{7,sin(0)}( x ) = x + (-1/6)*x^3 + (1/20)*x^5 - * R_{7,sin(0),b}( x ) = (-1/5040)*x^7 - * - * This gives us lower and upper bounds : - * P_u( x ) = P_{7,sin(0)}( x ) + R_{7,sin(0),b}( x ) - * ...where note P_u( 1 ) = 4243/5040 ~= .841865 - * P_l( x ) = P_{7,sin(0)}( x ) - R_{7,sin(0),b}( x ) - * ...where note P_l( 1 ) = 4241/5040 ~= .841468 - * - * Assume that M( sin(y) ) > P_u( 1 ). - * Since the concavity of sine in the region 0 < x < PI/2 is -1, - * we add a tangent plane refinement. - * The tangent plane at the point 1 in P_u is - * given by the formula: - * T( x ) = P_u( 1 ) + ((d/dx)(P_u(x)))( 1 )*( x - 1 ) - * We add the lemma: - * ( 0 < y < PI/2 ) => sin( y ) <= T( y ) - * which is: - * ( 0 < y < PI/2 ) => sin( y ) <= (391/720)*(y - 2737/1506) - * - * Assume that M( sin(y) ) < P_u( 1 ). - * Since the concavity of sine in the region 0 < x < PI/2 is -1, - * we add a secant plane refinement for some constants ( l, u ) - * such that 0 <= l < M( y ) < u <= PI/2. Assume we choose - * l = 0 and u = M( PI/2 ) = 150517/47912. - * The secant planes at point 1 for P_l - * are given by the formulas: - * S_l( x ) = (x-l)*(P_l( l )-P_l(c))/(l-1) + P_l( l ) - * S_u( x ) = (x-u)*(P_l( u )-P_l(c))/(u-1) + P_l( u ) - * We add the lemmas: - * ( 0 < y < 1 ) => sin( y ) >= S_l( y ) - * ( 1 < y < PI/2 ) => sin( y ) >= S_u( y ) - * which are: - * ( 0 < y < 1 ) => (sin y) >= 4251/5040*y - * ( 1 < y < PI/2 ) => (sin y) >= c1*(y+c2) - * where c1, c2 are rationals (for brevity, omitted here) - * such that c1 ~= .277 and c2 ~= 2.032. - * - * The argument lemSE is the "side effect" of the lemmas in the return - * value of this function (for details, see checkLastCall). - */ - std::vector<Node> checkTranscendentalTangentPlanes( - std::map<Node, NlLemmaSideEffect>& lemSE); - /** check transcendental function refinement for tf - * - * This method is called by the above method for each "master" - * transcendental function application that occurs in an assertion in the - * current context. For example, an application like sin(t) is not a master - * if we have introduced the constraints: - * t=y+2*pi*n ^ -pi <= y <= pi ^ sin(t) = sin(y). - * See d_trMaster/d_trSlaves for more detail. - * - * This runs Figure 3 of Cimatti et al., CADE 2017 for transcendental - * function application tf for Taylor degree d. It may add a secant or - * tangent plane lemma to lems and its side effect (if one exists) - * to lemSE. - * - * It returns false if the bounds are not precise enough to add a - * secant or tangent plane lemma. - */ - bool checkTfTangentPlanesFun(Node tf, - unsigned d, - std::vector<Node>& lems, - std::map<Node, NlLemmaSideEffect>& lemSE); //-------------------------------------------- end lemma schemas }; /* class NonlinearExtension */ diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index 8986e6894..2c748f188 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -19,6 +19,7 @@ #include "options/smt_options.h" #include "smt/smt_statistics_registry.h" +#include "theory/arith/arith_rewriter.h" #include "theory/arith/infer_bounds.h" #include "theory/arith/theory_arith_private.h" #include "theory/ext_theory.h" @@ -53,6 +54,11 @@ TheoryArith::~TheoryArith(){ delete d_internal; } +std::unique_ptr<TheoryRewriter> TheoryArith::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new ArithRewriter()); +} + void TheoryArith::preRegisterTerm(TNode n){ d_internal->preRegisterTerm(n); } diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index b39ab961f..92892d2ae 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -51,6 +51,8 @@ public: Valuation valuation, const LogicInfo& logicInfo); virtual ~TheoryArith(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + /** * Does non-context dependent setup for a node connected to a theory. */ diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 4e2a5bba1..bed59baf5 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -3943,15 +3943,38 @@ Node TheoryArithPrivate::branchIntegerVariable(ArithVar x) const { TNode var = d_partialModel.asNode(x); Integer floor_d = d.floor(); - //Node eq = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::EQUAL, var, mkRationalNode(floor_d+1))); - //Node diseq = eq.notNode(); - - Node ub = Rewriter::rewrite(NodeManager::currentNM()->mkNode(kind::LEQ, var, mkRationalNode(floor_d))); - Node lb = ub.notNode(); - + Node lem; + NodeManager* nm = NodeManager::currentNM(); + if (options::brabTest()) + { + Trace("integers") << "branch-round-and-bound enabled" << endl; + Integer ceil_d = d.ceiling(); + Rational f = r - floor_d; + // Multiply by -1 to get abs value. + Rational c = (r - ceil_d) * (-1); + Integer nearest = (c > f) ? floor_d : ceil_d; + + // Prioritize trying a simple rounding of the real solution first, + // it that fails, fall back on original branch and bound strategy. + Node ub = Rewriter::rewrite( + nm->mkNode(kind::LEQ, var, mkRationalNode(nearest - 1))); + Node lb = Rewriter::rewrite( + nm->mkNode(kind::GEQ, var, mkRationalNode(nearest + 1))); + lem = nm->mkNode(kind::OR, ub, lb); + Node eq = Rewriter::rewrite( + nm->mkNode(kind::EQUAL, var, mkRationalNode(nearest))); + Node literal = d_containing.getValuation().ensureLiteral(eq); + d_containing.getOutputChannel().requirePhase(literal, true); + lem = nm->mkNode(kind::OR, literal, lem); + } + else + { + Node ub = + Rewriter::rewrite(nm->mkNode(kind::LEQ, var, mkRationalNode(floor_d))); + Node lb = ub.notNode(); + lem = nm->mkNode(kind::OR, ub, lb); + } - //Node lem = NodeManager::currentNM()->mkNode(kind::OR, eq, diseq); - Node lem = NodeManager::currentNM()->mkNode(kind::OR, ub, lb); Trace("integers") << "integers: branch & bound: " << lem << endl; if(isSatLiteral(lem[0])) { Debug("integers") << " " << lem[0] << " == " << getSatValue(lem[0]) << endl; diff --git a/src/theory/arith/transcendental_solver.cpp b/src/theory/arith/transcendental_solver.cpp new file mode 100644 index 000000000..665accc0a --- /dev/null +++ b/src/theory/arith/transcendental_solver.cpp @@ -0,0 +1,1475 @@ +/********************* */ +/*! \file transcendental_solver.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of solver for handling transcendental functions. + **/ + +#include "theory/arith/transcendental_solver.h" + +#include <cmath> +#include <set> + +#include "expr/node_algorithm.h" +#include "expr/node_builder.h" +#include "options/arith_options.h" +#include "theory/arith/arith_msum.h" +#include "theory/arith/arith_utilities.h" +#include "theory/rewriter.h" + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { +namespace arith { + +TranscendentalSolver::TranscendentalSolver(NlModel& m) : d_model(m) +{ + NodeManager* nm = NodeManager::currentNM(); + d_true = nm->mkConst(true); + d_false = nm->mkConst(false); + d_zero = nm->mkConst(Rational(0)); + d_one = nm->mkConst(Rational(1)); + d_neg_one = nm->mkConst(Rational(-1)); + d_taylor_real_fv = nm->mkBoundVar("x", nm->realType()); + d_taylor_real_fv_base = nm->mkBoundVar("a", nm->realType()); + d_taylor_real_fv_base_rem = nm->mkBoundVar("b", nm->realType()); + d_taylor_degree = options::nlExtTfTaylorDegree(); +} + +TranscendentalSolver::~TranscendentalSolver() {} + +void TranscendentalSolver::initLastCall(const std::vector<Node>& assertions, + const std::vector<Node>& false_asserts, + const std::vector<Node>& xts, + std::vector<Node>& lems, + std::vector<Node>& lemsPp) +{ + d_funcCongClass.clear(); + d_funcMap.clear(); + d_tf_region.clear(); + + NodeManager* nm = NodeManager::currentNM(); + + // register the extended function terms + std::vector<Node> trNeedsMaster; + bool needPi = false; + // for computing congruence + std::map<Kind, ArgTrie> argTrie; + for (unsigned i = 0, xsize = xts.size(); i < xsize; i++) + { + Node a = xts[i]; + Kind ak = a.getKind(); + bool consider = true; + // if is an unpurified application of SINE, or it is a transcendental + // applied to a trancendental, purify. + if (isTranscendentalKind(ak)) + { + // if we've already computed master for a + if (d_trMaster.find(a) != d_trMaster.end()) + { + // a master has at least one slave + consider = (d_trSlaves.find(a) != d_trSlaves.end()); + } + else + { + if (ak == SINE) + { + // always not a master + consider = false; + } + else + { + for (const Node& ac : a) + { + if (isTranscendentalKind(ac.getKind())) + { + consider = false; + break; + } + } + } + if (!consider) + { + // wait to assign a master below + trNeedsMaster.push_back(a); + } + else + { + d_trMaster[a] = a; + d_trSlaves[a].insert(a); + } + } + } + if (ak == EXPONENTIAL || ak == SINE) + { + needPi = needPi || (ak == SINE); + // if we didn't indicate that it should be purified above + if (consider) + { + std::vector<Node> repList; + for (const Node& ac : a) + { + Node r = d_model.computeConcreteModelValue(ac); + repList.push_back(r); + } + Node aa = argTrie[ak].add(a, repList); + if (aa != a) + { + // apply congruence to pairs of terms that are disequal and congruent + Assert(aa.getNumChildren() == a.getNumChildren()); + Node mvaa = d_model.computeAbstractModelValue(a); + Node mvaaa = d_model.computeAbstractModelValue(aa); + if (mvaa != mvaaa) + { + std::vector<Node> exp; + for (unsigned j = 0, size = a.getNumChildren(); j < size; j++) + { + exp.push_back(a[j].eqNode(aa[j])); + } + Node expn = exp.size() == 1 ? exp[0] : nm->mkNode(AND, exp); + Node cong_lemma = nm->mkNode(OR, expn.negate(), a.eqNode(aa)); + lems.push_back(cong_lemma); + } + } + else + { + // new representative of congruence class + d_funcMap[ak].push_back(a); + } + // add to congruence class + d_funcCongClass[aa].push_back(a); + } + } + else if (ak == PI) + { + Assert(consider); + needPi = true; + d_funcMap[ak].push_back(a); + d_funcCongClass[a].push_back(a); + } + } + // initialize pi if necessary + if (needPi && d_pi.isNull()) + { + mkPi(); + getCurrentPiBounds(lems); + } + + if (!lems.empty()) + { + return; + } + + // process SINE phase shifting + for (const Node& a : trNeedsMaster) + { + // should not have processed this already + Assert(d_trMaster.find(a) == d_trMaster.end()); + Kind k = a.getKind(); + Assert(k == SINE || k == EXPONENTIAL); + Node y = + nm->mkSkolem("y", nm->realType(), "phase shifted trigonometric arg"); + Node new_a = nm->mkNode(k, y); + d_trSlaves[new_a].insert(new_a); + d_trSlaves[new_a].insert(a); + d_trMaster[a] = new_a; + d_trMaster[new_a] = new_a; + Node lem; + if (k == SINE) + { + Trace("nl-ext-tf") << "Basis sine : " << new_a << " for " << a + << std::endl; + Assert(!d_pi.isNull()); + Node shift = nm->mkSkolem("s", nm->integerType(), "number of shifts"); + // TODO : do not introduce shift here, instead needs model-based + // refinement for constant shifts (cvc4-projects #1284) + lem = nm->mkNode( + AND, + mkValidPhase(y, d_pi), + nm->mkNode( + ITE, + mkValidPhase(a[0], d_pi), + a[0].eqNode(y), + a[0].eqNode(nm->mkNode( + PLUS, + y, + nm->mkNode(MULT, nm->mkConst(Rational(2)), shift, d_pi)))), + new_a.eqNode(a)); + } + else + { + // do both equalities to ensure that new_a becomes a preregistered term + lem = nm->mkNode(AND, a.eqNode(new_a), a[0].eqNode(y)); + } + // note we must do preprocess on this lemma + Trace("nl-ext-lemma") << "NonlinearExtension::Lemma : purify : " << lem + << std::endl; + lemsPp.push_back(lem); + } + + if (Trace.isOn("nl-ext-mv")) + { + Trace("nl-ext-mv") << "Arguments of trancendental functions : " + << std::endl; + for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) + { + Kind k = tfl.first; + if (k == SINE || k == EXPONENTIAL) + { + for (const Node& tf : tfl.second) + { + Node v = tf[0]; + d_model.computeConcreteModelValue(v); + d_model.computeAbstractModelValue(v); + d_model.printModelValue("nl-ext-mv", v); + } + } + } + } +} + +bool TranscendentalSolver::preprocessAssertionsCheckModel( + std::vector<Node>& assertions) +{ + std::vector<Node> pvars; + std::vector<Node> psubs; + for (const std::pair<const Node, Node>& tb : d_trMaster) + { + pvars.push_back(tb.first); + psubs.push_back(tb.second); + } + + // initialize representation of assertions + std::vector<Node> passertions; + for (const Node& a : assertions) + + { + Node pa = a; + if (!pvars.empty()) + { + pa = arithSubstitute(pa, pvars, psubs); + pa = Rewriter::rewrite(pa); + } + if (!pa.isConst() || !pa.getConst<bool>()) + { + Trace("nl-ext-cm-assert") << "- assert : " << pa << std::endl; + passertions.push_back(pa); + } + } + // get model bounds for all transcendental functions + Trace("nl-ext-cm") << "----- Get bounds for transcendental functions..." + << std::endl; + for (std::pair<const Kind, std::vector<Node> >& tfs : d_funcMap) + { + Kind k = tfs.first; + for (const Node& tf : tfs.second) + { + Trace("nl-ext-cm") << "- Term: " << tf << std::endl; + bool success = true; + // tf is Figure 3 : tf( x ) + Node bl; + Node bu; + if (k == PI) + { + bl = d_pi_bound[0]; + bu = d_pi_bound[1]; + } + else + { + std::pair<Node, Node> bounds = getTfModelBounds(tf, d_taylor_degree); + bl = bounds.first; + bu = bounds.second; + if (bl != bu) + { + d_model.setUsedApproximate(); + } + } + if (!bl.isNull() && !bu.isNull()) + { + // for each function in the congruence classe + for (const Node& ctf : d_funcCongClass[tf]) + { + // each term in congruence classes should be master terms + Assert(d_trSlaves.find(ctf) != d_trSlaves.end()); + // we set the bounds for each slave of tf + for (const Node& stf : d_trSlaves[ctf]) + { + Trace("nl-ext-cm") << "...bound for " << stf << " : [" << bl << ", " + << bu << "]" << std::endl; + success = d_model.addCheckModelBound(stf, bl, bu); + } + } + } + else + { + Trace("nl-ext-cm") << "...no bound for " << tf << std::endl; + } + if (!success) + { + // a bound was conflicting + Trace("nl-ext-cm") << "...failed to set bound for " << tf << std::endl; + Trace("nl-ext-cm") << "-----" << std::endl; + return false; + } + } + } + // replace the assertions + assertions = passertions; + return true; +} + +void TranscendentalSolver::incrementTaylorDegree() { d_taylor_degree++; } +unsigned TranscendentalSolver::getTaylorDegree() const +{ + return d_taylor_degree; +} + +void TranscendentalSolver::processSideEffect(const NlLemmaSideEffect& se) +{ + for (const std::tuple<Node, unsigned, Node>& sp : se.d_secantPoint) + { + Node tf = std::get<0>(sp); + unsigned d = std::get<1>(sp); + Node c = std::get<2>(sp); + d_secant_points[tf][d].push_back(c); + } +} + +void TranscendentalSolver::mkPi() +{ + NodeManager* nm = NodeManager::currentNM(); + if (d_pi.isNull()) + { + d_pi = nm->mkNullaryOperator(nm->realType(), PI); + d_pi_2 = Rewriter::rewrite( + nm->mkNode(MULT, d_pi, nm->mkConst(Rational(1) / Rational(2)))); + d_pi_neg_2 = Rewriter::rewrite( + nm->mkNode(MULT, d_pi, nm->mkConst(Rational(-1) / Rational(2)))); + d_pi_neg = + Rewriter::rewrite(nm->mkNode(MULT, d_pi, nm->mkConst(Rational(-1)))); + // initialize bounds + d_pi_bound[0] = nm->mkConst(Rational(103993) / Rational(33102)); + d_pi_bound[1] = nm->mkConst(Rational(104348) / Rational(33215)); + } +} + +void TranscendentalSolver::getCurrentPiBounds(std::vector<Node>& lemmas) +{ + NodeManager* nm = NodeManager::currentNM(); + Node pi_lem = nm->mkNode(AND, + nm->mkNode(GEQ, d_pi, d_pi_bound[0]), + nm->mkNode(LEQ, d_pi, d_pi_bound[1])); + lemmas.push_back(pi_lem); +} + +std::vector<Node> TranscendentalSolver::checkTranscendentalInitialRefine() +{ + NodeManager* nm = NodeManager::currentNM(); + std::vector<Node> lemmas; + Trace("nl-ext") + << "Get initial refinement lemmas for transcendental functions..." + << std::endl; + for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) + { + Kind k = tfl.first; + for (const Node& t : tfl.second) + { + // initial refinements + if (d_tf_initial_refine.find(t) == d_tf_initial_refine.end()) + { + d_tf_initial_refine[t] = true; + Node lem; + if (k == SINE) + { + Node symn = nm->mkNode(SINE, nm->mkNode(MULT, d_neg_one, t[0])); + symn = Rewriter::rewrite(symn); + // Can assume it is its own master since phase is split over 0, + // hence -pi <= t[0] <= pi implies -pi <= -t[0] <= pi. + d_trMaster[symn] = symn; + d_trSlaves[symn].insert(symn); + Assert(d_trSlaves.find(t) != d_trSlaves.end()); + std::vector<Node> children; + + lem = nm->mkNode(AND, + // bounds + nm->mkNode(AND, + nm->mkNode(LEQ, t, d_one), + nm->mkNode(GEQ, t, d_neg_one)), + // symmetry + nm->mkNode(PLUS, t, symn).eqNode(d_zero), + // sign + nm->mkNode(EQUAL, + nm->mkNode(LT, t[0], d_zero), + nm->mkNode(LT, t, d_zero)), + // zero val + nm->mkNode(EQUAL, + nm->mkNode(GT, t[0], d_zero), + nm->mkNode(GT, t, d_zero))); + lem = nm->mkNode( + AND, + lem, + // zero tangent + nm->mkNode(AND, + nm->mkNode(IMPLIES, + nm->mkNode(GT, t[0], d_zero), + nm->mkNode(LT, t, t[0])), + nm->mkNode(IMPLIES, + nm->mkNode(LT, t[0], d_zero), + nm->mkNode(GT, t, t[0]))), + // pi tangent + nm->mkNode( + AND, + nm->mkNode(IMPLIES, + nm->mkNode(LT, t[0], d_pi), + nm->mkNode(LT, t, nm->mkNode(MINUS, d_pi, t[0]))), + nm->mkNode( + IMPLIES, + nm->mkNode(GT, t[0], d_pi_neg), + nm->mkNode(GT, t, nm->mkNode(MINUS, d_pi_neg, t[0]))))); + } + else if (k == EXPONENTIAL) + { + // ( exp(x) > 0 ) ^ ( x=0 <=> exp( x ) = 1 ) ^ ( x < 0 <=> exp( x ) < + // 1 ) ^ ( x <= 0 V exp( x ) > x + 1 ) + lem = nm->mkNode( + AND, + nm->mkNode(GT, t, d_zero), + nm->mkNode(EQUAL, t[0].eqNode(d_zero), t.eqNode(d_one)), + nm->mkNode(EQUAL, + nm->mkNode(LT, t[0], d_zero), + nm->mkNode(LT, t, d_one)), + nm->mkNode(OR, + nm->mkNode(LEQ, t[0], d_zero), + nm->mkNode(GT, t, nm->mkNode(PLUS, t[0], d_one)))); + } + if (!lem.isNull()) + { + lemmas.push_back(lem); + } + } + } + } + + return lemmas; +} + +std::vector<Node> TranscendentalSolver::checkTranscendentalMonotonic() +{ + std::vector<Node> lemmas; + Trace("nl-ext") << "Get monotonicity lemmas for transcendental functions..." + << std::endl; + + // sort arguments of all transcendentals + std::map<Kind, std::vector<Node> > sorted_tf_args; + std::map<Kind, std::map<Node, Node> > tf_arg_to_term; + + for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) + { + Kind k = tfl.first; + if (k == EXPONENTIAL || k == SINE) + { + for (const Node& tf : tfl.second) + { + Node a = tf[0]; + Node mvaa = d_model.computeAbstractModelValue(a); + if (mvaa.isConst()) + { + Trace("nl-ext-tf-mono-debug") << "...tf term : " << a << std::endl; + sorted_tf_args[k].push_back(a); + tf_arg_to_term[k][a] = tf; + } + } + } + } + + SortNlModel smv; + smv.d_nlm = &d_model; + // sort by concrete values + smv.d_isConcrete = true; + smv.d_reverse_order = true; + for (std::pair<const Kind, std::vector<Node> >& tfl : d_funcMap) + { + Kind k = tfl.first; + if (!sorted_tf_args[k].empty()) + { + std::sort(sorted_tf_args[k].begin(), sorted_tf_args[k].end(), smv); + Trace("nl-ext-tf-mono") << "Sorted transcendental function list for " << k + << " : " << std::endl; + for (unsigned i = 0; i < sorted_tf_args[k].size(); i++) + { + Node targ = sorted_tf_args[k][i]; + Node mvatarg = d_model.computeAbstractModelValue(targ); + Trace("nl-ext-tf-mono") + << " " << targ << " -> " << mvatarg << std::endl; + Node t = tf_arg_to_term[k][targ]; + Node mvat = d_model.computeAbstractModelValue(t); + Trace("nl-ext-tf-mono") << " f-val : " << mvat << std::endl; + } + std::vector<Node> mpoints; + std::vector<Node> mpoints_vals; + if (k == SINE) + { + mpoints.push_back(d_pi); + mpoints.push_back(d_pi_2); + mpoints.push_back(d_zero); + mpoints.push_back(d_pi_neg_2); + mpoints.push_back(d_pi_neg); + } + else if (k == EXPONENTIAL) + { + mpoints.push_back(Node::null()); + } + if (!mpoints.empty()) + { + // get model values for points + for (unsigned i = 0; i < mpoints.size(); i++) + { + Node mpv; + if (!mpoints[i].isNull()) + { + mpv = d_model.computeAbstractModelValue(mpoints[i]); + Assert(mpv.isConst()); + } + mpoints_vals.push_back(mpv); + } + + unsigned mdir_index = 0; + int monotonic_dir = -1; + Node mono_bounds[2]; + Node targ, targval, t, tval; + for (unsigned i = 0, size = sorted_tf_args[k].size(); i < size; i++) + { + Node sarg = sorted_tf_args[k][i]; + Node sargval = d_model.computeAbstractModelValue(sarg); + Assert(sargval.isConst()); + Node s = tf_arg_to_term[k][sarg]; + Node sval = d_model.computeAbstractModelValue(s); + Assert(sval.isConst()); + + // increment to the proper monotonicity region + bool increment = true; + while (increment && mdir_index < mpoints.size()) + { + increment = false; + if (mpoints[mdir_index].isNull()) + { + increment = true; + } + else + { + Node pval = mpoints_vals[mdir_index]; + Assert(pval.isConst()); + if (sargval.getConst<Rational>() < pval.getConst<Rational>()) + { + increment = true; + Trace("nl-ext-tf-mono") << "...increment at " << sarg + << " since model value is less than " + << mpoints[mdir_index] << std::endl; + } + } + if (increment) + { + tval = Node::null(); + mono_bounds[1] = mpoints[mdir_index]; + mdir_index++; + monotonic_dir = regionToMonotonicityDir(k, mdir_index); + if (mdir_index < mpoints.size()) + { + mono_bounds[0] = mpoints[mdir_index]; + } + else + { + mono_bounds[0] = Node::null(); + } + } + } + // store the concavity region + d_tf_region[s] = mdir_index; + Trace("nl-ext-concavity") << "Transcendental function " << s + << " is in region #" << mdir_index; + Trace("nl-ext-concavity") + << ", arg model value = " << sargval << std::endl; + + if (!tval.isNull()) + { + NodeManager* nm = NodeManager::currentNM(); + Node mono_lem; + if (monotonic_dir == 1 + && sval.getConst<Rational>() > tval.getConst<Rational>()) + { + mono_lem = nm->mkNode( + IMPLIES, nm->mkNode(GEQ, targ, sarg), nm->mkNode(GEQ, t, s)); + } + else if (monotonic_dir == -1 + && sval.getConst<Rational>() < tval.getConst<Rational>()) + { + mono_lem = nm->mkNode( + IMPLIES, nm->mkNode(LEQ, targ, sarg), nm->mkNode(LEQ, t, s)); + } + if (!mono_lem.isNull()) + { + if (!mono_bounds[0].isNull()) + { + Assert(!mono_bounds[1].isNull()); + mono_lem = nm->mkNode( + IMPLIES, + nm->mkNode(AND, + mkBounded(mono_bounds[0], targ, mono_bounds[1]), + mkBounded(mono_bounds[0], sarg, mono_bounds[1])), + mono_lem); + } + Trace("nl-ext-tf-mono") + << "Monotonicity lemma : " << mono_lem << std::endl; + lemmas.push_back(mono_lem); + } + } + // store the previous values + targ = sarg; + targval = sargval; + t = s; + tval = sval; + } + } + } + } + return lemmas; +} + +std::vector<Node> TranscendentalSolver::checkTranscendentalTangentPlanes( + std::map<Node, NlLemmaSideEffect>& lemSE) +{ + std::vector<Node> lemmas; + Trace("nl-ext") << "Get tangent plane lemmas for transcendental functions..." + << std::endl; + // this implements Figure 3 of "Satisfiaility Modulo Transcendental Functions + // via Incremental Linearization" by Cimatti et al + for (std::pair<const Kind, std::vector<Node> >& tfs : d_funcMap) + { + Kind k = tfs.first; + if (k == PI) + { + // We do not use Taylor approximation for PI currently. + // This is because the convergence is extremely slow, and hence an + // initial approximation is superior. + continue; + } + Trace("nl-ext-tftp-debug2") << "Taylor variables: " << std::endl; + Trace("nl-ext-tftp-debug2") + << " taylor_real_fv : " << d_taylor_real_fv << std::endl; + Trace("nl-ext-tftp-debug2") + << " taylor_real_fv_base : " << d_taylor_real_fv_base << std::endl; + Trace("nl-ext-tftp-debug2") + << " taylor_real_fv_base_rem : " << d_taylor_real_fv_base_rem + << std::endl; + Trace("nl-ext-tftp-debug2") << std::endl; + + // we substitute into the Taylor sum P_{n,f(0)}( x ) + + for (const Node& tf : tfs.second) + { + // tf is Figure 3 : tf( x ) + Trace("nl-ext-tftp") << "Compute tangent planes " << tf << std::endl; + // go until max degree is reached, or we don't meet bound criteria + for (unsigned d = 1; d <= d_taylor_degree; d++) + { + Trace("nl-ext-tftp") << "- run at degree " << d << "..." << std::endl; + unsigned prev = lemmas.size(); + if (checkTfTangentPlanesFun(tf, d, lemmas, lemSE)) + { + Trace("nl-ext-tftp") + << "...fail, #lemmas = " << (lemmas.size() - prev) << std::endl; + break; + } + else + { + Trace("nl-ext-tftp") << "...success" << std::endl; + } + } + } + } + + return lemmas; +} + +bool TranscendentalSolver::checkTfTangentPlanesFun( + Node tf, + unsigned d, + std::vector<Node>& lemmas, + std::map<Node, NlLemmaSideEffect>& lemSE) +{ + NodeManager* nm = NodeManager::currentNM(); + Kind k = tf.getKind(); + // this should only be run on master applications + Assert(d_trSlaves.find(tf) != d_trSlaves.end()); + + // Figure 3 : c + Node c = d_model.computeAbstractModelValue(tf[0]); + int csign = c.getConst<Rational>().sgn(); + if (csign == 0) + { + // no secant/tangent plane is necessary + return true; + } + Assert(csign == 1 || csign == -1); + + // Figure 3: P_l, P_u + // mapped to for signs of c + std::map<int, Node> poly_approx_bounds[2]; + std::vector<Node> pbounds; + getPolynomialApproximationBoundForArg(k, c, d, pbounds); + poly_approx_bounds[0][1] = pbounds[0]; + poly_approx_bounds[0][-1] = pbounds[1]; + poly_approx_bounds[1][1] = pbounds[2]; + poly_approx_bounds[1][-1] = pbounds[3]; + + // Figure 3 : v + Node v = d_model.computeAbstractModelValue(tf); + + // check value of tf + Trace("nl-ext-tftp-debug") << "Process tangent plane refinement for " << tf + << ", degree " << d << "..." << std::endl; + Trace("nl-ext-tftp-debug") << " value in model : " << v << std::endl; + Trace("nl-ext-tftp-debug") << " arg value in model : " << c << std::endl; + + std::vector<Node> taylor_vars; + taylor_vars.push_back(d_taylor_real_fv); + + // compute the concavity + int region = -1; + std::unordered_map<Node, int, NodeHashFunction>::iterator itr = + d_tf_region.find(tf); + if (itr != d_tf_region.end()) + { + region = itr->second; + Trace("nl-ext-tftp-debug") << " region is : " << region << std::endl; + } + // Figure 3 : conc + int concavity = regionToConcavity(k, itr->second); + Trace("nl-ext-tftp-debug") << " concavity is : " << concavity << std::endl; + if (concavity == 0) + { + // no secant/tangent plane is necessary + return true; + } + // bounds for which we are this concavity + // Figure 3: < l, u > + Node bounds[2]; + if (k == SINE) + { + bounds[0] = regionToLowerBound(k, region); + Assert(!bounds[0].isNull()); + bounds[1] = regionToUpperBound(k, region); + Assert(!bounds[1].isNull()); + } + + // Figure 3: P + Node poly_approx; + + // compute whether this is a tangent refinement or a secant refinement + bool is_tangent = false; + bool is_secant = false; + std::pair<Node, Node> mvb = getTfModelBounds(tf, d); + for (unsigned r = 0; r < 2; r++) + { + Node pab = poly_approx_bounds[r][csign]; + Node v_pab = r == 0 ? mvb.first : mvb.second; + if (!v_pab.isNull()) + { + Trace("nl-ext-tftp-debug2") + << "...model value of " << pab << " is " << v_pab << std::endl; + + Assert(v_pab.isConst()); + Node comp = nm->mkNode(r == 0 ? LT : GT, v, v_pab); + Trace("nl-ext-tftp-debug2") << "...compare : " << comp << std::endl; + Node compr = Rewriter::rewrite(comp); + Trace("nl-ext-tftp-debug2") << "...got : " << compr << std::endl; + if (compr == d_true) + { + // beyond the bounds + if (r == 0) + { + poly_approx = poly_approx_bounds[r][csign]; + is_tangent = concavity == 1; + is_secant = concavity == -1; + } + else + { + poly_approx = poly_approx_bounds[r][csign]; + is_tangent = concavity == -1; + is_secant = concavity == 1; + } + if (Trace.isOn("nl-ext-tftp")) + { + Trace("nl-ext-tftp") << "*** Outside boundary point ("; + Trace("nl-ext-tftp") << (r == 0 ? "low" : "high") << ") "; + printRationalApprox("nl-ext-tftp", v_pab); + Trace("nl-ext-tftp") << ", will refine..." << std::endl; + Trace("nl-ext-tftp") + << " poly_approx = " << poly_approx << std::endl; + Trace("nl-ext-tftp") + << " is_tangent = " << is_tangent << std::endl; + Trace("nl-ext-tftp") << " is_secant = " << is_secant << std::endl; + } + break; + } + else + { + Trace("nl-ext-tftp") + << " ...within " << (r == 0 ? "low" : "high") << " bound : "; + printRationalApprox("nl-ext-tftp", v_pab); + Trace("nl-ext-tftp") << std::endl; + } + } + } + + // Figure 3: P( c ) + Node poly_approx_c; + if (is_tangent || is_secant) + { + Assert(!poly_approx.isNull()); + std::vector<Node> taylor_subs; + taylor_subs.push_back(c); + Assert(taylor_vars.size() == taylor_subs.size()); + poly_approx_c = poly_approx.substitute(taylor_vars.begin(), + taylor_vars.end(), + taylor_subs.begin(), + taylor_subs.end()); + Trace("nl-ext-tftp-debug2") + << "...poly approximation at c is " << poly_approx_c << std::endl; + } + else + { + // we may want to continue getting better bounds + return false; + } + + if (is_tangent) + { + // compute tangent plane + // Figure 3: T( x ) + // We use zero slope tangent planes, since the concavity of the Taylor + // approximation cannot be easily established. + Node tplane = poly_approx_c; + + Node lem = nm->mkNode(concavity == 1 ? GEQ : LEQ, tf, tplane); + std::vector<Node> antec; + int mdir = regionToMonotonicityDir(k, region); + for (unsigned i = 0; i < 2; i++) + { + // Tangent plane is valid in the interval [c,u) if the slope of the + // function matches its concavity, and is valid in (l, c] otherwise. + Node use_bound = (mdir == concavity) == (i == 0) ? c : bounds[i]; + if (!use_bound.isNull()) + { + Node ant = nm->mkNode(i == 0 ? GEQ : LEQ, tf[0], use_bound); + antec.push_back(ant); + } + } + if (!antec.empty()) + { + Node antec_n = antec.size() == 1 ? antec[0] : nm->mkNode(AND, antec); + lem = nm->mkNode(IMPLIES, antec_n, lem); + } + Trace("nl-ext-tftp-debug2") + << "*** Tangent plane lemma (pre-rewrite): " << lem << std::endl; + lem = Rewriter::rewrite(lem); + Trace("nl-ext-tftp-lemma") + << "*** Tangent plane lemma : " << lem << std::endl; + Assert(d_model.computeAbstractModelValue(lem) == d_false); + // Figure 3 : line 9 + lemmas.push_back(lem); + } + else if (is_secant) + { + // bounds are the minimum and maximum previous secant points + // should not repeat secant points: secant lemmas should suffice to + // rule out previous assignment + Assert(std::find( + d_secant_points[tf][d].begin(), d_secant_points[tf][d].end(), c) + == d_secant_points[tf][d].end()); + // Insert into the (temporary) vector. We do not update this vector + // until we are sure this secant plane lemma has been processed. We do + // this by mapping the lemma to a side effect below. + std::vector<Node> spoints = d_secant_points[tf][d]; + spoints.push_back(c); + + // sort + SortNlModel smv; + smv.d_nlm = &d_model; + smv.d_isConcrete = true; + std::sort(spoints.begin(), spoints.end(), smv); + // get the resulting index of c + unsigned index = + std::find(spoints.begin(), spoints.end(), c) - spoints.begin(); + // bounds are the next closest upper/lower bound values + if (index > 0) + { + bounds[0] = spoints[index - 1]; + } + else + { + // otherwise, we use the lower boundary point for this concavity + // region + if (k == SINE) + { + Assert(!bounds[0].isNull()); + } + else if (k == EXPONENTIAL) + { + // pick c-1 + bounds[0] = Rewriter::rewrite(nm->mkNode(MINUS, c, d_one)); + } + } + if (index < spoints.size() - 1) + { + bounds[1] = spoints[index + 1]; + } + else + { + // otherwise, we use the upper boundary point for this concavity + // region + if (k == SINE) + { + Assert(!bounds[1].isNull()); + } + else if (k == EXPONENTIAL) + { + // pick c+1 + bounds[1] = Rewriter::rewrite(nm->mkNode(PLUS, c, d_one)); + } + } + Trace("nl-ext-tftp-debug2") << "...secant bounds are : " << bounds[0] + << " ... " << bounds[1] << std::endl; + + // the secant plane may be conjunction of 1-2 guarded inequalities + std::vector<Node> lemmaConj; + for (unsigned s = 0; s < 2; s++) + { + // compute secant plane + Assert(!poly_approx.isNull()); + Assert(!bounds[s].isNull()); + // take the model value of l or u (since may contain PI) + Node b = d_model.computeAbstractModelValue(bounds[s]); + Trace("nl-ext-tftp-debug2") << "...model value of bound " << bounds[s] + << " is " << b << std::endl; + Assert(b.isConst()); + if (c != b) + { + // Figure 3 : P(l), P(u), for s = 0,1 + Node poly_approx_b; + std::vector<Node> taylor_subs; + taylor_subs.push_back(b); + Assert(taylor_vars.size() == taylor_subs.size()); + poly_approx_b = poly_approx.substitute(taylor_vars.begin(), + taylor_vars.end(), + taylor_subs.begin(), + taylor_subs.end()); + // Figure 3: S_l( x ), S_u( x ) for s = 0,1 + Node splane; + Node rcoeff_n = Rewriter::rewrite(nm->mkNode(MINUS, b, c)); + Assert(rcoeff_n.isConst()); + Rational rcoeff = rcoeff_n.getConst<Rational>(); + Assert(rcoeff.sgn() != 0); + poly_approx_b = Rewriter::rewrite(poly_approx_b); + poly_approx_c = Rewriter::rewrite(poly_approx_c); + splane = nm->mkNode( + PLUS, + poly_approx_b, + nm->mkNode(MULT, + nm->mkNode(MINUS, poly_approx_b, poly_approx_c), + nm->mkConst(Rational(1) / rcoeff), + nm->mkNode(MINUS, tf[0], b))); + + Node lem = nm->mkNode(concavity == 1 ? LEQ : GEQ, tf, splane); + // With respect to Figure 3, this is slightly different. + // In particular, we chose b to be the model value of bounds[s], + // which is a constant although bounds[s] may not be (e.g. if it + // contains PI). + // To ensure that c...b does not cross an inflection point, + // we guard with the symbolic version of bounds[s]. + // This leads to lemmas e.g. of this form: + // ( c <= x <= PI/2 ) => ( sin(x) < ( P( b ) - P( c ) )*( x - + // b ) + P( b ) ) + // where b = (PI/2)^M, the current value of PI/2 in the model. + // This is sound since we are guarded by the symbolic + // representation of PI/2. + Node antec_n = + nm->mkNode(AND, + nm->mkNode(GEQ, tf[0], s == 0 ? bounds[s] : c), + nm->mkNode(LEQ, tf[0], s == 0 ? c : bounds[s])); + lem = nm->mkNode(IMPLIES, antec_n, lem); + Trace("nl-ext-tftp-debug2") + << "*** Secant plane lemma (pre-rewrite) : " << lem << std::endl; + lem = Rewriter::rewrite(lem); + Trace("nl-ext-tftp-lemma") + << "*** Secant plane lemma : " << lem << std::endl; + lemmaConj.push_back(lem); + Assert(d_model.computeAbstractModelValue(lem) == d_false); + } + } + // Figure 3 : line 22 + Assert(!lemmaConj.empty()); + Node lem = + lemmaConj.size() == 1 ? lemmaConj[0] : nm->mkNode(AND, lemmaConj); + lemmas.push_back(lem); + // The side effect says that if lem is added, then we should add the + // secant point c for (tf,d). + lemSE[lem].d_secantPoint.push_back(std::make_tuple(tf, d, c)); + } + return true; +} + +int TranscendentalSolver::regionToMonotonicityDir(Kind k, int region) +{ + if (k == EXPONENTIAL) + { + if (region == 1) + { + return 1; + } + } + else if (k == SINE) + { + if (region == 1 || region == 4) + { + return -1; + } + else if (region == 2 || region == 3) + { + return 1; + } + } + return 0; +} + +int TranscendentalSolver::regionToConcavity(Kind k, int region) +{ + if (k == EXPONENTIAL) + { + if (region == 1) + { + return 1; + } + } + else if (k == SINE) + { + if (region == 1 || region == 2) + { + return -1; + } + else if (region == 3 || region == 4) + { + return 1; + } + } + return 0; +} + +Node TranscendentalSolver::regionToLowerBound(Kind k, int region) +{ + if (k == SINE) + { + if (region == 1) + { + return d_pi_2; + } + else if (region == 2) + { + return d_zero; + } + else if (region == 3) + { + return d_pi_neg_2; + } + else if (region == 4) + { + return d_pi_neg; + } + } + return Node::null(); +} + +Node TranscendentalSolver::regionToUpperBound(Kind k, int region) +{ + if (k == SINE) + { + if (region == 1) + { + return d_pi; + } + else if (region == 2) + { + return d_pi_2; + } + else if (region == 3) + { + return d_zero; + } + else if (region == 4) + { + return d_pi_neg_2; + } + } + return Node::null(); +} + +Node TranscendentalSolver::getDerivative(Node n, Node x) +{ + NodeManager* nm = NodeManager::currentNM(); + Assert(x.isVar()); + // only handle the cases of the taylor expansion of d + if (n.getKind() == EXPONENTIAL) + { + if (n[0] == x) + { + return n; + } + } + else if (n.getKind() == SINE) + { + if (n[0] == x) + { + Node na = nm->mkNode(MINUS, d_pi_2, n[0]); + Node ret = nm->mkNode(SINE, na); + ret = Rewriter::rewrite(ret); + return ret; + } + } + else if (n.getKind() == PLUS) + { + std::vector<Node> dchildren; + for (unsigned i = 0; i < n.getNumChildren(); i++) + { + // PLUS is flattened in rewriter, recursion depth is bounded by 1 + Node dc = getDerivative(n[i], x); + if (dc.isNull()) + { + return dc; + } + else + { + dchildren.push_back(dc); + } + } + return nm->mkNode(PLUS, dchildren); + } + else if (n.getKind() == MULT) + { + Assert(n[0].isConst()); + Node dc = getDerivative(n[1], x); + if (!dc.isNull()) + { + return nm->mkNode(MULT, n[0], dc); + } + } + else if (n.getKind() == NONLINEAR_MULT) + { + unsigned xcount = 0; + std::vector<Node> children; + unsigned xindex = 0; + for (unsigned i = 0, size = n.getNumChildren(); i < size; i++) + { + if (n[i] == x) + { + xcount++; + xindex = i; + } + children.push_back(n[i]); + } + if (xcount == 0) + { + return d_zero; + } + else + { + children[xindex] = nm->mkConst(Rational(xcount)); + } + return nm->mkNode(MULT, children); + } + else if (n.isVar()) + { + return n == x ? d_one : d_zero; + } + else if (n.isConst()) + { + return d_zero; + } + Trace("nl-ext-debug") << "No derivative computed for " << n; + Trace("nl-ext-debug") << " for d/d{" << x << "}" << std::endl; + return Node::null(); +} + +std::pair<Node, Node> TranscendentalSolver::getTaylor(Node fa, unsigned n) +{ + NodeManager* nm = NodeManager::currentNM(); + Assert(n > 0); + Node fac; // what term we cache for fa + if (fa[0] == d_zero) + { + // optimization : simpler to compute (x-fa[0])^n if we are centered around 0 + fac = fa; + } + else + { + // otherwise we use a standard factor a in (x-a)^n + fac = nm->mkNode(fa.getKind(), d_taylor_real_fv_base); + } + Node taylor_rem; + Node taylor_sum; + // check if we have already computed this Taylor series + std::unordered_map<unsigned, Node>::iterator itt = d_taylor_sum[fac].find(n); + if (itt == d_taylor_sum[fac].end()) + { + Node i_exp_base; + if (fa[0] == d_zero) + { + i_exp_base = d_taylor_real_fv; + } + else + { + i_exp_base = Rewriter::rewrite( + nm->mkNode(MINUS, d_taylor_real_fv, d_taylor_real_fv_base)); + } + Node i_derv = fac; + Node i_fact = d_one; + Node i_exp = d_one; + int i_derv_status = 0; + unsigned counter = 0; + std::vector<Node> sum; + do + { + counter++; + if (fa.getKind() == EXPONENTIAL) + { + // unchanged + } + else if (fa.getKind() == SINE) + { + if (i_derv_status % 2 == 1) + { + Node arg = nm->mkNode(PLUS, d_pi_2, d_taylor_real_fv_base); + i_derv = nm->mkNode(SINE, arg); + } + else + { + i_derv = fa; + } + if (i_derv_status >= 2) + { + i_derv = nm->mkNode(MINUS, d_zero, i_derv); + } + i_derv = Rewriter::rewrite(i_derv); + i_derv_status = i_derv_status == 3 ? 0 : i_derv_status + 1; + } + if (counter == (n + 1)) + { + TNode x = d_taylor_real_fv_base; + i_derv = i_derv.substitute(x, d_taylor_real_fv_base_rem); + } + Node curr = nm->mkNode(MULT, nm->mkNode(DIVISION, i_derv, i_fact), i_exp); + if (counter == (n + 1)) + { + taylor_rem = curr; + } + else + { + sum.push_back(curr); + i_fact = Rewriter::rewrite( + nm->mkNode(MULT, nm->mkConst(Rational(counter)), i_fact)); + i_exp = Rewriter::rewrite(nm->mkNode(MULT, i_exp_base, i_exp)); + } + } while (counter <= n); + taylor_sum = sum.size() == 1 ? sum[0] : nm->mkNode(PLUS, sum); + + if (fac[0] != d_taylor_real_fv_base) + { + TNode x = d_taylor_real_fv_base; + taylor_sum = taylor_sum.substitute(x, fac[0]); + } + + // cache + d_taylor_sum[fac][n] = taylor_sum; + d_taylor_rem[fac][n] = taylor_rem; + } + else + { + taylor_sum = itt->second; + Assert(d_taylor_rem[fac].find(n) != d_taylor_rem[fac].end()); + taylor_rem = d_taylor_rem[fac][n]; + } + + // must substitute for the argument if we were using a different lookup + if (fa[0] != fac[0]) + { + TNode x = d_taylor_real_fv_base; + taylor_sum = taylor_sum.substitute(x, fa[0]); + } + return std::pair<Node, Node>(taylor_sum, taylor_rem); +} + +void TranscendentalSolver::getPolynomialApproximationBounds( + Kind k, unsigned d, std::vector<Node>& pbounds) +{ + if (d_poly_bounds[k][d].empty()) + { + NodeManager* nm = NodeManager::currentNM(); + Node tft = nm->mkNode(k, d_zero); + // n is the Taylor degree we are currently considering + unsigned n = 2 * d; + // n must be even + std::pair<Node, Node> taylor = getTaylor(tft, n); + Trace("nl-ext-tftp-debug2") + << "Taylor for " << k << " is : " << taylor.first << std::endl; + Node taylor_sum = Rewriter::rewrite(taylor.first); + Trace("nl-ext-tftp-debug2") + << "Taylor for " << k << " is (post-rewrite) : " << taylor_sum + << std::endl; + Assert(taylor.second.getKind() == MULT); + Assert(taylor.second.getNumChildren() == 2); + Assert(taylor.second[0].getKind() == DIVISION); + Trace("nl-ext-tftp-debug2") + << "Taylor remainder for " << k << " is " << taylor.second << std::endl; + // ru is x^{n+1}/(n+1)! + Node ru = nm->mkNode(DIVISION, taylor.second[1], taylor.second[0][1]); + ru = Rewriter::rewrite(ru); + Trace("nl-ext-tftp-debug2") + << "Taylor remainder factor is (post-rewrite) : " << ru << std::endl; + if (k == EXPONENTIAL) + { + pbounds.push_back(taylor_sum); + pbounds.push_back(taylor_sum); + pbounds.push_back(Rewriter::rewrite( + nm->mkNode(MULT, taylor_sum, nm->mkNode(PLUS, d_one, ru)))); + pbounds.push_back(Rewriter::rewrite(nm->mkNode(PLUS, taylor_sum, ru))); + } + else + { + Assert(k == SINE); + Node l = Rewriter::rewrite(nm->mkNode(MINUS, taylor_sum, ru)); + Node u = Rewriter::rewrite(nm->mkNode(PLUS, taylor_sum, ru)); + pbounds.push_back(l); + pbounds.push_back(l); + pbounds.push_back(u); + pbounds.push_back(u); + } + Trace("nl-ext-tf-tplanes") + << "Polynomial approximation for " << k << " is: " << std::endl; + Trace("nl-ext-tf-tplanes") << " Lower (pos): " << pbounds[0] << std::endl; + Trace("nl-ext-tf-tplanes") << " Upper (pos): " << pbounds[2] << std::endl; + Trace("nl-ext-tf-tplanes") << " Lower (neg): " << pbounds[1] << std::endl; + Trace("nl-ext-tf-tplanes") << " Upper (neg): " << pbounds[3] << std::endl; + d_poly_bounds[k][d].insert( + d_poly_bounds[k][d].end(), pbounds.begin(), pbounds.end()); + } + else + { + pbounds.insert( + pbounds.end(), d_poly_bounds[k][d].begin(), d_poly_bounds[k][d].end()); + } +} + +void TranscendentalSolver::getPolynomialApproximationBoundForArg( + Kind k, Node c, unsigned d, std::vector<Node>& pbounds) +{ + getPolynomialApproximationBounds(k, d, pbounds); + Assert(c.isConst()); + if (k == EXPONENTIAL && c.getConst<Rational>().sgn() == 1) + { + NodeManager* nm = NodeManager::currentNM(); + Node tft = nm->mkNode(k, d_zero); + bool success = false; + unsigned ds = d; + TNode ttrf = d_taylor_real_fv; + TNode tc = c; + do + { + success = true; + unsigned n = 2 * ds; + std::pair<Node, Node> taylor = getTaylor(tft, n); + // check that 1-c^{n+1}/(n+1)! > 0 + Node ru = nm->mkNode(DIVISION, taylor.second[1], taylor.second[0][1]); + Node rus = ru.substitute(ttrf, tc); + rus = Rewriter::rewrite(rus); + Assert(rus.isConst()); + if (rus.getConst<Rational>() > d_one.getConst<Rational>()) + { + success = false; + ds = ds + 1; + } + } while (!success); + if (ds > d) + { + Trace("nl-ext-exp-taylor") + << "*** Increase Taylor bound to " << ds << " > " << d << " for (" + << k << " " << c << ")" << std::endl; + // must use sound upper bound + std::vector<Node> pboundss; + getPolynomialApproximationBounds(k, ds, pboundss); + pbounds[2] = pboundss[2]; + } + } +} + +std::pair<Node, Node> TranscendentalSolver::getTfModelBounds(Node tf, + unsigned d) +{ + // compute the model value of the argument + Node c = d_model.computeAbstractModelValue(tf[0]); + Assert(c.isConst()); + int csign = c.getConst<Rational>().sgn(); + Kind k = tf.getKind(); + if (csign == 0) + { + // at zero, its trivial + if (k == SINE) + { + return std::pair<Node, Node>(d_zero, d_zero); + } + Assert(k == EXPONENTIAL); + return std::pair<Node, Node>(d_one, d_one); + } + bool isNeg = csign == -1; + + std::vector<Node> pbounds; + getPolynomialApproximationBoundForArg(k, c, d, pbounds); + + std::vector<Node> bounds; + TNode tfv = d_taylor_real_fv; + TNode tfs = tf[0]; + for (unsigned d2 = 0; d2 < 2; d2++) + { + int index = d2 == 0 ? (isNeg ? 1 : 0) : (isNeg ? 3 : 2); + Node pab = pbounds[index]; + if (!pab.isNull()) + { + // { x -> tf[0] } + pab = pab.substitute(tfv, tfs); + pab = Rewriter::rewrite(pab); + Node v_pab = d_model.computeAbstractModelValue(pab); + bounds.push_back(v_pab); + } + else + { + bounds.push_back(Node::null()); + } + } + return std::pair<Node, Node>(bounds[0], bounds[1]); +} + +Node TranscendentalSolver::mkValidPhase(Node a, Node pi) +{ + return mkBounded( + NodeManager::currentNM()->mkNode(MULT, mkRationalNode(-1), pi), a, pi); +} + +} // namespace arith +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/arith/transcendental_solver.h b/src/theory/arith/transcendental_solver.h new file mode 100644 index 000000000..8e539bbf0 --- /dev/null +++ b/src/theory/arith/transcendental_solver.h @@ -0,0 +1,419 @@ +/********************* */ +/*! \file transcendental_solver.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Solving for handling transcendental functions. + **/ + +#ifndef CVC4__THEORY__ARITH__TRANSCENDENTAL_SOLVER_H +#define CVC4__THEORY__ARITH__TRANSCENDENTAL_SOLVER_H + +#include <map> +#include <unordered_map> +#include <unordered_set> +#include <vector> + +#include "expr/node.h" +#include "theory/arith/nl_lemma_utils.h" +#include "theory/arith/nl_model.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +/** Transcendental solver class + * + * This class implements model-based refinement schemes + * for transcendental functions, described in: + * + * - "Satisfiability Modulo Transcendental + * Functions via Incremental Linearization" by Cimatti + * et al., CADE 2017. + * + * It's main functionality are methods that implement lemma schemas below, + * which return a set of lemmas that should be sent on the output channel. + */ +class TranscendentalSolver +{ + public: + TranscendentalSolver(NlModel& m); + ~TranscendentalSolver(); + + /** init last call + */ + void initLastCall(const std::vector<Node>& assertions, + const std::vector<Node>& false_asserts, + const std::vector<Node>& xts, + std::vector<Node>& lems, + std::vector<Node>& lemsPp); + /** increment taylor degree */ + void incrementTaylorDegree(); + /** get taylor degree */ + unsigned getTaylorDegree() const; + /** preprocess assertions check model + * + * This modifies the given assertions in preparation for running a call + * to check model. + * + * This method returns false if a bound for a transcendental function + * was conflicting. + */ + bool preprocessAssertionsCheckModel(std::vector<Node>& assertions); + /** Process side effect se */ + void processSideEffect(const NlLemmaSideEffect& se); + //-------------------------------------------- lemma schemas + /** check transcendental initial refine + * + * Returns a set of valid theory lemmas, based on + * simple facts about transcendental functions. + * This mostly follows the initial axioms described in + * Section 4 of "Satisfiability + * Modulo Transcendental Functions via Incremental + * Linearization" by Cimatti et al., CADE 2017. + * + * Examples: + * + * sin( x ) = -sin( -x ) + * ( PI > x > 0 ) => 0 < sin( x ) < 1 + * exp( x )>0 + * x<0 => exp( x )<1 + */ + std::vector<Node> checkTranscendentalInitialRefine(); + + /** check transcendental monotonic + * + * Returns a set of valid theory lemmas, based on a + * lemma scheme that ensures that applications + * of transcendental functions respect monotonicity. + * + * Examples: + * + * x > y => exp( x ) > exp( y ) + * PI/2 > x > y > 0 => sin( x ) > sin( y ) + * PI > x > y > PI/2 => sin( x ) < sin( y ) + */ + std::vector<Node> checkTranscendentalMonotonic(); + + /** check transcendental tangent planes + * + * Returns a set of valid theory lemmas, based on + * computing an "incremental linearization" of + * transcendental functions based on the model values + * of transcendental functions and their arguments. + * It is based on Figure 3 of "Satisfiability + * Modulo Transcendental Functions via Incremental + * Linearization" by Cimatti et al., CADE 2017. + * This schema is not terminating in general. + * It is not enabled by default, and can + * be enabled by --nl-ext-tf-tplanes. + * + * Example: + * + * Assume we have a term sin(y) where M( y ) = 1 where M is the current model. + * Note that: + * sin(1) ~= .841471 + * + * The Taylor series and remainder of sin(y) of degree 7 is + * P_{7,sin(0)}( x ) = x + (-1/6)*x^3 + (1/20)*x^5 + * R_{7,sin(0),b}( x ) = (-1/5040)*x^7 + * + * This gives us lower and upper bounds : + * P_u( x ) = P_{7,sin(0)}( x ) + R_{7,sin(0),b}( x ) + * ...where note P_u( 1 ) = 4243/5040 ~= .841865 + * P_l( x ) = P_{7,sin(0)}( x ) - R_{7,sin(0),b}( x ) + * ...where note P_l( 1 ) = 4241/5040 ~= .841468 + * + * Assume that M( sin(y) ) > P_u( 1 ). + * Since the concavity of sine in the region 0 < x < PI/2 is -1, + * we add a tangent plane refinement. + * The tangent plane at the point 1 in P_u is + * given by the formula: + * T( x ) = P_u( 1 ) + ((d/dx)(P_u(x)))( 1 )*( x - 1 ) + * We add the lemma: + * ( 0 < y < PI/2 ) => sin( y ) <= T( y ) + * which is: + * ( 0 < y < PI/2 ) => sin( y ) <= (391/720)*(y - 2737/1506) + * + * Assume that M( sin(y) ) < P_u( 1 ). + * Since the concavity of sine in the region 0 < x < PI/2 is -1, + * we add a secant plane refinement for some constants ( l, u ) + * such that 0 <= l < M( y ) < u <= PI/2. Assume we choose + * l = 0 and u = M( PI/2 ) = 150517/47912. + * The secant planes at point 1 for P_l + * are given by the formulas: + * S_l( x ) = (x-l)*(P_l( l )-P_l(c))/(l-1) + P_l( l ) + * S_u( x ) = (x-u)*(P_l( u )-P_l(c))/(u-1) + P_l( u ) + * We add the lemmas: + * ( 0 < y < 1 ) => sin( y ) >= S_l( y ) + * ( 1 < y < PI/2 ) => sin( y ) >= S_u( y ) + * which are: + * ( 0 < y < 1 ) => (sin y) >= 4251/5040*y + * ( 1 < y < PI/2 ) => (sin y) >= c1*(y+c2) + * where c1, c2 are rationals (for brevity, omitted here) + * such that c1 ~= .277 and c2 ~= 2.032. + * + * The argument lemSE is the "side effect" of the lemmas in the return + * value of this function (for details, see checkLastCall). + */ + std::vector<Node> checkTranscendentalTangentPlanes( + std::map<Node, NlLemmaSideEffect>& lemSE); + /** check transcendental function refinement for tf + * + * This method is called by the above method for each "master" + * transcendental function application that occurs in an assertion in the + * current context. For example, an application like sin(t) is not a master + * if we have introduced the constraints: + * t=y+2*pi*n ^ -pi <= y <= pi ^ sin(t) = sin(y). + * See d_trMaster/d_trSlaves for more detail. + * + * This runs Figure 3 of Cimatti et al., CADE 2017 for transcendental + * function application tf for Taylor degree d. It may add a secant or + * tangent plane lemma to lems and its side effect (if one exists) + * to lemSE. + * + * It returns false if the bounds are not precise enough to add a + * secant or tangent plane lemma. + */ + bool checkTfTangentPlanesFun(Node tf, + unsigned d, + std::vector<Node>& lems, + std::map<Node, NlLemmaSideEffect>& lemSE); + //-------------------------------------------- end lemma schemas + private: + /** polynomial approximation bounds + * + * This adds P_l+[x], P_l-[x], P_u+[x], P_u-[x] to pbounds, where x is + * d_taylor_real_fv. These are polynomial approximations of the Taylor series + * of <k>( 0 ) for degree 2*d where k is SINE or EXPONENTIAL. + * These correspond to P_l and P_u from Figure 3 of Cimatti et al., CADE 2017, + * for positive/negative (+/-) values of the argument of <k>( 0 ). + * + * Notice that for certain bounds (e.g. upper bounds for exponential), the + * Taylor approximation for a fixed degree is only sound up to a given + * upper bound on the argument. To obtain sound lower/upper bounds for a + * given <k>( c ), use the function below. + */ + void getPolynomialApproximationBounds(Kind k, + unsigned d, + std::vector<Node>& pbounds); + /** polynomial approximation bounds + * + * This computes polynomial approximations P_l+[x], P_l-[x], P_u+[x], P_u-[x] + * that are sound (lower, upper) bounds for <k>( c ). Notice that these + * polynomials may depend on c. In particular, for P_u+[x] for <k>( c ) where + * c>0, we return the P_u+[x] from the function above for the minimum degree + * d' >= d such that (1-c^{2*d'+1}/(2*d'+1)!) is positive. + */ + void getPolynomialApproximationBoundForArg(Kind k, + Node c, + unsigned d, + std::vector<Node>& pbounds); + /** get transcendental function model bounds + * + * This returns the current lower and upper bounds of transcendental + * function application tf based on Taylor of degree 2*d, which is dependent + * on the model value of its argument. + */ + std::pair<Node, Node> getTfModelBounds(Node tf, unsigned d); + /** get monotonicity direction + * + * Returns whether the slope is positive (+1) or negative(-1) + * in region of transcendental function with kind k. + * Returns 0 if region is invalid. + */ + int regionToMonotonicityDir(Kind k, int region); + /** get concavity + * + * Returns whether we are concave (+1) or convex (-1) + * in region of transcendental function with kind k, + * where region is defined above. + * Returns 0 if region is invalid. + */ + int regionToConcavity(Kind k, int region); + /** region to lower bound + * + * Returns the term corresponding to the lower + * bound of the region of transcendental function + * with kind k. Returns Node::null if the region + * is invalid, or there is no lower bound for the + * region. + */ + Node regionToLowerBound(Kind k, int region); + /** region to upper bound + * + * Returns the term corresponding to the upper + * bound of the region of transcendental function + * with kind k. Returns Node::null if the region + * is invalid, or there is no upper bound for the + * region. + */ + Node regionToUpperBound(Kind k, int region); + /** get derivative + * + * Returns d/dx n. Supports cases of n + * for transcendental functions applied to x, + * multiplication, addition, constants and variables. + * Returns Node::null() if derivative is an + * unhandled case. + */ + Node getDerivative(Node n, Node x); + + void mkPi(); + void getCurrentPiBounds(std::vector<Node>& lemmas); + /** Make the node -pi <= a <= pi */ + static Node mkValidPhase(Node a, Node pi); + + /** Reference to the non-linear model object */ + NlModel& d_model; + /** commonly used terms */ + Node d_zero; + Node d_one; + Node d_neg_one; + Node d_true; + Node d_false; + /** + * Some transcendental functions f(t) are "purified", e.g. we add + * t = y ^ f(t) = f(y) where y is a fresh variable. Those that are not + * purified we call "master terms". + * + * The maps below maintain a master/slave relationship over + * transcendental functions (SINE, EXPONENTIAL, PI), where above + * f(y) is the master of itself and of f(t). + * + * This is used for ensuring that the argument y of SINE we process is on the + * interval [-pi .. pi], and that exponentials are not applied to arguments + * that contain transcendental functions. + */ + std::map<Node, Node> d_trMaster; + std::map<Node, std::unordered_set<Node, NodeHashFunction>> d_trSlaves; + /** The transcendental functions we have done initial refinements on */ + std::map<Node, bool> d_tf_initial_refine; + + /** concavity region for transcendental functions + * + * This stores an integer that identifies an interval in + * which the current model value for an argument of an + * application of a transcendental function resides. + * + * For exp( x ): + * region #1 is -infty < x < infty + * For sin( x ): + * region #0 is pi < x < infty (this is an invalid region) + * region #1 is pi/2 < x <= pi + * region #2 is 0 < x <= pi/2 + * region #3 is -pi/2 < x <= 0 + * region #4 is -pi < x <= -pi/2 + * region #5 is -infty < x <= -pi (this is an invalid region) + * All regions not listed above, as well as regions 0 and 5 + * for SINE are "invalid". We only process applications + * of transcendental functions whose arguments have model + * values that reside in valid regions. + */ + std::unordered_map<Node, int, NodeHashFunction> d_tf_region; + /** cache of the above function */ + std::map<Kind, std::map<unsigned, std::vector<Node>>> d_poly_bounds; + + /** + * Maps representives of a congruence class to the members of that class. + * + * In detail, a congruence class is a set of terms of the form + * { f(t1), ..., f(tn) } + * such that t1 = ... = tn in the current context. We choose an arbitrary + * term among these to be the repesentative of this congruence class. + * + * Moreover, notice we compute congruence classes only over terms that + * are transcendental function applications that are "master terms", + * see d_trMaster/d_trSlave. + */ + std::map<Node, std::vector<Node>> d_funcCongClass; + /** + * A list of all functions for each kind in { EXPONENTIAL, SINE, POW, PI } + * that are representives of their congruence class. + */ + std::map<Kind, std::vector<Node>> d_funcMap; + + // tangent plane bounds + std::map<Node, std::map<Node, Node>> d_tangent_val_bound[4]; + + /** secant points (sorted list) for transcendental functions + * + * This is used for tangent plane refinements for + * transcendental functions. This is the set + * "get-previous-secant-points" in "Satisfiability + * Modulo Transcendental Functions via Incremental + * Linearization" by Cimatti et al., CADE 2017, for + * each transcendental function application. We store this set for each + * Taylor degree. + */ + std::unordered_map<Node, + std::map<unsigned, std::vector<Node>>, + NodeHashFunction> + d_secant_points; + + /** get Taylor series of degree n for function fa centered around point fa[0]. + * + * Return value is ( P_{n,f(a)}( x ), R_{n+1,f(a)}( x ) ) where + * the first part of the pair is the Taylor series expansion : + * P_{n,f(a)}( x ) = sum_{i=0}^n (f^i( a )/i!)*(x-a)^i + * and the second part of the pair is the Taylor series remainder : + * R_{n+1,f(a),b}( x ) = (f^{n+1}( b )/(n+1)!)*(x-a)^{n+1} + * + * The above values are cached for each (f,n) for a fixed variable "a". + * To compute the Taylor series for fa, we compute the Taylor series + * for ( fa.getKind(), n ) then substitute { a -> fa[0] } if fa[0]!=0. + * We compute P_{n,f(0)}( x )/R_{n+1,f(0),b}( x ) for ( fa.getKind(), n ) + * if fa[0]=0. + * In the latter case, note we compute the exponential x^{n+1} + * instead of (x-a)^{n+1}, which can be done faster. + */ + std::pair<Node, Node> getTaylor(Node fa, unsigned n); + + /** internal variables used for constructing (cached) versions of the Taylor + * series above. + */ + Node d_taylor_real_fv; // x above + Node d_taylor_real_fv_base; // a above + Node d_taylor_real_fv_base_rem; // b above + + /** cache of sum and remainder terms for getTaylor */ + std::unordered_map<Node, std::unordered_map<unsigned, Node>, NodeHashFunction> + d_taylor_sum; + std::unordered_map<Node, std::unordered_map<unsigned, Node>, NodeHashFunction> + d_taylor_rem; + /** taylor degree + * + * Indicates that the degree of the polynomials in the Taylor approximation of + * all transcendental functions is 2*d_taylor_degree. This value is set + * initially to options::nlExtTfTaylorDegree() and may be incremented + * if the option options::nlExtTfIncPrecision() is enabled. + */ + unsigned d_taylor_degree; + /** PI + * + * Note that PI is a (symbolic, non-constant) nullary operator. This is + * because its value cannot be computed exactly. We constraint PI to concrete + * lower and upper bounds stored in d_pi_bound below. + */ + Node d_pi; + /** PI/2 */ + Node d_pi_2; + /** -PI/2 */ + Node d_pi_neg_2; + /** -PI */ + Node d_pi_neg; + /** the concrete lower and upper bounds for PI */ + Node d_pi_bound[2]; +}; /* class TranscendentalSolver */ + +} // namespace arith +} // namespace theory +} // namespace CVC4 + +#endif /* CVC4__THEORY__ARITH__TRANSCENDENTAL_SOLVER_H */ diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index dcf82e6b4..787ae84e2 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -29,6 +29,7 @@ #include "smt/command.h" #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" +#include "theory/arrays/theory_arrays_rewriter.h" #include "theory/rewriter.h" #include "theory/theory_model.h" #include "theory/valuation.h" @@ -178,6 +179,11 @@ TheoryArrays::~TheoryArrays() { smtStatisticsRegistry()->unregisterStat(&d_numSetModelValConflicts); } +std::unique_ptr<TheoryRewriter> TheoryArrays::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheoryArraysRewriter()); +} + void TheoryArrays::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_equalityEngine.setMasterEqualityEngine(eq); } diff --git a/src/theory/arrays/theory_arrays.h b/src/theory/arrays/theory_arrays.h index 3d6d0692e..d1f912d95 100644 --- a/src/theory/arrays/theory_arrays.h +++ b/src/theory/arrays/theory_arrays.h @@ -144,6 +144,8 @@ class TheoryArrays : public Theory { std::string name = ""); ~TheoryArrays(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + void setMasterEqualityEngine(eq::EqualityEngine* eq) override; std::string identify() const override { return std::string("TheoryArrays"); } diff --git a/src/theory/booleans/theory_bool.cpp b/src/theory/booleans/theory_bool.cpp index 8fbe83951..e670121d1 100644 --- a/src/theory/booleans/theory_bool.cpp +++ b/src/theory/booleans/theory_bool.cpp @@ -14,15 +14,17 @@ ** The theory of booleans. **/ -#include "theory/theory.h" #include "theory/booleans/theory_bool.h" -#include "theory/booleans/circuit_propagator.h" -#include "theory/valuation.h" -#include "smt_util/boolean_simplification.h" -#include "theory/substitutions.h" -#include <vector> #include <stack> +#include <vector> + +#include "smt_util/boolean_simplification.h" +#include "theory/booleans/circuit_propagator.h" +#include "theory/booleans/theory_bool_rewriter.h" +#include "theory/substitutions.h" +#include "theory/theory.h" +#include "theory/valuation.h" #include "util/hash.h" using namespace std; @@ -31,6 +33,11 @@ namespace CVC4 { namespace theory { namespace booleans { +std::unique_ptr<TheoryRewriter> TheoryBool::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheoryBoolRewriter()); +} + Theory::PPAssertStatus TheoryBool::ppAssert(TNode in, SubstitutionMap& outSubstitutions) { if (in.getKind() == kind::CONST_BOOLEAN && !in.getConst<bool>()) { diff --git a/src/theory/booleans/theory_bool.h b/src/theory/booleans/theory_bool.h index abe024282..75e375ee6 100644 --- a/src/theory/booleans/theory_bool.h +++ b/src/theory/booleans/theory_bool.h @@ -33,6 +33,8 @@ public: : Theory(THEORY_BOOL, c, u, out, valuation, logicInfo) {} + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions) override; //void check(Effort); diff --git a/src/theory/builtin/theory_builtin.cpp b/src/theory/builtin/theory_builtin.cpp index b819b883d..8df5a8535 100644 --- a/src/theory/builtin/theory_builtin.cpp +++ b/src/theory/builtin/theory_builtin.cpp @@ -15,9 +15,11 @@ **/ #include "theory/builtin/theory_builtin.h" -#include "theory/valuation.h" + #include "expr/kind.h" +#include "theory/builtin/theory_builtin_rewriter.h" #include "theory/theory_model.h" +#include "theory/valuation.h" using namespace std; @@ -25,6 +27,33 @@ namespace CVC4 { namespace theory { namespace builtin { -}/* CVC4::theory::builtin namespace */ -}/* CVC4::theory */ -}/* CVC4 namespace */ +TheoryBuiltin::TheoryBuiltin(context::Context* c, + context::UserContext* u, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo) + : Theory(THEORY_BUILTIN, c, u, out, valuation, logicInfo) +{ +} + +std::unique_ptr<TheoryRewriter> TheoryBuiltin::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheoryBuiltinRewriter()); +} + +std::string TheoryBuiltin::identify() const +{ + return std::string("TheoryBuiltin"); +} + +void TheoryBuiltin::finishInit() +{ + // choice nodes are not evaluated in getModelValue + TheoryModel* theoryModel = d_valuation.getModel(); + Assert(theoryModel != nullptr); + theoryModel->setUnevaluatedKind(kind::CHOICE); +} + +} // namespace builtin +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/builtin/theory_builtin.h b/src/theory/builtin/theory_builtin.h index 8a7d1bf7b..d240f4f63 100644 --- a/src/theory/builtin/theory_builtin.h +++ b/src/theory/builtin/theory_builtin.h @@ -25,17 +25,25 @@ namespace CVC4 { namespace theory { namespace builtin { -class TheoryBuiltin : public Theory { -public: - TheoryBuiltin(context::Context* c, context::UserContext* u, - OutputChannel& out, Valuation valuation, - const LogicInfo& logicInfo) - : Theory(THEORY_BUILTIN, c, u, out, valuation, logicInfo) {} - std::string identify() const override { return std::string("TheoryBuiltin"); } -};/* class TheoryBuiltin */ - -}/* CVC4::theory::builtin namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ +class TheoryBuiltin : public Theory +{ + public: + TheoryBuiltin(context::Context* c, + context::UserContext* u, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo); + + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + + std::string identify() const override; + + /** finish initialization */ + void finishInit() override; +}; /* class TheoryBuiltin */ + +} // namespace builtin +} // namespace theory +} // namespace CVC4 #endif /* CVC4__THEORY__BUILTIN__THEORY_BUILTIN_H */ diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 94fc1e34c..27718b63f 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -112,6 +112,11 @@ TheoryBV::TheoryBV(context::Context* c, TheoryBV::~TheoryBV() {} +std::unique_ptr<TheoryRewriter> TheoryBV::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheoryBVRewriter()); +} + void TheoryBV::setMasterEqualityEngine(eq::EqualityEngine* eq) { if (options::bitblastMode() == options::BitblastMode::EAGER) { diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index 196535a19..ff1c9245a 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -72,6 +72,8 @@ public: ~TheoryBV(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + void setMasterEqualityEngine(eq::EqualityEngine* eq) override; void finishInit() override; diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index d202648f4..15220b9dc 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -25,6 +25,7 @@ #include "options/quantifiers_options.h" #include "options/smt_options.h" #include "options/theory_options.h" +#include "theory/datatypes/datatypes_rewriter.h" #include "theory/datatypes/theory_datatypes_type_rules.h" #include "theory/datatypes/theory_datatypes_utils.h" #include "theory/quantifiers_engine.h" @@ -85,6 +86,11 @@ TheoryDatatypes::~TheoryDatatypes() { } } +std::unique_ptr<TheoryRewriter> TheoryDatatypes::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new DatatypesRewriter()); +} + void TheoryDatatypes::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_equalityEngine.setMasterEqualityEngine(eq); } @@ -1306,15 +1312,7 @@ void TheoryDatatypes::collapseSelector( Node s, Node c ) { Trace("dt-collapse-sel") << "collapse selector : " << s << " " << c << std::endl; Node r; bool wrong = false; - Node use_s; - Node eq_exp; - if( options::dtRefIntro() ){ - eq_exp = d_true; - use_s = getTermSkolemFor( c ); - }else{ - eq_exp = c.eqNode( s[0] ); - use_s = s; - } + Node eq_exp = c.eqNode(s[0]); if( s.getKind()==kind::APPLY_SELECTOR_TOTAL ){ Node selector = s.getOperator(); size_t constructorIndex = utils::indexOf(c.getOperator()); @@ -1322,14 +1320,7 @@ void TheoryDatatypes::collapseSelector( Node s, Node c ) { const DTypeConstructor& dtc = dt[constructorIndex]; int selectorIndex = dtc.getSelectorIndexInternal(selector); wrong = selectorIndex<0; - - //if( wrong ){ - // return; - //} r = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, s.getOperator(), c ); - if( options::dtRefIntro() ){ - use_s = NodeManager::currentNM()->mkNode( kind::APPLY_SELECTOR_TOTAL, s.getOperator(), use_s ); - } } if( !r.isNull() ){ Node rr = Rewriter::rewrite( r ); @@ -1341,14 +1332,10 @@ void TheoryDatatypes::collapseSelector( Node s, Node c ) { std::map< Node, Node > visited; rrs = removeUninterpretedConstants( rr, visited ); } - if( use_s!=rrs ){ - Node eq = use_s.eqNode( rrs ); - Node peq; - if( options::dtRefIntro() ){ - peq = d_true; - }else{ - peq = c.eqNode(s[0]); - } + if (s != rrs) + { + Node eq = s.eqNode(rrs); + Node peq = c.eqNode(s[0]); Trace("datatypes-infer") << "DtInfer : collapse sel"; //Trace("datatypes-infer") << ( wrong ? " wrong" : ""); Trace("datatypes-infer") << " : " << eq << " by " << peq << std::endl; diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index a878647bc..7ccd04f39 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -271,6 +271,8 @@ private: const LogicInfo& logicInfo); ~TheoryDatatypes(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + void setMasterEqualityEngine(eq::EqualityEngine* eq) override; /** propagate */ diff --git a/src/theory/evaluator.cpp b/src/theory/evaluator.cpp index b827912d5..646f903f5 100644 --- a/src/theory/evaluator.cpp +++ b/src/theory/evaluator.cpp @@ -626,8 +626,7 @@ EvalResult Evaluator::evalInternal( const String& s = results[currNode[0]].d_str; if (s.size() == 1) { - results[currNode] = EvalResult( - Rational(String::convertUnsignedIntToCode(s.getVec()[0]))); + results[currNode] = EvalResult(Rational(s.getVec()[0])); } else { diff --git a/src/theory/evaluator.h b/src/theory/evaluator.h index 58e179fbe..b9b15c6c6 100644 --- a/src/theory/evaluator.h +++ b/src/theory/evaluator.h @@ -27,7 +27,7 @@ #include "expr/node.h" #include "util/bitvector.h" #include "util/rational.h" -#include "util/regexp.h" +#include "util/string.h" namespace CVC4 { namespace theory { diff --git a/src/theory/fp/theory_fp.cpp b/src/theory/fp/theory_fp.cpp index 2632a6f38..5ab285766 100644 --- a/src/theory/fp/theory_fp.cpp +++ b/src/theory/fp/theory_fp.cpp @@ -15,18 +15,18 @@ ** \todo document this file **/ - -#include "options/fp_options.h" -#include "theory/rewriter.h" -#include "theory/theory_model.h" #include "theory/fp/theory_fp.h" - #include <set> #include <stack> #include <unordered_set> #include <vector> +#include "options/fp_options.h" +#include "theory/fp/theory_fp_rewriter.h" +#include "theory/rewriter.h" +#include "theory/theory_model.h" + using namespace std; namespace CVC4 { @@ -177,6 +177,11 @@ TheoryFp::TheoryFp(context::Context *c, } /* TheoryFp::TheoryFp() */ +std::unique_ptr<TheoryRewriter> TheoryFp::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheoryFpRewriter()); +} + Node TheoryFp::minUF(Node node) { Assert(node.getKind() == kind::FLOATINGPOINT_MIN); TypeNode t(node.getType()); diff --git a/src/theory/fp/theory_fp.h b/src/theory/fp/theory_fp.h index ad093f924..802a70435 100644 --- a/src/theory/fp/theory_fp.h +++ b/src/theory/fp/theory_fp.h @@ -38,6 +38,8 @@ class TheoryFp : public Theory { TheoryFp(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + Node expandDefinition(LogicRequest& lr, Node node) override; void preRegisterTerm(TNode node) override; diff --git a/src/theory/idl/idl_assertion.cpp b/src/theory/idl/idl_assertion.cpp deleted file mode 100644 index 1e3905537..000000000 --- a/src/theory/idl/idl_assertion.cpp +++ /dev/null @@ -1,213 +0,0 @@ -/********************* */ -/*! \file idl_assertion.cpp - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "theory/idl/idl_assertion.h" - -using namespace CVC4; -using namespace theory; -using namespace idl; - -IDLAssertion::IDLAssertion() -: d_op(kind::LAST_KIND) -{} - -IDLAssertion::IDLAssertion(TNode node) { - bool ok = parse(node, 1, false); - if (!ok) { - d_x = d_y = TNode::null(); - } else { - if (d_op == kind::GT) { - // Turn GT into LT x - y > c is the same as y - x < -c - std::swap(d_x, d_y); - d_c = -d_c; - d_op = kind::LT; - } - if (d_op == kind::GEQ) { - // Turn GT into LT x - y >= c is the same as y - x <= -c - std::swap(d_x, d_y); - d_c = -d_c; - d_op = kind::LEQ; - } - if (d_op == kind::LT) { - // Turn strict into non-strict x - y < c is the same as x - y <= c-1 - d_c = d_c - 1; - d_op = kind::LEQ; - } - } - d_original = node; -} - -IDLAssertion::IDLAssertion(const IDLAssertion& other) -: d_x(other.d_x) -, d_y(other.d_y) -, d_op(other.d_op) -, d_c(other.d_c) -, d_original(other.d_original) -{} - -bool IDLAssertion::propagate(IDLModel& model) const { - Debug("theory::idl::model") << model << std::endl; - Assert(ok()); - // Should be d_x - d_y <= d_c, or d_x - d_c <= d_y - Integer x_value = model.getValue(d_x); - Integer y_value = model.getValue(d_y); - if (x_value - y_value > d_c) { - model.setValue(d_y, x_value - d_c, IDLReason(d_x, d_original)); - Debug("theory::idl::model") << model << std::endl; - return true; - } else { - return false; - } -} - -void IDLAssertion::toStream(std::ostream& out) const { - out << "IDL[" << d_x << " - " << d_y << " " << d_op << " " << d_c << "]"; -} - -/** Negates the given arithmetic kind */ -static Kind negateOp(Kind op) { - switch (op) { - case kind::LT: - // not (a < b) = (a >= b) - return kind::GEQ; - case kind::LEQ: - // not (a <= b) = (a > b) - return kind::GT; - case kind::GT: - // not (a > b) = (a <= b) - return kind::LEQ; - case kind::GEQ: - // not (a >= b) = (a < b) - return kind::LT; - case kind::EQUAL: - // not (a = b) = (a != b) - return kind::DISTINCT; - case kind::DISTINCT: - // not (a != b) = (a = b) - return kind::EQUAL; - default: - Unreachable(); - break; - } - return kind::LAST_KIND; -} - -bool IDLAssertion::parse(TNode node, int c, bool negated) { - - // Only unit coefficients allowed - if (c != 1 && c != -1) { - return false; - } - - // Assume we're ok - bool ok = true; - - // The kind of the node - switch(node.getKind()) { - - case kind::NOT: - // We parse the negation - ok = parse(node[0], c, true); - // Setup the kind - if (ok) { - d_op = negateOp(d_op); - } - break; - - case kind::EQUAL: - case kind::LT: - case kind::LEQ: - case kind::GT: - case kind::GEQ: { - // All relation operators are parsed on both sides - d_op = node.getKind(); - ok = parse(node[0], c, negated); - if (ok) { - ok = parse(node[1],-c, negated); - } - break; - } - - case kind::CONST_RATIONAL: { - // Constants - Rational m = node.getConst<Rational>(); - if (m.isIntegral()) { - d_c += m.getNumerator() * (-c); - } else { - ok = false; - } - break; - } - case kind::MULT: { - // Only unit multiplication of variables - if (node.getNumChildren() == 2 && node[0].isConst()) { - Rational a = node[0].getConst<Rational>(); - if (a == 1 || a == -1) { - ok = parse(node[1], c * a.sgn(), negated); - } else { - ok = false; - } - } else { - ok = false; - } - break; - } - - case kind::PLUS: { - for(unsigned i = 0; i < node.getNumChildren(); ++i) { - ok = parse(node[i], c, negated); - if(!ok) { - break; - } - } - break; - } - - case kind::MINUS: { - ok = parse(node[0], c, negated); - if (ok) { - ok = parse(node[1], -c, negated); - } - break; - } - - case kind::UMINUS: { - ok = parse(node[0], -c, negated); - break; - } - - default: { - if (c > 0) { - if (d_x.isNull()) { - d_x = node; - } else { - ok = false; - } - } else { - if (d_y.isNull()) { - d_y = node; - } else { - ok = false; - } - } - break; - } - } // End case - - // Difference logic OK - return ok; -} diff --git a/src/theory/idl/idl_assertion.h b/src/theory/idl/idl_assertion.h deleted file mode 100644 index e24fbfc67..000000000 --- a/src/theory/idl/idl_assertion.h +++ /dev/null @@ -1,91 +0,0 @@ -/********************* */ -/*! \file idl_assertion.h - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#pragma once - -#include "theory/idl/idl_model.h" - -namespace CVC4 { -namespace theory { -namespace idl { - -/** - * An internal representation of the IDL assertions. Each IDL assertions is - * of the form (x - y op c) where op is one of (<=, =, !=). IDL assertion - * can be constructed from an expression. - */ -class IDLAssertion { - - /** The positive variable */ - TNode d_x; - /** The negative variable */ - TNode d_y; - /** The relation */ - Kind d_op; - /** The RHS constant */ - Integer d_c; - - /** Original assertion we got this one from */ - TNode d_original; - - /** Parses the given node into an assertion, and return true if OK. */ - bool parse(TNode node, int c = 1, bool negated = false); - -public: - - /** Null assertion */ - IDLAssertion(); - /** Create the assertion from given node */ - IDLAssertion(TNode node); - /** Copy constructor */ - IDLAssertion(const IDLAssertion& other); - - TNode getX() const { return d_x; } - TNode getY() const { return d_y; } - Kind getOp() const { return d_op;} - Integer getC() const { return d_c; } - - /** - * Propagate the constraint using the model. For example, if the constraint - * is of the form x - y <= -1, and the value of x in the model is 0, then - * - * (x - y <= -1) and (x = 0) implies y >= x + 1 = 1 - * - * If the value of y is less then 1, is is set to 1 and true is returned. If - * the value of y is 1 or more, than false is return. - * - * @return true if value of y was updated - */ - bool propagate(IDLModel& model) const; - - /** Is this constraint proper */ - bool ok() const { - return !d_x.isNull() || !d_y.isNull(); - } - - /** Output to the stream */ - void toStream(std::ostream& out) const; -}; - -inline std::ostream& operator << (std::ostream& out, const IDLAssertion& assertion) { - assertion.toStream(out); - return out; -} - -} -} -} diff --git a/src/theory/idl/idl_assertion_db.cpp b/src/theory/idl/idl_assertion_db.cpp deleted file mode 100644 index 865d8b4f5..000000000 --- a/src/theory/idl/idl_assertion_db.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/********************* */ -/*! \file idl_assertion_db.cpp - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "theory/idl/idl_assertion_db.h" - -using namespace CVC4; -using namespace theory; -using namespace idl; - -IDLAssertionDB::IDLAssertionDB(context::Context* c) -: d_assertions(c) -, d_variableLists(c) -{} - -void IDLAssertionDB::add(const IDLAssertion& assertion, TNode var) { - // Is there a list for the variable already? - unsigned previous = -1; - var_to_unsigned_map::iterator find = d_variableLists.find(var); - if (find != d_variableLists.end()) { - previous = (*find).second; - } - // Add to the DB - d_variableLists[var] = d_assertions.size(); - d_assertions.push_back(IDLAssertionListElement(assertion, previous)); -} - -IDLAssertionDB::iterator::iterator(IDLAssertionDB& db, TNode var) -: d_db(db) -, d_current(-1) -{ - var_to_unsigned_map::const_iterator find = d_db.d_variableLists.find(var); - if (find != d_db.d_variableLists.end()) { - d_current = (*find).second; - } -} - -void IDLAssertionDB::iterator::next() { - if (d_current != (unsigned)(-1)) { - d_current = d_db.d_assertions[d_current].d_previous; - } -} - -IDLAssertion IDLAssertionDB::iterator::get() const { - return d_db.d_assertions[d_current].d_assertion; -} diff --git a/src/theory/idl/idl_assertion_db.h b/src/theory/idl/idl_assertion_db.h deleted file mode 100644 index ac87282d9..000000000 --- a/src/theory/idl/idl_assertion_db.h +++ /dev/null @@ -1,86 +0,0 @@ -/********************* */ -/*! \file idl_assertion_db.h - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic, Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#pragma once - -#include "theory/idl/idl_assertion.h" -#include "context/cdlist.h" - -namespace CVC4 { -namespace theory { -namespace idl { - -/** - * Context-dependent database assertions, organized by variable. Each variable - * can be associated a list of IDL assertions. The list of assertions can - * be iterated over using the provided iterator class. - */ -class IDLAssertionDB { - - /** Elements of the assertion lists */ - struct IDLAssertionListElement { - /** The assertion itself */ - IDLAssertion d_assertion; - /** The index of the previous element (-1 for null) */ - unsigned d_previous; - - IDLAssertionListElement(const IDLAssertion& assertion, unsigned previous) - : d_assertion(assertion), d_previous(previous) - {} - }; - - /** All assertions in a context dependent stack */ - context::CDList<IDLAssertionListElement> d_assertions; - - typedef context::CDHashMap<TNode, unsigned, TNodeHashFunction> var_to_unsigned_map; - - /** Map from variables to the first element of their list */ - var_to_unsigned_map d_variableLists; - -public: - - /** Create a new assertion database */ - IDLAssertionDB(context::Context* c); - - /** Add a new assertion, attach to the list of the given variable */ - void add(const IDLAssertion& assertion, TNode var); - - /** Iteration over the constraints of a variable */ - class iterator { - /** The database */ - const IDLAssertionDB& d_db; - /** Index of the current constraint */ - unsigned d_current; - public: - /** Construct the iterator for the variable */ - iterator(IDLAssertionDB& db, TNode var); - /** Is this iterator done */ - bool done() const { return d_current == (unsigned)(-1); } - /** Next element */ - void next(); - /** Get the assertion */ - IDLAssertion get() const; - }; -}; - -} -} -} - - - - diff --git a/src/theory/idl/idl_model.cpp b/src/theory/idl/idl_model.cpp deleted file mode 100644 index 4a3426222..000000000 --- a/src/theory/idl/idl_model.cpp +++ /dev/null @@ -1,74 +0,0 @@ -/********************* */ -/*! \file idl_model.cpp - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "theory/idl/idl_model.h" - -using namespace CVC4; -using namespace theory; -using namespace idl; - -IDLModel::IDLModel(context::Context* context) - : d_model(context), d_reason(context) -{ -} - -Integer IDLModel::getValue(TNode var) const -{ - model_value_map::const_iterator find = d_model.find(var); - if (find != d_model.end()) - { - return (*find).second; - } - else - { - return 0; - } -} - -void IDLModel::setValue(TNode var, Integer value, IDLReason reason) -{ - Assert(!reason.d_constraint.isNull()); - d_model[var] = value; - d_reason[var] = reason; -} - -void IDLModel::getReasonCycle(TNode var, std::vector<TNode>& reasons) -{ - TNode current = var; - do - { - Debug("theory::idl::model") << "processing: " << var << std::endl; - Assert(d_reason.find(current) != d_reason.end()); - IDLReason reason = d_reason[current]; - Debug("theory::idl::model") - << "adding reason: " << reason.d_constraint << std::endl; - reasons.push_back(reason.d_constraint); - current = reason.d_x; - } while (current != var); -} - -void IDLModel::toStream(std::ostream& out) const -{ - model_value_map::const_iterator it = d_model.begin(); - model_value_map::const_iterator it_end = d_model.end(); - out << "Model[" << std::endl; - for (; it != it_end; ++it) - { - out << (*it).first << " -> " << (*it).second << std::endl; - } - out << "]"; -} diff --git a/src/theory/idl/idl_model.h b/src/theory/idl/idl_model.h deleted file mode 100644 index 610b90695..000000000 --- a/src/theory/idl/idl_model.h +++ /dev/null @@ -1,84 +0,0 @@ -/********************* */ -/*! \file idl_model.h - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic, Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#pragma once - -#include "context/cdhashmap.h" -#include "expr/node.h" - -namespace CVC4 { -namespace theory { -namespace idl { - -/** - * A reason for a value of a variable in the model is a constraint that implies - * this value by means of the value of another variable. For example, if the - * value of x is 0, then the variable x and the constraint (y > 0) are a reason - * for the y taking the value 1. - */ -struct IDLReason -{ - /** The variable of the reason */ - TNode d_x; - /** The constraint of the reason */ - TNode d_constraint; - - IDLReason(TNode x, TNode constraint) : d_x(x), d_constraint(constraint) {} - IDLReason() {} -}; - -/** - * A model maps variables to integer values and backs them up with reasons. - * Default values (if not set with setValue) for all variables are 0. - */ -class IDLModel -{ - typedef context::CDHashMap<TNode, Integer, TNodeHashFunction> model_value_map; - typedef context::CDHashMap<TNode, IDLReason, TNodeHashFunction> - model_reason_map; - - /** Values assigned to individual variables */ - model_value_map d_model; - - /** Reasons constraining the individual variables */ - model_reason_map d_reason; - - public: - IDLModel(context::Context* context); - - /** Get the model value of the variable */ - Integer getValue(TNode var) const; - - /** Set the value of the variable */ - void setValue(TNode var, Integer value, IDLReason reason); - - /** Get the cycle of reasons behind the variable var */ - void getReasonCycle(TNode var, std::vector<TNode>& reasons); - - /** Output to the given stream */ - void toStream(std::ostream& out) const; -}; - -inline std::ostream& operator<<(std::ostream& out, const IDLModel& model) -{ - model.toStream(out); - return out; -} - -} // namespace idl -} // namespace theory -} // namespace CVC4 diff --git a/src/theory/idl/kinds b/src/theory/idl/kinds deleted file mode 100644 index 6bf0218b0..000000000 --- a/src/theory/idl/kinds +++ /dev/null @@ -1,8 +0,0 @@ -# kinds -*- sh -*- -# -# For documentation on this file format, please refer to -# src/theory/builtin/kinds. -# - -alternate THEORY_ARITH "idl" ::CVC4::theory::idl::TheoryIdl "theory/idl/theory_idl.h" - diff --git a/src/theory/idl/theory_idl.cpp b/src/theory/idl/theory_idl.cpp deleted file mode 100644 index f92738bcb..000000000 --- a/src/theory/idl/theory_idl.cpp +++ /dev/null @@ -1,156 +0,0 @@ -/********************* */ -/*! \file theory_idl.cpp - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic, Tim King, Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "theory/idl/theory_idl.h" - -#include <set> -#include <queue> - -#include "options/idl_options.h" -#include "theory/rewriter.h" - - -using namespace std; - -namespace CVC4 { -namespace theory { -namespace idl { - -TheoryIdl::TheoryIdl(context::Context* c, context::UserContext* u, - OutputChannel& out, Valuation valuation, - const LogicInfo& logicInfo) - : Theory(THEORY_ARITH, c, u, out, valuation, logicInfo) - , d_model(c) - , d_assertionsDB(c) -{} - -Node TheoryIdl::ppRewrite(TNode atom) { - if (atom.getKind() == kind::EQUAL && options::idlRewriteEq()) { - // If the option is turned on, each equality into two inequalities. This in - // effect removes equalities, and theorefore dis-equalities too. - Node leq = NodeBuilder<2>(kind::LEQ) << atom[0] << atom[1]; - Node geq = NodeBuilder<2>(kind::GEQ) << atom[0] << atom[1]; - Node rewritten = Rewriter::rewrite(leq.andNode(geq)); - return rewritten; - } else { - return atom; - } -} - -void TheoryIdl::check(Effort level) { - if (done() && !fullEffort(level)) { - return; - } - - TimerStat::CodeTimer checkTimer(d_checkTime); - - while(!done()) { - - // Get the next assertion - Assertion assertion = get(); - Debug("theory::idl") << "TheoryIdl::check(): processing " - << assertion.d_assertion << std::endl; - - // Convert the assertion into the internal representation - IDLAssertion idlAssertion(assertion.d_assertion); - Debug("theory::idl") << "TheoryIdl::check(): got " << idlAssertion << std::endl; - - if (idlAssertion.ok()) { - if (idlAssertion.getOp() == kind::DISTINCT) { - // We don't handle dis-equalities - d_out->setIncomplete(); - } else { - // Process the convex assertions immediately - bool ok = processAssertion(idlAssertion); - if (!ok) { - // In conflict, we're done - return; - } - } - } else { - // Not an IDL assertion, set incomplete - d_out->setIncomplete(); - } - } - -} - -bool TheoryIdl::processAssertion(const IDLAssertion& assertion) { - - Debug("theory::idl") << "TheoryIdl::processAssertion(" << assertion << ")" << std::endl; - - // Add the constraint (x - y op c) to the list assertions of x - d_assertionsDB.add(assertion, assertion.getX()); - - // Update the model, if forced by the assertion - bool y_updated = assertion.propagate(d_model); - - // If the value of y was updated, we might need to update further - if (y_updated) { - - std::queue<TNode> queue; // Queue of variables to consider - std::set<TNode> inQueue; // Current elements of the queue - - // Add the first updated variable to the queue - queue.push(assertion.getY()); - inQueue.insert(assertion.getY()); - - while (!queue.empty()) { - // Pop a new variable x off the queue - TNode x = queue.front(); - queue.pop(); - inQueue.erase(x); - - // Go through the constraint (x - y op c), and update values of y - IDLAssertionDB::iterator it(d_assertionsDB, x); - while (!it.done()) { - // Get the assertion and update y - IDLAssertion x_y_assertion = it.get(); - y_updated = x_y_assertion.propagate(d_model); - // If updated add to the queue - if (y_updated) { - // If the variable that we updated is the same as the first - // variable that we updated, it's a cycle of updates => conflict - if (x_y_assertion.getY() == assertion.getX()) { - std::vector<TNode> reasons; - d_model.getReasonCycle(x_y_assertion.getY(), reasons); - // Construct the reason of the conflict - Node conflict = NodeManager::currentNM()->mkNode(kind::AND, reasons); - d_out->conflict(conflict); - return false; - } else { - // No cycle, just a model update, so we add to the queue - TNode y = x_y_assertion.getY(); - if (inQueue.count(y) == 0) { - queue.push(y); - inQueue.insert(x_y_assertion.getY()); - } - } - } - // Go to the next constraint - it.next(); - } - } - } - - // Everything fine, no conflict - return true; -} - -} /* namepsace CVC4::theory::idl */ -} /* namepsace CVC4::theory */ -} /* namepsace CVC4 */ diff --git a/src/theory/idl/theory_idl.h b/src/theory/idl/theory_idl.h deleted file mode 100644 index 1d48d0785..000000000 --- a/src/theory/idl/theory_idl.h +++ /dev/null @@ -1,63 +0,0 @@ -/********************* */ -/*! \file theory_idl.h - ** \verbatim - ** Top contributors (to current version): - ** Dejan Jovanovic, Mathias Preiner, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#pragma once - -#include "cvc4_private.h" - -#include "theory/theory.h" -#include "theory/idl/idl_model.h" -#include "theory/idl/idl_assertion_db.h" - -namespace CVC4 { -namespace theory { -namespace idl { - -/** - * Handles integer difference logic (IDL) constraints. - */ -class TheoryIdl : public Theory { - - /** The current model */ - IDLModel d_model; - - /** The asserted constraints, organized by variable */ - IDLAssertionDB d_assertionsDB; - - /** Process a new assertion, returns false if in conflict */ - bool processAssertion(const IDLAssertion& assertion); - -public: - - /** Theory constructor. */ - TheoryIdl(context::Context* c, context::UserContext* u, OutputChannel& out, - Valuation valuation, const LogicInfo& logicInfo); - - /** Pre-processing of input atoms */ - Node ppRewrite(TNode atom) override; - - /** Check the assertions for satisfiability */ - void check(Effort effort) override; - - /** Identity string */ - std::string identify() const override { return "THEORY_IDL"; } - -};/* class TheoryIdl */ - -}/* CVC4::theory::idl namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ diff --git a/src/theory/mkrewriter b/src/theory/mkrewriter index dd5abd219..3c27f1b53 100755 --- a/src/theory/mkrewriter +++ b/src/theory/mkrewriter @@ -37,7 +37,6 @@ me=$(basename "$0") template=$1; shift rewriter_includes= -rewrite_init= pre_rewrite_get_cache= pre_rewrite_set_cache= @@ -140,8 +139,6 @@ function rewriter { rewriter_includes="${rewriter_includes}#include \"$header\" " - rewrite_init="${rewrite_init} d_theoryRewriters[${theory_id}].reset(new ${class}); -" pre_rewrite_attribute_ids="${pre_rewrite_attribute_ids} preids.push_back(expr::attr::AttributeManager::getAttributeId(RewriteAttibute<${theory_id}>::pre_rewrite())); " post_rewrite_attribute_ids="${post_rewrite_attribute_ids} postids.push_back(expr::attr::AttributeManager::getAttributeId(RewriteAttibute<${theory_id}>::post_rewrite())); @@ -257,7 +254,6 @@ for var in \ post_rewrite_get_cache \ pre_rewrite_set_cache \ post_rewrite_set_cache \ - rewrite_init \ pre_rewrite_attribute_ids \ post_rewrite_attribute_ids \ template \ diff --git a/src/theory/mktheorytraits b/src/theory/mktheorytraits index 8a900e1e7..a87203015 100755 --- a/src/theory/mktheorytraits +++ b/src/theory/mktheorytraits @@ -121,12 +121,6 @@ function alternate { theory_header="$4" theory_includes="${theory_includes}#include \"$theory_header\" " - - eval "alternate_for_$1=\"\${alternate_for_$1} - if(engine->useTheoryAlternative(\\\"$2\\\")) { - engine->addTheory< $3 >($1); - return; - }\"" } function rewriter { diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index 231c81bbf..10c5741fe 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -1009,7 +1009,7 @@ bool QuantifiersRewriter::getVarElimLit(Node lit, { slv = getVarElimLitBv(lit, args, var); } - else if (tt.isString()) + else if (tt.isStringLike()) { slv = getVarElimLitString(lit, args, var); } diff --git a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp index d33c72ede..f15b6780c 100644 --- a/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp +++ b/src/theory/quantifiers/sygus/sygus_grammar_cons.cpp @@ -27,6 +27,7 @@ #include "theory/quantifiers/sygus/term_database_sygus.h" #include "theory/quantifiers/term_util.h" #include "theory/quantifiers_engine.h" +#include "theory/strings/word.h" using namespace CVC4::kind; @@ -405,11 +406,15 @@ void CegGrammarConstructor::mkSygusConstantsForType(TypeNode type, ops.push_back(nm->mkConst(true)); ops.push_back(nm->mkConst(false)); } - else if (type.isString()) + else if (type.isStringLike()) { - ops.push_back(nm->mkConst(String(""))); - // dummy character "A" - ops.push_back(nm->mkConst(String("A"))); + ops.push_back(strings::Word::mkEmptyWord(type)); + if (type.isString()) + { + // Dummy character "A". This is not necessary for sequences which + // have the generic constructor seq.unit. + ops.push_back(nm->mkConst(String("A"))); + } } else if (type.isArray() || type.isSet()) { @@ -449,7 +454,7 @@ void CegGrammarConstructor::collectSygusGrammarTypesFor( { collectSygusGrammarTypesFor(range.getSetElementType(), types); } - else if (range.isString() ) + else if (range.isStringLike()) { // theory of strings shares the integer type TypeNode intType = NodeManager::currentNM()->integerType(); diff --git a/src/theory/quantifiers/sygus_sampler.cpp b/src/theory/quantifiers/sygus_sampler.cpp index 28cfa69df..e9c858814 100644 --- a/src/theory/quantifiers/sygus_sampler.cpp +++ b/src/theory/quantifiers/sygus_sampler.cpp @@ -560,8 +560,7 @@ Node SygusSampler::getRandomValue(TypeNode tn) for (unsigned ch : alphas) { d_rstring_alphabet.push_back(ch); - Trace("sygus-sample-str-alpha") - << " \"" << String::convertUnsignedIntToChar(ch) << "\""; + Trace("sygus-sample-str-alpha") << " \\u" << ch; } Trace("sygus-sample-str-alpha") << std::endl; } diff --git a/src/theory/quantifiers/term_util.cpp b/src/theory/quantifiers/term_util.cpp index 7f94130f3..cc920f1d7 100644 --- a/src/theory/quantifiers/term_util.cpp +++ b/src/theory/quantifiers/term_util.cpp @@ -25,6 +25,7 @@ #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_enumeration.h" #include "theory/quantifiers_engine.h" +#include "theory/strings/word.h" #include "theory/theory_engine.h" using namespace std; @@ -464,11 +465,11 @@ Node TermUtil::mkTypeValue(TypeNode tn, int val) n = NodeManager::currentNM()->mkConst(false); } } - else if (tn.isString()) + else if (tn.isStringLike()) { if (val == 0) { - n = NodeManager::currentNM()->mkConst(::CVC4::String("")); + n = strings::Word::mkEmptyWord(tn); } } return n; diff --git a/src/theory/quantifiers/theory_quantifiers.cpp b/src/theory/quantifiers/theory_quantifiers.cpp index 227f4d5b5..e3e3c3824 100644 --- a/src/theory/quantifiers/theory_quantifiers.cpp +++ b/src/theory/quantifiers/theory_quantifiers.cpp @@ -22,6 +22,7 @@ #include "theory/quantifiers/ematching/instantiation_engine.h" #include "theory/quantifiers/fmf/model_engine.h" #include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/quantifiers_rewriter.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" #include "theory/quantifiers_engine.h" @@ -53,6 +54,11 @@ TheoryQuantifiers::TheoryQuantifiers(Context* c, context::UserContext* u, Output TheoryQuantifiers::~TheoryQuantifiers() { } +std::unique_ptr<TheoryRewriter> TheoryQuantifiers::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new QuantifiersRewriter()); +} + void TheoryQuantifiers::finishInit() { // quantifiers are not evaluated in getModelValue diff --git a/src/theory/quantifiers/theory_quantifiers.h b/src/theory/quantifiers/theory_quantifiers.h index b5b07f2e6..7efe7419c 100644 --- a/src/theory/quantifiers/theory_quantifiers.h +++ b/src/theory/quantifiers/theory_quantifiers.h @@ -39,6 +39,8 @@ class TheoryQuantifiers : public Theory { const LogicInfo& logicInfo); ~TheoryQuantifiers(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + /** finish initialization */ void finishInit() override; void preRegisterTerm(TNode n) override; diff --git a/src/theory/rewriter.cpp b/src/theory/rewriter.cpp index b3f1e23d7..54b9f319d 100644 --- a/src/theory/rewriter.cpp +++ b/src/theory/rewriter.cpp @@ -96,6 +96,12 @@ Node Rewriter::rewrite(TNode node) { return getInstance().rewriteTo(theoryOf(node), node); } +void Rewriter::registerTheoryRewriter(theory::TheoryId tid, + std::unique_ptr<TheoryRewriter> trew) +{ + getInstance().d_theoryRewriters[tid] = std::move(trew); +} + void Rewriter::registerPreRewrite( Kind k, std::function<RewriteResponse(RewriteEnvironment*, TNode)> fn) { diff --git a/src/theory/rewriter.h b/src/theory/rewriter.h index f7298e1fb..8a641743b 100644 --- a/src/theory/rewriter.h +++ b/src/theory/rewriter.h @@ -63,6 +63,16 @@ class Rewriter { static void clearCaches(); /** + * Registers a theory rewriter with this rewriter. This transfers the + * ownership of the theory rewriter to the rewriter. + * + * @param tid The theory that the theory rewriter should be associated with. + * @param trew The theory rewriter to register. + */ + static void registerTheoryRewriter(theory::TheoryId tid, + std::unique_ptr<TheoryRewriter> trew); + + /** * Register a prerewrite for a given kind. * * @param k The kind to register a rewrite for. diff --git a/src/theory/rewriter_tables_template.h b/src/theory/rewriter_tables_template.h index 1bb03e253..9f6b07389 100644 --- a/src/theory/rewriter_tables_template.h +++ b/src/theory/rewriter_tables_template.h @@ -63,8 +63,6 @@ ${post_rewrite_set_cache} Rewriter::Rewriter() { -${rewrite_init} - for (size_t i = 0; i < kind::LAST_KIND; ++i) { d_preRewriters[i] = nullptr; @@ -75,7 +73,6 @@ for (size_t i = 0; i < theory::THEORY_LAST; ++i) { d_preRewritersEqual[i] = nullptr; d_postRewritersEqual[i] = nullptr; - d_theoryRewriters[i]->registerRewrites(this); } } diff --git a/src/theory/sep/theory_sep.cpp b/src/theory/sep/theory_sep.cpp index 0b6e7a5fb..1d0ad904c 100644 --- a/src/theory/sep/theory_sep.cpp +++ b/src/theory/sep/theory_sep.cpp @@ -29,6 +29,7 @@ #include "theory/quantifiers/term_util.h" #include "theory/quantifiers_engine.h" #include "theory/rewriter.h" +#include "theory/sep/theory_sep_rewriter.h" #include "theory/theory_model.h" #include "theory/valuation.h" @@ -64,6 +65,11 @@ TheorySep::~TheorySep() { } } +std::unique_ptr<TheoryRewriter> TheorySep::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheorySepRewriter()); +} + void TheorySep::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_equalityEngine.setMasterEqualityEngine(eq); } diff --git a/src/theory/sep/theory_sep.h b/src/theory/sep/theory_sep.h index ae044f6d7..df3294882 100644 --- a/src/theory/sep/theory_sep.h +++ b/src/theory/sep/theory_sep.h @@ -66,6 +66,8 @@ class TheorySep : public Theory { TheorySep(context::Context* c, context::UserContext* u, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo); ~TheorySep(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + void setMasterEqualityEngine(eq::EqualityEngine* eq) override; std::string identify() const override { return std::string("TheorySep"); } diff --git a/src/theory/sets/theory_sets.cpp b/src/theory/sets/theory_sets.cpp index 3b0427ec5..0b9e6d934 100644 --- a/src/theory/sets/theory_sets.cpp +++ b/src/theory/sets/theory_sets.cpp @@ -15,8 +15,10 @@ **/ #include "theory/sets/theory_sets.h" + #include "options/sets_options.h" #include "theory/sets/theory_sets_private.h" +#include "theory/sets/theory_sets_rewriter.h" #include "theory/theory_model.h" using namespace CVC4::kind; @@ -44,6 +46,11 @@ TheorySets::~TheorySets() // Do not move me to the header. See explanation in the constructor. } +std::unique_ptr<TheoryRewriter> TheorySets::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheorySetsRewriter()); +} + void TheorySets::finishInit() { TheoryModel* tm = d_valuation.getModel(); diff --git a/src/theory/sets/theory_sets.h b/src/theory/sets/theory_sets.h index ed2459b32..a55a22600 100644 --- a/src/theory/sets/theory_sets.h +++ b/src/theory/sets/theory_sets.h @@ -42,6 +42,8 @@ class TheorySets : public Theory const LogicInfo& logicInfo); ~TheorySets() override; + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + /** finish initialization */ void finishInit() override; void addSharedTerm(TNode) override; diff --git a/src/theory/strings/base_solver.cpp b/src/theory/strings/base_solver.cpp index 128893cf0..076fc1cc5 100644 --- a/src/theory/strings/base_solver.cpp +++ b/src/theory/strings/base_solver.cpp @@ -35,7 +35,6 @@ BaseSolver::BaseSolver(context::Context* c, d_emptyString = NodeManager::currentNM()->mkConst(::CVC4::String("")); d_false = NodeManager::currentNM()->mkConst(false); d_cardSize = utils::getAlphabetCardinality(); - d_type = NodeManager::currentNM()->stringType(); } BaseSolver::~BaseSolver() {} @@ -128,7 +127,7 @@ void BaseSolver::checkInit() } } // infer the equality - d_im.sendInference(exp, n.eqNode(nc), "I_Norm"); + d_im.sendInference(exp, n.eqNode(nc), Inference::I_NORM); } else { @@ -173,7 +172,7 @@ void BaseSolver::checkInit() } AlwaysAssert(foundNEmpty); // infer the equality - d_im.sendInference(exp, n.eqNode(ns), "I_Norm_S"); + d_im.sendInference(exp, n.eqNode(ns), Inference::I_NORM_S); } d_congruent.insert(n); congruent[k]++; @@ -254,7 +253,7 @@ void BaseSolver::checkConstantEquivalenceClasses(TermIndex* ti, if (!n.isNull()) { // construct the constant - Node c = utils::mkNConcat(vecc, d_type); + Node c = utils::mkNConcat(vecc, n.getType()); if (!d_state.areEqual(n, c)) { if (Trace.isOn("strings-debug")) @@ -302,7 +301,7 @@ void BaseSolver::checkConstantEquivalenceClasses(TermIndex* ti, Assert(countc == vecc.size()); if (d_state.hasTerm(c)) { - d_im.sendInference(exp, n.eqNode(c), "I_CONST_MERGE"); + d_im.sendInference(exp, n.eqNode(c), Inference::I_CONST_MERGE); return; } else if (!d_im.hasProcessed()) @@ -334,7 +333,7 @@ void BaseSolver::checkConstantEquivalenceClasses(TermIndex* ti, exp.push_back(d_eqcToConstExp[nr]); d_im.addToExplanation(n, d_eqcToConstBase[nr], exp); } - d_im.sendInference(exp, d_false, "I_CONST_CONFLICT"); + d_im.sendInference(exp, d_false, Inference::I_CONST_CONFLICT); return; } else @@ -484,7 +483,8 @@ void BaseSolver::checkCardinality() if (!cons.isConst() || !cons.getConst<bool>()) { std::vector<Node> emptyVec; - d_im.sendInference(emptyVec, vec_node, cons, "CARDINALITY", true); + d_im.sendInference( + emptyVec, vec_node, cons, Inference::CARDINALITY, true); return; } } diff --git a/src/theory/strings/base_solver.h b/src/theory/strings/base_solver.h index bf223bc0a..3681b49a4 100644 --- a/src/theory/strings/base_solver.h +++ b/src/theory/strings/base_solver.h @@ -191,8 +191,6 @@ class BaseSolver std::map<Kind, TermIndex> d_termIndex; /** the cardinality of the alphabet */ uint32_t d_cardSize; - /** The string-like type for this base solver */ - TypeNode d_type; }; /* class BaseSolver */ } // namespace strings diff --git a/src/theory/strings/core_solver.cpp b/src/theory/strings/core_solver.cpp index 556ae28c3..3384499a2 100644 --- a/src/theory/strings/core_solver.cpp +++ b/src/theory/strings/core_solver.cpp @@ -37,10 +37,8 @@ d_nf_pairs(c) d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); d_one = NodeManager::currentNM()->mkConst( Rational( 1 ) ); d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1)); - d_emptyString = Word::mkEmptyWord(CONST_STRING); d_true = NodeManager::currentNM()->mkConst( true ); d_false = NodeManager::currentNM()->mkConst( false ); - d_type = NodeManager::currentNM()->stringType(); } CoreSolver::~CoreSolver() { @@ -286,10 +284,11 @@ void CoreSolver::checkFlatForm(std::vector<Node>& eqc, << "Found endpoint (in a) with non-empty b = " << b << ", whose flat form is " << d_flat_form[b] << std::endl; // endpoint + Node emp = Word::mkEmptyWord(a.getType()); std::vector<Node> conc_c; for (unsigned j = count; j < bsize; j++) { - conc_c.push_back(b[d_flat_form_index[b][j]].eqNode(d_emptyString)); + conc_c.push_back(b[d_flat_form_index[b][j]].eqNode(emp)); } Assert(!conc_c.empty()); conc = utils::mkAnd(conc_c); @@ -325,10 +324,11 @@ void CoreSolver::checkFlatForm(std::vector<Node>& eqc, << "Found endpoint in b = " << b << ", whose flat form is " << d_flat_form[b] << std::endl; // endpoint + Node emp = Word::mkEmptyWord(a.getType()); std::vector<Node> conc_c; for (size_t j = count; j < asize; j++) { - conc_c.push_back(a[d_flat_form_index[a][j]].eqNode(d_emptyString)); + conc_c.push_back(a[d_flat_form_index[a][j]].eqNode(emp)); } Assert(!conc_c.empty()); conc = utils::mkAnd(conc_c); @@ -438,11 +438,12 @@ void CoreSolver::checkFlatForm(std::vector<Node>& eqc, } ssize_t startj = isRev ? jj + 1 : 0; ssize_t endj = isRev ? c.getNumChildren() : jj; + Node emp = Word::mkEmptyWord(a.getType()); for (ssize_t j = startj; j < endj; j++) { - if (d_state.areEqual(c[j], d_emptyString)) + if (d_state.areEqual(c[j], emp)) { - d_im.addToExplanation(c[j], d_emptyString, exp); + d_im.addToExplanation(c[j], emp, exp); } } } @@ -470,6 +471,7 @@ Node CoreSolver::checkCycles( Node eqc, std::vector< Node >& curr, std::vector< return eqc; }else if( std::find( d_strings_eqc.begin(), d_strings_eqc.end(), eqc )==d_strings_eqc.end() ){ curr.push_back( eqc ); + Node emp = Word::mkEmptyWord(eqc.getType()); //look at all terms in this equivalence class eq::EqualityEngine* ee = d_state.getEqualityEngine(); eq::EqClassIterator eqc_i = eq::EqClassIterator( eqc, ee ); @@ -478,22 +480,25 @@ Node CoreSolver::checkCycles( Node eqc, std::vector< Node >& curr, std::vector< if( !d_bsolver.isCongruent(n) ){ if( n.getKind() == kind::STRING_CONCAT ){ Trace("strings-cycle") << eqc << " check term : " << n << " in " << eqc << std::endl; - if( eqc!=d_emptyString ){ + if (eqc != emp) + { d_eqc[eqc].push_back( n ); } for( unsigned i=0; i<n.getNumChildren(); i++ ){ Node nr = d_state.getRepresentative(n[i]); - if( eqc==d_emptyString ){ + if (eqc == emp) + { //for empty eqc, ensure all components are empty - if( nr!=d_emptyString ){ + if (nr != emp) + { std::vector<Node> exps; - exps.push_back(n.eqNode(d_emptyString)); - d_im.sendInference( - exps, n[i].eqNode(d_emptyString), "I_CYCLE_E"); + exps.push_back(n.eqNode(emp)); + d_im.sendInference(exps, n[i].eqNode(emp), "I_CYCLE_E"); return Node::null(); } }else{ - if( nr!=d_emptyString ){ + if (nr != emp) + { d_flat_form[n].push_back( nr ); d_flat_form_index[n].push_back( i ); } @@ -507,10 +512,9 @@ Node CoreSolver::checkCycles( Node eqc, std::vector< Node >& curr, std::vector< //can infer all other components must be empty for( unsigned j=0; j<n.getNumChildren(); j++ ){ //take first non-empty - if (j != i && !d_state.areEqual(n[j], d_emptyString)) + if (j != i && !d_state.areEqual(n[j], emp)) { - d_im.sendInference( - exp, n[j].eqNode(d_emptyString), "I_CYCLE"); + d_im.sendInference(exp, n[j].eqNode(emp), "I_CYCLE"); return Node::null(); } } @@ -551,16 +555,17 @@ void CoreSolver::checkNormalFormsEq() std::map<Node, Node> eqc_to_exp; for (const Node& eqc : d_strings_eqc) { + TypeNode stype = eqc.getType(); Trace("strings-process-debug") << "- Verify normal forms are the same for " << eqc << std::endl; - normalizeEquivalenceClass(eqc); + normalizeEquivalenceClass(eqc, stype); Trace("strings-debug") << "Finished normalizing eqc..." << std::endl; if (d_im.hasProcessed()) { return; } NormalForm& nfe = getNormalForm(eqc); - Node nf_term = utils::mkNConcat(nfe.d_nf, d_type); + Node nf_term = utils::mkNConcat(nfe.d_nf, stype); std::map<Node, Node>::iterator itn = nf_to_eqc.find(nf_term); if (itn != nf_to_eqc.end()) { @@ -602,21 +607,23 @@ void CoreSolver::checkNormalFormsEq() } //compute d_normal_forms_(base,exp,exp_depend)[eqc] -void CoreSolver::normalizeEquivalenceClass( Node eqc ) { +void CoreSolver::normalizeEquivalenceClass(Node eqc, TypeNode stype) +{ Trace("strings-process-debug") << "Process equivalence class " << eqc << std::endl; - if (d_state.areEqual(eqc, d_emptyString)) + Node emp = Word::mkEmptyWord(stype); + if (d_state.areEqual(eqc, emp)) { #ifdef CVC4_ASSERTIONS for( unsigned j=0; j<d_eqc[eqc].size(); j++ ){ Node n = d_eqc[eqc][j]; for( unsigned i=0; i<n.getNumChildren(); i++ ){ - Assert(d_state.areEqual(n[i], d_emptyString)); + Assert(d_state.areEqual(n[i], emp)); } } #endif //do nothing Trace("strings-process-debug") << "Return process equivalence class " << eqc << " : empty." << std::endl; - d_normal_form[eqc].init(d_emptyString); + d_normal_form[eqc].init(emp); } else { @@ -627,13 +634,13 @@ void CoreSolver::normalizeEquivalenceClass( Node eqc ) { // map each term to its index in the above vector std::map<Node, unsigned> term_to_nf_index; // get the normal forms - getNormalForms(eqc, normal_forms, term_to_nf_index); + getNormalForms(eqc, normal_forms, term_to_nf_index, stype); if (d_im.hasProcessed()) { return; } // process the normal forms - processNEqc(normal_forms); + processNEqc(normal_forms, stype); if (d_im.hasProcessed()) { return; @@ -679,11 +686,12 @@ Node CoreSolver::getNormalString(Node x, std::vector<Node>& nf_exp) if (!x.isConst()) { Node xr = d_state.getRepresentative(x); + TypeNode stype = xr.getType(); std::map<Node, NormalForm>::iterator it = d_normal_form.find(xr); if (it != d_normal_form.end()) { NormalForm& nf = it->second; - Node ret = utils::mkNConcat(nf.d_nf, d_type); + Node ret = utils::mkNConcat(nf.d_nf, stype); nf_exp.insert(nf_exp.end(), nf.d_exp.begin(), nf.d_exp.end()); d_im.addToExplanation(x, nf.d_base, nf_exp); Trace("strings-debug") @@ -701,16 +709,18 @@ Node CoreSolver::getNormalString(Node x, std::vector<Node>& nf_exp) Node nc = getNormalString(x[i], nf_exp); vec_nodes.push_back(nc); } - return utils::mkNConcat(vec_nodes, d_type); + return utils::mkNConcat(vec_nodes, stype); } } return x; } void CoreSolver::getNormalForms(Node eqc, - std::vector<NormalForm>& normal_forms, - std::map<Node, unsigned>& term_to_nf_index) + std::vector<NormalForm>& normal_forms, + std::map<Node, unsigned>& term_to_nf_index, + TypeNode stype) { + Node emp = Word::mkEmptyWord(stype); //constant for equivalence class Node eqc_non_c = eqc; Trace("strings-process-debug") << "Get normal forms " << eqc << std::endl; @@ -719,11 +729,11 @@ void CoreSolver::getNormalForms(Node eqc, while( !eqc_i.isFinished() ){ Node n = (*eqc_i); if( !d_bsolver.isCongruent(n) ){ - if (n.getKind() == CONST_STRING || n.getKind() == STRING_CONCAT) + if (n.isConst() || n.getKind() == STRING_CONCAT) { Trace("strings-process-debug") << "Get Normal Form : Process term " << n << " in eqc " << eqc << std::endl; NormalForm nf_curr; - if (n.getKind() == CONST_STRING) + if (n.isConst()) { nf_curr.init(n); } @@ -803,8 +813,7 @@ void CoreSolver::getNormalForms(Node eqc, } //if not equal to self std::vector<Node>& currv = nf_curr.d_nf; - if (currv.size() > 1 - || (currv.size() == 1 && currv[0].getKind() == CONST_STRING)) + if (currv.size() > 1 || (currv.size() == 1 && currv[0].isConst())) { // if in a build with assertions, check that normal form is acyclic if (Configuration::isAssertionBuild()) @@ -827,7 +836,7 @@ void CoreSolver::getNormalForms(Node eqc, normal_forms.push_back(nf_curr); }else{ //this was redundant: combination of self + empty string(s) - Node nn = currv.size() == 0 ? d_emptyString : currv[0]; + Node nn = currv.size() == 0 ? emp : currv[0]; Assert(d_state.areEqual(nn, eqc)); } }else{ @@ -926,7 +935,8 @@ void CoreSolver::getNormalForms(Node eqc, } } -void CoreSolver::processNEqc(std::vector<NormalForm>& normal_forms) +void CoreSolver::processNEqc(std::vector<NormalForm>& normal_forms, + TypeNode stype) { //the possible inferences std::vector< InferInfo > pinfer; @@ -946,7 +956,7 @@ void CoreSolver::processNEqc(std::vector<NormalForm>& normal_forms) unsigned rindex = 0; nfi.reverse(); nfj.reverse(); - processSimpleNEq(nfi, nfj, rindex, true, 0, pinfer); + processSimpleNEq(nfi, nfj, rindex, true, 0, pinfer, stype); nfi.reverse(); nfj.reverse(); if (d_im.hasProcessed()) @@ -957,7 +967,7 @@ void CoreSolver::processNEqc(std::vector<NormalForm>& normal_forms) //rindex = 0; unsigned index = 0; - processSimpleNEq(nfi, nfj, index, false, rindex, pinfer); + processSimpleNEq(nfi, nfj, index, false, rindex, pinfer, stype); if (d_im.hasProcessed()) { return; @@ -1001,10 +1011,12 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, unsigned& index, bool isRev, unsigned rproc, - std::vector<InferInfo>& pinfer) + std::vector<InferInfo>& pinfer, + TypeNode stype) { NodeManager* nm = NodeManager::currentNM(); eq::EqualityEngine* ee = d_state.getEqualityEngine(); + Node emp = Word::mkEmptyWord(stype); const std::vector<Node>& nfiv = nfi.d_nf; const std::vector<Node>& nfjv = nfj.d_nf; @@ -1029,8 +1041,8 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, while (!d_state.isInConflict() && index_k < (nfkv.size() - rproc)) { // can infer that this string must be empty - Node eq = nfkv[index_k].eqNode(d_emptyString); - Assert(!d_state.areEqual(d_emptyString, nfkv[index_k])); + Node eq = nfkv[index_k].eqNode(emp); + Assert(!d_state.areEqual(emp, nfkv[index_k])); d_im.sendInference(curr_exp, eq, Inference::N_ENDPOINT_EMP); index_k++; } @@ -1082,9 +1094,8 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, d_im.sendInference(lenExp, eq, Inference::N_UNIFY); break; } - else if ((x.getKind() != CONST_STRING && index == nfiv.size() - rproc - 1) - || (y.getKind() != CONST_STRING - && index == nfjv.size() - rproc - 1)) + else if ((!x.isConst() && index == nfiv.size() - rproc - 1) + || (!y.isConst() && index == nfjv.size() - rproc - 1)) { // We have reached the last component in one of the normal forms and it // is not a constant. Thus, the last component must be equal to the @@ -1110,7 +1121,7 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, eqnc.push_back(nfkv[i]); } } - eqn[r] = utils::mkNConcat(eqnc, d_type); + eqn[r] = utils::mkNConcat(eqnc, stype); } if (!d_state.areEqual(eqn[0], eqn[1])) { @@ -1253,15 +1264,15 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, NormalForm& nfnc = x.isConst() ? nfj : nfi; const std::vector<Node>& nfncv = nfnc.d_nf; Node nc = nfncv[index]; - Assert(nc.getKind() != CONST_STRING) << "Other string is not constant."; + Assert(!nc.isConst()) << "Other string is not constant."; Assert(nc.getKind() != STRING_CONCAT) << "Other string is not CONCAT."; - if (!ee->areDisequal(nc, d_emptyString, true)) + if (!ee->areDisequal(nc, emp, true)) { // The non-constant side may be equal to the empty string. Split on // whether it is. // // E.g. "abc" ++ ... = nc ++ ... ---> (nc = "") v (nc != "") - Node eq = nc.eqNode(d_emptyString); + Node eq = nc.eqNode(emp); eq = Rewriter::rewrite(eq); if (eq.isConst()) { @@ -1269,7 +1280,7 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, // purify variable for this string to communicate that // we have inferred whether it is empty. Node p = d_skCache.mkSkolemCached(nc, SkolemCache::SK_PURIFY, "lsym"); - Node pEq = p.eqNode(d_emptyString); + Node pEq = p.eqNode(emp); // should not be constant Assert(!Rewriter::rewrite(pEq).isConst()); // infer the purification equality, and the (dis)equality @@ -1290,7 +1301,7 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, // At this point, we know that `nc` is non-empty, so we add that to our // explanation. - Node ncnz = nc.eqNode(d_emptyString).negate(); + Node ncnz = nc.eqNode(emp).negate(); info.d_ant.push_back(ncnz); size_t ncIndex = index + 1; @@ -1302,19 +1313,19 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, // // E.g. "abc" ++ ... = nc ++ "b" ++ ... ---> nc = "a" ++ k size_t cIndex = index; - Node constStr = nfc.collectConstantStringAt(cIndex); - Assert(!constStr.isNull()); - CVC4::String stra = constStr.getConst<String>(); - CVC4::String strb = nextConstStr.getConst<String>(); + Node stra = nfc.collectConstantStringAt(cIndex); + size_t straLen = Word::getLength(stra); + Assert(!stra.isNull()); + Node strb = nextConstStr; // Since `nc` is non-empty, we start with character 1 size_t p; if (isRev) { - CVC4::String stra1 = stra.prefix(stra.size() - 1); - p = stra.size() - stra1.roverlap(strb); - Trace("strings-csp-debug") << "Compute roverlap : " << constStr << " " - << nextConstStr << std::endl; - size_t p2 = stra1.rfind(strb); + Node stra1 = Word::prefix(stra, straLen - 1); + p = straLen - Word::roverlap(stra1, strb); + Trace("strings-csp-debug") + << "Compute roverlap : " << stra1 << " " << strb << std::endl; + size_t p2 = Word::rfind(stra1, strb); p = p2 == std::string::npos ? p : (p > p2 + 1 ? p2 + 1 : p); Trace("strings-csp-debug") << "roverlap : " << stra1 << " " << strb << " returned " << p @@ -1322,11 +1333,11 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, } else { - CVC4::String stra1 = stra.substr(1); - p = stra.size() - stra1.overlap(strb); - Trace("strings-csp-debug") << "Compute overlap : " << constStr << " " - << nextConstStr << std::endl; - size_t p2 = stra1.find(strb); + Node stra1 = Word::substr(stra, 1); + p = straLen - Word::overlap(stra1, strb); + Trace("strings-csp-debug") + << "Compute overlap : " << stra1 << " " << strb << std::endl; + size_t p2 = Word::find(stra1, strb); p = p2 == std::string::npos ? p : (p > p2 + 1 ? p2 + 1 : p); Trace("strings-csp-debug") << "overlap : " << stra1 << " " << strb << " returned " << p @@ -1340,9 +1351,9 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, { NormalForm::getExplanationForPrefixEq( nfc, nfnc, cIndex, ncIndex, info.d_ant); - Node prea = p == stra.size() ? constStr - : nm->mkConst(isRev ? stra.suffix(p) - : stra.prefix(p)); + Node prea = p == straLen ? stra + : (isRev ? Word::suffix(stra, p) + : Word::prefix(stra, p)); Node sk = d_skCache.mkSkolemCached( nc, prea, @@ -1364,17 +1375,17 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, // to start with the first character of the constant. // // E.g. "abc" ++ ... = nc ++ ... ---> nc = "a" ++ k - Node constStr = nfcv[index]; - CVC4::String stra = constStr.getConst<String>(); - Node firstChar = stra.size() == 1 ? constStr - : nm->mkConst(isRev ? stra.suffix(1) - : stra.prefix(1)); + Node stra = nfcv[index]; + size_t straLen = Word::getLength(stra); + Node firstChar = straLen == 1 ? stra + : (isRev ? Word::suffix(stra, 1) + : Word::prefix(stra, 1)); Node sk = d_skCache.mkSkolemCached( nc, isRev ? SkolemCache::SK_ID_VC_SPT_REV : SkolemCache::SK_ID_VC_SPT, "c_spt"); Trace("strings-csp") << "Const Split: " << firstChar - << " is removed from " << constStr << " (serial) " + << " is removed from " << stra << " (serial) " << std::endl; NormalForm::getExplanationForPrefixEq(nfi, nfj, index, index, info.d_ant); info.d_conc = nc.eqNode(isRev ? utils::mkNConcat(sk, firstChar) @@ -1391,8 +1402,8 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, // // E.g. x ++ ... = y ++ ... ---> (x = y ++ k) v (y = x ++ k) Assert(d_state.areDisequal(xLenTerm, yLenTerm)); - Assert(x.getKind() != CONST_STRING); - Assert(y.getKind() != CONST_STRING); + Assert(!x.isConst()); + Assert(!y.isConst()); int32_t lentTestSuccess = -1; Node lentTestExp; @@ -1404,7 +1415,7 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, { Node t = e == 0 ? x : y; // do not infer constants are larger than variables - if (t.getKind() != CONST_STRING) + if (!t.isConst()) { Node lt1 = e == 0 ? xLenTerm : yLenTerm; Node lt2 = e == 0 ? yLenTerm : xLenTerm; @@ -1431,8 +1442,8 @@ void CoreSolver::processSimpleNEq(NormalForm& nfi, for (unsigned xory = 0; xory < 2; xory++) { Node t = xory == 0 ? x : y; - Node tnz = x.eqNode(d_emptyString).negate(); - if (ee->areDisequal(x, d_emptyString, true)) + Node tnz = x.eqNode(emp).negate(); + if (ee->areDisequal(x, emp, true)) { info.d_ant.push_back(tnz); } @@ -1529,23 +1540,27 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, const std::vector<Node>& veci = nfi.d_nf; const std::vector<Node>& vecoi = nfj.d_nf; + TypeNode stype = veci[loop_index].getType(); + Trace("strings-loop") << "Detected possible loop for " << veci[loop_index] << std::endl; Trace("strings-loop") << " ... (X)= " << vecoi[index] << std::endl; Trace("strings-loop") << " ... T(Y.Z)= "; std::vector<Node> vec_t(veci.begin() + index, veci.begin() + loop_index); - Node t_yz = utils::mkNConcat(vec_t, d_type); + Node t_yz = utils::mkNConcat(vec_t, stype); Trace("strings-loop") << " (" << t_yz << ")" << std::endl; Trace("strings-loop") << " ... S(Z.Y)= "; std::vector<Node> vec_s(vecoi.begin() + index + 1, vecoi.end()); - Node s_zy = utils::mkNConcat(vec_s, d_type); + Node s_zy = utils::mkNConcat(vec_s, stype); Trace("strings-loop") << s_zy << std::endl; Trace("strings-loop") << " ... R= "; std::vector<Node> vec_r(veci.begin() + loop_index + 1, veci.end()); - Node r = utils::mkNConcat(vec_r, d_type); + Node r = utils::mkNConcat(vec_r, stype); Trace("strings-loop") << r << std::endl; - if (s_zy.isConst() && r.isConst() && r != d_emptyString) + Node emp = Word::mkEmptyWord(stype); + + if (s_zy.isConst() && r.isConst() && r != emp) { int c; bool flag = true; @@ -1553,8 +1568,8 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, { if (c >= 0) { - s_zy = nm->mkConst(s_zy.getConst<String>().substr(0, c)); - r = d_emptyString; + s_zy = Word::substr(s_zy, 0, c); + r = emp; vec_r.clear(); Trace("strings-loop") << "Strings::Loop: Refactor S(Z.Y)= " << s_zy << ", c=" << c << std::endl; @@ -1574,12 +1589,12 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, for (unsigned i = 0; i < 2; i++) { Node t = i == 0 ? veci[loop_index] : t_yz; - split_eq = t.eqNode(d_emptyString); + split_eq = t.eqNode(emp); Node split_eqr = Rewriter::rewrite(split_eq); // the equality could rewrite to false if (!split_eqr.isConst()) { - if (!d_state.areDisequal(t, d_emptyString)) + if (!d_state.areDisequal(t, emp)) { // try to make t equal to empty to avoid loop info.d_conc = nm->mkNode(kind::OR, split_eq, split_eq.negate()); @@ -1602,10 +1617,10 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, info.d_antn.push_back(ant); Node str_in_re; - if (s_zy == t_yz && r == d_emptyString && s_zy.isConst() + if (s_zy == t_yz && r == emp && s_zy.isConst() && s_zy.getConst<String>().isRepeated()) { - Node rep_c = nm->mkConst(s_zy.getConst<String>().substr(0, 1)); + Node rep_c = Word::substr(s_zy, 0, 1); Trace("strings-loop") << "Special case (X)=" << vecoi[index] << " " << std::endl; Trace("strings-loop") << "... (C)=" << rep_c << " " << std::endl; @@ -1628,13 +1643,13 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, Node z = Word::substr(t_yz, len, size - len); Node restr = s_zy; Node cc; - if (r != d_emptyString) + if (r != emp) { std::vector<Node> v2(vec_r); v2.insert(v2.begin(), y); v2.insert(v2.begin(), z); restr = utils::mkNConcat(z, y); - cc = Rewriter::rewrite(s_zy.eqNode(utils::mkNConcat(v2, d_type))); + cc = Rewriter::rewrite(s_zy.eqNode(utils::mkNConcat(v2, stype))); } else { @@ -1677,16 +1692,16 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, // right Node sk_w = d_skCache.mkSkolem("w_loop"); Node sk_y = d_skCache.mkSkolem("y_loop"); - d_im.registerLength(sk_y, LENGTH_GEQ_ONE); + d_im.registerTermAtomic(sk_y, LENGTH_GEQ_ONE); Node sk_z = d_skCache.mkSkolem("z_loop"); // t1 * ... * tn = y * z Node conc1 = t_yz.eqNode(utils::mkNConcat(sk_y, sk_z)); // s1 * ... * sk = z * y * r vec_r.insert(vec_r.begin(), sk_y); vec_r.insert(vec_r.begin(), sk_z); - Node conc2 = s_zy.eqNode(utils::mkNConcat(vec_r, d_type)); + Node conc2 = s_zy.eqNode(utils::mkNConcat(vec_r, stype)); Node conc3 = vecoi[index].eqNode(utils::mkNConcat(sk_y, sk_w)); - Node restr = r == d_emptyString ? s_zy : utils::mkNConcat(sk_z, sk_y); + Node restr = r == emp ? s_zy : utils::mkNConcat(sk_z, sk_y); str_in_re = nm->mkNode(kind::STRING_IN_REGEXP, sk_w, @@ -1698,7 +1713,6 @@ CoreSolver::ProcessLoopResult CoreSolver::processLoop(NormalForm& nfi, vec_conc.push_back(conc2); vec_conc.push_back(conc3); vec_conc.push_back(str_in_re); - // vec_conc.push_back(sk_y.eqNode(d_emptyString).negate());//by mkskolems conc = nm->mkNode(kind::AND, vec_conc); } // normal case @@ -1745,20 +1759,22 @@ void CoreSolver::processDeq( Node ni, Node nj ) { Trace("strings-solve-debug") << "...Processing(DEQ) " << i << " " << j << std::endl; if (!d_state.areEqual(i, j)) { - Assert(i.getKind() != kind::CONST_STRING - || j.getKind() != kind::CONST_STRING); + Assert(!i.isConst() || !j.isConst()); std::vector< Node > lexp; Node li = d_state.getLength(i, lexp); Node lj = d_state.getLength(j, lexp); if (d_state.areDisequal(li, lj)) { - if( i.getKind()==kind::CONST_STRING || j.getKind()==kind::CONST_STRING ){ + if (i.isConst() || j.isConst()) + { //check if empty - Node const_k = i.getKind() == kind::CONST_STRING ? i : j; - Node nconst_k = i.getKind() == kind::CONST_STRING ? j : i; - Node lnck = i.getKind() == kind::CONST_STRING ? lj : li; - if( !ee->areDisequal( nconst_k, d_emptyString, true ) ){ - Node eq = nconst_k.eqNode( d_emptyString ); + Node const_k = i.isConst() ? i : j; + Node nconst_k = i.isConst() ? j : i; + Node lnck = i.isConst() ? lj : li; + Node emp = Word::mkEmptyWord(nconst_k.getType()); + if (!ee->areDisequal(nconst_k, emp, true)) + { + Node eq = nconst_k.eqNode(emp); Node conc = NodeManager::currentNM()->mkNode( kind::OR, eq, eq.negate() ); d_im.sendInference(d_emptyVec, conc, "D-DISL-Emp-Split"); return; @@ -1787,7 +1803,7 @@ void CoreSolver::processDeq( Node ni, Node nj ) { { Node sk = d_skCache.mkSkolemCached( nconst_k, SkolemCache::SK_ID_DC_SPT, "dc_spt"); - d_im.registerLength(sk, LENGTH_ONE); + d_im.registerTermAtomic(sk, LENGTH_ONE); Node skr = d_skCache.mkSkolemCached(nconst_k, SkolemCache::SK_ID_DC_SPT_REM, @@ -1800,7 +1816,7 @@ void CoreSolver::processDeq( Node ni, Node nj ) { antec.end(), nfni.d_exp.begin(), nfni.d_exp.end()); antec.insert( antec.end(), nfnj.d_exp.begin(), nfnj.d_exp.end()); - antec.push_back( nconst_k.eqNode( d_emptyString ).negate() ); + antec.push_back(nconst_k.eqNode(emp).negate()); d_im.sendInference( antec, nm->mkNode( @@ -1836,9 +1852,7 @@ void CoreSolver::processDeq( Node ni, Node nj ) { i, j, SkolemCache::SK_ID_DEQ_Y, "y_dsplit"); Node sk3 = d_skCache.mkSkolemCached( i, j, SkolemCache::SK_ID_DEQ_Z, "z_dsplit"); - d_im.registerLength(sk3, LENGTH_GEQ_ONE); - //Node nemp = sk3.eqNode(d_emptyString).negate(); - //conc.push_back(nemp); + d_im.registerTermAtomic(sk3, LENGTH_GEQ_ONE); Node lsk1 = utils::mkNLength(sk1); conc.push_back( lsk1.eqNode( li ) ); Node lsk2 = utils::mkNLength(sk2); @@ -1914,6 +1928,8 @@ int CoreSolver::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node >& } } } + TypeNode stype = ni.getType(); + Node emp = Word::mkEmptyWord(stype); NormalForm& nfni = getNormalForm(ni); NormalForm& nfnj = getNormalForm(nj); while( index<nfi.size() || index<nfj.size() ) { @@ -1929,7 +1945,7 @@ int CoreSolver::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node >& std::vector< Node > cc; std::vector< Node >& nfk = index>=nfi.size() ? nfj : nfi; for( unsigned index_k=index; index_k<nfk.size(); index_k++ ){ - cc.push_back( nfk[index_k].eqNode( d_emptyString ) ); + cc.push_back(nfk[index_k].eqNode(emp)); } Node conc = cc.size()==1 ? cc[0] : NodeManager::currentNM()->mkNode( kind::AND, cc ); conc = Rewriter::rewrite( conc ); @@ -1941,11 +1957,13 @@ int CoreSolver::processSimpleDeq( std::vector< Node >& nfi, std::vector< Node >& Trace("strings-solve-debug") << "...Processing(QED) " << i << " " << j << std::endl; if (!d_state.areEqual(i, j)) { - if( i.getKind()==kind::CONST_STRING && j.getKind()==kind::CONST_STRING ) { + if (i.isConst() && j.isConst()) + { size_t lenI = Word::getLength(i); size_t lenJ = Word::getLength(j); unsigned int len_short = lenI < lenJ ? lenI : lenJ; - bool isSameFix = isRev ? i.getConst<String>().rstrncmp(j.getConst<String>(), len_short): i.getConst<String>().strncmp(j.getConst<String>(), len_short); + bool isSameFix = isRev ? Word::rstrncmp(i, j, len_short) + : Word::strncmp(i, j, len_short); if( isSameFix ) { //same prefix/suffix //k is the index of the string that is shorter @@ -2125,6 +2143,7 @@ void CoreSolver::checkNormalFormsDeq() void CoreSolver::checkLengthsEqc() { for (unsigned i = 0; i < d_strings_eqc.size(); i++) { + TypeNode stype = d_strings_eqc[i].getType(); NormalForm& nfi = getNormalForm(d_strings_eqc[i]); Trace("strings-process-debug") << "Process length constraints for " << d_strings_eqc[i] << std::endl; @@ -2141,7 +2160,7 @@ void CoreSolver::checkLengthsEqc() { // now, check if length normalization has occurred if (ei->d_normalizedLength.get().isNull()) { - Node nf = utils::mkNConcat(nfi.d_nf, d_type); + Node nf = utils::mkNConcat(nfi.d_nf, stype); if (Trace.isOn("strings-process-debug")) { Trace("strings-process-debug") @@ -2190,7 +2209,7 @@ void CoreSolver::doInferInfo(const InferInfo& ii) { for (const Node& n : sks.second) { - d_im.registerLength(n, sks.first); + d_im.registerTermAtomic(n, sks.first); } } } diff --git a/src/theory/strings/core_solver.h b/src/theory/strings/core_solver.h index c549fa886..3fea5e8de 100644 --- a/src/theory/strings/core_solver.h +++ b/src/theory/strings/core_solver.h @@ -218,16 +218,21 @@ class CoreSolver * current normal form for each term in this equivalence class is identical. * If it is not, then we add an inference via sendInference and abort the * call. + * + * stype is the string-like type of the equivalence class we are processing. */ - void normalizeEquivalenceClass(Node n); + void normalizeEquivalenceClass(Node n, TypeNode stype); /** * For each term in the equivalence class of eqc, this adds data regarding its * normal form to normal_forms. The map term_to_nf_index maps terms to the * index in normal_forms where their normal form data is located. + * + * stype is the string-like type of the equivalence class we are processing. */ void getNormalForms(Node eqc, std::vector<NormalForm>& normal_forms, - std::map<Node, unsigned>& term_to_nf_index); + std::map<Node, unsigned>& term_to_nf_index, + TypeNode stype); /** process normalize equivalence class * * This is called when an equivalence class contains a set of terms that @@ -240,8 +245,10 @@ class CoreSolver * corresponding to processing the normal form pair in the (forward, reverse) * directions. Once all possible inferences are recorded, it executes the * one with highest priority based on the enumeration type Inference. + * + * stype is the string-like type of the equivalence class we are processing. */ - void processNEqc(std::vector<NormalForm>& normal_forms); + void processNEqc(std::vector<NormalForm>& normal_forms, TypeNode stype); /** process simple normal equality * * This method is called when two equal terms have normal forms nfi and nfj. @@ -265,13 +272,16 @@ class CoreSolver * fowards/backwards traversals of normal forms to ensure that duplicate * inferences are not processed. * pinfer: the set of possible inferences we add to. + * + * stype is the string-like type of the equivalence class we are processing. */ void processSimpleNEq(NormalForm& nfi, NormalForm& nfj, unsigned& index, bool isRev, unsigned rproc, - std::vector<InferInfo>& pinfer); + std::vector<InferInfo>& pinfer, + TypeNode stype); //--------------------------end for checkNormalFormsEq //--------------------------for checkNormalFormsEq with loops @@ -325,7 +335,6 @@ class CoreSolver /** reference to the base solver, used for certain queries */ BaseSolver& d_bsolver; /** Commonly used constants */ - Node d_emptyString; Node d_true; Node d_false; Node d_zero; @@ -368,8 +377,6 @@ class CoreSolver * the argument number of the t1 ... tn they were generated from. */ std::map<Node, std::vector<int> > d_flat_form_index; - /** The string-like type for this solver */ - TypeNode d_type; }; /* class CoreSolver */ } // namespace strings diff --git a/src/theory/strings/eqc_info.cpp b/src/theory/strings/eqc_info.cpp index ab6d473bd..3c0dbc2a7 100644 --- a/src/theory/strings/eqc_info.cpp +++ b/src/theory/strings/eqc_info.cpp @@ -15,6 +15,7 @@ #include "theory/strings/eqc_info.h" #include "theory/strings/theory_strings_utils.h" +#include "theory/strings/word.h" using namespace std; using namespace CVC4::context; @@ -44,13 +45,13 @@ Node EqcInfo::addEndpointConst(Node t, Node c, bool isSuf) << " post=" << isSuf << std::endl; Node prevC = utils::getConstantEndpoint(prev, isSuf); Assert(!prevC.isNull()); - Assert(prevC.getKind() == CONST_STRING); + Assert(prevC.isConst()); if (c.isNull()) { c = utils::getConstantEndpoint(t, isSuf); Assert(!c.isNull()); } - Assert(c.getKind() == CONST_STRING); + Assert(c.isConst()); bool conflict = false; // if the constant prefixes are different if (c != prevC) @@ -59,10 +60,8 @@ Node EqcInfo::addEndpointConst(Node t, Node c, bool isSuf) Assert(!t.isConst() || !prev.isConst()); Trace("strings-eager-pconf-debug") << "Check conflict constants " << prevC << ", " << c << std::endl; - const String& ps = prevC.getConst<String>(); - const String& cs = c.getConst<String>(); - unsigned pvs = ps.size(); - unsigned cvs = cs.size(); + size_t pvs = Word::getLength(prevC); + size_t cvs = Word::getLength(c); if (pvs == cvs || (pvs > cvs && t.isConst()) || (cvs > pvs && prev.isConst())) { @@ -73,15 +72,15 @@ Node EqcInfo::addEndpointConst(Node t, Node c, bool isSuf) } else { - const String& larges = pvs > cvs ? ps : cs; - const String& smalls = pvs > cvs ? cs : ps; + Node larges = pvs > cvs ? prevC : c; + Node smalls = pvs > cvs ? c : prevC; if (isSuf) { - conflict = !larges.hasSuffix(smalls); + conflict = !Word::hasSuffix(larges, smalls); } else { - conflict = !larges.hasPrefix(smalls); + conflict = !Word::hasPrefix(larges, smalls); } } if (!conflict && (pvs > cvs || prev.isConst())) diff --git a/src/theory/strings/extf_solver.cpp b/src/theory/strings/extf_solver.cpp index c586df6dd..a1c04848a 100644 --- a/src/theory/strings/extf_solver.cpp +++ b/src/theory/strings/extf_solver.cpp @@ -34,14 +34,16 @@ ExtfSolver::ExtfSolver(context::Context* c, SkolemCache& skc, BaseSolver& bs, CoreSolver& cs, - ExtTheory* et) + ExtTheory* et, + SequencesStatistics& stats) : d_state(s), d_im(im), d_skCache(skc), d_bsolver(bs), d_csolver(cs), d_extt(et), - d_preproc(&skc, u), + d_statistics(stats), + d_preproc(&skc, u, stats), d_hasExtf(c, false), d_extfInferCache(c) { @@ -112,7 +114,7 @@ bool ExtfSolver::doReduction(int effort, Node n, bool& isCd) lexp.push_back(lenx.eqNode(lens)); lexp.push_back(n.negate()); Node xneqs = x.eqNode(s).negate(); - d_im.sendInference(lexp, xneqs, "NEG-CTN-EQL", true); + d_im.sendInference(lexp, xneqs, Inference::CTN_NEG_EQUAL, true); } // this depends on the current assertions, so we set that this // inference is context-dependent. @@ -157,7 +159,7 @@ bool ExtfSolver::doReduction(int effort, Node n, bool& isCd) Node eq = Rewriter::rewrite(x.eqNode(utils::mkNConcat(sk1, s, sk2))); std::vector<Node> exp_vec; exp_vec.push_back(n); - d_im.sendInference(d_emptyVec, exp_vec, eq, "POS-CTN", true); + d_im.sendInference(d_emptyVec, exp_vec, eq, Inference::CTN_POS, true); Trace("strings-extf-debug") << " resolve extf : " << n << " based on positive contain reduction." << std::endl; @@ -183,7 +185,7 @@ bool ExtfSolver::doReduction(int effort, Node n, bool& isCd) Trace("strings-red-lemma") << "Reduction_" << effort << " lemma : " << nnlem << std::endl; Trace("strings-red-lemma") << "...from " << n << std::endl; - d_im.sendInference(d_emptyVec, nnlem, "Reduction", true); + d_im.sendInference(d_emptyVec, nnlem, Inference::REDUCTION, true); Trace("strings-extf-debug") << " resolve extf : " << n << " based on reduction." << std::endl; isCd = false; @@ -362,8 +364,9 @@ void ExtfSolver::checkExtfEval(int effort) { Trace("strings-extf") << " resolve extf : " << sn << " -> " << nrc << std::endl; - d_im.sendInference( - einfo.d_exp, conc, effort == 0 ? "EXTF" : "EXTF-N", true); + Inference inf = effort == 0 ? Inference::EXTF : Inference::EXTF_N; + d_im.sendInference(einfo.d_exp, conc, inf, true); + d_statistics.d_cdSimplifications << n.getKind(); if (d_state.isInConflict()) { Trace("strings-extf-debug") << " conflict, return." << std::endl; @@ -513,7 +516,7 @@ void ExtfSolver::checkExtfInference(Node n, if (d_state.areEqual(conc, d_false)) { // we are in conflict - d_im.sendInference(in.d_exp, conc, "CTN_Decompose"); + d_im.sendInference(in.d_exp, conc, Inference::CTN_DECOMPOSE); } else if (d_extt->hasFunctionKind(conc.getKind())) { @@ -590,7 +593,7 @@ void ExtfSolver::checkExtfInference(Node n, exp_c.insert(exp_c.end(), d_extfInfoTmp[ofrom].d_exp.begin(), d_extfInfoTmp[ofrom].d_exp.end()); - d_im.sendInference(exp_c, conc, "CTN_Trans"); + d_im.sendInference(exp_c, conc, Inference::CTN_TRANS); } } } @@ -645,7 +648,7 @@ Node ExtfSolver::getCurrentSubstitutionFor(int effort, { return c; } - else if (effort >= 1 && n.getType().isString()) + else if (effort >= 1 && n.getType().isStringLike()) { Assert(effort < 3); // normal forms diff --git a/src/theory/strings/extf_solver.h b/src/theory/strings/extf_solver.h index 4c848f430..9ca72fed2 100644 --- a/src/theory/strings/extf_solver.h +++ b/src/theory/strings/extf_solver.h @@ -26,6 +26,7 @@ #include "theory/strings/base_solver.h" #include "theory/strings/core_solver.h" #include "theory/strings/inference_manager.h" +#include "theory/strings/sequences_stats.h" #include "theory/strings/skolem_cache.h" #include "theory/strings/solver_state.h" #include "theory/strings/theory_strings_preprocess.h" @@ -88,7 +89,8 @@ class ExtfSolver SkolemCache& skc, BaseSolver& bs, CoreSolver& cs, - ExtTheory* et); + ExtTheory* et, + SequencesStatistics& stats); ~ExtfSolver(); /** check extended functions evaluation @@ -184,6 +186,8 @@ class ExtfSolver CoreSolver& d_csolver; /** the extended theory object for the theory of strings */ ExtTheory* d_extt; + /** Reference to the statistics for the theory of strings/sequences. */ + SequencesStatistics& d_statistics; /** preprocessing utility, for performing strings reductions */ StringsPreprocess d_preproc; /** Common constants */ diff --git a/src/theory/strings/infer_info.cpp b/src/theory/strings/infer_info.cpp index aa7fe8974..1d0ee30ad 100644 --- a/src/theory/strings/infer_info.cpp +++ b/src/theory/strings/infer_info.cpp @@ -14,30 +14,54 @@ #include "theory/strings/infer_info.h" -using namespace CVC4::kind; - namespace CVC4 { namespace theory { namespace strings { -std::ostream& operator<<(std::ostream& out, Inference i) +const char* toString(Inference i) { switch (i) { - case Inference::N_ENDPOINT_EMP: out << "N_EndpointEmp"; break; - case Inference::N_UNIFY: out << "N_Unify"; break; - case Inference::N_ENDPOINT_EQ: out << "N_EndpointEq"; break; - case Inference::N_CONST: out << "N_Const"; break; - case Inference::INFER_EMP: out << "Infer-Emp"; break; - case Inference::SSPLIT_CST_PROP: out << "S-Split(CST-P)-prop"; break; - case Inference::SSPLIT_VAR_PROP: out << "S-Split(VAR)-prop"; break; - case Inference::LEN_SPLIT: out << "Len-Split(Len)"; break; - case Inference::LEN_SPLIT_EMP: out << "Len-Split(Emp)"; break; - case Inference::SSPLIT_CST: out << "S-Split(CST-P)"; break; - case Inference::SSPLIT_VAR: out << "S-Split(VAR)"; break; - case Inference::FLOOP: out << "F-Loop"; break; - default: out << "?"; break; + case Inference::I_NORM_S: return "I_NORM_S"; + case Inference::I_CONST_MERGE: return "I_CONST_MERGE"; + case Inference::I_CONST_CONFLICT: return "I_CONST_CONFLICT"; + case Inference::I_NORM: return "I_NORM"; + case Inference::CARDINALITY: return "CARDINALITY"; + case Inference::N_ENDPOINT_EMP: return "N_ENDPOINT_EMP"; + case Inference::N_UNIFY: return "N_UNIFY"; + case Inference::N_ENDPOINT_EQ: return "N_ENDPOINT_EQ"; + case Inference::N_CONST: return "N_CONST"; + case Inference::INFER_EMP: return "INFER_EMP"; + case Inference::SSPLIT_CST_PROP: return "SSPLIT_CST_PROP"; + case Inference::SSPLIT_VAR_PROP: return "SSPLIT_VAR_PROP"; + case Inference::LEN_SPLIT: return "LEN_SPLIT"; + case Inference::LEN_SPLIT_EMP: return "LEN_SPLIT_EMP"; + case Inference::SSPLIT_CST: return "SSPLIT_CST"; + case Inference::SSPLIT_VAR: return "SSPLIT_VAR"; + case Inference::FLOOP: return "FLOOP"; + case Inference::RE_NF_CONFLICT: return "RE_NF_CONFLICT"; + case Inference::RE_UNFOLD_POS: return "RE_UNFOLD_POS"; + case Inference::RE_UNFOLD_NEG: return "RE_UNFOLD_NEG"; + case Inference::RE_INTER_INCLUDE: return "RE_INTER_INCLUDE"; + case Inference::RE_INTER_CONF: return "RE_INTER_CONF"; + case Inference::RE_INTER_INFER: return "RE_INTER_INFER"; + case Inference::RE_DELTA: return "RE_DELTA"; + case Inference::RE_DELTA_CONF: return "RE_DELTA_CONF"; + case Inference::RE_DERIVE: return "RE_DERIVE"; + case Inference::EXTF: return "EXTF"; + case Inference::EXTF_N: return "EXTF_N"; + case Inference::CTN_TRANS: return "CTN_TRANS"; + case Inference::CTN_DECOMPOSE: return "CTN_DECOMPOSE"; + case Inference::CTN_NEG_EQUAL: return "CTN_NEG_EQUAL"; + case Inference::CTN_POS: return "CTN_POS"; + case Inference::REDUCTION: return "REDUCTION"; + default: return "?"; } +} + +std::ostream& operator<<(std::ostream& out, Inference i) +{ + out << toString(i); return out; } diff --git a/src/theory/strings/infer_info.h b/src/theory/strings/infer_info.h index 9d2f71b53..252445cb4 100644 --- a/src/theory/strings/infer_info.h +++ b/src/theory/strings/infer_info.h @@ -19,7 +19,9 @@ #include <map> #include <vector> + #include "expr/node.h" +#include "util/safe_print.h" namespace CVC4 { namespace theory { @@ -36,6 +38,31 @@ namespace strings { */ enum class Inference : uint32_t { + //-------------------------------------- base solver + // initial normalize singular + // x1 = "" ^ ... ^ x_{i-1} = "" ^ x_{i+1} = "" ^ ... ^ xn = "" => + // x1 ++ ... ++ xn = xi + I_NORM_S, + // initial constant merge + // explain_constant(x, c) => x = c + // Above, explain_constant(x,c) is a basic explanation of why x must be equal + // to string constant c, which is computed by taking arguments of + // concatentation terms that are entailed to be constants. For example: + // ( y = "AB" ^ z = "C" ) => y ++ z = "ABC" + I_CONST_MERGE, + // initial constant conflict + // ( explain_constant(x, c1) ^ explain_constant(x, c2) ^ x = y) => false + // where c1 != c2. + I_CONST_CONFLICT, + // initial normalize + // Given two concatenation terms, this is applied when we find that they are + // equal after e.g. removing strings that are currently empty. For example: + // y = "" ^ z = "" => x ++ y = z ++ x + I_NORM, + // The cardinality inference for strings, see Liang et al CAV 2014. + CARDINALITY, + //-------------------------------------- end base solver + //-------------------------------------- core solver // Given two normal forms, infers that the remainder one of them has to be // empty. For example: // If x1 ++ x2 = y1 and x1 = y1, then x2 = "" @@ -92,8 +119,94 @@ enum class Inference : uint32_t // for fresh u, u1, u2. // This is the rule F-Loop from Figure 5 of Liang et al CAV 2014. FLOOP, + //-------------------------------------- end core solver + //-------------------------------------- regexp solver + // regular expression normal form conflict + // ( x in R ^ x = y ^ rewrite((str.in_re y R)) = false ) => false + // where y is the normal form computed for x. + RE_NF_CONFLICT, + // regular expression unfolding + // This is a general class of inferences of the form: + // (x in R) => F + // where F is formula expressing the next step of checking whether x is in + // R. For example: + // (x in (R)*) => + // x = "" V x in R V ( x = x1 ++ x2 ++ x3 ^ x1 in R ^ x2 in (R)* ^ x3 in R) + RE_UNFOLD_POS, + // Same as above, for negative memberships + RE_UNFOLD_NEG, + // intersection inclusion conflict + // (x in R1 ^ ~ x in R2) => false where [[includes(R2,R1)]] + // Where includes(R2,R1) is a heuristic check for whether R2 includes R1. + RE_INTER_INCLUDE, + // intersection conflict, using regexp intersection computation + // (x in R1 ^ x in R2) => false where [[intersect(R1, R2) = empty]] + RE_INTER_CONF, + // intersection inference + // (x in R1 ^ y in R2 ^ x = y) => (x in re.inter(R1,R2)) + RE_INTER_INFER, + // regular expression delta + // (x = "" ^ x in R) => C + // where "" in R holds if and only if C holds. + RE_DELTA, + // regular expression delta conflict + // (x = "" ^ x in R) => false + // where R does not accept the empty string. + RE_DELTA_CONF, + // regular expression derive ??? + RE_DERIVE, + //-------------------------------------- end regexp solver + //-------------------------------------- extended function solver + // All extended function inferences from context-dependent rewriting produced + // by constant substitutions. See Reynolds et al CAV 2017. These are + // inferences of the form: + // X = Y => f(X) = t when rewrite( f(Y) ) = t + // where X = Y is a vector of equalities, where some of Y may be constants. + EXTF, + // Same as above, for normal form substitutions. + EXTF_N, + // contain transitive + // ( str.contains( s, t ) ^ ~contains( s, r ) ) => ~contains( t, r ). + CTN_TRANS, + // contain decompose + // str.contains( x, str.++( y1, ..., yn ) ) => str.contains( x, yi ) or + // ~str.contains( str.++( x1, ..., xn ), y ) => ~str.contains( xi, y ) + CTN_DECOMPOSE, + // contain neg equal + // ( len( x ) = len( s ) ^ ~contains( x, s ) ) => x != s + CTN_NEG_EQUAL, + // contain positive + // str.contains( x, y ) => x = w1 ++ y ++ w2 + // where w1 and w2 are skolem variables. + CTN_POS, + // All reduction inferences of the form: + // f(x1, .., xn) = y ^ P(x1, ..., xn, y) + // where f is an extended function, y is the purification variable for + // f(x1, .., xn) and P is the reduction predicate for f + // (see theory_strings_preprocess). + REDUCTION, + //-------------------------------------- end extended function solver NONE, }; + +/** + * Converts an inference to a string. Note: This function is also used in + * `safe_print()`. Changing this functions name or signature will result in + * `safe_print()` printing "<unsupported>" instead of the proper strings for + * the enum values. + * + * @param i The inference + * @return The name of the inference + */ +const char* toString(Inference i); + +/** + * Writes an inference name to a stream. + * + * @param out The stream to write to + * @param i The inference to write to the stream + * @return The stream + */ std::ostream& operator<<(std::ostream& out, Inference i); /** @@ -175,4 +288,4 @@ class InferInfo } // namespace theory } // namespace CVC4 -#endif /* CVC4__THEORY__STRINGS__THEORY_STRINGS_H */ +#endif /* CVC4__THEORY__STRINGS__INFER_INFO_H */ diff --git a/src/theory/strings/inference_manager.cpp b/src/theory/strings/inference_manager.cpp index eebad2274..cb0c807cc 100644 --- a/src/theory/strings/inference_manager.cpp +++ b/src/theory/strings/inference_manager.cpp @@ -20,6 +20,7 @@ #include "theory/rewriter.h" #include "theory/strings/theory_strings.h" #include "theory/strings/theory_strings_utils.h" +#include "theory/strings/word.h" using namespace std; using namespace CVC4::context; @@ -225,6 +226,7 @@ void InferenceManager::sendLemma(Node ant, Node conc, const char* c) << std::endl; Trace("strings-assert") << "(assert (not " << ant << ")) ; conflict " << c << std::endl; + ++(d_statistics.d_conflictsInfer); d_out.conflict(ant); d_state.setConflict(); return; @@ -368,29 +370,29 @@ Node InferenceManager::getSymbolicDefinition(Node n, return NodeManager::currentNM()->mkNode(n.getKind(), children); } -void InferenceManager::registerLength(Node n) +Node InferenceManager::registerTerm(Node n) { + Assert(n.getType().isStringLike()); NodeManager* nm = NodeManager::currentNM(); // register length information: // for variables, split on empty vs positive length // for concat/const/replace, introduce proxy var and state length relation Node lsum; - if (n.getKind() != STRING_CONCAT && n.getKind() != CONST_STRING) + if (n.getKind() != STRING_CONCAT && !n.isConst()) { Node lsumb = nm->mkNode(STRING_LENGTH, n); lsum = Rewriter::rewrite(lsumb); // can register length term if it does not rewrite if (lsum == lsumb) { - registerLength(n, LENGTH_SPLIT); - return; + registerTermAtomic(n, LENGTH_SPLIT); + return Node::null(); } } Node sk = d_skCache.mkSkolemCached(n, SkolemCache::SK_PURIFY, "lsym"); StringsProxyVarAttribute spva; sk.setAttribute(spva, true); Node eq = Rewriter::rewrite(sk.eqNode(n)); - Trace("strings-lemma") << "Strings::Lemma LENGTH Term : " << eq << std::endl; d_proxyVar[n] = sk; // If we are introducing a proxy for a constant or concat term, we do not // need to send lemmas about its length, since its length is already @@ -398,10 +400,8 @@ void InferenceManager::registerLength(Node n) if (n.isConst() || n.getKind() == STRING_CONCAT) { // do not send length lemma for sk. - registerLength(sk, LENGTH_IGNORE); + registerTermAtomic(sk, LENGTH_IGNORE); } - Trace("strings-assert") << "(assert " << eq << ")" << std::endl; - d_out.lemma(eq); Node skl = nm->mkNode(STRING_LENGTH, sk); if (n.getKind() == STRING_CONCAT) { @@ -422,21 +422,18 @@ void InferenceManager::registerLength(Node n) lsum = nm->mkNode(PLUS, nodeVec); lsum = Rewriter::rewrite(lsum); } - else if (n.getKind() == CONST_STRING) + else if (n.isConst()) { - lsum = nm->mkConst(Rational(n.getConst<String>().size())); + lsum = nm->mkConst(Rational(Word::getLength(n))); } Assert(!lsum.isNull()); d_proxyVarToLength[sk] = lsum; Node ceq = Rewriter::rewrite(skl.eqNode(lsum)); - Trace("strings-lemma") << "Strings::Lemma LENGTH : " << ceq << std::endl; - Trace("strings-lemma-debug") - << " prerewrite : " << skl.eqNode(lsum) << std::endl; - Trace("strings-assert") << "(assert " << ceq << ")" << std::endl; - d_out.lemma(ceq); + + return nm->mkNode(AND, eq, ceq); } -void InferenceManager::registerLength(Node n, LengthStatus s) +void InferenceManager::registerTermAtomic(Node n, LengthStatus s) { if (d_lengthLemmaTermsCache.find(n) != d_lengthLemmaTermsCache.end()) { @@ -449,7 +446,25 @@ void InferenceManager::registerLength(Node n, LengthStatus s) // ignore it return; } + std::map<Node, bool> reqPhase; + Node lenLem = getRegisterTermAtomicLemma(n, s, reqPhase); + if (!lenLem.isNull()) + { + Trace("strings-lemma") << "Strings::Lemma REGISTER-TERM-ATOMIC : " << lenLem + << std::endl; + Trace("strings-assert") << "(assert " << lenLem << ")" << std::endl; + ++(d_statistics.d_lemmasRegisterTermAtomic); + d_out.lemma(lenLem); + } + for (const std::pair<const Node, bool>& rp : reqPhase) + { + d_out.requirePhase(rp.first, rp.second); + } +} +Node InferenceManager::getRegisterTermAtomicLemma( + Node n, LengthStatus s, std::map<Node, bool>& reqPhase) +{ NodeManager* nm = NodeManager::currentNM(); Node n_len = nm->mkNode(kind::STRING_LENGTH, n); @@ -461,8 +476,7 @@ void InferenceManager::registerLength(Node n, LengthStatus s) Trace("strings-lemma") << "Strings::Lemma SK-GEQ-ONE : " << len_geq_one << std::endl; Trace("strings-assert") << "(assert " << len_geq_one << ")" << std::endl; - d_out.lemma(len_geq_one); - return; + return len_geq_one; } if (s == LENGTH_ONE) @@ -471,11 +485,11 @@ void InferenceManager::registerLength(Node n, LengthStatus s) Trace("strings-lemma") << "Strings::Lemma SK-ONE : " << len_one << std::endl; Trace("strings-assert") << "(assert " << len_one << ")" << std::endl; - d_out.lemma(len_one); - return; + return len_one; } Assert(s == LENGTH_SPLIT); + std::vector<Node> lems; if (options::stringSplitEmp() || !options::stringLenGeqZ()) { Node n_len_eq_z = n_len.eqNode(d_zero); @@ -486,7 +500,7 @@ void InferenceManager::registerLength(Node n, LengthStatus s) if (!case_empty.isConst()) { Node lem = nm->mkNode(OR, case_empty, case_nempty); - d_out.lemma(lem); + lems.push_back(lem); Trace("strings-lemma") << "Strings::Lemma LENGTH >= 0 : " << lem << std::endl; // prefer trying the empty case first @@ -494,10 +508,10 @@ void InferenceManager::registerLength(Node n, LengthStatus s) // occur in the CNF stream. n_len_eq_z = Rewriter::rewrite(n_len_eq_z); Assert(!n_len_eq_z.isConst()); - d_out.requirePhase(n_len_eq_z, true); + reqPhase[n_len_eq_z] = true; n_len_eq_z_2 = Rewriter::rewrite(n_len_eq_z_2); Assert(!n_len_eq_z_2.isConst()); - d_out.requirePhase(n_len_eq_z_2, true); + reqPhase[n_len_eq_z_2] = true; } else if (!case_empty.getConst<bool>()) { @@ -505,7 +519,7 @@ void InferenceManager::registerLength(Node n, LengthStatus s) Trace("strings-lemma") << "Strings::Lemma LENGTH > 0 (non-empty): " << case_nempty << std::endl; - d_out.lemma(case_nempty); + lems.push_back(case_nempty); } else { @@ -521,8 +535,14 @@ void InferenceManager::registerLength(Node n, LengthStatus s) { Node n_len_geq = nm->mkNode(kind::GEQ, n_len, d_zero); n_len_geq = Rewriter::rewrite(n_len_geq); - d_out.lemma(n_len_geq); + lems.push_back(n_len_geq); + } + + if (lems.empty()) + { + return Node::null(); } + return lems.size() == 1 ? lems[0] : nm->mkNode(AND, lems); } void InferenceManager::addToExplanation(Node a, diff --git a/src/theory/strings/inference_manager.h b/src/theory/strings/inference_manager.h index c9d483c73..bd2f85d29 100644 --- a/src/theory/strings/inference_manager.h +++ b/src/theory/strings/inference_manager.h @@ -210,18 +210,23 @@ class InferenceManager * exists, otherwise it returns null. */ Node getProxyVariableFor(Node n) const; - /** register length + /** register term + * + * This method is called on non-constant string terms n. It returns a lemma + * that should be sent on the output channel of theory of strings upon + * registration of this term, or null if no lemma is necessary. * - * This method is called on non-constant string terms n. It sends a lemma - * on the output channel that ensures that the length n satisfies its assigned - * status (given by argument s). + * If n is an atomic term, the method registerTermAtomic is called for n + * and s = LENGTH_SPLIT and no lemma is returned. */ - void registerLength(Node n); + Node registerTerm(Node n); /** register length * - * This method is called on non-constant string terms n. It sends a lemma - * on the output channel that ensures that the length n satisfies its assigned - * status (given by argument s). + * This method is called on non-constant string terms n that are "atomic" + * with respect to length. That is, the rewritten form of len(n) is itself. + * + * It sends a lemma on the output channel that ensures that the length n + * satisfies its assigned status (given by argument s). * * If the status is LENGTH_ONE, we send the lemma len( n ) = 1. * @@ -238,7 +243,7 @@ class InferenceManager * In contrast to the above functions, it makes immediate calls to the output * channel instead of adding them to pending lists. */ - void registerLength(Node n, LengthStatus s); + void registerTermAtomic(Node n, LengthStatus s); //---------------------------- end proxy variables and length elaboration //----------------------------constructing antecedants @@ -337,6 +342,17 @@ class InferenceManager * equality engine of this class. */ void sendInfer(Node eq_exp, Node eq, const char* c); + /** + * Get the lemma required for registering the length information for + * atomic term n given length status s. For details, see registerTermAtomic. + * + * Additionally, this method may map literals to a required polarity in the + * argument reqPhase, which should be processed by a call to requiredPhase by + * the caller of this method. + */ + Node getRegisterTermAtomicLemma(Node n, + LengthStatus s, + std::map<Node, bool>& reqPhase); /** the parent theory of strings object */ TheoryStrings& d_parent; diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds index 5b988061b..06f05a8af 100644 --- a/src/theory/strings/kinds +++ b/src/theory/strings/kinds @@ -37,15 +37,15 @@ operator STRING_REV 1 "string reverse" sort STRING_TYPE \ Cardinality::INTEGERS \ well-founded \ - "NodeManager::currentNM()->mkConst(::CVC4::String())" \ - "util/regexp.h" \ + "NodeManager::currentNM()->mkConst(::CVC4::String())" \ + "util/string.h" \ "String type" sort REGEXP_TYPE \ Cardinality::INTEGERS \ well-founded \ - "NodeManager::currentNM()->mkNode(REGEXP_EMPTY, std::vector<Node>() )" \ - "util/regexp.h" \ + "NodeManager::currentNM()->mkNode(REGEXP_EMPTY, std::vector<Node>() )" \ + "util/string.h" \ "RegExp type" enumerator STRING_TYPE \ @@ -55,7 +55,7 @@ enumerator STRING_TYPE \ constant CONST_STRING \ ::CVC4::String \ ::CVC4::strings::StringHashFunction \ - "util/regexp.h" \ + "util/string.h" \ "a string of characters" # equal equal / less than / output @@ -68,12 +68,25 @@ operator REGEXP_STAR 1 "regexp *" operator REGEXP_PLUS 1 "regexp +" operator REGEXP_OPT 1 "regexp ?" operator REGEXP_RANGE 2 "regexp range" -operator REGEXP_LOOP 2:3 "regexp loop" operator REGEXP_COMPLEMENT 1 "regexp complement" operator REGEXP_EMPTY 0 "regexp empty" operator REGEXP_SIGMA 0 "regexp all characters" +constant REGEXP_REPEAT_OP \ + ::CVC4::RegExpRepeat \ + ::CVC4::RegExpRepeatHashFunction \ + "util/regexp.h" \ + "operator for regular expression repeat; payload is an instance of the CVC4::RegExpRepeat class" +parameterized REGEXP_REPEAT REGEXP_REPEAT_OP 1 "regular expression repeat; first parameter is a REGEXP_REPEAT_OP, second is a regular expression term" + +constant REGEXP_LOOP_OP \ + ::CVC4::RegExpLoop \ + ::CVC4::RegExpLoopHashFunction \ + "util/regexp.h" \ + "operator for regular expression loop; payload is an instance of the CVC4::RegExpLoop class" +parameterized REGEXP_LOOP REGEXP_LOOP_OP 1 "regular expression loop; first parameter is a REGEXP_LOOP_OP, second is a regular expression term" + #internal operator REGEXP_RV 1 "regexp rv (internal use only)" typerule REGEXP_RV "SimpleTypeRule<RRegExp, AInteger>" @@ -88,7 +101,10 @@ typerule REGEXP_STAR "SimpleTypeRule<RRegExp, ARegExp>" typerule REGEXP_PLUS "SimpleTypeRule<RRegExp, ARegExp>" typerule REGEXP_OPT "SimpleTypeRule<RRegExp, ARegExp>" typerule REGEXP_RANGE ::CVC4::theory::strings::RegExpRangeTypeRule -typerule REGEXP_LOOP "SimpleTypeRule<RRegExp, ARegExp, AInteger, AOptional<AInteger>>" +typerule REGEXP_REPEAT_OP "SimpleTypeRule<RBuiltinOperator>" +typerule REGEXP_REPEAT "SimpleTypeRule<RRegExp, ARegExp>" +typerule REGEXP_LOOP_OP "SimpleTypeRule<RBuiltinOperator>" +typerule REGEXP_LOOP "SimpleTypeRule<RRegExp, ARegExp>" typerule REGEXP_COMPLEMENT "SimpleTypeRule<RRegExp, ARegExp>" typerule STRING_TO_REGEXP "SimpleTypeRule<RRegExp, AString>" typerule STRING_IN_REGEXP "SimpleTypeRule<RBool, AString, ARegExp>" diff --git a/src/theory/strings/normal_form.cpp b/src/theory/strings/normal_form.cpp index 7a2323d89..05be5f12a 100644 --- a/src/theory/strings/normal_form.cpp +++ b/src/theory/strings/normal_form.cpp @@ -18,6 +18,7 @@ #include "options/strings_options.h" #include "theory/rewriter.h" #include "theory/strings/theory_strings_utils.h" +#include "theory/strings/word.h" using namespace std; using namespace CVC4::kind; @@ -28,7 +29,7 @@ namespace strings { void NormalForm::init(Node base) { - Assert(base.getType().isString()); + Assert(base.getType().isStringLike()); Assert(base.getKind() != STRING_CONCAT); d_base = base; d_nf.clear(); @@ -37,7 +38,7 @@ void NormalForm::init(Node base) d_expDep.clear(); // add to normal form - if (!base.isConst() || !base.getConst<String>().isEmptyString()) + if (!base.isConst() || Word::getLength(base) > 0) { d_nf.push_back(base); } diff --git a/src/theory/strings/regexp_operation.cpp b/src/theory/strings/regexp_operation.cpp index d5105a489..d104f3ade 100644 --- a/src/theory/strings/regexp_operation.cpp +++ b/src/theory/strings/regexp_operation.cpp @@ -93,15 +93,7 @@ RegExpConstType RegExpOpr::getRegExpConstType(Node r) { d_constCache[cur] = RE_C_UNKNOWN; visit.push_back(cur); - if (ck == REGEXP_LOOP) - { - // only add the first child of loop - visit.push_back(cur[0]); - } - else - { - visit.insert(visit.end(), cur.begin(), cur.end()); - } + visit.insert(visit.end(), cur.begin(), cur.end()); } } else if (it->second == RE_C_UNKNOWN) @@ -260,7 +252,9 @@ int RegExpOpr::delta( Node r, Node &exp ) { break; } case kind::REGEXP_LOOP: { - if(r[1] == d_zero) { + uint32_t lo = utils::getLoopMinOccurrences(r); + if (lo == 0) + { ret = 1; } else { ret = delta(r[0], exp); @@ -501,18 +495,18 @@ int RegExpOpr::derivativeS( Node r, CVC4::String c, Node &retNode ) { break; } case kind::REGEXP_LOOP: { - if(r[1] == r[2] && r[1] == d_zero) { + uint32_t l = utils::getLoopMinOccurrences(r); + uint32_t u = utils::getLoopMaxOccurrences(r); + if (l == u && l == 0) + { ret = 2; //retNode = d_emptyRegexp; } else { Node dc; ret = derivativeS(r[0], c, dc); if(dc==d_emptyRegexp) { - unsigned l = r[1].getConst<Rational>().getNumerator().toUnsignedInt(); - unsigned u = r[2].getConst<Rational>().getNumerator().toUnsignedInt(); - Node r2 = NodeManager::currentNM()->mkNode(kind::REGEXP_LOOP, r[0], - NodeManager::currentNM()->mkConst(CVC4::Rational(l==0? 0 : (l-1))), - NodeManager::currentNM()->mkConst(CVC4::Rational(u-1))); + Node lop = nm->mkConst(RegExpLoop(l == 0 ? 0 : (l - 1), u - 1)); + Node r2 = nm->mkNode(REGEXP_LOOP, lop, r[0]); retNode = dc==d_emptySingleton? r2 : NodeManager::currentNM()->mkNode( kind::REGEXP_CONCAT, dc, r2 ); } else { retNode = d_emptyRegexp; @@ -686,16 +680,16 @@ Node RegExpOpr::derivativeSingle( Node r, CVC4::String c ) { break; } case kind::REGEXP_LOOP: { - if(r[1] == r[2] && r[1] == d_zero) { + uint32_t l = utils::getLoopMinOccurrences(r); + uint32_t u = utils::getLoopMaxOccurrences(r); + if (l == u || l == 0) + { retNode = d_emptyRegexp; } else { Node dc = derivativeSingle(r[0], c); if(dc != d_emptyRegexp) { - unsigned l = r[1].getConst<Rational>().getNumerator().toUnsignedInt(); - unsigned u = r[2].getConst<Rational>().getNumerator().toUnsignedInt(); - Node r2 = NodeManager::currentNM()->mkNode(kind::REGEXP_LOOP, r[0], - NodeManager::currentNM()->mkConst(CVC4::Rational(l==0? 0 : (l-1))), - NodeManager::currentNM()->mkConst(CVC4::Rational(u-1))); + Node lop = nm->mkConst(RegExpLoop(l == 0 ? 0 : (l - 1), u - 1)); + Node r2 = nm->mkNode(REGEXP_LOOP, lop, r[0]); retNode = dc==d_emptySingleton? r2 : NodeManager::currentNM()->mkNode( kind::REGEXP_CONCAT, dc, r2 ); } else { retNode = d_emptyRegexp; @@ -739,9 +733,7 @@ void RegExpOpr::firstChars(Node r, std::set<unsigned> &pcset, SetNodes &pvset) } case kind::REGEXP_RANGE: { unsigned a = r[0].getConst<String>().front(); - a = String::convertUnsignedIntToCode(a); unsigned b = r[1].getConst<String>().front(); - b = String::convertUnsignedIntToCode(b); Assert(a < b); Assert(b < std::numeric_limits<unsigned>::max()); for (unsigned c = a; c <= b; c++) @@ -756,7 +748,6 @@ void RegExpOpr::firstChars(Node r, std::set<unsigned> &pcset, SetNodes &pvset) String s = st.getConst<String>(); if(s.size() != 0) { unsigned sc = s.front(); - sc = String::convertUnsignedIntToCode(sc); cset.insert(sc); } } @@ -765,7 +756,6 @@ void RegExpOpr::firstChars(Node r, std::set<unsigned> &pcset, SetNodes &pvset) if(st[0].isConst()) { String s = st[0].getConst<String>(); unsigned sc = s.front(); - sc = String::convertUnsignedIntToCode(sc); cset.insert(sc); } else { vset.insert( st[0] ); @@ -854,8 +844,8 @@ void RegExpOpr::firstChars(Node r, std::set<unsigned> &pcset, SetNodes &pvset) void RegExpOpr::simplify(Node t, std::vector< Node > &new_nodes, bool polarity) { Trace("strings-regexp-simpl") << "RegExp-Simpl starts with " << t << ", polarity=" << polarity << std::endl; Assert(t.getKind() == kind::STRING_IN_REGEXP); - Node str = Rewriter::rewrite(t[0]); - Node re = Rewriter::rewrite(t[1]); + Node str = t[0]; + Node re = t[1]; if(polarity) { simplifyPRegExp( str, re, new_nodes ); } else { @@ -887,13 +877,11 @@ void RegExpOpr::simplifyNRegExp( Node s, Node r, std::vector< Node > &new_nodes case kind::REGEXP_RANGE: { std::vector< Node > vec; unsigned a = r[0].getConst<String>().front(); - a = String::convertUnsignedIntToCode(a); unsigned b = r[1].getConst<String>().front(); - b = String::convertUnsignedIntToCode(b); for (unsigned c = a; c <= b; c++) { std::vector<unsigned> tmpVec; - tmpVec.push_back(String::convertCodeToUnsignedInt(c)); + tmpVec.push_back(c); Node tmp = s.eqNode(nm->mkConst(String(tmpVec))).negate(); vec.push_back( tmp ); } @@ -1522,7 +1510,7 @@ Node RegExpOpr::intersectInternal( Node r1, Node r2, std::map< PairNodes, Node > ++it) { std::vector<unsigned> cvec; - cvec.push_back(String::convertCodeToUnsignedInt(*it)); + cvec.push_back(*it); String c(cvec); Trace("regexp-int-debug") << "Try character " << c << " ... " << std::endl; Node r1l = derivativeSingle(r1, c); diff --git a/src/theory/strings/regexp_operation.h b/src/theory/strings/regexp_operation.h index b9dbedba5..7845b2e00 100644 --- a/src/theory/strings/regexp_operation.h +++ b/src/theory/strings/regexp_operation.h @@ -24,10 +24,9 @@ #include <algorithm> #include <climits> #include "util/hash.h" -#include "util/regexp.h" +#include "util/string.h" #include "theory/theory.h" #include "theory/rewriter.h" -//#include "context/cdhashmap.h" namespace CVC4 { namespace theory { diff --git a/src/theory/strings/regexp_solver.cpp b/src/theory/strings/regexp_solver.cpp index 30f9c4a73..f6ef92b4d 100644 --- a/src/theory/strings/regexp_solver.cpp +++ b/src/theory/strings/regexp_solver.cpp @@ -36,12 +36,14 @@ RegExpSolver::RegExpSolver(TheoryStrings& p, SolverState& s, InferenceManager& im, ExtfSolver& es, + SequencesStatistics& stats, context::Context* c, context::UserContext* u) : d_parent(p), d_state(s), d_im(im), d_esolver(es), + d_statistics(stats), d_regexp_ucached(u), d_regexp_ccached(c), d_processed_memberships(c) @@ -160,6 +162,7 @@ void RegExpSolver::check(const std::map<Node, std::vector<Node> >& mems) << "We have regular expression assertion : " << assertion << std::endl; Node atom = assertion.getKind() == NOT ? assertion[0] : assertion; + Assert(atom == Rewriter::rewrite(atom)); bool polarity = assertion.getKind() != NOT; if (polarity != (e == 0)) { @@ -222,7 +225,7 @@ void RegExpSolver::check(const std::map<Node, std::vector<Node> >& mems) std::vector<Node> exp_n; exp_n.push_back(assertion); Node conc = Node::null(); - d_im.sendInference(nfexp, exp_n, conc, "REGEXP NF Conflict"); + d_im.sendInference(nfexp, exp_n, conc, Inference::RE_NF_CONFLICT); addedLemma = true; break; } @@ -268,7 +271,18 @@ void RegExpSolver::check(const std::map<Node, std::vector<Node> >& mems) std::vector<Node> exp_n; exp_n.push_back(assertion); Node conc = nvec.size() == 1 ? nvec[0] : nm->mkNode(AND, nvec); - d_im.sendInference(rnfexp, exp_n, conc, "REGEXP_Unfold"); + Assert(atom.getKind() == STRING_IN_REGEXP); + if (polarity) + { + d_statistics.d_regexpUnfoldingsPos << atom[1].getKind(); + } + else + { + d_statistics.d_regexpUnfoldingsNeg << atom[1].getKind(); + } + Inference inf = + polarity ? Inference::RE_UNFOLD_POS : Inference::RE_UNFOLD_NEG; + d_im.sendInference(rnfexp, exp_n, conc, inf); addedLemma = true; if (changed) { @@ -387,7 +401,8 @@ bool RegExpSolver::checkEqcInclusion(std::vector<Node>& mems) } Node conc; - d_im.sendInference(vec_nodes, conc, "Intersect inclusion", true); + d_im.sendInference( + vec_nodes, conc, Inference::RE_INTER_INCLUDE, true); return false; } } @@ -470,7 +485,7 @@ bool RegExpSolver::checkEqcIntersect(const std::vector<Node>& mems) vec_nodes.push_back(mi[0].eqNode(m[0])); } Node conc; - d_im.sendInference(vec_nodes, conc, "INTERSECT CONFLICT", true); + d_im.sendInference(vec_nodes, conc, Inference::RE_INTER_CONF, true); // conflict, return return false; } @@ -490,7 +505,7 @@ bool RegExpSolver::checkEqcIntersect(const std::vector<Node>& mems) else { // new conclusion - // (x in R ^ y in R2 ^ x = y) => (x in intersect(R1,R2)) + // (x in R1 ^ y in R2 ^ x = y) => (x in intersect(R1,R2)) std::vector<Node> vec_nodes; vec_nodes.push_back(mi); vec_nodes.push_back(m); @@ -498,7 +513,7 @@ bool RegExpSolver::checkEqcIntersect(const std::vector<Node>& mems) { vec_nodes.push_back(mi[0].eqNode(m[0])); } - d_im.sendInference(vec_nodes, mres, "INTERSECT INFER", true); + d_im.sendInference(vec_nodes, mres, Inference::RE_INTER_INFER, true); // both are reduced d_parent.getExtTheory()->markReduced(m); d_parent.getExtTheory()->markReduced(mi); @@ -522,7 +537,7 @@ bool RegExpSolver::checkPDerivative( std::vector<Node> exp_n; exp_n.push_back(atom); exp_n.push_back(x.eqNode(d_emptyString)); - d_im.sendInference(nf_exp, exp_n, exp, "RegExp Delta"); + d_im.sendInference(nf_exp, exp_n, exp, Inference::RE_DELTA); addedLemma = true; d_regexp_ccached.insert(atom); return false; @@ -538,7 +553,7 @@ bool RegExpSolver::checkPDerivative( exp_n.push_back(atom); exp_n.push_back(x.eqNode(d_emptyString)); Node conc; - d_im.sendInference(nf_exp, exp_n, conc, "RegExp Delta CONFLICT"); + d_im.sendInference(nf_exp, exp_n, conc, Inference::RE_DELTA_CONF); addedLemma = true; d_regexp_ccached.insert(atom); return false; @@ -628,7 +643,7 @@ bool RegExpSolver::deriveRegExp(Node x, } std::vector<Node> exp_n; exp_n.push_back(atom); - d_im.sendInference(ant, exp_n, conc, "RegExp-Derive"); + d_im.sendInference(ant, exp_n, conc, Inference::RE_DERIVE); return true; } return false; diff --git a/src/theory/strings/regexp_solver.h b/src/theory/strings/regexp_solver.h index 4880af905..1d065181b 100644 --- a/src/theory/strings/regexp_solver.h +++ b/src/theory/strings/regexp_solver.h @@ -26,8 +26,9 @@ #include "theory/strings/extf_solver.h" #include "theory/strings/inference_manager.h" #include "theory/strings/regexp_operation.h" +#include "theory/strings/sequences_stats.h" #include "theory/strings/solver_state.h" -#include "util/regexp.h" +#include "util/string.h" namespace CVC4 { namespace theory { @@ -49,6 +50,7 @@ class RegExpSolver SolverState& s, InferenceManager& im, ExtfSolver& es, + SequencesStatistics& stats, context::Context* c, context::UserContext* u); ~RegExpSolver() {} @@ -119,6 +121,8 @@ class RegExpSolver InferenceManager& d_im; /** reference to the extended function solver of the parent */ ExtfSolver& d_esolver; + /** Reference to the statistics for the theory of strings/sequences. */ + SequencesStatistics& d_statistics; // check membership constraints Node mkAnd(Node c1, Node c2); /** diff --git a/src/theory/strings/rewrites.cpp b/src/theory/strings/rewrites.cpp new file mode 100644 index 000000000..f1a818bf3 --- /dev/null +++ b/src/theory/strings/rewrites.cpp @@ -0,0 +1,214 @@ +/********************* */ +/*! \file rewrites.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of inference information utility. + **/ + +#include "theory/strings/rewrites.h" + +#include <iostream> + +namespace CVC4 { +namespace theory { +namespace strings { + +const char* toString(Rewrite r) +{ + switch (r) + { + case Rewrite::CTN_COMPONENT: return "CTN_COMPONENT"; + case Rewrite::CTN_CONCAT_CHAR: return "CTN_CONCAT_CHAR"; + case Rewrite::CTN_CONST: return "CTN_CONST"; + case Rewrite::CTN_EQ: return "CTN_EQ"; + case Rewrite::CTN_LEN_INEQ: return "CTN_LEN_INEQ"; + case Rewrite::CTN_LEN_INEQ_NSTRICT: return "CTN_LEN_INEQ_NSTRICT"; + case Rewrite::CTN_LHS_EMPTYSTR: return "CTN_LHS_EMPTYSTR"; + case Rewrite::CTN_MSET_NSS: return "CTN_MSET_NSS"; + case Rewrite::CTN_NCONST_CTN_CONCAT: return "CTN_NCONST_CTN_CONCAT"; + case Rewrite::CTN_REPL: return "CTN_REPL"; + case Rewrite::CTN_REPL_CHAR: return "CTN_REPL_CHAR"; + case Rewrite::CTN_REPL_CNSTS_TO_CTN: return "CTN_REPL_CNSTS_TO_CTN"; + case Rewrite::CTN_REPL_EMPTY: return "CTN_REPL_EMPTY"; + case Rewrite::CTN_REPL_LEN_ONE_TO_CTN: return "CTN_REPL_LEN_ONE_TO_CTN"; + case Rewrite::CTN_REPL_SELF: return "CTN_REPL_SELF"; + case Rewrite::CTN_REPL_SIMP_REPL: return "CTN_REPL_SIMP_REPL"; + case Rewrite::CTN_REPL_TO_CTN: return "CTN_REPL_TO_CTN"; + case Rewrite::CTN_REPL_TO_CTN_DISJ: return "CTN_REPL_TO_CTN_DISJ"; + case Rewrite::CTN_RHS_EMPTYSTR: return "CTN_RHS_EMPTYSTR"; + case Rewrite::CTN_RPL_NON_CTN: return "CTN_RPL_NON_CTN"; + case Rewrite::CTN_SPLIT: return "CTN_SPLIT"; + case Rewrite::CTN_SPLIT_ONES: return "CTN_SPLIT_ONES"; + case Rewrite::CTN_STRIP_ENDPT: return "CTN_STRIP_ENDPT"; + case Rewrite::CTN_SUBSTR: return "CTN_SUBSTR"; + case Rewrite::EQ_LEN_DEQ: return "EQ_LEN_DEQ"; + case Rewrite::EQ_NCTN: return "EQ_NCTN"; + case Rewrite::EQ_NFIX: return "EQ_NFIX"; + case Rewrite::FROM_CODE_EVAL: return "FROM_CODE_EVAL"; + case Rewrite::IDOF_DEF_CTN: return "IDOF_DEF_CTN"; + case Rewrite::IDOF_EMP_IDOF: return "IDOF_EMP_IDOF"; + case Rewrite::IDOF_EQ_CST_START: return "IDOF_EQ_CST_START"; + case Rewrite::IDOF_EQ_NORM: return "IDOF_EQ_NORM"; + case Rewrite::IDOF_EQ_NSTART: return "IDOF_EQ_NSTART"; + case Rewrite::IDOF_FIND: return "IDOF_FIND"; + case Rewrite::IDOF_LEN: return "IDOF_LEN"; + case Rewrite::IDOF_MAX: return "IDOF_MAX"; + case Rewrite::IDOF_NCTN: return "IDOF_NCTN"; + case Rewrite::IDOF_NEG: return "IDOF_NEG"; + case Rewrite::IDOF_NFIND: return "IDOF_NFIND"; + case Rewrite::IDOF_NORM_PREFIX: return "IDOF_NORM_PREFIX"; + case Rewrite::IDOF_PULL_ENDPT: return "IDOF_PULL_ENDPT"; + case Rewrite::IDOF_STRIP_CNST_ENDPTS: return "IDOF_STRIP_CNST_ENDPTS"; + case Rewrite::IDOF_STRIP_SYM_LEN: return "IDOF_STRIP_SYM_LEN"; + case Rewrite::ITOS_EVAL: return "ITOS_EVAL"; + case Rewrite::RE_AND_EMPTY: return "RE_AND_EMPTY"; + case Rewrite::RE_ANDOR_FLATTEN: return "RE_ANDOR_FLATTEN"; + case Rewrite::RE_CHAR_IN_STR_STAR: return "RE_CHAR_IN_STR_STAR"; + case Rewrite::RE_CONCAT: return "RE_CONCAT"; + case Rewrite::RE_CONCAT_FLATTEN: return "RE_CONCAT_FLATTEN"; + case Rewrite::RE_CONCAT_OPT: return "RE_CONCAT_OPT"; + case Rewrite::RE_CONCAT_PURE_ALLCHAR: return "RE_CONCAT_PURE_ALLCHAR"; + case Rewrite::RE_CONCAT_TO_CONTAINS: return "RE_CONCAT_TO_CONTAINS"; + case Rewrite::RE_EMPTY_IN_STR_STAR: return "RE_EMPTY_IN_STR_STAR"; + case Rewrite::RE_IN_DIST_CHAR_STAR: return "RE_IN_DIST_CHAR_STAR"; + case Rewrite::RE_IN_SIGMA_STAR: return "RE_IN_SIGMA_STAR"; + case Rewrite::RE_LOOP: return "RE_LOOP"; + case Rewrite::RE_LOOP_STAR: return "RE_LOOP_STAR"; + case Rewrite::RE_OR_ALL: return "RE_OR_ALL"; + case Rewrite::RE_SIMPLE_CONSUME: return "RE_SIMPLE_CONSUME"; + case Rewrite::RE_STAR_EMPTY: return "RE_STAR_EMPTY"; + case Rewrite::RE_STAR_EMPTY_STRING: return "RE_STAR_EMPTY_STRING"; + case Rewrite::RE_STAR_NESTED_STAR: return "RE_STAR_NESTED_STAR"; + case Rewrite::RE_STAR_UNION: return "RE_STAR_UNION"; + case Rewrite::REPL_CHAR_NCONTRIB_FIND: return "REPL_CHAR_NCONTRIB_FIND"; + case Rewrite::REPL_DUAL_REPL_ITE: return "REPL_DUAL_REPL_ITE"; + case Rewrite::REPL_REPL_SHORT_CIRCUIT: return "REPL_REPL_SHORT_CIRCUIT"; + case Rewrite::REPL_REPL2_INV: return "REPL_REPL2_INV"; + case Rewrite::REPL_REPL2_INV_ID: return "REPL_REPL2_INV_ID"; + case Rewrite::REPL_REPL3_INV: return "REPL_REPL3_INV"; + case Rewrite::REPL_REPL3_INV_ID: return "REPL_REPL3_INV_ID"; + case Rewrite::REPL_SUBST_IDX: return "REPL_SUBST_IDX"; + case Rewrite::REPLALL_CONST: return "REPLALL_CONST"; + case Rewrite::REPLALL_EMPTY_FIND: return "REPLALL_EMPTY_FIND"; + case Rewrite::RPL_CCTN: return "RPL_CCTN"; + case Rewrite::RPL_CCTN_RPL: return "RPL_CCTN_RPL"; + case Rewrite::RPL_CNTS_SUBSTS: return "RPL_CNTS_SUBSTS"; + case Rewrite::RPL_CONST_FIND: return "RPL_CONST_FIND"; + case Rewrite::RPL_CONST_NFIND: return "RPL_CONST_NFIND"; + case Rewrite::RPL_EMP_CNTS_SUBSTS: return "RPL_EMP_CNTS_SUBSTS"; + case Rewrite::RPL_ID: return "RPL_ID"; + case Rewrite::RPL_NCTN: return "RPL_NCTN"; + case Rewrite::RPL_PULL_ENDPT: return "RPL_PULL_ENDPT"; + case Rewrite::RPL_REPLACE: return "RPL_REPLACE"; + case Rewrite::RPL_RPL_EMPTY: return "RPL_RPL_EMPTY"; + case Rewrite::RPL_RPL_LEN_ID: return "RPL_RPL_LEN_ID"; + case Rewrite::RPL_X_Y_X_SIMP: return "RPL_X_Y_X_SIMP"; + case Rewrite::SPLIT_EQ: return "SPLIT_EQ"; + case Rewrite::SPLIT_EQ_STRIP_L: return "SPLIT_EQ_STRIP_L"; + case Rewrite::SPLIT_EQ_STRIP_R: return "SPLIT_EQ_STRIP_R"; + case Rewrite::SS_COMBINE: return "SS_COMBINE"; + case Rewrite::SS_CONST_END_OOB: return "SS_CONST_END_OOB"; + case Rewrite::SS_CONST_LEN_MAX_OOB: return "SS_CONST_LEN_MAX_OOB"; + case Rewrite::SS_CONST_LEN_NON_POS: return "SS_CONST_LEN_NON_POS"; + case Rewrite::SS_CONST_SS: return "SS_CONST_SS"; + case Rewrite::SS_CONST_START_MAX_OOB: return "SS_CONST_START_MAX_OOB"; + case Rewrite::SS_CONST_START_NEG: return "SS_CONST_START_NEG"; + case Rewrite::SS_CONST_START_OOB: return "SS_CONST_START_OOB"; + case Rewrite::SS_EMPTYSTR: return "SS_EMPTYSTR"; + case Rewrite::SS_END_PT_NORM: return "SS_END_PT_NORM"; + case Rewrite::SS_GEQ_ZERO_START_ENTAILS_EMP_S: + return "SS_GEQ_ZERO_START_ENTAILS_EMP_S"; + case Rewrite::SS_LEN_INCLUDE: return "SS_LEN_INCLUDE"; + case Rewrite::SS_LEN_NON_POS: return "SS_LEN_NON_POS"; + case Rewrite::SS_LEN_ONE_Z_Z: return "SS_LEN_ONE_Z_Z"; + case Rewrite::SS_NON_ZERO_LEN_ENTAILS_OOB: + return "SS_NON_ZERO_LEN_ENTAILS_OOB"; + case Rewrite::SS_START_ENTAILS_ZERO_LEN: return "SS_START_ENTAILS_ZERO_LEN"; + case Rewrite::SS_START_GEQ_LEN: return "SS_START_GEQ_LEN"; + case Rewrite::SS_START_NEG: return "SS_START_NEG"; + case Rewrite::SS_STRIP_END_PT: return "SS_STRIP_END_PT"; + case Rewrite::SS_STRIP_START_PT: return "SS_STRIP_START_PT"; + case Rewrite::STOI_CONCAT_NONNUM: return "STOI_CONCAT_NONNUM"; + case Rewrite::STOI_EVAL: return "STOI_EVAL"; + case Rewrite::STR_CONV_CONST: return "STR_CONV_CONST"; + case Rewrite::STR_CONV_IDEM: return "STR_CONV_IDEM"; + case Rewrite::STR_CONV_ITOS: return "STR_CONV_ITOS"; + case Rewrite::STR_CONV_MINSCOPE_CONCAT: return "STR_CONV_MINSCOPE_CONCAT"; + case Rewrite::STR_EMP_REPL_EMP: return "STR_EMP_REPL_EMP"; + case Rewrite::STR_EMP_REPL_EMP_R: return "STR_EMP_REPL_EMP_R"; + case Rewrite::STR_EMP_REPL_X_Y_X: return "STR_EMP_REPL_X_Y_X"; + case Rewrite::STR_EMP_SUBSTR_ELIM: return "STR_EMP_SUBSTR_ELIM"; + case Rewrite::STR_EMP_SUBSTR_LEQ_LEN: return "STR_EMP_SUBSTR_LEQ_LEN"; + case Rewrite::STR_EMP_SUBSTR_LEQ_Z: return "STR_EMP_SUBSTR_LEQ_Z"; + case Rewrite::STR_EQ_CONJ_LEN_ENTAIL: return "STR_EQ_CONJ_LEN_ENTAIL"; + case Rewrite::STR_EQ_CONST_NHOMOG: return "STR_EQ_CONST_NHOMOG"; + case Rewrite::STR_EQ_HOMOG_CONST: return "STR_EQ_HOMOG_CONST"; + case Rewrite::STR_EQ_REPL_EMP: return "STR_EQ_REPL_EMP"; + case Rewrite::STR_EQ_REPL_NOT_CTN: return "STR_EQ_REPL_NOT_CTN"; + case Rewrite::STR_EQ_REPL_TO_DIS: return "STR_EQ_REPL_TO_DIS"; + case Rewrite::STR_EQ_REPL_TO_EQ: return "STR_EQ_REPL_TO_EQ"; + case Rewrite::STR_EQ_UNIFY: return "STR_EQ_UNIFY"; + case Rewrite::STR_LEQ_CPREFIX: return "STR_LEQ_CPREFIX"; + case Rewrite::STR_LEQ_EMPTY: return "STR_LEQ_EMPTY"; + case Rewrite::STR_LEQ_EVAL: return "STR_LEQ_EVAL"; + case Rewrite::STR_LEQ_ID: return "STR_LEQ_ID"; + case Rewrite::STR_REV_CONST: return "STR_REV_CONST"; + case Rewrite::STR_REV_IDEM: return "STR_REV_IDEM"; + case Rewrite::STR_REV_MINSCOPE_CONCAT: return "STR_REV_MINSCOPE_CONCAT"; + case Rewrite::SUBSTR_REPL_SWAP: return "SUBSTR_REPL_SWAP"; + case Rewrite::SUF_PREFIX_CONST: return "SUF_PREFIX_CONST"; + case Rewrite::SUF_PREFIX_CTN: return "SUF_PREFIX_CTN"; + case Rewrite::SUF_PREFIX_EMPTY: return "SUF_PREFIX_EMPTY"; + case Rewrite::SUF_PREFIX_EMPTY_CONST: return "SUF_PREFIX_EMPTY_CONST"; + case Rewrite::SUF_PREFIX_EQ: return "SUF_PREFIX_EQ"; + case Rewrite::SUF_PREFIX_TO_EQS: return "SUF_PREFIX_TO_EQS"; + case Rewrite::TO_CODE_EVAL: return "TO_CODE_EVAL"; + case Rewrite::EQ_REFL: return "EQ_REFL"; + case Rewrite::EQ_CONST_FALSE: return "EQ_CONST_FALSE"; + case Rewrite::EQ_SYM: return "EQ_SYM"; + case Rewrite::CONCAT_NORM: return "CONCAT_NORM"; + case Rewrite::IS_DIGIT_ELIM: return "IS_DIGIT_ELIM"; + case Rewrite::RE_CONCAT_EMPTY: return "RE_CONCAT_EMPTY"; + case Rewrite::RE_CONSUME_CCONF: return "RE_CONSUME_CCONF"; + case Rewrite::RE_CONSUME_S: return "RE_CONSUME_S"; + case Rewrite::RE_CONSUME_S_CCONF: return "RE_CONSUME_S_CCONF"; + case Rewrite::RE_CONSUME_S_FULL: return "RE_CONSUME_S_FULL"; + case Rewrite::RE_IN_EMPTY: return "RE_IN_EMPTY"; + case Rewrite::RE_IN_SIGMA: return "RE_IN_SIGMA"; + case Rewrite::RE_IN_EVAL: return "RE_IN_EVAL"; + case Rewrite::RE_IN_COMPLEMENT: return "RE_IN_COMPLEMENT"; + case Rewrite::RE_IN_RANGE: return "RE_IN_RANGE"; + case Rewrite::RE_IN_CSTRING: return "RE_IN_CSTRING"; + case Rewrite::RE_IN_ANDOR: return "RE_IN_ANDOR"; + case Rewrite::RE_REPEAT_ELIM: return "RE_REPEAT_ELIM"; + case Rewrite::SUF_PREFIX_ELIM: return "SUF_PREFIX_ELIM"; + case Rewrite::STR_LT_ELIM: return "STR_LT_ELIM"; + case Rewrite::RE_RANGE_SINGLE: return "RE_RANGE_SINGLE"; + case Rewrite::RE_OPT_ELIM: return "RE_OPT_ELIM"; + case Rewrite::RE_PLUS_ELIM: return "RE_PLUS_ELIM"; + case Rewrite::RE_DIFF_ELIM: return "RE_DIFF_ELIM"; + case Rewrite::LEN_EVAL: return "LEN_EVAL"; + case Rewrite::LEN_CONCAT: return "LEN_CONCAT"; + case Rewrite::LEN_REPL_INV: return "LEN_REPL_INV"; + case Rewrite::LEN_CONV_INV: return "LEN_CONV_INV"; + case Rewrite::CHARAT_ELIM: return "CHARAT_ELIM"; + default: return "?"; + } +} + +std::ostream& operator<<(std::ostream& out, Rewrite r) +{ + out << toString(r); + return out; +} + +} // namespace strings +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/strings/rewrites.h b/src/theory/strings/rewrites.h new file mode 100644 index 000000000..cfa8c8448 --- /dev/null +++ b/src/theory/strings/rewrites.h @@ -0,0 +1,231 @@ +/********************* */ +/*! \file rewrites.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Type for rewrites for strings. + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__STRINGS__REWRITES_H +#define CVC4__THEORY__STRINGS__REWRITES_H + +#include <iosfwd> + +namespace CVC4 { +namespace theory { +namespace strings { + +/** Types of rewrites used by strings + * + * This rewrites are documented where they are used in the rewriter. + */ +enum class Rewrite : uint32_t +{ + CTN_COMPONENT, + CTN_CONCAT_CHAR, + CTN_CONST, + CTN_EQ, + CTN_LEN_INEQ, + CTN_LEN_INEQ_NSTRICT, + CTN_LHS_EMPTYSTR, + CTN_MSET_NSS, + CTN_NCONST_CTN_CONCAT, + CTN_REPL, + CTN_REPL_CHAR, + CTN_REPL_CNSTS_TO_CTN, + CTN_REPL_EMPTY, + CTN_REPL_LEN_ONE_TO_CTN, + CTN_REPL_SELF, + CTN_REPL_SIMP_REPL, + CTN_REPL_TO_CTN, + CTN_REPL_TO_CTN_DISJ, + CTN_RHS_EMPTYSTR, + CTN_RPL_NON_CTN, + CTN_SPLIT, + CTN_SPLIT_ONES, + CTN_STRIP_ENDPT, + CTN_SUBSTR, + EQ_LEN_DEQ, + EQ_NCTN, + EQ_NFIX, + FROM_CODE_EVAL, + IDOF_DEF_CTN, + IDOF_EMP_IDOF, + IDOF_EQ_CST_START, + IDOF_EQ_NORM, + IDOF_EQ_NSTART, + IDOF_FIND, + IDOF_LEN, + IDOF_MAX, + IDOF_NCTN, + IDOF_NEG, + IDOF_NFIND, + IDOF_NORM_PREFIX, + IDOF_PULL_ENDPT, + IDOF_STRIP_CNST_ENDPTS, + IDOF_STRIP_SYM_LEN, + ITOS_EVAL, + RE_AND_EMPTY, + RE_ANDOR_FLATTEN, + RE_CHAR_IN_STR_STAR, + RE_CONCAT, + RE_CONCAT_FLATTEN, + RE_CONCAT_OPT, + RE_CONCAT_PURE_ALLCHAR, + RE_CONCAT_TO_CONTAINS, + RE_EMPTY_IN_STR_STAR, + RE_IN_DIST_CHAR_STAR, + RE_IN_SIGMA_STAR, + RE_LOOP, + RE_LOOP_STAR, + RE_OR_ALL, + RE_SIMPLE_CONSUME, + RE_STAR_EMPTY, + RE_STAR_EMPTY_STRING, + RE_STAR_NESTED_STAR, + RE_STAR_UNION, + REPL_CHAR_NCONTRIB_FIND, + REPL_DUAL_REPL_ITE, + REPL_REPL_SHORT_CIRCUIT, + REPL_REPL2_INV, + REPL_REPL2_INV_ID, + REPL_REPL3_INV, + REPL_REPL3_INV_ID, + REPL_SUBST_IDX, + REPLALL_CONST, + REPLALL_EMPTY_FIND, + RPL_CCTN, + RPL_CCTN_RPL, + RPL_CNTS_SUBSTS, + RPL_CONST_FIND, + RPL_CONST_NFIND, + RPL_EMP_CNTS_SUBSTS, + RPL_ID, + RPL_NCTN, + RPL_PULL_ENDPT, + RPL_REPLACE, + RPL_RPL_EMPTY, + RPL_RPL_LEN_ID, + RPL_X_Y_X_SIMP, + SPLIT_EQ, + SPLIT_EQ_STRIP_L, + SPLIT_EQ_STRIP_R, + SS_COMBINE, + SS_CONST_END_OOB, + SS_CONST_LEN_MAX_OOB, + SS_CONST_LEN_NON_POS, + SS_CONST_SS, + SS_CONST_START_MAX_OOB, + SS_CONST_START_NEG, + SS_CONST_START_OOB, + SS_EMPTYSTR, + SS_END_PT_NORM, + SS_GEQ_ZERO_START_ENTAILS_EMP_S, + SS_LEN_INCLUDE, + SS_LEN_NON_POS, + SS_LEN_ONE_Z_Z, + SS_NON_ZERO_LEN_ENTAILS_OOB, + SS_START_ENTAILS_ZERO_LEN, + SS_START_GEQ_LEN, + SS_START_NEG, + SS_STRIP_END_PT, + SS_STRIP_START_PT, + STOI_CONCAT_NONNUM, + STOI_EVAL, + STR_CONV_CONST, + STR_CONV_IDEM, + STR_CONV_ITOS, + STR_CONV_MINSCOPE_CONCAT, + STR_EMP_REPL_EMP, + STR_EMP_REPL_EMP_R, + STR_EMP_REPL_X_Y_X, + STR_EMP_SUBSTR_ELIM, + STR_EMP_SUBSTR_LEQ_LEN, + STR_EMP_SUBSTR_LEQ_Z, + STR_EQ_CONJ_LEN_ENTAIL, + STR_EQ_CONST_NHOMOG, + STR_EQ_HOMOG_CONST, + STR_EQ_REPL_EMP, + STR_EQ_REPL_NOT_CTN, + STR_EQ_REPL_TO_DIS, + STR_EQ_REPL_TO_EQ, + STR_EQ_UNIFY, + STR_LEQ_CPREFIX, + STR_LEQ_EMPTY, + STR_LEQ_EVAL, + STR_LEQ_ID, + STR_REV_CONST, + STR_REV_IDEM, + STR_REV_MINSCOPE_CONCAT, + SUBSTR_REPL_SWAP, + SUF_PREFIX_CONST, + SUF_PREFIX_CTN, + SUF_PREFIX_EMPTY, + SUF_PREFIX_EMPTY_CONST, + SUF_PREFIX_EQ, + SUF_PREFIX_TO_EQS, + TO_CODE_EVAL, + EQ_REFL, + EQ_CONST_FALSE, + EQ_SYM, + CONCAT_NORM, + IS_DIGIT_ELIM, + RE_CONCAT_EMPTY, + RE_CONSUME_CCONF, + RE_CONSUME_S, + RE_CONSUME_S_CCONF, + RE_CONSUME_S_FULL, + RE_IN_EMPTY, + RE_IN_SIGMA, + RE_IN_EVAL, + RE_IN_COMPLEMENT, + RE_IN_RANGE, + RE_IN_CSTRING, + RE_IN_ANDOR, + RE_REPEAT_ELIM, + SUF_PREFIX_ELIM, + STR_LT_ELIM, + RE_RANGE_SINGLE, + RE_OPT_ELIM, + RE_PLUS_ELIM, + RE_DIFF_ELIM, + LEN_EVAL, + LEN_CONCAT, + LEN_REPL_INV, + LEN_CONV_INV, + CHARAT_ELIM +}; + +/** + * Converts an rewrite to a string. Note: This function is also used in + * `safe_print()`. Changing this functions name or signature will result in + * `safe_print()` printing "<unsupported>" instead of the proper strings for + * the enum values. + * + * @param r The rewrite + * @return The name of the rewrite + */ +const char* toString(Rewrite r); + +/** + * Writes an rewrite name to a stream. + * + * @param out The stream to write to + * @param r The rewrite to write to the stream + * @return The stream + */ +std::ostream& operator<<(std::ostream& out, Rewrite r); + +} // namespace strings +} // namespace theory +} // namespace CVC4 + +#endif /* CVC4__THEORY__STRINGS__REWRITES_H */ diff --git a/src/theory/strings/sequences_rewriter.cpp b/src/theory/strings/sequences_rewriter.cpp index 200d7a734..be1e13459 100644 --- a/src/theory/strings/sequences_rewriter.cpp +++ b/src/theory/strings/sequences_rewriter.cpp @@ -310,11 +310,13 @@ Node SequencesRewriter::rewriteEquality(Node node) Assert(node.getKind() == kind::EQUAL); if (node[0] == node[1]) { - return NodeManager::currentNM()->mkConst(true); + Node ret = NodeManager::currentNM()->mkConst(true); + return returnRewrite(node, ret, Rewrite::EQ_REFL); } else if (node[0].isConst() && node[1].isConst()) { - return NodeManager::currentNM()->mkConst(false); + Node ret = NodeManager::currentNM()->mkConst(false); + return returnRewrite(node, ret, Rewrite::EQ_CONST_FALSE); } // ( ~contains( s, t ) V ~contains( t, s ) ) => ( s == t ---> false ) @@ -328,7 +330,7 @@ Node SequencesRewriter::rewriteEquality(Node node) { if (!ctn.getConst<bool>()) { - return returnRewrite(node, ctn, "eq-nctn"); + return returnRewrite(node, ctn, Rewrite::EQ_NCTN); } else { @@ -347,7 +349,7 @@ Node SequencesRewriter::rewriteEquality(Node node) len_eq = Rewriter::rewrite(len_eq); if (len_eq.isConst() && !len_eq.getConst<bool>()) { - return returnRewrite(node, len_eq, "eq-len-deq"); + return returnRewrite(node, len_eq, Rewrite::EQ_LEN_DEQ); } std::vector<Node> c[2]; @@ -375,7 +377,7 @@ Node SequencesRewriter::rewriteEquality(Node node) if (!isSameFix) { Node ret = NodeManager::currentNM()->mkConst(false); - return returnRewrite(node, ret, "eq-nfix"); + return returnRewrite(node, ret, Rewrite::EQ_NFIX); } } if (c[0][index1] != c[1][index2]) @@ -388,7 +390,8 @@ Node SequencesRewriter::rewriteEquality(Node node) // standard ordering if (node[0] > node[1]) { - return NodeManager::currentNM()->mkNode(kind::EQUAL, node[1], node[0]); + Node ret = NodeManager::currentNM()->mkNode(kind::EQUAL, node[1], node[0]); + return returnRewrite(node, ret, Rewrite::EQ_SYM); } return node; } @@ -400,7 +403,7 @@ Node SequencesRewriter::rewriteEqualityExt(Node node) { return rewriteArithEqualityExt(node); } - if (node[0].getType().isString()) + if (node[0].getType().isStringLike()) { return rewriteStrEqualityExt(node); } @@ -409,7 +412,7 @@ Node SequencesRewriter::rewriteEqualityExt(Node node) Node SequencesRewriter::rewriteStrEqualityExt(Node node) { - Assert(node.getKind() == EQUAL && node[0].getType().isString()); + Assert(node.getKind() == EQUAL && node[0].getType().isStringLike()); TypeNode stype = node[0].getType(); NodeManager* nm = NodeManager::currentNM(); @@ -465,7 +468,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) Node s1 = utils::mkConcat(c[0], stype); Node s2 = utils::mkConcat(c[1], stype); new_ret = s1.eqNode(s2); - node = returnRewrite(node, new_ret, "str-eq-unify"); + node = returnRewrite(node, new_ret, Rewrite::STR_EQ_UNIFY); } // ------- homogeneous constants @@ -476,13 +479,12 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) { Assert(cn.isConst()); Assert(Word::getLength(cn) == 1); - unsigned hchar = cn.getConst<String>().front(); // The operands of the concat on each side of the equality without // constant strings std::vector<Node> trimmed[2]; - // Counts the number of `hchar`s on each side - size_t numHChars[2] = {0, 0}; + // Counts the number of `cn`s on each side + size_t numCns[2] = {0, 0}; for (size_t j = 0; j < 2; j++) { // Sort the operands of the concats on both sides of the equality @@ -493,21 +495,21 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) { if (cc.isConst()) { - // Count the number of `hchar`s in the string constant and make - // sure that all chars are `hchar`s - std::vector<unsigned> veccc = cc.getConst<String>().getVec(); - for (size_t k = 0, size = veccc.size(); k < size; k++) + // Count the number of `cn`s in the string constant and make + // sure that all chars are `cn`s + std::vector<Node> veccc = Word::getChars(cc); + for (const Node& cv : veccc) { - if (veccc[k] != hchar) + if (cv != cn) { // This conflict case should mostly should be taken care of by // multiset reasoning in the strings rewriter, but we recognize // this conflict just in case. new_ret = nm->mkConst(false); return returnRewrite( - node, new_ret, "string-eq-const-conflict-non-homog"); + node, new_ret, Rewrite::STR_EQ_CONST_NHOMOG); } - numHChars[j]++; + numCns[j]++; } } else @@ -517,18 +519,18 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) } } - // We have to remove the same number of `hchar`s from both sides, so the - // side with less `hchar`s determines how many we can remove - size_t trimmedConst = std::min(numHChars[0], numHChars[1]); + // We have to remove the same number of `cn`s from both sides, so the + // side with less `cn`s determines how many we can remove + size_t trimmedConst = std::min(numCns[0], numCns[1]); for (size_t j = 0; j < 2; j++) { - size_t diff = numHChars[j] - trimmedConst; + size_t diff = numCns[j] - trimmedConst; if (diff != 0) { - // Add a constant string to the side with more `hchar`s to restore - // the difference in number of `hchar`s - std::vector<unsigned> vec(diff, hchar); - trimmed[j].push_back(nm->mkConst(String(vec))); + // Add a constant string to the side with more `cn`s to restore + // the difference in number of `cn`s + std::vector<Node> vec(diff, cn); + trimmed[j].push_back(Word::mkWord(vec)); } } @@ -540,7 +542,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) // "AA" = y ++ x ---> "AA" = x ++ y if x < y // "AAA" = y ++ "A" ++ z ---> "AA" = y ++ z new_ret = lhs.eqNode(ss); - node = returnRewrite(node, new_ret, "str-eq-homog-const"); + node = returnRewrite(node, new_ret, Rewrite::STR_EQ_HOMOG_CONST); } } } @@ -558,7 +560,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) if (ne[0] == ne[2]) { Node ret = nm->mkNode(EQUAL, ne[0], empty); - return returnRewrite(node, ret, "str-emp-repl-x-y-x"); + return returnRewrite(node, ret, Rewrite::STR_EMP_REPL_X_Y_X); } // (= "" (str.replace x y "A")) ---> (and (= x "") (not (= y ""))) @@ -568,14 +570,14 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) nm->mkNode(AND, nm->mkNode(EQUAL, ne[0], empty), nm->mkNode(NOT, nm->mkNode(EQUAL, ne[1], empty))); - return returnRewrite(node, ret, "str-emp-repl-emp"); + return returnRewrite(node, ret, Rewrite::STR_EMP_REPL_EMP); } // (= "" (str.replace x "A" "")) ---> (str.prefix x "A") if (checkEntailLengthOne(ne[1]) && ne[2] == empty) { Node ret = nm->mkNode(STRING_PREFIX, ne[0], ne[1]); - return returnRewrite(node, ret, "str-emp-repl-emp"); + return returnRewrite(node, ret, Rewrite::STR_EMP_REPL_EMP); } } else if (ne.getKind() == STRING_SUBSTR) @@ -588,20 +590,20 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) if (ne[1] == zero) { Node ret = nm->mkNode(EQUAL, ne[0], empty); - return returnRewrite(node, ret, "str-emp-substr-leq-len"); + return returnRewrite(node, ret, Rewrite::STR_EMP_SUBSTR_LEQ_LEN); } // (= "" (str.substr x n m)) ---> (<= (str.len x) n) // if n >= 0 and m > 0 Node ret = nm->mkNode(LEQ, nm->mkNode(STRING_LENGTH, ne[0]), ne[1]); - return returnRewrite(node, ret, "str-emp-substr-leq-len"); + return returnRewrite(node, ret, Rewrite::STR_EMP_SUBSTR_LEQ_LEN); } // (= "" (str.substr "A" 0 z)) ---> (<= z 0) if (checkEntailNonEmpty(ne[0]) && ne[1] == zero) { Node ret = nm->mkNode(LEQ, ne[2], zero); - return returnRewrite(node, ret, "str-emp-substr-leq-z"); + return returnRewrite(node, ret, Rewrite::STR_EMP_SUBSTR_LEQ_Z); } } } @@ -620,14 +622,14 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) { Node ret = nm->mkNode( EQUAL, empty, nm->mkNode(STRING_STRREPL, x, repl[2], repl[1])); - return returnRewrite(node, ret, "str-eq-repl-emp"); + return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_EMP); } // (= x (str.replace y x y)) ---> (= x y) if (repl[0] == repl[2] && x == repl[1]) { Node ret = nm->mkNode(EQUAL, x, repl[0]); - return returnRewrite(node, ret, "str-eq-repl-to-eq"); + return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_TO_EQ); } // (= x (str.replace x "A" "B")) ---> (not (str.contains x "A")) @@ -637,7 +639,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) if (eq.isConst() && !eq.getConst<bool>()) { Node ret = nm->mkNode(NOT, nm->mkNode(STRING_STRCTN, x, repl[1])); - return returnRewrite(node, ret, "str-eq-repl-not-ctn"); + return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_NOT_CTN); } } @@ -652,7 +654,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) Node ret = nm->mkNode(OR, nm->mkNode(EQUAL, repl[0], repl[1]), nm->mkNode(EQUAL, repl[0], repl[2])); - return returnRewrite(node, ret, "str-eq-repl-to-dis"); + return returnRewrite(node, ret, Rewrite::STR_EQ_REPL_TO_DIS); } } } @@ -673,7 +675,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) new_ret = inferEqsFromContains(node[i], node[1 - i]); if (!new_ret.isNull()) { - return returnRewrite(node, new_ret, "str-eq-conj-len-entail"); + return returnRewrite(node, new_ret, Rewrite::STR_EQ_CONJ_LEN_ENTAIL); } } } @@ -715,7 +717,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) pfx0.eqNode(pfx1), utils::mkConcat(sfxv0, stype) .eqNode(utils::mkConcat(sfxv1, stype))); - return returnRewrite(node, ret, "split-eq"); + return returnRewrite(node, ret, Rewrite::SPLIT_EQ); } else if (checkEntailArith(lenPfx1, lenPfx0, true)) { @@ -735,7 +737,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) pfx0.eqNode(utils::mkConcat(rpfxv1, stype)), utils::mkConcat(sfxv0, stype) .eqNode(utils::mkConcat(pfxv1, stype))); - return returnRewrite(node, ret, "split-eq-strip-r"); + return returnRewrite(node, ret, Rewrite::SPLIT_EQ_STRIP_R); } // If the prefix of the right-hand side is (strictly) longer than @@ -762,7 +764,7 @@ Node SequencesRewriter::rewriteStrEqualityExt(Node node) utils::mkConcat(rpfxv0, stype).eqNode(pfx1), utils::mkConcat(pfxv0, stype) .eqNode(utils::mkConcat(sfxv1, stype))); - return returnRewrite(node, ret, "split-eq-strip-l"); + return returnRewrite(node, ret, Rewrite::SPLIT_EQ_STRIP_L); } // If the prefix of the left-hand side is (strictly) longer than @@ -790,6 +792,59 @@ Node SequencesRewriter::rewriteArithEqualityExt(Node node) return node; } +Node SequencesRewriter::rewriteLength(Node node) +{ + Assert(node.getKind() == STRING_LENGTH); + NodeManager* nm = NodeManager::currentNM(); + Kind nk0 = node[0].getKind(); + if (node[0].isConst()) + { + Node retNode = nm->mkConst(Rational(Word::getLength(node[0]))); + return returnRewrite(node, retNode, Rewrite::LEN_EVAL); + } + else if (nk0 == kind::STRING_CONCAT) + { + Node tmpNode = node[0]; + if (tmpNode.getKind() == kind::STRING_CONCAT) + { + std::vector<Node> node_vec; + for (unsigned int i = 0; i < tmpNode.getNumChildren(); ++i) + { + if (tmpNode[i].isConst()) + { + node_vec.push_back( + nm->mkConst(Rational(Word::getLength(tmpNode[i])))); + } + else + { + node_vec.push_back(NodeManager::currentNM()->mkNode( + kind::STRING_LENGTH, tmpNode[i])); + } + } + Node retNode = NodeManager::currentNM()->mkNode(kind::PLUS, node_vec); + return returnRewrite(node, retNode, Rewrite::LEN_CONCAT); + } + } + else if (nk0 == STRING_STRREPL || nk0 == STRING_STRREPLALL) + { + Node len1 = Rewriter::rewrite(nm->mkNode(STRING_LENGTH, node[0][1])); + Node len2 = Rewriter::rewrite(nm->mkNode(STRING_LENGTH, node[0][2])); + if (len1 == len2) + { + // len( y ) == len( z ) => len( str.replace( x, y, z ) ) ---> len( x ) + Node retNode = nm->mkNode(STRING_LENGTH, node[0][0]); + return returnRewrite(node, retNode, Rewrite::LEN_REPL_INV); + } + } + else if (nk0 == STRING_TOLOWER || nk0 == STRING_TOUPPER || nk0 == STRING_REV) + { + // len( f( x ) ) == len( x ) where f is tolower, toupper, or rev. + Node retNode = nm->mkNode(STRING_LENGTH, node[0][0]); + return returnRewrite(node, retNode, Rewrite::LEN_CONV_INV); + } + return node; +} + // TODO (#1180) add rewrite // str.++( str.substr( x, n1, n2 ), str.substr( x, n1+n2, n3 ) ) ---> // str.substr( x, n1, n2+n3 ) @@ -799,7 +854,6 @@ Node SequencesRewriter::rewriteConcat(Node node) Trace("strings-rewrite-debug") << "Strings::rewriteConcat start " << node << std::endl; NodeManager* nm = NodeManager::currentNM(); - Node retNode = node; std::vector<Node> node_vec; Node preNode = Node::null(); for (Node tmpNode : node) @@ -890,10 +944,14 @@ Node SequencesRewriter::rewriteConcat(Node node) std::sort(node_vec.begin() + lastIdx, node_vec.end()); TypeNode tn = node.getType(); - retNode = utils::mkConcat(node_vec, tn); + Node retNode = utils::mkConcat(node_vec, tn); Trace("strings-rewrite-debug") << "Strings::rewriteConcat end " << retNode << std::endl; - return retNode; + if (retNode != node) + { + return returnRewrite(node, retNode, Rewrite::CONCAT_NORM); + } + return node; } Node SequencesRewriter::rewriteConcatRegExp(TNode node) @@ -940,7 +998,8 @@ Node SequencesRewriter::rewriteConcatRegExp(TNode node) { // re.++( ..., empty, ... ) ---> empty std::vector<Node> nvec; - return nm->mkNode(REGEXP_EMPTY, nvec); + Node ret = nm->mkNode(REGEXP_EMPTY, nvec); + return returnRewrite(node, ret, Rewrite::RE_CONCAT_EMPTY); } else { @@ -961,7 +1020,7 @@ Node SequencesRewriter::rewriteConcatRegExp(TNode node) { retNode = vec.size() == 1 ? vec[0] : nm->mkNode(REGEXP_CONCAT, vec); } - return returnRewrite(node, retNode, "re.concat-flatten"); + return returnRewrite(node, retNode, Rewrite::RE_CONCAT_FLATTEN); } Trace("strings-rewrite-debug") << "Strings::rewriteConcatRegExp start " << node << std::endl; @@ -1039,7 +1098,7 @@ Node SequencesRewriter::rewriteConcatRegExp(TNode node) { // handles all cases where consecutive re constants are combined or // dropped as described in the loop above. - return returnRewrite(node, retNode, "re.concat"); + return returnRewrite(node, retNode, Rewrite::RE_CONCAT); } // flipping adjacent star arguments @@ -1056,7 +1115,7 @@ Node SequencesRewriter::rewriteConcatRegExp(TNode node) if (changed) { retNode = utils::mkConcat(cvec, rtype); - return returnRewrite(node, retNode, "re.concat.opt"); + return returnRewrite(node, retNode, Rewrite::RE_CONCAT_OPT); } return node; } @@ -1069,19 +1128,19 @@ Node SequencesRewriter::rewriteStarRegExp(TNode node) if (node[0].getKind() == REGEXP_STAR) { // ((R)*)* ---> R* - return returnRewrite(node, node[0], "re-star-nested-star"); + return returnRewrite(node, node[0], Rewrite::RE_STAR_NESTED_STAR); } else if (node[0].getKind() == STRING_TO_REGEXP && node[0][0].isConst() && Word::isEmpty(node[0][0])) { // ("")* ---> "" - return returnRewrite(node, node[0], "re-star-empty-string"); + return returnRewrite(node, node[0], Rewrite::RE_STAR_EMPTY_STRING); } else if (node[0].getKind() == REGEXP_EMPTY) { // (empty)* ---> "" retNode = nm->mkNode(STRING_TO_REGEXP, nm->mkConst(String(""))); - return returnRewrite(node, retNode, "re-star-empty"); + return returnRewrite(node, retNode, Rewrite::RE_STAR_EMPTY); } else if (node[0].getKind() == REGEXP_UNION) { @@ -1110,7 +1169,7 @@ Node SequencesRewriter::rewriteStarRegExp(TNode node) retNode = nm->mkNode(REGEXP_STAR, retNode); // simplification of union beneath star based on loop above // for example, ( "" | "a" )* ---> ("a")* - return returnRewrite(node, retNode, "re-star-union"); + return returnRewrite(node, retNode, Rewrite::RE_STAR_UNION); } } } @@ -1140,7 +1199,7 @@ Node SequencesRewriter::rewriteAndOrRegExp(TNode node) { if (nk == REGEXP_INTER) { - return returnRewrite(node, ni, "re.and-empty"); + return returnRewrite(node, ni, Rewrite::RE_AND_EMPTY); } // otherwise, can ignore } @@ -1148,7 +1207,7 @@ Node SequencesRewriter::rewriteAndOrRegExp(TNode node) { if (nk == REGEXP_UNION) { - return returnRewrite(node, ni, "re.or-all"); + return returnRewrite(node, ni, Rewrite::RE_OR_ALL); } // otherwise, can ignore } @@ -1178,7 +1237,7 @@ Node SequencesRewriter::rewriteAndOrRegExp(TNode node) if (retNode != node) { // flattening and removing children, based on loop above - return returnRewrite(node, retNode, "re.andor-flatten"); + return returnRewrite(node, retNode, Rewrite::RE_ANDOR_FLATTEN); } return node; } @@ -1190,69 +1249,101 @@ Node SequencesRewriter::rewriteLoopRegExp(TNode node) Node r = node[0]; if (r.getKind() == REGEXP_STAR) { - return returnRewrite(node, r, "re.loop-star"); + return returnRewrite(node, r, Rewrite::RE_LOOP_STAR); } - TNode n1 = node[1]; NodeManager* nm = NodeManager::currentNM(); CVC4::Rational rMaxInt(String::maxSize()); - AlwaysAssert(n1.isConst()) << "re.loop contains non-constant integer (1)."; - AlwaysAssert(n1.getConst<Rational>().sgn() >= 0) - << "Negative integer in string REGEXP_LOOP (1)"; - Assert(n1.getConst<Rational>() <= rMaxInt) - << "Exceeded UINT32_MAX in string REGEXP_LOOP (1)"; - uint32_t l = n1.getConst<Rational>().getNumerator().toUnsignedInt(); + uint32_t l = utils::getLoopMinOccurrences(node); std::vector<Node> vec_nodes; for (unsigned i = 0; i < l; i++) { vec_nodes.push_back(r); } - if (node.getNumChildren() == 3) + Node n = + vec_nodes.size() == 0 + ? nm->mkNode(STRING_TO_REGEXP, nm->mkConst(String(""))) + : vec_nodes.size() == 1 ? r : nm->mkNode(REGEXP_CONCAT, vec_nodes); + uint32_t u = utils::getLoopMaxOccurrences(node); + if (u < l) { - TNode n2 = Rewriter::rewrite(node[2]); - Node n = - vec_nodes.size() == 0 - ? nm->mkNode(STRING_TO_REGEXP, nm->mkConst(String(""))) - : vec_nodes.size() == 1 ? r : nm->mkNode(REGEXP_CONCAT, vec_nodes); - AlwaysAssert(n2.isConst()) << "re.loop contains non-constant integer (2)."; - AlwaysAssert(n2.getConst<Rational>().sgn() >= 0) - << "Negative integer in string REGEXP_LOOP (2)"; - Assert(n2.getConst<Rational>() <= rMaxInt) - << "Exceeded UINT32_MAX in string REGEXP_LOOP (2)"; - uint32_t u = n2.getConst<Rational>().getNumerator().toUnsignedInt(); - if (u <= l) - { - retNode = n; - } - else - { - std::vector<Node> vec2; - vec2.push_back(n); - TypeNode rtype = nm->regExpType(); - for (unsigned j = l; j < u; j++) - { - vec_nodes.push_back(r); - n = utils::mkConcat(vec_nodes, rtype); - vec2.push_back(n); - } - retNode = nm->mkNode(REGEXP_UNION, vec2); - } + std::vector<Node> nvec; + retNode = nm->mkNode(REGEXP_EMPTY, nvec); + } + else if (u == l) + { + retNode = n; } else { - Node rest = nm->mkNode(REGEXP_STAR, r); - retNode = vec_nodes.size() == 0 - ? rest - : vec_nodes.size() == 1 - ? nm->mkNode(REGEXP_CONCAT, r, rest) - : nm->mkNode(REGEXP_CONCAT, - nm->mkNode(REGEXP_CONCAT, vec_nodes), - rest); + std::vector<Node> vec2; + vec2.push_back(n); + TypeNode rtype = nm->regExpType(); + for (uint32_t j = l; j < u; j++) + { + vec_nodes.push_back(r); + n = utils::mkConcat(vec_nodes, rtype); + vec2.push_back(n); + } + retNode = nm->mkNode(REGEXP_UNION, vec2); } Trace("strings-lp") << "Strings::lp " << node << " => " << retNode << std::endl; if (retNode != node) { - return returnRewrite(node, retNode, "re.loop"); + return returnRewrite(node, retNode, Rewrite::RE_LOOP); + } + return node; +} + +Node SequencesRewriter::rewriteRepeatRegExp(TNode node) +{ + Assert(node.getKind() == REGEXP_REPEAT); + NodeManager* nm = NodeManager::currentNM(); + // ((_ re.^ n) R) --> ((_ re.loop n n) R) + unsigned r = utils::getRepeatAmount(node); + Node lop = nm->mkConst(RegExpLoop(r, r)); + Node retNode = nm->mkNode(REGEXP_LOOP, lop, node[0]); + return returnRewrite(node, retNode, Rewrite::RE_REPEAT_ELIM); +} + +Node SequencesRewriter::rewriteOptionRegExp(TNode node) +{ + Assert(node.getKind() == REGEXP_OPT); + NodeManager* nm = NodeManager::currentNM(); + Node retNode = + nm->mkNode(REGEXP_UNION, + nm->mkNode(STRING_TO_REGEXP, nm->mkConst(String(""))), + node[0]); + return returnRewrite(node, retNode, Rewrite::RE_OPT_ELIM); +} + +Node SequencesRewriter::rewritePlusRegExp(TNode node) +{ + Assert(node.getKind() == REGEXP_PLUS); + NodeManager* nm = NodeManager::currentNM(); + Node retNode = + nm->mkNode(REGEXP_CONCAT, node[0], nm->mkNode(REGEXP_STAR, node[0])); + return returnRewrite(node, retNode, Rewrite::RE_PLUS_ELIM); +} + +Node SequencesRewriter::rewriteDifferenceRegExp(TNode node) +{ + Assert(node.getKind() == REGEXP_DIFF); + NodeManager* nm = NodeManager::currentNM(); + Node retNode = + nm->mkNode(REGEXP_INTER, node[0], nm->mkNode(REGEXP_COMPLEMENT, node[1])); + return returnRewrite(node, retNode, Rewrite::RE_DIFF_ELIM); +} + +Node SequencesRewriter::rewriteRangeRegExp(TNode node) +{ + Assert(node.getKind() == REGEXP_RANGE); + if (node[0] == node[1]) + { + NodeManager* nm = NodeManager::currentNM(); + Node retNode = nm->mkNode(STRING_TO_REGEXP, node[0]); + // re.range( "A", "A" ) ---> str.to_re( "A" ) + return returnRewrite(node, retNode, Rewrite::RE_RANGE_SINGLE); } return node; } @@ -1421,11 +1512,8 @@ bool SequencesRewriter::testConstStringInRegExp(CVC4::String& s, if (s.size() == index_start + 1) { unsigned a = r[0].getConst<String>().front(); - a = String::convertUnsignedIntToCode(a); unsigned b = r[1].getConst<String>().front(); - b = String::convertUnsignedIntToCode(b); unsigned c = s.back(); - c = String::convertUnsignedIntToCode(c); return (a <= c && c <= b); } else @@ -1527,7 +1615,6 @@ bool SequencesRewriter::testConstStringInRegExp(CVC4::String& s, Node SequencesRewriter::rewriteMembership(TNode node) { NodeManager* nm = NodeManager::currentNM(); - Node retNode = node; Node x = node[0]; Node r = node[1]; @@ -1536,19 +1623,22 @@ Node SequencesRewriter::rewriteMembership(TNode node) if(r.getKind() == kind::REGEXP_EMPTY) { - retNode = NodeManager::currentNM()->mkConst( false ); + Node retNode = NodeManager::currentNM()->mkConst(false); + return returnRewrite(node, retNode, Rewrite::RE_IN_EMPTY); } else if (x.isConst() && isConstRegExp(r)) { // test whether x in node[1] CVC4::String s = x.getConst<String>(); - retNode = + Node retNode = NodeManager::currentNM()->mkConst(testConstStringInRegExp(s, 0, r)); + return returnRewrite(node, retNode, Rewrite::RE_IN_EVAL); } else if (r.getKind() == kind::REGEXP_SIGMA) { Node one = nm->mkConst(Rational(1)); - retNode = one.eqNode(nm->mkNode(STRING_LENGTH, x)); + Node retNode = one.eqNode(nm->mkNode(STRING_LENGTH, x)); + return returnRewrite(node, retNode, Rewrite::RE_IN_SIGMA); } else if (r.getKind() == kind::REGEXP_STAR) { @@ -1557,17 +1647,17 @@ Node SequencesRewriter::rewriteMembership(TNode node) String s = x.getConst<String>(); if (s.size() == 0) { - retNode = nm->mkConst(true); + Node retNode = nm->mkConst(true); // e.g. (str.in.re "" (re.* (str.to.re x))) ----> true - return returnRewrite(node, retNode, "re-empty-in-str-star"); + return returnRewrite(node, retNode, Rewrite::RE_EMPTY_IN_STR_STAR); } else if (s.size() == 1) { if (r[0].getKind() == STRING_TO_REGEXP) { - retNode = r[0][0].eqNode(x); + Node retNode = r[0][0].eqNode(x); // e.g. (str.in.re "A" (re.* (str.to.re x))) ----> "A" = x - return returnRewrite(node, retNode, "re-char-in-str-star"); + return returnRewrite(node, retNode, Rewrite::RE_CHAR_IN_STR_STAR); } } } @@ -1588,14 +1678,14 @@ Node SequencesRewriter::rewriteMembership(TNode node) nb << nm->mkNode(STRING_IN_REGEXP, xc, r); } return returnRewrite( - node, nb.constructNode(), "re-in-dist-char-star"); + node, nb.constructNode(), Rewrite::RE_IN_DIST_CHAR_STAR); } } } if (r[0].getKind() == kind::REGEXP_SIGMA) { - retNode = NodeManager::currentNM()->mkConst(true); - return returnRewrite(node, retNode, "re-in-sigma-star"); + Node retNode = NodeManager::currentNM()->mkConst(true); + return returnRewrite(node, retNode, Rewrite::RE_IN_SIGMA_STAR); } } else if (r.getKind() == kind::REGEXP_CONCAT) @@ -1644,15 +1734,15 @@ Node SequencesRewriter::rewriteMembership(TNode node) // x in re.++(_*, _, _) ---> str.len(x) >= 2 Node num = nm->mkConst(Rational(allSigmaMinSize)); Node lenx = nm->mkNode(STRING_LENGTH, x); - retNode = nm->mkNode(allSigmaStrict ? EQUAL : GEQ, lenx, num); - return returnRewrite(node, retNode, "re-concat-pure-allchar"); + Node retNode = nm->mkNode(allSigmaStrict ? EQUAL : GEQ, lenx, num); + return returnRewrite(node, retNode, Rewrite::RE_CONCAT_PURE_ALLCHAR); } else if (allSigmaMinSize == 0 && nchildren >= 3 && constIdx != 0 && constIdx != nchildren - 1) { // x in re.++(_*, "abc", _*) ---> str.contains(x, "abc") - retNode = nm->mkNode(STRING_STRCTN, x, constStr); - return returnRewrite(node, retNode, "re-concat-to-contains"); + Node retNode = nm->mkNode(STRING_STRCTN, x, constStr); + return returnRewrite(node, retNode, Rewrite::RE_CONCAT_TO_CONTAINS); } } } @@ -1665,132 +1755,125 @@ Node SequencesRewriter::rewriteMembership(TNode node) mvec.push_back( NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, r[i])); } - retNode = NodeManager::currentNM()->mkNode( + Node retNode = NodeManager::currentNM()->mkNode( r.getKind() == kind::REGEXP_INTER ? kind::AND : kind::OR, mvec); + return returnRewrite(node, retNode, Rewrite::RE_IN_ANDOR); } else if (r.getKind() == kind::STRING_TO_REGEXP) { - retNode = x.eqNode(r[0]); + Node retNode = x.eqNode(r[0]); + return returnRewrite(node, retNode, Rewrite::RE_IN_CSTRING); } else if (r.getKind() == REGEXP_RANGE) { // x in re.range( char_i, char_j ) ---> i <= str.code(x) <= j Node xcode = nm->mkNode(STRING_TO_CODE, x); - retNode = + Node retNode = nm->mkNode(AND, nm->mkNode(LEQ, nm->mkNode(STRING_TO_CODE, r[0]), xcode), nm->mkNode(LEQ, xcode, nm->mkNode(STRING_TO_CODE, r[1]))); + return returnRewrite(node, retNode, Rewrite::RE_IN_RANGE); } else if (r.getKind() == REGEXP_COMPLEMENT) { - retNode = nm->mkNode(STRING_IN_REGEXP, x, r[0]).negate(); - } - else if (x != node[0] || r != node[1]) - { - retNode = NodeManager::currentNM()->mkNode(kind::STRING_IN_REGEXP, x, r); + Node retNode = nm->mkNode(STRING_IN_REGEXP, x, r[0]).negate(); + return returnRewrite(node, retNode, Rewrite::RE_IN_COMPLEMENT); } // do simple consumes - if (retNode == node) + Node retNode = node; + if (r.getKind() == kind::REGEXP_STAR) { - if (r.getKind() == kind::REGEXP_STAR) + for (unsigned dir = 0; dir <= 1; dir++) { - for (unsigned dir = 0; dir <= 1; dir++) + std::vector<Node> mchildren; + utils::getConcat(x, mchildren); + bool success = true; + while (success) { - std::vector<Node> mchildren; - utils::getConcat(x, mchildren); - bool success = true; - while (success) + success = false; + std::vector<Node> children; + utils::getConcat(r[0], children); + Node scn = simpleRegexpConsume(mchildren, children, dir); + if (!scn.isNull()) { - success = false; - std::vector<Node> children; - utils::getConcat(r[0], children); - Node scn = simpleRegexpConsume(mchildren, children, dir); - if (!scn.isNull()) + Trace("regexp-ext-rewrite") + << "Regexp star : const conflict : " << node << std::endl; + return returnRewrite(node, scn, Rewrite::RE_CONSUME_S_CCONF); + } + else if (children.empty()) + { + // fully consumed one copy of the STAR + if (mchildren.empty()) { Trace("regexp-ext-rewrite") - << "Regexp star : const conflict : " << node << std::endl; - return scn; + << "Regexp star : full consume : " << node << std::endl; + Node ret = NodeManager::currentNM()->mkConst(true); + return returnRewrite(node, ret, Rewrite::RE_CONSUME_S_FULL); } - else if (children.empty()) + else { - // fully consumed one copy of the STAR - if (mchildren.empty()) - { - Trace("regexp-ext-rewrite") - << "Regexp star : full consume : " << node << std::endl; - return NodeManager::currentNM()->mkConst(true); - } - else - { - retNode = nm->mkNode(STRING_IN_REGEXP, - utils::mkConcat(mchildren, stype), - r); - success = true; - } + retNode = nm->mkNode( + STRING_IN_REGEXP, utils::mkConcat(mchildren, stype), r); + success = true; } } - if (retNode != node) - { - Trace("regexp-ext-rewrite") << "Regexp star : rewrite " << node - << " -> " << retNode << std::endl; - break; - } + } + if (retNode != node) + { + Trace("regexp-ext-rewrite") << "Regexp star : rewrite " << node + << " -> " << retNode << std::endl; + return returnRewrite(node, retNode, Rewrite::RE_CONSUME_S); } } - else - { - std::vector<Node> children; - utils::getConcat(r, children); - std::vector<Node> mchildren; - utils::getConcat(x, mchildren); - unsigned prevSize = children.size() + mchildren.size(); - Node scn = simpleRegexpConsume(mchildren, children); - if (!scn.isNull()) + } + else + { + std::vector<Node> children; + utils::getConcat(r, children); + std::vector<Node> mchildren; + utils::getConcat(x, mchildren); + unsigned prevSize = children.size() + mchildren.size(); + Node scn = simpleRegexpConsume(mchildren, children); + if (!scn.isNull()) + { + Trace("regexp-ext-rewrite") + << "Regexp : const conflict : " << node << std::endl; + return returnRewrite(node, scn, Rewrite::RE_CONSUME_CCONF); + } + else if ((children.size() + mchildren.size()) != prevSize) + { + // Given a membership (str.++ x1 ... xn) in (re.++ r1 ... rm), + // above, we strip components to construct an equivalent membership: + // (str.++ xi .. xj) in (re.++ rk ... rl). + Node xn = utils::mkConcat(mchildren, stype); + Node emptyStr = nm->mkConst(String("")); + if (children.empty()) { - Trace("regexp-ext-rewrite") - << "Regexp : const conflict : " << node << std::endl; - return scn; + // If we stripped all components on the right, then the left is + // equal to the empty string. + // e.g. (str.++ "a" x) in (re.++ (str.to.re "a")) ---> (= x "") + retNode = xn.eqNode(emptyStr); } else { - if ((children.size() + mchildren.size()) != prevSize) - { - // Given a membership (str.++ x1 ... xn) in (re.++ r1 ... rm), - // above, we strip components to construct an equivalent membership: - // (str.++ xi .. xj) in (re.++ rk ... rl). - Node xn = utils::mkConcat(mchildren, stype); - Node emptyStr = nm->mkConst(String("")); - if (children.empty()) - { - // If we stripped all components on the right, then the left is - // equal to the empty string. - // e.g. (str.++ "a" x) in (re.++ (str.to.re "a")) ---> (= x "") - retNode = xn.eqNode(emptyStr); - } - else - { - // otherwise, construct the updated regular expression - retNode = nm->mkNode( - STRING_IN_REGEXP, xn, utils::mkConcat(children, rtype)); - } - Trace("regexp-ext-rewrite") << "Regexp : rewrite : " << node << " -> " - << retNode << std::endl; - return returnRewrite(node, retNode, "re-simple-consume"); - } + // otherwise, construct the updated regular expression + retNode = + nm->mkNode(STRING_IN_REGEXP, xn, utils::mkConcat(children, rtype)); } + Trace("regexp-ext-rewrite") + << "Regexp : rewrite : " << node << " -> " << retNode << std::endl; + return returnRewrite(node, retNode, Rewrite::RE_SIMPLE_CONSUME); } } - return retNode; + return node; } RewriteResponse SequencesRewriter::postRewrite(TNode node) { Trace("strings-postrewrite") << "Strings::postRewrite start " << node << std::endl; - NodeManager* nm = NodeManager::currentNM(); Node retNode = node; - Node orig = retNode; Kind nk = node.getKind(); if (nk == kind::STRING_CONCAT) { @@ -1802,59 +1885,11 @@ RewriteResponse SequencesRewriter::postRewrite(TNode node) } else if (nk == kind::STRING_LENGTH) { - Kind nk0 = node[0].getKind(); - if (node[0].isConst()) - { - retNode = nm->mkConst(Rational(Word::getLength(node[0]))); - } - else if (nk0 == kind::STRING_CONCAT) - { - Node tmpNode = node[0]; - if (tmpNode.isConst()) - { - retNode = nm->mkConst(Rational(Word::getLength(tmpNode))); - } - else if (tmpNode.getKind() == kind::STRING_CONCAT) - { - std::vector<Node> node_vec; - for (unsigned int i = 0; i < tmpNode.getNumChildren(); ++i) - { - if (tmpNode[i].isConst()) - { - node_vec.push_back( - nm->mkConst(Rational(Word::getLength(tmpNode[i])))); - } - else - { - node_vec.push_back(NodeManager::currentNM()->mkNode( - kind::STRING_LENGTH, tmpNode[i])); - } - } - retNode = NodeManager::currentNM()->mkNode(kind::PLUS, node_vec); - } - } - else if (nk0 == STRING_STRREPL || nk0 == STRING_STRREPLALL) - { - Node len1 = Rewriter::rewrite(nm->mkNode(STRING_LENGTH, node[0][1])); - Node len2 = Rewriter::rewrite(nm->mkNode(STRING_LENGTH, node[0][2])); - if (len1 == len2) - { - // len( y ) == len( z ) => len( str.replace( x, y, z ) ) ---> len( x ) - retNode = nm->mkNode(STRING_LENGTH, node[0][0]); - } - } - else if (nk0 == STRING_TOLOWER || nk0 == STRING_TOUPPER - || nk0 == STRING_REV) - { - // len( f( x ) ) == len( x ) where f is tolower, toupper, or rev. - retNode = nm->mkNode(STRING_LENGTH, node[0][0]); - } + retNode = rewriteLength(node); } else if (nk == kind::STRING_CHARAT) { - Node one = NodeManager::currentNM()->mkConst(Rational(1)); - retNode = NodeManager::currentNM()->mkNode( - kind::STRING_SUBSTR, node[0], node[1], one); + retNode = rewriteCharAt(node); } else if (nk == kind::STRING_SUBSTR) { @@ -1866,10 +1901,7 @@ RewriteResponse SequencesRewriter::postRewrite(TNode node) } else if (nk == kind::STRING_LT) { - // eliminate s < t ---> s != t AND s <= t - retNode = nm->mkNode(AND, - node[0].eqNode(node[1]).negate(), - nm->mkNode(STRING_LEQ, node[0], node[1])); + retNode = StringsRewriter::rewriteStringLt(node); } else if (nk == kind::STRING_LEQ) { @@ -1901,11 +1933,7 @@ RewriteResponse SequencesRewriter::postRewrite(TNode node) } else if (nk == STRING_IS_DIGIT) { - // eliminate str.is_digit(s) ----> 48 <= str.to_code(s) <= 57 - Node t = nm->mkNode(STRING_TO_CODE, node[0]); - retNode = nm->mkNode(AND, - nm->mkNode(LEQ, nm->mkConst(Rational(48)), t), - nm->mkNode(LEQ, t, nm->mkConst(Rational(57)))); + retNode = StringsRewriter::rewriteStringIsDigit(node); } else if (nk == kind::STRING_ITOS) { @@ -1937,8 +1965,7 @@ RewriteResponse SequencesRewriter::postRewrite(TNode node) } else if (nk == REGEXP_DIFF) { - retNode = nm->mkNode( - REGEXP_INTER, node[0], nm->mkNode(REGEXP_COMPLEMENT, node[1])); + retNode = rewriteDifferenceRegExp(node); } else if (nk == REGEXP_STAR) { @@ -1946,36 +1973,34 @@ RewriteResponse SequencesRewriter::postRewrite(TNode node) } else if (nk == REGEXP_PLUS) { - retNode = - nm->mkNode(REGEXP_CONCAT, node[0], nm->mkNode(REGEXP_STAR, node[0])); + retNode = rewritePlusRegExp(node); } else if (nk == REGEXP_OPT) { - retNode = nm->mkNode(REGEXP_UNION, - nm->mkNode(STRING_TO_REGEXP, nm->mkConst(String(""))), - node[0]); + retNode = rewriteOptionRegExp(node); } else if (nk == REGEXP_RANGE) { - if (node[0] == node[1]) - { - retNode = nm->mkNode(STRING_TO_REGEXP, node[0]); - } + retNode = rewriteRangeRegExp(node); } else if (nk == REGEXP_LOOP) { retNode = rewriteLoopRegExp(node); } + else if (nk == REGEXP_REPEAT) + { + retNode = rewriteRepeatRegExp(node); + } Trace("strings-postrewrite") << "Strings::postRewrite returning " << retNode << std::endl; - if (orig != retNode) + if (node != retNode) { Trace("strings-rewrite-debug") - << "Strings: post-rewrite " << orig << " to " << retNode << std::endl; + << "Strings: post-rewrite " << node << " to " << retNode << std::endl; + return RewriteResponse(REWRITE_AGAIN_FULL, retNode); } - return RewriteResponse(orig == retNode ? REWRITE_DONE : REWRITE_AGAIN_FULL, - retNode); + return RewriteResponse(REWRITE_DONE, retNode); } bool SequencesRewriter::hasEpsilonNode(TNode node) @@ -1996,6 +2021,15 @@ RewriteResponse SequencesRewriter::preRewrite(TNode node) return RewriteResponse(REWRITE_DONE, node); } +Node SequencesRewriter::rewriteCharAt(Node node) +{ + Assert(node.getKind() == STRING_CHARAT); + NodeManager* nm = NodeManager::currentNM(); + Node one = nm->mkConst(Rational(1)); + Node retNode = nm->mkNode(STRING_SUBSTR, node[0], node[1], one); + return returnRewrite(node, retNode, Rewrite::CHARAT_ELIM); +} + Node SequencesRewriter::rewriteSubstr(Node node) { Assert(node.getKind() == kind::STRING_SUBSTR); @@ -2006,7 +2040,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) if (Word::isEmpty(node[0])) { Node ret = node[0]; - return returnRewrite(node, ret, "ss-emptystr"); + return returnRewrite(node, ret, Rewrite::SS_EMPTYSTR); } // rewriting for constant arguments if (node[1].isConst() && node[2].isConst()) @@ -2019,13 +2053,13 @@ Node SequencesRewriter::rewriteSubstr(Node node) // start beyond the maximum size of strings // thus, it must be beyond the end point of this string Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-const-start-max-oob"); + return returnRewrite(node, ret, Rewrite::SS_CONST_START_MAX_OOB); } else if (node[1].getConst<Rational>().sgn() < 0) { // start before the beginning of the string Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-const-start-neg"); + return returnRewrite(node, ret, Rewrite::SS_CONST_START_NEG); } else { @@ -2034,7 +2068,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) { // start beyond the end of the string Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-const-start-oob"); + return returnRewrite(node, ret, Rewrite::SS_CONST_START_OOB); } } if (node[2].getConst<Rational>() > rMaxInt) @@ -2042,12 +2076,12 @@ Node SequencesRewriter::rewriteSubstr(Node node) // take up to the end of the string size_t lenS = Word::getLength(s); Node ret = Word::suffix(s, lenS - start); - return returnRewrite(node, ret, "ss-const-len-max-oob"); + return returnRewrite(node, ret, Rewrite::SS_CONST_LEN_MAX_OOB); } else if (node[2].getConst<Rational>().sgn() <= 0) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-const-len-non-pos"); + return returnRewrite(node, ret, Rewrite::SS_CONST_LEN_NON_POS); } else { @@ -2058,13 +2092,13 @@ Node SequencesRewriter::rewriteSubstr(Node node) // take up to the end of the string size_t lenS = Word::getLength(s); Node ret = Word::suffix(s, lenS - start); - return returnRewrite(node, ret, "ss-const-end-oob"); + return returnRewrite(node, ret, Rewrite::SS_CONST_END_OOB); } else { // compute the substr using the constant string Node ret = Word::substr(s, start, len); - return returnRewrite(node, ret, "ss-const-ss"); + return returnRewrite(node, ret, Rewrite::SS_CONST_SS); } } } @@ -2075,12 +2109,12 @@ Node SequencesRewriter::rewriteSubstr(Node node) if (checkEntailArith(zero, node[1], true)) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-start-neg"); + return returnRewrite(node, ret, Rewrite::SS_START_NEG); } else if (checkEntailArith(zero, node[2])) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-len-non-pos"); + return returnRewrite(node, ret, Rewrite::SS_LEN_NON_POS); } if (node[0].getKind() == STRING_SUBSTR) @@ -2106,7 +2140,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) if (checkEntailArith(node[1], node[0][2])) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-start-geq-len"); + return returnRewrite(node, ret, Rewrite::SS_START_GEQ_LEN); } } else if (node[0].getKind() == STRING_STRREPL) @@ -2124,7 +2158,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) nm->mkNode(kind::STRING_SUBSTR, node[0][0], node[1], node[2]), node[0][1], node[0][2]); - return returnRewrite(node, ret, "substr-repl-swap"); + return returnRewrite(node, ret, Rewrite::SUBSTR_REPL_SWAP); } } } @@ -2146,7 +2180,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) kind::STRING_SUBSTR, utils::mkConcat(n1, stype), node[1], curr)); } Node ret = utils::mkConcat(childrenr, stype); - return returnRewrite(node, ret, "ss-len-include"); + return returnRewrite(node, ret, Rewrite::SS_LEN_INCLUDE); } } @@ -2174,7 +2208,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) { // end point beyond end point of string, map to tot_len Node ret = nm->mkNode(kind::STRING_SUBSTR, node[0], node[1], tot_len); - return returnRewrite(node, ret, "ss-end-pt-norm"); + return returnRewrite(node, ret, Rewrite::SS_END_PT_NORM); } else { @@ -2189,7 +2223,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) if (checkEntailArithWithAssumption(n1_lt_tot_len, zero, node[2], false)) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-start-entails-zero-len"); + return returnRewrite(node, ret, Rewrite::SS_START_ENTAILS_ZERO_LEN); } // (str.substr s x y) --> "" if 0 < y |= x >= str.len(s) @@ -2198,7 +2232,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) if (checkEntailArithWithAssumption(non_zero_len, node[1], tot_len, false)) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-non-zero-len-entails-oob"); + return returnRewrite(node, ret, Rewrite::SS_NON_ZERO_LEN_ENTAILS_OOB); } // (str.substr s x y) --> "" if x >= 0 |= 0 >= str.len(s) @@ -2207,14 +2241,15 @@ Node SequencesRewriter::rewriteSubstr(Node node) if (checkEntailArithWithAssumption(geq_zero_start, zero, tot_len, false)) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-geq-zero-start-entails-emp-s"); + return returnRewrite( + node, ret, Rewrite::SS_GEQ_ZERO_START_ENTAILS_EMP_S); } // (str.substr s x x) ---> "" if (str.len s) <= 1 if (node[1] == node[2] && checkEntailLengthOne(node[0])) { Node ret = Word::mkEmptyWord(node.getType()); - return returnRewrite(node, ret, "ss-len-one-z-z"); + return returnRewrite(node, ret, Rewrite::SS_LEN_ONE_Z_Z); } } if (!curr.isNull()) @@ -2228,7 +2263,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) { Node ret = nm->mkNode( kind::STRING_SUBSTR, utils::mkConcat(n1, stype), curr, node[2]); - return returnRewrite(node, ret, "ss-strip-start-pt"); + return returnRewrite(node, ret, Rewrite::SS_STRIP_START_PT); } else { @@ -2236,7 +2271,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) utils::mkConcat(n1, stype), node[1], node[2]); - return returnRewrite(node, ret, "ss-strip-end-pt"); + return returnRewrite(node, ret, Rewrite::SS_STRIP_END_PT); } } } @@ -2276,7 +2311,7 @@ Node SequencesRewriter::rewriteSubstr(Node node) Node new_start = nm->mkNode(kind::PLUS, start_inner, start_outer); Node ret = nm->mkNode(kind::STRING_SUBSTR, node[0][0], new_start, new_len); - return returnRewrite(node, ret, "ss-combine"); + return returnRewrite(node, ret, Rewrite::SS_COMBINE); } } } @@ -2292,15 +2327,14 @@ Node SequencesRewriter::rewriteContains(Node node) if (node[0] == node[1]) { Node ret = NodeManager::currentNM()->mkConst(true); - return returnRewrite(node, ret, "ctn-eq"); + return returnRewrite(node, ret, Rewrite::CTN_EQ); } if (node[0].isConst()) { - CVC4::String s = node[0].getConst<String>(); if (node[1].isConst()) { Node ret = nm->mkConst(Word::find(node[0], node[1]) != std::string::npos); - return returnRewrite(node, ret, "ctn-const"); + return returnRewrite(node, ret, Rewrite::CTN_CONST); } else { @@ -2315,26 +2349,25 @@ Node SequencesRewriter::rewriteContains(Node node) // uses this function, hence we want to conclude false if possible. // len(x)>0 => contains( "", x ) ---> false Node ret = NodeManager::currentNM()->mkConst(false); - return returnRewrite(node, ret, "ctn-lhs-emptystr"); + return returnRewrite(node, ret, Rewrite::CTN_LHS_EMPTYSTR); } } else if (checkEntailLengthOne(t)) { - const std::vector<unsigned>& vec = s.getVec(); - + std::vector<Node> vec = Word::getChars(node[0]); + Node emp = Word::mkEmptyWord(t.getType()); NodeBuilder<> nb(OR); - nb << nm->mkConst(String("")).eqNode(t); - for (unsigned c : vec) + nb << emp.eqNode(t); + for (const Node& c : vec) { - std::vector<unsigned> sv = {c}; - nb << nm->mkConst(String(sv)).eqNode(t); + nb << c.eqNode(t); } // str.contains("ABCabc", t) ---> // t = "" v t = "A" v t = "B" v t = "C" v t = "a" v t = "b" v t = "c" // if len(t) <= 1 Node ret = nb; - return returnRewrite(node, ret, "ctn-split"); + return returnRewrite(node, ret, Rewrite::CTN_SPLIT); } else if (node[1].getKind() == kind::STRING_CONCAT) { @@ -2342,7 +2375,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (!canConstantContainConcat(node[0], node[1], firstc, lastc)) { Node ret = NodeManager::currentNM()->mkConst(false); - return returnRewrite(node, ret, "ctn-nconst-ctn-concat"); + return returnRewrite(node, ret, Rewrite::CTN_NCONST_CTN_CONCAT); } } } @@ -2354,7 +2387,7 @@ Node SequencesRewriter::rewriteContains(Node node) { // contains( x, "" ) ---> true Node ret = NodeManager::currentNM()->mkConst(true); - return returnRewrite(node, ret, "ctn-rhs-emptystr"); + return returnRewrite(node, ret, Rewrite::CTN_RHS_EMPTYSTR); } else if (len == 1) { @@ -2373,7 +2406,7 @@ Node SequencesRewriter::rewriteContains(Node node) Node ret = nb.constructNode(); // str.contains( x ++ y, "A" ) ---> // str.contains( x, "A" ) OR str.contains( y, "A" ) - return returnRewrite(node, ret, "ctn-concat-char"); + return returnRewrite(node, ret, Rewrite::CTN_CONCAT_CHAR); } else if (node[0].getKind() == STRING_STRREPL) { @@ -2390,7 +2423,7 @@ Node SequencesRewriter::rewriteContains(Node node) // str.contains( str.replace( x, y, z ), "A" ) ---> // str.contains( x, "A" ) OR // ( str.contains( x, y ) AND str.contains( z, "A" ) ) - return returnRewrite(node, ret, "ctn-repl-char"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_CHAR); } } } @@ -2406,7 +2439,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (componentContains(nc1, nc2, nc1rb, nc1re) != -1) { Node ret = NodeManager::currentNM()->mkConst(true); - return returnRewrite(node, ret, "ctn-component"); + return returnRewrite(node, ret, Rewrite::CTN_COMPONENT); } TypeNode stype = node[0].getType(); @@ -2417,7 +2450,7 @@ Node SequencesRewriter::rewriteContains(Node node) { Node ret = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, utils::mkConcat(nc1, stype), node[1]); - return returnRewrite(node, ret, "ctn-strip-endpt"); + return returnRewrite(node, ret, Rewrite::CTN_STRIP_ENDPT); } for (const Node& n : nc2) @@ -2438,7 +2471,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (!ctnConst2.isNull() && !ctnConst2.getConst<bool>()) { Node res = nm->mkConst(false); - return returnRewrite(node, res, "ctn-rpl-non-ctn"); + return returnRewrite(node, res, Rewrite::CTN_RPL_NON_CTN); } } @@ -2470,7 +2503,7 @@ Node SequencesRewriter::rewriteContains(Node node) { ret = nm->mkNode(kind::EQUAL, node[0], node[1]); } - return returnRewrite(node, ret, "ctn-repl-self"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_SELF); } } } @@ -2482,7 +2515,7 @@ Node SequencesRewriter::rewriteContains(Node node) { // len( n2 ) > len( n1 ) => contains( n1, n2 ) ---> false Node ret = NodeManager::currentNM()->mkConst(false); - return returnRewrite(node, ret, "ctn-len-ineq"); + return returnRewrite(node, ret, Rewrite::CTN_LEN_INEQ); } // multi-set reasoning @@ -2492,14 +2525,14 @@ Node SequencesRewriter::rewriteContains(Node node) if (checkEntailMultisetSubset(node[0], node[1])) { Node ret = nm->mkConst(false); - return returnRewrite(node, ret, "ctn-mset-nss"); + return returnRewrite(node, ret, Rewrite::CTN_MSET_NSS); } if (checkEntailArith(len_n2, len_n1, false)) { // len( n2 ) >= len( n1 ) => contains( n1, n2 ) ---> n1 = n2 Node ret = node[0].eqNode(node[1]); - return returnRewrite(node, ret, "ctn-len-ineq-nstrict"); + return returnRewrite(node, ret, Rewrite::CTN_LEN_INEQ_NSTRICT); } // splitting @@ -2537,7 +2570,7 @@ Node SequencesRewriter::rewriteContains(Node node) NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, utils::mkConcat(spl[1], stype), node[1])); - return returnRewrite(node, ret, "ctn-split"); + return returnRewrite(node, ret, Rewrite::CTN_SPLIT); } } } @@ -2552,7 +2585,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (node[0][2] == nm->mkNode(kind::STRING_LENGTH, node[1])) { Node ret = nm->mkNode(kind::EQUAL, node[0], node[1]); - return returnRewrite(node, ret, "ctn-substr"); + return returnRewrite(node, ret, Rewrite::CTN_SUBSTR); } } else if (node[0].getKind() == kind::STRING_STRREPL) @@ -2565,7 +2598,7 @@ Node SequencesRewriter::rewriteContains(Node node) // (str.contains (str.replace x c1 c2) c3) ---> (str.contains x c3) // if there is no overlap between c1 and c3 and none between c2 and c3 Node ret = nm->mkNode(STRING_STRCTN, node[0][0], node[1]); - return returnRewrite(node, ret, "ctn-repl-cnsts-to-ctn"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_CNSTS_TO_CTN); } } @@ -2575,7 +2608,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (node[0][1] == node[1]) { Node ret = nm->mkNode(kind::STRING_STRCTN, node[0][0], node[1]); - return returnRewrite(node, ret, "ctn-repl-to-ctn"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_TO_CTN); } // (str.contains (str.replace x y x) z) ---> (str.contains x z) @@ -2583,7 +2616,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (checkEntailLengthOne(node[1])) { Node ret = nm->mkNode(kind::STRING_STRCTN, node[0][0], node[1]); - return returnRewrite(node, ret, "ctn-repl-len-one-to-ctn"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_LEN_ONE_TO_CTN); } } @@ -2594,7 +2627,7 @@ Node SequencesRewriter::rewriteContains(Node node) Node ret = nm->mkNode(OR, nm->mkNode(STRING_STRCTN, node[0][0], node[0][1]), nm->mkNode(STRING_STRCTN, node[0][0], node[0][2])); - return returnRewrite(node, ret, "ctn-repl-to-ctn-disj"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_TO_CTN_DISJ); } // (str.contains (str.replace x y z) w) ---> @@ -2610,7 +2643,7 @@ Node SequencesRewriter::rewriteContains(Node node) kind::STRING_STRCTN, nm->mkNode(kind::STRING_STRREPL, node[0][0], node[0][1], empty), node[1]); - return returnRewrite(node, ret, "ctn-repl-simp-repl"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_SIMP_REPL); } } } @@ -2622,7 +2655,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (node[0] == node[1][1] && node[1][0] == node[1][2]) { Node ret = nm->mkNode(kind::STRING_STRCTN, node[0], node[1][0]); - return returnRewrite(node, ret, "ctn-repl"); + return returnRewrite(node, ret, Rewrite::CTN_REPL); } // (str.contains x (str.replace "" x y)) ---> @@ -2635,7 +2668,7 @@ Node SequencesRewriter::rewriteContains(Node node) if (node[0] == node[1][1] && node[1][0] == emp) { Node ret = nm->mkNode(kind::EQUAL, emp, node[1]); - return returnRewrite(node, ret, "ctn-repl-empty"); + return returnRewrite(node, ret, Rewrite::CTN_REPL_EMPTY); } } @@ -2652,7 +2685,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) { // z<0 implies str.indexof( x, y, z ) --> -1 Node negone = nm->mkConst(Rational(-1)); - return returnRewrite(node, negone, "idof-neg"); + return returnRewrite(node, negone, Rewrite::IDOF_NEG); } // the string type @@ -2670,7 +2703,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) // in our implementation, that accessing a position greater than // rMaxInt is guaranteed to be out of bounds. Node negone = nm->mkConst(Rational(-1)); - return returnRewrite(node, negone, "idof-max"); + return returnRewrite(node, negone, Rewrite::IDOF_MAX); } Assert(node[2].getConst<Rational>().sgn() >= 0); Node s = children0[0]; @@ -2681,12 +2714,12 @@ Node SequencesRewriter::rewriteIndexof(Node node) if (ret != std::string::npos) { Node retv = nm->mkConst(Rational(static_cast<unsigned>(ret))); - return returnRewrite(node, retv, "idof-find"); + return returnRewrite(node, retv, Rewrite::IDOF_FIND); } else if (children0.size() == 1) { Node negone = nm->mkConst(Rational(-1)); - return returnRewrite(node, negone, "idof-nfind"); + return returnRewrite(node, negone, Rewrite::IDOF_NFIND); } } @@ -2698,21 +2731,21 @@ Node SequencesRewriter::rewriteIndexof(Node node) { // indexof( x, x, 0 ) --> 0 Node zero = nm->mkConst(Rational(0)); - return returnRewrite(node, zero, "idof-eq-cst-start"); + return returnRewrite(node, zero, Rewrite::IDOF_EQ_CST_START); } } if (checkEntailArith(node[2], true)) { // y>0 implies indexof( x, x, y ) --> -1 Node negone = nm->mkConst(Rational(-1)); - return returnRewrite(node, negone, "idof-eq-nstart"); + return returnRewrite(node, negone, Rewrite::IDOF_EQ_NSTART); } Node emp = nm->mkConst(CVC4::String("")); if (node[0] != emp) { // indexof( x, x, z ) ---> indexof( "", "", z ) Node ret = nm->mkNode(STRING_STRIDOF, emp, emp, node[2]); - return returnRewrite(node, ret, "idof-eq-norm"); + return returnRewrite(node, ret, Rewrite::IDOF_EQ_NORM); } } @@ -2727,7 +2760,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) if (checkEntailArith(len0, node[2]) && checkEntailArith(node[2])) { // len(x)>=z ^ z >=0 implies indexof( x, "", z ) ---> z - return returnRewrite(node, node[2], "idof-emp-idof"); + return returnRewrite(node, node[2], Rewrite::IDOF_EMP_IDOF); } } } @@ -2736,7 +2769,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) { // len(x)-z < len(y) implies indexof( x, y, z ) ----> -1 Node negone = nm->mkConst(Rational(-1)); - return returnRewrite(node, negone, "idof-len"); + return returnRewrite(node, negone, Rewrite::IDOF_LEN); } Node fstr = node[0]; @@ -2768,7 +2801,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) // str.indexof(str.++(x,y,z),y,0) ---> str.indexof(str.++(x,y),y,0) Node nn = utils::mkConcat(children0, stype); Node ret = nm->mkNode(kind::STRING_STRIDOF, nn, node[1], node[2]); - return returnRewrite(node, ret, "idof-def-ctn"); + return returnRewrite(node, ret, Rewrite::IDOF_DEF_CTN); } // Strip components from the beginning that are guaranteed not to match @@ -2783,7 +2816,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) utils::mkConcat(children0, stype), node[1], node[2])); - return returnRewrite(node, ret, "idof-strip-cnst-endpts"); + return returnRewrite(node, ret, Rewrite::IDOF_STRIP_CNST_ENDPTS); } } @@ -2802,14 +2835,14 @@ Node SequencesRewriter::rewriteIndexof(Node node) nm->mkNode(kind::PLUS, nm->mkNode(kind::MINUS, node[2], new_len), nm->mkNode(kind::STRING_STRIDOF, nn, node[1], new_len)); - return returnRewrite(node, ret, "idof-strip-sym-len"); + return returnRewrite(node, ret, Rewrite::IDOF_STRIP_SYM_LEN); } } else { // str.contains( x, y ) --> false implies str.indexof(x,y,z) --> -1 Node negone = nm->mkConst(Rational(-1)); - return returnRewrite(node, negone, "idof-nctn"); + return returnRewrite(node, negone, Rewrite::IDOF_NCTN); } } else @@ -2833,7 +2866,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) children.insert(children.end(), children0.begin(), children0.end()); Node nn = utils::mkConcat(children, stype); Node res = nm->mkNode(kind::STRING_STRIDOF, nn, node[1], node[2]); - return returnRewrite(node, res, "idof-norm-prefix"); + return returnRewrite(node, res, Rewrite::IDOF_NORM_PREFIX); } } } @@ -2848,7 +2881,7 @@ Node SequencesRewriter::rewriteIndexof(Node node) ret = nm->mkNode(STRING_STRIDOF, ret, node[1], node[2]); // For example: // str.indexof( str.++( x, "A" ), "B", 0 ) ---> str.indexof( x, "B", 0 ) - return returnRewrite(node, ret, "rpl-pull-endpt"); + return returnRewrite(node, ret, Rewrite::RPL_PULL_ENDPT); } } @@ -2864,7 +2897,7 @@ Node SequencesRewriter::rewriteReplace(Node node) if (node[1].isConst() && Word::isEmpty(node[1])) { Node ret = nm->mkNode(STRING_CONCAT, node[2], node[0]); - return returnRewrite(node, ret, "rpl-rpl-empty"); + return returnRewrite(node, ret, Rewrite::RPL_RPL_EMPTY); } // the string type TypeNode stype = node.getType(); @@ -2881,7 +2914,7 @@ Node SequencesRewriter::rewriteReplace(Node node) { if (children0.size() == 1) { - return returnRewrite(node, node[0], "rpl-const-nfind"); + return returnRewrite(node, node[0], Rewrite::RPL_CONST_NFIND); } } else @@ -2900,7 +2933,7 @@ Node SequencesRewriter::rewriteReplace(Node node) } children.insert(children.end(), children0.begin() + 1, children0.end()); Node ret = utils::mkConcat(children, stype); - return returnRewrite(node, ret, "rpl-const-find"); + return returnRewrite(node, ret, Rewrite::RPL_CONST_FIND); } } @@ -2919,7 +2952,7 @@ Node SequencesRewriter::rewriteReplace(Node node) Node l1 = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, node[1]); if (checkEntailArith(l1, l0)) { - return returnRewrite(node, node[0], "rpl-rpl-len-id"); + return returnRewrite(node, node[0], Rewrite::RPL_RPL_LEN_ID); } // (str.replace x y x) ---> (str.replace x (str.++ y1 ... yn) x) @@ -2941,7 +2974,7 @@ Node SequencesRewriter::rewriteReplace(Node node) if (node[1] != nn1) { Node ret = nm->mkNode(STRING_STRREPL, node[0], nn1, node[2]); - return returnRewrite(node, ret, "rpl-x-y-x-simp"); + return returnRewrite(node, ret, Rewrite::RPL_X_Y_X_SIMP); } } } @@ -2974,7 +3007,7 @@ Node SequencesRewriter::rewriteReplace(Node node) cres.push_back(node[2]); cres.insert(cres.end(), ce.begin(), ce.end()); Node ret = utils::mkConcat(cres, stype); - return returnRewrite(node, ret, "rpl-cctn-rpl"); + return returnRewrite(node, ret, Rewrite::RPL_CCTN_RPL); } else if (!ce.empty()) { @@ -2991,14 +3024,14 @@ Node SequencesRewriter::rewriteReplace(Node node) node[2])); scc.insert(scc.end(), ce.begin(), ce.end()); Node ret = utils::mkConcat(scc, stype); - return returnRewrite(node, ret, "rpl-cctn"); + return returnRewrite(node, ret, Rewrite::RPL_CCTN); } } } else { // ~contains( t, s ) => ( replace( t, s, r ) ----> t ) - return returnRewrite(node, node[0], "rpl-nctn"); + return returnRewrite(node, node[0], Rewrite::RPL_NCTN); } } else if (cmp_conr.getKind() == kind::EQUAL || cmp_conr.getKind() == kind::AND) @@ -3042,14 +3075,14 @@ Node SequencesRewriter::rewriteReplace(Node node) if (nn1 != node[1] || nn2 != node[2]) { Node res = nm->mkNode(kind::STRING_STRREPL, node[0], nn1, nn2); - return returnRewrite(node, res, "rpl-emp-cnts-substs"); + return returnRewrite(node, res, Rewrite::RPL_EMP_CNTS_SUBSTS); } } if (nn2 != node[2]) { Node res = nm->mkNode(kind::STRING_STRREPL, node[0], node[1], nn2); - return returnRewrite(node, res, "rpl-cnts-substs"); + return returnRewrite(node, res, Rewrite::RPL_CNTS_SUBSTS); } } } @@ -3075,7 +3108,7 @@ Node SequencesRewriter::rewriteReplace(Node node) node[2])); cc.insert(cc.end(), ce.begin(), ce.end()); Node ret = utils::mkConcat(cc, stype); - return returnRewrite(node, ret, "rpl-pull-endpt"); + return returnRewrite(node, ret, Rewrite::RPL_PULL_ENDPT); } } } @@ -3117,7 +3150,7 @@ Node SequencesRewriter::rewriteReplace(Node node) node[0], utils::mkConcat(children1, stype), node[2]); - return returnRewrite(node, res, "repl-subst-idx"); + return returnRewrite(node, res, Rewrite::REPL_SUBST_IDX); } } @@ -3165,7 +3198,7 @@ Node SequencesRewriter::rewriteReplace(Node node) nm->mkNode(kind::STRING_STRREPL, y, w, z), y, z); - return returnRewrite(node, ret, "repl-repl-short-circuit"); + return returnRewrite(node, ret, Rewrite::REPL_REPL_SHORT_CIRCUIT); } } } @@ -3178,7 +3211,7 @@ Node SequencesRewriter::rewriteReplace(Node node) if (node[1][0] == node[1][2] && node[1][0] == node[2]) { // str.replace( x, str.replace( x, y, x ), x ) ---> x - return returnRewrite(node, node[0], "repl-repl2-inv-id"); + return returnRewrite(node, node[0], Rewrite::REPL_REPL2_INV_ID); } bool dualReplIteSuccess = false; Node cmp_con2 = checkEntailContains(node[1][0], node[1][2]); @@ -3212,7 +3245,7 @@ Node SequencesRewriter::rewriteReplace(Node node) nm->mkNode(STRING_STRCTN, node[0], node[1][1]), node[0], node[2]); - return returnRewrite(node, res, "repl-dual-repl-ite"); + return returnRewrite(node, res, Rewrite::REPL_DUAL_REPL_ITE); } } @@ -3248,7 +3281,7 @@ Node SequencesRewriter::rewriteReplace(Node node) if (invSuccess) { Node res = nm->mkNode(kind::STRING_STRREPL, node[0], node[1][0], node[2]); - return returnRewrite(node, res, "repl-repl2-inv"); + return returnRewrite(node, res, Rewrite::REPL_REPL2_INV); } } if (node[2].getKind() == STRING_STRREPL) @@ -3262,7 +3295,7 @@ Node SequencesRewriter::rewriteReplace(Node node) { Node res = nm->mkNode(kind::STRING_STRREPL, node[0], node[1], node[2][0]); - return returnRewrite(node, res, "repl-repl3-inv"); + return returnRewrite(node, res, Rewrite::REPL_REPL3_INV); } } if (node[2][0] == node[1]) @@ -3282,7 +3315,7 @@ Node SequencesRewriter::rewriteReplace(Node node) } if (success) { - return returnRewrite(node, node[0], "repl-repl3-inv-id"); + return returnRewrite(node, node[0], Rewrite::REPL_REPL3_INV_ID); } } } @@ -3329,7 +3362,7 @@ Node SequencesRewriter::rewriteReplace(Node node) // second occurrence of x. Notice this is specific to single characters // due to complications with finds that span multiple components for // non-characters. - return returnRewrite(node, ret, "repl-char-ncontrib-find"); + return returnRewrite(node, ret, Rewrite::REPL_CHAR_NCONTRIB_FIND); } } @@ -3356,7 +3389,7 @@ Node SequencesRewriter::rewriteReplaceAll(Node node) Node t = node[1]; if (Word::isEmpty(s) || Word::isEmpty(t)) { - return returnRewrite(node, node[0], "replall-empty-find"); + return returnRewrite(node, node[0], Rewrite::REPLALL_EMPTY_FIND); } std::size_t sizeS = Word::getLength(s); std::size_t sizeT = Word::getLength(t); @@ -3381,7 +3414,7 @@ Node SequencesRewriter::rewriteReplaceAll(Node node) } while (curr != std::string::npos && curr < sizeS); // constant evaluation Node res = utils::mkConcat(children, stype); - return returnRewrite(node, res, "replall-const"); + return returnRewrite(node, res, Rewrite::REPLALL_CONST); } // rewrites that apply to both replace and replaceall @@ -3403,7 +3436,7 @@ Node SequencesRewriter::rewriteReplaceInternal(Node node) if (node[1] == node[2]) { - return returnRewrite(node, node[0], "rpl-id"); + return returnRewrite(node, node[0], Rewrite::RPL_ID); } if (node[0] == node[1]) @@ -3411,7 +3444,7 @@ Node SequencesRewriter::rewriteReplaceInternal(Node node) // only holds for replaceall if non-empty if (nk == STRING_STRREPL || checkEntailNonEmpty(node[1])) { - return returnRewrite(node, node[2], "rpl-replace"); + return returnRewrite(node, node[2], Rewrite::RPL_REPLACE); } } @@ -3428,7 +3461,7 @@ Node SequencesRewriter::rewriteStrReverse(Node node) std::vector<unsigned> nvec = node[0].getConst<String>().getVec(); std::reverse(nvec.begin(), nvec.end()); Node retNode = nm->mkConst(String(nvec)); - return returnRewrite(node, retNode, "str-conv-const"); + return returnRewrite(node, retNode, Rewrite::STR_CONV_CONST); } else if (x.getKind() == STRING_CONCAT) { @@ -3440,13 +3473,13 @@ Node SequencesRewriter::rewriteStrReverse(Node node) std::reverse(children.begin(), children.end()); // rev( x1 ++ x2 ) --> rev( x2 ) ++ rev( x1 ) Node retNode = nm->mkNode(STRING_CONCAT, children); - return returnRewrite(node, retNode, "str-rev-minscope-concat"); + return returnRewrite(node, retNode, Rewrite::STR_REV_MINSCOPE_CONCAT); } else if (x.getKind() == STRING_REV) { // rev( rev( x ) ) --> x Node retNode = x[0]; - return returnRewrite(node, retNode, "str-rev-idem"); + return returnRewrite(node, retNode, Rewrite::STR_REV_IDEM); } return node; } @@ -3459,7 +3492,7 @@ Node SequencesRewriter::rewritePrefixSuffix(Node n) if (n[0] == n[1]) { Node ret = NodeManager::currentNM()->mkConst(true); - return returnRewrite(n, ret, "suf/prefix-eq"); + return returnRewrite(n, ret, Rewrite::SUF_PREFIX_EQ); } if (n[0].isConst()) { @@ -3467,7 +3500,7 @@ Node SequencesRewriter::rewritePrefixSuffix(Node n) if (t.isEmptyString()) { Node ret = NodeManager::currentNM()->mkConst(true); - return returnRewrite(n, ret, "suf/prefix-empty-const"); + return returnRewrite(n, ret, Rewrite::SUF_PREFIX_EMPTY_CONST); } } if (n[1].isConst()) @@ -3487,12 +3520,12 @@ Node SequencesRewriter::rewritePrefixSuffix(Node n) ret = NodeManager::currentNM()->mkConst(true); } } - return returnRewrite(n, ret, "suf/prefix-const"); + return returnRewrite(n, ret, Rewrite::SUF_PREFIX_CONST); } else if (lenS == 0) { Node ret = n[0].eqNode(n[1]); - return returnRewrite(n, ret, "suf/prefix-empty"); + return returnRewrite(n, ret, Rewrite::SUF_PREFIX_EMPTY); } else if (lenS == 1) { @@ -3500,7 +3533,7 @@ Node SequencesRewriter::rewritePrefixSuffix(Node n) // (str.contains "A" x ) Node ret = NodeManager::currentNM()->mkNode(kind::STRING_STRCTN, n[1], n[0]); - return returnRewrite(n, ret, "suf/prefix-ctn"); + return returnRewrite(n, ret, Rewrite::SUF_PREFIX_CTN); } } Node lens = NodeManager::currentNM()->mkNode(kind::STRING_LENGTH, n[0]); @@ -3520,14 +3553,14 @@ Node SequencesRewriter::rewritePrefixSuffix(Node n) Node eqs = inferEqsFromContains(n[1], n[0]); if (!eqs.isNull()) { - return returnRewrite(n, eqs, "suf/prefix-to-eqs"); + return returnRewrite(n, eqs, Rewrite::SUF_PREFIX_TO_EQS); } // general reduction to equality + substr Node retNode = n[0].eqNode( NodeManager::currentNM()->mkNode(kind::STRING_SUBSTR, n[1], val, lens)); - return retNode; + return returnRewrite(n, retNode, Rewrite::SUF_PREFIX_ELIM); } Node SequencesRewriter::splitConstant(Node a, Node b, int& index, bool isRev) @@ -4196,7 +4229,7 @@ bool SequencesRewriter::stripConstantEndpoints(std::vector<Node>& n1, return changed; } -Node SequencesRewriter::canonicalStrForSymbolicLength(Node len) +Node SequencesRewriter::canonicalStrForSymbolicLength(Node len, TypeNode stype) { NodeManager* nm = NodeManager::currentNM(); @@ -4207,7 +4240,15 @@ Node SequencesRewriter::canonicalStrForSymbolicLength(Node len) Rational ratLen = len.getConst<Rational>(); Assert(ratLen.getDenominator() == 1); Integer intLen = ratLen.getNumerator(); - res = nm->mkConst(String(std::string(intLen.getUnsignedInt(), 'A'))); + uint32_t u = intLen.getUnsignedInt(); + if (stype.isString()) + { + res = nm->mkConst(String(std::string(u, 'A'))); + } + else + { + Unimplemented() << "canonicalStrForSymbolicLength for non-string"; + } } else if (len.getKind() == kind::PLUS) { @@ -4215,7 +4256,7 @@ Node SequencesRewriter::canonicalStrForSymbolicLength(Node len) NodeBuilder<> concatBuilder(kind::STRING_CONCAT); for (const auto& n : len) { - Node sn = canonicalStrForSymbolicLength(n); + Node sn = canonicalStrForSymbolicLength(n, stype); if (sn.isNull()) { return Node::null(); @@ -4234,7 +4275,7 @@ Node SequencesRewriter::canonicalStrForSymbolicLength(Node len) Assert(ratReps.getDenominator() == 1); Integer intReps = ratReps.getNumerator(); - Node nRep = canonicalStrForSymbolicLength(len[1]); + Node nRep = canonicalStrForSymbolicLength(len[1], stype); std::vector<Node> nRepChildren; utils::getConcat(nRep, nRepChildren); NodeBuilder<> concatBuilder(kind::STRING_CONCAT); @@ -4256,7 +4297,7 @@ Node SequencesRewriter::lengthPreserveRewrite(Node n) { NodeManager* nm = NodeManager::currentNM(); Node len = Rewriter::rewrite(nm->mkNode(kind::STRING_LENGTH, n)); - Node res = canonicalStrForSymbolicLength(len); + Node res = canonicalStrForSymbolicLength(len, n.getType()); return res.isNull() ? n : res; } @@ -4833,8 +4874,6 @@ void SequencesRewriter::getArithApproximations(Node a, bool SequencesRewriter::checkEntailMultisetSubset(Node a, Node b) { - NodeManager* nm = NodeManager::currentNM(); - std::vector<Node> avec; utils::getConcat(getMultisetApproximation(a), avec); std::vector<Node> bvec; @@ -4877,14 +4916,9 @@ bool SequencesRewriter::checkEntailMultisetSubset(Node a, Node b) { Node cn = ncp.first; Assert(cn.isConst()); - std::vector<unsigned> cc_vec; - const std::vector<unsigned>& cvec = cn.getConst<String>().getVec(); - for (unsigned i = 0, size = cvec.size(); i < size; i++) + std::vector<Node> cnChars = Word::getChars(cn); + for (const Node& ch : cnChars) { - // make the character - cc_vec.clear(); - cc_vec.insert(cc_vec.end(), cvec.begin() + i, cvec.begin() + i + 1); - Node ch = nm->mkConst(String(cc_vec)); count_const[j][ch] += ncp.second; if (std::find(chars.begin(), chars.end(), ch) == chars.end()) { @@ -4919,19 +4953,17 @@ bool SequencesRewriter::checkEntailMultisetSubset(Node a, Node b) Node SequencesRewriter::checkEntailHomogeneousString(Node a) { - NodeManager* nm = NodeManager::currentNM(); - std::vector<Node> avec; utils::getConcat(getMultisetApproximation(a), avec); bool cValid = false; - unsigned c = 0; + Node c; for (const Node& ac : avec) { if (ac.isConst()) { - std::vector<unsigned> acv = ac.getConst<String>().getVec(); - for (unsigned cc : acv) + std::vector<Node> acv = Word::getChars(ac); + for (const Node& cc : acv) { if (!cValid) { @@ -4954,11 +4986,10 @@ Node SequencesRewriter::checkEntailHomogeneousString(Node a) if (!cValid) { - return nm->mkConst(String("")); + return Word::mkEmptyWord(a.getType()); } - std::vector<unsigned> cv = {c}; - return nm->mkConst(String(cv)); + return c; } Node SequencesRewriter::getMultisetApproximation(Node a) @@ -5568,9 +5599,9 @@ std::pair<bool, std::vector<Node> > SequencesRewriter::collectEmptyEqs(Node x) allEmptyEqs, std::vector<Node>(emptyNodes.begin(), emptyNodes.end())); } -Node SequencesRewriter::returnRewrite(Node node, Node ret, const char* c) +Node SequencesRewriter::returnRewrite(Node node, Node ret, Rewrite r) { - Trace("strings-rewrite") << "Rewrite " << node << " to " << ret << " by " << c + Trace("strings-rewrite") << "Rewrite " << node << " to " << ret << " by " << r << "." << std::endl; NodeManager* nm = NodeManager::currentNM(); diff --git a/src/theory/strings/sequences_rewriter.h b/src/theory/strings/sequences_rewriter.h index 5aba4ab6f..0e5cd5705 100644 --- a/src/theory/strings/sequences_rewriter.h +++ b/src/theory/strings/sequences_rewriter.h @@ -23,6 +23,7 @@ #include <vector> #include "expr/attribute.h" +#include "theory/strings/rewrites.h" #include "theory/theory_rewriter.h" #include "theory/type_enumerator.h" @@ -120,6 +121,37 @@ class SequencesRewriter : public TheoryRewriter * Returns the rewritten form of node. */ static Node rewriteLoopRegExp(TNode node); + /** rewrite regular expression repeat + * + * This is the entry point for post-rewriting applications of re.repeat. + * Returns the rewritten form of node. + */ + static Node rewriteRepeatRegExp(TNode node); + /** rewrite regular expression option + * + * This is the entry point for post-rewriting applications of re.opt. + * Returns the rewritten form of node. + */ + static Node rewriteOptionRegExp(TNode node); + /** rewrite regular expression plus + * + * This is the entry point for post-rewriting applications of re.+. + * Returns the rewritten form of node. + */ + static Node rewritePlusRegExp(TNode node); + /** rewrite regular expression difference + * + * This is the entry point for post-rewriting applications of re.diff. + * Returns the rewritten form of node. + */ + static Node rewriteDifferenceRegExp(TNode node); + /** rewrite regular expression range + * + * This is the entry point for post-rewriting applications of re.range. + * Returns the rewritten form of node. + */ + static Node rewriteRangeRegExp(TNode node); + /** rewrite regular expression membership * * This is the entry point for post-rewriting applications of str.in.re @@ -149,7 +181,7 @@ class SequencesRewriter : public TheoryRewriter /** * Called when node rewrites to ret. * - * The string c indicates the justification for the rewrite, which is printed + * The rewrite r indicates the justification for the rewrite, which is printed * by this function for debugging. * * If node is not an equality and ret is an equality, this method applies @@ -157,7 +189,7 @@ class SequencesRewriter : public TheoryRewriter * additional rewrites on ret, after which we return the result of this call. * Otherwise, this method simply returns ret. */ - static Node returnRewrite(Node node, Node ret, const char* c); + static Node returnRewrite(Node node, Node ret, Rewrite r); public: RewriteResponse postRewrite(TNode node) override; @@ -181,12 +213,24 @@ class SequencesRewriter : public TheoryRewriter * necessarily one of { s = t, t = s, true, false }. */ static Node rewriteEqualityExt(Node node); + /** rewrite string length + * This is the entry point for post-rewriting terms node of the form + * str.len( t ) + * Returns the rewritten form of node. + */ + static Node rewriteLength(Node node); /** rewrite concat * This is the entry point for post-rewriting terms node of the form * str.++( t1, .., tn ) * Returns the rewritten form of node. */ static Node rewriteConcat(Node node); + /** rewrite character at + * This is the entry point for post-rewriting terms node of the form + * str.charat( s, i1 ) + * Returns the rewritten form of node. + */ + static Node rewriteCharAt(Node node); /** rewrite substr * This is the entry point for post-rewriting terms node of the form * str.substr( s, i1, i2 ) @@ -468,11 +512,12 @@ class SequencesRewriter : public TheoryRewriter int dir = 0); /** - * Given a symbolic length n, returns the canonical string for that length. - * For example if n is constant, this function returns a string consisting of - * "A" repeated n times. Returns the null node if no such string exists. + * Given a symbolic length n, returns the canonical string (of type stype) + * for that length. For example if n is constant, this function returns a + * string consisting of "A" repeated n times. Returns the null node if no such + * string exists. */ - static Node canonicalStrForSymbolicLength(Node n); + static Node canonicalStrForSymbolicLength(Node n, TypeNode stype); /** length preserving rewrite * diff --git a/src/theory/strings/sequences_stats.cpp b/src/theory/strings/sequences_stats.cpp index 0f1e93599..5cd844290 100644 --- a/src/theory/strings/sequences_stats.cpp +++ b/src/theory/strings/sequences_stats.cpp @@ -22,14 +22,51 @@ namespace theory { namespace strings { SequencesStatistics::SequencesStatistics() - : d_inferences("theory::strings::inferences") + : d_inferences("theory::strings::inferences"), + d_cdSimplifications("theory::strings::cdSimplifications"), + d_reductions("theory::strings::reductions"), + d_regexpUnfoldingsPos("theory::strings::regexpUnfoldingsPos"), + d_regexpUnfoldingsNeg("theory::strings::regexpUnfoldingsNeg"), + d_conflictsEqEngine("theory::strings::conflictsEqEngine", 0), + d_conflictsEagerPrefix("theory::strings::conflictsEagerPrefix", 0), + d_conflictsInfer("theory::strings::conflictsInfer", 0), + d_lemmasEagerPreproc("theory::strings::lemmasEagerPreproc", 0), + d_lemmasCmiSplit("theory::strings::lemmasCmiSplit", 0), + d_lemmasRegisterTerm("theory::strings::lemmasRegisterTerm", 0), + d_lemmasRegisterTermAtomic("theory::strings::lemmasRegisterTermAtomic", + 0), + d_lemmasInfer("theory::strings::lemmasInfer", 0) { smtStatisticsRegistry()->registerStat(&d_inferences); + smtStatisticsRegistry()->registerStat(&d_cdSimplifications); + smtStatisticsRegistry()->registerStat(&d_reductions); + smtStatisticsRegistry()->registerStat(&d_regexpUnfoldingsPos); + smtStatisticsRegistry()->registerStat(&d_regexpUnfoldingsNeg); + smtStatisticsRegistry()->registerStat(&d_conflictsEqEngine); + smtStatisticsRegistry()->registerStat(&d_conflictsEagerPrefix); + smtStatisticsRegistry()->registerStat(&d_conflictsInfer); + smtStatisticsRegistry()->registerStat(&d_lemmasEagerPreproc); + smtStatisticsRegistry()->registerStat(&d_lemmasCmiSplit); + smtStatisticsRegistry()->registerStat(&d_lemmasRegisterTerm); + smtStatisticsRegistry()->registerStat(&d_lemmasRegisterTermAtomic); + smtStatisticsRegistry()->registerStat(&d_lemmasInfer); } SequencesStatistics::~SequencesStatistics() { smtStatisticsRegistry()->unregisterStat(&d_inferences); + smtStatisticsRegistry()->unregisterStat(&d_cdSimplifications); + smtStatisticsRegistry()->unregisterStat(&d_reductions); + smtStatisticsRegistry()->unregisterStat(&d_regexpUnfoldingsPos); + smtStatisticsRegistry()->unregisterStat(&d_regexpUnfoldingsNeg); + smtStatisticsRegistry()->unregisterStat(&d_conflictsEqEngine); + smtStatisticsRegistry()->unregisterStat(&d_conflictsEagerPrefix); + smtStatisticsRegistry()->unregisterStat(&d_conflictsInfer); + smtStatisticsRegistry()->unregisterStat(&d_lemmasEagerPreproc); + smtStatisticsRegistry()->unregisterStat(&d_lemmasCmiSplit); + smtStatisticsRegistry()->unregisterStat(&d_lemmasRegisterTerm); + smtStatisticsRegistry()->unregisterStat(&d_lemmasRegisterTermAtomic); + smtStatisticsRegistry()->unregisterStat(&d_lemmasInfer); } } diff --git a/src/theory/strings/sequences_stats.h b/src/theory/strings/sequences_stats.h index b55178f4c..65f50dbbc 100644 --- a/src/theory/strings/sequences_stats.h +++ b/src/theory/strings/sequences_stats.h @@ -17,6 +17,7 @@ #ifndef CVC4__THEORY__STRINGS__SEQUENCES_STATS_H #define CVC4__THEORY__STRINGS__SEQUENCES_STATS_H +#include "expr/kind.h" #include "theory/strings/infer_info.h" #include "util/statistics_registry.h" @@ -24,17 +25,80 @@ namespace CVC4 { namespace theory { namespace strings { +/** + * Statistics for the theory of strings. + * + * This is roughly broken up into the following parts: + * (1) Inferences, + * (2) Conflicts, + * (3) Lemmas. + * + * "Inferences" (1) are steps invoked during solving, which either trigger: + * (a) An internal update to the state of the solver (e.g. adding an inferred + * equality to the equality engine), + * (b) A call to OutputChannel::conflict, + * (c) A call to OutputChannel::lemma. + * For details, see InferenceManager. We track stats on each kind of + * inference that have been indicated by the solvers in TheoryStrings. + * Some kinds of inferences are further distinguished by the Kind of the node + * they operate on (see d_cdSimplifications, d_reductions, d_regexpUnfoldings). + * + * "Conflicts" (2) arise from various kinds of reasoning, listed below, + * where inferences are one of the possible methods for deriving conflicts. + * + * "Lemmas" (3) also arise from various kinds of reasoning, listed below, + * where inferences are one of the possible methods for deriving lemmas. + */ class SequencesStatistics { public: SequencesStatistics(); ~SequencesStatistics(); - - /** Counts the number of inferences made of each type of inference */ + //--------------- inferences + /** Counts the number of applications of each type of inference */ HistogramStat<Inference> d_inferences; + /** + * Counts the number of applications of each type of context-dependent + * simplification. The sum of this map is equal to the number of EXTF or + * EXTF_N inferences. + */ + HistogramStat<Kind> d_cdSimplifications; + /** + * Counts the number of applications of each type of reduction. The sum of + * this map is equal to the number of REDUCTION inferences (when + * options::stringLazyPreproc is true). + */ + HistogramStat<Kind> d_reductions; + /** + * Counts the number of applications of each type of regular expression + * positive (resp. negative) unfoldings. The sum of this map is equal to the + * number of RE_UNFOLD_POS (resp. RE_UNFOLD_NEG) inferences. + */ + HistogramStat<Kind> d_regexpUnfoldingsPos; + HistogramStat<Kind> d_regexpUnfoldingsNeg; + //--------------- end of inferences + //--------------- conflicts, partition of calls to OutputChannel::conflict + /** Number of equality engine conflicts */ + IntStat d_conflictsEqEngine; + /** Number of eager prefix conflicts */ + IntStat d_conflictsEagerPrefix; + /** Number of inference conflicts */ + IntStat d_conflictsInfer; + //--------------- end of conflicts + //--------------- lemmas, partition of calls to OutputChannel::lemma + /** Number of lemmas added due to eager preprocessing */ + IntStat d_lemmasEagerPreproc; + /** Number of collect model info splits */ + IntStat d_lemmasCmiSplit; + /** Number of lemmas added due to registering terms */ + IntStat d_lemmasRegisterTerm; + /** Number of lemmas added due to registering atomic terms */ + IntStat d_lemmasRegisterTermAtomic; + /** Number of lemmas added due to inferences */ + IntStat d_lemmasInfer; + //--------------- end of lemmas }; - } } } diff --git a/src/theory/strings/solver_state.cpp b/src/theory/strings/solver_state.cpp index a38bf2c50..30acba9fd 100644 --- a/src/theory/strings/solver_state.cpp +++ b/src/theory/strings/solver_state.cpp @@ -108,7 +108,7 @@ void SolverState::eqNotifyNewClass(TNode t) ei->d_codeTerm = t[0]; } } - else if (k == CONST_STRING) + else if (t.isConst()) { EqcInfo* ei = getOrMakeEqcInfo(t); ei->d_prefixC = t; diff --git a/src/theory/strings/strings_fmf.cpp b/src/theory/strings/strings_fmf.cpp index 8ca6d2de1..02af3949e 100644 --- a/src/theory/strings/strings_fmf.cpp +++ b/src/theory/strings/strings_fmf.cpp @@ -40,7 +40,7 @@ StringsFmf::~StringsFmf() {} void StringsFmf::preRegisterTerm(TNode n) { - if (!n.getType().isString()) + if (!n.getType().isStringLike()) { return; } diff --git a/src/theory/strings/strings_rewriter.cpp b/src/theory/strings/strings_rewriter.cpp index 75dfe7432..28ed14095 100644 --- a/src/theory/strings/strings_rewriter.cpp +++ b/src/theory/strings/strings_rewriter.cpp @@ -41,7 +41,7 @@ Node StringsRewriter::rewriteStrToInt(Node node) { ret = nm->mkConst(Rational(-1)); } - return returnRewrite(node, ret, "stoi-eval"); + return returnRewrite(node, ret, Rewrite::STOI_EVAL); } else if (node[0].getKind() == STRING_CONCAT) { @@ -53,7 +53,7 @@ Node StringsRewriter::rewriteStrToInt(Node node) if (!t.isNumber()) { Node ret = nm->mkConst(Rational(-1)); - return returnRewrite(node, ret, "stoi-concat-nonnum"); + return returnRewrite(node, ret, Rewrite::STOI_CONCAT_NONNUM); } } } @@ -78,7 +78,7 @@ Node StringsRewriter::rewriteIntToStr(Node node) Assert(stmp[0] != '-'); ret = nm->mkConst(String(stmp)); } - return returnRewrite(node, ret, "itos-eval"); + return returnRewrite(node, ret, Rewrite::ITOS_EVAL); } return node; } @@ -93,7 +93,7 @@ Node StringsRewriter::rewriteStrConvert(Node node) std::vector<unsigned> nvec = node[0].getConst<String>().getVec(); for (unsigned i = 0, nvsize = nvec.size(); i < nvsize; i++) { - unsigned newChar = String::convertUnsignedIntToCode(nvec[i]); + unsigned newChar = nvec[i]; // transform it // upper 65 ... 90 // lower 97 ... 122 @@ -111,11 +111,10 @@ Node StringsRewriter::rewriteStrConvert(Node node) newChar = newChar + 32; } } - newChar = String::convertCodeToUnsignedInt(newChar); nvec[i] = newChar; } Node retNode = nm->mkConst(String(nvec)); - return returnRewrite(node, retNode, "str-conv-const"); + return returnRewrite(node, retNode, Rewrite::STR_CONV_CONST); } else if (node[0].getKind() == STRING_CONCAT) { @@ -126,7 +125,7 @@ Node StringsRewriter::rewriteStrConvert(Node node) } // tolower( x1 ++ x2 ) --> tolower( x1 ) ++ tolower( x2 ) Node retNode = concatBuilder.constructNode(); - return returnRewrite(node, retNode, "str-conv-minscope-concat"); + return returnRewrite(node, retNode, Rewrite::STR_CONV_MINSCOPE_CONCAT); } else if (node[0].getKind() == STRING_TOLOWER || node[0].getKind() == STRING_TOUPPER) @@ -134,16 +133,26 @@ Node StringsRewriter::rewriteStrConvert(Node node) // tolower( tolower( x ) ) --> tolower( x ) // tolower( toupper( x ) ) --> tolower( x ) Node retNode = nm->mkNode(nk, node[0][0]); - return returnRewrite(node, retNode, "str-conv-idem"); + return returnRewrite(node, retNode, Rewrite::STR_CONV_IDEM); } else if (node[0].getKind() == STRING_ITOS) { // tolower( str.from.int( x ) ) --> str.from.int( x ) - return returnRewrite(node, node[0], "str-conv-itos"); + return returnRewrite(node, node[0], Rewrite::STR_CONV_ITOS); } return node; } +Node StringsRewriter::rewriteStringLt(Node n) +{ + Assert(n.getKind() == kind::STRING_LT); + NodeManager* nm = NodeManager::currentNM(); + // eliminate s < t ---> s != t AND s <= t + Node retNode = nm->mkNode( + AND, n[0].eqNode(n[1]).negate(), nm->mkNode(STRING_LEQ, n[0], n[1])); + return returnRewrite(n, retNode, Rewrite::STR_LT_ELIM); +} + Node StringsRewriter::rewriteStringLeq(Node n) { Assert(n.getKind() == kind::STRING_LEQ); @@ -151,14 +160,14 @@ Node StringsRewriter::rewriteStringLeq(Node n) if (n[0] == n[1]) { Node ret = nm->mkConst(true); - return returnRewrite(n, ret, "str-leq-id"); + return returnRewrite(n, ret, Rewrite::STR_LEQ_ID); } if (n[0].isConst() && n[1].isConst()) { String s = n[0].getConst<String>(); String t = n[1].getConst<String>(); Node ret = nm->mkConst(s.isLeq(t)); - return returnRewrite(n, ret, "str-leq-eval"); + return returnRewrite(n, ret, Rewrite::STR_LEQ_EVAL); } // empty strings for (unsigned i = 0; i < 2; i++) @@ -166,7 +175,7 @@ Node StringsRewriter::rewriteStringLeq(Node n) if (n[i].isConst() && n[i].getConst<String>().isEmptyString()) { Node ret = i == 0 ? nm->mkConst(true) : n[0].eqNode(n[1]); - return returnRewrite(n, ret, "str-leq-empty"); + return returnRewrite(n, ret, Rewrite::STR_LEQ_EMPTY); } } @@ -190,7 +199,7 @@ Node StringsRewriter::rewriteStringLeq(Node n) if (!s.isLeq(t)) { Node ret = nm->mkConst(false); - return returnRewrite(n, ret, "str-leq-cprefix"); + return returnRewrite(n, ret, Rewrite::STR_LEQ_CPREFIX); } } return n; @@ -214,7 +223,7 @@ Node StringsRewriter::rewriteStringFromCode(Node n) { ret = nm->mkConst(String("")); } - return returnRewrite(n, ret, "from-code-eval"); + return returnRewrite(n, ret, Rewrite::FROM_CODE_EVAL); } return n; } @@ -231,17 +240,29 @@ Node StringsRewriter::rewriteStringToCode(Node n) { std::vector<unsigned> vec = s.getVec(); Assert(vec.size() == 1); - ret = nm->mkConst(Rational(String::convertUnsignedIntToCode(vec[0]))); + ret = nm->mkConst(Rational(vec[0])); } else { ret = nm->mkConst(Rational(-1)); } - return returnRewrite(n, ret, "to-code-eval"); + return returnRewrite(n, ret, Rewrite::TO_CODE_EVAL); } return n; } +Node StringsRewriter::rewriteStringIsDigit(Node n) +{ + Assert(n.getKind() == kind::STRING_IS_DIGIT); + NodeManager* nm = NodeManager::currentNM(); + // eliminate str.is_digit(s) ----> 48 <= str.to_code(s) <= 57 + Node t = nm->mkNode(STRING_TO_CODE, n[0]); + Node retNode = nm->mkNode(AND, + nm->mkNode(LEQ, nm->mkConst(Rational(48)), t), + nm->mkNode(LEQ, t, nm->mkConst(Rational(57)))); + return returnRewrite(n, retNode, Rewrite::IS_DIGIT_ELIM); +} + } // namespace strings } // namespace theory } // namespace CVC4 diff --git a/src/theory/strings/strings_rewriter.h b/src/theory/strings/strings_rewriter.h index e6a6b0693..0c5b0b2f8 100644 --- a/src/theory/strings/strings_rewriter.h +++ b/src/theory/strings/strings_rewriter.h @@ -56,6 +56,14 @@ class StringsRewriter : public SequencesRewriter */ static Node rewriteStrConvert(Node n); + /** rewrite string less than + * + * This is the entry point for post-rewriting terms n of the form + * str.<( t, s ) + * Returns the rewritten form of n. + */ + static Node rewriteStringLt(Node n); + /** rewrite string less than or equal * * This is the entry point for post-rewriting terms n of the form @@ -79,6 +87,14 @@ class StringsRewriter : public SequencesRewriter * Returns the rewritten form of n. */ static Node rewriteStringToCode(Node n); + + /** rewrite is digit + * + * This is the entry point for post-rewriting terms n of the form + * str.is_digit( t ) + * Returns the rewritten form of n. + */ + static Node rewriteStringIsDigit(Node n); }; } // namespace strings diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index 1006076d5..d5eb2dbbd 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -26,6 +26,7 @@ #include "smt/smt_statistics_registry.h" #include "theory/ext_theory.h" #include "theory/rewriter.h" +#include "theory/strings/sequences_rewriter.h" #include "theory/strings/theory_strings_utils.h" #include "theory/strings/type_enumerator.h" #include "theory/strings/word.h" @@ -69,7 +70,7 @@ TheoryStrings::TheoryStrings(context::Context* c, const LogicInfo& logicInfo) : Theory(THEORY_STRINGS, c, u, out, valuation, logicInfo), d_notify(*this), - d_equalityEngine(d_notify, c, "theory::strings", true), + d_equalityEngine(d_notify, c, "theory::strings::ee", true), d_state(c, d_equalityEngine, d_valuation), d_im(*this, c, u, d_state, d_sk_cache, out, d_statistics), d_pregistered_terms_cache(u), @@ -85,9 +86,17 @@ TheoryStrings::TheoryStrings(context::Context* c, { setupExtTheory(); ExtTheory* extt = getExtTheory(); - d_esolver.reset(new ExtfSolver( - c, u, d_state, d_im, d_sk_cache, d_bsolver, d_csolver, extt)); - d_rsolver.reset(new RegExpSolver(*this, d_state, d_im, *d_esolver, c, u)); + d_esolver.reset(new ExtfSolver(c, + u, + d_state, + d_im, + d_sk_cache, + d_bsolver, + d_csolver, + extt, + d_statistics)); + d_rsolver.reset( + new RegExpSolver(*this, d_state, d_im, *d_esolver, d_statistics, c, u)); // The kinds we are treating as function application in congruence d_equalityEngine.addFunctionKind(kind::STRING_LENGTH); @@ -122,6 +131,11 @@ TheoryStrings::~TheoryStrings() { } +std::unique_ptr<TheoryRewriter> TheoryStrings::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new SequencesRewriter()); +} + bool TheoryStrings::areCareDisequal( TNode x, TNode y ) { Assert(d_equalityEngine.hasTerm(x)); Assert(d_equalityEngine.hasTerm(y)); @@ -375,7 +389,7 @@ bool TheoryStrings::collectModelInfoType( ctv.getConst<Rational>().getNumerator().toUnsignedInt(); Trace("strings-model") << "(code: " << cvalue << ") "; std::vector<unsigned> vec; - vec.push_back(String::convertCodeToUnsignedInt(cvalue)); + vec.push_back(cvalue); Node mv = nm->mkConst(String(vec)); pure_eq_assign[eqc] = mv; m->getEqualityEngine()->addTerm(mv); @@ -464,6 +478,7 @@ bool TheoryStrings::collectModelInfoType( for (const Node& sl : len_splits) { Node spl = nm->mkNode(OR, sl, sl.negate()); + ++(d_statistics.d_lemmasCmiSplit); d_out->lemma(spl); } return false; @@ -700,7 +715,8 @@ void TheoryStrings::check(Effort e) { Trace("strings-eqc") << (t==0 ? "STRINGS:" : "OTHER:") << std::endl; while( !eqcs2_i.isFinished() ){ Node eqc = (*eqcs2_i); - bool print = (t==0 && eqc.getType().isString() ) || (t==1 && !eqc.getType().isString() ); + bool print = (t == 0 && eqc.getType().isStringLike()) + || (t == 1 && !eqc.getType().isStringLike()); if (print) { eq::EqClassIterator eqc2_i = eq::EqClassIterator( eqc, &d_equalityEngine ); Trace("strings-eqc") << "Eqc( " << eqc << " ) : { "; @@ -778,6 +794,7 @@ void TheoryStrings::conflict(TNode a, TNode b){ Node conflictNode; conflictNode = explain( a.eqNode(b) ); Trace("strings-conflict") << "CONFLICT: Eq engine conflict : " << conflictNode << std::endl; + ++(d_statistics.d_conflictsEqEngine); d_out->conflict( conflictNode ); } } @@ -872,7 +889,9 @@ void TheoryStrings::addCarePairs(TNodeTrie* t1, void TheoryStrings::computeCareGraph(){ //computing the care graph here is probably still necessary, due to operators that take non-string arguments TODO: verify Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Build term indices..." << std::endl; - std::map<Node, TNodeTrie> index; + // Term index for each (type, operator) pair. We require the operator here + // since operators are polymorphic, taking strings/sequences. + std::map<std::pair<TypeNode, Node>, TNodeTrie> index; std::map< Node, unsigned > arity; unsigned functionTerms = d_functionsTerms.size(); for (unsigned i = 0; i < functionTerms; ++ i) { @@ -888,16 +907,19 @@ void TheoryStrings::computeCareGraph(){ } } if( has_trigger_arg ){ - index[op].addTerm( f1, reps ); + TypeNode ft = utils::getOwnerStringType(f1); + std::pair<TypeNode, Node> ikey = std::pair<TypeNode, Node>(ft, op); + index[ikey].addTerm(f1, reps); arity[op] = reps.size(); } } //for each index - for (std::pair<const Node, TNodeTrie>& tt : index) + for (std::pair<const std::pair<TypeNode, Node>, TNodeTrie>& ti : index) { Trace("strings-cg") << "TheoryStrings::computeCareGraph(): Process index " - << tt.first << "..." << std::endl; - addCarePairs(&tt.second, nullptr, arity[tt.first], 0); + << ti.first << "..." << std::endl; + Node op = ti.first.second; + addCarePairs(&ti.second, nullptr, arity[op], 0); } } @@ -907,7 +929,9 @@ void TheoryStrings::assertPendingFact(Node atom, bool polarity, Node exp) { if( atom.getKind()==kind::EQUAL ){ Trace("strings-pending-debug") << " Register term" << std::endl; for( unsigned j=0; j<2; j++ ) { - if( !d_equalityEngine.hasTerm( atom[j] ) && atom[j].getType().isString() ) { + if (!d_equalityEngine.hasTerm(atom[j]) + && atom[j].getType().isStringLike()) + { registerTerm( atom[j], 0 ); } } @@ -939,6 +963,7 @@ void TheoryStrings::assertPendingFact(Node atom, bool polarity, Node exp) { d_state.setConflict(); Trace("strings-conflict") << "CONFLICT: Eager prefix : " << conflictNode << std::endl; + ++(d_statistics.d_conflictsEagerPrefix); d_out->conflict(conflictNode); } } @@ -1047,7 +1072,7 @@ void TheoryStrings::registerTerm(Node n, int effort) { TypeNode tn = n.getType(); bool do_register = true; - if (!tn.isString()) + if (!tn.isStringLike()) { if (options::stringEagerLen()) { @@ -1070,37 +1095,41 @@ void TheoryStrings::registerTerm(Node n, int effort) NodeManager* nm = NodeManager::currentNM(); Debug("strings-register") << "TheoryStrings::registerTerm() " << n << ", effort = " << effort << std::endl; - if (tn.isString()) + Node regTermLem; + if (tn.isStringLike()) { // register length information: // for variables, split on empty vs positive length // for concat/const/replace, introduce proxy var and state length relation - d_im.registerLength(n); + regTermLem = d_im.registerTerm(n); } else if (n.getKind() == STRING_TO_CODE) { d_has_str_code = true; - // ite( str.len(s)==1, 0 <= str.code(s) < num_codes, str.code(s)=-1 ) + // ite( str.len(s)==1, 0 <= str.code(s) < |A|, str.code(s)=-1 ) Node code_len = utils::mkNLength(n[0]).eqNode(d_one); Node code_eq_neg1 = n.eqNode(d_neg_one); Node code_range = nm->mkNode( AND, nm->mkNode(GEQ, n, d_zero), - nm->mkNode(LT, n, nm->mkConst(Rational(CVC4::String::num_codes())))); - Node lem = nm->mkNode(ITE, code_len, code_range, code_eq_neg1); - Trace("strings-lemma") << "Strings::Lemma CODE : " << lem << std::endl; - Trace("strings-assert") << "(assert " << lem << ")" << std::endl; - d_out->lemma(lem); + nm->mkNode( + LT, n, nm->mkConst(Rational(utils::getAlphabetCardinality())))); + regTermLem = nm->mkNode(ITE, code_len, code_range, code_eq_neg1); } else if (n.getKind() == STRING_STRIDOF) { Node len = utils::mkNLength(n[0]); - Node lem = nm->mkNode(AND, - nm->mkNode(GEQ, n, nm->mkConst(Rational(-1))), - nm->mkNode(LEQ, n, len)); - Trace("strings-lemma") << "Strings::Lemma IDOF range : " << lem + regTermLem = nm->mkNode(AND, + nm->mkNode(GEQ, n, nm->mkConst(Rational(-1))), + nm->mkNode(LEQ, n, len)); + } + if (!regTermLem.isNull()) + { + Trace("strings-lemma") << "Strings::Lemma REG-TERM : " << regTermLem << std::endl; - d_out->lemma(lem); + Trace("strings-assert") << "(assert " << regTermLem << ")" << std::endl; + ++(d_statistics.d_lemmasRegisterTerm); + d_out->lemma(regTermLem); } } @@ -1145,6 +1174,7 @@ Node TheoryStrings::ppRewrite(TNode atom) { Trace("strings-ppr") << " rewrote " << atom << " -> " << ret << ", with " << new_nodes.size() << " lemmas." << std::endl; for( unsigned i=0; i<new_nodes.size(); i++ ){ Trace("strings-ppr") << " lemma : " << new_nodes[i] << std::endl; + ++(d_statistics.d_lemmasEagerPreproc); d_out->lemma( new_nodes[i] ); } return ret; diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index 76c8f0469..0e95628bc 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -109,6 +109,8 @@ class TheoryStrings : public Theory { const LogicInfo& logicInfo); ~TheoryStrings(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + void setMasterEqualityEngine(eq::EqualityEngine* eq) override; std::string identify() const override { return std::string("TheoryStrings"); } diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index d4183700d..7777b9bd7 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -23,6 +23,7 @@ #include "proof/proof_manager.h" #include "smt/logic_exception.h" #include "theory/strings/sequences_rewriter.h" +#include "theory/strings/word.h" using namespace CVC4; using namespace CVC4::kind; @@ -31,14 +32,15 @@ namespace CVC4 { namespace theory { namespace strings { -StringsPreprocess::StringsPreprocess(SkolemCache *sc, context::UserContext *u) - : d_sc(sc) +StringsPreprocess::StringsPreprocess(SkolemCache* sc, + context::UserContext* u, + SequencesStatistics& stats) + : d_sc(sc), d_statistics(stats) { //Constants d_zero = NodeManager::currentNM()->mkConst(Rational(0)); d_one = NodeManager::currentNM()->mkConst(Rational(1)); d_neg_one = NodeManager::currentNM()->mkConst(Rational(-1)); - d_empty_str = NodeManager::currentNM()->mkConst(String("")); } StringsPreprocess::~StringsPreprocess(){ @@ -68,11 +70,13 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node c3 = nm->mkNode(GT, m, d_zero); Node cond = nm->mkNode(AND, c1, c2, c3); - Node sk1 = n == d_zero ? d_empty_str + Node emp = Word::mkEmptyWord(t.getType()); + + Node sk1 = n == d_zero ? emp : d_sc->mkSkolemCached( s, n, SkolemCache::SK_PREFIX, "sspre"); Node sk2 = SequencesRewriter::checkEntailArith(t12, lt0) - ? d_empty_str + ? emp : d_sc->mkSkolemCached( s, t12, SkolemCache::SK_SUFFIX_REM, "sssufr"); Node b11 = s.eqNode(nm->mkNode(STRING_CONCAT, sk1, skt, sk2)); @@ -89,7 +93,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node b14 = nm->mkNode(LEQ, nm->mkNode(STRING_LENGTH, skt), m); Node b1 = nm->mkNode(AND, b11, b12, b13, b14); - Node b2 = skt.eqNode(d_empty_str); + Node b2 = skt.eqNode(emp); Node lemma = nm->mkNode(ITE, cond, b1, b2); // assert: @@ -149,7 +153,8 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node cc1 = skk.eqNode(negone); // y = "" - Node cond2 = y.eqNode(d_empty_str); + Node emp = Word::mkEmptyWord(x.getType()); + Node cond2 = y.eqNode(emp); // skk = n Node cc2 = skk.eqNode(t[2]); @@ -237,8 +242,8 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node nonneg = nm->mkNode(GEQ, n, d_zero); - lem = nm->mkNode( - ITE, nonneg, nm->mkNode(AND, conc), itost.eqNode(d_empty_str)); + Node emp = Word::mkEmptyWord(t.getType()); + lem = nm->mkNode(ITE, nonneg, nm->mkNode(AND, conc), itost.eqNode(emp)); new_nodes.push_back(lem); // assert: // IF n>=0 @@ -275,7 +280,8 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Node lem = stoit.eqNode(d_neg_one); conc1.push_back(lem); - Node sEmpty = s.eqNode(d_empty_str); + Node emp = Word::mkEmptyWord(s.getType()); + Node sEmpty = s.eqNode(emp); Node k = nm->mkSkolem("k", nm->integerType()); Node kc1 = nm->mkNode(GEQ, k, d_zero); Node kc2 = nm->mkNode(LT, k, lens); @@ -476,8 +482,9 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // the index to begin searching in x for y after the i^th occurrence of y in // x, and Us( i ) is the result of processing the remainder after processing // the i^th occurrence of y in x. - Node assert = nm->mkNode( - ITE, y.eqNode(d_empty_str), rpaw.eqNode(x), nm->mkNode(AND, lem)); + Node emp = Word::mkEmptyWord(t.getType()); + Node assert = + nm->mkNode(ITE, y.eqNode(emp), rpaw.eqNode(x), nm->mkNode(AND, lem)); new_nodes.push_back(assert); // Thus, replaceall( x, y, z ) = rpaw @@ -637,6 +644,7 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { Trace("strings-preprocess") << " " << new_nodes[i] << std::endl; } } + d_statistics.d_reductions << t.getKind(); } else { diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h index b96d619ef..fb6404aa6 100644 --- a/src/theory/strings/theory_strings_preprocess.h +++ b/src/theory/strings/theory_strings_preprocess.h @@ -22,6 +22,7 @@ #include <vector> #include "context/cdhashmap.h" #include "theory/rewriter.h" +#include "theory/strings/sequences_stats.h" #include "theory/strings/skolem_cache.h" #include "theory/theory.h" #include "util/hash.h" @@ -38,48 +39,51 @@ namespace strings { * reductions" inference schema of TheoryStrings. */ class StringsPreprocess { -public: - StringsPreprocess(SkolemCache *sc, context::UserContext *u); - ~StringsPreprocess(); - /** - * Returns a node t' such that - * (exists k) new_nodes => t = t' - * is valid, where k are the free skolems introduced when constructing - * new_nodes. - */ - Node simplify(Node t, std::vector<Node> &new_nodes); - /** - * Applies simplifyRec on t until a fixed point is reached, and returns - * the resulting term t', which is such that - * (exists k) new_nodes => t = t' - * is valid, where k are the free skolems introduced when constructing - * new_nodes. - */ - Node processAssertion(Node t, std::vector<Node> &new_nodes); - /** - * Replaces all formulas t in vec_node with an equivalent formula t' that - * contains no free instances of extended functions (that is, extended - * functions may only appear beneath quantifiers). This applies simplifyRec - * on each assertion in vec_node until a fixed point is reached. - */ - void processAssertions(std::vector<Node> &vec_node); + public: + StringsPreprocess(SkolemCache* sc, + context::UserContext* u, + SequencesStatistics& stats); + ~StringsPreprocess(); + /** + * Returns a node t' such that + * (exists k) new_nodes => t = t' + * is valid, where k are the free skolems introduced when constructing + * new_nodes. + */ + Node simplify(Node t, std::vector<Node>& new_nodes); + /** + * Applies simplifyRec on t until a fixed point is reached, and returns + * the resulting term t', which is such that + * (exists k) new_nodes => t = t' + * is valid, where k are the free skolems introduced when constructing + * new_nodes. + */ + Node processAssertion(Node t, std::vector<Node>& new_nodes); + /** + * Replaces all formulas t in vec_node with an equivalent formula t' that + * contains no free instances of extended functions (that is, extended + * functions may only appear beneath quantifiers). This applies simplifyRec + * on each assertion in vec_node until a fixed point is reached. + */ + void processAssertions(std::vector<Node>& vec_node); -private: - /** commonly used constants */ - Node d_zero; - Node d_one; - Node d_neg_one; - Node d_empty_str; - /** pointer to the skolem cache used by this class */ - SkolemCache *d_sc; - /** - * Applies simplify to all top-level extended function subterms of t. New - * assertions created in this reduction are added to new_nodes. The argument - * visited stores a cache of previous results. - */ - Node simplifyRec(Node t, - std::vector<Node> &new_nodes, - std::map<Node, Node> &visited); + private: + /** commonly used constants */ + Node d_zero; + Node d_one; + Node d_neg_one; + /** pointer to the skolem cache used by this class */ + SkolemCache* d_sc; + /** Reference to the statistics for the theory of strings/sequences. */ + SequencesStatistics& d_statistics; + /** + * Applies simplify to all top-level extended function subterms of t. New + * assertions created in this reduction are added to new_nodes. The argument + * visited stores a cache of previous results. + */ + Node simplifyRec(Node t, + std::vector<Node>& new_nodes, + std::map<Node, Node>& visited); }; }/* CVC4::theory::strings namespace */ diff --git a/src/theory/strings/theory_strings_type_rules.h b/src/theory/strings/theory_strings_type_rules.h index 6abb57504..93a32f26e 100644 --- a/src/theory/strings/theory_strings_type_rules.h +++ b/src/theory/strings/theory_strings_type_rules.h @@ -291,14 +291,15 @@ public: if (!t.isString()) { throw TypeCheckingExceptionPrivate(n, "expecting a string term in regexp range"); } - if( (*it).getKind() != kind::CONST_STRING ) { + if (!(*it).isConst()) + { throw TypeCheckingExceptionPrivate(n, "expecting a constant string term in regexp range"); } if( (*it).getConst<String>().size() != 1 ) { throw TypeCheckingExceptionPrivate(n, "expecting a single constant string term in regexp range"); } unsigned ci = (*it).getConst<String>().front(); - ch[i] = String::convertUnsignedIntToCode(ci); + ch[i] = ci; ++it; } if(ch[0] > ch[1]) { diff --git a/src/theory/strings/theory_strings_utils.cpp b/src/theory/strings/theory_strings_utils.cpp index 5d27b8e2b..3b4c757f2 100644 --- a/src/theory/strings/theory_strings_utils.cpp +++ b/src/theory/strings/theory_strings_utils.cpp @@ -232,11 +232,10 @@ void getRegexpComponents(Node r, std::vector<Node>& result) } else if (r.getKind() == STRING_TO_REGEXP && r[0].isConst()) { - String s = r[0].getConst<String>(); - for (size_t i = 0, size = s.size(); i < size; i++) + size_t rlen = Word::getLength(r[0]); + for (size_t i = 0; i < rlen; i++) { - result.push_back( - nm->mkNode(STRING_TO_REGEXP, nm->mkConst(s.substr(i, 1)))); + result.push_back(nm->mkNode(STRING_TO_REGEXP, Word::substr(r[0], i, 1))); } } else @@ -264,6 +263,54 @@ void printConcatTrace(std::vector<Node>& n, const char* c) Trace(c) << ss.str(); } +bool isStringKind(Kind k) +{ + return k == STRING_STOI || k == STRING_ITOS || k == STRING_TOLOWER + || k == STRING_TOUPPER || k == STRING_LEQ || k == STRING_LT + || k == STRING_FROM_CODE || k == STRING_TO_CODE; +} + +TypeNode getOwnerStringType(Node n) +{ + TypeNode tn; + Kind k = n.getKind(); + if (k == STRING_STRIDOF || k == STRING_LENGTH || k == STRING_STRCTN + || k == STRING_PREFIX || k == STRING_SUFFIX) + { + // owning string type is the type of first argument + tn = n[0].getType(); + } + else if (isStringKind(k)) + { + tn = NodeManager::currentNM()->stringType(); + } + else + { + tn = n.getType(); + } + AlwaysAssert(tn.isStringLike()) + << "Unexpected term in getOwnerStringType : " << n << ", type " << tn; + return tn; +} + +unsigned getRepeatAmount(TNode node) +{ + Assert(node.getKind() == REGEXP_REPEAT); + return node.getOperator().getConst<RegExpRepeat>().d_repeatAmount; +} + +unsigned getLoopMaxOccurrences(TNode node) +{ + Assert(node.getKind() == REGEXP_LOOP); + return node.getOperator().getConst<RegExpLoop>().d_loopMaxOcc; +} + +unsigned getLoopMinOccurrences(TNode node) +{ + Assert(node.getKind() == REGEXP_LOOP); + return node.getOperator().getConst<RegExpLoop>().d_loopMinOcc; +} + } // namespace utils } // namespace strings } // namespace theory diff --git a/src/theory/strings/theory_strings_utils.h b/src/theory/strings/theory_strings_utils.h index 5f18d3936..846d3d563 100644 --- a/src/theory/strings/theory_strings_utils.h +++ b/src/theory/strings/theory_strings_utils.h @@ -140,6 +140,27 @@ void printConcat(std::ostream& out, std::vector<Node>& n); /** Print the vector n as a concatentation term on trace given by c */ void printConcatTrace(std::vector<Node>& n, const char* c); +/** Is k a string-specific kind? */ +bool isStringKind(Kind k); + +/** Get owner string type + * + * This returns a string-like type for a term n that belongs to the theory of + * strings. This type conceptually represents the subtheory of strings + * (Sequence(T) or String) that owns n. This is typically the type of n, + * but for instance, operators like str.indexof( s, t, n ), this is the type + * of s. + */ +TypeNode getOwnerStringType(Node n); + +/* Get the number of repetitions for a regexp repeat node */ +unsigned getRepeatAmount(TNode node); + +/* Get the maximum occurrences of given regexp loop node. */ +unsigned getLoopMaxOccurrences(TNode node); +/* Get the minimum occurrences of given regexp loop node. */ +unsigned getLoopMinOccurrences(TNode node); + } // namespace utils } // namespace strings } // namespace theory diff --git a/src/theory/strings/type_enumerator.cpp b/src/theory/strings/type_enumerator.cpp index 12cf899b4..d24206860 100644 --- a/src/theory/strings/type_enumerator.cpp +++ b/src/theory/strings/type_enumerator.cpp @@ -15,12 +15,50 @@ #include "theory/strings/type_enumerator.h" #include "theory/strings/theory_strings_utils.h" -#include "util/regexp.h" +#include "util/string.h" namespace CVC4 { namespace theory { namespace strings { +Node makeStandardModelConstant(const std::vector<unsigned>& vec, + uint32_t cardinality) +{ + std::vector<unsigned> mvec; + // if we contain all of the printable characters + if (cardinality >= 255) + { + for (unsigned i = 0, vsize = vec.size(); i < vsize; i++) + { + unsigned curr = vec[i]; + // convert + Assert(vec[i] < cardinality); + if (vec[i] <= 61) + { + // first 62 printable characters [\u{65}-\u{126}]: 'A', 'B', 'C', ... + curr = vec[i] + 65; + } + else if (vec[i] <= 94) + { + // remaining 33 printable characters [\u{32}-\u{64}]: ' ', '!', '"', ... + curr = vec[i] - 30; + } + else + { + // the remaining characters, starting with \u{127} and wrapping around + // the first 32 non-printable characters. + curr = (vec[i] + 32) % cardinality; + } + mvec.push_back(curr); + } + } + else + { + mvec = vec; + } + return NodeManager::currentNM()->mkConst(String(mvec)); +} + WordIter::WordIter(uint32_t startLength) : d_hasEndLength(false), d_endLength(0) { for (uint32_t i = 0; i < startLength; i++) @@ -117,7 +155,7 @@ bool StringEnumLen::increment() void StringEnumLen::mkCurr() { - d_curr = NodeManager::currentNM()->mkConst(String(d_witer->getData())); + d_curr = makeStandardModelConstant(d_witer->getData(), d_cardinality); } StringEnumerator::StringEnumerator(TypeNode type, TypeEnumeratorProperties* tep) diff --git a/src/theory/strings/type_enumerator.h b/src/theory/strings/type_enumerator.h index 2061628a5..b379ce5c3 100644 --- a/src/theory/strings/type_enumerator.h +++ b/src/theory/strings/type_enumerator.h @@ -28,6 +28,26 @@ namespace theory { namespace strings { /** + * Make standard model constant + * + * In our string representation, we represent characters using vectors + * of unsigned integers indicating code points for the characters of that + * string. + * + * To make models user-friendly, we make unsigned integer 0 correspond to the + * 65th character ("A") in the ASCII alphabet to make models intuitive. In + * particular, say if we have a set of string variables that are distinct but + * otherwise unconstrained, then the model may assign them "A", "B", "C", ... + * + * @param vec The code points of the string in a given model, + * @param cardinality The cardinality of the alphabet, + * @return A string whose characters have the code points corresponding + * to vec in the standard model construction described above. + */ +Node makeStandardModelConstant(const std::vector<unsigned>& vec, + uint32_t cardinality); + +/** * Generic iteration over vectors of indices of a given start/end length. */ class WordIter diff --git a/src/theory/strings/word.cpp b/src/theory/strings/word.cpp index dd573b68c..b42cf3160 100644 --- a/src/theory/strings/word.cpp +++ b/src/theory/strings/word.cpp @@ -14,7 +14,7 @@ #include "theory/strings/word.h" -#include "util/regexp.h" +#include "util/string.h" using namespace CVC4::kind; @@ -76,6 +76,29 @@ size_t Word::getLength(TNode x) return 0; } +std::vector<Node> Word::getChars(TNode x) +{ + Kind k = x.getKind(); + if (k == CONST_STRING) + { + std::vector<Node> ret; + NodeManager* nm = NodeManager::currentNM(); + std::vector<unsigned> ccVec; + const std::vector<unsigned>& cvec = x.getConst<String>().getVec(); + for (unsigned chVal : cvec) + { + ccVec.clear(); + ccVec.push_back(chVal); + Node ch = nm->mkConst(String(ccVec)); + ret.push_back(ch); + } + return ret; + } + Unimplemented(); + std::vector<Node> ret; + return ret; +} + bool Word::isEmpty(TNode x) { return getLength(x) == 0; } bool Word::strncmp(TNode x, TNode y, std::size_t n) diff --git a/src/theory/strings/word.h b/src/theory/strings/word.h index 7b813a0b2..8e6e7876e 100644 --- a/src/theory/strings/word.h +++ b/src/theory/strings/word.h @@ -42,6 +42,13 @@ class Word /** Return the length of word x */ static size_t getLength(TNode x); + /** Get characters + * + * Given word x, this returns the vector of words of length one whose + * concatenation is equivalent to x. + */ + static std::vector<Node> getChars(TNode x); + /** Return true if x is empty */ static bool isEmpty(TNode x); diff --git a/src/theory/theory.h b/src/theory/theory.h index d6b02e070..63ca46b41 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -42,6 +42,7 @@ #include "theory/logic_info.h" #include "theory/output_channel.h" #include "theory/theory_id.h" +#include "theory/theory_rewriter.h" #include "theory/valuation.h" #include "util/statistics_registry.h" @@ -317,6 +318,11 @@ public: virtual ~Theory(); /** + * Creates a new theory rewriter for the theory. + */ + virtual std::unique_ptr<TheoryRewriter> mkTheoryRewriter() = 0; + + /** * Subclasses of Theory may add additional efforts. DO NOT CHECK * equality with one of these values (e.g. if STANDARD xxx) but * rather use range checks (or use the helper functions below). diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 32a80a418..2c27c6054 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -266,7 +266,6 @@ void TheoryEngine::EngineOutputChannel::conflict(TNode conflictNode, } void TheoryEngine::finishInit() { - //initialize the quantifiers engine, master equality engine, model, model builder if( d_logicInfo.isQuantified() ) { // initialize the quantifiers engine @@ -350,7 +349,6 @@ TheoryEngine::TheoryEngine(context::Context* context, d_factsAsserted(context, false), d_preRegistrationVisitor(this, context), d_sharedTermsVisitor(d_sharedTerms), - d_theoryAlternatives(), d_attr_handle(), d_arithSubstitutionsAdded("theory::arith::zzz::arith::substitutions", 0) { @@ -2374,18 +2372,6 @@ void TheoryEngine::spendResource(ResourceManager::Resource r) d_resourceManager->spendResource(r); } -void TheoryEngine::enableTheoryAlternative(const std::string& name){ - Debug("TheoryEngine::enableTheoryAlternative") - << "TheoryEngine::enableTheoryAlternative(" << name << ")" << std::endl; - - d_theoryAlternatives.insert(name); -} - -bool TheoryEngine::useTheoryAlternative(const std::string& name) { - return d_theoryAlternatives.find(name) != d_theoryAlternatives.end(); -} - - TheoryEngine::Statistics::Statistics(theory::TheoryId theory): conflicts(getStatsPrefix(theory) + "::conflicts", 0), propagations(getStatsPrefix(theory) + "::propagations", 0), diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index c1e1e4cac..dec654e76 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -494,6 +494,8 @@ class TheoryEngine { *d_theoryOut[theoryId], theory::Valuation(this), d_logicInfo); + theory::Rewriter::registerTheoryRewriter( + theoryId, d_theoryTable[theoryId]->mkTheoryRewriter()); } void setPropEngine(prop::PropEngine* propEngine) @@ -895,14 +897,7 @@ public: /** Prints the assertions to the debug stream */ void printAssertions(const char* tag); - /** Theory alternative is in use. */ - bool useTheoryAlternative(const std::string& name); - - /** Enables using a theory alternative by name. */ - void enableTheoryAlternative(const std::string& name); - private: - std::set< std::string > d_theoryAlternatives; std::map< std::string, std::vector< theory::Theory* > > d_attr_handle; diff --git a/src/theory/theory_model.cpp b/src/theory/theory_model.cpp index 7bfb0e8f3..dae7261e5 100644 --- a/src/theory/theory_model.cpp +++ b/src/theory/theory_model.cpp @@ -147,7 +147,7 @@ Node TheoryModel::getValue(TNode n) const Node nn = d_substitutions.apply(n); Debug("model-getvalue-debug") << "[model-getvalue] getValue : substitute " << n << " to " << nn << std::endl; //get value in model - nn = getModelValue(nn, false); + nn = getModelValue(nn); if (nn.isNull()) return nn; if(options::condenseFunctionValues() || nn.getKind() != kind::LAMBDA) { //normalize @@ -193,7 +193,7 @@ Cardinality TheoryModel::getCardinality( Type t ) const{ } } -Node TheoryModel::getModelValue(TNode n, bool hasBoundVars) const +Node TheoryModel::getModelValue(TNode n) const { std::unordered_map<Node, Node, NodeHashFunction>::iterator it = d_modelCache.find(n); if (it != d_modelCache.end()) { @@ -220,7 +220,7 @@ Node TheoryModel::getModelValue(TNode n, bool hasBoundVars) const std::vector<Node> children; if (n.getKind() == APPLY_UF) { - Node op = getModelValue(n.getOperator(), hasBoundVars); + Node op = getModelValue(n.getOperator()); Debug("model-getvalue-debug") << " operator : " << op << std::endl; children.push_back(op); } @@ -231,7 +231,7 @@ Node TheoryModel::getModelValue(TNode n, bool hasBoundVars) const // evaluate the children for (unsigned i = 0, nchild = n.getNumChildren(); i < nchild; ++i) { - ret = getModelValue(n[i], hasBoundVars); + ret = getModelValue(n[i]); Debug("model-getvalue-debug") << " " << n << "[" << i << "] is " << ret << std::endl; children.push_back(ret); diff --git a/src/theory/theory_model.h b/src/theory/theory_model.h index d2ce63ac5..d984fbc6b 100644 --- a/src/theory/theory_model.h +++ b/src/theory/theory_model.h @@ -390,9 +390,8 @@ public: /** Get model value function. * * This function is a helper function for getValue. - * hasBoundVars is whether n may contain bound variables */ - Node getModelValue(TNode n, bool hasBoundVars = false) const; + Node getModelValue(TNode n) const; /** add term internal * * This will do any model-specific processing necessary for n, diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 3b42fa6a1..1ea5449b7 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -66,6 +66,11 @@ TheoryUF::TheoryUF(context::Context* c, TheoryUF::~TheoryUF() { } +std::unique_ptr<TheoryRewriter> TheoryUF::mkTheoryRewriter() +{ + return std::unique_ptr<TheoryRewriter>(new TheoryUfRewriter()); +} + void TheoryUF::setMasterEqualityEngine(eq::EqualityEngine* eq) { d_equalityEngine.setMasterEqualityEngine(eq); } diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index 93a709fe5..623c5c64b 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -188,6 +188,8 @@ private: ~TheoryUF(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override; + void setMasterEqualityEngine(eq::EqualityEngine* eq) override; void finishInit() override; diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 75597edac..eba5fb8c9 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -25,12 +25,12 @@ libcvc4_add_sources( proof.h random.cpp random.h - regexp.cpp - regexp.h resource_manager.cpp resource_manager.h result.cpp result.h + regexp.cpp + regexp.h safe_print.cpp safe_print.h sampler.cpp @@ -43,6 +43,8 @@ libcvc4_add_sources( statistics.h statistics_registry.cpp statistics_registry.h + string.cpp + string.h tuple.h unsafe_interrupt_exception.h utility.cpp diff --git a/src/util/regexp.cpp b/src/util/regexp.cpp index 00066edb6..7051b251f 100644 --- a/src/util/regexp.cpp +++ b/src/util/regexp.cpp @@ -2,514 +2,59 @@ /*! \file regexp.cpp ** \verbatim ** Top contributors (to current version): - ** Tim King, Tianyi Liang, Andrew Reynolds + ** Andrew Reynolds ** This file is part of the CVC4 project. ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file + ** \brief Implementation of data structures for regular expression operators. **/ #include "util/regexp.h" -#include <algorithm> -#include <climits> -#include <iomanip> -#include <iostream> -#include <sstream> - -#include "base/check.h" -#include "base/exception.h" - -using namespace std; +#include <ostream> namespace CVC4 { -static_assert(UCHAR_MAX == 255, "Unsigned char is assumed to have 256 values."); - -unsigned String::convertCharToUnsignedInt(unsigned char c) -{ - return convertCodeToUnsignedInt(static_cast<unsigned>(c)); -} -unsigned char String::convertUnsignedIntToChar(unsigned i) -{ - Assert(i < num_codes()); - return static_cast<unsigned char>(convertUnsignedIntToCode(i)); -} -bool String::isPrintable(unsigned i) +RegExpRepeat::RegExpRepeat(uint32_t repeatAmount) : d_repeatAmount(repeatAmount) { - Assert(i < num_codes()); - unsigned char c = convertUnsignedIntToChar(i); - return (c >= ' ' && c <= '~'); -} -unsigned String::convertCodeToUnsignedInt(unsigned c) -{ - Assert(c < num_codes()); - return (c < start_code() ? c + num_codes() : c) - start_code(); -} -unsigned String::convertUnsignedIntToCode(unsigned i) -{ - Assert(i < num_codes()); - return (i + start_code()) % num_codes(); } -String::String(const std::vector<unsigned> &s) : d_str(s) +bool RegExpRepeat::operator==(const RegExpRepeat& r) const { -#ifdef CVC4_ASSERTIONS - for (unsigned u : d_str) - { - Assert(convertUnsignedIntToCode(u) < num_codes()); - } -#endif -} - -int String::cmp(const String &y) const { - if (size() != y.size()) { - return size() < y.size() ? -1 : 1; - } - for (unsigned int i = 0; i < size(); ++i) { - if (d_str[i] != y.d_str[i]) { - unsigned cp = convertUnsignedIntToCode(d_str[i]); - unsigned cpy = convertUnsignedIntToCode(y.d_str[i]); - return cp < cpy ? -1 : 1; - } - } - return 0; -} - -String String::concat(const String &other) const { - std::vector<unsigned int> ret_vec(d_str); - ret_vec.insert(ret_vec.end(), other.d_str.begin(), other.d_str.end()); - return String(ret_vec); + return d_repeatAmount == r.d_repeatAmount; } -bool String::strncmp(const String& y, std::size_t n) const +RegExpLoop::RegExpLoop(uint32_t l, uint32_t h) + : d_loopMinOcc(l), d_loopMaxOcc(h) { - std::size_t b = (size() >= y.size()) ? size() : y.size(); - std::size_t s = (size() <= y.size()) ? size() : y.size(); - if (n > s) { - if (b == s) { - n = s; - } else { - return false; - } - } - for (std::size_t i = 0; i < n; ++i) { - if (d_str[i] != y.d_str[i]) return false; - } - return true; } -bool String::rstrncmp(const String& y, std::size_t n) const +bool RegExpLoop::operator==(const RegExpLoop& r) const { - std::size_t b = (size() >= y.size()) ? size() : y.size(); - std::size_t s = (size() <= y.size()) ? size() : y.size(); - if (n > s) { - if (b == s) { - n = s; - } else { - return false; - } - } - for (std::size_t i = 0; i < n; ++i) { - if (d_str[size() - i - 1] != y.d_str[y.size() - i - 1]) return false; - } - return true; -} - -std::vector<unsigned> String::toInternal(const std::string &s, - bool useEscSequences) { - std::vector<unsigned> str; - unsigned i = 0; - while (i < s.size()) { - if (s[i] == '\\' && useEscSequences) { - i++; - if (i < s.size()) { - switch (s[i]) { - case 'n': { - str.push_back(convertCharToUnsignedInt('\n')); - i++; - } break; - case 't': { - str.push_back(convertCharToUnsignedInt('\t')); - i++; - } break; - case 'v': { - str.push_back(convertCharToUnsignedInt('\v')); - i++; - } break; - case 'b': { - str.push_back(convertCharToUnsignedInt('\b')); - i++; - } break; - case 'r': { - str.push_back(convertCharToUnsignedInt('\r')); - i++; - } break; - case 'f': { - str.push_back(convertCharToUnsignedInt('\f')); - i++; - } break; - case 'a': { - str.push_back(convertCharToUnsignedInt('\a')); - i++; - } break; - case '\\': { - str.push_back(convertCharToUnsignedInt('\\')); - i++; - } break; - case 'x': { - if (i + 2 < s.size()) { - if (isxdigit(s[i + 1]) && isxdigit(s[i + 2])) { - str.push_back(convertCharToUnsignedInt(hexToDec(s[i + 1]) * 16 + - hexToDec(s[i + 2]))); - i += 3; - } else { - throw CVC4::Exception("Illegal String Literal: \"" + s + "\""); - } - } else { - throw CVC4::Exception("Illegal String Literal: \"" + s + - "\", must have two digits after \\x"); - } - } break; - default: { - if (isdigit(s[i])) { - // octal escape sequences TODO : revisit (issue #1251). - int num = (int)s[i] - (int)'0'; - bool flag = num < 4; - if (i + 1 < s.size() && num < 8 && isdigit(s[i + 1]) && - s[i + 1] < '8') { - num = num * 8 + (int)s[i + 1] - (int)'0'; - if (flag && i + 2 < s.size() && isdigit(s[i + 2]) && - s[i + 2] < '8') { - num = num * 8 + (int)s[i + 2] - (int)'0'; - str.push_back(convertCharToUnsignedInt((unsigned char)num)); - i += 3; - } else { - str.push_back(convertCharToUnsignedInt((unsigned char)num)); - i += 2; - } - } else { - str.push_back(convertCharToUnsignedInt((unsigned char)num)); - i++; - } - } else if ((unsigned)s[i] > 127) { - throw CVC4::Exception("Illegal String Literal: \"" + s + - "\", must use escaped sequence"); - } else { - str.push_back(convertCharToUnsignedInt(s[i])); - i++; - } - } - } - } else { - throw CVC4::Exception("should be handled by lexer: \"" + s + "\""); - // str.push_back( convertCharToUnsignedInt('\\') ); - } - } else if ((unsigned)s[i] > 127 && useEscSequences) { - throw CVC4::Exception("Illegal String Literal: \"" + s + - "\", must use escaped sequence"); - } else { - str.push_back(convertCharToUnsignedInt(s[i])); - i++; - } - } -#ifdef CVC4_ASSERTIONS - for (unsigned u : str) - { - Assert(convertUnsignedIntToCode(u) < num_codes()); - } -#endif - return str; + return d_loopMinOcc == r.d_loopMinOcc && d_loopMaxOcc == r.d_loopMaxOcc; } -unsigned String::front() const +size_t RegExpRepeatHashFunction::operator()(const RegExpRepeat& r) const { - Assert(!d_str.empty()); - return d_str.front(); + return r.d_repeatAmount; } -unsigned String::back() const +size_t RegExpLoopHashFunction::operator()(const RegExpLoop& r) const { - Assert(!d_str.empty()); - return d_str.back(); -} - -std::size_t String::overlap(const String &y) const { - std::size_t i = size() < y.size() ? size() : y.size(); - for (; i > 0; i--) { - String s = suffix(i); - String p = y.prefix(i); - if (s == p) { - return i; - } - } - return i; -} - -std::size_t String::roverlap(const String &y) const { - std::size_t i = size() < y.size() ? size() : y.size(); - for (; i > 0; i--) { - String s = prefix(i); - String p = y.suffix(i); - if (s == p) { - return i; - } - } - return i; + return r.d_loopMinOcc + r.d_loopMaxOcc; } -std::string String::toString(bool useEscSequences) const { - std::string str; - for (unsigned int i = 0; i < size(); ++i) { - unsigned char c = convertUnsignedIntToChar(d_str[i]); - if (!useEscSequences) { - str += c; - } else if (isprint(c)) { - if (c == '\\') { - str += "\\\\"; - } - // else if(c == '\"') { - // str += "\\\""; - //} - else { - str += c; - } - } else { - std::string s; - switch (c) { - case '\a': - s = "\\a"; - break; - case '\b': - s = "\\b"; - break; - case '\t': - s = "\\t"; - break; - case '\r': - s = "\\r"; - break; - case '\v': - s = "\\v"; - break; - case '\f': - s = "\\f"; - break; - case '\n': - s = "\\n"; - break; - case '\e': - s = "\\e"; - break; - default: { - std::stringstream ss; - ss << std::setfill('0') << std::setw(2) << std::hex << ((int)c); - std::string t = ss.str(); - t = t.substr(t.size() - 2, 2); - s = "\\x" + t; - // std::string s2 = static_cast<std::ostringstream*>( - // &(std::ostringstream() << (int)c) )->str(); - } - } - str += s; - } - } - return str; -} - -bool String::isLeq(const String &y) const +std::ostream& operator<<(std::ostream& os, const RegExpRepeat& r) { - for (unsigned i = 0; i < size(); ++i) - { - if (i >= y.size()) - { - return false; - } - unsigned ci = convertUnsignedIntToCode(d_str[i]); - unsigned cyi = convertUnsignedIntToCode(y.d_str[i]); - if (ci > cyi) - { - return false; - } - if (ci < cyi) - { - return true; - } - } - return true; -} - -bool String::isRepeated() const { - if (size() > 1) { - unsigned int f = d_str[0]; - for (unsigned i = 1; i < size(); ++i) { - if (f != d_str[i]) return false; - } - } - return true; + return os << r.d_repeatAmount; } -bool String::tailcmp(const String &y, int &c) const { - int id_x = size() - 1; - int id_y = y.size() - 1; - while (id_x >= 0 && id_y >= 0) { - if (d_str[id_x] != y.d_str[id_y]) { - c = id_x; - return false; - } - --id_x; - --id_y; - } - c = id_x == -1 ? (-(id_y + 1)) : (id_x + 1); - return true; -} - -std::size_t String::find(const String &y, const std::size_t start) const { - if (size() < y.size() + start) return std::string::npos; - if (y.empty()) return start; - if (empty()) return std::string::npos; - - std::vector<unsigned>::const_iterator itr = std::search( - d_str.begin() + start, d_str.end(), y.d_str.begin(), y.d_str.end()); - if (itr != d_str.end()) { - return itr - d_str.begin(); - } - return std::string::npos; -} - -std::size_t String::rfind(const String &y, const std::size_t start) const { - if (size() < y.size() + start) return std::string::npos; - if (y.empty()) return start; - if (empty()) return std::string::npos; - - std::vector<unsigned>::const_reverse_iterator itr = std::search( - d_str.rbegin() + start, d_str.rend(), y.d_str.rbegin(), y.d_str.rend()); - if (itr != d_str.rend()) { - return itr - d_str.rbegin(); - } - return std::string::npos; -} - -bool String::hasPrefix(const String& y) const +std::ostream& operator<<(std::ostream& os, const RegExpLoop& r) { - size_t s = size(); - size_t ys = y.size(); - if (ys > s) - { - return false; - } - for (size_t i = 0; i < ys; i++) - { - if (d_str[i] != y.d_str[i]) - { - return false; - } - } - return true; -} - -bool String::hasSuffix(const String& y) const -{ - size_t s = size(); - size_t ys = y.size(); - if (ys > s) - { - return false; - } - size_t idiff = s - ys; - for (size_t i = 0; i < ys; i++) - { - if (d_str[i + idiff] != y.d_str[i]) - { - return false; - } - } - return true; -} - -String String::replace(const String &s, const String &t) const { - std::size_t ret = find(s); - if (ret != std::string::npos) { - std::vector<unsigned int> vec; - vec.insert(vec.begin(), d_str.begin(), d_str.begin() + ret); - vec.insert(vec.end(), t.d_str.begin(), t.d_str.end()); - vec.insert(vec.end(), d_str.begin() + ret + s.size(), d_str.end()); - return String(vec); - } else { - return *this; - } -} - -String String::substr(std::size_t i) const { - Assert(i <= size()); - std::vector<unsigned int> ret_vec; - std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; - ret_vec.insert(ret_vec.end(), itr, d_str.end()); - return String(ret_vec); -} - -String String::substr(std::size_t i, std::size_t j) const { - Assert(i + j <= size()); - std::vector<unsigned int> ret_vec; - std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; - ret_vec.insert(ret_vec.end(), itr, itr + j); - return String(ret_vec); -} - -bool String::noOverlapWith(const String& y) const -{ - return y.find(*this) == std::string::npos - && this->find(y) == std::string::npos && this->overlap(y) == 0 - && y.overlap(*this) == 0; -} - -bool String::isNumber() const { - if (d_str.empty()) { - return false; - } - for (unsigned character : d_str) { - if (!isDigit(character)) - { - return false; - } - } - return true; -} - -bool String::isDigit(unsigned character) -{ - unsigned char c = convertUnsignedIntToChar(character); - return c >= '0' && c <= '9'; -} - -size_t String::maxSize() { return std::numeric_limits<uint32_t>::max(); } - -Rational String::toNumber() const -{ - // when smt2 standard for strings is set, this may change, based on the - // semantics of str.from.int for leading zeros - return Rational(toString()); -} - -unsigned char String::hexToDec(unsigned char c) { - if (c >= '0' && c <= '9') { - return c - '0'; - } else if (c >= 'a' && c <= 'f') { - return c - 'a' + 10; - } else { - Assert(c >= 'A' && c <= 'F'); - return c - 'A' + 10; - } -} - -std::ostream &operator<<(std::ostream &os, const String &s) { - return os << "\"" << s.toString(true) << "\""; + return os << "[" << r.d_loopMinOcc << ".." << r.d_loopMaxOcc << "]"; } } // namespace CVC4 diff --git a/src/util/regexp.h b/src/util/regexp.h index 731736f72..aaba9e4db 100644 --- a/src/util/regexp.h +++ b/src/util/regexp.h @@ -2,269 +2,74 @@ /*! \file regexp.h ** \verbatim ** Top contributors (to current version): - ** Andrew Reynolds, Tim King, Tianyi Liang + ** Andrew Reynolds ** This file is part of the CVC4 project. ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file + ** \brief Data structures for regular expression operators. **/ #include "cvc4_public.h" -#ifndef CVC4__REGEXP_H -#define CVC4__REGEXP_H +#ifndef CVC4__UTIL__REGEXP_H +#define CVC4__UTIL__REGEXP_H -#include <cstddef> -#include <functional> -#include <ostream> -#include <string> -#include <vector> -#include "util/rational.h" +#include <cstdint> +#include <iosfwd> namespace CVC4 { -/** The CVC4 string class - * - * This data structure is the domain of values for the string type. It can also - * be used as a generic utility for representing strings. - */ -class CVC4_PUBLIC String { - public: - /** - * The start ASCII code. In our string representation below, we represent - * characters using a vector d_str of unsigned integers. We refer to this as - * the "internal representation" for the string. - * - * We make unsigned integer 0 correspond to the 65th character ("A") in the - * ASCII alphabet to make models intuitive. In particular, say if we have - * a set of string variables that are distinct but otherwise unconstrained, - * then the model may assign them "A", "B", "C", ... - */ - static inline unsigned start_code() { return 65; } - /** - * This is the cardinality of the alphabet that is representable by this - * class. Notice that this must be greater than or equal to the cardinality - * of the alphabet that the string theory reasons about. - * - * This must be strictly less than std::numeric_limits<unsigned>::max(). - */ - static inline unsigned num_codes() { return 256; } - /** - * Convert unsigned char to the unsigned used in the internal representation - * in d_str below. - */ - static unsigned convertCharToUnsignedInt(unsigned char c); - /** Convert the internal unsigned to a unsigned char. */ - static unsigned char convertUnsignedIntToChar(unsigned i); - /** Does the internal unsigned correspond to a printable character? */ - static bool isPrintable(unsigned i); - /** get the internal unsigned for ASCII code c. */ - static unsigned convertCodeToUnsignedInt(unsigned c); - /** get the ASCII code number that internal unsigned i corresponds to. */ - static unsigned convertUnsignedIntToCode(unsigned i); - - /** constructors for String - * - * Internally, a CVC4::String is represented by a vector of unsigned - * integers (d_str), where the correspondence between C++ characters - * to and from unsigned integers is determined by - * by convertCharToUnsignedInt and convertUnsignedIntToChar. - * - * If useEscSequences is true, then the escape sequences in the input - * are converted to the corresponding character. This constructor may - * throw an exception if the input contains unrecognized escape sequences. - * Currently supported escape sequences are \n, \t, \v, \b, \r, \f, \a, \\, - * \x[N] where N is a hexidecimal, and octal escape sequences of the - * form \[c1]([c2]([c3])?)? where c1, c2, c3 are digits from 0 to 7. - * - * If useEscSequences is false, then the characters of the constructed - * CVC4::String correspond one-to-one with the input string. - */ - String() = default; - explicit String(const std::string& s, bool useEscSequences = false) - : d_str(toInternal(s, useEscSequences)) {} - explicit String(const char* s, bool useEscSequences = false) - : d_str(toInternal(std::string(s), useEscSequences)) {} - explicit String(const std::vector<unsigned>& s); - - String& operator=(const String& y) { - if (this != &y) { - d_str = y.d_str; - } - return *this; - } - - String concat(const String& other) const; - - bool operator==(const String& y) const { return cmp(y) == 0; } - bool operator!=(const String& y) const { return cmp(y) != 0; } - bool operator<(const String& y) const { return cmp(y) < 0; } - bool operator>(const String& y) const { return cmp(y) > 0; } - bool operator<=(const String& y) const { return cmp(y) <= 0; } - bool operator>=(const String& y) const { return cmp(y) >= 0; } - - /** - * Returns true if this string is equal to y for their first n characters. - * If n is larger than the length of this string or y, this method returns - * true if and only if this string is equal to y. - */ - bool strncmp(const String& y, std::size_t n) const; - /** - * Returns true if this string is equal to y for their last n characters. - * Similar to strncmp, if n is larger than the length of this string or y, - * this method returns true if and only if this string is equal to y. - */ - bool rstrncmp(const String& y, std::size_t n) const; +struct CVC4_PUBLIC RegExpRepeat +{ + RegExpRepeat(uint32_t repeatAmount); - /* toString - * Converts this string to a std::string. - * - * If useEscSequences is true, then unprintable characters - * are converted to escape sequences. The escape sequences - * \n, \t, \v, \b, \r, \f, \a, \\ are printed in this way. - * For all other unprintable characters, we print \x[N] where - * [N] is the 2 digit hexidecimal corresponding to value of - * the character. - * - * If useEscSequences is false, the returned std::string's characters - * map one-to-one with the characters in this string. - * Notice that for all std::string s, we have that - * CVC4::String( s ).toString() = s. - */ - std::string toString(bool useEscSequences = false) const; - /** is this the empty string? */ - bool empty() const { return d_str.empty(); } - /** is this the empty string? */ - bool isEmptyString() const { return empty(); } - /** is less than or equal to string y */ - bool isLeq(const String& y) const; - /** Return the length of the string */ - std::size_t size() const { return d_str.size(); } + bool operator==(const RegExpRepeat& r) const; + /** The amount of repetitions of the regular expression */ + uint32_t d_repeatAmount; +}; - bool isRepeated() const; - bool tailcmp(const String& y, int& c) const; +struct CVC4_PUBLIC RegExpLoop +{ + RegExpLoop(uint32_t l, uint32_t h); - /** - * Return the first position y occurs in this string, or std::string::npos - * otherwise. - */ - std::size_t find(const String& y, const std::size_t start = 0) const; - /** - * Return the first position y occurs in this string searching from the end, - * or std::string::npos otherwise. - */ - std::size_t rfind(const String& y, const std::size_t start = 0) const; - /** Returns true if y is a prefix of this */ - bool hasPrefix(const String& y) const; - /** Returns true if y is a suffix of this */ - bool hasSuffix(const String& y) const; - /** Replace the first occurrence of s in this string with t */ - String replace(const String& s, const String& t) const; - /** Return the substring of this string starting at index i */ - String substr(std::size_t i) const; - /** Return the substring of this string starting at index i with size at most - * j */ - String substr(std::size_t i, std::size_t j) const; - /** Return the prefix of this string of size at most i */ - String prefix(std::size_t i) const { return substr(0, i); } - /** Return the suffix of this string of size at most i */ - String suffix(std::size_t i) const { return substr(size() - i, i); } + bool operator==(const RegExpLoop& r) const; + /** The minimum number of repetitions of the regular expression */ + uint32_t d_loopMinOcc; + /** The maximum number of repetitions of the regular expression */ + uint32_t d_loopMaxOcc; +}; - /** - * Checks if there is any overlap between this string and another string. This - * corresponds to checking whether one string contains the other and whether a - * substring of one is a prefix of the other and vice-versa. - * - * @param y The other string - * @return True if there is an overlap, false otherwise - */ - bool noOverlapWith(const String& y) const; +/* ----------------------------------------------------------------------- + ** Hash Function structs + * ----------------------------------------------------------------------- */ - /** string overlap - * - * if overlap returns m>0, - * then the maximal suffix of this string that is a prefix of y is of length m. - * - * For example, if x is "abcdef", then: - * x.overlap("defg") = 3 - * x.overlap("ab") = 0 - * x.overlap("d") = 0 - * x.overlap("bcdefdef") = 5 - */ - std::size_t overlap(const String& y) const; - /** string reverse overlap - * - * if roverlap returns m>0, - * then the maximal prefix of this string that is a suffix of y is of length m. - * - * For example, if x is "abcdef", then: - * x.roverlap("aaabc") = 3 - * x.roverlap("def") = 0 - * x.roverlap("d") = 0 - * x.roverlap("defabcde") = 5 - * - * Notice that x.overlap(y) = y.roverlap(x) - */ - std::size_t roverlap(const String& y) const; - - /** - * Returns true if this string corresponds in text to a number, for example - * this returns true for strings "7", "12", "004", "0" and false for strings - * "abc", "4a", "-4", "". - */ - bool isNumber() const; - /** Returns the corresponding rational for the text of this string. */ - Rational toNumber() const; - /** get the internal unsigned representation of this string */ - const std::vector<unsigned>& getVec() const { return d_str; } - /** get the internal unsigned value of the first character in this string */ - unsigned front() const; - /** get the internal unsigned value of the last character in this string */ - unsigned back() const; - /** is the unsigned a digit? - * The input should be the same type as the element type of d_str - */ - static bool isDigit(unsigned character); - - /** - * Returns the maximum length of string representable by this class. - * Corresponds to the maximum size of d_str. - */ - static size_t maxSize(); - private: - // guarded - static unsigned char hexToDec(unsigned char c); - - static std::vector<unsigned> toInternal(const std::string& s, - bool useEscSequences = true); - - /** - * Returns a negative number if *this < y, 0 if *this and y are equal and a - * positive number if *this > y. - */ - int cmp(const String& y) const; - - std::vector<unsigned> d_str; -}; /* class String */ +/* + * Hash function for the RegExpRepeat constants. + */ +struct CVC4_PUBLIC RegExpRepeatHashFunction +{ + size_t operator()(const RegExpRepeat& r) const; +}; -namespace strings { +/** + * Hash function for the RegExpLoop objects. + */ +struct CVC4_PUBLIC RegExpLoopHashFunction +{ + size_t operator()(const RegExpLoop& r) const; +}; -struct CVC4_PUBLIC StringHashFunction { - size_t operator()(const ::CVC4::String& s) const { - return std::hash<std::string>()(s.toString()); - } -}; /* struct StringHashFunction */ +/* ----------------------------------------------------------------------- + ** Output stream + * ----------------------------------------------------------------------- */ -} // namespace strings +std::ostream& operator<<(std::ostream& os, const RegExpRepeat& bv) CVC4_PUBLIC; -std::ostream& operator<<(std::ostream& os, const String& s) CVC4_PUBLIC; +std::ostream& operator<<(std::ostream& os, const RegExpLoop& bv) CVC4_PUBLIC; } // namespace CVC4 -#endif /* CVC4__REGEXP_H */ +#endif /* CVC4__UTIL__REGEXP_H */ diff --git a/src/util/regexp.i b/src/util/regexp.i index afc51abd7..775e778f7 100644 --- a/src/util/regexp.i +++ b/src/util/regexp.i @@ -2,24 +2,11 @@ #include "util/regexp.h" %} -%rename(CVC4String) String; -%rename(CVC4StringHashFunction) CVC4::strings::StringHashFunction; +%rename(equals) CVC4::RegExpRepeat::operator==(const RegExpRepeat&) const; -%ignore CVC4::String::String(const std::string&); +%rename(equals) CVC4::RegExpLoop::operator==(const RegExpLoop&) const; -%rename(assign) CVC4::String::operator=(const String&); -%rename(getChar) CVC4::String::operator[](const unsigned int) const; -%rename(equals) CVC4::String::operator==(const String&) const; -%ignore CVC4::String::operator!=(const String&) const; -%rename(less) CVC4::String::operator<(const String&) const; -%rename(lessEqual) CVC4::String::operator<=(const String&) const; -%rename(greater) CVC4::String::operator>(const String&) const; -%rename(greaterEqual) CVC4::String::operator>=(const String&) const; +%ignore CVC4::operator<<(std::ostream&, const RegExpRepeat&); +%ignore CVC4::operator<<(std::ostream&, const RegExpLoop&); -%rename(apply) CVC4::strings::StringHashFunction::operator()(const ::CVC4::String&) const; - -%ignore CVC4::operator<<(std::ostream&, const String&); - -%apply int &OUTPUT { int &c }; %include "util/regexp.h" -%clear int &c; diff --git a/src/util/result.cpp b/src/util/result.cpp index 433dcbf29..916e16b4f 100644 --- a/src/util/result.cpp +++ b/src/util/result.cpp @@ -29,59 +29,68 @@ namespace CVC4 { Result::Result() : d_sat(SAT_UNKNOWN), - d_validity(VALIDITY_UNKNOWN), + d_entailment(ENTAILMENT_UNKNOWN), d_which(TYPE_NONE), d_unknownExplanation(UNKNOWN_REASON), - d_inputName("") {} + d_inputName("") +{ +} Result::Result(enum Sat s, std::string inputName) : d_sat(s), - d_validity(VALIDITY_UNKNOWN), + d_entailment(ENTAILMENT_UNKNOWN), d_which(TYPE_SAT), d_unknownExplanation(UNKNOWN_REASON), - d_inputName(inputName) { + d_inputName(inputName) +{ PrettyCheckArgument(s != SAT_UNKNOWN, "Must provide a reason for satisfiability being unknown"); } -Result::Result(enum Validity v, std::string inputName) +Result::Result(enum Entailment e, std::string inputName) : d_sat(SAT_UNKNOWN), - d_validity(v), - d_which(TYPE_VALIDITY), + d_entailment(e), + d_which(TYPE_ENTAILMENT), d_unknownExplanation(UNKNOWN_REASON), - d_inputName(inputName) { - PrettyCheckArgument(v != VALIDITY_UNKNOWN, - "Must provide a reason for validity being unknown"); + d_inputName(inputName) +{ + PrettyCheckArgument(e != ENTAILMENT_UNKNOWN, + "Must provide a reason for entailment being unknown"); } -Result::Result(enum Sat s, enum UnknownExplanation unknownExplanation, +Result::Result(enum Sat s, + enum UnknownExplanation unknownExplanation, std::string inputName) : d_sat(s), - d_validity(VALIDITY_UNKNOWN), + d_entailment(ENTAILMENT_UNKNOWN), d_which(TYPE_SAT), d_unknownExplanation(unknownExplanation), - d_inputName(inputName) { + d_inputName(inputName) +{ PrettyCheckArgument(s == SAT_UNKNOWN, "improper use of unknown-result constructor"); } -Result::Result(enum Validity v, enum UnknownExplanation unknownExplanation, +Result::Result(enum Entailment e, + enum UnknownExplanation unknownExplanation, std::string inputName) : d_sat(SAT_UNKNOWN), - d_validity(v), - d_which(TYPE_VALIDITY), + d_entailment(e), + d_which(TYPE_ENTAILMENT), d_unknownExplanation(unknownExplanation), - d_inputName(inputName) { - PrettyCheckArgument(v == VALIDITY_UNKNOWN, + d_inputName(inputName) +{ + PrettyCheckArgument(e == ENTAILMENT_UNKNOWN, "improper use of unknown-result constructor"); } Result::Result(const std::string& instr, std::string inputName) : d_sat(SAT_UNKNOWN), - d_validity(VALIDITY_UNKNOWN), + d_entailment(ENTAILMENT_UNKNOWN), d_which(TYPE_NONE), d_unknownExplanation(UNKNOWN_REASON), - d_inputName(inputName) { + d_inputName(inputName) +{ string s = instr; transform(s.begin(), s.end(), s.begin(), ::tolower); if (s == "sat" || s == "satisfiable") { @@ -90,38 +99,56 @@ Result::Result(const std::string& instr, std::string inputName) } else if (s == "unsat" || s == "unsatisfiable") { d_which = TYPE_SAT; d_sat = UNSAT; - } else if (s == "valid") { - d_which = TYPE_VALIDITY; - d_validity = VALID; - } else if (s == "invalid") { - d_which = TYPE_VALIDITY; - d_validity = INVALID; - } else if (s == "incomplete") { + } + else if (s == "entailed") + { + d_which = TYPE_ENTAILMENT; + d_entailment = ENTAILED; + } + else if (s == "not_entailed") + { + d_which = TYPE_ENTAILMENT; + d_entailment = NOT_ENTAILED; + } + else if (s == "incomplete") + { d_which = TYPE_SAT; d_sat = SAT_UNKNOWN; d_unknownExplanation = INCOMPLETE; - } else if (s == "timeout") { + } + else if (s == "timeout") + { d_which = TYPE_SAT; d_sat = SAT_UNKNOWN; d_unknownExplanation = TIMEOUT; - } else if (s == "resourceout") { + } + else if (s == "resourceout") + { d_which = TYPE_SAT; d_sat = SAT_UNKNOWN; d_unknownExplanation = RESOURCEOUT; - } else if (s == "memout") { + } + else if (s == "memout") + { d_which = TYPE_SAT; d_sat = SAT_UNKNOWN; d_unknownExplanation = MEMOUT; - } else if (s == "interrupted") { + } + else if (s == "interrupted") + { d_which = TYPE_SAT; d_sat = SAT_UNKNOWN; d_unknownExplanation = INTERRUPTED; - } else if (s.size() >= 7 && s.compare(0, 7, "unknown") == 0) { + } + else if (s.size() >= 7 && s.compare(0, 7, "unknown") == 0) + { d_which = TYPE_SAT; d_sat = SAT_UNKNOWN; - } else { + } + else + { IllegalArgument(s, - "expected satisfiability/validity result, " + "expected satisfiability/entailment result, " "instead got `%s'", s.c_str()); } @@ -142,37 +169,41 @@ bool Result::operator==(const Result& r) const { return d_sat == r.d_sat && (d_sat != SAT_UNKNOWN || d_unknownExplanation == r.d_unknownExplanation); } - if (d_which == TYPE_VALIDITY) { - return d_validity == r.d_validity && - (d_validity != VALIDITY_UNKNOWN || - d_unknownExplanation == r.d_unknownExplanation); + if (d_which == TYPE_ENTAILMENT) + { + return d_entailment == r.d_entailment + && (d_entailment != ENTAILMENT_UNKNOWN + || d_unknownExplanation == r.d_unknownExplanation); } return false; } bool operator==(enum Result::Sat sr, const Result& r) { return r == sr; } -bool operator==(enum Result::Validity vr, const Result& r) { return r == vr; } +bool operator==(enum Result::Entailment e, const Result& r) { return r == e; } bool operator!=(enum Result::Sat s, const Result& r) { return !(s == r); } -bool operator!=(enum Result::Validity v, const Result& r) { return !(v == r); } +bool operator!=(enum Result::Entailment e, const Result& r) +{ + return !(e == r); +} Result Result::asSatisfiabilityResult() const { if (d_which == TYPE_SAT) { return *this; } - if (d_which == TYPE_VALIDITY) { - switch (d_validity) { - case INVALID: - return Result(SAT, d_inputName); + if (d_which == TYPE_ENTAILMENT) + { + switch (d_entailment) + { + case NOT_ENTAILED: return Result(SAT, d_inputName); - case VALID: - return Result(UNSAT, d_inputName); + case ENTAILED: return Result(UNSAT, d_inputName); - case VALIDITY_UNKNOWN: + case ENTAILMENT_UNKNOWN: return Result(SAT_UNKNOWN, d_unknownExplanation, d_inputName); - default: Unhandled() << d_validity; + default: Unhandled() << d_entailment; } } @@ -180,28 +211,28 @@ Result Result::asSatisfiabilityResult() const { return Result(SAT_UNKNOWN, NO_STATUS, d_inputName); } -Result Result::asValidityResult() const { - if (d_which == TYPE_VALIDITY) { +Result Result::asEntailmentResult() const +{ + if (d_which == TYPE_ENTAILMENT) + { return *this; } if (d_which == TYPE_SAT) { switch (d_sat) { - case SAT: - return Result(INVALID, d_inputName); + case SAT: return Result(NOT_ENTAILED, d_inputName); - case UNSAT: - return Result(VALID, d_inputName); + case UNSAT: return Result(ENTAILED, d_inputName); case SAT_UNKNOWN: - return Result(VALIDITY_UNKNOWN, d_unknownExplanation, d_inputName); + return Result(ENTAILMENT_UNKNOWN, d_unknownExplanation, d_inputName); default: Unhandled() << d_sat; } } // TYPE_NONE - return Result(VALIDITY_UNKNOWN, NO_STATUS, d_inputName); + return Result(ENTAILMENT_UNKNOWN, NO_STATUS, d_inputName); } string Result::toString() const { @@ -226,18 +257,14 @@ ostream& operator<<(ostream& out, enum Result::Sat s) { return out; } -ostream& operator<<(ostream& out, enum Result::Validity v) { - switch (v) { - case Result::INVALID: - out << "INVALID"; - break; - case Result::VALID: - out << "VALID"; - break; - case Result::VALIDITY_UNKNOWN: - out << "VALIDITY_UNKNOWN"; - break; - default: Unhandled() << v; +ostream& operator<<(ostream& out, enum Result::Entailment e) +{ + switch (e) + { + case Result::NOT_ENTAILED: out << "NOT_ENTAILED"; break; + case Result::ENTAILED: out << "ENTAILED"; break; + case Result::ENTAILMENT_UNKNOWN: out << "ENTAILMENT_UNKNOWN"; break; + default: Unhandled() << e; } return out; } @@ -301,14 +328,11 @@ void Result::toStreamDefault(std::ostream& out) const { break; } } else { - switch (isValid()) { - case Result::INVALID: - out << "invalid"; - break; - case Result::VALID: - out << "valid"; - break; - case Result::VALIDITY_UNKNOWN: + switch (isEntailed()) + { + case Result::NOT_ENTAILED: out << "not_entailed"; break; + case Result::ENTAILED: out << "entailed"; break; + case Result::ENTAILMENT_UNKNOWN: out << "unknown"; if (whyUnknown() != Result::UNKNOWN_REASON) { out << " (" << whyUnknown() << ")"; @@ -332,11 +356,17 @@ void Result::toStreamTptp(std::ostream& out) const { out << "Satisfiable"; } else if (isSat() == Result::UNSAT) { out << "Unsatisfiable"; - } else if (isValid() == Result::VALID) { + } + else if (isEntailed() == Result::ENTAILED) + { out << "Theorem"; - } else if (isValid() == Result::INVALID) { + } + else if (isEntailed() == Result::NOT_ENTAILED) + { out << "CounterSatisfiable"; - } else { + } + else + { out << "GaveUp"; } out << " for " << getInputName(); diff --git a/src/util/result.h b/src/util/result.h index f34a9bb5a..10df05388 100644 --- a/src/util/result.h +++ b/src/util/result.h @@ -38,9 +38,19 @@ class CVC4_PUBLIC Result { public: enum Sat { UNSAT = 0, SAT = 1, SAT_UNKNOWN = 2 }; - enum Validity { INVALID = 0, VALID = 1, VALIDITY_UNKNOWN = 2 }; + enum Entailment + { + NOT_ENTAILED = 0, + ENTAILED = 1, + ENTAILMENT_UNKNOWN = 2 + }; - enum Type { TYPE_SAT, TYPE_VALIDITY, TYPE_NONE }; + enum Type + { + TYPE_SAT, + TYPE_ENTAILMENT, + TYPE_NONE + }; enum UnknownExplanation { REQUIRES_FULL_CHECK, @@ -57,7 +67,7 @@ class CVC4_PUBLIC Result { private: enum Sat d_sat; - enum Validity d_validity; + enum Entailment d_entailment; enum Type d_which; enum UnknownExplanation d_unknownExplanation; std::string d_inputName; @@ -67,12 +77,13 @@ class CVC4_PUBLIC Result { Result(enum Sat s, std::string inputName = ""); - Result(enum Validity v, std::string inputName = ""); + Result(enum Entailment v, std::string inputName = ""); Result(enum Sat s, enum UnknownExplanation unknownExplanation, std::string inputName = ""); - Result(enum Validity v, enum UnknownExplanation unknownExplanation, + Result(enum Entailment v, + enum UnknownExplanation unknownExplanation, std::string inputName = ""); Result(const std::string& s, std::string inputName = ""); @@ -84,12 +95,13 @@ class CVC4_PUBLIC Result { enum Sat isSat() const { return d_which == TYPE_SAT ? d_sat : SAT_UNKNOWN; } - enum Validity isValid() const { - return d_which == TYPE_VALIDITY ? d_validity : VALIDITY_UNKNOWN; + enum Entailment isEntailed() const + { + return d_which == TYPE_ENTAILMENT ? d_entailment : ENTAILMENT_UNKNOWN; } bool isUnknown() const { - return isSat() == SAT_UNKNOWN && isValid() == VALIDITY_UNKNOWN; + return isSat() == SAT_UNKNOWN && isEntailed() == ENTAILMENT_UNKNOWN; } Type getType() const { return d_which; } @@ -101,7 +113,7 @@ class CVC4_PUBLIC Result { bool operator==(const Result& r) const; inline bool operator!=(const Result& r) const; Result asSatisfiabilityResult() const; - Result asValidityResult() const; + Result asEntailmentResult() const; std::string toString() const; @@ -128,7 +140,7 @@ class CVC4_PUBLIC Result { * Write a Result out to a stream. * * The default implementation writes a reasonable string in lowercase - * for sat, unsat, valid, invalid, or unknown results. This behavior + * for sat, unsat, entailed, not entailed, or unknown results. This behavior * is overridable by each Printer, since sometimes an output language * has a particular preference for how results should appear. */ @@ -139,15 +151,15 @@ inline bool Result::operator!=(const Result& r) const { return !(*this == r); } std::ostream& operator<<(std::ostream& out, enum Result::Sat s) CVC4_PUBLIC; std::ostream& operator<<(std::ostream& out, - enum Result::Validity v) CVC4_PUBLIC; + enum Result::Entailment e) CVC4_PUBLIC; std::ostream& operator<<(std::ostream& out, enum Result::UnknownExplanation e) CVC4_PUBLIC; bool operator==(enum Result::Sat s, const Result& r) CVC4_PUBLIC; -bool operator==(enum Result::Validity v, const Result& r) CVC4_PUBLIC; +bool operator==(enum Result::Entailment e, const Result& r) CVC4_PUBLIC; bool operator!=(enum Result::Sat s, const Result& r) CVC4_PUBLIC; -bool operator!=(enum Result::Validity v, const Result& r) CVC4_PUBLIC; +bool operator!=(enum Result::Entailment e, const Result& r) CVC4_PUBLIC; } /* CVC4 namespace */ diff --git a/src/util/result.i b/src/util/result.i index b77bfd881..cb835fbb0 100644 --- a/src/util/result.i +++ b/src/util/result.i @@ -8,13 +8,13 @@ %ignore CVC4::Result::operator!=(const Result& r) const; %ignore CVC4::operator<<(std::ostream&, enum Result::Sat); -%ignore CVC4::operator<<(std::ostream&, enum Result::Validity); +%ignore CVC4::operator<<(std::ostream&, enum Result::Entailment); %ignore CVC4::operator<<(std::ostream&, enum Result::UnknownExplanation); %ignore CVC4::operator==(enum Result::Sat, const Result&); %ignore CVC4::operator!=(enum Result::Sat, const Result&); -%ignore CVC4::operator==(enum Result::Validity, const Result&); -%ignore CVC4::operator!=(enum Result::Validity, const Result&); +%ignore CVC4::operator==(enum Result::Entailment, const Result&); +%ignore CVC4::operator!=(enum Result::Entailment, const Result&); %include "util/result.h" diff --git a/src/util/safe_print.h b/src/util/safe_print.h index 75a517b18..fa9430f2c 100644 --- a/src/util/safe_print.h +++ b/src/util/safe_print.h @@ -21,6 +21,11 @@ ** in statistics_registry.h would require specialization or we would have to ** use function overloading). ** + ** If there exists a function `toString(obj)` for a given object, it will be + ** used automatically. This is useful for printing enum values for example. + ** IMPORTANT: The `toString(obj)` function *must not* perform any allocations + ** or call other functions that are not async-signal-safe. + ** ** This header is a "cvc4_private_library.h" header because it is private but ** the safe_print functions are used in the driver. See also the description ** of "statistics_registry.h" for more information on @@ -41,6 +46,9 @@ #include <unistd.h> +#include <cstring> +#include <type_traits> + #include "lib/clock_gettime.h" #include "util/result.h" @@ -58,10 +66,51 @@ void CVC4_PUBLIC safe_print(int fd, const char (&msg)[N]) { } } -/** Prints a variable of type T. Safe to use in a signal handler. */ +/** + * The default method for converting an object to a string for safe printing. + * This method simply returns "<unsupported>". The `long` argument is used to + * indicate that we do not prefer this method over the version that calls + * `toString()`. + */ +template <typename T> +const char* toStringImpl(const T& obj, long) +{ + return "<unsupported>"; +} + +/** + * Returns the result of calling `toString(obj)`. This method is only defined + * if such an overload of `toString()` exists. To detect the existence of such + * a method, we use SFINAE and a trailing return type. The trailing return type + * is necessary because it allows us to refer to `obj`. The `int` argument is + * used to prefer this version of the function instead of the one that prints + * "<unsupported>". + */ +template <typename T> +auto toStringImpl(const T& obj, int) -> decltype(toString(obj)) +{ + return toString(obj); +} + +/** + * Prints a variable of type T. Safe to use in a signal handler. The default + * implementation either prints "<unsupported>" or the result of calling + * `toString(obj)` if such a method exists (this is useful for printing enum + * values for example without implementing a template specialization here). + * + * @param fd The file descriptor to print to + * @param obj The object to print + */ template <typename T> -void CVC4_PUBLIC safe_print(int fd, const T& msg) { - safe_print(fd, "<unsupported>"); +void CVC4_PUBLIC safe_print(int fd, const T& obj) +{ + const char* s = + toStringImpl(obj, /* prefer the method that uses `toString()` */ 0); + ssize_t slen = static_cast<ssize_t>(strlen(s)); + if (write(fd, s, slen) != slen) + { + abort(); + } } template <> diff --git a/src/util/string.cpp b/src/util/string.cpp new file mode 100644 index 000000000..ff522ba7b --- /dev/null +++ b/src/util/string.cpp @@ -0,0 +1,485 @@ +/********************* */ +/*! \file string.cpp + ** \verbatim + ** Top contributors (to current version): + ** Tim King, Tianyi Liang, Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of the string data type. + **/ + +#include "util/string.h" + +#include <algorithm> +#include <climits> +#include <iomanip> +#include <iostream> +#include <sstream> + +#include "base/check.h" +#include "base/exception.h" + +using namespace std; + +namespace CVC4 { + +static_assert(UCHAR_MAX == 255, "Unsigned char is assumed to have 256 values."); + +String::String(const std::vector<unsigned> &s) : d_str(s) +{ +#ifdef CVC4_ASSERTIONS + for (unsigned u : d_str) + { + Assert(u < num_codes()); + } +#endif +} + +int String::cmp(const String &y) const { + if (size() != y.size()) { + return size() < y.size() ? -1 : 1; + } + for (unsigned int i = 0; i < size(); ++i) { + if (d_str[i] != y.d_str[i]) { + unsigned cp = d_str[i]; + unsigned cpy = y.d_str[i]; + return cp < cpy ? -1 : 1; + } + } + return 0; +} + +String String::concat(const String &other) const { + std::vector<unsigned int> ret_vec(d_str); + ret_vec.insert(ret_vec.end(), other.d_str.begin(), other.d_str.end()); + return String(ret_vec); +} + +bool String::strncmp(const String& y, std::size_t n) const +{ + std::size_t b = (size() >= y.size()) ? size() : y.size(); + std::size_t s = (size() <= y.size()) ? size() : y.size(); + if (n > s) { + if (b == s) { + n = s; + } else { + return false; + } + } + for (std::size_t i = 0; i < n; ++i) { + if (d_str[i] != y.d_str[i]) return false; + } + return true; +} + +bool String::rstrncmp(const String& y, std::size_t n) const +{ + std::size_t b = (size() >= y.size()) ? size() : y.size(); + std::size_t s = (size() <= y.size()) ? size() : y.size(); + if (n > s) { + if (b == s) { + n = s; + } else { + return false; + } + } + for (std::size_t i = 0; i < n; ++i) { + if (d_str[size() - i - 1] != y.d_str[y.size() - i - 1]) return false; + } + return true; +} + +void String::addCharToInternal(unsigned char ch, std::vector<unsigned>& str) +{ + // if not a printable character + if (ch > 127 || ch < 32) + { + std::stringstream serr; + serr << "Illegal string character: \"" << ch + << "\", must use escape sequence"; + throw CVC4::Exception(serr.str()); + } + else + { + str.push_back(static_cast<unsigned>(ch)); + } +} + +std::vector<unsigned> String::toInternal(const std::string& s, + bool useEscSequences) +{ + std::vector<unsigned> str; + unsigned i = 0; + while (i < s.size()) + { + // get the current character + char si = s[i]; + if (si != '\\' || !useEscSequences) + { + addCharToInternal(si, str); + ++i; + continue; + } + // the vector of characters, in case we fail to read an escape sequence + std::vector<unsigned> nonEscCache; + // process the '\' + addCharToInternal(si, nonEscCache); + ++i; + // are we an escape sequence? + bool isEscapeSequence = true; + // the string corresponding to the hexidecimal code point + std::stringstream hexString; + // is the slash followed by a 'u'? Could be last character. + if (i >= s.size() || s[i] != 'u') + { + isEscapeSequence = false; + } + else + { + // process the 'u' + addCharToInternal(s[i], nonEscCache); + ++i; + bool isStart = true; + bool isEnd = false; + bool hasBrace = false; + while (i < s.size()) + { + // add the next character + si = s[i]; + if (isStart) + { + isStart = false; + // possibly read '{' + if (si == '{') + { + hasBrace = true; + addCharToInternal(si, nonEscCache); + ++i; + continue; + } + } + else if (si == '}') + { + // can only end if we had an open brace and read at least one digit + isEscapeSequence = hasBrace && !hexString.str().empty(); + isEnd = true; + addCharToInternal(si, nonEscCache); + ++i; + break; + } + // must be a hex digit at this point + if (!isHexDigit(static_cast<unsigned>(si))) + { + isEscapeSequence = false; + break; + } + hexString << si; + addCharToInternal(si, nonEscCache); + ++i; + if (!hasBrace && hexString.str().size() == 4) + { + // will be finished reading \ u d_3 d_2 d_1 d_0 with no parens + isEnd = true; + break; + } + else if (hasBrace && hexString.str().size() > 5) + { + // too many digits enclosed in brace, not an escape sequence + isEscapeSequence = false; + break; + } + } + if (!isEnd) + { + // if we were interupted before ending, then this is not a valid + // escape sequence + isEscapeSequence = false; + } + } + if (isEscapeSequence) + { + Assert(!hexString.str().empty() && hexString.str().size() <= 5); + // Otherwise, we add the escaped character. + // This is guaranteed not to overflow due to the length of hstr. + uint32_t val; + hexString >> std::hex >> val; + if (val > num_codes()) + { + // Failed due to being out of range. This can happen for strings of + // the form \ u { d_4 d_3 d_2 d_1 d_0 } where d_4 is a hexidecimal not + // in the range [0-2]. + isEscapeSequence = false; + } + else + { + str.push_back(val); + } + } + // if we did not successfully parse an escape sequence, we add back all + // characters that we cached + if (!isEscapeSequence) + { + str.insert(str.end(), nonEscCache.begin(), nonEscCache.end()); + } + } +#ifdef CVC4_ASSERTIONS + for (unsigned u : str) + { + Assert(u < num_codes()); + } +#endif + return str; +} + +unsigned String::front() const +{ + Assert(!d_str.empty()); + return d_str.front(); +} + +unsigned String::back() const +{ + Assert(!d_str.empty()); + return d_str.back(); +} + +std::size_t String::overlap(const String &y) const { + std::size_t i = size() < y.size() ? size() : y.size(); + for (; i > 0; i--) { + String s = suffix(i); + String p = y.prefix(i); + if (s == p) { + return i; + } + } + return i; +} + +std::size_t String::roverlap(const String &y) const { + std::size_t i = size() < y.size() ? size() : y.size(); + for (; i > 0; i--) { + String s = prefix(i); + String p = y.suffix(i); + if (s == p) { + return i; + } + } + return i; +} + +std::string String::toString(bool useEscSequences) const { + std::stringstream str; + for (unsigned int i = 0; i < size(); ++i) { + // we always print forward slash as a code point so that it cannot + // be interpreted as specifying part of a code point, e.g. the string + // '\' + 'u' + '0' of length three. + if (isPrintable(d_str[i]) && d_str[i] != '\\' && !useEscSequences) + { + str << static_cast<char>(d_str[i]); + } + else + { + std::stringstream ss; + ss << std::hex << d_str[i]; + str << "\\u{" << ss.str() << "}"; + } + } + return str.str(); +} + +bool String::isLeq(const String &y) const +{ + for (unsigned i = 0; i < size(); ++i) + { + if (i >= y.size()) + { + return false; + } + unsigned ci = d_str[i]; + unsigned cyi = y.d_str[i]; + if (ci > cyi) + { + return false; + } + if (ci < cyi) + { + return true; + } + } + return true; +} + +bool String::isRepeated() const { + if (size() > 1) { + unsigned int f = d_str[0]; + for (unsigned i = 1; i < size(); ++i) { + if (f != d_str[i]) return false; + } + } + return true; +} + +bool String::tailcmp(const String &y, int &c) const { + int id_x = size() - 1; + int id_y = y.size() - 1; + while (id_x >= 0 && id_y >= 0) { + if (d_str[id_x] != y.d_str[id_y]) { + c = id_x; + return false; + } + --id_x; + --id_y; + } + c = id_x == -1 ? (-(id_y + 1)) : (id_x + 1); + return true; +} + +std::size_t String::find(const String &y, const std::size_t start) const { + if (size() < y.size() + start) return std::string::npos; + if (y.empty()) return start; + if (empty()) return std::string::npos; + + std::vector<unsigned>::const_iterator itr = std::search( + d_str.begin() + start, d_str.end(), y.d_str.begin(), y.d_str.end()); + if (itr != d_str.end()) { + return itr - d_str.begin(); + } + return std::string::npos; +} + +std::size_t String::rfind(const String &y, const std::size_t start) const { + if (size() < y.size() + start) return std::string::npos; + if (y.empty()) return start; + if (empty()) return std::string::npos; + + std::vector<unsigned>::const_reverse_iterator itr = std::search( + d_str.rbegin() + start, d_str.rend(), y.d_str.rbegin(), y.d_str.rend()); + if (itr != d_str.rend()) { + return itr - d_str.rbegin(); + } + return std::string::npos; +} + +bool String::hasPrefix(const String& y) const +{ + size_t s = size(); + size_t ys = y.size(); + if (ys > s) + { + return false; + } + for (size_t i = 0; i < ys; i++) + { + if (d_str[i] != y.d_str[i]) + { + return false; + } + } + return true; +} + +bool String::hasSuffix(const String& y) const +{ + size_t s = size(); + size_t ys = y.size(); + if (ys > s) + { + return false; + } + size_t idiff = s - ys; + for (size_t i = 0; i < ys; i++) + { + if (d_str[i + idiff] != y.d_str[i]) + { + return false; + } + } + return true; +} + +String String::replace(const String &s, const String &t) const { + std::size_t ret = find(s); + if (ret != std::string::npos) { + std::vector<unsigned int> vec; + vec.insert(vec.begin(), d_str.begin(), d_str.begin() + ret); + vec.insert(vec.end(), t.d_str.begin(), t.d_str.end()); + vec.insert(vec.end(), d_str.begin() + ret + s.size(), d_str.end()); + return String(vec); + } else { + return *this; + } +} + +String String::substr(std::size_t i) const { + Assert(i <= size()); + std::vector<unsigned int> ret_vec; + std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; + ret_vec.insert(ret_vec.end(), itr, d_str.end()); + return String(ret_vec); +} + +String String::substr(std::size_t i, std::size_t j) const { + Assert(i + j <= size()); + std::vector<unsigned int> ret_vec; + std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; + ret_vec.insert(ret_vec.end(), itr, itr + j); + return String(ret_vec); +} + +bool String::noOverlapWith(const String& y) const +{ + return y.find(*this) == std::string::npos + && this->find(y) == std::string::npos && this->overlap(y) == 0 + && y.overlap(*this) == 0; +} + +bool String::isNumber() const { + if (d_str.empty()) { + return false; + } + for (unsigned character : d_str) { + if (!isDigit(character)) + { + return false; + } + } + return true; +} + +bool String::isDigit(unsigned character) +{ + // '0' to '9' + return 48 <= character && character <= 57; +} + +bool String::isHexDigit(unsigned character) +{ + // '0' to '9' or 'A' to 'F' or 'a' to 'f' + return isDigit(character) || (65 <= character && character <= 70) + || (97 <= character && character <= 102); +} + +bool String::isPrintable(unsigned character) +{ + // Unicode 0x00020 (' ') to 0x0007E ('~') + return 32 <= character && character <= 126; +} + +size_t String::maxSize() { return std::numeric_limits<uint32_t>::max(); } + +Rational String::toNumber() const +{ + // when smt2 standard for strings is set, this may change, based on the + // semantics of str.from.int for leading zeros + return Rational(toString()); +} + +std::ostream &operator<<(std::ostream &os, const String &s) { + return os << "\"" << s.toString(true) << "\""; +} + +} // namespace CVC4 diff --git a/src/util/string.h b/src/util/string.h new file mode 100644 index 000000000..032105812 --- /dev/null +++ b/src/util/string.h @@ -0,0 +1,271 @@ +/********************* */ +/*! \file string.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds, Tim King, Tianyi Liang + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2019 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief The string data type. + **/ + +#include "cvc4_public.h" + +#ifndef CVC4__UTIL__STRING_H +#define CVC4__UTIL__STRING_H + +#include <cstddef> +#include <functional> +#include <ostream> +#include <string> +#include <vector> +#include "util/rational.h" + +namespace CVC4 { + +/** The CVC4 string class + * + * This data structure is the domain of values for the string type. It can also + * be used as a generic utility for representing strings. + */ +class CVC4_PUBLIC String { + public: + /** + * This is the cardinality of the alphabet that is representable by this + * class. Notice that this must be greater than or equal to the cardinality + * of the alphabet that the string theory reasons about. + * + * This must be strictly less than std::numeric_limits<unsigned>::max(). + * + * As per the SMT-LIB standard for strings, we support the first 3 planes of + * Unicode characters, where 196608 = 3*16^4. + */ + static inline unsigned num_codes() { return 196608; } + /** constructors for String + * + * Internally, a CVC4::String is represented by a vector of unsigned + * integers (d_str) representing the code points of the characters. + * + * To build a string from a C++ string, we may process escape sequences + * according to the SMT-LIB standard. In particular, if useEscSequences is + * true, we convert unicode escape sequences: + * \u d_3 d_2 d_1 d_0 + * \u{d_0} + * \u{d_1 d_0} + * \u{d_2 d_1 d_0} + * \u{d_3 d_2 d_1 d_0} + * \u{d_4 d_3 d_2 d_1 d_0} + * where d_0 ... d_4 are hexidecimal digits, to the appropriate character. + * + * If useEscSequences is false, then the characters of the constructed + * CVC4::String correspond one-to-one with the input string. + */ + String() = default; + explicit String(const std::string& s, bool useEscSequences = false) + : d_str(toInternal(s, useEscSequences)) + { + } + explicit String(const char* s, bool useEscSequences = false) + : d_str(toInternal(std::string(s), useEscSequences)) + { + } + explicit String(const std::vector<unsigned>& s); + + String& operator=(const String& y) { + if (this != &y) { + d_str = y.d_str; + } + return *this; + } + + String concat(const String& other) const; + + bool operator==(const String& y) const { return cmp(y) == 0; } + bool operator!=(const String& y) const { return cmp(y) != 0; } + bool operator<(const String& y) const { return cmp(y) < 0; } + bool operator>(const String& y) const { return cmp(y) > 0; } + bool operator<=(const String& y) const { return cmp(y) <= 0; } + bool operator>=(const String& y) const { return cmp(y) >= 0; } + + /** + * Returns true if this string is equal to y for their first n characters. + * If n is larger than the length of this string or y, this method returns + * true if and only if this string is equal to y. + */ + bool strncmp(const String& y, std::size_t n) const; + /** + * Returns true if this string is equal to y for their last n characters. + * Similar to strncmp, if n is larger than the length of this string or y, + * this method returns true if and only if this string is equal to y. + */ + bool rstrncmp(const String& y, std::size_t n) const; + + /* toString + * Converts this string to a std::string. + * + * The unprintable characters are converted to unicode escape sequences as + * described above. + * + * If useEscSequences is false, the string's printable characters are + * printed as characters. Notice that for all std::string s having only + * printable characters, we have that + * CVC4::String( s ).toString() = s. + */ + std::string toString(bool useEscSequences = false) const; + /** is this the empty string? */ + bool empty() const { return d_str.empty(); } + /** is this the empty string? */ + bool isEmptyString() const { return empty(); } + /** is less than or equal to string y */ + bool isLeq(const String& y) const; + /** Return the length of the string */ + std::size_t size() const { return d_str.size(); } + + bool isRepeated() const; + bool tailcmp(const String& y, int& c) const; + + /** + * Return the first position y occurs in this string, or std::string::npos + * otherwise. + */ + std::size_t find(const String& y, const std::size_t start = 0) const; + /** + * Return the first position y occurs in this string searching from the end, + * or std::string::npos otherwise. + */ + std::size_t rfind(const String& y, const std::size_t start = 0) const; + /** Returns true if y is a prefix of this */ + bool hasPrefix(const String& y) const; + /** Returns true if y is a suffix of this */ + bool hasSuffix(const String& y) const; + /** Replace the first occurrence of s in this string with t */ + String replace(const String& s, const String& t) const; + /** Return the substring of this string starting at index i */ + String substr(std::size_t i) const; + /** Return the substring of this string starting at index i with size at most + * j */ + String substr(std::size_t i, std::size_t j) const; + /** Return the prefix of this string of size at most i */ + String prefix(std::size_t i) const { return substr(0, i); } + /** Return the suffix of this string of size at most i */ + String suffix(std::size_t i) const { return substr(size() - i, i); } + + /** + * Checks if there is any overlap between this string and another string. This + * corresponds to checking whether one string contains the other and whether a + * substring of one is a prefix of the other and vice-versa. + * + * @param y The other string + * @return True if there is an overlap, false otherwise + */ + bool noOverlapWith(const String& y) const; + + /** string overlap + * + * if overlap returns m>0, + * then the maximal suffix of this string that is a prefix of y is of length m. + * + * For example, if x is "abcdef", then: + * x.overlap("defg") = 3 + * x.overlap("ab") = 0 + * x.overlap("d") = 0 + * x.overlap("bcdefdef") = 5 + */ + std::size_t overlap(const String& y) const; + /** string reverse overlap + * + * if roverlap returns m>0, + * then the maximal prefix of this string that is a suffix of y is of length m. + * + * For example, if x is "abcdef", then: + * x.roverlap("aaabc") = 3 + * x.roverlap("def") = 0 + * x.roverlap("d") = 0 + * x.roverlap("defabcde") = 5 + * + * Notice that x.overlap(y) = y.roverlap(x) + */ + std::size_t roverlap(const String& y) const; + + /** + * Returns true if this string corresponds in text to a number, for example + * this returns true for strings "7", "12", "004", "0" and false for strings + * "abc", "4a", "-4", "". + */ + bool isNumber() const; + /** Returns the corresponding rational for the text of this string. */ + Rational toNumber() const; + /** Get the unsigned representation (code points) of this string */ + const std::vector<unsigned>& getVec() const { return d_str; } + /** + * Get the unsigned (code point) value of the first character in this string + */ + unsigned front() const; + /** + * Get the unsigned (code point) value of the last character in this string + */ + unsigned back() const; + /** is the unsigned a digit? + * + * This is true for code points between 48 ('0') and 57 ('9'). + */ + static bool isDigit(unsigned character); + /** is the unsigned a hexidecimal digit? + * + * This is true for code points between 48 ('0') and 57 ('9'), code points + * between 65 ('A') and 70 ('F) and code points between 97 ('a') and 102 ('f). + */ + static bool isHexDigit(unsigned character); + /** is the unsigned a printable code point? + * + * This is true for Unicode 32 (' ') to 126 ('~'). + */ + static bool isPrintable(unsigned character); + + /** + * Returns the maximum length of string representable by this class. + * Corresponds to the maximum size of d_str. + */ + static size_t maxSize(); + private: + /** + * Helper for toInternal: add character ch to vector vec, storing a string in + * internal format. This throws an error if ch is not a printable character, + * since non-printable characters must be escaped in SMT-LIB. + */ + static void addCharToInternal(unsigned char ch, std::vector<unsigned>& vec); + /** + * Convert the string s to the internal format (vector of code points). + * The argument useEscSequences is whether to process unicode escape + * sequences. + */ + static std::vector<unsigned> toInternal(const std::string& s, + bool useEscSequences); + + /** + * Returns a negative number if *this < y, 0 if *this and y are equal and a + * positive number if *this > y. + */ + int cmp(const String& y) const; + + std::vector<unsigned> d_str; +}; /* class String */ + +namespace strings { + +struct CVC4_PUBLIC StringHashFunction { + size_t operator()(const ::CVC4::String& s) const { + return std::hash<std::string>()(s.toString()); + } +}; /* struct StringHashFunction */ + +} // namespace strings + +std::ostream& operator<<(std::ostream& os, const String& s) CVC4_PUBLIC; + +} // namespace CVC4 + +#endif /* CVC4__UTIL__STRING_H */ diff --git a/src/util/string.i b/src/util/string.i new file mode 100644 index 000000000..1ded901aa --- /dev/null +++ b/src/util/string.i @@ -0,0 +1,25 @@ +%{ +#include "util/string.h" +%} + +%rename(CVC4String) String; +%rename(CVC4StringHashFunction) CVC4::strings::StringHashFunction; + +%ignore CVC4::String::String(const std::string&); + +%rename(assign) CVC4::String::operator=(const String&); +%rename(getChar) CVC4::String::operator[](const unsigned int) const; +%rename(equals) CVC4::String::operator==(const String&) const; +%ignore CVC4::String::operator!=(const String&) const; +%rename(less) CVC4::String::operator<(const String&) const; +%rename(lessEqual) CVC4::String::operator<=(const String&) const; +%rename(greater) CVC4::String::operator>(const String&) const; +%rename(greaterEqual) CVC4::String::operator>=(const String&) const; + +%rename(apply) CVC4::strings::StringHashFunction::operator()(const ::CVC4::String&) const; + +%ignore CVC4::operator<<(std::ostream&, const String&); + +%apply int &OUTPUT { int &c }; +%include "util/string.h" +%clear int &c; diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index 0eb6bc2d2..87bea4583 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -610,6 +610,8 @@ set(regress_0_tests regress0/parser/as.smt2 regress0/parser/bv_arity_smt2.6.smt2 regress0/parser/bv_nat.smt2 + regress0/parser/choice.cvc + regress0/parser/choice.smt2 regress0/parser/constraint.smt2 regress0/parser/declarefun-emptyset-uf.smt2 regress0/parser/force_logic_set_logic.smt2 @@ -902,6 +904,7 @@ set(regress_0_tests regress0/smtlib/global-decls.smt2 regress0/smtlib/issue4028.smt2 regress0/smtlib/issue4077.smt2 + regress0/smtlib/issue4151.smt2 regress0/smtlib/reason-unknown.smt2 regress0/smtlib/reset.smt2 regress0/smtlib/reset-assertions1.smt2 @@ -915,6 +918,7 @@ set(regress_0_tests regress0/strings/bug002.smt2 regress0/strings/bug612.smt2 regress0/strings/bug613.smt2 + regress0/strings/char-representations.smt2 regress0/strings/code-eval.smt2 regress0/strings/code-perf.smt2 regress0/strings/code-sat-neg-one.smt2 @@ -922,6 +926,7 @@ set(regress_0_tests regress0/strings/escchar.smt2 regress0/strings/escchar_25.smt2 regress0/strings/from_code.smt2 + regress0/strings/gen-esc-seq.smt2 regress0/strings/hconst-092618.smt2 regress0/strings/idof-rewrites.smt2 regress0/strings/idof-sem.smt2 @@ -938,7 +943,10 @@ set(regress_0_tests regress0/strings/large-model.smt2 regress0/strings/leadingzero001.smt2 regress0/strings/loop001.smt2 + regress0/strings/loop-wrong-sem.smt2 regress0/strings/model001.smt2 + regress0/strings/model-code-point.smt2 + regress0/strings/model-friendly.smt2 regress0/strings/ncontrib-rewrites.smt2 regress0/strings/norn-31.smt2 regress0/strings/norn-simp-rew.smt2 @@ -967,6 +975,7 @@ set(regress_0_tests regress0/strings/tolower-rrs.smt2 regress0/strings/tolower-simple.smt2 regress0/strings/type001.smt2 + regress0/strings/unicode-esc.smt2 regress0/strings/unsound-0908.smt2 regress0/strings/unsound-repl-rewrite.smt2 regress0/sygus/General_plus10.sy @@ -1857,7 +1866,6 @@ set(regress_1_tests regress1/sygus/issue3498.smt2 regress1/sygus/issue3514.smt2 regress1/sygus/issue3507.smt2 - regress1/sygus/issue3580.sy regress1/sygus/issue3633.smt2 regress1/sygus/issue3634.smt2 regress1/sygus/issue3635.smt2 @@ -2388,6 +2396,8 @@ set(regression_disabled_tests regress1/sygus/array_search_2.sy regress1/sygus/array_sum_2_5.sy regress1/sygus/crcy-si-rcons.sy + # currently slow at c9fd28a + regress1/sygus/issue3580.sy regress2/arith/arith-int-098.cvc regress2/arith/miplib-opt1217--27.smt2 regress2/arith/miplib-pp08a-3000.smt2 diff --git a/test/regress/regress0/arith/arith.01.cvc b/test/regress/regress0/arith/arith.01.cvc index 1de397ab1..08d591590 100644 --- a/test/regress/regress0/arith/arith.01.cvc +++ b/test/regress/regress0/arith/arith.01.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x : REAL; y : REAL; diff --git a/test/regress/regress0/arith/arith.02.cvc b/test/regress/regress0/arith/arith.02.cvc index d7b0291f7..e0a48c357 100644 --- a/test/regress/regress0/arith/arith.02.cvc +++ b/test/regress/regress0/arith/arith.02.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x : REAL; y : REAL; z : REAL; diff --git a/test/regress/regress0/arith/arith.03.cvc b/test/regress/regress0/arith/arith.03.cvc index 288c341ef..ce54c8b7e 100644 --- a/test/regress/regress0/arith/arith.03.cvc +++ b/test/regress/regress0/arith/arith.03.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x : REAL; y : REAL; diff --git a/test/regress/regress0/arith/bug549.cvc b/test/regress/regress0/arith/bug549.cvc index 54df5e62c..bfb3e75d5 100644 --- a/test/regress/regress0/arith/bug549.cvc +++ b/test/regress/regress0/arith/bug549.cvc @@ -1,3 +1,3 @@ -% EXPECT: valid +% EXPECT: entailed a, b : REAL; -QUERY (a*b)^5 = b*a*a*a*a*b*b*b*b*a;
\ No newline at end of file +QUERY (a*b)^5 = b*a*a*a*a*b*b*b*b*a; diff --git a/test/regress/regress0/arith/integers/arith-int-014.cvc b/test/regress/regress0/arith/integers/arith-int-014.cvc index 265d18a84..84954a3ea 100644 --- a/test/regress/regress0/arith/integers/arith-int-014.cvc +++ b/test/regress/regress0/arith/integers/arith-int-014.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (10 * x0) + (25 * x1) + (10 * x2) + (-28 * x3) <= 20 ; ASSERT (24 * x0) + (-9 * x1) + (-12 * x2) + (15 * x3) <= 3; diff --git a/test/regress/regress0/arith/integers/arith-int-015.cvc b/test/regress/regress0/arith/integers/arith-int-015.cvc index d2e2639ab..8f8b01fc9 100644 --- a/test/regress/regress0/arith/integers/arith-int-015.cvc +++ b/test/regress/regress0/arith/integers/arith-int-015.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-22 * x0) + (-3 * x1) + (9 * x2) + (-13 * x3) > -31 ; ASSERT (31 * x0) + (-17 * x1) + (28 * x2) + (-16 * x3) > -28; diff --git a/test/regress/regress0/arith/integers/arith-int-021.cvc b/test/regress/regress0/arith/integers/arith-int-021.cvc index 345c90899..66f02401b 100644 --- a/test/regress/regress0/arith/integers/arith-int-021.cvc +++ b/test/regress/regress0/arith/integers/arith-int-021.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (8 * x0) + (-27 * x1) + (29 * x2) + (-13 * x3) < 12; QUERY FALSE; diff --git a/test/regress/regress0/arith/integers/arith-int-023.cvc b/test/regress/regress0/arith/integers/arith-int-023.cvc index 01d51a226..b3d79e8ff 100644 --- a/test/regress/regress0/arith/integers/arith-int-023.cvc +++ b/test/regress/regress0/arith/integers/arith-int-023.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (29 * x0) + (-19 * x1) + (23 * x2) + (15 * x3) <= 9; QUERY FALSE; diff --git a/test/regress/regress0/arith/integers/arith-int-025.cvc b/test/regress/regress0/arith/integers/arith-int-025.cvc index 5a11212d5..e905da9a0 100644 --- a/test/regress/regress0/arith/integers/arith-int-025.cvc +++ b/test/regress/regress0/arith/integers/arith-int-025.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-19 * x0) + (-29 * x1) + (2 * x2) + (26 * x3) >= 3; QUERY FALSE; diff --git a/test/regress/regress0/arith/integers/arith-int-042.cvc b/test/regress/regress0/arith/integers/arith-int-042.cvc index c38231695..b6db26e86 100644 --- a/test/regress/regress0/arith/integers/arith-int-042.cvc +++ b/test/regress/regress0/arith/integers/arith-int-042.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-9 * x0) + (25 * x1) + (0 * x2) + (13 * x3) = 17 ; ASSERT (-6 * x0) + (32 * x1) + (2 * x2) + (-32 * x3) = -5 ; diff --git a/test/regress/regress0/arith/integers/arith-int-042.min.cvc b/test/regress/regress0/arith/integers/arith-int-042.min.cvc index 77571e526..3fd20a0b6 100644 --- a/test/regress/regress0/arith/integers/arith-int-042.min.cvc +++ b/test/regress/regress0/arith/integers/arith-int-042.min.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x1: INT; x0: INT; QUERY NOT (((x0 * 6) + (x1 * 32)) = 1); diff --git a/test/regress/regress0/arith/integers/arith-int-079.cvc b/test/regress/regress0/arith/integers/arith-int-079.cvc index 7fa2fc937..1739b3364 100644 --- a/test/regress/regress0/arith/integers/arith-int-079.cvc +++ b/test/regress/regress0/arith/integers/arith-int-079.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (6 * x0) + (2 * x1) + (22 * x2) + (-18 * x3) = -15 ; ASSERT (-8 * x0) + (-25 * x1) + (-25 * x2) + (7 * x3) > 10 ; diff --git a/test/regress/regress0/arith/integers/arith-interval.cvc b/test/regress/regress0/arith/integers/arith-interval.cvc index d79ec94a7..ed6cb747e 100644 --- a/test/regress/regress0/arith/integers/arith-interval.cvc +++ b/test/regress/regress0/arith/integers/arith-interval.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x: INT; P: INT -> BOOLEAN; diff --git a/test/regress/regress0/boolean-prec.cvc b/test/regress/regress0/boolean-prec.cvc index 9d1029cbf..07a2aa2f5 100644 --- a/test/regress/regress0/boolean-prec.cvc +++ b/test/regress/regress0/boolean-prec.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of AND, <=>, NOT. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/bug310.cvc b/test/regress/regress0/bug310.cvc index 0cb6cf0fe..12cb80494 100644 --- a/test/regress/regress0/bug310.cvc +++ b/test/regress/regress0/bug310.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed b : BOOLEAN; DATATYPE D = c(s:INT) END; QUERY c(IF b THEN 1 ELSE 0 ENDIF) = IF b THEN c(1) ELSE c(0) ENDIF; diff --git a/test/regress/regress0/bug32.cvc b/test/regress/regress0/bug32.cvc index 304dd6ec4..400f55885 100644 --- a/test/regress/regress0/bug32.cvc +++ b/test/regress/regress0/bug32.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed a:BOOLEAN; b:BOOLEAN; ASSERT(a); diff --git a/test/regress/regress0/bug322b.cvc b/test/regress/regress0/bug322b.cvc index fa9462f64..0e06cbc13 100644 --- a/test/regress/regress0/bug322b.cvc +++ b/test/regress/regress0/bug322b.cvc @@ -1,7 +1,7 @@ % COMMAND-LINE: --incremental -% EXPECT: valid -% EXPECT: valid -% EXPECT: valid +% EXPECT: entailed +% EXPECT: entailed +% EXPECT: entailed x : INT; y : INT = x + 1; z : INT = -10; diff --git a/test/regress/regress0/bug486.cvc b/test/regress/regress0/bug486.cvc index c51af4e24..665bcdafb 100644 --- a/test/regress/regress0/bug486.cvc +++ b/test/regress/regress0/bug486.cvc @@ -1,6 +1,6 @@ % COMMAND-LINE: --finite-model-find -i -% EXPECT: invalid -% EXPECT: valid +% EXPECT: not_entailed +% EXPECT: entailed prin:TYPE; form:TYPE; diff --git a/test/regress/regress0/bv/bvcomp.cvc b/test/regress/regress0/bv/bvcomp.cvc index b9b4b8e17..17064a2f2 100644 --- a/test/regress/regress0/bv/bvcomp.cvc +++ b/test/regress/regress0/bv/bvcomp.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x : BITVECTOR(10); diff --git a/test/regress/regress0/bv/bvsimple.cvc b/test/regress/regress0/bv/bvsimple.cvc index dcacd643a..6bc649a03 100644 --- a/test/regress/regress0/bv/bvsimple.cvc +++ b/test/regress/regress0/bv/bvsimple.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Some tests from the CVC3 user manual. % http://www.cs.nyu.edu/acsys/cvc3/doc/user_doc.html diff --git a/test/regress/regress0/cvc-rerror-print.cvc b/test/regress/regress0/cvc-rerror-print.cvc index dd05723d2..e134b5666 100644 --- a/test/regress/regress0/cvc-rerror-print.cvc +++ b/test/regress/regress0/cvc-rerror-print.cvc @@ -1,5 +1,5 @@ -% EXPECT: valid -% EXPECT: Cannot get model unless immediately preceded by SAT/INVALID or UNKNOWN response. +% EXPECT: entailed +% EXPECT: Cannot get model unless immediately preceded by SAT/NOT_ENTAILED or UNKNOWN response. OPTION "logic" "ALL"; OPTION "produce-models" true; x : INT; diff --git a/test/regress/regress0/cvc3-bug15.cvc b/test/regress/regress0/cvc3-bug15.cvc index 860ce38bd..779a2eab4 100644 --- a/test/regress/regress0/cvc3-bug15.cvc +++ b/test/regress/regress0/cvc3-bug15.cvc @@ -1,5 +1,5 @@ %% This test borrowed from CVC3 regressions, bug15.cvc -% EXPECT: valid +% EXPECT: entailed x : REAL; y : REAL; f : REAL -> REAL; diff --git a/test/regress/regress0/cvc3.userdoc.01.cvc b/test/regress/regress0/cvc3.userdoc.01.cvc index 7c89de4ac..e3e9aaf0a 100644 --- a/test/regress/regress0/cvc3.userdoc.01.cvc +++ b/test/regress/regress0/cvc3.userdoc.01.cvc @@ -1,31 +1,31 @@ % COMMAND-LINE: --incremental -% EXPECT: valid +% EXPECT: entailed QUERY 0bin0000111101010000 = 0hex0f50; -% EXPECT: valid +% EXPECT: entailed QUERY 0bin01@0bin0 = 0bin010; -% EXPECT: valid +% EXPECT: entailed QUERY 0bin0011[3:1] = 0bin001; -% EXPECT: valid +% EXPECT: entailed QUERY 0bin0011 << 3 = 0bin0011000; -% EXPECT: valid +% EXPECT: entailed QUERY 0bin1000 >> 3 = 0bin0001; -% EXPECT: valid +% EXPECT: entailed QUERY SX(0bin100, 5) = 0bin11100; -% EXPECT: valid +% EXPECT: entailed QUERY BVZEROEXTEND(0bin1,3) = 0bin0001; -% EXPECT: valid +% EXPECT: entailed QUERY BVREPEAT(0bin10,3) = 0bin101010; -% EXPECT: valid +% EXPECT: entailed QUERY BVROTL(0bin101,1) = 0bin011; -% EXPECT: valid +% EXPECT: entailed QUERY BVROTR(0bin101,1) = 0bin110; diff --git a/test/regress/regress0/cvc3.userdoc.02.cvc b/test/regress/regress0/cvc3.userdoc.02.cvc index b6329e953..e143ea275 100644 --- a/test/regress/regress0/cvc3.userdoc.02.cvc +++ b/test/regress/regress0/cvc3.userdoc.02.cvc @@ -2,6 +2,6 @@ x : BITVECTOR(5); y : BITVECTOR(4); yy : BITVECTOR(3); -% EXPECT: valid +% EXPECT: entailed QUERY BVPLUS(9, x@0bin0000, (0bin000@(~y)@0bin11))[8:4] = BVPLUS(5, x, ~(y[3:2])) ; diff --git a/test/regress/regress0/cvc3.userdoc.03.cvc b/test/regress/regress0/cvc3.userdoc.03.cvc index 4fc6a5f8c..3b3829ebc 100644 --- a/test/regress/regress0/cvc3.userdoc.03.cvc +++ b/test/regress/regress0/cvc3.userdoc.03.cvc @@ -1,7 +1,7 @@ bv : BITVECTOR(10); a : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY 0bin01100000[5:3]=(0bin1111001@bv[0:0])[4:2] AND diff --git a/test/regress/regress0/cvc3.userdoc.04.cvc b/test/regress/regress0/cvc3.userdoc.04.cvc index 824690bcf..da8b1e82c 100644 --- a/test/regress/regress0/cvc3.userdoc.04.cvc +++ b/test/regress/regress0/cvc3.userdoc.04.cvc @@ -5,5 +5,5 @@ ASSERT x&y&t&z&q = x; ASSERT x|y = t; ASSERT BVXOR(x,~x) = t; -% EXPECT: valid +% EXPECT: entailed QUERY FALSE; diff --git a/test/regress/regress0/cvc3.userdoc.05.cvc b/test/regress/regress0/cvc3.userdoc.05.cvc index 37da96b3c..85e0c2105 100644 --- a/test/regress/regress0/cvc3.userdoc.05.cvc +++ b/test/regress/regress0/cvc3.userdoc.05.cvc @@ -3,7 +3,7 @@ x, y : BITVECTOR(4); ASSERT x = 0hex5; ASSERT y = 0bin0101; -% EXPECT: valid +% EXPECT: entailed QUERY BVMULT(8,x,y)=BVMULT(8,y,x) AND diff --git a/test/regress/regress0/cvc3.userdoc.06.cvc b/test/regress/regress0/cvc3.userdoc.06.cvc index 3801b6d53..3968365ad 100644 --- a/test/regress/regress0/cvc3.userdoc.06.cvc +++ b/test/regress/regress0/cvc3.userdoc.06.cvc @@ -6,8 +6,8 @@ z, t : BITVECTOR(12); ASSERT x = 0hexff; ASSERT z = 0hexff0; -% EXPECT: valid +% EXPECT: entailed QUERY z = x << 4; -% EXPECT: valid +% EXPECT: entailed QUERY (z >> 4)[7:0] = x; diff --git a/test/regress/regress0/datatypes/bug286.cvc b/test/regress/regress0/datatypes/bug286.cvc index 501f5f737..33a320acd 100644 --- a/test/regress/regress0/datatypes/bug286.cvc +++ b/test/regress/regress0/datatypes/bug286.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE foo = f(i:INT) END; x : foo; y : INT; diff --git a/test/regress/regress0/datatypes/datatype-dump.cvc b/test/regress/regress0/datatypes/datatype-dump.cvc index 8e2818106..e28fc6305 100644 --- a/test/regress/regress0/datatypes/datatype-dump.cvc +++ b/test/regress/regress0/datatypes/datatype-dump.cvc @@ -10,7 +10,7 @@ % EXPECT: END; % EXPECT: x : nat; % EXPECT: QUERY NOT(is_succ(x)) AND NOT(is_zero(x)); -% EXPECT: invalid +% EXPECT: not_entailed % DATATYPE nat = succ(pred : nat) | zero END; diff --git a/test/regress/regress0/datatypes/datatype.cvc b/test/regress/regress0/datatypes/datatype.cvc index 20fec4fdd..6a0e97fc3 100644 --- a/test/regress/regress0/datatypes/datatype.cvc +++ b/test/regress/regress0/datatypes/datatype.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE nat = succ(pred : nat) | zero END; diff --git a/test/regress/regress0/datatypes/datatype0.cvc b/test/regress/regress0/datatypes/datatype0.cvc index 8fff50a86..0b9e41024 100644 --- a/test/regress/regress0/datatypes/datatype0.cvc +++ b/test/regress/regress0/datatypes/datatype0.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero END; diff --git a/test/regress/regress0/datatypes/datatype1.cvc b/test/regress/regress0/datatypes/datatype1.cvc index 3934959f1..9e746c0e4 100644 --- a/test/regress/regress0/datatypes/datatype1.cvc +++ b/test/regress/regress0/datatypes/datatype1.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE tree = node(left : tree, right : tree) | leaf diff --git a/test/regress/regress0/datatypes/datatype13.cvc b/test/regress/regress0/datatypes/datatype13.cvc index 91e582dbd..78d28be00 100644 --- a/test/regress/regress0/datatypes/datatype13.cvc +++ b/test/regress/regress0/datatypes/datatype13.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE enum = enum1 | enum2 diff --git a/test/regress/regress0/datatypes/datatype2.cvc b/test/regress/regress0/datatypes/datatype2.cvc index 939dff197..64c84de45 100644 --- a/test/regress/regress0/datatypes/datatype2.cvc +++ b/test/regress/regress0/datatypes/datatype2.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, diff --git a/test/regress/regress0/datatypes/datatype3.cvc b/test/regress/regress0/datatypes/datatype3.cvc index fff1a5dd7..ce99edbb7 100644 --- a/test/regress/regress0/datatypes/datatype3.cvc +++ b/test/regress/regress0/datatypes/datatype3.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE tree = node(left : tree, right : tree) | leaf diff --git a/test/regress/regress0/datatypes/datatype4.cvc b/test/regress/regress0/datatypes/datatype4.cvc index 217777bdf..83962f49a 100644 --- a/test/regress/regress0/datatypes/datatype4.cvc +++ b/test/regress/regress0/datatypes/datatype4.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE TypeGeneric = generic END; diff --git a/test/regress/regress0/datatypes/empty_tuprec.cvc b/test/regress/regress0/datatypes/empty_tuprec.cvc index 4f6320028..b6950845e 100644 --- a/test/regress/regress0/datatypes/empty_tuprec.cvc +++ b/test/regress/regress0/datatypes/empty_tuprec.cvc @@ -7,15 +7,15 @@ b1, b2 : [##]; % empty records (a unit type) c1, c2 : [[]]; % 1-tuples of the empty tuple (a unit type) d1, d2 : [#x:[[##]],y:[#z:[]#]#]; % more complicated records (still a unit type) -% EXPECT: valid +% EXPECT: entailed QUERY a1 = a2; -% EXPECT: valid +% EXPECT: entailed QUERY b1 = b2; -% EXPECT: valid +% EXPECT: entailed QUERY c1 = c2; -% EXPECT: valid +% EXPECT: entailed QUERY d1 = d2; diff --git a/test/regress/regress0/datatypes/mutually-recursive.cvc b/test/regress/regress0/datatypes/mutually-recursive.cvc index b86b647b5..5bd857b6e 100644 --- a/test/regress/regress0/datatypes/mutually-recursive.cvc +++ b/test/regress/regress0/datatypes/mutually-recursive.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat2) | zero, nat2 = succ2(pred2 : nat) | zero2 END; diff --git a/test/regress/regress0/datatypes/rec1.cvc b/test/regress/regress0/datatypes/rec1.cvc index 79c9facda..b3e205985 100644 --- a/test/regress/regress0/datatypes/rec1.cvc +++ b/test/regress/regress0/datatypes/rec1.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed c : BOOLEAN; a17 : BOOLEAN = ((# _a := 2, _b := 2 #) = ( IF c THEN (# _a := 3, _b := 2 #) diff --git a/test/regress/regress0/datatypes/rec2.cvc b/test/regress/regress0/datatypes/rec2.cvc index 44d523a46..56864c088 100644 --- a/test/regress/regress0/datatypes/rec2.cvc +++ b/test/regress/regress0/datatypes/rec2.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed c : BOOLEAN; a16 : [# _a : INT, _b : INT #] = ( IF c THEN (# _a := 3, _b := 2 #) diff --git a/test/regress/regress0/datatypes/rec4.cvc b/test/regress/regress0/datatypes/rec4.cvc index 08a9988ef..f7f29755f 100644 --- a/test/regress/regress0/datatypes/rec4.cvc +++ b/test/regress/regress0/datatypes/rec4.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed a : BOOLEAN; a49 : BOOLEAN = ( IF a THEN (# _a := 1 #) diff --git a/test/regress/regress0/datatypes/rewriter.cvc b/test/regress/regress0/datatypes/rewriter.cvc index 18f8b3441..e9b7662a0 100644 --- a/test/regress/regress0/datatypes/rewriter.cvc +++ b/test/regress/regress0/datatypes/rewriter.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % simple test for rewriter cases diff --git a/test/regress/regress0/datatypes/tuple-record-bug.cvc b/test/regress/regress0/datatypes/tuple-record-bug.cvc index 33c68dece..0e519a64f 100644 --- a/test/regress/regress0/datatypes/tuple-record-bug.cvc +++ b/test/regress/regress0/datatypes/tuple-record-bug.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed Mem_0 : TYPE = ARRAY INT OF INT; diff --git a/test/regress/regress0/datatypes/tuple.cvc b/test/regress/regress0/datatypes/tuple.cvc index 366737525..00ab8d4db 100644 --- a/test/regress/regress0/datatypes/tuple.cvc +++ b/test/regress/regress0/datatypes/tuple.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x: [REAL,INT,REAL] = ( 4/5, 9, 11/9 ); first_elem: REAL = x.0; third_elem: REAL = x.2; diff --git a/test/regress/regress0/datatypes/typed_v10l30054.cvc b/test/regress/regress0/datatypes/typed_v10l30054.cvc index ee414ed65..72c23d888 100644 --- a/test/regress/regress0/datatypes/typed_v10l30054.cvc +++ b/test/regress/regress0/datatypes/typed_v10l30054.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/typed_v1l80005.cvc b/test/regress/regress0/datatypes/typed_v1l80005.cvc index 988646f21..95b1a935f 100644 --- a/test/regress/regress0/datatypes/typed_v1l80005.cvc +++ b/test/regress/regress0/datatypes/typed_v1l80005.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/typed_v2l30079.cvc b/test/regress/regress0/datatypes/typed_v2l30079.cvc index f32c9e551..2295d1518 100644 --- a/test/regress/regress0/datatypes/typed_v2l30079.cvc +++ b/test/regress/regress0/datatypes/typed_v2l30079.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/typed_v3l20092.cvc b/test/regress/regress0/datatypes/typed_v3l20092.cvc index c6260e233..e067852df 100644 --- a/test/regress/regress0/datatypes/typed_v3l20092.cvc +++ b/test/regress/regress0/datatypes/typed_v3l20092.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/typed_v5l30069.cvc b/test/regress/regress0/datatypes/typed_v5l30069.cvc index 05d0247cc..f980acc69 100644 --- a/test/regress/regress0/datatypes/typed_v5l30069.cvc +++ b/test/regress/regress0/datatypes/typed_v5l30069.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/v10l40099.cvc b/test/regress/regress0/datatypes/v10l40099.cvc index 7b2da4b65..257bf2ccc 100644 --- a/test/regress/regress0/datatypes/v10l40099.cvc +++ b/test/regress/regress0/datatypes/v10l40099.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/v2l40025.cvc b/test/regress/regress0/datatypes/v2l40025.cvc index fc466f300..d3411e12e 100644 --- a/test/regress/regress0/datatypes/v2l40025.cvc +++ b/test/regress/regress0/datatypes/v2l40025.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/v3l60006.cvc b/test/regress/regress0/datatypes/v3l60006.cvc index b7019e7ae..ea27672d5 100644 --- a/test/regress/regress0/datatypes/v3l60006.cvc +++ b/test/regress/regress0/datatypes/v3l60006.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/v5l30058.cvc b/test/regress/regress0/datatypes/v5l30058.cvc index be101b8fb..d3db742ae 100644 --- a/test/regress/regress0/datatypes/v5l30058.cvc +++ b/test/regress/regress0/datatypes/v5l30058.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE nat = succ(pred : nat) | zero, list = cons(car : tree, cdr : list) | null, diff --git a/test/regress/regress0/datatypes/wrong-sel-simp.cvc b/test/regress/regress0/datatypes/wrong-sel-simp.cvc index b0dbdc46e..67be78912 100644 --- a/test/regress/regress0/datatypes/wrong-sel-simp.cvc +++ b/test/regress/regress0/datatypes/wrong-sel-simp.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE nat = succ(pred : nat) | zero END; diff --git a/test/regress/regress0/fmf/bug-041417-set-options.cvc b/test/regress/regress0/fmf/bug-041417-set-options.cvc index 16f59f78c..c4e9ba834 100644 --- a/test/regress/regress0/fmf/bug-041417-set-options.cvc +++ b/test/regress/regress0/fmf/bug-041417-set-options.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed OPTION "finite-model-find"; OPTION "fmf-fun"; diff --git a/test/regress/regress0/let.cvc b/test/regress/regress0/let.cvc index 996b09d39..d0f5c5e1f 100644 --- a/test/regress/regress0/let.cvc +++ b/test/regress/regress0/let.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed U: TYPE; a: U; b: U; diff --git a/test/regress/regress0/logops.01.cvc b/test/regress/regress0/logops.01.cvc index 2c2ac2f79..305f2d396 100644 --- a/test/regress/regress0/logops.01.cvc +++ b/test/regress/regress0/logops.01.cvc @@ -1,3 +1,3 @@ a, b, c: BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY (a XOR b) <=> (NOT a AND b) OR (NOT b AND a); diff --git a/test/regress/regress0/logops.02.cvc b/test/regress/regress0/logops.02.cvc index 67415e95d..c74c11983 100644 --- a/test/regress/regress0/logops.02.cvc +++ b/test/regress/regress0/logops.02.cvc @@ -1,3 +1,3 @@ a, b, c: BOOLEAN; -% EXPECT: invalid +% EXPECT: not_entailed QUERY NOT c AND b; diff --git a/test/regress/regress0/logops.03.cvc b/test/regress/regress0/logops.03.cvc index 42298a8f4..1c86ce594 100644 --- a/test/regress/regress0/logops.03.cvc +++ b/test/regress/regress0/logops.03.cvc @@ -1,3 +1,3 @@ a, b, c: BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY (IF c THEN a ELSE b ENDIF) <=> ((c AND a) OR (NOT c AND b)); diff --git a/test/regress/regress0/logops.04.cvc b/test/regress/regress0/logops.04.cvc index 89a9db320..254b0ed87 100644 --- a/test/regress/regress0/logops.04.cvc +++ b/test/regress/regress0/logops.04.cvc @@ -1,3 +1,3 @@ a, b, c: BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY (a => b) <=> (NOT a OR b); diff --git a/test/regress/regress0/logops.05.cvc b/test/regress/regress0/logops.05.cvc index 1ec94e5ae..2897dbc0c 100644 --- a/test/regress/regress0/logops.05.cvc +++ b/test/regress/regress0/logops.05.cvc @@ -1,4 +1,4 @@ a, b, c: BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY TRUE XOR FALSE; diff --git a/test/regress/regress0/nl/ext-rew-aggr-test.smt2 b/test/regress/regress0/nl/ext-rew-aggr-test.smt2 index 47006622d..c540ecbe5 100644 --- a/test/regress/regress0/nl/ext-rew-aggr-test.smt2 +++ b/test/regress/regress0/nl/ext-rew-aggr-test.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --ext-rew-prep --ext-rew-prep-agg --no-new-prop +; COMMAND-LINE: --ext-rew-prep --ext-rew-prep-agg --no-new-prop --nl-ext-tplanes ; EXPECT: sat (set-info :smt-lib-version 2.6) (set-logic QF_NIA) diff --git a/test/regress/regress0/parser/choice.cvc b/test/regress/regress0/parser/choice.cvc new file mode 100644 index 000000000..e0ebac051 --- /dev/null +++ b/test/regress/regress0/parser/choice.cvc @@ -0,0 +1,10 @@ +% EXPECT: sat + +a : INT; +b : INT; +c : INT; + +ASSERT (CHOICE(x: INT): x = a) = 1; +ASSERT (CHOICE(x: INT): x = b) = 2; + +CHECKSAT;
\ No newline at end of file diff --git a/test/regress/regress0/parser/choice.smt2 b/test/regress/regress0/parser/choice.smt2 new file mode 100644 index 000000000..19763e222 --- /dev/null +++ b/test/regress/regress0/parser/choice.smt2 @@ -0,0 +1,10 @@ +(set-logic ALL) +(set-info :status sat) +(declare-fun a () Int) +(declare-fun b () Int) +(declare-fun c () Int) +(assert (= (choice ((x Int)) (= x a)) 1)) +(assert (= (choice ((x Int)) (= x b)) 2)) +;(assert (let ((x (choice ((x Int)) true))) (and (distinct a b x)(= x c)))) +(check-sat) + diff --git a/test/regress/regress0/precedence/and-not.cvc b/test/regress/regress0/precedence/and-not.cvc index 5115a90c1..3ede8d211 100644 --- a/test/regress/regress0/precedence/and-not.cvc +++ b/test/regress/regress0/precedence/and-not.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of AND and NOT. A, B: BOOLEAN; diff --git a/test/regress/regress0/precedence/and-xor.cvc b/test/regress/regress0/precedence/and-xor.cvc index 879becbbf..ec69087d1 100644 --- a/test/regress/regress0/precedence/and-xor.cvc +++ b/test/regress/regress0/precedence/and-xor.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of XOR and AND. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/bool-cmp.cvc b/test/regress/regress0/precedence/bool-cmp.cvc index b8729e92a..4f81c86ad 100644 --- a/test/regress/regress0/precedence/bool-cmp.cvc +++ b/test/regress/regress0/precedence/bool-cmp.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of comparisons and booleans x , y, z: INT; diff --git a/test/regress/regress0/precedence/cmp-plus.cvc b/test/regress/regress0/precedence/cmp-plus.cvc index a7c07fe30..54ee7b8f8 100644 --- a/test/regress/regress0/precedence/cmp-plus.cvc +++ b/test/regress/regress0/precedence/cmp-plus.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of comparisons and plus/minus x, y, z: INT; diff --git a/test/regress/regress0/precedence/eq-fun.cvc b/test/regress/regress0/precedence/eq-fun.cvc index 9e581d514..af81ea10d 100644 --- a/test/regress/regress0/precedence/eq-fun.cvc +++ b/test/regress/regress0/precedence/eq-fun.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of function application and = T : TYPE; diff --git a/test/regress/regress0/precedence/iff-assoc.cvc b/test/regress/regress0/precedence/iff-assoc.cvc index 745cc7474..4643292b6 100644 --- a/test/regress/regress0/precedence/iff-assoc.cvc +++ b/test/regress/regress0/precedence/iff-assoc.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right associativity of <=> A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/iff-implies.cvc b/test/regress/regress0/precedence/iff-implies.cvc index 947433c88..24181487d 100644 --- a/test/regress/regress0/precedence/iff-implies.cvc +++ b/test/regress/regress0/precedence/iff-implies.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of <=> and =>. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/implies-assoc.cvc b/test/regress/regress0/precedence/implies-assoc.cvc index 1a7cef3b1..c6883c6ad 100644 --- a/test/regress/regress0/precedence/implies-assoc.cvc +++ b/test/regress/regress0/precedence/implies-assoc.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right associativity of => A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/implies-iff.cvc b/test/regress/regress0/precedence/implies-iff.cvc index 3de26eb18..9ebed72d4 100644 --- a/test/regress/regress0/precedence/implies-iff.cvc +++ b/test/regress/regress0/precedence/implies-iff.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of <=> and =>. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/implies-or.cvc b/test/regress/regress0/precedence/implies-or.cvc index d724d33ef..883e7d4a1 100644 --- a/test/regress/regress0/precedence/implies-or.cvc +++ b/test/regress/regress0/precedence/implies-or.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of => and OR. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/not-and.cvc b/test/regress/regress0/precedence/not-and.cvc index fc671d7b5..579531ded 100644 --- a/test/regress/regress0/precedence/not-and.cvc +++ b/test/regress/regress0/precedence/not-and.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of AND and NOT. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/not-eq.cvc b/test/regress/regress0/precedence/not-eq.cvc index f658c127e..f8c7366be 100644 --- a/test/regress/regress0/precedence/not-eq.cvc +++ b/test/regress/regress0/precedence/not-eq.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of = and NOT. A, B: INT; diff --git a/test/regress/regress0/precedence/or-implies.cvc b/test/regress/regress0/precedence/or-implies.cvc index 209df8559..746f142e4 100644 --- a/test/regress/regress0/precedence/or-implies.cvc +++ b/test/regress/regress0/precedence/or-implies.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of => and OR. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/or-xor.cvc b/test/regress/regress0/precedence/or-xor.cvc index 2a25bac63..405cb68b7 100644 --- a/test/regress/regress0/precedence/or-xor.cvc +++ b/test/regress/regress0/precedence/or-xor.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of OR and XOR. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/plus-mult.cvc b/test/regress/regress0/precedence/plus-mult.cvc index 5d980f90d..57b9b99cf 100644 --- a/test/regress/regress0/precedence/plus-mult.cvc +++ b/test/regress/regress0/precedence/plus-mult.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of plus/minus and mult/divide a, b, c, d, e: INT; diff --git a/test/regress/regress0/precedence/xor-and.cvc b/test/regress/regress0/precedence/xor-and.cvc index 68896db10..08c939c5e 100644 --- a/test/regress/regress0/precedence/xor-and.cvc +++ b/test/regress/regress0/precedence/xor-and.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of XOR and AND. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/xor-assoc.cvc b/test/regress/regress0/precedence/xor-assoc.cvc index f31324a53..5f4646b1f 100644 --- a/test/regress/regress0/precedence/xor-assoc.cvc +++ b/test/regress/regress0/precedence/xor-assoc.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for left associativity of XOR A, B, C: BOOLEAN; diff --git a/test/regress/regress0/precedence/xor-or.cvc b/test/regress/regress0/precedence/xor-or.cvc index 757286764..87abc0e73 100644 --- a/test/regress/regress0/precedence/xor-or.cvc +++ b/test/regress/regress0/precedence/xor-or.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed % Simple test for right precedence of OR and XOR. A, B, C: BOOLEAN; diff --git a/test/regress/regress0/preprocess/preprocess_00.cvc b/test/regress/regress0/preprocess/preprocess_00.cvc index c86843535..2e51a42ad 100644 --- a/test/regress/regress0/preprocess/preprocess_00.cvc +++ b/test/regress/regress0/preprocess/preprocess_00.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed a0, a1, a2, a3, a4, a5, a6, a7, a8, a9: BOOLEAN; diff --git a/test/regress/regress0/preprocess/preprocess_02.cvc b/test/regress/regress0/preprocess/preprocess_02.cvc index e2c93a359..0f94103f6 100644 --- a/test/regress/regress0/preprocess/preprocess_02.cvc +++ b/test/regress/regress0/preprocess/preprocess_02.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed a0, a1, a2, a3, a4, a5, a6, a7, a8, a9: BOOLEAN; diff --git a/test/regress/regress0/preprocess/preprocess_06.cvc b/test/regress/regress0/preprocess/preprocess_06.cvc index 3e45c529a..abcc7a6ac 100644 --- a/test/regress/regress0/preprocess/preprocess_06.cvc +++ b/test/regress/regress0/preprocess/preprocess_06.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed a0, a1, a2, a3, a4, a5: BOOLEAN; diff --git a/test/regress/regress0/preprocess/preprocess_13.cvc b/test/regress/regress0/preprocess/preprocess_13.cvc index 8b26c0d08..9a6cd797c 100644 --- a/test/regress/regress0/preprocess/preprocess_13.cvc +++ b/test/regress/regress0/preprocess/preprocess_13.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed a0, a1, a2, a3, a4, a5, a6, a7, a8, a9: BOOLEAN; diff --git a/test/regress/regress0/printer/tuples_and_records.cvc b/test/regress/regress0/printer/tuples_and_records.cvc index 267a316e8..966668002 100644 --- a/test/regress/regress0/printer/tuples_and_records.cvc +++ b/test/regress/regress0/printer/tuples_and_records.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed % EXPECT: ((r.a, "active")) % EXPECT: ((y.1, 9)) OPTION "produce-models"; diff --git a/test/regress/regress0/push-pop/bug233.cvc b/test/regress/regress0/push-pop/bug233.cvc index 2b9eedcdb..1a6049329 100644 --- a/test/regress/regress0/push-pop/bug233.cvc +++ b/test/regress/regress0/push-pop/bug233.cvc @@ -3,9 +3,9 @@ a, b: BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY (a AND b) OR NOT (a AND b); -% EXPECT: invalid +% EXPECT: not_entailed QUERY (a OR b); diff --git a/test/regress/regress0/push-pop/incremental-subst-bug.cvc b/test/regress/regress0/push-pop/incremental-subst-bug.cvc index 9b10ef843..657e74486 100644 --- a/test/regress/regress0/push-pop/incremental-subst-bug.cvc +++ b/test/regress/regress0/push-pop/incremental-subst-bug.cvc @@ -1,21 +1,21 @@ % COMMAND-LINE: --incremental U : TYPE; x, y : U; -% EXPECT: invalid +% EXPECT: not_entailed QUERY x = y; ASSERT x = y; -% EXPECT: valid +% EXPECT: entailed QUERY x = y; PUSH; z : U; -% EXPECT: valid +% EXPECT: entailed QUERY x = y; -% EXPECT: invalid +% EXPECT: not_entailed QUERY x = z; -% EXPECT: invalid +% EXPECT: not_entailed QUERY z = x; -% EXPECT: invalid +% EXPECT: not_entailed QUERY z /= x; POP; -% EXPECT: invalid +% EXPECT: not_entailed QUERY z /= x; diff --git a/test/regress/regress0/quantifiers/cegqi-nl-simp.cvc b/test/regress/regress0/quantifiers/cegqi-nl-simp.cvc index 63cf52fd6..d01d7b7d2 100644 --- a/test/regress/regress0/quantifiers/cegqi-nl-simp.cvc +++ b/test/regress/regress0/quantifiers/cegqi-nl-simp.cvc @@ -1,2 +1,2 @@ -% EXPECT: valid +% EXPECT: entailed QUERY FORALL (x:INT) : EXISTS (y:INT) : (x*y=x) ; diff --git a/test/regress/regress0/sets/cvc-sample.cvc b/test/regress/regress0/sets/cvc-sample.cvc index 6740faa8c..06d2b5049 100644 --- a/test/regress/regress0/sets/cvc-sample.cvc +++ b/test/regress/regress0/sets/cvc-sample.cvc @@ -4,7 +4,7 @@ % EXPECT: unsat % EXPECT: unsat % EXPECT: unsat -% EXPECT: invalid +% EXPECT: not_entailed OPTION "incremental" true; OPTION "logic" "ALL_SUPPORTED"; SetInt : TYPE = SET OF INT; diff --git a/test/regress/regress0/simple.cvc b/test/regress/regress0/simple.cvc index 83d0225bd..def48254e 100644 --- a/test/regress/regress0/simple.cvc +++ b/test/regress/regress0/simple.cvc @@ -3,5 +3,5 @@ ASSERT x1 OR NOT x0; ASSERT x0 OR NOT x3; ASSERT x3 OR x2; ASSERT x1 AND NOT x1; -% EXPECT: valid +% EXPECT: entailed QUERY x2; diff --git a/test/regress/regress0/smallcnf.cvc b/test/regress/regress0/smallcnf.cvc index bd732b4dc..dcb7c6f0d 100644 --- a/test/regress/regress0/smallcnf.cvc +++ b/test/regress/regress0/smallcnf.cvc @@ -4,6 +4,6 @@ ASSERT NOT a OR NOT b; ASSERT c OR b OR a; ASSERT b OR NOT a; ASSERT a OR NOT b OR c; -% EXPECT: invalid +% EXPECT: not_entailed QUERY FALSE; diff --git a/test/regress/regress0/smtlib/issue4151.smt2 b/test/regress/regress0/smtlib/issue4151.smt2 new file mode 100644 index 000000000..629ec48b6 --- /dev/null +++ b/test/regress/regress0/smtlib/issue4151.smt2 @@ -0,0 +1,13 @@ +; EXPECT: sat +; EXPECT: unsat +; EXPECT: ( +; EXPECT: ) +(set-logic ALL) +(set-option :incremental true) +(set-option :produce-unsat-assumptions true) +(set-option :produce-unsat-cores true) +(check-sat) +(reset-assertions) +(assert false) +(check-sat) +(get-unsat-core) diff --git a/test/regress/regress0/smtlib/set-info-status.smt2 b/test/regress/regress0/smtlib/set-info-status.smt2 index 489d686b3..4bfa1766a 100644 --- a/test/regress/regress0/smtlib/set-info-status.smt2 +++ b/test/regress/regress0/smtlib/set-info-status.smt2 @@ -1,4 +1,4 @@ -; EXPECT: (error "Cannot get an unsat core unless immediately preceded by UNSAT/VALID response.") +; EXPECT: (error "Cannot get an unsat core unless immediately preceded by UNSAT/ENTAILED response.") ; EXPECT: sat ; EXPECT: sat ; EXPECT: unsat diff --git a/test/regress/regress0/strings/bug002.smt2 b/test/regress/regress0/strings/bug002.smt2 index fd60089fd..5bf21ebb9 100644 --- a/test/regress/regress0/strings/bug002.smt2 +++ b/test/regress/regress0/strings/bug002.smt2 @@ -4,7 +4,7 @@ (set-info :status sat) ; regex = [\*-,\t\*-\|](.{6,}()?)+ -(define-fun strinre ((?s String)) Bool (str.in.re ?s (re.union re.nostr (re.++ (str.to.re "") (str.to.re "") (re.union re.nostr (re.range "*" ",") (str.to.re "\t") (re.range "*" "|") ) (re.+ (re.union re.nostr (re.++ (str.to.re "") (str.to.re "") (re.loop re.allchar 6 ) (re.opt (re.union re.nostr (re.++ (str.to.re "") (str.to.re "") ) ) ) ) ) ) ) ) ) ) +(define-fun strinre ((?s String)) Bool (str.in.re ?s (re.union re.nostr (re.++ (str.to.re "") (str.to.re "") (re.union re.nostr (re.range "*" ",") (str.to.re "\t") (re.range "*" "|") ) (re.+ (re.union re.nostr (re.++ (str.to.re "") (str.to.re "") ((_ re.^ 6) re.allchar) (re.opt (re.union re.nostr (re.++ (str.to.re "") (str.to.re "") ) ) ) ) ) ) ) ) ) ) (assert (not (strinre "6O\1\127\n?"))) (check-sat) diff --git a/test/regress/regress0/strings/char-representations.smt2 b/test/regress/regress0/strings/char-representations.smt2 new file mode 100644 index 000000000..aaf119ab4 --- /dev/null +++ b/test/regress/regress0/strings/char-representations.smt2 @@ -0,0 +1,22 @@ +; COMMAND-LINE: --lang=smt2.6.1 +; EXPECT: sat +(set-logic SLIA) +(set-info :status sat) + +(declare-fun x () String) +(declare-fun y () String) +(declare-fun z () String) + +(assert (= x (_ char #x0D4))) +(assert (= x "\u00d4")) + + +(assert (= y (_ char #x1))) +(assert (= y "\u0001")) + +(assert (= z (_ char #xAF))) +(assert (= z (_ char #x0af))) +(assert (= z "\u{af}")) +(assert (= z "\u00af")) + +(check-sat) diff --git a/test/regress/regress0/strings/gen-esc-seq.smt2 b/test/regress/regress0/strings/gen-esc-seq.smt2 new file mode 100644 index 000000000..59f66046f --- /dev/null +++ b/test/regress/regress0/strings/gen-esc-seq.smt2 @@ -0,0 +1,9 @@ +; COMMAND-LINE: --produce-models --lang=smt2.6.1 +; EXPECT: sat +; EXPECT: ((x "\u{5c}u1000")) +(set-logic ALL) +(set-info :status sat) +(declare-const x String) +(assert (= x (str.++ "\u" "1000"))) +(check-sat) +(get-value (x)) diff --git a/test/regress/regress0/strings/loop-wrong-sem.smt2 b/test/regress/regress0/strings/loop-wrong-sem.smt2 new file mode 100644 index 000000000..d0dd3fcb2 --- /dev/null +++ b/test/regress/regress0/strings/loop-wrong-sem.smt2 @@ -0,0 +1,4 @@ +(set-logic ALL) +(set-info :status unsat) +(assert (str.in.re "" ((_ re.loop 1 0) (str.to.re "")))) +(check-sat) diff --git a/test/regress/regress0/strings/model-code-point.smt2 b/test/regress/regress0/strings/model-code-point.smt2 new file mode 100644 index 000000000..1200ae704 --- /dev/null +++ b/test/regress/regress0/strings/model-code-point.smt2 @@ -0,0 +1,13 @@ +; COMMAND-LINE: --lang=smt2.6.1 --produce-models +; EXPECT: sat +; EXPECT: ((x "\u{a}")) +; EXPECT: ((y "\u{7f}")) +(set-logic ALL) +(set-info :status sat) +(declare-fun x () String) +(declare-fun y () String) +(assert (= (str.to_code x) 10)) +(assert (= (str.to_code y) 127)) +(check-sat) +(get-value (x)) +(get-value (y)) diff --git a/test/regress/regress0/strings/model-friendly.smt2 b/test/regress/regress0/strings/model-friendly.smt2 new file mode 100644 index 000000000..985ffaa62 --- /dev/null +++ b/test/regress/regress0/strings/model-friendly.smt2 @@ -0,0 +1,9 @@ +; COMMAND-LINE: --lang=smt2.6.1 --produce-models +; EXPECT: sat +; EXPECT: ((x "AAAAA")) +(set-logic ALL) +(set-info :status sat) +(declare-fun x () String) +(assert (= (str.len x) 5)) +(check-sat) +(get-value (x)) diff --git a/test/regress/regress0/strings/unicode-esc.smt2 b/test/regress/regress0/strings/unicode-esc.smt2 new file mode 100644 index 000000000..02b313d4a --- /dev/null +++ b/test/regress/regress0/strings/unicode-esc.smt2 @@ -0,0 +1,32 @@ +; COMMAND-LINE: --strings-exp --lang=smt2.6.1 +; EXPECT: sat +(set-logic ALL) + +(assert (= "\u{14}" "\u0014")) +(assert (= "\u{00}" "\u{0}")) +(assert (= "\u0000" "\u{0}")) +(assert (= (str.len "\u1234") 1)) +(assert (= (str.len "\u{1}") 1)) +(assert (= (str.len "\u{99}") 1)) +(assert (= (str.len "\u{779}") 1)) +(assert (= (str.len "\u{0779}") 1)) +(assert (= (str.len "\u{01779}") 1)) +(assert (= (str.len "\u{001779}") 10)) +(assert (= (str.len "\u{0vv79}") 9)) +(assert (= (str.len "\u{11\u1234}") 7)) +(assert (= (str.len "\u12345") 2)) +(assert (= (str.len "\uu") 3)) +(assert (= (str.len "\u{123}\u{567}") 2)) +(assert (= (str.len "\u{0017") 7)) +(assert (= (str.len "\\u00178") 3)) +(assert (= (str.len "2\u{}") 5)) +(assert (= (str.len "\uaaaa") 1)) +(assert (= (str.len "\uAAAA") 1)) +(assert (= (str.len "\u{0AbC}") 1)) +(assert (= (str.len "\u{E}") 1)) +(assert (= (str.len "\u{44444}") 9)) +(assert (= (str.len "\u") 2)) +(assert (= (str.len "\u001") 5)) +(assert (= (str.len "\u0001") 1)) + +(check-sat) diff --git a/test/regress/regress0/sygus/c100.sy b/test/regress/regress0/sygus/c100.sy index ef124c953..994fb6de3 100644 --- a/test/regress/regress0/sygus/c100.sy +++ b/test/regress/regress0/sygus/c100.sy @@ -1,9 +1,10 @@ ; EXPECT: unsat -; COMMAND-LINE: --cegqi-si=all --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --sygus-out=status (set-logic LIA) (synth-fun constant ((x Int)) Int + ((Start Int)) ((Start Int (0 2 3 @@ -15,4 +16,3 @@ (declare-var x Int) (constraint (= (constant x) 100)) (check-synth) - diff --git a/test/regress/regress0/sygus/check-generic-red.sy b/test/regress/regress0/sygus/check-generic-red.sy index e169e1a5c..d593a7d9e 100644 --- a/test/regress/regress0/sygus/check-generic-red.sy +++ b/test/regress/regress0/sygus/check-generic-red.sy @@ -1,8 +1,9 @@ ; EXPECT: unsat -; COMMAND-LINE: --cegqi-si=all --sygus-out=status --decision=justification +; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --sygus-out=status --decision=justification (set-logic LIA) (synth-fun P ((x Int) (y Int)) Bool + ((Start Bool) (StartIntC Int) (StartInt Int)) ((Start Bool ((and Start Start) (not Start) (<= StartInt StartIntC) diff --git a/test/regress/regress0/sygus/const-var-test.sy b/test/regress/regress0/sygus/const-var-test.sy index 305f5783a..31e88f523 100644 --- a/test/regress/regress0/sygus/const-var-test.sy +++ b/test/regress/regress0/sygus/const-var-test.sy @@ -1,9 +1,10 @@ ; EXPECT: unsat -; COMMAND-LINE: --cegqi-si=all --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --sygus-out=status (set-logic LIA) (synth-fun max2 ((x Int) (y Int)) Int + ((Start Int) (StartBool Bool)) ((Start Int ((Variable Int) (Constant Int) (+ Start Start) @@ -21,6 +22,4 @@ (constraint (= (max2 x y) (+ x y 500))) - (check-synth) - diff --git a/test/regress/regress0/sygus/sygus-uf.sy b/test/regress/regress0/sygus/sygus-uf.sy index d506dd5b2..b08aa8929 100644 --- a/test/regress/regress0/sygus/sygus-uf.sy +++ b/test/regress/regress0/sygus/sygus-uf.sy @@ -1,10 +1,11 @@ -; COMMAND-LINE: --sygus-out=status --uf-ho +; COMMAND-LINE: --lang=sygus2 --sygus-out=status --uf-ho ; EXPECT: unsat -(set-logic UFLIA) +(set-logic ALL) -(declare-fun uf (Int) Int) +(declare-var uf (-> Int Int)) (synth-fun f ((x Int) (y Int)) Bool + ((Start Bool) (IntExpr Int)) ((Start Bool (true false (<= IntExpr IntExpr) (= IntExpr IntExpr) diff --git a/test/regress/regress0/test11.cvc b/test/regress/regress0/test11.cvc index 45052deeb..26dda442e 100644 --- a/test/regress/regress0/test11.cvc +++ b/test/regress/regress0/test11.cvc @@ -3,5 +3,5 @@ x, y : BOOLEAN; ASSERT (x OR y); ASSERT NOT (x OR y); -% EXPECT: valid +% EXPECT: entailed QUERY FALSE; diff --git a/test/regress/regress0/test9.cvc b/test/regress/regress0/test9.cvc index bfe1a3285..7872f5a1a 100644 --- a/test/regress/regress0/test9.cvc +++ b/test/regress/regress0/test9.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed P,Q:BOOLEAN; ASSERT (P OR Q); QUERY (P OR Q); diff --git a/test/regress/regress0/uf/simple.01.cvc b/test/regress/regress0/uf/simple.01.cvc index 42b99cc44..6b0f93324 100644 --- a/test/regress/regress0/uf/simple.01.cvc +++ b/test/regress/regress0/uf/simple.01.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed A: TYPE; B: TYPE; x, y: A; diff --git a/test/regress/regress0/uf/simple.02.cvc b/test/regress/regress0/uf/simple.02.cvc index 1dd96fd1c..2bd0b1e1e 100644 --- a/test/regress/regress0/uf/simple.02.cvc +++ b/test/regress/regress0/uf/simple.02.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed A: TYPE; B: TYPE; x, y: A; diff --git a/test/regress/regress0/uf/simple.03.cvc b/test/regress/regress0/uf/simple.03.cvc index cc1721ca6..15fe5907c 100644 --- a/test/regress/regress0/uf/simple.03.cvc +++ b/test/regress/regress0/uf/simple.03.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed A: TYPE; B: TYPE; x, y: A; diff --git a/test/regress/regress0/uf/simple.04.cvc b/test/regress/regress0/uf/simple.04.cvc index 66223ca7b..0fc52bcca 100644 --- a/test/regress/regress0/uf/simple.04.cvc +++ b/test/regress/regress0/uf/simple.04.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed A: TYPE; B: TYPE; x, y: A; diff --git a/test/regress/regress0/uf20-03.cvc b/test/regress/regress0/uf20-03.cvc index 9de754284..b25d8a471 100644 --- a/test/regress/regress0/uf20-03.cvc +++ b/test/regress/regress0/uf20-03.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x_1 : BOOLEAN; x_2 : BOOLEAN; x_3 : BOOLEAN; diff --git a/test/regress/regress0/wiki.01.cvc b/test/regress/regress0/wiki.01.cvc index fb38fab65..1516503ff 100644 --- a/test/regress/regress0/wiki.01.cvc +++ b/test/regress/regress0/wiki.01.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR (b OR c) <=> (a OR b) OR c; diff --git a/test/regress/regress0/wiki.02.cvc b/test/regress/regress0/wiki.02.cvc index 93d555c96..ebeadee14 100644 --- a/test/regress/regress0/wiki.02.cvc +++ b/test/regress/regress0/wiki.02.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND (b AND c) <=> (a AND b) AND c; diff --git a/test/regress/regress0/wiki.03.cvc b/test/regress/regress0/wiki.03.cvc index 08b049c17..ca0e6a8d1 100644 --- a/test/regress/regress0/wiki.03.cvc +++ b/test/regress/regress0/wiki.03.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR b <=> b OR a; diff --git a/test/regress/regress0/wiki.04.cvc b/test/regress/regress0/wiki.04.cvc index b88de6144..75fe17238 100644 --- a/test/regress/regress0/wiki.04.cvc +++ b/test/regress/regress0/wiki.04.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND b <=> b AND a; diff --git a/test/regress/regress0/wiki.05.cvc b/test/regress/regress0/wiki.05.cvc index 0fe647f7b..2f87e4ca0 100644 --- a/test/regress/regress0/wiki.05.cvc +++ b/test/regress/regress0/wiki.05.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR (a AND b) <=> a; diff --git a/test/regress/regress0/wiki.06.cvc b/test/regress/regress0/wiki.06.cvc index 1d466a86e..a4075bda3 100644 --- a/test/regress/regress0/wiki.06.cvc +++ b/test/regress/regress0/wiki.06.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND (a OR b) <=> a; diff --git a/test/regress/regress0/wiki.07.cvc b/test/regress/regress0/wiki.07.cvc index 146d92832..3b4527bdc 100644 --- a/test/regress/regress0/wiki.07.cvc +++ b/test/regress/regress0/wiki.07.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR (b AND c) <=> (a OR b) AND (a OR c); diff --git a/test/regress/regress0/wiki.08.cvc b/test/regress/regress0/wiki.08.cvc index e9c7d3fa3..e15a21412 100644 --- a/test/regress/regress0/wiki.08.cvc +++ b/test/regress/regress0/wiki.08.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND (b OR c) <=> (a AND b) OR (a AND c); diff --git a/test/regress/regress0/wiki.09.cvc b/test/regress/regress0/wiki.09.cvc index 478be2db9..5bc5faca5 100644 --- a/test/regress/regress0/wiki.09.cvc +++ b/test/regress/regress0/wiki.09.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR NOT a; diff --git a/test/regress/regress0/wiki.10.cvc b/test/regress/regress0/wiki.10.cvc index 226a3da82..613022e84 100644 --- a/test/regress/regress0/wiki.10.cvc +++ b/test/regress/regress0/wiki.10.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND NOT a <=> FALSE; diff --git a/test/regress/regress0/wiki.11.cvc b/test/regress/regress0/wiki.11.cvc index d615fef3b..8debef9cd 100644 --- a/test/regress/regress0/wiki.11.cvc +++ b/test/regress/regress0/wiki.11.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR a <=> a; diff --git a/test/regress/regress0/wiki.12.cvc b/test/regress/regress0/wiki.12.cvc index 209e512a6..2274fc78e 100644 --- a/test/regress/regress0/wiki.12.cvc +++ b/test/regress/regress0/wiki.12.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND a <=> a; diff --git a/test/regress/regress0/wiki.13.cvc b/test/regress/regress0/wiki.13.cvc index 2cc69f048..80b95802d 100644 --- a/test/regress/regress0/wiki.13.cvc +++ b/test/regress/regress0/wiki.13.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR FALSE <=> a; diff --git a/test/regress/regress0/wiki.14.cvc b/test/regress/regress0/wiki.14.cvc index 5a6c16248..7b8a14edb 100644 --- a/test/regress/regress0/wiki.14.cvc +++ b/test/regress/regress0/wiki.14.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND TRUE <=> a; diff --git a/test/regress/regress0/wiki.15.cvc b/test/regress/regress0/wiki.15.cvc index 6dc84f679..dfb4f13fc 100644 --- a/test/regress/regress0/wiki.15.cvc +++ b/test/regress/regress0/wiki.15.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a OR TRUE <=> TRUE; diff --git a/test/regress/regress0/wiki.16.cvc b/test/regress/regress0/wiki.16.cvc index 6b2bf4113..9c69baa52 100644 --- a/test/regress/regress0/wiki.16.cvc +++ b/test/regress/regress0/wiki.16.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY a AND FALSE <=> FALSE; diff --git a/test/regress/regress0/wiki.17.cvc b/test/regress/regress0/wiki.17.cvc index 7c6701acc..c58160bb9 100644 --- a/test/regress/regress0/wiki.17.cvc +++ b/test/regress/regress0/wiki.17.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY NOT FALSE <=> TRUE; diff --git a/test/regress/regress0/wiki.18.cvc b/test/regress/regress0/wiki.18.cvc index 7c1b1b8e4..17773b899 100644 --- a/test/regress/regress0/wiki.18.cvc +++ b/test/regress/regress0/wiki.18.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY NOT TRUE <=> FALSE; diff --git a/test/regress/regress0/wiki.19.cvc b/test/regress/regress0/wiki.19.cvc index d5812b5ea..46b6fc02e 100644 --- a/test/regress/regress0/wiki.19.cvc +++ b/test/regress/regress0/wiki.19.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY NOT (a OR b) <=> NOT a AND NOT b; diff --git a/test/regress/regress0/wiki.20.cvc b/test/regress/regress0/wiki.20.cvc index 8d2570620..42e114010 100644 --- a/test/regress/regress0/wiki.20.cvc +++ b/test/regress/regress0/wiki.20.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY NOT (a AND b) <=> NOT a OR NOT b; diff --git a/test/regress/regress0/wiki.21.cvc b/test/regress/regress0/wiki.21.cvc index d65cbcf65..bcc69beea 100644 --- a/test/regress/regress0/wiki.21.cvc +++ b/test/regress/regress0/wiki.21.cvc @@ -1,4 +1,4 @@ a, b, c : BOOLEAN; -% EXPECT: valid +% EXPECT: entailed QUERY NOT NOT a <=> a; diff --git a/test/regress/regress1/arith/arith-brab-test.smt2 b/test/regress/regress1/arith/arith-brab-test.smt2 new file mode 100644 index 000000000..7856ae0e6 --- /dev/null +++ b/test/regress/regress1/arith/arith-brab-test.smt2 @@ -0,0 +1,23 @@ +; COMMAND-LINE: --arith-brab +; COMMAND-LINE: --no-arith-brab +; EXPECT: sat +(set-logic ALL) + +(declare-fun x1 () Real) +(declare-fun y1 () Real) +(declare-fun m1 () Real) +(declare-fun b1 () Real) + +(declare-fun x () Int) +(declare-fun y () Int) + +(assert (= y1 (+ b1 (* m1 x1)))) +(assert (= x1 (/ m1 (- y1 b1)))) +(assert (= b1 1.25)) +(assert (= m1 (/ 1 3))) + +(assert (and (> x x1) (> y y1))) + +(check-sat) +(exit) + diff --git a/test/regress/regress1/arith/arith-int-001.cvc b/test/regress/regress1/arith/arith-int-001.cvc index 03ed1a6ae..3fd528c11 100644 --- a/test/regress/regress1/arith/arith-int-001.cvc +++ b/test/regress/regress1/arith/arith-int-001.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-23 * x0) + (-23 * x1) + (5 * x2) + (-17 * x3) = 7 ; ASSERT (-14 * x0) + (-14 * x1) + (19 * x2) + (-24 * x3) = 29 ; diff --git a/test/regress/regress1/arith/arith-int-002.cvc b/test/regress/regress1/arith/arith-int-002.cvc index 849daba79..6cc4b2c5e 100644 --- a/test/regress/regress1/arith/arith-int-002.cvc +++ b/test/regress/regress1/arith/arith-int-002.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (17 * x0) + (-23 * x1) + (2 * x2) + (-19 * x3) = -18 ; ASSERT (25 * x0) + (23 * x1) + (21 * x2) + (20 * x3) = 2 ; diff --git a/test/regress/regress1/arith/arith-int-003.cvc b/test/regress/regress1/arith/arith-int-003.cvc index 9c060c469..f294babe6 100644 --- a/test/regress/regress1/arith/arith-int-003.cvc +++ b/test/regress/regress1/arith/arith-int-003.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (17 * x0) + (-7 * x1) + (15 * x2) + (21 * x3) = 19 ; ASSERT (6 * x0) + (-24 * x1) + (25 * x2) + (-18 * x3) > -25 ; diff --git a/test/regress/regress1/arith/arith-int-004.cvc b/test/regress/regress1/arith/arith-int-004.cvc index 314b76d18..15b060d92 100644 --- a/test/regress/regress1/arith/arith-int-004.cvc +++ b/test/regress/regress1/arith/arith-int-004.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (12 * x0) + (-25 * x1) + (21 * x2) + (7 * x3) < 27 ; diff --git a/test/regress/regress1/arith/arith-int-005.cvc b/test/regress/regress1/arith/arith-int-005.cvc index 9b9776ad3..3701d60b4 100644 --- a/test/regress/regress1/arith/arith-int-005.cvc +++ b/test/regress/regress1/arith/arith-int-005.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (13 * x0) + (0 * x1) + (6 * x2) + (-30 * x3) = -16 ; ASSERT (-4 * x0) + (-8 * x1) + (14 * x2) + (-8 * x3) = -11 ; diff --git a/test/regress/regress1/arith/arith-int-006.cvc b/test/regress/regress1/arith/arith-int-006.cvc index 999b4a5b4..53a80310a 100644 --- a/test/regress/regress1/arith/arith-int-006.cvc +++ b/test/regress/regress1/arith/arith-int-006.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-7 * x0) + (-28 * x1) + (8 * x2) + (29 * x3) = -18 ; ASSERT (11 * x0) + (2 * x1) + (4 * x2) + (23 * x3) = 6 ; diff --git a/test/regress/regress1/arith/arith-int-007.cvc b/test/regress/regress1/arith/arith-int-007.cvc index 4cb4d88ef..c0732e2b2 100644 --- a/test/regress/regress1/arith/arith-int-007.cvc +++ b/test/regress/regress1/arith/arith-int-007.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-19 * x0) + (17 * x1) + (30 * x2) + (-31 * x3) <= -20 ; ASSERT (-3 * x0) + (16 * x1) + (20 * x2) + (-25 * x3) < 28 ; diff --git a/test/regress/regress1/arith/arith-int-008.cvc b/test/regress/regress1/arith/arith-int-008.cvc index 1ae22c993..1810d6f28 100644 --- a/test/regress/regress1/arith/arith-int-008.cvc +++ b/test/regress/regress1/arith/arith-int-008.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-12 * x0) + (-15 * x1) + (-31 * x2) + (17 * x3) = -16 ; ASSERT (11 * x0) + (-5 * x1) + (-8 * x2) + (-17 * x3) > -4 ; diff --git a/test/regress/regress1/arith/arith-int-009.cvc b/test/regress/regress1/arith/arith-int-009.cvc index 9bd7a2ce4..14b26da6c 100644 --- a/test/regress/regress1/arith/arith-int-009.cvc +++ b/test/regress/regress1/arith/arith-int-009.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-16 * x0) + (-21 * x1) + (32 * x2) + (32 * x3) = -19 ; ASSERT (-10 * x0) + (-21 * x1) + (13 * x2) + (-7 * x3) = 2 ; diff --git a/test/regress/regress1/arith/arith-int-010.cvc b/test/regress/regress1/arith/arith-int-010.cvc index 4ac85a984..aa649ba4a 100644 --- a/test/regress/regress1/arith/arith-int-010.cvc +++ b/test/regress/regress1/arith/arith-int-010.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (19 * x0) + (-2 * x1) + (-29 * x2) + (-24 * x3) = 3 ; ASSERT (3 * x0) + (11 * x1) + (-14 * x2) + (6 * x3) = 4 ; diff --git a/test/regress/regress1/arith/arith-int-011.cvc b/test/regress/regress1/arith/arith-int-011.cvc index bd2fa2a0d..7de68533f 100644 --- a/test/regress/regress1/arith/arith-int-011.cvc +++ b/test/regress/regress1/arith/arith-int-011.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (13 * x0) + (-1 * x1) + (11 * x2) + (10 * x3) = 9 ; ASSERT (-7 * x0) + (3 * x1) + (-22 * x2) + (16 * x3) >= 9; diff --git a/test/regress/regress1/arith/arith-int-012.cvc b/test/regress/regress1/arith/arith-int-012.cvc index 11b0dab27..10922dd89 100644 --- a/test/regress/regress1/arith/arith-int-012.cvc +++ b/test/regress/regress1/arith/arith-int-012.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (18 * x0) + (32 * x1) + (-11 * x2) + (18 * x3) < -25 ; ASSERT (-31 * x0) + (16 * x1) + (24 * x2) + (9 * x3) >= -24; diff --git a/test/regress/regress1/arith/arith-int-013.cvc b/test/regress/regress1/arith/arith-int-013.cvc index 329251cae..8a1f76add 100644 --- a/test/regress/regress1/arith/arith-int-013.cvc +++ b/test/regress/regress1/arith/arith-int-013.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-22 * x0) + (-14 * x1) + (4 * x2) + (-12 * x3) > 25 ; ASSERT (14 * x0) + (11 * x1) + (32 * x2) + (-8 * x3) >= 2; diff --git a/test/regress/regress1/arith/arith-int-016.cvc b/test/regress/regress1/arith/arith-int-016.cvc index 6774dd2d1..951650461 100644 --- a/test/regress/regress1/arith/arith-int-016.cvc +++ b/test/regress/regress1/arith/arith-int-016.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-13 * x0) + (-4 * x1) + (-20 * x2) + (-26 * x3) = 2 ; ASSERT (13 * x0) + (13 * x1) + (-14 * x2) + (26 * x3) = -8 ; diff --git a/test/regress/regress1/arith/arith-int-017.cvc b/test/regress/regress1/arith/arith-int-017.cvc index e9a06125a..48287249f 100644 --- a/test/regress/regress1/arith/arith-int-017.cvc +++ b/test/regress/regress1/arith/arith-int-017.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (23 * x0) + (-4 * x1) + (-26 * x2) + (-1 * x3) = 10 ; ASSERT (15 * x0) + (31 * x1) + (31 * x2) + (31 * x3) = 13 ; diff --git a/test/regress/regress1/arith/arith-int-018.cvc b/test/regress/regress1/arith/arith-int-018.cvc index 4cb97b77e..cae6fed72 100644 --- a/test/regress/regress1/arith/arith-int-018.cvc +++ b/test/regress/regress1/arith/arith-int-018.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-11 * x0) + (-26 * x1) + (9 * x2) + (32 * x3) = -11 ; ASSERT (-5 * x0) + (-11 * x1) + (-10 * x2) + (-31 * x3) = -23 ; diff --git a/test/regress/regress1/arith/arith-int-019.cvc b/test/regress/regress1/arith/arith-int-019.cvc index cf9ae2d70..a26bbac01 100644 --- a/test/regress/regress1/arith/arith-int-019.cvc +++ b/test/regress/regress1/arith/arith-int-019.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (25 * x0) + (6 * x1) + (-30 * x2) + (29 * x3) = -5 ; ASSERT (14 * x0) + (16 * x1) + (24 * x2) + (-7 * x3) <= 31 ; diff --git a/test/regress/regress1/arith/arith-int-020.cvc b/test/regress/regress1/arith/arith-int-020.cvc index 07a827465..c1416b38f 100644 --- a/test/regress/regress1/arith/arith-int-020.cvc +++ b/test/regress/regress1/arith/arith-int-020.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-32 * x0) + (31 * x1) + (-32 * x2) + (-21 * x3) = 5 ; ASSERT (32 * x0) + (5 * x1) + (23 * x2) + (-16 * x3) = 8 ; diff --git a/test/regress/regress1/arith/arith-int-022.cvc b/test/regress/regress1/arith/arith-int-022.cvc index 584348da4..4612f72c9 100644 --- a/test/regress/regress1/arith/arith-int-022.cvc +++ b/test/regress/regress1/arith/arith-int-022.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-24 * x0) + (25 * x1) + (-28 * x2) + (31 * x3) > 18; QUERY FALSE; diff --git a/test/regress/regress1/arith/arith-int-024.cvc b/test/regress/regress1/arith/arith-int-024.cvc index f57136dd1..73ae7c4ad 100644 --- a/test/regress/regress1/arith/arith-int-024.cvc +++ b/test/regress/regress1/arith/arith-int-024.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (4 * x0) + (8 * x1) + (27 * x2) + (-12 * x3) = -5; QUERY FALSE; diff --git a/test/regress/regress1/arith/arith-int-026.cvc b/test/regress/regress1/arith/arith-int-026.cvc index 9e69aa2d1..52f2478e0 100644 --- a/test/regress/regress1/arith/arith-int-026.cvc +++ b/test/regress/regress1/arith/arith-int-026.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (22 * x0) + (25 * x1) + (1 * x2) + (-11 * x3) = 19 ; ASSERT (-10 * x0) + (-27 * x1) + (6 * x2) + (6 * x3) = 28 ; diff --git a/test/regress/regress1/arith/arith-int-027.cvc b/test/regress/regress1/arith/arith-int-027.cvc index b45622fea..6c38642d2 100644 --- a/test/regress/regress1/arith/arith-int-027.cvc +++ b/test/regress/regress1/arith/arith-int-027.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (17 * x0) + (29 * x1) + (-11 * x2) + (24 * x3) = 13 ; ASSERT (16 * x0) + (-20 * x1) + (-5 * x2) + (12 * x3) = 13 ; diff --git a/test/regress/regress1/arith/arith-int-028.cvc b/test/regress/regress1/arith/arith-int-028.cvc index 61fee4203..7e8b78893 100644 --- a/test/regress/regress1/arith/arith-int-028.cvc +++ b/test/regress/regress1/arith/arith-int-028.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-31 * x0) + (-5 * x1) + (-28 * x2) + (16 * x3) = 10 ; ASSERT (3 * x0) + (-20 * x1) + (-11 * x2) + (-2 * x3) = 25 ; diff --git a/test/regress/regress1/arith/arith-int-029.cvc b/test/regress/regress1/arith/arith-int-029.cvc index ee49bbb68..ba49219d8 100644 --- a/test/regress/regress1/arith/arith-int-029.cvc +++ b/test/regress/regress1/arith/arith-int-029.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-29 * x0) + (-17 * x1) + (11 * x2) + (1 * x3) = -15 ; ASSERT (-13 * x0) + (1 * x1) + (-6 * x2) + (-15 * x3) = 32 ; diff --git a/test/regress/regress1/arith/arith-int-030.cvc b/test/regress/regress1/arith/arith-int-030.cvc index 70b6a3785..a6348b107 100644 --- a/test/regress/regress1/arith/arith-int-030.cvc +++ b/test/regress/regress1/arith/arith-int-030.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-13 * x0) + (26 * x1) + (-11 * x2) + (17 * x3) = 17 ; ASSERT (-15 * x0) + (2 * x1) + (-9 * x2) + (17 * x3) = -11 ; diff --git a/test/regress/regress1/arith/arith-int-031.cvc b/test/regress/regress1/arith/arith-int-031.cvc index 86242f7aa..056ab622e 100644 --- a/test/regress/regress1/arith/arith-int-031.cvc +++ b/test/regress/regress1/arith/arith-int-031.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-21 * x0) + (-24 * x1) + (-31 * x2) + (12 * x3) = -10 ; ASSERT (-4 * x0) + (22 * x1) + (9 * x2) + (17 * x3) > -20 ; diff --git a/test/regress/regress1/arith/arith-int-032.cvc b/test/regress/regress1/arith/arith-int-032.cvc index 1ee4c9844..08c29108e 100644 --- a/test/regress/regress1/arith/arith-int-032.cvc +++ b/test/regress/regress1/arith/arith-int-032.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (4 * x0) + (-29 * x1) + (-9 * x2) + (9 * x3) = 8 ; ASSERT (-26 * x0) + (-26 * x1) + (26 * x2) + (-18 * x3) = -20 ; diff --git a/test/regress/regress1/arith/arith-int-033.cvc b/test/regress/regress1/arith/arith-int-033.cvc index 599ba4e9a..8259a7725 100644 --- a/test/regress/regress1/arith/arith-int-033.cvc +++ b/test/regress/regress1/arith/arith-int-033.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-14 * x0) + (16 * x1) + (-16 * x2) + (0 * x3) = -8 ; ASSERT (3 * x0) + (-20 * x1) + (-12 * x2) + (-3 * x3) = -7 ; diff --git a/test/regress/regress1/arith/arith-int-034.cvc b/test/regress/regress1/arith/arith-int-034.cvc index ec615a785..2b5ae4f4f 100644 --- a/test/regress/regress1/arith/arith-int-034.cvc +++ b/test/regress/regress1/arith/arith-int-034.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-20 * x0) + (-5 * x1) + (30 * x2) + (-24 * x3) = 12 ; ASSERT (24 * x0) + (27 * x1) + (18 * x2) + (-5 * x3) = -16 ; diff --git a/test/regress/regress1/arith/arith-int-035.cvc b/test/regress/regress1/arith/arith-int-035.cvc index e7dee2484..1bad259e2 100644 --- a/test/regress/regress1/arith/arith-int-035.cvc +++ b/test/regress/regress1/arith/arith-int-035.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-3 * x0) + (2 * x1) + (17 * x2) + (-4 * x3) = -17 ; ASSERT (5 * x0) + (-4 * x1) + (22 * x2) + (14 * x3) = -15 ; diff --git a/test/regress/regress1/arith/arith-int-036.cvc b/test/regress/regress1/arith/arith-int-036.cvc index 9594f9586..0eb783815 100644 --- a/test/regress/regress1/arith/arith-int-036.cvc +++ b/test/regress/regress1/arith/arith-int-036.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-9 * x0) + (-21 * x1) + (-25 * x2) + (-1 * x3) = -11 ; ASSERT (31 * x0) + (-18 * x1) + (5 * x2) + (-11 * x3) = 10 ; diff --git a/test/regress/regress1/arith/arith-int-037.cvc b/test/regress/regress1/arith/arith-int-037.cvc index 4d4422d3f..c3ed60011 100644 --- a/test/regress/regress1/arith/arith-int-037.cvc +++ b/test/regress/regress1/arith/arith-int-037.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (12 * x0) + (14 * x1) + (-22 * x2) + (-6 * x3) = 29 ; ASSERT (-9 * x0) + (14 * x1) + (-23 * x2) + (-31 * x3) = 4 ; diff --git a/test/regress/regress1/arith/arith-int-038.cvc b/test/regress/regress1/arith/arith-int-038.cvc index 476133b24..52ac2b1e3 100644 --- a/test/regress/regress1/arith/arith-int-038.cvc +++ b/test/regress/regress1/arith/arith-int-038.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-24 * x0) + (25 * x1) + (28 * x2) + (-31 * x3) = -1 ; ASSERT (29 * x0) + (17 * x1) + (-2 * x2) + (-6 * x3) <= 4 ; diff --git a/test/regress/regress1/arith/arith-int-039.cvc b/test/regress/regress1/arith/arith-int-039.cvc index 9e9235ae8..cecb7f085 100644 --- a/test/regress/regress1/arith/arith-int-039.cvc +++ b/test/regress/regress1/arith/arith-int-039.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (22 * x0) + (21 * x1) + (-18 * x2) + (21 * x3) = 30 ; ASSERT (-31 * x0) + (22 * x1) + (-20 * x2) + (18 * x3) = -32 ; diff --git a/test/regress/regress1/arith/arith-int-040.cvc b/test/regress/regress1/arith/arith-int-040.cvc index 68502349f..f2dff7796 100644 --- a/test/regress/regress1/arith/arith-int-040.cvc +++ b/test/regress/regress1/arith/arith-int-040.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-1 * x0) + (-24 * x1) + (3 * x2) + (-8 * x3) > -5 ; ASSERT (29 * x0) + (17 * x1) + (-26 * x2) + (20 * x3) > 11 ; diff --git a/test/regress/regress1/arith/arith-int-041.cvc b/test/regress/regress1/arith/arith-int-041.cvc index a0c2dc0f9..9df03a9bd 100644 --- a/test/regress/regress1/arith/arith-int-041.cvc +++ b/test/regress/regress1/arith/arith-int-041.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-31 * x0) + (8 * x1) + (16 * x2) + (5 * x3) >= 1 ; ASSERT (-30 * x0) + (13 * x1) + (-17 * x2) + (13 * x3) < -24 ; diff --git a/test/regress/regress1/arith/arith-int-043.cvc b/test/regress/regress1/arith/arith-int-043.cvc index 7efea85e5..7a2d6d6af 100644 --- a/test/regress/regress1/arith/arith-int-043.cvc +++ b/test/regress/regress1/arith/arith-int-043.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-21 * x0) + (-23 * x1) + (29 * x2) + (-4 * x3) = 25 ; ASSERT (20 * x0) + (-19 * x1) + (3 * x2) + (-1 * x3) <= -8 ; diff --git a/test/regress/regress1/arith/arith-int-044.cvc b/test/regress/regress1/arith/arith-int-044.cvc index f933b014b..649532a4b 100644 --- a/test/regress/regress1/arith/arith-int-044.cvc +++ b/test/regress/regress1/arith/arith-int-044.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed %%%% down from 24, up from 6, up from 39 x0, x1, x2, x3 : INT; ASSERT (-30 * x0) + (18 * x1) + (17 * x2) + (3 * x3) = 0; diff --git a/test/regress/regress1/arith/arith-int-045.cvc b/test/regress/regress1/arith/arith-int-045.cvc index ca1a12ba6..2c552c915 100644 --- a/test/regress/regress1/arith/arith-int-045.cvc +++ b/test/regress/regress1/arith/arith-int-045.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-22 * x0) + (-5 * x1) + (-5 * x2) + (25 * x3) = 22 ; ASSERT (2 * x0) + (-25 * x1) + (4 * x2) + (-21 * x3) >= 0 ; diff --git a/test/regress/regress1/arith/arith-int-046.cvc b/test/regress/regress1/arith/arith-int-046.cvc index d4d206c6e..acf4dc9a9 100644 --- a/test/regress/regress1/arith/arith-int-046.cvc +++ b/test/regress/regress1/arith/arith-int-046.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (2 * x0) + (-6 * x1) + (14 * x2) + (-24 * x3) > 4 ; ASSERT (-13 * x0) + (-2 * x1) + (-9 * x2) + (-7 * x3) >= 29 ; diff --git a/test/regress/regress1/arith/arith-int-047.cvc b/test/regress/regress1/arith/arith-int-047.cvc index 0763e5dc3..bb1225b9d 100644 --- a/test/regress/regress1/arith/arith-int-047.cvc +++ b/test/regress/regress1/arith/arith-int-047.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-14 * x0) + (27 * x1) + (10 * x2) + (1 * x3) = 10; ASSERT (-29 * x0) + (-26 * x1) + (-16 * x2) + (17 * x3) >= 16 ; diff --git a/test/regress/regress1/arith/arith-int-048.cvc b/test/regress/regress1/arith/arith-int-048.cvc index e7c05332d..ccc84f389 100644 --- a/test/regress/regress1/arith/arith-int-048.cvc +++ b/test/regress/regress1/arith/arith-int-048.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-13 * x0) + (-11 * x1) + (-14 * x2) + (21 * x3) = 6 ; ASSERT (7 * x0) + (5 * x1) + (13 * x2) + (21 * x3) <= 27 ; diff --git a/test/regress/regress1/arith/arith-int-049.cvc b/test/regress/regress1/arith/arith-int-049.cvc index 8eabc78a8..72e3b7f31 100644 --- a/test/regress/regress1/arith/arith-int-049.cvc +++ b/test/regress/regress1/arith/arith-int-049.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-15 * x0) + (-20 * x1) + (-32 * x2) + (-16 * x3) = -19 ; ASSERT (24 * x0) + (23 * x1) + (22 * x2) + (30 * x3) >= 19 ; diff --git a/test/regress/regress1/arith/arith-int-050.cvc b/test/regress/regress1/arith/arith-int-050.cvc index f0ba939b7..21dbfe09a 100644 --- a/test/regress/regress1/arith/arith-int-050.cvc +++ b/test/regress/regress1/arith/arith-int-050.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-20 * x0) + (-19 * x1) + (6 * x2) + (32 * x3) > 16 ; ASSERT (-1 * x0) + (-30 * x1) + (15 * x2) + (7 * x3) < -10 ; diff --git a/test/regress/regress1/arith/arith-int-051.cvc b/test/regress/regress1/arith/arith-int-051.cvc index 9a2497432..68654a7df 100644 --- a/test/regress/regress1/arith/arith-int-051.cvc +++ b/test/regress/regress1/arith/arith-int-051.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-13 * x0) + (7 * x1) + (-3 * x2) + (9 * x3) = -3 ; ASSERT (17 * x0) + (-22 * x1) + (-15 * x2) + (-21 * x3) >= 9 ; diff --git a/test/regress/regress1/arith/arith-int-052.cvc b/test/regress/regress1/arith/arith-int-052.cvc index 83fdc89c8..9c9433ede 100644 --- a/test/regress/regress1/arith/arith-int-052.cvc +++ b/test/regress/regress1/arith/arith-int-052.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-25 * x0) + (-23 * x1) + (11 * x2) + (10 * x3) = 7 ; ASSERT (32 * x0) + (-15 * x1) + (-1 * x2) + (29 * x3) > -25 ; diff --git a/test/regress/regress1/arith/arith-int-053.cvc b/test/regress/regress1/arith/arith-int-053.cvc index fa38fa3da..544d53fb9 100644 --- a/test/regress/regress1/arith/arith-int-053.cvc +++ b/test/regress/regress1/arith/arith-int-053.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-21 * x0) + (21 * x1) + (23 * x2) + (-20 * x3) = -8 ; ASSERT (-31 * x0) + (-15 * x1) + (-23 * x2) + (29 * x3) = 17; diff --git a/test/regress/regress1/arith/arith-int-054.cvc b/test/regress/regress1/arith/arith-int-054.cvc index 9b0066966..5b4181a11 100644 --- a/test/regress/regress1/arith/arith-int-054.cvc +++ b/test/regress/regress1/arith/arith-int-054.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-31 * x0) + (-29 * x1) + (6 * x2) + (8 * x3) = -10 ; ASSERT (0 * x0) + (8 * x1) + (-20 * x2) + (12 * x3) = 16 ; diff --git a/test/regress/regress1/arith/arith-int-055.cvc b/test/regress/regress1/arith/arith-int-055.cvc index 9729fb565..fdfa45848 100644 --- a/test/regress/regress1/arith/arith-int-055.cvc +++ b/test/regress/regress1/arith/arith-int-055.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-21 * x0) + (-4 * x1) + (-28 * x2) + (-7 * x3) = -23 ; ASSERT (-7 * x0) + (-21 * x1) + (29 * x2) + (11 * x3) = 29 ; diff --git a/test/regress/regress1/arith/arith-int-056.cvc b/test/regress/regress1/arith/arith-int-056.cvc index e1c3ee1da..394b3dd4e 100644 --- a/test/regress/regress1/arith/arith-int-056.cvc +++ b/test/regress/regress1/arith/arith-int-056.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-25 * x0) + (23 * x1) + (29 * x2) + (21 * x3) = -2 ; ASSERT (1 * x0) + (10 * x1) + (-32 * x2) + (-17 * x3) = -2 ; diff --git a/test/regress/regress1/arith/arith-int-057.cvc b/test/regress/regress1/arith/arith-int-057.cvc index 4e7b939b4..252601514 100644 --- a/test/regress/regress1/arith/arith-int-057.cvc +++ b/test/regress/regress1/arith/arith-int-057.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-8 * x0) + (10 * x1) + (-25 * x2) + (-10 * x3) = -18 ; ASSERT (27 * x0) + (5 * x1) + (8 * x2) + (13 * x3) = -8; diff --git a/test/regress/regress1/arith/arith-int-058.cvc b/test/regress/regress1/arith/arith-int-058.cvc index 4d964f1c6..7e2a04d45 100644 --- a/test/regress/regress1/arith/arith-int-058.cvc +++ b/test/regress/regress1/arith/arith-int-058.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-15 * x0) + (3 * x1) + (31 * x2) + (2 * x3) = -18 ; ASSERT (-25 * x0) + (-10 * x1) + (15 * x2) + (29 * x3) = -18 ; diff --git a/test/regress/regress1/arith/arith-int-059.cvc b/test/regress/regress1/arith/arith-int-059.cvc index 841d9c8e1..87773679e 100644 --- a/test/regress/regress1/arith/arith-int-059.cvc +++ b/test/regress/regress1/arith/arith-int-059.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (31 * x0) + (-19 * x1) + (0 * x2) + (32 * x3) = -14 ; ASSERT (12 * x0) + (-25 * x1) + (-32 * x2) + (-18 * x3) = 18 ; diff --git a/test/regress/regress1/arith/arith-int-060.cvc b/test/regress/regress1/arith/arith-int-060.cvc index 227cb49b1..74dd16dca 100644 --- a/test/regress/regress1/arith/arith-int-060.cvc +++ b/test/regress/regress1/arith/arith-int-060.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (3 * x0) + (8 * x1) + (26 * x2) + (-17 * x3) = 31 ; ASSERT (-14 * x0) + (25 * x1) + (4 * x2) + (-8 * x3) = 15 ; diff --git a/test/regress/regress1/arith/arith-int-061.cvc b/test/regress/regress1/arith/arith-int-061.cvc index 4a3cc28d0..b3bd247b2 100644 --- a/test/regress/regress1/arith/arith-int-061.cvc +++ b/test/regress/regress1/arith/arith-int-061.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (16 * x0) + (20 * x1) + (-8 * x2) + (-27 * x3) = -2 ; ASSERT (15 * x0) + (9 * x1) + (-1 * x2) + (4 * x3) = 1 ; diff --git a/test/regress/regress1/arith/arith-int-062.cvc b/test/regress/regress1/arith/arith-int-062.cvc index f9a3156a2..0a185eb68 100644 --- a/test/regress/regress1/arith/arith-int-062.cvc +++ b/test/regress/regress1/arith/arith-int-062.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (11 * x0) + (22 * x1) + (19 * x2) + (-8 * x3) = 12 ; ASSERT (23 * x0) + (-6 * x1) + (-5 * x2) + (26 * x3) = 0 ; diff --git a/test/regress/regress1/arith/arith-int-063.cvc b/test/regress/regress1/arith/arith-int-063.cvc index d88104688..13c4aae2e 100644 --- a/test/regress/regress1/arith/arith-int-063.cvc +++ b/test/regress/regress1/arith/arith-int-063.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (20 * x0) + (-10 * x1) + (-10 * x2) + (26 * x3) = -9 ; ASSERT (10 * x0) + (0 * x1) + (16 * x2) + (7 * x3) = 7 ; diff --git a/test/regress/regress1/arith/arith-int-064.cvc b/test/regress/regress1/arith/arith-int-064.cvc index 21ca822e1..f50b3cd97 100644 --- a/test/regress/regress1/arith/arith-int-064.cvc +++ b/test/regress/regress1/arith/arith-int-064.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-8 * x0) + (-11 * x1) + (27 * x2) + (4 * x3) = 6 ; ASSERT (32 * x0) + (27 * x1) + (31 * x2) + (-13 * x3) = 21 ; diff --git a/test/regress/regress1/arith/arith-int-065.cvc b/test/regress/regress1/arith/arith-int-065.cvc index b1b9e1b51..354eb981c 100644 --- a/test/regress/regress1/arith/arith-int-065.cvc +++ b/test/regress/regress1/arith/arith-int-065.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (3 * x0) + (-21 * x1) + (-3 * x2) + (6 * x3) = -18 ; ASSERT (-15 * x0) + (19 * x1) + (-21 * x2) + (-29 * x3) = -8 ; diff --git a/test/regress/regress1/arith/arith-int-066.cvc b/test/regress/regress1/arith/arith-int-066.cvc index 9532b4198..f53a254bd 100644 --- a/test/regress/regress1/arith/arith-int-066.cvc +++ b/test/regress/regress1/arith/arith-int-066.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (28 * x0) + (-8 * x1) + (32 * x2) + (-3 * x3) = -18 ; ASSERT (-4 * x0) + (5 * x1) + (-2 * x2) + (-17 * x3) > 19 ; diff --git a/test/regress/regress1/arith/arith-int-067.cvc b/test/regress/regress1/arith/arith-int-067.cvc index 5d7b52e69..61159e9aa 100644 --- a/test/regress/regress1/arith/arith-int-067.cvc +++ b/test/regress/regress1/arith/arith-int-067.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-25 * x0) + (-32 * x1) + (-29 * x2) + (-9 * x3) = -2 ; ASSERT (22 * x0) + (10 * x1) + (-18 * x2) + (2 * x3) = -17 ; diff --git a/test/regress/regress1/arith/arith-int-068.cvc b/test/regress/regress1/arith/arith-int-068.cvc index 107a21a12..683d36801 100644 --- a/test/regress/regress1/arith/arith-int-068.cvc +++ b/test/regress/regress1/arith/arith-int-068.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-20 * x0) + (-8 * x1) + (5 * x2) + (-7 * x3) = -7 ; ASSERT (-30 * x0) + (24 * x1) + (-4 * x2) + (-30 * x3) = 22 ; diff --git a/test/regress/regress1/arith/arith-int-069.cvc b/test/regress/regress1/arith/arith-int-069.cvc index 3fab229b0..356a28013 100644 --- a/test/regress/regress1/arith/arith-int-069.cvc +++ b/test/regress/regress1/arith/arith-int-069.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-12 * x0) + (20 * x1) + (2 * x2) + (-24 * x3) = 16 ; ASSERT (-32 * x0) + (27 * x1) + (1 * x2) + (-3 * x3) = -3 ; diff --git a/test/regress/regress1/arith/arith-int-070.cvc b/test/regress/regress1/arith/arith-int-070.cvc index cd828da5f..791b3b8af 100644 --- a/test/regress/regress1/arith/arith-int-070.cvc +++ b/test/regress/regress1/arith/arith-int-070.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (0 * x0) + (-16 * x1) + (14 * x2) + (20 * x3) = 1 ; ASSERT (-27 * x0) + (-5 * x1) + (-22 * x2) + (-24 * x3) = -7 ; diff --git a/test/regress/regress1/arith/arith-int-071.cvc b/test/regress/regress1/arith/arith-int-071.cvc index ce5336476..d44b18b45 100644 --- a/test/regress/regress1/arith/arith-int-071.cvc +++ b/test/regress/regress1/arith/arith-int-071.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (22 * x0) + (3 * x1) + (-17 * x2) + (-21 * x3) = -9 ; ASSERT (-12 * x0) + (-9 * x1) + (-9 * x2) + (-16 * x3) = -12 ; diff --git a/test/regress/regress1/arith/arith-int-072.cvc b/test/regress/regress1/arith/arith-int-072.cvc index 10222deae..fb13a6616 100644 --- a/test/regress/regress1/arith/arith-int-072.cvc +++ b/test/regress/regress1/arith/arith-int-072.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (1 * x0) + (-1 * x1) + (-16 * x2) + (6 * x3) = -11 ; ASSERT (-17 * x0) + (17 * x1) + (-15 * x2) + (24 * x3) = -21 ; diff --git a/test/regress/regress1/arith/arith-int-073.cvc b/test/regress/regress1/arith/arith-int-073.cvc index 98e74be8f..784190cad 100644 --- a/test/regress/regress1/arith/arith-int-073.cvc +++ b/test/regress/regress1/arith/arith-int-073.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (8 * x0) + (-14 * x1) + (0 * x2) + (7 * x3) = 26 ; ASSERT (-7 * x0) + (-14 * x1) + (15 * x2) + (31 * x3) = 8 ; diff --git a/test/regress/regress1/arith/arith-int-074.cvc b/test/regress/regress1/arith/arith-int-074.cvc index 28cc48186..914cbe8e3 100644 --- a/test/regress/regress1/arith/arith-int-074.cvc +++ b/test/regress/regress1/arith/arith-int-074.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (14 * x0) + (-6 * x1) + (-23 * x2) + (-8 * x3) = -18 ; ASSERT (-11 * x0) + (12 * x1) + (8 * x2) + (-1 * x3) = -32 ; diff --git a/test/regress/regress1/arith/arith-int-075.cvc b/test/regress/regress1/arith/arith-int-075.cvc index 3b5131e8b..d3851e284 100644 --- a/test/regress/regress1/arith/arith-int-075.cvc +++ b/test/regress/regress1/arith/arith-int-075.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-8 * x0) + (29 * x1) + (15 * x2) + (32 * x3) = 32 ; ASSERT (18 * x0) + (-8 * x1) + (18 * x2) + (22 * x3) = 20 ; diff --git a/test/regress/regress1/arith/arith-int-076.cvc b/test/regress/regress1/arith/arith-int-076.cvc index 2c8de7cdf..25a3a7d35 100644 --- a/test/regress/regress1/arith/arith-int-076.cvc +++ b/test/regress/regress1/arith/arith-int-076.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-20 * x0) + (0 * x1) + (4 * x2) + (29 * x3) = -15 ; ASSERT (3 * x0) + (19 * x1) + (21 * x2) + (-32 * x3) = 11 ; diff --git a/test/regress/regress1/arith/arith-int-077.cvc b/test/regress/regress1/arith/arith-int-077.cvc index d14da386e..7e4482093 100644 --- a/test/regress/regress1/arith/arith-int-077.cvc +++ b/test/regress/regress1/arith/arith-int-077.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (26 * x0) + (-28 * x1) + (27 * x2) + (8 * x3) = 31 ; ASSERT (-32 * x0) + (11 * x1) + (-5 * x2) + (14 * x3) = 2; diff --git a/test/regress/regress1/arith/arith-int-078.cvc b/test/regress/regress1/arith/arith-int-078.cvc index 3197c6524..eacccc375 100644 --- a/test/regress/regress1/arith/arith-int-078.cvc +++ b/test/regress/regress1/arith/arith-int-078.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (17 * x0) + (-14 * x1) + (13 * x2) + (13 * x3) = -18 ; ASSERT (13 * x0) + (16 * x1) + (-12 * x2) + (19 * x3) = -20 ; diff --git a/test/regress/regress1/arith/arith-int-080.cvc b/test/regress/regress1/arith/arith-int-080.cvc index 8be0f9a73..bf6b90c67 100644 --- a/test/regress/regress1/arith/arith-int-080.cvc +++ b/test/regress/regress1/arith/arith-int-080.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (5 * x0) + (-17 * x1) + (15 * x2) + (-15 * x3) = -14 ; ASSERT (-28 * x0) + (-17 * x1) + (-29 * x2) + (-19 * x3) = 14; diff --git a/test/regress/regress1/arith/arith-int-081.cvc b/test/regress/regress1/arith/arith-int-081.cvc index 546148376..47cc66ae2 100644 --- a/test/regress/regress1/arith/arith-int-081.cvc +++ b/test/regress/regress1/arith/arith-int-081.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-8 * x0) + (31 * x1) + (-23 * x2) + (-8 * x3) = 8; ASSERT (24 * x0) + (-2 * x1) + (2 * x2) + (-2 * x3) >= -17 ; diff --git a/test/regress/regress1/arith/arith-int-082.cvc b/test/regress/regress1/arith/arith-int-082.cvc index 62bd45de7..a6245f036 100644 --- a/test/regress/regress1/arith/arith-int-082.cvc +++ b/test/regress/regress1/arith/arith-int-082.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-29 * x0) + (-3 * x1) + (27 * x2) + (13 * x3) = -10 ; ASSERT (7 * x0) + (-17 * x1) + (11 * x2) + (-30 * x3) <= 6 ; diff --git a/test/regress/regress1/arith/arith-int-083.cvc b/test/regress/regress1/arith/arith-int-083.cvc index 6b1084353..3a7c635cc 100644 --- a/test/regress/regress1/arith/arith-int-083.cvc +++ b/test/regress/regress1/arith/arith-int-083.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (19 * x0) + (-31 * x1) + (31 * x2) + (28 * x3) = -13 ; ASSERT (1 * x0) + (13 * x1) + (12 * x2) + (-15 * x3) > -8 ; diff --git a/test/regress/regress1/arith/arith-int-084.cvc b/test/regress/regress1/arith/arith-int-084.cvc index 5f0e17afe..d4a0a966c 100644 --- a/test/regress/regress1/arith/arith-int-084.cvc +++ b/test/regress/regress1/arith/arith-int-084.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-2 * x0) + (-13 * x1) + (-14 * x2) + (-26 * x3) <= 4 ; ASSERT (-17 * x0) + (-17 * x1) + (21 * x2) + (-4 * x3) < 18 ; diff --git a/test/regress/regress1/arith/arith-int-085.cvc b/test/regress/regress1/arith/arith-int-085.cvc index 74dd714e8..b1a343e73 100644 --- a/test/regress/regress1/arith/arith-int-085.cvc +++ b/test/regress/regress1/arith/arith-int-085.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed %% down from 3 x0, x1, x2, x3 : INT; ASSERT (22 * x0) + (-25 * x1) + (-20 * x2) + (8 * x3) = -6 ; diff --git a/test/regress/regress1/arith/arith-int-086.cvc b/test/regress/regress1/arith/arith-int-086.cvc index 64c212b3c..6ee96589b 100644 --- a/test/regress/regress1/arith/arith-int-086.cvc +++ b/test/regress/regress1/arith/arith-int-086.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-16 * x0) + (28 * x1) + (2 * x2) + (7 * x3) = -25 ; ASSERT (-20 * x0) + (-24 * x1) + (4 * x2) + (32 * x3) = -22 ; diff --git a/test/regress/regress1/arith/arith-int-087.cvc b/test/regress/regress1/arith/arith-int-087.cvc index 312c08917..b969df1a3 100644 --- a/test/regress/regress1/arith/arith-int-087.cvc +++ b/test/regress/regress1/arith/arith-int-087.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-4 * x0) + (25 * x1) + (-2 * x2) + (-16 * x3) = 27 ; ASSERT (-11 * x0) + (26 * x1) + (18 * x2) + (-18 * x3) = -15 ; diff --git a/test/regress/regress1/arith/arith-int-088.cvc b/test/regress/regress1/arith/arith-int-088.cvc index 5212640be..de0d23844 100644 --- a/test/regress/regress1/arith/arith-int-088.cvc +++ b/test/regress/regress1/arith/arith-int-088.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-19 * x0) + (-9 * x1) + (-27 * x2) + (9 * x3) = -1 ; ASSERT (-26 * x0) + (11 * x1) + (23 * x2) + (-5 * x3) >= 20 ; diff --git a/test/regress/regress1/arith/arith-int-089.cvc b/test/regress/regress1/arith/arith-int-089.cvc index 7ff36d29e..e50daa9de 100644 --- a/test/regress/regress1/arith/arith-int-089.cvc +++ b/test/regress/regress1/arith/arith-int-089.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (14 * x0) + (-14 * x1) + (-29 * x2) + (31 * x3) = -15 ; ASSERT (-14 * x0) + (2 * x1) + (26 * x2) + (29 * x3) = 25 ; diff --git a/test/regress/regress1/arith/arith-int-090.cvc b/test/regress/regress1/arith/arith-int-090.cvc index 52b9c13f0..74d4ba4db 100644 --- a/test/regress/regress1/arith/arith-int-090.cvc +++ b/test/regress/regress1/arith/arith-int-090.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-13 * x0) + (-14 * x1) + (-10 * x2) + (32 * x3) = 11 ; ASSERT (28 * x0) + (21 * x1) + (-20 * x2) + (-32 * x3) > -31 ; diff --git a/test/regress/regress1/arith/arith-int-091.cvc b/test/regress/regress1/arith/arith-int-091.cvc index 29a19db39..c03b544a3 100644 --- a/test/regress/regress1/arith/arith-int-091.cvc +++ b/test/regress/regress1/arith/arith-int-091.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (26 * x0) + (32 * x1) + (-26 * x2) + (-26 * x3) = -26 ; ASSERT (30 * x0) + (17 * x1) + (28 * x2) + (-9 * x3) = -21 ; diff --git a/test/regress/regress1/arith/arith-int-092.cvc b/test/regress/regress1/arith/arith-int-092.cvc index 51c8a6bc4..d080cde0c 100644 --- a/test/regress/regress1/arith/arith-int-092.cvc +++ b/test/regress/regress1/arith/arith-int-092.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-20 * x0) + (19 * x1) + (16 * x2) + (-27 * x3) = -22 ; ASSERT (12 * x0) + (-18 * x1) + (-25 * x2) + (-1 * x3) = -22 ; diff --git a/test/regress/regress1/arith/arith-int-093.cvc b/test/regress/regress1/arith/arith-int-093.cvc index 7d2123d41..e910def47 100644 --- a/test/regress/regress1/arith/arith-int-093.cvc +++ b/test/regress/regress1/arith/arith-int-093.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (22 * x0) + (-2 * x1) + (-1 * x2) + (-24 * x3) = 8 ; ASSERT (-6 * x0) + (9 * x1) + (-20 * x2) + (-23 * x3) = 14 ; diff --git a/test/regress/regress1/arith/arith-int-094.cvc b/test/regress/regress1/arith/arith-int-094.cvc index a5f1aefce..2204bba4e 100644 --- a/test/regress/regress1/arith/arith-int-094.cvc +++ b/test/regress/regress1/arith/arith-int-094.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (-7 * x0) + (-11 * x1) + (26 * x2) + (10 * x3) = 31 ; ASSERT (-17 * x0) + (-20 * x1) + (24 * x2) + (-9 * x3) = -32 ; diff --git a/test/regress/regress1/arith/arith-int-095.cvc b/test/regress/regress1/arith/arith-int-095.cvc index bc47d6f49..e803dbe9b 100644 --- a/test/regress/regress1/arith/arith-int-095.cvc +++ b/test/regress/regress1/arith/arith-int-095.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x0, x1, x2, x3 : INT; ASSERT (2 * x0) + (28 * x1) + (3 * x2) + (8 * x3) > -32 ; ASSERT (-15 * x0) + (21 * x1) + (-11 * x2) + (28 * x3) <= -19 ; diff --git a/test/regress/regress1/arith/arith-int-096.cvc b/test/regress/regress1/arith/arith-int-096.cvc index 2f6cf3155..354ae180d 100644 --- a/test/regress/regress1/arith/arith-int-096.cvc +++ b/test/regress/regress1/arith/arith-int-096.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (23 * x0) + (24 * x1) + (19 * x2) + (-3 * x3) = -16 ; ASSERT (2 * x0) + (-13 * x1) + (5 * x2) + (-1 * x3) = 28; diff --git a/test/regress/regress1/arith/arith-int-097.cvc b/test/regress/regress1/arith/arith-int-097.cvc index b05061192..67eb614eb 100644 --- a/test/regress/regress1/arith/arith-int-097.cvc +++ b/test/regress/regress1/arith/arith-int-097.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (19 * x0) + (-11 * x1) + (-19 * x2) + (5 * x3) = 26 ; ASSERT (1 * x0) + (-28 * x1) + (-2 * x2) + (15 * x3) < 9 ; diff --git a/test/regress/regress1/arith/arith-int-099.cvc b/test/regress/regress1/arith/arith-int-099.cvc index 0d74dcb39..57a45de03 100644 --- a/test/regress/regress1/arith/arith-int-099.cvc +++ b/test/regress/regress1/arith/arith-int-099.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-31 * x0) + (-20 * x1) + (-30 * x2) + (-28 * x3) = -24 ; ASSERT (11 * x0) + (-32 * x1) + (-2 * x2) + (8 * x3) <= 16 ; diff --git a/test/regress/regress1/arith/arith-int-100.cvc b/test/regress/regress1/arith/arith-int-100.cvc index 7e07bee14..66be1f8f7 100644 --- a/test/regress/regress1/arith/arith-int-100.cvc +++ b/test/regress/regress1/arith/arith-int-100.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (27 * x0) + (-21 * x1) + (-6 * x2) + (-6 * x3) > -15 ; ASSERT (-5 * x0) + (-10 * x1) + (2 * x2) + (-16 * x3) <= -7 ; diff --git a/test/regress/regress1/arith/bug547.1.smt2 b/test/regress/regress1/arith/bug547.1.smt2 index 4b7cf9780..38d1dfcb1 100644 --- a/test/regress/regress1/arith/bug547.1.smt2 +++ b/test/regress/regress1/arith/bug547.1.smt2 @@ -1,5 +1,5 @@ -; COMMAND-LINE: --rewrite-divk -; EXPECT: unknown +; COMMAND-LINE: --rewrite-divk --nl-ext-tplanes +; EXPECT: sat (set-logic QF_NIA) (declare-fun x () Int) (declare-fun y () Int) diff --git a/test/regress/regress1/boolean.cvc b/test/regress/regress1/boolean.cvc index eb0e7ab52..2c861c0f0 100644 --- a/test/regress/regress1/boolean.cvc +++ b/test/regress/regress1/boolean.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed p : BOOLEAN; q : BOOLEAN; r : BOOLEAN; diff --git a/test/regress/regress1/fmf/fmf-strange-bounds.smt2 b/test/regress/regress1/fmf/fmf-strange-bounds.smt2 index 7812c2431..20738245c 100644 --- a/test/regress/regress1/fmf/fmf-strange-bounds.smt2 +++ b/test/regress/regress1/fmf/fmf-strange-bounds.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --fmf-bound +; COMMAND-LINE: --fmf-bound --finite-model-find ; EXPECT: sat (set-logic ALL) (set-info :status sat) @@ -17,11 +17,16 @@ (assert (>= (h (g 77)) 2)) (assert (not (= (g 77) (f 77)))) -(assert (forall ((x Int) (y Int) (z U)) (=> +(assert (forall ((x Int) (z U)) (=> (or (= z (f x)) (= z (g x))) (=> (member x S) -(=> (and (<= 0 y) (<= y (h z))) -(P x y z)))))) +(P x 0 z))))) + +(assert (forall ((x Int) (y Int) (z U)) (=> +(or (= x 5) (= x 6)) +(=> (and (<= 0 y) (<= y x)) +(P x y z))))) + (declare-fun Q (U Int) Bool) diff --git a/test/regress/regress1/fmf/ko-bound-set.cvc b/test/regress/regress1/fmf/ko-bound-set.cvc index eebcbc2f8..5306a1513 100644 --- a/test/regress/regress1/fmf/ko-bound-set.cvc +++ b/test/regress/regress1/fmf/ko-bound-set.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed OPTION "finite-model-find"; OPTION "fmf-bound-int"; OPTION "produce-models"; diff --git a/test/regress/regress1/hole6.cvc b/test/regress/regress1/hole6.cvc index dfa9b72d5..5ec31d801 100644 --- a/test/regress/regress1/hole6.cvc +++ b/test/regress/regress1/hole6.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x_1 : BOOLEAN; x_2 : BOOLEAN; x_3 : BOOLEAN; diff --git a/test/regress/regress1/quantifiers/set-choice-koikonomou.cvc b/test/regress/regress1/quantifiers/set-choice-koikonomou.cvc index f7407a2a5..6f2a8764b 100644 --- a/test/regress/regress1/quantifiers/set-choice-koikonomou.cvc +++ b/test/regress/regress1/quantifiers/set-choice-koikonomou.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed OPTION "finite-model-find"; OPTION "fmf-bound-int"; diff --git a/test/regress/regress1/rr-verify/regex.sy b/test/regress/regress1/rr-verify/regex.sy index 6c6da3dd2..2d911e56a 100644 --- a/test/regress/regress1/rr-verify/regex.sy +++ b/test/regress/regress1/rr-verify/regex.sy @@ -1,17 +1,18 @@ -; COMMAND-LINE: --sygus-rr --sygus-samples=1000 --sygus-abort-size=3 --sygus-rr-verify-abort --no-sygus-sym-break +; COMMAND-LINE: --lang=sygus2 --sygus-rr --sygus-samples=1000 --sygus-abort-size=3 --sygus-rr-verify-abort --no-sygus-sym-break ; EXPECT: (error "Maximum term size (3) for enumerative SyGuS exceeded.") ; SCRUBBER: grep -v -E '(\(define-fun|\(candidate-rewrite)' ; EXIT: 1 (set-logic SLIA) -(synth-fun f ((x String) (y String)) Bool ( +(synth-fun f ((x String) (y String)) Bool +((Start Bool) (StartRe RegLan) (StartStr String)) ( (Start Bool ( true false (= StartStr StartStr) - (str.in.re StartStr StartRe) + (str.in_re StartStr StartRe) )) (StartRe RegLan ( @@ -19,7 +20,7 @@ (re.++ StartRe StartRe) (re.union StartRe StartRe) (re.* StartRe) - (str.to.re StartStr) + (str.to_re StartStr) )) (StartStr String ( diff --git a/test/regress/regress1/strings/bug686dd.smt2 b/test/regress/regress1/strings/bug686dd.smt2 index 0cb9fac26..b5c9457ff 100644 --- a/test/regress/regress1/strings/bug686dd.smt2 +++ b/test/regress/regress1/strings/bug686dd.smt2 @@ -8,7 +8,7 @@ (declare-fun root6 () T) (assert (and -(str.in.re root5 (re.loop (re.range "0" "9") 4 4) ) -(str.in.re (TCb root6) (re.loop (re.range "0" "9") 4 4) ) +(str.in.re root5 ((_ re.loop 4 4) (re.range "0" "9")) ) +(str.in.re (TCb root6) ((_ re.loop 4 4) (re.range "0" "9")) ) ) ) (check-sat) diff --git a/test/regress/regress1/strings/pierre150331.smt2 b/test/regress/regress1/strings/pierre150331.smt2 index add60d534..e04db8e9a 100644 --- a/test/regress/regress1/strings/pierre150331.smt2 +++ b/test/regress/regress1/strings/pierre150331.smt2 @@ -6,7 +6,7 @@ (define-fun stringEval ((?s String)) Bool (str.in.re ?s
(re.union
(str.to.re "H")
-(re.++ (re.loop (str.to.re "{") 2 2 ) (re.loop (re.union re.nostr (re.range "" "]") (re.range "" "^") ) 2 4 ) ) ) ) )
+(re.++ ((_ re.loop 2 2) (str.to.re "{") ) ((_ re.loop 2 4) (re.union re.nostr (re.range "" "]") (re.range "" "^") ) ) ) ) ) )
(declare-fun s0() String)
(declare-fun s1() String)
(declare-fun s2() String)
diff --git a/test/regress/regress1/strings/reloop.smt2 b/test/regress/regress1/strings/reloop.smt2 index 22537b957..6230d1656 100644 --- a/test/regress/regress1/strings/reloop.smt2 +++ b/test/regress/regress1/strings/reloop.smt2 @@ -8,11 +8,11 @@ (declare-fun z () String) (declare-fun w () String) -(assert (str.in.re x (re.loop (str.to.re "a") 5))) -(assert (str.in.re y (re.loop (str.to.re "b") 2 5))) -(assert (str.in.re z (re.loop (str.to.re "c") 5))) +(assert (str.in.re x ((_ re.^ 5) (str.to.re "a")))) +(assert (str.in.re y ((_ re.loop 2 5) (str.to.re "b")))) +(assert (str.in.re z ((_ re.loop 5 15) (str.to.re "c")))) (assert (> (str.len z) 7)) -(assert (str.in.re w (re.loop (str.to.re "b") 2 7))) +(assert (str.in.re w ((_ re.loop 2 7) (str.to.re "b")))) (assert (> (str.len w) 2)) (assert (< (str.len w) 5)) diff --git a/test/regress/regress1/sygus/dt-test-ns.sy b/test/regress/regress1/sygus/dt-test-ns.sy index 3d078cc25..90fa57827 100644 --- a/test/regress/regress1/sygus/dt-test-ns.sy +++ b/test/regress/regress1/sygus/dt-test-ns.sy @@ -1,6 +1,6 @@ ; EXPECT: unsat ; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --sygus-out=status -(set-logic LIA) +(set-logic DTLIA) (declare-datatypes ((List 0)) (((cons (head Int) (tail List)) (nil)))) @@ -8,6 +8,6 @@ (declare-var x Int) -(constraint (is-cons (f x))) +(constraint ((_ is cons) (f x))) (constraint (and (= (head (f x)) x) (= (head (f x)) (+ 5 (head (tail (f x))))))) (check-synth) diff --git a/test/regress/regress1/sygus/hd-19-d1-prog-dup-op.sy b/test/regress/regress1/sygus/hd-19-d1-prog-dup-op.sy index abcfc2217..089a8f11f 100644 --- a/test/regress/regress1/sygus/hd-19-d1-prog-dup-op.sy +++ b/test/regress/regress1/sygus/hd-19-d1-prog-dup-op.sy @@ -1,14 +1,15 @@ ; EXPECT: unsat -; COMMAND-LINE: --cegqi-si=all --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --sygus-out=status (set-logic BV) -(define-fun hd19 ((x (BitVec 32)) (m (BitVec 32)) (k (BitVec 32))) (BitVec 32) - (bvxor x (bvxor (bvshl (bvand (bvxor (bvlshr x k) x) m) k) (bvand (bvxor (bvlshr x k) x) m)))) +(define-fun hd19 ((x (_ BitVec 32)) (m (_ BitVec 32)) (k (_ BitVec 32))) (_ BitVec 32) + (bvxor x (bvxor (bvshl (bvand (bvxor (bvlshr x k) x) m) k) (bvand (bvxor (bvlshr x k) x) m)))) ; bvand is a duplicate -(synth-fun f ((x (BitVec 32)) (m (BitVec 32)) (k (BitVec 32))) (BitVec 32) - ((Start (BitVec 32) ((bvand Start Start) +(synth-fun f ((x (_ BitVec 32)) (m (_ BitVec 32)) (k (_ BitVec 32))) (_ BitVec 32) + ((Start (_ BitVec 32))) + ((Start (_ BitVec 32) ((bvand Start Start) (bvsub Start Start) (bvxor Start Start) (bvor Start Start) @@ -23,10 +24,9 @@ k)))) -(declare-var x (BitVec 32)) -(declare-var m (BitVec 32)) -(declare-var k (BitVec 32)) +(declare-var x (_ BitVec 32)) +(declare-var m (_ BitVec 32)) +(declare-var k (_ BitVec 32)) (constraint (= (hd19 x m k) (f x m k))) (check-synth) - diff --git a/test/regress/regress1/sygus/issue3461.sy b/test/regress/regress1/sygus/issue3461.sy index 1f839c229..08b5738c1 100644 --- a/test/regress/regress1/sygus/issue3461.sy +++ b/test/regress/regress1/sygus/issue3461.sy @@ -1,15 +1,16 @@ ; EXPECT: unsat -; COMMAND-LINE: --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --sygus-out=status (set-logic ALL_SUPPORTED) (declare-datatype Doc ((D (owner Int) (body Int)))) -(declare-datatype Policy - ((p (principal Int)) +(declare-datatype Policy + ((p (principal Int)) (por (left Policy) (right Policy)))) (synth-fun mkPolicy ((d Doc)) Policy + ((Start Policy) (Q Policy)) ((Start Policy (Q)) (Q Policy ((p 0) (p 1) (por Q Q)))) ) diff --git a/test/regress/regress1/sygus/list-head-x.sy b/test/regress/regress1/sygus/list-head-x.sy index 83ac8290d..ae2bcc00e 100644 --- a/test/regress/regress1/sygus/list-head-x.sy +++ b/test/regress/regress1/sygus/list-head-x.sy @@ -8,6 +8,6 @@ (declare-var x Int) -(constraint (is-cons (f x))) +(constraint ((_ is cons) (f x))) (constraint (= (head (f x)) (+ x 7))) (check-synth) diff --git a/test/regress/regress1/sygus/max.sy b/test/regress/regress1/sygus/max.sy index 37ed848ef..f191d784f 100644 --- a/test/regress/regress1/sygus/max.sy +++ b/test/regress/regress1/sygus/max.sy @@ -1,8 +1,9 @@ ; EXPECT: unsat -; COMMAND-LINE: --cegqi-si=all --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --sygus-out=status (set-logic LIA) (synth-fun max ((x Int) (y Int)) Int + ((Start Int) (StartBool Bool)) ((Start Int (0 1 x y (+ Start Start) (- Start Start) @@ -12,6 +13,7 @@ (<= Start Start))))) ;(synth-fun min ((x Int) (y Int)) Int +; ((Start Int) (StartBool Bool)) ; ((Start Int ((Constant Int) (Variable Int) ; (+ Start Start) ; (- Start Start) diff --git a/test/regress/regress1/sygus/parity-si-rcons.sy b/test/regress/regress1/sygus/parity-si-rcons.sy index a836c9726..850cc6610 100644 --- a/test/regress/regress1/sygus/parity-si-rcons.sy +++ b/test/regress/regress1/sygus/parity-si-rcons.sy @@ -1,5 +1,5 @@ ; EXPECT: unsat -; COMMAND-LINE: --cegqi-si=all --cegqi-si-abort --decision=internal --cbqi-prereg-inst --cegqi-si-rcons=try --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --cegqi-si=all --cegqi-si-abort --decision=internal --cbqi-prereg-inst --cegqi-si-rcons=try --sygus-out=status (set-logic BV) @@ -7,6 +7,7 @@ (xor (not (xor a b)) (not (xor c d)))) (synth-fun NAND ((a Bool) (b Bool) (c Bool) (d Bool)) Bool + ((Start Bool) (StartAnd Bool) (Vars Bool) (Constants Bool)) ((Start Bool ((not StartAnd) Vars Constants)) (StartAnd Bool ((and Start Start))) (Vars Bool (a b c d)) diff --git a/test/regress/regress1/sygus/re-concat.sy b/test/regress/regress1/sygus/re-concat.sy index 3449ed505..ac1172e33 100644 --- a/test/regress/regress1/sygus/re-concat.sy +++ b/test/regress/regress1/sygus/re-concat.sy @@ -1,13 +1,13 @@ ; EXPECT: unsat -; COMMAND-LINE: --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --sygus-out=status (set-logic SLIA) -(synth-fun f () RegLan ( +(synth-fun f () RegLan ((Start RegLan) (Tokens String)) ( (Start RegLan ( - (str.to.re Tokens) + (str.to_re Tokens) (re.++ Start Start))) (Tokens String ("A" "B")) )) -(constraint (str.in.re "AB" f)) +(constraint (str.in_re "AB" f)) (check-synth) diff --git a/test/regress/regress1/sygus/simple-regexp.sy b/test/regress/regress1/sygus/simple-regexp.sy index b4c248de9..b7646725d 100644 --- a/test/regress/regress1/sygus/simple-regexp.sy +++ b/test/regress/regress1/sygus/simple-regexp.sy @@ -1,30 +1,32 @@ ; EXPECT: unsat -; COMMAND-LINE: --sygus-out=status +; COMMAND-LINE: --lang=sygus2 --sygus-out=status (set-logic SLIA) -(synth-fun P ((x String)) Bool ( -(Start Bool ( - (str.in.re StartStr StartRL) +(synth-fun P ((x String)) Bool + ((Start Bool) (StartStr String) (StartStrC String) (StartRL RegLan) (StartRLi RegLan)) ( + (Start Bool ( + (str.in_re StartStr StartRL) )) -(StartStr String ( - x - (str.++ StartStr StartStr) + (StartStr String ( + x + (str.++ StartStr StartStr) + )) + (StartStrC String ( + "A" "B" "" + (str.++ StartStrC StartStrC) + )) + (StartRL RegLan ( + (re.++ StartRLi StartRLi) + (re.inter StartRL StartRL) + (re.union StartRL StartRL) + (re.* StartRLi) + )) + (StartRLi RegLan ( + (str.to_re StartStrC) + (re.inter StartRLi StartRLi) + (re.union StartRLi StartRLi) + (re.++ StartRLi StartRLi) + (re.* StartRLi) )) -(StartStrC String ( - "A" "B" "" - (str.++ StartStrC StartStrC))) -(StartRL RegLan ( -(re.++ StartRLi StartRLi) -(re.inter StartRL StartRL) -(re.union StartRL StartRL) -(re.* StartRLi) -)) -(StartRLi RegLan ( -(str.to.re StartStrC) -(re.inter StartRLi StartRLi) -(re.union StartRLi StartRLi) -(re.++ StartRLi StartRLi) -(re.* StartRLi) -)) )) (constraint (P "AAAAA")) @@ -33,5 +35,5 @@ (constraint (not (P "AB"))) (constraint (not (P "B"))) -; (str.in.re x (re.* (str.to.re "A"))) is a solution +; (str.in_re x (re.* (str.to_re "A"))) is a solution (check-synth) diff --git a/test/regress/regress1/sygus/sygus-uf-ex.sy b/test/regress/regress1/sygus/sygus-uf-ex.sy index 66880eafa..7e1cd80b3 100644 --- a/test/regress/regress1/sygus/sygus-uf-ex.sy +++ b/test/regress/regress1/sygus/sygus-uf-ex.sy @@ -1,18 +1,24 @@ ; EXPECT: unsat -; COMMAND-LINE: --sygus-out=status --uf-ho -(set-logic UFLIA) -(declare-fun uf ( Int ) Int) +; COMMAND-LINE: --lang=sygus2 --sygus-out=status --uf-ho +(set-logic ALL) + +(declare-var uf (-> Int Int)) + (synth-fun f ((x Int) (y Int)) Bool -((Start Bool (true false - (<= IntExpr IntExpr ) - (= IntExpr IntExpr ) - (and Start Start ) - (or Start Start ) - (not Start ))) -(IntExpr Int (0 1 x y - (+ IntExpr IntExpr ) - (- IntExpr IntExpr ))))) + ((Start Bool) (IntExpr Int)) + ((Start Bool (true false + (<= IntExpr IntExpr) + (= IntExpr IntExpr) + (and Start Start) + (or Start Start) + (not Start ))) + (IntExpr Int (0 1 x y + (+ IntExpr IntExpr) + (- IntExpr IntExpr))))) + (declare-var x Int) + (constraint (f (uf x) (uf x))) (constraint (not (f 3 4))) + (check-synth) diff --git a/test/regress/regress1/test12.cvc b/test/regress/regress1/test12.cvc index 37687bee1..39ced0428 100644 --- a/test/regress/regress1/test12.cvc +++ b/test/regress/regress1/test12.cvc @@ -1,34 +1,34 @@ % COMMAND-LINE: --incremental -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: valid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: valid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: invalid -% EXPECT: valid -% EXPECT: valid -% EXPECT: valid +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: not_entailed +% EXPECT: entailed +% EXPECT: entailed +% EXPECT: entailed A: TYPE; P_1: BOOLEAN; P_2: BOOLEAN; diff --git a/test/regress/regress2/arith/arith-int-098.cvc b/test/regress/regress2/arith/arith-int-098.cvc index 08cfd9c9c..cf20f2b61 100644 --- a/test/regress/regress2/arith/arith-int-098.cvc +++ b/test/regress/regress2/arith/arith-int-098.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed x0, x1, x2, x3 : INT; ASSERT (-28 * x0) + (12 * x1) + (-19 * x2) + (10 * x3) = 16 ; ASSERT (19 * x0) + (-25 * x1) + (-8 * x2) + (-32 * x3) = 12; diff --git a/test/regress/regress2/hole7.cvc b/test/regress/regress2/hole7.cvc index 1f762477a..e73588bad 100644 --- a/test/regress/regress2/hole7.cvc +++ b/test/regress/regress2/hole7.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x_1 : BOOLEAN; x_2 : BOOLEAN; x_3 : BOOLEAN; diff --git a/test/regress/regress2/hole8.cvc b/test/regress/regress2/hole8.cvc index 705c95ea6..a46c4da97 100644 --- a/test/regress/regress2/hole8.cvc +++ b/test/regress/regress2/hole8.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x_1 : BOOLEAN; x_2 : BOOLEAN; x_3 : BOOLEAN; diff --git a/test/regress/regress2/strings/range-perf.smt2 b/test/regress/regress2/strings/range-perf.smt2 index 62ec10711..33960e124 100644 --- a/test/regress/regress2/strings/range-perf.smt2 +++ b/test/regress/regress2/strings/range-perf.smt2 @@ -2,6 +2,6 @@ ; EXPECT: sat (set-logic QF_SLIA) (declare-const x String) -(assert (str.in.re x (re.loop (re.range "0" "9") 12 12))) +(assert (str.in.re x ((_ re.loop 12 12) (re.range "0" "9")))) (assert (str.in.re x (re.++ (re.* re.allchar) (str.to.re "01") (re.* re.allchar)))) (check-sat) diff --git a/test/regress/regress2/sygus/issue4022-conjecture-gen.smt2 b/test/regress/regress2/sygus/issue4022-conjecture-gen.smt2 index 9c3fe7ac5..471cc519b 100644 --- a/test/regress/regress2/sygus/issue4022-conjecture-gen.smt2 +++ b/test/regress/regress2/sygus/issue4022-conjecture-gen.smt2 @@ -2,7 +2,6 @@ (set-option :conjecture-filter-model true) (set-option :conjecture-gen true) (set-option :conjecture-no-filter true) -(set-option :dt-ref-sk-intro true) (set-option :quant-ind true) (set-option :sygus-inference true) (set-info :status sat) diff --git a/test/regress/regress2/typed_v1l50016-simp.cvc b/test/regress/regress2/typed_v1l50016-simp.cvc index b4a1e4b32..1d576ab74 100644 --- a/test/regress/regress2/typed_v1l50016-simp.cvc +++ b/test/regress/regress2/typed_v1l50016-simp.cvc @@ -1,4 +1,4 @@ -% EXPECT: invalid +% EXPECT: not_entailed DATATYPE nat = succ(pred : nat) | zero, diff --git a/test/regress/regress3/hole9.cvc b/test/regress/regress3/hole9.cvc index e60839f7b..b86b3b039 100644 --- a/test/regress/regress3/hole9.cvc +++ b/test/regress/regress3/hole9.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x_1 : BOOLEAN; x_2 : BOOLEAN; x_3 : BOOLEAN; diff --git a/test/regress/regress4/hole10.cvc b/test/regress/regress4/hole10.cvc index ebc8279d3..fb4c41b35 100644 --- a/test/regress/regress4/hole10.cvc +++ b/test/regress/regress4/hole10.cvc @@ -1,4 +1,4 @@ -% EXPECT: valid +% EXPECT: entailed x_1 : BOOLEAN; x_2 : BOOLEAN; x_3 : BOOLEAN; diff --git a/test/system/boilerplate.cpp b/test/system/boilerplate.cpp index 141db4eea..2ce50949b 100644 --- a/test/system/boilerplate.cpp +++ b/test/system/boilerplate.cpp @@ -29,8 +29,8 @@ int main() { ExprManager em; Options opts; SmtEngine smt(&em); - Result r = smt.query(em.mkConst(true)); + Result r = smt.checkEntailed(em.mkConst(true)); - return (Result::VALID == r) ? 0 : 1; + return (Result::ENTAILED == r) ? 0 : 1; } diff --git a/test/system/statistics.cpp b/test/system/statistics.cpp index fb9714d4b..2924359e8 100644 --- a/test/system/statistics.cpp +++ b/test/system/statistics.cpp @@ -35,9 +35,10 @@ int main() { Expr y = em.mkVar("y", em.integerType()); smt.assertFormula(em.mkExpr(kind::GT, em.mkExpr(kind::PLUS, x, y), em.mkConst(Rational(5)))); Expr q = em.mkExpr(kind::GT, x, em.mkConst(Rational(0))); - Result r = smt.query(q); + Result r = smt.checkEntailed(q); - if(r != Result::INVALID) { + if (r != Result::NOT_ENTAILED) + { exit(1); } @@ -47,7 +48,7 @@ int main() { } smt.assertFormula(em.mkExpr(kind::LT, y, em.mkConst(Rational(5)))); - r = smt.query(q); + r = smt.checkEntailed(q); Statistics stats2 = smt.getStatistics(); bool different = false; for(Statistics::iterator i = stats2.begin(); i != stats2.end(); ++i) { @@ -68,5 +69,5 @@ int main() { } #endif /* CVC4_STATISTICS_ON */ - return r == Result::VALID ? 0 : 1; + return r == Result::ENTAILED ? 0 : 1; } diff --git a/test/system/two_smt_engines.cpp b/test/system/two_smt_engines.cpp index a7266e0b0..8698fde0e 100644 --- a/test/system/two_smt_engines.cpp +++ b/test/system/two_smt_engines.cpp @@ -28,9 +28,9 @@ int main() { Options opts; SmtEngine smt(&em); SmtEngine smt2(&em); - Result r = smt.query(em.mkConst(true)); - Result r2 = smt2.query(em.mkConst(true)); + Result r = smt.checkEntailed(em.mkConst(true)); + Result r2 = smt2.checkEntailed(em.mkConst(true)); - return r == Result::VALID && r2 == Result::VALID ? 0 : 1; + return r == Result::ENTAILED && r2 == Result::ENTAILED ? 0 : 1; } diff --git a/test/unit/api/result_black.h b/test/unit/api/result_black.h index ab5d65e72..cbfc9cf23 100644 --- a/test/unit/api/result_black.h +++ b/test/unit/api/result_black.h @@ -29,9 +29,8 @@ class ResultBlack : public CxxTest::TestSuite void testIsSat(); void testIsUnsat(); void testIsSatUnknown(); - void testIsValid(); - void testIsInvalid(); - void testIsValidUnknown(); + void testIsEntailed(); + void testIsEntailmentUnknown(); private: std::unique_ptr<Solver> d_solver; @@ -44,9 +43,9 @@ void ResultBlack::testIsNull() TS_ASSERT(!res_null.isSat()); TS_ASSERT(!res_null.isUnsat()); TS_ASSERT(!res_null.isSatUnknown()); - TS_ASSERT(!res_null.isValid()); - TS_ASSERT(!res_null.isInvalid()); - TS_ASSERT(!res_null.isValidUnknown()); + TS_ASSERT(!res_null.isEntailed()); + TS_ASSERT(!res_null.isNotEntailed()); + TS_ASSERT(!res_null.isEntailmentUnknown()); Sort u_sort = d_solver->mkUninterpretedSort("u"); Term x = d_solver->mkVar(u_sort, "x"); d_solver->assertFormula(x.eqTerm(x)); @@ -100,27 +99,24 @@ void ResultBlack::testIsSatUnknown() TS_ASSERT(res.isSatUnknown()); } -void ResultBlack::testIsValid() +void ResultBlack::testIsEntailed() { + d_solver->setOption("incremental", "true"); Sort u_sort = d_solver->mkUninterpretedSort("u"); - Term x = d_solver->mkVar(u_sort, "x"); - d_solver->assertFormula(x.eqTerm(x).notTerm()); - Result res = d_solver->checkValid(); - TS_ASSERT(res.isValid()); - TS_ASSERT(!res.isValidUnknown()); -} - -void ResultBlack::testIsInvalid() -{ - Sort u_sort = d_solver->mkUninterpretedSort("u"); - Term x = d_solver->mkVar(u_sort, "x"); - d_solver->assertFormula(x.eqTerm(x)); - Result res = d_solver->checkValid(); - TS_ASSERT(res.isInvalid()); - TS_ASSERT(!res.isValidUnknown()); + Term x = d_solver->mkConst(u_sort, "x"); + Term y = d_solver->mkConst(u_sort, "y"); + Term a = x.eqTerm(y).notTerm(); + Term b = x.eqTerm(y); + d_solver->assertFormula(a); + Result entailed = d_solver->checkEntailed(a); + TS_ASSERT(entailed.isEntailed()); + TS_ASSERT(!entailed.isEntailmentUnknown()); + Result not_entailed = d_solver->checkEntailed(b); + TS_ASSERT(not_entailed.isNotEntailed()); + TS_ASSERT(!not_entailed.isEntailmentUnknown()); } -void ResultBlack::testIsValidUnknown() +void ResultBlack::testIsEntailmentUnknown() { d_solver->setLogic("QF_NIA"); d_solver->setOption("incremental", "false"); @@ -128,9 +124,9 @@ void ResultBlack::testIsValidUnknown() Sort int_sort = d_solver->getIntegerSort(); Term x = d_solver->mkVar(int_sort, "x"); d_solver->assertFormula(x.eqTerm(x).notTerm()); - Result res = d_solver->checkValid(); - TS_ASSERT(!res.isValid()); - TS_ASSERT(res.isValidUnknown()); + Result res = d_solver->checkEntailed(x.eqTerm(x)); + TS_ASSERT(!res.isEntailed()); + TS_ASSERT(res.isEntailmentUnknown()); TS_ASSERT_EQUALS(res.getUnknownExplanation(), "UNKNOWN_REASON"); } diff --git a/test/unit/api/solver_black.h b/test/unit/api/solver_black.h index 27f5aca12..7c9af1714 100644 --- a/test/unit/api/solver_black.h +++ b/test/unit/api/solver_black.h @@ -69,6 +69,7 @@ class SolverBlack : public CxxTest::TestSuite void testMkRoundingMode(); void testMkSepNil(); void testMkString(); + void testMkChar(); void testMkTerm(); void testMkTermFromOp(); void testMkTrue(); @@ -95,10 +96,9 @@ class SolverBlack : public CxxTest::TestSuite void testPop3(); void testSimplify(); - void testCheckValid1(); - void testCheckValid2(); - void testCheckValidAssuming1(); - void testCheckValidAssuming2(); + void testCheckEntailed(); + void testCheckEntailed1(); + void testCheckEntailed2(); void testSetInfo(); void testSetLogic(); @@ -556,9 +556,20 @@ void SolverBlack::testMkString() TS_ASSERT_THROWS_NOTHING(d_solver->mkString("")); TS_ASSERT_THROWS_NOTHING(d_solver->mkString("asdfasdf")); TS_ASSERT_EQUALS(d_solver->mkString("asdf\\nasdf").toString(), - "\"asdf\\\\nasdf\""); - TS_ASSERT_EQUALS(d_solver->mkString("asdf\\nasdf", true).toString(), - "\"asdf\\nasdf\""); + "\"asdf\\u{5c}nasdf\""); + TS_ASSERT_EQUALS(d_solver->mkString("asdf\\u{005c}nasdf", true).toString(), + "\"asdf\\u{5c}nasdf\""); +} + +void SolverBlack::testMkChar() +{ + TS_ASSERT_THROWS_NOTHING(d_solver->mkChar(std::string("0123"))); + TS_ASSERT_THROWS_NOTHING(d_solver->mkChar("aA")); + TS_ASSERT_THROWS(d_solver->mkChar(nullptr), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->mkChar(""), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->mkChar("0g0"), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->mkChar("100000"), CVC4ApiException&); + TS_ASSERT_EQUALS(d_solver->mkChar("abc"), d_solver->mkChar("ABC")); } void SolverBlack::testMkTerm() @@ -1104,51 +1115,28 @@ void SolverBlack::testSimplify() TS_ASSERT_THROWS_NOTHING(d_solver->simplify(f2)); } -void SolverBlack::testCheckValid1() +void SolverBlack::testCheckEntailed() { d_solver->setOption("incremental", "false"); - TS_ASSERT_THROWS_NOTHING(d_solver->checkValid()); - TS_ASSERT_THROWS(d_solver->checkValid(), CVC4ApiException&); + TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(d_solver->mkTrue())); + TS_ASSERT_THROWS(d_solver->checkEntailed(d_solver->mkTrue()), + CVC4ApiException&); } -void SolverBlack::testCheckValid2() +void SolverBlack::testCheckEntailed1() { + Sort boolSort = d_solver->getBooleanSort(); + Term x = d_solver->mkConst(boolSort, "x"); + Term y = d_solver->mkConst(boolSort, "y"); + Term z = d_solver->mkTerm(AND, x, y); d_solver->setOption("incremental", "true"); - - Sort realSort = d_solver->getRealSort(); - Sort intSort = d_solver->getIntegerSort(); - - // Constants - Term x = d_solver->mkConst(intSort, "x"); - Term y = d_solver->mkConst(realSort, "y"); - // Values - Term three = d_solver->mkReal(3); - Term neg2 = d_solver->mkReal(-2); - Term two_thirds = d_solver->mkReal(2, 3); - // Terms - Term three_y = d_solver->mkTerm(MULT, three, y); - Term diff = d_solver->mkTerm(MINUS, y, x); - // Formulas - Term x_geq_3y = d_solver->mkTerm(GEQ, x, three_y); - Term x_leq_y = d_solver->mkTerm(LEQ, x, y); - Term neg2_lt_x = d_solver->mkTerm(LT, neg2, x); - // Assertions - Term assertions = d_solver->mkTerm(AND, x_geq_3y, x_leq_y, neg2_lt_x); - - TS_ASSERT_THROWS_NOTHING(d_solver->checkValid()); - d_solver->assertFormula(assertions); - TS_ASSERT_THROWS_NOTHING(d_solver->checkValid()); -} - -void SolverBlack::testCheckValidAssuming1() -{ - d_solver->setOption("incremental", "false"); - TS_ASSERT_THROWS_NOTHING(d_solver->checkValidAssuming(d_solver->mkTrue())); - TS_ASSERT_THROWS(d_solver->checkValidAssuming(d_solver->mkTrue()), - CVC4ApiException&); + TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(d_solver->mkTrue())); + TS_ASSERT_THROWS(d_solver->checkEntailed(Term()), CVC4ApiException&); + TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(d_solver->mkTrue())); + TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(z)); } -void SolverBlack::testCheckValidAssuming2() +void SolverBlack::testCheckEntailed2() { d_solver->setOption("incremental", "true"); @@ -1185,15 +1173,15 @@ void SolverBlack::testCheckValidAssuming2() p_f_y // p(f(y)) }); - TS_ASSERT_THROWS_NOTHING(d_solver->checkValidAssuming(d_solver->mkTrue())); + TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed(d_solver->mkTrue())); d_solver->assertFormula(assertions); TS_ASSERT_THROWS_NOTHING( - d_solver->checkValidAssuming(d_solver->mkTerm(DISTINCT, x, y))); - TS_ASSERT_THROWS_NOTHING(d_solver->checkValidAssuming( + d_solver->checkEntailed(d_solver->mkTerm(DISTINCT, x, y))); + TS_ASSERT_THROWS_NOTHING(d_solver->checkEntailed( {d_solver->mkFalse(), d_solver->mkTerm(DISTINCT, x, y)})); - TS_ASSERT_THROWS(d_solver->checkValidAssuming(n), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->checkEntailed(n), CVC4ApiException&); TS_ASSERT_THROWS( - d_solver->checkValidAssuming({n, d_solver->mkTerm(DISTINCT, x, y)}), + d_solver->checkEntailed({n, d_solver->mkTerm(DISTINCT, x, y)}), CVC4ApiException&); } diff --git a/test/unit/expr/CMakeLists.txt b/test/unit/expr/CMakeLists.txt index d487bf560..438b7f7b6 100644 --- a/test/unit/expr/CMakeLists.txt +++ b/test/unit/expr/CMakeLists.txt @@ -13,6 +13,7 @@ cvc4_add_unit_test_black(node_builder_black expr) cvc4_add_unit_test_black(node_manager_black expr) cvc4_add_unit_test_white(node_manager_white expr) cvc4_add_unit_test_black(node_self_iterator_black expr) +cvc4_add_unit_test_black(node_traversal_black expr) cvc4_add_unit_test_white(node_white expr) cvc4_add_unit_test_black(symbol_table_black expr) cvc4_add_unit_test_black(type_cardinality_public expr) diff --git a/test/unit/expr/node_traversal_black.h b/test/unit/expr/node_traversal_black.h new file mode 100644 index 000000000..b4a7c449c --- /dev/null +++ b/test/unit/expr/node_traversal_black.h @@ -0,0 +1,281 @@ +/********************* */ +/*! \file node_traversal_black.h + ** \verbatim + ** Top contributors (to current version): + ** Alex Ozdemir + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Black box testing of node traversal iterators. + **/ + +#include <cxxtest/TestSuite.h> + +// Used in some of the tests +#include <algorithm> +#include <cstddef> +#include <iterator> +#include <sstream> +#include <string> +#include <vector> + +#include "expr/expr_manager.h" +#include "expr/node.h" +#include "expr/node_builder.h" +#include "expr/node_manager.h" +#include "expr/node_traversal.h" +#include "expr/node_value.h" + +using namespace CVC4; +using namespace CVC4::kind; +using namespace std; + +class NodePostorderTraversalBlack : public CxxTest::TestSuite +{ + private: + NodeManager* d_nodeManager; + NodeManagerScope* d_scope; + + public: + void setUp() override + { + d_nodeManager = new NodeManager(NULL); + d_scope = new NodeManagerScope(d_nodeManager); + } + + void tearDown() override + { + delete d_scope; + delete d_nodeManager; + } + + void testPreincrementIteration() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + auto traversal = NodeDfsIterable(cnd).inPostorder(); + NodeDfsIterator i = traversal.begin(); + NodeDfsIterator end = traversal.end(); + TS_ASSERT_EQUALS(*i, tb); + TS_ASSERT_DIFFERS(i, end); + ++i; + TS_ASSERT_EQUALS(*i, eb); + TS_ASSERT_DIFFERS(i, end); + ++i; + TS_ASSERT_EQUALS(*i, cnd); + TS_ASSERT_DIFFERS(i, end); + ++i; + TS_ASSERT_EQUALS(i, end); + } + + void testPostincrementIteration() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + auto traversal = NodeDfsIterable(cnd).inPostorder(); + NodeDfsIterator i = traversal.begin(); + NodeDfsIterator end = traversal.end(); + TS_ASSERT_EQUALS(*(i++), tb); + TS_ASSERT_EQUALS(*(i++), eb); + TS_ASSERT_EQUALS(*(i++), cnd); + TS_ASSERT_EQUALS(i, end); + } + + void testPostorderIsDefault() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + auto traversal = NodeDfsIterable(cnd); + NodeDfsIterator i = traversal.begin(); + NodeDfsIterator end = traversal.end(); + TS_ASSERT_EQUALS(*i, tb); + TS_ASSERT_DIFFERS(i, end); + } + + void testRangeForLoop() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + size_t count = 0; + for (auto i : NodeDfsIterable(cnd).inPostorder()) + { + ++count; + } + TS_ASSERT_EQUALS(count, 3); + } + + void testCountIfWithLoop() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + size_t count = 0; + for (auto i : NodeDfsIterable(cnd).inPostorder()) + { + if (i.isConst()) + { + ++count; + } + } + TS_ASSERT_EQUALS(count, 2); + } + + void testStlCountIf() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + Node top = d_nodeManager->mkNode(XOR, cnd, cnd); + + auto traversal = NodeDfsIterable(top).inPostorder(); + + size_t count = std::count_if(traversal.begin(), + traversal.end(), + [](TNode n) { return n.isConst(); }); + TS_ASSERT_EQUALS(count, 2); + } + + void testStlCopy() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + Node top = d_nodeManager->mkNode(XOR, cnd, cnd); + std::vector<TNode> expected = {tb, eb, cnd, top}; + + auto traversal = NodeDfsIterable(top).inPostorder(); + + std::vector<TNode> actual; + std::copy(traversal.begin(), traversal.end(), std::back_inserter(actual)); + TS_ASSERT_EQUALS(actual, expected); + } +}; + +class NodePreorderTraversalBlack : public CxxTest::TestSuite +{ + private: + NodeManager* d_nodeManager; + NodeManagerScope* d_scope; + + public: + void setUp() override + { + d_nodeManager = new NodeManager(NULL); + d_scope = new NodeManagerScope(d_nodeManager); + } + + void tearDown() override + { + delete d_scope; + delete d_nodeManager; + } + + void testPreincrementIteration() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + auto traversal = NodeDfsIterable(cnd).inPreorder(); + NodeDfsIterator i = traversal.begin(); + NodeDfsIterator end = traversal.end(); + TS_ASSERT_EQUALS(*i, cnd); + TS_ASSERT_DIFFERS(i, end); + ++i; + TS_ASSERT_EQUALS(*i, tb); + TS_ASSERT_DIFFERS(i, end); + ++i; + TS_ASSERT_EQUALS(*i, eb); + TS_ASSERT_DIFFERS(i, end); + ++i; + TS_ASSERT_EQUALS(i, end); + } + + void testPostincrementIteration() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + auto traversal = NodeDfsIterable(cnd).inPreorder(); + NodeDfsIterator i = traversal.begin(); + NodeDfsIterator end = traversal.end(); + TS_ASSERT_EQUALS(*(i++), cnd); + TS_ASSERT_EQUALS(*(i++), tb); + TS_ASSERT_EQUALS(*(i++), eb); + TS_ASSERT_EQUALS(i, end); + } + + void testRangeForLoop() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + size_t count = 0; + for (auto i : NodeDfsIterable(cnd).inPreorder()) + { + ++count; + } + TS_ASSERT_EQUALS(count, 3); + } + + void testCountIfWithLoop() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + + size_t count = 0; + for (auto i : NodeDfsIterable(cnd).inPreorder()) + { + if (i.isConst()) + { + ++count; + } + } + TS_ASSERT_EQUALS(count, 2); + } + + void testStlCountIf() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + Node top = d_nodeManager->mkNode(XOR, cnd, cnd); + + auto traversal = NodeDfsIterable(top).inPreorder(); + + size_t count = std::count_if(traversal.begin(), + traversal.end(), + [](TNode n) { return n.isConst(); }); + TS_ASSERT_EQUALS(count, 2); + } + + void testStlCopy() + { + Node tb = d_nodeManager->mkConst(true); + Node eb = d_nodeManager->mkConst(false); + Node cnd = d_nodeManager->mkNode(XOR, tb, eb); + Node top = d_nodeManager->mkNode(XOR, cnd, cnd); + std::vector<TNode> expected = {top, cnd, tb, eb}; + + auto traversal = NodeDfsIterable(top).inPreorder(); + + std::vector<TNode> actual; + std::copy(traversal.begin(), traversal.end(), std::back_inserter(actual)); + TS_ASSERT_EQUALS(actual, expected); + } +}; diff --git a/test/unit/preprocessing/pass_bv_gauss_white.h b/test/unit/preprocessing/pass_bv_gauss_white.h index 4037c7191..bd21ed69c 100644 --- a/test/unit/preprocessing/pass_bv_gauss_white.h +++ b/test/unit/preprocessing/pass_bv_gauss_white.h @@ -176,6 +176,7 @@ class TheoryBVGaussWhite : public CxxTest::TestSuite d_nm = NodeManager::fromExprManager(d_em); d_smt = new SmtEngine(d_em); d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); d_zero = bv::utils::mkZero(16); @@ -2373,7 +2374,7 @@ class TheoryBVGaussWhite : public CxxTest::TestSuite AssertionPipeline apipe; apipe.push_back(a); - passes::BVGauss bgauss(nullptr); + passes::BVGauss bgauss(nullptr, "bv-gauss-unit"); std::unordered_map<Node, Node, NodeHashFunction> res; PreprocessingPassResult pres = bgauss.applyInternal(&apipe); TS_ASSERT (pres == PreprocessingPassResult::NO_CONFLICT); @@ -2457,7 +2458,7 @@ class TheoryBVGaussWhite : public CxxTest::TestSuite apipe.push_back(a); apipe.push_back(eq4); apipe.push_back(eq5); - passes::BVGauss bgauss(nullptr); + passes::BVGauss bgauss(nullptr, "bv-gauss-unit"); std::unordered_map<Node, Node, NodeHashFunction> res; PreprocessingPassResult pres = bgauss.applyInternal(&apipe); TS_ASSERT (pres == PreprocessingPassResult::NO_CONFLICT); @@ -2507,7 +2508,7 @@ class TheoryBVGaussWhite : public CxxTest::TestSuite AssertionPipeline apipe; apipe.push_back(eq1); apipe.push_back(eq2); - passes::BVGauss bgauss(nullptr); + passes::BVGauss bgauss(nullptr, "bv-gauss-unit"); std::unordered_map<Node, Node, NodeHashFunction> res; PreprocessingPassResult pres = bgauss.applyInternal(&apipe); TS_ASSERT (pres == PreprocessingPassResult::NO_CONFLICT); diff --git a/test/unit/theory/evaluator_white.h b/test/unit/theory/evaluator_white.h index c02aaaed8..547ce27cf 100644 --- a/test/unit/theory/evaluator_white.h +++ b/test/unit/theory/evaluator_white.h @@ -52,6 +52,7 @@ class TheoryEvaluatorWhite : public CxxTest::TestSuite d_nm = NodeManager::fromExprManager(d_em); d_smt = new SmtEngine(d_em); d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); } void tearDown() override diff --git a/test/unit/theory/regexp_operation_black.h b/test/unit/theory/regexp_operation_black.h index 81f5cf34a..826e14a31 100644 --- a/test/unit/theory/regexp_operation_black.h +++ b/test/unit/theory/regexp_operation_black.h @@ -45,6 +45,10 @@ class RegexpOperationBlack : public CxxTest::TestSuite d_scope = new SmtScope(d_smt); d_regExpOpr = new RegExpOpr(); + // Ensure that the SMT engine is fully initialized (required for the + // rewriter) + d_smt->push(); + d_nm = NodeManager::currentNM(); } diff --git a/test/unit/theory/sequences_rewriter_white.h b/test/unit/theory/sequences_rewriter_white.h index 200a36d0b..c823c0704 100644 --- a/test/unit/theory/sequences_rewriter_white.h +++ b/test/unit/theory/sequences_rewriter_white.h @@ -45,6 +45,7 @@ class SequencesRewriterWhite : public CxxTest::TestSuite d_em = new ExprManager(opts); d_smt = new SmtEngine(d_em); d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); d_rewriter = new ExtendedRewriter(true); d_nm = NodeManager::currentNM(); diff --git a/test/unit/theory/theory_black.h b/test/unit/theory/theory_black.h index 8ae90c4c3..72b74a7b9 100644 --- a/test/unit/theory/theory_black.h +++ b/test/unit/theory/theory_black.h @@ -37,19 +37,15 @@ using namespace CVC4::theory; using namespace CVC4::smt; class TheoryBlack : public CxxTest::TestSuite { -private: - - ExprManager* d_em; - SmtEngine* d_smt; - NodeManager* d_nm; - SmtScope* d_scope; - public: void setUp() override { d_em = new ExprManager(); d_smt = new SmtEngine(d_em); d_scope = new SmtScope(d_smt); + // Ensure that the SMT engine is fully initialized (required for the + // rewriter) + d_smt->push(); d_nm = NodeManager::fromExprManager(d_em); } @@ -152,4 +148,9 @@ private: } + private: + ExprManager* d_em; + SmtEngine* d_smt; + NodeManager* d_nm; + SmtScope* d_scope; }; diff --git a/test/unit/theory/theory_bv_rewriter_white.h b/test/unit/theory/theory_bv_rewriter_white.h index bf0ca73b3..b6348339b 100644 --- a/test/unit/theory/theory_bv_rewriter_white.h +++ b/test/unit/theory/theory_bv_rewriter_white.h @@ -43,6 +43,7 @@ class TheoryBvRewriterWhite : public CxxTest::TestSuite d_em = new ExprManager(opts); d_smt = new SmtEngine(d_em); d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); d_nm = NodeManager::currentNM(); } diff --git a/test/unit/theory/theory_engine_white.h b/test/unit/theory/theory_engine_white.h index 5af670d5e..4a019ac08 100644 --- a/test/unit/theory/theory_engine_white.h +++ b/test/unit/theory/theory_engine_white.h @@ -38,6 +38,7 @@ #include "theory/rewriter.h" #include "theory/theory.h" #include "theory/theory_engine.h" +#include "theory/theory_rewriter.h" #include "theory/valuation.h" #include "util/integer.h" #include "util/proof.h" @@ -91,118 +92,72 @@ struct RewriteItem { bool d_topLevel; };/* struct RewriteItem */ +class FakeTheoryRewriter : public TheoryRewriter +{ + public: + RewriteResponse preRewrite(TNode n) override + { + return RewriteResponse(REWRITE_DONE, n); + } + + RewriteResponse postRewrite(TNode n) override + { + return RewriteResponse(REWRITE_DONE, n); + } +}; + /** - * Fake Theory interface. Looks like a Theory, but really all it does is note when and - * how rewriting behavior is requested. + * Fake Theory interface. Looks like a Theory, but really all it does is note + * when and how rewriting behavior is requested. */ -template<TheoryId theoryId> -class FakeTheory : public Theory { +template <TheoryId theoryId> +class FakeTheory : public Theory +{ /** - * This fake theory class is equally useful for bool, uf, arith, etc. It keeps an - * identifier to identify itself. + * This fake theory class is equally useful for bool, uf, arith, etc. It + * keeps an identifier to identify itself. */ std::string d_id; /** - * The expected sequence of rewrite calls. Filled by FakeTheory::expect() and consumed - * by FakeTheory::preRewrite() and FakeTheory::postRewrite(). + * The expected sequence of rewrite calls. Filled by FakeTheory::expect() and + * consumed by FakeTheory::preRewrite() and FakeTheory::postRewrite(). */ // static std::deque<RewriteItem> s_expected; -public: - FakeTheory(context::Context* ctxt, context::UserContext* uctxt, OutputChannel& out, Valuation valuation, const LogicInfo& logicInfo) : - Theory(theoryId, ctxt, uctxt, out, valuation, logicInfo) - { } - - /** Register an expected rewrite call */ - static void expect(RewriteType type, FakeTheory* thy, TNode n, bool topLevel) - throw() + public: + FakeTheory(context::Context* ctxt, + context::UserContext* uctxt, + OutputChannel& out, + Valuation valuation, + const LogicInfo& logicInfo) + : Theory(theoryId, ctxt, uctxt, out, valuation, logicInfo) { - RewriteItem item = { type, thy, n, topLevel }; - //s_expected.push_back(item); } - /** - * Returns whether the expected queue is empty. This is done after a call into - * the rewriter to ensure that the actual set of observed rewrite calls completed - * the sequence of expected rewrite calls. - */ - static bool nothingMoreExpected() throw() { - return true; // s_expected.empty(); + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() override + { + return std::unique_ptr<TheoryRewriter>(new FakeTheoryRewriter()); } - /** - * Overrides Theory::preRewrite(). This "fake theory" version ensures that - * this actual, observed pre-rewrite call matches the next "expected" call set up - * by the test. - */ - RewriteResponse preRewrite(TNode n, bool topLevel) { -// if(false) { //s_expected.empty()) { -// cout << std::endl -// << "didn't expect anything more, but got" << std::endl -// << " PRE " << topLevel << " " << identify() << " " << n -// << std::endl; -// } -// TS_ASSERT(!s_expected.empty()); -// -// RewriteItem expected = s_expected.front(); -// s_expected.pop_front(); -// -// if(expected.d_type != PRE || -//// expected.d_theory != this || -// expected.d_node != n || -// expected.d_topLevel != topLevel) { -// cout << std::endl -// << "HAVE PRE " << topLevel << " " << identify() << " " << n -// << std::endl -// << "WANT " << (expected.d_type == PRE ? "PRE " : "POST ") -// // << expected.d_topLevel << " " << expected.d_theory->identify() -// << " " << expected.d_node << std::endl << std::endl; -// } -// -// TS_ASSERT_EQUALS(expected.d_type, PRE); -//// TS_ASSERT_EQUALS(expected.d_theory, this); -// TS_ASSERT_EQUALS(expected.d_node, n); -// TS_ASSERT_EQUALS(expected.d_topLevel, topLevel); - - return RewriteResponse(REWRITE_DONE, n); + /** Register an expected rewrite call */ + static void expect(RewriteType type, + FakeTheory* thy, + TNode n, + bool topLevel) throw() + { + RewriteItem item = {type, thy, n, topLevel}; + // s_expected.push_back(item); } /** - * Overrides Theory::postRewrite(). This "fake theory" version ensures that - * this actual, observed post-rewrite call matches the next "expected" call set up - * by the test. + * Returns whether the expected queue is empty. This is done after a call + * into the rewriter to ensure that the actual set of observed rewrite calls + * completed the sequence of expected rewrite calls. */ - RewriteResponse postRewrite(TNode n, bool topLevel) { -// if(s_expected.empty()) { -// cout << std::endl -// << "didn't expect anything more, but got" << std::endl -// << " POST " << topLevel << " " << identify() << " " << n -// << std::endl; -// } -// TS_ASSERT(!s_expected.empty()); -// -// RewriteItem expected = s_expected.front(); -// s_expected.pop_front(); -// -// if(expected.d_type != POST || -//// expected.d_theory != this || -// expected.d_node != n || -// expected.d_topLevel != topLevel) { -// cout << std::endl -// << "HAVE POST " << topLevel << " " << identify() << " " << n -// << std::endl -// << "WANT " << (expected.d_type == PRE ? "PRE " : "POST ") -//// << expected.d_topLevel << " " << expected.d_theory->identify() -// << " " << expected.d_node << std::endl << std::endl; -// } -// -// TS_ASSERT_EQUALS(expected.d_type, POST); -// TS_ASSERT_EQUALS(expected.d_theory, this); -// TS_ASSERT_EQUALS(expected.d_node, n); -// TS_ASSERT_EQUALS(expected.d_topLevel, topLevel); - - return RewriteResponse(REWRITE_DONE, n); + static bool nothingMoreExpected() throw() + { + return true; // s_expected.empty(); } std::string identify() const override { @@ -221,8 +176,7 @@ public: return Node::null(); } Node getValue(TNode n) { return Node::null(); } -};/* class FakeTheory */ - +}; /* class FakeTheory */ /* definition of the s_expected static field in FakeTheory; see above */ // std::deque<RewriteItem> FakeTheory::s_expected; diff --git a/test/unit/theory/theory_quantifiers_bv_instantiator_white.h b/test/unit/theory/theory_quantifiers_bv_instantiator_white.h index 0fa7a3f82..7c37b1a85 100644 --- a/test/unit/theory/theory_quantifiers_bv_instantiator_white.h +++ b/test/unit/theory/theory_quantifiers_bv_instantiator_white.h @@ -65,6 +65,7 @@ void BvInstantiatorWhite::setUp() d_nm = NodeManager::fromExprManager(d_em); d_smt = new SmtEngine(d_em); d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); } void BvInstantiatorWhite::tearDown() diff --git a/test/unit/theory/theory_sets_type_enumerator_white.h b/test/unit/theory/theory_sets_type_enumerator_white.h index 3b5016fad..0ece5a056 100644 --- a/test/unit/theory/theory_sets_type_enumerator_white.h +++ b/test/unit/theory/theory_sets_type_enumerator_white.h @@ -18,9 +18,12 @@ #include <cxxtest/TestSuite.h> +#include "smt/smt_engine.h" +#include "smt/smt_engine_scope.h" #include "theory/sets/theory_sets_type_enumerator.h" using namespace CVC4; +using namespace CVC4::smt; using namespace CVC4::theory; using namespace CVC4::kind; using namespace CVC4::theory::sets; @@ -28,21 +31,20 @@ using namespace std; class SetEnumeratorWhite : public CxxTest::TestSuite { - ExprManager* d_em; - NodeManager* d_nm; - NodeManagerScope* d_scope; - public: void setUp() override { d_em = new ExprManager(); + d_smt = new SmtEngine(d_em); d_nm = NodeManager::fromExprManager(d_em); - d_scope = new NodeManagerScope(d_nm); + d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); } void tearDown() override { delete d_scope; + delete d_smt; delete d_em; } @@ -68,8 +70,9 @@ class SetEnumeratorWhite : public CxxTest::TestSuite TS_ASSERT_EQUALS(expected2, actual2); TS_ASSERT(!setEnumerator.isFinished()); - Node actual3 = *++setEnumerator; - Node expected3 = d_nm->mkNode(Kind::UNION, expected1, expected2); + Node actual3 = Rewriter::rewrite(*++setEnumerator); + Node expected3 = + Rewriter::rewrite(d_nm->mkNode(Kind::UNION, expected1, expected2)); TS_ASSERT_EQUALS(expected3, actual3); TS_ASSERT(!setEnumerator.isFinished()); @@ -301,4 +304,9 @@ class SetEnumeratorWhite : public CxxTest::TestSuite TS_ASSERT(setEnumerator.isFinished()); } + private: + ExprManager* d_em; + SmtEngine* d_smt; + NodeManager* d_nm; + SmtScope* d_scope; }; /* class SetEnumeratorWhite */ diff --git a/test/unit/theory/theory_strings_skolem_cache_black.h b/test/unit/theory/theory_strings_skolem_cache_black.h index 34e8d88c6..8f41153f3 100644 --- a/test/unit/theory/theory_strings_skolem_cache_black.h +++ b/test/unit/theory/theory_strings_skolem_cache_black.h @@ -16,13 +16,17 @@ #include <memory> +#include "expr/expr_manager.h" #include "expr/node.h" #include "expr/node_manager.h" +#include "smt/smt_engine.h" +#include "smt/smt_engine_scope.h" #include "theory/strings/skolem_cache.h" #include "util/rational.h" -#include "util/regexp.h" +#include "util/string.h" using namespace CVC4; +using namespace CVC4::smt; using namespace CVC4::theory::strings; class TheoryStringsSkolemCacheBlack : public CxxTest::TestSuite @@ -30,8 +34,14 @@ class TheoryStringsSkolemCacheBlack : public CxxTest::TestSuite public: void setUp() override { - d_nm.reset(new NodeManager(nullptr)); - d_scope.reset(new NodeManagerScope(d_nm.get())); + d_em.reset(new ExprManager()); + d_smt.reset(new SmtEngine(d_em.get())); + d_scope.reset(new SmtScope(d_smt.get())); + // Ensure that the SMT engine is fully initialized (required for the + // rewriter) + d_smt->push(); + + d_nm = NodeManager::fromExprManager(d_em.get()); } void tearDown() override {} @@ -87,6 +97,8 @@ class TheoryStringsSkolemCacheBlack : public CxxTest::TestSuite } private: - std::unique_ptr<NodeManager> d_nm; - std::unique_ptr<NodeManagerScope> d_scope; + std::unique_ptr<ExprManager> d_em; + std::unique_ptr<SmtEngine> d_smt; + NodeManager* d_nm; + std::unique_ptr<SmtScope> d_scope; }; diff --git a/test/unit/theory/theory_white.h b/test/unit/theory/theory_white.h index 0dd52be8c..eb43e00cb 100644 --- a/test/unit/theory/theory_white.h +++ b/test/unit/theory/theory_white.h @@ -93,7 +93,7 @@ class TestOutputChannel : public OutputChannel { }; class DummyTheory : public Theory { -public: + public: set<Node> d_registered; vector<Node> d_getSequence; @@ -102,6 +102,11 @@ public: : Theory(theory::THEORY_BUILTIN, ctxt, uctxt, out, valuation, logicInfo) {} + std::unique_ptr<TheoryRewriter> mkTheoryRewriter() + { + return std::unique_ptr<TheoryRewriter>(); + } + void registerTerm(TNode n) { // check that we registerTerm() a term only once TS_ASSERT(d_registered.find(n) == d_registered.end()); diff --git a/test/unit/theory/type_enumerator_white.h b/test/unit/theory/type_enumerator_white.h index da915b7ee..58dadd27a 100644 --- a/test/unit/theory/type_enumerator_white.h +++ b/test/unit/theory/type_enumerator_white.h @@ -25,30 +25,32 @@ #include "expr/node_manager.h" #include "expr/type_node.h" #include "options/language.h" +#include "smt/smt_engine.h" +#include "smt/smt_engine_scope.h" #include "theory/type_enumerator.h" using namespace CVC4; +using namespace CVC4::smt; using namespace CVC4::theory; using namespace CVC4::kind; using namespace std; class TypeEnumeratorWhite : public CxxTest::TestSuite { - ExprManager* d_em; - NodeManager* d_nm; - NodeManagerScope* d_scope; - public: void setUp() override { d_em = new ExprManager(); + d_smt = new SmtEngine(d_em); d_nm = NodeManager::fromExprManager(d_em); - d_scope = new NodeManagerScope(d_nm); + d_scope = new SmtScope(d_smt); + d_smt->finalOptionsAreSet(); } void tearDown() override { delete d_scope; + delete d_smt; delete d_em; } @@ -272,4 +274,9 @@ class TypeEnumeratorWhite : public CxxTest::TestSuite { TS_ASSERT_THROWS(*++te, NoMoreValuesException&); } + private: + ExprManager* d_em; + SmtEngine* d_smt; + NodeManager* d_nm; + SmtScope* d_scope; };/* class TypeEnumeratorWhite */ |