diff options
158 files changed, 14365 insertions, 1722 deletions
diff --git a/.travis.yml b/.travis.yml index f351e8a41..9b8482c00 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,9 +4,11 @@ compiler: - gcc - clang env: - - TRAVIS_CVC4_CONFIG='production --enable-language-bindings=java,c' - - TRAVIS_CVC4_CONFIG='debug --enable-language-bindings=java,c' - - TRAVIS_CVC4_DISTCHECK=yes + - TRAVIS_CVC4=yes TRAVIS_CVC4_CONFIG='production --enable-language-bindings=java,c' + - TRAVIS_CVC4=yes TRAVIS_CVC4_CONFIG='debug --enable-language-bindings=java,c' + - TRAVIS_CVC4=yes TRAVIS_CVC4_DISTCHECK=yes + - TRAVIS_LFSC=yes + - TRAVIS_LFSC=yes TRAVIS_LFSC_DISTCHECK=yes before_install: # dhart/ppa is for cxxtest package, which doesn't appear officially until quantal - travis_retry sudo apt-add-repository -y ppa:dhart/ppa @@ -17,17 +19,29 @@ before_script: - export PATH=$PATH:$JAVA_HOME/bin - export JAVA_CPPFLAGS=-I$JAVA_HOME/include - ./autogen.sh - - echo $TRAVIS_CVC4_CONFIG - - normal="$(echo -e '\033[0m')" red="$normal$(echo -e '\033[01;31m')" green="$normal$(echo -e '\033[01;32m')" - - ./configure --enable-unit-testing --enable-proof --with-portfolio $TRAVIS_CVC4_CONFIG || (echo; cat builds/config.log; echo; echo "${red}CONFIGURE FAILED${normal}"; exit 1) script: - normal="$(echo -e '\033[0m')" red="$normal$(echo -e '\033[01;31m')" green="$normal$(echo -e '\033[01;32m')" - - if [ -n "$TRAVIS_CVC4_DISTCHECK" ]; then - make -j2 distcheck CVC4_REGRESSION_ARGS='--no-early-exit' || (echo; echo "${red}DISTCHECK FAILED${normal}"; echo; exit 1); + - if [ -n "$TRAVIS_CVC4" ]; then + echo "CVC4 config - $TRAVIS_CVC4_CONFIG" && + (./configure --enable-unit-testing --enable-proof --with-portfolio $TRAVIS_CVC4_CONFIG || (echo; cat builds/config.log; echo; echo "${red}CONFIGURE FAILED${normal}"; exit 1)) && + if [ -n "$TRAVIS_CVC4_DISTCHECK" ]; then + make -j2 distcheck CVC4_REGRESSION_ARGS='--no-early-exit' || (echo; echo "${red}DISTCHECK FAILED${normal}"; echo; exit 1); + else + (make -j2 check CVC4_REGRESSION_ARGS='--no-early-exit' || (echo; echo "${red}BUILD/TEST FAILED${normal}"; echo; exit 1)) && + (make check BINARY=pcvc4 CVC4_REGRESSION_ARGS='--fallback-sequential --no-early-exit' RUN_REGRESSION_ARGS= || (echo; echo "${red}PORTFOLIO TEST FAILED${normal}"; echo; exit 1)) && + (make -j2 examples || (echo; echo "${red}COULD NOT BUILD EXAMPLES${normal}"; echo; exit 1)); + fi; + elif [ -n "$TRAVIS_LFSC" ]; then + cd proofs/lfsc_checker && + (./configure || (echo; cat builds/config.log; echo; echo "${red}CONFIGURE FAILED${normal}"; exit 1)) && + if [ -n "$TRAVIS_LFSC_DISTCHECK" ]; then + make -j2 distcheck || (echo; echo "${red}LFSC DISTCHECK FAILED${normal}"; echo; exit 1); + else + make -j2 || (echo; echo "${red}LFSC BUILD FAILED${normal}"; echo; exit 1); + fi; else - (make -j2 check CVC4_REGRESSION_ARGS='--no-early-exit' || (echo; echo "${red}BUILD/TEST FAILED${normal}"; echo; exit 1)) && - (make check BINARY=pcvc4 CVC4_REGRESSION_ARGS='--fallback-sequential --no-early-exit' || (echo; echo "${red}PORTFOLIO TEST FAILED${normal}"; echo; exit 1)) && - (make -j2 examples || (echo; echo "${red}COULD NOT BUILD EXAMPLES${normal}"; echo; exit 1)); + echo "${red}Unknown Travis-CI configuration${normal}"; + exit 1; fi && (echo; echo "${green}EVERYTHING SEEMED TO PASS!${normal}") matrix: @@ -272,3 +272,24 @@ you build a version of CVC4 that uses no GPLed libraries, configure CVC4 with the "--bsd" option before building (which is the default). CVC4 can then be used in contexts where you want to license CVC4 under the (modified) BSD license. + +CVC4 sources incorporate those of the LFSC proof checker, which is +covered by the following license: + + LFSC is copyright (C) 2012, 2013 The University of Iowa. All rights + reserved. + + LFSC is open-source; distribution is under the terms of the modified + BSD license. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT OWNERS AND CONTRIBUTORS + AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Makefile.am b/Makefile.am index 41586cbe7..55c357def 100644 --- a/Makefile.am +++ b/Makefile.am @@ -5,8 +5,13 @@ AM_CXXFLAGS = -Wall -Wno-unknown-pragmas AUTOMAKE_OPTIONS = foreign ACLOCAL_AMFLAGS = -I config -SUBDIRS = src test contrib -DIST_SUBDIRS = $(SUBDIRS) examples +SUBDIRS_BASE = src test contrib +if CVC4_PROOF + SUBDIRS = proofs/signatures proofs/lfsc_checker $(SUBDIRS_BASE) +else + SUBDIRS = $(SUBDIRS_BASE) +endif +DIST_SUBDIRS = proofs/signatures proofs/lfsc_checker $(SUBDIRS_BASE) examples .PHONY: examples examples: all @@ -126,6 +131,7 @@ EXTRA_DIST = \ doc/options.3cvc_template.in \ doc/libcvc4parser.3.in \ doc/libcvc4compat.3.in + man_MANS = \ doc/cvc4.1 \ doc/pcvc4.1 \ diff --git a/Makefile.builds.in b/Makefile.builds.in index 33df24f95..296e5a974 100644 --- a/Makefile.builds.in +++ b/Makefile.builds.in @@ -192,12 +192,12 @@ endif # The descent into "src" with target "check" is to build check # prerequisites (e.g. CHECK_PROGRAMS, CHECK_LTLIBRARIES, ...). -check test units: +check test units: all (cd $(CURRENT_BUILD)/src && $(MAKE) check) +(cd $(CURRENT_BUILD)/test && $(MAKE) $@) systemtests regress: all +(cd $(CURRENT_BUILD)/test && $(MAKE) $@) -units%: +units%: all (cd $(CURRENT_BUILD)/src && $(MAKE) check) +(cd $(CURRENT_BUILD)/test && $(MAKE) units TEST_PREFIX=$(subst units:,,$@)) regress%: all @@ -4,6 +4,7 @@ Changes since 1.3 ================= * Timed statistics are now properly updated even on process abort. +* The LFSC proof checker has been incorporated into CVC4 sources. * By default, CVC4 builds in "production" mode (optimized, with fewer internal checks on). The common alternative is a "debug" build, which is much slower. By default, CVC4 builds with no GPL'ed dependences. @@ -11,6 +12,8 @@ Changes since 1.3 configure with "--enable-gpl --best", which links against GPL'ed libraries that improve usability and performance. For details on licensing and dependences, see the README file. +* Small API adjustments to Datatypes to even out the API and make it + function better in Java. * Better automatic handling of output language setting when using CVC4 via API. Previously, the "automatic" language setting was sometimes (though not always) defaulting to the internal "AST" language; it diff --git a/config/cvc4.m4 b/config/cvc4.m4 index 7c2f6c82d..40e2054e6 100644 --- a/config/cvc4.m4 +++ b/config/cvc4.m4 @@ -42,6 +42,9 @@ handle_option() { handle_option --with-cln return ;; + -enable-proofs|--enable-proofs) + ac_option='--enable-proof' + ;; -*|*=*) ;; production|production-*|debug|debug-*|competition|competition-*) diff --git a/configure.ac b/configure.ac index 24ebf95f8..bf1e0dd0c 100644 --- a/configure.ac +++ b/configure.ac @@ -2,8 +2,8 @@ # Process this file with autoconf to produce a configure script. m4_define(_CVC4_MAJOR, 1) dnl version (major) -m4_define(_CVC4_MINOR, 3) dnl version (minor) -m4_define(_CVC4_RELEASE, 1) dnl version (alpha) +m4_define(_CVC4_MINOR, 4) dnl version (minor) +m4_define(_CVC4_RELEASE, 0) dnl version (alpha) m4_define(_CVC4_EXTRAVERSION, [-prerelease]) dnl version (extra) m4_define(_CVC4_RELEASE_STRING, _CVC4_MAJOR[.]_CVC4_MINOR[]m4_if(_CVC4_RELEASE,[0],,[.]_CVC4_RELEASE)_CVC4_EXTRAVERSION) dnl version string @@ -16,6 +16,7 @@ AC_CONFIG_SRCDIR([src/include/cvc4_public.h]) AC_CONFIG_AUX_DIR([config]) AC_CONFIG_MACRO_DIR([config]) AC_CONFIG_LIBOBJ_DIR([src/lib]) +AC_CONFIG_SUBDIRS([proofs/lfsc_checker]) m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) @@ -533,6 +534,7 @@ AC_MSG_RESULT([$enable_proof]) if test "$enable_proof" = yes; then CVC4CPPFLAGS="${CVC4CPPFLAGS:+$CVC4CPPFLAGS }-DCVC4_PROOF" fi +AM_CONDITIONAL([CVC4_PROOF], [test "$enable_proof" = yes]) AC_MSG_CHECKING([whether to optimize libcvc4]) @@ -896,29 +898,13 @@ if test "$CVC4_CONFIGURE_IN_BUILDS" = yes -a -n "$CXXTEST"; then esac fi -AC_ARG_VAR(LFSC, [path to LFSC proof checker]) -AC_ARG_VAR(LFSCARGS, [arguments to pass to LFSC proof checker]) -if test -z "$LFSC"; then - AC_CHECK_PROGS(LFSC, lfsc, [], []) -else - AC_CHECK_PROG(LFSC, "$LFSC", [], []) -fi -AM_CONDITIONAL([PROOF_REGRESSIONS], [test -n "$LFSC" -a "$enable_proof" = yes]) -if test -n "$LFSC" -a "$enable_proof" = yes; then - TESTS_ENVIRONMENT="${TESTS_ENVIRONMENT:+$TESTS_ENVIRONMENT }LFSC=\"$LFSC $LFSCARGS\"" +TESTS_ENVIRONMENT= +RUN_REGRESSION_ARGS= +if test "$enable_proof" = yes; then RUN_REGRESSION_ARGS="${RUN_REGRESSION_ARGS:+$RUN_REGRESSION_ARGS }--proof" fi AC_SUBST([TESTS_ENVIRONMENT]) AC_SUBST([RUN_REGRESSION_ARGS]) -if test -z "$LFSC"; then - support_proof_tests='no, lfsc proof checker unavailable' -elif test "$enable_proof" = yes; then - support_proof_tests='yes, proof regression tests enabled' -else - support_proof_tests='no, proof-generation disabled for this build' -fi -AC_SUBST([LFSC]) -AC_SUBST([LFSCARGS]) CXXTESTGEN= AC_PATH_PROG(CXXTESTGEN, cxxtestgen.pl, [], [$CXXTEST:$PATH]) @@ -1290,7 +1276,8 @@ AC_SUBST(MAN_DATE) AC_CONFIG_FILES([ Makefile.builds - Makefile] + Makefile + proofs/signatures/Makefile] m4_esyscmd([find contrib src test examples -name Makefile.am | grep -v '^contrib/theoryskel/' | grep -v '^contrib/alttheoryskel/' | sort | sed 's,\.am$,,']) ) @@ -1428,7 +1415,6 @@ Dumping : $enable_dumping Muzzle : $enable_muzzle Unit tests : $support_unit_tests -Proof tests : $support_proof_tests gcov support : $enable_coverage gprof support: $enable_profiling diff --git a/examples/README b/examples/README index 246388085..d64ed3469 100644 --- a/examples/README +++ b/examples/README @@ -10,6 +10,12 @@ world" examples, and do not fully demonstrate the interfaces, but function as a starting point to using simple expressions and solving functionality through each library. +*** Targetted examples + +The "api" directory contains some more specifically-targetted +examples (for bitvectors, for arithmetic, etc.). The "api/java" +directory contains the same examples in Java. + *** Installing example source code Examples are not automatically installed by "make install". If you @@ -21,13 +27,8 @@ in /usr/local/share/doc/cvc4/examples). *** Building examples Examples can be built as a separate step, after building CVC4 from -source. After building CVC4, you can run "make examples" (or just -"make" from *inside* the examples directory). You'll find the built -binaries in builds/examples (or just in "examples" if you configured a -build directory outside of the source tree). - -Many of the language bindings examples (python, ocaml, ruby, etc.) do -not need to be compiled to run. These are not compiled by -"make"---see the comments in the files for ideas on how to run them. +source. After building CVC4, you can run "make examples". You'll +find the built binaries in builds/examples (or just in "examples" if +you configured a build directory outside of the source tree). --- Morgan Deters <mdeters@cs.nyu.edu> Wed, 03 Oct 2012 15:47:33 -0400 +-- Morgan Deters <mdeters@cs.nyu.edu> Tue, 24 Dec 2013 09:12:59 -0500 diff --git a/examples/api/Makefile.am b/examples/api/Makefile.am index 6dba17b05..0d0236376 100644 --- a/examples/api/Makefile.am +++ b/examples/api/Makefile.am @@ -10,8 +10,8 @@ noinst_PROGRAMS = \ helloworld \ combination \ bitvectors \ - bitvectors_and_arrays - + bitvectors_and_arrays \ + datatypes noinst_DATA = @@ -40,6 +40,11 @@ bitvectors_SOURCES = \ bitvectors_LDADD = \ @builddir@/../../src/libcvc4.la +datatypes_SOURCES = \ + datatypes.cpp +datatypes_LDADD = \ + @builddir@/../../src/libcvc4.la + # for installation examplesdir = $(docdir)/$(subdir) examples_DATA = $(DIST_SOURCES) $(EXTRA_DIST) diff --git a/examples/api/datatypes.cpp b/examples/api/datatypes.cpp new file mode 100644 index 000000000..dea83a95a --- /dev/null +++ b/examples/api/datatypes.cpp @@ -0,0 +1,103 @@ +/********************* */ +/*! \file datatypes.cpp + ** \verbatim + ** Original author: Morgan Deters + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief An example of using inductive datatypes in CVC4 + ** + ** An example of using inductive datatypes in CVC4. + **/ + +#include <iostream> +#include "smt/smt_engine.h" // for use with make examples +#include "util/language.h" // for use with make examples +//#include <cvc4/cvc4.h> // To follow the wiki + +using namespace CVC4; + +int main() { + ExprManager em; + SmtEngine smt(&em); + + // This example builds a simple "cons list" of integers, with + // two constructors, "cons" and "nil." + + // Building a datatype consists of two steps. First, the datatype + // is specified. Second, it is "resolved"---at which point function + // symbols are assigned to its constructors, selectors, and testers. + + Datatype consListSpec("list"); // give the datatype a name + DatatypeConstructor cons("cons"); + cons.addArg("head", em.integerType()); + cons.addArg("tail", DatatypeSelfType()); // a list + consListSpec.addConstructor(cons); + DatatypeConstructor nil("nil"); + consListSpec.addConstructor(nil); + + std::cout << "spec is:" << std::endl + << consListSpec << std::endl; + + // Keep in mind that "Datatype" is the specification class for + // datatypes---"Datatype" is not itself a CVC4 Type. Now that + // our Datatype is fully specified, we can get a Type for it. + // This step resolves the "SelfType" reference and creates + // symbols for all the constructors, etc. + + DatatypeType consListType = em.mkDatatypeType(consListSpec); + + // Now our old "consListSpec" is useless--the relevant information + // has been copied out, so we can throw that spec away. We can get + // the complete spec for the datatype from the DatatypeType, and + // this Datatype object has constructor symbols (and others) filled in. + + const Datatype& consList = consListType.getDatatype(); + + // e = cons 0 nil + // + // Here, consList["cons"] gives you the DatatypeConstructor. To get + // the constructor symbol for application, use .getConstructor("cons"), + // which is equivalent to consList["cons"].getConstructor(). Note that + // "nil" is a constructor too, so it needs to be applied with + // APPLY_CONSTRUCTOR, even though it has no arguments. + Expr e = em.mkExpr(kind::APPLY_CONSTRUCTOR, + consList.getConstructor("cons"), + em.mkConst(Rational(0)), + em.mkExpr(kind::APPLY_CONSTRUCTOR, + consList.getConstructor("nil"))); + + std::cout << "e is " << e << std::endl + << "type of cons is " << consList.getConstructor("cons").getType() + << std::endl + << "type of nil is " << consList.getConstructor("nil").getType() + << std::endl; + + // e2 = head(cons 0 nil), and of course this can be evaluated + // + // Here we first get the DatatypeConstructor for cons (with + // consList["cons"]) in order to get the "head" selector symbol + // to apply. + Expr e2 = em.mkExpr(kind::APPLY_SELECTOR, + consList["cons"].getSelector("head"), + e); + + std::cout << "e2 is " << e2 << std::endl + << "simplify(e2) is " << smt.simplify(e2) + << std::endl << std::endl; + + // You can also iterate over a Datatype to get all its constructors, + // and over a DatatypeConstructor to get all its "args" (selectors) + for(Datatype::iterator i = consList.begin(); i != consList.end(); ++i) { + std::cout << "ctor: " << *i << std::endl; + for(DatatypeConstructor::iterator j = (*i).begin(); j != (*i).end(); ++j) { + std::cout << " + arg: " << *j << std::endl; + } + } + + return 0; +} diff --git a/examples/api/java/Datatypes.java b/examples/api/java/Datatypes.java new file mode 100644 index 000000000..9406031c1 --- /dev/null +++ b/examples/api/java/Datatypes.java @@ -0,0 +1,103 @@ +/********************* */ +/*! \file Datatypes.java + ** \verbatim + ** Original author: Morgan Deters + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** See the file COPYING in the top-level source directory for licensing + ** information.\endverbatim + ** + ** \brief An example of using inductive datatypes in CVC4 (Java version) + ** + ** An example of using inductive datatypes in CVC4 (Java version). + **/ + +import edu.nyu.acsys.CVC4.*; +import java.util.Iterator; + +public class Datatypes { + public static void main(String[] args) { + System.loadLibrary("cvc4jni"); + + ExprManager em = new ExprManager(); + SmtEngine smt = new SmtEngine(em); + + // This example builds a simple "cons list" of integers, with + // two constructors, "cons" and "nil." + + // Building a datatype consists of two steps. First, the datatype + // is specified. Second, it is "resolved"---at which point function + // symbols are assigned to its constructors, selectors, and testers. + + Datatype consListSpec = new Datatype("list"); // give the datatype a name + DatatypeConstructor cons = new DatatypeConstructor("cons"); + cons.addArg("head", em.integerType()); + cons.addArg("tail", new DatatypeSelfType()); // a list + consListSpec.addConstructor(cons); + DatatypeConstructor nil = new DatatypeConstructor("nil"); + consListSpec.addConstructor(nil); + + System.out.println("spec is:"); + System.out.println(consListSpec); + + // Keep in mind that "Datatype" is the specification class for + // datatypes---"Datatype" is not itself a CVC4 Type. Now that + // our Datatype is fully specified, we can get a Type for it. + // This step resolves the "SelfType" reference and creates + // symbols for all the constructors, etc. + + DatatypeType consListType = em.mkDatatypeType(consListSpec); + + // Now our old "consListSpec" is useless--the relevant information + // has been copied out, so we can throw that spec away. We can get + // the complete spec for the datatype from the DatatypeType, and + // this Datatype object has constructor symbols (and others) filled in. + + Datatype consList = consListType.getDatatype(); + + // e = cons 0 nil + // + // Here, consList.get("cons") gives you the DatatypeConstructor + // (just as consList["cons"] does in C++). To get the constructor + // symbol for application, use .getConstructor("cons"), which is + // equivalent to consList.get("cons").getConstructor(). Note that + // "nil" is a constructor too, so it needs to be applied with + // APPLY_CONSTRUCTOR, even though it has no arguments. + Expr e = em.mkExpr(Kind.APPLY_CONSTRUCTOR, + consList.getConstructor("cons"), + em.mkConst(new Rational(0)), + em.mkExpr(Kind.APPLY_CONSTRUCTOR, + consList.getConstructor("nil"))); + + System.out.println("e is " + e); + System.out.println("type of cons is " + + consList.getConstructor("cons").getType()); + System.out.println("type of nil is " + + consList.getConstructor("nil").getType()); + + // e2 = head(cons 0 nil), and of course this can be evaluated + // + // Here we first get the DatatypeConstructor for cons (with + // consList.get("cons") in order to get the "head" selector + // symbol to apply. + Expr e2 = em.mkExpr(Kind.APPLY_SELECTOR, + consList.get("cons").getSelector("head"), + e); + + System.out.println("e2 is " + e2); + System.out.println("simplify(e2) is " + smt.simplify(e2)); + System.out.println(); + + // You can also iterate over a Datatype to get all its constructors, + // and over a DatatypeConstructor to get all its "args" (selectors) + for(Iterator<DatatypeConstructor> i = consList.iterator(); i.hasNext();) { + DatatypeConstructor ctor = i.next(); + System.out.println("ctor: " + ctor); + for(Iterator j = ctor.iterator(); j.hasNext();) { + System.out.println(" + arg: " + j.next()); + } + } + } +} diff --git a/examples/api/java/Makefile.am b/examples/api/java/Makefile.am index f4b8f1043..7216d758e 100644 --- a/examples/api/java/Makefile.am +++ b/examples/api/java/Makefile.am @@ -8,6 +8,7 @@ noinst_DATA += \ Combination.class \ HelloWorld.class \ LinearArith.class \ + Datatypes.class \ PipedInput.class endif @@ -21,6 +22,7 @@ EXTRA_DIST = \ Combination.java \ HelloWorld.java \ LinearArith.java \ + Datatypes.java \ PipedInput.java # for installation diff --git a/library_versions b/library_versions index 7c519729b..865ea8d77 100644 --- a/library_versions +++ b/library_versions @@ -55,3 +55,4 @@ 1\.3-prerelease libcvc4:2:0:0 libcvc4parser:2:0:0 libcvc4compat:2:0:0 libcvc4bindings:2:0:0 1\.3 libcvc4:2:0:0 libcvc4parser:2:0:0 libcvc4compat:2:0:0 libcvc4bindings:2:0:0 1\.3\.1-prerelease libcvc4:2:0:0 libcvc4parser:2:0:0 libcvc4compat:2:0:0 libcvc4bindings:2:0:0 +1\.4-prerelease libcvc4:2:0:0 libcvc4parser:2:0:0 libcvc4compat:2:0:0 libcvc4bindings:2:0:0 diff --git a/proofs/lfsc_checker/.gitignore b/proofs/lfsc_checker/.gitignore new file mode 100644 index 000000000..1f799d15a --- /dev/null +++ b/proofs/lfsc_checker/.gitignore @@ -0,0 +1,16 @@ +/autom4te.cache +/stamp-h +/config.h.in +/config.log +/config.status +/config.cache +/libtool +/stamp-h1 +.dep +Makefile.in +/configure +/aclocal.m4 +*~ +\#*\# +/config/ +*.swp diff --git a/proofs/lfsc_checker/AUTHORS b/proofs/lfsc_checker/AUTHORS new file mode 100644 index 000000000..0bd0a37b0 --- /dev/null +++ b/proofs/lfsc_checker/AUTHORS @@ -0,0 +1,5 @@ +The core authors and designers of the LFSC proof checker are: + + Andy Reynolds + Aaron Stump + diff --git a/proofs/lfsc_checker/COPYING b/proofs/lfsc_checker/COPYING new file mode 100644 index 000000000..b220a3147 --- /dev/null +++ b/proofs/lfsc_checker/COPYING @@ -0,0 +1,17 @@ +LFSC is copyright (C) 2012, 2013 The University of Iowa. +All rights reserved. + +LFSC is open-source; distribution is under the terms of the modified +BSD license. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT OWNERS AND CONTRIBUTORS +AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/proofs/lfsc_checker/INSTALL b/proofs/lfsc_checker/INSTALL new file mode 100644 index 000000000..a1e89e18a --- /dev/null +++ b/proofs/lfsc_checker/INSTALL @@ -0,0 +1,370 @@ +Installation Instructions +************************* + +Copyright (C) 1994-1996, 1999-2002, 2004-2011 Free Software Foundation, +Inc. + + Copying and distribution of this file, with or without modification, +are permitted in any medium without royalty provided the copyright +notice and this notice are preserved. This file is offered as-is, +without warranty of any kind. + +Basic Installation +================== + + Briefly, the shell commands `./configure; make; make install' should +configure, build, and install this package. The following +more-detailed instructions are generic; see the `README' file for +instructions specific to this package. Some packages provide this +`INSTALL' file but do not implement all of the features documented +below. The lack of an optional feature in a given package is not +necessarily a bug. More recommendations for GNU packages can be found +in *note Makefile Conventions: (standards)Makefile Conventions. + + The `configure' shell script attempts to guess correct values for +various system-dependent variables used during compilation. It uses +those values to create a `Makefile' in each directory of the package. +It may also create one or more `.h' files containing system-dependent +definitions. Finally, it creates a shell script `config.status' that +you can run in the future to recreate the current configuration, and a +file `config.log' containing compiler output (useful mainly for +debugging `configure'). + + It can also use an optional file (typically called `config.cache' +and enabled with `--cache-file=config.cache' or simply `-C') that saves +the results of its tests to speed up reconfiguring. Caching is +disabled by default to prevent problems with accidental use of stale +cache files. + + If you need to do unusual things to compile the package, please try +to figure out how `configure' could check whether to do them, and mail +diffs or instructions to the address given in the `README' so they can +be considered for the next release. If you are using the cache, and at +some point `config.cache' contains results you don't want to keep, you +may remove or edit it. + + The file `configure.ac' (or `configure.in') is used to create +`configure' by a program called `autoconf'. You need `configure.ac' if +you want to change it or regenerate `configure' using a newer version +of `autoconf'. + + The simplest way to compile this package is: + + 1. `cd' to the directory containing the package's source code and type + `./configure' to configure the package for your system. + + Running `configure' might take a while. While running, it prints + some messages telling which features it is checking for. + + 2. Type `make' to compile the package. + + 3. Optionally, type `make check' to run any self-tests that come with + the package, generally using the just-built uninstalled binaries. + + 4. Type `make install' to install the programs and any data files and + documentation. When installing into a prefix owned by root, it is + recommended that the package be configured and built as a regular + user, and only the `make install' phase executed with root + privileges. + + 5. Optionally, type `make installcheck' to repeat any self-tests, but + this time using the binaries in their final installed location. + This target does not install anything. Running this target as a + regular user, particularly if the prior `make install' required + root privileges, verifies that the installation completed + correctly. + + 6. You can remove the program binaries and object files from the + source code directory by typing `make clean'. To also remove the + files that `configure' created (so you can compile the package for + a different kind of computer), type `make distclean'. There is + also a `make maintainer-clean' target, but that is intended mainly + for the package's developers. If you use it, you may have to get + all sorts of other programs in order to regenerate files that came + with the distribution. + + 7. Often, you can also type `make uninstall' to remove the installed + files again. In practice, not all packages have tested that + uninstallation works correctly, even though it is required by the + GNU Coding Standards. + + 8. Some packages, particularly those that use Automake, provide `make + distcheck', which can by used by developers to test that all other + targets like `make install' and `make uninstall' work correctly. + This target is generally not run by end users. + +Compilers and Options +===================== + + Some systems require unusual options for compilation or linking that +the `configure' script does not know about. Run `./configure --help' +for details on some of the pertinent environment variables. + + You can give `configure' initial values for configuration parameters +by setting variables in the command line or in the environment. Here +is an example: + + ./configure CC=c99 CFLAGS=-g LIBS=-lposix + + *Note Defining Variables::, for more details. + +Compiling For Multiple Architectures +==================================== + + You can compile the package for more than one kind of computer at the +same time, by placing the object files for each architecture in their +own directory. To do this, you can use GNU `make'. `cd' to the +directory where you want the object files and executables to go and run +the `configure' script. `configure' automatically checks for the +source code in the directory that `configure' is in and in `..'. This +is known as a "VPATH" build. + + With a non-GNU `make', it is safer to compile the package for one +architecture at a time in the source code directory. After you have +installed the package for one architecture, use `make distclean' before +reconfiguring for another architecture. + + On MacOS X 10.5 and later systems, you can create libraries and +executables that work on multiple system types--known as "fat" or +"universal" binaries--by specifying multiple `-arch' options to the +compiler but only a single `-arch' option to the preprocessor. Like +this: + + ./configure CC="gcc -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CXX="g++ -arch i386 -arch x86_64 -arch ppc -arch ppc64" \ + CPP="gcc -E" CXXCPP="g++ -E" + + This is not guaranteed to produce working output in all cases, you +may have to build one architecture at a time and combine the results +using the `lipo' tool if you have problems. + +Installation Names +================== + + By default, `make install' installs the package's commands under +`/usr/local/bin', include files under `/usr/local/include', etc. You +can specify an installation prefix other than `/usr/local' by giving +`configure' the option `--prefix=PREFIX', where PREFIX must be an +absolute file name. + + You can specify separate installation prefixes for +architecture-specific files and architecture-independent files. If you +pass the option `--exec-prefix=PREFIX' to `configure', the package uses +PREFIX as the prefix for installing programs and libraries. +Documentation and other data files still use the regular prefix. + + In addition, if you use an unusual directory layout you can give +options like `--bindir=DIR' to specify different values for particular +kinds of files. Run `configure --help' for a list of the directories +you can set and what kinds of files go in them. In general, the +default for these options is expressed in terms of `${prefix}', so that +specifying just `--prefix' will affect all of the other directory +specifications that were not explicitly provided. + + The most portable way to affect installation locations is to pass the +correct locations to `configure'; however, many packages provide one or +both of the following shortcuts of passing variable assignments to the +`make install' command line to change installation locations without +having to reconfigure or recompile. + + The first method involves providing an override variable for each +affected directory. For example, `make install +prefix=/alternate/directory' will choose an alternate location for all +directory configuration variables that were expressed in terms of +`${prefix}'. Any directories that were specified during `configure', +but not in terms of `${prefix}', must each be overridden at install +time for the entire installation to be relocated. The approach of +makefile variable overrides for each directory variable is required by +the GNU Coding Standards, and ideally causes no recompilation. +However, some platforms have known limitations with the semantics of +shared libraries that end up requiring recompilation when using this +method, particularly noticeable in packages that use GNU Libtool. + + The second method involves providing the `DESTDIR' variable. For +example, `make install DESTDIR=/alternate/directory' will prepend +`/alternate/directory' before all installation names. The approach of +`DESTDIR' overrides is not required by the GNU Coding Standards, and +does not work on platforms that have drive letters. On the other hand, +it does better at avoiding recompilation issues, and works well even +when some directory options were not specified in terms of `${prefix}' +at `configure' time. + +Optional Features +================= + + If the package supports it, you can cause programs to be installed +with an extra prefix or suffix on their names by giving `configure' the +option `--program-prefix=PREFIX' or `--program-suffix=SUFFIX'. + + Some packages pay attention to `--enable-FEATURE' options to +`configure', where FEATURE indicates an optional part of the package. +They may also pay attention to `--with-PACKAGE' options, where PACKAGE +is something like `gnu-as' or `x' (for the X Window System). The +`README' should mention any `--enable-' and `--with-' options that the +package recognizes. + + For packages that use the X Window System, `configure' can usually +find the X include and library files automatically, but if it doesn't, +you can use the `configure' options `--x-includes=DIR' and +`--x-libraries=DIR' to specify their locations. + + Some packages offer the ability to configure how verbose the +execution of `make' will be. For these packages, running `./configure +--enable-silent-rules' sets the default to minimal output, which can be +overridden with `make V=1'; while running `./configure +--disable-silent-rules' sets the default to verbose, which can be +overridden with `make V=0'. + +Particular systems +================== + + On HP-UX, the default C compiler is not ANSI C compatible. If GNU +CC is not installed, it is recommended to use the following options in +order to use an ANSI C compiler: + + ./configure CC="cc -Ae -D_XOPEN_SOURCE=500" + +and if that doesn't work, install pre-built binaries of GCC for HP-UX. + + HP-UX `make' updates targets which have the same time stamps as +their prerequisites, which makes it generally unusable when shipped +generated files such as `configure' are involved. Use GNU `make' +instead. + + On OSF/1 a.k.a. Tru64, some versions of the default C compiler cannot +parse its `<wchar.h>' header file. The option `-nodtk' can be used as +a workaround. If GNU CC is not installed, it is therefore recommended +to try + + ./configure CC="cc" + +and if that doesn't work, try + + ./configure CC="cc -nodtk" + + On Solaris, don't put `/usr/ucb' early in your `PATH'. This +directory contains several dysfunctional programs; working variants of +these programs are available in `/usr/bin'. So, if you need `/usr/ucb' +in your `PATH', put it _after_ `/usr/bin'. + + On Haiku, software installed for all users goes in `/boot/common', +not `/usr/local'. It is recommended to use the following options: + + ./configure --prefix=/boot/common + +Specifying the System Type +========================== + + There may be some features `configure' cannot figure out +automatically, but needs to determine by the type of machine the package +will run on. Usually, assuming the package is built to be run on the +_same_ architectures, `configure' can figure that out, but if it prints +a message saying it cannot guess the machine type, give it the +`--build=TYPE' option. TYPE can either be a short name for the system +type, such as `sun4', or a canonical name which has the form: + + CPU-COMPANY-SYSTEM + +where SYSTEM can have one of these forms: + + OS + KERNEL-OS + + See the file `config.sub' for the possible values of each field. If +`config.sub' isn't included in this package, then this package doesn't +need to know the machine type. + + If you are _building_ compiler tools for cross-compiling, you should +use the option `--target=TYPE' to select the type of system they will +produce code for. + + If you want to _use_ a cross compiler, that generates code for a +platform different from the build platform, you should specify the +"host" platform (i.e., that on which the generated programs will +eventually be run) with `--host=TYPE'. + +Sharing Defaults +================ + + If you want to set default values for `configure' scripts to share, +you can create a site shell script called `config.site' that gives +default values for variables like `CC', `cache_file', and `prefix'. +`configure' looks for `PREFIX/share/config.site' if it exists, then +`PREFIX/etc/config.site' if it exists. Or, you can set the +`CONFIG_SITE' environment variable to the location of the site script. +A warning: not all `configure' scripts look for a site script. + +Defining Variables +================== + + Variables not defined in a site shell script can be set in the +environment passed to `configure'. However, some packages may run +configure again during the build, and the customized values of these +variables may be lost. In order to avoid this problem, you should set +them in the `configure' command line, using `VAR=value'. For example: + + ./configure CC=/usr/local2/bin/gcc + +causes the specified `gcc' to be used as the C compiler (unless it is +overridden in the site shell script). + +Unfortunately, this technique does not work for `CONFIG_SHELL' due to +an Autoconf bug. Until the bug is fixed you can use this workaround: + + CONFIG_SHELL=/bin/bash /bin/bash ./configure CONFIG_SHELL=/bin/bash + +`configure' Invocation +====================== + + `configure' recognizes the following options to control how it +operates. + +`--help' +`-h' + Print a summary of all of the options to `configure', and exit. + +`--help=short' +`--help=recursive' + Print a summary of the options unique to this package's + `configure', and exit. The `short' variant lists options used + only in the top level, while the `recursive' variant lists options + also present in any nested packages. + +`--version' +`-V' + Print the version of Autoconf used to generate the `configure' + script, and exit. + +`--cache-file=FILE' + Enable the cache: use and save the results of the tests in FILE, + traditionally `config.cache'. FILE defaults to `/dev/null' to + disable caching. + +`--config-cache' +`-C' + Alias for `--cache-file=config.cache'. + +`--quiet' +`--silent' +`-q' + Do not print messages saying which checks are being made. To + suppress all normal output, redirect it to `/dev/null' (any error + messages will still be shown). + +`--srcdir=DIR' + Look for the package's source code in directory DIR. Usually + `configure' can determine that directory automatically. + +`--prefix=DIR' + Use DIR as the installation prefix. *note Installation Names:: + for more details, including other options available for fine-tuning + the installation locations. + +`--no-create' +`-n' + Run the configure checks, but stop before creating any output + files. + +`configure' also accepts some other, not widely useful, options. Run +`configure --help' for more details. + diff --git a/proofs/lfsc_checker/Makefile.am b/proofs/lfsc_checker/Makefile.am new file mode 100644 index 000000000..ff483f5fb --- /dev/null +++ b/proofs/lfsc_checker/Makefile.am @@ -0,0 +1,30 @@ +AM_CXXFLAGS = -Wall -Wno-deprecated + +bin_PROGRAMS = lfsc-checker + +lfsc_checker_SOURCES = \ + main.cpp +lfsc_checker_LDADD = \ + @builddir@/liblfsc_checker.la + +noinst_LTLIBRARIES = liblfsc_checker.la + +liblfsc_checker_la_SOURCES = \ + check.cpp \ + check.h \ + chunking_memory_management.h \ + code.cpp \ + code.h \ + expr.cpp \ + expr.h \ + libwriter.cpp \ + libwriter.h \ + position.h \ + print_smt2.cpp \ + print_smt2.h \ + scccode.cpp \ + scccode.h \ + sccwriter.cpp \ + sccwriter.h \ + trie.cpp \ + trie.h diff --git a/proofs/lfsc_checker/NEWS b/proofs/lfsc_checker/NEWS new file mode 100644 index 000000000..1a357ab4c --- /dev/null +++ b/proofs/lfsc_checker/NEWS @@ -0,0 +1,9 @@ +This file contains a summary of important user-visible changes to the +LFSC proof checker. + +Changes since pre-1.0 (unversioned) releases +============================================ + +* Incorporated the LFSC checker into the CVC4 project. + +-- Morgan Deters <mdeters@cs.nyu.edu> Thu, 12 Dec 2013 18:16:08 -0500 diff --git a/proofs/lfsc_checker/README b/proofs/lfsc_checker/README new file mode 100644 index 000000000..5073569bc --- /dev/null +++ b/proofs/lfsc_checker/README @@ -0,0 +1,83 @@ +lfsc: a high-performance LFSC proof checker. + +Andy Reynolds and Aaron Stump + +---------------------------------------------------------------------- +Command line parameters for LFSC: + +lfsc [sig_1 .... sig_n] [opts_1...opts_n] + +[sig_1 .... sig_n] are signature files, and options [opts_1...opts_n] +are any of the following: + +--compile-scc : Write out all side conditions contained in signatures + specified on the command line to files scccode.h, scccode.cpp (see + below for example) + +--run-scc : Run proof checking with compiled side condition code (see + below). + +--compile-scc-debug : Write side condition code to scccode.h, + scccode.cpp that contains print statements (for debugging running of + side condition code). + + + + +Typical usage: + +./src/opt/lfsc [sig_1 .... sig_n] [proof] [opts_1...opts_n] + +A proof is typically specified at the end of the list of input files +in file [proof]. This will tell LFSC to type check the proof term in +the file [proof]. The extension (*.plf) is commonly used for both +user signature files and proof files. + + + + +Side condition code compilation: + +LFSC may be used with side condition code compilation. This will take +all side conditions ("program" constructs) in the user signature and +produce equivalent C++ code in the output files scccode.h, +scccode.cpp. + +An example for QF_IDL running with side condition code compilation: + +(1) In the src/ directory, run LFSC with the command line parameters: + +./opt/lfsc ../sat-tests/sat.plf ../sat-tests/smt.plf \ + ../sat-tests/cnf_conv.plf ../sat-tests/th_base.plf \ + ../sat-tests/th_idl.plf --compile-scc + +This will produce scccode.h and scccode.cpp in the working directory +where lfsc was run (here, src/). + +(2) Recompile the code base for lfsc. This will produce a copy of the +LFSC executable that is capable of calling side conditions directly as +compiled C++. + +(3) To check a proof.plf* with side condition code compilation, run +LFSC with the command line parameters: + +./opt/lfsc ../sat-tests/sat.plf ../sat-tests/smt.plf \ + ../sat-tests/cnf_conv.plf ../sat-tests/th_base.plf \ + ../sat-tests/th_idl.plf --run-scc proof.plf + + + +*Note that this proof must be compatible with the proof checking + signature. The proof generator is responsible for producing a proof + in the proper format that can be checked by the proof signature + specified when running LFSC. + +For example, in the case of CLSAT in the QF_IDL logic, older proofs +(proofs produced before Feb 2009) may be incompatible with the newest +version of the resolution checking signature (sat.plf). The newest +version of CLSAT -- which can be checked out from the Iowa repository +with + +svn co https://svn.divms.uiowa.edu/repos/clc/clsat/trunk clsat + +should produce proofs compatible with the current version of sat.plf. diff --git a/proofs/lfsc_checker/check.cpp b/proofs/lfsc_checker/check.cpp new file mode 100644 index 000000000..8ef114115 --- /dev/null +++ b/proofs/lfsc_checker/check.cpp @@ -0,0 +1,1383 @@ +#include "position.h" +#include "check.h" +#include "code.h" +#include "expr.h" +#include "trie.h" +#include "sccwriter.h" +#include "libwriter.h" +#ifndef _MSC_VER +#include <libgen.h> +#endif +#include <stack> +#include <string.h> +#include <time.h> +#include "scccode.h" +#include "print_smt2.h" + +using namespace std; +#ifndef _MSC_VER +using namespace __gnu_cxx; +#endif + +int linenum = 1; +int colnum = 1; +const char *filename = 0; +FILE *curfile = 0; + +//#define USE_HASH_MAPS + +symmap2 progs; +std::vector< Expr* > ascHoles; + +#ifdef USE_HASH_MAPS +hash_map<string, Expr *> symbols; +hash_map<string, Expr *> symbol_types; +#else +Trie<pair<Expr *, Expr *> > *symbols = new Trie<pair<Expr *, Expr *> >; +#endif + +hash_map<string, bool > imports; +std::map<SymExpr*, int > mark_map; +std::vector< std::pair< std::string, std::pair<Expr *, Expr *> > > local_sym_names; + +Expr *not_defeq1 = 0; +Expr *not_defeq2 = 0; + +bool tail_calls = true; +bool big_check = true; + +void report_error(const string &msg) { + if (filename) { + Position p(filename,linenum,colnum); + p.print(cout); + } + cout << "\n"; + cout << msg; + cout << "\n"; + if (not_defeq1 && not_defeq2) { + cout << "The following terms are not definitionally equal:\n1. "; + not_defeq1->print(cout); + cout << "\n2. "; + not_defeq2->print(cout); + } + cout.flush(); + _exit(1); +} + +Expr *call_run_code(Expr *code) { + if (dbg_prog) { + cout << "[Running "; + code->print(cout); + cout << "\n"; + } + Expr *computed_result = run_code(code); + if (dbg_prog) { + cout << "] returning "; + if (computed_result) + computed_result->print(cout); + else + cout << "fail"; + cout << "\n"; + } + return computed_result; +} + +char our_getc_c = 0; + +int IDBUF_LEN = 2048; +char idbuf[2048]; + +Expr *statType = new CExpr(TYPE, 0); +Expr *statKind = new CExpr(KIND, 0); +Expr *statMpz = new CExpr(MPZ,0); +Expr *statMpq = new CExpr(MPQ,0); + +int open_parens = 0; + +// only call in check() +void eat_rparen() { + eat_char(')'); + open_parens--; +} + +void eat_excess(int prev) { + while(open_parens > prev) + eat_rparen(); +} + +/* There are four cases for check(): + +1. expected=0, create is false: check() sets computed to be the classifier of + the checked term. + +2. expected=0, create is true: check() returns + the checked expression and sets computed to be its classifier. + +3. expected is non-null, create is false: check returns NULL. + +4. expected is non-null, create is true: check returns the term that + was checked. + +We consume the reference for expected, to enable tail calls in the +application case. + +If is_hole is NULL, then the expression parsed may not be a hole. +Otherwise, it may be, and we will set *is_hole to true if it is +(but leave *is_hole alone if it is not). + +*/ + +bool allow_run = false; +int app_rec_level = 0; + +Expr *check(bool create, Expr *expected, Expr **computed = NULL, + bool *is_hole = 0, bool return_pos = false, bool inAsc = false ) { + start_check: + //std::cout << "check code "; + //if( expected ) + // expected->print( std::cout ); + //std::cout << std::endl; + char d = non_ws(); + switch(d) { + case '(': { + + open_parens++; + + char c = non_ws(); + switch (c) { + case EOF: + report_error("Unexpected end of file."); + break; + case '!': { // the pi case + string id(prefix_id()); +#ifdef DEBUG_SYM_NAMES + Expr *sym = new SymSExpr(id,SYMS_EXPR); +#else + Expr *sym = new SymExpr(id); + //std::cout << "name " << id << " " << sym << std::endl; +#endif + allow_run = true; + int prevo = open_parens; + Expr *domain = check(true, statType); + eat_excess(prevo); + allow_run = false; +#ifdef USE_HASH_MAPS + Expr *prev = symbols[id]; + Expr *prevtp = symbol_types[id]; + symbols[id] = sym; + symbol_types[id] = domain; +#else + pair<Expr *,Expr *>prev = + symbols->insert(id.c_str(),pair<Expr *,Expr *>(sym,domain)); +#endif + if (expected) + expected->inc(); + Expr *range = check(create, expected, computed, NULL, return_pos); + eat_excess(prevo); + eat_rparen(); + +#ifdef USE_HASH_MAPS + symbols[id] = prev; + symbol_types[id] = prevtp; +#else + symbols->insert(id.c_str(),prev); +#endif + if (expected) { + int o = expected->followDefs()->getop(); + expected->dec(); + if (o != TYPE && o != KIND) + report_error(string("The expected classifier for a pi abstraction") + +string("is neither \"type\" nor \"kind\".\n") + +string("1. the expected classifier: ") + +expected->toString()); + if (create){ + CExpr* ret = new CExpr(PI, sym, domain, range); + ret->calc_free_in(); + return ret; + } + return 0; + } + else { + if (create){ + CExpr* ret = new CExpr(PI, sym, domain, range); + ret->calc_free_in(); + return ret; + } + int o = (*computed)->followDefs()->getop(); + if (o != TYPE && o != KIND) + report_error(string("The classifier for the range of a pi") + +string("abstraction is neither \"type\" nor ") + +string("\"kind\".\n1. the computed classifier: ") + +range->toString()); + return 0; + } + } + case '%': { // the case for big lambda + if (expected || create || !return_pos || !big_check) + report_error(string("Big lambda abstractions can only be used") + +string("in the return position of a \"bigcheck\"\n") + +string("command.")); + string id(prefix_id()); +#ifdef DEBUG_SYM_NAMES + SymExpr *sym = new SymSExpr(id, SYMS_EXPR); +#else + SymExpr *sym = new SymExpr(id); + //std::cout << "name " << id << " " << sym << std::endl; +#endif + + int prevo = open_parens; + Expr *expected_domain = check(true, statType); + eat_excess(prevo); + +#ifdef USE_HASH_MAPS + Expr *prev = symbols[id]; + Expr *prevtp = symbol_types[id]; + symbols[id] = sym; + symbol_types[id] = expected_domain; +#else + pair<Expr *, Expr *> prevpr = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(sym,expected_domain)); + Expr *prev = prevpr.first; + Expr *prevtp = prevpr.second; +#endif + expected_domain->inc(); // because we have stored it in the symbol table + + //will clean up local sym name eventually + local_sym_names.push_back( std::pair< std::string, std::pair<Expr *, Expr *> >( id, prevpr ) ); + if (prev) + prev->dec(); + if (prevtp) + prevtp->dec(); + create = false; + expected = NULL; + // computed unchanged + is_hole = NULL; + // return_pos unchanged + + // note we will not store the proper return type in computed. + + goto start_check; + } + + case '\\': { // the lambda case + if (!expected) + report_error(string("We are computing a type for a lambda ") + +string("abstraction, but we can only check\n") + +string("such against a type. Try inserting an ") + +string("ascription (using ':').\n")); + Expr *orig_expected = expected; + expected = expected->followDefs(); + if (expected->getop() != PI) + report_error(string("We are type-checking a lambda abstraction, but\n") + +string("the expected type is not a pi abstraction.\n") + +string("1. The expected type: ") + expected->toString()); + string id(prefix_id()); +#ifdef DEBUG_SYM_NAMES + SymExpr *sym = new SymSExpr(id, SYMS_EXPR); +#else + SymExpr *sym = new SymExpr(id); + //std::cout << "name " << id << " " << sym << std::endl; +#endif + + CExpr *pitp = (CExpr *)expected; + Expr *expected_domain = pitp->kids[1]; + Expr *expected_range = pitp->kids[2]; + SymExpr *pivar = (SymExpr *)pitp->kids[0]; + if (expected_range->followDefs()->getop() == TYPE) + report_error(string("The expected classifier for a lambda abstraction") + +string(" a kind, not a type.\n") + +string("1. The expected classifier: ") + +expected->toString()); + + /* we need to map the pivar to the new sym, because in our + higher-order matching we may have (_ x) to unify with t. + The x must be something from an expected type, since only these + can have holes. We want to map expected vars x to computed vars y, + so that we can set the hole to be \ y t, where t contains ys but + not xs. */ + +#ifdef USE_HASH_MAPS + Expr *prev = symbols[id]; + Expr *prevtp = symbol_types[id]; + symbols[id] = sym; + symbol_types[id] = expected_domain; +#else + pair<Expr *, Expr *> prevpr = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(sym,expected_domain)); + Expr *prev = prevpr.first; + Expr *prevtp = prevpr.second; +#endif + Expr *prev_pivar_val = pivar->val; + sym->inc(); + pivar->val = sym; + + expected_domain->inc(); // because we have stored it in the symbol table + expected_range->inc(); // because we will pass it to a recursive call + + if (tail_calls && big_check && return_pos && !create) { + //will clean up local sym name eventually + local_sym_names.push_back( std::pair< std::string, std::pair<Expr *, Expr *> >( id, prevpr ) ); + if (prev_pivar_val) + prev_pivar_val->dec(); + if (prev) + prev->dec(); + if (prevtp) + prevtp->dec(); + orig_expected->dec(); + create = false; + expected = expected_range; + computed = NULL; + is_hole = NULL; + // return_pos unchanged + goto start_check; + } + else { + + int prev = open_parens; + Expr *range = check(create, expected_range, NULL, NULL, return_pos); + eat_excess(prev); + eat_rparen(); + +#ifdef USE_HASH_MAPS + symbols[id] = prev; + symbol_types[id] = prevtp; +#else + symbols->insert(id.c_str(), prevpr); +#endif + expected_domain->dec(); // because removed from the symbol table now + + pivar->val = prev_pivar_val; + + orig_expected->dec(); + + sym->dec(); // the pivar->val reference + if (create) + return new CExpr(LAM, sym, range); + sym->dec(); // the symbol table reference, otherwise in the new LAM + return 0; + } + } + case '^': { // the run case + if (!allow_run || !create || !expected) + report_error(string("A run expression (operator \"^\") appears in") + +string(" a disallowed position.")); + + Expr *code = read_code(); + //string errstr = (string("The first argument in a run expression must be") + // +string(" a call to a program.\n1. the argument: ") + // +code->toString()); + + /* determine expected type of the result term, and make sure + the code term is an allowed one. */ +#if 0 + Expr *progret; + if (code->isArithTerm()) + progret = statMpz; + else { + if (code->getop() != APP) + report_error(errstr); + + CExpr *call = (CExpr *)code; + + // prog is not known to be a SymExpr yet + CExpr *prog = (CExpr *)call->get_head(); + + if (prog->getop() != PROG) + report_error(errstr); + + progret = prog->kids[0]->get_body(); + } +#else + Expr *progret = NULL; + if (code->isArithTerm()) + progret = statMpz; + else { + if (code->getop() == APP) + { + CExpr *call = (CExpr *)code; + + // prog is not known to be a SymExpr yet + CExpr *prog = (CExpr *)call->get_head(); + + if (prog->getop() == PROG) + progret = prog->kids[0]->get_body(); + } + } +#endif + /* determine expected type of the result term, and make sure + the code term is an allowed one. */ + //Expr* progret = check_code( code ); + + /* the next term cannot be a hole where run expressions are introduced. + When they are checked in applications, it can be. */ + int prev = open_parens; + if( progret ) + progret->inc(); + Expr *trm = check(true, progret); + eat_excess(prev); + eat_rparen(); + + if (expected->getop() != TYPE) + report_error(string("The expected type for a run expression is not ") + +string("\"type\".\n") + +string("1. The expected type: ")+expected->toString()); + expected->dec(); + return new CExpr(RUN, code, trm); + } + + case ':': { // the ascription case + statType->inc(); + int prev = open_parens; + Expr *tp = check(true, statType, NULL, NULL, false, true ); + eat_excess(prev); + + if (!expected) + tp->inc(); + + Expr *trm = check(create, tp, NULL, NULL, return_pos); + eat_excess(prev); + eat_rparen(); + if (expected) { + if (!expected->defeq(tp)) + report_error(string("The expected type does not match the ") + +string("ascribed type in an ascription.\n") + +string("1. The expected type: ")+expected->toString() + +string("\n2. The ascribed type: ")+tp->toString()); + + // no need to dec tp, since it was consumed by the call to check + expected->dec(); + if (create) + return trm; + trm->dec(); + return 0; + } + else { + *computed = tp; + if (create) + return trm; + return 0; + } + } + case '@': { // the local definition case + string id(prefix_id()); +#ifdef DEBUG_SYM_NAMES + SymExpr *sym = new SymSExpr(id, SYMS_EXPR); +#else + SymExpr *sym = new SymExpr(id); +#endif + int prev_open = open_parens; + Expr *tp_of_trm; + Expr *trm = check(true, NULL, &tp_of_trm); + eat_excess(prev_open); + + sym->val = trm; + +#ifdef USE_HASH_MAPS + Expr *prev = symbols[id]; + Expr *prevtp = symbol_types[id]; + symbols[id] = sym; + symbol_types[id] = tp_of_trm; +#else + pair<Expr *, Expr *> prevpr = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(sym,tp_of_trm)); + Expr *prev = prevpr.first; + Expr *prevtp = prevpr.second; +#endif + + if (tail_calls && big_check && return_pos && !create) { + if (prev) + prev->dec(); + if (prevtp) + prevtp->dec(); + // all parameters to check() unchanged here + goto start_check; + } + else { + int prev_open = open_parens; + Expr *body = check(create, expected, computed, is_hole, return_pos); + eat_excess(prev_open); + eat_rparen(); + +#ifdef USE_HASH_MAPS + symbols[id] = prev; + symbol_types[id] = prevtp; +#else + symbols->insert(id.c_str(), prevpr); +#endif + tp_of_trm->dec(); // because removed from the symbol table now + + sym->dec(); + return body; + } + } + case '~': { + int prev = open_parens; + Expr *e = check(create, expected, computed, is_hole, return_pos); + eat_excess(prev); + eat_rparen(); + + // this has been only very lightly tested -- ads. + + if (expected) { + if (expected != statMpz && expected != statMpq) + report_error("Negative sign where an numeric expression is expected."); + } + else { + if ((*computed) != statMpz && (*computed) != statMpq) + report_error("Negative sign where an numeric expression is expected."); + } + + if (create) { + if (e->getclass() == INT_EXPR) + { + IntExpr *ee = (IntExpr *)e; + mpz_neg(ee->n, ee->n); + return ee; + } + else if( e->getclass() == RAT_EXPR ) + { + RatExpr *ee = (RatExpr *)e; + mpq_neg(ee->n, ee->n); + return ee; + } + else + { + report_error("Negative sign with expr that is not an int. literal."); + } + } + else + return 0; + } + default: { // the application case + our_ungetc(c); + Expr *head_computed; + int prev = open_parens; + Expr *headtrm = check(create,0,&head_computed); + eat_excess(prev); + + CExpr *headtp = (CExpr *)head_computed->followDefs(); + headtp->inc(); + head_computed->dec(); + if ( headtp->cloned()) { + // we must clone + Expr *orig_headtp = headtp; + headtp = (CExpr *)headtp->clone(); + orig_headtp->dec(); + } + else + headtp->setcloned(); +#ifdef DEBUG_APPS + char tmp[100]; + sprintf(tmp,"(%d) ", app_rec_level++); + cout << tmp << "{ headtp = "; + headtp->debug(); +#endif + char c; + vector<HoleExpr *> holes; + vector<bool> free_holes; + while ((c = non_ws()) != ')') { + our_ungetc(c); + if (headtp->getop() != PI) + report_error(string("The type of an applied term is not ") + + string("a pi-type.\n") + + string("\n1. the type of the term: ") + + headtp->toString() + + (headtrm ? (string("\n2. the term: ") + + headtrm->toString()) + : string(""))); + SymExpr *headtp_var = (SymExpr *)headtp->kids[0]; + Expr *headtp_domain = headtp->kids[1]; + Expr *headtp_range = headtp->kids[2]; + if (headtp_domain->getop() == RUN) { + CExpr *run = (CExpr *)headtp_domain; + Expr *code = run->kids[0]; + Expr *expected_result = run->kids[1]; + Expr *computed_result = call_run_code(code); + if (!computed_result) + report_error(string("A side condition failed.\n") + +string("1. the side condition: ") + +code->toString()); + if (!expected_result->defeq(computed_result)) + report_error(string("The expected result of a side condition ") + +string("does not match the computed result.\n") + +string("1. expected result: ") + +expected_result->toString() + +string("\n2. computed result: ") + +computed_result->toString()); + computed_result->dec(); + } + else { + // check an argument + bool var_in_range = headtp->get_free_in();//headtp_range->free_in(headtp_var); + bool arg_is_hole = false; + bool consumed_arg = false; + + bool create_arg = (create || var_in_range); + + headtp_domain->inc(); + + if (tail_calls && !create_arg && headtp_range->getop() != PI) { + // we can make a tail call to check() here. + + if (expected) { + if (!expected->defeq(headtp_range)) + report_error(string("The type expected for an application ") + + string("does not match the computed type.\n") + + string("1. The expected type: ") + + expected->toString() + + string("\n2. The computed type: ") + + headtp_range->toString() + + (headtrm ? (string("\n3. the application: ") + + headtrm->toString()) + : string(""))); + expected->dec(); + } + else { + headtp_range->inc(); + *computed = headtp_range; + } + + headtp->dec(); + + // same as below + for (int i = 0, iend = holes.size(); i < iend; i++) { + if (!holes[i]->val) + /* if the hole is free in the domain, we will be filling + it in when we make our tail call, since the domain + is the expected type for the argument */ + if (!headtp_domain->free_in(holes[i])) + report_error(string("A hole was left unfilled after ") + +string("checking an application.\n")); + holes[i]->dec(); + } + + create = false; + expected = headtp_domain; + computed = NULL; + is_hole = NULL; // the argument cannot be a hole + // return_pos is unchanged + +#ifdef DEBUG_APPS + cout << "Making tail call.\n"; +#endif + + goto start_check; + } + + Expr *arg = check(create_arg, headtp_domain, NULL, &arg_is_hole); + eat_excess(prev); + if (create) { +#ifndef USE_FLAT_APP + headtrm = new CExpr(APP, headtrm, arg); +#else + headtrm = Expr::make_app( headtrm, arg ); +#endif + consumed_arg = true; + } + if (var_in_range) { + Expr *tmp = arg->followDefs(); + tmp->inc(); + headtp_var->val = tmp; + } + if (arg_is_hole) { + if (consumed_arg) + arg->inc(); + else + consumed_arg = true; // not used currently +#ifdef DEBUG_HOLES + cout << "An argument is a hole: "; + arg->debug(); +#endif + holes.push_back((HoleExpr *)arg); + } + } + headtp_range->inc(); + headtp->dec(); + headtp = (CExpr *)headtp_range; + } + open_parens--; + + // check for remaining RUN in the head's type after all the arguments + + if (headtp->getop() == PI && headtp->kids[1]->getop() == RUN) { + CExpr *run = (CExpr *)headtp->kids[1]; + Expr *code = run->kids[0]->followDefs(); + Expr *expected_result = run->kids[1]; + Expr *computed_result = call_run_code(code); + if (!computed_result) + report_error(string("A side condition failed.\n") + +string("1. the side condition: ")+code->toString()); + if (!expected_result->defeq(computed_result)) + report_error(string("The expected result of a side condition ") + +string("does not match the computed result.\n") + +string("1. expected result: ") + +expected_result->toString() + +string("\n2. computed result: ") + +computed_result->toString()); + Expr *tmp = headtp->kids[2]; + tmp->inc(); + headtp->dec(); + headtp = (CExpr *)tmp; + computed_result->dec(); + } + +#ifdef DEBUG_APPS + for (int i = 0, iend = holes.size(); i < iend; i++) { + cout << tmp << "hole "; + holes[i]->debug(); + } + cout << "}"; + app_rec_level--; +#endif + + Expr *ret = 0; + if (expected) { + if (!expected->defeq(headtp)){ + report_error(string("The type expected for an application does not") + + string(" match the computed type.(2) \n") + + string("1. The expected type: ") + + expected->toString() + + string("\n2. The computed type: ") + + headtp->toString() + + (headtrm ? (string("\n3. the application: ") + + headtrm->toString()) + : string(""))); + + } + expected->dec(); + headtp->dec(); + if (create) + ret = headtrm; + } + else { + *computed = headtp; + if (create) + ret = headtrm; + } + + /* do this check here to give the defeq() call above a + chance to fill in some holes */ + for (int i = 0, iend = holes.size(); i < iend; i++) { + if (!holes[i]->val){ + if( inAsc ){ +#ifdef DEBUG_HOLES + std::cout << "Ascription Hole: "; + holes[i]->print( std::cout ); + std::cout << std::endl; +#endif + ascHoles.push_back( holes[i] ); + }else{ + report_error(string("A hole was left unfilled after checking") + +string(" an application (2).\n")); + } + } + holes[i]->dec(); + } + + return ret; + + } // end application case + } + } + case EOF: + report_error("Unexpected end of file."); + break; + + case '_': + if (!is_hole) + report_error("A hole is being used in a disallowed position."); + *is_hole = true; + if (expected) + expected->dec(); + return new HoleExpr(); + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + our_ungetc(d); + string v; + char c; + while (isdigit(c = our_getc())) + v.push_back(c); + bool parseMpq = false; + string v2; + if( c=='/' ) + { + parseMpq = true; + v.push_back( c ); + while(isdigit(c = our_getc())) + v.push_back(c); + } + our_ungetc(c); + + + Expr *i = 0; + if (create) { + if( parseMpq ) + { + mpq_t num; + mpq_init(num); + if (mpq_set_str(num,v.c_str(),10) == -1) + report_error("Error reading a numeral."); + i = new RatExpr(num); + } + else + { + mpz_t num; + if (mpz_init_set_str(num,v.c_str(),10) == -1) + report_error("Error reading a numeral."); + i = new IntExpr(num); + } + } + + if (expected) { + if( ( !parseMpq && expected != statMpz ) || ( parseMpq && expected != statMpq ) ) + report_error(string("We parsed a numeric literal, but were ") + +string("expecting a term of a different type.\n") + +string("1. the expected type: ")+expected->toString()); + expected->dec(); + if (create) + return i; + return 0; + } + else { + if( parseMpq ) + { + statMpq->inc(); + *computed = statMpq; + if (create) + return i; + return statMpq; + } + else + { + statMpz->inc(); + *computed = statMpz; + if (create) + return i; + return statMpz; + } + } + } + default: { + our_ungetc(d); + string id(prefix_id()); +#ifdef USE_HASH_MAPS + Expr *ret = symbols[id]; + Expr *rettp = symbol_types[id]; +#else + pair<Expr *, Expr *> p = symbols->get(id.c_str()); + Expr *ret = p.first; + Expr *rettp = p.second; +#endif + if (!ret) + report_error(string("Undeclared identifier: ")+id); + if (expected) { + if (!expected->defeq(rettp)) + report_error(string("The type expected for a symbol does not") + + string(" match the symbol's type.\n") + + string("1. The symbol: ") + + id + + string("\n2. The expected type: ") + + expected->toString() + + string("\n3. The symbol's type: ") + + rettp->toString()); + expected->dec(); + if (create) { + ret->inc(); + return ret; + } + return 0; + } + else { + if( computed ){ + *computed = rettp; + (*computed)->inc(); + } + if (create) { + ret->inc(); + return ret; + } + return 0; + } + } + } + + report_error("Unexpected operator at the start of a term."); + return 0; +} + +#ifdef USE_HASH_MAPS +void discard_old_symbol(const string &id) { + Expr *tmp = symbols[id]; + if (tmp) + tmp->dec(); + tmp = symbol_types[id]; + if (tmp) + tmp->dec(); +} +#endif + +int check_time; + +void check_file(const char *_filename, args a, sccwriter* scw, libwriter* lw) { + int prev_linenum = linenum; + int prev_colnum = colnum; + const char *prev_filename = filename; + FILE * prev_curfile = curfile; + + // from code.h + dbg_prog = a.show_runs; + run_scc = a.run_scc; + tail_calls = !a.no_tail_calls; + + + char *f; + if (strcmp(_filename,"stdin") == 0) { + curfile = stdin; + f = strdup(_filename); + } + else { + if (prev_curfile) { + f = strdup(prev_filename); +#ifdef _MSC_VER + std::string str( f ); + for( int n=str.length(); n>=0; n-- ){ + if( str[n]=='\\' || str[n]=='/' ){ + str = str.erase( n, str.length()-n ); + break; + } + } + char *tmp = (char*)str.c_str(); +#else + char *tmp = dirname(f); +#endif + delete f; + f = new char[strlen(tmp) + 10 + strlen(_filename)]; + strcpy(f,tmp); + strcat(f,"/"); + strcat(f,_filename); + } + else + f = strdup(_filename); + curfile = fopen(f,"r"); + if (!curfile) + report_error(string("Could not open file \"") + + string(f) + + string("\" for reading.\n")); + } + + linenum = 1; + colnum = 1; + filename = f; + + char c; + while ((c = non_ws()) && c!=EOF ) { + if( c == '(' ) + { + char d; + switch ((d = non_ws())) { + case 'd': + char b; + if ((b = our_getc()) != 'e') + report_error(string("Unexpected start of command.")); + + switch ((b = our_getc())) { + case 'f': {// expecting "define" + + if (our_getc() != 'i' || our_getc() != 'n' || our_getc() != 'e') + report_error(string("Unexpected start of command.")); + + string id(prefix_id()); + Expr *ttp; + int prevo = open_parens; + Expr *t = check(true, 0, &ttp, NULL, true); + eat_excess(prevo); + + int o = ttp->followDefs()->getop(); + if (o == KIND) + report_error(string("Kind-level definitions are not supported.\n")); + SymSExpr *s = new SymSExpr(id); + s->val = t; +#ifdef USE_HASH_MAPS + discard_old_symbol(id); + symbols[id] = s; + symbol_types[id] = ttp; +#else + pair<Expr *, Expr *> prev = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(s,ttp)); + if (prev.first) + prev.first->dec(); + if (prev.second) + prev.second->dec(); +#endif + break; + } + case 'c': {// expecting "declare" + if (our_getc() != 'l' || our_getc() != 'a' || our_getc() != 'r' + || our_getc() != 'e') + report_error(string("Unexpected start of command.")); + + string id(prefix_id()); + Expr *ttp; + int prevo = open_parens; + Expr *t = check(true, 0, &ttp, NULL, true); + eat_excess(prevo); + + ttp = ttp->followDefs(); + if (ttp->getop() != TYPE && ttp->getop() != KIND) + report_error(string("The expression declared for \"") + +id+string("\" is neither\na type nor a kind.\n") + +string("1. The expression: ") + +t->toString() + +string("\n2. Its classifier (should be \"type\" ") + +string("or \"kind\"): ")+ttp->toString()); + ttp->dec(); + SymSExpr *s = new SymSExpr(id); +#ifdef USE_HASH_MAPS + discard_old_symbol(id); + symbols[id] = s; + symbol_types[id] = t; +#else + pair<Expr *, Expr *> prev = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(s,t)); + if( lw ) + lw->add_symbol( s, t ); + if (prev.first) + prev.first->dec(); + if (prev.second) + prev.second->dec(); +#endif + break; + } + default: + report_error(string("Unexpected start of command.")); + } // switch((b = our_getc())) following "de" + break; + case 'c': { + if (our_getc() != 'h' || our_getc() != 'e' || our_getc() != 'c' || our_getc() != 'k') + report_error(string("Unexpected start of command.")); + if( run_scc ){ + init_compiled_scc(); + } + Expr *computed; + big_check = true; + int prev = open_parens; + (void)check(false, 0, &computed, NULL, true); + + //print out ascription holes + for( int a=0; a<(int)ascHoles.size(); a++ ){ +#ifdef PRINT_SMT2 + print_smt2( ascHoles[a], std::cout ); +#else + ascHoles[a]->print( std::cout ); +#endif + std::cout << std::endl; + } + if( !ascHoles.empty() ) + std::cout << std::endl; + ascHoles.clear(); + + //clean up local symbols + for( int a=0; a<(int)local_sym_names.size(); a++ ){ +#ifdef USE_HASH_MAPS +#else + symbols->insert( local_sym_names[a].first.c_str(), local_sym_names[a].second ); +#endif + } + local_sym_names.clear(); + mark_map.clear(); + + eat_excess(prev); + + computed->dec(); + //cleanup(); + //exit(0); + break; + } + case 'o': { // opaque case + if (our_getc() != 'p' || our_getc() != 'a' || our_getc() != 'q' + || our_getc() != 'u' || our_getc() != 'e') + report_error(string("Unexpected start of command.")); + + string id(prefix_id()); + Expr *ttp; + int prevo = open_parens; + (void)check(false, 0, &ttp, NULL, true); + eat_excess(prevo); + + int o = ttp->followDefs()->getop(); + if (o == KIND) + report_error(string("Kind-level definitions are not supported.\n")); + SymSExpr *s = new SymSExpr(id); +#ifdef USE_HASH_MAPS + discard_old_symbol(id); + symbols[id] = s; + symbol_types[id] = ttp; +#else + pair<Expr *, Expr *> prev = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(s,ttp)); + if (prev.first) + prev.first->dec(); + if (prev.second) + prev.second->dec(); +#endif + break; + } + case 'r': { // run case + if (our_getc() != 'u' || our_getc() != 'n') + report_error(string("Unexpected start of command.")); + Expr *code = read_code(); + check_code(code); + cout << "[Running-sc "; + code->print(cout); + Expr *tmp = run_code(code); + cout << "] = \n"; + if (tmp) { + tmp->print(cout); + tmp->dec(); + } + else + cout << "fail"; + cout << "\n"; + code->dec(); + break; + } + case 'p': { // program case + if (our_getc() != 'r' || our_getc() != 'o' || our_getc() != 'g' + || our_getc() != 'r' || our_getc() != 'a' || our_getc() != 'm') + report_error(string("Unexpected start of command.")); + + string progstr(prefix_id()); + SymSExpr *prog = new SymSExpr(progstr); + if (progs.find(progstr) != progs.end()) + report_error(string("Redeclaring program ")+progstr+string(".")); + progs[progstr] = prog; + eat_char('('); + char d; + vector<Expr *> vars; + vector<Expr *> tps; + Expr *tmp; + while ((d = non_ws()) != ')') { + our_ungetc(d); + eat_char('('); + string varstr = prefix_id(); +#ifdef USE_HASH_MAPS + if (symbols.find(varstr) != symbols.end()) +#else + if (symbols->get(varstr.c_str()).first != NULL) +#endif + report_error(string("A program variable is already declared") + +string(" (as a constant).\n1. The variable: ") + +varstr); + Expr *var = new SymSExpr(varstr); + vars.push_back(var); + statType->inc(); + int prev = open_parens; + Expr *tp = check(true, NULL, &tmp, 0, true); + if( tp->getclass()==SYMS_EXPR ){ +#ifdef USE_HASH_MAPS + Expr *tptp = symbol_types[((SymSExpr*)tp)->s]; +#else + pair<Expr *, Expr *> p = symbols->get(((SymSExpr*)tp)->s.c_str()); + Expr *tptp = p.second; +#endif + if( !tptp->isType( statType ) ){ + report_error(string("Bad argument for side condition")); + } + }else{ + if (!tp->isDatatype()){ + report_error(string("Type for a program variable is not a ") + +string("datatype.\n1. the type: ")+tp->toString()); + } + } + eat_excess(prev); + + tps.push_back(tp); + eat_char(')'); + +#ifdef USE_HASH_MAPS + symbols[varstr] = var; + symbol_types[varstr] = tp; +#else + symbols->insert(varstr.c_str(), pair<Expr *, Expr *>(var,tp)); +#endif + } + + if (!vars.size()) + report_error("A program lacks input variables."); + + statType->inc(); + int prev = open_parens; + Expr *progtp = check(true,statType,&tmp,0, true); + eat_excess(prev); + + if (!progtp->isDatatype()) + report_error(string("Return type for a program is not a") + +string(" datatype.\n1. the type: ")+progtp->toString()); + + Expr *progcode = read_code(); + + for (int i = vars.size() - 1, iend = 0; i >= iend; i--) { + vars[i]->inc(); // used below for the program code (progcode) + progtp = new CExpr(PI, vars[i], tps[i], progtp); + progtp->calc_free_in(); + } + + // just put the type here for type checking. Make sure progtp is kid 0. + prog->val = new CExpr(PROG, progtp); + + check_code(progcode); + + progcode = new CExpr(PROG, progtp, new CExpr(PROGVARS, vars), progcode); + //if compiling side condition code, give this code to the side condition code writer + if( a.compile_scc ){ + if( scw ){ + scw->add_scc( progstr, (CExpr*)progcode ); + } + } + + // remove the variables from the symbol table. + for (int i = 0, iend = vars.size(); i < iend; i++) { + string &s = ((SymSExpr *)vars[i])->s; + +#ifdef USE_HASH_MAPS + symbols[s] = NULL; + symbol_types[s] = NULL; +#else + symbols->insert(s.c_str(), pair<Expr*,Expr*>(NULL,NULL)); +#endif + } + + progtp->inc(); + prog->val->dec(); + + prog->val = progcode; + + break; + } + + default: + report_error(string("Unexpected start of command.")); + } // switch((d = non_ws()) + + eat_char(')'); + } // while + else + { + if( c != ')' ) + { + char c2[2]; + c2[1] = 0; + c2[0] = c; + string syn = string("Bad syntax (mismatched parentheses?): "); + syn.append(string(c2)); + report_error(syn); + } + } + } + free(f); + if (curfile != stdin) + fclose(curfile); + linenum = prev_linenum; + colnum = prev_colnum; + filename = prev_filename; + curfile = prev_curfile; + +} + +class Deref : public Trie<pair<Expr *, Expr *> >::Cleaner { +public: + ~Deref() {} + void clean(pair<Expr *, Expr *> p) { + Expr *tmp = p.first; + if (tmp) { +#ifdef DEBUG + cout << "Cleaning up "; + tmp->debug(); +#endif + tmp->dec(); + } + tmp = p.second; + if (tmp) { +#ifdef DEBUG + cout << " : "; + tmp->debug(); +#endif + tmp->dec(); + } +#ifdef DEBUG + cout << "\n"; +#endif + } +}; + +template <> +Trie<pair<Expr *, Expr *> >::Cleaner * +Trie<pair<Expr *, Expr *> >::cleaner = new Deref; + +void cleanup() { + symmap::iterator i, iend; +#ifdef USE_HASH_MAPS + Expr *tmp; + for (i = symbols.begin(), iend = symbols.end(); i != iend; i++) { + tmp = i->second; + if (tmp) { +#ifdef DEBUG + cout << "Cleaning up " << i->first << " : "; + tmp->debug(); +#endif + tmp->dec(); + } + } + for (i = symbol_types.begin(), iend = symbol_types.end(); i != iend; i++) { + tmp = i->second; + if (tmp) { +#ifdef DEBUG + cout << "Cleaning up " << i->first << " : "; + tmp->debug(); +#endif + tmp->dec(); + } + } +#else + delete symbols; +#endif + + // clean up programs + + symmap2::iterator j, jend; + for (j = progs.begin(), jend = progs.end(); j != jend; j++) { + SymExpr *p = j->second; + if (p) { + Expr *progcode = p->val; + p->val = NULL; + progcode->dec(); + p->dec(); + } + } +} + +void init() { +#ifdef USE_HASH_MAPS + string tp("type"); + symbols[tp] = statType; + symbol_types[tp] = statKind; + string mpz("mpz"); + symbols[mpz] = statMpz; + symbol_types[mpz] = statType; + statType->inc(); + sym +#else + symbols->insert("type", pair<Expr *, Expr *>(statType, statKind)); + statType->inc(); + symbols->insert("mpz", pair<Expr *, Expr *>(statMpz, statType)); + symbols->insert("mpq", pair<Expr *, Expr *>(statMpq, statType)); +#endif +} diff --git a/proofs/lfsc_checker/check.h b/proofs/lfsc_checker/check.h new file mode 100644 index 000000000..a70599b0f --- /dev/null +++ b/proofs/lfsc_checker/check.h @@ -0,0 +1,167 @@ +#ifndef SC2_CHECK_H +#define SC2_CHECK_H + +#include "expr.h" +#include "trie.h" + +#ifdef _MSC_VER +#include <hash_map> +#include <stdio.h> +#else +#include <ext/hash_map> +#endif + +#include <stack> +#include <string> +#include <map> + +// see the help message in main.cpp for explanation +typedef struct args { + std::vector<std::string> files; + bool show_runs; + bool no_tail_calls; + bool compile_scc; + bool compile_scc_debug; + bool run_scc; + bool use_nested_app; + bool compile_lib; +} args; + +extern int check_time; + +class sccwriter; +class libwriter; + +void init(); + +void check_file(const char *_filename, args a, sccwriter* scw = NULL, libwriter* lw = NULL); + +void cleanup(); + +extern char our_getc_c; + +void report_error(const std::string &); + +extern int linenum; +extern int colnum; +extern const char *filename; +extern FILE *curfile; + +inline void our_ungetc(char c) { + if (our_getc_c != 0) + report_error("Internal error: our_ungetc buffer full"); + our_getc_c = c; + if (c == '\n') { + linenum--; + colnum=-1; + } + else + colnum--; +} + +inline char our_getc() { + char c; + if (our_getc_c > 0) { + c = our_getc_c; + our_getc_c = 0; + } + else{ +#ifndef __linux__ + c = fgetc(curfile); +#else + c = fgetc_unlocked(curfile); +#endif + } + switch(c) { + case '\n': + linenum++; +#ifdef DEBUG_LINES + std::cout << "line " << linenum << "." << std::endl; +#endif + colnum = 1; + break; + case EOF: + break; + default: + colnum++; + } + + return c; +} + +// return the next character that is not whitespace +inline char non_ws() { + char c; + while(isspace(c = our_getc())); + if (c == ';') { + // comment to end of line + while((c = our_getc()) != '\n' && c != EOF); + return non_ws(); + } + return c; +} + +inline void eat_char(char expected) { + if (non_ws() != expected) { + char tmp[80]; + sprintf(tmp,"Expecting a \'%c\'",expected); + report_error(tmp); + } +} + +extern int IDBUF_LEN; +extern char idbuf[]; + +inline const char *prefix_id() { + int i = 0; + char c = idbuf[i++] = non_ws(); + while (!isspace(c) && c != '(' && c != ')' && c != EOF) { + if (i == IDBUF_LEN) + report_error("Identifier is too long"); + + idbuf[i++] = c = our_getc(); + } + our_ungetc(c); + idbuf[i-1] = 0; + return idbuf; +} + +#ifdef _MSC_VER +typedef std::hash_map<std::string, Expr *> symmap; +typedef std::hash_map<std::string, SymExpr *> symmap2; +#else +typedef __gnu_cxx::hash_map<std::string, Expr *> symmap; +typedef __gnu_cxx::hash_map<std::string, SymExpr *> symmap2; +#endif +extern symmap2 progs; +extern std::vector< Expr* > ascHoles; + +#ifdef USE_HASH_MAPS +extern symmap symbols; +extern symmap symbol_types; +#else +extern Trie<std::pair<Expr *, Expr *> > *symbols; +#endif + +extern std::map<SymExpr*, int > mark_map; + +extern std::vector< std::pair< std::string, std::pair<Expr *, Expr *> > > local_sym_names; + +#ifndef _MSC_VER +namespace __gnu_cxx +{ + template<> struct hash< std::string > + { + size_t operator()( const std::string& x ) const + { + return hash< const char* >()( x.c_str() ); + } + }; +} +#endif + +extern Expr *statMpz; +extern Expr *statMpq; +extern Expr *statType; + +#endif diff --git a/proofs/lfsc_checker/chunking_memory_management.h b/proofs/lfsc_checker/chunking_memory_management.h new file mode 100644 index 000000000..bdf938d32 --- /dev/null +++ b/proofs/lfsc_checker/chunking_memory_management.h @@ -0,0 +1,157 @@ +/////////////////////////////////////////////////////////////////////////////// +// // +// Copyright (C) 2002 by the Board of Trustees of Leland Stanford // +// Junior University. See LICENSE for details. // +// // +/////////////////////////////////////////////////////////////////////////////// +/* chunking_memory_management.h + Aaron Stump, 6/11/99 + + This file contains macros that allow you easily to add chunking + memory management for classes. + + RCS Version: $Id: chunking_memory_management.h,v 1.1 2004/11/12 16:26:19 stump Exp $ + +*/ +#ifndef _chunking_memory_management_h_ +#define _chunking_memory_management_h_ + +#include <assert.h> + +/************************************************************************ + * MACRO: ADD_CHUNKING_MEMORY_MANAGEMENT_H() + * Aaron Stump, 6/11/99 + * + * ABSTRACT: This macro should be called exactly once inside the body + * of the declaration of the class THE_CLASS to add chunking memory + * management for the class. That class should not itself declare the + * operators new and delete. The macro + * C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_CC should be called with + * the same value for THE_CLASS in a .cc file for that class. + * + * NOTE that the access specifier will be public after calling this + * macro. + * + * THE_FIELD is a field of the class to use for the next pointer in the + * free list data structure. It can be of any type, but must have enough + * space to hold a pointer. + ************************************************************************/ +#define C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_H(THE_CLASS,THE_FIELD) \ +private:\ + static unsigned C_MACROS__CHUNK_SIZE;\ + static unsigned C_MACROS__BLOCK_SIZE;\ + static void *C_MACROS__freelist;\ + static bool C_MACROS__initialized;\ + static char *C_MACROS__next_free_block;\ + static char *C_MACROS__end_of_current_chunk;\ + \ + static void C_MACROS__allocate_new_chunk();\ +\ +public:\ + static void C_MACROS__init_chunks() {\ + if (!C_MACROS__initialized) {\ + C_MACROS__allocate_new_chunk();\ + C_MACROS__initialized = true;\ + }\ + }\ +\ + static void *operator new(size_t size, void *h = NULL);\ + static void operator delete(void *ptr)\ + + +/************************************************************************ + * MACRO: ADD_CHUNKING_MEMORY_MANAGEMENT_CC() + * Aaron Stump, 6/11/99 + * + * ABSTRACT: This macro should be called exactly once in a .cc file + * for the class THE_CLASS to add chunking memory management for the + * class. This macro should be called with the same value for + * THE_CLASS as was used in calling + * C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_H in the body of the + * declaration of THE_CLASS. THE_CHUNK_SIZE is the number of blocks + * of memory to get at once using malloc(). A block is the portion + * of memory needed for one instance of THE_CLASS. + * + * + * IMPLEMENTATION: + ************************************************************************ + * FUNCTION: allocate_new_chunk() + * Aaron Stump, 6/8/99 + * + * ABSTRACT: This method allocates a new chunk of memory to use for + * allocating instances of THE_CLASS. + ************************************************************************ + * FUNCTION: new() + * Aaron Stump, 6/8/99 + * + * ABSTRACT: This allocator uses chunks for more efficient allocation. + ************************************************************************ + * FUNCTION: delete() + * Aaron Stump, 6/8/99 + * + * ABSTRACT: This delete() puts the chunk pointed to by ptr on the + * freelist. + ************************************************************************ + * Chunking_Memory_Management_Initializer and its static instance are used + * to call the static init_chunks() method for THE_CLASS. + ************************************************************************/ +#define C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_CC(THE_CLASS,THE_FIELD,THE_CHUNK_SIZE) \ +unsigned THE_CLASS::C_MACROS__CHUNK_SIZE = THE_CHUNK_SIZE;\ +\ +unsigned THE_CLASS::C_MACROS__BLOCK_SIZE = sizeof(THE_CLASS);\ +\ +void * THE_CLASS::C_MACROS__freelist = NULL;\ +char * THE_CLASS::C_MACROS__next_free_block = NULL;\ +char * THE_CLASS::C_MACROS__end_of_current_chunk = NULL;\ +bool THE_CLASS::C_MACROS__initialized = false;\ +\ +void \ +THE_CLASS::C_MACROS__allocate_new_chunk() {\ +\ + unsigned tmp = C_MACROS__CHUNK_SIZE * C_MACROS__BLOCK_SIZE;\ + char *chunk = (char *)malloc(tmp);\ + \ + assert (chunk != NULL); \ +\ + C_MACROS__next_free_block = chunk;\ + C_MACROS__end_of_current_chunk = chunk + tmp;\ +}\ +\ +void * \ +THE_CLASS::operator new(size_t size, void *h) {\ + (void)size; /* size should always be _BLOCK_SIZE */\ +\ + if (h != NULL)\ + /* we're being told what memory we should use */\ + return h;\ +\ + char *new_block;\ +\ + if (C_MACROS__freelist) {\ + /* we have a block on the freelist that we can use */\ + new_block = (char *)C_MACROS__freelist; \ + C_MACROS__freelist = (void *)((THE_CLASS *)new_block)->THE_FIELD; \ + }\ + else {\ + /* we have to get a new block from a chunk (which we may */\ + /* have to allocate*/\ + \ + if (C_MACROS__next_free_block == C_MACROS__end_of_current_chunk)\ + C_MACROS__allocate_new_chunk();\ + \ + new_block = C_MACROS__next_free_block;\ + C_MACROS__next_free_block += C_MACROS__BLOCK_SIZE;\ + }\ + \ + return new_block;\ +}\ +\ +void \ +THE_CLASS::operator delete(void *ptr) {\ + void **f = (void **)&((THE_CLASS *)ptr)->THE_FIELD; \ + *f = C_MACROS__freelist; \ + C_MACROS__freelist = ptr; \ +} + +#endif + diff --git a/proofs/lfsc_checker/code.cpp b/proofs/lfsc_checker/code.cpp new file mode 100644 index 000000000..225700580 --- /dev/null +++ b/proofs/lfsc_checker/code.cpp @@ -0,0 +1,1377 @@ +#include "check.h" +#include "code.h" +#include <string> + +#include "scccode.h" + +using namespace std; + +string *eat_str(const char *str, bool check_end = true) { + string *s = new string(); + char c, d; + while ((c = *str++)) { + d = our_getc(); + if (c != d) { + our_ungetc(d); + return s; + } + s->push_back(d); + } + + if (check_end && + (d = our_getc()) != ' ' && d != '(' && d != '\n' && d != '\t') { + our_ungetc(d); + return s; + } + + delete s; + return 0; +} + +SymSExpr *read_ctor() { + string id(prefix_id()); +#ifdef USE_HASH_TABLES + Expr *s = symbols[id]; + Expr *stp = symbol_types[id]; +#else + pair<Expr *, Expr *> p = symbols->get(id.c_str()); + Expr *s = p.first; + Expr *stp = p.second; +#endif + + if (!stp) + report_error("Undeclared identifier parsing a pattern."); + + if (s->getclass() != SYMS_EXPR || ((SymExpr *)s)->val) + report_error("The head of a pattern is not a constructor."); + + s->inc(); + + return (SymSExpr *)s; +} + +Expr *read_case() { + eat_char('('); + Expr *pat = NULL; + vector<SymSExpr *> vars; + +#ifdef USE_HASH_MAPS + vector<Expr *>prevs; +#else + vector<pair<Expr *,Expr *> >prevs; +#endif + char d = non_ws(); + switch(d) { + case '(': { + // parse application + SymSExpr *s = read_ctor(); + pat = s; + char c; + while ((c = non_ws()) != ')') { + our_ungetc(c); + string varstr(prefix_id()); + SymSExpr *var = new SymSExpr(varstr); + vars.push_back(var); +#ifdef USE_HASH_MAPS + prevs.push_back(symbols[varstr]); + symbols[varstr] = var; +#else + prevs.push_back(symbols->insert(varstr.c_str(), + pair<Expr *, Expr *>(var,NULL))); +#endif +#ifndef USE_FLAT_APP + pat = new CExpr(APP,pat,var); +#else + pat = Expr::make_app(pat,var); +#endif + } + break; + } + // default case + case 'd': { + delete eat_str("efault"); + } + break; + case EOF: + report_error("Unexpected end of file parsing a pattern."); + break; + default: + // could be an identifier + our_ungetc(d); + pat = read_ctor(); + break; + } + + Expr *ret = read_code(); + if( pat ) + ret = new CExpr(CASE, pat, ret); + + for (int i = 0, iend = prevs.size(); i < iend; i++) { + string &s = vars[i]->s; +#ifdef USE_HASH_MAPS + symbols[s] = prevs[i]; +#else + symbols->insert(s.c_str(), prevs[i]); +#endif + } + + eat_char(')'); + + return ret; +} + +Expr *read_code() { + string *pref = NULL; + char d = non_ws(); + switch(d) { + case '(': + { + char c = non_ws(); + switch (c) + { + case 'd': + { + our_ungetc('d'); + pref = eat_str("do"); + if (pref) + break; + Expr *ret = read_code(); + while ((c = non_ws()) != ')') { + our_ungetc(c); + ret = new CExpr(DO,ret,read_code()); + } + return ret; + } + case 'f': + { + our_ungetc('f'); + pref = eat_str("fail"); + if (pref) + break; + + Expr *c = read_code(); + eat_char(')'); + + //do we need to check this??? + //if (c->getclass() != SYMS_EXPR || ((SymExpr *)c)->val) + // report_error(string("\"fail\" must be used with a (undefined) base ") + // +string("type.\n1. the expression used: "+c->toString())); + + return new CExpr(FAIL, c); + } + case 'l': + { + our_ungetc('l'); + pref = eat_str("let"); + if (pref) + break; + + string id(prefix_id()); + SymSExpr *var = new SymSExpr(id); + + Expr *t1 = read_code(); + +#ifdef USE_HASH_MAPS + Expr *prev = symbols[id]; + symbols[id] = var; +#else + pair<Expr *, Expr *> prev = + symbols->insert(id.c_str(), pair<Expr *, Expr *>(var,NULL)); +#endif + + Expr *t2 = read_code(); + +#ifdef USE_HASH_MAPS + symbols[id] = prev; +#else + symbols->insert(id.c_str(), prev); +#endif + eat_char(')'); + return new CExpr(LET, var, t1, t2); + } + case 'i': + { + our_ungetc('i'); + pref = eat_str("ifmarked",false); + if (pref) + break; +#ifndef MARKVAR_32 + Expr *e1 = read_code(); + Expr *e2 = read_code(); + Expr *e3 = read_code(); + Expr *ret = new CExpr(IFMARKED, e1, e2, e3); +#else + int index = read_index(); + Expr *e1 = read_code(); + Expr *e2 = read_code(); + Expr *e3 = read_code(); + Expr *ret = NULL; + if( index>=1 && index<=32 ) + { + ret = new CExpr( IFMARKED, new IntExpr( index-1 ), e1, e2, e3 ); + } + else + { + std::cout << "Can't make IFMARKED with index = " << index << std::endl; + } + Expr::markedCount++; + //Expr *ret = new CExpr(IFMARKED, e1, e2, e3); +#endif + eat_char(')'); + return ret; + } + case 'm': + { + char c; + switch ((c = our_getc())) + { + case 'a': + { + char cc; + switch ((cc = our_getc())) { + case 't': + { + our_ungetc('t'); + pref = eat_str("tch"); + if (pref) { + pref->insert(0,"ma"); + break; + } + vector<Expr *> cases; + cases.push_back(read_code()); // the scrutinee + while ((c = non_ws()) != ')' && c != 'd') { + our_ungetc(c); + cases.push_back(read_case()); + } + if (cases.size() == 1) // counting scrutinee + report_error("A match has no cases."); + if (c == 'd') { + // we have a default case + //delete eat_str("efault"); + our_ungetc(c); + cases.push_back(read_case()); + } + return new CExpr(MATCH,cases); + } + case 'r': + { + our_ungetc('r'); + pref = eat_str("rkvar", false); + if (pref) { + pref->insert(0,"ma"); + break; + } + #ifndef MARKVAR_32 + Expr *ret = new CExpr(MARKVAR,read_code()); + #else + int index = read_index(); + CExpr* ret = NULL; + if( index>=1 && index<=32 ) + { + ret = new CExpr( MARKVAR, new IntExpr( index-1 ), read_code() ); + } + else + { + std::cout << "Can't make MARKVAR with index = " << index << std::endl; + } + Expr::markedCount++; + //Expr *ret = new CExpr(MARKVAR,read_code()); + #endif + + eat_char(')'); + return ret; + } + default: + our_ungetc(c); + pref = new string("ma"); + break; + } + } + case 'p': + { + our_ungetc('p'); + pref = eat_str("p_",false); + if (pref) { + pref->insert(0,"m"); + break; + } + char c = our_getc(); + switch(c) { + case 'a': + { + our_ungetc('a'); + pref = eat_str("add"); + if (pref) { + pref->insert(0,"mp_"); + break; + } + Expr* e1 = read_code(); + Expr* e2 = read_code(); + Expr *ret = new CExpr(ADD, e1, e2); + eat_char(')'); + return ret; + } + case 'n': + { + our_ungetc('n'); + pref = eat_str("neg"); + if (pref) { + pref->insert(0,"mp_"); + break; + } + + Expr *ret = new CExpr(NEG, read_code()); + eat_char(')'); + return ret; + } + case 'i': + { // mpz_if_neg + char c = our_getc(); + if( c=='f' ) + { + c = our_getc(); + switch( c ) + { + case 'n': { + our_ungetc('n'); + pref = eat_str("neg"); + if( pref ) { + pref->insert(0,"mp_if"); + break; + } + Expr* e1 = read_code(); + Expr* e2 = read_code(); + Expr* e3 = read_code(); + Expr* ret = new CExpr(IFNEG, e1, e2, e3 ); + eat_char(')'); + return ret; + } + case 'z': { + our_ungetc('z'); + pref = eat_str("zero"); + if( pref ) { + pref->insert(0,"mp_if"); + break; + } + Expr* e1 = read_code(); + Expr* e2 = read_code(); + Expr* e3 = read_code(); + Expr* ret = new CExpr(IFZERO, e1, e2, e3 ); + eat_char(')'); + return ret; + } + default: + our_ungetc(c); + pref = new string("mp_if"); + break; + } + } + else + { + our_ungetc(c); + pref = new string("mp_i"); + break; + } + } + case 'm': + { + our_ungetc('m'); + pref = eat_str("mul"); + if( pref ){ + pref->insert(0,"mp_"); + break; + } + Expr* e1 = read_code(); + Expr* e2 = read_code(); + Expr* ret = new CExpr(MUL, e1, e2 ); + eat_char(')'); + return ret; + } + case 'd': + { + our_ungetc('d'); + pref = eat_str("div"); + if( pref ){ + pref->insert(0,"mp_"); + break; + } + Expr* e1 = read_code(); + Expr* e2 = read_code(); + Expr* ret = new CExpr(DIV, e1, e2 ); + eat_char(')'); + return ret; + } + default: + our_ungetc(c); + pref = new string("mp_"); + break; + } + } + default: + our_ungetc(c); + pref = new string("m"); + break; + } + break; + } + case '~': { + Expr *e = read_code(); + if( e->getclass()==INT_EXPR ) + { + IntExpr *ee = (IntExpr *)e; + mpz_neg(ee->n, ee->n); + eat_char(')'); + return ee; + } + else if( e->getclass() == RAT_EXPR ) + { + RatExpr *ee = (RatExpr *)e; + mpq_neg(ee->n, ee->n); + eat_char(')'); + return ee; + } + else + { + report_error("Negative sign with expr that is not an int. literal."); + } + } + case 'c': + { + our_ungetc('c'); + pref = eat_str("compare"); + if (pref) + break; + Expr *e1 = read_code(); + Expr *e2 = read_code(); + Expr *e3 = read_code(); + Expr *e4 = read_code(); + eat_char(')'); + return new CExpr(COMPARE, e1, e2, e3, e4); + } + break; + case EOF: + report_error("Unexpected end of file."); + break; + default: + { // the application case + our_ungetc(c); + break; + } + } + // parse application + if (pref) + // we have eaten part of the name of an applied identifier + pref->append(prefix_id()); + else + pref = new string(prefix_id()); + + Expr *ret = progs[*pref]; + if (!ret) +#ifdef USE_HASH_TABLES + ret = symbols[*pref]; +#else + ret = symbols->get(pref->c_str()).first; +#endif + + if (!ret) + report_error(string("Undeclared identifier at head of an application: ") + +*pref); + + ret->inc(); + delete pref; + + while ((c = non_ws()) != ')') { + our_ungetc(c); +#ifndef USE_FLAT_APP + ret = new CExpr(APP,ret,read_code()); +#else + Expr* ke = read_code(); + ret = Expr::make_app(ret,ke); +#endif + } + return ret; + } // end case '(' + case EOF: + report_error("Unexpected end of file."); + break; + case '_': + report_error("Holes may not be used in code."); + return NULL; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + { + our_ungetc(d); + string v; + char c; + while (isdigit(c = our_getc())) + v.push_back(c); + bool parseMpq = false; + if( c=='/' ) + { + parseMpq = true; + v.push_back(c); + while(isdigit(c = our_getc())) + v.push_back(c); + } + our_ungetc(c); + if( parseMpq ) + { + mpq_t num; + mpq_init(num); + if (mpq_set_str(num,v.c_str(),10) == -1 ) + report_error("Error reading a mpq numeral."); + + Expr* e = new RatExpr( num ); + return e; + } + else + { + mpz_t num; + if (mpz_init_set_str(num,v.c_str(),10) == -1) + report_error("Error reading a numeral."); + return new IntExpr(num); + } + } + default: + { + our_ungetc(d); + string id(prefix_id()); +#ifdef USE_HASH_MAPS + Expr *ret = symbols[id]; +#else + pair<Expr *, Expr *> p = symbols->get(id.c_str()); + Expr *ret = p.first; +#endif + if (!ret) + ret = progs[id]; + if (!ret) + report_error(string("Undeclared identifier: ")+id); + ret->inc(); + return ret; + } + } + report_error("Unexpected operator in a piece of code."); + return 0; +} + +// the input is owned by the caller, the output by us (so do not dec it). +Expr *check_code(Expr *_e) { + CExpr *e = (CExpr *)_e; + switch (e->getop()) { + case NOT_CEXPR: + switch (e->getclass()) { + case INT_EXPR: + return statMpz; + case RAT_EXPR: + return statMpq; + case SYM_EXPR: { + report_error("Internal error: an LF variable is encountered in code"); + break; + } + case SYMS_EXPR: { +#ifdef USE_HASH_MAPS + Expr *tp = symbol_types[((SymSExpr *)e)->s]; +#else + Expr *tp = symbols->get(((SymSExpr *)e)->s.c_str()).second; +#endif + if (!tp) + report_error(string("A symbol is missing a type in a piece of code.") + +string("\n1. the symbol: ")+((SymSExpr *)e)->s); + return tp; + } + case HOLE_EXPR: + report_error("Encountered a hole unexpectedly in code."); + default: + report_error("Unrecognized form of expr in code."); + } + case APP: { +#ifdef USE_FLAT_APP + Expr* h = e->kids[0]->followDefs(); + vector<Expr *> argtps; + int counter = 1; + while( e->kids[counter] ) + { + argtps.push_back( check_code( e->kids[counter] ) ); + counter++; + } + int iend = counter-1; +#else + vector<Expr *> args; + Expr *h = (Expr *)e->collect_args(args); + + int iend = args.size(); + vector<Expr *> argtps(iend); + for (int i = 0; i < iend; i++) + argtps[i] = check_code(args[i]); +#endif + + Expr *tp = NULL; + if (h->getop() == PROG){ + tp = ((CExpr *)h)->kids[0]; + }else { +#ifdef USE_HASH_MAPS + tp = symbol_types[((SymSExpr *)h)->s]; +#else + tp = symbols->get(((SymSExpr *)h)->s.c_str()).second; +#endif + } + + if (!tp) + report_error(string("The head of an application is missing a type in ") + +string("code.\n1. the application: ")+e->toString()); + + tp = tp->followDefs(); + + if (tp->getop() != PI) + report_error(string("The head of an application does not have ") + +string("functional type in code.") + +string("\n1. the application: ")+e->toString()); + + CExpr *cur = (CExpr *)tp; + int i = 0; + while (cur->getop() == PI) { + if (i >= iend) + report_error(string("A function is not being fully applied in code.\n") + +string("1. the application: ")+e->toString() + +string("\n2. its (functional) type: ")+cur->toString()); + if( argtps[i]->getop()==APP ) + argtps[i] = ((CExpr*)argtps[i])->kids[0]; + if (argtps[i] != cur->kids[1]) { + char buf[1024]; + sprintf(buf,"%d",i); + report_error(string("Type mismatch for argument ") + + string(buf) + + string(" in application in code.\n") + + string("1. the application: ")+e->toString() + + string("\n2. the head's type: ")+tp->toString() +#ifdef USE_FLAT_APP + + string("\n3. the argument: ")+e->kids[i+1]->toString() +#else + + string("\n3. the argument: ")+args[i]->toString() +#endif + + string("\n4. computed type: ")+argtps[i]->toString() + + string("\n5. expected type: ") + +cur->kids[1]->toString()); + } + + //if (cur->kids[2]->free_in((SymExpr *)cur->kids[0])) + if( cur->get_free_in() ){ + cur->calc_free_in(); + //are you sure? + if( cur->get_free_in() ) + report_error(string("A dependently typed function is being applied in") + +string(" code.\n1. the application: ")+e->toString() + +string("\n2. the head's type: ")+tp->toString()); + //ok, reset the mark + cur->setexmark(); + } + + i++; + cur = (CExpr *)cur->kids[2]; + } + if (i < iend) + report_error(string("A function is being fully applied to too many ") + +string("arguments in code.\n") + +string("1. the application: ")+e->toString() + +string("\n2. the head's type: ")+tp->toString()); + + + return cur; + } + //is this right? + case MPZ: + return statType; + break; + case MPQ: + return statType; + break; + case DO: + check_code(e->kids[0]); + return check_code(e->kids[1]); + + case LET: { + SymSExpr * var = (SymSExpr *)e->kids[0]; + + Expr *tp1 = check_code(e->kids[1]); + +#ifdef USE_HASH_MAPS + Expr *prevtp = symbol_types[var->s]; + symbol_types[var->s] = tp1; +#else + pair<Expr *, Expr *> prev = + symbols->insert(var->s.c_str(), pair<Expr *, Expr *>(NULL,tp1)); +#endif + + Expr *tp2 = check_code(e->kids[2]); + +#ifdef USE_HASH_MAPS + symbol_types[var->s] = prevtp; +#else + symbols->insert(var->s.c_str(), prev); +#endif + + return tp2; + } + + case ADD: + case MUL: + case DIV: + { + Expr *tp0 = check_code(e->kids[0]); + Expr *tp1 = check_code(e->kids[1]); + + if (tp0 != statMpz && tp0 != statMpq ) + report_error(string("Argument to mp_[arith] does not have type \"mpz\" or \"mpq\".\n") + +string("1. the argument: ")+e->kids[0]->toString() + +string("\n1. its type: ")+tp0->toString()); + + if (tp0 != tp1) + report_error(string("Arguments to mp_[arith] have differing types.\n") + +string("1. argument 1: ")+e->kids[0]->toString() + +string("\n1. its type: ")+tp0->toString() + +string("2. argument 2: ")+e->kids[1]->toString() + +string("\n2. its type: ")+tp1->toString()); + + return tp0; + } + + case NEG: { + Expr *tp0 = check_code(e->kids[0]); + if (tp0 != statMpz && tp0 != statMpq ) + report_error(string("Argument to mp_neg does not have type \"mpz\" or \"mpq\".\n") + +string("1. the argument: ")+e->kids[0]->toString() + +string("\n1. its type: ")+tp0->toString()); + + return tp0; + } + + case IFNEG: + case IFZERO: { + Expr *tp0 = check_code(e->kids[0]); + if (tp0 != statMpz && tp0 != statMpq) + report_error(string("Argument to mp_if does not have type \"mpz\" or \"mpq\".\n") + +string("1. the argument: ")+e->kids[0]->toString() + +string("\n1. its type: ")+tp0->toString()); + + SymSExpr *tp1 = (SymSExpr *)check_code(e->kids[1]); + SymSExpr *tp2 = (SymSExpr *)check_code(e->kids[2]); + if (tp1->getclass() != SYMS_EXPR || tp1->val || tp1 != tp2) + report_error(string("\"mp_if\" used with expressions that do not ") + +string("have equal simple datatypes\nfor their types.\n") + +string("0. 0'th expression: ")+e->kids[0]->toString() + +string("\n1. first expression: ")+e->kids[1]->toString() + +string("\n2. second expression: ")+e->kids[2]->toString() + +string("\n3. first expression's type: ")+tp1->toString() + +string("\n4. second expression's type: ")+tp2->toString()); + return tp1; + } + + case FAIL: { + Expr *tp = check_code(e->kids[0]); + if (tp != statType) + report_error(string("\"fail\" is used with an expression which is ") + +string("not a type.\n1. the expression :") + +e->kids[0]->toString() + +string("\n2. its type: ")+tp->toString()); + return e->kids[0]; + } + case MARKVAR: { + SymSExpr *tp = (SymSExpr *)check_code(e->kids[1]); + + Expr* tptp = NULL; + + if (tp->getclass() == SYMS_EXPR && !tp->val){ +#ifdef USE_HASH_MAPS + tptp = symbol_types[tp->s]; +#else + tptp = symbols->get(tp->s.c_str()).second; +#endif + } + + if (!tptp->isType( statType )){ + string errstr = (string("\"markvar\" is used with an expression which ") + +string("cannot be a lambda-bound variable.\n") + +string("1. the expression :") + +e->kids[1]->toString() + +string("\n2. its type: ")+tp->toString()); + report_error(errstr); + } + + return tp; + } + + case IFMARKED: + { + SymSExpr *tp = (SymSExpr *)check_code(e->kids[1]); + + Expr* tptp = NULL; + + if (tp->getclass() == SYMS_EXPR && !tp->val){ +#ifdef USE_HASH_MAPS + tptp = symbol_types[tp->s]; +#else + tptp = symbols->get(tp->s.c_str()).second; +#endif + } + + if (!tptp->isType( statType ) ){ + string errstr = (string("\"ifmarked\" is used with an expression which ") + +string("cannot be a lambda-bound variable.\n") + +string("1. the expression :") + +e->kids[1]->toString() + +string("\n2. its type: ")+tp->toString()); + report_error(errstr); + } + + SymSExpr *tp1 = (SymSExpr *)check_code(e->kids[2]); + SymSExpr *tp2 = (SymSExpr *)check_code(e->kids[3]); + if (tp1->getclass() != SYMS_EXPR || tp1->val || tp1 != tp2) + report_error(string("\"ifmarked\" used with expressions that do not ") + +string("have equal simple datatypes\nfor their types.\n") + +string("0. 0'th expression: ")+e->kids[1]->toString() + +string("\n1. first expression: ")+e->kids[2]->toString() + +string("\n2. second expression: ")+e->kids[3]->toString() + +string("\n3. first expression's type: ")+tp1->toString() + +string("\n4. second expression's type: ")+tp2->toString()); + return tp1; + } + case COMPARE: + { + SymSExpr *tp0 = (SymSExpr *)check_code(e->kids[0]); + if (tp0->getclass() != SYMS_EXPR || tp0->val){ + string errstr0 = (string("\"compare\" is used with a first expression which ") + +string("cannot be a lambda-bound variable.\n") + +string("1. the expression :") + +e->kids[0]->toString() + +string("\n2. its type: ")+tp0->toString()); + report_error(errstr0); + } + + SymSExpr *tp1 = (SymSExpr *)check_code(e->kids[1]); + + if (tp1->getclass() != SYMS_EXPR || tp1->val){ + string errstr1 = (string("\"compare\" is used with a second expression which ") + +string("cannot be a lambda-bound variable.\n") + +string("1. the expression :") + +e->kids[1]->toString() + +string("\n2. its type: ")+tp1->toString()); + report_error(errstr1); + } + + SymSExpr *tp2 = (SymSExpr *)check_code(e->kids[2]); + SymSExpr *tp3 = (SymSExpr *)check_code(e->kids[3]); + if (tp2->getclass() != SYMS_EXPR || tp2->val || tp2 != tp3) + report_error(string("\"compare\" used with expressions that do not ") + +string("have equal simple datatypes\nfor their types.\n") + +string("\n1. first expression: ")+e->kids[2]->toString() + +string("\n2. second expression: ")+e->kids[3]->toString() + +string("\n3. first expression's type: ")+tp2->toString() + +string("\n4. second expression's type: ")+tp3->toString()); + return tp2; + } + case MATCH: + { + SymSExpr *scruttp = (SymSExpr *)check_code(e->kids[0]); + Expr *tptp = NULL; + if (scruttp->getclass() == SYMS_EXPR && !scruttp->val){ +#ifdef USE_HASH_MAPS + tptp = symbol_types[scruttp->s]; +#else + tptp = symbols->get(scruttp->s.c_str()).second; +#endif + } + if (!tptp->isType( statType )){ + string errstr = (string("The scrutinee of a match is not ") + +string("a plain piece of data.\n") + +string("1. the scrutinee: ") + +e->kids[0]->toString() + +string("\n2. its type: ")+scruttp->toString()); + report_error(errstr); + } + + int i = 1; + Expr **cur = &e->kids[i]; + Expr *mtp = NULL; + Expr *c_or_default; + CExpr *c; + while ((c_or_default = *cur++)) { + Expr *tp = NULL; + CExpr *pat = NULL; + if (c_or_default->getop() != CASE) + // this is the default of the MATCH + tp = check_code(c_or_default); + else { + // this is a CASE of the MATCH + c = (CExpr *)c_or_default; + pat = (CExpr *)c->kids[0]; // might be just a SYMS_EXPR + if (pat->getclass() == SYMS_EXPR) + tp = check_code(c->kids[1]); + else { + // extend type context and then check the body of the case +#ifdef USE_HASH_MAPS + vector<Expr *>prevs; +#else + vector<pair<Expr *,Expr *> >prevs; +#endif + vector<Expr *> vars; + SymSExpr *ctor = (SymSExpr *)pat->collect_args(vars); +#ifdef USE_HASH_MAPS + CExpr *ctortp = (CExpr *)symbol_types[ctor->s]; +#else + CExpr *ctortp = (CExpr *)symbols->get(ctor->s.c_str()).second; +#endif + CExpr *curtp = ctortp; + for (int i = 0, iend = vars.size(); i < iend; i++) { + if ( curtp->followDefs()->getop() != PI) + report_error(string("Too many arguments to a constructor in") + +string(" a pattern.\n1. the pattern: ") + +pat->toString() + +string("\n2. the head's type: " + +ctortp->toString())); +#ifdef USE_HASH_MAPS + prevs.push_back(symbol_types[((SymSExpr *)vars[i])->s]); + symbol_types[((SymSExpr *)vars[i])] = curtp->followDefs()->kids[1]; +#else + prevs.push_back + (symbols->insert(((SymSExpr *)vars[i])->s.c_str(), + pair<Expr *, Expr *>(NULL, + ((CExpr *)(curtp->followDefs()))->kids[1]))); +#endif + curtp = (CExpr *)((CExpr *)(curtp->followDefs()))->kids[2]; + } + + tp = check_code(c->kids[1]); + + for (int i = 0, iend = prevs.size(); i < iend; i++) { +#ifdef USE_HASH_MAPS + symbol_types[((SymSExpr *)vars[i])->s] = prevs[i]; +#else + symbols->insert(((SymSExpr *)vars[i])->s.c_str(), prevs[i]); +#endif + } + } + } + + // check that the type for the body of this case -- or the default value -- + // matches the type for the previous case if we had one. + + if (!mtp) + mtp = tp; + else + if (mtp != tp) + report_error(string("Types for bodies of match cases or the default differ.") + +string("\n1. type for first case's body: ") + +mtp->toString() + +(pat == NULL ? string("\n2. type for the default") + : (string("\n2. type for the body of case for ") + +pat->toString())) + +string(": ")+tp->toString()); + + } + + return mtp; + } + } // end switch + + report_error("Type checking an unrecognized form of code (internal error)."); + return NULL; +} + +bool dbg_prog; +bool run_scc; +int dbg_prog_indent_lvl = 0; + +void dbg_prog_indent(std::ostream &os) { + for (int i = 0; i < dbg_prog_indent_lvl; i++) + os << " "; +} + +Expr *run_code(Expr *_e) { + start_run_code: + CExpr *e = (CExpr *)_e; + if( e ) + { + //std::cout << ". "; + //e->print( std::cout ); + //std::cout << std::endl; + //std::cout << e->getop() << " " << e->getclass() << std::endl; + } + switch (e->getop()) { + case NOT_CEXPR: + switch(e->getclass()) { + case INT_EXPR: + case RAT_EXPR: + e->inc(); + return e; + case HOLE_EXPR: { + Expr *tmp = e->followDefs(); + if (tmp == e) + report_error("Encountered an unfilled hole running code."); + tmp->inc(); + return tmp; + } + case SYMS_EXPR: + case SYM_EXPR: { + Expr *tmp = e->followDefs(); + //std::cout << "follow def = "; + //tmp->print( std::cout ); + //std::cout << std::endl; + if (tmp == e) { + e->inc(); + return e; + } + tmp->inc(); + return tmp; + } + } + case FAIL: + return NULL; + case DO: { + Expr *tmp = run_code(e->kids[0]); + if (!tmp) + return NULL; + tmp->dec(); + _e = e->kids[1]; + goto start_run_code; + } + case LET: { + Expr *r0 = run_code(e->kids[1]); + if (!r0) + return NULL; + SymExpr *var = (SymExpr *)e->kids[0]; + Expr *prev = var->val; + var->val = r0; + Expr *r1 = run_code(e->kids[2]); + var->val = prev; + r0->dec(); + return r1; + } + case ADD: + case MUL: + case DIV: + { + Expr *r1 = run_code(e->kids[0]); + if (!r1) + return NULL; + Expr *r2 = run_code(e->kids[1]); + if (!r2) + return NULL; + if( r1->getclass()==INT_EXPR && r2->getclass()==INT_EXPR ) + { + mpz_t r; + mpz_init(r); + if( e->getop()==ADD ) + mpz_add(r, ((IntExpr *)r1)->n, ((IntExpr *)r2)->n); + else if( e->getop()==MUL ) + mpz_mul(r, ((IntExpr *)r1)->n, ((IntExpr *)r2)->n); + else if( e->getop()==DIV ) + mpz_cdiv_q(r, ((IntExpr *)r1)->n, ((IntExpr *)r2)->n); + r1->dec(); + r2->dec(); + return new IntExpr(r); + } + else if( r1->getclass()==RAT_EXPR && r2->getclass()==RAT_EXPR ) + { + mpq_t q; + mpq_init(q); + if( e->getop()==ADD ) + mpq_add(q, ((RatExpr *)r1)->n, ((RatExpr *)r2)->n); + else if( e->getop()==MUL ) + mpq_mul(q, ((RatExpr *)r1)->n, ((RatExpr *)r2)->n); + else if( e->getop()==DIV ) + mpq_div(q, ((RatExpr *)r1)->n, ((RatExpr *)r2)->n); + r1->dec(); + r2->dec(); + return new RatExpr(q); + } + else + { + //std::cout << "An arithmetic operation failed. " << r1->getclass() << " " << r2->getclass() << std::endl; + r1->dec(); + r2->dec(); + return NULL; + } + } + case NEG: { + Expr *r1 = run_code(e->kids[0]); + if (!r1) + return NULL; + if (r1->getclass() == INT_EXPR) { + mpz_t r; + mpz_init(r); + mpz_neg(r, ((IntExpr *)r1)->n); + r1->dec(); + return new IntExpr(r); + } + else if( r1->getclass() == RAT_EXPR ) { + mpq_t q; + mpq_init(q); + mpq_neg(q, ((RatExpr *)r1)->n); + r1->dec(); + return new RatExpr(q); + } + else + { + std::cout << "An arithmetic negation failed. " << r1->getclass() << std::endl; + //((SymSExpr*)r1)->val->print( std::cout ); + std::cout << ((SymSExpr*)r1)->val << std::endl; + r1->dec(); + return NULL; + } + } + case IFNEG: + case IFZERO:{ + Expr *r1 = run_code(e->kids[0]); + if (!r1) + return NULL; + + bool cond; + if( r1->getclass() == INT_EXPR ){ + if( e->getop() == IFNEG ) + cond = mpz_sgn( ((IntExpr *)r1)->n )<0; + else if( e->getop() == IFZERO ) + cond = mpz_sgn( ((IntExpr *)r1)->n )==0; + }else if( r1->getclass() == RAT_EXPR ){ + if( e->getop() == IFNEG ) + cond = mpq_sgn( ((RatExpr *)r1)->n )<0; + else if( e->getop() == IFZERO ) + cond = mpq_sgn( ((RatExpr *)r1)->n )==0; + } + else + { + std::cout << "An arithmetic if-expression failed. " << r1->getclass() << std::endl; + r1->dec(); + return NULL; + } + r1->dec(); + + + if( cond ) + _e = e->kids[1]; + else + _e = e->kids[2]; + goto start_run_code; + } + case IFMARKED: { + Expr *r1 = run_code(e->kids[1]); + if (!r1) + return NULL; + if(r1->getclass() != SYM_EXPR && r1->getclass() != SYMS_EXPR ){ + r1->dec(); + return NULL; + } +#ifndef MARKVAR_32 + if (r1->getexmark()) { +#else + if(((SymExpr*)r1)->getmark( ((IntExpr*)e->kids[0])->get_num() ) ){ +#endif + r1->dec(); + _e = e->kids[2]; + goto start_run_code; + } + // else + r1->dec(); + _e = e->kids[3]; + goto start_run_code; + } + case COMPARE: + { + Expr *r1 = run_code(e->kids[0]); + if (!r1) + return NULL; + if (r1->getclass() != SYM_EXPR && r1->getclass() != SYMS_EXPR) { + r1->dec(); + return NULL; + } + Expr *r2 = run_code(e->kids[1]); + if (!r2) + return NULL; + if (r2->getclass() != SYM_EXPR && r2->getclass() != SYMS_EXPR) { + r2->dec(); + return NULL; + } + if( r1<r2 ){ + r1->dec(); + _e = e->kids[2]; + goto start_run_code; + } + //else + r2->dec(); + _e = e->kids[3]; + goto start_run_code; + } + case MARKVAR: { + Expr *r1 = run_code(e->kids[1]); + if (!r1) + return NULL; + if (r1->getclass() != SYM_EXPR && r1->getclass() != SYMS_EXPR) { + r1->dec(); + return NULL; + } +#ifndef MARKVAR_32 + if (r1->getexmark()) + r1->clearexmark(); + else + r1->setexmark(); +#else + if(((SymExpr*)r1)->getmark( ((IntExpr*)e->kids[0])->get_num() ) ) + ((SymExpr*)r1)->clearmark( ((IntExpr*)e->kids[0])->get_num() ); + else + ((SymExpr*)r1)->setmark( ((IntExpr*)e->kids[0])->get_num() ); +#endif + return r1; + } + case MATCH: { + Expr *scrut = run_code(e->kids[0]); + if (!scrut) + return 0; + vector<Expr *> args; + Expr *hd = scrut->collect_args(args); + Expr **cases = &e->kids[1]; + CExpr *c; + Expr *c_or_default; + while ((c_or_default = *cases++)) { + + if (c_or_default->getop() != CASE){ + //std::cout << "run the default " << std::endl; + //c_or_default->print( std::cout ); + // this is the default of the MATCH + return run_code(c_or_default); + } + + // this is a CASE of the MATCH + CExpr *c = (CExpr *)c_or_default; + Expr *p = c->kids[0]; + if (hd == p->get_head()) { + vector<Expr *> vars; + p->collect_args(vars); + int jend = args.size(); + vector<Expr *> old_vals(jend); + for (int j = 0; j < jend; j++) { + SymExpr *var = (SymExpr *)vars[j]; + old_vals[j] = var->val; + var->val = args[j]; + args[j]->inc(); + } + scrut->dec(); + Expr *ret = run_code(c->kids[1] /* the body of the case */); + for (int j = 0; j < jend; j++) { + ((SymExpr *)vars[j])->val = old_vals[j]; + args[j]->dec(); + } + return ret; + } + } + break; + } + case APP: { + Expr *tmp = e->whr(); + if (e != tmp) { + _e = tmp; + goto start_run_code; + } + + // e is in weak head normal form + + vector<Expr *> args; + Expr *hd = e->collect_args(args); + for (int i = 0, iend = args.size(); i < iend; i++) + if (!(args[i] = run_code(args[i]))) { + for (int j = 0; j < i; j++) + args[j]->dec(); + return NULL; + } + if (hd->getop() != PROG) { + hd->inc(); + Expr *tmp = Expr::build_app(hd,args); + return tmp; + } + + CExpr *prog = (CExpr *)hd; + Expr **cur = ((CExpr *)prog->kids[1])->kids; + vector<Expr *> old_vals; + SymExpr *var; + int i = 0; + + if( run_scc && e->get_head( false )->getclass()==SYMS_EXPR ) + { + //std::cout << "running " << ((SymSExpr*)e->get_head( false ))->s.c_str() << " with " << (int)args.size() << " arguments" << std::endl; +//#ifndef USE_FLAT_APP +// for( int a=0; a<(int)args.size(); a++ ) +// { +// args[a] = CExpr::convert_to_flat_app( args[a] ); +// } +//#endif + Expr *ret = run_compiled_scc( e->get_head( false ), args ); + for (int i = 0, iend = args.size(); i < iend; i++) { + args[i]->dec(); + } +//#ifndef USE_FLAT_APP +// ret = CExpr::convert_to_tree_app( ret ); +//#endif + //ret->inc(); + return ret; + } + else + { + while((var = (SymExpr *)*cur++)) { + old_vals.push_back(var->val); + var->val = args[i++]; + } + + if (dbg_prog) { + dbg_prog_indent(cout); + cout << "["; + e->print(cout); + cout << "\n"; + } + dbg_prog_indent_lvl++; + + Expr *ret = run_code(prog->kids[2]); + + dbg_prog_indent_lvl--; + if (dbg_prog) { + dbg_prog_indent(cout); + cout << "= "; + if (ret) + ret->print(cout); + else + cout << "fail"; + cout << "]\n"; + } + + cur = ((CExpr *)prog->kids[1])->kids; + i = 0; + while((var = (SymExpr *)*cur++)) { + args[i]->dec(); + var->val = old_vals[i++]; + } + return ret; + } + } + } // end switch + return NULL; +} + +int read_index() +{ + int index = 1; + string v; + char c; + while (isdigit(c = our_getc())) + v.push_back(c); + our_ungetc(c); + if( v.length()>0 ) + { + index = atoi( v.c_str() ); + } + return index; +}
\ No newline at end of file diff --git a/proofs/lfsc_checker/code.h b/proofs/lfsc_checker/code.h new file mode 100644 index 000000000..9d00a6378 --- /dev/null +++ b/proofs/lfsc_checker/code.h @@ -0,0 +1,15 @@ +#ifndef SC2_CODE_H +#define SC2_CODE_H + +#include "expr.h" + +Expr *read_code(); +Expr *check_code(Expr *); // compute the type for the given code +Expr *run_code(Expr *); + +int read_index(); + +extern bool dbg_prog; +extern bool run_scc; + +#endif diff --git a/proofs/lfsc_checker/configure.ac b/proofs/lfsc_checker/configure.ac new file mode 100644 index 000000000..5f4353664 --- /dev/null +++ b/proofs/lfsc_checker/configure.ac @@ -0,0 +1,47 @@ +# -*- Autoconf -*- +# Process this file with autoconf to produce a configure script. + +AC_PREREQ([2.61]) +AC_INIT([lfsc-checker], [1.0], [cvc-bugs@cs.nyu.edu]) +AC_CONFIG_SRCDIR([libwriter.h]) +AC_CONFIG_AUX_DIR([config]) +AC_CONFIG_MACRO_DIR([config]) +AC_CONFIG_HEADERS([config.h]) +AM_INIT_AUTOMAKE([1.11 foreign no-define tar-pax]) +LT_INIT + +AC_CANONICAL_BUILD +AC_CANONICAL_HOST +AC_CANONICAL_TARGET + +m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])]) + +# on by default +AM_MAINTAINER_MODE([enable]) + +# turn off static lib building by default +AC_ENABLE_SHARED +AC_DISABLE_STATIC + +# Checks for programs. +AC_PROG_CXX +AC_PROG_CC + +# Checks for libraries. +# FIXME: Replace `main' with a function in `-lgmp': +AC_CHECK_LIB([gmp], [__gmpz_init]) + +# Checks for header files. +AC_CHECK_HEADERS([stdlib.h string.h]) + +# Checks for typedefs, structures, and compiler characteristics. +AC_HEADER_STDBOOL +AC_C_INLINE +AC_TYPE_SIZE_T + +# Checks for library functions. +AC_FUNC_MALLOC +AC_CHECK_FUNCS([strdup]) + +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/proofs/lfsc_checker/expr.cpp b/proofs/lfsc_checker/expr.cpp new file mode 100644 index 000000000..7ffc6469a --- /dev/null +++ b/proofs/lfsc_checker/expr.cpp @@ -0,0 +1,966 @@ +#include "expr.h" +#include <stdlib.h> +#include <sstream> +#ifdef _MSC_VER +#include <algorithm> +#endif +#include "check.h" + +using namespace std; + +int HoleExpr::next_id = 0; +int Expr::markedCount = 0; + +C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_CC(CExpr,kids,32768); + +//C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_CC(IntCExpr,_n,32768); + +#define USE_HOLE_PATH_COMPRESSION + +void Expr::debug() { + print(cout); + /* + cout << "\nAt " << this << "\n"; + cout << "marked = " << getmark() << "\n"; + */ + cout << "\n"; + cout.flush(); +} + +bool destroy_progs = false; + +#define destroydec(rr) \ + do { \ + Expr *r = rr; \ + int ref = r->data >> 9; \ + ref = ref - 1; \ + r->debugrefcnt(ref,DEC); \ + if (ref == 0) { \ + _e = r; \ + goto start_destroy; \ + } \ + else \ + r->data = (ref << 9) | (r->data & 511); \ + } while(0) + + +void Expr::destroy(Expr *_e, bool dec_kids) { + start_destroy: + switch (_e->getclass()) { + case INT_EXPR: + delete (IntExpr *)_e; + break; + case SYMS_EXPR: { + SymSExpr *e = (SymSExpr *)_e; + if (e->val && e->val->getop() != PROG) { + Expr *tmp = e->val; + delete e; + destroydec(tmp); + } + else + delete e; + break; + } + case SYM_EXPR: { + SymExpr *e = (SymExpr *)_e; + if (e->val && e->val->getop() != PROG) { + Expr *tmp = e->val; + delete e; + destroydec(tmp); + } + else + delete e; + break; + } + case HOLE_EXPR: { + HoleExpr *e = (HoleExpr *)_e; + if (e->val) { + Expr *tmp = e->val; + delete e; + destroydec(tmp); + } + else + delete e; + break; + } + case CEXPR: { + CExpr *e = (CExpr *)_e; + if (dec_kids) { + Expr **cur = e->kids; + Expr *tmp; + while((tmp = *cur++)) { + if (*cur) + tmp->dec(); + else { + delete e; + destroydec(tmp); + break; + } + } + } + else + delete e; + break; + } + } +} + +Expr *Expr::clone() { + switch (getclass()) { + case INT_EXPR: + case RAT_EXPR: + inc(); + return this; + case SYMS_EXPR: + case SYM_EXPR: { + SymExpr *e = (SymExpr *)this; + if (e->val) + if (e->val->getop() != PROG) + return e->val->clone(); + e->inc(); + return e; + } + case HOLE_EXPR: { + HoleExpr *e = (HoleExpr *)this; + if (e->val) + return e->val->clone(); + e->inc(); + return e; + } + case CEXPR: { + CExpr *e = (CExpr *)this; + int op = e->getop(); + switch(op) { + case LAM: { +#ifdef DEBUG_SYM_NAMES + SymSExpr *var = (SymSExpr *)e->kids[0]; + SymSExpr *newvar = new SymSExpr(*var,SYMS_EXPR); +#else + SymExpr *var = (SymExpr *)e->kids[0]; + SymExpr *newvar = new SymExpr(*var); +#endif + Expr *prev = var->val; + var->val = newvar; + Expr *bod = e->kids[1]->clone(); + var->val = prev; + return new CExpr(LAM,newvar,bod); + } + case PI: { +#ifdef DEBUG_SYM_NAMES + SymSExpr *var = (SymSExpr *)e->kids[0]; + SymSExpr *newvar = new SymSExpr(*var,SYMS_EXPR); +#else + SymExpr *var = (SymExpr *)e->kids[0]; + SymExpr *newvar = new SymExpr(*var); +#endif + Expr *tp = e->kids[1]->clone(); + Expr *prev = var->val; + var->val = newvar; + Expr *bod = e->kids[2]->clone(); + var->val = prev; + Expr* ret = new CExpr(PI,newvar,tp,bod); + if( data&256 ) + ret->data |=256; + return ret; + } + default: { + Expr **cur = e->kids; + Expr *tmp; + int size = 0; + while((*cur++)) + size++; + Expr **kids = new Expr*[size+1]; + kids[size]=0; + cur = e->kids; + bool diff_kid = false; + int i = 0; + while((tmp = *cur++)) { + Expr *c = tmp->clone(); + diff_kid |= (c != tmp); + kids[i++] = c; + } + if (diff_kid) + return new CExpr(op, true /* dummy */, kids); + for (int i = 0, iend = size; i != iend; i++) + kids[i]->dec(); + delete[] kids; + e->inc(); + return e; + } + } + } + } + return 0; // should never be reached +} + + +Expr* Expr::build_app(Expr *hd, const std::vector<Expr *> &args, int start) { +#ifndef USE_FLAT_APP + Expr *ret = hd; + for (int i = start, iend = args.size(); i < iend; i++) + ret = new CExpr(APP,ret,args[i]); + return ret; +#else + if( start>=(int)args.size() ) + return hd; + else + { + CExpr *ret = new CExpr( APP ); + ret->kids = new Expr* [args.size()-start+2]; + ret->kids[0] = hd; + for (int i = start, iend = args.size(); i < iend; i++) + ret->kids[i-start+1] = args[i]; + ret->kids[args.size()-start+1] = NULL; + return ret; + } +#endif +} + +Expr* Expr::make_app(Expr* e1, Expr* e2 ) +{ + //std::cout << "make app from "; + //e1->print( std::cout ); + //std::cout << " "; + //e2->print( std::cout ); + //std::cout << std::endl; + CExpr *ret; + if( e1->getclass()==CEXPR ){ + int counter = 0; + while( ((CExpr*)e1)->kids[counter] ){ + counter++; + } + ret = new CExpr( APP ); + ret->kids = new Expr* [counter+2]; + counter = 0; + while( ((CExpr*)e1)->kids[counter] ){ + ret->kids[counter] = ((CExpr*)e1)->kids[counter]; + counter++; + } + ret->kids[counter] = e2; + ret->kids[counter+1] = NULL; + }else{ + ret = new CExpr( APP, e1, e2 ); + } + //ret->print( std::cout ); + //std::cout << std::endl; + return ret; +} + +int Expr::cargCount = 0; + +Expr *Expr::collect_args(std::vector<Expr *> &args, bool follow_defs) { + //cargCount++; + //if( cargCount%1000==0) + //std::cout << cargCount << std::endl; +#ifndef USE_FLAT_APP + CExpr *e = (CExpr *)this; + args.reserve(16); + while( e->getop() == APP ) { + args.push_back(e->kids[1]); + e = (CExpr *)e->kids[0]; + if (follow_defs) + e = (CExpr *)e->followDefs(); + } + std::reverse(args.begin(),args.end()); + return e; +#else + CExpr *e = (CExpr *)this; + args.reserve(16); + if( e->getop()==APP ){ + int counter = 1; + while( e->kids[counter] ) { + args.push_back(e->kids[counter]); + counter++; + } + e = (CExpr*)e->kids[0]; + } + if (follow_defs) + return e->followDefs(); + else + return e; +#endif +} + +Expr *Expr::get_head(bool follow_defs) { + CExpr *e = (CExpr *)this; + while( e->getop() == APP ) { + e = (CExpr *)e->kids[0]; + if (follow_defs) + e = (CExpr *)e->followDefs(); + } + return e; +} + +Expr *Expr::get_body(int op, bool follow_defs) { + CExpr *e = (CExpr *)this; + while( e->getop() == op ) { + e = (CExpr *)e->kids[2]; + if (follow_defs) + e = (CExpr *)e->followDefs(); + } + return e; +} + +// if the return value is different from this, then it is a new reference +Expr *CExpr::whr() { + vector<Expr *> args; + if (get_head()->getop() == LAM) { + CExpr *head = (CExpr *)collect_args(args, true); + Expr *cloned_head; + if (head->cloned()) { + // we must clone + head = (CExpr *)head->clone(); + cloned_head = head; + } + else { + head->setcloned(); + cloned_head = 0; + } + int i = 0; + int iend = args.size(); + + /* we will end up incrementing the ref count for all the args, + since each is either pointed to by a var (following a + beta-reduction), or else just an argument in the new + application we build below. */ + + do { + Expr *tmp = args[i++]->followDefs(); + ((SymExpr *)head->kids[0])->val = tmp; + tmp->inc(); + head = (CExpr *)head->kids[1]; + } while(head->getop() == LAM && i < iend); + for (; i < iend; i++) + args[i]->inc(); + head->inc(); + if (cloned_head) + cloned_head->dec(); + return build_app(head,args,i); + } + else + return this; +} + +Expr* CExpr::convert_to_tree_app( Expr* e ) +{ + if( e->getop()==APP ) + { + std::vector< Expr* > kds; + int counter = 1;
+ while( ((CExpr*)e)->kids[counter] )
+ { + kds.push_back( convert_to_tree_app( ((CExpr*)e)->kids[counter] ) ); + counter++; + } + Expr* app = Expr::build_app( e->get_head(), kds ); + //app->inc(); + return app; + } + else + { + return e; + } +} + +Expr* CExpr::convert_to_flat_app( Expr* e ) +{ + if( e->getop()==APP ) + { + std::vector< Expr* > args; + Expr* hd = ((CExpr*)e)->collect_args( args ); + CExpr* nce = new CExpr( APP ); + nce->kids = new Expr *[(int)args.size()+2]; + nce->kids[0] = hd; + for( int a=0; a<(int)args.size(); a++ ) + { + nce->kids[a+1] = convert_to_flat_app( args[a] ); + } + nce->kids[(int)args.size()+1] = 0; + nce->inc(); + return nce; + } + else + { + return e; + } +} + +bool Expr::defeq(Expr *e) { + + /* we handle a few special cases up front, where this Expr might + equal e, even though they have different opclass (i.e., different + structure). */ + + if (this == e) + return true; + int op1 = getop(); + int op2 = e->getop(); + switch (op1) { + case ASCRIBE: + return ((CExpr *)this)->kids[0]->defeq(e); + case APP: { + Expr *tmp = ((CExpr *)this)->whr(); + if (tmp != this) { + bool b = tmp->defeq(e); + tmp->dec(); + return b; + } + if (get_head()->getclass() == HOLE_EXPR) { + vector<Expr *> args; + Expr *head = collect_args(args, true); + Expr *t = e; + t->inc(); + for (int i = 0, iend = args.size(); i < iend; i++) { + // don't worry about SYMS_EXPR's, since we should not be in code here. + if (args[i]->getclass() != SYM_EXPR || args[i]->getexmark()) + /* we cannot fill the hole in this case. Either this is not + a variable or we are using a variable again. */ + return false; + SymExpr *v = (SymExpr *)args[i]; + + // we may have been mapping from expected var v to a computed var + Expr *tmp = (v->val ? v->val : v); + + tmp->inc(); + t = new CExpr(LAM, tmp, t); + args[i]->setexmark(); + } + for (int i = 0, iend = args.size(); i < iend; i++) + args[i]->clearexmark(); +#ifdef DEBUG_HOLES + cout << "Filling hole "; + head->debug(); + cout << "with "; + t->debug(); +#endif + ((HoleExpr *)head)->val = t; + return true; + } + break; + } + case NOT_CEXPR: + switch (getclass()) { + case HOLE_EXPR: { + HoleExpr *h = (HoleExpr *)this; + if (h->val) + return h->val->defeq(e); +#ifdef DEBUG_HOLES + cout << "Filling hole "; + h->debug(); + cout << "with "; + e->debug(); +#endif +#ifdef USE_HOLE_PATH_COMPRESSION + Expr *tmp = e->followDefs(); +#else + Expr *tmp = e; +#endif + h->val = tmp; + tmp->inc(); + return true; + } + case SYMS_EXPR: + case SYM_EXPR: { + SymExpr *s = (SymExpr *)this; + if (s->val) + return s->val->defeq(e); + break; + } + } + break; + } + + switch (op2) { + case ASCRIBE: + return defeq(((CExpr *)e)->kids[0]); + case APP: { + Expr *tmp = ((CExpr *)e)->whr(); + if (tmp != e) { + bool b = defeq(tmp); + tmp->dec(); + return b; + } + break; + } + case NOT_CEXPR: + switch (e->getclass()) { + case HOLE_EXPR: { + HoleExpr *h = (HoleExpr *)e; + if (h->val) + return defeq(h->val); + +#ifdef DEBUG_HOLES + cout << "Filling hole "; + h->debug(); + cout << "with "; + debug(); +#endif +#ifdef USE_HOLE_PATH_COMPRESSION + Expr *tmp = followDefs(); +#else + Expr *tmp = this; +#endif + h->val = tmp; + tmp->inc(); + return true; + } + case SYMS_EXPR: + case SYM_EXPR: { + SymExpr *s = (SymExpr *)e; + if (s->val) + return defeq(s->val); + break; + } + } + break; + } + + /* at this point, e1 and e2 must have the same opclass if they are + to be equal. */ + + if (op1 != op2) + return false; + + if (op1 == NOT_CEXPR) { + switch(getclass()) { + case INT_EXPR: { + IntExpr *i1 = (IntExpr *)this; + IntExpr *i2 = (IntExpr *)e; + return (mpz_cmp(i1->n,i2->n) == 0); + } + case RAT_EXPR: { + RatExpr *r1 = (RatExpr *)this; + RatExpr *r2 = (RatExpr *)e; + return (mpq_cmp(r1->n,r2->n) == 0); + } + case SYMS_EXPR: + case SYM_EXPR: + return (this == e); + } + } + + /* Now op1 and op2 must both be CExprs, and must have the same op to be + equal. */ + + CExpr *e1 = (CExpr *)this; + CExpr *e2 = (CExpr *)e; + + int last = 1; + switch (op1) { + case PI: + if (!e1->kids[1]->defeq(e2->kids[1])) + return false; + last++; + // fall through to LAM case + case LAM: { + + /* It is critical that we point e1's var. (v1) back to e2's (call + it v2). The reason this is critical is that we assume any + holes are in e1. So we could end up with (_ v1) = t. We wish + to fill _ in this case with (\ v2 t). If v2 pointed to v1, we + could not return (\ v1 t), because the fact that v2 points to + v1 would then be lost. + */ + SymExpr *v1 = (SymExpr *)e1->kids[0]; + Expr *prev_v1_val = v1->val; + v1->val = e2->kids[0]->followDefs(); + bool bodies_equal = e1->kids[last]->defeq(e2->kids[last]); + v1->val = prev_v1_val; + return bodies_equal; + } + case APP: +#ifndef USE_FLAT_APP + return (e1->kids[0]->defeq(e2->kids[0]) && + e1->kids[1]->defeq(e2->kids[1])); +#else + { + int counter = 0; + while( e1->kids[counter] ){ + if( !e2->kids[counter] || !e1->kids[counter]->defeq( e2->kids[counter] ) ) + return false; + counter++; + } + return e2->kids[counter]==NULL; + } +#endif + case TYPE: + case KIND: + case MPZ: + // already checked that both exprs have the same opclass. + return true; + } // switch(op1) + + return false; // never reached. +} + +int Expr::fiCounter = 0; + +bool Expr::free_in(Expr *x) { + //fiCounter++; + //if( fiCounter%1==0 ) + // std::cout << fiCounter << std::endl; + switch(getop()) { + case NOT_CEXPR: + switch (getclass()) { + case HOLE_EXPR: { + HoleExpr *h = (HoleExpr *)this; + if (h->val) + return h->val->free_in(x); + return (h == x); + } + case SYMS_EXPR: + case SYM_EXPR: { + SymExpr *s = (SymExpr *)this; + if (s->val && s->val->getclass() == HOLE_EXPR) + /* we do not need to follow the "val" pointer except in this + one case, when x is a hole (which we do not bother to check + here) */ + return s->val->free_in(x); + return (s == x); + } + case INT_EXPR: + return false; + } + break; + case LAM: + case PI: + if (x == ((CExpr *)this)->kids[0]) + return false; + // fall through + default: { + // must be a CExpr + CExpr *e = (CExpr *)this; + Expr *tmp; + Expr **cur = e->kids; + while ((tmp = *cur++)) + if (tmp->free_in(x)) + return true; + return false; + } + } + return false; // should not be reached +} + +void Expr::calc_free_in(){ + data &= ~256; + data |= 256*((CExpr *)this)->kids[2]->free_in( ((CExpr *)this)->kids[0] ); +} + +string Expr::toString() { + ostringstream oss; + print(oss); + return oss.str(); +} + +static void print_kids(ostream &os, Expr **kids) { + Expr *tmp; + while ((tmp = *kids++)) { + os << " "; + tmp->print(os); + } +} + +static void print_vector(ostream &os, const vector<Expr *> &v) { + for(int i = 0, iend = v.size(); i < iend; i++) { + os << " "; + v[i]->print(os); + } +} + +void Expr::print(ostream &os) { + CExpr *e = (CExpr *)this; // for CEXPR cases + + //std::cout << e->getop() << " "; + /* +#ifdef DEBUG_REFCNT + os << "<"; + char tmp[10]; + sprintf(tmp,"%d",getrefcnt()); + os << tmp << "> "; +#endif +*/ + + switch(getop()) { + case NOT_CEXPR: { + switch(getclass()) { + case INT_EXPR: + { + IntExpr *e = (IntExpr *)this; + if (mpz_sgn(e->n) < 0) { + os << "(~ "; + mpz_t tmp; + mpz_init(tmp); + mpz_neg(tmp,e->n); + char *s = mpz_get_str(0,10,tmp); + os << s; + free(s); + mpz_clear(tmp); + os << ")"; + //os << "mpz"; + } + else { + char *s = mpz_get_str(0,10,e->n); + os << s; + free(s); + //os << "mpz"; + } + break; + } + case RAT_EXPR: + { + RatExpr *e = (RatExpr *)this; + char *s = mpq_get_str(0,10,e->n); + os << s; + if (mpq_sgn(e->n) < 0) { + os << "(~ "; + mpq_t tmp; + mpq_init(tmp); + mpq_neg(tmp,e->n); + char *s = mpq_get_str(0,10,tmp); + os << s; + free(s); + mpq_clear(tmp); + os << ")"; + } + else { + char *s = mpq_get_str(0,10,e->n); + os << s; + free(s); + } + break; + } +#ifndef DEBUG_SYM_NAMES + case SYM_EXPR: + { + SymExpr *e = (SymExpr *)this; + if (e->val) { + if (e->val->getop() == PROG) { + os << e; +#ifdef DEBUG_SYMS + os << "[PROG]"; +#endif + }else{ +#ifdef DEBUG_SYMS + os << e; + os << "[SYM "; +#endif + e->val->print(os); +#ifdef DEBUG_SYMS + os << "]"; +#endif + } + } + else + os << e; + break; + } +#else + case SYM_EXPR: /* if we are debugging sym names, then + SYM_EXPRs are really SymSExprs. */ +#endif + case SYMS_EXPR: { + SymSExpr *e = (SymSExpr *)this; + if (e->val) { + if (e->val->getop() == PROG) { + os << e->s; +#ifdef DEBUG_SYMS + os << "[PROG]"; +#endif + }else{ +#ifdef DEBUG_SYMS + os << e->s; + os << "[SYM "; +#endif + e->val->print(os); +#ifdef DEBUG_SYMS + os << "]"; +#endif + } + } + else + os << e->s; + break; + } + case HOLE_EXPR: + { + HoleExpr *e = (HoleExpr *)this; + if (e->val) { +#ifdef DEBUG_SYMS + os << "_" << "[HOLE "; +#endif + e->val->print(os); +#ifdef DEBUG_SYMS + os << "]"; +#endif + }else { + os << "_"; +#ifdef DEBUG_HOLE_NAMES + char tmp[100]; + sprintf(tmp,"%d",e->id); + os << "[ " << tmp << "]"; +#else + os << "[ " << e << "]"; +#endif + } + break; + } + default: + os << "; unrecognized form of expr"; + break; + } + break; + } // case NOT_CEXPR + case APP: { + os << "("; + vector<Expr *> args; + Expr *head = collect_args(args, false /* follow_defs */); + head->print(os); + print_vector(os, args); + os << ")"; + break; + } + case LAM: + os << "(\\"; + print_kids(os, e->kids); + os << ")"; + break; + case PI: + os << "(!"; + print_kids(os, e->kids); + os << ")"; + break; + case TYPE: + os << "type"; + break; + case KIND: + os << "kind"; + break; + case MPZ: + os << "mpz"; + break; + case MPQ: + os << "mpq"; + break; + case ADD: + os << "(mp_add"; + print_kids(os,e->kids); + os << ")"; + break; + case MUL: + os << "(mp_mul"; + print_kids(os,e->kids); + os << ")"; + break; + case DIV: + os << "(mp_div"; + print_kids(os,e->kids); + os << ")"; + break; + case NEG: + os << "(mp_neg"; + print_kids(os,e->kids); + os << ")"; + break; + case IFNEG: + os << "(ifneg"; + print_kids(os,e->kids); + os << ")"; + break; + case IFZERO: + os << "(ifzero"; + print_kids(os,e->kids); + os << ")"; + break; + case RUN: + os << "(run"; + print_kids(os,e->kids); + os << ")"; + break; + case PROG: + os << "(prog"; + print_kids(os,e->kids); + os << ")"; + break; + case PROGVARS: + os << "("; + print_kids(os,e->kids); + os << ")"; + break; + case MATCH: + os << "(match"; + print_kids(os,e->kids); + os << ")"; + break; + case CASE: + os << "("; + print_kids(os,e->kids); + os << ")"; + break; + case LET: + os << "(let"; + print_kids(os,e->kids); + os << ")"; + break; + case DO: + os << "(do"; + print_kids(os,e->kids); + os << ")"; + break; + case IFMARKED: + os << "(ifmarked"; + print_kids(os,e->kids); + os << ")"; + break; + case COMPARE: + os << "(compare"; + print_kids(os,e->kids); + os << ")"; + break; + case MARKVAR: + os << "(markvar"; + print_kids(os,e->kids); + os << ")"; + break; + case FAIL: + os << "(fail "; + print_kids(os, e->kids); + os << ")"; + break; + case ASCRIBE: + os << "(:"; + print_kids(os, e->kids); + os << ")"; + break; + default: + os << "; unrecognized form of expr(2) " << getop() << " " << getclass(); + } // switch(getop()) +} + +bool Expr::isType( Expr* statType ){ + Expr* typ = this; + while( typ!=statType ){ + if( typ->getop()==PI ){ + typ = ((CExpr*)typ)->kids[2]; + }else{ + return false; + } + } + return true; +} + +int SymExpr::symmCount = 0; +#ifdef MARKVAR_32 +int SymExpr::mark() +{ + if( mark_map.find( this )== mark_map.end() ) + { + symmCount++; + mark_map[this] = 0; + } + return mark_map[this]; +} +void SymExpr::smark( int m ) +{ + mark_map[this] = m; +} +#endif
\ No newline at end of file diff --git a/proofs/lfsc_checker/expr.h b/proofs/lfsc_checker/expr.h new file mode 100644 index 000000000..32a62ab33 --- /dev/null +++ b/proofs/lfsc_checker/expr.h @@ -0,0 +1,367 @@ +#ifndef sc2__expr_h +#define sc2__expr_h + +#include "gmp.h" +#include <string> +#include <vector> +#include <iostream> +#include <map> +#include "chunking_memory_management.h" + +#define USE_FLAT_APP +#define MARKVAR_32 +#define DEBUG_SYM_NAMES +//#define DEBUG_SYMS + +// Expr class +enum { CEXPR = 0, + INT_EXPR, + RAT_EXPR, + HOLE_EXPR, + SYM_EXPR, + SYMS_EXPR }; + +// operators for CExprs +enum { NOT_CEXPR = 0, // for INT_EXPR, HOLE_EXPR, SYM_EXPR, SYMS_EXPR + APP, + PI, + LAM, + TYPE, + KIND, + ASCRIBE, + MPZ, + MPQ, + + PROG, + PROGVARS, + MATCH, + CASE, + PAT, + DO, + ADD, + MUL, + DIV, + NEG, + IFNEG, + IFZERO, + LET, + RUN, + FAIL, + MARKVAR, + IFMARKED, + COMPARE +}; + +class SymExpr; + +class Expr { +protected: + /* bits 0-2: Expr class + bits 3-7: operator + bit 8: a flag for already cloned, free_in calculation + bits 9-31: ref count*/ + int data; + + void destroy(bool dec_kids); + + enum { INC, DEC, CREATE }; + void debugrefcnt(int ref, int what) { +#ifdef DEBUG_REFCNT + std::cout << "["; + debug(); + switch(what) { + case INC: + std::cout << " inc to "; + break; + case DEC: + std::cout << " dec to "; + break; + case CREATE: + std::cout << " creating]\n"; + return; + } + char tmp[10]; + sprintf(tmp,"%d",ref); + std::cout << tmp << "]\n"; +#else + (void)ref; + (void)what; +#endif + } + + Expr(int _class, int _op) + : data(1 << 9 /* refcount 1, not cloned */| (_op << 3) | _class) + { } + +public: + static int markedCount; + inline Expr* followDefs(); + inline int getclass() const { return data & 7; } + int getexmark() const { return data & 256; } + void setexmark() { data |= 256; } + void clearexmark() { data &= ~256; } + inline int getop() const { return (data >> 3) & 31; } + int cloned() const { return data & 256; } + void setcloned() { data |= 256; } + + inline int getrefcnt() { return data >> 9; } + inline void inc() { + int ref = getrefcnt(); + //static int iCounter = 0; + //iCounter++; + //if( iCounter%10000==0 ){ + // //print( std::cout ); + // std::cout << " " << ref << std::endl; + //} + ref = ref<4194303 ? ref + 1 : ref; + debugrefcnt(ref,INC); + data = (ref << 9) | (data & 511); + } + static void destroy(Expr *, bool); + inline void dec(bool dec_kids = true) { + int ref = getrefcnt(); + ref = ref - 1; + debugrefcnt(ref,DEC); + if (ref == 0) + destroy(this,dec_kids); + else + data = (ref << 9) | (data & 511); + } + + //must pass statType (the expr representing "type") to this function + bool isType( Expr* statType ); + + inline bool isDatatype() { + return getclass() == SYMS_EXPR || getop() == MPZ || getop() == MPQ; + } + inline bool isArithTerm() { + return getop() == ADD || getop() == NEG; + } + + static Expr *build_app(Expr *hd, const std::vector<Expr *> &args, + int start = 0); + + static Expr *make_app(Expr* e1, Expr* e2 ); + + /* if this is an APP, return the head, and store the args in args. + If follow_defs is true, we proceed through defined heads; + otherwise not. */ + Expr *collect_args(std::vector<Expr *> &args, bool follow_defs = true); + + Expr *get_head(bool follow_defs = true); + + Expr *get_body(int op = PI, bool follow_defs = true); + + std::string toString(); + + void print(std::ostream &); + void debug(); + + /* check whether or not this expr is alpha equivalent to e. If this + expr contains unfilled holes, fill them as we go. We do not fill + holes in e. We do not take responsibility for the reference to + this nor the reference to e. */ + bool defeq(Expr *e); + + /* return a clone of this expr. All abstractions are really duplicated + in memory. Other expressions may not actually be duplicated in + memory, but their refcounts will be incremented. */ + Expr *clone(); + + // x can be a SymExpr or a HoleExpr. + bool free_in(Expr *x); + bool get_free_in() { return data & 256; } + void calc_free_in(); + + static int cargCount; + static int fiCounter; +}; + +class CExpr : public Expr { +public: + C_MACROS__ADD_CHUNKING_MEMORY_MANAGEMENT_H(CExpr,kids); + + Expr **kids; + ~CExpr() { + delete[] kids; + } + CExpr(int _op) : Expr(CEXPR, _op), kids() { + kids = new Expr *[1]; + kids[0] = 0; + debugrefcnt(1,CREATE); + } + CExpr(int _op, Expr *e1) : Expr(CEXPR, _op), kids() { + kids = new Expr *[2]; + kids[0] = e1; + kids[1] = 0; + debugrefcnt(1,CREATE); + } + CExpr(int _op, Expr *e1, Expr *e2) + : Expr(CEXPR, _op), kids() { + kids = new Expr *[3]; + kids[0] = e1; + kids[1] = e2; + kids[2] = 0; + debugrefcnt(1,CREATE); + } + CExpr(int _op, Expr *e1, Expr *e2, Expr *e3) + : Expr(CEXPR, _op), kids() { + kids = new Expr *[4]; + kids[0] = e1; + kids[1] = e2; + kids[2] = e3; + kids[3] = 0; + debugrefcnt(1,CREATE); + } + CExpr(int _op, Expr *e1, Expr *e2, Expr *e3, Expr *e4) + : Expr(CEXPR, _op), kids() { + kids = new Expr *[5]; + kids[0] = e1; + kids[1] = e2; + kids[2] = e3; + kids[3] = e4; + kids[4] = 0; + debugrefcnt(1,CREATE); + } + CExpr(int _op, const std::vector<Expr *> &_kids) + : Expr(CEXPR, _op), kids() { + int i, iend = _kids.size(); + kids = new Expr *[iend + 1]; + for (i = 0; i < iend; i++) + kids[i] = _kids[i]; + kids[i] = 0; + debugrefcnt(1,CREATE); + } + + // _kids must be null-terminated. + CExpr(int _op, bool dummy, Expr **_kids) : Expr(CEXPR, _op), kids(_kids) { + (void)dummy; + debugrefcnt(1,CREATE); + } + + Expr *whr(); + + static Expr* convert_to_tree_app( Expr* ce ); + static Expr* convert_to_flat_app( Expr* ce ); +}; + +class IntExpr : public Expr { + public: + mpz_t n; + ~IntExpr() { + mpz_clear(n); + } + IntExpr(mpz_t _n) : Expr(INT_EXPR, 0), n() { + mpz_init_set(n,_n); + debugrefcnt(1,CREATE); + } + IntExpr(signed long int _n ) : Expr(INT_EXPR, 0), n() { + mpz_init_set_si( n, _n ); + } + + unsigned long int get_num() { return mpz_get_ui( n ); } +}; + +class RatExpr : public Expr { + public: + mpq_t n; + ~RatExpr() { + mpq_clear(n); + } + RatExpr(mpq_t _n) : Expr(RAT_EXPR, 0), n() { + mpq_init( n ); + mpq_set(n,_n); + debugrefcnt(1,CREATE); + mpq_canonicalize( n ); + } + RatExpr(signed long int _n1, unsigned long int _n2 ) : Expr(RAT_EXPR, 0), n() { + mpq_init( n ); + mpq_set_si( n, _n1, _n2 ); + mpq_canonicalize( n ); + } +}; + +class SymExpr : public Expr { + public: + Expr *val; // may be set by beta-reduction and clone(). + static int symmCount; + + SymExpr(std::string _s, int theclass = SYM_EXPR) + : Expr(theclass, 0), val(0) + { + (void)_s; + if (theclass == SYM_EXPR) + debugrefcnt(1,CREATE); + } + SymExpr(const SymExpr &e, int theclass = SYM_EXPR) + : Expr(theclass, 0), val(0) + { + (void)e; + if (theclass == SYM_EXPR) + debugrefcnt(1,CREATE); + } +#ifdef MARKVAR_32 +private: + int mark(); + void smark( int m ); +public: + int getmark( int i = 0 ) { return (mark() >> i)&1; } + void setmark( int i = 0 ) { smark( mark() | (1 << i) ); } + void clearmark( int i = 0 ) { smark( mark() & ~(1 << i) ); } +#endif +}; + +class SymSExpr : public SymExpr { + public: + std::string s; + SymSExpr(std::string _s, int theclass = SYMS_EXPR) + : SymExpr(_s, theclass), s(_s) + { + debugrefcnt(1,CREATE); + } + SymSExpr(const SymSExpr &e, int theclass = SYMS_EXPR) + : SymExpr(e, theclass), s(e.s) + { + debugrefcnt(1,CREATE); + } +}; + +class HoleExpr : public Expr { + static int next_id; +public: +#ifdef DEBUG_HOLE_NAMES + int id; +#endif + HoleExpr() + : Expr(HOLE_EXPR, 0), val(0) + { +#ifdef DEBUG_HOLE_NAMES + id = next_id++; +#endif + debugrefcnt(1,CREATE); + } + Expr *val; // may be set during subst(), defeq(), and clone(). +}; + +inline Expr * Expr::followDefs() { + switch(getclass()) { + case HOLE_EXPR: { + HoleExpr *h = (HoleExpr *)this; + if (h->val) + return h->val->followDefs(); + break; + } + case SYMS_EXPR: + case SYM_EXPR: { + SymExpr *h = (SymExpr *)this; + if (h->val) + return h->val->followDefs(); + break; + } + } + + return this; +} + +#endif + diff --git a/proofs/lfsc_checker/libwriter.cpp b/proofs/lfsc_checker/libwriter.cpp new file mode 100644 index 000000000..49e9bbaad --- /dev/null +++ b/proofs/lfsc_checker/libwriter.cpp @@ -0,0 +1,238 @@ +#include "libwriter.h"
+#include <sstream>
+#include <algorithm>
+#include <fstream>
+
+void libwriter::get_var_name( const std::string& n, std::string& nn ) {
+ nn = std::string( n.c_str() );
+ for( int i = 0; i <(int)n.length(); i++ ){
+ char c = n[i];
+ if (c <= 47)
+ c += 65;
+ else if (c >= 58 && c <= 64)
+ c += 97-58;
+ if ((c >= 91 && c <= 94) || c == 96)
+ c += 104-91;
+ else if (c >= 123)
+ c -= 4;
+ nn[i] = c;
+ }
+}
+
+void libwriter::write_file()
+{
+ //std::cout << "write lib" << std::endl;
+ std::ostringstream os_enum;
+ std::ostringstream os_print;
+ std::ostringstream os_constructor_h;
+ std::ostringstream os_constructor_c;
+
+ for ( int a=0; a<(int)syms.size(); a++ ) {
+ //std::cout << "sym #" << (a+1) << ": ";
+ //std::cout << ((SymSExpr*)syms[a])->s.c_str() << std::endl;
+ //defs[a]->print( std::cout );
+ //std::cout << std::endl;
+
+ if( defs[a]->getclass()==CEXPR ){
+ //calculate which arguments are required for input
+ std::vector< Expr* > args;
+ std::vector< bool > argsNeed;
+ std::vector< Expr* > argTypes;
+ CExpr* c = ((CExpr*)defs[a]);
+ while( c->getop()==PI ){
+ //std::cout << c->kids[0] << std::endl;
+ if( ((CExpr*)c->kids[1])->getop()!=RUN ){
+ args.push_back( c->kids[0] );
+ argsNeed.push_back( true );
+ argTypes.push_back( c->kids[1] );
+ }
+ for( int b=0; b<(int)args.size(); b++ ){
+ if( argsNeed[b] ){
+ if( ((CExpr*)c->kids[1])->getop()==RUN ){
+ if( ((CExpr*)c->kids[1])->kids[1]->free_in( args[b] ) ){
+ argsNeed[b] = false;
+ }
+ }else{
+ if( c->kids[1]->free_in( args[b] ) ){
+ argsNeed[b] = false;
+ }
+ }
+ }
+ }
+ c = (CExpr*)(c->kids[2]);
+ }
+
+ //record if this declares a judgement
+ if( ((CExpr*)defs[a])->getop()==PI && c->getop()==TYPE ){
+ //std::cout << "This is a judgement" << std::endl;
+ judgements.push_back( syms[a] );
+ //record if this declares a proof rule
+ }else if( c->getclass()==CEXPR && std::find( judgements.begin(), judgements.end(), c->kids[0] )!=judgements.end() ){
+ std::cout << "Handle rule: " << ((SymSExpr*)syms[a])->s.c_str() << std::endl;
+ //std::cout << "These are required to input:" << std::endl;
+ //for( int b=0; b<(int)args.size(); b++ ){
+ // if( argsNeed[b] ){
+ // std::cout << ((SymSExpr*)args[b])->s.c_str() << std::endl;
+ // }
+ //}
+ os_enum << " rule_" << ((SymSExpr*)syms[a])->s.c_str() << "," << std::endl;
+
+ os_print << " case rule_" << ((SymSExpr*)syms[a])->s.c_str() << ": os << \"";
+ os_print << ((SymSExpr*)syms[a])->s.c_str() << "\";break;" << std::endl;
+
+ std::ostringstream os_args;
+ os_args << "(";
+ bool firstTime = true;
+ for( int b=0; b<(int)args.size(); b++ ){
+ if( argsNeed[b] ){
+ if( !firstTime )
+ os_args << ",";
+ std::string str;
+ get_var_name( ((SymSExpr*)args[b])->s, str );
+ os_args << " LFSCProof* " << str.c_str();
+ firstTime = false;
+ }
+ }
+ if( !firstTime ){
+ os_args << " ";
+ }
+ os_args << ")";
+
+ os_constructor_h << " static LFSCProof* make_" << ((SymSExpr*)syms[a])->s.c_str();
+ os_constructor_h << os_args.str().c_str() << ";" << std::endl;
+
+ os_constructor_c << "LFSCProof* LFSCProof::make_" << ((SymSExpr*)syms[a])->s.c_str();
+ os_constructor_c << os_args.str().c_str() << "{" << std::endl;
+ os_constructor_c << " LFSCProof **kids = new LFSCProof *[" << (int)args.size()+1 << "];" << std::endl;
+ for( int b=0; b<(int)args.size(); b++ ){
+ os_constructor_c << " kids[" << b << "] = ";
+ if( argsNeed[b] ){
+ std::string str;
+ get_var_name( ((SymSExpr*)args[b])->s, str );
+ os_constructor_c << str.c_str();
+ }else{
+ os_constructor_c << "hole";
+ }
+ os_constructor_c << ";" << std::endl;
+ }
+ os_constructor_c << " kids[" << (int)args.size() << "] = 0;" << std::endl;
+ os_constructor_c << " return new LFSCProofC( rule_" << ((SymSExpr*)syms[a])->s.c_str() << ", kids );" << std::endl;
+ os_constructor_c << "}" << std::endl << std::endl;
+ }
+ }
+
+ //write the header
+ static std::string filename( "lfsc_proof" );
+ std::fstream fsh;
+ std::string fnameh( filename );
+ fnameh.append(".h");
+ fsh.open( fnameh.c_str(), std::ios::out );
+
+ fsh << "#ifndef LFSC_PROOF_LIB_H" << std::endl;
+ fsh << "#define LFSC_PROOF_LIB_H" << std::endl;
+ fsh << std::endl;
+ fsh << "#include <string>" << std::endl;
+ fsh << std::endl;
+ fsh << "class LFSCProof{" << std::endl;
+ fsh << "protected:" << std::endl;
+ fsh << " enum{" << std::endl;
+ fsh << os_enum.str().c_str();
+ fsh << " };" << std::endl;
+ fsh << " static LFSCProof* hole;" << std::endl;
+ fsh << " LFSCProof(){}" << std::endl;
+ fsh << "public:" << std::endl;
+ fsh << " virtual ~LFSCProof(){}" << std::endl;
+ fsh << " static void init();" << std::endl;
+ fsh << std::endl;
+ fsh << " //functions to build LFSC proofs" << std::endl;
+ fsh << os_constructor_h.str().c_str();
+ fsh << std::endl;
+ fsh << " virtual void set_child( int i, LFSCProof* e ) {}" << std::endl;
+ fsh << " virtual void print( std::ostream& os ){}" << std::endl;
+ fsh << "};" << std::endl;
+ fsh << std::endl;
+ fsh << "class LFSCProofC : public LFSCProof{" << std::endl;
+ fsh << " short id;" << std::endl;
+ fsh << " LFSCProof **kids;" << std::endl;
+ fsh << "public:" << std::endl;
+ fsh << " LFSCProofC( short d_id, LFSCProof **d_kids ) : id( d_id ), kids( d_kids ){}" << std::endl;
+ fsh << " void set_child( int i, LFSCProof* e ) { kids[i] = e; }" << std::endl;
+ fsh << " void print( std::ostream& os );" << std::endl;
+ fsh << "};" << std::endl;
+ fsh << std::endl;
+ fsh << "class LFSCProofSym : public LFSCProof{" << std::endl;
+ fsh << "private:" << std::endl;
+ fsh << " std::string s;" << std::endl;
+ fsh << " LFSCProofSym( std::string ss ) : s( ss ){}" << std::endl;
+ fsh << "public:" << std::endl;
+ fsh << " static LFSCProofSym* make( std::string ss ) { return new LFSCProofSym( ss ); }" << std::endl;
+ fsh << " static LFSCProofSym* make( const char* ss ) { return new LFSCProofSym( std::string( ss ) ); }" << std::endl;
+ fsh << " ~LFSCProofSym(){}" << std::endl;
+ fsh << " void print( std::ostream& os ) { os << s.c_str(); }" << std::endl;
+ fsh << "};" << std::endl;
+ fsh << std::endl;
+ fsh << "class LFSCProofLam : public LFSCProof{" << std::endl;
+ fsh << " LFSCProofSym* var;" << std::endl;
+ fsh << " LFSCProof* body;" << std::endl;
+ fsh << " LFSCProof* typ;" << std::endl;
+ fsh << " LFSCProofLam( LFSCProofSym* d_var, LFSCProof* d_body, LFSCProof* d_typ ) : var( d_var ), body( d_body ), typ( d_typ ){}" << std::endl;
+ fsh << "public:" << std::endl;
+ fsh << " static LFSCProof* make( LFSCProofSym* d_var, LFSCProof* d_body, LFSCProof* d_typ = NULL ) {" << std::endl;
+ fsh << " return new LFSCProofLam( d_var, d_body, d_typ );" << std::endl;
+ fsh << " }" << std::endl;
+ fsh << " ~LFSCProofLam(){}" << std::endl;
+ fsh << std::endl;
+ fsh << " void print( std::ostream& os );" << std::endl;
+ fsh << "};" << std::endl;
+ fsh << std::endl;
+ fsh << "#endif" << std::endl;
+
+ //write the cpp
+ std::fstream fsc;
+ std::string fnamec( filename );
+ fnamec.append(".cpp");
+ fsc.open( fnamec.c_str(), std::ios::out );
+
+ fsc << "#include \"lfsc_proof.h\"" << std::endl;
+ fsc << std::endl;
+ fsc << "LFSCProof* LFSCProof::hole = NULL;" << std::endl;
+ fsc << std::endl;
+ fsc << "void LFSCProof::init(){" << std::endl;
+ fsc << " hole = LFSCProofSym::make( \"_\" );" << std::endl;
+ fsc << "}" << std::endl;
+ fsc << std::endl;
+ fsc << "void LFSCProofC::print( std::ostream& os ){" << std::endl;
+ fsc << " os << \"(\";" << std::endl;
+ fsc << " switch( id ){" << std::endl;
+ fsc << os_print.str().c_str();
+ fsc << " }" << std::endl;
+ fsc << " int counter = 0;" << std::endl;
+ fsc << " while( kids[counter] ){" << std::endl;
+ fsc << " os << \" \";" << std::endl;
+ fsc << " kids[counter]->print( os );" << std::endl;
+ fsc << " counter++;" << std::endl;
+ fsc << " }" << std::endl;
+ fsc << " os << \")\";" << std::endl;
+ fsc << "}" << std::endl;
+ fsc << std::endl;
+ fsc << "void LFSCProofLam::print( std::ostream& os ){" << std::endl;
+ fsc << " os << \"(\";" << std::endl;
+ fsc << " if( typ ){" << std::endl;
+ fsc << " os << \"% \";" << std::endl;
+ fsc << " }else{" << std::endl;
+ fsc << " os << \"\\\\ \";" << std::endl;
+ fsc << " }" << std::endl;
+ fsc << " var->print( os );" << std::endl;
+ fsc << " if( typ ){" << std::endl;
+ fsc << " os << \" \";" << std::endl;
+ fsc << " typ->print( os );" << std::endl;
+ fsc << " }" << std::endl;
+ fsc << " os << std::endl;" << std::endl;
+ fsc << " body->print( os );" << std::endl;
+ fsc << " os << \")\";" << std::endl;
+ fsc << "}" << std::endl;
+ fsc << std::endl;
+ fsc << os_constructor_c.str().c_str();
+ fsc << std::endl;
+ }
+}
diff --git a/proofs/lfsc_checker/libwriter.h b/proofs/lfsc_checker/libwriter.h new file mode 100644 index 000000000..093cf541b --- /dev/null +++ b/proofs/lfsc_checker/libwriter.h @@ -0,0 +1,28 @@ +#ifndef LIB_WRITER_H
+#define LIB_WRITER_H
+
+#include "expr.h"
+#include <map>
+
+class libwriter
+{
+private:
+ std::vector< Expr* > syms;
+ std::vector< Expr* > defs;
+
+ std::vector< Expr* > judgements;
+ //get the variable name
+ void get_var_name( const std::string& n, std::string& nn );
+public:
+ libwriter(){}
+ virtual ~libwriter(){}
+
+ void add_symbol( Expr* s, Expr* t ) {
+ syms.push_back( s );
+ defs.push_back( t );
+ }
+
+ void write_file();
+};
+
+#endif
diff --git a/proofs/lfsc_checker/main.cpp b/proofs/lfsc_checker/main.cpp new file mode 100644 index 000000000..80f36e69f --- /dev/null +++ b/proofs/lfsc_checker/main.cpp @@ -0,0 +1,139 @@ +#include "expr.h" +#include "check.h" +#include <signal.h> +#include "sccwriter.h" +#include "libwriter.h" +#include <time.h> + +using namespace std; + +args a; + +static void parse_args(int argc, char **argv, args &a) +{ + char *arg0 = *argv; + + /* skip 0'th argument */ + argv++; + argc--; + + while (argc) { + + if ((strncmp("-h", *argv, 2) == 0) || + (strncmp("--h", *argv, 3) == 0)) { + cout << "Usage: " << arg0 << " [options] infile1 ...infile_n\n"; + cout << "If no infiles are named on the command line, input is read\n" + << "from stdin. Specifying the infile \"stdin\" will also read\n" + << "from stdin. Options are:\n\n"; + cout << "--show-runs: print debugging information for runs of side condition code\n"; + cout << "--compile-scc: compile side condition code\n"; + cout << "--compile-scc-debug: compile debug versions of side condition code\n"; + cout << "--run-scc: use compiled side condition code\n"; + exit(0); + } + else if(strcmp("--show-runs", *argv) == 0) { + argc--; argv++; + a.show_runs = true; + } + else if(strcmp("--no-tail-calls", *argv) == 0) { + // this is just for debugging. + argc--; argv++; + a.no_tail_calls = true; + } + else if( strcmp("--compile-scc", *argv) == 0 ){ + argc--; argv++; + a.compile_scc = true; + a.compile_scc_debug = false; + } + else if( strcmp("--compile-scc-debug", *argv) == 0 ) + { + argc--; argv++; + a.compile_scc = true; + a.compile_scc_debug = true; + } + else if( strcmp("--compile-lib", *argv) == 0 ) + { + argc--; argv++; + a.compile_lib = true; + } + else if( strcmp("--run-scc", *argv) == 0 ){ + argc--; argv++; + a.run_scc = true; + } + else if( strcmp("--use-nested-app", *argv) == 0 ){ + argc--; argv++; + a.use_nested_app = true; //not implemented yet + }else { + a.files.push_back(*argv); + argc--; argv++; + } + } +} + +void sighandler(int /* signum */) { + cerr << "\nInterrupted. sc is aborting.\n"; + exit(1); +} + +int main(int argc, char **argv) { + + a.show_runs = false; + a.no_tail_calls = false; + a.compile_scc = false; + a.run_scc = false; + a.use_nested_app = false; + + signal(SIGINT, sighandler); + + parse_args(argc, argv, a); + + init(); + + check_time = (int)clock(); + + if (a.files.size()) { + sccwriter* scw = NULL; + libwriter* lw = NULL; + if( a.compile_scc ){ + scw = new sccwriter( a.compile_scc_debug ? opt_write_call_debug : 0 ); + } + if( a.compile_lib ){ + lw = new libwriter; + } + /* process the files named */ + int i = 0, iend = a.files.size(); + for (; i < iend; i++) { + const char *filename = a.files[i].c_str(); + check_file(filename, a, scw, lw); + } + if( scw ){ + scw->write_file(); + delete scw; + } + if( lw ){ +#ifdef DEBUG_SYM_NAMES + lw->write_file(); + delete lw; +#else + std::cout << "ERROR libwriter: Must compile LFSC with DEBUG_SYM_NAMES flag (see Expr.h)" << std::endl; +#endif + } + } + else + check_file("stdin", a); + + //std::cout << "time = " << (int)clock() - t << std::endl; + //while(1){} + +#ifdef DEBUG + cout << "Clearing globals.\n"; + cout.flush(); + + cleanup(); + a.files.clear(); +#endif + + std::cout << "time = " << (int)clock() - check_time << std::endl; + std::cout << "sym count = " << SymExpr::symmCount << std::endl; + std::cout << "marked count = " << Expr::markedCount << std::endl; +} diff --git a/proofs/lfsc_checker/position.h b/proofs/lfsc_checker/position.h new file mode 100644 index 000000000..a5c51ffc6 --- /dev/null +++ b/proofs/lfsc_checker/position.h @@ -0,0 +1,30 @@ +#ifndef sc2__position_h +#define sc2__position_h + +#include <iostream> +#include <stdio.h> + +class Position { +public: + const char *filename; + int linenum; + int colnum; + + Position(const char *_f, int l, int c) : filename(_f), linenum(l), colnum(c) + {} + void print(std::ostream &os) { + os << filename; + if (colnum == -1) { + char tmp[1024]; + sprintf(tmp, ", line %d, end of column: ", linenum); + os << tmp; + } + else { + char tmp[1024]; + sprintf(tmp, ", line %d, column %d: ", linenum, colnum); + os << tmp; + } + } +}; + +#endif diff --git a/proofs/lfsc_checker/print_smt2.cpp b/proofs/lfsc_checker/print_smt2.cpp new file mode 100644 index 000000000..bf068c248 --- /dev/null +++ b/proofs/lfsc_checker/print_smt2.cpp @@ -0,0 +1,122 @@ +#include "print_smt2.h"
+
+#ifdef PRINT_SMT2
+
+void print_smt2( Expr* p, std::ostream& s, short mode )
+{
+ switch( p->getclass() )
+ {
+ case CEXPR:
+ {
+ switch( p->getop() )
+ {
+ case APP:
+ {
+ std::vector<Expr *> args; + Expr *head = p->collect_args(args, false);
+ short newMode = get_mode( head );
+ if( is_smt2_poly_formula( head ) )
+ {
+ s << "(";
+ head->print( s );
+ s << " ";
+ print_smt2( args[1], s, newMode );
+ s << " ";
+ print_smt2( args[2], s, newMode );
+ s << ")";
+ }
+ else if( ( mode==2 || mode==3 ) && mode==newMode )
+ {
+ print_smt2( args[0], s, newMode );
+ s << " ";
+ print_smt2( args[1], s, newMode );
+ }
+ else if( newMode==1 )
+ {
+ if( mode!=1 || newMode!=mode ){
+ s << "(";
+ }
+ print_smt2( args[2], s, newMode );
+ s << " ";
+ print_smt2( args[3], s, 0 );
+ if( mode!=1 || newMode!=mode ){
+ s << ")";
+ }
+ }
+ else
+ {
+ s << "(";
+ switch( newMode )
+ {
+ case 4: s << "=>";break;
+ default: head->print( s );break;
+ }
+ s << " ";
+ for( int a=0; a<(int)args.size(); a++ ){
+ print_smt2( args[a], s, newMode );
+ if( a!=args.size()-1 )
+ s << " ";
+ }
+ s << ")";
+ }
+ }
+ break;
+ default:
+ std::cout << "Unhandled op " << p->getop() << std::endl;
+ break;
+ }
+ }
+ break;
+ case HOLE_EXPR:
+ {
+ HoleExpr *e = (HoleExpr *)p;
+ if( e->val ){
+ print_smt2( e->val, s, mode );
+ }else{
+ s << "_";
+ }
+ }
+ break;
+ case SYMS_EXPR:
+ case SYM_EXPR:
+ if( ((SymExpr*)p)->val )
+ print_smt2( ((SymExpr*)p)->val, s, mode );
+ else
+ p->print( s );
+ break;
+ default:
+ std::cout << "Unhandled class " << p->getclass() << std::endl;
+ break;
+ }
+}
+
+bool is_smt2_poly_formula( Expr* e )
+{
+ if( e->getclass()==SYMS_EXPR )
+ {
+ SymSExpr* s = (SymSExpr*)e;
+ static std::string eq("=");
+ static std::string distinct("distinct");
+ return s->s==eq || s->s==distinct;
+ }else{
+ return false;
+ }
+}
+
+short get_mode( Expr* e )
+{
+ if( e->getclass()==SYMS_EXPR ){
+ SymSExpr* s = (SymSExpr*)e;
+ static std::string applys("apply");
+ if ( s->s==applys ) return 1;
+ static std::string ands("and");
+ if ( s->s==ands ) return 2;
+ static std::string ors("or");
+ if ( s->s==ors ) return 3;
+ static std::string impls("impl");
+ if ( s->s==impls ) return 4;
+ }
+ return 0;
+}
+
+#endif
diff --git a/proofs/lfsc_checker/print_smt2.h b/proofs/lfsc_checker/print_smt2.h new file mode 100644 index 000000000..c70b1dfa4 --- /dev/null +++ b/proofs/lfsc_checker/print_smt2.h @@ -0,0 +1,17 @@ +#ifndef PRINT_SMT2_H
+#define PRINT_SMT2_H
+
+#define PRINT_SMT2
+
+#include "expr.h"
+
+#ifdef PRINT_SMT2
+void print_smt2( Expr* p, std::ostream& s, short mode = 0 );
+
+bool is_smt2_poly_formula( Expr* p );
+short get_mode( Expr* p );
+
+#endif
+
+
+#endif
diff --git a/proofs/lfsc_checker/scccode.cpp b/proofs/lfsc_checker/scccode.cpp new file mode 100644 index 000000000..cff762a08 --- /dev/null +++ b/proofs/lfsc_checker/scccode.cpp @@ -0,0 +1,609 @@ +#include "scccode.h" + +Expr* e_pos; +Expr* e_neg; +Expr* e_tt; +Expr* e_ff; +Expr* e_cln; +Expr* e_clc; +Expr* e_concat; +Expr* e_clr; +Expr* e_litvar; +Expr* e_litpol; +Expr* e_notb; +Expr* e_iffb; +Expr* e_clear_mark; +Expr* e_append; +Expr* e_simplify_clause_h; +Expr* e_simplify_clause; + +void init_compiled_scc(){ + e_pos = symbols->get("pos").first; + e_neg = symbols->get("neg").first; + e_tt = symbols->get("tt").first; + e_ff = symbols->get("ff").first; + e_cln = symbols->get("cln").first; + e_clc = symbols->get("clc").first; + e_concat = symbols->get("concat").first; + e_clr = symbols->get("clr").first; + e_litvar = progs["litvar"]; + e_litpol = progs["litpol"]; + e_notb = progs["notb"]; + e_iffb = progs["iffb"]; + e_clear_mark = progs["clear_mark"]; + e_append = progs["append"]; + e_simplify_clause_h = progs["simplify_clause_h"]; + e_simplify_clause = progs["simplify_clause"]; +} + +Expr* run_compiled_scc( Expr* p, std::vector< Expr* >& args ){ + if( p==e_litvar ){ + return f_litvar( args[0] ); + }else if( p==e_litpol ){ + return f_litpol( args[0] ); + }else if( p==e_notb ){ + return f_notb( args[0] ); + }else if( p==e_iffb ){ + return f_iffb( args[0], args[1] ); + }else if( p==e_clear_mark ){ + return f_clear_mark( args[0] ); + }else if( p==e_append ){ + return f_append( args[0], args[1] ); + }else if( p==e_simplify_clause_h ){ + return f_simplify_clause_h( args[0], args[1] ); + }else if( p==e_simplify_clause ){ + return f_simplify_clause( args[0] ); + }else{ + return NULL; + } +} + +Expr* f_litvar( Expr* l ){ + Expr* e0; + l->inc(); + Expr* e1 = l->followDefs()->get_head(); + Expr* e2; + e2 = e_pos; + e2->inc(); + Expr* e3; + e3 = e_neg; + e3->inc(); + if( e1==e2 ){ + Expr* x = ((CExpr*)l->followDefs())->kids[1]; + e0 = x; + e0->inc(); + }else if( e1==e3 ){ + Expr* x = ((CExpr*)l->followDefs())->kids[1]; + e0 = x; + e0->inc(); + }else{ + std::cout << "Could not find match for expression in function f_litvar "; + e1->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + l->dec(); + e2->dec(); + e3->dec(); + return e0; +} + +Expr* f_litpol( Expr* l ){ + Expr* e0; + l->inc(); + Expr* e1 = l->followDefs()->get_head(); + Expr* e2; + e2 = e_pos; + e2->inc(); + Expr* e3; + e3 = e_neg; + e3->inc(); + if( e1==e2 ){ + Expr* x = ((CExpr*)l->followDefs())->kids[1]; + e0 = e_tt; + e0->inc(); + }else if( e1==e3 ){ + Expr* x = ((CExpr*)l->followDefs())->kids[1]; + e0 = e_ff; + e0->inc(); + }else{ + std::cout << "Could not find match for expression in function f_litpol "; + e1->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + l->dec(); + e2->dec(); + e3->dec(); + return e0; +} + +Expr* f_notb( Expr* b ){ + Expr* e0; + b->inc(); + Expr* e1 = b->followDefs()->get_head(); + Expr* e2; + e2 = e_ff; + e2->inc(); + Expr* e3; + e3 = e_tt; + e3->inc(); + if( e1==e2 ){ + e0 = e_tt; + e0->inc(); + }else if( e1==e3 ){ + e0 = e_ff; + e0->inc(); + }else{ + std::cout << "Could not find match for expression in function f_notb "; + e1->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + b->dec(); + e2->dec(); + e3->dec(); + return e0; +} + +Expr* f_iffb( Expr* b1, Expr* b2 ){ + Expr* e0; + b1->inc(); + Expr* e1 = b1->followDefs()->get_head(); + Expr* e2; + e2 = e_tt; + e2->inc(); + Expr* e3; + e3 = e_ff; + e3->inc(); + if( e1==e2 ){ + e0 = b2; + e0->inc(); + }else if( e1==e3 ){ + b2->inc(); + e0 = f_notb( b2 ); + b2->dec(); + }else{ + std::cout << "Could not find match for expression in function f_iffb "; + e1->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + b1->dec(); + e2->dec(); + e3->dec(); + return e0; +} + +Expr* f_clear_mark( Expr* v ){ + Expr* e0; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)){ + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)) + ((SymExpr*)v->followDefs())->clearmark(0); + else + ((SymExpr*)v->followDefs())->setmark(0); + e0 = v; + e0->inc(); + v->dec(); + }else{ + e0 = v; + e0->inc(); + } + v->dec(); + return e0; +} + +Expr* f_append( Expr* c1, Expr* c2 ){ + Expr* e0; + c1->inc(); + Expr* e1 = c1->followDefs()->get_head(); + Expr* e2; + e2 = e_cln; + e2->inc(); + Expr* e3; + e3 = e_clc; + e3->inc(); + if( e1==e2 ){ + e0 = c2; + e0->inc(); + }else if( e1==e3 ){ + Expr* l = ((CExpr*)c1->followDefs())->kids[1]; + Expr* c1h = ((CExpr*)c1->followDefs())->kids[2]; + l->inc(); + Expr* e4; + c1h->inc(); + c2->inc(); + e4 = f_append( c1h, c2 ); + c1h->dec(); + c2->dec(); + static Expr* e5; + e5 = e_clc; + e5->inc(); + e0 = new CExpr( APP, e5, l, e4 ); + }else{ + std::cout << "Could not find match for expression in function f_append "; + e1->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + c1->dec(); + e2->dec(); + e3->dec(); + return e0; +} + +Expr* f_simplify_clause_h( Expr* pol, Expr* c ){ + Expr* e0; + c->inc(); + Expr* e1 = c->followDefs()->get_head(); + Expr* e2; + e2 = e_cln; + e2->inc(); + Expr* e3; + e3 = e_clc; + e3->inc(); + Expr* e4; + e4 = e_concat; + e4->inc(); + Expr* e5; + e5 = e_clr; + e5->inc(); + if( e1==e2 ){ + e0 = e_cln; + e0->inc(); + }else if( e1==e3 ){ + Expr* l = ((CExpr*)c->followDefs())->kids[1]; + Expr* c1 = ((CExpr*)c->followDefs())->kids[2]; + Expr* v; + l->inc(); + v = f_litvar( l ); + l->dec(); + Expr* e6; + Expr* e7; + l->inc(); + e7 = f_litpol( l ); + l->dec(); + pol->inc(); + e6 = f_iffb( e7, pol ); + e7->dec(); + pol->dec(); + Expr* e8 = e6->followDefs()->get_head(); + Expr* e9; + e9 = e_tt; + e9->inc(); + Expr* e10; + e10 = e_ff; + e10->inc(); + if( e8==e9 ){ + Expr* m; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)){ + m = e_tt; + m->inc(); + }else{ + Expr* e11; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)) + ((SymExpr*)v->followDefs())->clearmark(0); + else + ((SymExpr*)v->followDefs())->setmark(0); + e11 = v; + e11->inc(); + v->dec(); + e11->dec(); + m = e_ff; + m->inc(); + } + v->dec(); + Expr* ch; + pol->inc(); + c1->inc(); + ch = f_simplify_clause_h( pol, c1 ); + pol->dec(); + c1->dec(); + m->inc(); + Expr* e12 = m->followDefs()->get_head(); + Expr* e13; + e13 = e_tt; + e13->inc(); + Expr* e14; + e14 = e_ff; + e14->inc(); + if( e12==e13 ){ + Expr* e15; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(1)){ + e15 = v; + e15->inc(); + }else{ + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(1)) + ((SymExpr*)v->followDefs())->clearmark(1); + else + ((SymExpr*)v->followDefs())->setmark(1); + e15 = v; + e15->inc(); + v->dec(); + } + v->dec(); + e15->dec(); + e0 = ch; + e0->inc(); + }else if( e12==e14 ){ + Expr* e16; + Expr* e17; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(1)){ + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(1)) + ((SymExpr*)v->followDefs())->clearmark(1); + else + ((SymExpr*)v->followDefs())->setmark(1); + e17 = v; + e17->inc(); + v->dec(); + }else{ + e17 = v; + e17->inc(); + } + v->dec(); + e17->dec(); + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)) + ((SymExpr*)v->followDefs())->clearmark(0); + else + ((SymExpr*)v->followDefs())->setmark(0); + e16 = v; + e16->inc(); + v->dec(); + e16->dec(); + l->inc(); + ch->inc(); + static Expr* e18; + e18 = e_clc; + e18->inc(); + e0 = new CExpr( APP, e18, l, ch ); + }else{ + std::cout << "Could not find match for expression in function f_simplify_clause_h "; + e12->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + m->dec(); + e13->dec(); + e14->dec(); + ch->dec(); + m->dec(); + }else if( e8==e10 ){ + l->inc(); + Expr* e19; + pol->inc(); + c1->inc(); + e19 = f_simplify_clause_h( pol, c1 ); + pol->dec(); + c1->dec(); + static Expr* e20; + e20 = e_clc; + e20->inc(); + e0 = new CExpr( APP, e20, l, e19 ); + }else{ + std::cout << "Could not find match for expression in function f_simplify_clause_h "; + e8->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + e6->dec(); + e9->dec(); + e10->dec(); + v->dec(); + }else if( e1==e4 ){ + Expr* c1 = ((CExpr*)c->followDefs())->kids[1]; + Expr* c2 = ((CExpr*)c->followDefs())->kids[2]; + pol->inc(); + Expr* e21 = pol->followDefs()->get_head(); + Expr* e22; + e22 = e_ff; + e22->inc(); + Expr* e23; + e23 = e_tt; + e23->inc(); + if( e21==e22 ){ + Expr* e24; + pol->inc(); + c1->inc(); + e24 = f_simplify_clause_h( pol, c1 ); + pol->dec(); + c1->dec(); + Expr* e25; + pol->inc(); + c2->inc(); + e25 = f_simplify_clause_h( pol, c2 ); + pol->dec(); + c2->dec(); + static Expr* e26; + e26 = e_concat; + e26->inc(); + e0 = new CExpr( APP, e26, e24, e25 ); + }else if( e21==e23 ){ + Expr* e27; + pol->inc(); + c1->inc(); + e27 = f_simplify_clause_h( pol, c1 ); + pol->dec(); + c1->dec(); + Expr* e28; + pol->inc(); + c2->inc(); + e28 = f_simplify_clause_h( pol, c2 ); + pol->dec(); + c2->dec(); + e0 = f_append( e27, e28 ); + e27->dec(); + e28->dec(); + }else{ + std::cout << "Could not find match for expression in function f_simplify_clause_h "; + e21->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + pol->dec(); + e22->dec(); + e23->dec(); + }else if( e1==e5 ){ + Expr* l = ((CExpr*)c->followDefs())->kids[1]; + Expr* c1 = ((CExpr*)c->followDefs())->kids[2]; + Expr* e29; + Expr* e30; + l->inc(); + e30 = f_litpol( l ); + l->dec(); + pol->inc(); + e29 = f_iffb( e30, pol ); + e30->dec(); + pol->dec(); + Expr* e31 = e29->followDefs()->get_head(); + Expr* e32; + e32 = e_ff; + e32->inc(); + Expr* e33; + e33 = e_tt; + e33->inc(); + if( e31==e32 ){ + l->inc(); + Expr* e34; + pol->inc(); + c1->inc(); + e34 = f_simplify_clause_h( pol, c1 ); + pol->dec(); + c1->dec(); + static Expr* e35; + e35 = e_clr; + e35->inc(); + e0 = new CExpr( APP, e35, l, e34 ); + }else if( e31==e33 ){ + Expr* v; + l->inc(); + v = f_litvar( l ); + l->dec(); + Expr* m; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)){ + m = e_tt; + m->inc(); + }else{ + Expr* e36; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)) + ((SymExpr*)v->followDefs())->clearmark(0); + else + ((SymExpr*)v->followDefs())->setmark(0); + e36 = v; + e36->inc(); + v->dec(); + e36->dec(); + m = e_ff; + m->inc(); + } + v->dec(); + Expr* ch; + pol->inc(); + c1->inc(); + ch = f_simplify_clause_h( pol, c1 ); + pol->dec(); + c1->dec(); + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(1)){ + m->inc(); + Expr* e37 = m->followDefs()->get_head(); + Expr* e38; + e38 = e_tt; + e38->inc(); + Expr* e39; + e39 = e_ff; + e39->inc(); + if( e37==e38 ){ + e0 = ch; + e0->inc(); + }else if( e37==e39 ){ + Expr* e40; + Expr* e41; + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(1)) + ((SymExpr*)v->followDefs())->clearmark(1); + else + ((SymExpr*)v->followDefs())->setmark(1); + e41 = v; + e41->inc(); + v->dec(); + e41->dec(); + v->inc(); + if ( ((SymExpr*)v->followDefs())->getmark(0)) + ((SymExpr*)v->followDefs())->clearmark(0); + else + ((SymExpr*)v->followDefs())->setmark(0); + e40 = v; + e40->inc(); + v->dec(); + e40->dec(); + e0 = ch; + e0->inc(); + }else{ + std::cout << "Could not find match for expression in function f_simplify_clause_h "; + e37->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + m->dec(); + e38->dec(); + e39->dec(); + }else{ + e0 = NULL; + } + v->dec(); + ch->dec(); + m->dec(); + v->dec(); + }else{ + std::cout << "Could not find match for expression in function f_simplify_clause_h "; + e31->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + e29->dec(); + e32->dec(); + e33->dec(); + }else{ + std::cout << "Could not find match for expression in function f_simplify_clause_h "; + e1->print( std::cout ); + std::cout << std::endl; + exit( 1 ); + } + c->dec(); + e2->dec(); + e3->dec(); + e4->dec(); + e5->dec(); + return e0; +} + +Expr* f_simplify_clause( Expr* c ){ + Expr* e0; + static Expr* e1; + e1 = e_tt; + e1->inc(); + Expr* e2; + static Expr* e3; + e3 = e_ff; + e3->inc(); + c->inc(); + e2 = f_simplify_clause_h( e3, c ); + e3->dec(); + c->dec(); + e0 = f_simplify_clause_h( e1, e2 ); + e1->dec(); + e2->dec(); + return e0; +} + diff --git a/proofs/lfsc_checker/scccode.h b/proofs/lfsc_checker/scccode.h new file mode 100644 index 000000000..6f5efc8b5 --- /dev/null +++ b/proofs/lfsc_checker/scccode.h @@ -0,0 +1,27 @@ +#ifndef SCC_CODE_H +#define SCC_CODE_H + +#include "check.h" + +void init_compiled_scc(); + +Expr* run_compiled_scc( Expr* p, std::vector< Expr* >& args ); + +inline Expr* f_litvar( Expr* l ); + +inline Expr* f_litpol( Expr* l ); + +inline Expr* f_notb( Expr* b ); + +inline Expr* f_iffb( Expr* b1, Expr* b2 ); + +inline Expr* f_clear_mark( Expr* v ); + +inline Expr* f_append( Expr* c1, Expr* c2 ); + +inline Expr* f_simplify_clause_h( Expr* pol, Expr* c ); + +inline Expr* f_simplify_clause( Expr* c ); + +#endif + diff --git a/proofs/lfsc_checker/sccwriter.cpp b/proofs/lfsc_checker/sccwriter.cpp new file mode 100644 index 000000000..d11a96b19 --- /dev/null +++ b/proofs/lfsc_checker/sccwriter.cpp @@ -0,0 +1,977 @@ +#include "sccwriter.h"
+
+#include <fstream>
+#include <sstream>
+
+int sccwriter::exprCount = 0;
+int sccwriter::strCount = 0;
+int sccwriter::argsCount = 0;
+int sccwriter::rnumCount = 0;
+
+int sccwriter::get_prog_index( const std::string& str )
+{
+ for( int a=0; a<(int)progNames.size(); a++ ){
+ if( progNames[a]==str ){
+ return a;
+ }
+ }
+ return -1;
+}
+
+int sccwriter::get_prog_index_by_expr( Expr* e )
+{
+ for( int a=0; a<(int)progPtrs.size(); a++ ){
+ if( progPtrs[a]==e ){
+ return a;
+ }
+ }
+ return -1;
+}
+
+bool sccwriter::is_var( const std::string& str )
+{
+ for( int a=0; a<(int)vars.size(); a++ ){
+ if( vars[a]==str ){
+ return true;
+ }
+ }
+ return false;
+}
+
+void sccwriter::add_global_sym( const std::string& str )
+{
+ for( int a=0; a<(int)globalSyms.size(); a++ ){
+ if( globalSyms[a]==str ){
+ return;
+ }
+ }
+ globalSyms.push_back( str );
+}
+
+void sccwriter::indent( std::ostream& os, int ind )
+{
+ for( int a=0; a<ind; a++ )
+ {
+ os << " ";
+ }
+}
+
+void sccwriter::write_function_header( std::ostream& os, int index, int opts )
+{
+ //write the function header
+ std::string fname;
+ get_function_name( progNames[index], fname );
+ os << "Expr* " << fname.c_str() << "( ";
+ CExpr* progvars = (CExpr*)get_prog( index )->kids[1];
+ int counter = 0;
+ //write each argument
+ while( progvars->kids[counter] )
+ {
+ if( counter!=0 )
+ {
+ os << ", ";
+ }
+ os << "Expr* ";
+ write_variable( ((SymSExpr*)progvars->kids[counter])->s, os );
+ //add to vars if options are set to do so
+ if( opts&opt_write_add_args )
+ {
+ vars.push_back( ((SymSExpr*)progvars->kids[counter])->s );
+ }
+ counter++;
+ }
+ os << " )";
+ if( opts&opt_write_call_debug )
+ {
+ os << "{" << std::endl;
+ indent( os, 1 );
+ os << "std::cout << \"Call function " << fname.c_str() << " with arguments \";" << std::endl;
+ counter = 0;
+ while( progvars->kids[counter] )
+ {
+ if( counter!=0 )
+ {
+ indent( os, 1 );
+ os << "std::cout << \", \";" << std::endl;
+ }
+ indent( os, 1 );
+ write_variable( ((SymSExpr*)progvars->kids[counter])->s, os );
+ os << "->print( std::cout );" << std::endl;
+ counter++;
+ }
+ indent( os, 1 );
+ os << "std::cout << std::endl;" << std::endl;
+ }
+}
+
+void sccwriter::get_function_name( const std::string& pname, std::string& fname )
+{
+ fname = std::string( "f_" );
+ fname.append( pname );
+}
+
+void sccwriter::write_variable( const std::string& n, std::ostream& os )
+{
+ std::string nn;
+ get_var_name( n, nn );
+ os << nn.c_str();
+}
+
+void sccwriter::get_var_name( const std::string& n, std::string& nn ) {
+ nn = std::string( n.c_str() );
+ for( int i = 0; i <(int)n.length(); i++ ){
+ char c = n[i];
+ if (c <= 47)
+ c += 65;
+ else if (c >= 58 && c <= 64)
+ c += 97-58;
+ if ((c >= 91 && c <= 94) || c == 96)
+ c += 104-91;
+ else if (c >= 123)
+ c -= 4;
+ nn[i] = c;
+ }
+}
+
+void sccwriter::write_file()
+{
+ static std::string filename( "scccode" );
+
+ //writer the h file
+ std::fstream fsh;
+ std::string fnameh( filename );
+ fnameh.append(".h");
+ fsh.open( fnameh.c_str(), std::ios::out );
+ //write the header in h
+ fsh << "#ifndef SCC_CODE_H" << std::endl;
+ fsh << "#define SCC_CODE_H" << std::endl << std::endl;
+ //include necessary files in h file
+ fsh << "#include \"check.h\"" << std::endl << std::endl;
+ //write the init function
+ fsh << "void init_compiled_scc();" << std::endl << std::endl;
+ //write the entry function
+ fsh << "Expr* run_compiled_scc( Expr* p, std::vector< Expr* >& args );" << std::endl << std::endl;
+ //write the side condition code functions
+ for( int n=0; n<(int)progs.size(); n++ )
+ {
+ //write the header in the h file
+ fsh << "inline ";
+ write_function_header( fsh, n );
+ fsh << ";" << std::endl << std::endl;
+ }
+ fsh << "#endif" << std::endl << std::endl;
+ fsh.close();
+
+
+ //writer the cpp code
+ std::fstream fsc;
+ std::string fnamec( filename );
+ fnamec.append(".cpp");
+ fsc.open( fnamec.c_str(), std::ios::out );
+ //include the h file in the cpp
+ fsc << "#include \"scccode.h\"" << std::endl << std::endl;
+ std::ostringstream fsc_funcs;
+ //write the side condition code functions
+ for( currProgram=0; currProgram<(int)progs.size(); currProgram++ )
+ {
+ //reset naming counters
+ vars.clear();
+ exprCount = 0;
+ strCount = 0;
+ argsCount = 0;
+ rnumCount = 0;
+
+ //for debugging
+ std::cout << "program #" << currProgram << " " << progNames[currProgram].c_str() << std::endl;
+
+ //write the function header
+ write_function_header( fsc_funcs, currProgram, opt_write_add_args|options );
+ if( (options&opt_write_call_debug)==0 )
+ {
+ fsc_funcs << "{" << std::endl;
+ }
+ //write the code
+ //std::vector< std::string > cleanVec;
+ //write_code( get_prog( n )->kids[2], fsc, 1, "return ", cleanVec );
+ //debug_write_code( progs[n].second->kids[2], fsc, 1 );
+ std::string expr;
+ write_expr( get_prog( currProgram )->kids[2], fsc_funcs, 1, expr );
+ indent( fsc_funcs, 1 );
+ fsc_funcs << "return " << expr.c_str() << ";" << std::endl;
+ fsc_funcs << "}" << std::endl << std::endl;
+ }
+ //write the predefined symbols necessary - symbols and progs
+ for( int a=0; a<(int)globalSyms.size(); a++ )
+ {
+ fsc << "Expr* e_" << globalSyms[a].c_str() << ";" << std::endl;
+ }
+ for( int a=0; a<(int)progs.size(); a++ )
+ {
+ fsc << "Expr* e_" << progNames[a].c_str() << ";" << std::endl;
+ }
+ fsc << std::endl;
+ //write the init function - initialize symbols and progs
+ fsc << "void init_compiled_scc(){" << std::endl;
+ for( int a=0; a<(int)globalSyms.size(); a++ )
+ {
+ indent( fsc, 1 );
+ fsc << "e_" << globalSyms[a].c_str() << " = symbols->get(\"" << globalSyms[a].c_str() << "\").first;" << std::endl;
+ }
+ for( int a=0; a<(int)progs.size(); a++ )
+ {
+ indent( fsc, 1 );
+ fsc << "e_" << progNames[a].c_str() << " = progs[\"" << progNames[a].c_str() << "\"];" << std::endl;
+ }
+ fsc << "}" << std::endl << std::endl;
+ fsc << "Expr* run_compiled_scc( Expr* p, std::vector< Expr* >& args ){" << std::endl;
+ //for( int n=0; n<(int)progs.size(); n++ ){
+ // indent( fsc, 1 );
+ // fsc << "static std::string s_" << progNames[n].c_str() << " = std::string( \"" << progNames[n].c_str() << "\" );" << std::endl;
+ //}
+ for( int n=0; n<(int)progs.size(); n++ ){
+ indent( fsc, 1 );
+ if( n!=0 ){
+ fsc << "}else ";
+ }
+ //for each function, test to see if the string matches the name of the function
+ fsc << "if( p==e_" << progNames[n].c_str() << " ){" << std::endl;
+ indent( fsc, 2 );
+ std::string fname;
+ get_function_name( progNames[n], fname );
+ //map the function to the proper function
+ fsc << "return " << fname.c_str() << "( ";
+ //write the arguments to the function from args
+ CExpr* progvars = (CExpr*)get_prog( n )->kids[1];
+ int counter = 0;
+ bool firstTime = true;
+ while( progvars->kids[counter] )
+ {
+ if( !firstTime )
+ {
+ fsc << ", ";
+ }
+ fsc << "args[" << counter << "]";
+ firstTime = false;
+ counter++;
+ }
+ fsc << " );" << std::endl;
+ }
+ indent( fsc, 1 );
+ fsc << "}else{" << std::endl;
+ indent( fsc, 2 );
+ //return null in the case the function could not be found
+ fsc << "return NULL;" << std::endl;
+ indent( fsc, 1 );
+ fsc << "}" << std::endl;
+ fsc << "}" << std::endl << std::endl;
+ fsc << fsc_funcs.str().c_str();
+
+ fsc.close();
+}
+
+void sccwriter::write_code( Expr* code, std::ostream& os, int ind, const char* retModStr, int opts )
+{
+ std::string retModString;
+ std::string incString;
+ if ( retModStr )
+ {
+ retModString = std::string( retModStr );
+ retModString.append( " = " );
+ incString = std::string( retModStr );
+ incString.append( "->inc();" );
+ }
+ switch( code->getclass() )
+ {
+ case INT_EXPR:
+ {
+ indent( os, ind );
+ os << retModString.c_str();
+ os << "new IntExpr( " << mpz_get_si( ((IntExpr*)code)->n ) << " );" << std::endl;
+ indent( os, ind );
+ os << incString.c_str() << std::endl;
+ }
+ break;
+ case RAT_EXPR:
+ {
+ mpz_t num, den;
+ mpz_init(num);
+ mpz_init(den);
+ mpq_get_num( num, ((RatExpr*)code)->n );
+ mpq_get_den( den, ((RatExpr*)code)->n );
+ indent( os, ind );
+ os << retModString.c_str();
+ os << "new RatExpr( " << mpz_get_si( num ) << ", " << mpz_get_si( den ) << " );" << std::endl;
+ indent( os, ind );
+ os << incString.c_str() << std::endl;
+ }
+ break;
+ case SYMS_EXPR:
+ {
+ //if it is a variable, simply write it to buffer
+ if( is_var( ((SymSExpr*)code)->s ) )
+ {
+ indent( os, ind );
+ os << retModString.c_str();
+ write_variable( ((SymSExpr*)code)->s.c_str(), os );
+ os << ";" << std::endl;
+ }
+ else //else must look at symbol lookup table
+ {
+ std::string var;
+ get_var_name( ((SymSExpr*)code)->s, var );
+ indent( os, ind );
+ os << retModString.c_str() << "e_" << var.c_str() << ";" << std::endl;
+ add_global_sym( var );
+ }
+ indent( os, ind );
+ os << incString.c_str() << std::endl;
+ }
+ break;
+ default:
+ switch( code->getop() )
+ {
+ case APP: + { + //collect the arguments + std::vector< Expr* > argVector; + code->collect_args( argVector ); + //write the arguments + std::vector< std::string > args; + for( int a=0; a<(int)argVector.size(); a++ ) + { + std::string expr; + write_expr( argVector[a], os, ind, expr ); + args.push_back( expr ); + } + //write_args( (CExpr*)code, os, ind, 1, args ); + Expr* hd = code->get_head(); + //map to a program in the case that it is a program + if( hd->getop()==PROG && get_prog_index_by_expr( hd )!=-1 ) + { + indent( os, ind ); + os << retModString << "f_" << progNames[ get_prog_index_by_expr( hd ) ].c_str() << "( "; + for( int a=0; a<(int)args.size(); a++ ) + { + os << args[a].c_str(); + if( a!=(int)( args.size()-1 ) ){ + os << ", "; + } + } + os << " );" << std::endl; + for( int a=0; a<(int)args.size(); a++ ) + { + write_dec( args[a], os, ind ); + } + } + else + { +#ifdef USE_FLAT_APP + std::string expr; + write_expr( hd, os, ind, expr ); + indent( os, ind ); + os << retModString << "new CExpr( APP, "; + os << expr.c_str() << ", "; + for( int a=0; a<(int)args.size(); a++ ) + { + os << args[a].c_str(); + if( a!=(int)( args.size()-1 ) ){ + os << ", "; + } + } + os << " );" << std::endl; +#else + std::string expr; + write_expr( hd, os, ind, expr ); + indent( os, ind ); + os << retModString; + for( int a=0; a<(int)args.size(); a++ ) + { + os << "new CExpr( APP, "; + } + os << expr.c_str() << ", "; + for( int a=0; a<(int)args.size(); a++ ) + { + os << args[a].c_str(); + os << " )"; + if( a!=(int)( args.size()-1 ) ){ + os << ", "; + } + } + os << ";" << std::endl; +#endif + //indent( os, ind ); + //os << expr.c_str() << "->dec();" << std::endl; + } + } + break;
+ case MATCH: + { + //calculate the value for the expression + std::string expr; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr ); + //get the head + std::ostringstream sshd; + sshd << "e" << exprCount; + exprCount++; + indent( os, ind ); + os << "Expr* " << sshd.str().c_str() << " = " << expr.c_str() << "->followDefs()->get_head();" << std::endl; + //write the arguments + std::vector< std::string > args; + write_args( (CExpr*)code, os, ind, 1, args ); + bool encounterDefault = false; + //now make an if statement corresponding to the match + int a = 0; + while( ((CExpr*)code)->kids[a+1] ) + { + indent( os, ind ); + if( a!=0 ){ + os << "}else"; + } + if( ((CExpr*)code)->kids[a+1]->getop()!=CASE ){ + encounterDefault = true; + os << "{" << std::endl; + //write the body of the case + write_code( ((CExpr*)code)->kids[a+1], os, ind+1, retModStr ); + indent( os, ind ); + os << "}" << std::endl; + }else{ + if( a!=0 ) + os << " "; + os << "if( " << sshd.str().c_str() << "==" << args[a].c_str() << " ){" << std::endl; + //collect args from the variable in the code + std::ostringstream ssargs; + ssargs << "args" << argsCount; + argsCount++; +#ifndef USE_FLAT_APP + indent( os, ind+1 ); + os << "std::vector< Expr* > " << ssargs.str().c_str() << ";" << std::endl; + indent( os, ind+1 ); + os << expr.c_str() << "->followDefs()->collect_args( " << ssargs.str().c_str() << " );" << std::endl; +#endif + //set the variables defined in the pattern equal to the arguments + std::vector< Expr* > caseArgs; + ((CExpr*)((CExpr*)code)->kids[a+1])->kids[0]->collect_args( caseArgs ); + for( int b=0; b<(int)caseArgs.size(); b++ ) + { + indent( os, ind+1 ); + os << "Expr* "; + write_variable( ((SymSExpr*)caseArgs[b])->s.c_str(), os ); +#ifdef USE_FLAT_APP + os << " = ((CExpr*)" << expr.c_str() << "->followDefs())->kids[" << b+1 << "];" << std::endl; +#else + os << " = " << ssargs.str().c_str() << "[" << b << "];" << std::endl; +#endif + vars.push_back( ((SymSExpr*)caseArgs[b])->s ); + } + //write the body of the case + write_code( ((CExpr*)code)->kids[a+1], os, ind+1, retModStr, opt_write_case_body ); + } + a++; + } + if( !encounterDefault ) + { + indent( os, ind ); + os << "}else{" << std::endl; + indent( os, ind + 1 ); + os << "std::cout << \"Could not find match for expression in function f_"; + os << progNames[currProgram].c_str() << " \";" << std::endl; + indent( os, ind + 1 ); + os << sshd.str().c_str() << "->print( std::cout );" << std::endl; + indent( os, ind + 1 ); + os << "std::cout << std::endl;" << std::endl; + indent( os, ind + 1 ); + os << "exit( 1 );" << std::endl; + indent( os, ind ); + os << "}" << std::endl; + } + write_dec( expr, os, ind ); + for( int a=0; a<(int)args.size(); a++ ) + { + write_dec( args[a], os, ind ); + } + } + break; + case CASE: + if( opts&opt_write_case_body ) + { + write_code( ((CExpr*)code)->kids[1], os, ind, retModStr ); + } + else + { + write_code( ((CExpr*)code)->kids[0]->get_head(), os, ind, retModStr ); + } + break; + case DO: + { + //write each of the children in sequence + int counter = 0; + while( ((CExpr*)code)->kids[counter] ) + { + if( ((CExpr*)code)->kids[counter+1]==NULL ) + { + write_code( ((CExpr*)code)->kids[counter], os, ind, retModStr ); + } + else + { + std::string expr; + write_expr( ((CExpr*)code)->kids[counter], os, ind, expr ); + //clean up memory + write_dec( expr, os, ind ); + } + counter++; + } + } + break; + case LET: + { + indent( os, ind ); + os << "Expr* "; + write_variable( ((SymSExpr*)((CExpr*)code)->kids[0])->s, os ); + os << ";" << std::endl; + std::ostringstream ss; + write_variable( ((SymSExpr*)((CExpr*)code)->kids[0])->s, ss ); + write_code( ((CExpr*)code)->kids[1], os, ind, ss.str().c_str() ); + //add it to the variables + vars.push_back( ((SymSExpr*)((CExpr*)code)->kids[0])->s ); + write_code( ((CExpr*)code)->kids[2], os, ind, retModStr ); + //clean up memory + indent( os, ind ); + write_variable( ((SymSExpr*)((CExpr*)code)->kids[0])->s, os ); + os << "->dec();" << std::endl; + } + break; + case FAIL: + { + indent( os, ind ); + os << retModString.c_str() << "NULL;" << std::endl; + } + break; +#ifndef MARKVAR_32 + case MARKVAR: + { + //calculate the value for the expression + std::string expr; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr, opt_write_check_sym_expr ); + //set the mark on the expression + indent( os, ind ); + os << "if (" << expr.c_str() << "->followDefs()->getmark())" << std::endl; + indent( os, ind+1 ); + os << expr.c_str() << "->followDefs()->clearmark();" << std::endl; + indent( os, ind ); + os << "else" << std::endl; + indent( os, ind+1 ); + os << expr.c_str() << "->followDefs()->setmark();" << std::endl; + //write the return if necessary + if( retModStr!=NULL ){ + indent( os, ind ); + os << retModString.c_str() << expr.c_str() << ";" << std::endl; + indent( os, ind );
+ os << incString.c_str() << std::endl; + } + write_dec( expr, os, ind ); + } + break; + case IFMARKED:
+ { + //calculate the value for the expression + std::string expr; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr, opt_write_check_sym_expr ); + //if mark is set, write code for kids[1] + indent( os, ind ); + os << "if (" << expr.c_str() << "->followDefs()->getmark()){" << std::endl; + write_code( ((CExpr*)code)->kids[1], os, ind+1, retModStr ); + //else write code for kids[2] + indent( os, ind ); + os << "}else{" << std::endl; + write_code( ((CExpr*)code)->kids[2], os, ind+1, retModStr ); + indent( os, ind ); + os << "}" << std::endl;
+ //clean up memory
+ write_dec( expr, os, ind );
+ }
+ break; +#else + case MARKVAR: + { + //calculate the value for the expression + std::string expr; + write_expr( ((CExpr*)code)->kids[1], os, ind, expr, opt_write_check_sym_expr ); + //set the mark on the expression + indent( os, ind ); + os << "if ( ((SymExpr*)" << expr.c_str() << "->followDefs())->getmark("; + os << ((IntExpr*)((CExpr*)code)->kids[0])->get_num() << "))" << std::endl; + indent( os, ind+1 ); + os << "((SymExpr*)" << expr.c_str() << "->followDefs())->clearmark("; + os << ((IntExpr*)((CExpr*)code)->kids[0])->get_num() << ");" << std::endl; + indent( os, ind ); + os << "else" << std::endl; + indent( os, ind+1 ); + os << "((SymExpr*)" << expr.c_str() << "->followDefs())->setmark("; + os << ((IntExpr*)((CExpr*)code)->kids[0])->get_num() << ");" << std::endl; + //write the return if necessary + if( retModStr!=NULL ){ + indent( os, ind ); + os << retModString.c_str() << expr.c_str() << ";" << std::endl; + indent( os, ind );
+ os << incString.c_str() << std::endl; + } + write_dec( expr, os, ind ); + } + break; + case COMPARE: + { + std::string expr1, expr2; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr1, opt_write_check_sym_expr ); + write_expr( ((CExpr*)code)->kids[1], os, ind, expr2, opt_write_check_sym_expr ); + indent( os, ind ); + os << "if( ((SymExpr*)" << expr1.c_str() << ")->followDefs() < ((SymExpr*)" << expr2.c_str() << ")->followDefs() ){" << std::endl; + write_code( ((CExpr*)code)->kids[2], os, ind+1, retModStr ); + indent( os, ind ); + os << "}else{" << std::endl; + write_code( ((CExpr*)code)->kids[3], os, ind+1, retModStr ); + indent( os, ind ); + os << "}" << std::endl; + //clean up memory
+ write_dec( expr1, os, ind ); + write_dec( expr2, os, ind ); + } + break; + case IFMARKED:
+ { + //calculate the value for the expression + std::string expr; + write_expr( ((CExpr*)code)->kids[1], os, ind, expr, opt_write_check_sym_expr ); + //if mark is set, write code for kids[1] + indent( os, ind ); + os << "if ( ((SymExpr*)" << expr.c_str() << "->followDefs())->getmark("; + os << ((IntExpr*)((CExpr*)code)->kids[0])->get_num() << ")){" << std::endl; + write_code( ((CExpr*)code)->kids[2], os, ind+1, retModStr ); + //else write code for kids[2] + indent( os, ind ); + os << "}else{" << std::endl; + write_code( ((CExpr*)code)->kids[3], os, ind+1, retModStr ); + indent( os, ind ); + os << "}" << std::endl;
+ //clean up memory
+ write_dec( expr, os, ind );
+ }
+ break; +#endif + case ADD: + case MUL: + case DIV: + { + //calculate the value for the first expression + std::string expr1; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr1 ); + //calculate the value for the second expression + std::string expr2; + write_expr( ((CExpr*)code)->kids[1], os, ind, expr2 ); + std::ostringstream ss; + ss << "rnum" << rnumCount; + rnumCount++; + indent( os, ind ); + os << "if( " << expr1.c_str() << "->followDefs()->getclass()==INT_EXPR ){" << std::endl; + indent( os, ind+1 ); + os << "mpz_t " << ss.str().c_str() << ";" << std::endl; + indent( os, ind+1 ); + os << "mpz_init(" << ss.str().c_str() << ");" << std::endl; + indent( os, ind+1 ); + os << "mpz_"; + if( code->getop()==ADD ) + os << "add"; + else + os << "mul"; + os << "( " << ss.str().c_str() << ", ((IntExpr*)" << expr1.c_str() << "->followDefs())->n, ((IntExpr*)" << expr2.c_str() << "->followDefs())->n);" << std::endl; + indent( os, ind+1 ); + os << retModString.c_str() << "new IntExpr(" << ss.str().c_str() << ");" << std::endl; + indent( os, ind ); + os << "}else if( " << expr1.c_str() << "->followDefs()->getclass()==RAT_EXPR ){" << std::endl; + indent( os, ind+1 ); + os << "mpq_t " << ss.str().c_str() << ";" << std::endl; + indent( os, ind+1 ); + os << "mpq_init(" << ss.str().c_str() << ");" << std::endl; + indent( os, ind+1 ); + os << "mpq_"; + if( code->getop()==ADD ) + os << "add"; + else if( code->getop()==MUL ) + os << "mul"; + else + os << "div"; + os << "( " << ss.str().c_str() << ", ((RatExpr*)" << expr1.c_str() << "->followDefs())->n, ((RatExpr*)" << expr2.c_str() << "->followDefs())->n);" << std::endl; + indent( os, ind+1 ); + os << retModString.c_str() << "new RatExpr(" << ss.str().c_str() << ");" << std::endl; + indent( os, ind ); + os << "}" << std::endl; + //clean up memory + write_dec( expr1, os, ind ); + write_dec( expr2, os, ind ); + } + break; + case NEG:
+ {
+ //calculate the value for the first expression + std::string expr1; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr1 ); + std::ostringstream ss; + ss << "rnum" << rnumCount; + rnumCount++; + indent( os, ind ); + os << "if( " << expr1.c_str() << "->followDefs()->getclass()==INT_EXPR ){" << std::endl; + indent( os, ind+1 ); + os << "mpz_t " << ss.str().c_str() << ";" << std::endl; + indent( os, ind+1 ); + os << "mpz_init(" << ss.str().c_str() << ");" << std::endl; + indent( os, ind+1 ); + os << "mpz_neg( " << ss.str().c_str() << ", ((IntExpr*)" << expr1.c_str() << "->followDefs())->n );" << std::endl; + indent( os, ind+1 ); + os << retModString.c_str() << "new IntExpr(" << ss.str().c_str() << ");" << std::endl;
+ indent( os, ind ); + os << "}else if( " << expr1.c_str() << "->followDefs()->getclass()==RAT_EXPR ){" << std::endl;
+ indent( os, ind+1 ); + os << "mpq_t " << ss.str().c_str() << ";" << std::endl; + indent( os, ind+1 ); + os << "mpq_init(" << ss.str().c_str() << ");" << std::endl; + indent( os, ind+1 ); + os << "mpq_neg( " << ss.str().c_str() << ", ((RatExpr*)" << expr1.c_str() << "->followDefs())->n );" << std::endl; + indent( os, ind+1 ); + os << retModString.c_str() << "new RatExpr(" << ss.str().c_str() << ");" << std::endl;
+ indent( os, ind ); + os << "}" << std::endl;
+ //clean up memory
+ write_dec( expr1, os, ind );
+ }
+ break;
+ case IFNEG:
+ case IFZERO:
+ {
+ std::string expr1; + write_expr( ((CExpr*)code)->kids[0], os, ind, expr1 );
+ indent( os, ind ); + os << "if( " << expr1.c_str() << "->followDefs()->getclass()==INT_EXPR ){" << std::endl;
+ indent( os, ind+1 );
+ os << "if( mpz_sgn( ((IntExpr *)" << expr1.c_str() << "->followDefs())->n ) ";
+ if( code->getop()==IFNEG )
+ os << "<";
+ else
+ os << "==";
+ os << " 0 ){" << std::endl;
+ write_code( ((CExpr*)code)->kids[1], os, ind+2, retModStr );
+ indent( os, ind+1 );
+ os << "}else{" << std::endl;
+ write_code( ((CExpr*)code)->kids[2], os, ind+2, retModStr );
+ indent( os, ind+1 );
+ os << "}" << std::endl;
+ indent( os, ind ); + os << "}else if( " << expr1.c_str() << "->followDefs()->getclass()==RAT_EXPR ){" << std::endl;
+ indent( os, ind+1 );
+ os << "if( mpq_sgn( ((RatExpr *)" << expr1.c_str() << "->followDefs())->n ) ";
+ if( code->getop()==IFNEG )
+ os << "<";
+ else
+ os << "==";
+ os << " 0 ){" << std::endl;
+ write_code( ((CExpr*)code)->kids[1], os, ind+2, retModStr );
+ indent( os, ind+1 );
+ os << "}else{" << std::endl;
+ write_code( ((CExpr*)code)->kids[2], os, ind+2, retModStr );
+ indent( os, ind+1 );
+ os << "}" << std::endl;
+ indent( os, ind ); + os << "}" << std::endl;
+ //clean up memory
+ write_dec( expr1, os, ind );
+ }
+ break;
+ case RUN:/*?*/break;
+ case PI:/*?*/break; + case LAM:/*?*/break; + case TYPE:/*?*/break; + case KIND:/*?*/break; + case ASCRIBE:/*?*/break; + case MPZ:/*?*/break; + case PROG:/*?*/break; + case PROGVARS:/*?*/break; + case PAT:/*?*/break;
+ }
+ break;
+ }
+}
+
+void sccwriter::write_args( CExpr* code, std::ostream& os, int ind, int childCounter,
+ std::vector< std::string >& args, int opts )
+{
+ bool encounterCase = false;
+ while( code->kids[childCounter] && + (!encounterCase || code->kids[childCounter]->getop()==CASE ) ) + { + encounterCase = encounterCase || code->kids[childCounter]->getop()==CASE; + if( code->kids[childCounter]->getclass()==SYMS_EXPR ) + { + args.push_back( ((SymSExpr*)code->kids[childCounter])->s ); + } + else + { + //calculate the value for the argument + std::string expr; + write_expr( code->kids[childCounter], os, ind, expr, opts ); + args.push_back( expr ); + } + childCounter++; + }
+}
+
+void sccwriter::write_expr( Expr* code, std::ostream& os, int ind, std::string& expr, int opts )
+{
+ if( code->getclass()==SYMS_EXPR && is_var( ((SymSExpr*)code)->s ) )
+ {
+ get_var_name( ((SymSExpr*)code)->s, expr );
+ indent( os, ind );
+ os << expr.c_str() << "->inc();" << std::endl;
+ }
+ else
+ {
+ std::ostringstream ss; + ss << "e" << exprCount; + exprCount++;
+ //declare the expression
+ indent( os, ind );
+ if( code->getclass()==SYMS_EXPR && !is_var( ((SymSExpr*)code)->s ) )
+ {
+ os << "static ";
+ }
+ os << "Expr* " << ss.str().c_str() << ";" << std::endl;
+ //write the expression
+ std::ostringstream ss2; + ss2 << ss.str().c_str(); + write_code( code, os, ind, ss2.str().c_str(), opts );
+
+ //if is not a sym expression, then decrement the expression and return null
+ if( opts&opt_write_check_sym_expr )
+ { + indent( os, ind ); + os << "if (" << expr.c_str() << "->getclass() != SYM_EXPR) {" << std::endl; + indent( os, ind+1 ); + os << "exit( 1 );" << std::endl; + indent( os, ind ); + os << "}" << std::endl;
+ }
+
+ expr = std::string( ss.str().c_str() );
+ vars.push_back( expr );
+ }
+ //increment the counter for memory management
+ //indent( os, ind );
+ //os << expr.c_str() << "->inc();" << std::endl;
+}
+
+void sccwriter::write_dec( const std::string& expr, std::ostream& os, int ind )
+{
+ bool wd = true;
+ if( wd )
+ {
+ indent( os, ind );
+ os << expr.c_str() << "->dec();" << std::endl;
+ }
+}
+
+void sccwriter::debug_write_code( Expr* code, std::ostream& os, int ind )
+{
+ indent( os, ind );
+ switch( code->getclass() )
+ {
+ case INT_EXPR: + os << "int_expr"; + break; + case HOLE_EXPR: + os << "hole_expr"; + break; + case SYM_EXPR: + os << "sym_expr"; + break; + case SYMS_EXPR:
+ os << "syms_expr: " << ((SymSExpr*)code)->s.c_str();
+ break;
+ default:
+ switch( code->getop() )
+ {
+ case APP: + os << "app"; + break; + case PI: + os << "pi"; + break; + case LAM: + os << "lam"; + break; + case TYPE: + os << "type"; + break; + case KIND: + os << "kind"; + break; + case ASCRIBE: + os << "ascribe"; + break; + case MPZ: + os << "mpz"; + break; + case PROG: + os << "prog"; + break; + case PROGVARS:
+ os << "progvars";
+ break;
+ case MATCH: + os << "match"; + break; + case CASE: + os << "case"; + break; + case PAT: + os << "pat"; + break; + case DO: + os << "do"; + break; + case ADD: + os << "add"; + break; + case NEG: + os << "neg"; + break; + case LET: + os << "let"; + break; + case RUN: + os << "run"; + break; + case FAIL: + os << "fail"; + break; + case MARKVAR: + os << "markvar"; + break; + case IFMARKED:
+ os << "ifmarked";
+ break;
+ case COMPARE:
+ os << "compare";
+ break;
+ default:
+ os << "???";
+ break;
+ }
+ }
+ os << std::endl;
+ if( code->getop()!=0 )
+ {
+ CExpr* ce = (CExpr*)code;
+ int counter = 0;
+ while( ce->kids[counter] ){
+ debug_write_code( ce->kids[counter], os, ind+1 );
+ counter++;
+ }
+ }
+}
diff --git a/proofs/lfsc_checker/sccwriter.h b/proofs/lfsc_checker/sccwriter.h new file mode 100644 index 000000000..f8922934f --- /dev/null +++ b/proofs/lfsc_checker/sccwriter.h @@ -0,0 +1,86 @@ +#ifndef SCC_WRITER_H
+#define SCC_WRITER_H
+
+#include "expr.h"
+#include <vector>
+#include "check.h"
+
+enum
+{
+ opt_write_case_body = 0x00000001,
+ opt_write_check_sym_expr = 0x00000002,
+ opt_write_add_args = 0x000000004,
+ opt_write_no_inc = 0x00000008,
+
+ opt_write_call_debug = 0x00000010,
+ opt_write_nested_app = 0x00000020,
+};
+
+class sccwriter
+{
+private:
+ //options
+ int options;
+ //programs to write to file
+ symmap progs;
+ //list of indicies in progs
+ std::vector< Expr* > progPtrs;
+ std::vector< std::string > progNames;
+ int currProgram;
+ //current variables in the scope
+ std::vector< std::string > vars;
+ //global variables stored for lookups
+ std::vector< std::string > globalSyms;
+ //symbols that must be dec'ed
+ std::vector< std::string > decSyms;
+ //get program
+ CExpr* get_prog( int n ) { return (CExpr*)progs[ progNames[n] ]; }
+ //get index for string
+ int get_prog_index_by_expr( Expr* e );
+ int get_prog_index( const std::string& str );
+ //is variable in current scope
+ bool is_var( const std::string& str );
+ //add global sym
+ void add_global_sym( const std::string& str );
+ //expression count
+ static int exprCount;
+ //string count
+ static int strCount;
+ //args count
+ static int argsCount;
+ //num count
+ static int rnumCount;
+ //indent
+ static void indent( std::ostream& os, int ind );
+ //write function header
+ void write_function_header( std::ostream& os, int index, int opts = 0 );
+ void write_code( Expr* code, std::ostream& os, int ind, const char* retModStr, int opts = 0 );
+ //write all children starting at child counter to stream, store in Expr* e_...e_;
+ void write_args( CExpr* code, std::ostream& os, int ind, int childCounter, std::vector< std::string >& args, int opts = 0 );
+ //write expression - store result of code into e_ for some Expr* e_;
+ void write_expr( Expr* code, std::ostream& os, int ind, std::string& expr, int opts = 0 );
+ //write variable
+ void write_variable( const std::string& n, std::ostream& os );
+ //get function name
+ void get_function_name( const std::string& pname, std::string& fname );
+ //get the variable name
+ void get_var_name( const std::string& n, std::string& nn );
+ //write dec
+ void write_dec( const std::string& expr, std::ostream& os, int ind );
+public:
+ sccwriter( int opts = 0 ) : options( opts ){}
+ virtual ~sccwriter(){}
+
+ void add_scc( const std::string& pname, Expr* exp ) {
+ //progs.push_back( std::pair< std::string, CExpr* >( pname, exp ) );
+ progs[pname] = exp;
+ progPtrs.push_back( exp );
+ progNames.push_back( pname );
+ }
+
+ void write_file();
+ //write code
+ static void debug_write_code( Expr* code, std::ostream& os, int ind );
+};
+
+#endif
diff --git a/proofs/lfsc_checker/trie.cpp b/proofs/lfsc_checker/trie.cpp new file mode 100644 index 000000000..fedb508b0 --- /dev/null +++ b/proofs/lfsc_checker/trie.cpp @@ -0,0 +1,24 @@ +#include "trie.h" +#include <iostream> + +class Simple : public Trie<int>::Cleaner { +public: + ~Simple() {} + void clean(int p) { + (void)p; + } +}; + +template <> +Trie<int>::Cleaner *Trie<int>::cleaner = new Simple; + +void unit_test_trie() { + Trie<int> t; + t.insert("a", 1); + t.insert("b", 2); + t.insert("abc", 3); + t.insert("b", 0); + std::cout << "a: " << t.get("a") << "\n"; + std::cout << "b: " << t.get("b") << "\n"; + std::cout << "abc: " << t.get("abc") << "\n"; +} diff --git a/proofs/lfsc_checker/trie.h b/proofs/lfsc_checker/trie.h new file mode 100644 index 000000000..094a9060a --- /dev/null +++ b/proofs/lfsc_checker/trie.h @@ -0,0 +1,129 @@ +#ifndef sc2__trie_h +#define sc2__trie_h + +#include <vector> +#include <string.h> +#include <stdlib.h> + +template<class Data> +class Trie { +protected: + char *str; + Data d; + bool using_next; + std::vector<Trie<Data> *> next; + + // s is assumed to be non-empty (and non-null) + Data insert_next(const char *s, const Data &x) { + unsigned c = s[0]; + if (c >= next.size()) { + using_next = true; + next.resize(c+1); + next[c] = new Trie<Data>; + } + else if (!next[c]) + next[c] = new Trie<Data>; + + return next[c]->insert(&s[1], x); + } + + // s is assumed to be non-empty (and non-null) + Data get_next(const char *s) { + unsigned c = s[0]; + if (c >= next.size()) + return Data(); + Trie<Data> *n = next[c]; + if (!n) + return Data(); + return n->get(&s[1]); + } + +public: + Trie() : str(), d(), using_next(false), next() { } + + ~Trie(); + + class Cleaner { + public: + virtual ~Cleaner() {} + virtual void clean(Data d) = 0; + }; + + static Cleaner *cleaner; + + bool eqstr(const char *s1, const char *s2) { + while (*s1 && *s2) { + if (*s1++ != *s2++) + return false; + } + return (*s1 == *s2); + } + + Data get(const char *s) { + if (!s[0] && (!str || !str[0])) + return d; + if (str && eqstr(str,s)) + return d; + if (using_next) + return get_next(s); + return Data(); + } + + Data insert(const char *s, const Data &x) { + if (s[0] == 0) { + // we need to insert x right here. + if (str) { + if (str[0] == 0) { + // we need to replace d with x + Data old = d; + d = x; + return old; + } + // we need to push str into next. + (void)insert_next(str,d); + free(str); + } + str = strdup(s); + d = x; + return Data(); + } + + if (str) { + // cannot store x here + if (str[0] != 0) { + insert_next(str,d); + free(str); + str = 0; + d = Data(); + } + return insert_next(s,x); + } + + if (using_next) + // also cannot store x here + return insert_next(s,x); + + // we can insert x here as an optimization + str = strdup(s); + d = x; + return Data(); + } + +}; + +template<class Data> +Trie<Data>::~Trie() +{ + cleaner->clean(d); + for (int i = 0, iend = next.size(); i < iend; i++) { + Trie *t = next[i]; + if (t) + delete t; + } + if (str) + free(str); +} + +extern void unit_test_trie(); + +#endif diff --git a/proofs/signatures/Makefile.am b/proofs/signatures/Makefile.am new file mode 100644 index 000000000..610990ba2 --- /dev/null +++ b/proofs/signatures/Makefile.am @@ -0,0 +1,34 @@ +# These CORE_PLFs are combined to give a "master signature" against +# which proofs are checked internally when using --check-proofs. To +# add support for more theories, just list them here in the same order +# you would to the LFSC proof-checker binary. +# +CORE_PLFS = sat.plf smt.plf th_base.plf + +noinst_LTLIBRARIES = libsignatures.la + +dist_pkgdata_DATA = \ + $(CORE_PLFS) + +libsignatures_la_SOURCES = \ + signatures.cpp + +BUILT_SOURCES = \ + signatures.cpp + +signatures.cpp: $(CORE_PLFS) + $(AM_V_GEN)( \ + echo "namespace CVC4 {"; \ + echo "namespace proof {"; \ + echo; \ + echo "extern const char *const plf_signatures;"; \ + echo "const char *const plf_signatures = \"\\"; \ + cat $+ | sed 's,\\,\\\\,g;s,",\\",g;s,$$,\\n\\,g'; \ + echo "\";"; \ + echo; \ + echo "} /* CVC4::proof namespace */"; \ + echo "} /* CVC4 namespace */"; \ + ) > $@ + +EXTRA_DIST = \ + example.plf diff --git a/proofs/signatures/example.plf b/proofs/signatures/example.plf index 5df1f31c3..ab690eb51 100755 --- a/proofs/signatures/example.plf +++ b/proofs/signatures/example.plf @@ -1,116 +1,116 @@ -; to check, run : lfsc sat.plf smt.plf th_base.plf example.plf
-
-; --------------------------------------------------------------------------------
-; input :
-; ( a = b )
-; ( b = f(c) )
-; ( a != f(c) V a != b )
-
-; theory lemma (by transitivity) :
-; ( a != b V b != f(c) V a = f(c) )
-
-
-; With the theory lemma, the input is unsatisfiable.
-; --------------------------------------------------------------------------------
-
-
-
-(check
-(% s sort
-(% a (term s)
-(% b (term s)
-(% c (term s)
-(% f (term (arrow s s))
-
-; -------------------- declaration of input formula -----------------------------------
-
-(% A1 (th_holds (= s a b))
-(% A2 (th_holds (= s b (apply _ _ f c)))
-(% A3 (th_holds (or (not (= s a (apply _ _ f c))) (not (= s a b))))
-
-
-; ------------------- specify that the following is a proof of the empty clause -----------------
-
-(: (holds cln)
-
-; ---------- use atoms (a1, a2, a3) to map theory literals to boolean literals (v1, v2, v3) ------
-
-(decl_atom (= s a b) (\ v1 (\ a1
-(decl_atom (= s b (apply _ _ f c)) (\ v2 (\ a2
-(decl_atom (= s a (apply _ _ f c)) (\ v3 (\ a3
-
-
-; --------------------------- proof of theory lemma ---------------------------------------------
-
-(satlem _ _
-(ast _ _ _ a1 (\ l1
-(ast _ _ _ a2 (\ l2
-(asf _ _ _ a3 (\ l3
-(clausify_false
-
-; this should be a proof of l1 ^ l2 ^ ~l3 => false
-; this is done by theory proof rules (currently just use "trust")
-
- trust
-
-))))))) (\ CT
-; CT is the clause ( ~v1 V ~v2 V v3 )
-
-; -------------------- clausification of input formulas -----------------------------------------
-
-(satlem _ _
-(asf _ _ _ a1 (\ l1
-(clausify_false
-
-; input formula A1 is ( ~l1 )
-; the following should be a proof of l1 ^ ( ~l1 ) => false
-; this is done by natural deduction rules
-
- (contra _ A1 l1)
-
-))) (\ C1
-; C1 is the clause ( v1 )
-
-
-(satlem _ _
-(asf _ _ _ a2 (\ l2
-(clausify_false
-
-; input formula A2 is ( ~l2 )
-; the following should be a proof of l2 ^ ( ~l2 ) => false
-; this is done by natural deduction rules
-
- (contra _ A2 l2)
-
-))) (\ C2
-; C2 is the clause ( v2 )
-
-
-(satlem _ _
-(ast _ _ _ a3 (\ l3
-(ast _ _ _ a1 (\ l1
-(clausify_false
-
-; input formula A3 is ( ~a3 V ~a1 )
-; the following should be a proof of a3 ^ a1 ^ ( ~a3 V ~a1 ) => false
-; this is done by natural deduction rules
-
- (contra _ l1 (or_elim_1 _ _ (not_not_intro _ l3) A3))
-
-))))) (\ C3
-; C3 is the clause ( ~v3 V ~v1 )
-
-
-
-; -------------------- resolution proof ------------------------------------------------------------
-
-(satlem_simplify _ _ _
-
-(R _ _ C1
-(R _ _ C2
-(R _ _ CT C3 v3) v2) v1)
-
-(\ x x))
-
-))))))))))))))))))))))))))
-)
\ No newline at end of file +; to check, run : lfsc sat.plf smt.plf th_base.plf example.plf + +; -------------------------------------------------------------------------------- +; input : +; ( a = b ) +; ( b = f(c) ) +; ( a != f(c) V a != b ) + +; theory lemma (by transitivity) : +; ( a != b V b != f(c) V a = f(c) ) + + +; With the theory lemma, the input is unsatisfiable. +; -------------------------------------------------------------------------------- + + + +(check +(% s sort +(% a (term s) +(% b (term s) +(% c (term s) +(% f (term (arrow s s)) + +; -------------------- declaration of input formula ----------------------------------- + +(% A1 (th_holds (= s a b)) +(% A2 (th_holds (= s b (apply _ _ f c))) +(% A3 (th_holds (or (not (= s a (apply _ _ f c))) (not (= s a b)))) + + +; ------------------- specify that the following is a proof of the empty clause ----------------- + +(: (holds cln) + +; ---------- use atoms (a1, a2, a3) to map theory literals to boolean literals (v1, v2, v3) ------ + +(decl_atom (= s a b) (\ v1 (\ a1 +(decl_atom (= s b (apply _ _ f c)) (\ v2 (\ a2 +(decl_atom (= s a (apply _ _ f c)) (\ v3 (\ a3 + + +; --------------------------- proof of theory lemma --------------------------------------------- + +(satlem _ _ +(ast _ _ _ a1 (\ l1 +(ast _ _ _ a2 (\ l2 +(asf _ _ _ a3 (\ l3 +(clausify_false + +; this should be a proof of l1 ^ l2 ^ ~l3 => false +; this is done by theory proof rules (currently just use "trust") + + trust + +))))))) (\ CT +; CT is the clause ( ~v1 V ~v2 V v3 ) + +; -------------------- clausification of input formulas ----------------------------------------- + +(satlem _ _ +(asf _ _ _ a1 (\ l1 +(clausify_false + +; input formula A1 is ( ~l1 ) +; the following should be a proof of l1 ^ ( ~l1 ) => false +; this is done by natural deduction rules + + (contra _ A1 l1) + +))) (\ C1 +; C1 is the clause ( v1 ) + + +(satlem _ _ +(asf _ _ _ a2 (\ l2 +(clausify_false + +; input formula A2 is ( ~l2 ) +; the following should be a proof of l2 ^ ( ~l2 ) => false +; this is done by natural deduction rules + + (contra _ A2 l2) + +))) (\ C2 +; C2 is the clause ( v2 ) + + +(satlem _ _ +(ast _ _ _ a3 (\ l3 +(ast _ _ _ a1 (\ l1 +(clausify_false + +; input formula A3 is ( ~a3 V ~a1 ) +; the following should be a proof of a3 ^ a1 ^ ( ~a3 V ~a1 ) => false +; this is done by natural deduction rules + + (contra _ l1 (or_elim_1 _ _ (not_not_intro _ l3) A3)) + +))))) (\ C3 +; C3 is the clause ( ~v3 V ~v1 ) + + + +; -------------------- resolution proof ------------------------------------------------------------ + +(satlem_simplify _ _ _ + +(R _ _ C1 +(R _ _ C2 +(R _ _ CT C3 v3) v2) v1) + +(\ x x)) + +)))))))))))))))))))))))))) +) diff --git a/proofs/signatures/sat.plf b/proofs/signatures/sat.plf index 09255f612..5e2f1cc44 100755 --- a/proofs/signatures/sat.plf +++ b/proofs/signatures/sat.plf @@ -1,127 +1,127 @@ -(declare bool type)
-(declare tt bool)
-(declare ff bool)
-
-(declare var type)
-
-(declare lit type)
-(declare pos (! x var lit))
-(declare neg (! x var lit))
-
-(declare clause type)
-(declare cln clause)
-(declare clc (! x lit (! c clause clause)))
-
-; constructs for general clauses for R, Q, satlem
-
-(declare concat (! c1 clause (! c2 clause clause)))
-(declare clr (! l lit (! c clause clause)))
-
-; code to check resolutions
-
-(program append ((c1 clause) (c2 clause)) clause
- (match c1 (cln c2) ((clc l c1') (clc l (append c1' c2)))))
-
-; we use marks as follows:
-; -- mark 1 to record if we are supposed to remove a positive occurrence of the variable.
-; -- mark 2 to record if we are supposed to remove a negative occurrence of the variable.
-; -- mark 3 if we did indeed remove the variable positively
-; -- mark 4 if we did indeed remove the variable negatively
-(program simplify_clause ((c clause)) clause
- (match c
- (cln cln)
- ((clc l c1)
- (match l
- ; Set mark 1 on v if it is not set, to indicate we should remove it.
- ; After processing the rest of the clause, set mark 3 if we were already
- ; supposed to remove v (so if mark 1 was set when we began). Clear mark3
- ; if we were not supposed to be removing v when we began this call.
- ((pos v)
- (let m (ifmarked v tt (do (markvar v) ff))
- (let c' (simplify_clause c1)
- (match m
- (tt (do (ifmarked3 v v (markvar3 v)) c'))
- (ff (do (ifmarked3 v (markvar3 v) v) (markvar v) (clc l c')))))))
- ; the same as the code for tt, but using different marks.
- ((neg v)
- (let m (ifmarked2 v tt (do (markvar2 v) ff))
- (let c' (simplify_clause c1)
- (match m
- (tt (do (ifmarked4 v v (markvar4 v)) c'))
- (ff (do (ifmarked4 v (markvar4 v) v) (markvar2 v) (clc l c')))))))))
- ((concat c1 c2) (append (simplify_clause c1) (simplify_clause c2)))
- ((clr l c1)
- (match l
- ; set mark 1 to indicate we should remove v, and fail if
- ; mark 3 is not set after processing the rest of the clause
- ; (we will set mark 3 if we remove a positive occurrence of v).
- ((pos v)
- (let m (ifmarked v tt (do (markvar v) ff))
- (let m3 (ifmarked3 v (do (markvar3 v) tt) ff)
- (let c' (simplify_clause c1)
- (ifmarked3 v (do (match m3 (tt v) (ff (markvar3 v)))
- (match m (tt v) (ff (markvar v))) c')
- (fail clause))))))
- ; same as the tt case, but with different marks.
- ((neg v)
- (let m2 (ifmarked2 v tt (do (markvar2 v) ff))
- (let m4 (ifmarked4 v (do (markvar4 v) tt) ff)
- (let c' (simplify_clause c1)
- (ifmarked4 v (do (match m4 (tt v) (ff (markvar4 v)))
- (match m2 (tt v) (ff (markvar2 v))) c')
- (fail clause))))))
- ))))
-
-
-; resolution proofs
-
-(declare holds (! c clause type))
-
-(declare R (! c1 clause (! c2 clause
- (! u1 (holds c1)
- (! u2 (holds c2)
- (! n var
- (holds (concat (clr (pos n) c1)
- (clr (neg n) c2)))))))))
-
-(declare Q (! c1 clause (! c2 clause
- (! u1 (holds c1)
- (! u2 (holds c2)
- (! n var
- (holds (concat (clr (neg n) c1)
- (clr (pos n) c2)))))))))
-
-(declare satlem_simplify
- (! c1 clause
- (! c2 clause
- (! c3 clause
- (! u1 (holds c1)
- (! r (^ (simplify_clause c1) c2)
- (! u2 (! x (holds c2) (holds c3))
- (holds c3))))))))
-
-(declare satlem
- (! c clause
- (! c2 clause
- (! u (holds c)
- (! u2 (! v (holds c) (holds c2))
- (holds c2))))))
-
-; A little example to demonstrate simplify_clause.
-; It can handle nested clr's of both polarities,
-; and correctly cleans up marks when it leaves a
-; clr or clc scope. Uncomment and run with
-; --show-runs to see it in action.
-;
-; (check
-; (% v1 var
-; (% u1 (holds (concat (clr (neg v1) (clr (pos v1) (clc (pos v1) (clr (pos v1) (clc (pos v1) (clc (neg v1) cln))))))
-; (clc (pos v1) (clc (pos v1) cln))))
-; (satlem _ _ _ u1 (\ x x))))))
-
-
-;(check
-; (% v1 var
-; (% u1 (holds (clr (neg v1) (concat (clc (neg v1) cln)
-; (clr (neg v1) (clc (neg v1) cln)))))
-; (satlem _ _ _ u1 (\ x x))))))
\ No newline at end of file +(declare bool type) +(declare tt bool) +(declare ff bool) + +(declare var type) + +(declare lit type) +(declare pos (! x var lit)) +(declare neg (! x var lit)) + +(declare clause type) +(declare cln clause) +(declare clc (! x lit (! c clause clause))) + +; constructs for general clauses for R, Q, satlem + +(declare concat (! c1 clause (! c2 clause clause))) +(declare clr (! l lit (! c clause clause))) + +; code to check resolutions + +(program append ((c1 clause) (c2 clause)) clause + (match c1 (cln c2) ((clc l c1') (clc l (append c1' c2))))) + +; we use marks as follows: +; -- mark 1 to record if we are supposed to remove a positive occurrence of the variable. +; -- mark 2 to record if we are supposed to remove a negative occurrence of the variable. +; -- mark 3 if we did indeed remove the variable positively +; -- mark 4 if we did indeed remove the variable negatively +(program simplify_clause ((c clause)) clause + (match c + (cln cln) + ((clc l c1) + (match l + ; Set mark 1 on v if it is not set, to indicate we should remove it. + ; After processing the rest of the clause, set mark 3 if we were already + ; supposed to remove v (so if mark 1 was set when we began). Clear mark3 + ; if we were not supposed to be removing v when we began this call. + ((pos v) + (let m (ifmarked v tt (do (markvar v) ff)) + (let c' (simplify_clause c1) + (match m + (tt (do (ifmarked3 v v (markvar3 v)) c')) + (ff (do (ifmarked3 v (markvar3 v) v) (markvar v) (clc l c'))))))) + ; the same as the code for tt, but using different marks. + ((neg v) + (let m (ifmarked2 v tt (do (markvar2 v) ff)) + (let c' (simplify_clause c1) + (match m + (tt (do (ifmarked4 v v (markvar4 v)) c')) + (ff (do (ifmarked4 v (markvar4 v) v) (markvar2 v) (clc l c'))))))))) + ((concat c1 c2) (append (simplify_clause c1) (simplify_clause c2))) + ((clr l c1) + (match l + ; set mark 1 to indicate we should remove v, and fail if + ; mark 3 is not set after processing the rest of the clause + ; (we will set mark 3 if we remove a positive occurrence of v). + ((pos v) + (let m (ifmarked v tt (do (markvar v) ff)) + (let m3 (ifmarked3 v (do (markvar3 v) tt) ff) + (let c' (simplify_clause c1) + (ifmarked3 v (do (match m3 (tt v) (ff (markvar3 v))) + (match m (tt v) (ff (markvar v))) c') + (fail clause)))))) + ; same as the tt case, but with different marks. + ((neg v) + (let m2 (ifmarked2 v tt (do (markvar2 v) ff)) + (let m4 (ifmarked4 v (do (markvar4 v) tt) ff) + (let c' (simplify_clause c1) + (ifmarked4 v (do (match m4 (tt v) (ff (markvar4 v))) + (match m2 (tt v) (ff (markvar2 v))) c') + (fail clause)))))) + )))) + + +; resolution proofs + +(declare holds (! c clause type)) + +(declare R (! c1 clause (! c2 clause + (! u1 (holds c1) + (! u2 (holds c2) + (! n var + (holds (concat (clr (pos n) c1) + (clr (neg n) c2))))))))) + +(declare Q (! c1 clause (! c2 clause + (! u1 (holds c1) + (! u2 (holds c2) + (! n var + (holds (concat (clr (neg n) c1) + (clr (pos n) c2))))))))) + +(declare satlem_simplify + (! c1 clause + (! c2 clause + (! c3 clause + (! u1 (holds c1) + (! r (^ (simplify_clause c1) c2) + (! u2 (! x (holds c2) (holds c3)) + (holds c3)))))))) + +(declare satlem + (! c clause + (! c2 clause + (! u (holds c) + (! u2 (! v (holds c) (holds c2)) + (holds c2)))))) + +; A little example to demonstrate simplify_clause. +; It can handle nested clr's of both polarities, +; and correctly cleans up marks when it leaves a +; clr or clc scope. Uncomment and run with +; --show-runs to see it in action. +; +; (check +; (% v1 var +; (% u1 (holds (concat (clr (neg v1) (clr (pos v1) (clc (pos v1) (clr (pos v1) (clc (pos v1) (clc (neg v1) cln)))))) +; (clc (pos v1) (clc (pos v1) cln)))) +; (satlem _ _ _ u1 (\ x x)))))) + + +;(check +; (% v1 var +; (% u1 (holds (clr (neg v1) (concat (clc (neg v1) cln) +; (clr (neg v1) (clc (neg v1) cln))))) +; (satlem _ _ _ u1 (\ x x)))))) diff --git a/proofs/signatures/smt.plf b/proofs/signatures/smt.plf index 75bfc442f..bbee2d49b 100755 --- a/proofs/signatures/smt.plf +++ b/proofs/signatures/smt.plf @@ -3,15 +3,14 @@ ; SMT syntax and semantics (not theory-specific) ; ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; depends on sat.plf (declare formula type) (declare th_holds (! f formula type)) -; constants +; standard logic definitions (declare true formula) (declare false formula) - -; logical connectives (declare not (! f formula formula)) (declare and (! f1 formula (! f2 formula formula))) (declare or (! f1 formula (! f2 formula formula))) @@ -21,24 +20,19 @@ (declare ifte (! b formula (! f1 formula (! f2 formula formula)))) ; terms -(declare sort type) ; sort in the theory -(declare arrow (! s1 sort (! s2 sort sort))) ; function constructor - +(declare sort type) (declare term (! t sort type)) ; declared terms in formula -(declare apply (! s1 sort - (! s2 sort - (! t1 (term (arrow s1 s2)) - (! t2 (term s1) - (term s2)))))) - +; standard definitions for =, ite, let and flet +(declare = (! s sort + (! x (term s) + (! y (term s) + formula)))) (declare ite (! s sort (! f formula (! t1 (term s) (! t2 (term s) (term s)))))) - -; let/flet (declare let (! s sort (! t (term s) (! f (! v (term s) formula) @@ -47,38 +41,52 @@ (! f2 (! v formula formula) formula))) -; predicates -(declare = (! s sort - (! x (term s) - (! y (term s) - formula)))) - -; To avoid duplicating some of the rules (e.g., cong), we will view -; applications of predicates as terms of sort "Bool". +; We view applications of predicates as terms of sort "Bool". ; Such terms can be injected as atomic formulas using "p_app". - (declare Bool sort) ; the special sort for predicates (declare p_app (! x (term Bool) formula)) ; propositional application of term ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; -; Examples +; +; CNF Clausification +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +; binding between an LF var and an (atomic) formula +(declare atom (! v var (! p formula type))) + +(declare decl_atom + (! f formula + (! u (! v var + (! a (atom v f) + (holds cln))) + (holds cln)))) -; an example of "(p1 or p2(0)) and t1=t2(1)" -;(! p1 (term Bool) -;(! p2 (term (arrow Int Bool)) -;(! t1 (term Int) -;(! t2 (term (arrow Int Int)) -;(! F (th_holds (and (or (p_app p1) (p_app (apply _ _ p2 0))) -; (= _ t1 (apply _ _ t2 1)))) -; ... +; clausify a formula directly +(declare clausify_form + (! f formula + (! v var + (! a (atom v f) + (! u (th_holds f) + (holds (clc (pos v) cln))))))) + +(declare clausify_form_not + (! f formula + (! v var + (! a (atom v f) + (! u (th_holds (not f)) + (holds (clc (neg v) cln))))))) + +(declare clausify_false + (! u (th_holds false) + (holds cln))) -; another example of "p3(a,b)" -;(! a (term Int) -;(! b (term Int) -;(! p3 (term (arrow Int (arrow Int Bool))) ; arrow is right assoc. -;(! F (th_holds (p_app (apply _ _ (apply _ _ p3 a) b))) ; apply is left assoc. -; ... +(declare th_let_pf + (! f formula + (! u (th_holds f) + (! u2 (! v (th_holds f) (holds cln)) + (holds cln))))) ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; @@ -102,7 +110,7 @@ (! f formula (! u (th_holds f) (th_holds (not (not f)))))) - + (declare not_not_elim (! f formula (! u (th_holds (not (not f))) @@ -116,14 +124,14 @@ (! u1 (th_holds (not f1)) (! u2 (th_holds (or f1 f2)) (th_holds f2)))))) - + (declare or_elim_2 (! f1 formula (! f2 formula (! u1 (th_holds (not f2)) (! u2 (th_holds (or f1 f2)) (th_holds f1)))))) - + ;; and elimination (declare and_elim_1 @@ -131,13 +139,13 @@ (! f2 formula (! u (th_holds (and f1 f2)) (th_holds f1))))) - + (declare and_elim_2 (! f1 formula (! f2 formula (! u (th_holds (and f1 f2)) (th_holds f2))))) - + ;; not impl elimination (declare not_impl_elim_1 @@ -145,18 +153,18 @@ (! f2 formula (! u (th_holds (not (impl f1 f2))) (th_holds f1))))) - + (declare not_impl_elim_2 (! f1 formula (! f2 formula (! u (th_holds (not (impl f1 f2))) (th_holds (not f2)))))) - + ;; impl elimination (declare impl_intro (! f1 formula (! f2 formula - (! i1 (! u (th_holds f1) + (! i1 (! u (th_holds f1) (th_holds f2)) (th_holds (impl f1 f2)))))) @@ -164,9 +172,9 @@ (! f1 formula (! f2 formula (! u1 (th_holds f1) - (! u2 (th_holds (impl f1 f2)) + (! u2 (th_holds (impl f1 f2)) (th_holds f2)))))) - + ;; iff elimination (declare iff_elim_1 @@ -174,7 +182,7 @@ (! f2 formula (! u1 (th_holds (iff f1 f2)) (th_holds (impl f1 f2)))))) - + (declare iff_elim_2 (! f1 formula (! f2 formula @@ -204,7 +212,7 @@ (! u1 (th_holds a) (! u2 (th_holds (ifte a b c)) (th_holds b))))))) - + (declare ite_elim_2 (! a formula (! b formula @@ -212,7 +220,7 @@ (! u1 (th_holds (not a)) (! u2 (th_holds (ifte a b c)) (th_holds c))))))) - + (declare ite_elim_3 (! a formula (! b formula @@ -220,7 +228,7 @@ (! u1 (th_holds (not b)) (! u2 (th_holds (ifte a b c)) (th_holds c))))))) - + (declare ite_elim_2n (! a formula (! b formula @@ -236,7 +244,7 @@ (! u1 (th_holds a) (! u2 (th_holds (not (ifte a b c))) (th_holds (not b)))))))) - + (declare not_ite_elim_2 (! a formula (! b formula @@ -244,7 +252,7 @@ (! u1 (th_holds (not a)) (! u2 (th_holds (not (ifte a b c))) (th_holds (not c)))))))) - + (declare not_ite_elim_3 (! a formula (! b formula @@ -252,7 +260,7 @@ (! u1 (th_holds b) (! u2 (th_holds (not (ifte a b c))) (th_holds (not c)))))))) - + (declare not_ite_elim_2n (! a formula (! b formula @@ -260,14 +268,42 @@ (! u1 (th_holds a) (! u2 (th_holds (not (ifte (not a) b c))) (th_holds (not c)))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; For theory lemmas +; - make a series of assumptions and then derive a contradiction (or false) +; - then the assumptions yield a formula like "v1 -> v2 -> ... -> vn -> false" +; - In CNF, it becomes a clause: "~v1, ~v2, ..., ~vn" +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; + +(declare ast + (! v var + (! f formula + (! C clause + (! r (atom v f) ;this is specified + (! u (! o (th_holds f) + (holds C)) + (holds (clc (neg v) C)))))))) + +(declare asf + (! v var + (! f formula + (! C clause + (! r (atom v f) + (! u (! o (th_holds (not f)) + (holds C)) + (holds (clc (pos v) C)))))))) - - -;; How to do CNF for disjunctions of theory literals. + + + +;; Example: ;; -;; Given theory literals F1....Fn, and an input formula A of the form (th_holds (or F1 (or F2 .... (or F{n-1} Fn))))). +;; Given theory literals (F1....Fn), and an input formula A of the form (th_holds (or F1 (or F2 .... (or F{n-1} Fn))))). ;; -;; We introduce atoms a1...an for literals F1...Fn, mapping them to boolean literals v1...vn. +;; We introduce atoms (a1,...,an) to map boolean literals (v1,...,vn) top literals (F1,...,Fn). ;; Do this at the beginning of the proof: ;; ;; (decl_atom F1 (\ v1 (\ a1 @@ -275,7 +311,7 @@ ;; .... ;; (decl_atom Fn (\ vn (\ an ;; -;; Our input A is clausified by the following proof: +;; A is then clausified by the following proof: ;; ;;(satlem _ _ ;;(asf _ _ _ a1 (\ l1 @@ -287,14 +323,14 @@ ;; (contra _ ;; (or_elim_1 _ _ l{n-1} ;; ... -;; (or_elim_1 _ _ l2 +;; (or_elim_1 _ _ l2 ;; (or_elim_1 _ _ l1 A))))) ln) ;; ;;))))))) (\ C ;; ;; We now have the free variable C, which should be the clause (v1 V ... V vn). ;; -;; We also need to consider the polarity of literals, say we have A of the form (th_holds (or (not F1) (or F2 (not F3)))). +;; Polarity of literals should be considered, say we have A of the form (th_holds (or (not F1) (or F2 (not F3)))). ;; Where necessary, we use "ast" instead of "asf", introduce negations by "not_not_intro" for pattern matching, and flip ;; the arguments of contra: ;; @@ -305,9 +341,11 @@ ;;(clausify_false ;; ;; (contra _ l3 -;; (or_elim_1 _ _ l2 +;; (or_elim_1 _ _ l2 ;; (or_elim_1 _ _ (not_not_intro l1) A)))) ;; ;;))))))) (\ C ;; -;; C should be the clause (~v1 V v2 V ~v3 )
\ No newline at end of file +;; C should be the clause (~v1 V v2 V ~v3 ) + + diff --git a/proofs/signatures/th_arrays.plf b/proofs/signatures/th_arrays.plf new file mode 100755 index 000000000..6e0e6438d --- /dev/null +++ b/proofs/signatures/th_arrays.plf @@ -0,0 +1,53 @@ +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+;
+; Theory of Arrays
+;
+;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+; depdends on : th_base.plf
+
+; sorts
+
+(declare array (! s1 sort (! s2 sort sort))) ; s1 is index, s2 is element
+
+; functions
+(declare write (! s1 sort
+ (! s2 sort
+ (! t1 (term (array s1 s2))
+ (! t2 (term s1)
+ (! t3 (term s2)
+ (term (array s1 s2))))))))
+(declare read (! s1 sort
+ (! s2 sort
+ (! t1 (term (array s1 s2))
+ (! t2 (term s1)
+ (term s2))))))
+
+; inference rules
+(declare row1 (! s1 sort
+ (! s2 sort
+ (! t1 (term (array s1 s2))
+ (! t2 (term s1)
+ (! t3 (term s2)
+ (th_holds (= _ (read (write t1 t2 t3) t2) t3))))))))
+
+
+(declare row (! s1 sort
+ (! s2 sort
+ (! t1 (term (array s1 s2))
+ (! t2 (term s1)
+ (! t3 (term s1)
+ (! t4 (term s2)
+ (! u (th_holds (not (= _ t2 t3)))
+ (th_holds (= _ (read (write t1 t2 t4) t3) (read t1 t3))))))))))
+
+(declare ext (! s1 sort
+ (! s2 sort
+ (! f formula
+ (! t1 (term (array s1 s2))
+ (! t2 (term (array s1 s2))
+ (! u (th_holds (not (= _ t1 t2)))
+ (! u1 (! k (term s1)
+ (! u2 (th_holds (not (= _ (read t1 k) (read t2 k))))
+ (th_holds f)))
+ (th_holds f)))))))))
+
\ No newline at end of file diff --git a/proofs/signatures/th_base.plf b/proofs/signatures/th_base.plf index e66990de4..977409b6a 100755 --- a/proofs/signatures/th_base.plf +++ b/proofs/signatures/th_base.plf @@ -1,107 +1,73 @@ -;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; Atomization / Clausification
-;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; binding between an LF var and an (atomic) formula
-(declare atom (! v var (! p formula type)))
-
-(declare decl_atom
- (! f formula
- (! u (! v var
- (! a (atom v f)
- (holds cln)))
- (holds cln))))
-
-; direct clausify
-(declare clausify_form
- (! f formula
- (! v var
- (! a (atom v f)
- (! u (th_holds f)
- (holds (clc (pos v) cln)))))))
-
-(declare clausify_form_not
- (! f formula
- (! v var
- (! a (atom v f)
- (! u (th_holds (not f))
- (holds (clc (neg v) cln)))))))
-
-(declare clausify_false
- (! u (th_holds false)
- (holds cln)))
-
-
-(declare th_let_pf
- (! f formula
- (! u (th_holds f)
- (! u2 (! v (th_holds f) (holds cln))
- (holds cln)))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; Theory reasoning
-; - make a series of assumptions and then derive a contradiction (or false)
-; - then the assumptions yield a formula like "v1 -> v2 -> ... -> vn -> false"
-; - In CNF, it becomes a clause: "~v1, ~v2, ..., ~vn"
-;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-(declare ast
- (! v var
- (! f formula
- (! C clause
- (! r (atom v f) ;this is specified
- (! u (! o (th_holds f)
- (holds C))
- (holds (clc (neg v) C))))))))
-
-(declare asf
- (! v var
- (! f formula
- (! C clause
- (! r (atom v f)
- (! u (! o (th_holds (not f))
- (holds C))
- (holds (clc (pos v) C))))))))
-
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-;
-; Theory of Equality and Congruence Closure
-;
-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-
-; temporary :
-(declare trust (th_holds false))
-
-(declare refl
- (! s sort
- (! t (term s)
- (th_holds (= s t t)))))
-
-(declare symm (! s sort
- (! x (term s)
- (! y (term s)
- (! u (th_holds (= _ x y))
- (th_holds (= _ y x)))))))
-
-(declare trans (! s sort
- (! x (term s)
- (! y (term s)
- (! z (term s)
- (! u (th_holds (= _ x y))
- (! u (th_holds (= _ y z))
- (th_holds (= _ x z)))))))))
-
-(declare cong (! s1 sort
- (! s2 sort
- (! a1 (term (arrow s1 s2))
- (! b1 (term (arrow s1 s2))
- (! a2 (term s1)
- (! b2 (term s1)
- (! u1 (th_holds (= _ a1 b1))
- (! u2 (th_holds (= _ a2 b2))
- (th_holds (= _ (apply _ _ a1 a2) (apply _ _ b1 b2))))))))))))
-
+ + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; +; Theory of Equality and Congruence Closure +; +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; depends on : smt.plf + +; sorts : + +(declare arrow (! s1 sort (! s2 sort sort))) ; function constructor + +; functions : + +(declare apply (! s1 sort + (! s2 sort + (! t1 (term (arrow s1 s2)) + (! t2 (term s1) + (term s2)))))) + + +; inference rules : + +(declare trust (th_holds false)) ; temporary + +(declare refl + (! s sort + (! t (term s) + (th_holds (= s t t))))) + +(declare symm (! s sort + (! x (term s) + (! y (term s) + (! u (th_holds (= _ x y)) + (th_holds (= _ y x))))))) + +(declare trans (! s sort + (! x (term s) + (! y (term s) + (! z (term s) + (! u (th_holds (= _ x y)) + (! u (th_holds (= _ y z)) + (th_holds (= _ x z))))))))) + +(declare cong (! s1 sort + (! s2 sort + (! a1 (term (arrow s1 s2)) + (! b1 (term (arrow s1 s2)) + (! a2 (term s1) + (! b2 (term s1) + (! u1 (th_holds (= _ a1 b1)) + (! u2 (th_holds (= _ a2 b2)) + (th_holds (= _ (apply _ _ a1 a2) (apply _ _ b1 b2)))))))))))) + +;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; +; Examples + +; an example of "(p1 or p2(0)) and t1=t2(1)" +;(! p1 (term Bool) +;(! p2 (term (arrow Int Bool)) +;(! t1 (term Int) +;(! t2 (term (arrow Int Int)) +;(! F (th_holds (and (or (p_app p1) (p_app (apply _ _ p2 0))) +; (= _ t1 (apply _ _ t2 1)))) +; ... + +; another example of "p3(a,b)" +;(! a (term Int) +;(! b (term Int) +;(! p3 (term (arrow Int (arrow Int Bool))) ; arrow is right assoc. +;(! F (th_holds (p_app (apply _ _ (apply _ _ p3 a) b))) ; apply is left assoc. +; ... diff --git a/src/Makefile.am b/src/Makefile.am index 3637cb089..e5e1b9346 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -16,7 +16,7 @@ AM_CPPFLAGS = \ -D__BUILDING_CVC4LIB \ -D __STDC_LIMIT_MACROS \ -D __STDC_FORMAT_MACROS \ - -I@builddir@ -I@srcdir@/include -I@srcdir@ + -I@builddir@ -I@srcdir@/include -I@srcdir@ -I@top_srcdir@/proofs/lfsc_checker AM_CXXFLAGS = -Wall -Wno-unknown-pragmas -Wno-parentheses $(FLAG_VISIBILITY_HIDDEN) SUBDIRS = lib options expr util prop/minisat prop/bvminisat . parser compat bindings main @@ -104,6 +104,7 @@ libcvc4_la_SOURCES = \ prop/sat_solver_registry.cpp \ prop/options_handlers.h \ smt/smt_engine.cpp \ + smt/smt_engine_check_proof.cpp \ smt/smt_engine.h \ smt/model_postprocessor.cpp \ smt/model_postprocessor.h \ @@ -290,6 +291,10 @@ libcvc4_la_SOURCES = \ theory/quantifiers/relevant_domain.cpp \ theory/quantifiers/symmetry_breaking.h \ theory/quantifiers/symmetry_breaking.cpp \ + theory/quantifiers/qinterval_builder.h \ + theory/quantifiers/qinterval_builder.cpp \ + theory/quantifiers/quant_conflict_find.h \ + theory/quantifiers/quant_conflict_find.cpp \ theory/quantifiers/options_handlers.h \ theory/rewriterules/theory_rewriterules_rules.h \ theory/rewriterules/theory_rewriterules_rules.cpp \ @@ -393,6 +398,11 @@ libcvc4_la_LIBADD = \ @builddir@/expr/libexpr.la \ @builddir@/prop/minisat/libminisat.la \ @builddir@/prop/bvminisat/libbvminisat.la +if CVC4_PROOF +libcvc4_la_LIBADD += \ + @top_builddir@/proofs/lfsc_checker/liblfsc_checker.la \ + @top_builddir@/proofs/signatures/libsignatures.la +endif if CVC4_NEEDS_REPLACEMENT_FUNCTIONS libcvc4_la_LIBADD += \ diff --git a/src/cvc4.i b/src/cvc4.i index ec3aa43cb..aadbc374d 100644 --- a/src/cvc4.i +++ b/src/cvc4.i @@ -269,6 +269,7 @@ std::set<JavaInputStreamAdapter*> CVC4::JavaInputStreamAdapter::s_adapters; %include "util/record.i" %include "util/regexp.i" %include "util/uninterpreted_constant.i" +%include "util/proof.i" %include "expr/kind.i" %include "expr/expr.i" diff --git a/src/main/command_executor.cpp b/src/main/command_executor.cpp index 467b150d3..485a478d8 100644 --- a/src/main/command_executor.cpp +++ b/src/main/command_executor.cpp @@ -76,14 +76,20 @@ bool CommandExecutor::doCommandSingleton(Command* cmd) if(q != NULL) { d_result = res = q->getResult(); } - // dump the model if option is set - if( status && - d_options[options::produceModels] && - d_options[options::dumpModels] && - ( res.asSatisfiabilityResult() == Result::SAT || - (res.isUnknown() && res.whyUnknown() == Result::INCOMPLETE) ) ) { - Command* gm = new GetModelCommand(); - status = doCommandSingleton(gm); + // dump the model/proof if option is set + if(status) { + if( d_options[options::produceModels] && + d_options[options::dumpModels] && + ( res.asSatisfiabilityResult() == Result::SAT || + (res.isUnknown() && res.whyUnknown() == Result::INCOMPLETE) ) ) { + Command* gm = new GetModelCommand(); + status = doCommandSingleton(gm); + } else if( d_options[options::proof] && + d_options[options::dumpProofs] && + res.asSatisfiabilityResult() == Result::UNSAT ) { + Command* gp = new GetProofCommand(); + status = doCommandSingleton(gp); + } } return status; } diff --git a/src/main/command_executor_portfolio.cpp b/src/main/command_executor_portfolio.cpp index 971aa2131..24469c668 100644 --- a/src/main/command_executor_portfolio.cpp +++ b/src/main/command_executor_portfolio.cpp @@ -213,7 +213,6 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) if(d_lastWinner != 0) delete cmdExported; return ret; } else if(mode == 1) { // portfolio - d_seq->addCommand(cmd->clone()); // We currently don't support changing number of threads for each @@ -327,7 +326,26 @@ bool CommandExecutorPortfolio::doCommandSingleton(Command* cmd) lemmaSharingCleanup(); delete[] fns; - return portfolioReturn.second; + + bool status = portfolioReturn.second; + + // dump the model/proof if option is set + if(status) { + if( d_options[options::produceModels] && + d_options[options::dumpModels] && + ( d_result.asSatisfiabilityResult() == Result::SAT || + (d_result.isUnknown() && d_result.whyUnknown() == Result::INCOMPLETE) ) ) { + Command* gm = new GetModelCommand(); + status = doCommandSingleton(gm); + } else if( d_options[options::proof] && + d_options[options::dumpProofs] && + d_result.asSatisfiabilityResult() == Result::UNSAT ) { + Command* gp = new GetProofCommand(); + status = doCommandSingleton(gp); + } + } + + return status; } else if(mode == 2) { Command* cmdExported = d_lastWinner == 0 ? diff --git a/src/main/driver_unified.cpp b/src/main/driver_unified.cpp index d1baaa2e9..bf66629dd 100644 --- a/src/main/driver_unified.cpp +++ b/src/main/driver_unified.cpp @@ -108,6 +108,10 @@ int runCvc4(int argc, char* argv[], Options& opts) { ! opts[options::threadArgv].empty() ) { throw OptionException("Thread options cannot be used with sequential CVC4. Please build and use the portfolio binary `pcvc4'."); } +# else + if( opts[options::checkProofs] ) { + throw OptionException("Cannot run portfolio in check-proofs mode."); + } # endif progName = opts[options::binary_name].c_str(); @@ -201,8 +205,7 @@ int runCvc4(int argc, char* argv[], Options& opts) { << "Notice: ...the experimental --incremental-parallel option.\n"; exprMgr = new ExprManager(opts); pExecutor = new CommandExecutor(*exprMgr, opts); - } - else { + } else { exprMgr = new ExprManager(threadOpts[0]); pExecutor = new CommandExecutorPortfolio(*exprMgr, opts, threadOpts); } diff --git a/src/options/mkoptions b/src/options/mkoptions index 65a49afa6..087af0ef6 100755 --- a/src/options/mkoptions +++ b/src/options/mkoptions @@ -241,6 +241,7 @@ function handle_option { predicates= links= links_alternate= + smt_links= options_already_documented=false alternate_options_already_documented=false @@ -412,10 +413,28 @@ function handle_option { links_alternate="${links_alternate} $(echo "${args[$i]}" | sed 's,[^/]*/,,')" else links="${links} ${args[$i]}" - links_alternate="${links_alternate} ${args[$i]}" fi done ;; + :link-smt) + j=0 + while [ $(($i+1)) -lt ${#args[@]} ] && ! expr "${args[$(($i+1))]}" : '\:' &>/dev/null; do + let ++i + let ++j + if [ $j -eq 3 ]; then + echo "$kf:$lineno: error: attribute :link-smt can only take two arguments" >&2 + exit 1 + fi + if expr "${args[$i]}" : '.*/' &>/dev/null; then + echo "$kf:$lineno: error: attribute :link-smt cannot take alternates" >&2 + exit 1 + fi + smt_links="${smt_links} ${args[$i]}" + done + if [ $j -eq 1 ]; then + smt_links="${smt_links} \"true\"" + fi + ;; :include) while [ $(($i+1)) -lt ${#args[@]} ] && ! expr "${args[$(($i+1))]}" : '\:' &>/dev/null; do let ++i @@ -544,14 +563,33 @@ template <> bool Options::wasSetByUser(options::${internal}__option_t) const { r fi run_links= run_links_alternate= + run_smt_links= + if [ -n "$links" -a -z "$smt_links" -a -n "$smtname" ]; then + echo "$kf:$lineno: warning: $smtname has no :link-smt, but equivalent command-line has :link" >&2 + elif [ -n "$smt_links" -a -z "$links" ] && [ -n "$short_option" -o -n "$short_option_alternate" -o -n "$long_option" -o "$long_option_alternate" ]; then + echo "$kf:$lineno: warning: $smtname has a :link-smt, but equivalent command-line has no :link" >&2 + fi if [ -n "$links" ]; then + # command-line links for link in $links; do run_links="$run_links #line $lineno \"$kf\" preemptGetopt(extra_argc, extra_argv, \"$link\");" done fi + if [ -n "$smt_links" ]; then + # smt links + smt_links=($smt_links) + i=0 + while [ $i -lt ${#smt_links[@]} ]; do + run_smt_links="$run_smt_links +#line $lineno \"$kf\" + smt->setOption(std::string(\"${smt_links[$i]}\"), SExpr(${smt_links[$(($i+1))]}));" + i=$((i+2)) + done + fi if [ -n "$links_alternate" ]; then + # command-line links for link in $links_alternate; do run_links_alternate="$run_links_alternate #line $lineno \"$kf\" @@ -732,7 +770,7 @@ template <> options::${internal}__option_t::type runHandlerAndPredicates(options #line $lineno \"$kf\" if(key == \"$smtname\") { #line $lineno \"$kf\" - Options::current().assignBool(options::$internal, \"$smtname\", optionarg == \"true\", smt);$run_links + Options::current().assignBool(options::$internal, \"$smtname\", optionarg == \"true\", smt);$run_smt_links return; }" elif [ -n "$expect_arg" -a "$internal" != - ]; then @@ -749,7 +787,7 @@ template <> options::${internal}__option_t::type runHandlerAndPredicates(options #line $lineno \"$kf\" if(key == \"$smtname\") { #line $lineno \"$kf\" - Options::current().assign(options::$internal, \"$smtname\", optionarg, smt);$run_links + Options::current().assign(options::$internal, \"$smtname\", optionarg, smt);$run_smt_links return; }" elif [ -n "$expect_arg" ]; then @@ -764,7 +802,7 @@ template <> options::${internal}__option_t::type runHandlerAndPredicates(options #line $lineno \"$kf\" if(key == \"$smtname\") { #line $lineno \"$kf\" - $run_handlers$run_links + $run_handlers$run_smt_links return; }" else @@ -779,7 +817,7 @@ template <> options::${internal}__option_t::type runHandlerAndPredicates(options #line $lineno \"$kf\" if(key == \"$smtname\") { #line $lineno \"$kf\" - $run_handlers$run_links + $run_handlers$run_smt_links return; }" fi diff --git a/src/parser/smt2/Smt2.g b/src/parser/smt2/Smt2.g index 022c4a069..497705cb6 100644 --- a/src/parser/smt2/Smt2.g +++ b/src/parser/smt2/Smt2.g @@ -1252,6 +1252,10 @@ builtinOp[CVC4::Kind& kind] | STRCON_TOK { $kind = CVC4::kind::STRING_CONCAT; } | STRLEN_TOK { $kind = CVC4::kind::STRING_LENGTH; } | STRSUB_TOK { $kind = CVC4::kind::STRING_SUBSTR; } + | STRCTN_TOK { $kind = CVC4::kind::STRING_STRCTN; } + | STRCAT_TOK { $kind = CVC4::kind::STRING_CHARAT; } + | STRIDOF_TOK { $kind = CVC4::kind::STRING_STRIDOF; } + | STRREPL_TOK { $kind = CVC4::kind::STRING_STRREPL; } | STRINRE_TOK { $kind = CVC4::kind::STRING_IN_REGEXP; } | STRTORE_TOK { $kind = CVC4::kind::STRING_TO_REGEXP; } | RECON_TOK { $kind = CVC4::kind::REGEXP_CONCAT; } @@ -1625,7 +1629,11 @@ INT2BV_TOK : 'int2bv'; //STRCST_TOK : 'str.cst'; STRCON_TOK : 'str.++'; STRLEN_TOK : 'str.len'; -STRSUB_TOK : 'str.sub' ; +STRSUB_TOK : 'str.substr' ; +STRCTN_TOK : 'str.contain' ; +STRCAT_TOK : 'str.at' ; +STRIDOF_TOK : 'str.indexof' ; +STRREPL_TOK : 'str.replace' ; STRINRE_TOK : 'str.in.re'; STRTORE_TOK : 'str.to.re'; RECON_TOK : 're.++'; diff --git a/src/printer/ast/ast_printer.cpp b/src/printer/ast/ast_printer.cpp index 8ab5c121d..72bfa5603 100644 --- a/src/printer/ast/ast_printer.cpp +++ b/src/printer/ast/ast_printer.cpp @@ -153,6 +153,7 @@ void AstPrinter::toStream(std::ostream& out, const Command* c, tryToStream<GetModelCommand>(out, c) || tryToStream<GetAssignmentCommand>(out, c) || tryToStream<GetAssertionsCommand>(out, c) || + tryToStream<GetProofCommand>(out, c) || tryToStream<SetBenchmarkStatusCommand>(out, c) || tryToStream<SetBenchmarkLogicCommand>(out, c) || tryToStream<SetInfoCommand>(out, c) || @@ -307,6 +308,9 @@ static void toStream(std::ostream& out, const GetAssignmentCommand* c) throw() { static void toStream(std::ostream& out, const GetAssertionsCommand* c) throw() { out << "GetAssertions()"; } +static void toStream(std::ostream& out, const GetProofCommand* c) throw() { + out << "GetProof()"; +} static void toStream(std::ostream& out, const SetBenchmarkStatusCommand* c) throw() { out << "SetBenchmarkStatus(" << c->getStatus() << ")"; } diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index b0dfbbd67..ca463d10b 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -776,6 +776,7 @@ void CvcPrinter::toStream(std::ostream& out, const Command* c, tryToStream<GetModelCommand>(out, c) || tryToStream<GetAssignmentCommand>(out, c) || tryToStream<GetAssertionsCommand>(out, c) || + tryToStream<GetProofCommand>(out, c) || tryToStream<SetBenchmarkStatusCommand>(out, c) || tryToStream<SetBenchmarkLogicCommand>(out, c) || tryToStream<SetInfoCommand>(out, c) || @@ -1031,6 +1032,10 @@ static void toStream(std::ostream& out, const GetAssertionsCommand* c) throw() { out << "WHERE;"; } +static void toStream(std::ostream& out, const GetProofCommand* c) throw() { + out << "DUMP_PROOF;"; +} + static void toStream(std::ostream& out, const SetBenchmarkStatusCommand* c) throw() { out << "% (set-info :status " << c->getStatus() << ")"; } diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index b743ba70e..09de0d378 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -273,6 +273,28 @@ void Smt2Printer::toStream(std::ostream& out, TNode n, case kind::STORE: case kind::ARRAY_TYPE: out << smtKindString(k) << " "; break; + // string theory + case kind::STRING_CONCAT: out << "str.++ "; break; + case kind::STRING_IN_REGEXP: out << "str.in.re "; break; + case kind::STRING_LENGTH: out << "str.len "; break; + case kind::STRING_SUBSTR: + case kind::STRING_SUBSTR_TOTAL: + out << "str.substr "; break; + case kind::STRING_CHARAT: + case kind::STRING_CHARAT_TOTAL: + out << "str.at "; break; + case kind::STRING_STRCTN: out << "str.contain "; break; + case kind::STRING_STRIDOF: out << "str.indexof "; break; + case kind::STRING_STRREPL: out << "str.replace "; break; + case kind::STRING_TO_REGEXP: out << "str.to.re "; break; + case kind::REGEXP_CONCAT: out << "re.++ "; break; + case kind::REGEXP_OR: out << "re.or "; break; + case kind::REGEXP_INTER: out << "re.itr "; break; + case kind::REGEXP_STAR: out << "re.* "; break; + case kind::REGEXP_PLUS: out << "re.+ "; break; + case kind::REGEXP_OPT: out << "re.opt "; break; + case kind::REGEXP_RANGE: out << "re.range "; break; + // bv theory case kind::BITVECTOR_CONCAT: out << "concat "; break; case kind::BITVECTOR_AND: out << "bvand "; break; @@ -577,6 +599,7 @@ void Smt2Printer::toStream(std::ostream& out, const Command* c, tryToStream<GetModelCommand>(out, c) || tryToStream<GetAssignmentCommand>(out, c) || tryToStream<GetAssertionsCommand>(out, c) || + tryToStream<GetProofCommand>(out, c) || tryToStream<SetBenchmarkStatusCommand>(out, c) || tryToStream<SetBenchmarkLogicCommand>(out, c) || tryToStream<SetInfoCommand>(out, c) || @@ -858,6 +881,10 @@ static void toStream(std::ostream& out, const GetAssertionsCommand* c) throw() { out << "(get-assertions)"; } +static void toStream(std::ostream& out, const GetProofCommand* c) throw() { + out << "(get-proof)"; +} + static void toStream(std::ostream& out, const SetBenchmarkStatusCommand* c) throw() { out << "(set-info :status " << c->getStatus() << ")"; } diff --git a/src/proof/cnf_proof.cpp b/src/proof/cnf_proof.cpp index 8e9c4cd21..39e802b62 100644 --- a/src/proof/cnf_proof.cpp +++ b/src/proof/cnf_proof.cpp @@ -32,37 +32,45 @@ CnfProof::CnfProof(CnfStream* stream) Expr CnfProof::getAtom(prop::SatVariable var) { - prop::SatLiteral lit (var); - Node node = d_cnfStream->getNode(lit); + prop::SatLiteral lit (var); + Node node = d_cnfStream->getNode(lit); Expr atom = node.toExpr(); - return atom; + return atom; } CnfProof::~CnfProof() { } +LFSCCnfProof::iterator LFSCCnfProof::begin_atom_mapping() { + return iterator(*this, ProofManager::currentPM()->begin_vars()); +} + +LFSCCnfProof::iterator LFSCCnfProof::end_atom_mapping() { + return iterator(*this, ProofManager::currentPM()->end_vars()); +} + void LFSCCnfProof::printAtomMapping(std::ostream& os, std::ostream& paren) { ProofManager::var_iterator it = ProofManager::currentPM()->begin_vars(); ProofManager::var_iterator end = ProofManager::currentPM()->end_vars(); - + for (;it != end; ++it) { os << "(decl_atom "; - + if (ProofManager::currentPM()->getLogic().compare("QF_UF") == 0) { Expr atom = getAtom(*it); - LFSCTheoryProof::printFormula(atom, os); + LFSCTheoryProof::printTerm(atom, os); } else { // print fake atoms for all other logics - os << "true "; + os << "true "; } os << " (\\ " << ProofManager::getVarName(*it) << " (\\ " << ProofManager::getAtomName(*it) << "\n"; - paren << ")))"; + paren << ")))"; } } void LFSCCnfProof::printClauses(std::ostream& os, std::ostream& paren) { - printInputClauses(os, paren); + printInputClauses(os, paren); printTheoryLemmas(os, paren); } @@ -70,51 +78,49 @@ void LFSCCnfProof::printInputClauses(std::ostream& os, std::ostream& paren) { os << " ;; Input Clauses \n"; ProofManager::clause_iterator it = ProofManager::currentPM()->begin_input_clauses(); ProofManager::clause_iterator end = ProofManager::currentPM()->end_input_clauses(); - + for (; it != end; ++it) { ClauseId id = it->first; const prop::SatClause* clause = it->second; os << "(satlem _ _ "; - std::ostringstream clause_paren; + std::ostringstream clause_paren; printClause(*clause, os, clause_paren); os << " (clausify_false trust)" << clause_paren.str(); - os << "( \\ " << ProofManager::getInputClauseName(id) << "\n"; - paren << "))"; + os << "( \\ " << ProofManager::getInputClauseName(id) << "\n"; + paren << "))"; } } void LFSCCnfProof::printTheoryLemmas(std::ostream& os, std::ostream& paren) { - os << " ;; Theory Lemmas \n"; + os << " ;; Theory Lemmas \n"; ProofManager::clause_iterator it = ProofManager::currentPM()->begin_lemmas(); ProofManager::clause_iterator end = ProofManager::currentPM()->end_lemmas(); - + for (; it != end; ++it) { ClauseId id = it->first; const prop::SatClause* clause = it->second; os << "(satlem _ _ "; - std::ostringstream clause_paren; + std::ostringstream clause_paren; printClause(*clause, os, clause_paren); os << " (clausify_false trust)" << clause_paren.str(); - os << "( \\ " << ProofManager::getLemmaClauseName(id) <<"\n"; - paren << "))"; + os << "( \\ " << ProofManager::getLemmaClauseName(id) <<"\n"; + paren << "))"; } } void LFSCCnfProof::printClause(const prop::SatClause& clause, std::ostream& os, std::ostream& paren) { for (unsigned i = 0; i < clause.size(); ++i) { prop::SatLiteral lit = clause[i]; - prop::SatVariable var = lit.getSatVariable(); + prop::SatVariable var = lit.getSatVariable(); if (lit.isNegated()) { os << "(ast _ _ _ " << ProofManager::getAtomName(var) <<" (\\ " << ProofManager::getLitName(lit) << " "; - paren << "))"; + paren << "))"; } else { os << "(asf _ _ _ " << ProofManager::getAtomName(var) <<" (\\ " << ProofManager::getLitName(lit) << " "; - paren << "))"; + paren << "))"; } } } - } /* CVC4 namespace */ - diff --git a/src/proof/cnf_proof.h b/src/proof/cnf_proof.h index 9a2dbe655..0a932f906 100644 --- a/src/proof/cnf_proof.h +++ b/src/proof/cnf_proof.h @@ -13,7 +13,7 @@ ** ** A manager for CnfProofs. ** - ** + ** **/ #ifndef __CVC4__CNF_PROOF_H @@ -25,36 +25,68 @@ #include <ext/hash_set> #include <ext/hash_map> -#include <iostream> +#include <iostream> namespace CVC4 { namespace prop { -class CnfStream; + class CnfStream; } +class CnfProof; + +class AtomIterator { + CnfProof& d_cnf; + ProofManager::var_iterator d_it; + +public: + AtomIterator(CnfProof& cnf, const ProofManager::var_iterator& it) + : d_cnf(cnf), d_it(it) + {} + inline Expr operator*(); + AtomIterator& operator++() { ++d_it; return *this; } + AtomIterator operator++(int) { AtomIterator x = *this; ++d_it; return x; } + bool operator==(const AtomIterator& it) const { return &d_cnf == &it.d_cnf && d_it == it.d_it; } + bool operator!=(const AtomIterator& it) const { return !(*this == it); } +};/* class AtomIterator */ + class CnfProof { protected: CVC4::prop::CnfStream* d_cnfStream; Expr getAtom(prop::SatVariable var); + friend class AtomIterator; public: CnfProof(CVC4::prop::CnfStream* cnfStream); + typedef AtomIterator iterator; + virtual iterator begin_atom_mapping() = 0; + virtual iterator end_atom_mapping() = 0; + virtual void printAtomMapping(std::ostream& os, std::ostream& paren) = 0; virtual void printClauses(std::ostream& os, std::ostream& paren) = 0; - virtual ~CnfProof(); + virtual ~CnfProof(); }; -class LFSCCnfProof: public CnfProof { +class LFSCCnfProof : public CnfProof { void printInputClauses(std::ostream& os, std::ostream& paren); void printTheoryLemmas(std::ostream& os, std::ostream& paren); void printClause(const prop::SatClause& clause, std::ostream& os, std::ostream& paren); + public: LFSCCnfProof(CVC4::prop::CnfStream* cnfStream) : CnfProof(cnfStream) {} + + virtual iterator begin_atom_mapping(); + virtual iterator end_atom_mapping(); + virtual void printAtomMapping(std::ostream& os, std::ostream& paren); virtual void printClauses(std::ostream& os, std::ostream& paren); }; +inline Expr AtomIterator::operator*() { + return d_cnf.getAtom(*d_it); +} + } /* CVC4 namespace */ + #endif /* __CVC4__CNF_PROOF_H */ diff --git a/src/proof/proof.h b/src/proof/proof.h index 02f8f7684..e3b776cce 100644 --- a/src/proof/proof.h +++ b/src/proof/proof.h @@ -23,7 +23,7 @@ #ifdef CVC4_PROOF # define PROOF(x) if(options::proof()) { x; } -# define NULLPROOF(x) (options::proof())? x : NULL +# define NULLPROOF(x) (options::proof()) ? x : NULL # define PROOF_ON() options::proof() #else /* CVC4_PROOF */ # define PROOF(x) diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index 110e6b79a..14a82b17b 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -28,14 +28,10 @@ namespace CVC4 { std::string append(const std::string& str, uint64_t num) { std::ostringstream os; - os << str << num; - return os.str(); + os << str << num; + return os.str(); } - -bool ProofManager::isInitialized = false; -ProofManager* ProofManager::proofManager = NULL; - ProofManager::ProofManager(ProofFormat format): d_satProof(NULL), d_cnfProof(NULL), @@ -50,41 +46,43 @@ ProofManager::~ProofManager() { delete d_cnfProof; delete d_theoryProof; delete d_fullProof; - for (IdToClause::iterator it = d_inputClauses.begin(); it != d_inputClauses.end(); ++it) { - delete it->second; + + for(IdToClause::iterator it = d_inputClauses.begin(); + it != d_inputClauses.end(); + ++it) { + delete it->second; } - for (IdToClause::iterator it = d_theoryLemmas.begin(); it != d_theoryLemmas.end(); ++it) { - delete it->second; + + for(IdToClause::iterator it = d_theoryLemmas.begin(); + it != d_theoryLemmas.end(); + ++it) { + delete it->second; } - // FIXME: memory leak because there are deleted theory lemmas that were not used in the - // SatProof + + // FIXME: memory leak because there are deleted theory lemmas that + // were not used in the SatProof } ProofManager* ProofManager::currentPM() { - if (isInitialized) { - return proofManager; - } else { - proofManager = new ProofManager(); - isInitialized = true; - return proofManager; - } + return smt::currentProofManager(); } Proof* ProofManager::getProof(SmtEngine* smt) { - if (currentPM()->d_fullProof != NULL) + if (currentPM()->d_fullProof != NULL) { return currentPM()->d_fullProof; + } Assert (currentPM()->d_format == LFSC); currentPM()->d_fullProof = new LFSCProof(smt, (LFSCSatProof*)getSatProof(), (LFSCCnfProof*)getCnfProof(), - (LFSCTheoryProof*)getTheoryProof()); + (LFSCTheoryProof*)getTheoryProof()); return currentPM()->d_fullProof; } SatProof* ProofManager::getSatProof() { Assert (currentPM()->d_satProof); - return currentPM()->d_satProof; + return currentPM()->d_satProof; } CnfProof* ProofManager::getCnfProof() { @@ -107,7 +105,7 @@ void ProofManager::initSatProof(Minisat::Solver* solver) { void ProofManager::initCnfProof(prop::CnfStream* cnfStream) { Assert (currentPM()->d_cnfProof == NULL); Assert (currentPM()->d_format == LFSC); - currentPM()->d_cnfProof = new LFSCCnfProof(cnfStream); + currentPM()->d_cnfProof = new LFSCCnfProof(cnfStream); } void ProofManager::initTheoryProof() { @@ -126,8 +124,8 @@ std::string ProofManager::getLitName(prop::SatLiteral lit) {return append("l", l void ProofManager::addClause(ClauseId id, const prop::SatClause* clause, ClauseKind kind) { for (unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = clause->operator[](i); - d_propVars.insert(lit.getSatVariable()); + prop::SatLiteral lit = clause->operator[](i); + d_propVars.insert(lit.getSatVariable()); } if (kind == INPUT) { d_inputClauses.insert(std::make_pair(id, clause)); @@ -138,11 +136,11 @@ void ProofManager::addClause(ClauseId id, const prop::SatClause* clause, ClauseK } void ProofManager::addAssertion(Expr formula) { - d_inputFormulas.insert(formula); + d_inputFormulas.insert(formula); } void ProofManager::setLogic(const std::string& logic_string) { - d_logic = logic_string; + d_logic = logic_string; } @@ -158,17 +156,24 @@ LFSCProof::LFSCProof(SmtEngine* smtEngine, LFSCSatProof* sat, LFSCCnfProof* cnf, void LFSCProof::toStream(std::ostream& out) { smt::SmtScope scope(d_smtEngine); std::ostringstream paren; - out << "(check \n"; - if (ProofManager::currentPM()->getLogic().compare("QF_UF") == 0) { - d_theoryProof->printAssertions(out, paren); + out << "(check\n"; + if (d_theoryProof == NULL) { + d_theoryProof = new LFSCTheoryProof(); + } + for(LFSCCnfProof::iterator i = d_cnfProof->begin_atom_mapping(); + i != d_cnfProof->end_atom_mapping(); + ++i) { + d_theoryProof->addDeclaration(*i); } - out << "(: (holds cln) \n"; + d_theoryProof->printAssertions(out, paren); + out << "(: (holds cln)\n"; d_cnfProof->printAtomMapping(out, paren); d_cnfProof->printClauses(out, paren); - d_satProof->printResolutions(out, paren); + d_satProof->printResolutions(out, paren); paren <<")))\n;;"; - out << paren.str(); + out << paren.str(); + out << "\n"; } -} /* CVC4 namespace */ +} /* CVC4 namespace */ diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index e33f1a63f..ab8a7b2bc 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -9,11 +9,9 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief A manager for Proofs. + ** \brief A manager for Proofs ** ** A manager for Proofs. - ** - ** **/ #include "cvc4_private.h" @@ -21,7 +19,7 @@ #ifndef __CVC4__PROOF_MANAGER_H #define __CVC4__PROOF_MANAGER_H -#include <iostream> +#include <iostream> #include "proof/proof.h" #include "util/proof.h" @@ -29,15 +27,15 @@ // forward declarations namespace Minisat { class Solver; -} +}/* Minisat namespace */ namespace CVC4 { namespace prop { class CnfStream; -} +}/* CVC4::prop namespace */ -class SmtEngine; +class SmtEngine; typedef int ClauseId; @@ -51,10 +49,10 @@ class LFSCCnfProof; class LFSCTheoryProof; namespace prop { -typedef uint64_t SatVariable; -class SatLiteral; -typedef std::vector<SatLiteral> SatClause; -} + typedef uint64_t SatVariable; + class SatLiteral; + typedef std::vector<SatLiteral> SatClause; +}/* CVC4::prop namespace */ // different proof modes enum ProofFormat { @@ -64,7 +62,7 @@ enum ProofFormat { std::string append(const std::string& str, uint64_t num); -typedef __gnu_cxx::hash_map < ClauseId, const prop::SatClause* > IdToClause; +typedef __gnu_cxx::hash_map < ClauseId, const prop::SatClause* > IdToClause; typedef __gnu_cxx::hash_set<prop::SatVariable > VarSet; typedef __gnu_cxx::hash_set<Expr, ExprHashFunction > ExprSet; @@ -74,35 +72,36 @@ enum ClauseKind { INPUT, THEORY_LEMMA, LEARNT -}; +};/* enum ClauseKind */ class ProofManager { SatProof* d_satProof; CnfProof* d_cnfProof; - TheoryProof* d_theoryProof; + TheoryProof* d_theoryProof; // information that will need to be shared across proofs IdToClause d_inputClauses; IdToClause d_theoryLemmas; ExprSet d_inputFormulas; VarSet d_propVars; - - Proof* d_fullProof; + + Proof* d_fullProof; ProofFormat d_format; - - static ProofManager* proofManager; - static bool isInitialized; - ProofManager(ProofFormat format = LFSC); - ~ProofManager(); + protected: std::string d_logic; + public: + ProofManager(ProofFormat format = LFSC); + ~ProofManager(); + static ProofManager* currentPM(); - // initialization - static void initSatProof(Minisat::Solver* solver); + + // initialization + static void initSatProof(Minisat::Solver* solver); static void initCnfProof(CVC4::prop::CnfStream* cnfStream); static void initTheoryProof(); - + static Proof* getProof(SmtEngine* smt); static SatProof* getSatProof(); static CnfProof* getCnfProof(); @@ -110,9 +109,9 @@ public: // iterators over data shared by proofs typedef IdToClause::const_iterator clause_iterator; - typedef ExprSet::const_iterator assertions_iterator; + typedef ExprSet::const_iterator assertions_iterator; typedef VarSet::const_iterator var_iterator; - + clause_iterator begin_input_clauses() const { return d_inputClauses.begin(); } clause_iterator end_input_clauses() const { return d_inputClauses.end(); } @@ -124,10 +123,10 @@ public: var_iterator begin_vars() const { return d_propVars.begin(); } var_iterator end_vars() const { return d_propVars.end(); } - + void addAssertion(Expr formula); - void addClause(ClauseId id, const prop::SatClause* clause, ClauseKind kind); - + void addClause(ClauseId id, const prop::SatClause* clause, ClauseKind kind); + // variable prefixes static std::string getInputClauseName(ClauseId id); static std::string getLemmaClauseName(ClauseId id); @@ -136,7 +135,7 @@ public: static std::string getVarName(prop::SatVariable var); static std::string getAtomName(prop::SatVariable var); static std::string getLitName(prop::SatLiteral lit); - + void setLogic(const std::string& logic_string); const std::string getLogic() const { return d_logic; } };/* class ProofManager */ @@ -145,13 +144,13 @@ class LFSCProof : public Proof { LFSCSatProof* d_satProof; LFSCCnfProof* d_cnfProof; LFSCTheoryProof* d_theoryProof; - SmtEngine* d_smtEngine; + SmtEngine* d_smtEngine; public: - LFSCProof(SmtEngine* smtEngine, LFSCSatProof* sat, LFSCCnfProof* cnf, LFSCTheoryProof* theory); + LFSCProof(SmtEngine* smtEngine, LFSCSatProof* sat, LFSCCnfProof* cnf, LFSCTheoryProof* theory); virtual void toStream(std::ostream& out); virtual ~LFSCProof() {} -}; - +};/* class LFSCProof */ + }/* CVC4 namespace */ #endif /* __CVC4__PROOF_MANAGER_H */ diff --git a/src/proof/sat_proof.cpp b/src/proof/sat_proof.cpp index da9df0d42..3b5509ffb 100644 --- a/src/proof/sat_proof.cpp +++ b/src/proof/sat_proof.cpp @@ -25,7 +25,7 @@ using namespace Minisat; using namespace CVC4::prop; namespace CVC4 { -/// some helper functions +/// some helper functions void printLit (Minisat::Lit l) { Debug("proof:sat") << (sign(l) ? "-" : "") << var(l) + 1; @@ -33,16 +33,16 @@ void printLit (Minisat::Lit l) { void printClause (Minisat::Clause& c) { for (int i = 0; i < c.size(); i++) { - Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; + Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; } } void printLitSet(const LitSet& s) { for(LitSet::iterator it = s.begin(); it != s.end(); ++it) { printLit(*it); - Debug("proof:sat") << " "; + Debug("proof:sat") << " "; } - Debug("proof:sat") << endl; + Debug("proof:sat") << endl; } // purely debugging functions @@ -52,39 +52,38 @@ void printDebug (Minisat::Lit l) { void printDebug (Minisat::Clause& c) { for (int i = 0; i < c.size(); i++) { - Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; + Debug("proof:sat") << (sign(c[i]) ? "-" : "") << var(c[i]) + 1 << " "; } Debug("proof:sat") << endl; } -int SatProof::d_idCounter = 0; +int SatProof::d_idCounter = 0; -/** +/** * Converts the clause associated to id to a set of literals - * + * * @param id the clause id - * @param set the clause converted to a set of literals + * @param set the clause converted to a set of literals */ void SatProof::createLitSet(ClauseId id, LitSet& set) { - Assert (set.empty()); + Assert(set.empty()); if(isUnit(id)) { set.insert(getUnit(id)); return; } if ( id == d_emptyClauseId) { - return; + return; } CRef ref = getClauseRef(id); - Assert (ref != CRef_Undef); - Clause& c = d_solver->ca[ref]; + Clause& c = getClause(ref); for (int i = 0; i < c.size(); i++) { - set.insert(c[i]); + set.insert(c[i]); } } -/** +/** * Resolves clause1 and clause2 on variable var and stores the * result in clause1 * @param v @@ -93,36 +92,40 @@ void SatProof::createLitSet(ClauseId id, LitSet& set) { */ bool resolve(const Lit v, LitSet& clause1, LitSet& clause2, bool s) { Assert(!clause1.empty()); - Assert(!clause2.empty()); - Lit var = sign(v) ? ~v : v; + Assert(!clause2.empty()); + Lit var = sign(v) ? ~v : v; if (s) { // literal appears positive in the first clause if( !clause2.count(~var)) { - Debug("proof:sat") << "proof:resolve: Missing literal "; - printLit(var); - Debug("proof:sat") << endl; - return false; + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "proof:resolve: Missing literal "; + printLit(var); + Debug("proof:sat") << endl; + } + return false; } clause1.erase(var); clause2.erase(~var); for (LitSet::iterator it = clause2.begin(); it!= clause2.end(); ++it) { - clause1.insert(*it); + clause1.insert(*it); } } else { // literal appears negative in the first clause if( !clause1.count(~var) || !clause2.count(var)) { - Debug("proof:sat") << "proof:resolve: Missing literal "; - printLit(var); - Debug("proof:sat") << endl; - return false; + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "proof:resolve: Missing literal "; + printLit(var); + Debug("proof:sat") << endl; + } + return false; } clause1.erase(~var); clause2.erase(var); for (LitSet::iterator it = clause2.begin(); it!= clause2.end(); ++it) { - clause1.insert(*it); + clause1.insert(*it); } } - return true; + return true; } /// ResChain @@ -135,16 +138,16 @@ ResChain::ResChain(ClauseId start) : void ResChain::addStep(Lit lit, ClauseId id, bool sign) { ResStep step(lit, id, sign); - d_steps.push_back(step); + d_steps.push_back(step); } void ResChain::addRedundantLit(Lit lit) { if (d_redundantLits) { - d_redundantLits->insert(lit); + d_redundantLits->insert(lit); } else { d_redundantLits = new LitSet(); - d_redundantLits->insert(lit); + d_redundantLits->insert(lit); } } @@ -156,7 +159,7 @@ ProofProxy::ProofProxy(SatProof* proof): {} void ProofProxy::updateCRef(CRef oldref, CRef newref) { - d_proof->updateCRef(oldref, newref); + d_proof->updateCRef(oldref, newref); } @@ -183,27 +186,27 @@ SatProof::SatProof(Minisat::Solver* solver, bool checkRes) : d_seenInput(), d_seenLemmas() { - d_proxy = new ProofProxy(this); + d_proxy = new ProofProxy(this); } -/** +/** * Returns true if the resolution chain corresponding to id * does resolve to the clause associated to id - * @param id - * - * @return + * @param id + * + * @return */ bool SatProof::checkResolution(ClauseId id) { if(d_checkRes) { - bool validRes = true; - Assert (d_resChains.find(id) != d_resChains.end()); + bool validRes = true; + Assert(d_resChains.find(id) != d_resChains.end()); ResChain* res = d_resChains[id]; LitSet clause1; createLitSet(res->getStart(), clause1); - ResSteps& steps = res->getSteps(); + ResSteps& steps = res->getSteps(); for (unsigned i = 0; i < steps.size(); i++) { Lit var = steps[i].lit; - LitSet clause2; + LitSet clause2; createLitSet (steps[i].id, clause2); bool res = resolve (var, clause1, clause2, steps[i].sign); if(res == false) { @@ -215,35 +218,38 @@ bool SatProof::checkResolution(ClauseId id) { if (isUnit(id)) { // special case if it was a unit clause Lit unit = getUnit(id); - validRes = clause1.size() == clause1.count(unit) && !clause1.empty(); - return validRes; + validRes = clause1.size() == clause1.count(unit) && !clause1.empty(); + return validRes; } if (id == d_emptyClauseId) { - return clause1.empty(); + return clause1.empty(); } CRef ref = getClauseRef(id); - Assert (ref != CRef_Undef); - Clause& c = d_solver->ca[ref]; + Clause& c = getClause(ref); for (int i = 0; i < c.size(); ++i) { int count = clause1.erase(c[i]); if (count == 0) { - Debug("proof:sat") << "proof:checkResolution::literal not in computed result "; - printLit(c[i]); - Debug("proof:sat") << "\n"; - validRes = false; + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "proof:checkResolution::literal not in computed result "; + printLit(c[i]); + Debug("proof:sat") << "\n"; + } + validRes = false; } } validRes = clause1.empty(); if (! validRes) { - Debug("proof:sat") << "proof:checkResolution::Invalid Resolution, unremoved literals: \n"; - printLitSet(clause1); - Debug("proof:sat") << "proof:checkResolution:: result should be: \n"; - printClause(c); + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "proof:checkResolution::Invalid Resolution, unremoved literals: \n"; + printLitSet(clause1); + Debug("proof:sat") << "proof:checkResolution:: result should be: \n"; + printClause(c); + } } - return validRes; - + return validRes; + } else { - return true; + return true; } } @@ -254,16 +260,16 @@ bool SatProof::checkResolution(ClauseId id) { ClauseId SatProof::getClauseId(::Minisat::CRef ref) { if(d_clauseId.find(ref) == d_clauseId.end()) { - Debug("proof:sat") << "Missing clause \n"; + Debug("proof:sat") << "Missing clause \n"; } Assert(d_clauseId.find(ref) != d_clauseId.end()); - return d_clauseId[ref]; + return d_clauseId[ref]; } ClauseId SatProof::getClauseId(::Minisat::Lit lit) { Assert(d_unitId.find(toInt(lit)) != d_unitId.end()); - return d_unitId[toInt(lit)]; + return d_unitId[toInt(lit)]; } Minisat::CRef SatProof::getClauseRef(ClauseId id) { @@ -273,79 +279,85 @@ Minisat::CRef SatProof::getClauseRef(ClauseId id) { << (isUnit(id)? "Unit" : "") << endl; } Assert(d_idClause.find(id) != d_idClause.end()); - return d_idClause[id]; + return d_idClause[id]; } Clause& SatProof::getClause(CRef ref) { - return d_solver->ca[ref]; + Assert(ref != CRef_Undef); + Assert(ref >= 0 && ref < d_solver->ca.size()); + return d_solver->ca[ref]; } + Minisat::Lit SatProof::getUnit(ClauseId id) { - Assert (d_idUnit.find(id) != d_idUnit.end()); - return d_idUnit[id]; + Assert(d_idUnit.find(id) != d_idUnit.end()); + return d_idUnit[id]; } bool SatProof::isUnit(ClauseId id) { - return d_idUnit.find(id) != d_idUnit.end(); + return d_idUnit.find(id) != d_idUnit.end(); } bool SatProof::isUnit(::Minisat::Lit lit) { - return d_unitId.find(toInt(lit)) != d_unitId.end(); + return d_unitId.find(toInt(lit)) != d_unitId.end(); } ClauseId SatProof::getUnitId(::Minisat::Lit lit) { - Assert(isUnit(lit)); - return d_unitId[toInt(lit)]; + Assert(isUnit(lit)); + return d_unitId[toInt(lit)]; } bool SatProof::hasResolution(ClauseId id) { - return d_resChains.find(id) != d_resChains.end(); + return d_resChains.find(id) != d_resChains.end(); } bool SatProof::isInputClause(ClauseId id) { - return (d_inputClauses.find(id) != d_inputClauses.end()); + return (d_inputClauses.find(id) != d_inputClauses.end()); } bool SatProof::isLemmaClause(ClauseId id) { - return (d_lemmaClauses.find(id) != d_lemmaClauses.end()); + return (d_lemmaClauses.find(id) != d_lemmaClauses.end()); } void SatProof::print(ClauseId id) { if (d_deleted.find(id) != d_deleted.end()) { - Debug("proof:sat") << "del"<<id; + Debug("proof:sat") << "del"<<id; } else if (isUnit(id)) { - printLit(getUnit(id)); + printLit(getUnit(id)); } else if (id == d_emptyClauseId) { - Debug("proof:sat") << "empty "<< endl; + Debug("proof:sat") << "empty "<< endl; } else { - CRef ref = getClauseRef(id); - Assert (ref != CRef_Undef); - printClause(d_solver->ca[ref]); + CRef ref = getClauseRef(id); + printClause(getClause(ref)); } } void SatProof::printRes(ClauseId id) { Assert(hasResolution(id)); Debug("proof:sat") << "id "<< id <<": "; - printRes(d_resChains[id]); + printRes(d_resChains[id]); } void SatProof::printRes(ResChain* res) { ClauseId start_id = res->getStart(); - Debug("proof:sat") << "("; - print(start_id); + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "("; + print(start_id); + } ResSteps& steps = res->getSteps(); for(unsigned i = 0; i < steps.size(); i++ ) { Lit v = steps[i].lit; ClauseId id = steps[i].id; - Debug("proof:sat") << "["; - printLit(v); - Debug("proof:sat") << "] "; - print(id); + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "["; + printLit(v); + Debug("proof:sat") << "] "; + print(id); + } } Debug("proof:sat") << ") \n"; } @@ -353,23 +365,23 @@ void SatProof::printRes(ResChain* res) { /// registration methods ClauseId SatProof::registerClause(::Minisat::CRef clause, ClauseKind kind) { - Assert(clause != CRef_Undef); + Assert(clause != CRef_Undef); ClauseIdMap::iterator it = d_clauseId.find(clause); - if (it == d_clauseId.end()) { - ClauseId newId = d_idCounter++; - d_clauseId[clause]= newId; - d_idClause[newId] =clause; - if (kind == INPUT) { - Assert (d_inputClauses.find(newId) == d_inputClauses.end()); - d_inputClauses.insert(newId); - } - if (kind == THEORY_LEMMA) { - Assert (d_lemmaClauses.find(newId) == d_lemmaClauses.end()); - d_lemmaClauses.insert(newId); - } - } - Debug("proof:sat:detailed") <<"registerClause CRef:" << clause <<" id:" << d_clauseId[clause] << " " << kind << "\n"; - return d_clauseId[clause]; + if (it == d_clauseId.end()) { + ClauseId newId = d_idCounter++; + d_clauseId[clause] = newId; + d_idClause[newId] = clause; + if (kind == INPUT) { + Assert(d_inputClauses.find(newId) == d_inputClauses.end()); + d_inputClauses.insert(newId); + } + if (kind == THEORY_LEMMA) { + Assert(d_lemmaClauses.find(newId) == d_lemmaClauses.end()); + d_lemmaClauses.insert(newId); + } + } + Debug("proof:sat:detailed") <<"registerClause CRef:" << clause <<" id:" << d_clauseId[clause] << " " << kind << "\n"; + return d_clauseId[clause]; } ClauseId SatProof::registerUnitClause(::Minisat::Lit lit, ClauseKind kind) { @@ -377,44 +389,42 @@ ClauseId SatProof::registerUnitClause(::Minisat::Lit lit, ClauseKind kind) { if (it == d_unitId.end()) { ClauseId newId = d_idCounter++; d_unitId[toInt(lit)] = newId; - d_idUnit[newId] = lit; + d_idUnit[newId] = lit; if (kind == INPUT) { - Assert (d_inputClauses.find(newId) == d_inputClauses.end()); - d_inputClauses.insert(newId); + Assert(d_inputClauses.find(newId) == d_inputClauses.end()); + d_inputClauses.insert(newId); } if (kind == THEORY_LEMMA) { - Assert (d_lemmaClauses.find(newId) == d_lemmaClauses.end()); - d_lemmaClauses.insert(newId); + Assert(d_lemmaClauses.find(newId) == d_lemmaClauses.end()); + d_lemmaClauses.insert(newId); } - } - Debug("proof:sat:detailed") <<"registerUnitClause " << d_unitId[toInt(lit)] << " " << kind <<"\n"; - return d_unitId[toInt(lit)]; + Debug("proof:sat:detailed") <<"registerUnitClause " << d_unitId[toInt(lit)] << " " << kind <<"\n"; + return d_unitId[toInt(lit)]; } void SatProof::removedDfs(::Minisat::Lit lit, LitSet* removedSet, LitVector& removeStack, LitSet& inClause, LitSet& seen) { // if we already added the literal return if (seen.count(lit)) { - return; + return; } CRef reason_ref = d_solver->reason(var(lit)); if (reason_ref == CRef_Undef) { seen.insert(lit); - removeStack.push_back(lit); - return; + removeStack.push_back(lit); + return; } - Assert (reason_ref != CRef_Undef); - int size = d_solver->ca[reason_ref].size(); + int size = getClause(reason_ref).size(); for (int i = 1; i < size; i++ ) { - Lit v = d_solver->ca[reason_ref][i]; + Lit v = getClause(reason_ref)[i]; if(inClause.count(v) == 0 && seen.count(v) == 0) { removedDfs(v, removedSet, removeStack, inClause, seen); } } if(seen.count(lit) == 0) { - seen.insert(lit); + seen.insert(lit); removeStack.push_back(lit); } } @@ -427,39 +437,41 @@ void SatProof::removeRedundantFromRes(ResChain* res, ClauseId id) { } LitSet inClause; - createLitSet(id, inClause); - + createLitSet(id, inClause); + LitVector removeStack; - LitSet seen; + LitSet seen; for (LitSet::iterator it = removed->begin(); it != removed->end(); ++it) { - removedDfs(*it, removed, removeStack, inClause, seen); + removedDfs(*it, removed, removeStack, inClause, seen); } - + for (int i = removeStack.size()-1; i >= 0; --i) { Lit lit = removeStack[i]; CRef reason_ref = d_solver->reason(var(lit)); - ClauseId reason_id; + ClauseId reason_id; if (reason_ref == CRef_Undef) { Assert(isUnit(~lit)); - reason_id = getUnitId(~lit); + reason_id = getUnitId(~lit); } else { reason_id = registerClause(reason_ref); } res->addStep(lit, reason_id, !sign(lit)); } - removed->clear(); + removed->clear(); } void SatProof::registerResolution(ClauseId id, ResChain* res) { Assert(res != NULL); removeRedundantFromRes(res, id); - Assert(res->redundantRemoved()); + Assert(res->redundantRemoved()); d_resChains[id] = res; - printRes(id); - if (d_checkRes) { + if(Debug.isOn("proof:sat")) { + printRes(id); + } + if(d_checkRes) { Assert(checkResolution(id)); } } @@ -468,48 +480,46 @@ void SatProof::registerResolution(ClauseId id, ResChain* res) { /// recording resolutions void SatProof::startResChain(::Minisat::CRef start) { - ClauseId id = getClauseId(start); + ClauseId id = getClauseId(start); ResChain* res = new ResChain(id); - d_resStack.push_back(res); + d_resStack.push_back(res); } void SatProof::addResolutionStep(::Minisat::Lit lit, ::Minisat::CRef clause, bool sign) { ClauseId id = registerClause(clause); ResChain* res = d_resStack.back(); - res->addStep(lit, id, sign); + res->addStep(lit, id, sign); } void SatProof::endResChain(CRef clause) { Assert(d_resStack.size() > 0); - ClauseId id = registerClause(clause); + ClauseId id = registerClause(clause); ResChain* res = d_resStack.back(); registerResolution(id, res); - d_resStack.pop_back(); + d_resStack.pop_back(); } void SatProof::endResChain(::Minisat::Lit lit) { Assert(d_resStack.size() > 0); - ClauseId id = registerUnitClause(lit); + ClauseId id = registerUnitClause(lit); ResChain* res = d_resStack.back(); - - registerResolution(id, res); - d_resStack.pop_back(); + d_resStack.pop_back(); } void SatProof::storeLitRedundant(::Minisat::Lit lit) { Assert(d_resStack.size() > 0); ResChain* res = d_resStack.back(); - res->addRedundantLit(lit); + res->addRedundantLit(lit); } -/// constructing resolutions +/// constructing resolutions void SatProof::resolveOutUnit(::Minisat::Lit lit) { ClauseId id = resolveUnit(~lit); ResChain* res = d_resStack.back(); - res->addStep(lit, id, !sign(lit)); + res->addStep(lit, id, !sign(lit)); } void SatProof::storeUnitResolution(::Minisat::Lit lit) { @@ -520,28 +530,30 @@ ClauseId SatProof::resolveUnit(::Minisat::Lit lit) { // first check if we already have a resolution for lit if(isUnit(lit)) { ClauseId id = getClauseId(lit); - if(hasResolution(id) || isInputClause(id)) { - return id; - } - Assert (false); + Assert(hasResolution(id) || isInputClause(id) || isLemmaClause(id)); + return id; } CRef reason_ref = d_solver->reason(var(lit)); - Assert (reason_ref != CRef_Undef); - - ClauseId reason_id = registerClause(reason_ref); - - ResChain* res = new ResChain(reason_id); - Clause& reason = d_solver->ca[reason_ref]; - for (int i = 0; i < reason.size(); i++) { - Lit l = reason[i]; - if(lit != l) { + Assert(reason_ref != CRef_Undef); + + ClauseId reason_id = registerClause(reason_ref); + + ResChain* res = new ResChain(reason_id); + // Here, the call to resolveUnit() can reallocate memory in the + // clause allocator. So reload reason ptr each time. + Clause* reason = &getClause(reason_ref); + for (int i = 0; + i < reason->size(); + i++, reason = &getClause(reason_ref)) { + Lit l = (*reason)[i]; + if(lit != l) { ClauseId res_id = resolveUnit(~l); res->addStep(l, res_id, !sign(l)); } } - ClauseId unit_id = registerUnitClause(lit); + ClauseId unit_id = registerUnitClause(lit); registerResolution(unit_id, res); - return unit_id; + return unit_id; } void SatProof::toStream(std::ostream& out) { @@ -549,50 +561,62 @@ void SatProof::toStream(std::ostream& out) { Unimplemented("native proof printing not supported yet"); } -void SatProof::storeUnitConflict(::Minisat::Lit conflict_lit) { - Assert (!d_storedUnitConflict); - d_unitConflictId = registerUnitClause(conflict_lit); +void SatProof::storeUnitConflict(::Minisat::Lit conflict_lit, ClauseKind kind) { + Assert(!d_storedUnitConflict); + d_unitConflictId = registerUnitClause(conflict_lit, kind); d_storedUnitConflict = true; - Debug("proof:sat:detailed") <<"storeUnitConflict " << d_unitConflictId << "\n"; + Debug("proof:sat:detailed") <<"storeUnitConflict " << d_unitConflictId << "\n"; } void SatProof::finalizeProof(::Minisat::CRef conflict_ref) { Assert(d_resStack.size() == 0); - Assert (conflict_ref != ::Minisat::CRef_Undef); - ClauseId conflict_id; + Assert(conflict_ref != ::Minisat::CRef_Undef); + ClauseId conflict_id; if (conflict_ref == ::Minisat::CRef_Lazy) { - Assert (d_storedUnitConflict); - conflict_id = d_unitConflictId; + Assert(d_storedUnitConflict); + conflict_id = d_unitConflictId; + + ResChain* res = new ResChain(conflict_id); + Lit lit = d_idUnit[conflict_id]; + ClauseId res_id = resolveUnit(~lit); + res->addStep(lit, res_id, !sign(lit)); + + registerResolution(d_emptyClauseId, res); + + return; } else { - Assert (!d_storedUnitConflict); + Assert(!d_storedUnitConflict); conflict_id = registerClause(conflict_ref); //FIXME } - Debug("proof:sat") << "proof::finalizeProof Final Conflict "; - print(conflict_id); - + if(Debug.isOn("proof:sat")) { + Debug("proof:sat") << "proof::finalizeProof Final Conflict "; + print(conflict_id); + } + ResChain* res = new ResChain(conflict_id); - Clause& conflict = d_solver->ca[conflict_ref] ; - for (int i = 0; i < conflict.size(); ++i) { - Lit lit = conflict[i]; + // Here, the call to resolveUnit() can reallocate memory in the + // clause allocator. So reload conflict ptr each time. + Clause* conflict = &getClause(conflict_ref); + for (int i = 0; + i < conflict->size(); + ++i, conflict = &getClause(conflict_ref)) { + Lit lit = (*conflict)[i]; ClauseId res_id = resolveUnit(~lit); - res->addStep(lit, res_id, !sign(lit)); + res->addStep(lit, res_id, !sign(lit)); } registerResolution(d_emptyClauseId, res); - // // FIXME: massive hack - // Proof* proof = ProofManager::getProof(); - // proof->toStream(std::cout); } /// CRef manager void SatProof::updateCRef(::Minisat::CRef oldref, ::Minisat::CRef newref) { if (d_clauseId.find(oldref) == d_clauseId.end()) { - return; + return; } ClauseId id = getClauseId(oldref); - Assert (d_temp_clauseId.find(newref) == d_temp_clauseId.end()); - Assert (d_temp_idClause.find(id) == d_temp_idClause.end()); + Assert(d_temp_clauseId.find(newref) == d_temp_clauseId.end()); + Assert(d_temp_idClause.find(id) == d_temp_idClause.end()); d_temp_clauseId[newref] = id; d_temp_idClause[id] = newref; } @@ -602,39 +626,39 @@ void SatProof::finishUpdateCRef() { d_temp_clauseId.clear(); d_idClause.swap(d_temp_idClause); - d_temp_idClause.clear(); + d_temp_idClause.clear(); } void SatProof::markDeleted(CRef clause) { if (d_clauseId.find(clause) != d_clauseId.end()) { ClauseId id = getClauseId(clause); - Assert (d_deleted.find(id) == d_deleted.end()); + Assert(d_deleted.find(id) == d_deleted.end()); d_deleted.insert(id); if (isLemmaClause(id)) { const Clause& minisat_cl = getClause(clause); - SatClause* sat_cl = new SatClause(); - MinisatSatSolver::toSatClause(minisat_cl, *sat_cl); - d_deletedTheoryLemmas.insert(std::make_pair(id, sat_cl)); + SatClause* sat_cl = new SatClause(); + MinisatSatSolver::toSatClause(minisat_cl, *sat_cl); + d_deletedTheoryLemmas.insert(std::make_pair(id, sat_cl)); } } } void SatProof::constructProof() { - collectClauses(d_emptyClauseId); + collectClauses(d_emptyClauseId); } std::string SatProof::clauseName(ClauseId id) { ostringstream os; if (isInputClause(id)) { - os << ProofManager::getInputClauseName(id); - return os.str(); - } else + os << ProofManager::getInputClauseName(id); + return os.str(); + } else if (isLemmaClause(id)) { - os << ProofManager::getLemmaClauseName(id); - return os.str(); + os << ProofManager::getLemmaClauseName(id); + return os.str(); }else { os << ProofManager::getLearntClauseName(id); - return os.str(); + return os.str(); } } @@ -643,58 +667,56 @@ void SatProof::addToProofManager(ClauseId id, ClauseKind kind) { Minisat::Lit lit = getUnit(id); prop::SatLiteral sat_lit = MinisatSatSolver::toSatLiteral(lit); prop::SatClause* clause = new SatClause(); - clause->push_back(sat_lit); - ProofManager::currentPM()->addClause(id, clause, kind); - return; + clause->push_back(sat_lit); + ProofManager::currentPM()->addClause(id, clause, kind); + return; } - + if (isDeleted(id)) { - Assert (kind == THEORY_LEMMA); + Assert(kind == THEORY_LEMMA); SatClause* clause = d_deletedTheoryLemmas.find(id)->second; - ProofManager::currentPM()->addClause(id, clause, kind); - return; + ProofManager::currentPM()->addClause(id, clause, kind); + return; } - + CRef ref = getClauseRef(id); const Clause& minisat_cl = getClause(ref); SatClause* clause = new SatClause(); - MinisatSatSolver::toSatClause(minisat_cl, *clause); - ProofManager::currentPM()->addClause(id, clause, kind); + MinisatSatSolver::toSatClause(minisat_cl, *clause); + ProofManager::currentPM()->addClause(id, clause, kind); } void SatProof::collectClauses(ClauseId id) { if (d_seenLearnt.find(id) != d_seenLearnt.end()) { - return; + return; } if (d_seenInput.find(id) != d_seenInput.end()) { - return; + return; } if (d_seenLemmas.find(id) != d_seenLemmas.end()) { - return; + return; } if (isInputClause(id)) { - addToProofManager(id, INPUT); + addToProofManager(id, INPUT); d_seenInput.insert(id); - return; - } - else if (isLemmaClause(id)) { - addToProofManager(id, THEORY_LEMMA); + return; + } else if (isLemmaClause(id)) { + addToProofManager(id, THEORY_LEMMA); d_seenLemmas.insert(id); - return; - } - else { - d_seenLearnt.insert(id); + return; + } else { + d_seenLearnt.insert(id); } - Assert (d_resChains.find(id) != d_resChains.end()); + Assert(d_resChains.find(id) != d_resChains.end()); ResChain* res = d_resChains[id]; ClauseId start = res->getStart(); collectClauses(start); - ResSteps steps = res->getSteps(); - for(unsigned i = 0; i < steps.size(); i++) { - collectClauses(steps[i].id); + ResSteps steps = res->getSteps(); + for(size_t i = 0; i < steps.size(); i++) { + collectClauses(steps[i].id); } } @@ -703,29 +725,29 @@ void SatProof::collectClauses(ClauseId id) { void LFSCSatProof::printResolution(ClauseId id, std::ostream& out, std::ostream& paren) { out << "(satlem_simplify _ _ _ "; - ResChain* res = d_resChains[id]; + ResChain* res = d_resChains[id]; ResSteps& steps = res->getSteps(); - + for (int i = steps.size()-1; i >= 0; i--) { out << "("; out << (steps[i].sign? "R" : "Q") << " _ _ "; - + } - + ClauseId start_id = res->getStart(); // WHY DID WE NEED THIS? // if(isInputClause(start_id)) { - // d_seenInput.insert(start_id); + // d_seenInput.insert(start_id); // } out << clauseName(start_id) << " "; - + for(unsigned i = 0; i < steps.size(); i++) { - out << clauseName(steps[i].id) << " "<<ProofManager::getVarName(MinisatSatSolver::toSatVariable(var(steps[i].lit))) <<")"; + out << clauseName(steps[i].id) << " "<<ProofManager::getVarName(MinisatSatSolver::toSatVariable(var(steps[i].lit))) <<")"; } - + if (id == d_emptyClauseId) { out <<"(\\empty empty)"; - return; + return; } out << "(\\" << clauseName(id) << "\n"; // bind to lemma name diff --git a/src/proof/sat_proof.h b/src/proof/sat_proof.h index a4178f518..d555ca529 100644 --- a/src/proof/sat_proof.h +++ b/src/proof/sat_proof.h @@ -9,7 +9,7 @@ ** See the file COPYING in the top-level source directory for licensing ** information.\endverbatim ** - ** \brief Resolution proof + ** \brief Resolution proof ** ** Resolution proof **/ @@ -43,11 +43,11 @@ namespace std { namespace CVC4 { -/** +/** * Helper debugging functions */ void printDebug(::Minisat::Lit l); -void printDebug(::Minisat::Clause& c); +void printDebug(::Minisat::Clause& c); struct ResStep { ::Minisat::Lit lit; @@ -60,8 +60,8 @@ struct ResStep { {} };/* struct ResStep */ -typedef std::vector< ResStep > ResSteps; -typedef std::set < ::Minisat::Lit> LitSet; +typedef std::vector< ResStep > ResSteps; +typedef std::set < ::Minisat::Lit> LitSet; class ResChain { private: @@ -72,7 +72,7 @@ public: ResChain(ClauseId start); void addStep(::Minisat::Lit, ClauseId, bool); bool redundantRemoved() { return (d_redundantLits == NULL || d_redundantLits->empty()); } - void addRedundantLit(::Minisat::Lit lit); + void addRedundantLit(::Minisat::Lit lit); ~ResChain(); // accessor methods ClauseId getStart() { return d_start; } @@ -83,16 +83,16 @@ public: typedef std::hash_map < ClauseId, ::Minisat::CRef > IdCRefMap; typedef std::hash_map < ::Minisat::CRef, ClauseId > ClauseIdMap; typedef std::hash_map < ClauseId, ::Minisat::Lit> IdUnitMap; -typedef std::hash_map < int, ClauseId> UnitIdMap; //FIXME -typedef std::hash_map < ClauseId, ResChain*> IdResMap; +typedef std::hash_map < int, ClauseId> UnitIdMap; //FIXME +typedef std::hash_map < ClauseId, ResChain*> IdResMap; typedef std::hash_set < ClauseId > IdHashSet; -typedef std::vector < ResChain* > ResStack; -typedef std::hash_map <ClauseId, prop::SatClause* > IdToSatClause; -typedef std::set < ClauseId > IdSet; -typedef std::vector < ::Minisat::Lit > LitVector; +typedef std::vector < ResChain* > ResStack; +typedef std::hash_map <ClauseId, prop::SatClause* > IdToSatClause; +typedef std::set < ClauseId > IdSet; +typedef std::vector < ::Minisat::Lit > LitVector; typedef __gnu_cxx::hash_map<ClauseId, ::Minisat::Clause& > IdToMinisatClause; -class SatProof; +class SatProof; class ProofProxy : public ProofProxyAbstract { private: @@ -103,31 +103,31 @@ public: };/* class ProofProxy */ -class CnfProof; +class CnfProof; class SatProof { protected: ::Minisat::Solver* d_solver; - // clauses + // clauses IdCRefMap d_idClause; ClauseIdMap d_clauseId; IdUnitMap d_idUnit; UnitIdMap d_unitId; IdHashSet d_deleted; - IdToSatClause d_deletedTheoryLemmas; - IdHashSet d_inputClauses; - IdHashSet d_lemmaClauses; - // resolutions + IdToSatClause d_deletedTheoryLemmas; + IdHashSet d_inputClauses; + IdHashSet d_lemmaClauses; + // resolutions IdResMap d_resChains; - ResStack d_resStack; + ResStack d_resStack; bool d_checkRes; - - static ClauseId d_idCounter; + + static ClauseId d_idCounter; const ClauseId d_emptyClauseId; const ClauseId d_nullId; - // proxy class to break circular dependencies + // proxy class to break circular dependencies ProofProxy* d_proxy; - + // temporary map for updating CRefs ClauseIdMap d_temp_clauseId; IdCRefMap d_temp_idClause; @@ -135,43 +135,43 @@ protected: // unit conflict ClauseId d_unitConflictId; bool d_storedUnitConflict; -public: +public: SatProof(::Minisat::Solver* solver, bool checkRes = false); virtual ~SatProof() {} protected: - void print(ClauseId id); + void print(ClauseId id); void printRes(ClauseId id); - void printRes(ResChain* res); - + void printRes(ResChain* res); + bool isInputClause(ClauseId id); bool isLemmaClause(ClauseId id); bool isUnit(ClauseId id); - bool isUnit(::Minisat::Lit lit); - bool hasResolution(ClauseId id); - void createLitSet(ClauseId id, LitSet& set); + bool isUnit(::Minisat::Lit lit); + bool hasResolution(ClauseId id); + void createLitSet(ClauseId id, LitSet& set); void registerResolution(ClauseId id, ResChain* res); - + ClauseId getClauseId(::Minisat::CRef clause); - ClauseId getClauseId(::Minisat::Lit lit); + ClauseId getClauseId(::Minisat::Lit lit); ::Minisat::CRef getClauseRef(ClauseId id); ::Minisat::Lit getUnit(ClauseId id); - ClauseId getUnitId(::Minisat::Lit lit); + ClauseId getUnitId(::Minisat::Lit lit); ::Minisat::Clause& getClause(::Minisat::CRef ref); virtual void toStream(std::ostream& out); bool checkResolution(ClauseId id); - /** + /** * Constructs a resolution tree that proves lit * and returns the ClauseId for the unit clause lit * @param lit the literal we are proving - * - * @return + * + * @return */ ClauseId resolveUnit(::Minisat::Lit lit); - /** + /** * Does a depth first search on removed literals and adds the literals - * to be removed in the proper order to the stack. - * + * to be removed in the proper order to the stack. + * * @param lit the literal we are recursing on * @param removedSet the previously computed set of redundant literals * @param removeStack the stack of literals in reverse order of resolution @@ -181,71 +181,71 @@ protected: public: void startResChain(::Minisat::CRef start); void addResolutionStep(::Minisat::Lit lit, ::Minisat::CRef clause, bool sign); - /** + /** * Pops the current resolution of the stack and stores it * in the resolution map. Also registers the 'clause' parameter - * @param clause the clause the resolution is proving + * @param clause the clause the resolution is proving */ void endResChain(::Minisat::CRef clause); void endResChain(::Minisat::Lit lit); - /** - * Stores in the current derivation the redundant literals that were - * eliminated from the conflict clause during conflict clause minimization. - * @param lit the eliminated literal + /** + * Stores in the current derivation the redundant literals that were + * eliminated from the conflict clause during conflict clause minimization. + * @param lit the eliminated literal */ void storeLitRedundant(::Minisat::Lit lit); /// update the CRef Id maps when Minisat does memory reallocation x void updateCRef(::Minisat::CRef old_ref, ::Minisat::CRef new_ref); void finishUpdateCRef(); - - /** + + /** * Constructs the empty clause resolution from the final conflict - * - * @param conflict + * + * @param conflict */ void finalizeProof(::Minisat::CRef conflict); - /// clause registration methods + /// clause registration methods ClauseId registerClause(const ::Minisat::CRef clause, ClauseKind kind = LEARNT); ClauseId registerUnitClause(const ::Minisat::Lit lit, ClauseKind kind = LEARNT); - void storeUnitConflict(::Minisat::Lit lit); - - /** + void storeUnitConflict(::Minisat::Lit lit, ClauseKind kind = LEARNT); + + /** * Marks the deleted clauses as deleted. Note we may still use them in the final - * resolution. - * @param clause + * resolution. + * @param clause */ void markDeleted(::Minisat::CRef clause); bool isDeleted(ClauseId id) { return d_deleted.find(id) != d_deleted.end(); } - /** + /** * Constructs the resolution of ~q and resolves it with the current * resolution thus eliminating q from the current clause * @param q the literal to be resolved out */ void resolveOutUnit(::Minisat::Lit q); - /** + /** * Constructs the resolution of the literal lit. Called when a clause - * containing lit becomes satisfied and is removed. - * @param lit + * containing lit becomes satisfied and is removed. + * @param lit */ - void storeUnitResolution(::Minisat::Lit lit); - + void storeUnitResolution(::Minisat::Lit lit); + ProofProxy* getProxy() {return d_proxy; } /** - Constructs the SAT proof identifying the needed lemmas + Constructs the SAT proof identifying the needed lemmas */ void constructProof(); - + protected: IdSet d_seenLearnt; IdHashSet d_seenInput; - IdHashSet d_seenLemmas; - + IdHashSet d_seenLemmas; + inline std::string varName(::Minisat::Lit lit); - inline std::string clauseName(ClauseId id); + inline std::string clauseName(ClauseId id); void collectClauses(ClauseId id); void addToProofManager(ClauseId id, ClauseKind kind); @@ -253,7 +253,7 @@ public: virtual void printResolutions(std::ostream& out, std::ostream& paren) = 0; };/* class SatProof */ -class LFSCSatProof: public SatProof { +class LFSCSatProof : public SatProof { private: void printResolution(ClauseId id, std::ostream& out, std::ostream& paren); public: diff --git a/src/proof/theory_proof.cpp b/src/proof/theory_proof.cpp index 696bd8309..4ed00aaaa 100644 --- a/src/proof/theory_proof.cpp +++ b/src/proof/theory_proof.cpp @@ -26,9 +26,10 @@ TheoryProof::TheoryProof() {} void TheoryProof::addDeclaration(Expr term) { - if (d_declarationCache.count(term)) + if (d_declarationCache.count(term)) { return; - + } + Type type = term.getType(); if (type.isSort()) d_sortDeclarations.insert(type); @@ -36,32 +37,14 @@ void TheoryProof::addDeclaration(Expr term) { Expr function = term.getOperator(); d_termDeclarations.insert(function); } else if (term.isVariable()) { - Assert (type.isSort()); + //Assert (type.isSort() || type.isBoolean()); d_termDeclarations.insert(term); } // recursively declare all other terms for (unsigned i = 0; i < term.getNumChildren(); ++i) { - addDeclaration(term[i]); - } - d_declarationCache.insert(term); -} - -void LFSCTheoryProof::printTerm(Expr term, std::ostream& os) { - if (term.isVariable()) { - os << term; - return; - } - - Assert (term.getKind() == kind::APPLY_UF); - Expr func = term.getOperator(); - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - os<< "(apply _ _ "; - } - os << func << " "; - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - printTerm(term[i], os); - os << ")"; + addDeclaration(term[i]); } + d_declarationCache.insert(term); } std::string toLFSCKind(Kind kind) { @@ -70,71 +53,138 @@ std::string toLFSCKind(Kind kind) { case kind::AND: return "and"; case kind::XOR: return "xor"; case kind::EQUAL: return "="; + case kind::IFF: return "iff"; case kind::IMPLIES: return "impl"; case kind::NOT: return "not"; default: - Unreachable(); + Unreachable(); } } -void LFSCTheoryProof::printFormula(Expr atom, std::ostream& os) { - // should make this more general and overall sane - Assert (atom.getType().isBoolean() && "Only printing booleans." ); - Kind kind = atom.getKind(); - // this is the only predicate we have - if (kind == kind::EQUAL) { - os << "("; - os <<"= "; - os << atom[0].getType() <<" "; - printTerm(atom[0], os); - os <<" "; - printTerm(atom[1], os); - os <<")"; - } else if ( kind == kind::DISTINCT) { - os <<"(not (= "; - os << atom[0].getType() <<" "; - printTerm(atom[0], os); - os <<" "; - printTerm(atom[1], os); - os <<"))"; - } else if ( kind == kind::OR || - kind == kind::AND || - kind == kind::XOR || - kind == kind::IMPLIES || - kind == kind::NOT) { - // print the boolean operators +void LFSCTheoryProof::printTerm(Expr term, std::ostream& os) { + if (term.isVariable()) { + if(term.getType().isBoolean()) { + os << "(p_app " << term << ")"; + } else { + os << term; + } + return; + } + + switch(Kind k = term.getKind()) { + case kind::APPLY_UF: { + if(term.getType().isBoolean()) { + os << "(p_app "; + } + Expr func = term.getOperator(); + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + os << "(apply _ _ "; + } + os << func << " "; + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + printTerm(term[i], os); + os << ")"; + } + if(term.getType().isBoolean()) { + os << ")"; + } + return; + } + + case kind::ITE: + os << (term.getType().isBoolean() ? "(ifte " : "(ite _ "); + printTerm(term[0], os); + os << " "; + printTerm(term[1], os); + os << " "; + printTerm(term[2], os); + os << ")"; + return; + + case kind::EQUAL: os << "("; - os << toLFSCKind(kind); - if (atom.getNumChildren() > 2) { + os << "= "; + os << term[0].getType() << " "; + printTerm(term[0], os); + os << " "; + printTerm(term[1], os); + os << ")"; + return; + + case kind::DISTINCT: + os << "(not (= "; + os << term[0].getType() << " "; + printTerm(term[0], os); + os << " "; + printTerm(term[1], os); + os << "))"; + return; + + case kind::OR: + case kind::AND: + case kind::XOR: + case kind::IFF: + case kind::IMPLIES: + case kind::NOT: + // print the Boolean operators + os << "(" << toLFSCKind(k); + if(term.getNumChildren() > 2) { + // LFSC doesn't allow declarations with variable numbers of + // arguments, so we have to flatten these N-ary versions. std::ostringstream paren; os << " "; - for (unsigned i =0; i < atom.getNumChildren(); ++i) { - printFormula(atom[i], os); + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + printTerm(term[i], os); os << " "; - if (i < atom.getNumChildren() - 2) { - os << "("<< toLFSCKind(kind) << " "; - paren << ")"; + if(i < term.getNumChildren() - 2) { + os << "(" << toLFSCKind(k) << " "; + paren << ")"; } } - os << paren.str() <<")"; + os << paren.str() << ")"; } else { - // this is for binary and unary operators - for (unsigned i = 0; i < atom.getNumChildren(); ++i) { - os <<" "; - printFormula(atom[i], os); + // this is for binary and unary operators + for (unsigned i = 0; i < term.getNumChildren(); ++i) { + os << " "; + printTerm(term[i], os); + } + os << ")"; + } + return; + + case kind::CONST_BOOLEAN: + os << (term.getConst<bool>() ? "true" : "false"); + return; + + case kind::CHAIN: { + // LFSC doesn't allow declarations with variable numbers of + // arguments, so we have to flatten chained operators, like =. + Kind op = term.getOperator().getConst<Chain>().getOperator(); + size_t n = term.getNumChildren(); + std::ostringstream paren; + for(size_t i = 1; i < n; ++i) { + if(i + 1 < n) { + os << "(" << toLFSCKind(kind::AND) << " "; + paren << ")"; + } + os << "(" << toLFSCKind(op) << " "; + printTerm(term[i - 1], os); + os << " "; + printTerm(term[i], os); + os << ")"; + if(i + 1 < n) { + os << " "; } - os <<")"; } - } else if (kind == kind::CONST_BOOLEAN) { - if (atom.getConst<bool>()) - os << "true"; - else - os << "false"; + os << paren.str(); + return; } - else { - std::cout << kind << "\n"; - Assert (false && "Unsupported kind"); + + default: + Unhandled(k); } + + Unreachable(); } void LFSCTheoryProof::printAssertions(std::ostream& os, std::ostream& paren) { @@ -142,56 +192,57 @@ void LFSCTheoryProof::printAssertions(std::ostream& os, std::ostream& paren) { ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); - // collect declarations first + // collect declarations first for(; it != end; ++it) { - addDeclaration(*it); + addDeclaration(*it); } printDeclarations(os, paren); it = ProofManager::currentPM()->begin_assertions(); for (; it != end; ++it) { os << "(% A" << counter++ << " (th_holds "; - printFormula(*it, os); + printTerm(*it, os); os << ")\n"; - paren <<")"; + paren << ")"; } } void LFSCTheoryProof::printDeclarations(std::ostream& os, std::ostream& paren) { // declaring the sorts for (SortSet::const_iterator it = d_sortDeclarations.begin(); it != d_sortDeclarations.end(); ++it) { - os << "(% " << *it << " sort \n"; - paren << ")"; + os << "(% " << *it << " sort\n"; + paren << ")"; } // declaring the terms for (ExprSet::const_iterator it = d_termDeclarations.begin(); it != d_termDeclarations.end(); ++it) { Expr term = *it; - os << "(% " << term << " (term "; - paren <<")"; + os << "(% " << term << " "; + os << "(term "; Type type = term.getType(); if (type.isFunction()) { - std::ostringstream fparen; + std::ostringstream fparen; FunctionType ftype = (FunctionType)type; std::vector<Type> args = ftype.getArgTypes(); - args.push_back(ftype.getRangeType()); - os << "(arrow "; + args.push_back(ftype.getRangeType()); + os << "(arrow"; for (unsigned i = 0; i < args.size(); i++) { Type arg_type = args[i]; - Assert (arg_type.isSort()); - os << arg_type << " "; + //Assert (arg_type.isSort() || arg_type.isBoolean()); + os << " " << arg_type; if (i < args.size() - 2) { - os << "(arrow "; - fparen <<")"; + os << " (arrow"; + fparen << ")"; } } - os << fparen.str() << "))\n"; + os << fparen.str() << "))\n"; } else { Assert (term.isVariable()); - Assert (type.isSort()); + //Assert (type.isSort() || type.isBoolean()); os << type << ")\n"; } + paren << ")"; } -} +} diff --git a/src/proof/theory_proof.h b/src/proof/theory_proof.h index 457023a59..0a7772a4b 100644 --- a/src/proof/theory_proof.h +++ b/src/proof/theory_proof.h @@ -13,7 +13,7 @@ ** ** A manager for UfProofs. ** - ** + ** **/ @@ -24,33 +24,32 @@ #include "util/proof.h" #include "expr/expr.h" #include <ext/hash_set> -#include <iostream> +#include <iostream> namespace CVC4 { - typedef __gnu_cxx::hash_set<Type, TypeHashFunction > SortSet; - typedef __gnu_cxx::hash_set<Expr, ExprHashFunction > ExprSet; + typedef __gnu_cxx::hash_set<Type, TypeHashFunction > SortSet; + typedef __gnu_cxx::hash_set<Expr, ExprHashFunction > ExprSet; class TheoryProof { protected: ExprSet d_termDeclarations; - SortSet d_sortDeclarations; + SortSet d_sortDeclarations; ExprSet d_declarationCache; - - void addDeclaration(Expr atom); + public: TheoryProof(); virtual ~TheoryProof() {} virtual void printAssertions(std::ostream& os, std::ostream& paren) = 0; + void addDeclaration(Expr atom); }; - class LFSCTheoryProof: public TheoryProof { - static void printTerm(Expr term, std::ostream& os); + class LFSCTheoryProof : public TheoryProof { void printDeclarations(std::ostream& os, std::ostream& paren); public: - static void printFormula(Expr atom, std::ostream& os); + static void printTerm(Expr term, std::ostream& os); virtual void printAssertions(std::ostream& os, std::ostream& paren); - }; + }; } /* CVC4 namespace */ #endif /* __CVC4__THEORY_PROOF_H */ diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index 16fa3ba60..610023b70 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -263,7 +263,7 @@ CRef Solver::reason(Var x) { // Construct the reason CRef real_reason = ca.alloc(explLevel, explanation, true); - PROOF (ProofManager::getSatProof()->registerClause(real_reason, THEORY_LEMMA); ); + PROOF (ProofManager::getSatProof()->registerClause(real_reason, THEORY_LEMMA); ); vardata[x] = VarData(real_reason, level(x), user_level(x), intro_level(x), trail_index(x)); clauses_removable.push(real_reason); attachClause(real_reason); @@ -324,7 +324,18 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable) } else { // If all false, we're in conflict if (ps.size() == falseLiteralsCount) { + if(PROOF_ON()) { + // Take care of false units here; otherwise, we need to + // construct the clause below to give to the proof manager + // as the final conflict. + if(falseLiteralsCount == 1) { + PROOF( ProofManager::getSatProof()->storeUnitConflict(ps[0], INPUT); ) + PROOF( ProofManager::getSatProof()->finalizeProof(::Minisat::CRef_Lazy); ) + return ok = false; + } + } else { return ok = false; + } } CRef cr = CRef_Undef; @@ -339,7 +350,13 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable) clauses_persistent.push(cr); attachClause(cr); - PROOF( ProofManager::getSatProof()->registerClause(cr, INPUT); ) + if(PROOF_ON()) { + PROOF( ProofManager::getSatProof()->registerClause(cr, INPUT); ) + if(ps.size() == falseLiteralsCount) { + PROOF( ProofManager::getSatProof()->finalizeProof(cr); ) + return ok = false; + } + } } // Check if it propagates @@ -347,8 +364,17 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable) if(assigns[var(ps[0])] == l_Undef) { assert(assigns[var(ps[0])] != l_False); uncheckedEnqueue(ps[0], cr); - PROOF( if (ps.size() == 1) { ProofManager::getSatProof()->registerUnitClause(ps[0], INPUT); } ) - return ok = (propagate(CHECK_WITHOUT_THEORY) == CRef_Undef); + PROOF( if(ps.size() == 1) { ProofManager::getSatProof()->registerUnitClause(ps[0], INPUT); } ); + CRef confl = propagate(CHECK_WITHOUT_THEORY); + if(! (ok = (confl == CRef_Undef)) ) { + if(ca[confl].size() == 1) { + PROOF( ProofManager::getSatProof()->storeUnitConflict(ca[confl][0], LEARNT); ); + PROOF( ProofManager::getSatProof()->finalizeProof(::Minisat::CRef_Lazy); ) + } else { + PROOF( ProofManager::getSatProof()->finalizeProof(confl); ); + } + } + return ok; } else return ok; } } @@ -370,7 +396,7 @@ void Solver::attachClause(CRef cr) { void Solver::detachClause(CRef cr, bool strict) { const Clause& c = ca[cr]; - PROOF( ProofManager::getSatProof()->markDeleted(cr); ) + PROOF( ProofManager::getSatProof()->markDeleted(cr); ); Debug("minisat") << "Solver::detachClause(" << c << ")" << std::endl; assert(c.size() > 1); @@ -1580,7 +1606,7 @@ CRef Solver::updateLemmas() { vec<Lit>& lemma = lemmas[i]; // If it's an empty lemma, we have a conflict at zero level if (lemma.size() == 0) { - Assert (! PROOF_ON()); + Assert (! PROOF_ON()); conflict = CRef_Lazy; backtrackLevel = 0; Debug("minisat::lemmas") << "Solver::updateLemmas(): found empty clause" << std::endl; @@ -1630,13 +1656,15 @@ CRef Solver::updateLemmas() { } lemma_ref = ca.alloc(clauseLevel, lemma, removable); - PROOF (ProofManager::getSatProof()->registerClause(lemma_ref, THEORY_LEMMA); ); + PROOF( ProofManager::getSatProof()->registerClause(lemma_ref, THEORY_LEMMA); ); if (removable) { clauses_removable.push(lemma_ref); } else { clauses_persistent.push(lemma_ref); } attachClause(lemma_ref); + } else { + PROOF( ProofManager::getSatProof()->registerUnitClause(lemma[0], THEORY_LEMMA); ); } // If the lemma is propagating enqueue its literal (or set the conflict) @@ -1650,7 +1678,7 @@ CRef Solver::updateLemmas() { } else { Debug("minisat::lemmas") << "Solver::updateLemmas(): unit conflict or empty clause" << std::endl; conflict = CRef_Lazy; - PROOF(ProofManager::getSatProof()->storeUnitConflict(lemma[0]);); + PROOF( ProofManager::getSatProof()->storeUnitConflict(lemma[0]); ); } } else { Debug("minisat::lemmas") << "lemma size is " << lemma.size() << std::endl; diff --git a/src/smt/options b/src/smt/options index 05a138f60..b76822caf 100644 --- a/src/smt/options +++ b/src/smt/options @@ -22,12 +22,16 @@ option expandDefinitions expand-definitions bool :default false always expand symbol definitions in output common-option produceModels produce-models -m --produce-models bool :default false :predicate CVC4::smt::beforeSearch :predicate-include "smt/smt_engine.h" support the get-value and get-model commands -option checkModels check-models --check-models bool :predicate CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" +option checkModels check-models --check-models bool :link --produce-models --interactive :link-smt produce-models :link-smt interactive-mode :predicate CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" after SAT/INVALID/UNKNOWN, check that the generated model satisfies user assertions -option dumpModels --dump-models bool :default false +option dumpModels --dump-models bool :default false :link --produce-models output models after every SAT/INVALID/UNKNOWN response option proof produce-proofs --proof bool :default false :predicate CVC4::smt::proofEnabledBuild CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" turn on proof generation +option checkProofs check-proofs --check-proofs bool :link --proof :link-smt produce-proofs :predicate CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" + after UNSAT/VALID, machine-check the generated proof +option dumpProofs --dump-proofs bool :default false :link --proof + output proofs after every UNSAT/VALID response # this is just a placeholder for later; it doesn't show up in command-line options listings undocumented-option unsatCores produce-unsat-cores --produce-unsat-cores bool :predicate CVC4::smt::unsatCoresEnabledBuild CVC4::smt::beforeSearch :predicate-include "smt/options_handlers.h" turn on unsat core generation (NOT YET SUPPORTED) diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 0fadca424..761348890 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -46,8 +46,10 @@ #include "theory/theory_engine.h" #include "theory/bv/theory_bv_rewriter.h" #include "proof/proof_manager.h" +#include "main/options.h" #include "util/proof.h" #include "proof/proof.h" +#include "proof/proof_manager.h" #include "util/boolean_simplification.h" #include "util/node_visitor.h" #include "util/configuration.h" @@ -75,6 +77,7 @@ #include "prop/options.h" #include "theory/arrays/options.h" #include "util/sort_inference.h" +#include "theory/quantifiers/quant_conflict_find.h" #include "theory/quantifiers/macros.h" #include "theory/datatypes/options.h" #include "theory/quantifiers/first_order_reasoning.h" @@ -157,6 +160,8 @@ struct SmtEngineStatistics { IntStat d_numAssertionsPost; /** time spent in checkModel() */ TimerStat d_checkModelTime; + /** time spent in checkProof() */ + TimerStat d_checkProofTime; /** time spent in PropEngine::checkSat() */ TimerStat d_solveTime; /** time spent in pushing/popping */ @@ -183,11 +188,11 @@ struct SmtEngineStatistics { d_numAssertionsPre("smt::SmtEngine::numAssertionsPreITERemoval", 0), d_numAssertionsPost("smt::SmtEngine::numAssertionsPostITERemoval", 0), d_checkModelTime("smt::SmtEngine::checkModelTime"), + d_checkProofTime("smt::SmtEngine::checkProofTime"), d_solveTime("smt::SmtEngine::solveTime"), d_pushPopTime("smt::SmtEngine::pushPopTime"), d_processAssertionsTime("smt::SmtEngine::processAssertionsTime"), d_simplifiedToFalse("smt::SmtEngine::simplifiedToFalse", 0) - { StatisticsRegistry::registerStat(&d_definitionExpansionTime); @@ -301,6 +306,13 @@ class SmtEnginePrivate : public NodeManagerListener { hash_map<Node, Node, NodeHashFunction> d_abstractValues; /** + * Function symbol used to implement uninterpreted undefined string + * semantics. Needed to deal with partial charat/substr function. + */ + Node d_charAtUndef; + Node d_substrUndef; + + /** * Function symbol used to implement uninterpreted division-by-zero * semantics. Needed to deal with partial division function ("/"). */ @@ -428,6 +440,8 @@ public: d_fakeContext(), d_abstractValueMap(&d_fakeContext), d_abstractValues(), + d_charAtUndef(), + d_substrUndef(), d_divByZero(), d_intDivByZero(), d_modZero(), @@ -667,6 +681,7 @@ SmtEngine::SmtEngine(ExprManager* em) throw() : d_decisionEngine(NULL), d_theoryEngine(NULL), d_propEngine(NULL), + d_proofManager(NULL), d_definedFunctions(NULL), d_assertionList(NULL), d_assignments(NULL), @@ -696,6 +711,8 @@ SmtEngine::SmtEngine(ExprManager* em) throw() : d_statisticsRegistry = new StatisticsRegistry(); d_stats = new SmtEngineStatistics(); + PROOF( d_proofManager = new ProofManager(); ); + // We have mutual dependency here, so we add the prop engine to the theory // engine later (it is non-essential there) d_theoryEngine = new TheoryEngine(d_context, d_userContext, d_private->d_iteRemover, const_cast<const LogicInfo&>(d_logic)); @@ -763,7 +780,8 @@ void SmtEngine::finishInit() { if(options::cumulativeMillisecondLimit() != 0) { setTimeLimit(options::cumulativeMillisecondLimit(), true); } - PROOF( ProofManager::currentPM()->setLogic(d_logic.getLogicString()); ); + + PROOF( ProofManager::currentPM()->setLogic(d_logic.getLogicString()); ); } void SmtEngine::finalOptionsAreSet() { @@ -777,16 +795,11 @@ void SmtEngine::finalOptionsAreSet() { } if(options::checkModels()) { - if(! options::produceModels()) { - Notice() << "SmtEngine: turning on produce-models to support check-model" << endl; - setOption("produce-models", SExpr("true")); - } if(! options::interactive()) { - Notice() << "SmtEngine: turning on interactive-mode to support check-model" << endl; + Notice() << "SmtEngine: turning on interactive-mode to support check-models" << endl; setOption("interactive-mode", SExpr("true")); } } - if(options::produceAssignments() && !options::produceModels()) { Notice() << "SmtEngine: turning on produce-models to support produce-assignments" << endl; setOption("produce-models", SExpr("true")); @@ -1139,15 +1152,16 @@ void SmtEngine::setLogicInternal() throw() { options::instWhenMode.set( INST_WHEN_LAST_CALL ); } } - if ( ! options::fmfInstGen.wasSetByUser()) { - //if full model checking is on, disable inst-gen techniques - if( options::fmfFullModelCheck() ){ - options::fmfInstGen.set( false ); + if ( options::fmfBoundInt() ){ + if( options::mbqiMode()!=quantifiers::MBQI_NONE && + options::mbqiMode()!=quantifiers::MBQI_FMC_INTERVAL ){ + //if bounded integers are set, must use full model check for MBQI + options::mbqiMode.set( quantifiers::MBQI_FMC ); } } - if ( options::fmfBoundInt() ){ - //if bounded integers are set, must use full model check for MBQI - options::fmfFullModelCheck.set( true ); + if( options::mbqiMode()==quantifiers::MBQI_INTERVAL ){ + //must do pre-skolemization + options::preSkolemQuant.set( true ); } if( options::ufssSymBreak() ){ options::sortInference.set( true ); @@ -1520,6 +1534,59 @@ Node SmtEnginePrivate::expandDefinitions(TNode n, hash_map<Node, Node, NodeHashF node = expandBVDivByZero(node); break; + case kind::STRING_CHARAT: { + if(d_charAtUndef.isNull()) { + std::vector< TypeNode > argTypes; + argTypes.push_back(NodeManager::currentNM()->stringType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + d_charAtUndef = NodeManager::currentNM()->mkSkolem("charAt_undef", + NodeManager::currentNM()->mkFunctionType( + argTypes, + NodeManager::currentNM()->stringType()), + "partial charat undef", + NodeManager::SKOLEM_EXACT_NAME); + if(!d_smt.d_logic.isTheoryEnabled(THEORY_UF)) { + d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); + d_smt.d_logic.enableTheory(THEORY_UF); + d_smt.d_logic.lock(); + } + } + TNode str = n[0], num = n[1]; + Node lenx = nm->mkNode(kind::STRING_LENGTH, str); + Node cond = nm->mkNode(kind::GT, lenx, num); + Node total = nm->mkNode(kind::STRING_CHARAT_TOTAL, str, num); + Node undef = nm->mkNode(kind::APPLY_UF, d_charAtUndef, str, num); + node = nm->mkNode(kind::ITE, cond, total, undef); + } + break; + case kind::STRING_SUBSTR: { + if(d_substrUndef.isNull()) { + std::vector< TypeNode > argTypes; + argTypes.push_back(NodeManager::currentNM()->stringType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + argTypes.push_back(NodeManager::currentNM()->integerType()); + d_substrUndef = NodeManager::currentNM()->mkSkolem("substr_undef", + NodeManager::currentNM()->mkFunctionType( + argTypes, + NodeManager::currentNM()->stringType()), + "partial substring undef", + NodeManager::SKOLEM_EXACT_NAME); + if(!d_smt.d_logic.isTheoryEnabled(THEORY_UF)) { + d_smt.d_logic = d_smt.d_logic.getUnlockedCopy(); + d_smt.d_logic.enableTheory(THEORY_UF); + d_smt.d_logic.lock(); + } + } + TNode str = n[0]; + Node lenx = nm->mkNode(kind::STRING_LENGTH, str); + Node num = nm->mkNode(kind::PLUS, n[1], n[2]); + Node cond = nm->mkNode(kind::GEQ, lenx, num); + Node total = nm->mkNode(kind::STRING_SUBSTR_TOTAL, str, n[1], n[2]); + Node undef = nm->mkNode(kind::APPLY_UF, d_substrUndef, str, n[1], n[2]); + node = nm->mkNode(kind::ITE, cond, total, undef); + } + break; + case kind::DIVISION: { // partial function: division if(d_divByZero.isNull()) { @@ -2503,7 +2570,7 @@ void SmtEnginePrivate::doMiplibTrick() { const uint64_t mark = (*j).second; const unsigned numVars = pos.getKind() == kind::AND ? pos.getNumChildren() : 1; uint64_t expected = (uint64_t(1) << (1 << numVars)) - 1; - expected = (expected == 0) ? -1 : expected;// fix for overflow + expected = (expected == 0) ? -1 : expected; // fix for overflow Debug("miplib") << "[" << pos << "] => " << hex << mark << " expect " << expected << dec << endl; Assert(pos.getKind() == kind::AND || pos.isVar()); if(mark != expected) { @@ -2511,7 +2578,7 @@ void SmtEnginePrivate::doMiplibTrick() { } else { if(mark != 3) { // exclude single-var case; nothing to check there uint64_t sz = (uint64_t(1) << checks[pos_var].size()) - 1; - sz = (sz == 0) ? -1 : sz;// fix for overflow + sz = (sz == 0) ? -1 : sz; // fix for overflow Assert(sz == mark, "expected size %u == mark %u", sz, mark); for(size_t k = 0; k < checks[pos_var].size(); ++k) { if((k & (k - 1)) != 0) { @@ -2531,12 +2598,12 @@ void SmtEnginePrivate::doMiplibTrick() { break; } } else { - Assert(checks[pos_var][k] == 0, "checks[(%s,%s)][%u] should be 0, but it's %s", pos.toString().c_str(), var.toString().c_str(), k, checks[pos_var][k].toString().c_str());// we never set for single-positive-var + Assert(checks[pos_var][k] == 0, "checks[(%s,%s)][%u] should be 0, but it's %s", pos.toString().c_str(), var.toString().c_str(), k, checks[pos_var][k].toString().c_str()); // we never set for single-positive-var } } } if(!eligible) { - eligible = true;// next is still eligible + eligible = true; // next is still eligible continue; } @@ -2560,7 +2627,7 @@ void SmtEnginePrivate::doMiplibTrick() { Node leq = Rewriter::rewrite(nm->mkNode(kind::LEQ, newVar, one)); d_assertionsToCheck.push_back(Rewriter::rewrite(geq.andNode(leq))); SubstitutionMap nullMap(&d_fakeContext); - Theory::PPAssertStatus status CVC4_UNUSED;// just for assertions + Theory::PPAssertStatus status CVC4_UNUSED; // just for assertions status = d_smt.d_theoryEngine->solve(geq, nullMap); Assert(status == Theory::PP_ASSERT_STATUS_UNSOLVED, "unexpected solution from arith's ppAssert()"); @@ -3042,35 +3109,35 @@ void SmtEnginePrivate::processAssertions() { d_assertionsToPreprocess[i] = Rewriter::rewrite( d_assertionsToPreprocess[i] ); } } - - dumpAssertions("pre-skolem-quant", d_assertionsToPreprocess); - if( options::preSkolemQuant() ){ - //apply pre-skolemization to existential quantifiers - for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { - Node prev = d_assertionsToPreprocess[i]; - vector< Node > fvs; - d_assertionsToPreprocess[i] = Rewriter::rewrite( preSkolemizeQuantifiers( d_assertionsToPreprocess[i], true, fvs ) ); - if( prev!=d_assertionsToPreprocess[i] ){ - Trace("quantifiers-rewrite") << "*** Pre-skolemize " << prev << endl; - Trace("quantifiers-rewrite") << " ...got " << d_assertionsToPreprocess[i] << endl; + if( d_smt.d_logic.isQuantified() ){ + dumpAssertions("pre-skolem-quant", d_assertionsToPreprocess); + if( options::preSkolemQuant() ){ + //apply pre-skolemization to existential quantifiers + for (unsigned i = 0; i < d_assertionsToPreprocess.size(); ++ i) { + Node prev = d_assertionsToPreprocess[i]; + vector< Node > fvs; + d_assertionsToPreprocess[i] = Rewriter::rewrite( preSkolemizeQuantifiers( d_assertionsToPreprocess[i], true, fvs ) ); + if( prev!=d_assertionsToPreprocess[i] ){ + Trace("quantifiers-rewrite") << "*** Pre-skolemize " << prev << endl; + Trace("quantifiers-rewrite") << " ...got " << d_assertionsToPreprocess[i] << endl; + } } } - } - dumpAssertions("post-skolem-quant", d_assertionsToPreprocess); - - if( options::macrosQuant() ){ - //quantifiers macro expansion - bool success; - do{ - QuantifierMacros qm; - success = qm.simplify( d_assertionsToPreprocess, true ); - }while( success ); - } + dumpAssertions("post-skolem-quant", d_assertionsToPreprocess); + if( options::macrosQuant() ){ + //quantifiers macro expansion + bool success; + do{ + QuantifierMacros qm; + success = qm.simplify( d_assertionsToPreprocess, true ); + }while( success ); + } - Trace("fo-rsn-enable") << std::endl; - if( options::foPropQuant() ){ - FirstOrderPropagation fop; - fop.simplify( d_assertionsToPreprocess ); + Trace("fo-rsn-enable") << std::endl; + if( options::foPropQuant() ){ + FirstOrderPropagation fop; + fop.simplify( d_assertionsToPreprocess ); + } } if( options::sortInference() ){ @@ -3078,6 +3145,10 @@ void SmtEnginePrivate::processAssertions() { d_smt.d_theoryEngine->getSortInference()->simplify( d_assertionsToPreprocess ); } + //if( options::quantConflictFind() ){ + // d_smt.d_theoryEngine->getQuantConflictFind()->registerAssertions( d_assertionsToPreprocess ); + //} + dumpAssertions("pre-simplify", d_assertionsToPreprocess); Chat() << "simplifying assertions..." << endl; bool noConflict = simplifyAssertions(); @@ -3291,9 +3362,6 @@ Result SmtEngine::checkSat(const Expr& ex) throw(TypeCheckingException, ModalExc finalOptionsAreSet(); doPendingPops(); - - PROOF( ProofManager::currentPM()->addAssertion(ex); ); - Trace("smt") << "SmtEngine::checkSat(" << ex << ")" << endl; if(d_queryMade && !options::incrementalSolving()) { @@ -3308,6 +3376,8 @@ Result SmtEngine::checkSat(const Expr& ex) throw(TypeCheckingException, ModalExc e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); // Ensure expr is type-checked at this point. ensureBoolean(e); + // Give it to proof manager + PROOF( ProofManager::currentPM()->addAssertion(e); ); } // check to see if a postsolve() is pending @@ -3358,6 +3428,13 @@ Result SmtEngine::checkSat(const Expr& ex) throw(TypeCheckingException, ModalExc checkModel(/* hard failure iff */ ! r.isUnknown()); } } + // Check that UNSAT results generate a proof correctly. + if(options::checkProofs()) { + if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { + TimerStat::CodeTimer checkProofTimer(d_stats->d_checkProofTime); + checkProof(); + } + } return r; }/* SmtEngine::checkSat() */ @@ -3378,9 +3455,10 @@ Result SmtEngine::query(const Expr& ex) throw(TypeCheckingException, ModalExcept // Substitute out any abstract values in ex Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); - // Ensure that the expression is type-checked at this point, and Boolean ensureBoolean(e); + // Give it to proof manager + PROOF( ProofManager::currentPM()->addAssertion(e.notExpr()); ); // check to see if a postsolve() is pending if(d_needPostsolve) { @@ -3428,6 +3506,13 @@ Result SmtEngine::query(const Expr& ex) throw(TypeCheckingException, ModalExcept checkModel(/* hard failure iff */ ! r.isUnknown()); } } + // Check that UNSAT results generate a proof correctly. + if(options::checkProofs()) { + if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { + TimerStat::CodeTimer checkProofTimer(d_stats->d_checkProofTime); + checkProof(); + } + } return r; }/* SmtEngine::query() */ @@ -3437,7 +3522,9 @@ Result SmtEngine::assertFormula(const Expr& ex) throw(TypeCheckingException, Log SmtScope smts(this); finalOptionsAreSet(); doPendingPops(); - PROOF( ProofManager::currentPM()->addAssertion(ex);); + + PROOF( ProofManager::currentPM()->addAssertion(ex); ); + Trace("smt") << "SmtEngine::assertFormula(" << ex << ")" << endl; // Substitute out any abstract values in ex @@ -3474,7 +3561,7 @@ Expr SmtEngine::simplify(const Expr& ex) throw(TypeCheckingException, LogicExcep Expr e = d_private->substituteAbstractValues(Node::fromExpr(ex)).toExpr(); if( options::typeChecking() ) { - e.getType(true);// ensure expr is type-checked at this point + e.getType(true); // ensure expr is type-checked at this point } // Make sure all preprocessing is done diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index 9655297b3..8e400468c 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -56,6 +56,8 @@ class SmtEngine; class DecisionEngine; class TheoryEngine; +class ProofManager; + class Model; class StatisticsRegistry; @@ -83,6 +85,7 @@ namespace smt { class BooleanTermConverter; void beforeSearch(std::string, bool, SmtEngine*) throw(ModalException); + ProofManager* currentProofManager(); struct CommandCleanup; typedef context::CDList<Command*, CommandCleanup> CommandList; @@ -135,8 +138,11 @@ class CVC4_PUBLIC SmtEngine { TheoryEngine* d_theoryEngine; /** The propositional engine */ prop::PropEngine* d_propEngine; + /** The proof manager */ + ProofManager* d_proofManager; /** An index of our defined functions */ DefinedFunctionMap* d_definedFunctions; + /** * The assertion list (before any conversion) for supporting * getAssertions(). Only maintained if in interactive mode. @@ -249,6 +255,11 @@ class CVC4_PUBLIC SmtEngine { smt::SmtEnginePrivate* d_private; /** + * Check that a generated Proof (via getProof()) checks. + */ + void checkProof(); + + /** * Check that a generated Model (via getModel()) actually satisfies * all user assertions. */ @@ -322,6 +333,7 @@ class CVC4_PUBLIC SmtEngine { friend class ::CVC4::smt::BooleanTermConverter; friend ::CVC4::StatisticsRegistry* ::CVC4::stats::getStatisticsRegistry(SmtEngine*); friend void ::CVC4::smt::beforeSearch(std::string, bool, SmtEngine*) throw(ModalException); + friend ProofManager* ::CVC4::smt::currentProofManager(); // to access d_modelCommands friend class ::CVC4::Model; friend class ::CVC4::theory::TheoryModel; diff --git a/src/smt/smt_engine.i b/src/smt/smt_engine.i index ff4105241..00c332bd1 100644 --- a/src/smt/smt_engine.i +++ b/src/smt/smt_engine.i @@ -43,8 +43,8 @@ SWIGEXPORT void JNICALL Java_edu_nyu_acsys_CVC4_SmtEngine_dlRef(JNIEnv* jenv, jc } %ignore CVC4::SmtEngine::setLogic(const char*); -%ignore CVC4::SmtEngine::getProof; %ignore CVC4::stats::getStatisticsRegistry(SmtEngine*); %ignore CVC4::smt::beforeSearch(std::string, bool, SmtEngine*); +%ignore CVC4::smt::currentProofManager(); %include "smt/smt_engine.h" diff --git a/src/smt/smt_engine_check_proof.cpp b/src/smt/smt_engine_check_proof.cpp new file mode 100644 index 000000000..a731ff024 --- /dev/null +++ b/src/smt/smt_engine_check_proof.cpp @@ -0,0 +1,95 @@ +/********************* */ +/*! \file smt_engine_check_proof.cpp + ** \verbatim + ** Original author: Morgan Deters + ** Major contributors: none + ** Minor contributors (to current version): none + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2013 New York University and The University of Iowa + ** 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 "smt/smt_engine.h" +#include "util/statistics_registry.h" +#include "check.h" + +#include <cstdlib> +#include <cstring> +#include <fstream> +#include <string> +#include <unistd.h> + +using namespace CVC4; +using namespace std; + +namespace CVC4 { + +namespace proof { + extern const char *const plf_signatures; +}/* CVC4::proof namespace */ + +namespace smt { + +class UnlinkProofFile { + string d_filename; +public: + UnlinkProofFile(const char* filename) : d_filename(filename) {} + ~UnlinkProofFile() { unlink(d_filename.c_str()); } +};/* class UnlinkProofFile */ + +}/* CVC4::smt namespace */ + +}/* CVC4 namespace */ + +void SmtEngine::checkProof() { + +#ifdef CVC4_PROOF + + Chat() << "generating proof..." << endl; + + Proof* pf = getProof(); + + Chat() << "checking proof..." << endl; + + if(!d_logic.isPure(theory::THEORY_BOOL) && + !d_logic.isPure(theory::THEORY_UF)) { + // no checking for these yet + Notice() << "Notice: no proof-checking for non-UF proofs yet" << endl; + return; + } + + char* pfFile = strdup("/tmp/cvc4_proof.XXXXXX"); + int fd = mkstemp(pfFile); + + // ensure this temp file is removed after + smt::UnlinkProofFile unlinker(pfFile); + + ofstream pfStream(pfFile); + pfStream << proof::plf_signatures << endl; + pf->toStream(pfStream); + pfStream.close(); + args a; + a.show_runs = false; + a.no_tail_calls = false; + a.compile_scc = false; + a.compile_scc_debug = false; + a.run_scc = false; + a.use_nested_app = false; + a.compile_lib = false; + init(); + check_file(pfFile, args()); + close(fd); + +#else /* CVC4_PROOF */ + + Unreachable("This version of CVC4 was built without proof support; cannot check proofs."); + +#endif /* CVC4_PROOF */ + +} diff --git a/src/smt/smt_engine_scope.h b/src/smt/smt_engine_scope.h index 21644d3f4..2389181b5 100644 --- a/src/smt/smt_engine_scope.h +++ b/src/smt/smt_engine_scope.h @@ -22,10 +22,14 @@ #include "util/cvc4_assert.h" #include "expr/node_manager.h" #include "util/output.h" +#include "proof/proof.h" #pragma once namespace CVC4 { + +class ProofManager; + namespace smt { extern CVC4_THREADLOCAL(SmtEngine*) s_smtEngine_current; @@ -35,6 +39,12 @@ inline SmtEngine* currentSmtEngine() { return s_smtEngine_current; } +inline ProofManager* currentProofManager() { + Assert(PROOF_ON()); + Assert(s_smtEngine_current != NULL); + return s_smtEngine_current->d_proofManager; +} + class SmtScope : public NodeManagerScope { /** The old NodeManager, to be restored on destruction. */ SmtEngine* d_oldSmtEngine; diff --git a/src/theory/quantifiers/bounded_integers.cpp b/src/theory/quantifiers/bounded_integers.cpp index 28485a979..d4faf41fe 100644 --- a/src/theory/quantifiers/bounded_integers.cpp +++ b/src/theory/quantifiers/bounded_integers.cpp @@ -198,10 +198,9 @@ void BoundedIntegers::processLiteral( Node f, Node lit, bool pol, void BoundedIntegers::process( Node f, Node n, bool pol, std::map< int, std::map< Node, Node > >& bound_lit_map, std::map< int, std::map< Node, bool > >& bound_lit_pol_map ){ - if( (( n.getKind()==IMPLIES || n.getKind()==OR) && pol) || (n.getKind()==AND && !pol) ){ + if( (n.getKind()==OR && pol) || (n.getKind()==AND && !pol) ){ for( unsigned i=0; i<n.getNumChildren(); i++) { - bool newPol = n.getKind()==IMPLIES && i==0 ? !pol : pol; - process( f, n[i], newPol, bound_lit_map, bound_lit_pol_map ); + process( f, n[i], pol, bound_lit_map, bound_lit_pol_map ); } }else if( n.getKind()==NOT ){ process( f, n[0], !pol, bound_lit_map, bound_lit_pol_map ); diff --git a/src/theory/quantifiers/first_order_model.cpp b/src/theory/quantifiers/first_order_model.cpp index bda124e96..a05abfa29 100644 --- a/src/theory/quantifiers/first_order_model.cpp +++ b/src/theory/quantifiers/first_order_model.cpp @@ -16,6 +16,8 @@ #include "theory/quantifiers/model_engine.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/full_model_check.h" +#include "theory/quantifiers/qinterval_builder.h" #define USE_INDEX_ORDERING @@ -27,8 +29,9 @@ using namespace CVC4::theory; using namespace CVC4::theory::quantifiers; using namespace CVC4::theory::quantifiers::fmcheck; -FirstOrderModel::FirstOrderModel( context::Context* c, std::string name ) : TheoryModel( c, name, true ), -d_axiom_asserted( c, false ), d_forall_asserts( c ), d_isModelSet( c, false ){ +FirstOrderModel::FirstOrderModel(QuantifiersEngine * qe, context::Context* c, std::string name ) : +TheoryModel( c, name, true ), +d_qe( qe ), d_axiom_asserted( c, false ), d_forall_asserts( c ), d_isModelSet( c, false ){ } @@ -66,16 +69,23 @@ Node FirstOrderModel::getCurrentModelValue( Node n, bool partial ) { } void FirstOrderModel::initialize( bool considerAxioms ) { - processInitialize(); + processInitialize( true ); //this is called after representatives have been chosen and the equality engine has been built //for each quantifier, collect all operators we care about for( int i=0; i<getNumAssertedQuantifiers(); i++ ){ Node f = getAssertedQuantifier( i ); + processInitializeQuantifier( f ); + if( d_quant_var_id.find( f )==d_quant_var_id.end() ){ + for(unsigned i=0; i<f[0].getNumChildren(); i++){ + d_quant_var_id[f][f[0][i]] = i; + } + } if( considerAxioms || !f.hasAttribute(AxiomAttribute()) ){ //initialize relevant models within bodies of all quantifiers initializeModelForTerm( f[1] ); } } + processInitialize( false ); } void FirstOrderModel::initializeModelForTerm( Node n ){ @@ -85,14 +95,30 @@ void FirstOrderModel::initializeModelForTerm( Node n ){ } } -FirstOrderModelIG::FirstOrderModelIG(context::Context* c, std::string name) : FirstOrderModel(c,name) { +Node FirstOrderModel::getSomeDomainElement(TypeNode tn){ + //check if there is even any domain elements at all + if (!d_rep_set.hasType(tn)) { + Trace("fmc-model-debug") << "Must create domain element for " << tn << "..." << std::endl; + Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(tn); + d_rep_set.add(mbt); + }else if( d_rep_set.d_type_reps[tn].size()==0 ){ + Message() << "empty reps" << std::endl; + exit(0); + } + return d_rep_set.d_type_reps[tn][0]; +} + +FirstOrderModelIG::FirstOrderModelIG(QuantifiersEngine * qe, context::Context* c, std::string name) : +FirstOrderModel(qe, c,name) { } -void FirstOrderModelIG::processInitialize(){ - //rebuild models - d_uf_model_tree.clear(); - d_uf_model_gen.clear(); +void FirstOrderModelIG::processInitialize( bool ispre ){ + if( ispre ){ + //rebuild models + d_uf_model_tree.clear(); + d_uf_model_gen.clear(); + } } void FirstOrderModelIG::processInitializeModelForTerm( Node n ){ @@ -143,12 +169,12 @@ void FirstOrderModelIG::resetEvaluate(){ // each n{ri->d_index[0]/x_0...ri->d_index[depIndex]/x_depIndex, */x_(depIndex+1) ... */x_n } is equivalent in the current model int FirstOrderModelIG::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ ++d_eval_formulas; - //Debug("fmf-eval-debug") << "Evaluate " << n << " " << phaseReq << std::endl; + Debug("fmf-eval-debug2") << "Evaluate " << n << std::endl; //Notice() << "Eval " << n << std::endl; if( n.getKind()==NOT ){ int val = evaluate( n[0], depIndex, ri ); return val==1 ? -1 : ( val==-1 ? 1 : 0 ); - }else if( n.getKind()==OR || n.getKind()==AND || n.getKind()==IMPLIES ){ + }else if( n.getKind()==OR || n.getKind()==AND ){ int baseVal = n.getKind()==AND ? 1 : -1; int eVal = baseVal; int posDepIndex = ri->getNumTerms(); @@ -156,7 +182,7 @@ int FirstOrderModelIG::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ //evaluate subterm int childDepIndex; - Node nn = ( i==0 && n.getKind()==IMPLIES ) ? n[i].notNode() : n[i]; + Node nn = n[i]; int eValT = evaluate( nn, childDepIndex, ri ); if( eValT==baseVal ){ if( eVal==baseVal ){ @@ -184,12 +210,12 @@ int FirstOrderModelIG::evaluate( Node n, int& depIndex, RepSetIterator* ri ){ }else{ return 0; } - }else if( n.getKind()==IFF || n.getKind()==XOR ){ + }else if( n.getKind()==IFF ){ int depIndex1; int eVal = evaluate( n[0], depIndex1, ri ); if( eVal!=0 ){ int depIndex2; - int eVal2 = evaluate( n.getKind()==XOR ? n[1].notNode() : n[1], depIndex2, ri ); + int eVal2 = evaluate( n[1], depIndex2, ri ); if( eVal2!=0 ){ depIndex = depIndex1>depIndex2 ? depIndex1 : depIndex2; return eVal==eVal2 ? 1 : -1; @@ -421,7 +447,7 @@ void FirstOrderModelIG::makeEvalUfModel( Node n ){ d_eval_uf_model[n] = uf::UfModelTree( op, d_eval_term_index_order[n] ); d_uf_model_gen[op].makeModel( this, d_eval_uf_model[n] ); //Debug("fmf-index-order") << "Make model for " << n << " : " << std::endl; - //d_eval_uf_model[n].debugPrint( "fmf-index-order", d_qe, 2 ); + //d_eval_uf_model[n].debugPrint( std::cout, d_qe->getModel(), 2 ); } } } @@ -513,10 +539,8 @@ Node FirstOrderModelIG::getCurrentUfModelValue( Node n, std::vector< Node > & ar - - FirstOrderModelFmc::FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name) : -FirstOrderModel(c, name), d_qe(qe){ +FirstOrderModel(qe, c, name){ } @@ -552,19 +576,21 @@ Node FirstOrderModelFmc::getCurrentUfModelValue( Node n, std::vector< Node > & a return d_models[n.getOperator()]->evaluate(this, args); } -void FirstOrderModelFmc::processInitialize() { - if( options::fmfFmcInterval() && intervalOp.isNull() ){ - std::vector< TypeNode > types; - for(unsigned i=0; i<2; i++){ - types.push_back(NodeManager::currentNM()->integerType()); +void FirstOrderModelFmc::processInitialize( bool ispre ) { + if( ispre ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && intervalOp.isNull() ){ + std::vector< TypeNode > types; + for(unsigned i=0; i<2; i++){ + types.push_back(NodeManager::currentNM()->integerType()); + } + TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->integerType() ); + intervalOp = NodeManager::currentNM()->mkSkolem( "interval_$$", typ, "op representing interval" ); } - TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->integerType() ); - intervalOp = NodeManager::currentNM()->mkSkolem( "interval_$$", typ, "op representing interval" ); - } - for( std::map<Node, Def * >::iterator it = d_models.begin(); it != d_models.end(); ++it ){ - it->second->reset(); + for( std::map<Node, Def * >::iterator it = d_models.begin(); it != d_models.end(); ++it ){ + it->second->reset(); + } + d_model_basis_rep.clear(); } - d_model_basis_rep.clear(); } void FirstOrderModelFmc::processInitializeModelForTerm(Node n) { @@ -575,19 +601,6 @@ void FirstOrderModelFmc::processInitializeModelForTerm(Node n) { } } -Node FirstOrderModelFmc::getSomeDomainElement(TypeNode tn){ - //check if there is even any domain elements at all - if (!d_rep_set.hasType(tn)) { - Trace("fmc-model-debug") << "Must create domain element for " << tn << "..." << std::endl; - Node mbt = d_qe->getTermDatabase()->getModelBasisTerm(tn); - d_rep_set.d_type_reps[tn].push_back(mbt); - }else if( d_rep_set.d_type_reps[tn].size()==0 ){ - Message() << "empty reps" << std::endl; - exit(0); - } - return d_rep_set.d_type_reps[tn][0]; -} - bool FirstOrderModelFmc::isStar(Node n) { return n==getStar(n.getType()); @@ -603,7 +616,7 @@ Node FirstOrderModelFmc::getStar(TypeNode tn) { Node FirstOrderModelFmc::getStarElement(TypeNode tn) { Node st = getStar(tn); - if( options::fmfFmcInterval() && tn.isInteger() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && tn.isInteger() ){ st = getInterval( st, st ); } return st; @@ -684,3 +697,163 @@ bool FirstOrderModelFmc::isInRange( Node v, Node i ) { return v==i; } } + + +FirstOrderModelQInt::FirstOrderModelQInt(QuantifiersEngine * qe, context::Context* c, std::string name) : +FirstOrderModel(qe, c, name) { + +} + +void FirstOrderModelQInt::processInitialize( bool ispre ) { + if( !ispre ){ + Trace("qint-debug") << "Process initialize" << std::endl; + for( std::map<Node, QIntDef * >::iterator it = d_models.begin(); it != d_models.end(); ++it ) { + Node op = it->first; + TypeNode tno = op.getType(); + Trace("qint-debug") << " Init " << op << " " << tno << std::endl; + for( unsigned i=0; i<tno.getNumChildren(); i++) { + //make sure a representative of the type exists + if( !d_rep_set.hasType( tno[i] ) ){ + Node e = getSomeDomainElement( tno[i] ); + Trace("qint-debug") << " * Initialize type " << tno[i] << ", add "; + Trace("qint-debug") << e << " " << e.getType() << std::endl; + //d_rep_set.add( e ); + } + } + } + } +} + +Node FirstOrderModelQInt::getFunctionValue(Node op, const char* argPrefix ) { + Trace("qint-debug") << "Get function value for " << op << std::endl; + TypeNode type = op.getType(); + std::vector< Node > vars; + for( size_t i=0; i<type.getNumChildren()-1; i++ ){ + std::stringstream ss; + ss << argPrefix << (i+1); + Node b = NodeManager::currentNM()->mkBoundVar( ss.str(), type[i] ); + vars.push_back( b ); + } + Node boundVarList = NodeManager::currentNM()->mkNode(kind::BOUND_VAR_LIST, vars); + Node curr = d_models[op]->getFunctionValue( this, vars ); + Node fv = NodeManager::currentNM()->mkNode(kind::LAMBDA, boundVarList, curr); + Trace("qint-debug") << "Return " << fv << std::endl; + return fv; +} + +Node FirstOrderModelQInt::getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) { + Debug("qint-debug") << "get curr uf value " << n << std::endl; + return d_models[n]->evaluate( this, args ); +} + +void FirstOrderModelQInt::processInitializeModelForTerm(Node n) { + Debug("qint-debug") << "process init " << n << " " << n.getKind() << std::endl; + + if( n.getKind()==APPLY_UF || n.getKind()==VARIABLE || n.getKind()==SKOLEM ){ + Node op = n.getKind()==APPLY_UF ? n.getOperator() : n; + if( d_models.find(op)==d_models.end()) { + Debug("qint-debug") << "init model for " << op << std::endl; + d_models[op] = new QIntDef; + } + } +} + +Node FirstOrderModelQInt::getUsedRepresentative( Node n ) { + if( hasTerm( n ) ){ + if( n.getType().isBoolean() ){ + return areEqual(n, d_true) ? d_true : d_false; + }else{ + return getRepresentative( n ); + } + }else{ + Trace("qint-debug") << "Get rep " << n << " " << n.getType() << std::endl; + Assert( d_rep_set.hasType( n.getType() ) && !d_rep_set.d_type_reps[n.getType()].empty() ); + return d_rep_set.d_type_reps[n.getType()][0]; + } +} + +void FirstOrderModelQInt::processInitializeQuantifier( Node q ) { + if( d_var_order.find( q )==d_var_order.end() ){ + d_var_order[q] = new QuantVarOrder( q ); + d_var_order[q]->debugPrint("qint-var-order"); + Trace("qint-var-order") << std::endl; + } +} +unsigned FirstOrderModelQInt::getOrderedNumVars( Node q ) { + //return q[0].getNumChildren(); + return d_var_order[q]->getNumVars(); +} + +TypeNode FirstOrderModelQInt::getOrderedVarType( Node q, int i ) { + //return q[0][i].getType(); + return d_var_order[q]->getVar( i ).getType(); +} + +int FirstOrderModelQInt::getOrderedVarNumToVarNum( Node q, int i ) { + return getVariableId( q, d_var_order[q]->getVar( i ) ); +} + +bool FirstOrderModelQInt::isLessThan( Node v1, Node v2 ) { + Assert( !v1.isNull() ); + Assert( !v2.isNull() ); + if( v1.getType().isSort() ){ + Assert( getRepId( v1 )!=-1 ); + Assert( getRepId( v2 )!=-1 ); + int rid1 = d_rep_id[v1]; + int rid2 = d_rep_id[v2]; + return rid1<rid2; + }else{ + return false; + } +} + +Node FirstOrderModelQInt::getMin( Node v1, Node v2 ) { + return isLessThan( v1, v2 ) ? v1 : v2; +} + +Node FirstOrderModelQInt::getMax( Node v1, Node v2 ) { + return isLessThan( v1, v2 ) ? v2 : v1; +} + +Node FirstOrderModelQInt::getMaximum( TypeNode tn ) { + return d_max[tn]; +} + +Node FirstOrderModelQInt::getNext( TypeNode tn, Node v ) { + if( v.isNull() ){ + return d_min[tn]; + }else{ + Assert( getRepId( v )!=-1 ); + int rid = d_rep_id[v]; + if( rid==(int)(d_rep_set.d_type_reps[tn].size()-1) ){ + Assert( false ); + return Node::null(); + }else{ + return d_rep_set.d_type_reps[tn][ rid+1 ]; + } + } +} +Node FirstOrderModelQInt::getPrev( TypeNode tn, Node v ) { + if( v.isNull() ){ + Assert( false ); + return Node::null(); + }else{ + Assert( getRepId( v )!=-1 ); + int rid = d_rep_id[v]; + if( rid==0 ){ + return Node::null(); + }else{ + return d_rep_set.d_type_reps[tn][ rid-1 ]; + } + } +} + +bool FirstOrderModelQInt::doMeet( Node l1, Node u1, Node l2, Node u2, Node& lr, Node& ur ) { + Trace("qint-debug2") << "doMeet " << l1 << "..." << u1 << " with " << l2 << "..." << u2 << std::endl; + Assert( !u1.isNull() ); + Assert( !u2.isNull() ); + lr = l1.isNull() ? l2 : ( l2.isNull() ? l1 : getMax( l1, l2 ) ); + ur = getMin( u1, u2 ); + //return lr==ur || lr.isNull() || isLessThan( lr, ur ); + return lr.isNull() || isLessThan( lr, ur ); +} diff --git a/src/theory/quantifiers/first_order_model.h b/src/theory/quantifiers/first_order_model.h index b5bdff9ee..ab3a1aa52 100644 --- a/src/theory/quantifiers/first_order_model.h +++ b/src/theory/quantifiers/first_order_model.h @@ -33,16 +33,21 @@ class FirstOrderModelIG; namespace fmcheck { class FirstOrderModelFmc; } +class FirstOrderModelQInt; class FirstOrderModel : public TheoryModel { -private: +protected: + /** quant engine */ + QuantifiersEngine * d_qe; /** whether an axiom is asserted */ context::CDO< bool > d_axiom_asserted; /** list of quantifiers asserted in the current context */ context::CDList<Node> d_forall_asserts; /** is model set */ context::CDO< bool > d_isModelSet; + /** get variable id */ + std::map< Node, std::map< Node, int > > d_quant_var_id; /** get current model value */ virtual Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ) = 0; public: //for Theory Quantifiers: @@ -57,20 +62,28 @@ public: //for Theory Quantifiers: /** initialize model for term */ void initializeModelForTerm( Node n ); virtual void processInitializeModelForTerm( Node n ) = 0; + virtual void processInitializeQuantifier( Node q ) {} public: - FirstOrderModel( context::Context* c, std::string name ); + FirstOrderModel(QuantifiersEngine * qe, context::Context* c, std::string name ); virtual ~FirstOrderModel(){} virtual FirstOrderModelIG * asFirstOrderModelIG() { return NULL; } virtual fmcheck::FirstOrderModelFmc * asFirstOrderModelFmc() { return NULL; } + virtual FirstOrderModelQInt * asFirstOrderModelQInt() { return NULL; } // initialize the model void initialize( bool considerAxioms = true ); - virtual void processInitialize() = 0; + virtual void processInitialize( bool ispre ) = 0; /** mark model set */ void markModelSet() { d_isModelSet = true; } /** is model set */ bool isModelSet() { return d_isModelSet; } /** get current model value */ Node getCurrentModelValue( Node n, bool partial = false ); + /** get variable id */ + int getVariableId(Node f, Node n) { + return d_quant_var_id.find( f )!=d_quant_var_id.end() ? d_quant_var_id[f][n] : -1; + } + /** get some domain element */ + Node getSomeDomainElement(TypeNode tn); };/* class FirstOrderModel */ @@ -93,10 +106,10 @@ private: Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ); //the following functions are for evaluating quantifier bodies public: - FirstOrderModelIG(context::Context* c, std::string name); + FirstOrderModelIG(QuantifiersEngine * qe, context::Context* c, std::string name); FirstOrderModelIG * asFirstOrderModelIG() { return this; } // initialize the model - void processInitialize(); + void processInitialize( bool ispre ); //for initialize model void processInitializeModelForTerm( Node n ); /** reset evaluation */ @@ -128,8 +141,6 @@ class FirstOrderModelFmc : public FirstOrderModel { friend class FullModelChecker; private: - /** quant engine */ - QuantifiersEngine * d_qe; /** models for UF */ std::map<Node, Def * > d_models; std::map<TypeNode, Node > d_model_basis_rep; @@ -143,8 +154,7 @@ public: FirstOrderModelFmc(QuantifiersEngine * qe, context::Context* c, std::string name); FirstOrderModelFmc * asFirstOrderModelFmc() { return this; } // initialize the model - void processInitialize(); - + void processInitialize( bool ispre ); Node getFunctionValue(Node op, const char* argPrefix ); bool isStar(Node n); @@ -152,7 +162,6 @@ public: Node getStarElement(TypeNode tn); bool isModelBasisTerm(Node n); Node getModelBasisTerm(TypeNode tn); - Node getSomeDomainElement(TypeNode tn); bool isInterval(Node n); Node getInterval( Node lb, Node ub ); bool isInRange( Node v, Node i ); @@ -161,6 +170,50 @@ public: } +class QIntDef; +class QuantVarOrder; +class FirstOrderModelQInt : public FirstOrderModel +{ + friend class QIntervalBuilder; +private: + /** uf op to some representation */ + std::map<Node, QIntDef * > d_models; + /** representatives to ids */ + std::map< Node, int > d_rep_id; + std::map< TypeNode, Node > d_min; + std::map< TypeNode, Node > d_max; + /** quantifiers to information regarding variable ordering */ + std::map<Node, QuantVarOrder * > d_var_order; + /** get current model value */ + Node getCurrentUfModelValue( Node n, std::vector< Node > & args, bool partial ); + void processInitializeModelForTerm(Node n); +public: + FirstOrderModelQInt(QuantifiersEngine * qe, context::Context* c, std::string name); + FirstOrderModelQInt * asFirstOrderModelQInt() { return this; } + void processInitialize( bool ispre ); + Node getFunctionValue(Node op, const char* argPrefix ); + + Node getUsedRepresentative( Node n ); + int getRepId( Node n ) { return d_rep_id.find( n )==d_rep_id.end() ? -1 : d_rep_id[n]; } + bool isLessThan( Node v1, Node v2 ); + Node getMin( Node v1, Node v2 ); + Node getMax( Node v1, Node v2 ); + Node getMinimum( TypeNode tn ) { return getNext( tn, Node::null() ); } + Node getMaximum( TypeNode tn ); + bool isMinimum( Node n ) { return n==getMinimum( n.getType() ); } + bool isMaximum( Node n ) { return n==getMaximum( n.getType() ); } + Node getNext( TypeNode tn, Node v ); + Node getPrev( TypeNode tn, Node v ); + bool doMeet( Node l1, Node u1, Node l2, Node u2, Node& lr, Node& ur ); + QuantVarOrder * getVarOrder( Node q ) { return d_var_order[q]; } + + void processInitializeQuantifier( Node q ) ; + unsigned getOrderedNumVars( Node q ); + TypeNode getOrderedVarType( Node q, int i ); + int getOrderedVarNumToVarNum( Node q, int i ); +}; + + }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/full_model_check.cpp b/src/theory/quantifiers/full_model_check.cpp index 2f32ec5e6..c7d7b7415 100644 --- a/src/theory/quantifiers/full_model_check.cpp +++ b/src/theory/quantifiers/full_model_check.cpp @@ -60,9 +60,9 @@ bool EntryTrie::hasGeneralization( FirstOrderModelFmc * m, Node c, int index ) { return true; } } - if( !options::fmfFmcInterval() || !c[index].getType().isInteger() ){ + if( options::mbqiMode()!=quantifiers::MBQI_FMC_INTERVAL || !c[index].getType().isInteger() ){ //for star: check if all children are defined and have generalizations - if( options::fmfFmcCoverSimplify() && c[index]==st ){ + if( c[index]==st ){ ///options::fmfFmcCoverSimplify() //check if all children exist and are complete int num_child_def = d_child.size() - (d_child.find(st)!=d_child.end() ? 1 : 0); if( num_child_def==m->d_rep_set.getNumRepresentatives(tn) ){ @@ -92,7 +92,7 @@ int EntryTrie::getGeneralizationIndex( FirstOrderModelFmc * m, std::vector<Node> return d_data; }else{ int minIndex = -1; - if( options::fmfFmcInterval() && inst[index].getType().isInteger() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && inst[index].getType().isInteger() ){ for( std::map<Node,EntryTrie>::iterator it = d_child.begin(); it != d_child.end(); ++it ){ //if( !m->isInterval( it->first ) ){ // std::cout << "Not an interval during getGenIndex " << it->first << std::endl; @@ -327,7 +327,7 @@ QModelBuilder( c, qe ){ bool FullModelChecker::optBuildAtFullModel() { //need to build after full model has taken effect if we are constructing interval models // this is because we need to have a constant in all integer equivalence classes - return options::fmfFmcInterval(); + return options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL; } void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){ @@ -443,7 +443,7 @@ void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){ Trace("fmc-warn") << "Warning : model has non-constant argument in model " << ri << std::endl; } children.push_back(ri); - if( !options::fmfFmcInterval() || !ri.getType().isInteger() ){ + if( options::mbqiMode()!=quantifiers::MBQI_FMC_INTERVAL || !ri.getType().isInteger() ){ if (fm->isModelBasisTerm(ri) ) { ri = fm->getStar( ri.getType() ); }else{ @@ -485,7 +485,7 @@ void FullModelChecker::processBuildModel(TheoryModel* m, bool fullModel){ } - if( options::fmfFmcInterval() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL ){ convertIntervalModel( fm, op ); } @@ -588,7 +588,6 @@ bool FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, std::vector< TypeNode > types; for(unsigned i=0; i<f[0].getNumChildren(); i++){ types.push_back(f[0][i].getType()); - d_quant_var_id[f][f[0][i]] = i; } TypeNode typ = NodeManager::currentNM()->mkFunctionType( types, NodeManager::currentNM()->booleanType() ); Node op = NodeManager::currentNM()->mkSkolem( "fmc_$$", typ, "op created for full-model checking" ); @@ -599,7 +598,7 @@ bool FullModelChecker::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, initializeType( fmfmc, f[0][i].getType() ); } - if( !options::fmfModelBasedInst() ){ + if( options::mbqiMode()==MBQI_NONE ){ //just exhaustive instantiate Node c = mkCondDefault( fmfmc, f ); d_quant_models[f].addEntry( fmfmc, c, d_false ); @@ -958,8 +957,8 @@ void FullModelChecker::doVariableEquality( FirstOrderModelFmc * fm, Node f, Def }else{ TypeNode tn = eq[0].getType(); if( tn.isSort() ){ - int j = getVariableId(f, eq[0]); - int k = getVariableId(f, eq[1]); + int j = fm->getVariableId(f, eq[0]); + int k = fm->getVariableId(f, eq[1]); if( !fm->d_rep_set.hasType( tn ) ){ getSomeDomainElement( fm, tn ); //to verify the type is initialized } @@ -977,7 +976,7 @@ void FullModelChecker::doVariableEquality( FirstOrderModelFmc * fm, Node f, Def } void FullModelChecker::doVariableRelation( FirstOrderModelFmc * fm, Node f, Def & d, Def & dc, Node v) { - int j = getVariableId(f, v); + int j = fm->getVariableId(f, v); for (unsigned i=0; i<dc.d_cond.size(); i++) { Node val = dc.d_value[i]; if( val.isNull() ){ @@ -1074,7 +1073,7 @@ void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, Trace("fmc-uf-process") << "Process " << v << std::endl; bool bind_var = false; if( !v.isNull() && v.getKind()==kind::BOUND_VARIABLE ){ - int j = getVariableId(f, v); + int j = fm->getVariableId(f, v); Trace("fmc-uf-process") << v << " is variable #" << j << std::endl; if (!fm->isStar(cond[j+1]) && !fm->isInterval(cond[j+1])) { v = cond[j+1]; @@ -1084,7 +1083,7 @@ void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, } if (bind_var) { Trace("fmc-uf-process") << "bind variable..." << std::endl; - int j = getVariableId(f, v); + int j = fm->getVariableId(f, v); if( fm->isStar(cond[j+1]) ){ for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { cond[j+1] = it->first; @@ -1104,7 +1103,7 @@ void FullModelChecker::doUninterpretedCompose2( FirstOrderModelFmc * fm, Node f, } }else{ if( !v.isNull() ){ - if( options::fmfFmcInterval() && v.getType().isInteger() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && v.getType().isInteger() ){ for (std::map<Node, EntryTrie>::iterator it = curr.d_child.begin(); it != curr.d_child.end(); ++it) { if( fm->isInRange( v, it->first ) ){ doUninterpretedCompose2(fm, f, entries, index+1, cond, val, it->second); @@ -1166,7 +1165,7 @@ int FullModelChecker::isCompat( FirstOrderModelFmc * fm, std::vector< Node > & c Trace("fmc-debug3") << "isCompat " << c << std::endl; Assert(cond.size()==c.getNumChildren()+1); for (unsigned i=1; i<cond.size(); i++) { - if( options::fmfFmcInterval() && cond[i].getType().isInteger() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && cond[i].getType().isInteger() ){ Node iv = doIntervalMeet( fm, cond[i], c[i-1], false ); if( iv.isNull() ){ return 0; @@ -1185,7 +1184,7 @@ bool FullModelChecker::doMeet( FirstOrderModelFmc * fm, std::vector< Node > & co Assert(cond.size()==c.getNumChildren()+1); for (unsigned i=1; i<cond.size(); i++) { if( cond[i]!=c[i-1] ) { - if( options::fmfFmcInterval() && cond[i].getType().isInteger() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL && cond[i].getType().isInteger() ){ Node iv = doIntervalMeet( fm, cond[i], c[i-1] ); if( !iv.isNull() ){ cond[i] = iv; @@ -1255,7 +1254,7 @@ Node FullModelChecker::mkCondDefault( FirstOrderModelFmc * fm, Node f) { } void FullModelChecker::mkCondDefaultVec( FirstOrderModelFmc * fm, Node f, std::vector< Node > & cond ) { - Trace("fmc-debug") << "Make default vec, intervals = " << options::fmfFmcInterval() << std::endl; + Trace("fmc-debug") << "Make default vec, intervals = " << (options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL) << std::endl; //get function symbol for f cond.push_back(d_quant_cond[f]); for (unsigned i=0; i<f[0].getNumChildren(); i++) { diff --git a/src/theory/quantifiers/full_model_check.h b/src/theory/quantifiers/full_model_check.h index 606392831..db5abb01e 100644 --- a/src/theory/quantifiers/full_model_check.h +++ b/src/theory/quantifiers/full_model_check.h @@ -92,7 +92,6 @@ protected: std::map<Node, Node > d_quant_cond; std::map< TypeNode, Node > d_array_cond; std::map< Node, Node > d_array_term_cond; - std::map<Node, std::map< Node, int > > d_quant_var_id; std::map<Node, std::vector< int > > d_star_insts; void initializeType( FirstOrderModelFmc * fm, TypeNode tn ); Node normalizeArgReps(FirstOrderModelFmc * fm, Node op, Node n); @@ -138,7 +137,6 @@ public: bool optBuildAtFullModel(); - int getVariableId(Node f, Node n) { return d_quant_var_id[f][n]; } void debugPrintCond(const char * tr, Node n, bool dispStar = false); void debugPrint(const char * tr, Node n, bool dispStar = false); diff --git a/src/theory/quantifiers/macros.cpp b/src/theory/quantifiers/macros.cpp index 9cd12fbfb..600f8c0b9 100644 --- a/src/theory/quantifiers/macros.cpp +++ b/src/theory/quantifiers/macros.cpp @@ -233,7 +233,7 @@ bool QuantifierMacros::getSubstitution( std::vector< Node >& v_quant, std::map< void QuantifierMacros::process( Node n, bool pol, std::vector< Node >& args, Node f ){ if( n.getKind()==NOT ){ process( n[0], !pol, args, f ); - }else if( n.getKind()==AND || n.getKind()==OR || n.getKind()==IMPLIES ){ + }else if( n.getKind()==AND || n.getKind()==OR ){ //bool favorPol = (n.getKind()==AND)==pol; //conditional? }else if( n.getKind()==ITE ){ diff --git a/src/theory/quantifiers/model_builder.cpp b/src/theory/quantifiers/model_builder.cpp index ea6f2d775..493d54b53 100644 --- a/src/theory/quantifiers/model_builder.cpp +++ b/src/theory/quantifiers/model_builder.cpp @@ -44,7 +44,7 @@ bool QModelBuilder::isQuantifierActive( Node f ) { bool QModelBuilder::optUseModel() { - return options::fmfModelBasedInst() || options::fmfBoundInt(); + return options::mbqiMode()!=MBQI_NONE || options::fmfBoundInt(); } void QModelBuilder::debugModel( FirstOrderModel* fm ){ @@ -124,6 +124,7 @@ Node QModelBuilderIG::getCurrentUfModelValue( FirstOrderModel* fm, Node n, std:: void QModelBuilderIG::processBuildModel( TheoryModel* m, bool fullModel ) { FirstOrderModel* f = (FirstOrderModel*)m; FirstOrderModelIG* fm = f->asFirstOrderModelIG(); + Trace("model-engine-debug") << "Process build model, fullModel = " << fullModel << " " << optUseModel() << std::endl; if( fullModel ){ Assert( d_curr_model==fm ); //update models @@ -145,7 +146,7 @@ void QModelBuilderIG::processBuildModel( TheoryModel* m, bool fullModel ) { reset( fm ); //only construct first order model if optUseModel() is true if( optUseModel() ){ - Trace("model-engine-debug") << "Initializing quantifiers..." << std::endl; + Trace("model-engine-debug") << "Initializing " << fm->getNumAssertedQuantifiers() << " quantifiers..." << std::endl; //check if any quantifiers are un-initialized for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ Node f = fm->getAssertedQuantifier( i ); @@ -272,17 +273,15 @@ int QModelBuilderIG::initializeQuantifier( Node f, Node fp ){ ++(d_statistics.d_num_quants_init); } //try to add it - if( optInstGen() ){ - Trace("inst-fmf-init") << "Init: try to add match " << d_quant_basis_match[f] << std::endl; - //add model basis instantiation - if( d_qe->addInstantiation( fp, d_quant_basis_match[f], false, false, false ) ){ - d_quant_basis_match_added[f] = true; - return 1; - }else{ - //shouldn't happen usually, but will occur if x != y is a required literal for f. - //Notice() << "No model basis for " << f << std::endl; - d_quant_basis_match_added[f] = false; - } + Trace("inst-fmf-init") << "Init: try to add match " << d_quant_basis_match[f] << std::endl; + //add model basis instantiation + if( d_qe->addInstantiation( fp, d_quant_basis_match[f], false, false, false ) ){ + d_quant_basis_match_added[f] = true; + return 1; + }else{ + //shouldn't happen usually, but will occur if x != y is a required literal for f. + //Notice() << "No model basis for " << f << std::endl; + d_quant_basis_match_added[f] = false; } } return 0; @@ -397,7 +396,6 @@ bool QModelBuilderIG::isTermActive( Node n ){ //do exhaustive instantiation bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, int effort ) { if( optUseModel() ){ - RepSetIterator riter( d_qe, &(d_qe->getModel()->d_rep_set) ); if( riter.setQuantifier( f ) ){ FirstOrderModelIG * fmig = (FirstOrderModelIG*)d_qe->getModel(); @@ -418,6 +416,7 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i //if evaluate(...)==1, then the instantiation is already true in the model // depIndex is the index of the least significant variable that this evaluation relies upon depIndex = riter.getNumTerms()-1; + Debug("fmf-model-eval") << "We will evaluate " << d_qe->getTermDatabase()->getInstConstantBody( f ) << std::endl; eval = fmig->evaluate( d_qe->getTermDatabase()->getInstConstantBody( f ), depIndex, &riter ); if( eval==1 ){ Debug("fmf-model-eval") << " Returned success with depIndex = " << depIndex << std::endl; @@ -456,7 +455,7 @@ bool QModelBuilderIG::doExhaustiveInstantiation( FirstOrderModel * fm, Node f, i d_statistics.d_eval_lits += fmig->d_eval_lits; d_statistics.d_eval_lits_unknown += fmig->d_eval_lits_unknown; } - Trace("inst-fmf-ei") << "Finished: " << std::endl; + Trace("inst-fmf-ei") << "For " << f << ", finished: " << std::endl; Trace("inst-fmf-ei") << " Inst Tried: " << d_triedLemmas << std::endl; Trace("inst-fmf-ei") << " Inst Added: " << d_addedLemmas << std::endl; if( d_addedLemmas>1000 ){ @@ -930,13 +929,13 @@ Node QModelBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polarity, Node ret; if( n.getKind()==NOT ){ ret = getSelectionFormula( fn[0], n[0], !polarity, useOption ); - }else if( n.getKind()==OR || n.getKind()==IMPLIES || n.getKind()==AND ){ + }else if( n.getKind()==OR || n.getKind()==AND ){ //whether we only need to find one or all bool favorPol = ( n.getKind()!=AND && polarity ) || ( n.getKind()==AND && !polarity ); std::vector< Node > children; for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node fnc = ( i==0 && fn.getKind()==IMPLIES ) ? fn[i].negate() : fn[i]; - Node nc = ( i==0 && n.getKind()==IMPLIES ) ? n[i].negate() : n[i]; + Node fnc = fn[i]; + Node nc = n[i]; Node nn = getSelectionFormula( fnc, nc, polarity, useOption ); if( nn.isNull() && !favorPol ){ //cannot make selection formula @@ -994,8 +993,8 @@ Node QModelBuilderInstGen::getSelectionFormula( Node fn, Node n, bool polarity, if( ret.isNull() && !nc[0].isNull() && !nc[1].isNull() ){ ret = mkAndSelectionFormula( nc[0], nc[1] ); } - }else if( n.getKind()==IFF || n.getKind()==XOR ){ - bool opPol = polarity ? n.getKind()==XOR : n.getKind()==IFF; + }else if( n.getKind()==IFF ){ + bool opPol = !polarity; for( int p=0; p<2; p++ ){ Node nn[2]; for( int i=0; i<2; i++ ){ diff --git a/src/theory/quantifiers/model_engine.cpp b/src/theory/quantifiers/model_engine.cpp index 99f5e8df6..9e3e77c8e 100644 --- a/src/theory/quantifiers/model_engine.cpp +++ b/src/theory/quantifiers/model_engine.cpp @@ -21,6 +21,8 @@ #include "theory/quantifiers/first_order_model.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/quantifiers_attributes.h" +#include "theory/quantifiers/full_model_check.h" +#include "theory/quantifiers/qinterval_builder.h" using namespace std; using namespace CVC4; @@ -34,11 +36,18 @@ using namespace CVC4::theory::inst; ModelEngine::ModelEngine( context::Context* c, QuantifiersEngine* qe ) : QuantifiersModule( qe ){ - if( options::fmfFullModelCheck() || options::fmfBoundInt() ){ + Trace("model-engine-debug") << "Initialize model engine, mbqi : " << options::mbqiMode() << " " << options::fmfBoundInt() << std::endl; + if( options::mbqiMode()==MBQI_FMC || options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL || options::fmfBoundInt() ){ + Trace("model-engine-debug") << "...make fmc builder." << std::endl; d_builder = new fmcheck::FullModelChecker( c, qe ); - }else if( options::fmfNewInstGen() ){ + }else if( options::mbqiMode()==MBQI_INTERVAL ){ + Trace("model-engine-debug") << "...make interval builder." << std::endl; + d_builder = new QIntervalBuilder( c, qe ); + }else if( options::mbqiMode()==MBQI_INST_GEN ){ + Trace("model-engine-debug") << "...make inst-gen builder." << std::endl; d_builder = new QModelBuilderInstGen( c, qe ); }else{ + Trace("model-engine-debug") << "...make default model builder." << std::endl; d_builder = new QModelBuilderDefault( c, qe ); } @@ -169,15 +178,17 @@ int ModelEngine::checkModel(){ it != fm->d_rep_set.d_type_reps.end(); ++it ){ if( it->first.isSort() ){ Trace("model-engine") << "Cardinality( " << it->first << " )" << " = " << it->second.size() << std::endl; - Trace("model-engine-debug") << " "; - Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); - for( size_t i=0; i<it->second.size(); i++ ){ - //Trace("model-engine-debug") << it->second[i] << " "; - Node r = d_quantEngine->getEqualityQuery()->getInternalRepresentative( it->second[i], Node::null(), 0 ); - Trace("model-engine-debug") << r << " "; + if( Trace.isOn("model-engine-debug") ){ + Trace("model-engine-debug") << " "; + Node mbt = d_quantEngine->getTermDatabase()->getModelBasisTerm(it->first); + for( size_t i=0; i<it->second.size(); i++ ){ + //Trace("model-engine-debug") << it->second[i] << " "; + Node r = d_quantEngine->getEqualityQuery()->getInternalRepresentative( it->second[i], Node::null(), 0 ); + Trace("model-engine-debug") << r << " "; + } + Trace("model-engine-debug") << std::endl; + Trace("model-engine-debug") << " Model basis term : " << mbt << std::endl; } - Trace("model-engine-debug") << std::endl; - Trace("model-engine-debug") << " Model basis term : " << mbt << std::endl; } } } @@ -203,7 +214,7 @@ int ModelEngine::checkModel(){ } Trace("model-engine-debug") << "Do exhaustive instantiation..." << std::endl; - int e_max = options::fmfFullModelCheck() && options::fmfModelBasedInst() ? 2 : 1; + int e_max = options::mbqiMode()==MBQI_FMC || options::mbqiMode()==MBQI_FMC_INTERVAL ? 2 : 1; for( int e=0; e<e_max; e++) { if (d_addedLemmas==0) { for( int i=0; i<fm->getNumAssertedQuantifiers(); i++ ){ @@ -221,6 +232,8 @@ int ModelEngine::checkModel(){ if( optOneQuantPerRound() && d_addedLemmas>0 ){ break; } + }else{ + Trace("inst-fmf-ei") << "-> Inactive : " << f << std::endl; } } } @@ -229,7 +242,6 @@ int ModelEngine::checkModel(){ Trace("model-engine-debug") << "Instantiate axioms : " << ( d_builder->d_considerAxioms ? "yes" : "no" ) << std::endl; Trace("model-engine") << "Added Lemmas = " << d_addedLemmas << " / " << d_triedLemmas << " / "; Trace("model-engine") << d_totalLemmas << std::endl; - d_statistics.d_exh_inst_lemmas += d_addedLemmas; return d_addedLemmas; } @@ -239,11 +251,13 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ d_builder->d_addedLemmas = 0; d_builder->d_incomplete_check = false; if( d_builder->doExhaustiveInstantiation( d_quantEngine->getModel(), f, effort ) ){ + Trace("inst-fmf-ei") << "-> Builder determined instantiation(s)." << std::endl; d_triedLemmas += d_builder->d_triedLemmas; d_addedLemmas += d_builder->d_addedLemmas; d_incomplete_check = d_incomplete_check || d_builder->d_incomplete_check; + d_statistics.d_mbqi_inst_lemmas += d_builder->d_addedLemmas; }else{ - Trace("inst-fmf-ei") << "Exhaustive instantiate " << f << ", effort = " << effort << "..." << std::endl; + Trace("inst-fmf-ei") << "-> Exhaustive instantiate " << f << ", effort = " << effort << "..." << std::endl; Debug("inst-fmf-ei") << " Instantiation Constants: "; for( size_t i=0; i<f[0].getNumChildren(); i++ ){ Debug("inst-fmf-ei") << d_quantEngine->getTermDatabase()->getInstantiationConstant( f, i ) << " "; @@ -273,6 +287,7 @@ void ModelEngine::exhaustiveInstantiate( Node f, int effort ){ } d_addedLemmas += addedLemmas; d_triedLemmas += triedLemmas; + d_statistics.d_exh_inst_lemmas += addedLemmas; } //if the iterator is incomplete, we will return unknown instead of sat if no instantiations are added this round d_incomplete_check = d_incomplete_check || riter.d_incomplete; @@ -296,15 +311,18 @@ void ModelEngine::debugPrint( const char* c ){ ModelEngine::Statistics::Statistics(): d_inst_rounds("ModelEngine::Inst_Rounds", 0), - d_exh_inst_lemmas("ModelEngine::Exhaustive_Instantiation_Lemmas", 0 ) + d_exh_inst_lemmas("ModelEngine::Instantiations_Exhaustive", 0 ), + d_mbqi_inst_lemmas("ModelEngine::Instantiations_Mbqi", 0 ) { StatisticsRegistry::registerStat(&d_inst_rounds); StatisticsRegistry::registerStat(&d_exh_inst_lemmas); + StatisticsRegistry::registerStat(&d_mbqi_inst_lemmas); } ModelEngine::Statistics::~Statistics(){ StatisticsRegistry::unregisterStat(&d_inst_rounds); StatisticsRegistry::unregisterStat(&d_exh_inst_lemmas); + StatisticsRegistry::unregisterStat(&d_mbqi_inst_lemmas); } diff --git a/src/theory/quantifiers/model_engine.h b/src/theory/quantifiers/model_engine.h index 0c3c74b5f..ba54d7ba4 100644 --- a/src/theory/quantifiers/model_engine.h +++ b/src/theory/quantifiers/model_engine.h @@ -20,7 +20,6 @@ #include "theory/quantifiers_engine.h" #include "theory/quantifiers/model_builder.h" #include "theory/theory_model.h" -#include "theory/quantifiers/full_model_check.h" #include "theory/quantifiers/relevant_domain.h" namespace CVC4 { @@ -69,6 +68,7 @@ public: public: IntStat d_inst_rounds; IntStat d_exh_inst_lemmas; + IntStat d_mbqi_inst_lemmas; Statistics(); ~Statistics(); }; diff --git a/src/theory/quantifiers/modes.cpp b/src/theory/quantifiers/modes.cpp index 7da3b150f..10185914e 100644 --- a/src/theory/quantifiers/modes.cpp +++ b/src/theory/quantifiers/modes.cpp @@ -77,5 +77,28 @@ std::ostream& operator<<(std::ostream& out, theory::quantifiers::AxiomInstMode m return out; } +std::ostream& operator<<(std::ostream& out, theory::quantifiers::MbqiMode mode) { + switch(mode) { + case theory::quantifiers::MBQI_DEFAULT: + out << "MBQI_DEFAULT"; + break; + case theory::quantifiers::MBQI_NONE: + out << "MBQI_NONE"; + break; + case theory::quantifiers::MBQI_INST_GEN: + out << "MBQI_INST_GEN"; + break; + case theory::quantifiers::MBQI_FMC: + out << "MBQI_FMC"; + break; + case theory::quantifiers::MBQI_INTERVAL: + out << "MBQI_INTERVAL"; + break; + default: + out << "MbqiMode!UNKNOWN"; + } + return out; +} + }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/modes.h b/src/theory/quantifiers/modes.h index edf9c78fe..7a7ce9b54 100644 --- a/src/theory/quantifiers/modes.h +++ b/src/theory/quantifiers/modes.h @@ -55,6 +55,29 @@ typedef enum { AXIOM_INST_MODE_PRIORITY, } AxiomInstMode; +typedef enum { + /** default, mbqi from CADE 24 paper */ + MBQI_DEFAULT, + /** no mbqi */ + MBQI_NONE, + /** implementation that mimics inst-gen */ + MBQI_INST_GEN, + /** mbqi from Section 5.4.2 of AJR thesis */ + MBQI_FMC, + /** mbqi with integer intervals */ + MBQI_FMC_INTERVAL, + /** mbqi with interval abstraction of uninterpreted sorts */ + MBQI_INTERVAL, +} MbqiMode; + +typedef enum { + /** default, apply at full effort */ + QCF_WHEN_MODE_DEFAULT, + /** apply at standard effort */ + QCF_WHEN_MODE_STD, + /** default */ + QCF_WHEN_MODE_STD_H, +} QcfWhenMode; }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ diff --git a/src/theory/quantifiers/options b/src/theory/quantifiers/options index 1eb98e7b7..dc016be3f 100644 --- a/src/theory/quantifiers/options +++ b/src/theory/quantifiers/options @@ -40,7 +40,7 @@ option clauseSplit --clause-split bool :default false # Whether to pre-skolemize quantifier bodies. # For example, forall x. ( P( x ) => (exists y. f( y ) = x) ) will be rewritten to # forall x. P( x ) => f( S( x ) ) = x -option preSkolemQuant --pre-skolem-quant bool :default false +option preSkolemQuant --pre-skolem-quant bool :read-write :default false apply skolemization eagerly to bodies of quantified formulas option iteRemoveQuant --ite-remove-quant bool :default false apply ite removal to bodies of quantifiers @@ -76,9 +76,8 @@ option eagerInstQuant --eager-inst-quant bool :default false option literalMatchMode --literal-matching=MODE CVC4::theory::quantifiers::LiteralMatchMode :default CVC4::theory::quantifiers::LITERAL_MATCH_NONE :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToLiteralMatchMode :handler-include "theory/quantifiers/options_handlers.h" :predicate CVC4::theory::quantifiers::checkLiteralMatchMode :predicate-include "theory/quantifiers/options_handlers.h" choose literal matching mode -option cbqi --enable-cbqi/--disable-cbqi bool :default false - turns on counterexample-based quantifier instantiation [off by default] -/turns off counterexample-based quantifier instantiation +option cbqi --enable-cbqi bool :default false + turns on counterexample-based quantifier instantiation option recurseCbqi --cbqi-recurse bool :default false turns on recursive counterexample-based quantifier instantiation @@ -95,41 +94,35 @@ option internalReps /--disable-quant-internal-reps bool :default true option finiteModelFind --finite-model-find bool :default false use finite model finding heuristic for quantifier instantiation -option fmfModelBasedInst /--disable-fmf-mbqi bool :default true - disable model-based quantifier instantiation for finite model finding +option mbqiMode --mbqi=MODE CVC4::theory::quantifiers::MbqiMode :read-write :default CVC4::theory::quantifiers::MBQI_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToMbqiMode :handler-include "theory/quantifiers/options_handlers.h" :predicate CVC4::theory::quantifiers::checkMbqiMode :predicate-include "theory/quantifiers/options_handlers.h" + choose mode for model-based quantifier instantiation +option fmfOneInstPerRound --mbqi-one-inst-per-round bool :default false + only add one instantiation per quantifier per round for mbqi +option fmfOneQuantPerRound --mbqi-one-quant-per-round bool :default false + only add instantiations for one quantifier per round for mbqi -option fmfFullModelCheck --fmf-fmc bool :default false :read-write - enable full model check for finite model finding -option fmfFmcSimple /--disable-fmf-fmc-simple bool :default true - disable simple models in full model check for finite model finding -option fmfFmcCoverSimplify /--disable-fmf-fmc-cover-simplify bool :default true - disable covering simplification of fmc models -option fmfFmcInterval --fmf-fmc-interval bool :default false - construct interval models for fmc models - -option fmfOneInstPerRound --fmf-one-inst-per-round bool :default false - only add one instantiation per quantifier per round for fmf -option fmfOneQuantPerRound --fmf-one-quant-per-round bool :default false - only add instantiations for one quantifier per round for fmf option fmfInstEngine --fmf-inst-engine bool :default false use instantiation engine in conjunction with finite model finding option fmfRelevantDomain --fmf-relevant-domain bool :default false use relevant domain computation, similar to complete instantiation (Ge, deMoura 09) -option fmfNewInstGen --fmf-new-inst-gen bool :default false - use new inst gen technique for answering sat without exhaustive instantiation -option fmfInstGen --fmf-inst-gen/--disable-fmf-inst-gen bool :read-write :default true - enable Inst-Gen instantiation techniques for finite model finding (default) -/disable Inst-Gen instantiation techniques for finite model finding +option fmfInstGen /--disable-fmf-inst-gen bool :default true + disable Inst-Gen instantiation techniques for finite model finding option fmfInstGenOneQuantPerRound --fmf-inst-gen-one-quant-per-round bool :default false only perform Inst-Gen instantiation techniques on one quantifier per round option fmfFreshDistConst --fmf-fresh-dc bool :default false use fresh distinguished representative when applying Inst-Gen techniques - +option fmfFmcSimple /--disable-fmf-fmc-simple bool :default true + disable simple models in full model check for finite model finding option fmfBoundInt --fmf-bound-int bool :default false finite model finding on bounded integer quantification option axiomInstMode --axiom-inst=MODE CVC4::theory::quantifiers::AxiomInstMode :default CVC4::theory::quantifiers::AXIOM_INST_MODE_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToAxiomInstMode :handler-include "theory/quantifiers/options_handlers.h" policy for instantiating axioms +option quantConflictFind --quant-cf bool :default false + enable conflict find mechanism for quantifiers +option qcfWhenMode --quant-cf-when=MODE CVC4::theory::quantifiers::QcfWhenMode :default CVC4::theory::quantifiers::QCF_WHEN_MODE_DEFAULT :include "theory/quantifiers/modes.h" :handler CVC4::theory::quantifiers::stringToQcfWhenMode :handler-include "theory/quantifiers/options_handlers.h" + when to invoke conflict find mechanism for quantifiers + endmodule diff --git a/src/theory/quantifiers/options_handlers.h b/src/theory/quantifiers/options_handlers.h index 410578af0..e0b1e30e8 100644 --- a/src/theory/quantifiers/options_handlers.h +++ b/src/theory/quantifiers/options_handlers.h @@ -73,6 +73,44 @@ priority \n\ \n\ "; +static const std::string mbqiModeHelp = "\ +Model-based quantifier instantiation modes currently supported by the --mbqi option:\n\ +\n\ +default \n\ ++ Default, use model-based quantifier instantiation algorithm from CADE 24 finite\n\ + model finding paper.\n\ +\n\ +none \n\ ++ Disable model-based quantifier instantiation.\n\ +\n\ +instgen \n\ ++ Use instantiation algorithm that mimics Inst-Gen calculus. \n\ +\n\ +fmc \n\ ++ Use algorithm from Section 5.4.2 of thesis Finite Model Finding in Satisfiability \n\ + Modulo Theories.\n\ +\n\ +fmc-interval \n\ ++ Same as fmc, but with intervals for models of integer functions.\n\ +\n\ +interval \n\ ++ Use algorithm that abstracts domain elements as intervals. \n\ +\n\ +"; +static const std::string qcfWhenModeHelp = "\ +Quantifier conflict find modes currently supported by the --quant-cf-when option:\n\ +\n\ +default \n\ ++ Default, apply conflict finding at full effort.\n\ +\n\ +std \n\ ++ Apply conflict finding at standard effort.\n\ +\n\ +std-h \n\ ++ Apply conflict finding at standard effort when heuristic says to. \n\ +\n\ +"; + inline InstWhenMode stringToInstWhenMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { if(optarg == "pre-full") { return INST_WHEN_PRE_FULL; @@ -135,6 +173,48 @@ inline AxiomInstMode stringToAxiomInstMode(std::string option, std::string optar } } +inline MbqiMode stringToMbqiMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + if(optarg == "default") { + return MBQI_DEFAULT; + } else if(optarg == "none") { + return MBQI_NONE; + } else if(optarg == "instgen") { + return MBQI_INST_GEN; + } else if(optarg == "fmc") { + return MBQI_FMC; + } else if(optarg == "fmc-interval") { + return MBQI_FMC_INTERVAL; + } else if(optarg == "interval") { + return MBQI_INTERVAL; + } else if(optarg == "help") { + puts(mbqiModeHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --mbqi: `") + + optarg + "'. Try --mbqi help."); + } +} + +inline void checkMbqiMode(std::string option, MbqiMode mode, SmtEngine* smt) throw(OptionException) { + +} + +inline QcfWhenMode stringToQcfWhenMode(std::string option, std::string optarg, SmtEngine* smt) throw(OptionException) { + if(optarg == "default") { + return QCF_WHEN_MODE_DEFAULT; + } else if(optarg == "std") { + return QCF_WHEN_MODE_STD; + } else if(optarg == "std-h") { + return QCF_WHEN_MODE_STD_H; + } else if(optarg == "help") { + puts(qcfWhenModeHelp.c_str()); + exit(1); + } else { + throw OptionException(std::string("unknown option for --quant-cf-when: `") + + optarg + "'. Try --quant-cf-when help."); + } +} + }/* CVC4::theory::quantifiers namespace */ }/* CVC4::theory namespace */ }/* CVC4 namespace */ diff --git a/src/theory/quantifiers/qinterval_builder.cpp b/src/theory/quantifiers/qinterval_builder.cpp new file mode 100755 index 000000000..ce85cecc0 --- /dev/null +++ b/src/theory/quantifiers/qinterval_builder.cpp @@ -0,0 +1,1111 @@ +/********************* */
+/*! \file qinterval_builder.cpp
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief Implementation of qinterval builder
+ **/
+
+
+#include "theory/quantifiers/qinterval_builder.h"
+
+using namespace std;
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::context;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+
+//lower bound is exclusive
+//upper bound is inclusive
+
+struct QIntSort
+{
+ FirstOrderModelQInt * m;
+ bool operator() (Node i, Node j) {
+ return m->isLessThan( i, j );
+ }
+};
+
+void QIntDef::init_vec( FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u ) {
+ for( unsigned i=0; i<m->getOrderedNumVars( q ); i++ ){
+ l.push_back( Node::null() );
+ u.push_back( m->getMaximum( m->getOrderedVarType( q, i ) ) );
+ }
+}
+
+void QIntDef::debugPrint( const char * c, FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u )
+{
+ Trace(c) << "( ";
+ for( unsigned i=0; i<l.size(); i++ ){
+ if( i>0 ) Trace(c) << ", ";
+ //Trace(c) << l[i] << "..." << u[i];
+ int lindex = l[i].isNull() ? 0 : m->getRepId( l[i] ) + 1;
+ int uindex = m->getRepId( u[i] );
+ Trace(c) << lindex << "..." << uindex;
+ }
+ Trace(c) << " )";
+}
+
+
+int QIntDef::getEvIndex( FirstOrderModelQInt * m, Node n, bool exc ) {
+ if( n.isNull() ){
+ Assert( exc );
+ return 0;
+ }else{
+ int min = 0;
+ int max = (int)(d_def_order.size()-1);
+ while( min!=max ){
+ int index = (min+max)/2;
+ Assert( index>=0 && index<(int)d_def_order.size() );
+ if( n==d_def_order[index] ){
+ max = index;
+ min = index;
+ }else if( m->isLessThan( n, d_def_order[index] ) ){
+ max = index;
+ }else{
+ min = index+1;
+ }
+ }
+ if( n==d_def_order[min] && exc ){
+ min++;
+ }
+ Assert( min>=0 && min<(int)d_def_order.size() );
+ if( ( min!=0 && !m->isLessThan( d_def_order[min-1], n ) && ( !exc || d_def_order[min-1]!=n ) ) ||
+ ( ( exc || d_def_order[min]!=n ) && !m->isLessThan( n, d_def_order[min] ) ) ){
+ Debug("qint-error") << "ERR size : " << d_def_order.size() << ", exc : " << exc << std::endl;
+ for( unsigned i=0; i<d_def_order.size(); i++ ){
+ Debug("qint-error") << "ERR ch #" << i << " : " << d_def_order[i];
+ Debug("qint-error") << " " << m->getRepId( d_def_order[i] ) << std::endl;
+ }
+ Debug("qint-error") << " : " << n << " " << min << " " << m->getRepId( n ) << std::endl;
+ }
+
+ Assert( min==0 || m->isLessThan( d_def_order[min-1], n ) || ( exc && d_def_order[min-1]==n ) );
+ Assert( ( !exc && n==d_def_order[min] ) || m->isLessThan( n, d_def_order[min] ) );
+ return min;
+ }
+}
+
+void QIntDef::addEntry( FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u,
+ Node v, unsigned depth ) {
+ if( depth==0 ){
+ Trace("qint-compose-debug") << "Add entry ";
+ debugPrint( "qint-compose-debug", m, q, l, u );
+ Trace("qint-compose-debug") << " -> " << v << "..." << std::endl;
+ }
+ //Assert( false );
+ if( depth==u.size() ){
+ Assert( d_def_order.empty() );
+ Assert( v.isNull() || v.isConst() || ( v.getType().isSort() && m->getRepId( v )!=-1 ) );
+ d_def_order.push_back( v );
+ }else{
+ /*
+ if( !d_def_order.empty() &&
+ ( l[depth].isNull() || m->isLessThan( l[depth], d_def_order[d_def_order.size()-1] ) ) ){
+ int startEvIndex = getEvIndex( m, l[depth], true );
+ int endEvIndex;
+ if( m->isLessThan( u[depth], d_def_order[d_def_order.size()-1] ) ){
+ endEvIndex = getEvIndex( m, u[depth] );
+ }else{
+ endEvIndex = d_def_order.size()-1;
+ }
+ Trace("qint-compose-debug2") << this << " adding for bounds " << l[depth] << "..." << u[depth] << std::endl;
+ for( int i=startEvIndex; i<=endEvIndex; i++ ){
+ Trace("qint-compose-debug2") << this << " add entry " << d_def_order[i] << std::endl;
+ d_def[d_def_order[i]].addEntry( m, q, l, u, v, depth+1 );
+ }
+ }
+ if( !d_def_order.empty() &&
+ d_def.find(u[depth])==d_def.end() &&
+ !m->isLessThan( d_def_order[d_def_order.size()-1], u[depth] ) ){
+ Trace("qint-compose-debug2") << "Bad : depth : " << depth << std::endl;
+ }
+ Assert( d_def_order.empty() ||
+ d_def.find(u[depth])!=d_def.end() ||
+ m->isLessThan( d_def_order[d_def_order.size()-1], u[depth] ) );
+
+ if( d_def_order.empty() || m->isLessThan( d_def_order[d_def_order.size()-1], u[depth] ) ){
+ Trace("qint-compose-debug2") << this << " add entry new : " << u[depth] << std::endl;
+ d_def_order.push_back( u[depth] );
+ d_def[u[depth]].addEntry( m, q, l, u, v, depth+1 );
+ }
+ */
+ //%%%%%%
+ bool success = true;
+ int nnum = m->getVarOrder( q )->getNextNum( depth );
+ Node pl;
+ Node pu;
+ if( nnum!=-1 ){
+ Trace("qint-compose-debug2") << "...adding entry #" << depth << " is #" << nnum << std::endl;
+ //Assert( l[nnum].isNull() || l[nnum]==l[depth] || m->isLessThan( l[nnum], l[depth] ) );
+ //Assert( u[nnum]==u[depth] || m->isLessThan( u[depth], u[nnum] ) );
+ pl = l[nnum];
+ pu = u[nnum];
+ if( !m->doMeet( l[nnum], u[nnum], l[depth], u[depth], l[nnum], u[nnum] ) ){
+ success = false;
+ }
+ }
+ //%%%%%%
+ if( success ){
+ Node r = u[depth];
+ if( d_def.find( r )!=d_def.end() ){
+ d_def[r].addEntry( m, q, l, u, v, depth+1 );
+ }else{
+ if( !d_def_order.empty() &&
+ !m->isLessThan( d_def_order[d_def_order.size()-1], u[depth] ) ){
+ Trace("qint-compose-debug2") << "Bad : depth : " << depth << " ";
+ Trace("qint-compose-debug2") << d_def_order[d_def_order.size()-1] << " " << u[depth] << std::endl;
+ }
+ Assert( d_def_order.empty() || m->isLessThan( d_def_order[d_def_order.size()-1], r ) );
+ d_def_order.push_back( r );
+ d_def[r].addEntry( m, q, l, u, v, depth+1 );
+ }
+ }
+ if( nnum!=-1 ){
+ l[nnum] = pl;
+ u[nnum] = pu;
+ }
+ }
+}
+
+Node QIntDef::simplify_r( FirstOrderModelQInt * m, Node q, std::vector< Node >& il, std::vector< Node >& iu,
+ unsigned depth ) {
+ if( d_def.empty() ){
+ if( d_def_order.size()!=0 ){
+ Debug("qint-error") << "Simplify, size = " << d_def_order.size() << std::endl;
+ }
+ Assert( d_def_order.size()==1 );
+ return d_def_order[0];
+ }else{
+ Assert( !d_def_order.empty() );
+ std::vector< Node > newDefs;
+ Node curr;
+ for( unsigned i=0; i<d_def_order.size(); i++ ){
+ Node n = d_def[d_def_order[i]].simplify_r( m, q, il, iu, depth+1 );
+ if( i>0 ){
+ if( n==curr && !n.isNull() ){
+ d_def.erase( d_def_order[i-1] );
+ }else{
+ newDefs.push_back( d_def_order[i-1] );
+ }
+ }
+ curr = n;
+ }
+ newDefs.push_back( d_def_order[d_def_order.size()-1] );
+ d_def_order.clear();
+ d_def_order.insert( d_def_order.end(), newDefs.begin(), newDefs.end() );
+ return d_def_order.size()==1 ? curr : Node::null();
+ }
+}
+
+Node QIntDef::simplify( FirstOrderModelQInt * m, Node q ) {
+ std::vector< Node > l;
+ std::vector< Node > u;
+ if( !q.isNull() ){
+ //init_vec( m, q, l, u );
+ }
+ return simplify_r( m, q, l, u, 0 );
+}
+
+bool QIntDef::isTotal_r( FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u,
+ unsigned depth ) {
+ if( d_def_order.empty() ){
+ return false;
+ }else if( d_def.empty() ){
+ return true;
+ }else{
+ //get the current maximum
+ Node mx;
+ if( !q.isNull() ){
+ int pnum = m->getVarOrder( q )->getPrevNum( depth );
+ if( pnum!=-1 ){
+ mx = u[pnum];
+ }
+ }
+ if( mx.isNull() ){
+ mx = m->getMaximum( d_def_order[d_def_order.size()-1].getType() );
+ }
+ //if not current maximum
+ if( d_def_order[d_def_order.size()-1]!=mx ){
+ return false;
+ }else{
+ Node pu = u[depth];
+ for( unsigned i=0; i<d_def_order.size(); i++ ){
+ u[depth] = d_def_order[i];
+ if( !d_def[d_def_order[i]].isTotal_r( m, q, l, u, depth+1 ) ){
+ return false;
+ }
+ }
+ u[depth] = pu;
+ return true;
+ }
+ }
+}
+
+bool QIntDef::isTotal( FirstOrderModelQInt * m, Node q ) {
+ std::vector< Node > l;
+ std::vector< Node > u;
+ if( !q.isNull() ){
+ init_vec( m, q, l, u );
+ }
+ return isTotal_r( m, q, l, u, 0 );
+}
+
+void QIntDef::construct_compose_r( FirstOrderModelQInt * m, Node q,
+ std::vector< Node >& l, std::vector< Node >& u,
+ Node n, QIntDef * f,
+ std::vector< Node >& args,
+ std::map< unsigned, QIntDef >& children,
+ std::map< unsigned, Node >& bchildren,
+ QIntVarNumIndex& vindex, unsigned depth ) {
+ //check for short circuit
+ if( !f ){
+ if( !args.empty() ){
+ if( ( n.getKind()==OR && args[args.size()-1]==m->d_true ) ||
+ ( n.getKind()==AND && args[args.size()-1]==m->d_false ) ){
+ addEntry( m, q, l, u, args[args.size()-1] );
+ return;
+ }
+ }
+ }
+
+ for( unsigned i=0; i<depth; i++ ) { Trace("qint-compose") << " "; }
+ Trace("qint-compose") << (f ? "U" : "I" ) << "C( ";
+ for( unsigned i=0; i<l.size(); i++ ){
+ if( i>0 ) Trace("qint-compose") << ", ";
+ //Trace("qint-compose") << l[i] << "..." << u[i];
+ int lindex = l[i].isNull() ? 0 : m->getRepId( l[i] ) + 1;
+ int uindex = m->getRepId( u[i] );
+ Trace( "qint-compose" ) << lindex << "..." << uindex;
+ }
+ Trace("qint-compose") << " )...";
+
+ //finished?
+ if( ( f && f->d_def.empty() ) || args.size()==n.getNumChildren() ){
+ if( f ){
+ Assert( f->d_def_order.size()==1 );
+ Trace("qint-compose") << "UVALUE(" << f->d_def_order[0] << ")" << std::endl;
+ addEntry( m, q, l, u, f->d_def_order[0] );
+ }else{
+ Node nn;
+ bool nnSet = false;
+ for( unsigned i=0; i<args.size(); i++ ){
+ if( args[i].isNull() ){
+ nnSet = true;
+ break;
+ }
+ }
+ if( !nnSet ){
+ if( n.getKind()==EQUAL ){
+ nn = NodeManager::currentNM()->mkConst( args[0]==args[1] );
+ }else{
+ //apply the operator to args
+ nn = NodeManager::currentNM()->mkNode( n.getKind(), args );
+ nn = Rewriter::rewrite( nn );
+ }
+ }
+ Trace("qint-compose") << "IVALUE(" << nn << ")" << std::endl;
+ addEntry( m, q, l, u, nn );
+ Trace("qint-compose-debug2") << "...added entry." << std::endl;
+ }
+ }else{
+ //if a non-simple child
+ if( children.find( depth )!=children.end() ){
+ //***************************
+ Trace("qint-compose") << "compound child, recurse" << std::endl;
+ std::vector< int > currIndex;
+ std::vector< int > endIndex;
+ std::vector< Node > prevL;
+ std::vector< Node > prevU;
+ std::vector< QIntDef * > visited;
+ do{
+ Assert( currIndex.size()==visited.size() );
+
+ //populate the vectors
+ while( visited.size()<m->getOrderedNumVars( q ) ){
+ unsigned i = visited.size();
+ QIntDef * qq = visited.empty() ? &children[depth] : visited[i-1]->getChild( currIndex[i-1] );
+ visited.push_back( qq );
+ Node qq_mx = qq->getMaximum();
+ Trace("qint-compose-debug2") << "...Get ev indices " << i << " " << l[i] << " " << u[i] << std::endl;
+ currIndex.push_back( qq->getEvIndex( m, l[i], true ) );
+ Trace("qint-compose-debug2") << "...Done get curr index " << currIndex[currIndex.size()-1] << std::endl;
+ if( m->isLessThan( qq_mx, u[i] ) ){
+ endIndex.push_back( qq->getNumChildren()-1 );
+ }else{
+ endIndex.push_back( qq->getEvIndex( m, u[i] ) );
+ }
+ Trace("qint-compose-debug2") << "...Done get end index " << endIndex[endIndex.size()-1] << std::endl;
+ prevL.push_back( l[i] );
+ prevU.push_back( u[i] );
+ if( !m->doMeet( prevL[i], prevU[i],
+ qq->getLower( currIndex[i] ), qq->getUpper( currIndex[i] ), l[i], u[i] ) ){
+ Assert( false );
+ }
+ }
+ for( unsigned i=0; i<depth; i++ ) { Trace("qint-compose") << " "; }
+ for( unsigned i=0; i<currIndex.size(); i++ ){
+ Trace("qint-compose") << "[" << currIndex[i] << "/" << endIndex[i] << "]";
+ }
+ Trace("qint-compose") << std::endl;
+ //consider the current
+ int activeIndex = visited.size()-1;
+ QIntDef * qa = visited.empty() ? &children[depth] : visited[activeIndex]->getChild( currIndex[activeIndex] );
+ if( f ){
+ int fIndex = f->getEvIndex( m, qa->getValue() );
+ construct_compose_r( m, q, l, u, n, f->getChild( fIndex ), args, children, bchildren, vindex, depth+1 );
+ }else{
+ args.push_back( qa->getValue() );
+ construct_compose_r( m, q, l, u, n, f, args, children, bchildren, vindex, depth+1 );
+ args.pop_back();
+ }
+
+ //increment the index (if possible)
+ while( activeIndex>=0 && currIndex[activeIndex]==endIndex[activeIndex] ){
+ currIndex.pop_back();
+ endIndex.pop_back();
+ l[activeIndex] = prevL[activeIndex];
+ u[activeIndex] = prevU[activeIndex];
+ prevL.pop_back();
+ prevU.pop_back();
+ visited.pop_back();
+ activeIndex--;
+ }
+ if( activeIndex>=0 ){
+ for( unsigned i=0; i<depth; i++ ) { Trace("qint-compose") << " "; }
+ Trace("qint-compose-debug") << "Increment at " << activeIndex << std::endl;
+ currIndex[activeIndex]++;
+ if( !m->doMeet( prevL[activeIndex], prevU[activeIndex],
+ visited[activeIndex]->getLower( currIndex[activeIndex] ),
+ visited[activeIndex]->getUpper( currIndex[activeIndex] ),
+ l[activeIndex], u[activeIndex] ) ){
+ Assert( false );
+ }
+ }
+ }while( !visited.empty() );
+ //***************************
+ }else{
+ Assert( bchildren.find( depth )!=bchildren.end() );
+ Node v = bchildren[depth];
+ if( f ){
+ if( v.getKind()==BOUND_VARIABLE ){
+ int vn = vindex.d_var_num[depth];
+ Trace("qint-compose") << "variable #" << vn << ", recurse" << std::endl;
+ //int vn = m->getOrderedVarOccurId( q, n, depth );
+ Trace("qint-compose-debug") << "-process " << v << ", which is var #" << vn << std::endl;
+ Node lprev = l[vn];
+ Node uprev = u[vn];
+ //restrict to last variable in order
+ int pnum = m->getVarOrder( q )->getPrevNum( vn );
+ if( pnum!=-1 ){
+ Trace("qint-compose-debug") << "-restrict to var #" << pnum << " " << l[pnum] << " " << u[pnum] << std::endl;
+ l[vn] = l[pnum];
+ u[vn] = u[pnum];
+ }
+ int startIndex = f->getEvIndex( m, l[vn], true );
+ int endIndex = f->getEvIndex( m, u[vn] );
+ Trace("qint-compose-debug") << "--will process " << startIndex << " " << endIndex << std::endl;
+ for( int i=startIndex; i<=endIndex; i++ ){
+ if( m->doMeet( lprev, uprev, f->getLower( i ), f->getUpper( i ), l[vn], u[vn] ) ){
+ construct_compose_r( m, q, l, u, n, f->getChild( i ), args, children, bchildren, vindex, depth+1 );
+ }else{
+ Assert( false );
+ }
+ }
+ l[vn] = lprev;
+ u[vn] = uprev;
+ }else{
+ Trace("qint-compose") << "value, recurse" << std::endl;
+ //simple
+ int ei = f->getEvIndex( m, v );
+ construct_compose_r( m, q, l, u, n, f->getChild( ei ), args, children, bchildren, vindex, depth+1 );
+ }
+ }else{
+ Trace("qint-compose") << "value, recurse" << std::endl;
+ args.push_back( v );
+ construct_compose_r( m, q, l, u, n, f, args, children, bchildren, vindex, depth+1 );
+ args.pop_back();
+ }
+ }
+ }
+}
+
+
+void QIntDef::construct_enum_r( FirstOrderModelQInt * m, Node q, unsigned vn, unsigned depth, Node v ) {
+ if( depth==m->getOrderedNumVars( q ) ){
+ Assert( !v.isNull() );
+ d_def_order.push_back( v );
+ }else{
+ TypeNode tn = m->getOrderedVarType( q, depth );
+ //int vnum = m->getVarOrder( q )->getVar( depth )==
+ if( depth==vn ){
+ for( unsigned i=0; i<m->d_rep_set.d_type_reps[tn].size(); i++ ){
+ Node vv = m->d_rep_set.d_type_reps[tn][i];
+ d_def_order.push_back( vv );
+ d_def[vv].construct_enum_r( m, q, vn, depth+1, vv );
+ }
+ }else if( m->getVarOrder( q )->getVar( depth )==m->getVarOrder( q )->getVar( vn ) && depth>vn ){
+ d_def_order.push_back( v );
+ d_def[v].construct_enum_r( m, q, vn, depth+1, v );
+ }else{
+ Node mx = m->getMaximum( tn );
+ d_def_order.push_back( mx );
+ d_def[mx].construct_enum_r( m, q, vn, depth+1, v );
+ }
+ }
+}
+
+bool QIntDef::construct_enum( FirstOrderModelQInt * m, Node q, unsigned vn ) {
+ TypeNode tn = m->getOrderedVarType( q, vn );
+ if( tn.isSort() ){
+ construct_enum_r( m, q, vn, 0, Node::null() );
+ return true;
+ }else{
+ return false;
+ }
+}
+
+bool QIntDef::construct_compose( FirstOrderModelQInt * m, Node q, Node n, QIntDef * f,
+ std::map< unsigned, QIntDef >& children,
+ std::map< unsigned, Node >& bchildren, int varChCount,
+ QIntVarNumIndex& vindex ) {
+ Trace("qint-compose") << "Do " << (f ? "uninterpreted" : "interpreted");
+ Trace("qint-compose") << " compose, var count = " << varChCount << "..." << std::endl;
+ std::vector< Node > l;
+ std::vector< Node > u;
+ init_vec( m, q, l, u );
+ if( varChCount==0 || f ){
+ //standard (no variable child) interpreted compose, or uninterpreted compose
+ std::vector< Node > args;
+ construct_compose_r( m, q, l, u, n, f, args, children, bchildren, vindex, 0 );
+ }else{
+ //special cases
+ bool success = false;
+ int varIndex = ( bchildren.find( 0 )!=bchildren.end() && bchildren[0].getKind()==BOUND_VARIABLE ) ? 0 : 1;
+ if( varChCount>1 ){
+ if( n.getKind()==EQUAL ){
+ //make it an enumeration
+ unsigned vn = vindex.d_var_num[0];
+ if( children[0].construct_enum( m, q, vn ) ){
+ bchildren.erase( 0 );
+ varIndex = 1;
+ success = true;
+ }
+ }
+ }else{
+ success = n.getKind()==EQUAL;
+ }
+ if( success ){
+ int oIndex = varIndex==0 ? 1 : 0;
+ Node v = bchildren[varIndex];
+ unsigned vn = vindex.d_var_num[varIndex];
+ if( children.find( oIndex )==children.end() ){
+ Assert( bchildren.find( oIndex )!=bchildren.end() );
+ Node at = bchildren[oIndex];
+ Trace("qint-icompose") << "Basic child, " << at << " with var " << v << std::endl;
+ Node prev = m->getPrev( bchildren[oIndex].getType(), bchildren[oIndex] );
+ Node above = u[vn];
+ if( !prev.isNull() ){
+ u[vn] = prev;
+ addEntry( m, q, l, u, NodeManager::currentNM()->mkConst( false ) );
+ }
+ l[vn] = prev;
+ u[vn] = at;
+ addEntry( m, q, l, u, NodeManager::currentNM()->mkConst( true ) );
+ if( at!=above ){
+ l[vn] = at;
+ u[vn] = above;
+ addEntry( m, q, l, u, NodeManager::currentNM()->mkConst( false ) );
+ }
+ }else{
+ QIntDef * qid = &children[oIndex];
+ qid->debugPrint("qint-icompose", m, q );
+ Trace("qint-icompose") << " against variable..." << v << ", which is var #" << vn << std::endl;
+
+ TypeNode tn = v.getType();
+ QIntDefIter qdi( m, q, qid );
+ while( !qdi.isFinished() ){
+ std::vector< Node > us;
+ qdi.getUppers( us );
+ std::vector< Node > ls;
+ qdi.getLowers( ls );
+ qdi.debugPrint( "qint-icompose" );
+
+ Node n_below = ls[vn];
+ Node n_prev = m->getPrev( tn, qdi.getValue() );
+ Node n_at = qdi.getValue();
+ Node n_above = us[vn];
+ Trace("qint-icompose") << n_below << " < " << n_prev << " < " << n_at << " < " << n_above << std::endl;
+ if( n.getKind()==EQUAL ){
+ bool atLtAbove = m->isLessThan( n_at, n_above );
+ Node currL = n_below;
+ if( n_at==n_above || atLtAbove ){
+ //add for value (at-1)
+ if( !n_prev.isNull() && ( n_below.isNull() || m->isLessThan( n_below, n_prev ) ) ){
+ ls[vn] = currL;
+ us[vn] = n_prev;
+ currL = n_prev;
+ Trace("qint-icompose") << "-add entry(-) at " << ls[vn] << "..." << us[vn] << std::endl;
+ addEntry( m, q, ls, us, NodeManager::currentNM()->mkConst( false ) );
+ }
+ //add for value (at)
+ if( ( n_below.isNull() || m->isLessThan( n_below, n_at ) ) && atLtAbove ){
+ ls[vn] = currL;
+ us[vn] = n_at;
+ currL = n_at;
+ Trace("qint-icompose") << "-add entry(=) at " << ls[vn] << "..." << us[vn] << std::endl;
+ addEntry( m, q, ls, us, NodeManager::currentNM()->mkConst( true ) );
+ }
+ }
+ ls[vn] = currL;
+ us[vn] = n_above;
+ Trace("qint-icompose") << "-add entry(+) at " << ls[vn] << "..." << us[vn] << std::endl;
+ addEntry( m, q, ls, us, NodeManager::currentNM()->mkConst( n_at==n_above ) );
+ }else{
+ return false;
+ }
+ qdi.increment();
+
+ Trace("qint-icompose-debug") << "Now : " << std::endl;
+ debugPrint("qint-icompose-debug", m, q );
+ Trace("qint-icompose-debug") << std::endl;
+ }
+ }
+
+ Trace("qint-icompose") << "Result : " << std::endl;
+ debugPrint("qint-icompose", m, q );
+ Trace("qint-icompose") << std::endl;
+
+ }else{
+ return false;
+ }
+ }
+ Trace("qint-compose") << "Done i-compose" << std::endl;
+ return true;
+}
+
+
+void QIntDef::construct( FirstOrderModelQInt * m, std::vector< Node >& fapps, unsigned depth ) {
+ d_def.clear();
+ d_def_order.clear();
+ Assert( !fapps.empty() );
+ if( depth==fapps[0].getNumChildren() ){
+ //get representative in model for this term
+ Assert( fapps.size()>=1 );
+ Node r = m->getUsedRepresentative( fapps[0] );
+ d_def_order.push_back( r );
+ }else{
+ std::map< Node, std::vector< Node > > fapp_child;
+ //partition based on evaluations of fapps[1][depth]....fapps[n][depth]
+ for( unsigned i=0; i<fapps.size(); i++ ){
+ Node r = m->getUsedRepresentative( fapps[i][depth] );
+ fapp_child[r].push_back( fapps[i] );
+ }
+ //sort by QIntSort
+ for( std::map< Node, std::vector< Node > >::iterator it = fapp_child.begin(); it != fapp_child.end(); ++it ){
+ d_def_order.push_back( it->first );
+ }
+ QIntSort qis;
+ qis.m = m;
+ std::sort( d_def_order.begin(), d_def_order.end(), qis );
+ //construct children
+ for( unsigned i=0; i<d_def_order.size(); i++ ){
+ Node n = d_def_order[i];
+ if( i==d_def_order.size()-1 ){
+ d_def_order[i] = m->getMaximum( d_def_order[i].getType() );
+ }
+ Debug("qint-model-debug2") << "Construct for " << n << ", terms = " << fapp_child[n].size() << std::endl;
+ d_def[d_def_order[i]].construct( m, fapp_child[n], depth+1 );
+ }
+ }
+}
+
+Node QIntDef::getFunctionValue( FirstOrderModelQInt * m, std::vector< Node >& vars, unsigned depth ) {
+ if( d_def.empty() ){
+ Assert( d_def_order.size()==1 );
+ //must convert to actual domain constant
+ if( d_def_order[0].getType().isSort() ){
+ return m->d_rep_set.d_type_reps[ d_def_order[0].getType() ][ m->getRepId( d_def_order[0] ) ];
+ }else{
+ return m->getUsedRepresentative( d_def_order[0] );
+ }
+ }else{
+ TypeNode tn = vars[depth].getType();
+ Node curr;
+ int rep_id = m->d_rep_set.getNumRepresentatives( tn );
+ for( int i=(int)(d_def_order.size()-1); i>=0; i-- ){
+ int curr_rep_id = i==0 ? 0 : m->getRepId( d_def_order[i-1] )+1;
+ Node ccurr = d_def[d_def_order[i]].getFunctionValue( m, vars, depth+1 );
+ if( curr.isNull() ){
+ curr = ccurr;
+ }else{
+ std::vector< Node > c;
+ Assert( curr_rep_id<rep_id );
+ for( int j=curr_rep_id; j<rep_id; j++ ){
+ c.push_back( vars[depth].eqNode( m->d_rep_set.d_type_reps[tn][j] ) );
+ }
+ Node cond = c.size()==1 ? c[0] : NodeManager::currentNM()->mkNode( OR, c );
+ curr = NodeManager::currentNM()->mkNode( ITE, cond, ccurr, curr );
+ }
+ rep_id = curr_rep_id;
+ }
+ return curr;
+ }
+}
+
+Node QIntDef::evaluate_r( FirstOrderModelQInt * m, std::vector< Node >& reps, unsigned depth ) {
+ if( depth==reps.size() ){
+ Assert( d_def_order.size()==1 );
+ return d_def_order[0];
+ }else{
+ if( d_def.find( reps[depth] )!=d_def.end() ){
+ return d_def[reps[depth]].evaluate_r( m, reps, depth+1 );
+ }else{
+ int ei = getEvIndex( m, reps[depth] );
+ return d_def[d_def_order[ei]].evaluate_r( m, reps, depth+1 );
+ }
+ }
+}
+Node QIntDef::evaluate_n_r( FirstOrderModelQInt * m, Node n, unsigned depth ) {
+ if( depth==n.getNumChildren() ){
+ Assert( d_def_order.size()==1 );
+ return d_def_order[0];
+ }else{
+ Node r = m->getUsedRepresentative( n[depth] );
+ if( d_def.find( r )!=d_def.end() ){
+ return d_def[r].evaluate_n_r( m, n, depth+1 );
+ }else{
+ int ei = getEvIndex( m, r );
+ return d_def[d_def_order[ei]].evaluate_n_r( m, n, depth+1 );
+ }
+ }
+}
+
+
+
+QIntDef * QIntDef::getChild( unsigned i ) {
+ Assert( i<d_def_order.size() );
+ Assert( d_def.find( d_def_order[i] )!=d_def.end() );
+ return &d_def[ d_def_order[i] ];
+}
+
+void QIntDef::debugPrint( const char * c, FirstOrderModelQInt * m, Node q, int t ) {
+ /*
+ for( unsigned i=0; i<d_def_order.size(); i++ ){
+ for( int j=0; j<t; j++ ) { Trace(c) << " "; }
+ //Trace(c) << this << " ";
+ Trace(c) << d_def_order[i] << " : " << std::endl;
+ if( d_def.find( d_def_order[i] )!=d_def.end() ){
+ d_def[d_def_order[i]].debugPrint( c, m, t+1 );
+ }
+ }
+ */
+ //if( t==0 ){
+ QIntDefIter qdi( m, q, this );
+ while( !qdi.isFinished() ){
+ qdi.debugPrint( c, t );
+ qdi.increment();
+ }
+ //}
+}
+
+
+QIntDefIter::QIntDefIter( FirstOrderModelQInt * m, Node q, QIntDef * qid ) : d_fm( m ), d_q( q ){
+ resetIndex( qid );
+}
+
+void QIntDefIter::debugPrint( const char * c, int t ) {
+ //Trace( c ) << getSize() << " " << d_index_visited.size() << " ";
+ for( int j=0; j<t; j++ ) { Trace(c) << " "; }
+ std::vector< Node > l;
+ std::vector< Node > u;
+ getLowers( l );
+ getUppers( u );
+ QIntDef::debugPrint( c, d_fm, d_q, l, u );
+ Trace( c ) << " -> " << getValue() << std::endl;
+}
+
+void QIntDefIter::resetIndex( QIntDef * qid ){
+ //std::cout << "check : " << qid << " " << qid->d_def_order.size() << " " << qid->d_def.size() << std::endl;
+ if( !qid->d_def.empty() ){
+ //std::cout << "add to visited " << qid << std::endl;
+ d_index.push_back( 0 );
+ d_index_visited.push_back( qid );
+ resetIndex( qid->getChild( 0 ) );
+ }
+}
+
+bool QIntDefIter::increment( int index ) {
+ if( !isFinished() ){
+ index = index==-1 ? (int)(d_index.size()-1) : index;
+ while( (int)(d_index.size()-1)>index ){
+ //std::cout << "remove from visit 1 " << std::endl;
+ d_index.pop_back();
+ d_index_visited.pop_back();
+ }
+ while( index>=0 && d_index[index]>=(int)(d_index_visited[index]->d_def_order.size()-1) ){
+ //std::cout << "remove from visit " << d_index_visited[ d_index_visited.size()-1 ] << std::endl;
+ d_index.pop_back();
+ d_index_visited.pop_back();
+ index--;
+ }
+ if( index>=0 ){
+ //std::cout << "increment at index = " << index << std::endl;
+ d_index[index]++;
+ resetIndex( d_index_visited[index]->getChild( d_index[index] ) );
+ return true;
+ }else{
+ d_index.clear();
+ return false;
+ }
+ }else{
+ return false;
+ }
+}
+
+Node QIntDefIter::getLower( int index ) {
+ if( d_index[index]==0 && !d_q.isNull() ){
+ int pnum = d_fm->getVarOrder( d_q )->getPrevNum( index );
+ if( pnum!=-1 ){
+ return getLower( pnum );
+ }
+ }
+ return d_index_visited[index]->getLower( d_index[index] );
+}
+
+Node QIntDefIter::getUpper( int index ) {
+ return d_index_visited[index]->getUpper( d_index[index] );
+}
+
+void QIntDefIter::getLowers( std::vector< Node >& reps ) {
+ for( unsigned i=0; i<getSize(); i++ ){
+ bool added = false;
+ if( d_index[i]==0 && !d_q.isNull() ){
+ int pnum = d_fm->getVarOrder( d_q )->getPrevNum( i );
+ if( pnum!=-1 ){
+ added = true;
+ reps.push_back( reps[pnum] );
+ }
+ }
+ if( !added ){
+ reps.push_back( getLower( i ) );
+ }
+ }
+}
+
+void QIntDefIter::getUppers( std::vector< Node >& reps ) {
+ for( unsigned i=0; i<getSize(); i++ ){
+ reps.push_back( getUpper( i ) );
+ }
+}
+
+Node QIntDefIter::getValue() {
+ return d_index_visited[ d_index_visited.size()-1 ]->getChild( d_index[d_index.size()-1] )->getValue();
+}
+
+
+//------------------------variable ordering----------------------------
+
+QuantVarOrder::QuantVarOrder( Node q ) : d_q( q ) {
+ d_var_count = 0;
+ initialize( q[1], 0, d_var_occur );
+}
+
+int QuantVarOrder::initialize( Node n, int minVarIndex, QIntVarNumIndex& vindex ) {
+ if( n.getKind()!=FORALL ){
+ //std::vector< Node > vars;
+ //std::vector< int > args;
+ int procVarOn = n.getKind()==APPLY_UF ? 0 : 1;
+ for( int r=0; r<=procVarOn; r++ ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( n[i].getKind()==BOUND_VARIABLE && r==procVarOn ){
+ int occ_index = -1;
+ for( unsigned j=0; j<d_var_to_num[n[i]].size(); j++ ){
+ if( d_var_to_num[n[i]][j]>=minVarIndex ){
+ occ_index = d_var_to_num[n[i]][j];
+ }
+ }
+ if( occ_index==-1 ){
+ //need to assign new
+ d_num_to_var[d_var_count] = n[i];
+ if( !d_var_to_num[n[i]].empty() ){
+ int v = d_var_to_num[n[i]][ d_var_to_num[n[i]].size()-1 ];
+ d_num_to_prev_num[ d_var_count ] = v;
+ d_num_to_next_num[ v ] = d_var_count;
+ }
+ d_var_num_index[ d_var_count ] = d_var_to_num[n[i]].size();
+ d_var_to_num[n[i]].push_back( d_var_count );
+ occ_index = d_var_count;
+ d_var_count++;
+ }
+ vindex.d_var_num[i] = occ_index;
+ minVarIndex = occ_index;
+ }else if( r==0 ){
+ minVarIndex = initialize( n[i], minVarIndex, vindex.d_var_index[i] );
+ }
+ }
+ }
+ }
+ return minVarIndex;
+}
+
+bool QuantVarOrder::getInstantiation( FirstOrderModelQInt * m, std::vector< Node >& l, std::vector< Node >& u,
+ std::vector< Node >& inst ) {
+ Debug("qint-var-order-debug2") << "Get for " << d_q << " " << l.size() << " " << u.size() << std::endl;
+ for( unsigned i=0; i<d_q[0].getNumChildren(); i++ ){
+ Debug("qint-var-order-debug2") << "Get for " << d_q[0][i] << " " << d_var_to_num[d_q[0][i]].size() << std::endl;
+ Node ll = Node::null();
+ Node uu = m->getMaximum( d_q[0][i].getType() );
+ for( unsigned j=0; j<d_var_to_num[d_q[0][i]].size(); j++ ){
+ Debug("qint-var-order-debug2") << "Go " << j << std::endl;
+ Node cl = ll;
+ Node cu = uu;
+ int index = d_var_to_num[d_q[0][i]][j];
+ Debug("qint-var-order-debug2") << "Do meet for " << index << "..." << std::endl;
+ Debug("qint-var-order-debug2") << l[index] << " " << u[index] << " " << cl << " " << cu << std::endl;
+ if( !m->doMeet( l[index], u[index], cl, cu, ll, uu ) ){
+ Debug("qint-var-order-debug2") << "FAILED" << std::endl;
+ return false;
+ }
+ Debug("qint-var-order-debug2") << "Result : " << ll << " " << uu << std::endl;
+ }
+ Debug("qint-var-order-debug2") << "Got " << uu << std::endl;
+ inst.push_back( uu );
+ }
+ return true;
+}
+
+void QuantVarOrder::debugPrint( const char * c ) {
+ Trace( c ) << "Variable order for " << d_q << " is : " << std::endl;
+ debugPrint( c, d_q[1], d_var_occur );
+ Trace( c ) << std::endl;
+ for( unsigned i=0; i<d_q[0].getNumChildren(); i++ ){
+ Trace( c ) << d_q[0][i] << " : ";
+ for( unsigned j=0; j<d_var_to_num[d_q[0][i]].size(); j++ ){
+ Trace( c ) << d_var_to_num[d_q[0][i]][j] << " ";
+ }
+ Trace( c ) << std::endl;
+ }
+}
+
+void QuantVarOrder::debugPrint( const char * c, Node n, QIntVarNumIndex& vindex ) {
+ if( n.getKind()==FORALL ){
+ Trace(c) << "NESTED_QUANT";
+ }else{
+ Trace(c) << n.getKind() << "(";
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( i>0 ) Trace( c ) << ",";
+ Trace( c ) << " ";
+ if( n[i].getKind()==BOUND_VARIABLE ){
+ Trace(c) << "VAR[" << vindex.d_var_num[i] << "]";
+ }else{
+ debugPrint( c, n[i], vindex.d_var_index[i] );
+ }
+ if( i==n.getNumChildren()-1 ) Trace( c ) << " ";
+ }
+ Trace(c) << ")";
+ }
+}
+
+QIntervalBuilder::QIntervalBuilder( context::Context* c, QuantifiersEngine* qe ) :
+QModelBuilder( c, qe ){
+ d_true = NodeManager::currentNM()->mkConst( true );
+}
+
+
+//------------------------model construction----------------------------
+
+void QIntervalBuilder::processBuildModel(TheoryModel* m, bool fullModel) {
+ Trace("fmf-qint-debug") << "process build model " << fullModel << std::endl;
+ FirstOrderModel* f = (FirstOrderModel*)m;
+ FirstOrderModelQInt* fm = f->asFirstOrderModelQInt();
+ if( fullModel ){
+ Trace("qint-model") << "Construct model representation..." << std::endl;
+ //make function values
+ for( std::map<Node, QIntDef * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) {
+ if( it->first.getType().getNumChildren()>1 ){
+ Trace("qint-model") << "Construct for " << it->first << "..." << std::endl;
+ m->d_uf_models[ it->first ] = fm->getFunctionValue( it->first, "$x" );
+ }
+ }
+ TheoryEngineModelBuilder::processBuildModel( m, fullModel );
+ //mark that the model has been set
+ fm->markModelSet();
+ //debug the model
+ debugModel( fm );
+ }else{
+ fm->initialize( d_considerAxioms );
+ //process representatives
+ fm->d_rep_id.clear();
+ fm->d_max.clear();
+ fm->d_min.clear();
+ Trace("qint-model") << std::endl << "Making representatives..." << std::endl;
+ for( std::map< TypeNode, std::vector< Node > >::iterator it = fm->d_rep_set.d_type_reps.begin();
+ it != fm->d_rep_set.d_type_reps.end(); ++it ){
+ if( it->first.isSort() ){
+ if( it->second.empty() ){
+ std::cout << "Empty rep for " << it->first << std::endl;
+ exit(0);
+ }
+ Trace("qint-model") << "Representatives for " << it->first << " : " << std::endl;
+ for( unsigned i=0; i<it->second.size(); i++ ){
+ Trace("qint-model") << i << " : " << it->second[i] << std::endl;
+ fm->d_rep_id[it->second[i]] = i;
+ }
+ fm->d_min[it->first] = it->second[0];
+ fm->d_max[it->first] = it->second[it->second.size()-1];
+ }else{
+ //TODO: enumerate?
+ }
+ }
+ Trace("qint-model") << std::endl << "Making function definitions..." << std::endl;
+ //construct the models for functions
+ for( std::map<Node, QIntDef * >::iterator it = fm->d_models.begin(); it != fm->d_models.end(); ++it ) {
+ Node f = it->first;
+ Trace("qint-model-debug") << "Building Model for " << f << std::endl;
+ //reset the model
+ //get all (non-redundant) f-applications
+ std::vector< Node > fapps;
+ Trace("qint-model-debug") << "Initial terms: " << std::endl;
+ for( size_t i=0; i<fm->d_uf_terms[f].size(); i++ ){
+ Node n = fm->d_uf_terms[f][i];
+ if( !n.getAttribute(NoMatchAttribute()) ){
+ Trace("qint-model-debug") << " " << n << std::endl;
+ fapps.push_back( n );
+ }
+ }
+ if( fapps.empty() ){
+ //choose arbitrary value
+ Node mbt = d_qe->getTermDatabase()->getModelBasisOpTerm(f);
+ Trace("qint-model-debug") << "Initial terms empty, add " << mbt << std::endl;
+ fapps.push_back( mbt );
+ }
+ //construct the interval model
+ it->second->construct( fm, fapps );
+ Trace("qint-model-debug") << "Definition for " << f << " : " << std::endl;
+ it->second->debugPrint("qint-model-debug", fm, Node::null() );
+
+ it->second->simplify( fm, Node::null() );
+ Trace("qint-model") << "(Simplified) definition for " << f << " : " << std::endl;
+ it->second->debugPrint("qint-model", fm, Node::null() );
+
+ if( Debug.isOn("qint-model-debug") ){
+ for( size_t i=0; i<fm->d_uf_terms[f].size(); i++ ){
+ Node e = it->second->evaluate_n( fm, fm->d_uf_terms[f][i] );
+ Debug("qint-model-debug") << fm->d_uf_terms[f][i] << " evaluates to " << e << std::endl;
+ Assert( fm->areEqual( e, fm->d_uf_terms[f][i] ) );
+ }
+ }
+ }
+ }
+}
+
+
+//--------------------model checking---------------------------------------
+
+//do exhaustive instantiation
+bool QIntervalBuilder::doExhaustiveInstantiation( FirstOrderModel * fm, Node q, int effort ) {
+ Trace("qint-check") << "exhaustive instantiation " << q << " " << effort << std::endl;
+ if (effort==0) {
+
+ FirstOrderModelQInt * fmqint = fm->asFirstOrderModelQInt();
+ QIntDef qid;
+ doCheck( fmqint, q, qid, q[1], fmqint->d_var_order[q]->d_var_occur );
+ //now process entries
+ Trace("qint-inst") << "Interpretation for " << q << " is : " << std::endl;
+ qid.debugPrint( "qint-inst", fmqint, q );
+ Trace("qint-inst") << std::endl;
+ Debug("qint-check-debug2") << "Make iterator..." << std::endl;
+ QIntDefIter qdi( fmqint, q, &qid );
+ while( !qdi.isFinished() ){
+ if( qdi.getValue()!=d_true ){
+ Debug("qint-check-debug2") << "Set up vectors..." << std::endl;
+ std::vector< Node > l;
+ std::vector< Node > u;
+ std::vector< Node > inst;
+ qdi.getLowers( l );
+ qdi.getUppers( u );
+ Debug("qint-check-debug2") << "Get instantiation..." << std::endl;
+ if( fmqint->d_var_order[q]->getInstantiation( fmqint, l, u, inst ) ){
+ Trace("qint-inst") << "** Instantiate with ";
+ //just add the instance
+ InstMatch m;
+ for( unsigned j=0; j<inst.size(); j++) {
+ m.set( d_qe, q, j, inst[j] );
+ Trace("qint-inst") << inst[j] << " ";
+ }
+ Trace("qint-inst") << std::endl;
+ d_triedLemmas++;
+ if( d_qe->addInstantiation( q, m ) ){
+ Trace("qint-inst") << " ...added instantiation." << std::endl;
+ d_addedLemmas++;
+ }else{
+ Trace("qint-inst") << " ...duplicate instantiation" << std::endl;
+ //verify that instantiation is witness for current entry
+ if( Debug.isOn("qint-check-debug2") ){
+ Debug("qint-check-debug2") << "Check if : ";
+ std::vector< Node > exp_inst;
+ for( unsigned i=0; i<fmqint->getOrderedNumVars( q ); i++ ){
+ int index = fmqint->getOrderedVarNumToVarNum( q, i );
+ exp_inst.push_back( inst[ index ] );
+ Debug("qint-check-debug2") << inst[index] << " ";
+ }
+ Debug("qint-check-debug2") << " evaluates to " << qdi.getValue() << std::endl;
+ Assert( qid.evaluate( fmqint, exp_inst )==qdi.getValue() );
+ }
+ }
+ }else{
+ Trace("qint-inst") << "** Spurious instantiation." << std::endl;
+ }
+ }
+ qdi.increment();
+ }
+ }
+ return true;
+}
+
+bool QIntervalBuilder::doCheck( FirstOrderModelQInt * m, Node q, QIntDef & qid, Node n,
+ QIntVarNumIndex& vindex ) {
+ Assert( n.getKind()!=FORALL );
+ std::map< unsigned, QIntDef > children;
+ std::map< unsigned, Node > bchildren;
+ int varChCount = 0;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( n[i].getKind()==FORALL ){
+ bchildren[i] = Node::null();
+ }else if( n[i].getKind() == BOUND_VARIABLE ){
+ varChCount++;
+ bchildren[i] = n[i];
+ }else if( m->hasTerm( n[i] ) ){
+ bchildren[i] = m->getUsedRepresentative( n[i] );
+ }else{
+ if( !doCheck( m, q, children[i], n[i], vindex.d_var_index[i] ) ){
+ bchildren[i] = Node::null();
+ }
+ }
+ }
+ Trace("qint-check-debug") << "Compute Interpretation of " << n << " " << n.getKind() << std::endl;
+ if( n.getKind() == APPLY_UF || n.getKind() == VARIABLE || n.getKind() == SKOLEM ){
+ Node op = n.getKind() == APPLY_UF ? n.getOperator() : n;
+ //uninterpreted compose
+ qid.construct_compose( m, q, n, m->d_models[op], children, bchildren, varChCount, vindex );
+ }else if( !qid.construct_compose( m, q, n, NULL, children, bchildren, varChCount, vindex ) ){
+ Trace("qint-check-debug") << "** Cannot produce definition for " << n << std::endl;
+ return false;
+ }
+ Trace("qint-check-debug2") << "Definition for " << n << " is : " << std::endl;
+ qid.debugPrint("qint-check-debug2", m, q);
+ qid.simplify( m, q );
+ Trace("qint-check-debug") << "(Simplified) Definition for " << n << " is : " << std::endl;
+ qid.debugPrint("qint-check-debug", m, q);
+ Trace("qint-check-debug") << std::endl;
+ Assert( qid.isTotal( m, q ) );
+ return true;
+}
diff --git a/src/theory/quantifiers/qinterval_builder.h b/src/theory/quantifiers/qinterval_builder.h new file mode 100755 index 000000000..8f48776cc --- /dev/null +++ b/src/theory/quantifiers/qinterval_builder.h @@ -0,0 +1,155 @@ +/********************* */
+/*! \file qinterval_builder.h
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief qinterval model class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef QINTERVAL_BUILDER
+#define QINTERVAL_BUILDER
+
+#include "theory/quantifiers/model_builder.h"
+#include "theory/quantifiers/first_order_model.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class FirstOrderModelQInt;
+
+class QIntVarNumIndex
+{
+public:
+ std::map< int, int > d_var_num;
+ std::map< int, QIntVarNumIndex > d_var_index;
+};
+
+class QIntDef
+{
+private:
+ Node evaluate_r( FirstOrderModelQInt * m, std::vector< Node >& reps, unsigned depth );
+ Node evaluate_n_r( FirstOrderModelQInt * m, Node n, unsigned depth );
+ void construct_compose_r( FirstOrderModelQInt * m, Node q,
+ std::vector< Node >& l, std::vector< Node >& u, Node n, QIntDef * f,
+ std::vector< Node >& args,
+ std::map< unsigned, QIntDef >& children,
+ std::map< unsigned, Node >& bchildren,
+ QIntVarNumIndex& vindex,
+ unsigned depth );
+
+ void construct_enum_r( FirstOrderModelQInt * m, Node q, unsigned vn, unsigned depth, Node v );
+ int getEvIndex( FirstOrderModelQInt * m, Node n, bool exc = false );
+ void addEntry( FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u,
+ Node v, unsigned depth = 0 );
+ Node simplify_r( FirstOrderModelQInt * m, Node q, std::vector< Node >& il, std::vector< Node >& iu,
+ unsigned depth );
+ bool isTotal_r( FirstOrderModelQInt * m, Node q, std::vector< Node >& il, std::vector< Node >& iu,
+ unsigned depth );
+public:
+ QIntDef(){}
+ std::map< Node, QIntDef > d_def;
+ std::vector< Node > d_def_order;
+
+ void construct( FirstOrderModelQInt * m, std::vector< Node >& fapps, unsigned depth = 0 );
+ bool construct_compose( FirstOrderModelQInt * m, Node q, Node n, QIntDef * f,
+ std::map< unsigned, QIntDef >& children,
+ std::map< unsigned, Node >& bchildren, int varChCount,
+ QIntVarNumIndex& vindex );
+ bool construct_enum( FirstOrderModelQInt * m, Node q, unsigned vn );
+
+ Node evaluate( FirstOrderModelQInt * m, std::vector< Node >& reps ) { return evaluate_r( m, reps, 0 ); }
+ Node evaluate_n( FirstOrderModelQInt * m, Node n ) { return evaluate_n_r( m, n, 0 ); }
+
+ void debugPrint( const char * c, FirstOrderModelQInt * m, Node q, int t = 0 );
+ QIntDef * getChild( unsigned i );
+ Node getValue() { return d_def_order[0]; }
+ Node getLower( unsigned i ) { return i==0 ? Node::null() : d_def_order[i-1]; }
+ Node getUpper( unsigned i ) { return d_def_order[i]; }
+ Node getMaximum() { return d_def_order.empty() ? Node::null() : getUpper( d_def_order.size()-1 ); }
+ int getNumChildren() { return d_def_order.size(); }
+ bool isTotal( FirstOrderModelQInt * m, Node q );
+
+ Node simplify( FirstOrderModelQInt * m, Node q );
+ Node getFunctionValue( FirstOrderModelQInt * m, std::vector< Node >& vars, unsigned depth = 0 );
+
+ static void init_vec( FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u );
+ static void debugPrint( const char * c, FirstOrderModelQInt * m, Node q, std::vector< Node >& l, std::vector< Node >& u );
+};
+
+class QIntDefIter {
+private:
+ FirstOrderModelQInt * d_fm;
+ Node d_q;
+ void resetIndex( QIntDef * qid );
+public:
+ QIntDefIter( FirstOrderModelQInt * m, Node q, QIntDef * qid );
+ void debugPrint( const char * c, int t = 0 );
+ std::vector< QIntDef * > d_index_visited;
+ std::vector< int > d_index;
+ bool isFinished() { return d_index.empty(); }
+ bool increment( int index = -1 );
+ unsigned getSize() { return d_index.size(); }
+ Node getLower( int index );
+ Node getUpper( int index );
+ void getLowers( std::vector< Node >& reps );
+ void getUppers( std::vector< Node >& reps );
+ Node getValue();
+};
+
+
+class QuantVarOrder
+{
+private:
+ int initialize( Node n, int minVarIndex, QIntVarNumIndex& vindex );
+ int d_var_count;
+ Node d_q;
+ void debugPrint( const char * c, Node n, QIntVarNumIndex& vindex );
+public:
+ QuantVarOrder( Node q );
+ std::map< int, Node > d_num_to_var;
+ std::map< int, int > d_num_to_prev_num;
+ std::map< int, int > d_num_to_next_num;
+ std::map< Node, std::vector< int > > d_var_to_num;
+ std::map< int, int > d_var_num_index;
+ //std::map< Node, std::map< int, int > > d_var_occur;
+ //int getVarNum( Node n, int arg ) { return d_var_occur[n][arg]; }
+ unsigned getNumVars() { return d_var_count; }
+ Node getVar( int i ) { return d_num_to_var[i]; }
+ int getPrevNum( int i ) { return d_num_to_prev_num.find( i )!=d_num_to_prev_num.end() ? d_num_to_prev_num[i] : -1; }
+ int getNextNum( int i ) { return d_num_to_next_num.find( i )!=d_num_to_next_num.end() ? d_num_to_next_num[i] : -1; }
+ int getVarNumIndex( int i ) { return d_var_num_index[i]; }
+ bool getInstantiation( FirstOrderModelQInt * m, std::vector< Node >& l, std::vector< Node >& u,
+ std::vector< Node >& inst );
+ void debugPrint( const char * c );
+ QIntVarNumIndex d_var_occur;
+};
+
+class QIntervalBuilder : public QModelBuilder
+{
+private:
+ Node d_true;
+ bool doCheck( FirstOrderModelQInt * m, Node q, QIntDef & qid, Node n,
+ QIntVarNumIndex& vindex );
+public:
+ QIntervalBuilder( context::Context* c, QuantifiersEngine* qe );
+ //process build model
+ void processBuildModel(TheoryModel* m, bool fullModel);
+ //do exhaustive instantiation
+ bool doExhaustiveInstantiation( FirstOrderModel * fm, Node q, int effort );
+};
+
+
+}
+}
+}
+
+#endif
\ No newline at end of file diff --git a/src/theory/quantifiers/quant_conflict_find.cpp b/src/theory/quantifiers/quant_conflict_find.cpp new file mode 100755 index 000000000..d7111ea39 --- /dev/null +++ b/src/theory/quantifiers/quant_conflict_find.cpp @@ -0,0 +1,1907 @@ +/********************* */
+/*! \file quant_conflict_find.cpp
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief quant conflict find class
+ **
+ **/
+
+#include <vector>
+
+#include "theory/quantifiers/quant_conflict_find.h"
+#include "theory/quantifiers/quant_util.h"
+#include "theory/theory_engine.h"
+
+using namespace CVC4;
+using namespace CVC4::kind;
+using namespace CVC4::theory;
+using namespace CVC4::theory::quantifiers;
+using namespace std;
+
+namespace CVC4 {
+
+/*
+Node QcfNodeIndex::existsTerm( QuantConflictFind * qcf, Node n, int index ) {
+ if( index==(int)n.getNumChildren() ){
+ if( d_children.empty() ){
+ return Node::null();
+ }else{
+ return d_children.begin()->first;
+ }
+ }else{
+ Node r = qcf->getRepresentative( n[index] );
+ if( d_children.find( r )==d_children.end() ){
+ return n;
+ }else{
+ return d_children[r].existsTerm( qcf, n, index+1 );
+ }
+ }
+}
+
+
+Node QcfNodeIndex::addTerm( QuantConflictFind * qcf, Node n, int index ) {
+ if( index==(int)n.getNumChildren() ){
+ if( d_children.empty() ){
+ d_children[ n ].clear();
+ return n;
+ }else{
+ return d_children.begin()->first;
+ }
+ }else{
+ Node r = qcf->getRepresentative( n[index] );
+ return d_children[r].addTerm( qcf, n, index+1 );
+ }
+}
+
+bool QcfNodeIndex::addTermEq( QuantConflictFind * qcf, Node n1, Node n2, int index ) {
+ if( index==(int)n1.getNumChildren() ){
+ Node n = addTerm( qcf, n2 );
+ return n==n2;
+ }else{
+ Node r = qcf->getRepresentative( n1[index] );
+ return d_children[r].addTermEq( qcf, n1, n2, index+1 );
+ }
+}
+*/
+
+
+
+Node QcfNodeIndex::existsTerm( Node n, std::vector< Node >& reps, int index ) {
+ if( index==(int)reps.size() ){
+ if( d_children.empty() ){
+ return Node::null();
+ }else{
+ return d_children.begin()->first;
+ }
+ }else{
+ if( d_children.find( reps[index] )==d_children.end() ){
+ return Node::null();
+ }else{
+ return d_children[reps[index]].existsTerm( n, reps, index+1 );
+ }
+ }
+}
+
+Node QcfNodeIndex::addTerm( Node n, std::vector< Node >& reps, int index ) {
+ if( index==(int)reps.size() ){
+ if( d_children.empty() ){
+ d_children[ n ].clear();
+ return n;
+ }else{
+ return d_children.begin()->first;
+ }
+ }else{
+ return d_children[reps[index]].addTerm( n, reps, index+1 );
+ }
+}
+
+bool QcfNodeIndex::addTermEq( Node n1, Node n2, std::vector< Node >& reps1, std::vector< Node >& reps2, int index ) {
+ if( index==(int)reps1.size() ){
+ Node n = addTerm( n2, reps2 );
+ return n==n2;
+ }else{
+ return d_children[reps1[index]].addTermEq( n1, n2, reps1, reps2, index+1 );
+ }
+}
+
+
+
+void QcfNodeIndex::debugPrint( const char * c, int t ) {
+ for( std::map< Node, QcfNodeIndex >::iterator it = d_children.begin(); it != d_children.end(); ++it ){
+ if( !it->first.isNull() ){
+ for( int j=0; j<t; j++ ){ Trace(c) << " "; }
+ Trace(c) << it->first << " : " << std::endl;
+ it->second.debugPrint( c, t+1 );
+ }
+ }
+}
+
+EqRegistry::EqRegistry( context::Context* c ) : d_active( c, false ){//: d_t_eqc( c ){
+
+}
+
+void EqRegistry::debugPrint( const char * c, int t ) {
+ d_qni.debugPrint( c, t );
+}
+
+//QcfNode::QcfNode( context::Context* c ) : d_parent( NULL ){
+// d_reg[0] = NULL;
+// d_reg[1] = NULL;
+//}
+
+QuantConflictFind::QuantConflictFind( QuantifiersEngine * qe, context::Context* c ) :
+QuantifiersModule( qe ),
+d_c( c ),
+d_conflict( c, false ),
+d_qassert( c ) {
+ d_fid_count = 0;
+ d_true = NodeManager::currentNM()->mkConst<bool>(true);
+ d_false = NodeManager::currentNM()->mkConst<bool>(false);
+ getFunctionId( d_true );
+ getFunctionId( d_false );
+}
+
+int QuantConflictFind::getFunctionId( Node f ) {
+ std::map< Node, int >::iterator it = d_fid.find( f );
+ if( it==d_fid.end() ){
+ d_fid[f] = d_fid_count;
+ d_fid_count++;
+ return d_fid[f];
+ }
+ return it->second;
+}
+
+bool QuantConflictFind::isLessThan( Node a, Node b ) {
+ Assert( a.getKind()==APPLY_UF );
+ Assert( b.getKind()==APPLY_UF );
+ /*
+ if( a.getOperator()==b.getOperator() ){
+ for( unsigned i=0; i<a.getNumChildren(); i++ ){
+ Node acr = getRepresentative( a[i] );
+ Node bcr = getRepresentative( b[i] );
+ if( acr<bcr ){
+ return true;
+ }else if( acr>bcr ){
+ return false;
+ }
+ }
+ return false;
+ }else{
+ */
+ return getFunctionId( a.getOperator() )<getFunctionId( b.getOperator() );
+ //}
+}
+
+Node QuantConflictFind::getFunction( Node n, bool isQuant ) {
+ if( isQuant && !quantifiers::TermDb::hasBoundVarAttr( n ) ){
+ return n;
+ }else if( n.getKind()==APPLY_UF ){
+ std::map< Node, Node >::iterator it = d_op_node.find( n.getOperator() );
+ if( it==d_op_node.end() ){
+ std::vector< Node > children;
+ children.push_back( n.getOperator() );
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ children.push_back( getFv( n[i].getType() ) );
+ }
+ Node nn = NodeManager::currentNM()->mkNode( APPLY_UF, children );
+ d_op_node[n.getOperator()] = nn;
+ return nn;
+ }else{
+ return it->second;
+ }
+ }else{
+ //should be a variable
+ return Node::null();
+ }
+}
+
+Node QuantConflictFind::getFv( TypeNode tn ) {
+ if( d_fv.find( tn )==d_fv.end() ){
+ std::stringstream ss;
+ ss << "_u";
+ d_fv[tn] = NodeManager::currentNM()->mkSkolem( ss.str(), tn, "is for QCF" );
+ }
+ return d_fv[tn];
+}
+
+Node QuantConflictFind::mkEqNode( Node a, Node b ) {
+ if( a.getType().isBoolean() ){
+ return a.iffNode( b );
+ }else{
+ return a.eqNode( b );
+ }
+}
+
+//-------------------------------------------------- registration
+
+/*
+void QuantConflictFind::registerAssertions( std::vector< Node >& assertions ) {
+ for( unsigned i=0; i<assertions.size(); i++ ){
+ registerAssertion( assertions[i] );
+ }
+}
+
+void QuantConflictFind::registerAssertion( Node n ) {
+ if( n.getKind()==FORALL ){
+ registerNode( Node::null(), n[1], NULL, true, true );
+ }else{
+ if( n.getType().isBoolean() ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ registerAssertion( n[i] );
+ }
+ }
+ }
+}
+*/
+void QuantConflictFind::registerNode( Node q, Node n, bool hasPol, bool pol ) {
+ //qcf->d_node = n;
+ bool recurse = true;
+ if( n.getKind()!=OR && n.getKind()!=AND && n.getKind()!=IFF && n.getKind()!=ITE && n.getKind()!=NOT ){
+ if( quantifiers::TermDb::hasBoundVarAttr( n ) ){
+ //literals
+ for( unsigned i=0; i<2; i++ ){
+ if( !hasPol || ( pol!=(i==0) ) ){
+ EqRegistry * eqr = getEqRegistry( i==0, n );
+ if( eqr ){
+ d_qinfo[q].d_rel_eqr[ eqr ] = true;
+ }
+ }
+ }
+ if( n.getKind()==APPLY_UF ||
+ ( n.getKind()==EQUAL && ( n[0].getKind()==BOUND_VARIABLE || n[1].getKind()==BOUND_VARIABLE ) ) ){
+ flatten( q, n );
+ }else{
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ flatten( q, n[i] );
+ }
+ }
+ }else{
+ Trace("qcf-qregister") << " RegisterGroundLit : " << n;
+ }
+ recurse = false;
+ }
+ if( recurse ){
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ bool newHasPol;
+ bool newPol;
+ QuantPhaseReq::getPolarity( n, i, hasPol, pol, newHasPol, newPol );
+ //QcfNode * qcfc = new QcfNode( d_c );
+ //qcfc->d_parent = qcf;
+ //qcf->d_child[i] = qcfc;
+ registerNode( q, n[i], newHasPol, newPol );
+ }
+ }
+}
+
+void QuantConflictFind::flatten( Node q, Node n ) {
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ if( quantifiers::TermDb::hasBoundVarAttr( n[i] ) && n.getKind()!=BOUND_VARIABLE ){
+ if( d_qinfo[q].d_var_num.find( n[i] )==d_qinfo[q].d_var_num.end() ){
+ //Trace("qcf-qregister") << " Flatten term " << n[i] << std::endl;
+ d_qinfo[q].d_var_num[n[i]] = d_qinfo[q].d_vars.size();
+ d_qinfo[q].d_vars.push_back( n[i] );
+ flatten( q, n[i] );
+ }
+ }
+ }
+}
+
+void QuantConflictFind::registerQuantifier( Node q ) {
+ d_quants.push_back( q );
+ d_quant_id[q] = d_quants.size();
+ Trace("qcf-qregister") << "Register ";
+ debugPrintQuant( "qcf-qregister", q );
+ Trace("qcf-qregister") << " : " << q << std::endl;
+
+ //register the variables
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ d_qinfo[q].d_var_num[q[0][i]] = i;
+ d_qinfo[q].d_vars.push_back( q[0][i] );
+ }
+
+ //make QcfNode structure
+ Trace("qcf-qregister") << "- Get relevant equality/disequality pairs, calculate flattening..." << std::endl;
+ //d_qinfo[q].d_cf = new QcfNode( d_c );
+ registerNode( q, q[1], true, true );
+
+ //debug print
+ Trace("qcf-qregister") << "- Flattened structure is :" << std::endl;
+ Trace("qcf-qregister") << " ";
+ debugPrintQuantBody( "qcf-qregister", q, q[1] );
+ Trace("qcf-qregister") << std::endl;
+ if( d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){
+ Trace("qcf-qregister") << " with additional constraints : " << std::endl;
+ for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){
+ Trace("qcf-qregister") << " ?x" << j << " = ";
+ debugPrintQuantBody( "qcf-qregister", q, d_qinfo[q].d_vars[j], false );
+ Trace("qcf-qregister") << std::endl;
+ }
+ }
+
+ Trace("qcf-qregister") << "- Make match gen structure..." << std::endl;
+ d_qinfo[q].d_mg = new MatchGen( this, q, q[1], true );
+
+ for( unsigned j=q[0].getNumChildren(); j<d_qinfo[q].d_vars.size(); j++ ){
+ d_qinfo[q].d_var_mg[j] = new MatchGen( this, q, d_qinfo[q].d_vars[j], false, true );
+ if( !d_qinfo[q].d_var_mg[j]->isValid() ){
+ d_qinfo[q].d_mg->setInvalid();
+ break;
+ }
+ }
+
+ Trace("qcf-qregister") << "Done registering quantifier." << std::endl;
+}
+
+EqRegistry * QuantConflictFind::getEqRegistry( bool polarity, Node lit, bool doCreate ) {
+ Assert( quantifiers::TermDb::hasBoundVarAttr( lit ) );
+ int i;
+ Node f1;
+ Node f2;
+ if( lit.getKind()==EQUAL ){
+ i = polarity ? 0 : 1;
+ f1 = getFunction( lit[0], true );
+ f2 = getFunction( lit[1], true );
+ }else if( lit.getKind()==APPLY_UF ){
+ i = 0;
+ f1 = getFunction( lit, true );
+ f2 = polarity ? d_true : d_false;
+ }
+ if( !f1.isNull() && !f2.isNull() ){
+ if( d_eqr[i][f1].find( f2 )==d_eqr[i][f1].end() ){
+ if( doCreate ){
+ Trace("qcf-register") << "RegisterEqr : " << f1 << " " << f2 << ", polarity = " << (i==0) << std::endl;
+ d_eqr[i][f1][f2] = new EqRegistry( d_c );
+ d_eqr[i][f2][f1] = d_eqr[i][f1][f2];
+ }else{
+ return NULL;
+ }
+ }
+ return d_eqr[i][f1][f2];
+ }else{
+ return NULL;
+ }
+}
+
+void QuantConflictFind::getEqRegistryApps( Node lit, std::vector< Node >& terms ) {
+ Assert( quantifiers::TermDb::hasBoundVarAttr( lit ) );
+ if( lit.getKind()==EQUAL ){
+ bool allUf = false;
+ for( unsigned i=0; i<2; i++ ){
+ if( quantifiers::TermDb::hasBoundVarAttr( lit[i] ) ){
+ if( lit[i].getKind()==BOUND_VARIABLE ){
+ //do not handle variable equalities
+ terms.clear();
+ return;
+ }else{
+ allUf = allUf && lit[i].getKind()==APPLY_UF;
+ terms.push_back( lit[i] );
+ }
+ }
+ }
+ if( terms.size()==2 && allUf && isLessThan( terms[1], terms[0] ) ){
+ Node t = terms[0];
+ terms[0] = terms[1];
+ terms[1] = t;
+ }
+ }else if( lit.getKind()==APPLY_UF ){
+ terms.push_back( lit );
+ }
+}
+
+int QuantConflictFind::evaluate( Node n ) {
+ int ret = 0;
+ if( n.getKind()==EQUAL ){
+ Node n1 = getTerm( n[0] );
+ Node n2 = getTerm( n[1] );
+ Debug("qcf-eval") << "Evaluate : Normalize " << n << " to " << n1 << " = " << n2 << std::endl;
+ if( areEqual( n1, n2 ) ){
+ ret = 1;
+ }else if( areDisequal( n1, n2 ) ){
+ ret = -1;
+ }
+ }else if( n.getKind()==APPLY_UF ){
+ Node nn = getTerm( n );
+ Debug("qcf-eval") << "Evaluate : Normalize " << nn << " to " << n << std::endl;
+ if( areEqual( nn, d_true ) ){
+ ret = 1;
+ }else if( areEqual( nn, d_false ) ){
+ ret = -1;
+ }
+ }else if( n.getKind()==NOT ){
+ return -evaluate( n[0] );
+ }else if( n.getKind()==ITE ){
+ int cev1 = evaluate( n[0] );
+ int cevc[2] = { 0, 0 };
+ for( unsigned i=0; i<2; i++ ){
+ if( ( i==0 && cev1!=-1 ) || ( i==1 && cev1!=1 ) ){
+ cevc[i] = evaluate( n[i+1] );
+ if( cev1!=0 ){
+ ret = cevc[i];
+ break;
+ }else if( cevc[i]==0 ){
+ break;
+ }
+ }
+ }
+ if( ret==0 && cevc[0]!=0 && cevc[0]==cevc[1] ){
+ ret = cevc[0];
+ }
+ }else if( n.getKind()==IFF ){
+ int cev1 = evaluate( n[0] );
+ if( cev1!=0 ){
+ int cev2 = evaluate( n[1] );
+ if( cev2!=0 ){
+ ret = cev1==cev2 ? 1 : -1;
+ }
+ }
+
+ }else{
+ int ssval = 0;
+ if( n.getKind()==OR ){
+ ssval = 1;
+ }else if( n.getKind()==AND ){
+ ssval = -1;
+ }
+ bool isUnk = false;
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ int cev = evaluate( n[i] );
+ if( cev==ssval ){
+ ret = ssval;
+ break;
+ }else if( cev==0 ){
+ isUnk = true;
+ }
+ }
+ if( ret==0 && !isUnk ){
+ ret = -ssval;
+ }
+ }
+ Debug("qcf-eval") << "Evaluate " << n << " to " << ret << std::endl;
+ return ret;
+}
+
+//-------------------------------------------------- handling assertions / eqc
+
+void QuantConflictFind::assertNode( Node q ) {
+ Trace("qcf-proc") << "QCF : assertQuantifier : ";
+ debugPrintQuant("qcf-proc", q);
+ Trace("qcf-proc") << std::endl;
+ d_qassert.push_back( q );
+ //set the eqRegistries that this depends on to true
+ for( std::map< EqRegistry *, bool >::iterator it = d_qinfo[q].d_rel_eqr.begin(); it != d_qinfo[q].d_rel_eqr.end(); ++it ){
+ it->first->d_active.set( true );
+ }
+}
+
+eq::EqualityEngine * QuantConflictFind::getEqualityEngine() {
+ //return ((uf::TheoryUF*)d_quantEngine->getTheoryEngine()->theoryOf( theory::THEORY_UF ))->getEqualityEngine();
+ return d_quantEngine->getTheoryEngine()->getMasterEqualityEngine();
+}
+bool QuantConflictFind::areEqual( Node n1, Node n2 ) {
+ return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areEqual( n1,n2 );
+}
+bool QuantConflictFind::areDisequal( Node n1, Node n2 ) {
+ return getEqualityEngine()->hasTerm( n1 ) && getEqualityEngine()->hasTerm( n2 ) && getEqualityEngine()->areDisequal( n1,n2, false );
+}
+Node QuantConflictFind::getRepresentative( Node n ) {
+ if( getEqualityEngine()->hasTerm( n ) ){
+ return getEqualityEngine()->getRepresentative( n );
+ }else{
+ return n;
+ }
+}
+Node QuantConflictFind::getTerm( Node n ) {
+ if( n.getKind()==APPLY_UF ){
+ computeArgReps( n );
+ Node nn = d_uf_terms[n.getOperator()].existsTerm( n, d_arg_reps[n] );
+ if( !nn.isNull() ){
+ return nn;
+ }
+ }
+ return n;
+}
+
+QuantConflictFind::EqcInfo * QuantConflictFind::getEqcInfo( Node n, bool doCreate ) {
+ /*
+ NodeBoolMap::iterator it = d_eqc.find( n );
+ if( it==d_eqc.end() ){
+ if( doCreate ){
+ d_eqc[n] = true;
+ }else{
+ //equivalence class does not currently exist
+ return NULL;
+ }
+ }else{
+ //should only ask for valid equivalence classes
+ Assert( (*it).second );
+ }
+ */
+ std::map< Node, EqcInfo * >::iterator it2 = d_eqc_info.find( n );
+ if( it2==d_eqc_info.end() ){
+ if( doCreate ){
+ EqcInfo * eqci = new EqcInfo( d_c );
+ d_eqc_info[n] = eqci;
+ return eqci;
+ }else{
+ return NULL;
+ }
+ }
+ return it2->second;
+}
+
+/** new node */
+void QuantConflictFind::newEqClass( Node n ) {
+ Trace("qcf-proc-debug") << "QCF : newEqClass : " << n << std::endl;
+
+ Trace("qcf-proc2-debug") << "QCF : finished newEqClass : " << n << std::endl;
+}
+
+/** merge */
+void QuantConflictFind::merge( Node a, Node b ) {
+ if( b.getKind()==EQUAL ){
+ if( a==d_true ){
+ //will merge anyways
+ //merge( b[0], b[1] );
+ }else if( a==d_false ){
+ assertDisequal( b[0], b[1] );
+ }
+ }else{
+ Trace("qcf-proc") << "QCF : merge : " << a << " " << b << std::endl;
+ EqcInfo * eqc_b = getEqcInfo( b, false );
+ EqcInfo * eqc_a = NULL;
+ if( eqc_b ){
+ eqc_a = getEqcInfo( a );
+ //move disequalities of b into a
+ for( NodeBoolMap::iterator it = eqc_b->d_diseq.begin(); it != eqc_b->d_diseq.end(); ++it ){
+ if( (*it).second ){
+ Node n = (*it).first;
+ EqcInfo * eqc_n = getEqcInfo( n, false );
+ Assert( eqc_n );
+ if( !eqc_n->isDisequal( a ) ){
+ Assert( !eqc_a->isDisequal( n ) );
+ eqc_n->setDisequal( a );
+ eqc_a->setDisequal( n );
+ //setEqual( eqc_a, eqc_b, a, n, false );
+ }
+ eqc_n->setDisequal( b, false );
+ }
+ }
+ /*
+ //move all previous EqcRegistry's regarding equalities within b
+ for( NodeBoolMap::iterator it = eqc_b->d_rel_eqr_e.begin(); it != eqc_b->d_rel_eqr_e.end(); ++it ){
+ if( (*it).second ){
+ eqc_a->d_rel_eqr_e[(*it).first] = true;
+ }
+ }
+ */
+ }
+ //process new equalities
+ //setEqual( eqc_a, eqc_b, a, b, true );
+ Trace("qcf-proc2") << "QCF : finished merge : " << a << " " << b << std::endl;
+ }
+}
+
+/** assert disequal */
+void QuantConflictFind::assertDisequal( Node a, Node b ) {
+ a = getRepresentative( a );
+ b = getRepresentative( b );
+ Trace("qcf-proc") << "QCF : assert disequal : " << a << " " << b << std::endl;
+ EqcInfo * eqc_a = getEqcInfo( a );
+ EqcInfo * eqc_b = getEqcInfo( b );
+ if( !eqc_a->isDisequal( b ) ){
+ Assert( !eqc_b->isDisequal( a ) );
+ eqc_b->setDisequal( a );
+ eqc_a->setDisequal( b );
+ //setEqual( eqc_a, eqc_b, a, b, false );
+ }
+ Trace("qcf-proc2") << "QCF : finished assert disequal : " << a << " " << b << std::endl;
+}
+
+//-------------------------------------------------- check function
+
+/** check */
+void QuantConflictFind::check( Theory::Effort level ) {
+ Trace("qcf-check") << "QCF : check : " << level << std::endl;
+ if( d_conflict ){
+ Trace("qcf-check2") << "QCF : finished check : already in conflict." << std::endl;
+ if( level>=Theory::EFFORT_FULL ){
+ Assert( false );
+ }
+ }else{
+ bool addedLemma = false;
+ if( d_performCheck ){
+ ++(d_statistics.d_inst_rounds);
+ double clSet = 0;
+ if( Trace.isOn("qcf-engine") ){
+ clSet = double(clock())/double(CLOCKS_PER_SEC);
+ Trace("qcf-engine") << "---Conflict Find Engine Round, effort = " << level << "---" << std::endl;
+ }
+ Trace("qcf-check") << "Compute relevant equalities..." << std::endl;
+ computeRelevantEqr();
+
+ Trace("qcf-debug") << std::endl;
+ debugPrint("qcf-debug");
+ Trace("qcf-debug") << std::endl;
+
+
+ Trace("qcf-check") << "Checking quantified formulas..." << std::endl;
+ for( unsigned j=0; j<d_qassert.size(); j++ ){
+ Node q = d_qassert[j];
+ Trace("qcf-check") << "Check quantified formula ";
+ debugPrintQuant("qcf-check", q);
+ Trace("qcf-check") << " : " << q << "..." << std::endl;
+
+ Assert( d_qinfo.find( q )!=d_qinfo.end() );
+ if( d_qinfo[q].d_mg->isValid() ){
+ d_qinfo[q].reset_round( this );
+ //try to make a matches making the body false
+ d_qinfo[q].d_mg->reset( this, false, q );
+ while( d_qinfo[q].d_mg->getNextMatch( this, q ) ){
+
+ Trace("qcf-check") << "*** Produced match : " << std::endl;
+ d_qinfo[q].debugPrintMatch("qcf-check");
+ Trace("qcf-check") << std::endl;
+
+ if( !d_qinfo[q].isMatchSpurious( this ) ){
+ std::vector< int > assigned;
+ if( d_qinfo[q].completeMatch( this, q, assigned ) ){
+ InstMatch m;
+ for( unsigned i=0; i<q[0].getNumChildren(); i++ ){
+ Node cv = d_qinfo[q].getCurrentValue( d_qinfo[q].d_match[i] );
+ Debug("qcf-check-inst") << "INST : " << i << " -> " << cv << ", from " << d_qinfo[q].d_match[i] << std::endl;
+ m.set( d_quantEngine, q, i, cv );
+ }
+ if( Debug.isOn("qcf-check-inst") ){
+ Node inst = d_quantEngine->getInstantiation( q, m );
+ Debug("qcf-check-inst") << "Check instantiation " << inst << "..." << std::endl;
+ Assert( evaluate( inst )==-1 );
+ }
+ if( d_quantEngine->addInstantiation( q, m ) ){
+ Trace("qcf-check") << " ... Added instantiation" << std::endl;
+ d_quantEngine->flushLemmas();
+ d_conflict.set( true );
+ addedLemma = true;
+ ++(d_statistics.d_conflict_inst);
+ break;
+ }else{
+ Trace("qcf-check") << " ... Failed to add instantiation" << std::endl;
+ Assert( false );
+ }
+ //clean up assigned
+ for( unsigned i=0; i<assigned.size(); i++ ){
+ d_qinfo[q].d_match.erase( assigned[i] );
+ }
+ }else{
+ Trace("qcf-check") << " ... Spurious instantiation (cannot assign unassigned variables)" << std::endl;
+ }
+ }else{
+ Trace("qcf-check") << " ... Spurious instantiation (does not meet variable constraints)" << std::endl;
+ }
+ }
+ }
+ if( addedLemma ){
+ break;
+ }
+ }
+ if( Trace.isOn("qcf-engine") ){
+ double clSet2 = double(clock())/double(CLOCKS_PER_SEC);
+ Trace("qcf-engine") << "Finished conflict find engine, time = " << (clSet2-clSet) << ", addedLemma = " << addedLemma << std::endl;
+ }
+ }
+ Trace("qcf-check2") << "QCF : finished check : " << level << std::endl;
+ }
+}
+
+bool QuantConflictFind::needsCheck( Theory::Effort level ) {
+ d_performCheck = false;
+ if( !d_conflict ){
+ if( level==Theory::EFFORT_FULL ){
+ d_performCheck = options::qcfWhenMode()==QCF_WHEN_MODE_DEFAULT;
+ }else if( level==Theory::EFFORT_STANDARD ){
+ d_performCheck = options::qcfWhenMode()==QCF_WHEN_MODE_STD;
+ }
+ }
+ return d_performCheck;
+}
+
+void QuantConflictFind::computeRelevantEqr() {
+ //first, reset information
+ for( unsigned i=0; i<2; i++ ){
+ for( std::map< Node, std::map< Node, EqRegistry * > >::iterator it = d_eqr[i].begin(); it != d_eqr[i].end(); ++it ){
+ for( std::map< Node, EqRegistry * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
+ it2->second->clear();
+ }
+ }
+ }
+ d_uf_terms.clear();
+ d_eqc_uf_terms.clear();
+ d_eqcs.clear();
+ d_arg_reps.clear();
+
+ //which nodes are irrelevant for disequality matches
+ std::map< Node, bool > irrelevant_dnode;
+ //which eqc we have processed
+ std::map< Node, bool > process_eqc;
+ //now, store matches
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( getEqualityEngine() );
+ while( !eqcs_i.isFinished() ){
+ Node r = (*eqcs_i);
+ d_eqcs[r.getType()].push_back( r );
+ EqcInfo * eqcir = getEqcInfo( r, false );
+ //get relevant nodes that we are disequal from
+ std::vector< Node > deqc;
+ if( eqcir ){
+ for( NodeBoolMap::iterator it = eqcir->d_diseq.begin(); it != eqcir->d_diseq.end(); ++it ){
+ if( (*it).second ){
+ Node rd = (*it).first;
+ //if we have processed the other direction
+ if( process_eqc.find( rd )!=process_eqc.end() ){
+ eq::EqClassIterator eqcd_i = eq::EqClassIterator( rd, getEqualityEngine() );
+ while( !eqcd_i.isFinished() ){
+ Node nd = (*eqcd_i);
+ if( irrelevant_dnode.find( nd )==irrelevant_dnode.end() ){
+ deqc.push_back( nd );
+ }
+ ++eqcd_i;
+ }
+ }
+ }
+ }
+ }
+ //the relevant nodes in this eqc
+ std::vector< Node > eqc;
+ //process disequalities
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( r, getEqualityEngine() );
+ while( !eqc_i.isFinished() ){
+ Node n = (*eqc_i);
+ bool isRedundant;
+ if( n.getKind()==APPLY_UF ){
+ computeArgReps( n );
+ Node nadd = d_eqc_uf_terms[r][n.getOperator()].addTerm( n, d_arg_reps[n] );
+ isRedundant = (nadd!=n);
+ d_uf_terms[n.getOperator()].addTerm( n, d_arg_reps[n] );
+ }else{
+ isRedundant = false;
+ }
+ //process all relevant equalities and disequalities to n
+ for( unsigned index=0; index<2; index++ ){
+ std::map< Node, std::map< Node, EqRegistry * > >::iterator itn[2];
+ itn[0] = d_eqr[index].find( n );
+ Node fn;
+ if( n.getKind()==APPLY_UF && !isRedundant ){
+ fn = getFunction( n );
+ itn[1] = d_eqr[index].find( fn );
+ }
+ //for n, fn...
+ bool relevant = false;
+ for( unsigned j=0; j<2; j++ ){
+ //if this node is relevant as an ground term or f-application
+ if( ( j==0 || !fn.isNull() ) && itn[j]!=d_eqr[index].end() ){
+ relevant = true;
+ std::vector< Node >& rel_nodes = index==0 ? eqc : deqc;
+ for( unsigned i=0; i<rel_nodes.size(); i++ ){
+ Node m = rel_nodes[i];
+ Node fm;
+ if( m.getKind()==APPLY_UF ){
+ fm = getFunction( m );
+ }
+ //process equality/disequality
+ if( j==1 ){
+ //fn with m
+ std::map< Node, EqRegistry * >::iterator itm = itn[j]->second.find( m );
+ if( itm!=itn[j]->second.end() ){
+ if( itm->second->d_qni.addTerm( n, d_arg_reps[n] )==n ){
+ Trace("qcf-reqr") << "Add relevant : " << n << (index==0?"":"!") << "=" << m << " for ";
+ Trace("qcf-reqr") << fn << " " << m << std::endl;
+ }
+ }
+ }
+ if( !fm.isNull() ){
+ std::map< Node, EqRegistry * >::iterator itm = itn[j]->second.find( fm );
+ if( itm!=itn[j]->second.end() ){
+ Assert( d_arg_reps.find( m )!=d_arg_reps.end() );
+ if( j==0 ){
+ //n with fm
+ if( itm->second->d_qni.addTerm( m, d_arg_reps[m] )==m ){
+ Trace("qcf-reqr") << "Add relevant : " << n << (index==0?"":"!") << "=" << m << " for ";
+ Trace("qcf-reqr") << n << " " << fm << std::endl;
+ }
+ }else{
+ //fn with fm
+ bool mltn = isLessThan( m, n );
+ for( unsigned i=0; i<2; i++ ){
+ if( i==0 || m.getOperator()==n.getOperator() ){
+ Node am = ((i==0)==mltn) ? n : m;
+ Node an = ((i==0)==mltn) ? m : n;
+ if( itm->second->d_qni.addTermEq( an, am, d_arg_reps[n], d_arg_reps[m] ) ){
+ Trace("qcf-reqr") << "Add relevant (eq) : " << an << (index==0?"":"!") << "=" << am << " for ";
+ Trace("qcf-reqr") << fn << " " << fm << std::endl;
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if( !relevant ){
+ //if not relevant for disequalities, store it
+ if( index==1 ){
+ irrelevant_dnode[n] = true;
+ }
+ }else{
+ //if relevant for equalities, store it
+ if( index==0 ){
+ eqc.push_back( n );
+ }
+ }
+ }
+ ++eqc_i;
+ }
+ process_eqc[r] = true;
+ ++eqcs_i;
+ }
+}
+
+void QuantConflictFind::computeArgReps( Node n ) {
+ if( d_arg_reps.find( n )==d_arg_reps.end() ){
+ for( unsigned j=0; j<n.getNumChildren(); j++ ){
+ d_arg_reps[n].push_back( getRepresentative( n[j] ) );
+ }
+ }
+}
+
+
+void QuantConflictFind::QuantInfo::reset_round( QuantConflictFind * p ) {
+ d_match.clear();
+ d_curr_var_deq.clear();
+ //add built-in variable constraints
+ for( unsigned r=0; r<2; r++ ){
+ for( std::map< int, std::vector< Node > >::iterator it = d_var_constraint[r].begin();
+ it != d_var_constraint[r].end(); ++it ){
+ for( unsigned j=0; j<it->second.size(); j++ ){
+ Node rr = it->second[j];
+ if( !isVar( rr ) ){
+ rr = p->getRepresentative( rr );
+ }
+ if( addConstraint( p, it->first, rr, r==0 )==-1 ){
+ d_var_constraint[0].clear();
+ d_var_constraint[1].clear();
+ //quantified formula is actually equivalent to true
+ Trace("qcf-qregister") << "Quantifier is equivalent to true!!!" << std::endl;
+ d_mg->d_children.clear();
+ d_mg->d_n = NodeManager::currentNM()->mkConst( true );
+ d_mg->d_type = MatchGen::typ_ground;
+ return;
+ }
+ }
+ }
+ }
+ d_mg->reset_round( p );
+ for( std::map< int, MatchGen * >::iterator it = d_var_mg.begin(); it != d_var_mg.end(); ++it ){
+ it->second->reset_round( p );
+ }
+}
+
+int QuantConflictFind::QuantInfo::getCurrentRepVar( int v ) {
+ std::map< int, Node >::iterator it = d_match.find( v );
+ if( it!=d_match.end() ){
+ int vn = getVarNum( it->second );
+ if( vn!=-1 ){
+ //int vr = getCurrentRepVar( vn );
+ //d_match[v] = d_vars[vr];
+ //return vr;
+ return getCurrentRepVar( vn );
+ }
+ }
+ return v;
+}
+
+Node QuantConflictFind::QuantInfo::getCurrentValue( Node n ) {
+ int v = getVarNum( n );
+ if( v==-1 ){
+ return n;
+ }else{
+ std::map< int, Node >::iterator it = d_match.find( v );
+ if( it==d_match.end() ){
+ return n;
+ }else{
+ Assert( getVarNum( it->second )!=v );
+ return getCurrentValue( it->second );
+ }
+ }
+}
+
+bool QuantConflictFind::QuantInfo::getCurrentCanBeEqual( QuantConflictFind * p, int v, Node n ) {
+ //check disequalities
+ for( std::map< Node, int >::iterator it = d_curr_var_deq[v].begin(); it != d_curr_var_deq[v].end(); ++it ){
+ Node cv = getCurrentValue( it->first );
+ Debug("qcf-ccbe") << "compare " << cv << " " << n << std::endl;
+ if( cv==n ){
+ return false;
+ }else if( !isVar( n ) && !isVar( cv ) ){
+ //they must actually be disequal
+ if( !p->areDisequal( n, cv ) ){
+ return false;
+ }
+ }
+ }
+ return true;
+}
+
+int QuantConflictFind::QuantInfo::addConstraint( QuantConflictFind * p, int v, Node n, bool polarity ) {
+ v = getCurrentRepVar( v );
+ int vn = getVarNum( n );
+ vn = vn==-1 ? -1 : getCurrentRepVar( vn );
+ n = getCurrentValue( n );
+ return addConstraint( p, v, n, vn, polarity, false );
+}
+
+int QuantConflictFind::QuantInfo::addConstraint( QuantConflictFind * p, int v, Node n, int vn, bool polarity, bool doRemove ) {
+ //for handling equalities between variables, and disequalities involving variables
+ Debug("qcf-match-debug") << "- " << (doRemove ? "un" : "" ) << "constrain : " << v << " -> " << n << " (cv=" << getCurrentValue( n ) << ")";
+ Debug("qcf-match-debug") << ", (vn=" << vn << "), polarity = " << polarity << std::endl;
+ Assert( doRemove || n==getCurrentValue( n ) );
+ Assert( doRemove || v==getCurrentRepVar( v ) );
+ Assert( doRemove || vn==getCurrentRepVar( getVarNum( n ) ) );
+ if( polarity ){
+ if( vn!=v ){
+ if( doRemove ){
+ if( vn!=-1 ){
+ //if set to this in the opposite direction, clean up opposite instead
+ std::map< int, Node >::iterator itmn = d_match.find( vn );
+ if( itmn!=d_match.end() && itmn->second==d_vars[v] ){
+ return addConstraint( p, vn, d_vars[v], v, true, true );
+ }else{
+ //unsetting variables equal
+
+ //remove disequalities owned by this
+ std::vector< Node > remDeq;
+ for( std::map< Node, int >::iterator it = d_curr_var_deq[vn].begin(); it != d_curr_var_deq[vn].end(); ++it ){
+ if( it->second==v ){
+ remDeq.push_back( it->first );
+ }
+ }
+ for( unsigned i=0; i<remDeq.size(); i++ ){
+ d_curr_var_deq[vn].erase( remDeq[i] );
+ }
+ }
+ }
+ d_match.erase( v );
+ return 1;
+ }else{
+ std::map< int, Node >::iterator itm = d_match.find( v );
+
+ if( vn!=-1 ){
+ Debug("qcf-match-debug") << " ...Variable bound to variable" << std::endl;
+ std::map< int, Node >::iterator itmn = d_match.find( vn );
+ if( itm==d_match.end() ){
+ //setting variables equal
+ bool alreadySet = false;
+ if( itmn!=d_match.end() ){
+ alreadySet = true;
+ Assert( !itmn->second.isNull() && !isVar( itmn->second ) );
+ }
+
+ //copy or check disequalities
+ std::vector< Node > addDeq;
+ for( std::map< Node, int >::iterator it = d_curr_var_deq[v].begin(); it != d_curr_var_deq[v].end(); ++it ){
+ Node dv = getCurrentValue( it->first );
+ if( !alreadySet ){
+ if( d_curr_var_deq[vn].find( dv )==d_curr_var_deq[vn].end() ){
+ d_curr_var_deq[vn][dv] = v;
+ addDeq.push_back( dv );
+ }
+ }else{
+ if( itmn->second!=dv ){
+ Debug("qcf-match-debug") << " -> fail, conflicting disequality" << std::endl;
+ return -1;
+ }
+ }
+ }
+ if( alreadySet ){
+ n = getCurrentValue( n );
+ }
+ }else{
+ if( itmn==d_match.end() ){
+ Debug("qcf-match-debug") << " ...Reverse direction" << std::endl;
+ //set the opposite direction
+ return addConstraint( p, vn, d_vars[v], v, true, false );
+ }else{
+ Debug("qcf-match-debug") << " -> Both variables bound, compare" << std::endl;
+ //are they currently equal
+ return itm->second==itmn->second ? 0 : -1;
+ }
+ }
+ }else{
+ Debug("qcf-match-debug") << " ...Variable bound to ground" << std::endl;
+ if( itm==d_match.end() ){
+
+ }else{
+ //compare ground values
+ Debug("qcf-match-debug") << " -> Ground value, compare " << itm->second << " "<< n << std::endl;
+ return itm->second==n ? 0 : -1;
+ }
+ }
+ if( setMatch( p, v, n ) ){
+ Debug("qcf-match-debug") << " -> success" << std::endl;
+ return 1;
+ }else{
+ Debug("qcf-match-debug") << " -> fail, conflicting disequality" << std::endl;
+ return -1;
+ }
+ }
+ }else{
+ Debug("qcf-match-debug") << " -> redundant, variable identity" << std::endl;
+ return 0;
+ }
+ }else{
+ if( vn==v ){
+ Debug("qcf-match-debug") << " -> fail, variable identity" << std::endl;
+ return -1;
+ }else{
+ if( doRemove ){
+ Assert( d_curr_var_deq[v].find( n )!=d_curr_var_deq[v].end() );
+ d_curr_var_deq[v].erase( n );
+ return 1;
+ }else{
+ if( d_curr_var_deq[v].find( n )==d_curr_var_deq[v].end() ){
+ //check if it respects equality
+ std::map< int, Node >::iterator itm = d_match.find( v );
+ if( itm!=d_match.end() ){
+ if( getCurrentValue( n )==itm->second ){
+ Debug("qcf-match-debug") << " -> fail, conflicting disequality" << std::endl;
+ return -1;
+ }
+ }
+ d_curr_var_deq[v][n] = v;
+ Debug("qcf-match-debug") << " -> success" << std::endl;
+ return 1;
+ }else{
+ Debug("qcf-match-debug") << " -> redundant disequality" << std::endl;
+ return 0;
+ }
+ }
+ }
+ }
+}
+
+bool QuantConflictFind::QuantInfo::setMatch( QuantConflictFind * p, int v, Node n ) {
+ if( getCurrentCanBeEqual( p, v, n ) ){
+ Debug("qcf-match-debug") << "-- bind : " << v << " -> " << n << ", checked " << d_curr_var_deq[v].size() << " disequalities" << std::endl;
+ d_match[v] = n;
+ return true;
+ }else{
+ return false;
+ }
+}
+
+bool QuantConflictFind::QuantInfo::isMatchSpurious( QuantConflictFind * p ) {
+ for( int i=0; i<getNumVars(); i++ ){
+ std::map< int, Node >::iterator it = d_match.find( i );
+ if( it!=d_match.end() ){
+ if( !getCurrentCanBeEqual( p, i, it->second ) ){
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+bool QuantConflictFind::QuantInfo::completeMatch( QuantConflictFind * p, Node q, std::vector< int >& assigned ) {
+ //assign values for variables that were unassigned (usually not necessary, but handles corner cases)
+ Trace("qcf-check") << std::endl;
+ std::vector< int > unassigned[2];
+ std::vector< TypeNode > unassigned_tn[2];
+ for( int i=0; i<getNumVars(); i++ ){
+ if( d_match.find( i )==d_match.end() ){
+ Assert( i<(int)q[0].getNumChildren() );
+ int rindex = d_var_mg.find( i )==d_var_mg.end() ? 1 : 0;
+ unassigned[rindex].push_back( i );
+ unassigned_tn[rindex].push_back( getVar( i ).getType() );
+ assigned.push_back( i );
+ }
+ }
+ bool success = true;
+ for( unsigned r=0; r<2; r++ ){
+ if( success && !unassigned[r].empty() ){
+ Trace("qcf-check") << "Assign to unassigned, rep = " << r << "..." << std::endl;
+ int index = 0;
+ std::vector< int > eqc_count;
+ do {
+ bool invalidMatch;
+ while( ( index>=0 && (int)index<(int)unassigned[r].size() ) || invalidMatch ){
+ invalidMatch = false;
+ if( index==(int)eqc_count.size() ){
+ //check if it has now been assigned
+ if( r==0 ){
+ d_var_mg[ unassigned[r][index] ]->reset( p, true, q );
+ }
+ eqc_count.push_back( 0 );
+ }else{
+ if( r==0 ){
+ if( d_var_mg[unassigned[r][index]]->getNextMatch( p, q ) ){
+ Trace("qcf-check-unassign") << "Succeeded match with mg" << std::endl;
+ index++;
+ }else{
+ Trace("qcf-check-unassign") << "Failed match with mg" << std::endl;
+ eqc_count.pop_back();
+ index--;
+ }
+ }else{
+ Assert( index==(int)eqc_count.size()-1 );
+ if( eqc_count[index]<(int)p->d_eqcs[unassigned_tn[r][index]].size() ){
+ int currIndex = eqc_count[index];
+ eqc_count[index]++;
+ Trace("qcf-check-unassign") << unassigned[r][index] << "->" << p->d_eqcs[unassigned_tn[r][index]][currIndex] << std::endl;
+ if( setMatch( p, unassigned[r][index], p->d_eqcs[unassigned_tn[r][index]][currIndex] ) ){
+ Trace("qcf-check-unassign") << "Succeeded match" << std::endl;
+ index++;
+ }else{
+ Trace("qcf-check-unassign") << "Failed match" << std::endl;
+ invalidMatch = true;
+ }
+ }else{
+ Trace("qcf-check-unassign") << "No more matches" << std::endl;
+ eqc_count.pop_back();
+ index--;
+ }
+ }
+ }
+ }
+ success = index>=0;
+ if( success ){
+ index = (int)unassigned[r].size()-1;
+ Trace("qcf-check-unassign") << " Try: " << std::endl;
+ for( unsigned i=0; i<unassigned[r].size(); i++ ){
+ int ui = unassigned[r][i];
+ Trace("qcf-check-unassign") << " Assigned #" << ui << " : " << d_vars[ui] << " -> " << d_match[ui] << std::endl;
+ }
+ }
+ }while( success && isMatchSpurious( p ) );
+ }
+ }
+ if( success ){
+ return true;
+ }else{
+ for( unsigned i=0; i<assigned.size(); i++ ){
+ d_match.erase( assigned[i] );
+ }
+ assigned.clear();
+ return false;
+ }
+}
+
+void QuantConflictFind::QuantInfo::debugPrintMatch( const char * c ) {
+ for( int i=0; i<getNumVars(); i++ ){
+ Trace(c) << " " << d_vars[i] << " -> ";
+ if( d_match.find( i )!=d_match.end() ){
+ Trace(c) << d_match[i];
+ }else{
+ Trace(c) << "(unassigned) ";
+ }
+ if( !d_curr_var_deq[i].empty() ){
+ Trace(c) << ", DEQ{ ";
+ for( std::map< Node, int >::iterator it = d_curr_var_deq[i].begin(); it != d_curr_var_deq[i].end(); ++it ){
+ Trace(c) << it->first << " ";
+ }
+ Trace(c) << "}";
+ }
+ Trace(c) << std::endl;
+ }
+}
+
+
+struct MatchGenSort {
+ QuantConflictFind::MatchGen * d_mg;
+ bool operator() (int i,int j) {
+ return d_mg->d_children[i].d_type<d_mg->d_children[j].d_type;
+ }
+};
+
+QuantConflictFind::MatchGen::MatchGen( QuantConflictFind * p, Node q, Node n, bool isTop, bool isVar ){
+ Trace("qcf-qregister-debug") << "Make match gen for " << n << ", top/var = " << isTop << " " << isVar << std::endl;
+ std::vector< Node > qni_apps;
+ d_qni_size = 0;
+ if( isVar ){
+ Assert( p->d_qinfo[q].d_var_num.find( n )!=p->d_qinfo[q].d_var_num.end() );
+ if( n.getKind()==APPLY_UF ){
+ d_type = typ_var;
+ //d_type_not = true; //implicit disequality, in disjunction at top level
+ d_type_not = false;
+ d_n = n;
+ qni_apps.push_back( n );
+ }else{
+ //for now, unknown term
+ d_type = typ_invalid;
+ }
+ }else{
+ /*
+ if( isTop && n.getKind()!=OR && p->d_qinfo[q].d_vars.size()>q[0].getNumChildren() ){
+ //conjoin extra constraints based on flattening with quantifier body
+ d_children.push_back( MatchGen( p, q, n ) );
+ if( d_children[0].d_type==typ_invalid ){
+ d_children.clear();
+ d_type = typ_invalid;
+ }else{
+ d_type = typ_top;
+ }
+ d_type_not = false;
+ }else
+ */
+ if( quantifiers::TermDb::hasBoundVarAttr( n ) ){
+ //we handle not immediately
+ d_n = n.getKind()==NOT ? n[0] : n;
+ d_type_not = n.getKind()==NOT;
+ if( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF || d_n.getKind()==ITE ){
+ //non-literals
+ d_type = typ_valid;
+ for( unsigned i=0; i<d_n.getNumChildren(); i++ ){
+ d_children.push_back( MatchGen( p, q, d_n[i] ) );
+ if( d_children[d_children.size()-1].d_type==typ_invalid ){
+ setInvalid();
+ break;
+ }else if( isTop && n.getKind()==OR && d_children[d_children.size()-1].d_type==typ_var_eq ){
+ Trace("qcf-qregister-debug") << "Remove child, make built-in constraint" << std::endl;
+ //if variable equality/disequality at top level, remove immediately
+ bool cIsNot = d_children[d_children.size()-1].d_type_not;
+ Node cn = d_children[d_children.size()-1].d_n;
+ Assert( cn.getKind()==EQUAL );
+ Assert( p->d_qinfo[q].isVar( cn[0] ) || p->d_qinfo[q].isVar( cn[1] ) );
+ //make it a built-in constraint instead
+ for( unsigned i=0; i<2; i++ ){
+ if( p->d_qinfo[q].isVar( cn[i] ) ){
+ int v = p->d_qinfo[q].getVarNum( cn[i] );
+ Node cno = cn[i==0 ? 1 : 0];
+ p->d_qinfo[q].d_var_constraint[ cIsNot ? 0 : 1 ][v].push_back( cno );
+ break;
+ }
+ }
+ d_children.pop_back();
+ }
+ }
+ }else{
+ d_type = typ_invalid;
+ //literals
+ if( d_n.getKind()==APPLY_UF || d_n.getKind()==EQUAL ){
+ //get the applications (in order) that will be matching
+ p->getEqRegistryApps( d_n, qni_apps );
+ bool isValid = true;
+ if( qni_apps.size()>0 ){
+ for( unsigned i=0; i<qni_apps.size(); i++ ){
+ if( qni_apps[i].getKind()!=APPLY_UF ){
+ //for now, cannot handle anything besides uf
+ isValid = false;
+ qni_apps.clear();
+ break;
+ }
+ }
+ if( isValid ){
+ d_type = typ_valid_lit;
+ }
+ }else if( d_n.getKind()==EQUAL ){
+ for( unsigned i=0; i<2; i++ ){
+ if( quantifiers::TermDb::hasBoundVarAttr( d_n[i] ) && !p->d_qinfo[q].isVar( d_n[i] ) ){
+ isValid = false;
+ break;
+ }
+ }
+ if( isValid ){
+ Assert( p->d_qinfo[q].isVar( d_n[0] ) || p->d_qinfo[q].isVar( d_n[1] ) );
+ // variable equality
+ d_type = typ_var_eq;
+ }
+ }
+ }
+ }
+ }else{
+ //we will just evaluate
+ d_n = n;
+ d_type = typ_ground;
+ }
+ if( d_type!=typ_invalid ){
+ //determine an efficient children ordering
+ if( !d_children.empty() ){
+ for( unsigned i=0; i<d_children.size(); i++ ){
+ d_children_order.push_back( i );
+ }
+ //if( !d_n.isNull() && ( d_n.getKind()==OR || d_n.getKind()==AND || d_n.getKind()==IFF ) ){
+ //sort based on the type of the constraint : ground comes first, then literals, then others
+ //MatchGenSort mgs;
+ //mgs.d_mg = this;
+ //std::sort( d_children_order.begin(), d_children_order.end(), mgs );
+ //}
+ }
+ /*
+ if( isTop ){
+ int base = d_children.size();
+ //add additional constraints based on flattening
+ for( unsigned j=q[0].getNumChildren(); j<p->d_qinfo[q].d_vars.size(); j++ ){
+ d_children.push_back( MatchGen( p, q, p->d_qinfo[q].d_vars[j], false, true ) );
+ }
+
+ //choose variable order for variables based on when they are bound
+ std::vector< int > varOrder;
+ varOrder.insert( varOrder.end(), d_children_order.begin(), d_children_order.end() );
+ d_children_order.clear();
+ std::map< int, bool > bound;
+ for( unsigned i=0; i<varOrder.size(); i++ ){
+ int curr = varOrder[i];
+ Trace("qcf-qregister-debug") << "Var Order : " << curr << std::endl;
+ d_children_order.push_back( curr );
+ for( std::map< int, int >::iterator it = d_children[curr].d_qni_var_num.begin();
+ it != d_children[curr].d_qni_var_num.end(); ++it ){
+ if( it->second>=(int)q[0].getNumChildren() && bound.find( it->second )==bound.end() ){
+ bound[ it->second ] = true;
+ int var = base + it->second - (int)q[0].getNumChildren();
+ d_children_order.push_back( var );
+ Trace("qcf-qregister-debug") << "Var Order, bound : " << var << std::endl;
+ }
+ }
+ }
+ for( unsigned j=q[0].getNumChildren(); j<p->d_qinfo[q].d_vars.size(); j++ ){
+ if( bound.find( j )==bound.end() ){
+ int var = base + j - (int)q[0].getNumChildren();
+ d_children_order.push_back( var );
+ Trace("qcf-qregister-debug") << "Var Order, remaining : " << j << std::endl;
+ }
+ }
+ }
+ */
+ }
+ }
+ if( d_type!=typ_invalid ){
+ if( !qni_apps.empty() ){
+ Trace("qcf-qregister-debug") << "Initialize matching..." << std::endl;
+ for( unsigned i=0; i<qni_apps.size(); i++ ){
+ for( unsigned j=0; j<qni_apps[i].getNumChildren(); j++ ){
+ Node nn = qni_apps[i][j];
+ Trace("qcf-qregister-debug") << " " << d_qni_size;
+ if( p->d_qinfo[q].isVar( nn ) ){
+ Trace("qcf-qregister-debug") << " is var #" << p->d_qinfo[q].d_var_num[nn] << std::endl;
+ d_qni_var_num[d_qni_size] = p->d_qinfo[q].d_var_num[nn];
+ }else{
+ Trace("qcf-qregister-debug") << " is gterm " << nn << std::endl;
+ d_qni_gterm[d_qni_size] = nn;
+ }
+ d_qni_size++;
+ }
+ }
+ }
+ }
+ Trace("qcf-qregister-debug") << "Done make match gen " << n << ", type = ";
+ debugPrintType( "qcf-qregister-debug", d_type, true );
+ Trace("qcf-qregister-debug") << std::endl;
+ Assert( d_children.size()==d_children_order.size() );
+}
+
+void QuantConflictFind::MatchGen::reset_round( QuantConflictFind * p ) {
+ for( std::map< int, Node >::iterator it = d_qni_gterm.begin(); it != d_qni_gterm.end(); ++it ){
+ d_qni_gterm_rep[it->first] = p->getRepresentative( it->second );
+ }
+ for( unsigned i=0; i<d_children.size(); i++ ){
+ d_children[i].reset_round( p );
+ }
+}
+
+void QuantConflictFind::MatchGen::reset( QuantConflictFind * p, bool tgt, Node q ) {
+ d_tgt = d_type_not ? !tgt : tgt;
+ Debug("qcf-match") << " Reset for : " << d_n << ", type : ";
+ debugPrintType( "qcf-match", d_type );
+ Debug("qcf-match") << ", tgt = " << d_tgt << ", children = " << d_children.size() << std::endl;
+ d_qn.clear();
+ d_qni.clear();
+ d_qni_bound.clear();
+ d_child_counter = -1;
+
+ //set up processing matches
+ if( d_type==typ_ground ){
+ if( p->evaluate( d_n )==( d_tgt ? 1 : -1 ) ){
+ //store dummy variable
+ d_qn.push_back( NULL );
+ }
+ }else if( d_type==typ_var ){
+ //check if variable is bound by now
+ int vi = p->d_qinfo[q].getVarNum( d_n );
+ Assert( vi!=-1 );
+ int repVar = p->d_qinfo[q].getCurrentRepVar( vi );
+ Assert( d_n.getKind()==APPLY_UF );
+ Node f = d_n.getOperator();
+ std::map< int, Node >::iterator it = p->d_qinfo[q].d_match.find( repVar );
+ if( it!=p->d_qinfo[q].d_match.end() && d_tgt ) {
+ Debug("qcf-match") << " will be matching var within eqc = " << it->second << std::endl;
+ //f-applications in the equivalence class in match[ repVar ]
+ std::map< Node, QcfNodeIndex >::iterator itut = p->d_eqc_uf_terms[ it->second ].find( f );
+ if( itut!=p->d_eqc_uf_terms[ it->second ].end() ){
+ d_qn.push_back( &itut->second );
+ }
+ }else{
+ Debug("qcf-match") << " will be matching var within any eqc." << std::endl;
+ //we are binding rep var
+ d_qni_bound_cons[repVar] = Node::null();
+ //must look at all f-applications
+ std::map< Node, QcfNodeIndex >::iterator itut = p->d_uf_terms.find( f );
+ if( itut!=p->d_uf_terms.end() ){
+ d_qn.push_back( &itut->second );
+ }
+ }
+ }else if( d_type==typ_var_eq ){
+ bool success = false;
+ for( unsigned i=0; i<2; i++ ){
+ int var = p->d_qinfo[q].getVarNum( d_n[i] );
+ if( var!=-1 ){
+ int repVar = p->d_qinfo[q].getCurrentRepVar( var );
+ Node o = d_n[ i==0 ? 1 : 0 ];
+ o = p->d_qinfo[q].getCurrentValue( o );
+ int vo = p->d_qinfo[q].getCurrentRepVar( p->d_qinfo[q].getVarNum( o ) );
+ int addCons = p->d_qinfo[q].addConstraint( p, repVar, o, vo, d_tgt, false );
+ success = addCons!=-1;
+ //if successful and non-redundant, store that we need to cleanup this
+ if( addCons==1 ){
+ d_qni_bound_cons[repVar] = o;
+ d_qni_bound[repVar] = vo;
+ }
+ break;
+ }
+ }
+ if( success ){
+ //store dummy
+ d_qn.push_back( NULL );
+ }
+ }else if( d_type==typ_valid_lit ){
+ //literal
+ EqRegistry * er = p->getEqRegistry( d_tgt, d_n, false );
+ Assert( er );
+ d_qn.push_back( &(er->d_qni) );
+ }else{
+ if( d_children.empty() ){
+ //add dummy
+ d_qn.push_back( NULL );
+ }else{
+ //reset the first child to d_tgt
+ d_child_counter = 0;
+ getChild( d_child_counter )->reset( p, d_tgt, q );
+ }
+ }
+ Debug("qcf-match") << " Finished reset for " << d_n << ", success = " << ( !d_qn.empty() || d_child_counter!=-1 ) << std::endl;
+}
+
+bool QuantConflictFind::MatchGen::getNextMatch( QuantConflictFind * p, Node q ) {
+ Debug("qcf-match") << " Get next match for : " << d_n << ", type = ";
+ debugPrintType( "qcf-match", d_type );
+ Debug("qcf-match") << ", children = " << d_children.size() << std::endl;
+ if( d_children.empty() ){
+ bool success = doMatching( p, q );
+ if( success ){
+ Debug("qcf-match") << " Produce matches for bound variables..." << std::endl;
+ //also need to create match for each variable we bound
+ std::map< int, int >::iterator it = d_qni_bound.begin();
+ bool doReset = true;
+ while( success && it!=d_qni_bound.end() ){
+ std::map< int, MatchGen * >::iterator itm = p->d_qinfo[q].d_var_mg.find( it->second );
+ if( itm!=p->d_qinfo[q].d_var_mg.end() ){
+ Debug("qcf-match-debug") << " process variable " << it->second << ", reset = " << doReset << std::endl;
+ if( doReset ){
+ itm->second->reset( p, true, q );
+ }
+ if( !itm->second->getNextMatch( p, q ) ){
+ do {
+ if( it==d_qni_bound.begin() ){
+ Debug("qcf-match-debug") << " failed." << std::endl;
+ success = false;
+ }else{
+ Debug("qcf-match-debug") << " decrement..." << std::endl;
+ --it;
+ }
+ }while( success && p->d_qinfo[q].d_var_mg.find( it->second )==p->d_qinfo[q].d_var_mg.end() );
+ doReset = false;
+ }else{
+ Debug("qcf-match-debug") << " increment..." << std::endl;
+ ++it;
+ doReset = true;
+ }
+ }else{
+ Debug("qcf-match-debug") << " skip..." << std::endl;
+ ++it;
+ doReset = true;
+ }
+ }
+ }
+ if( !success ){
+ for( std::map< int, Node >::iterator it = d_qni_bound_cons.begin(); it != d_qni_bound_cons.end(); ++it ){
+ if( !it->second.isNull() ){
+ Debug("qcf-match") << " Clean up bound var " << it->first << (d_tgt ? "!" : "") << " = " << it->second << std::endl;
+ std::map< int, int >::iterator itb = d_qni_bound.find( it->first );
+ int vn = itb!=d_qni_bound.end() ? itb->second : -1;
+ p->d_qinfo[q].addConstraint( p, it->first, it->second, vn, d_tgt, true );
+ if( vn!=-1 ){
+ d_qni_bound.erase( vn );
+ }
+ }
+ }
+ d_qni_bound_cons.clear();
+ //clean up the match : remove equalities/disequalities
+ for( std::map< int, int >::iterator it = d_qni_bound.begin(); it != d_qni_bound.end(); ++it ){
+ Debug("qcf-match") << " Clean up bound var " << it->second << std::endl;
+ Assert( it->second<p->d_qinfo[q].getNumVars() );
+ p->d_qinfo[q].d_match.erase( it->second );
+ }
+ d_qni_bound.clear();
+ }
+ Debug("qcf-match") << " ...finished matching for " << d_n << ", success = " << success << std::endl;
+ return success;
+ }else{
+ if( d_child_counter!=-1 ){
+ bool success = false;
+ while( !success && d_child_counter>=0 ){
+ //transition system based on d_child_counter
+ if( d_type==typ_top || d_n.getKind()==OR || d_n.getKind()==AND ){
+ if( (d_n.getKind()==AND)==d_tgt ){
+ //all children must match simultaneously
+ if( getChild( d_child_counter )->getNextMatch( p, q ) ){
+ if( d_child_counter<(int)(getNumChildren()-1) ){
+ d_child_counter++;
+ Debug("qcf-match-debug") << " Reset child " << d_child_counter << " of " << d_n << ", all match " << d_children_order.size() << " " << d_children_order[d_child_counter] << std::endl;
+ getChild( d_child_counter )->reset( p, d_tgt, q );
+ }else{
+ success = true;
+ }
+ }else{
+ d_child_counter--;
+ }
+ }else{
+ //one child must match
+ if( !getChild( d_child_counter )->getNextMatch( p, q ) ){
+ if( d_child_counter<(int)(getNumChildren()-1) ){
+ d_child_counter++;
+ Debug("qcf-match-debug") << " Reset child " << d_child_counter << " of " << d_n << ", one match" << std::endl;
+ getChild( d_child_counter )->reset( p, d_tgt, q );
+ }else{
+ d_child_counter = -1;
+ }
+ }else{
+ success = true;
+ }
+ }
+ }else if( d_n.getKind()==IFF ){
+ //construct match based on both children
+ if( d_child_counter%2==0 ){
+ if( getChild( 0 )->getNextMatch( p, q ) ){
+ d_child_counter++;
+ getChild( 1 )->reset( p, d_child_counter==1, q );
+ }else{
+ if( d_child_counter==0 ){
+ d_child_counter = 2;
+ getChild( 0 )->reset( p, !d_tgt, q );
+ }else{
+ d_child_counter = -1;
+ }
+ }
+ }
+ if( d_child_counter%2==1 ){
+ if( getChild( 1 )->getNextMatch( p, q ) ){
+ success = true;
+ }else{
+ d_child_counter--;
+ }
+ }
+ }else if( d_n.getKind()==ITE ){
+ if( d_child_counter%2==0 ){
+ int index1 = d_child_counter==4 ? 1 : 0;
+ if( getChild( index1 )->getNextMatch( p, q ) ){
+ d_child_counter++;
+ getChild( d_child_counter==5 ? 2 : (d_tgt==(d_child_counter==0) ? 1 : 2) )->reset( p, d_tgt, q );
+ }else{
+ if( d_child_counter==4 ){
+ d_child_counter = -1;
+ }else{
+ d_child_counter +=2;
+ getChild( d_child_counter==4 ? 1 : 0 )->reset( p, d_child_counter==2 ? !d_tgt : d_tgt, q );
+ }
+ }
+ }
+ if( d_child_counter%2==1 ){
+ int index2 = d_child_counter==5 ? 2 : (d_tgt==(d_child_counter==0) ? 1 : 2);
+ if( getChild( index2 )->getNextMatch( p, q ) ){
+ success = true;
+ }else{
+ d_child_counter--;
+ }
+ }
+ }
+ }
+ Debug("qcf-match") << " ...finished construct match for " << d_n << ", success = " << success << std::endl;
+ return success;
+ }
+ }
+ Debug("qcf-match") << " ...already finished for " << d_n << std::endl;
+ return false;
+}
+
+bool QuantConflictFind::MatchGen::doMatching( QuantConflictFind * p, Node q ) {
+ if( !d_qn.empty() ){
+ if( d_qn[0]==NULL ){
+ d_qn.clear();
+ return true;
+ }else{
+ Assert( d_qni_size>0 );
+ bool invalidMatch;
+ do {
+ invalidMatch = false;
+ Debug("qcf-match-debug") << " Do matching " << d_qn.size() << " " << d_qni.size() << std::endl;
+ if( d_qn.size()==d_qni.size()+1 ) {
+ int index = (int)d_qni.size();
+ //initialize
+ Node val;
+ std::map< int, int >::iterator itv = d_qni_var_num.find( index );
+ if( itv!=d_qni_var_num.end() ){
+ //get the representative variable this variable is equal to
+ int repVar = p->d_qinfo[q].getCurrentRepVar( itv->second );
+ Debug("qcf-match-debug") << " Match " << index << " is a variable " << itv->second << ", which is repVar " << repVar << std::endl;
+ //get the value the rep variable
+ std::map< int, Node >::iterator itm = p->d_qinfo[q].d_match.find( repVar );
+ if( itm!=p->d_qinfo[q].d_match.end() ){
+ val = itm->second;
+ Assert( !val.isNull() );
+ Debug("qcf-match-debug") << " Variable is already bound to " << val << std::endl;
+ }else{
+ //binding a variable
+ d_qni_bound[index] = repVar;
+ std::map< Node, QcfNodeIndex >::iterator it = d_qn[index]->d_children.begin();
+ if( it != d_qn[index]->d_children.end() ) {
+ d_qni.push_back( it );
+ //set the match
+ if( p->d_qinfo[q].setMatch( p, d_qni_bound[index], it->first ) ){
+ Debug("qcf-match-debug") << " Binding variable" << std::endl;
+ if( d_qn.size()<d_qni_size ){
+ d_qn.push_back( &it->second );
+ }
+ }else{
+ Debug("qcf-match") << " Binding variable, currently fail." << std::endl;
+ invalidMatch = true;
+ }
+ }else{
+ Debug("qcf-match-debug") << " Binding variable, fail, no more variables to bind" << std::endl;
+ d_qn.pop_back();
+ }
+ }
+ }else{
+ Debug("qcf-match-debug") << " Match " << index << " is ground term" << std::endl;
+ Assert( d_qni_gterm.find( index )!=d_qni_gterm.end() );
+ Assert( d_qni_gterm_rep.find( index )!=d_qni_gterm_rep.end() );
+ val = d_qni_gterm_rep[index];
+ Assert( !val.isNull() );
+ }
+ if( !val.isNull() ){
+ //constrained by val
+ std::map< Node, QcfNodeIndex >::iterator it = d_qn[index]->d_children.find( val );
+ if( it!=d_qn[index]->d_children.end() ){
+ Debug("qcf-match-debug") << " Match" << std::endl;
+ d_qni.push_back( it );
+ if( d_qn.size()<d_qni_size ){
+ d_qn.push_back( &it->second );
+ }
+ }else{
+ Debug("qcf-match-debug") << " Failed to match" << std::endl;
+ d_qn.pop_back();
+ }
+ }
+ }else{
+ Assert( d_qn.size()==d_qni.size() );
+ int index = d_qni.size()-1;
+ //increment if binding this variable
+ bool success = false;
+ std::map< int, int >::iterator itb = d_qni_bound.find( index );
+ if( itb!=d_qni_bound.end() ){
+ d_qni[index]++;
+ if( d_qni[index]!=d_qn[index]->d_children.end() ){
+ success = true;
+ if( p->d_qinfo[q].setMatch( p, itb->second, d_qni[index]->first ) ){
+ Debug("qcf-match-debug") << " Bind next variable" << std::endl;
+ if( d_qn.size()<d_qni_size ){
+ d_qn.push_back( &d_qni[index]->second );
+ }
+ }else{
+ Debug("qcf-match-debug") << " Bind next variable, currently fail" << std::endl;
+ invalidMatch = true;
+ }
+ }else{
+ Debug("qcf-match-debug") << " Bind next variable, no more variables to bind" << std::endl;
+ }
+ }
+ //if not incrementing, move to next
+ if( !success ){
+ d_qn.pop_back();
+ d_qni.pop_back();
+ }
+ }
+ if( d_type==typ_var ){
+ if( !invalidMatch && d_qni.size()==d_qni_size ){
+ //if in the act of binding the variable by this term, bind the variable
+ std::map< int, Node >::iterator itb = d_qni_bound_cons.begin();
+ if( itb!=d_qni_bound_cons.end() ){
+ QcfNodeIndex * v_qni = &d_qni[d_qni.size()-1]->second;
+ Assert( v_qni->d_children.begin()!=v_qni->d_children.end() );
+ Node vb = v_qni->d_children.begin()->first;
+ Assert( !vb.isNull() );
+ vb = p->getRepresentative( vb );
+ Debug("qcf-match-debug") << " For var, require binding " << itb->first << " to " << vb << ", d_tgt = " << d_tgt << std::endl;
+ if( !itb->second.isNull() ){
+ p->d_qinfo[q].addConstraint( p, itb->first, itb->second, -1, d_tgt, true );
+ }
+ int addCons = p->d_qinfo[q].addConstraint( p, itb->first, vb, -1, d_tgt, false );
+ if( addCons==-1 ){
+ Debug("qcf-match-debug") << " Failed set for var." << std::endl;
+ invalidMatch = true;
+ d_qni_bound_cons[itb->first] = Node::null();
+ }else{
+ Debug("qcf-match-debug") << " Succeeded set for var." << std::endl;
+ if( addCons==1 ){
+ d_qni_bound_cons[itb->first] = vb;
+ }
+ }
+ }
+ }
+ }
+ }while( ( !d_qn.empty() && d_qni.size()!=d_qni_size ) || invalidMatch );
+ }
+ }
+ return !d_qn.empty();
+}
+
+void QuantConflictFind::MatchGen::debugPrintType( const char * c, short typ, bool isTrace ) {
+ if( isTrace ){
+ switch( typ ){
+ case typ_invalid: Trace(c) << "invalid";break;
+ case typ_ground: Trace(c) << "ground";break;
+ case typ_valid_lit: Trace(c) << "valid_lit";break;
+ case typ_valid: Trace(c) << "valid";break;
+ case typ_var: Trace(c) << "var";break;
+ case typ_var_eq: Trace(c) << "var_eq";break;
+ case typ_top: Trace(c) << "top";break;
+ }
+ }else{
+ switch( typ ){
+ case typ_invalid: Debug(c) << "invalid";break;
+ case typ_ground: Debug(c) << "ground";break;
+ case typ_valid_lit: Debug(c) << "valid_lit";break;
+ case typ_valid: Debug(c) << "valid";break;
+ case typ_var: Debug(c) << "var";break;
+ case typ_var_eq: Debug(c) << "var_eq";break;
+ case typ_top: Debug(c) << "top";break;
+ }
+ }
+}
+
+void QuantConflictFind::MatchGen::setInvalid() {
+ d_type = typ_invalid;
+ d_children.clear();
+}
+
+
+//-------------------------------------------------- debugging
+
+
+void QuantConflictFind::debugPrint( const char * c ) {
+ //print the equivalance classes
+ Trace(c) << "----------EQ classes" << std::endl;
+ eq::EqClassesIterator eqcs_i = eq::EqClassesIterator( getEqualityEngine() );
+ while( !eqcs_i.isFinished() ){
+ Node n = (*eqcs_i);
+ if( !n.getType().isInteger() ){
+ Trace(c) << " - " << n << " : {";
+ eq::EqClassIterator eqc_i = eq::EqClassIterator( n, getEqualityEngine() );
+ bool pr = false;
+ while( !eqc_i.isFinished() ){
+ Node nn = (*eqc_i);
+ if( nn.getKind()!=EQUAL && nn!=n ){
+ Trace(c) << (pr ? "," : "" ) << " " << nn;
+ pr = true;
+ }
+ ++eqc_i;
+ }
+ Trace(c) << (pr ? " " : "" ) << "}" << std::endl;
+ EqcInfo * eqcn = getEqcInfo( n, false );
+ if( eqcn ){
+ Trace(c) << " DEQ : {";
+ pr = false;
+ for( NodeBoolMap::iterator it = eqcn->d_diseq.begin(); it != eqcn->d_diseq.end(); ++it ){
+ if( (*it).second ){
+ Trace(c) << (pr ? "," : "" ) << " " << (*it).first;
+ pr = true;
+ }
+ }
+ Trace(c) << (pr ? " " : "" ) << "}" << std::endl;
+ }
+ }
+ ++eqcs_i;
+ }
+ std::map< Node, std::map< Node, bool > > printed;
+ //print the equality registries
+ for( unsigned i=0; i<2; i++ ){
+ printed.clear();
+ Trace(c) << "----------EQR, polarity = " << (i==0) << std::endl;
+ for( std::map< Node, std::map< Node, EqRegistry * > >::iterator it = d_eqr[i].begin(); it != d_eqr[i].end(); ++it ){
+ bool prHead = false;
+ for( std::map< Node, EqRegistry * >::iterator it2 = it->second.begin(); it2 != it->second.end(); ++it2 ){
+ bool print;
+ if( it->first.getKind()==APPLY_UF && it2->first.getKind()==APPLY_UF &&
+ it->first.getOperator()!=it2->first.getOperator() ){
+ print = isLessThan( it->first, it2->first );
+ }else{
+ print = printed[it->first].find( it2->first )==printed[it->first].end();
+ }
+ if( print ){
+ printed[it->first][it2->first] = true;
+ printed[it2->first][it->first] = true;
+ if( !prHead ){
+ Trace(c) << "- " << it->first << std::endl;
+ prHead = true;
+ }
+ Trace(c) << " " << it2->first << ", terms : " << std::endl;
+
+ /*
+ Trace(c) << " " << it2->first << " : {";
+ bool pr = false;
+ for( NodeBoolMap::iterator it3 = it2->second->d_t_eqc.begin(); it3 != it2->second->d_t_eqc.end(); ++it3 ){
+ if( (*it3).second ){
+ Trace(c) << (pr ? "," : "" ) << " " << (*it3).first;
+ pr = true;
+ }
+ }
+ Trace(c) << (pr ? " " : "" ) << "}" << std::endl;
+ */
+ //print qni structure
+ it2->second->debugPrint( c, 3 );
+ }
+ }
+ }
+ }
+}
+
+void QuantConflictFind::debugPrintQuant( const char * c, Node q ) {
+ Trace(c) << "Q" << d_quant_id[q];
+}
+
+void QuantConflictFind::debugPrintQuantBody( const char * c, Node q, Node n, bool doVarNum ) {
+ if( n.getNumChildren()==0 ){
+ Trace(c) << n;
+ }else if( doVarNum && d_qinfo[q].d_var_num.find( n )!=d_qinfo[q].d_var_num.end() ){
+ Trace(c) << "?x" << d_qinfo[q].d_var_num[n];
+ }else{
+ Trace(c) << "(";
+ if( n.getKind()==APPLY_UF ){
+ Trace(c) << n.getOperator();
+ }else{
+ Trace(c) << n.getKind();
+ }
+ for( unsigned i=0; i<n.getNumChildren(); i++ ){
+ Trace(c) << " ";
+ debugPrintQuantBody( c, q, n[i] );
+ }
+ Trace(c) << ")";
+ }
+}
+
+QuantConflictFind::Statistics::Statistics():
+ d_inst_rounds("QuantConflictFind::Inst_Rounds", 0),
+ d_conflict_inst("QuantConflictFind::Instantiations_Conflict_Find", 0 )
+{
+ StatisticsRegistry::registerStat(&d_inst_rounds);
+ StatisticsRegistry::registerStat(&d_conflict_inst);
+}
+
+QuantConflictFind::Statistics::~Statistics(){
+ StatisticsRegistry::unregisterStat(&d_inst_rounds);
+ StatisticsRegistry::unregisterStat(&d_conflict_inst);
+}
+
+}
\ No newline at end of file diff --git a/src/theory/quantifiers/quant_conflict_find.h b/src/theory/quantifiers/quant_conflict_find.h new file mode 100755 index 000000000..0b503d49b --- /dev/null +++ b/src/theory/quantifiers/quant_conflict_find.h @@ -0,0 +1,258 @@ +/********************* */
+/*! \file quant_conflict_find.h
+ ** \verbatim
+ ** Original author: Andrew Reynolds
+ ** Major contributors: none
+ ** Minor contributors (to current version): none
+ ** This file is part of the CVC4 project.
+ ** Copyright (c) 2009-2013 New York University and The University of Iowa
+ ** See the file COPYING in the top-level source directory for licensing
+ ** information.\endverbatim
+ **
+ ** \brief quantifiers conflict find class
+ **/
+
+#include "cvc4_private.h"
+
+#ifndef QUANT_CONFLICT_FIND
+#define QUANT_CONFLICT_FIND
+
+#include "context/cdhashmap.h"
+#include "context/cdchunk_list.h"
+#include "theory/quantifiers_engine.h"
+
+namespace CVC4 {
+namespace theory {
+namespace quantifiers {
+
+class QcfNode;
+
+class QuantConflictFind;
+
+class QcfNodeIndex {
+public:
+ std::map< Node, QcfNodeIndex > d_children;
+ void clear() { d_children.clear(); }
+ //Node existsTerm( QuantConflictFind * qcf, Node n, int index = 0 );
+ //Node addTerm( QuantConflictFind * qcf, Node n, int index = 0 );
+ //bool addTermEq( QuantConflictFind * qcf, Node n1, Node n2, int index = 0 );
+ void debugPrint( const char * c, int t );
+ //optimized versions
+ Node existsTerm( Node n, std::vector< Node >& reps, int index = 0 );
+ Node addTerm( Node n, std::vector< Node >& reps, int index = 0 );
+ bool addTermEq( Node n1, Node n2, std::vector< Node >& reps1, std::vector< Node >& reps2, int index = 0 );
+};
+
+class EqRegistry {
+ typedef context::CDChunkList<Node> NodeList;
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+public:
+ EqRegistry( context::Context* c );
+ //active
+ context::CDO< bool > d_active;
+ //NodeIndex describing pairs that meet the criteria of the EqRegistry
+ QcfNodeIndex d_qni;
+
+ //qcf nodes that this helps to 1:satisfy -1:falsify 0:both a quantifier conflict node
+ //std::map< QcfNode *, int > d_qcf;
+ //has eqc
+ //bool hasEqc( Node n ) { return d_t_eqc.find( n )!=d_t_eqc.end() && d_t_eqc[n]; }
+ //void setEqc( Node n, bool val = true ) { d_t_eqc[n] = val; }
+ void clear() { d_qni.clear(); }
+ void debugPrint( const char * c, int t );
+};
+
+/*
+class QcfNode {
+public:
+ QcfNode( context::Context* c );
+ QcfNode * d_parent;
+ std::map< int, QcfNode * > d_child;
+ Node d_node;
+ EqRegistry * d_reg[2];
+};
+*/
+
+class QuantConflictFind : public QuantifiersModule
+{
+ friend class QcfNodeIndex;
+ typedef context::CDChunkList<Node> NodeList;
+ typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap;
+private:
+ context::Context* d_c;
+ context::CDO< bool > d_conflict;
+ bool d_performCheck;
+ //void registerAssertion( Node n );
+ void registerNode( Node q, Node n, bool hasPol, bool pol );
+ void flatten( Node q, Node n );
+private:
+ std::map< TypeNode, Node > d_fv;
+ Node getFv( TypeNode tn );
+ std::map< Node, Node > d_op_node;
+ int getFunctionId( Node f );
+ bool isLessThan( Node a, Node b );
+ Node getFunction( Node n, bool isQuant = false );
+ int d_fid_count;
+ std::map< Node, int > d_fid;
+ Node mkEqNode( Node a, Node b );
+private: //for ground terms
+ Node d_true;
+ Node d_false;
+ std::map< Node, std::map< Node, EqRegistry * > > d_eqr[2];
+ EqRegistry * getEqRegistry( bool polarity, Node lit, bool doCreate = true );
+ void getEqRegistryApps( Node lit, std::vector< Node >& terms );
+ int evaluate( Node n );
+public: //for quantifiers
+ //match generator
+ class MatchGen {
+ private:
+ //current children information
+ int d_child_counter;
+ //children of this object
+ std::vector< int > d_children_order;
+ unsigned getNumChildren() { return d_children.size(); }
+ MatchGen * getChild( int i ) { return &d_children[d_children_order[i]]; }
+ //current matching information
+ std::vector< QcfNodeIndex * > d_qn;
+ std::vector< std::map< Node, QcfNodeIndex >::iterator > d_qni;
+ bool doMatching( QuantConflictFind * p, Node q );
+ //for matching : each index is either a variable or a ground term
+ unsigned d_qni_size;
+ std::map< int, int > d_qni_var_num;
+ std::map< int, Node > d_qni_gterm;
+ std::map< int, Node > d_qni_gterm_rep;
+ std::map< int, int > d_qni_bound;
+ std::map< int, Node > d_qni_bound_cons;
+ public:
+ //type of the match generator
+ enum {
+ typ_invalid,
+ typ_ground,
+ typ_var_eq,
+ typ_valid_lit,
+ typ_valid,
+ typ_var,
+ typ_top,
+ };
+ void debugPrintType( const char * c, short typ, bool isTrace = false );
+ public:
+ MatchGen() : d_type( typ_invalid ){}
+ MatchGen( QuantConflictFind * p, Node q, Node n, bool isTop = false, bool isVar = false );
+ bool d_tgt;
+ Node d_n;
+ std::vector< MatchGen > d_children;
+ short d_type;
+ bool d_type_not;
+ void reset_round( QuantConflictFind * p );
+ void reset( QuantConflictFind * p, bool tgt, Node q );
+ bool getNextMatch( QuantConflictFind * p, Node q );
+ bool isValid() { return d_type!=typ_invalid; }
+ void setInvalid();
+ };
+private:
+ //currently asserted quantifiers
+ NodeList d_qassert;
+ //info for quantifiers
+ class QuantInfo {
+ public:
+ QuantInfo() : d_mg( NULL ) {}
+ std::vector< Node > d_vars;
+ std::map< Node, int > d_var_num;
+ std::map< EqRegistry *, bool > d_rel_eqr;
+ std::map< int, std::vector< Node > > d_var_constraint[2];
+ int getVarNum( Node v ) { return d_var_num.find( v )!=d_var_num.end() ? d_var_num[v] : -1; }
+ bool isVar( Node v ) { return d_var_num.find( v )!=d_var_num.end(); }
+ int getNumVars() { return (int)d_vars.size(); }
+ Node getVar( int i ) { return d_vars[i]; }
+ MatchGen * d_mg;
+ std::map< int, MatchGen * > d_var_mg;
+ void reset_round( QuantConflictFind * p );
+ public:
+ //current constraints
+ std::map< int, Node > d_match;
+ std::map< int, std::map< Node, int > > d_curr_var_deq;
+ int getCurrentRepVar( int v );
+ Node getCurrentValue( Node n );
+ bool getCurrentCanBeEqual( QuantConflictFind * p, int v, Node n );
+ int addConstraint( QuantConflictFind * p, int v, Node n, bool polarity );
+ int addConstraint( QuantConflictFind * p, int v, Node n, int vn, bool polarity, bool doRemove );
+ bool setMatch( QuantConflictFind * p, int v, Node n );
+ bool isMatchSpurious( QuantConflictFind * p );
+ bool completeMatch( QuantConflictFind * p, Node q, std::vector< int >& assigned );
+ void debugPrintMatch( const char * c );
+ };
+ std::map< Node, QuantInfo > d_qinfo;
+private: //for equivalence classes
+ eq::EqualityEngine * getEqualityEngine();
+ bool areDisequal( Node n1, Node n2 );
+ bool areEqual( Node n1, Node n2 );
+ Node getRepresentative( Node n );
+ Node getTerm( Node n );
+
+ class EqcInfo {
+ public:
+ EqcInfo( context::Context* c ) : d_diseq( c ) {}
+ NodeBoolMap d_diseq;
+ bool isDisequal( Node n ) { return d_diseq.find( n )!=d_diseq.end() && d_diseq[n]; }
+ void setDisequal( Node n, bool val = true ) { d_diseq[n] = val; }
+ //NodeBoolMap& getRelEqr( int index ) { return index==0 ? d_rel_eqr_e : d_rel_eqr_d; }
+ };
+ std::map< Node, EqcInfo * > d_eqc_info;
+ EqcInfo * getEqcInfo( Node n, bool doCreate = true );
+ // operator -> index(terms)
+ std::map< Node, QcfNodeIndex > d_uf_terms;
+ // eqc x operator -> index(terms)
+ std::map< Node, std::map< Node, QcfNodeIndex > > d_eqc_uf_terms;
+ // type -> list(eqc)
+ std::map< TypeNode, std::vector< Node > > d_eqcs;
+ //mapping from UF terms to representatives of their arguments
+ std::map< Node, std::vector< Node > > d_arg_reps;
+ //compute arg reps
+ void computeArgReps( Node n );
+public:
+ QuantConflictFind( QuantifiersEngine * qe, context::Context* c );
+
+ /** register assertions */
+ //void registerAssertions( std::vector< Node >& assertions );
+ /** register quantifier */
+ void registerQuantifier( Node q );
+
+public:
+ /** assert quantifier */
+ void assertNode( Node q );
+ /** new node */
+ void newEqClass( Node n );
+ /** merge */
+ void merge( Node a, Node b );
+ /** assert disequal */
+ void assertDisequal( Node a, Node b );
+ /** check */
+ void check( Theory::Effort level );
+ /** needs check */
+ bool needsCheck( Theory::Effort level );
+private:
+ void computeRelevantEqr();
+private:
+ void debugPrint( const char * c );
+ //for debugging
+ std::vector< Node > d_quants;
+ std::map< Node, int > d_quant_id;
+ void debugPrintQuant( const char * c, Node q );
+ void debugPrintQuantBody( const char * c, Node q, Node n, bool doVarNum = true );
+public:
+ /** statistics class */
+ class Statistics {
+ public:
+ IntStat d_inst_rounds;
+ IntStat d_conflict_inst;
+ Statistics();
+ ~Statistics();
+ };
+ Statistics d_statistics;
+};
+
+}
+}
+}
+
+#endif
diff --git a/src/theory/quantifiers/quant_util.cpp b/src/theory/quantifiers/quant_util.cpp index 59995a510..31a95fb8a 100644 --- a/src/theory/quantifiers/quant_util.cpp +++ b/src/theory/quantifiers/quant_util.cpp @@ -239,3 +239,17 @@ void QuantPhaseReq::computePhaseReqs( Node n, bool polarity, std::map< Node, int } } } + +void QuantPhaseReq::getPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol ) { + newHasPol = hasPol; + newPol = pol; + if( n.getKind()==NOT ){ + newPol = !pol; + }else if( n.getKind()==IFF ){ + newHasPol = false; + }else if( n.getKind()==ITE ){ + if( child==0 ){ + newHasPol = false; + } + } +}
\ No newline at end of file diff --git a/src/theory/quantifiers/quant_util.h b/src/theory/quantifiers/quant_util.h index 86c7bc3a0..711144e7e 100644 --- a/src/theory/quantifiers/quant_util.h +++ b/src/theory/quantifiers/quant_util.h @@ -82,6 +82,8 @@ public: std::map< Node, bool > d_phase_reqs; std::map< Node, bool > d_phase_reqs_equality; std::map< Node, Node > d_phase_reqs_equality_term; + + static void getPolarity( Node n, int child, bool hasPol, bool pol, bool& newHasPol, bool& newPol ); }; diff --git a/src/theory/quantifiers/quantifiers_rewriter.cpp b/src/theory/quantifiers/quantifiers_rewriter.cpp index ecc3c02bc..3a6785d00 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.cpp +++ b/src/theory/quantifiers/quantifiers_rewriter.cpp @@ -229,6 +229,32 @@ RewriteResponse QuantifiersRewriter::postRewrite(TNode in) { return RewriteResponse(REWRITE_DONE, in); } +Node QuantifiersRewriter::computeElimSymbols( Node body ) { + if( isLiteral( body ) ){ + return body; + }else{ + bool childrenChanged = false; + std::vector< Node > children; + for( unsigned i=0; i<body.getNumChildren(); i++ ){ + Node c = computeElimSymbols( body[i] ); + if( i==0 && ( body.getKind()==IMPLIES || body.getKind()==XOR ) ){ + c = c.negate(); + } + children.push_back( c ); + childrenChanged = childrenChanged || c!=body[i]; + } + if( body.getKind()==IMPLIES ){ + return NodeManager::currentNM()->mkNode( OR, children ); + }else if( body.getKind()==XOR ){ + return NodeManager::currentNM()->mkNode( IFF, children ); + }else if( childrenChanged ){ + return NodeManager::currentNM()->mkNode( body.getKind(), children ); + }else{ + return body; + } + } +} + Node QuantifiersRewriter::computeNNF( Node body ){ if( body.getKind()==NOT ){ if( body[0].getKind()==NOT ){ @@ -238,10 +264,9 @@ Node QuantifiersRewriter::computeNNF( Node body ){ }else{ std::vector< Node > children; Kind k = body[0].getKind(); - if( body[0].getKind()==OR || body[0].getKind()==IMPLIES || body[0].getKind()==AND ){ + if( body[0].getKind()==OR || body[0].getKind()==AND ){ for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - Node nn = body[0].getKind()==IMPLIES && i==0 ? body[0][i] : body[0][i].notNode(); - children.push_back( computeNNF( nn ) ); + children.push_back( computeNNF( body[0][i].notNode() ) ); } k = body[0].getKind()==AND ? OR : AND; }else if( body[0].getKind()==XOR || body[0].getKind()==IFF ){ @@ -376,8 +401,7 @@ Node QuantifiersRewriter::computeClause( Node n ){ } }else{ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node nc = ( ( i==0 && n.getKind()==IMPLIES ) ? n[i].notNode() : n[i] ); - Node nn = computeClause( nc ); + Node nn = computeClause( n[i] ); addNodeToOrBuilder( nn, t ); } } @@ -391,10 +415,10 @@ Node QuantifiersRewriter::computeCNF( Node n, std::vector< Node >& args, NodeBui }else if( !forcePred && isClause( n ) ){ return computeClause( n ); }else{ - Kind k = ( n.getKind()==IMPLIES ? OR : ( n.getKind()==XOR ? IFF : n.getKind() ) ); + Kind k = n.getKind(); NodeBuilder<> t(k); for( int i=0; i<(int)n.getNumChildren(); i++ ){ - Node nc = ( i==0 && ( n.getKind()==IMPLIES || n.getKind()==XOR ) ) ? n[i].notNode() : n[i]; + Node nc = n[i]; Node ncnf = computeCNF( nc, args, defs, k!=OR ); if( k==OR ){ addNodeToOrBuilder( ncnf, t ); @@ -523,7 +547,7 @@ Node QuantifiersRewriter::computePrenex( Node body, std::vector< Node >& args, b bool childrenChanged = false; std::vector< Node > newChildren; for( int i=0; i<(int)body.getNumChildren(); i++ ){ - bool newPol = ( body.getKind()==NOT || ( body.getKind()==IMPLIES && i==0 ) ) ? !pol : pol; + bool newPol = body.getKind()==NOT ? !pol : pol; Node n = computePrenex( body[i], args, newPol ); newChildren.push_back( n ); if( n!=body[i] ){ @@ -661,7 +685,9 @@ Node QuantifiersRewriter::computeOperation( Node f, int computeOption ){ if( f.getNumChildren()==3 ){ ipl = f[2]; } - if( computeOption==COMPUTE_MINISCOPING ){ + if( computeOption==COMPUTE_ELIM_SYMBOLS ){ + n = computeElimSymbols( n ); + }else if( computeOption==COMPUTE_MINISCOPING ){ //return directly return computeMiniscoping( args, n, ipl, f.hasAttribute(NestedQuantAttribute()) ); }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ @@ -746,11 +772,11 @@ Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node bo } return computeMiniscoping( args, t.constructNode(), ipl ); } - }else if( body[0].getKind()==OR || body[0].getKind()==IMPLIES ){ + }else if( body[0].getKind()==OR ){ if( doMiniscopingAnd() ){ NodeBuilder<> t(kind::AND); for( int i=0; i<(int)body[0].getNumChildren(); i++ ){ - Node trm = ( body[0].getKind()==IMPLIES && i==0 ) ? body[0][i] : ( body[0][i].getKind()==NOT ? body[0][i][0] : body[0][i].notNode() ); + Node trm = body[0][i].negate(); t << computeMiniscoping( args, trm, ipl ); } return t.constructNode(); @@ -766,13 +792,13 @@ Node QuantifiersRewriter::computeMiniscoping( std::vector< Node >& args, Node bo Node retVal = t; return retVal; } - }else if( body.getKind()==OR || body.getKind()==IMPLIES ){ + }else if( body.getKind()==OR ){ if( doMiniscopingNoFreeVar() ){ Node newBody = body; NodeBuilder<> body_split(kind::OR); NodeBuilder<> tb(kind::OR); for( int i=0; i<(int)body.getNumChildren(); i++ ){ - Node trm = ( body.getKind()==IMPLIES && i==0 ) ? ( body[i].getKind()==NOT ? body[i][0] : body[i].notNode() ) : body[i]; + Node trm = body[i]; if( hasArg( args, body[i] ) ){ tb << trm; }else{ @@ -916,7 +942,9 @@ bool QuantifiersRewriter::doMiniscopingAnd(){ } bool QuantifiersRewriter::doOperation( Node f, bool isNested, int computeOption ){ - if( computeOption==COMPUTE_MINISCOPING ){ + if( computeOption==COMPUTE_ELIM_SYMBOLS ){ + return true; + }else if( computeOption==COMPUTE_MINISCOPING ){ return true; }else if( computeOption==COMPUTE_AGGRESSIVE_MINISCOPING ){ return options::aggressiveMiniscopeQuant(); diff --git a/src/theory/quantifiers/quantifiers_rewriter.h b/src/theory/quantifiers/quantifiers_rewriter.h index 40234904f..e97d84701 100644 --- a/src/theory/quantifiers/quantifiers_rewriter.h +++ b/src/theory/quantifiers/quantifiers_rewriter.h @@ -44,6 +44,7 @@ private: static void setNestedQuantifiers2( Node n, Node q, std::vector< Node >& processed ); static Node computeClause( Node n ); private: + static Node computeElimSymbols( Node body ); static Node computeMiniscoping( std::vector< Node >& args, Node body, Node ipl, bool isNested = false ); static Node computeAggressiveMiniscoping( std::vector< Node >& args, Node body, bool isNested = false ); static Node computeNNF( Node body ); @@ -54,7 +55,8 @@ private: static Node computeSplit( Node f, Node body, std::vector< Node >& args ); private: enum{ - COMPUTE_MINISCOPING = 0, + COMPUTE_ELIM_SYMBOLS = 0, + COMPUTE_MINISCOPING, COMPUTE_AGGRESSIVE_MINISCOPING, COMPUTE_NNF, COMPUTE_SIMPLE_ITE_LIFT, diff --git a/src/theory/quantifiers/term_database.cpp b/src/theory/quantifiers/term_database.cpp index e18a4e0dc..6b1368be1 100644 --- a/src/theory/quantifiers/term_database.cpp +++ b/src/theory/quantifiers/term_database.cpp @@ -146,6 +146,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ if( !d_func_map_trie[ it->first ].addTerm( d_quantEngine, n ) ){ NoMatchAttribute nma; n.setAttribute(nma,true); + Debug("term-db-cong") << n << " is redundant." << std::endl; congruentCount++; }else{ nonCongruentCount++; @@ -173,6 +174,7 @@ void TermDb::addTerm( Node n, std::set< Node >& added, bool withinQuant ){ if( !d_pred_map_trie[i][op].addTerm( d_quantEngine, en ) ){ NoMatchAttribute nma; en.setAttribute(nma,true); + Debug("term-db-cong") << en << " is redundant." << std::endl; congruentCount++; }else{ nonCongruentCount++; @@ -222,10 +224,14 @@ Node TermDb::getModelBasisOpTerm( Node op ){ TypeNode t = op.getType(); std::vector< Node > children; children.push_back( op ); - for( size_t i=0; i<t.getNumChildren()-1; i++ ){ + for( int i=0; i<(int)(t.getNumChildren()-1); i++ ){ children.push_back( getModelBasisTerm( t[i] ) ); } - d_model_basis_op_term[op] = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + if( children.size()==1 ){ + d_model_basis_op_term[op] = op; + }else{ + d_model_basis_op_term[op] = NodeManager::currentNM()->mkNode( APPLY_UF, children ); + } } return d_model_basis_op_term[op]; } diff --git a/src/theory/quantifiers/trigger.cpp b/src/theory/quantifiers/trigger.cpp index 39063942d..b13e76afb 100644 --- a/src/theory/quantifiers/trigger.cpp +++ b/src/theory/quantifiers/trigger.cpp @@ -342,7 +342,7 @@ bool Trigger::isSimpleTrigger( Node n ){ bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< Node, bool >& patMap, int tstrt, bool pol, bool hasPol ){ if( patMap.find( n )==patMap.end() ){ patMap[ n ] = false; - bool newHasPol = (n.getKind()==IFF || n.getKind()==XOR) ? false : hasPol; + bool newHasPol = n.getKind()==IFF ? false : hasPol; bool newPol = n.getKind()==NOT ? !pol : pol; if( tstrt==TS_MIN_TRIGGER ){ if( n.getKind()==FORALL ){ @@ -350,9 +350,8 @@ bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< }else{ bool retVal = false; for( int i=0; i<(int)n.getNumChildren(); i++ ){ - bool newPol2 = (n.getKind()==IMPLIES && i==0) ? !newPol : newPol; bool newHasPol2 = (n.getKind()==ITE && i==0) ? false : newHasPol; - if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol2, newHasPol2 ) ){ + if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol, newHasPol2 ) ){ retVal = true; } } @@ -381,9 +380,8 @@ bool Trigger::collectPatTerms2( QuantifiersEngine* qe, Node f, Node n, std::map< } if( n.getKind()!=FORALL ){ for( int i=0; i<(int)n.getNumChildren(); i++ ){ - bool newPol2 = (n.getKind()==IMPLIES && i==0) ? !newPol : newPol; bool newHasPol2 = (n.getKind()==ITE && i==0) ? false : newHasPol; - if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol2, newHasPol2 ) ){ + if( collectPatTerms2( qe, f, n[i], patMap, tstrt, newPol, newHasPol2 ) ){ retVal = true; } } diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index e9a69bd30..2ddc83a4b 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -29,6 +29,7 @@ #include "theory/rewriterules/rr_trigger.h" #include "theory/quantifiers/bounded_integers.h" #include "theory/quantifiers/rewrite_engine.h" +#include "theory/quantifiers/quant_conflict_find.h" #include "theory/uf/options.h" using namespace std; @@ -49,14 +50,24 @@ d_lemmas_produced_c(u){ d_eem = new EfficientEMatcher( this ); d_hasAddedLemma = false; + Trace("quant-engine-debug") << "Initialize model, mbqi : " << options::mbqiMode() << std::endl; //the model object - if( options::fmfFullModelCheck() || options::fmfBoundInt() ){ + if( options::mbqiMode()==quantifiers::MBQI_FMC || + options::mbqiMode()==quantifiers::MBQI_FMC_INTERVAL || options::fmfBoundInt() ){ d_model = new quantifiers::fmcheck::FirstOrderModelFmc( this, c, "FirstOrderModelFmc" ); + }else if( options::mbqiMode()==quantifiers::MBQI_INTERVAL ){ + d_model = new quantifiers::FirstOrderModelQInt( this, c, "FirstOrderModelQInt" ); }else{ - d_model = new quantifiers::FirstOrderModelIG( c, "FirstOrderModelIG" ); + d_model = new quantifiers::FirstOrderModelIG( this, c, "FirstOrderModelIG" ); } //add quantifiers modules + if( options::quantConflictFind() ){ + d_qcf = new quantifiers::QuantConflictFind( this, c); + d_modules.push_back( d_qcf ); + }else{ + d_qcf = NULL; + } if( !options::finiteModelFind() || options::fmfInstEngine() ){ //the instantiation must set incomplete flag unless finite model finding is turned on d_inst_engine = new quantifiers::InstantiationEngine( this, !options::finiteModelFind() ); @@ -389,6 +400,10 @@ Node QuantifiersEngine::getInstantiation( Node f, InstMatch& m ){ return getInstantiation( f, vars, terms ); } +Node QuantifiersEngine::getInstantiation( Node f, std::vector< Node >& terms ) { + return getInstantiation( f, d_term_db->d_inst_constants[f], terms ); +} + bool QuantifiersEngine::existsInstantiation( Node f, InstMatch& m, bool modEq, bool modInst ){ if( d_inst_match_trie.find( f )!=d_inst_match_trie.end() ){ if( d_inst_match_trie[f]->existsInstMatch( this, f, m, modEq, modInst ) ){ @@ -493,12 +508,13 @@ bool QuantifiersEngine::addSplitEquality( Node n1, Node n2, bool reqPhase, bool void QuantifiersEngine::flushLemmas( OutputChannel* out ){ if( !d_lemmas_waiting.empty() ){ + if( !out ){ + out = &getOutputChannel(); + } //take default output channel if none is provided d_hasAddedLemma = true; for( int i=0; i<(int)d_lemmas_waiting.size(); i++ ){ - if( out ){ - out->lemma( d_lemmas_waiting[i] ); - } + out->lemma( d_lemmas_waiting[i] ); } d_lemmas_waiting.clear(); } diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index 6de13ff3c..ee7e6e6d8 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -52,7 +52,7 @@ public: /* Call during quantifier engine's check */ virtual void check( Theory::Effort e ) = 0; /* Called for new quantifiers */ - virtual void registerQuantifier( Node n ) = 0; + virtual void registerQuantifier( Node q ) = 0; virtual void assertNode( Node n ) = 0; virtual void propagate( Theory::Effort level ){} virtual Node getNextDecisionRequest() { return TNode::null(); } @@ -66,6 +66,7 @@ namespace quantifiers { class InstantiationEngine; class ModelEngine; class BoundedIntegers; + class QuantConflictFind; class RewriteEngine; }/* CVC4::theory::quantifiers */ @@ -105,6 +106,8 @@ private: quantifiers::ModelEngine* d_model_engine; /** bounded integers utility */ quantifiers::BoundedIntegers * d_bint; + /** Conflict find mechanism for quantifiers */ + quantifiers::QuantConflictFind* d_qcf; /** rewrite rules utility */ quantifiers::RewriteEngine * d_rr_engine; private: @@ -165,6 +168,8 @@ public: EfficientEMatcher* getEfficientEMatcher() { return d_eem; } /** get bounded integers utility */ quantifiers::BoundedIntegers * getBoundedIntegers() { return d_bint; } + /** Conflict find mechanism for quantifiers */ + quantifiers::QuantConflictFind* getConflictFind() { return d_qcf; } public: /** initialize */ void finishInit(); @@ -194,6 +199,8 @@ public: Node getInstantiation( Node f, std::vector< Node >& vars, std::vector< Node >& terms ); /** get instantiation */ Node getInstantiation( Node f, InstMatch& m ); + /** get instantiation */ + Node getInstantiation( Node f, std::vector< Node >& terms ); /** exist instantiation ? */ bool existsInstantiation( Node f, InstMatch& m, bool modEq = true, bool modInst = false ); /** add lemma lem */ @@ -207,7 +214,7 @@ public: /** has added lemma */ bool hasAddedLemma() { return !d_lemmas_waiting.empty() || d_hasAddedLemma; } /** flush lemmas */ - void flushLemmas( OutputChannel* out ); + void flushLemmas( OutputChannel* out = NULL ); /** get number of waiting lemmas */ int getNumLemmasWaiting() { return (int)d_lemmas_waiting.size(); } public: diff --git a/src/theory/strings/kinds b/src/theory/strings/kinds index 20db916c9..b30bf8f36 100644 --- a/src/theory/strings/kinds +++ b/src/theory/strings/kinds @@ -11,12 +11,15 @@ typechecker "theory/strings/theory_strings_type_rules.h" operator STRING_CONCAT 2: "string concat" - operator STRING_IN_REGEXP 2 "membership" - operator STRING_LENGTH 1 "string length" - -operator STRING_SUBSTR 3 "string substr" +operator STRING_SUBSTR 3 "string substr (user symbol)" +operator STRING_SUBSTR_TOTAL 3 "string substr (internal symbol)" +operator STRING_CHARAT 2 "string charat (user symbol)" +operator STRING_CHARAT_TOTAL 2 "string charat (internal symbol)" +operator STRING_STRCTN 2 "string contains" +operator STRING_STRIDOF 3 "string indexof" +operator STRING_STRREPL 3 "string replace" #sort CHAR_TYPE \ # Cardinality::INTEGERS \ @@ -104,6 +107,12 @@ typerule STRING_TO_REGEXP ::CVC4::theory::strings::StringToRegExpTypeRule typerule STRING_CONCAT ::CVC4::theory::strings::StringConcatTypeRule typerule STRING_LENGTH ::CVC4::theory::strings::StringLengthTypeRule typerule STRING_SUBSTR ::CVC4::theory::strings::StringSubstrTypeRule +typerule STRING_SUBSTR_TOTAL ::CVC4::theory::strings::StringSubstrTypeRule +typerule STRING_CHARAT ::CVC4::theory::strings::StringCharAtTypeRule +typerule STRING_CHARAT_TOTAL ::CVC4::theory::strings::StringCharAtTypeRule +typerule STRING_STRCTN ::CVC4::theory::strings::StringContainTypeRule +typerule STRING_STRIDOF ::CVC4::theory::strings::StringIndexOfTypeRule +typerule STRING_STRREPL ::CVC4::theory::strings::StringReplaceTypeRule typerule STRING_IN_REGEXP ::CVC4::theory::strings::StringInRegExpTypeRule diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index 62e71327e..4b479e348 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -43,6 +43,8 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, Outpu d_nf_pairs(c), //d_var_lmin( c ), //d_var_lmax( c ), + d_str_pos_ctn( c ), + d_str_neg_ctn( c ), d_reg_exp_mem( c ), d_curr_cardinality( c, 0 ) { @@ -50,6 +52,7 @@ TheoryStrings::TheoryStrings(context::Context* c, context::UserContext* u, Outpu //d_equalityEngine.addFunctionKind(kind::STRING_IN_REGEXP); d_equalityEngine.addFunctionKind(kind::STRING_LENGTH); d_equalityEngine.addFunctionKind(kind::STRING_CONCAT); + d_equalityEngine.addFunctionKind(kind::STRING_STRCTN); d_zero = NodeManager::currentNM()->mkConst( Rational( 0 ) ); d_emptyString = NodeManager::currentNM()->mkConst( ::CVC4::String("") ); @@ -308,7 +311,7 @@ void TheoryStrings::collectModelInfo( TheoryModel* m, bool fullModel ) { } } } - Trace("strings-model") << "String Model : Finished." << std::endl; + Trace("strings-model") << "String Model : Pure Assigned." << std::endl; //step 4 : assign constants to all other equivalence classes for( unsigned i=0; i<nodes.size(); i++ ){ if( processed.find( nodes[i] )==processed.end() ){ @@ -336,6 +339,20 @@ void TheoryStrings::collectModelInfo( TheoryModel* m, bool fullModel ) { m->assertEquality( nodes[i], cc, true ); } } + Trace("strings-model") << "String Model : Assigned." << std::endl; + //check for negative contains + /* + Trace("strings-model") << "String Model : Check Neg Contains, size = " << d_str_neg_ctn.size() << std::endl; + for( unsigned i=0; i<d_str_neg_ctn.size(); i++ ) { + Node x = d_str_neg_ctn[i][0]; + Node y = d_str_neg_ctn[i][1]; + Trace("strings-model") << "String Model : Check Neg contains: ~contains(" << x << ", " << y << ")." << std::endl; + //Node xv = m->getValue(x); + //Node yv = m->getValue(y); + //Trace("strings-model") << "String Model : Check Neg contains Value: ~contains(" << xv << ", " << yv << ")." << std::endl; + } + */ + Trace("strings-model") << "String Model : Finished." << std::endl; } ///////////////////////////////////////////////////////////////////////////// @@ -387,14 +404,13 @@ void TheoryStrings::check(Effort e) { bool polarity; TNode atom; - if( !done() && !hasTerm( d_emptyString ) ){ + if( !done() && !hasTerm( d_emptyString ) ) { preRegisterTerm( d_emptyString ); } // Trace("strings-process") << "Theory of strings, check : " << e << std::endl; Trace("strings-check") << "Theory of strings, check : " << e << std::endl; - while ( !done() && !d_conflict) - { + while ( !done() && !d_conflict ) { // Get all the assertions Assertion assertion = get(); TNode fact = assertion.assertion; @@ -406,7 +422,14 @@ void TheoryStrings::check(Effort e) { //must record string in regular expressions if ( atom.getKind() == kind::STRING_IN_REGEXP ) { d_reg_exp_mem.push_back( assertion ); - }else if (atom.getKind() == kind::EQUAL) { + } else if (atom.getKind() == kind::STRING_STRCTN) { + if(polarity) { + d_str_pos_ctn.push_back( atom ); + } else { + d_str_neg_ctn.push_back( atom ); + } + d_equalityEngine.assertPredicate(atom, polarity, fact); + } else if (atom.getKind() == kind::EQUAL) { d_equalityEngine.assertEquality(atom, polarity, fact); } else { d_equalityEngine.assertPredicate(atom, polarity, fact); @@ -417,24 +440,28 @@ void TheoryStrings::check(Effort e) { bool addedLemma = false; if( e == EFFORT_FULL && !d_conflict ) { - addedLemma = checkLengths(); - Trace("strings-process") << "Done check (constant) lengths, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; - if( !addedLemma ){ - addedLemma = checkNormalForms(); - Trace("strings-process") << "Done check normal forms, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; - if(!d_conflict && !addedLemma) { - addedLemma = checkLengthsEqc(); - Trace("strings-process") << "Done check lengths, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; - if(!d_conflict && !addedLemma) { - addedLemma = checkCardinality(); - Trace("strings-process") << "Done check cardinality, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; - if( !d_conflict && !addedLemma ){ + addedLemma = checkLengths(); + Trace("strings-process") << "Done check (constant) lengths, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if( !addedLemma ) { + addedLemma = checkNormalForms(); + Trace("strings-process") << "Done check normal forms, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if(!d_conflict && !addedLemma) { + addedLemma = checkLengthsEqc(); + Trace("strings-process") << "Done check lengths, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if(!d_conflict && !addedLemma) { + addedLemma = checkCardinality(); + Trace("strings-process") << "Done check cardinality, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if( !d_conflict && !addedLemma ) { addedLemma = checkMemberships(); Trace("strings-process") << "Done check membership constraints, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; - } - } - } - } + if( !d_conflict && !addedLemma ) { + addedLemma = checkContains(); + Trace("strings-process") << "Done check contain constraints, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + } + } + } + } + } } Trace("strings-check") << "Theory of strings, done check : " << e << std::endl; Trace("strings-process") << "Theory of strings, done check : " << e << std::endl; @@ -713,6 +740,7 @@ bool TheoryStrings::getNormalForms(Node &eqc, std::vector< Node > & visited, std Trace("strings-solve") << "--- Normal forms for equivlance class " << eqc << " : " << std::endl; for( unsigned i=0; i<normal_forms.size(); i++ ) { Trace("strings-solve") << "#" << i << " (from " << normal_form_src[i] << ") : "; + //mergeCstVec(normal_forms[i]); for( unsigned j=0; j<normal_forms[i].size(); j++ ) { if(j>0) Trace("strings-solve") << ", "; Trace("strings-solve") << normal_forms[i][j]; @@ -733,6 +761,27 @@ bool TheoryStrings::getNormalForms(Node &eqc, std::vector< Node > & visited, std return true; } +void TheoryStrings::mergeCstVec(std::vector< Node > &vec_strings) { + std::vector< Node >::iterator itr = vec_strings.begin(); + while(itr != vec_strings.end()) { + if(itr->isConst()) { + std::vector< Node >::iterator itr2 = itr + 1; + if(itr2 == vec_strings.end()) { + break; + } else if(itr2->isConst()) { + CVC4::String s1 = itr->getConst<String>(); + CVC4::String s2 = itr2->getConst<String>(); + *itr = NodeManager::currentNM()->mkConst(s1.concat(s2)); + vec_strings.erase(itr2); + } else { + ++itr; + } + } else { + ++itr; + } + } +} + bool TheoryStrings::detectLoop( std::vector< std::vector< Node > > &normal_forms, int i, int j, int index_i, int index_j, int &loop_in_i, int &loop_in_j) { @@ -1380,7 +1429,7 @@ bool TheoryStrings::isNormalFormPair2( Node n1, Node n2 ) { } void TheoryStrings::sendLemma( Node ant, Node conc, const char * c ) { - if( conc.isNull() || conc==d_false ){ + if( conc.isNull() ){ d_out->conflict(ant); Trace("strings-conflict") << "Strings::Conflict : " << ant << std::endl; d_conflict = true; @@ -2028,33 +2077,18 @@ bool TheoryStrings::checkMemberships() { } } else { //TODO: negative membership - //Node r = Rewriter::rewrite( atom[1] ); - //r = d_regexp_opr.complement( r ); - //Trace("strings-regexp-test") << "Compl( " << d_regexp_opr.mkString( atom[1] ) << " ) is " << d_regexp_opr.mkString( r ) << std::endl; - //Trace("strings-regexp-test") << "Delta( " << d_regexp_opr.mkString( atom[1] ) << " ) is " << d_regexp_opr.delta( atom[1] ) << std::endl; - //Trace("strings-regexp-test") << "Delta( " << d_regexp_opr.mkString( r ) << " ) is " << d_regexp_opr.delta( r ) << std::endl; - //Trace("strings-regexp-test") << "Deriv( " << d_regexp_opr.mkString( r ) << ", c='b' ) is " << d_regexp_opr.mkString( d_regexp_opr.derivativeSingle( r, ::CVC4::String("b") ) ) << std::endl; - //Trace("strings-regexp-test") << "FHC( " << d_regexp_opr.mkString( r ) <<" ) is " << std::endl; - //d_regexp_opr.firstChar( r ); - //r = NodeManager::currentNM()->mkNode( kind::STRING_IN_REGEXP, atom[0], r ); - /* - std::vector< Node > vec_r; - vec_r.push_back( r ); - - StringsPreprocess spp; - spp.simplify( vec_r ); - for( unsigned i=1; i<vec_r.size(); i++ ){ - if(vec_r[i].getKind() == kind::STRING_IN_REGEXP) { - d_reg_exp_mem.push_back( vec_r[i] ); - } else if(vec_r[i].getKind() == kind::EQUAL) { - d_equalityEngine.assertEquality(vec_r[i], true, vec_r[i]); - } else { - Assert(false); - } + Node x = atom[0]; + Node r = atom[1]; + Assert( r.getKind()==kind::REGEXP_STAR ); + if( areEqual( x, d_emptyString ) ) { + Node ant = NodeManager::currentNM()->mkNode( kind::AND, assertion, x.eqNode( d_emptyString ) ); + Node conc = Node::null(); + sendLemma( ant, conc, "RegExp Empty Conflict" ); + addedLemma = true; + } else { + Trace("strings-regexp") << "RegEx is incomplete due to " << assertion << "." << std::endl; + is_unk = true; } - */ - Trace("strings-regexp") << "RegEx is incomplete due to " << assertion << "." << std::endl; - is_unk = true; } } if( addedLemma ){ @@ -2077,6 +2111,130 @@ bool TheoryStrings::checkMemberships() { } } +bool TheoryStrings::checkContains() { + bool addedLemma = checkPosContains(); + Trace("strings-process") << "Done check positive contain constraints, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + if(!addedLemma) { + addedLemma = checkNegContains(); + Trace("strings-process") << "Done check positive contain constraints, addedLemma = " << addedLemma << ", d_conflict = " << d_conflict << std::endl; + } + return addedLemma; +} + +bool TheoryStrings::checkPosContains() { + bool addedLemma = false; + for( unsigned i=0; i<d_str_pos_ctn.size(); i++ ) { + Node atom = d_str_pos_ctn[i]; + Trace("strings-ctn") << "We have positive contain assertion : " << atom << std::endl; + Assert( atom.getKind()==kind::STRING_STRCTN ); + Node x = atom[0]; + Node s = atom[1]; + if( !areEqual( s, d_emptyString ) && !areEqual( s, x ) ) { + if(d_str_pos_ctn_rewritten.find(atom) == d_str_pos_ctn_rewritten.end()) { + Node sk1 = NodeManager::currentNM()->mkSkolem( "sc1_$$", s.getType(), "created for contain" ); + Node sk2 = NodeManager::currentNM()->mkSkolem( "sc2_$$", s.getType(), "created for contain" ); + Node eq = Rewriter::rewrite( x.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, s, sk2 ) ) ); + sendLemma( atom, eq, "POS-INC" ); + addedLemma = true; + d_str_pos_ctn_rewritten[ atom ] = true; + } else { + Trace("strings-ctn") << "... is already rewritten." << std::endl; + } + } else { + Trace("strings-ctn") << "... is satisfied." << std::endl; + } + } + if( addedLemma ){ + doPendingLemmas(); + return true; + } else { + return false; + } +} + +bool TheoryStrings::checkNegContains() { + bool is_unk = false; + bool addedLemma = false; + for( unsigned i=0; i<d_str_neg_ctn.size(); i++ ){ + Node atom = d_str_neg_ctn[i]; + Trace("strings-ctn") << "We have nagetive contain assertion : (not " << atom << " )" << std::endl; + if( areEqual( atom[1], d_emptyString ) ) { + Node ant = NodeManager::currentNM()->mkNode( kind::AND, atom.negate(), atom[1].eqNode( d_emptyString ) ); + Node conc = Node::null(); + sendLemma( ant, conc, "NEG-CTN Conflict 1" ); + addedLemma = true; + } else if( areEqual( atom[1], atom[0] ) ) { + Node ant = NodeManager::currentNM()->mkNode( kind::AND, atom.negate(), atom[1].eqNode( atom[0] ) ); + Node conc = Node::null(); + sendLemma( ant, conc, "NEG-CTN Conflict 2" ); + addedLemma = true; + } else { + if(options::stringExp()) { + Node x = atom[0]; + Node s = atom[1]; + Node lenx = getLength(x); + Node lens = getLength(s); + if(areEqual(lenx, lens)) { + if(d_str_ctn_eqlen.find(atom) == d_str_ctn_eqlen.end()) { + Node eq = lenx.eqNode(lens); + Node antc = Rewriter::rewrite( NodeManager::currentNM()->mkNode( kind::AND, atom.negate(), eq ) ); + Node xneqs = x.eqNode(s).negate(); + d_str_ctn_eqlen[ atom ] = true; + sendLemma( antc, xneqs, "NEG-CTN-EQL" ); + addedLemma = true; + } + } else if(!areDisequal(lenx, lens)) { + sendSplit(lenx, lens, "NEG-CTN-SP"); + addedLemma = true; + } else { + if(d_str_neg_ctn_rewritten.find(atom) == d_str_neg_ctn_rewritten.end()) { + Node e1 = NodeManager::currentNM()->mkSkolem( "nc1_$$", s.getType(), "created for contain" ); + Node e2 = NodeManager::currentNM()->mkSkolem( "nc2_$$", s.getType(), "created for contain" ); + Node z = NodeManager::currentNM()->mkSkolem( "ncz_$$", s.getType(), "created for contain" ); + Node w = NodeManager::currentNM()->mkSkolem( "ncw_$$", s.getType(), "created for contain" ); + std::vector< Node > vec_conc; + Node cc = Rewriter::rewrite( x.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, e1, z ) ) ); + vec_conc.push_back(cc); + cc = Rewriter::rewrite( x.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, w, e2 ) ) ); + vec_conc.push_back(cc); + cc = Rewriter::rewrite( z.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, s, e2 ) ).negate() ); + vec_conc.push_back(cc); + cc = Rewriter::rewrite( w.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, e1, s ) ).negate() ); + vec_conc.push_back(cc); + cc = Rewriter::rewrite( lenx.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, e1, s, e2 ) ) ) ); + vec_conc.push_back(cc); + //Incomplete + //cc = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, e1, s ).negate(); + //vec_conc.push_back(cc); + //cc = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, e2, s ).negate(); + //vec_conc.push_back(cc); + Node conc = NodeManager::currentNM()->mkNode( kind::AND, vec_conc ); + Node xlss = NodeManager::currentNM()->mkNode( kind::GT, lens, lenx ); + conc = NodeManager::currentNM()->mkNode( kind::OR, xlss, conc ); + d_str_neg_ctn_rewritten[ atom ] = true; + sendLemma( atom.negate(), conc, "NEG-INC-BRK" ); + addedLemma = true; + } + } + } else { + Trace("strings-ctn") << "Strings Incomplete (due to Negative Contain) by default." << std::endl; + is_unk = true; + } + } + } + if( addedLemma ){ + doPendingLemmas(); + return true; + } else { + if( is_unk ){ + Trace("strings-ctn") << "Strings::inc: possibly incomplete." << std::endl; + d_out->setIncomplete(); + } + return false; + } +} + CVC4::String TheoryStrings::getHeadConst( Node x ) { if( x.isConst() ) { return x.getConst< String >(); @@ -2092,8 +2250,8 @@ CVC4::String TheoryStrings::getHeadConst( Node x ) { } bool TheoryStrings::addMembershipLength(Node atom) { - Node x = atom[0]; - Node r = atom[1]; + //Node x = atom[0]; + //Node r = atom[1]; /*std::vector< int > co; co.push_back(0); diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index 875f407c5..1d92abbd2 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -191,6 +191,7 @@ private: //maintain which concat terms have the length lemma instantiated std::map< Node, bool > d_length_inst; private: + void mergeCstVec(std::vector< Node > &vec_strings); bool getNormalForms(Node &eqc, std::vector< Node > & visited, std::vector< Node > & nf, std::vector< std::vector< Node > > &normal_forms, std::vector< std::vector< Node > > &normal_forms_exp, @@ -216,6 +217,9 @@ private: bool checkCardinality(); bool checkInductiveEquations(); bool checkMemberships(); + bool checkContains(); + bool checkPosContains(); + bool checkNegContains(); public: void preRegisterTerm(TNode n); @@ -269,6 +273,13 @@ private: Node mkSplitEq( const char * c, const char * info, Node lhs, Node rhs, bool lgtZero ); int getMaxPossibleLength( Node x ); + // Special String Functions + NodeList d_str_pos_ctn; + NodeList d_str_neg_ctn; + std::map< Node, bool > d_str_ctn_eqlen; + std::map< Node, bool > d_str_pos_ctn_rewritten; + std::map< Node, bool > d_str_neg_ctn_rewritten; + // Regular Expression private: // regular expression memberships diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index ff01b1887..11e206eeb 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -23,6 +23,9 @@ namespace CVC4 { namespace theory { namespace strings { +StringsPreprocess::StringsPreprocess() { +} + void StringsPreprocess::simplifyRegExp( Node s, Node r, std::vector< Node > &ret, std::vector< Node > &nn ) { int k = r.getKind(); switch( k ) { @@ -115,28 +118,117 @@ Node StringsPreprocess::simplify( Node t, std::vector< Node > &new_nodes ) { // TODO // return (t0 == t[0]) ? t : NodeManager::currentNM()->mkNode( kind::STRING_IN_REGEXP, t0, t[1] ); //} - } else if( t.getKind() == kind::STRING_SUBSTR ){ + } else if( t.getKind() == kind::STRING_SUBSTR_TOTAL ){ + Node sk1 = NodeManager::currentNM()->mkSkolem( "st1_$$", t.getType(), "created for substr" ); + Node sk2 = NodeManager::currentNM()->mkSkolem( "st2_$$", t.getType(), "created for substr" ); + Node sk3 = NodeManager::currentNM()->mkSkolem( "st3_$$", t.getType(), "created for substr" ); + Node x = simplify( t[0], new_nodes ); + Node lenxgti = NodeManager::currentNM()->mkNode( kind::GEQ, + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, x ), + NodeManager::currentNM()->mkNode( kind::PLUS, t[1], t[2] ) ); + Node x_eq_123 = NodeManager::currentNM()->mkNode( kind::EQUAL, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2, sk3 ), x ); + Node len_sk1_eq_i = NodeManager::currentNM()->mkNode( kind::EQUAL, t[1], + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ) ); + Node len_sk2_eq_j = NodeManager::currentNM()->mkNode( kind::EQUAL, t[2], + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2 ) ); + + Node tp = NodeManager::currentNM()->mkNode( kind::AND, x_eq_123, len_sk1_eq_i, len_sk2_eq_j ); + tp = NodeManager::currentNM()->mkNode( kind::IMPLIES, lenxgti, tp ); + new_nodes.push_back( tp ); + + d_cache[t] = sk2; + retNode = sk2; + } else if( t.getKind() == kind::STRING_CHARAT_TOTAL ){ + Node sk1 = NodeManager::currentNM()->mkSkolem( "ca1_$$", t.getType(), "created for charat" ); + Node sk2 = NodeManager::currentNM()->mkSkolem( "ca2_$$", t.getType(), "created for charat" ); + Node sk3 = NodeManager::currentNM()->mkSkolem( "ca3_$$", t.getType(), "created for charat" ); + Node x = simplify( t[0], new_nodes ); + Node lenxgti = NodeManager::currentNM()->mkNode( kind::GT, + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, x ), t[1] ); + Node x_eq_123 = NodeManager::currentNM()->mkNode( kind::EQUAL, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2, sk3 ), x ); + Node len_sk1_eq_i = NodeManager::currentNM()->mkNode( kind::EQUAL, t[1], + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ) ); + Node len_sk2_eq_1 = NodeManager::currentNM()->mkNode( kind::EQUAL, NodeManager::currentNM()->mkConst( Rational( 1 ) ), + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2 ) ); + + Node tp = NodeManager::currentNM()->mkNode( kind::AND, x_eq_123, len_sk1_eq_i, len_sk2_eq_1 ); + tp = NodeManager::currentNM()->mkNode( kind::IMPLIES, lenxgti, tp ); + new_nodes.push_back( tp ); + + d_cache[t] = sk2; + retNode = sk2; + } else if( t.getKind() == kind::STRING_STRIDOF ){ + if(options::stringExp()) { + Node sk1 = NodeManager::currentNM()->mkSkolem( "io1_$$", t[0].getType(), "created for indexof" ); + Node sk2 = NodeManager::currentNM()->mkSkolem( "io2_$$", t[0].getType(), "created for indexof" ); + Node sk3 = NodeManager::currentNM()->mkSkolem( "io3_$$", t[0].getType(), "created for indexof" ); + Node sk4 = NodeManager::currentNM()->mkSkolem( "io4_$$", t[0].getType(), "created for indexof" ); + Node skk = NodeManager::currentNM()->mkSkolem( "iok_$$", t[2].getType(), "created for indexof" ); + Node eq = t[0].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2, sk3, sk4 ) ); + new_nodes.push_back( eq ); + Node negone = NodeManager::currentNM()->mkConst( ::CVC4::Rational(-1) ); + Node krange = NodeManager::currentNM()->mkNode( kind::AND, + NodeManager::currentNM()->mkNode( kind::GEQ, skk, negone ), + NodeManager::currentNM()->mkNode( kind::GT, + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] ), + skk)); + new_nodes.push_back( krange ); + + //str.len(s1) < y + str.len(s2) + Node c1 = NodeManager::currentNM()->mkNode( kind::GT, NodeManager::currentNM()->mkNode( kind::PLUS, t[2], NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[1] )), + NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, t[0] )); + //str.len(t1) = y + Node c2 = t[2].eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ) ); + //~contain(t234, s2) + Node c3 = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk2, sk3, sk4), t[1] ).negate(); + //left + Node left = NodeManager::currentNM()->mkNode( kind::OR, c1, NodeManager::currentNM()->mkNode( kind::AND, c2, c3 ) ); + //t3 = s2 + Node c4 = t[1].eqNode( sk3 ); + //~contain(t2, s2) + Node c5 = NodeManager::currentNM()->mkNode( kind::STRING_STRCTN, sk2, t[1] ).negate(); + //k=str.len(s1, s2) + Node c6 = skk.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, + NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2 ))); + //right + Node right = NodeManager::currentNM()->mkNode( kind::AND, c2, c4, c5, c6 ); + Node cond = skk.eqNode( negone ); + Node rr = NodeManager::currentNM()->mkNode( kind::ITE, cond, left, right ); + new_nodes.push_back( rr ); + d_cache[t] = skk; + retNode = skk; + } else { + throw LogicException("string indexof not supported in this release"); + } + } else if( t.getKind() == kind::STRING_STRREPL ){ if(options::stringExp()) { - Node sk1 = NodeManager::currentNM()->mkSkolem( "st1sym_$$", t.getType(), "created for substr" ); - Node sk2 = NodeManager::currentNM()->mkSkolem( "st2sym_$$", t.getType(), "created for substr" ); - Node sk3 = NodeManager::currentNM()->mkSkolem( "st3sym_$$", t.getType(), "created for substr" ); - Node x = simplify( t[0], new_nodes ); - Node x_eq_123 = NodeManager::currentNM()->mkNode( kind::EQUAL, - NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, sk2, sk3 ), x ); - new_nodes.push_back( x_eq_123 ); - Node len_sk1_eq_i = NodeManager::currentNM()->mkNode( kind::EQUAL, t[1], - NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ) ); - new_nodes.push_back( len_sk1_eq_i ); - Node len_sk2_eq_j = NodeManager::currentNM()->mkNode( kind::EQUAL, t[2], - NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk2 ) ); - new_nodes.push_back( len_sk2_eq_j ); - - d_cache[t] = sk2; - retNode = sk2; + Node zero = NodeManager::currentNM()->mkConst( ::CVC4::Rational(0) ); + Node x = t[0]; + Node y = t[1]; + Node z = t[2]; + Node sk1 = NodeManager::currentNM()->mkSkolem( "rp1_$$", t[0].getType(), "created for replace" ); + Node sk2 = NodeManager::currentNM()->mkSkolem( "rp2_$$", t[0].getType(), "created for replace" ); + Node skw = NodeManager::currentNM()->mkSkolem( "rpw_$$", t[0].getType(), "created for replace" ); + Node iof = NodeManager::currentNM()->mkNode( kind::STRING_STRIDOF, x, y, zero ); + Node igeq0 = NodeManager::currentNM()->mkNode( kind::GEQ, iof, zero ); + Node c1 = x.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, y, sk2 ) ); + Node c2 = skw.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, sk1, z, sk2 ) ); + Node c3 = iof.eqNode( NodeManager::currentNM()->mkNode( kind::STRING_LENGTH, sk1 ) ); + Node rr = NodeManager::currentNM()->mkNode( kind::ITE, igeq0, + NodeManager::currentNM()->mkNode( kind::AND, c1, c2, c3 ), + skw.eqNode(x) ); + new_nodes.push_back( rr ); + d_cache[t] = skw; + retNode = skw; } else { - throw LogicException("substring not supported in this release"); + throw LogicException("string replace not supported in this release"); } - } else if( t.getNumChildren()>0 ){ + } else if(t.getKind() == kind::STRING_CHARAT || t.getKind() == kind::STRING_SUBSTR) { + InternalError("CharAt and Substr should not be reached here."); + } else if( t.getNumChildren()>0 ) { std::vector< Node > cc; if (t.getMetaKind() == kind::metakind::PARAMETERIZED) { cc.push_back(t.getOperator()); diff --git a/src/theory/strings/theory_strings_preprocess.h b/src/theory/strings/theory_strings_preprocess.h index 698ef6ba1..6d278cc7a 100644 --- a/src/theory/strings/theory_strings_preprocess.h +++ b/src/theory/strings/theory_strings_preprocess.h @@ -36,6 +36,7 @@ private: Node simplify( Node t, std::vector< Node > &new_nodes ); public: void simplify(std::vector< Node > &vec_node); + StringsPreprocess(); }; }/* CVC4::theory::strings namespace */ diff --git a/src/theory/strings/theory_strings_rewriter.cpp b/src/theory/strings/theory_strings_rewriter.cpp index 0e37367bf..9f278aac2 100644 --- a/src/theory/strings/theory_strings_rewriter.cpp +++ b/src/theory/strings/theory_strings_rewriter.cpp @@ -340,22 +340,67 @@ RewriteResponse TheoryStringsRewriter::postRewrite(TNode node) { retNode = NodeManager::currentNM()->mkNode(kind::PLUS, node_vec); } } - } else if(node.getKind() == kind::STRING_SUBSTR) { - if(options::stringExp()) { - if( node[0].isConst() && node[1].isConst() && node[2].isConst() ) { - int i = node[1].getConst<Rational>().getNumerator().toUnsignedInt(); - int j = node[2].getConst<Rational>().getNumerator().toUnsignedInt(); - if( node[0].getConst<String>().size() >= (unsigned) (i + j) ) { - retNode = NodeManager::currentNM()->mkConst( node[0].getConst<String>().substr(i, j) ); + } else if(node.getKind() == kind::STRING_SUBSTR_TOTAL) { + if( node[0].isConst() && node[1].isConst() && node[2].isConst() ) { + int i = node[1].getConst<Rational>().getNumerator().toUnsignedInt(); + int j = node[2].getConst<Rational>().getNumerator().toUnsignedInt(); + if( node[0].getConst<String>().size() >= (unsigned) (i + j) ) { + retNode = NodeManager::currentNM()->mkConst( node[0].getConst<String>().substr(i, j) ); + } + } + } else if(node.getKind() == kind::STRING_STRCTN) { + if( node[0] == node[1] ) { + retNode = NodeManager::currentNM()->mkConst( true ); + } else if( node[0].isConst() && node[1].isConst() ) { + CVC4::String s = node[0].getConst<String>(); + CVC4::String t = node[1].getConst<String>(); + if( s.find(t) != std::string::npos ) { + retNode = NodeManager::currentNM()->mkConst( true ); + } else { + retNode = NodeManager::currentNM()->mkConst( false ); + } + } + } else if(node.getKind() == kind::STRING_CHARAT_TOTAL) { + if( node[0].isConst() && node[1].isConst() ) { + int i = node[1].getConst<Rational>().getNumerator().toUnsignedInt(); + if( node[0].getConst<String>().size() > (unsigned) i ) { + retNode = NodeManager::currentNM()->mkConst( node[0].getConst<String>().substr(i, 1) ); + } + } + } else if(node.getKind() == kind::STRING_STRIDOF) { + if( node[0].isConst() && node[1].isConst() && node[2].isConst() ) { + CVC4::String s = node[0].getConst<String>(); + CVC4::String t = node[1].getConst<String>(); + int i = node[2].getConst<Rational>().getNumerator().toUnsignedInt(); + std::size_t ret = s.find(t, i); + if( ret != std::string::npos ) { + retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational((int) ret) ); + } else { + retNode = NodeManager::currentNM()->mkConst( ::CVC4::Rational(-1) ); + } + } + } else if(node.getKind() == kind::STRING_STRREPL) { + if(node[1] != node[2]) { + if(node[0].isConst() && node[1].isConst()) { + CVC4::String s = node[0].getConst<String>(); + CVC4::String t = node[1].getConst<String>(); + std::size_t p = s.find(t); + if( p != std::string::npos ) { + if(node[2].isConst()) { + CVC4::String r = node[2].getConst<String>(); + CVC4::String ret = s.replace(t, r); + retNode = NodeManager::currentNM()->mkConst( ::CVC4::String(ret) ); + } else { + CVC4::String s1 = s.substr(0, (int)p); + CVC4::String s3 = s.substr((int)p + (int)t.size()); + Node ns1 = NodeManager::currentNM()->mkConst( ::CVC4::String(s1) ); + Node ns3 = NodeManager::currentNM()->mkConst( ::CVC4::String(s3) ); + retNode = NodeManager::currentNM()->mkNode( kind::STRING_CONCAT, ns1, node[2], ns3 ); + } } else { - // TODO: some issues, must be guarded by users - retNode = NodeManager::currentNM()->mkConst( false ); + retNode = node[0]; } - } else { - //handled by preprocess } - } else { - throw LogicException("substring not supported in this release"); } } else if(node.getKind() == kind::STRING_IN_REGEXP) { retNode = rewriteMembership(node); diff --git a/src/theory/strings/theory_strings_type_rules.h b/src/theory/strings/theory_strings_type_rules.h index d5019ab39..29fdf27a6 100644 --- a/src/theory/strings/theory_strings_type_rules.h +++ b/src/theory/strings/theory_strings_type_rules.h @@ -73,15 +73,95 @@ public: if( check ){ TypeNode t = n[0].getType(check); if (!t.isString()) { - throw TypeCheckingExceptionPrivate(n, "expecting string terms in substr"); + throw TypeCheckingExceptionPrivate(n, "expecting a string term in substr"); } t = n[1].getType(check); if (!t.isInteger()) { - throw TypeCheckingExceptionPrivate(n, "expecting start int terms in substr"); + throw TypeCheckingExceptionPrivate(n, "expecting a start int term in substr"); } t = n[2].getType(check); if (!t.isInteger()) { - throw TypeCheckingExceptionPrivate(n, "expecting length int terms in substr"); + throw TypeCheckingExceptionPrivate(n, "expecting a length int term in substr"); + } + } + return nodeManager->stringType(); + } +}; + +class StringContainTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ){ + TypeNode t = n[0].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting an orginal string term in string contain"); + } + t = n[1].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a target string term in string contain"); + } + } + return nodeManager->booleanType(); + } +}; + +class StringCharAtTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ){ + TypeNode t = n[0].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a string term in string char at 0"); + } + t = n[1].getType(check); + if (!t.isInteger()) { + throw TypeCheckingExceptionPrivate(n, "expecting an integer term in string char at 1"); + } + } + return nodeManager->stringType(); + } +}; + +class StringIndexOfTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ){ + TypeNode t = n[0].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a string term in string indexof 0"); + } + t = n[1].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a string term in string indexof 1"); + } + t = n[2].getType(check); + if (!t.isInteger()) { + throw TypeCheckingExceptionPrivate(n, "expecting an integer term in string indexof 2"); + } + } + return nodeManager->integerType(); + } +}; + +class StringReplaceTypeRule { +public: + inline static TypeNode computeType(NodeManager* nodeManager, TNode n, bool check) + throw (TypeCheckingExceptionPrivate, AssertionException) { + if( check ){ + TypeNode t = n[0].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a string term in string replace 0"); + } + t = n[1].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a string term in string replace 1"); + } + t = n[2].getType(check); + if (!t.isString()) { + throw TypeCheckingExceptionPrivate(n, "expecting a string term in string replace 2"); } } return nodeManager->stringType(); diff --git a/src/theory/theory.h b/src/theory/theory.h index fdd2d0518..43d35ac9d 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -254,6 +254,7 @@ protected: , d_sharedTerms(satContext) , d_out(&out) , d_valuation(valuation) + , d_proofEnabled(false) { StatisticsRegistry::registerStat(&d_computeCareGraphTime); } @@ -299,6 +300,12 @@ protected: void printFacts(std::ostream& os) const; void debugPrintFacts() const; + /** + * Whether proofs are enabled + * + */ + bool d_proofEnabled; + public: /** diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index 7cf4d7ad9..e55d6a9c9 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -75,12 +75,18 @@ void TheoryEngine::finishInit() { void TheoryEngine::eqNotifyNewClass(TNode t){ d_quantEngine->addTermToDatabase( t ); + if( d_logicInfo.isQuantified() && options::quantConflictFind() ){ + d_quantEngine->getConflictFind()->newEqClass( t ); + } } void TheoryEngine::eqNotifyPreMerge(TNode t1, TNode t2){ //TODO: add notification to efficient E-matching - if (d_logicInfo.isQuantified()) { + if( d_logicInfo.isQuantified() ){ d_quantEngine->getEfficientEMatcher()->merge( t1, t2 ); + if( options::quantConflictFind() ){ + d_quantEngine->getConflictFind()->merge( t1, t2 ); + } } } @@ -89,7 +95,11 @@ void TheoryEngine::eqNotifyPostMerge(TNode t1, TNode t2){ } void TheoryEngine::eqNotifyDisequal(TNode t1, TNode t2, TNode reason){ - + if( d_logicInfo.isQuantified() ){ + if( options::quantConflictFind() ){ + d_quantEngine->getConflictFind()->assertDisequal( t1, t2 ); + } + } } @@ -146,6 +156,7 @@ TheoryEngine::TheoryEngine(context::Context* context, StatisticsRegistry::registerStat(&d_combineTheoriesTime); d_true = NodeManager::currentNM()->mkConst<bool>(true); d_false = NodeManager::currentNM()->mkConst<bool>(false); + PROOF (ProofManager::currentPM()->initTheoryProof(); ); d_iteUtilities = new ITEUtilities(d_iteRemover.getContainsVisitor()); diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index 92469aa31..8f292e008 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -39,6 +39,7 @@ #include "util/statistics_registry.h" #include "util/cvc4_assert.h" #include "util/sort_inference.h" +#include "theory/quantifiers/quant_conflict_find.h" #include "theory/uf/equality_engine.h" #include "theory/bv/bv_to_bool.h" #include "theory/atom_requests.h" @@ -778,10 +779,10 @@ private: UnconstrainedSimplifier* d_unconstrainedSimp; /** For preprocessing pass lifting bit-vectors of size 1 to booleans */ - theory::bv::BvToBoolPreprocessor d_bvToBoolPreprocessor; + theory::bv::BvToBoolPreprocessor d_bvToBoolPreprocessor; public: - void ppBvToBool(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); + void ppBvToBool(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); Node ppSimpITE(TNode assertion); /** Returns false if an assertion simplified to false. */ bool donePPSimpITE(std::vector<Node>& assertions); @@ -790,6 +791,8 @@ public: SharedTermsDatabase* getSharedTermsDatabase() { return &d_sharedTerms; } + theory::eq::EqualityEngine* getMasterEqualityEngine() { return d_masterEqualityEngine; } + SortInference* getSortInference() { return &d_sortInfer; } private: std::map< std::string, std::vector< theory::Theory* > > d_attr_handle; diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index cb44b42df..df1d2ebde 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -71,8 +71,8 @@ void EqualityEngine::init() { addTermInternal(d_false); d_trueId = getNodeId(d_true); - d_falseId = getNodeId(d_false); -} + d_falseId = getNodeId(d_false); +} EqualityEngine::~EqualityEngine() throw(AssertionException) { free(d_triggerDatabase); @@ -287,7 +287,7 @@ void EqualityEngine::addTermInternal(TNode t, bool isOperator) { d_isConstant[result] = t.isConst(); // If interpreted, set the number of non-interpreted children if (isInterpreted) { - // How many children are not constants yet + // How many children are not constants yet d_subtermsToEvaluate[result] = t.getNumChildren(); for (unsigned i = 0; i < t.getNumChildren(); ++ i) { if (isConstant(getNodeId(t[i]))) { @@ -316,11 +316,11 @@ void EqualityEngine::addTermInternal(TNode t, bool isOperator) { for (TheoryId currentTheory = THEORY_FIRST; currentTheory != THEORY_LAST; ++ currentTheory) { d_newSetTags = Theory::setInsert(currentTheory, d_newSetTags); d_newSetTriggers[currentTheory] = tId; - } + } // Add it to the list for backtracking d_triggerTermSetUpdates.push_back(TriggerSetUpdate(tId, null_set_id)); d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1; - // Mark the the new set as a trigger + // Mark the the new set as a trigger d_nodeIndividualTrigger[tId] = newTriggerTermSet(); } @@ -333,7 +333,7 @@ void EqualityEngine::addTermInternal(TNode t, bool isOperator) { propagate(); Assert(hasTerm(t)); - + Debug("equality") << d_name << "::eq::addTermInternal(" << t << ") => " << result << std::endl; } @@ -419,12 +419,12 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { Debug("equality::trigger") << d_name << "::eq::addEquality(" << eq << "," << (polarity ? "true" : "false") << ")" << std::endl; assertEqualityInternal(eq, d_false, reason); - propagate(); - + propagate(); + if (d_done) { return; } - + // If both have constant representatives, we don't notify anyone EqualityNodeId a = getNodeId(eq[0]); EqualityNodeId b = getNodeId(eq[1]); @@ -432,8 +432,8 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { EqualityNodeId bClassId = getEqualityNode(b).getFind(); if (d_isConstant[aClassId] && d_isConstant[bClassId]) { return; - } - + } + // If we are adding a disequality, notify of the shared term representatives EqualityNodeId eqId = getNodeId(eq); TriggerTermSetRef aTriggerRef = d_nodeIndividualTrigger[aClassId]; @@ -443,16 +443,16 @@ void EqualityEngine::assertEquality(TNode eq, bool polarity, TNode reason) { // The sets of trigger terms TriggerTermSet& aTriggerTerms = getTriggerTermSet(aTriggerRef); TriggerTermSet& bTriggerTerms = getTriggerTermSet(bTriggerRef); - // Go through and notify the shared dis-equalities - Theory::Set aTags = aTriggerTerms.tags; - Theory::Set bTags = bTriggerTerms.tags; + // Go through and notify the shared dis-equalities + Theory::Set aTags = aTriggerTerms.tags; + Theory::Set bTags = bTriggerTerms.tags; TheoryId aTag = Theory::setPop(aTags); TheoryId bTag = Theory::setPop(bTags); int a_i = 0, b_i = 0; while (aTag != THEORY_LAST && bTag != THEORY_LAST) { if (aTag < bTag) { aTag = Theory::setPop(aTags); - ++ a_i; + ++ a_i; } else if (aTag > bTag) { bTag = Theory::setPop(bTags); ++ b_i; @@ -499,7 +499,7 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << ")" << std::endl; Assert(triggersFired.empty()); - + ++ d_stats.mergesCount; EqualityNodeId class1Id = class1.getFind(); @@ -539,8 +539,8 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect TaggedEqualitiesSet class1disequalitiesToNotify; // Individual tags - Theory::Set class1OnlyTags = Theory::setDifference(class1Tags, class2Tags); - Theory::Set class2OnlyTags = Theory::setDifference(class2Tags, class1Tags); + Theory::Set class1OnlyTags = Theory::setDifference(class1Tags, class2Tags); + Theory::Set class2OnlyTags = Theory::setDifference(class2Tags, class1Tags); // Only get disequalities if they are not both constant if (!class1isConstant || !class2isConstant) { @@ -590,9 +590,9 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): updating lookups of " << class2Id << std::endl; do { // Get the current node - EqualityNode& currentNode = getEqualityNode(currentId); + EqualityNode& currentNode = getEqualityNode(currentId); Debug("equality") << d_name << "::eq::merge(" << class1.getFind() << "," << class2.getFind() << "): updating lookups of node " << currentId << std::endl; - + // Go through the uselist and check for congruences UseListNodeId currentUseId = currentNode.getUseList(); while (currentUseId != null_uselist_id) { @@ -604,7 +604,7 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect const FunctionApplication& fun = d_applications[useNode.getApplicationId()].normalized; // If it's interpreted and we can interpret if (fun.isInterpreted() && class1isConstant && !d_isInternal[currentId]) { - // Get the actual term id + // Get the actual term id TNode term = d_nodes[funId]; subtermEvaluates(getNodeId(term)); } @@ -622,16 +622,16 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect // There is no representative, so we can add one, we remove this when backtracking storeApplicationLookup(funNormalized, funId); } - + // Go to the next one in the use list currentUseId = useNode.getNext(); } - + // Move to the next node currentId = currentNode.getNext(); } while (currentId != class2Id); } - + // Now merge the lists class1.merge<true>(class2); @@ -660,24 +660,24 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect // Get the triggers TriggerTermSet& class1triggers = getTriggerTermSet(class1triggerRef); TriggerTermSet& class2triggers = getTriggerTermSet(class2triggerRef); - + // Initialize the merged set d_newSetTags = Theory::setUnion(class1triggers.tags, class2triggers.tags); d_newSetTriggersSize = 0; - + int i1 = 0; int i2 = 0; Theory::Set tags1 = class1triggers.tags; Theory::Set tags2 = class2triggers.tags; TheoryId tag1 = Theory::setPop(tags1); TheoryId tag2 = Theory::setPop(tags2); - + // Comparing the THEORY_LAST is OK because all other theories are // smaller, and will therefore be preferred - while (tag1 != THEORY_LAST || tag2 != THEORY_LAST) + while (tag1 != THEORY_LAST || tag2 != THEORY_LAST) { if (tag1 < tag2) { - // copy tag1 + // copy tag1 d_newSetTriggers[d_newSetTriggersSize++] = class1triggers.triggers[i1++]; tag1 = Theory::setPop(tags1); } else if (tag1 > tag2) { @@ -685,7 +685,7 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect d_newSetTriggers[d_newSetTriggersSize++] = class2triggers.triggers[i2++]; tag2 = Theory::setPop(tags2); } else { - // copy tag1 + // copy tag1 EqualityNodeId tag1id = d_newSetTriggers[d_newSetTriggersSize++] = class1triggers.triggers[i1++]; // since they are both tagged notify of merge if (d_performNotify) { @@ -698,17 +698,17 @@ bool EqualityEngine::merge(EqualityNode& class1, EqualityNode& class2, std::vect tag1 = Theory::setPop(tags1); tag2 = Theory::setPop(tags2); } - } - + } + // Add the new trigger set, if different from previous one if (class1triggers.tags != class2triggers.tags) { // Add it to the list for backtracking d_triggerTermSetUpdates.push_back(TriggerSetUpdate(class1Id, class1triggerRef)); d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1; - // Mark the the new set as a trigger + // Mark the the new set as a trigger d_nodeIndividualTrigger[class1Id] = newTriggerTermSet(); - } - } + } + } } // Everything fine @@ -792,14 +792,14 @@ void EqualityEngine::backtrack() { } d_triggerTermSetUpdates.resize(d_triggerTermSetUpdatesSize); } - + if (d_equalityTriggers.size() > d_equalityTriggersCount) { // Unlink the triggers from the lists for (int i = d_equalityTriggers.size() - 1, i_end = d_equalityTriggersCount; i >= i_end; -- i) { const Trigger& trigger = d_equalityTriggers[i]; d_nodeTriggers[trigger.classId] = trigger.nextTrigger; } - // Get rid of the triggers + // Get rid of the triggers d_equalityTriggers.resize(d_equalityTriggersCount); d_equalityTriggersOriginal.resize(d_equalityTriggersCount); } @@ -859,7 +859,7 @@ void EqualityEngine::backtrack() { d_deducedDisequalityReasons.resize(d_deducedDisequalityReasonsSize); d_deducedDisequalities.resize(d_deducedDisequalitiesSize); } - + } void EqualityEngine::addGraphEdge(EqualityNodeId t1, EqualityNodeId t2, MergeReasonType type, TNode reason) { @@ -892,7 +892,7 @@ std::string EqualityEngine::edgesToString(EqualityEdgeId edgeId) const { return out.str(); } -void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vector<TNode>& equalities) const { +void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vector<TNode>& equalities, EqProof * eqp) const { Debug("equality") << d_name << "::eq::explainEquality(" << t1 << ", " << t2 << ", " << (polarity ? "true" : "false") << ")" << std::endl; // The terms must be there already @@ -904,7 +904,7 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vec if (polarity) { // Get the explanation - getExplanation(t1Id, t2Id, equalities); + getExplanation(t1Id, t2Id, equalities, eqp); } else { // Get the reason for this disequality EqualityPair pair(t1Id, t2Id); @@ -912,20 +912,20 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, std::vec DisequalityReasonRef reasonRef = d_disequalityReasonsMap.find(pair)->second; for (unsigned i = reasonRef.mergesStart; i < reasonRef.mergesEnd; ++ i) { EqualityPair toExplain = d_deducedDisequalityReasons[i]; - getExplanation(toExplain.first, toExplain.second, equalities); + getExplanation(toExplain.first, toExplain.second, equalities, eqp); } } } -void EqualityEngine::explainPredicate(TNode p, bool polarity, std::vector<TNode>& assertions) const { +void EqualityEngine::explainPredicate(TNode p, bool polarity, std::vector<TNode>& assertions, EqProof * eqp) const { Debug("equality") << d_name << "::eq::explainPredicate(" << p << ")" << std::endl; // Must have the term Assert(hasTerm(p)); // Get the explanation - getExplanation(getNodeId(p), polarity ? d_trueId : d_falseId, assertions); + getExplanation(getNodeId(p), polarity ? d_trueId : d_falseId, assertions, eqp); } -void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, std::vector<TNode>& equalities) const { +void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, std::vector<TNode>& equalities, EqProof * eqp) const { Debug("equality") << d_name << "::eq::getExplanation(" << d_nodes[t1Id] << "," << d_nodes[t2Id] << ")" << std::endl; @@ -933,17 +933,23 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st #ifdef CVC4_ASSERTIONS bool canExplain = getEqualityNode(t1Id).getFind() == getEqualityNode(t2Id).getFind() || (d_done && isConstant(t1Id) && isConstant(t2Id)); - + if (!canExplain) { Warning() << "Can't explain equality:" << std::endl; Warning() << d_nodes[t1Id] << " with find " << d_nodes[getEqualityNode(t1Id).getFind()] << std::endl; - Warning() << d_nodes[t2Id] << " with find " << d_nodes[getEqualityNode(t2Id).getFind()] << std::endl; + Warning() << d_nodes[t2Id] << " with find " << d_nodes[getEqualityNode(t2Id).getFind()] << std::endl; } Assert(canExplain); #endif // If the nodes are the same, we're done - if (t1Id == t2Id) return; + if (t1Id == t2Id){ + if( eqp ) { + eqp->d_node = d_nodes[t1Id]; + } + return; + } + if (Debug.isOn("equality::internal")) { debugPrintGraph(); @@ -986,6 +992,8 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st Debug("equality") << d_name << "::eq::getExplanation(): path found: " << std::endl; + std::vector< EqProof * > eqp_trans; + // Reconstruct the path do { // The current node @@ -995,6 +1003,12 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st Debug("equality") << d_name << "::eq::getExplanation(): currentEdge = " << currentEdge << ", currentNode = " << currentNode << std::endl; + EqProof * eqpc = NULL; + //make child proof if a proof is being constructed + if( eqp ){ + eqpc = new EqProof; + eqpc->d_id = reasonType; + } // Add the actual equality to the vector switch (reasonType) { case MERGED_THROUGH_CONGRUENCE: { @@ -1003,32 +1017,45 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st const FunctionApplication& f1 = d_applications[currentNode].original; const FunctionApplication& f2 = d_applications[edgeNode].original; Debug("equality") << push; - getExplanation(f1.a, f2.a, equalities); - getExplanation(f1.b, f2.b, equalities); + EqProof * eqpc1 = eqpc ? new EqProof : NULL; + getExplanation(f1.a, f2.a, equalities, eqpc1); + EqProof * eqpc2 = eqpc ? new EqProof : NULL; + getExplanation(f1.b, f2.b, equalities, eqpc2); + if( eqpc ){ + eqpc->d_children.push_back( eqpc1 ); + eqpc->d_children.push_back( eqpc2 ); + } Debug("equality") << pop; break; - } + } case MERGED_THROUGH_EQUALITY: // Construct the equality Debug("equality") << d_name << "::eq::getExplanation(): adding: " << d_equalityEdges[currentEdge].getReason() << std::endl; + if( eqpc ){ + eqpc->d_node = d_equalityEdges[currentEdge].getReason(); + } equalities.push_back(d_equalityEdges[currentEdge].getReason()); break; case MERGED_THROUGH_REFLEXIVITY: { - // x1 == x1 + // x1 == x1 Debug("equality") << d_name << "::eq::getExplanation(): due to reflexivity, going deeper" << std::endl; EqualityNodeId eqId = currentNode == d_trueId ? edgeNode : currentNode; const FunctionApplication& eq = d_applications[eqId].original; Assert(eq.isEquality(), "Must be an equality"); - + // Explain why a = b constant Debug("equality") << push; - getExplanation(eq.a, eq.b, equalities); + EqProof * eqpc1 = eqpc ? new EqProof : NULL; + getExplanation(eq.a, eq.b, equalities, eqpc1); + if( eqpc ){ + eqpc->d_children.push_back( eqpc1 ); + } Debug("equality") << pop; - - break; + + break; } case MERGED_THROUGH_CONSTANTS: { - // f(c1, ..., cn) = c semantically, we can just ignore it + // f(c1, ..., cn) = c semantically, we can just ignore it Debug("equality") << d_name << "::eq::getExplanation(): due to constants, explain the constants" << std::endl; Debug("equality") << push; @@ -1042,7 +1069,11 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st for (unsigned i = 0; i < interpreted.getNumChildren(); ++ i) { EqualityNodeId childId = getNodeId(interpreted[i]); Assert(isConstant(childId)); - getExplanation(childId, getEqualityNode(childId).getFind(), equalities); + EqProof * eqpcc = eqpc ? new EqProof : NULL; + getExplanation(childId, getEqualityNode(childId).getFind(), equalities, eqpcc); + if( eqpc ) { + eqpc->d_children.push_back( eqpcc ); + } } Debug("equality") << pop; @@ -1051,14 +1082,21 @@ void EqualityEngine::getExplanation(EqualityNodeId t1Id, EqualityNodeId t2Id, st } default: Unreachable(); - } - + } + // Go to the previous currentEdge = bfsQueue[currentIndex].edgeId; currentIndex = bfsQueue[currentIndex].previousIndex; - + + eqp_trans.push_back( eqpc ); + } while (currentEdge != null_id); + if( eqp ){ + eqp->d_id = MERGED_THROUGH_TRANS; + eqp->d_children.insert( eqp->d_children.end(), eqp_trans.begin(), eqp_trans.end() ); + } + // Done return; } @@ -1220,7 +1258,7 @@ void EqualityEngine::processEvaluationQueue() { // Get the node EqualityNodeId id = d_evaluationQueue.front(); d_evaluationQueue.pop(); - + // Replace the children with their representatives (must be constants) Node nodeEvaluated = evaluateTerm(d_nodes[id]); Debug("equality::evaluation") << d_name << "::eq::processEvaluationQueue(): " << d_nodes[id] << " evaluates to " << nodeEvaluated << std::endl; @@ -1240,11 +1278,11 @@ void EqualityEngine::propagate() { if (d_inPropagate) { // We're already in propagate, go back return; - } - + } + // Make sure we don't get in again ScopedBool inPropagate(d_inPropagate, true); - + Debug("equality") << d_name << "::eq::propagate()" << std::endl; while (!d_propagationQueue.empty() || !d_evaluationQueue.empty()) { @@ -1255,13 +1293,13 @@ void EqualityEngine::propagate() { while (!d_evaluationQueue.empty()) d_evaluationQueue.pop(); continue; } - + // Process any evaluation requests if (!d_evaluationQueue.empty()) { processEvaluationQueue(); continue; } - + // The current merge candidate const MergeCandidate current = d_propagationQueue.front(); d_propagationQueue.pop_front(); @@ -1288,7 +1326,7 @@ void EqualityEngine::propagate() { // Add the actual equality to the equality graph addGraphEdge(current.t1Id, current.t2Id, current.type, current.reason); - // If constants are being merged we're done + // If constants are being merged we're done if (d_isConstant[t1classId] && d_isConstant[t2classId]) { // When merging constants we are inconsistent, hence done d_done = true; @@ -1462,7 +1500,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const return true; } } - + // Check the other equality itself if it exists eq = t2.eqNode(t1); if (hasTerm(eq)) { @@ -1474,7 +1512,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const return true; } } - + // Create the equality FunctionApplication eqNormalized(APP_EQUALITY, t1ClassId, t2ClassId); ApplicationIdsMap::const_iterator find = d_applicationLookup.find(eqNormalized); @@ -1492,7 +1530,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const return true; } } - + // Check the symmetric disequality std::swap(eqNormalized.a, eqNormalized.b); find = d_applicationLookup.find(eqNormalized); @@ -1510,7 +1548,7 @@ bool EqualityEngine::areDisequal(TNode t1, TNode t2, bool ensureProof) const return true; } } - + // Couldn't deduce dis-equalityReturn whether the terms are disequal return false; } @@ -1568,19 +1606,19 @@ void EqualityEngine::addTriggerTerm(TNode t, TheoryId tag) // Initialize the new set for copy/insert d_newSetTags = Theory::setInsert(tag, triggerSet.tags); d_newSetTriggersSize = 0; - // Copy into to new one, and insert the new tag/id + // Copy into to new one, and insert the new tag/id unsigned i = 0; Theory::Set tags = d_newSetTags; - TheoryId current; + TheoryId current; while ((current = Theory::setPop(tags)) != THEORY_LAST) { // Remove from the tags tags = Theory::setRemove(current, tags); // Insert the id into the triggers - d_newSetTriggers[d_newSetTriggersSize++] = + d_newSetTriggers[d_newSetTriggersSize++] = current == tag ? eqNodeId : triggerSet.triggers[i++]; } } else { - // Setup a singleton + // Setup a singleton d_newSetTags = Theory::setInsert(tag); d_newSetTriggers[0] = eqNodeId; d_newSetTriggersSize = 1; @@ -1589,7 +1627,7 @@ void EqualityEngine::addTriggerTerm(TNode t, TheoryId tag) // Add it to the list for backtracking d_triggerTermSetUpdates.push_back(TriggerSetUpdate(classId, triggerSetRef)); d_triggerTermSetUpdatesSize = d_triggerTermSetUpdatesSize + 1; - // Mark the the new set as a trigger + // Mark the the new set as a trigger d_nodeIndividualTrigger[classId] = triggerSetRef = newTriggerTermSet(); // Propagate trigger term disequalities we remembered @@ -1843,7 +1881,7 @@ void EqualityEngine::getDisequalities(bool allowConstants, EqualityNodeId classI } bool EqualityEngine::propagateTriggerTermDisequalities(Theory::Set tags, TriggerTermSetRef triggerSetRef, const TaggedEqualitiesSet& disequalitiesToNotify) { - + // No tags, no food if (!tags) { return !d_done; @@ -1852,14 +1890,14 @@ bool EqualityEngine::propagateTriggerTermDisequalities(Theory::Set tags, Trigger Assert(triggerSetRef != null_set_id); // This is the class trigger set - const TriggerTermSet& triggerSet = getTriggerTermSet(triggerSetRef); + const TriggerTermSet& triggerSet = getTriggerTermSet(triggerSetRef); // Go through the disequalities and notify TaggedEqualitiesSet::const_iterator it = disequalitiesToNotify.begin(); TaggedEqualitiesSet::const_iterator it_end = disequalitiesToNotify.end(); for (; !d_done && it != it_end; ++ it) { // The information about the equality that is asserted to false const TaggedEquality& disequalityInfo = *it; - const TriggerTermSet& disequalityTriggerSet = getTriggerTermSet(disequalityInfo.triggerSetRef); + const TriggerTermSet& disequalityTriggerSet = getTriggerTermSet(disequalityInfo.triggerSetRef); Theory::Set commonTags = Theory::setIntersection(disequalityTriggerSet.tags, tags); Assert(commonTags); // This is the actual function @@ -1897,7 +1935,7 @@ bool EqualityEngine::propagateTriggerTermDisequalities(Theory::Set tags, Trigger } } } - + return !d_done; } @@ -2005,6 +2043,25 @@ bool EqClassIterator::isFinished() const { } +void EqProof::debug_print( const char * c, unsigned tb ){ + for( unsigned i=0; i<tb; i++ ) { Debug( c ) << " "; } + Debug( c ) << d_id << "("; + if( !d_children.empty() || !d_node.isNull() ){ + if( !d_node.isNull() ){ + Debug( c ) << std::endl; + for( unsigned i=0; i<tb+1; i++ ) { Debug( c ) << " "; } + Debug( c ) << d_node; + } + for( unsigned i=0; i<d_children.size(); i++ ){ + if( i>0 || !d_node.isNull() ) Debug( c ) << ","; + std::cout << std::endl; + d_children[i]->debug_print( c, tb+1 ); + } + } + Debug( c ) << ")"; +} + + } // Namespace uf } // Namespace theory } // Namespace CVC4 diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index ab106bc8d..f8e361081 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -39,6 +39,8 @@ namespace CVC4 { namespace theory { namespace eq { + +class EqProof; class EqClassesIterator; class EqClassIterator; @@ -421,35 +423,35 @@ private: /** * Map from ids of proper terms, to the number of non-constant direct subterms. If we update an interpreted * application to a constant, we can decrease this value. If we hit 0, we can evaluate the term. - * + * */ std::vector<unsigned> d_subtermsToEvaluate; - - /** + + /** * For nodes that we need to postpone evaluation. */ std::queue<EqualityNodeId> d_evaluationQueue; - + /** * Evaluate all terms in the evaluation queue. */ void processEvaluationQueue(); - + /** Vector of nodes that evaluate. */ std::vector<EqualityNodeId> d_subtermEvaluates; /** Size of the nodes that evaluate vector. */ context::CDO<unsigned> d_subtermEvaluatesSize; - + /** Set the node evaluate flag */ void subtermEvaluates(EqualityNodeId id); /** - * Returns the evaluation of the term when all (direct) children are replaced with + * Returns the evaluation of the term when all (direct) children are replaced with * the constant representatives. */ Node evaluateTerm(TNode node); - + /** * Returns true if it's a constant */ @@ -487,7 +489,7 @@ private: /** Enqueue to the propagation queue */ void enqueue(const MergeCandidate& candidate, bool back = true); - + /** Do the propagation */ void propagate(); @@ -499,7 +501,7 @@ private: * imply t1 = t2. Returns TNodes as the assertion equalities should be hashed somewhere * else. */ - void getExplanation(EqualityEdgeId t1Id, EqualityNodeId t2Id, std::vector<TNode>& equalities) const; + void getExplanation(EqualityEdgeId t1Id, EqualityNodeId t2Id, std::vector<TNode>& equalities, EqProof * eqp) const; /** * Print the equality graph. @@ -752,7 +754,7 @@ public: void assertPredicate(TNode p, bool polarity, TNode reason); /** - * Adds predicate p and q and makes them equal. + * Adds predicate p and q and makes them equal. */ void mergePredicates(TNode p, TNode q, TNode reason); @@ -782,14 +784,14 @@ public: * Returns the reasons (added when asserting) that imply it * in the assertions vector. */ - void explainEquality(TNode t1, TNode t2, bool polarity, std::vector<TNode>& assertions) const; + void explainEquality(TNode t1, TNode t2, bool polarity, std::vector<TNode>& assertions, EqProof * eqp = NULL) const; /** * Get an explanation of the predicate being true or false. * Returns the reasons (added when asserting) that imply imply it * in the assertions vector. */ - void explainPredicate(TNode p, bool polarity, std::vector<TNode>& assertions) const; + void explainPredicate(TNode p, bool polarity, std::vector<TNode>& assertions, EqProof * eqp = NULL) const; /** * Add term to the set of trigger terms with a corresponding tag. The notify class will get @@ -890,6 +892,16 @@ public: bool isFinished() const; };/* class EqClassIterator */ +class EqProof +{ +public: + EqProof() : d_id(MERGED_THROUGH_REFLEXIVITY){} + MergeReasonType d_id; + Node d_node; + std::vector< EqProof * > d_children; + void debug_print( const char * c, unsigned tb = 0 ); +}; + } // Namespace eq } // Namespace theory } // Namespace CVC4 diff --git a/src/theory/uf/equality_engine_types.h b/src/theory/uf/equality_engine_types.h index a36291974..435a1ece5 100644 --- a/src/theory/uf/equality_engine_types.h +++ b/src/theory/uf/equality_engine_types.h @@ -54,7 +54,7 @@ static const EqualityEdgeId null_edge = (EqualityEdgeId)(-1); /** * A reason for a merge. Either an equality x = y, a merge of two - * function applications f(x1, x2), f(y1, y2) due to congruence, + * function applications f(x1, x2), f(y1, y2) due to congruence, * or a merge of an equality to false due to both sides being * (different) constants. */ @@ -67,6 +67,9 @@ enum MergeReasonType { MERGED_THROUGH_REFLEXIVITY, /** Equality was merged to false, due to both sides of equality being a constant */ MERGED_THROUGH_CONSTANTS, + + /** (for proofs only) Equality was merged due to transitivity */ + MERGED_THROUGH_TRANS, }; inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { @@ -83,6 +86,10 @@ inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { case MERGED_THROUGH_CONSTANTS: out << "constants disequal"; break; + // (for proofs only) + case MERGED_THROUGH_TRANS: + out << "transitivity"; + break; default: Unreachable(); } @@ -98,7 +105,7 @@ struct MergeCandidate { MergeReasonType type; TNode reason; MergeCandidate(EqualityNodeId x, EqualityNodeId y, MergeReasonType type, TNode reason) - : t1Id(x), t2Id(y), type(type), reason(reason) + : t1Id(x), t2Id(y), type(type), reason(reason) {} }; @@ -112,9 +119,9 @@ struct DisequalityReasonRef { : mergesStart(mergesStart), mergesEnd(mergesEnd) {} }; -/** +/** * We maintain uselist where a node appears in, and this is the node - * of such a list. + * of such a list. */ class UseListNode { @@ -150,12 +157,12 @@ public: }; /** - * Main class for representing nodes in the equivalence class. The + * Main class for representing nodes in the equivalence class. The * nodes are a circular list, with the representative carrying the * size. Each individual node carries with itself the uselist of - * function applications it appears in and the list of asserted + * function applications it appears in and the list of asserted * disequalities it belongs to. In order to get these lists one must - * traverse the entire class and pick up all the individual lists. + * traverse the entire class and pick up all the individual lists. */ class EqualityNode { @@ -180,7 +187,7 @@ public: */ EqualityNode(EqualityNodeId nodeId = null_id) : d_size(1) - , d_findId(nodeId) + , d_findId(nodeId) , d_nextId(nodeId) , d_useList(null_uselist_id) {} @@ -232,7 +239,7 @@ public: /** * Note that this node is used in a function application funId, or - * a negatively asserted equality (dis-equality) with funId. + * a negatively asserted equality (dis-equality) with funId. */ template<typename memory_class> void usedIn(EqualityNodeId funId, memory_class& memory) { @@ -275,8 +282,8 @@ enum FunctionApplicationType { /** * Represents the function APPLY a b. If isEquality is true then it - * represents the predicate (a = b). Note that since one can not - * construct the equality over function terms, the equality and hash + * represents the predicate (a = b). Note that since one can not + * construct the equality over function terms, the equality and hash * function below are still well defined. */ struct FunctionApplication { diff --git a/src/theory/uf/options b/src/theory/uf/options index cfa6e6c04..26f87da79 100644 --- a/src/theory/uf/options +++ b/src/theory/uf/options @@ -15,20 +15,14 @@ option ufssRegions /--disable-uf-ss-regions bool :default true disable region-based method for discovering cliques and splits in uf strong solver option ufssEagerSplits --uf-ss-eager-split bool :default false add splits eagerly for uf strong solver -option ufssColoringSat --uf-ss-coloring-sat bool :default false - use coloring-based SAT heuristic for uf strong solver option ufssTotality --uf-ss-totality bool :default false always use totality axioms for enforcing cardinality constraints option ufssTotalityLimited --uf-ss-totality-limited=N int :default -1 apply totality axioms, but only up to cardinality N (-1 == do not apply totality axioms, default) -option ufssTotalityLazy --uf-ss-totality-lazy bool :default false - apply totality axioms lazily option ufssTotalitySymBreak --uf-ss-totality-sym-break bool :default false apply symmetry breaking for totality axioms option ufssAbortCardinality --uf-ss-abort-card=N int :default -1 tells the uf strong solver a cardinality to abort at (-1 == no limit, default) -option ufssSmartSplits --uf-ss-smart-split bool :default false - use smart splitting heuristic for uf strong solver option ufssExplainedCliques --uf-ss-explained-cliques bool :default false use explained clique lemmas for uf strong solver option ufssSimpleCliques --uf-ss-simple-cliques bool :default true diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index 1045c5a24..fd46ed7f4 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -178,10 +178,16 @@ void TheoryUF::explain(TNode literal, std::vector<TNode>& assumptions) { // Do the work bool polarity = literal.getKind() != kind::NOT; TNode atom = polarity ? literal : literal[0]; + eq::EqProof * eqp = d_proofEnabled ? new eq::EqProof : NULL; if (atom.getKind() == kind::EQUAL || atom.getKind() == kind::IFF) { - d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions); + d_equalityEngine.explainEquality(atom[0], atom[1], polarity, assumptions, eqp); } else { - d_equalityEngine.explainPredicate(atom, polarity, assumptions); + d_equalityEngine.explainPredicate(atom, polarity, assumptions, eqp); + } + //for now, just print debug + //TODO : send the proof outwards : d_out->conflict( lem, eqp ); + if( eqp ){ + eqp->debug_print("uf-pf"); } } @@ -462,6 +468,7 @@ void TheoryUF::computeCareGraph() { }/* TheoryUF::computeCareGraph() */ void TheoryUF::conflict(TNode a, TNode b) { + //TODO: create EqProof at this level if d_proofEnabled = true if (a.getKind() == kind::CONST_BOOLEAN) { d_conflictNode = explain(a.iffNode(b)); } else { diff --git a/src/theory/uf/theory_uf_model.cpp b/src/theory/uf/theory_uf_model.cpp index 409b41e3f..3b59c1c58 100644 --- a/src/theory/uf/theory_uf_model.cpp +++ b/src/theory/uf/theory_uf_model.cpp @@ -346,23 +346,21 @@ void UfModelTreeGenerator::setValue( TheoryModel* m, Node n, Node v, bool ground d_set_values[ isReq ? 1 : 0 ][ ground ? 1 : 0 ][n] = v; if( optUsePartialDefaults() ){ if( !ground ){ - if (!options::fmfFullModelCheck()) { - int defSize = (int)d_defaults.size(); - for( int i=0; i<defSize; i++ ){ - //for soundness, to allow variable order-independent function interpretations, - // we must ensure that the intersection of all default terms - // is also defined. - //for example, if we have that f( e, a ) = ..., and f( b, e ) = ..., - // then we must define f( b, a ). - bool isGround; - Node ni = getIntersection( m, n, d_defaults[i], isGround ); - if( !ni.isNull() ){ - //if the intersection exists, and is not already defined - if( d_set_values[0][ isGround ? 1 : 0 ].find( ni )==d_set_values[0][ isGround ? 1 : 0 ].end() && - d_set_values[1][ isGround ? 1 : 0 ].find( ni )==d_set_values[1][ isGround ? 1 : 0 ].end() ){ - //use the current value - setValue( m, ni, v, isGround, false ); - } + int defSize = (int)d_defaults.size(); + for( int i=0; i<defSize; i++ ){ + //for soundness, to allow variable order-independent function interpretations, + // we must ensure that the intersection of all default terms + // is also defined. + //for example, if we have that f( e, a ) = ..., and f( b, e ) = ..., + // then we must define f( b, a ). + bool isGround; + Node ni = getIntersection( m, n, d_defaults[i], isGround ); + if( !ni.isNull() ){ + //if the intersection exists, and is not already defined + if( d_set_values[0][ isGround ? 1 : 0 ].find( ni )==d_set_values[0][ isGround ? 1 : 0 ].end() && + d_set_values[1][ isGround ? 1 : 0 ].find( ni )==d_set_values[1][ isGround ? 1 : 0 ].end() ){ + //use the current value + setValue( m, ni, v, isGround, false ); } } } diff --git a/src/theory/uf/theory_uf_strong_solver.cpp b/src/theory/uf/theory_uf_strong_solver.cpp index 052b2f568..54a3075a1 100644 --- a/src/theory/uf/theory_uf_strong_solver.cpp +++ b/src/theory/uf/theory_uf_strong_solver.cpp @@ -428,12 +428,10 @@ void StrongSolverTheoryUF::SortModel::initialize( OutputChannel* out ){ void StrongSolverTheoryUF::SortModel::newEqClass( Node n ){ if( !d_conflict ){ if( d_regions_map.find( n )==d_regions_map.end() ){ - if( !options::ufssTotalityLazy() ){ - //must generate totality axioms for every cardinality we have allocated thus far - for( std::map< int, Node >::iterator it = d_cardinality_literal.begin(); it != d_cardinality_literal.end(); ++it ){ - if( applyTotality( it->first ) ){ - addTotalityAxiom( n, it->first, &d_thss->getOutputChannel() ); - } + //must generate totality axioms for every cardinality we have allocated thus far + for( std::map< int, Node >::iterator it = d_cardinality_literal.begin(); it != d_cardinality_literal.end(); ++it ){ + if( applyTotality( it->first ) ){ + addTotalityAxiom( n, it->first, &d_thss->getOutputChannel() ); } } if( options::ufssTotality() ){ @@ -449,9 +447,6 @@ void StrongSolverTheoryUF::SortModel::newEqClass( Node n ){ d_regions_index = 0; } d_regions_map[n] = d_regions_index; - if( options::ufssSmartSplits() ){ - setSplitScore( n, 0 ); - } Debug("uf-ss") << "StrongSolverTheoryUF: New Eq Class " << n << std::endl; Debug("uf-ss-debug") << d_regions_index << " " << (int)d_regions.size() << std::endl; if( d_regions_index<d_regions.size() ){ @@ -634,18 +629,7 @@ void StrongSolverTheoryUF::SortModel::check( Theory::Effort level, OutputChannel } } } - if( applyTotality( d_cardinality ) ){ - //add totality axioms for all nodes that have not yet been equated to cardinality terms - if( options::ufssTotalityLazy() ){ //this should always be true - if( level==Theory::EFFORT_FULL ){ - for( NodeIntMap::iterator it = d_regions_map.begin(); it != d_regions_map.end(); ++it ){ - if( !options::ufssTotality() || d_regions_map[ (*it).first ]!=-1 ){ - addTotalityAxiom( (*it).first, d_cardinality, &d_thss->getOutputChannel() ); - } - } - } - } - }else{ + if( !applyTotality( d_cardinality ) ){ //do splitting on demand bool addedLemma = false; if( level==Theory::EFFORT_FULL || options::ufssEagerSplits() ){ @@ -1073,7 +1057,7 @@ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){ //out->propagateAsDecision( lem[0] ); d_thss->d_statistics.d_max_model_size.maxAssign( d_aloc_cardinality ); - if( applyTotality( d_aloc_cardinality ) && !options::ufssTotalityLazy() ){ + if( applyTotality( d_aloc_cardinality ) ){ //must send totality axioms for each existing term for( NodeIntMap::iterator it = d_regions_map.begin(); it != d_regions_map.end(); ++it ){ addTotalityAxiom( (*it).first, d_aloc_cardinality, &d_thss->getOutputChannel() ); @@ -1085,27 +1069,11 @@ void StrongSolverTheoryUF::SortModel::allocateCardinality( OutputChannel* out ){ int StrongSolverTheoryUF::SortModel::addSplit( Region* r, OutputChannel* out ){ Node s; if( r->hasSplits() ){ - if( !options::ufssSmartSplits() ){ - //take the first split you find - for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){ - if( (*it).second ){ - s = (*it).first; - break; - } - } - }else{ - int maxScore = -1; - std::vector< Node > splits; - for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){ - if( (*it).second ){ - int score1 = d_split_score[ (*it).first[0] ]; - int score2 = d_split_score[ (*it).first[1] ]; - int score = score1<score2 ? score1 : score2; - if( score>maxScore ){ - maxScore = -1; - s = (*it).first; - } - } + //take the first split you find + for( NodeBoolMap::iterator it = r->d_splits.begin(); it != r->d_splits.end(); ++it ){ + if( (*it).second ){ + s = (*it).first; + break; } } Assert( s!=Node::null() ); @@ -1338,6 +1306,21 @@ void StrongSolverTheoryUF::SortModel::addTotalityAxiom( Node n, int cardinality, d_sym_break_terms[n.getType()][sort_id].push_back( n ); d_sym_break_index[n] = use_cardinality; Trace("uf-ss-totality") << "Allocate symmetry breaking term " << n << ", index = " << use_cardinality << std::endl; + if( d_sym_break_terms[n.getType()][sort_id].size()>1 ){ + //enforce canonicity + for( int i=2; i<use_cardinality; i++ ){ + //can only be assigned to domain constant d if someone has been assigned domain constant d-1 + Node eq = n.eqNode( getTotalityLemmaTerm( cardinality, i ) ); + std::vector< Node > eqs; + for( unsigned j=0; j<(d_sym_break_terms[n.getType()][sort_id].size()-1); j++ ){ + eqs.push_back( d_sym_break_terms[n.getType()][sort_id][j].eqNode( getTotalityLemmaTerm( cardinality, i-1 ) ) ); + } + Node ax = NodeManager::currentNM()->mkNode( OR, eqs ); + Node lem = NodeManager::currentNM()->mkNode( IMPLIES, eq, ax ); + Trace("uf-ss-lemma") << "*** Add (canonicity) totality axiom " << lem << std::endl; + d_thss->getOutputChannel().lemma( lem ); + } + } } } @@ -1459,11 +1442,6 @@ Node StrongSolverTheoryUF::SortModel::getCardinalityLiteral( int c ) { StrongSolverTheoryUF::StrongSolverTheoryUF(context::Context* c, context::UserContext* u, OutputChannel& out, TheoryUF* th) : d_out( &out ), d_th( th ), d_conflict( c, false ), d_rep_model(), d_aloc_com_card( u, 0 ), d_com_card_assertions( c ) { - if( options::ufssColoringSat() ){ - d_term_amb = new TermDisambiguator( th->getQuantifiersEngine(), c ); - }else{ - d_term_amb = NULL; - } if( options::ufssDiseqPropagation() ){ d_deq_prop = new DisequalityPropagator( th->getQuantifiersEngine(), this ); }else{ @@ -1499,6 +1477,7 @@ void StrongSolverTheoryUF::newEqClass( Node n ){ if( options::ufssSymBreak() ){ d_sym_break->newEqClass( n ); } + Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done New eq class." << std::endl; } } @@ -1508,6 +1487,7 @@ void StrongSolverTheoryUF::merge( Node a, Node b ){ if( c ){ Trace("uf-ss-solver") << "StrongSolverTheoryUF: Merge " << a << " " << b << " : " << a.getType() << std::endl; c->merge( a, b ); + Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done Merge." << std::endl; }else{ if( options::ufssDiseqPropagation() ){ d_deq_prop->merge(a, b); @@ -1523,6 +1503,7 @@ void StrongSolverTheoryUF::assertDisequal( Node a, Node b, Node reason ){ //Assert( d_th->d_equalityEngine.getRepresentative( a )==a ); //Assert( d_th->d_equalityEngine.getRepresentative( b )==b ); c->assertDisequal( a, b, reason ); + Trace("uf-ss-solver") << "StrongSolverTheoryUF: Done Assert disequal." << std::endl; }else{ if( options::ufssDiseqPropagation() ){ d_deq_prop->assertDisequal(a, b, reason); @@ -1610,13 +1591,6 @@ void StrongSolverTheoryUF::check( Theory::Effort level ){ if( level==Theory::EFFORT_FULL ){ debugPrint( "uf-ss-debug" ); } - if( !d_conflict && level==Theory::EFFORT_FULL && options::ufssColoringSat() ){ - int lemmas = d_term_amb->disambiguateTerms( d_out ); - d_statistics.d_disamb_term_lemmas += lemmas; - if( lemmas>=0 ){ - return; - } - } for( std::map< TypeNode, SortModel* >::iterator it = d_rep_model.begin(); it != d_rep_model.end(); ++it ){ it->second->check( level, d_out ); if( it->second->isConflict() ){ @@ -1628,11 +1602,6 @@ void StrongSolverTheoryUF::check( Theory::Effort level ){ if( !d_conflict && options::ufssSymBreak() ){ d_sym_break->check( level ); } - //disambiguate terms if necessary - //if( !d_conflict && level==Theory::EFFORT_FULL && options::ufssColoringSat() ){ - // Assert( d_term_amb!=NULL ); - // d_statistics.d_disamb_term_lemmas += d_term_amb->disambiguateTerms( d_out ); - //} Trace("uf-ss-solver") << "Done StrongSolverTheoryUF: check " << level << std::endl; } } @@ -1900,76 +1869,6 @@ StrongSolverTheoryUF::Statistics::~Statistics(){ } -int TermDisambiguator::disambiguateTerms( OutputChannel* out ){ - Debug("uf-ss-disamb") << "Disambiguate terms." << std::endl; - int lemmaAdded = 0; - //otherwise, determine ambiguous pairs of ground terms for relevant sorts - quantifiers::TermDb* db = d_qe->getTermDatabase(); - for( std::map< Node, std::vector< Node > >::iterator it = db->d_op_map.begin(); it != db->d_op_map.end(); ++it ){ - Debug("uf-ss-disamb") << "Check " << it->first << std::endl; - if( it->second.size()>1 ){ - if(involvesRelevantType( it->second[0] ) ){ - for( int i=0; i<(int)it->second.size(); i++ ){ - for( int j=(i+1); j<(int)it->second.size(); j++ ){ - Kind knd = it->second[i].getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; - Node eq = NodeManager::currentNM()->mkNode( knd, it->second[i], it->second[j] ); - eq = Rewriter::rewrite(eq); - //determine if they are ambiguous - if( d_term_amb.find( eq )==d_term_amb.end() ){ - Debug("uf-ss-disamb") << "Check disambiguate " << it->second[i] << " " << it->second[j] << std::endl; - d_term_amb[ eq ] = true; - //if they are equal - if( d_qe->getEqualityQuery()->areEqual( it->second[i], it->second[j] ) ){ - d_term_amb[ eq ] = false; - }else{ - //if an argument is disequal, then they are not ambiguous - for( int k=0; k<(int)it->second[i].getNumChildren(); k++ ){ - if( d_qe->getEqualityQuery()->areDisequal( it->second[i][k], it->second[j][k] ) ){ - d_term_amb[ eq ] = false; - break; - } - } - } - if( d_term_amb[ eq ] ){ - Debug("uf-ss-disamb") << "Disambiguate " << it->second[i] << " " << it->second[j] << std::endl; - //must add lemma - std::vector< Node > children; - children.push_back( eq ); - for( int k=0; k<(int)it->second[i].getNumChildren(); k++ ){ - Kind knd2 = it->second[i][k].getType()==NodeManager::currentNM()->booleanType() ? IFF : EQUAL; - Node eqc = NodeManager::currentNM()->mkNode( knd2, it->second[i][k], it->second[j][k] ); - children.push_back( eqc.notNode() ); - } - Assert( children.size()>1 ); - Node lem = NodeManager::currentNM()->mkNode( OR, children ); - Trace( "uf-ss-lemma" ) << "*** Disambiguate lemma : " << lem << std::endl; - //Notice() << "*** Disambiguate lemma : " << lem << std::endl; - out->lemma( lem ); - d_term_amb[ eq ] = false; - lemmaAdded++; - return lemmaAdded; - } - } - } - } - } - } - } - Debug("uf-ss-disamb") << "Done disambiguate terms. " << lemmaAdded << std::endl; - return lemmaAdded; -} - -bool TermDisambiguator::involvesRelevantType( Node n ){ - if( n.getKind()==APPLY_UF ){ - for( int i=0; i<(int)n.getNumChildren(); i++ ){ - if( n[i].getType().isSort() ){ - return true; - } - } - } - return false; -} - DisequalityPropagator::DisequalityPropagator(QuantifiersEngine* qe, StrongSolverTheoryUF* ufss) : d_qe(qe), d_ufss(ufss){ d_true = NodeManager::currentNM()->mkConst( true ); diff --git a/src/theory/uf/theory_uf_strong_solver.h b/src/theory/uf/theory_uf_strong_solver.h index 4f41ebf2e..51783c1e3 100644 --- a/src/theory/uf/theory_uf_strong_solver.h +++ b/src/theory/uf/theory_uf_strong_solver.h @@ -37,7 +37,6 @@ class SubsortSymmetryBreaker; namespace uf { class TheoryUF; -class TermDisambiguator; class DisequalityPropagator; class StrongSolverTheoryUF{ @@ -45,8 +44,6 @@ protected: typedef context::CDHashMap<Node, bool, NodeHashFunction> NodeBoolMap; typedef context::CDHashMap<Node, int, NodeHashFunction> NodeIntMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; - typedef context::CDChunkList<Node> NodeList; - typedef context::CDList<bool> BoolList; typedef context::CDHashMap<TypeNode, bool, TypeNodeHashFunction> TypeNodeBoolMap; public: /** information for incremental conflict/clique finding for a particular sort */ @@ -320,8 +317,6 @@ private: /** check */ void checkCombinedCardinality(); private: - /** term disambiguator */ - TermDisambiguator* d_term_amb; /** disequality propagator */ DisequalityPropagator* d_deq_prop; /** symmetry breaking techniques */ @@ -331,8 +326,6 @@ public: ~StrongSolverTheoryUF() {} /** get theory */ TheoryUF* getTheory() { return d_th; } - /** term disambiguator */ - TermDisambiguator* getTermDisambiguator() { return d_term_amb; } /** disequality propagator */ DisequalityPropagator* getDisequalityPropagator() { return d_deq_prop; } /** symmetry breaker */ @@ -401,23 +394,6 @@ public: Statistics d_statistics; };/* class StrongSolverTheoryUF */ - -class TermDisambiguator -{ -private: - /** quantifiers engine */ - QuantifiersEngine* d_qe; - /** whether two terms are ambiguous (indexed by equalities) */ - context::CDHashMap<Node, bool, NodeHashFunction> d_term_amb; - /** involves relevant type */ - static bool involvesRelevantType( Node n ); -public: - TermDisambiguator( QuantifiersEngine* qe, context::Context* c ) : d_qe( qe ), d_term_amb( c ){} - ~TermDisambiguator(){} - /** check ambiguous terms */ - int disambiguateTerms( OutputChannel* out ); -}; - class DisequalityPropagator { private: diff --git a/src/util/Makefile.am b/src/util/Makefile.am index 2f278625a..1d6ce1a73 100644 --- a/src/util/Makefile.am +++ b/src/util/Makefile.am @@ -144,7 +144,8 @@ EXTRA_DIST = \ predicate.i \ uninterpreted_constant.i \ chain.i \ - regexp.i + regexp.i \ + proof.i DISTCLEANFILES = \ integer.h.tmp \ diff --git a/src/util/datatype.cpp b/src/util/datatype.cpp index 5fa893336..8d70e4ffc 100644 --- a/src/util/datatype.cpp +++ b/src/util/datatype.cpp @@ -102,7 +102,7 @@ void Datatype::resolve(ExprManager* em, CheckArgument(&self.getDatatype() == this, resolutions, "Datatype::resolve(): resolutions doesn't contain me!"); d_resolved = true; size_t index = 0; - for(iterator i = begin(), i_end = end(); i != i_end; ++i) { + for(std::vector<DatatypeConstructor>::iterator i = d_constructors.begin(), i_end = d_constructors.end(); i != i_end; ++i) { (*i).resolve(em, self, resolutions, placeholders, replacements, paramTypes, paramReplacements); Node::fromExpr((*i).d_constructor).setAttribute(DatatypeIndexAttr(), index); Node::fromExpr((*i).d_tester).setAttribute(DatatypeIndexAttr(), index++); @@ -416,7 +416,7 @@ void DatatypeConstructor::resolve(ExprManager* em, DatatypeType self, NodeManager* nm = NodeManager::fromExprManager(em); TypeNode selfTypeNode = TypeNode::fromType(self); size_t index = 0; - for(iterator i = begin(), i_end = end(); i != i_end; ++i) { + for(std::vector<DatatypeConstructorArg>::iterator i = d_args.begin(), i_end = d_args.end(); i != i_end; ++i) { if((*i).d_selector.isNull()) { // the unresolved type wasn't created here; do name resolution string typeName = (*i).d_name.substr((*i).d_name.find('\0') + 1); @@ -461,7 +461,7 @@ void DatatypeConstructor::resolve(ExprManager* em, DatatypeType self, d_tester = nm->mkSkolem(getTesterName(), nm->mkTesterType(selfTypeNode), "is a tester", NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY).toExpr(); d_constructor = nm->mkSkolem(getName(), nm->mkConstructorType(*this, selfTypeNode), "is a constructor", NodeManager::SKOLEM_EXACT_NAME | NodeManager::SKOLEM_NO_NOTIFY).toExpr(); // associate constructor with all selectors - for(iterator i = begin(), i_end = end(); i != i_end; ++i) { + for(std::vector<DatatypeConstructorArg>::iterator i = d_args.begin(), i_end = d_args.end(); i != i_end; ++i) { (*i).d_constructor = d_constructor; } } diff --git a/src/util/datatype.h b/src/util/datatype.h index 802704803..99a303950 100644 --- a/src/util/datatype.h +++ b/src/util/datatype.h @@ -40,6 +40,47 @@ namespace CVC4 { class CVC4_PUBLIC ExprManager; +class CVC4_PUBLIC DatatypeConstructor; +class CVC4_PUBLIC DatatypeConstructorArg; + +class CVC4_PUBLIC DatatypeConstructorIterator { + const std::vector<DatatypeConstructor>* d_v; + size_t d_i; + + friend class Datatype; + + DatatypeConstructorIterator(const std::vector<DatatypeConstructor>& v, bool start) : d_v(&v), d_i(start ? 0 : v.size()) { + } + +public: + typedef const DatatypeConstructor& value_type; + const DatatypeConstructor& operator*() const { return (*d_v)[d_i]; } + const DatatypeConstructor* operator->() const { return &(*d_v)[d_i]; } + DatatypeConstructorIterator& operator++() { ++d_i; return *this; } + DatatypeConstructorIterator operator++(int) { DatatypeConstructorIterator i(*this); ++d_i; return i; } + bool operator==(const DatatypeConstructorIterator& other) const { return d_v == other.d_v && d_i == other.d_i; } + bool operator!=(const DatatypeConstructorIterator& other) const { return d_v != other.d_v || d_i != other.d_i; } +};/* class DatatypeConstructorIterator */ + +class CVC4_PUBLIC DatatypeConstructorArgIterator { + const std::vector<DatatypeConstructorArg>* d_v; + size_t d_i; + + friend class DatatypeConstructor; + + DatatypeConstructorArgIterator(const std::vector<DatatypeConstructorArg>& v, bool start) : d_v(&v), d_i(start ? 0 : v.size()) { + } + +public: + typedef const DatatypeConstructorArg& value_type; + const DatatypeConstructorArg& operator*() const { return (*d_v)[d_i]; } + const DatatypeConstructorArg* operator->() const { return &(*d_v)[d_i]; } + DatatypeConstructorArgIterator& operator++() { ++d_i; return *this; } + DatatypeConstructorArgIterator operator++(int) { DatatypeConstructorArgIterator i(*this); ++d_i; return i; } + bool operator==(const DatatypeConstructorArgIterator& other) const { return d_v == other.d_v && d_i == other.d_i; } + bool operator!=(const DatatypeConstructorArgIterator& other) const { return d_v != other.d_v || d_i != other.d_i; } +};/* class DatatypeConstructorArgIterator */ + /** * An exception that is thrown when a datatype resolution fails. */ @@ -134,9 +175,9 @@ class CVC4_PUBLIC DatatypeConstructor { public: /** The type for iterators over constructor arguments. */ - typedef std::vector<DatatypeConstructorArg>::iterator iterator; + typedef DatatypeConstructorArgIterator iterator; /** The (const) type for iterators over constructor arguments. */ - typedef std::vector<DatatypeConstructorArg>::const_iterator const_iterator; + typedef DatatypeConstructorArgIterator const_iterator; private: @@ -394,9 +435,9 @@ public: static size_t indexOf(Expr item) CVC4_PUBLIC; /** The type for iterators over constructors. */ - typedef std::vector<DatatypeConstructor>::iterator iterator; + typedef DatatypeConstructorIterator iterator; /** The (const) type for iterators over constructors. */ - typedef std::vector<DatatypeConstructor>::const_iterator const_iterator; + typedef DatatypeConstructorIterator const_iterator; private: std::string d_name; @@ -540,13 +581,13 @@ public: inline bool isResolved() const throw(); /** Get the beginning iterator over DatatypeConstructors. */ - inline std::vector<DatatypeConstructor>::iterator begin() throw(); + inline iterator begin() throw(); /** Get the ending iterator over DatatypeConstructors. */ - inline std::vector<DatatypeConstructor>::iterator end() throw(); + inline iterator end() throw(); /** Get the beginning const_iterator over DatatypeConstructors. */ - inline std::vector<DatatypeConstructor>::const_iterator begin() const throw(); + inline const_iterator begin() const throw(); /** Get the ending const_iterator over DatatypeConstructors. */ - inline std::vector<DatatypeConstructor>::const_iterator end() const throw(); + inline const_iterator end() const throw(); /** Get the ith DatatypeConstructor. */ const DatatypeConstructor& operator[](size_t index) const; @@ -669,19 +710,19 @@ inline bool Datatype::isResolved() const throw() { } inline Datatype::iterator Datatype::begin() throw() { - return d_constructors.begin(); + return iterator(d_constructors, true); } inline Datatype::iterator Datatype::end() throw() { - return d_constructors.end(); + return iterator(d_constructors, false); } inline Datatype::const_iterator Datatype::begin() const throw() { - return d_constructors.begin(); + return const_iterator(d_constructors, true); } inline Datatype::const_iterator Datatype::end() const throw() { - return d_constructors.end(); + return const_iterator(d_constructors, false); } inline bool DatatypeConstructor::isResolved() const throw() { @@ -710,19 +751,19 @@ inline bool DatatypeConstructorArg::isResolved() const throw() { } inline DatatypeConstructor::iterator DatatypeConstructor::begin() throw() { - return d_args.begin(); + return iterator(d_args, true); } inline DatatypeConstructor::iterator DatatypeConstructor::end() throw() { - return d_args.end(); + return iterator(d_args, false); } inline DatatypeConstructor::const_iterator DatatypeConstructor::begin() const throw() { - return d_args.begin(); + return const_iterator(d_args, true); } inline DatatypeConstructor::const_iterator DatatypeConstructor::end() const throw() { - return d_args.end(); + return const_iterator(d_args, false); } }/* CVC4 namespace */ diff --git a/src/util/datatype.i b/src/util/datatype.i index c07caa805..403fb31bc 100644 --- a/src/util/datatype.i +++ b/src/util/datatype.i @@ -1,5 +1,12 @@ %{ #include "util/datatype.h" + +#ifdef SWIGJAVA + +#include "bindings/java_iterator_adapter.h" +#include "bindings/java_stream_adapters.h" + +#endif /* SWIGJAVA */ %} %extend std::vector< CVC4::Datatype > { @@ -32,34 +39,137 @@ %ignore set(int i, const CVC4::Datatype::Constructor& x); %ignore to_array(); }; -%template(vectorDatatypeConstructor) std::vector< CVC4::DatatypeConstructor >; +//%template(vectorDatatypeConstructor) std::vector< CVC4::DatatypeConstructor >; %rename(equals) CVC4::Datatype::operator==(const Datatype&) const; %ignore CVC4::Datatype::operator!=(const Datatype&) const; -%rename(beginConst) CVC4::Datatype::begin() const; -%rename(endConst) CVC4::Datatype::end() const; +%ignore CVC4::Datatype::begin(); +%ignore CVC4::Datatype::end(); +%ignore CVC4::Datatype::begin() const; +%ignore CVC4::Datatype::end() const; -%rename(getConstructor) CVC4::Datatype::operator[](size_t) const; -%ignore CVC4::Datatype::operator[](std::string) const; +%rename(get) CVC4::Datatype::operator[](size_t) const; +%rename(get) CVC4::Datatype::operator[](std::string) const; %rename(apply) CVC4::DatatypeHashFunction::operator()(const Datatype&) const; %ignore CVC4::DatatypeHashFunction::operator()(const Datatype*) const; %rename(apply) CVC4::DatatypeHashFunction::operator()(const DatatypeConstructor&) const; %ignore CVC4::DatatypeHashFunction::operator()(const DatatypeConstructor*) const; -%rename(beginConst) CVC4::DatatypeConstructor::begin() const; -%rename(endConst) CVC4::DatatypeConstructor::end() const; +%ignore CVC4::DatatypeConstructor::begin(); +%ignore CVC4::DatatypeConstructor::end(); +%ignore CVC4::DatatypeConstructor::begin() const; +%ignore CVC4::DatatypeConstructor::end() const; -%rename(getArg) CVC4::DatatypeConstructor::operator[](size_t) const; -%rename(getArg) CVC4::DatatypeConstructor::operator[](std::string) const; +%rename(get) CVC4::DatatypeConstructor::operator[](size_t) const; +%rename(get) CVC4::DatatypeConstructor::operator[](std::string) const; %ignore CVC4::operator<<(std::ostream&, const Datatype&); %ignore CVC4::operator<<(std::ostream&, const DatatypeConstructor&); %ignore CVC4::operator<<(std::ostream&, const DatatypeConstructorArg&); +%ignore CVC4::DatatypeConstructorIterator; +%ignore CVC4::DatatypeConstructorArgIterator; + %feature("valuewrapper") CVC4::DatatypeUnresolvedType; %feature("valuewrapper") CVC4::DatatypeConstructor; +#ifdef SWIGJAVA + +// Instead of Datatype::begin() and end(), create an +// iterator() method on the Java side that returns a Java-style +// Iterator. +%extend CVC4::Datatype { + CVC4::JavaIteratorAdapter<CVC4::Datatype> iterator() { + return CVC4::JavaIteratorAdapter<CVC4::Datatype>(*$self); + } + + std::string toString() const { + std::stringstream ss; + ss << *$self; + return ss.str(); + } +} +%extend CVC4::DatatypeConstructor { + CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor> iterator() { + return CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor>(*$self); + } + + std::string toString() const { + std::stringstream ss; + ss << *$self; + return ss.str(); + } +} +%extend CVC4::DatatypeConstructorArg { + std::string toString() const { + std::stringstream ss; + ss << *$self; + return ss.str(); + } +} + +// Datatype is "iterable" on the Java side +%typemap(javainterfaces) CVC4::Datatype "java.lang.Iterable<DatatypeConstructor>"; +%typemap(javainterfaces) CVC4::DatatypeConstructor "java.lang.Iterable<DatatypeConstructorArg>"; + +// the JavaIteratorAdapter should not be public, and implements Iterator +%typemap(javaclassmodifiers) CVC4::JavaIteratorAdapter<CVC4::Datatype> "class"; +%typemap(javaclassmodifiers) CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor> "class"; +%typemap(javainterfaces) CVC4::JavaIteratorAdapter<CVC4::Datatype> "java.util.Iterator<DatatypeConstructor>"; +%typemap(javainterfaces) CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor> "java.util.Iterator<DatatypeConstructorArg>"; +// add some functions to the Java side (do it here because there's no way to do these in C++) +%typemap(javacode) CVC4::JavaIteratorAdapter<CVC4::Datatype> " + public void remove() { + throw new java.lang.UnsupportedOperationException(); + } + + public DatatypeConstructor next() { + if(hasNext()) { + return getNext(); + } else { + throw new java.util.NoSuchElementException(); + } + } +" +%typemap(javacode) CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor> " + public void remove() { + throw new java.lang.UnsupportedOperationException(); + } + + public DatatypeConstructorArg next() { + if(hasNext()) { + return getNext(); + } else { + throw new java.util.NoSuchElementException(); + } + } +" +// getNext() just allows C++ iterator access from Java-side next(), make it private +%javamethodmodifiers CVC4::JavaIteratorAdapter<CVC4::Datatype>::getNext() "private"; +%javamethodmodifiers CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor>::getNext() "private"; + +// map the types appropriately. +%typemap(jni) CVC4::Datatype::iterator::value_type "jobject"; +%typemap(jtype) CVC4::Datatype::iterator::value_type "edu.nyu.acsys.CVC4.DatatypeConstructor"; +%typemap(jstype) CVC4::Datatype::iterator::value_type "edu.nyu.acsys.CVC4.DatatypeConstructor"; +%typemap(javaout) CVC4::Datatype::iterator::value_type { return $jnicall; } +%typemap(jni) CVC4::DatatypeConstructor::iterator::value_type "jobject"; +%typemap(jtype) CVC4::DatatypeConstructor::iterator::value_type "edu.nyu.acsys.CVC4.DatatypeConstructorArg"; +%typemap(jstype) CVC4::DatatypeConstructor::iterator::value_type "edu.nyu.acsys.CVC4.DatatypeConstructorArg"; +%typemap(javaout) CVC4::DatatypeConstructor::iterator::value_type { return $jnicall; } + +#endif /* SWIGJAVA */ + %include "util/datatype.h" +#ifdef SWIGJAVA + +%include "bindings/java_iterator_adapter.h" +%include "bindings/java_stream_adapters.h" + +%template(JavaIteratorAdapter_Datatype) CVC4::JavaIteratorAdapter<CVC4::Datatype>; +%template(JavaIteratorAdapter_DatatypeConstructor) CVC4::JavaIteratorAdapter<CVC4::DatatypeConstructor>; + +#endif /* SWIGJAVA */ diff --git a/src/util/proof.i b/src/util/proof.i new file mode 100644 index 000000000..22dff1043 --- /dev/null +++ b/src/util/proof.i @@ -0,0 +1,5 @@ +%{ +#include "util/proof.h" +%} + +%include "util/proof.h" diff --git a/src/util/regexp.h b/src/util/regexp.h index 3a8fc7170..f6c5b2b0f 100644 --- a/src/util/regexp.h +++ b/src/util/regexp.h @@ -183,6 +183,39 @@ public: return true; } + std::size_t find(const String &y, const int start = 0) const { + if(d_str.size() < y.d_str.size() + (std::size_t) start) return std::string::npos; + if(y.d_str.size() == 0) return (std::size_t) start; + if(d_str.size() == 0) return std::string::npos; + std::size_t ret = std::string::npos; + for(int i = start; i <= (int) d_str.size() - (int) y.d_str.size(); i++) { + if(d_str[i] == y.d_str[0]) { + std::size_t j=0; + for(; j<y.d_str.size(); j++) { + if(d_str[i+j] != y.d_str[j]) break; + } + if(j == y.d_str.size()) { + ret = (std::size_t) i; + break; + } + } + } + return ret; + } + + 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.d_str.size(), d_str.end()); + return String(vec); + } else { + return *this; + } + } + String substr(unsigned i) const { std::vector<unsigned int> ret_vec; std::vector<unsigned int>::const_iterator itr = d_str.begin() + i; diff --git a/test/regress/regress0/Makefile.am b/test/regress/regress0/Makefile.am index 5c591d39c..d7663e298 100644 --- a/test/regress/regress0/Makefile.am +++ b/test/regress/regress0/Makefile.am @@ -7,12 +7,12 @@ DIST_SUBDIRS = . arith precedence uf uflra uflia bv arrays aufbv auflia datatype end@mk_if@ LOG_COMPILER = @srcdir@/../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/arith/Makefile.am b/test/regress/regress0/arith/Makefile.am index 6897ee3c4..e7810c7c4 100644 --- a/test/regress/regress0/arith/Makefile.am +++ b/test/regress/regress0/arith/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . integers end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/arith/integers/Makefile.am b/test/regress/regress0/arith/integers/Makefile.am index 3511c6b30..3b6a86bc0 100644 --- a/test/regress/regress0/arith/integers/Makefile.am +++ b/test/regress/regress0/arith/integers/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/arrays/Makefile.am b/test/regress/regress0/arrays/Makefile.am index 33f05ab40..62877ddf3 100644 --- a/test/regress/regress0/arrays/Makefile.am +++ b/test/regress/regress0/arrays/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/aufbv/Makefile.am b/test/regress/regress0/aufbv/Makefile.am index e151a4846..e45358a8a 100644 --- a/test/regress/regress0/aufbv/Makefile.am +++ b/test/regress/regress0/aufbv/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/auflia/Makefile.am b/test/regress/regress0/auflia/Makefile.am index ca1fc25d3..31d9c0797 100644 --- a/test/regress/regress0/auflia/Makefile.am +++ b/test/regress/regress0/auflia/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/boolean.cvc b/test/regress/regress0/boolean.cvc index cba092e9e..eb0e7ab52 100644 --- a/test/regress/regress0/boolean.cvc +++ b/test/regress/regress0/boolean.cvc @@ -804,4 +804,3 @@ a288 : BOOLEAN = ELSE FALSE ENDIF; QUERY a288; -% PROOF diff --git a/test/regress/regress0/bv/Makefile.am b/test/regress/regress0/bv/Makefile.am index f0bfb2842..5d2a54b11 100644 --- a/test/regress/regress0/bv/Makefile.am +++ b/test/regress/regress0/bv/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . core end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/bv/core/Makefile.am b/test/regress/regress0/bv/core/Makefile.am index 888e9d8dc..7c411121a 100644 --- a/test/regress/regress0/bv/core/Makefile.am +++ b/test/regress/regress0/bv/core/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/datatypes/Makefile.am b/test/regress/regress0/datatypes/Makefile.am index 67b97add3..84adb4f84 100644 --- a/test/regress/regress0/datatypes/Makefile.am +++ b/test/regress/regress0/datatypes/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/datatypes/empty_tuprec.cvc b/test/regress/regress0/datatypes/empty_tuprec.cvc index 415da3c18..5fe17b412 100644 --- a/test/regress/regress0/datatypes/empty_tuprec.cvc +++ b/test/regress/regress0/datatypes/empty_tuprec.cvc @@ -1,3 +1,5 @@ +% COMMAND-LINE: --no-check-proofs +% OPTION "incremental"; a1, a2 : []; % empty tuples (a unit type) diff --git a/test/regress/regress0/decision/Makefile.am b/test/regress/regress0/decision/Makefile.am index 0f8ef8e8e..366204191 100644 --- a/test/regress/regress0/decision/Makefile.am +++ b/test/regress/regress0/decision/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/fmf/Makefile.am b/test/regress/regress0/fmf/Makefile.am index bfbc851ef..2633949c8 100644 --- a/test/regress/regress0/fmf/Makefile.am +++ b/test/regress/regress0/fmf/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/fmf/PUZ001+1.smt2 b/test/regress/regress0/fmf/PUZ001+1.smt2 index bf156367e..f0e53fc98 100644 --- a/test/regress/regress0/fmf/PUZ001+1.smt2 +++ b/test/regress/regress0/fmf/PUZ001+1.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --finite-model-find +; COMMAND-LINE: --finite-model-find --no-check-proofs ; EXPECT: unsat ;%------------------------------------------------------------------------------ ;% File : PUZ001+1 : TPTP v5.4.0. Released v2.0.0. diff --git a/test/regress/regress0/hole6.cvc b/test/regress/regress0/hole6.cvc index 5cc4de9be..dfa9b72d5 100644 --- a/test/regress/regress0/hole6.cvc +++ b/test/regress/regress0/hole6.cvc @@ -177,4 +177,3 @@ ASSERT x_42 OR x_41 OR x_40 OR x_39 OR x_38 OR x_37; QUERY FALSE; -% PROOF diff --git a/test/regress/regress0/lemmas/Makefile.am b/test/regress/regress0/lemmas/Makefile.am index 260b3600d..9ede6d4c0 100644 --- a/test/regress/regress0/lemmas/Makefile.am +++ b/test/regress/regress0/lemmas/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/precedence/Makefile.am b/test/regress/regress0/precedence/Makefile.am index 141510ea2..1d980997d 100644 --- a/test/regress/regress0/precedence/Makefile.am +++ b/test/regress/regress0/precedence/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/preprocess/Makefile.am b/test/regress/regress0/preprocess/Makefile.am index 73d13e78d..d83df4192 100644 --- a/test/regress/regress0/preprocess/Makefile.am +++ b/test/regress/regress0/preprocess/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/push-pop/Makefile.am b/test/regress/regress0/push-pop/Makefile.am index 0a1094238..29ad34255 100644 --- a/test/regress/regress0/push-pop/Makefile.am +++ b/test/regress/regress0/push-pop/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = boolean arith . end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/push-pop/arith/Makefile.am b/test/regress/regress0/push-pop/arith/Makefile.am index 6fd183ec3..7838e202d 100644 --- a/test/regress/regress0/push-pop/arith/Makefile.am +++ b/test/regress/regress0/push-pop/arith/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/push-pop/boolean/Makefile.am b/test/regress/regress0/push-pop/boolean/Makefile.am index 0757ebfc2..995312cee 100644 --- a/test/regress/regress0/push-pop/boolean/Makefile.am +++ b/test/regress/regress0/push-pop/boolean/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/quantifiers/Makefile.am b/test/regress/regress0/quantifiers/Makefile.am index 0b74d83b7..d0a93a142 100644 --- a/test/regress/regress0/quantifiers/Makefile.am +++ b/test/regress/regress0/quantifiers/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/rewriterules/Makefile.am b/test/regress/regress0/rewriterules/Makefile.am index d2e748fbf..32f8a72ba 100644 --- a/test/regress/regress0/rewriterules/Makefile.am +++ b/test/regress/regress0/rewriterules/Makefile.am @@ -7,12 +7,12 @@ export CVC4_REGRESSION_ARGS end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/strings/Makefile.am b/test/regress/regress0/strings/Makefile.am index e24cbc565..a2142eeb3 100644 --- a/test/regress/regress0/strings/Makefile.am +++ b/test/regress/regress0/strings/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif @@ -19,6 +19,7 @@ MAKEFLAGS = -k # If a test shouldn't be run in e.g. competition mode, # put it below in "TESTS +=" TESTS = \ + at001.smt2 \ cardinality.smt2 \ str001.smt2 \ str002.smt2 \ diff --git a/test/regress/regress0/strings/at001.smt2 b/test/regress/regress0/strings/at001.smt2 new file mode 100644 index 000000000..855957430 --- /dev/null +++ b/test/regress/regress0/strings/at001.smt2 @@ -0,0 +1,12 @@ +(set-logic QF_S)
+(set-info :status sat)
+
+(declare-fun x () String)
+(declare-fun i () Int)
+
+(assert (= (str.at x i) "b"))
+(assert (> i 5))
+(assert (< (str.len x) 4))
+(assert (> (str.len x) 2))
+
+(check-sat)
diff --git a/test/regress/regress0/strings/substr001.smt2 b/test/regress/regress0/strings/substr001.smt2 index 2b2ae9820..476b82699 100644 --- a/test/regress/regress0/strings/substr001.smt2 +++ b/test/regress/regress0/strings/substr001.smt2 @@ -1,5 +1,4 @@ (set-logic QF_S)
-(set-option :strings-exp true)
(set-info :status sat)
(declare-fun x () String)
@@ -8,8 +7,8 @@ (declare-fun i3 () Int)
(declare-fun i4 () Int)
-(assert (= "efg" (str.sub x i1 i2) ) )
-(assert (= "bef" (str.sub x i3 i4) ) )
+(assert (= "efg" (str.substr x i1 i2) ) )
+(assert (= "bef" (str.substr x i3 i4) ) )
(assert (> (str.len x) 5))
(check-sat)
diff --git a/test/regress/regress0/tptp/Makefile.am b/test/regress/regress0/tptp/Makefile.am index e227e0bba..f8f106362 100644 --- a/test/regress/regress0/tptp/Makefile.am +++ b/test/regress/regress0/tptp/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/uf/Makefile.am b/test/regress/regress0/uf/Makefile.am index 19e673fea..98194413d 100644 --- a/test/regress/regress0/uf/Makefile.am +++ b/test/regress/regress0/uf/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/uflia/Makefile.am b/test/regress/regress0/uflia/Makefile.am index 2ef7be862..2946d886a 100644 --- a/test/regress/regress0/uflia/Makefile.am +++ b/test/regress/regress0/uflia/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/uflra/Makefile.am b/test/regress/regress0/uflra/Makefile.am index 63d362bf9..cd39284b8 100644 --- a/test/regress/regress0/uflra/Makefile.am +++ b/test/regress/regress0/uflra/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/unconstrained/Makefile.am b/test/regress/regress0/unconstrained/Makefile.am index c9a38d7b1..ecf427fb5 100644 --- a/test/regress/regress0/unconstrained/Makefile.am +++ b/test/regress/regress0/unconstrained/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress0/wiki.05.cvc b/test/regress/regress0/wiki.05.cvc index fa9f56f81..0fe647f7b 100644 --- a/test/regress/regress0/wiki.05.cvc +++ b/test/regress/regress0/wiki.05.cvc @@ -2,4 +2,3 @@ a, b, c : BOOLEAN; % EXPECT: valid QUERY a OR (a AND b) <=> a; -% PROOF diff --git a/test/regress/regress1/Makefile.am b/test/regress/regress1/Makefile.am index 674f5c75e..5f292b893 100644 --- a/test/regress/regress1/Makefile.am +++ b/test/regress/regress1/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . arith end@mk_if@ LOG_COMPILER = @srcdir@/../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress1/arith/Makefile.am b/test/regress/regress1/arith/Makefile.am index ca362f479..fff5372c6 100644 --- a/test/regress/regress1/arith/Makefile.am +++ b/test/regress/regress1/arith/Makefile.am @@ -4,12 +4,12 @@ end@mk_if@ LOG_COMPILER = @srcdir@/../../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress2/Makefile.am b/test/regress/regress2/Makefile.am index e4e5d8d29..9deb1f37b 100644 --- a/test/regress/regress2/Makefile.am +++ b/test/regress/regress2/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/regress3/Makefile.am b/test/regress/regress3/Makefile.am index 213157491..3fb798bcc 100644 --- a/test/regress/regress3/Makefile.am +++ b/test/regress/regress3/Makefile.am @@ -6,12 +6,12 @@ SUBDIRS = . end@mk_if@ LOG_COMPILER = @srcdir@/../run_regression -AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @top_builddir@/src/main/$(BINARY)$(EXEEXT) +AM_LOG_FLAGS = $(RUN_REGRESSION_ARGS) @abs_top_builddir@/src/main/$(BINARY)$(EXEEXT) if AUTOMAKE_1_11 # old-style (pre-automake 1.12) test harness TESTS_ENVIRONMENT = \ - $(TESTS_ENVIRONMENT) $(LOG_COMPILER) \ + $(LOG_COMPILER) \ $(AM_LOG_FLAGS) $(LOG_FLAGS) endif diff --git a/test/regress/run_regression b/test/regress/run_regression index 4d23e796b..ef2bb9a35 100755 --- a/test/regress/run_regression +++ b/test/regress/run_regression @@ -71,10 +71,8 @@ function gettemp { tmpbenchmark= if expr "$benchmark" : '.*\.smt$' &>/dev/null; then - proof_command=PROOFS-NOT-SUPPORTED-IN-SMTLIB-V1 lang=smt1 if test -e "$benchmark.expect"; then - expected_proof=`grep '^% PROOF' "$benchmark.expect" &>/dev/null && echo yes` expected_output=`grep '^% EXPECT: ' "$benchmark.expect" | sed 's,^% EXPECT: ,,'` expected_error=`grep '^% EXPECT-ERROR: ' "$benchmark.expect" | sed 's,^% EXPECT-ERROR: ,,'` expected_exit_status=`grep -m 1 '^% EXIT: ' "$benchmark.expect" | perl -pe 's,^% EXIT: ,,;s,\r,,'` @@ -83,7 +81,6 @@ if expr "$benchmark" : '.*\.smt$' &>/dev/null; then expected_exit_status=0 fi elif grep '^% \(PROOF\|EXPECT\|EXPECT-ERROR\|EXIT\|COMMAND-LINE\): ' "$benchmark" "$benchmark" &>/dev/null; then - expected_proof=`grep '^% PROOF' "$benchmark" &>/dev/null && echo yes` expected_output=`grep '^% EXPECT: ' "$benchmark" | sed 's,^% EXPECT: ,,'` expected_error=`grep '^% EXPECT-ERROR: ' "$benchmark" | sed 's,^% EXPECT-ERROR: ,,'` expected_exit_status=`grep -m 1 '^% EXIT: ' "$benchmark" | perl -pe 's,^% EXIT: ,,;s,\r,,'` @@ -97,12 +94,10 @@ if expr "$benchmark" : '.*\.smt$' &>/dev/null; then fi benchmark=$tmpbenchmark elif grep '^ *:status *sat' "$benchmark" &>/dev/null; then - expected_proof= expected_output=sat expected_exit_status=0 command_line= elif grep '^ *:status *unsat' "$benchmark" &>/dev/null; then - expected_proof= expected_output=unsat expected_exit_status=0 command_line= @@ -110,10 +105,8 @@ if expr "$benchmark" : '.*\.smt$' &>/dev/null; then error "cannot determine status of \`$benchmark'" fi elif expr "$benchmark" : '.*\.smt2$' &>/dev/null; then - proof_command='(get-proof)' lang=smt2 if test -e "$benchmark.expect"; then - expected_proof=`grep '^[%;] PROOF' "$benchmark.expect" &>/dev/null && echo yes` expected_output=`grep '^% EXPECT: ' "$benchmark.expect" | sed 's,^% EXPECT: ,,'` expected_error=`grep '^% EXPECT-ERROR: ' "$benchmark.expect" | sed 's,^% EXPECT-ERROR: ,,'` expected_exit_status=`grep -m 1 '^% EXIT: ' "$benchmark.expect" | perl -pe 's,^% EXIT: ,,;s,\r,,'` @@ -122,7 +115,6 @@ elif expr "$benchmark" : '.*\.smt2$' &>/dev/null; then expected_exit_status=0 fi elif grep '^\(%\|;\) \(EXPECT\|EXPECT-ERROR\|EXIT\|COMMAND-LINE\): ' "$benchmark" "$benchmark" &>/dev/null; then - expected_proof=`grep '^[%;] PROOF' "$benchmark" &>/dev/null && echo yes` expected_output=`grep '^[%;] EXPECT: ' "$benchmark" | sed 's,^[%;] EXPECT: ,,'` expected_error=`grep '^[%;] EXPECT-ERROR: ' "$benchmark" | sed 's,^[%;] EXPECT-ERROR: ,,'` expected_exit_status=`grep -m 1 '^[%;] EXIT: ' "$benchmark" | perl -pe 's,^[%;] EXIT: ,,;s,\r,,'` @@ -136,12 +128,10 @@ elif expr "$benchmark" : '.*\.smt2$' &>/dev/null; then fi benchmark=$tmpbenchmark elif grep '^ *( *set-info *:status *sat' "$benchmark" &>/dev/null; then - expected_proof= expected_output=sat expected_exit_status=0 command_line= elif grep '^ *( *set-info *:status *unsat' "$benchmark" &>/dev/null; then - expected_proof=`grep '^; PROOF' "$benchmark" &>/dev/null && echo yes` expected_output=unsat expected_exit_status=0 command_line= @@ -149,9 +139,7 @@ elif expr "$benchmark" : '.*\.smt2$' &>/dev/null; then error "cannot determine status of \`$benchmark'" fi elif expr "$benchmark" : '.*\.cvc$' &>/dev/null; then - proof_command='DUMP_PROOF;' lang=cvc4 - expected_proof=`grep '^% PROOF' "$benchmark" &>/dev/null && echo yes` expected_output=$(grep '^% EXPECT: ' "$benchmark") expected_error=`grep '^% EXPECT-ERROR: ' "$benchmark" | sed 's,^% EXPECT-ERROR: ,,'` if [ -z "$expected_output" -a -z "$expected_error" ]; then @@ -165,10 +153,8 @@ elif expr "$benchmark" : '.*\.cvc$' &>/dev/null; then fi command_line=`grep '^% COMMAND-LINE: ' "$benchmark" | sed 's,^% COMMAND-LINE: ,,'` elif expr "$benchmark" : '.*\.p$' &>/dev/null; then - proof_command=PROOFS-NOT-SUPPORTED-IN-TPTP; lang=tptp command_line=--finite-model-find - expected_proof=`grep '^% PROOF' "$benchmark" &>/dev/null && echo yes` expected_output=$(grep '^% EXPECT: ' "$benchmark") expected_error=`grep '^% EXPECT-ERROR: ' "$benchmark" | sed 's,^% EXPECT-ERROR: ,,'` if [ -z "$expected_output" -a -z "$expected_error" ]; then @@ -213,14 +199,28 @@ if [ -z "$expected_output" ]; then else echo "$expected_output" >"$expoutfile" fi + check_models=false if grep '^sat$' "$expoutfile" &>/dev/null || grep '^invalid$' "$expoutfile" &>/dev/null || grep '^unknown$' "$expoptfile" &>/dev/null; then - if ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--check-models' &>/dev/null && - ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--no-check-models' &>/dev/null; then + if ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--check-models\>' &>/dev/null && + ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--no-check-models\>' &>/dev/null; then # later on, we'll run another test with --check-models on check_models=true fi fi +check_proofs=false +if [ "$proof" = yes ]; then + # proofs not currently supported in incremental mode, turn it off + if grep '^unsat$' "$expoutfile" &>/dev/null || grep '^valid$' "$expoutfile" &>/dev/null &>/dev/null; then + if ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--check-proofs\>' &>/dev/null && + ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--no-check-proofs\>' &>/dev/null && + ! expr "$CVC4_REGRESSION_ARGS $command_line" : '.*--incremental\>' &>/dev/null && + ! expr " $CVC4_REGRESSION_ARGS $command_line" : '.* -[a-zA-Z]*i' &>/dev/null; then + # later on, we'll run another test with --check-proofs on + check_proofs=true + fi + fi +fi if [ -z "$expected_error" ]; then # in case expected stderr output is empty, make sure we don't differ # by a newline, which we would if we echo "" >"$experrfile" @@ -275,47 +275,16 @@ if [ "$exit_status" != "$expected_exit_status" ]; then exitcode=1 fi -if [ "$proof" = yes -a "$expected_proof" = yes ]; then - gettemp pfbenchmark cvc4_pfbenchmark.$$.XXXXXXXXXX - # remove exit command to add proof command for smt2 benchmarks - if expr "$benchmark" : '.*\.smt2$' &>/dev/null; then - head -n -0 "$benchmark" > "$pfbenchmark"; - echo "$proof_command" >>"$pfbenchmark"; - echo "(exit)" >> "$pfbenchmark"; - else - cp $benchmark $pfbenchmark - echo "$proof_command" >>"$pfbenchmark"; +if $check_models || $check_proofs; then + check_cmdline= + if $check_models; then + check_cmdline="$check_cmdline --check-models" fi - echo running $wrapper $cvc4full $CVC4_REGRESSION_ARGS $command_line --proof `basename "$pfbenchmark"` [from working dir `dirname "$pfbenchmark"`] - time ( :; \ - ( cd `dirname "$pfbenchmark"`; - $wrapper "$cvc4full" $CVC4_REGRESSION_ARGS $command_line --proof `basename "$pfbenchmark"`; - echo $? >"$exitstatusfile" - ) > "$outfile" 2> "$errfile" ) - - gettemp pfoutfile cvc4_proof.$$.XXXXXXXXXX - - diff --unchanged-group-format='' \ - --old-group-format='' \ - --new-group-format='%>' \ - "$expoutfile" "$outfile" > "$pfoutfile" - if [ ! -s "$pfoutfile" ]; then - echo "$prog: error: proof generation failed with empty output (stderr follows)" - cat "$errfile" - exitcode=1 - else - echo running $LFSC "$pfoutfile" [from working dir `dirname "$pfbenchmark"`] - if ! $LFSC "$pfoutfile" &> "$errfile"; then - echo "$prog: error: proof checker failed (output follows)" - cat "$errfile" - exitcode=1 - fi + if $check_proofs; then + check_cmdline="$check_cmdline --check-proofs" fi -fi - -if $check_models; then - # at least one sat/invalid response: run an extra model-checking pass - if ! CVC4_REGRESSION_ARGS="$CVC4_REGRESSION_ARGS --check-models" "$0" $wrapper "$cvc4" "$benchmark_orig"; then + # at least one sat/invalid response: run an extra model/proof-checking pass + if ! CVC4_REGRESSION_ARGS="$CVC4_REGRESSION_ARGS$check_cmdline" "$0" $wrapper "$cvc4" "$benchmark_orig"; then exitcode=1 fi fi |