diff options
author | Andrew Reynolds <andrew.j.reynolds@gmail.com> | 2020-09-02 16:02:41 -0500 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-09-02 16:02:41 -0500 |
commit | 2d6d62b7bc0c15a44b38641a52ba389591ecc7f6 (patch) | |
tree | e11ae0a24c157cf01dbcf287727240b4e75b7b8a | |
parent | dba70e10ef8ae0a991969cb7ca0cba2d0e9d9d4d (diff) | |
parent | 0f9fb31069d51e003a39b0e93f506324dec2bdac (diff) |
Merge branch 'master' into fixCMSBuildPRfixCMSBuildPR
275 files changed, 5953 insertions, 19753 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 00fa15bc5..d7909b961 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -90,12 +90,12 @@ jobs: python3 -m pip install pexpect echo "::add-path::/usr/local/opt/ccache/libexec" - # Note: We install Cython with sudo since cmake can't find Cython otherwise. - name: Install Cython if: matrix.python-bindings && runner.os == 'Linux' run: | - sudo python3 -m pip install \ - Cython==0.29 --install-option="--no-cython-compile" + python3 -m pip install \ + Cython==0.29.* --install-option="--no-cython-compile" + echo "::add-path::$(python3 -m site --user-base)/bin" - name: Install Cython (macOS) if: matrix.python-bindings && runner.os == 'macOS' diff --git a/CMakeLists.txt b/CMakeLists.txt index 5b1d1e292..06e9c44f6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -571,6 +571,7 @@ add_subdirectory(src) add_subdirectory(test) if(BUILD_BINDINGS_PYTHON) + set(BUILD_BINDINGS_PYTHON_VERSION ${PYTHON_VERSION_MAJOR}) add_subdirectory(src/api/python) endif() @@ -767,7 +768,7 @@ message("") if(GPL_LIBS) message( - "CVC4 license : GPLv3 (due to optional libraries; see below)" + "CVC4 license : ${Yellow}GPLv3 (due to optional libraries; see below)${ResetColor}" "\n" "\n" "Please note that CVC4 will be built against the following GPLed libraries:" @@ -22,6 +22,8 @@ Changes: BSD-licensed Editline. Compiling with `--best` now enables Editline, instead of Readline. Without selecting optional GPL components, Editline-enabled CVC4 builds will be BSD licensed. +* The `competition` build type includes the dependencies used for SMT-COMP by + default. Note that this makes this build type produce GPL-licensed binaries. Changes since 1.7 ================= diff --git a/cmake/CVC4Config.cmake.in b/cmake/CVC4Config.cmake.in index 76535762d..7f6a80995 100644 --- a/cmake/CVC4Config.cmake.in +++ b/cmake/CVC4Config.cmake.in @@ -1,7 +1,8 @@ @PACKAGE_INIT@ -set(CVC4_BINDINGS_JAVA @BUILD_SWIG_BINDINGS_JAVA@) -set(CVC4_BINDINGS_PYTHON @BUILD_SWIG_BINDINGS_PYTHON@) +set(CVC4_BINDINGS_JAVA @BUILD_BINDINGS_JAVA@) +set(CVC4_BINDINGS_PYTHON @BUILD_BINDINGS_PYTHON@) +set(CVC4_BINDINGS_PYTHON_VERSION @BUILD_BINDINGS_PYTHON_VERSION@) if(NOT TARGET CVC4::cvc4) include(${CMAKE_CURRENT_LIST_DIR}/CVC4Targets.cmake) diff --git a/cmake/ConfigCompetition.cmake b/cmake/ConfigCompetition.cmake index e18d2b2f1..d7188f60a 100644 --- a/cmake/ConfigCompetition.cmake +++ b/cmake/ConfigCompetition.cmake @@ -22,3 +22,13 @@ cvc4_set_option(ENABLE_MUZZLE ON) # enable_shared=no cvc4_set_option(ENABLE_SHARED OFF) cvc4_set_option(ENABLE_UNIT_TESTING OFF) + +# By default, we include all dependencies in our competition build that are +# required to achieve the best performance +set(ENABLE_GPL ON) +cvc4_set_option(USE_CADICAL ON) +cvc4_set_option(USE_CLN ON) +cvc4_set_option(USE_CRYPTOMINISAT ON) +cvc4_set_option(USE_EDITLINE OFF) +cvc4_set_option(USE_GLPK ON) +cvc4_set_option(USE_SYMFPU ON) diff --git a/cmake/Helpers.cmake b/cmake/Helpers.cmake index 692e32900..79c8f7bf2 100644 --- a/cmake/Helpers.cmake +++ b/cmake/Helpers.cmake @@ -1,6 +1,12 @@ include(CheckCCompilerFlag) include(CheckCXXCompilerFlag) +if(NOT WIN32) + string(ASCII 27 Esc) + set(Yellow "${Esc}[33m") + set(ResetColor "${Esc}[m") +endif() + # Add a C flag to the global list of C flags. macro(add_c_flag flag) if(CMAKE_C_FLAGS) diff --git a/contrib/get-cryptominisat b/contrib/get-cryptominisat index 2b85e1a91..476d1ff10 100755 --- a/contrib/get-cryptominisat +++ b/contrib/get-cryptominisat @@ -3,7 +3,7 @@ source "$(dirname "$0")/get-script-header.sh" CMS_DIR="$DEPS_DIR/cryptominisat5" -version="5.6.3" +version="5.8.0" check_dep_dir "$CMS_DIR" setup_dep \ diff --git a/contrib/get-glpk-cut-log b/contrib/get-glpk-cut-log index 16acf97ae..951e10620 100755 --- a/contrib/get-glpk-cut-log +++ b/contrib/get-glpk-cut-log @@ -2,15 +2,24 @@ # source "$(dirname "$0")/get-script-header.sh" +# get the full path to the contrib dir; needs to be the full path so we can +# refer the patch from within the 'glpk-cut-log' build directory +contrib_dir="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd -P)" + +# Get the full path the patch we wish to apply +patch_file=${contrib_dir}/glpk-cut-log.patch + GLPK_DIR="$DEPS_DIR/glpk-cut-log" -commit=b420454e732f4b3d229c552ef7cd46fec75fe65c +version="4.52" check_dep_dir "$GLPK_DIR" setup_dep \ - "https://github.com/timothy-king/glpk-cut-log/archive/$commit.tar.gz" \ + "https://ftp.gnu.org/gnu/glpk/glpk-${version}.tar.gz" \ "$GLPK_DIR" cd "$GLPK_DIR" +patch -p1 < ${patch_file} + libtoolize aclocal autoheader diff --git a/contrib/glpk-cut-log.patch b/contrib/glpk-cut-log.patch new file mode 100644 index 000000000..9ef01886c --- /dev/null +++ b/contrib/glpk-cut-log.patch @@ -0,0 +1,1528 @@ +@@ This patch is taken from https://github.com/timothy-king/glpk-cut-log and +@@ has the following license: +@@ +@@ GLPK is free software: you can redistribute it and/or modify it under the +@@ terms of the GNU General Public License as published by the Free Software +@@ Foundation, either version 3 of the License, or (at your option) any later +@@ version. +@@ +From cf84e3855d8c5676557daaca434b42b2fc17dc29 Mon Sep 17 00:00:00 2001 +From: Tim King <taking@cs.nyu.edu> +Date: Sat, 14 Dec 2013 16:03:35 -0500 +Subject: [PATCH] Adding a function for returning the iteration count. + +Adding GLP_ICUTADDED callback: +- Adds a new callback location that is called after every cut is added to the pool. +- During this callback users can request a copy of this cut and query what kind of cut it is. +- GMI cuts also support returning what row in the tableau it came from. Users can use this to replay how the cut was generated. + +Logging MIR Cuts +- Changed the IOSAUX to be defined as a linear sum of input rows. + This is visible from the interface +- Logging the multipliers used to create the aggregate row in MIR. +- Logging the delta selected by final successful cmir_sep call. +- Logging the cset selected by final successful cmir_sep call. +- The delta and cset are not yet user visible. + +The extra mir logging information is now user visible. + +Adding GLP_ICUTSELECT callback: +- ios_process_cuts marks the cuts as selected after turning the cut into a row. +- This callback happens after ios_process_cuts marks and before the pool is + cleared by ios_clear_pool. + +Cleaning up the git directory to not track autogenerated files. + +Branch logging +- Adds to the integer interface a callback function for when branches are made. + This is called after branch_on. +- Added a public function glp_ios_branch_log. + This returns the structural variable for the branch as well as the value + branched on, and the ids of the nodes for the down and up branches. + If both branches are infeasible, the ids are -1 for both dn and up. + If one branch is infeasible (and no branch is done), this returns -1 for + the infeasible branch and the current node id for the up branch. +- Each node now has a unique ordinal associated with it. +- Added a public function to convert the id of an active node to the unique + node ordinal. +- Added a callback for when a node is closed due to being linearly infeasible. + +Improved the mir cut logging facilities to now include: +- the rid id used for virtual lower/upper bound selection, +- and the subst map, +- Changed the size of the cset array to be the same as the new arrays (m+n) +- Fixed a bug in returning the cset selected. + +Fixing a bug selecting the correct branch. + +Added a callback for tracking row deletion. + +Adding notes to node freeze and revive functions. + +Commenting out a few debugging printfs for gomory cuts. + +Changing an overly aggressive assert in MIR generate when an assignment is outside of the bounds to skipping the application of the mir rule. + +Removing some printfs. + +Weakening assert to a failure. + +Adding a stability failure limit to simplex. + +Updating the installation instructions. + +--- +diff --git a/configure.ac b/configure.ac +index 0141a8a..ffb9ae4 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -1,7 +1,7 @@ + dnl Process this file with autoconf to produce a configure script + + AC_INIT([GLPK], [4.52], [bug-glpk@gnu.org]) +- ++AM_INIT_AUTOMAKE([subdir-objects]) + AC_CONFIG_SRCDIR([src/glpk.h]) + + AC_CONFIG_MACRO_DIR([m4]) +diff --git a/src/Makefile.am b/src/Makefile.am +index b39efa6..2a025bc 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -20,7 +20,10 @@ libglpk_la_LDFLAGS = \ + -version-info 36:0:1 \ + -export-symbols-regex '^glp_*' + ++ ++# all of the cut log sources are listed first + libglpk_la_SOURCES = \ ++cutlog01.c \ + glpapi01.c \ + glpapi02.c \ + glpapi03.c \ +diff --git a/src/cutlog01.c b/src/cutlog01.c +new file mode 100644 +index 0000000..9a85bb7 +--- /dev/null ++++ b/src/cutlog01.c +@@ -0,0 +1,452 @@ ++/* cutlog01.c (api extension routines) */ ++ ++ ++#include "glpapi.h" ++#include "glpios.h" ++ ++int glp_get_it_cnt(glp_prob *P){ ++ if(P == NULL){ ++ return 0; ++ }else{ ++ return P->it_cnt; ++ } ++} ++ ++int glp_ios_get_cut(glp_tree *T, int i, int* ind, double* val, int* klass, int* type, double* rhs){ ++ xassert(T != NULL); ++ ++ IOSCUT* cut; ++ int len; ++ IOSAIJ* aij; ++ glp_prob* prob; ++ ++ if (T->reason != GLP_ICUTADDED){ ++ xerror("glp_ios_get_cut: not called during cut added.\n"); ++ } ++ cut = ios_find_row(T->local, i); ++ if ( cut == NULL ) { ++ xerror("glp_ios_get_cut: called with an invalid index."); ++ } ++ len = 0; ++ for(len = 0, aij = cut->ptr; aij != NULL; aij = aij->next) ++ { ++ len++; ++ if(ind != NULL){ ind[len] = aij->j; } ++ if(val != NULL){ val[len] = aij->val; } ++ } ++ if(klass != NULL){ *klass = cut->klass; } ++ if(type != NULL){ *type = cut->type; } ++ if(rhs != NULL){ *rhs = cut->rhs; } ++ return len; ++} ++ ++ ++IOSAUX *ios_create_aux(int n, const int rows[], const double coeffs[]){ ++ IOSAUX *aux; ++ int i; ++ aux = xmalloc(sizeof(IOSAUX)); ++ aux->nrows = n; ++ aux->rows = xcalloc(1+n, sizeof(int)); ++ aux->mult = xcalloc(1+n, sizeof(double)); ++ aux->selected = -1; ++ aux->mir_cset = NULL; ++ ++ for ( i = 1; i <= n; i++) ++ { aux->rows[i] = rows[i]; ++ aux->mult[i] = coeffs[i]; ++ } ++ ++ return aux; ++} ++ ++void ios_delete_aux(IOSAUX *aux){ ++ xassert(aux != NULL); ++ xfree(aux->rows); ++ xfree(aux->mult); ++ if( aux->mir_cset != NULL ){ ++ xfree(aux->mir_cset); ++ xfree(aux->mir_subst); ++ xfree(aux->mir_vlb_rows); ++ xfree(aux->mir_vub_rows); ++ } ++ xfree(aux); ++ return; ++} ++ ++static void cut_set_aux(IOSCUT *cut, int n, ++ const int rows[], const double coeffs[]){ ++ int i; ++ xassert( cut != NULL ); ++ if( cut->aux != NULL ) { ++ ios_delete_aux(cut-> aux); ++ } ++ ++ cut->aux = ios_create_aux(n, rows, coeffs); ++ xassert( cut->aux->nrows == n ); ++} ++ ++static void cut_set_aux_mir(IOSAUX *aux, double delta, int m, int n, ++ const char cset[], const char subst[], ++ const int vlbrs[], const int vubrs[]){ ++ int i; ++ xassert( aux != NULL ); ++ if ( aux->mir_cset != NULL ) ++ { xfree(aux->mir_cset); ++ xfree(aux->mir_subst); ++ xfree(aux->mir_vlb_rows); ++ xfree(aux->mir_vub_rows); ++ } ++ ++ aux->mir_cset = xcalloc(1+n+m, sizeof(char)); ++ aux->mir_subst = xcalloc(1+n+m, sizeof(char)); ++ aux->mir_vlb_rows = xcalloc(1+n+m, sizeof(int)); ++ aux->mir_vub_rows = xcalloc(1+n+m, sizeof(int)); ++ ++ for ( i = 1; i <= n+m; i++) ++ { aux->mir_cset[i] = cset[i]; ++ aux->mir_subst[i] = subst[i]; ++ aux->mir_vlb_rows[i] = vlbrs[i]; ++ aux->mir_vub_rows[i] = vubrs[i]; ++ } ++ ++ aux->mir_delta = delta; ++} ++ ++void ios_cut_set_aux_mir(glp_tree *T, int ord, double delta, ++ const char cset[], const char subst[], ++ const int vlbrs[], const int vubrs[]){ ++ int m, n; ++ IOSCUT *cut; ++ glp_prob *mip; ++ mip = T->mip; ++ m = mip->m; ++ n = mip->n; ++ cut = ios_find_row(T->local, ord); ++ xassert(cut != NULL); ++ cut_set_aux_mir(cut->aux, delta, m, n, cset, subst, vlbrs, vubrs); ++} ++ ++void ios_cut_set_single_aux(glp_tree *T, int ord, int j){ ++ IOSCUT *cut; ++ cut = ios_find_row(T->local, ord); ++ xassert(cut != NULL); ++ ++ /* set up arrays */ ++ int ind[1+1]; ++ double coeffs[1+1]; ++ ind[1] = j; ++ coeffs[1] = +1.0; ++ ++ /* call general procedure */ ++ cut_set_aux(cut, 1, ind, coeffs); ++} ++ ++void ios_cut_set_aux(glp_tree *T, int ord, int n, ++ const int rows[], const double coeffs[]){ ++ IOSCUT *cut; ++ cut = ios_find_row(T->local, ord); ++ xassert(cut != NULL); ++ cut_set_aux(cut, n, rows, coeffs); ++} ++ ++int glp_ios_cut_get_aux_nrows(glp_tree *tree, int ord){ ++ IOSCUT *cut; ++ IOSAUX *aux; ++ if (tree->reason != GLP_ICUTADDED){ ++ xerror("glp_ios_cut_get_aux_nrows: not called during cut added.\n"); ++ } ++ cut = ios_find_row(tree->local, ord); ++ if ( cut == NULL ){ ++ xerror("glp_ios_cut_get_aux_nrows: not called on a valid cut.\n"); ++ } ++ aux = cut->aux; ++ return (aux == NULL) ? 0 : aux->nrows; ++} ++ ++void glp_ios_cut_get_aux_rows(glp_tree *tree, int ord, ++ int rows[], double coeffs[]){ ++ IOSCUT *cut; ++ IOSAUX *aux; ++ int j, nrows; ++ if (tree->reason != GLP_ICUTADDED){ ++ xerror("glp_ios_cut_get_aux_rows: not called during cut added.\n"); ++ } ++ cut = ios_find_row(tree->local, ord); ++ if ( cut == NULL ){ ++ xerror("glp_ios_cut_get_aux_rows: not called on a valid cut.\n"); ++ } ++ aux = cut->aux; ++ if( aux != NULL ){ ++ nrows = aux->nrows; ++ for ( j = 1; j <= nrows; j++ ) ++ { if ( rows != NULL ) { rows[j] = aux->rows[j]; } ++ if ( coeffs != NULL ) { coeffs[j] = aux->mult[j]; } ++ } ++ } ++ return; ++} ++ ++ ++void glp_ios_cut_get_mir_subst(glp_tree *tree, int ord, char subst[]); ++/* gets mir cut substition information. */ ++void glp_ios_cut_get_mir_virtual_rows(glp_tree *tree, int ord, ++ int vlb[], int vub[]); ++/* gets mir cut virtual bounds rows. */ ++ ++void glp_ios_cut_get_mir_cset(glp_tree *tree, int ord, char *cset){ ++ glp_prob *mip; ++ IOSCUT *cut; ++ IOSAUX *aux; ++ int j, n, m; ++ if ( tree->reason != GLP_ICUTADDED ){ ++ xerror("glp_ios_cut_get_aux_mir: not called during cut added.\n"); ++ } ++ cut = ios_find_row(tree->local, ord); ++ if ( cut == NULL ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a cut.\n"); ++ } ++ if ( cut->klass != GLP_RF_MIR ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a mir cut.\n"); ++ } ++ aux = cut->aux; ++ mip = tree->mip; ++ m = mip->m; ++ n = mip->n; ++ ++ if( cset != NULL ){ ++ for ( j=1; j <= n+m; j++ ){ ++ cset[j] = (aux->mir_cset == NULL) ? 0 : aux->mir_cset[j]; ++ } ++ } ++} ++void glp_ios_cut_get_mir_subst(glp_tree *tree, int ord, char *subst){ ++ glp_prob *mip; ++ IOSCUT *cut; ++ IOSAUX *aux; ++ int j, n, m; ++ if ( tree->reason != GLP_ICUTADDED ){ ++ xerror("glp_ios_cut_get_aux_mir: not called during cut added.\n"); ++ } ++ cut = ios_find_row(tree->local, ord); ++ if ( cut == NULL ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a cut.\n"); ++ } ++ if ( cut->klass != GLP_RF_MIR ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a mir cut.\n"); ++ } ++ aux = cut->aux; ++ mip = tree->mip; ++ m = mip->m; ++ n = mip->n; ++ ++ if( subst != NULL ){ ++ for ( j=1; j <= n+m; j++ ){ ++ subst[j] = (aux->mir_subst == NULL) ? 0 : aux->mir_subst[j]; ++ } ++ } ++} ++void glp_ios_cut_get_mir_virtual_rows(glp_tree *tree, int ord, int vlb_rows[], int vub_rows[]){ ++ glp_prob *mip; ++ IOSCUT *cut; ++ IOSAUX *aux; ++ int j, n, m; ++ if ( tree->reason != GLP_ICUTADDED ){ ++ xerror("glp_ios_cut_get_aux_mir: not called during cut added.\n"); ++ } ++ cut = ios_find_row(tree->local, ord); ++ if ( cut == NULL ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a cut.\n"); ++ } ++ if ( cut->klass != GLP_RF_MIR ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a mir cut.\n"); ++ } ++ aux = cut->aux; ++ mip = tree->mip; ++ m = mip->m; ++ n = mip->n; ++ ++ for ( j=1; j <= n+m; j++ ){ ++ vlb_rows[j] = (aux->mir_vlb_rows == NULL) ? 0 : aux->mir_vlb_rows[j]; ++ vub_rows[j] = (aux->mir_vub_rows == NULL) ? 0 : aux->mir_vub_rows[j]; ++ } ++} ++double glp_ios_cut_get_mir_delta(glp_tree *tree, int ord){ ++ glp_prob *mip; ++ IOSCUT *cut; ++ IOSAUX *aux; ++ int j, n, m; ++ if ( tree->reason != GLP_ICUTADDED ){ ++ xerror("glp_ios_cut_get_aux_mir: not called during cut added.\n"); ++ } ++ cut = ios_find_row(tree->local, ord); ++ if ( cut == NULL ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a cut.\n"); ++ } ++ if ( cut->klass != GLP_RF_MIR ){ ++ xerror("glp_ios_cut_get_aux_mir: not called on a mir cut.\n"); ++ } ++ aux = cut->aux; ++ return aux->mir_delta; ++} ++ ++ ++void ios_cut_set_selected(IOSCUT *cut, int sel){ ++#ifdef CUT_DEBUG ++ static int i = 0; ++ ++i; ++ printf("ios_cut_set_selected: %d %d %p\n", i, sel, cut); ++#endif ++ ++ IOSAUX *aux; ++ aux = cut->aux; ++ if ( aux != NULL ){ ++ aux->selected = sel; ++ } ++} ++ ++int glp_ios_selected_cuts(glp_tree *tree, int ords[], int sel[]){ ++ int len, j, N, s; ++ IOSPOOL* pool; ++ IOSCUT* cut; ++ IOSAUX* aux; ++ if ( tree == NULL ){ ++ xerror("glp_ios_selected_cuts: not called with a valid tree.\n"); ++ } ++ if ( tree->reason != GLP_ICUTSELECT ){ ++ xerror("glp_ios_selected_cuts: not called during cut selected.\n"); ++ } ++ ++ pool = tree->local; ++ if ( pool == NULL ){ ++ xerror("glp_ios_selected_cuts: called on a malformed tree.\n"); ++ } ++ ++ for (len = 0, j = 1, cut = pool->head; cut != NULL; cut = cut->next, j++) ++ { aux = cut->aux; ++#ifdef CUT_DEBUG ++ printf("glp_ios_selected_cuts: %d %p\n", j, cut); ++#endif ++ if ( aux != NULL ) ++ { s = aux->selected; ++ if ( s >= 0 ) ++ { len++; ++ if (ords != NULL) { ords[len] = j; } ++ if (sel != NULL) { sel[len] = s; } ++ } ++ } ++ } ++ return len; ++} ++ ++int glp_ios_branch_log(glp_tree *tree, double *br_val, int* parent, int* dn, int* up){ ++ IOSNPD *node; ++ int br_result, br_var; ++ int p, d, u; ++ double v; ++ glp_prob *mip; ++ if ( tree == NULL ){ ++ xerror("glp_ios_branch_log: not called with a valid tree \n"); ++ } ++ if ( tree->reason != GLP_LI_BRANCH ){ ++ xerror("glp_ios_branch_log: not called during GLP_LI_BRANCH \n"); ++ } ++ mip = tree->mip; ++ if ( mip == NULL ){ ++ xerror("glp_ios_branch_log: not called with a valid tree\n"); ++ } ++ br_result = tree->br_result; ++ br_var = tree->br_var; ++ switch(br_result){ ++ case 0: ++ p = tree->br_node; ++ node = tree->slot[p].node; ++ break; ++ case 1: ++ case 2: ++ node = tree->curr; ++ p = node->p; ++ break; ++ default: ++ xerror("glp_ios_branch_log: br_result is not properly set \n"); ++ } ++ if( node == NULL ){ ++ xerror("glp_ios_branch_log: not called with a valid tree \n"); ++ } ++ switch(br_result){ ++ case 0: ++ v = node->br_val; ++ d = tree->dn_child; ++ u = tree->up_child; ++ break; ++ case 1: ++ v = mip->col[br_var]->prim; ++ if(tree->br_to_up){ ++ d = -1; ++ u = p; ++ }else{ ++ d = p; ++ u = -1; ++ } ++ break; ++ case 2: ++ v = mip->col[br_var]->prim; ++ d = -1; ++ u = -1; ++ break; ++ default: ++ xerror("glp_ios_branch_log: not called with a valid tree \n"); ++ } ++ ++ if(br_val != NULL){ *br_val = v; } ++ if(parent != NULL){ *parent = p; } ++ if(dn != NULL){ *dn = d; } ++ if(up != NULL){ *up = u; } ++ ++ return br_var; ++} ++ ++int glp_ios_node_ord(glp_tree *tree, int p){ ++ IOSNPD *node; ++ if ( tree == NULL ){ ++ xerror("glp_ios_node_ord: not called with a valid tree.\n"); ++ } ++ if (!(1 <= p && p <= tree->nslots)){ ++ xerror("glp_ios_node_ord: not called with a valid p.\n"); ++ } ++ node = tree->slot[p].node; ++ return node->ord; ++} ++ ++void ios_cb_rows_deleted(glp_tree *T, int n, const int* rows){ ++ if (T->parm->cb_func != NULL) ++ { ++ xassert(T->reason == 0); ++ xassert(T->deleting_rows == NULL); ++ xassert(T->num_deleting_rows == 0); ++ T->num_deleting_rows = n; ++ T->deleting_rows = rows; ++ ++ T->reason = GLP_LI_DELROW; ++ T->parm->cb_func(T, T->parm->cb_info); ++ T->reason = 0; ++ T->num_deleting_rows = 0; ++ T->deleting_rows = NULL; ++ } ++} ++ ++int glp_ios_rows_deleted(glp_tree *tree, int* rows){ ++ if ( tree == NULL ){ ++ xerror("glp_ios_rows_deleted: not called with a valid tree.\n"); ++ } ++ if ( tree->reason != GLP_LI_DELROW ){ ++ xerror("glp_ios_rows_deleted: not called with a valid reason.\n"); ++ } ++ ++ int j; ++ if(rows != NULL){ ++ for(j=1; j <= tree->num_deleting_rows; j++){ ++ rows[j] = tree->deleting_rows[j]; ++ } ++ } ++ return tree->num_deleting_rows; ++} +diff --git a/src/glpapi06.c b/src/glpapi06.c +index 743d6be..05d0498 100644 +--- a/src/glpapi06.c ++++ b/src/glpapi06.c +@@ -488,6 +488,7 @@ void glp_init_smcp(glp_smcp *parm) + parm->out_frq = 500; + parm->out_dly = 0; + parm->presolve = GLP_OFF; ++ parm->stability_lmt = 200; + return; + } + +diff --git a/src/glpapi13.c b/src/glpapi13.c +index 926dda4..55adf44 100644 +--- a/src/glpapi13.c ++++ b/src/glpapi13.c +@@ -453,8 +453,14 @@ void glp_ios_row_attr(glp_tree *tree, int i, glp_attr *attr) + + int glp_ios_pool_size(glp_tree *tree) + { /* determine current size of the cut pool */ +- if (tree->reason != GLP_ICUTGEN) +- xerror("glp_ios_pool_size: operation not allowed\n"); ++ switch(tree->reason) ++ { case GLP_ICUTGEN: ++ case GLP_ICUTADDED: ++ case GLP_ICUTSELECT: ++ break; ++ default: ++ xerror("glp_ios_pool_size: operation not allowed\n"); ++ } + xassert(tree->local != NULL); + return tree->local->size; + } +diff --git a/src/glpios.h b/src/glpios.h +index 9e2d6b2..3b31901 100644 +--- a/src/glpios.h ++++ b/src/glpios.h +@@ -36,6 +36,9 @@ typedef struct IOSAIJ IOSAIJ; + typedef struct IOSPOOL IOSPOOL; + typedef struct IOSCUT IOSCUT; + ++ ++typedef struct IOSAUX IOSAUX; ++ + struct glp_tree + { /* branch-and-bound tree */ + int magic; +@@ -217,6 +220,19 @@ struct glp_tree + GLP_NO_BRNCH - use general selection technique */ + int child; + /* subproblem reference number corresponding to br_sel */ ++ ++ /* start of cut log extras */ ++ int br_result; ++ int br_to_up; ++ int br_node; ++ /* subproblem reference number for the just branched node.*/ ++ int dn_child; ++ /* subproblem reference number for the just created down node */ ++ int up_child; ++ /* subproblem reference number for the just created up node */ ++ ++ const int* deleting_rows; ++ int num_deleting_rows; + }; + + struct IOSLOT +@@ -229,6 +245,8 @@ struct IOSLOT + + struct IOSNPD + { /* node subproblem descriptor */ ++ int ord; ++ /* this is a unique ordinal for each subproblem */ + int p; + /* subproblem reference number (it is the index to corresponding + slot, i.e. slot[p] points to this descriptor) */ +@@ -393,12 +411,51 @@ struct IOSCUT + GLP_FX: sum a[j] * x[j] = b */ + double rhs; + /* cut right-hand side */ ++ ++ IOSAUX *aux; ++ /* cut auxillary source information */ ++ + IOSCUT *prev; + /* pointer to previous cut */ + IOSCUT *next; + /* pointer to next cut */ + }; + ++struct IOSAUX ++{ ++ /* aux (auxillary source information for each cut) ++ * Each cut operates on a sum of rows ++ * Each cut operates on a row that can be described using ++ * a current row or a previous cut. ++ * To generalize, we assume each row is a sum of two rows: ++ * row[r]* r_mult + pool[c] * c_mult ++ */ ++ int nrows; ++ int *rows; ++ double *mult; ++ ++ int selected; ++ /* when < 0 this has not yet been turned into a row ++ when >=0 this is the id of the row added. */ ++ ++ double mir_delta; ++ /* the delta selected by a mir cut */ ++ ++ char *mir_cset; ++ /* complimented set */ ++ /* if this is NULL, it is implicitly all 0s */ ++ ++ char *mir_subst; ++ /* the substition vectors */ ++ ++ int *mir_vlb_rows; ++ /* the substition vectors */ ++ ++ int *mir_vub_rows; ++ /* the substition vectors */ ++}; ++ ++ + #define ios_create_tree _glp_ios_create_tree + glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm); + /* create branch-and-bound tree */ +@@ -615,6 +672,31 @@ int ios_choose_node(glp_tree *T); + int ios_choose_var(glp_tree *T, int *next); + /* select variable to branch on */ + ++/* functions added to retrieve information */ ++IOSAUX *ios_create_aux(int n, const int rows[], const double coeffs[]); ++/* creates an aux with n rows */ ++ ++void ios_delete_aux(IOSAUX *aux); ++/* deletes an aux */ ++ ++void ios_cut_set_single_aux(glp_tree *T, int ord, int j); ++/* sets the aux of row ord to be a single row j */ ++ ++ ++void ios_cut_set_aux(glp_tree *T, int ord, int n, ++ const int rows[], const double coeffs[]); ++/* sets an arbitrary aux sum */ ++ ++void ios_cut_set_aux_mir(glp_tree *T, int ord, double delta, ++ const char cset[], const char subst[], ++ const int vlbrs[], const int vubrs[]); ++/* sets the extra mir information */ ++ ++void ios_cut_set_selected(IOSCUT *cut, int i); ++/* the cut has been added as row i */ ++ ++void ios_cb_rows_deleted(glp_tree *T, int n, const int* rows); ++ + #endif + + /* eof */ +diff --git a/src/glpios01.c b/src/glpios01.c +index 70798fd..d9e5703 100644 +--- a/src/glpios01.c ++++ b/src/glpios01.c +@@ -159,6 +159,16 @@ glp_tree *ios_create_tree(glp_prob *mip, const glp_iocp *parm) + tree->stop = 0; + /* create the root subproblem, which initially is identical to + the original MIP */ ++ ++ tree->br_result = 0; ++ tree->br_to_up = 0; ++ tree->br_node = 0; ++ tree->dn_child = 0; ++ tree->up_child = 0; ++ ++ tree->deleting_rows = NULL; ++ tree->num_deleting_rows = 0; ++ + new_node(tree, NULL); + return tree; + } +@@ -278,6 +288,7 @@ void ios_revive_node(glp_tree *tree, int p) + double *val; + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); ++ /* maintains the row order during revival */ + for (r = node->r_ptr; r != NULL; r = r->next) + { i = glp_add_rows(mip, 1); + glp_set_row_name(mip, i, r->name); +@@ -457,6 +468,13 @@ void ios_freeze_node(glp_tree *tree) + double *val; + ind = xcalloc(1+n, sizeof(int)); + val = xcalloc(1+n, sizeof(double)); ++ /* Added rows are stored in the same order! ++ * This is done by 2 reversals. ++ * - Going from i = m down pred_m. ++ * - Storing the list by a stack "push" ++ * node->r_ptr = r; ++ * r->next = node->r_ptr; ++ */ + for (i = m; i > pred_m; i--) + { GLPROW *row = mip->row[i]; + IOSROW *r; +@@ -501,6 +519,8 @@ void ios_freeze_node(glp_tree *tree) + xassert(nrs > 0); + num = xcalloc(1+nrs, sizeof(int)); + for (i = 1; i <= nrs; i++) num[i] = root_m + i; ++ /* To not call ios_cb_rows_deleted here. ++ These rows have been saved earlier. */ + glp_del_rows(mip, nrs, num); + xfree(num); + } +@@ -636,6 +656,9 @@ static IOSNPD *new_node(glp_tree *tree, IOSNPD *parent) + tree->a_cnt++; + tree->n_cnt++; + tree->t_cnt++; ++ ++ node->ord = tree->t_cnt; ++ + /* increase the number of child subproblems */ + if (parent == NULL) + xassert(p == 1); +@@ -818,6 +841,8 @@ void ios_delete_tree(glp_tree *tree) + xassert(nrs > 0); + num = xcalloc(1+nrs, sizeof(int)); + for (i = 1; i <= nrs; i++) num[i] = tree->orig_m + i; ++ /* Do not call ios_cb_rows_deleted here. ++ This does not help log information. */ + glp_del_rows(mip, nrs, num); + xfree(num); + } +@@ -1417,6 +1442,7 @@ int ios_add_row(glp_tree *tree, IOSPOOL *pool, + cut->rhs = rhs; + cut->prev = pool->tail; + cut->next = NULL; ++ cut->aux = NULL; + if (cut->prev == NULL) + pool->head = cut; + else +@@ -1517,6 +1543,11 @@ void ios_del_row(glp_tree *tree, IOSPOOL *pool, int i) + cut->ptr = aij->next; + dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); + } ++ ++ if (cut->aux != NULL){ ++ ios_delete_aux(cut->aux); ++ } ++ + dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); + pool->size--; + return; +@@ -1535,6 +1566,10 @@ void ios_clear_pool(glp_tree *tree, IOSPOOL *pool) + cut->ptr = aij->next; + dmp_free_atom(tree->pool, aij, sizeof(IOSAIJ)); + } ++ ++ if (cut->aux != NULL){ ++ ios_delete_aux(cut->aux); ++ } + dmp_free_atom(tree->pool, cut, sizeof(IOSCUT)); + } + pool->size = 0; +diff --git a/src/glpios03.c b/src/glpios03.c +index 80d701b..03e9208 100644 +--- a/src/glpios03.c ++++ b/src/glpios03.c +@@ -358,12 +358,12 @@ static void fix_by_red_cost(glp_tree *T) + * 2 - both branches are hopeless and have been pruned; new subproblem + * selection is needed to continue the search. */ + +-static int branch_on(glp_tree *T, int j, int next) ++static int branch_on(glp_tree *T, int j, int next, int clone[], int* to_up) + { glp_prob *mip = T->mip; + IOSNPD *node; + int m = mip->m; + int n = mip->n; +- int type, dn_type, up_type, dn_bad, up_bad, p, ret, clone[1+2]; ++ int type, dn_type, up_type, dn_bad, up_bad, p, ret; + double lb, ub, beta, new_ub, new_lb, dn_lp, up_lp, dn_bnd, up_bnd; + /* determine bounds and value of x[j] in optimal solution to LP + relaxation of the current subproblem */ +@@ -431,6 +431,7 @@ static int branch_on(glp_tree *T, int j, int next) + else + xassert(mip != mip); + ret = 1; ++ *to_up = 0; /* up is bad. Do not go to up. */ + goto done; + } + else if (dn_bad) +@@ -449,6 +450,7 @@ static int branch_on(glp_tree *T, int j, int next) + else + xassert(mip != mip); + ret = 1; ++ *to_up = 1; /* down is bad. Go to up. */ + goto done; + } + /* both down- and up-branches seem to be hopeful */ +@@ -702,7 +704,8 @@ static void remove_cuts(glp_tree *T) + } + } + if (cnt > 0) +- { glp_del_rows(T->mip, cnt, num); ++ { ios_cb_rows_deleted(T, cnt, num); ++ glp_del_rows(T->mip, cnt, num); + #if 0 + xprintf("%d inactive cut(s) removed\n", cnt); + #endif +@@ -784,6 +787,7 @@ static void display_cut_info(glp_tree *T) + + int ios_driver(glp_tree *T) + { int p, curr_p, p_stat, d_stat, ret; ++ int branch_clones[1 + 2]; + #if 1 /* carry out to glp_tree */ + int pred_p = 0; + /* if the current subproblem has been just created due to +@@ -1010,6 +1014,12 @@ more: /* minor loop starts here */ + if (T->parm->msg_lev >= GLP_MSG_DBG) + xprintf("LP relaxation has no feasible solution\n"); + /* prune the branch */ ++ if (T->parm->cb_func != NULL) ++ { xassert(T->reason == 0); ++ T->reason = GLP_LI_CLOSE; ++ T->parm->cb_func(T, T->parm->cb_info); ++ T->reason = 0; ++ } + goto fath; + } + else +@@ -1219,6 +1229,14 @@ more: /* minor loop starts here */ + ios_process_cuts(T); + T->reason = 0; + } ++ /* if the local cut pool is not empty and the callback func is there, ++ this gives the callback the chance to see what was selected. */ ++ if (T->parm->cb_func != NULL && T->local->size > 0) ++ { xassert(T->reason == 0); ++ T->reason = GLP_ICUTSELECT; ++ T->parm->cb_func(T, T->parm->cb_info); ++ T->reason = 0; ++ } + /* clear the local cut pool */ + ios_clear_pool(T, T->local); + /* perform re-optimization, if necessary */ +@@ -1255,7 +1273,34 @@ more: /* minor loop starts here */ + T->br_var = ios_choose_var(T, &T->br_sel); + /* perform actual branching */ + curr_p = T->curr->p; +- ret = branch_on(T, T->br_var, T->br_sel); ++ xassert(T->br_to_up == 0); ++ ret = branch_on(T, T->br_var, T->br_sel, branch_clones, &T->br_to_up); ++ if (T->parm->cb_func != NULL) ++ { xassert(T->reason == 0); ++ xassert(T->br_node == 0); ++ xassert(T->dn_child == 0); ++ xassert(T->up_child == 0); ++ // record a branch here ++ T->reason = GLP_LI_BRANCH; ++ // at this point T->br_var is the branching variable ++ T->br_node = curr_p; ++ T->br_result = ret; ++ if(ret == 0){ ++ T->dn_child = branch_clones[1]; ++ T->up_child = branch_clones[2]; ++ } ++ T->parm->cb_func(T, T->parm->cb_info); ++ T->reason = 0; ++ T->br_node = 0; ++ T->dn_child = 0; ++ T->up_child = 0; ++ if (T->stop) ++ { ret = GLP_ESTOP; ++ goto done; ++ } ++ } ++ T->br_to_up = 0; ++ + T->br_var = T->br_sel = 0; + if (ret == 0) + { /* both branches have been created */ +diff --git a/src/glpios05.c b/src/glpios05.c +index b9322b9..caf69d7 100644 +--- a/src/glpios05.c ++++ b/src/glpios05.c +@@ -65,6 +65,9 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + double *phi = worka->phi; + int i, k, len, kind, stat; + double lb, ub, alfa, beta, ksi, phi1, rhs; ++ int input_j; ++ input_j = j; ++ + /* compute row of the simplex tableau, which (row) corresponds + to specified basic variable xB[i] = x[m+j]; see (23) */ + len = glp_eval_tab_row(mip, m+j, ind, val); +@@ -73,6 +76,7 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + if it would be computed with formula (27); it is assumed that + beta[i] is fractional enough */ + beta = mip->col[j]->prim; ++ + /* compute cut coefficients phi and right-hand side rho, which + correspond to formula (30); dense format is used, because rows + of the simplex tableau is usually dense */ +@@ -104,6 +108,7 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + xassert(stat != GLP_BS); + /* determine row coefficient ksi[i,j] at xN[j]; see (23) */ + ksi = val[j]; ++ /* printf("%d %4.15f ", k, ksi); */ + /* if ksi[i,j] is too large in the magnitude, do not generate + the cut */ + if (fabs(ksi) > 1e+05) goto fini; +@@ -135,6 +140,7 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + /* y[j] is integer */ + if (fabs(alfa - floor(alfa + 0.5)) < 1e-10) + { /* alfa[i,j] is close to nearest integer; skip it */ ++ /* printf("(skip)"); */ + goto skip; + } + else if (f(alfa) <= f(beta)) +@@ -170,6 +176,13 @@ static void gen_cut(glp_tree *tree, struct worka *worka, int j) + } + skip: ; + } ++ /* printf("\n"); */ ++ /* for (i = 1; i <= m+n; i++) */ ++ /* { */ ++ /* printf("%i %f, ", i, phi[i]); */ ++ /* } */ ++ /* printf("\n"); */ ++ + /* now the cut has the form sum_k phi[k] * x[k] >= rho, where cut + coefficients are stored in the array phi in dense format; + x[1,...,m] are auxiliary variables, x[m+1,...,m+n] are struc- +@@ -218,8 +231,20 @@ skip: ; + ios_add_cut_row(tree, pool, GLP_RF_GMI, len, ind, val, GLP_LO, + rhs); + #else +- glp_ios_add_row(tree, NULL, GLP_RF_GMI, 0, len, ind, val, GLP_LO, +- rhs); ++ int ord; ++ ord = glp_ios_add_row(tree, NULL, GLP_RF_GMI, 0, len, ind, val, ++ GLP_LO, rhs); ++ ios_cut_set_single_aux(tree, ord, input_j); ++ ++ /* printf("ord: % d beta %f\n", ord, beta); */ ++ ++ /** callback for a cut being added to the cut pool */ ++ if (tree->parm->cb_func != NULL) ++ { xassert(tree->reason == GLP_ICUTGEN); ++ tree->reason = GLP_ICUTADDED; ++ tree->parm->cb_func(tree, tree->parm->cb_info); ++ tree->reason = GLP_ICUTGEN; ++ } + #endif + fini: return; + } +diff --git a/src/glpios06.c b/src/glpios06.c +index 2bd0453..4bd3cf5 100644 +--- a/src/glpios06.c ++++ b/src/glpios06.c +@@ -100,6 +100,29 @@ struct MIR + /* sparse vector of cutting plane coefficients, alpha[k] */ + double cut_rhs; + /* right-hand size of the cutting plane, beta */ ++ ++ /*-------------------------------------------------------------*/ ++ /* Extras I've added to reproduce a cut externally */ ++ double cut_delta; ++ /* the delta used for generating the cut */ ++ char *cut_cset; /* char cut_vec[1+m+n]; */ ++ /* cut_cset[k], 1 <= k <= m+n, is set to true if structural ++ variable x[k] was complemented in the cut: ++ 0 - x[k] has been not been complemented ++ non 0 - x[k] has been complemented */ ++ int *vlb_rows; /* int vlb_rows[1+m+n]; */ ++ /* vlb_rows[k], 1 <= k <= m+n, ++ * vlb_rows[k] <= 0 if virtual lower bound has not been set ++ * vlb_rows[k] = r if virtual lower bound was set using the row r ++ */ ++ int *vub_rows; /* int vub_rows[1+m+n]; */ ++ /* vub_rows[k], 1 <= k <= m+n, ++ * vub_rows[k] <= 0 if virtual upper bound has not been set ++ * vub_rows[k] = r if virtual upper bound was set using the row r ++ */ ++ ++ double *agg_coeffs; ++ /* coefficients used to multiply agg_coeffs */ + }; + + /*********************************************************************** +@@ -146,6 +169,7 @@ static void set_row_attrib(glp_tree *tree, struct MIR *mir) + xassert(row != row); + } + mir->vlb[k] = mir->vub[k] = 0; ++ mir->vlb_rows[k] = mir->vub_rows[k] = 0; + } + return; + } +@@ -181,6 +205,7 @@ static void set_col_attrib(glp_tree *tree, struct MIR *mir) + xassert(col != col); + } + mir->vlb[k] = mir->vub[k] = 0; ++ mir->vlb_rows[k] = mir->vub_rows[k] = 0; + } + return; + } +@@ -230,6 +255,7 @@ static void set_var_bounds(glp_tree *tree, struct MIR *mir) + { /* set variable lower bound for x1 */ + mir->lb[k1] = - a2 / a1; + mir->vlb[k1] = k2; ++ mir->vlb_rows[k1] = i; + /* the row should not be used */ + mir->skip[i] = 1; + } +@@ -240,6 +266,7 @@ static void set_var_bounds(glp_tree *tree, struct MIR *mir) + { /* set variable upper bound for x1 */ + mir->ub[k1] = - a2 / a1; + mir->vub[k1] = k2; ++ mir->vub_rows[k1] = i; + /* the row should not be used */ + mir->skip[i] = 1; + } +@@ -313,6 +340,13 @@ void *ios_mir_init(glp_tree *tree) + mir->subst = xcalloc(1+m+n, sizeof(char)); + mir->mod_vec = ios_create_vec(m+n); + mir->cut_vec = ios_create_vec(m+n); ++ ++ /* added */ ++ mir->cut_cset = xcalloc(1+m+n, sizeof(char)); ++ mir->vlb_rows = xcalloc(1+m+n, sizeof(int)); ++ mir->vub_rows = xcalloc(1+m+n, sizeof(int)); ++ mir->agg_coeffs = xcalloc(1+MAXAGGR, sizeof(double)); ++ + /* set global row attributes */ + set_row_attrib(tree, mir); + /* set global column attributes */ +@@ -405,6 +439,9 @@ static void initial_agg_row(glp_tree *tree, struct MIR *mir, int i) + mir->skip[i] = 2; + mir->agg_cnt = 1; + mir->agg_row[1] = i; ++ ++ mir->agg_coeffs[1] = +1.0; ++ + /* use x[i] - sum a[i,j] * x[m+j] = 0, where x[i] is auxiliary + variable of row i, x[m+j] are structural variables */ + ios_clear_vec(mir->agg_vec); +@@ -784,19 +821,19 @@ static int cmir_cmp(const void *p1, const void *p2) + + static double cmir_sep(const int n, const double a[], const double b, + const double u[], const double x[], const double s, +- double alpha[], double *beta, double *gamma) ++ double alpha[], double *beta, double *gamma, ++ double* delta, char cset[]) + { int fail, j, k, nv, v; +- double delta, eps, d_try[1+3], r, r_best; +- char *cset; ++ double eps, d_try[1+3], r, r_best; + struct vset *vset; + /* allocate working arrays */ +- cset = xcalloc(1+n, sizeof(char)); ++ //cset = xcalloc(1+n, sizeof(char)); + vset = xcalloc(1+n, sizeof(struct vset)); + /* choose initial C */ + for (j = 1; j <= n; j++) + cset[j] = (char)(x[j] >= 0.5 * u[j]); + /* choose initial delta */ +- r_best = delta = 0.0; ++ r_best = (*delta) = 0.0; + for (j = 1; j <= n; j++) + { xassert(a[j] != 0.0); + /* if x[j] is close to its bounds, skip it */ +@@ -809,16 +846,16 @@ static double cmir_sep(const int n, const double a[], const double b, + /* compute violation */ + r = - (*beta) - (*gamma) * s; + for (k = 1; k <= n; k++) r += alpha[k] * x[k]; +- if (r_best < r) r_best = r, delta = fabs(a[j]); ++ if (r_best < r) r_best = r, (*delta) = fabs(a[j]); + } + if (r_best < 0.001) r_best = 0.0; + if (r_best == 0.0) goto done; +- xassert(delta > 0.0); ++ xassert((*delta) > 0.0); + /* try to increase violation by dividing delta by 2, 4, and 8, + respectively */ +- d_try[1] = delta / 2.0; +- d_try[2] = delta / 4.0; +- d_try[3] = delta / 8.0; ++ d_try[1] = (*delta) / 2.0; ++ d_try[2] = (*delta) / 4.0; ++ d_try[3] = (*delta) / 8.0; + for (j = 1; j <= 3; j++) + { /* construct c-MIR inequality */ + fail = cmir_ineq(n, a, b, u, cset, d_try[j], alpha, beta, +@@ -827,7 +864,7 @@ static double cmir_sep(const int n, const double a[], const double b, + /* compute violation */ + r = - (*beta) - (*gamma) * s; + for (k = 1; k <= n; k++) r += alpha[k] * x[k]; +- if (r_best < r) r_best = r, delta = d_try[j]; ++ if (r_best < r) r_best = r, (*delta) = d_try[j]; + } + /* build subset of variables lying strictly between their bounds + and order it by nondecreasing values of |x[j] - u[j]/2| */ +@@ -849,7 +886,7 @@ static double cmir_sep(const int n, const double a[], const double b, + /* replace x[j] by its complement or vice versa */ + cset[j] = (char)!cset[j]; + /* construct c-MIR inequality */ +- fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); ++ fail = cmir_ineq(n, a, b, u, cset, (*delta), alpha, beta, gamma); + /* restore the variable */ + cset[j] = (char)!cset[j]; + /* do not replace the variable in case of failure */ +@@ -860,10 +897,11 @@ static double cmir_sep(const int n, const double a[], const double b, + if (r_best < r) r_best = r, cset[j] = (char)!cset[j]; + } + /* construct the best c-MIR inequality chosen */ +- fail = cmir_ineq(n, a, b, u, cset, delta, alpha, beta, gamma); ++ fail = cmir_ineq(n, a, b, u, cset, (*delta), alpha, beta, gamma); + xassert(!fail); ++ + done: /* free working arrays */ +- xfree(cset); ++ + xfree(vset); + /* return to the calling routine */ + return r_best; +@@ -874,7 +912,8 @@ static double generate(struct MIR *mir) + int m = mir->m; + int n = mir->n; + int j, k, kk, nint; +- double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma; ++ double s, *u, *x, *alpha, r_best = 0.0, b, beta, gamma, delta; ++ char *cset; + ios_copy_vec(mir->cut_vec, mir->mod_vec); + mir->cut_rhs = mir->mod_rhs; + /* remove small terms, which can appear due to substitution of +@@ -923,6 +962,7 @@ static double generate(struct MIR *mir) + u = xcalloc(1+nint, sizeof(double)); + x = xcalloc(1+nint, sizeof(double)); + alpha = xcalloc(1+nint, sizeof(double)); ++ cset = xcalloc(1+nint, sizeof(char)); + /* determine u and x */ + for (j = 1; j <= nint; j++) + { k = mir->cut_vec->ind[j]; +@@ -936,6 +976,7 @@ static double generate(struct MIR *mir) + x[j] = mir->ub[k] - mir->x[k]; + else + xassert(k != k); ++ if(!(x[j] >= -0.001)) { goto skip; } + xassert(x[j] >= -0.001); + if (x[j] < 0.0) x[j] = 0.0; + } +@@ -965,6 +1006,7 @@ static double generate(struct MIR *mir) + } + else + xassert(k != k); ++ if(!(x >= -0.001)) { goto skip; } + xassert(x >= -0.001); + if (x < 0.0) x = 0.0; + s -= mir->cut_vec->val[j] * x; +@@ -973,7 +1015,7 @@ static double generate(struct MIR *mir) + /* apply heuristic to obtain most violated c-MIR inequality */ + b = mir->cut_rhs; + r_best = cmir_sep(nint, mir->cut_vec->val, b, u, x, s, alpha, +- &beta, &gamma); ++ &beta, &gamma, &delta, cset); + if (r_best == 0.0) goto skip; + xassert(r_best > 0.0); + /* convert to raw cut */ +@@ -988,10 +1030,22 @@ static double generate(struct MIR *mir) + #if _MIR_DEBUG + ios_check_vec(mir->cut_vec); + #endif ++ /* added */ ++ mir->cut_delta = delta; ++ // this is not a great place for resetting the array, ++ // but it should be sufficient ++ for (j = 1; j <= n+m; j++) ++ mir->cut_cset[j] = 0; ++ for (j = 1; j <= nint; j++) ++ { k = mir->cut_vec->ind[j]; ++ xassert(m <= k && k <= m+n); ++ mir->cut_cset[k] = cset[j]; ++ } + skip: /* free working arrays */ + xfree(u); + xfree(x); + xfree(alpha); ++ xfree(cset); + done: return r_best; + } + +@@ -1199,8 +1253,25 @@ static void add_cut(glp_tree *tree, struct MIR *mir) + ios_add_cut_row(tree, pool, GLP_RF_MIR, len, ind, val, GLP_UP, + mir->cut_rhs); + #else +- glp_ios_add_row(tree, NULL, GLP_RF_MIR, 0, len, ind, val, GLP_UP, ++ int ord; ++ ord = glp_ios_add_row(tree, NULL, GLP_RF_MIR, 0, len, ind, val, GLP_UP, + mir->cut_rhs); ++ ios_cut_set_aux(tree, ord, mir->agg_cnt, mir->agg_row, mir->agg_coeffs); ++ ios_cut_set_aux_mir(tree, ord, mir->cut_delta, ++ mir->cut_cset, mir->subst, ++ mir->vlb_rows, mir->vub_rows); ++ ++ /** callback for a cut being added to the cut pool */ ++ /* printf("mir tree parm %p %d\n", tree->parm->cb_func, ord); */ ++ /* printf(" agg_rhs %f\n", mir->agg_rhs); */ ++ /* printf(" mod_rhs %f\n", mir->mod_rhs); */ ++ /* printf(" cut_rhs %f\n", mir->cut_rhs); */ ++ if (tree->parm->cb_func != NULL) ++ { xassert(tree->reason == GLP_ICUTGEN); ++ tree->reason = GLP_ICUTADDED; ++ tree->parm->cb_func(tree, tree->parm->cb_info); ++ tree->reason = GLP_ICUTGEN; ++ } + #endif + xfree(ind); + xfree(val); +@@ -1216,6 +1287,7 @@ static int aggregate_row(glp_tree *tree, struct MIR *mir) + IOSVEC *v; + int ii, j, jj, k, kk, kappa = 0, ret = 0; + double d1, d2, d, d_max = 0.0; ++ double guass_coeff; + /* choose appropriate structural variable in the aggregated row + to be substituted */ + for (j = 1; j <= mir->agg_vec->nnz; j++) +@@ -1300,8 +1372,9 @@ static int aggregate_row(glp_tree *tree, struct MIR *mir) + xassert(j != 0); + jj = v->pos[kappa]; + xassert(jj != 0); +- ios_linear_comb(mir->agg_vec, +- - mir->agg_vec->val[j] / v->val[jj], v); ++ guass_coeff = - mir->agg_vec->val[j] / v->val[jj]; ++ mir->agg_coeffs[mir->agg_cnt] = guass_coeff; ++ ios_linear_comb(mir->agg_vec, guass_coeff, v); + ios_delete_vec(v); + ios_set_vj(mir->agg_vec, kappa, 0.0); + #if _MIR_DEBUG +@@ -1440,6 +1513,13 @@ void ios_mir_term(void *gen) + xfree(mir->subst); + ios_delete_vec(mir->mod_vec); + ios_delete_vec(mir->cut_vec); ++ ++ /* added */ ++ xfree(mir->cut_cset); ++ xfree(mir->vlb_rows); ++ xfree(mir->vub_rows); ++ xfree(mir->agg_coeffs); ++ + xfree(mir); + return; + } +diff --git a/src/glpios07.c b/src/glpios07.c +index 0892550..9f742e6 100644 +--- a/src/glpios07.c ++++ b/src/glpios07.c +@@ -543,6 +543,14 @@ void ios_cov_gen(glp_tree *tree) + /* add the cut to the cut pool */ + glp_ios_add_row(tree, NULL, GLP_RF_COV, 0, len, ind, val, + GLP_UP, val[0]); ++ ++ /** callback for a cut being added to the cut pool */ ++ if (tree->parm->cb_func != NULL) ++ { xassert(tree->reason == GLP_ICUTGEN); ++ tree->reason = GLP_ICUTADDED; ++ tree->parm->cb_func(tree, tree->parm->cb_info); ++ tree->reason = GLP_ICUTGEN; ++ } + } + /* free working arrays */ + xfree(ind); +diff --git a/src/glpios08.c b/src/glpios08.c +index 2d2fcd5..3aad808 100644 +--- a/src/glpios08.c ++++ b/src/glpios08.c +@@ -129,6 +129,15 @@ void ios_clq_gen(glp_tree *T, void *G_) + /* add cut inequality to local cut pool */ + glp_ios_add_row(T, NULL, GLP_RF_CLQ, 0, len, ind, val, GLP_UP, + rhs); ++ ++ /** callback for a cut being added to the cut pool */ ++ if (T->parm->cb_func != NULL) ++ { xassert(T->reason == GLP_ICUTGEN); ++ T->reason = GLP_ICUTADDED; ++ T->parm->cb_func(T, T->parm->cb_info); ++ T->reason = GLP_ICUTGEN; ++ } ++ + skip: /* free working arrays */ + tfree(ind); + tfree(val); +diff --git a/src/glpios11.c b/src/glpios11.c +index c40e9a5..82d5698 100644 +--- a/src/glpios11.c ++++ b/src/glpios11.c +@@ -193,6 +193,9 @@ void ios_process_cuts(glp_tree *T) + glp_set_mat_row(T->mip, i, len, ind, val); + xassert(cut->type == GLP_LO || cut->type == GLP_UP); + glp_set_row_bnds(T->mip, i, cut->type, cut->rhs, cut->rhs); ++ ++ /* setting this as selected */ ++ ios_cut_set_selected(cut, i); + } + /* free working arrays */ + xfree(info); +diff --git a/src/glpk.h b/src/glpk.h +index 75c292c..e117782 100644 +--- a/src/glpk.h ++++ b/src/glpk.h +@@ -130,6 +130,7 @@ typedef struct + int out_frq; /* spx.out_frq */ + int out_dly; /* spx.out_dly (milliseconds) */ + int presolve; /* enable/disable using LP presolver */ ++ int stability_lmt; /* maximum number of check stability failures before stopping */ + double foo_bar[36]; /* (reserved) */ + } glp_smcp; + +@@ -229,6 +230,11 @@ typedef struct + #define GLP_IBRANCH 0x05 /* request for branching */ + #define GLP_ISELECT 0x06 /* request for subproblem selection */ + #define GLP_IPREPRO 0x07 /* request for preprocessing */ ++#define GLP_ICUTADDED 0x08 /* cut was added to the pool */ ++#define GLP_ICUTSELECT 0x09 /* cuts were selected as rows */ ++#define GLP_LI_BRANCH 0x10 /* a branch was made */ ++#define GLP_LI_CLOSE 0x11 /* an active node was closed */ ++#define GLP_LI_DELROW 0x12 /* an active node was closed */ + + /* branch selection indicator: */ + #define GLP_NO_BRNCH 0 /* select no branch */ +@@ -1057,6 +1063,54 @@ int glp_top_sort(glp_graph *G, int v_num); + int glp_wclique_exact(glp_graph *G, int v_wgt, double *sol, int v_set); + /* find maximum weight clique with exact algorithm */ + ++/*******************************************/ ++/*** CUT LOG ***/ ++/*******************************************/ ++ ++int glp_get_it_cnt(glp_prob *P); ++/* get the iteration count of the current problem */ ++ ++ ++int glp_ios_get_cut(glp_tree *T, int i, int ind[], double val[], int* klass, int* type, double* rhs); ++/* determine reason for calling the callback routine */ ++ ++int glp_ios_cut_get_aux_nrows(glp_tree *tree, int ord); ++/* gets the number of rows used to generate a cut. */ ++ ++ ++void glp_ios_cut_get_aux_rows(glp_tree *tree, int ord, ++ int rows[], double coeffs[]); ++/* gets a cut as an input sequence of rows times coefficients. */ ++ ++ ++void glp_ios_cut_get_mir_cset(glp_tree *tree, int ord, char cset[]); ++/* gets mir cut complement set. */ ++double glp_ios_cut_get_mir_delta(glp_tree *tree, int ord); ++/* gets mir cut delta. */ ++void glp_ios_cut_get_mir_subst(glp_tree *tree, int ord, char subst[]); ++/* gets mir cut substition information. */ ++void glp_ios_cut_get_mir_virtual_rows(glp_tree *tree, int ord, ++ int vlb_rows[], int vub_rows[]); ++/* gets mir cut virtual bounds rows. */ ++ ++int glp_ios_selected_cuts(glp_tree *tree, int ords[], int sel[]); ++/* gets the list of selected cuts. ++ Can only be called when GLP_ICUTSELECT */ ++ ++ ++int glp_ios_branch_log(glp_tree *tree, double *val, int* parent, int* dn, int* up); ++/* can only be called when GLP_LI_BRANCH. ++ * If id is non-null, returns the id of the structural variable branched upon. ++ * If val is non-null, it is set to the value branched upon. ++ * If parent is non-null, it is set to node id of the node branched upon. ++ * If dn is non-null, it is set to node id of the newly created down node. ++ * If up is non-null, it is set to node id of the newly created up node. ++ */ ++ ++int glp_ios_node_ord(glp_tree *tree, int node_p); ++ ++int glp_ios_rows_deleted(glp_tree *tree, int* rows); ++ + #ifdef __cplusplus + } + #endif +diff --git a/src/glpspx01.c b/src/glpspx01.c +index 5c17114..caaab27 100644 +--- a/src/glpspx01.c ++++ b/src/glpspx01.c +@@ -238,6 +238,9 @@ struct csa + double *work2; /* double work2[1+m]; */ + double *work3; /* double work3[1+m]; */ + double *work4; /* double work4[1+m]; */ ++ ++ /** Things Tim has added. */ ++ int stability_failures; + }; + + static const double kappa = 0.10; +@@ -400,6 +403,8 @@ static void init_csa(struct csa *csa, glp_prob *lp) + csa->refct = 0; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (j = 1; j <= n; j++) gamma[j] = 1.0; ++ ++ csa->stability_failures = 0; + return; + } + +@@ -2647,6 +2652,11 @@ loop: /* main loop starts here */ + if (check_stab(csa, parm->tol_bnd)) + { /* there are excessive bound violations due to round-off + errors */ ++ csa->stability_failures++; ++ if (csa->stability_failures >= parm->stability_lmt){ ++ ret = GLP_EINSTAB; ++ goto done; ++ } + if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: numerical instability (primal simplex," + " phase %s)\n", csa->phase == 1 ? "I" : "II"); +diff --git a/src/glpspx02.c b/src/glpspx02.c +index 19152a6..de3d677 100644 +--- a/src/glpspx02.c ++++ b/src/glpspx02.c +@@ -288,6 +288,9 @@ struct csa + double *work2; /* double work2[1+m]; */ + double *work3; /* double work3[1+m]; */ + double *work4; /* double work4[1+m]; */ ++ ++ /* count the number of stability failures */ ++ int stability_failures; + }; + + static const double kappa = 0.10; +@@ -501,6 +504,8 @@ static void init_csa(struct csa *csa, glp_prob *lp) + csa->refct = 0; + memset(&refsp[1], 0, (m+n) * sizeof(char)); + for (i = 1; i <= m; i++) gamma[i] = 1.0; ++ ++ csa->stability_failures = 0; + return; + } + +@@ -2741,7 +2746,13 @@ loop: /* main loop starts here */ + /* make sure that the current basic solution remains dual + feasible */ + if (check_stab(csa, parm->tol_dj) != 0) +- { if (parm->msg_lev >= GLP_MSG_ERR) ++ { ++ csa->stability_failures++; ++ if (csa->stability_failures >= parm->stability_lmt){ ++ ret = GLP_EINSTAB; ++ goto done; ++ } ++ if (parm->msg_lev >= GLP_MSG_ERR) + xprintf("Warning: numerical instability (dual simplex, p" + "hase %s)\n", csa->phase == 1 ? "I" : "II"); + #if 1 +@@ -3023,6 +3034,10 @@ loop: /* main loop starts here */ + /* accuracy check based on the pivot element */ + { double piv1 = csa->tcol_vec[csa->p]; /* more accurate */ + double piv2 = csa->trow_vec[csa->q]; /* less accurate */ ++ if(piv1 == 0.0){ ++ ret = GLP_EFAIL; ++ goto done; ++ } + xassert(piv1 != 0.0); + if (fabs(piv1 - piv2) > 1e-8 * (1.0 + fabs(piv1)) || + !(piv1 > 0.0 && piv2 > 0.0 || piv1 < 0.0 && piv2 < 0.0)) +-- +2.26.2 diff --git a/examples/api/python/CMakeLists.txt b/examples/api/python/CMakeLists.txt index e3966fa2d..0da960513 100644 --- a/examples/api/python/CMakeLists.txt +++ b/examples/api/python/CMakeLists.txt @@ -1,9 +1,22 @@ set(EXAMPLES_API_PYTHON + bitvectors_and_arrays + bitvectors + combination + datatypes exceptions + extract + floating_point + helloworld + linear_arith sequences + sets + strings + sygus-fun + sygus-grammar + sygus-inv ) -find_package(PythonInterp REQUIRED) +find_package(PythonInterp ${CVC4_BINDINGS_PYTHON_VERSION} REQUIRED) # Find Python bindings in the corresponding python-*/site-packages directory. # Lookup Python module directory and store path in PYTHON_MODULE_PATH. diff --git a/examples/api/python/exceptions.py b/examples/api/python/exceptions.py index 780f75bf7..27f068011 100644 --- a/examples/api/python/exceptions.py +++ b/examples/api/python/exceptions.py @@ -16,40 +16,40 @@ ## A simple demonstration of catching CVC4 execptions with the legacy Python ## API. -import CVC4 +import pycvc4 +from pycvc4 import kinds import sys def main(): - em = CVC4.ExprManager() - smt = CVC4.SmtEngine(em) + slv = pycvc4.Solver() - smt.setOption("produce-models", CVC4.SExpr("true")) + slv.setOption("produce-models", "true") # Setting an invalid option try: - smt.setOption("non-existing", CVC4.SExpr("true")) + slv.setOption("non-existing", "true") return 1 - except CVC4.Exception as e: - print(e.toString()) + except: + pass # Creating a term with an invalid type try: - integer = em.integerType() - x = em.mkVar("x", integer) - invalidExpr = em.mkExpr(CVC4.AND, x, x) - smt.checkSat(invalidExpr) + integer = slv.getIntegerSort() + x = slv.mkConst("x", integer) + invalidTerm = em.mkTerm(AND, x, x) + slv.checkSat(invalidTerm) return 1 - except CVC4.Exception as e: - print(e.toString()) + except: + pass # Asking for a model after unsat result try: - smt.checkSat(em.mkBoolConst(False)) - smt.getModel() + slv.checkSat(slv.mkBoolean(False)) + slv.getModel() return 1 - except CVC4.Exception as e: - print(e.toString()) + except: + pass return 0 diff --git a/examples/api/python/floating_point.py b/examples/api/python/floating_point.py index c92666c0b..6fb595e34 100755 --- a/examples/api/python/floating_point.py +++ b/examples/api/python/floating_point.py @@ -20,8 +20,15 @@ from pycvc4 import kinds if __name__ == "__main__": slv = pycvc4.Solver() + + if not slv.supportsFloatingPoint(): + # CVC4 must be built with SymFPU to support the theory of + # floating-point numbers + print("CVC4 was not built with floating-point support.") + exit() + slv.setOption("produce-models", "true") - slv.setLogic("FP") + slv.setLogic("QF_FP") # single 32-bit precision fp32 = slv.mkFloatingPointSort(8, 24) diff --git a/examples/api/python/sygus-fun.py b/examples/api/python/sygus-fun.py index 0f53bd343..25090bd8f 100644 --- a/examples/api/python/sygus-fun.py +++ b/examples/api/python/sygus-fun.py @@ -53,7 +53,7 @@ if __name__ == "__main__": leq = slv.mkTerm(kinds.Leq, start, start) # create the grammar object - g = slv.mkSygusGrammar({x, y}, {start, start_bool}) + g = slv.mkSygusGrammar([x, y], [start, start_bool]) # bind each non-terminal to its rules g.addRules(start, {zero, one, x, y, plus, minus, ite}) @@ -61,8 +61,8 @@ if __name__ == "__main__": # declare the functions-to-synthesize. Optionally, provide the grammar # constraints - max = slv.synthFun("max", {x, y}, integer, g) - min = slv.synthFun("min", {x, y}, integer) + max = slv.synthFun("max", [x, y], integer, g) + min = slv.synthFun("min", [x, y], integer) # declare universal variables. varX = slv.mkSygusVar(integer, "x") diff --git a/proofs/signatures/drat.plf b/proofs/signatures/drat.plf index ad3c8ec8d..20795901f 100644 --- a/proofs/signatures/drat.plf +++ b/proofs/signatures/drat.plf @@ -354,7 +354,7 @@ ; Helper for `is_operational_drat_proof` which takes a UP model for the working ; formula. The UP model is important for determining which clause deletions ; actually are executed in operational DRAT. Passing the UP model along -; prevents it from being fully recomputed everytime. +; prevents it from being fully recomputed every time. (program is_operational_drat_proof_h ((f cnf) (up_model clause) (pf DRATProof)) bool (match pf (DRATProofn (cnf_has_bottom f)) diff --git a/proofs/signatures/lrat.plf b/proofs/signatures/lrat.plf index b5d46be43..c10f8d6c8 100644 --- a/proofs/signatures/lrat.plf +++ b/proofs/signatures/lrat.plf @@ -101,7 +101,7 @@ ((pos v2) ff) ((neg v2) (ifequal v1 v2 tt ff)))))) -; Remove **all** occurences of a literal from clause +; Remove **all** occurrences of a literal from clause (program clause_remove_all ((l lit) (c clause)) clause (match c (cln cln) @@ -180,7 +180,7 @@ (CMapc ci c (CMap_remove_many is cs')))))))) ; Given a map of clauses and a literal, return all indices in the map -; corresponsing to clauses that could resolve against that literal. i.e. for x, +; corresponding to clauses that could resolve against that literal. i.e. for x, ; return the indices of all clauses containing x. (program collect_resolution_targets_w_lit ((cs CMap) (l lit)) CIList (match cs @@ -260,14 +260,14 @@ (fail Unit) (ifmarked2 v unit (do (markvar2 v) unit)))))) -; Unmarks the variable within a satified literal to render it neither satified nor falsified +; Unmarks the variable within a satisfied literal to render it neither satisfied nor falsified ; fails if the literal is not already satisfied (program lit_un_mk_sat ((l lit)) Unit (match l ((pos v) (ifmarked1 v (do (markvar1 v) unit) (fail Unit))) ((neg v) (ifmarked2 v (do (markvar2 v) unit) (fail Unit))))) -; Unmarks the variable within a falsified literal to render it neither satified nor falsified +; Unmarks the variable within a falsified literal to render it neither satisfied nor falsified ; fails if the literal is not already falsified (program lit_un_mk_unsat ((l lit)) Unit (match l @@ -348,7 +348,7 @@ ; The return type for verifying that a clause is unit and modifying the global ; assignment to satisfy it (declare MarkResult type) -; The clause is unit, and this is the (previoiusly floating) literal that is now satified. +; The clause is unit, and this is the (previoiusly floating) literal that is now satisfied. (declare MRUnit (! l lit MarkResult)) ; The clause was unsat! (declare MRUnsat MarkResult) @@ -357,7 +357,7 @@ ; The clause had multiple floating literals. (declare MRNotUnit MarkResult) -; Determine wether this clause is sat, unsat, unit, or not unit, and if it is +; Determine whether this clause is sat, unsat, unit, or not unit, and if it is ; unit, it modifies the global assignment to satisfy the clause, and returns ; the literal that was made SAT by the new mark. ; diff --git a/proofs/signatures/th_lira.plf b/proofs/signatures/th_lira.plf index af326bf27..e3f6df112 100644 --- a/proofs/signatures/th_lira.plf +++ b/proofs/signatures/th_lira.plf @@ -128,7 +128,7 @@ (default (fail (term Int))) )) -; This function negates linear interger terms---sums of terms of the form +; This function negates linear integer terms---sums of terms of the form ; recognized by `negate_linear_monomial_int_term`. (program negate_linear_int_term ((t (term Int))) (term Int) (match t @@ -339,7 +339,7 @@ ; the statement that `a` satisfies `b` for all inputs (declare bounded_aff (! a aff (! b bound formula))) -; Sum of two bounds (the bound satisfied by the sum of two functions satifying +; Sum of two bounds (the bound satisfied by the sum of two functions satisfying ; the input bounds) (program bound_add ((b bound) (b2 bound)) bound (match b diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 06bc9917f..692ae09ac 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -118,50 +118,13 @@ libcvc4_add_sources( printer/smt2/smt2_printer.h printer/tptp/tptp_printer.cpp printer/tptp/tptp_printer.h - proof/arith_proof.cpp - proof/arith_proof.h - proof/arith_proof_recorder.cpp - proof/arith_proof_recorder.h - proof/array_proof.cpp - proof/array_proof.h - proof/bitvector_proof.cpp - proof/bitvector_proof.h - proof/clausal_bitvector_proof.cpp - proof/clausal_bitvector_proof.h proof/clause_id.h proof/cnf_proof.cpp proof/cnf_proof.h - proof/dimacs.cpp - proof/dimacs.h - proof/drat/drat_proof.cpp - proof/drat/drat_proof.h - proof/er/er_proof.cpp - proof/er/er_proof.h - proof/lemma_proof.cpp - proof/lemma_proof.h - proof/lfsc_proof_printer.cpp - proof/lfsc_proof_printer.h - proof/lrat/lrat_proof.cpp - proof/lrat/lrat_proof.h - proof/proof.h proof/proof_manager.cpp proof/proof_manager.h - proof/proof_output_channel.cpp - proof/proof_output_channel.h - proof/proof_utils.cpp - proof/proof_utils.h - proof/resolution_bitvector_proof.cpp - proof/resolution_bitvector_proof.h proof/sat_proof.h proof/sat_proof_implementation.h - proof/simplify_boolean_node.cpp - proof/simplify_boolean_node.h - proof/skolemization_manager.cpp - proof/skolemization_manager.h - proof/theory_proof.cpp - proof/theory_proof.h - proof/uf_proof.cpp - proof/uf_proof.h proof/unsat_core.cpp proof/unsat_core.h prop/bvminisat/bvminisat.cpp @@ -247,6 +210,8 @@ libcvc4_add_sources( smt/model_core_builder.h smt/model_blocker.cpp smt/model_blocker.h + smt/node_command.cpp + smt/node_command.h smt/options_manager.cpp smt/options_manager.h smt/quant_elim_solver.cpp @@ -288,6 +253,8 @@ libcvc4_add_sources( theory/arith/approx_simplex.h theory/arith/arith_ite_utils.cpp theory/arith/arith_ite_utils.h + theory/arith/arith_lemma.cpp + theory/arith/arith_lemma.h theory/arith/arith_msum.cpp theory/arith/arith_msum.h theory/arith/arith_rewriter.cpp @@ -324,6 +291,8 @@ libcvc4_add_sources( theory/arith/fc_simplex.h theory/arith/infer_bounds.cpp theory/arith/infer_bounds.h + theory/arith/inference_manager.cpp + theory/arith/inference_manager.h theory/arith/linear_equality.cpp theory/arith/linear_equality.h theory/arith/matrix.cpp @@ -387,10 +356,6 @@ libcvc4_add_sources( theory/arith/type_enumerator.h theory/arrays/array_info.cpp theory/arrays/array_info.h - theory/arrays/array_proof_reconstruction.cpp - theory/arrays/array_proof_reconstruction.h - theory/arrays/static_fact_manager.cpp - theory/arrays/static_fact_manager.h theory/arrays/theory_arrays.cpp theory/arrays/theory_arrays.h theory/arrays/theory_arrays_rewriter.cpp @@ -471,6 +436,8 @@ libcvc4_add_sources( theory/combination_engine.h theory/datatypes/datatypes_rewriter.cpp theory/datatypes/datatypes_rewriter.h + theory/datatypes/inference_manager.cpp + theory/datatypes/inference_manager.h theory/datatypes/sygus_datatype_utils.cpp theory/datatypes/sygus_datatype_utils.h theory/datatypes/sygus_extension.cpp @@ -825,6 +792,8 @@ libcvc4_add_sources( theory/theory_engine_proof_generator.h theory/theory_id.cpp theory/theory_id.h + theory/theory_inference.cpp + theory/theory_inference.h theory/theory_inference_manager.cpp theory/theory_inference_manager.h theory/theory_model.cpp @@ -1098,7 +1067,6 @@ install(FILES util/integer_gmp_imp.h util/maybe.h util/poly_util.h - util/proof.h util/rational_cln_imp.h util/rational_gmp_imp.h util/real_algebraic_number_poly_imp.h diff --git a/src/api/cvc4cpp.cpp b/src/api/cvc4cpp.cpp index 51ecea9f2..5b3384439 100644 --- a/src/api/cvc4cpp.cpp +++ b/src/api/cvc4cpp.cpp @@ -3044,10 +3044,10 @@ Term Solver::mkCharFromStrHelper(const std::string& s) const CVC4_API_CHECK(s.find_first_not_of("0123456789abcdefABCDEF", 0) == std::string::npos && s.size() <= 5 && s.size() > 0) - << "Unexpected string for hexidecimal character " << s; + << "Unexpected string for hexadecimal character " << s; uint32_t val = static_cast<uint32_t>(std::stoul(s, 0, 16)); CVC4_API_CHECK(val < String::num_codes()) - << "Not a valid code point for hexidecimal character " << s; + << "Not a valid code point for hexadecimal character " << s; std::vector<unsigned> cpts; cpts.push_back(val); return mkValHelper<CVC4::String>(CVC4::String(cpts)); @@ -3148,8 +3148,8 @@ Term Solver::mkTermHelper(Kind kind, const std::vector<Term>& children) const } std::vector<Sort> Solver::mkDatatypeSortsInternal( - std::vector<DatatypeDecl>& dtypedecls, - std::set<Sort>& unresolvedSorts) const + const std::vector<DatatypeDecl>& dtypedecls, + const std::set<Sort>& unresolvedSorts) const { NodeManagerScope scope(getNodeManager()); CVC4_API_SOLVER_TRY_CATCH_BEGIN; @@ -3240,6 +3240,14 @@ void Solver::checkMkTerm(Kind kind, uint32_t nchildren) const << " children (the one under construction has " << nchildren << ")"; } +/* Solver Configuration */ +/* -------------------------------------------------------------------------- */ + +bool Solver::supportsFloatingPoint() const +{ + return Configuration::isBuiltWithSymFPU(); +} + /* Sorts Handling */ /* -------------------------------------------------------------------------- */ @@ -3285,9 +3293,11 @@ Sort Solver::getStringSort(void) const CVC4_API_SOLVER_TRY_CATCH_END; } -Sort Solver::getRoundingmodeSort(void) const +Sort Solver::getRoundingModeSort(void) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_CHECK(Configuration::isBuiltWithSymFPU()) + << "Expected CVC4 to be compiled with SymFPU support"; return Sort(this, d_exprMgr->roundingModeType()); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -3323,6 +3333,8 @@ Sort Solver::mkBitVectorSort(uint32_t size) const Sort Solver::mkFloatingPointSort(uint32_t exp, uint32_t sig) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_CHECK(Configuration::isBuiltWithSymFPU()) + << "Expected CVC4 to be compiled with SymFPU support"; CVC4_API_ARG_CHECK_EXPECTED(exp > 0, exp) << "exponent size > 0"; CVC4_API_ARG_CHECK_EXPECTED(sig > 0, sig) << "significand size > 0"; @@ -3355,8 +3367,9 @@ std::vector<Sort> Solver::mkDatatypeSorts( CVC4_API_SOLVER_TRY_CATCH_END; } -std::vector<Sort> Solver::mkDatatypeSorts(std::vector<DatatypeDecl>& dtypedecls, - std::set<Sort>& unresolvedSorts) const +std::vector<Sort> Solver::mkDatatypeSorts( + const std::vector<DatatypeDecl>& dtypedecls, + const std::set<Sort>& unresolvedSorts) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; return mkDatatypeSortsInternal(dtypedecls, unresolvedSorts); @@ -3803,6 +3816,8 @@ Term Solver::mkNegZero(uint32_t exp, uint32_t sig) const Term Solver::mkRoundingMode(RoundingMode rm) const { CVC4_API_SOLVER_TRY_CATCH_BEGIN; + CVC4_API_CHECK(Configuration::isBuiltWithSymFPU()) + << "Expected CVC4 to be compiled with SymFPU support"; return mkValHelper<CVC4::RoundingMode>(s_rmodes.at(rm)); CVC4_API_SOLVER_TRY_CATCH_END; } @@ -5396,7 +5411,7 @@ Term Solver::synthFunHelper(const std::string& symbol, { CVC4_API_CHECK(g->d_ntSyms[0].d_node->getType().toType() == *sort.d_type) << "Invalid Start symbol for Grammar g, Expected Start's sort to be " - << *sort.d_type; + << *sort.d_type << " but found " << g->d_ntSyms[0].d_node->getType(); } Type funType = varTypes.empty() @@ -5506,7 +5521,7 @@ Term Solver::getSynthSolution(Term term) const std::map<CVC4::Node, CVC4::Node> map; CVC4_API_CHECK(d_smtEngine->getSynthSolutions(map)) - << "The solver is not in a state immediately preceeded by a " + << "The solver is not in a state immediately preceded by a " "successful call to checkSynth"; std::map<CVC4::Node, CVC4::Node>::const_iterator it = map.find(*term.d_node); @@ -5535,7 +5550,7 @@ std::vector<Term> Solver::getSynthSolutions( std::map<CVC4::Node, CVC4::Node> map; CVC4_API_CHECK(d_smtEngine->getSynthSolutions(map)) - << "The solver is not in a state immediately preceeded by a " + << "The solver is not in a state immediately preceded by a " "successful call to checkSynth"; std::vector<Term> synthSolution; diff --git a/src/api/cvc4cpp.h b/src/api/cvc4cpp.h index 0c322d7da..acf34abf9 100644 --- a/src/api/cvc4cpp.h +++ b/src/api/cvc4cpp.h @@ -2153,6 +2153,12 @@ class CVC4_PUBLIC Solver Solver& operator=(const Solver&) = delete; /* .................................................................... */ + /* Solver Configuration */ + /* .................................................................... */ + + bool supportsFloatingPoint() const; + + /* .................................................................... */ /* Sorts Handling */ /* .................................................................... */ @@ -2184,7 +2190,7 @@ class CVC4_PUBLIC Solver /** * @return sort RoundingMode */ - Sort getRoundingmodeSort() const; + Sort getRoundingModeSort() const; /** * @return sort String @@ -2248,8 +2254,9 @@ class CVC4_PUBLIC Solver * @param unresolvedSorts the list of unresolved sorts * @return the datatype sorts */ - std::vector<Sort> mkDatatypeSorts(std::vector<DatatypeDecl>& dtypedecls, - std::set<Sort>& unresolvedSorts) const; + std::vector<Sort> mkDatatypeSorts( + const std::vector<DatatypeDecl>& dtypedecls, + const std::set<Sort>& unresolvedSorts) const; /** * Create function sort. @@ -3169,7 +3176,9 @@ class CVC4_PUBLIC Solver Term mkSygusVar(Sort sort, const std::string& symbol = std::string()) const; /** - * Create a Sygus grammar. + * Create a Sygus grammar. The first non-terminal is treated as the starting + * non-terminal, so the order of non-terminals matters. + * * @param boundVars the parameters to corresponding synth-fun/synth-inv * @param ntSymbols the pre-declaration of the non-terminal symbols * @return the grammar @@ -3345,8 +3354,8 @@ class CVC4_PUBLIC Solver * @return the datatype sorts */ std::vector<Sort> mkDatatypeSortsInternal( - std::vector<DatatypeDecl>& dtypedecls, - std::set<Sort>& unresolvedSorts) const; + const std::vector<DatatypeDecl>& dtypedecls, + const std::set<Sort>& unresolvedSorts) const; /** * Synthesize n-ary function following specified syntactic constraints. diff --git a/src/api/python/cvc4.pxd b/src/api/python/cvc4.pxd index 16d64b85e..76dcc5317 100644 --- a/src/api/python/cvc4.pxd +++ b/src/api/python/cvc4.pxd @@ -1,6 +1,7 @@ # import dereference and increment operators from cython.operator cimport dereference as deref, preincrement as inc from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t +from libcpp.set cimport set from libcpp.string cimport string from libcpp.vector cimport vector from libcpp.pair cimport pair @@ -27,6 +28,12 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": Term getConstructorTerm(const string& name) except + size_t getNumConstructors() except + bint isParametric() except + + bint isCodatatype() except + + bint isTuple() except + + bint isRecord() except + + bint isFinite() except + + bint isWellFounded() except + + bint hasNestedRecursion() except + string toString() except + cppclass const_iterator: const_iterator() except + @@ -116,16 +123,19 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": cdef cppclass Solver: Solver(Options*) except + + bint supportsFloatingPoint() except + Sort getBooleanSort() except + Sort getIntegerSort() except + Sort getRealSort() except + Sort getRegExpSort() except + - Sort getRoundingmodeSort() except + + Sort getRoundingModeSort() except + Sort getStringSort() except + Sort mkArraySort(Sort indexSort, Sort elemSort) except + Sort mkBitVectorSort(uint32_t size) except + Sort mkFloatingPointSort(uint32_t exp, uint32_t sig) except + Sort mkDatatypeSort(DatatypeDecl dtypedecl) except + + vector[Sort] mkDatatypeSorts(const vector[DatatypeDecl]& dtypedecls, + const set[Sort]& unresolvedSorts) except + Sort mkFunctionSort(Sort domain, Sort codomain) except + Sort mkFunctionSort(const vector[Sort]& sorts, Sort codomain) except + Sort mkParamSort(const string& symbol) except + @@ -312,6 +322,7 @@ cdef extern from "api/cvc4cpp.h" namespace "CVC4::api": Term() bint operator==(const Term&) except + bint operator!=(const Term&) except + + Term operator[](size_t idx) except + Kind getKind() except + Sort getSort() except + Term substitute(const vector[Term] es, const vector[Term] & reps) except + diff --git a/src/api/python/cvc4.pxi b/src/api/python/cvc4.pxi index a51307d21..8c4bfe5e5 100644 --- a/src/api/python/cvc4.pxi +++ b/src/api/python/cvc4.pxi @@ -3,6 +3,7 @@ import sys from libc.stdint cimport int32_t, int64_t, uint32_t, uint64_t from libcpp.pair cimport pair +from libcpp.set cimport set from libcpp.string cimport string from libcpp.vector cimport vector @@ -21,7 +22,8 @@ from cvc4 cimport Grammar as c_Grammar from cvc4 cimport Sort as c_Sort from cvc4 cimport SortHashFunction as c_SortHashFunction from cvc4 cimport ROUND_NEAREST_TIES_TO_EVEN, ROUND_TOWARD_POSITIVE -from cvc4 cimport ROUND_TOWARD_ZERO, ROUND_NEAREST_TIES_TO_AWAY +from cvc4 cimport ROUND_TOWARD_NEGATIVE, ROUND_TOWARD_ZERO +from cvc4 cimport ROUND_NEAREST_TIES_TO_AWAY from cvc4 cimport Term as c_Term from cvc4 cimport TermHashFunction as c_TermHashFunction @@ -88,7 +90,7 @@ cdef class Datatype: if isinstance(index, int) and index >= 0: dc.cdc = self.cd[(<int?> index)] elif isinstance(index, str): - dc.cdc = self.cd[(<const string &> name.encode())] + dc.cdc = self.cd[(<const string &> index.encode())] else: raise ValueError("Expecting a non-negative integer or string") return dc @@ -109,6 +111,24 @@ cdef class Datatype: def isParametric(self): return self.cd.isParametric() + def isCodatatype(self): + return self.cd.isCodatatype() + + def isTuple(self): + return self.cd.isTuple() + + def isRecord(self): + return self.cd.isRecord() + + def isFinite(self): + return self.cd.isFinite() + + def isWellFounded(self): + return self.cd.isWellFounded() + + def hasNestedRecursion(self): + return self.cd.hasNestedRecursion() + def __str__(self): return self.cd.toString().decode() @@ -395,6 +415,9 @@ cdef class Solver: def __dealloc__(self): del self.csolver + def supportsFloatingPoint(self): + return self.csolver.supportsFloatingPoint() + def getBooleanSort(self): cdef Sort sort = Sort(self) sort.csort = self.csolver.getBooleanSort() @@ -415,9 +438,9 @@ cdef class Solver: sort.csort = self.csolver.getRegExpSort() return sort - def getRoundingmodeSort(self): + def getRoundingModeSort(self): cdef Sort sort = Sort(self) - sort.csort = self.csolver.getRoundingmodeSort() + sort.csort = self.csolver.getRoundingModeSort() return sort def getStringSort(self): @@ -445,6 +468,26 @@ cdef class Solver: sort.csort = self.csolver.mkDatatypeSort(dtypedecl.cdd) return sort + def mkDatatypeSorts(self, list dtypedecls, unresolvedSorts): + sorts = [] + + cdef vector[c_DatatypeDecl] decls + for decl in dtypedecls: + decls.push_back((<DatatypeDecl?> decl).cdd) + + cdef set[c_Sort] usorts + for usort in unresolvedSorts: + usorts.insert((<Sort?> usort).csort) + + csorts = self.csolver.mkDatatypeSorts( + <const vector[c_DatatypeDecl]&> decls, <const set[c_Sort]&> usorts) + for csort in csorts: + sort = Sort(self) + sort.csort = csort + sorts.append(sort) + + return sorts + def mkFunctionSort(self, sorts, Sort codomain): cdef Sort sort = Sort(self) @@ -1350,6 +1393,14 @@ cdef class Term: def __ne__(self, Term other): return self.cterm != other.cterm + def __getitem__(self, int index): + cdef Term term = Term(self.solver) + if index >= 0: + term.cterm = self.cterm[index] + else: + raise ValueError("Expecting a non-negative integer or string") + return term + def __str__(self): return self.cterm.toString().decode() @@ -1457,6 +1508,7 @@ cdef class Term: cdef __rounding_modes = { <int> ROUND_NEAREST_TIES_TO_EVEN: "RoundNearestTiesToEven", <int> ROUND_TOWARD_POSITIVE: "RoundTowardPositive", + <int> ROUND_TOWARD_NEGATIVE: "RoundTowardNegative", <int> ROUND_TOWARD_ZERO: "RoundTowardZero", <int> ROUND_NEAREST_TIES_TO_AWAY: "RoundNearestTiesToAway" } diff --git a/src/expr/CMakeLists.txt b/src/expr/CMakeLists.txt index 8bc732314..aed7a866c 100644 --- a/src/expr/CMakeLists.txt +++ b/src/expr/CMakeLists.txt @@ -63,6 +63,10 @@ libcvc4_add_sources( term_canonize.h term_context.cpp term_context.h + term_context_node.cpp + term_context_node.h + term_context_stack.cpp + term_context_stack.h term_conversion_proof_generator.cpp term_conversion_proof_generator.h type.cpp diff --git a/src/expr/expr_manager_template.cpp b/src/expr/expr_manager_template.cpp index 0d22a3c41..bac9e3b43 100644 --- a/src/expr/expr_manager_template.cpp +++ b/src/expr/expr_manager_template.cpp @@ -26,12 +26,6 @@ ${includes} -// This is a hack, but an important one: if there's an error, the -// compiler directs the user to the template file instead of the -// generated one. We don't want the user to modify the generated one, -// since it'll get overwritten on a later build. -#line 34 "${template}" - #ifdef CVC4_STATISTICS_ON #define INC_STAT(kind) \ { \ diff --git a/src/expr/expr_manager_template.h b/src/expr/expr_manager_template.h index a6fce56a2..145f64bd3 100644 --- a/src/expr/expr_manager_template.h +++ b/src/expr/expr_manager_template.h @@ -28,12 +28,6 @@ ${includes} -// This is a hack, but an important one: if there's an error, the -// compiler directs the user to the template file instead of the -// generated one. We don't want the user to modify the generated one, -// since it'll get overwritten on a later build. -#line 36 "${template}" - namespace CVC4 { namespace api { diff --git a/src/expr/expr_template.cpp b/src/expr/expr_template.cpp index 226736e8f..a92081fe8 100644 --- a/src/expr/expr_template.cpp +++ b/src/expr/expr_template.cpp @@ -29,12 +29,6 @@ ${includes} -// This is a hack, but an important one: if there's an error, the -// compiler directs the user to the template file instead of the -// generated one. We don't want the user to modify the generated one, -// since it'll get overwritten on a later build. -#line 37 "${template}" - using namespace CVC4::kind; using namespace std; diff --git a/src/expr/expr_template.h b/src/expr/expr_template.h index 34374d354..0acce2cb0 100644 --- a/src/expr/expr_template.h +++ b/src/expr/expr_template.h @@ -39,12 +39,6 @@ ${includes} #include "options/language.h" #include "util/hash.h" -// This is a hack, but an important one: if there's an error, the -// compiler directs the user to the template file instead of the -// generated one. We don't want the user to modify the generated one, -// since it'll get overwritten on a later build. -#line 47 "${template}" - namespace CVC4 { // The internal expression representation @@ -621,8 +615,6 @@ private: ${getConst_instantiations} -#line 625 "${template}" - inline size_t ExprHashFunction::operator()(CVC4::Expr e) const { return (size_t) e.getId(); } diff --git a/src/expr/kind_template.cpp b/src/expr/kind_template.cpp index 0d7f5f4e4..b2e165558 100644 --- a/src/expr/kind_template.cpp +++ b/src/expr/kind_template.cpp @@ -69,7 +69,6 @@ std::string kindToString(::CVC4::Kind k) { std::ostream& operator<<(std::ostream& out, TypeConstant typeConstant) { switch(typeConstant) { ${type_constant_descriptions} -#line 73 "${template}" default: out << "UNKNOWN_TYPE_CONSTANT"; break; @@ -85,7 +84,6 @@ TheoryId kindToTheoryId(::CVC4::Kind k) { case kind::NULL_EXPR: break; ${kind_to_theory_id} -#line 89 "${template}" case kind::LAST_KIND: break; } @@ -97,7 +95,6 @@ TheoryId typeConstantToTheoryId(::CVC4::TypeConstant typeConstant) switch (typeConstant) { ${type_constant_to_theory_id} -#line 101 "${template}" case LAST_TYPE: break; } throw IllegalArgumentException( diff --git a/src/expr/kind_template.h b/src/expr/kind_template.h index d34179252..8ae7018b3 100644 --- a/src/expr/kind_template.h +++ b/src/expr/kind_template.h @@ -63,8 +63,6 @@ const char* toString(CVC4::Kind k); */ std::ostream& operator<<(std::ostream&, CVC4::Kind) CVC4_PUBLIC; -#line 67 "${template}" - /** Returns true if the given kind is associative. This is used by ExprManager to * decide whether it's safe to modify big expressions by changing the grouping of * the arguments. */ @@ -86,7 +84,6 @@ struct KindHashFunction { enum CVC4_PUBLIC TypeConstant { ${type_constant_list} -#line 90 "${template}" LAST_TYPE }; /* enum TypeConstant */ diff --git a/src/expr/metakind_template.h b/src/expr/metakind_template.h index 5fa10c90a..e1668836b 100644 --- a/src/expr/metakind_template.h +++ b/src/expr/metakind_template.h @@ -201,8 +201,6 @@ Kind operatorToKind(::CVC4::expr::NodeValue* nv); }/* CVC4::kind namespace */ -#line 205 "${template}" - }/* CVC4 namespace */ #endif /* CVC4__NODE_MANAGER_NEEDS_CONSTANT_MAP */ diff --git a/src/expr/mkexpr b/src/expr/mkexpr index c5f12f487..58531cba4 100755 --- a/src/expr/mkexpr +++ b/src/expr/mkexpr @@ -150,9 +150,7 @@ function typerule { lineno=${BASH_LINENO[0]} check_theory_seen typerules="${typerules} -#line $lineno \"$kf\" case kind::$1: -#line $lineno \"$kf\" typeNode = $2::computeType(nodeManager, n, check); break; " @@ -163,9 +161,7 @@ function construle { lineno=${BASH_LINENO[0]} check_theory_seen construles="${construles} -#line $lineno \"$kf\" case kind::$1: -#line $lineno \"$kf\" return $2::computeIsConst(nodeManager, n); " } @@ -218,26 +214,19 @@ function constant { fi mkConst_instantiations="${mkConst_instantiations} -#line $lineno \"$kf\" template <> Expr ExprManager::mkConst($2 const& val); " mkConst_implementations="${mkConst_implementations} -#line $lineno \"$kf\" template <> Expr ExprManager::mkConst($2 const& val) { -#line $lineno \"$kf\" return Expr(this, new Node(d_nodeManager->mkConst< $2 >(val))); } " getConst_instantiations="${getConst_instantiations} -#line $lineno \"$kf\" template <> $2 const & Expr::getConst< $2 >() const; " getConst_implementations="${getConst_implementations} -#line $lineno \"$kf\" template <> $2 const & Expr::getConst() const { -#line $lineno \"$kf\" PrettyCheckArgument(getKind() == ::CVC4::kind::$1, *this, \"Improper kind for getConst<$2>()\"); -#line $lineno \"$kf\" return d_node->getConst< $2 >(); } " @@ -288,10 +277,6 @@ check_builtin_theory_seen ## output -# generate warnings about incorrect #line annotations in templates -nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | - awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 - text=$(cat "$template") for var in \ includes \ diff --git a/src/expr/mkkind b/src/expr/mkkind index fbf37eff4..55276549a 100755 --- a/src/expr/mkkind +++ b/src/expr/mkkind @@ -257,23 +257,23 @@ function register_sort { " type_constant_to_theory_id="${type_constant_to_theory_id} case $id: return $theory_id; " - type_constant_cardinalities="${type_constant_cardinalities}#line $lineno \"$kf\" + type_constant_cardinalities="${type_constant_cardinalities} case $id: return Cardinality($cardinality); " - type_constant_wellfoundednesses="${type_constant_wellfoundednesses}#line $lineno \"$kf\" + type_constant_wellfoundednesses="${type_constant_wellfoundednesses} case $id: return $wellfoundedness; " if [ -n "$groundterm" ]; then - type_constant_groundterms="${type_constant_groundterms}#line $lineno \"$kf\" + type_constant_groundterms="${type_constant_groundterms} case $id: return $groundterm; " if [ -n "$header" ]; then - type_properties_includes="${type_properties_includes}#line $lineno \"$kf\" + type_properties_includes="${type_properties_includes} #include \"$header\" " fi else - type_constant_groundterms="${type_constant_groundterms}#line $lineno \"$kf\" + type_constant_groundterms="${type_constant_groundterms} case $id: Unhandled() << tc; " fi @@ -284,11 +284,11 @@ function register_cardinality { cardinality_computer=$(sed 's,%TYPE%,typeNode,g' <<<"$2") header=$3 - type_cardinalities="${type_cardinalities}#line $lineno \"$kf\" + type_cardinalities="${type_cardinalities} case $id: return $cardinality_computer; " if [ -n "$header" ]; then - type_properties_includes="${type_properties_includes}#line $lineno \"$kf\" + type_properties_includes="${type_properties_includes} #include \"$header\" " fi @@ -314,20 +314,20 @@ function register_wellfoundedness { fi fi - type_wellfoundednesses="${type_wellfoundednesses}#line $lineno \"$kf\" + type_wellfoundednesses="${type_wellfoundednesses} case $id: return $wellfoundedness_computer; " if [ -n "$groundterm_computer" ]; then - type_groundterms="${type_groundterms}#line $lineno \"$kf\" + type_groundterms="${type_groundterms} case $id: return $groundterm_computer; " else - type_groundterms="${type_groundterms}#line $lineno \"$kf\" + type_groundterms="${type_groundterms} case $id: Unhandled() << typeNode; " fi if [ -n "$header" ]; then - type_properties_includes="${type_properties_includes}#line $lineno \"$kf\" + type_properties_includes="${type_properties_includes} #include \"$header\" " fi @@ -390,10 +390,6 @@ check_builtin_theory_seen ## output -# generate warnings about incorrect #line annotations in templates -nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | - awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 - text=$(cat "$template") for var in \ kind_decls \ diff --git a/src/expr/mkmetakind b/src/expr/mkmetakind index e2a733ec8..9d2f0a475 100755 --- a/src/expr/mkmetakind +++ b/src/expr/mkmetakind @@ -270,19 +270,16 @@ $2 const& NodeValue::getConst< $2 >() const { " metakind_constHashes="${metakind_constHashes} case kind::$1: -#line $lineno \"$kf\" return $3()(nv->getConst< $2 >()); " metakind_constPrinters="${metakind_constPrinters} case kind::$1: -#line $lineno \"$kf\" out << nv->getConst< $2 >(); break; " cname=`echo "$2" | awk 'BEGIN {FS="::"} {print$NF}'` metakind_constDeleters="${metakind_constDeleters} case kind::$1: -#line $lineno \"$kf\" std::allocator< $2 >().destroy(reinterpret_cast< $2* >(nv->d_children)); break; " @@ -301,7 +298,6 @@ function registerOperatorToKind { operatorKind=$1 applyKind=$2 metakind_operatorKinds="${metakind_operatorKinds} -#line $lineno \"$kf\" case kind::$applyKind: return kind::$operatorKind;"; } @@ -396,10 +392,6 @@ check_builtin_theory_seen ## output -# generate warnings about incorrect #line annotations in templates -nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | - awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 - text=$(cat "$template") for var in \ metakind_includes \ diff --git a/src/expr/proof_rule.cpp b/src/expr/proof_rule.cpp index 0a45c6790..1d46b183f 100644 --- a/src/expr/proof_rule.cpp +++ b/src/expr/proof_rule.cpp @@ -27,13 +27,15 @@ const char* toString(PfRule id) case PfRule::SCOPE: return "SCOPE"; case PfRule::SUBS: return "SUBS"; case PfRule::REWRITE: return "REWRITE"; + case PfRule::EVALUATE: return "EVALUATE"; case PfRule::MACRO_SR_EQ_INTRO: return "MACRO_SR_EQ_INTRO"; case PfRule::MACRO_SR_PRED_INTRO: return "MACRO_SR_PRED_INTRO"; case PfRule::MACRO_SR_PRED_ELIM: return "MACRO_SR_PRED_ELIM"; case PfRule::MACRO_SR_PRED_TRANSFORM: return "MACRO_SR_PRED_TRANSFORM"; - case PfRule::THEORY_REWRITE: return "THEORY_REWRITE"; case PfRule::REMOVE_TERM_FORMULA_AXIOM: return "REMOVE_TERM_FORMULA_AXIOM"; //================================================= Trusted rules + case PfRule::THEORY_LEMMA: return "THEORY_LEMMA"; + case PfRule::THEORY_REWRITE: return "THEORY_REWRITE"; case PfRule::PREPROCESS: return "PREPROCESS"; case PfRule::PREPROCESS_LEMMA: return "PREPROCESS_LEMMA"; case PfRule::THEORY_PREPROCESS: return "THEORY_PREPROCESS"; @@ -94,6 +96,8 @@ const char* toString(PfRule id) case PfRule::TRUE_ELIM: return "TRUE_ELIM"; case PfRule::FALSE_INTRO: return "FALSE_INTRO"; case PfRule::FALSE_ELIM: return "FALSE_ELIM"; + case PfRule::HO_APP_ENCODE: return "HO_APP_ENCODE"; + case PfRule::HO_CONG: return "HO_CONG"; //================================================= Quantifiers rules case PfRule::WITNESS_INTRO: return "WITNESS_INTRO"; case PfRule::EXISTS_INTRO: return "EXISTS_INTRO"; diff --git a/src/expr/proof_rule.h b/src/expr/proof_rule.h index 59c406d28..825503d5d 100644 --- a/src/expr/proof_rule.h +++ b/src/expr/proof_rule.h @@ -91,6 +91,14 @@ enum class PfRule : uint32_t // where idr is a MethodId identifier, which determines the kind of rewriter // to apply, e.g. Rewriter::rewrite. REWRITE, + // ======== Evaluate + // Children: none + // Arguments: (t) + // ---------------------------------------- + // Conclusion: (= t Evaluator::evaluate(t)) + // Note this is equivalent to: + // (REWRITE t MethodId::RW_EVALUATE) + EVALUATE, // ======== Substitution + Rewriting equality introduction // // In this rule, we provide a term t and conclude that it is equal to its @@ -163,15 +171,6 @@ enum class PfRule : uint32_t // Notice that we apply rewriting on the witness form of F and G, similar to // MACRO_SR_PRED_INTRO. MACRO_SR_PRED_TRANSFORM, - // ======== Theory Rewrite - // Children: none - // Arguments: (t, preRewrite?) - // ---------------------------------------- - // Conclusion: (= t t') - // where - // t' is the result of applying either a pre-rewrite or a post-rewrite step - // to t (depending on the second argument). - THEORY_REWRITE, //================================================= Processing rules // ======== Remove Term Formulas Axiom @@ -182,6 +181,28 @@ enum class PfRule : uint32_t REMOVE_TERM_FORMULA_AXIOM, //================================================= Trusted rules + // ======== Theory lemma + // Children: none + // Arguments: (F, tid) + // --------------------------------------------------------------- + // Conclusion: F + // where F is a (T-valid) theory lemma generated by theory with TheoryId tid. + // This is a "coarse-grained" rule that is used as a placeholder if a theory + // did not provide a proof for a lemma or conflict. + THEORY_LEMMA, + // ======== Theory Rewrite + // Children: none + // Arguments: (F, tid, preRewrite?) + // ---------------------------------------- + // Conclusion: F + // where F is an equality of the form (= t t') where t' is obtained by + // applying the theory rewriter with identifier tid in either its prewrite + // (when preRewrite is true) or postrewrite method. Notice that the checker + // for this rule does not replay the rewrite to ensure correctness, since + // theory rewriter methods are not static. For example, the quantifiers + // rewriter involves constructing new bound variables that are not guaranteed + // to be consistent on each call. + THEORY_REWRITE, // The rules in this section have the signature of a "trusted rule": // // Children: none @@ -520,6 +541,20 @@ enum class PfRule : uint32_t // ---------------------------------------- // Conclusion: (not F) FALSE_ELIM, + // ======== HO trust + // Children: none + // Arguments: (t) + // --------------------- + // Conclusion: (= t TheoryUfRewriter::getHoApplyForApplyUf(t)) + // For example, this rule concludes (f x y) = (HO_APPLY (HO_APPLY f x) y) + HO_APP_ENCODE, + // ======== Congruence + // Children: (P1:(= f g), P2:(= t1 s1), ..., Pn+1:(= tn sn)) + // Arguments: () + // --------------------------------------------- + // Conclusion: (= (f t1 ... tn) (g s1 ... sn)) + // Notice that this rule is only used when the application kinds are APPLY_UF. + HO_CONG, //================================================= Quantifiers rules // ======== Witness intro diff --git a/src/expr/term_conversion_proof_generator.cpp b/src/expr/term_conversion_proof_generator.cpp index 1c4baeed7..8cd7561b4 100644 --- a/src/expr/term_conversion_proof_generator.cpp +++ b/src/expr/term_conversion_proof_generator.cpp @@ -14,6 +14,8 @@ #include "expr/term_conversion_proof_generator.h" +#include "expr/term_context_stack.h" + using namespace CVC4::kind; namespace CVC4 { @@ -45,31 +47,38 @@ TConvProofGenerator::TConvProofGenerator(ProofNodeManager* pnm, context::Context* c, TConvPolicy pol, TConvCachePolicy cpol, - std::string name) + std::string name, + TermContext* tccb) : d_proof(pnm, nullptr, c, name + "::LazyCDProof"), d_rewriteMap(c ? c : &d_context), d_policy(pol), d_cpolicy(cpol), - d_name(name) + d_name(name), + d_tcontext(tccb) { } TConvProofGenerator::~TConvProofGenerator() {} -void TConvProofGenerator::addRewriteStep(Node t, Node s, ProofGenerator* pg) +void TConvProofGenerator::addRewriteStep( + Node t, Node s, ProofGenerator* pg, bool isClosed, uint32_t tctx) { - Node eq = registerRewriteStep(t, s); + Node eq = registerRewriteStep(t, s, tctx); if (!eq.isNull()) { - d_proof.addLazyStep(eq, pg); + d_proof.addLazyStep(eq, pg, isClosed); } } -void TConvProofGenerator::addRewriteStep(Node t, Node s, ProofStep ps) +void TConvProofGenerator::addRewriteStep(Node t, + Node s, + ProofStep ps, + uint32_t tctx) { - Node eq = registerRewriteStep(t, s); + Node eq = registerRewriteStep(t, s, tctx); if (!eq.isNull()) { + AlwaysAssert(ps.d_rule != PfRule::ASSUME); d_proof.addStep(eq, ps); } } @@ -78,33 +87,55 @@ void TConvProofGenerator::addRewriteStep(Node t, Node s, PfRule id, const std::vector<Node>& children, - const std::vector<Node>& args) + const std::vector<Node>& args, + uint32_t tctx) { - Node eq = registerRewriteStep(t, s); + Node eq = registerRewriteStep(t, s, tctx); if (!eq.isNull()) { + AlwaysAssert(id != PfRule::ASSUME); d_proof.addStep(eq, id, children, args); } } -bool TConvProofGenerator::hasRewriteStep(Node t) const +bool TConvProofGenerator::hasRewriteStep(Node t, uint32_t tctx) const { - return !getRewriteStep(t).isNull(); + return !getRewriteStep(t, tctx).isNull(); } -Node TConvProofGenerator::registerRewriteStep(Node t, Node s) +Node TConvProofGenerator::getRewriteStep(Node t, uint32_t tctx) const +{ + Node thash = t; + if (d_tcontext != nullptr) + { + thash = TCtxNode::computeNodeHash(t, tctx); + } + return getRewriteStepInternal(thash); +} + +Node TConvProofGenerator::registerRewriteStep(Node t, Node s, uint32_t tctx) { if (t == s) { return Node::null(); } + Node thash = t; + if (d_tcontext != nullptr) + { + thash = TCtxNode::computeNodeHash(t, tctx); + } + else + { + // don't use term context ids if not using term context + Assert(tctx == 0); + } // should not rewrite term to two different things - if (!getRewriteStep(t).isNull()) + if (!getRewriteStepInternal(thash).isNull()) { - Assert(getRewriteStep(t) == s); + Assert(getRewriteStepInternal(thash) == s); return Node::null(); } - d_rewriteMap[t] = s; + d_rewriteMap[thash] = s; if (d_cpolicy == TConvCachePolicy::DYNAMIC) { // clear the cache @@ -115,66 +146,140 @@ Node TConvProofGenerator::registerRewriteStep(Node t, Node s) std::shared_ptr<ProofNode> TConvProofGenerator::getProofFor(Node f) { - Trace("tconv-pf-gen") << "TConvProofGenerator::getProofFor: " << f - << std::endl; + Trace("tconv-pf-gen") << "TConvProofGenerator::getProofFor: " << identify() + << ": " << f << std::endl; if (f.getKind() != EQUAL) { - Trace("tconv-pf-gen") << "... fail, non-equality" << std::endl; - Assert(false); + std::stringstream serr; + serr << "TConvProofGenerator::getProofFor: " << identify() + << ": fail, non-equality " << f; + Unhandled() << serr.str(); + Trace("tconv-pf-gen") << serr.str() << std::endl; return nullptr; } // we use the existing proofs LazyCDProof lpf( d_proof.getManager(), &d_proof, nullptr, d_name + "::LazyCDProof"); - Node conc = getProofForRewriting(f[0], lpf); - if (conc != f) + if (f[0] == f[1]) { - Trace("tconv-pf-gen") << "...failed, mismatch: returned proof concludes " - << conc << ", expected " << f << std::endl; - Assert(false); - return nullptr; + // assertion failure in debug + Assert(false) << "TConvProofGenerator::getProofFor: " << identify() + << ": don't ask for trivial proofs"; + lpf.addStep(f, PfRule::REFL, {}, {f[0]}); + } + else + { + Node conc = getProofForRewriting(f[0], lpf, d_tcontext); + if (conc != f) + { + Assert(conc.getKind() == EQUAL && conc[0] == f[0]); + std::stringstream serr; + serr << "TConvProofGenerator::getProofFor: " << toStringDebug() + << ": failed, mismatch (see -t tconv-pf-gen-debug for details)" + << std::endl; + serr << " source: " << f[0] << std::endl; + serr << "expected after rewriting: " << f[1] << std::endl; + serr << " actual after rewriting: " << conc[1] << std::endl; + + if (Trace.isOn("tconv-pf-gen-debug")) + { + Trace("tconv-pf-gen-debug") << "Printing rewrite steps..." << std::endl; + serr << "Rewrite steps: " << std::endl; + for (NodeNodeMap::const_iterator it = d_rewriteMap.begin(); + it != d_rewriteMap.end(); + ++it) + { + serr << (*it).first << " -> " << (*it).second << std::endl; + } + } + Unhandled() << serr.str(); + return nullptr; + } } Trace("tconv-pf-gen") << "... success" << std::endl; return lpf.getProofFor(f); } -Node TConvProofGenerator::getProofForRewriting(Node t, LazyCDProof& pf) +Node TConvProofGenerator::getProofForRewriting(Node t, + LazyCDProof& pf, + TermContext* tctx) { NodeManager* nm = NodeManager::currentNM(); - // Invariant: if visited[t] = s or rewritten[t] = s and t,s are distinct, - // then pf is able to generate a proof of t=s. + // Invariant: if visited[hash(t)] = s or rewritten[hash(t)] = s and t,s are + // distinct, then pf is able to generate a proof of t=s. We must + // Node in the domains of the maps below due to hashing creating new (SEXPR) + // nodes. + // the final rewritten form of terms - std::unordered_map<TNode, Node, TNodeHashFunction> visited; + std::unordered_map<Node, Node, TNodeHashFunction> visited; // the rewritten form of terms we have processed so far - std::unordered_map<TNode, Node, TNodeHashFunction> rewritten; - std::unordered_map<TNode, Node, TNodeHashFunction>::iterator it; - std::unordered_map<TNode, Node, TNodeHashFunction>::iterator itr; + std::unordered_map<Node, Node, TNodeHashFunction> rewritten; + std::unordered_map<Node, Node, TNodeHashFunction>::iterator it; + std::unordered_map<Node, Node, TNodeHashFunction>::iterator itr; std::map<Node, std::shared_ptr<ProofNode> >::iterator itc; + Trace("tconv-pf-gen-rewrite") + << "TConvProofGenerator::getProofForRewriting: " << toStringDebug() + << std::endl; + Trace("tconv-pf-gen-rewrite") << "Input: " << t << std::endl; + // if provided, we use term context for cache + std::shared_ptr<TCtxStack> visitctx; + // otherwise, visit is used if we don't have a term context std::vector<TNode> visit; - TNode cur; - visit.push_back(t); + Node tinitialHash; + if (tctx != nullptr) + { + visitctx = std::make_shared<TCtxStack>(tctx); + visitctx->pushInitial(t); + tinitialHash = TCtxNode::computeNodeHash(t, tctx->initialValue()); + } + else + { + visit.push_back(t); + tinitialHash = t; + } + Node cur; + uint32_t curCVal = 0; + Node curHash; do { - cur = visit.back(); - visit.pop_back(); + // pop the top element + if (tctx != nullptr) + { + std::pair<Node, uint32_t> curPair = visitctx->getCurrent(); + cur = curPair.first; + curCVal = curPair.second; + curHash = TCtxNode::computeNodeHash(cur, curCVal); + visitctx->pop(); + } + else + { + cur = visit.back(); + curHash = cur; + visit.pop_back(); + } + Trace("tconv-pf-gen-rewrite") << "* visit : " << curHash << std::endl; // has the proof for cur been cached? - itc = d_cache.find(cur); + itc = d_cache.find(curHash); if (itc != d_cache.end()) { Node res = itc->second->getResult(); Assert(res.getKind() == EQUAL); - visited[cur] = res[1]; + Assert(!res[1].isNull()); + visited[curHash] = res[1]; pf.addProof(itc->second); continue; } - it = visited.find(cur); + it = visited.find(curHash); if (it == visited.end()) { - visited[cur] = Node::null(); + Trace("tconv-pf-gen-rewrite") << "- previsit" << std::endl; + visited[curHash] = Node::null(); // did we rewrite the current node (possibly at pre-rewrite)? - Node rcur = getRewriteStep(cur); + Node rcur = getRewriteStepInternal(curHash); if (!rcur.isNull()) { + Trace("tconv-pf-gen-rewrite") + << "*** " << curHash << " prerewrites to " << rcur << std::endl; // d_proof has a proof of cur = rcur. Hence there is nothing // to do here, as pf will reference d_proof to get its proof. if (d_policy == TConvPolicy::FIXPOINT) @@ -182,18 +287,34 @@ Node TConvProofGenerator::getProofForRewriting(Node t, LazyCDProof& pf) // It may be the case that rcur also rewrites, thus we cannot assign // the final rewritten form for cur yet. Instead we revisit cur after // finishing visiting rcur. - rewritten[cur] = rcur; - visit.push_back(cur); - visit.push_back(rcur); + rewritten[curHash] = rcur; + if (tctx != nullptr) + { + visitctx->push(cur, curCVal); + visitctx->push(rcur, curCVal); + } + else + { + visit.push_back(cur); + visit.push_back(rcur); + } } else { Assert(d_policy == TConvPolicy::ONCE); + Trace("tconv-pf-gen-rewrite") << "-> (once, prewrite) " << curHash + << " = " << rcur << std::endl; // not rewriting again, rcur is final - visited[cur] = rcur; - doCache(cur, rcur, pf); + Assert(!rcur.isNull()); + visited[curHash] = rcur; + doCache(curHash, cur, rcur, pf); } } + else if (tctx != nullptr) + { + visitctx->push(cur, curCVal); + visitctx->pushChildren(cur, curCVal); + } else { visit.push_back(cur); @@ -202,7 +323,7 @@ Node TConvProofGenerator::getProofForRewriting(Node t, LazyCDProof& pf) } else if (it->second.isNull()) { - itr = rewritten.find(cur); + itr = rewritten.find(curHash); if (itr != rewritten.end()) { // only can generate partially rewritten nodes when rewrite again is @@ -211,9 +332,17 @@ Node TConvProofGenerator::getProofForRewriting(Node t, LazyCDProof& pf) // if it was rewritten, check the status of the rewritten node, // which should be finished now Node rcur = itr->second; + Trace("tconv-pf-gen-rewrite") + << "- postvisit, previously rewritten to " << rcur << std::endl; + Node rcurHash = rcur; + if (tctx != nullptr) + { + rcurHash = TCtxNode::computeNodeHash(rcur, curCVal); + } Assert(cur != rcur); // the final rewritten form of cur is the final form of rcur - Node rcurFinal = visited[rcur]; + Node rcurFinal = visited[rcurHash]; + Assert(!rcurFinal.isNull()); if (rcurFinal != rcur) { // must connect via TRANS @@ -223,30 +352,54 @@ Node TConvProofGenerator::getProofForRewriting(Node t, LazyCDProof& pf) Node result = cur.eqNode(rcurFinal); pf.addStep(result, PfRule::TRANS, pfChildren, {}); } - visited[cur] = rcurFinal; - doCache(cur, rcurFinal, pf); + Trace("tconv-pf-gen-rewrite") + << "-> (rewritten postrewrite) " << curHash << " = " << rcurFinal + << std::endl; + visited[curHash] = rcurFinal; + doCache(curHash, cur, rcurFinal, pf); } else { + Trace("tconv-pf-gen-rewrite") << "- postvisit" << std::endl; Node ret = cur; + Node retHash = curHash; bool childChanged = false; std::vector<Node> children; if (cur.getMetaKind() == metakind::PARAMETERIZED) { children.push_back(cur.getOperator()); } - for (const Node& cn : cur) + // get the results of the children + if (tctx != nullptr) + { + for (size_t i = 0, nchild = cur.getNumChildren(); i < nchild; i++) + { + Node cn = cur[i]; + uint32_t cnval = tctx->computeValue(cur, curCVal, i); + Node cnHash = TCtxNode::computeNodeHash(cn, cnval); + it = visited.find(cnHash); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cn != it->second; + children.push_back(it->second); + } + } + else { - it = visited.find(cn); - Assert(it != visited.end()); - Assert(!it->second.isNull()); - childChanged = childChanged || cn != it->second; - children.push_back(it->second); + // can use simple loop if not term-context-sensitive + for (const Node& cn : cur) + { + it = visited.find(cn); + Assert(it != visited.end()); + Assert(!it->second.isNull()); + childChanged = childChanged || cn != it->second; + children.push_back(it->second); + } } if (childChanged) { ret = nm->mkNode(cur.getKind(), children); - rewritten[cur] = ret; + rewritten[curHash] = ret; // congruence to show (cur = ret) std::vector<Node> pfChildren; for (size_t i = 0, size = cur.getNumChildren(); i < size; i++) @@ -260,62 +413,96 @@ Node TConvProofGenerator::getProofForRewriting(Node t, LazyCDProof& pf) } std::vector<Node> pfArgs; Kind k = cur.getKind(); + pfArgs.push_back(ProofRuleChecker::mkKindNode(k)); if (kind::metaKindOf(k) == kind::metakind::PARAMETERIZED) { pfArgs.push_back(cur.getOperator()); } - else - { - pfArgs.push_back(nm->operatorOf(k)); - } Node result = cur.eqNode(ret); pf.addStep(result, PfRule::CONG, pfChildren, pfArgs); + // must update the hash + retHash = ret; + if (tctx != nullptr) + { + retHash = TCtxNode::computeNodeHash(ret, curCVal); + } + } + else if (tctx != nullptr) + { + // now we need the hash + retHash = TCtxNode::computeNodeHash(cur, curCVal); } // did we rewrite ret (at post-rewrite)? Node rret; // only if not ONCE policy, which only does pre-rewrite if (d_policy != TConvPolicy::ONCE) { - rret = getRewriteStep(ret); + rret = getRewriteStepInternal(retHash); } if (!rret.isNull()) { - if (cur != ret) - { - visit.push_back(cur); - } + Trace("tconv-pf-gen-rewrite") + << "*** " << retHash << " postrewrites to " << rret << std::endl; // d_proof should have a proof of ret = rret, hence nothing to do // here, for the same reasons as above. It also may be the case that // rret rewrites, hence we must revisit ret. - rewritten[ret] = rret; - visit.push_back(ret); - visit.push_back(rret); + rewritten[retHash] = rret; + if (tctx != nullptr) + { + if (cur != ret) + { + visitctx->push(cur, curCVal); + } + visitctx->push(ret, curCVal); + visitctx->push(rret, curCVal); + } + else + { + if (cur != ret) + { + visit.push_back(cur); + } + visit.push_back(ret); + visit.push_back(rret); + } } else { + Trace("tconv-pf-gen-rewrite") + << "-> (postrewrite) " << curHash << " = " << ret << std::endl; // it is final - visited[cur] = ret; - doCache(cur, ret, pf); + Assert(!ret.isNull()); + visited[curHash] = ret; + doCache(curHash, cur, ret, pf); } } } - } while (!visit.empty()); - Assert(visited.find(t) != visited.end()); - Assert(!visited.find(t)->second.isNull()); + else + { + Trace("tconv-pf-gen-rewrite") << "- already visited" << std::endl; + } + } while (!(tctx != nullptr ? visitctx->empty() : visit.empty())); + Assert(visited.find(tinitialHash) != visited.end()); + Assert(!visited.find(tinitialHash)->second.isNull()); + Trace("tconv-pf-gen-rewrite") + << "...finished, return " << visited[tinitialHash] << std::endl; // return the conclusion of the overall proof - return t.eqNode(visited[t]); + return t.eqNode(visited[tinitialHash]); } -void TConvProofGenerator::doCache(Node cur, Node r, LazyCDProof& pf) +void TConvProofGenerator::doCache(Node curHash, + Node cur, + Node r, + LazyCDProof& pf) { if (d_cpolicy != TConvCachePolicy::NEVER) { Node eq = cur.eqNode(r); - d_cache[cur] = pf.getProofFor(eq); + d_cache[curHash] = pf.getProofFor(eq); } } -Node TConvProofGenerator::getRewriteStep(Node t) const +Node TConvProofGenerator::getRewriteStepInternal(Node t) const { NodeNodeMap::const_iterator it = d_rewriteMap.find(t); if (it == d_rewriteMap.end()) @@ -326,4 +513,12 @@ Node TConvProofGenerator::getRewriteStep(Node t) const } std::string TConvProofGenerator::identify() const { return d_name; } +std::string TConvProofGenerator::toStringDebug() const +{ + std::stringstream ss; + ss << identify() << " (policy=" << d_policy << ", cache policy=" << d_cpolicy + << (d_tcontext != nullptr ? ", term-context-sensitive" : "") << ")"; + return ss.str(); +} + } // namespace CVC4 diff --git a/src/expr/term_conversion_proof_generator.h b/src/expr/term_conversion_proof_generator.h index e634b8a83..faee2b9e3 100644 --- a/src/expr/term_conversion_proof_generator.h +++ b/src/expr/term_conversion_proof_generator.h @@ -21,6 +21,7 @@ #include "expr/lazy_proof.h" #include "expr/proof_generator.h" #include "expr/proof_node_manager.h" +#include "expr/term_context.h" namespace CVC4 { @@ -84,6 +85,35 @@ std::ostream& operator<<(std::ostream& out, TConvCachePolicy tcpol); * addRewriteStep. In particular, notice that in the above example, we realize * that f(a) --> c at pre-rewrite instead of post-rewriting a --> b and then * ending with f(a)=f(b). + * + * This class may additionally be used for term-context-sensitive rewrite + * systems. An example is the term formula removal pass which rewrites + * terms dependending on whether they occur in a "term position", for details + * see RtfTermContext in expr/term_context.h. To use this class in a way + * that takes into account term contexts, the user of the term conversion + * proof generator should: + * (1) Provide a term context callback to the constructor of this class (tccb), + * (2) Register rewrite steps that indicate the term context identifier of + * the rewrite, which is a uint32_t. + * + * For example, RtfTermContext uses hash value 2 to indicate we are in a "term + * position". Say the user of this class calls: + * addRewriteStep( (and A B), BOOLEAN_TERM_VARIABLE_1, pg, true, 2) + * This indicates that (and A B) should rewrite to BOOLEAN_TERM_VARIABLE_1 if + * (and A B) occurs in a term position, where pg is a proof generator that can + * provide a closed proof of: + * (= (and A B) BOOLEAN_TERM_VARIABLE_1) + * Subsequently, this class may respond to a call to getProofFor on: + * (= + * (or (and A B) (P (and A B))) + * (or (and A B) (P BOOLEAN_TERM_VARIABLE_1))) + * where P is a predicate Bool -> Bool. The proof returned by this class + * involves congruence and pg's proof of the equivalence above. In particular, + * assuming its proof of the equivalence is P1, this proof is: + * (CONG{=} (CONG{or} (REFL (and A B)) (CONG{P} P1))) + * Notice the callback provided to this class ensures that the rewrite is + * replayed in the expected way, e.g. the occurrence of (and A B) that is not + * in term position is not rewritten. */ class TConvProofGenerator : public ProofGenerator { @@ -99,32 +129,48 @@ class TConvProofGenerator : public ProofGenerator * details, see d_policy. * @param cpol The caching policy for this generator. * @param name The name of this generator (for debugging). + * @param tccb The term context callback that this class depends on. If this + * is non-null, then this class stores a term-context-sensitive rewrite + * system. The rewrite steps should be given term context identifiers. */ TConvProofGenerator(ProofNodeManager* pnm, context::Context* c = nullptr, TConvPolicy pol = TConvPolicy::FIXPOINT, TConvCachePolicy cpol = TConvCachePolicy::NEVER, - std::string name = "TConvProofGenerator"); + std::string name = "TConvProofGenerator", + TermContext* tccb = nullptr); ~TConvProofGenerator(); /** * Add rewrite step t --> s based on proof generator. + * + * @param isClosed whether to expect that pg can provide a closed proof for + * this fact. + * @param tctx The term context identifier for the rewrite step. This + * value should correspond to one generated by the term context callback + * class provided in the argument tccb provided to the constructor of this + * class. */ - void addRewriteStep(Node t, Node s, ProofGenerator* pg); + void addRewriteStep(Node t, + Node s, + ProofGenerator* pg, + bool isClosed = true, + uint32_t tctx = 0); /** Same as above, for a single step */ - void addRewriteStep(Node t, Node s, ProofStep ps); + void addRewriteStep(Node t, Node s, ProofStep ps, uint32_t tctx = 0); /** Same as above, with explicit arguments */ void addRewriteStep(Node t, Node s, PfRule id, const std::vector<Node>& children, - const std::vector<Node>& args); + const std::vector<Node>& args, + uint32_t tctx = 0); /** Has rewrite step for term t */ - bool hasRewriteStep(Node t) const; + bool hasRewriteStep(Node t, uint32_t tctx = 0) const; /** * Get rewrite step for term t, returns the s provided in a call to * addRewriteStep if one exists, or null otherwise. */ - Node getRewriteStep(Node t) const; + Node getRewriteStep(Node t, uint32_t tctx = 0) const; /** * Get the proof for formula f. It should be the case that f is of the form * t = t', where t' is the result of rewriting t based on the rewrite steps @@ -161,19 +207,25 @@ class TConvProofGenerator : public ProofGenerator std::string d_name; /** The cache for terms */ std::map<Node, std::shared_ptr<ProofNode> > d_cache; + /** An (optional) term context object */ + TermContext* d_tcontext; + /** Get rewrite step for (hash value of) term. */ + Node getRewriteStepInternal(Node thash) const; /** * Adds a proof of t = t' to the proof pf where t' is the result of rewriting * t based on the rewrite steps registered to this class. This method then * returns the proved equality t = t'. */ - Node getProofForRewriting(Node t, LazyCDProof& pf); + Node getProofForRewriting(Node t, LazyCDProof& pf, TermContext* tc = nullptr); /** * Register rewrite step, returns the equality t=s if t is distinct from s * and a rewrite step has not already been registered for t. */ - Node registerRewriteStep(Node t, Node s); + Node registerRewriteStep(Node t, Node s, uint32_t tctx); /** cache that r is the rewritten form of cur, pf can provide a proof */ - void doCache(Node cur, Node r, LazyCDProof& pf); + void doCache(Node curHash, Node cur, Node r, LazyCDProof& pf); + /** get debug information on this generator */ + std::string toStringDebug() const; }; } // namespace CVC4 diff --git a/src/expr/type_checker_template.cpp b/src/expr/type_checker_template.cpp index 3dedd856a..8e4dd9236 100644 --- a/src/expr/type_checker_template.cpp +++ b/src/expr/type_checker_template.cpp @@ -14,8 +14,6 @@ ** TypeChecker implementation. **/ -#line 18 "${template}" - #include "expr/node_manager.h" #include "expr/node_manager_attributes.h" #include "expr/type_checker.h" @@ -23,8 +21,6 @@ ${typechecker_includes} -#line 27 "${template}" - namespace CVC4 { namespace expr { @@ -44,8 +40,6 @@ TypeNode TypeChecker::computeType(NodeManager* nodeManager, TNode n, bool check) ${typerules} -#line 48 "${template}" - default: Debug("getType") << "FAILURE" << std::endl; Unhandled() << n.getKind(); @@ -68,8 +62,6 @@ bool TypeChecker::computeIsConst(NodeManager* nodeManager, TNode n) switch(n.getKind()) { ${construles} -#line 72 "${template}" - default:; } diff --git a/src/expr/type_properties_template.h b/src/expr/type_properties_template.h index a8adadf6e..9bccd5892 100644 --- a/src/expr/type_properties_template.h +++ b/src/expr/type_properties_template.h @@ -19,8 +19,6 @@ #ifndef CVC4__TYPE_PROPERTIES_H #define CVC4__TYPE_PROPERTIES_H -#line 23 "${template}" - #include <sstream> #include "base/check.h" @@ -31,8 +29,6 @@ ${type_properties_includes} -#line 35 "${template}" - namespace CVC4 { namespace kind { @@ -47,7 +43,6 @@ inline Cardinality getCardinality(TypeConstant tc) switch (tc) { ${type_constant_cardinalities} -#line 51 "${template}" default: InternalError() << "No cardinality known for type constant " << tc; } } /* getCardinality(TypeConstant) */ @@ -64,7 +59,6 @@ inline Cardinality getCardinality(TypeNode typeNode) { case TYPE_CONSTANT: return getCardinality(typeNode.getConst<TypeConstant>()); ${type_cardinalities} -#line 68 "${template}" default: InternalError() << "A theory kinds file did not provide a cardinality " << "or cardinality computer for type:\n" @@ -75,7 +69,6 @@ ${type_cardinalities} inline bool isWellFounded(TypeConstant tc) { switch(tc) { ${type_constant_wellfoundednesses} -#line 79 "${template}" default: InternalError() << "No well-foundedness status known for type constant: " << tc; @@ -88,7 +81,6 @@ inline bool isWellFounded(TypeNode typeNode) { case TYPE_CONSTANT: return isWellFounded(typeNode.getConst<TypeConstant>()); ${type_wellfoundednesses} -#line 92 "${template}" default: InternalError() << "A theory kinds file did not provide a well-foundedness " << "or well-foundedness computer for type:\n" @@ -101,7 +93,6 @@ inline Node mkGroundTerm(TypeConstant tc) switch (tc) { ${type_constant_groundterms} -#line 105 "${template}" default: InternalError() << "No ground term known for type constant: " << tc; } @@ -115,7 +106,6 @@ inline Node mkGroundTerm(TypeNode typeNode) case TYPE_CONSTANT: return mkGroundTerm(typeNode.getConst<TypeConstant>()); ${type_groundterms} -#line 119 "${template}" default: InternalError() << "A theory kinds file did not provide a ground term " << "or ground term computer for type:\n" diff --git a/src/fix-install-headers.sh b/src/fix-install-headers.sh index 39d8bc663..7f9fa5d5b 100755 --- a/src/fix-install-headers.sh +++ b/src/fix-install-headers.sh @@ -2,6 +2,7 @@ set -e -o pipefail -dir=$1 +dir="$DESTDIR$1" + find "$dir/include/cvc4/" -type f \ -exec sed -i'' -e 's/include.*"\(.*\)"/include <cvc4\/\1>/' {} + diff --git a/src/options/bv_options.toml b/src/options/bv_options.toml index 7c0aca100..e00db9393 100644 --- a/src/options/bv_options.toml +++ b/src/options/bv_options.toml @@ -3,44 +3,6 @@ name = "Bitvector theory" header = "options/bv_options.h" [[option]] - name = "bvProofFormat" - category = "expert" - long = "bv-proof-format=MODE" - type = "BvProofFormat" - default = "ER" - predicates = ["checkSatSolverEnabled<BvProofFormat>"] - help = "choose which UNSAT proof format to use, see --bv-sat-solver=help" - help_mode = "Bit-vector proof formats." -[[option.mode.ER]] - name = "er" - help = "Extended Resolution, i.e. resolution with new variable definitions." -[[option.mode.DRAT]] - name = "drat" - help = "Deletion and Resolution Asymmetric Tautology Additions." -[[option.mode.LRAT]] - name = "lrat" - help = "DRAT with unit propagation hints to accelerate checking." - -[[option]] - name = "bvOptimizeSatProof" - category = "expert" - long = "bv-optimize-sat-proof=MODE" - type = "BvOptimizeSatProof" - default = "FORMULA" - predicates = ["checkSatSolverEnabled<BvOptimizeSatProof>"] - help = "enable SAT proof optimizations, see --bv-optimize-sat-proof=help" - help_mode = "SAT proof optimization level." -[[option.mode.NONE]] - name = "none" - help = "Do not optimize the SAT proof." -[[option.mode.PROOF]] - name = "proof" - help = "Use drat-trim to shrink the SAT proof." -[[option.mode.FORMULA]] - name = "formula" - help = "Use drat-trim to shrink the SAT proof and formula." - -[[option]] name = "bvSatSolver" smt_name = "bv-sat-solver" category = "expert" diff --git a/src/options/options.h b/src/options/options.h index 44f4be7b4..abcf21264 100644 --- a/src/options/options.h +++ b/src/options/options.h @@ -150,7 +150,6 @@ public: options::InstFormatMode getInstFormatMode() const; OutputLanguage getOutputLanguage() const; bool getUfHo() const; - bool getCheckProofs() const; bool getDumpInstantiations() const; bool getDumpModels() const; bool getDumpProofs() const; @@ -167,7 +166,6 @@ public: bool getMemoryMap() const; bool getParseOnly() const; bool getProduceModels() const; - bool getProof() const; bool getSegvSpin() const; bool getSemanticChecks() const; bool getStatistics() const; diff --git a/src/options/options_public_functions.cpp b/src/options/options_public_functions.cpp index c8104c584..2dc28b10d 100644 --- a/src/options/options_public_functions.cpp +++ b/src/options/options_public_functions.cpp @@ -54,10 +54,6 @@ OutputLanguage Options::getOutputLanguage() const { bool Options::getUfHo() const { return (*this)[options::ufHo]; } -bool Options::getCheckProofs() const{ - return (*this)[options::checkProofs]; -} - bool Options::getDumpInstantiations() const{ return (*this)[options::dumpInstantiations]; } @@ -124,10 +120,6 @@ bool Options::getProduceModels() const{ return (*this)[options::produceModels]; } -bool Options::getProof() const{ - return (*this)[options::proof]; -} - bool Options::getSegvSpin() const{ return (*this)[options::segvSpin]; } diff --git a/src/options/proof_options.toml b/src/options/proof_options.toml index a23241e3d..9db541e27 100644 --- a/src/options/proof_options.toml +++ b/src/options/proof_options.toml @@ -1,38 +1,3 @@ id = "PROOF" name = "Proof" header = "options/proof_options.h" - -[[option]] - name = "lfscLetification" - category = "regular" - long = "lfsc-letification" - type = "bool" - default = "true" - read_only = true - help = "turns on global letification in LFSC proofs" - -[[option]] - name = "aggressiveCoreMin" - category = "regular" - long = "aggressive-core-min" - type = "bool" - default = "false" - read_only = true - help = "turns on aggressive unsat core minimization (experimental)" - -[[option]] - name = "fewerPreprocessingHoles" - category = "regular" - long = "fewer-preprocessing-holes" - type = "bool" - default = "false" - help = "try to eliminate preprocessing holes in proofs" - -[[option]] - name = "allowEmptyDependencies" - category = "regular" - long = "allow-empty-dependencies" - type = "bool" - default = "false" - read_only = true - help = "if unable to track the dependencies of a rewritten/preprocessed assertion, fail silently" diff --git a/src/options/smt_options.toml b/src/options/smt_options.toml index 6b5bee6bb..2c87158de 100644 --- a/src/options/smt_options.toml +++ b/src/options/smt_options.toml @@ -131,24 +131,6 @@ header = "options/smt_options.h" help = "Block models based on the concrete model values for the free variables." [[option]] - name = "proof" - smt_name = "produce-proofs" - category = "regular" - long = "proof" - type = "bool" - default = "false" - predicates = ["proofEnabledBuild"] - help = "turn on proof generation" - -[[option]] - name = "checkProofs" - category = "regular" - long = "check-proofs" - type = "bool" - predicates = ["LFSCEnabledBuild"] - help = "after UNSAT/VALID, machine-check the generated proof" - -[[option]] name = "dumpProofs" category = "regular" long = "dump-proofs" @@ -390,7 +372,7 @@ header = "options/smt_options.h" type = "bool" default = "false" help = "calculate sort inference of input problem, convert the input based on monotonic sorts" - + [[option]] name = "incrementalSolving" category = "common" @@ -660,7 +642,7 @@ header = "options/smt_options.h" [[option.mode.BITWISE]] name = "bitwise" help = "use bitwise comparisons on binary representation of integer for refinement (experimental)" - + [[option]] name = "solveIntAsBV" category = "undocumented" diff --git a/src/parser/cvc/Cvc.g b/src/parser/cvc/Cvc.g index 091b5a22b..865776dcd 100644 --- a/src/parser/cvc/Cvc.g +++ b/src/parser/cvc/Cvc.g @@ -2481,7 +2481,7 @@ fragment DOT:; fragment DOTDOT:; /** - * Matches the hexidecimal digits (0-9, a-f, A-F) + * Matches the hexadecimal digits (0-9, a-f, A-F) */ fragment HEX_DIGIT : DIGIT | 'a'..'f' | 'A'..'F'; diff --git a/src/parser/smt2/smt2.cpp b/src/parser/smt2/smt2.cpp index 1e5d2155a..c4899c8a8 100644 --- a/src/parser/smt2/smt2.cpp +++ b/src/parser/smt2/smt2.cpp @@ -718,7 +718,7 @@ Command* Smt2::setLogic(std::string name, bool fromCommand) } if (d_logic.isTheoryEnabled(theory::THEORY_FP)) { - defineType("RoundingMode", d_solver->getRoundingmodeSort()); + defineType("RoundingMode", d_solver->getRoundingModeSort()); defineType("Float16", d_solver->mkFloatingPointSort(5, 11)); defineType("Float32", d_solver->mkFloatingPointSort(8, 24)); defineType("Float64", d_solver->mkFloatingPointSort(11, 53)); diff --git a/src/preprocessing/assertion_pipeline.cpp b/src/preprocessing/assertion_pipeline.cpp index a6b9531b6..a9e2d4d36 100644 --- a/src/preprocessing/assertion_pipeline.cpp +++ b/src/preprocessing/assertion_pipeline.cpp @@ -16,7 +16,7 @@ #include "preprocessing/assertion_pipeline.h" #include "expr/node_manager.h" -#include "proof/proof.h" +#include "options/smt_options.h" #include "proof/proof_manager.h" #include "theory/rewriter.h" @@ -77,7 +77,10 @@ void AssertionPipeline::pushBackTrusted(theory::TrustNode trn) void AssertionPipeline::replace(size_t i, Node n, ProofGenerator* pgen) { - PROOF(ProofManager::currentPM()->addDependence(n, d_nodes[i]);); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, d_nodes[i]); + } if (isProofEnabled()) { d_pppg->notifyPreprocessed(d_nodes[i], n, pgen); @@ -96,11 +99,14 @@ void AssertionPipeline::replace(size_t i, const std::vector<Node>& addnDeps, ProofGenerator* pgen) { - PROOF(ProofManager::currentPM()->addDependence(n, d_nodes[i]); - for (const auto& addnDep - : addnDeps) { - ProofManager::currentPM()->addDependence(n, addnDep); - }); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, d_nodes[i]); + for (const auto& addnDep : addnDeps) + { + ProofManager::currentPM()->addDependence(n, addnDep); + } + } if (isProofEnabled()) { d_pppg->notifyPreprocessed(d_nodes[i], n, pgen); diff --git a/src/preprocessing/passes/ackermann.cpp b/src/preprocessing/passes/ackermann.cpp index 31c92a09f..ab9c2482b 100644 --- a/src/preprocessing/passes/ackermann.cpp +++ b/src/preprocessing/passes/ackermann.cpp @@ -70,11 +70,14 @@ void addLemmaForPair(TNode args1, } else { - Assert(args1.getKind() == kind::SELECT && args1[0] == func); - Assert(args2.getKind() == kind::SELECT && args2[0] == func); + Assert(args1.getKind() == kind::SELECT && args1.getOperator() == func); + Assert(args2.getKind() == kind::SELECT && args2.getOperator() == func); Assert(args1.getNumChildren() == 2); Assert(args2.getNumChildren() == 2); - args_eq = nm->mkNode(kind::EQUAL, args1[1], args2[1]); + args_eq = nm->mkNode(Kind::AND, + nm->mkNode(kind::EQUAL, args1[0], args2[0]), + nm->mkNode(kind::EQUAL, args1[1], args2[1]) + ); } Node func_eq = nm->mkNode(kind::EQUAL, args1, args2); Node lemma = nm->mkNode(kind::IMPLIES, args_eq, func_eq); @@ -153,7 +156,7 @@ void collectFunctionsAndLemmas(FunctionToArgsMap& fun_to_args, if (seen.find(term) == seen.end()) { TNode func; - if (term.getKind() == kind::APPLY_UF) + if (term.getKind() == kind::APPLY_UF || term.getKind() == kind::SELECT) { storeFunctionAndAddLemmas(term.getOperator(), term, @@ -163,11 +166,6 @@ void collectFunctionsAndLemmas(FunctionToArgsMap& fun_to_args, nm, vec); } - else if (term.getKind() == kind::SELECT) - { - storeFunctionAndAddLemmas( - term[0], term, fun_to_args, fun_to_skolem, assertions, nm, vec); - } else { AlwaysAssert(term.getKind() != kind::STORE) diff --git a/src/preprocessing/passes/ite_simp.cpp b/src/preprocessing/passes/ite_simp.cpp index 9a6a8ec61..388c5742d 100644 --- a/src/preprocessing/passes/ite_simp.cpp +++ b/src/preprocessing/passes/ite_simp.cpp @@ -16,7 +16,6 @@ #include <vector> -#include "options/proof_options.h" #include "smt/smt_statistics_registry.h" #include "smt_util/nary_builder.h" #include "theory/arith/arith_ite_utils.h" @@ -118,7 +117,7 @@ bool ITESimp::doneSimpITE(AssertionPipeline* assertionsToPreprocess) // This pass does not support dependency tracking yet // (learns substitutions from all assertions so just // adding addDependence is not enough) - if (options::unsatCores() || options::fewerPreprocessingHoles()) + if (options::unsatCores()) { return true; } diff --git a/src/preprocessing/passes/miplib_trick.cpp b/src/preprocessing/passes/miplib_trick.cpp index f64fce118..3a8bbdb70 100644 --- a/src/preprocessing/passes/miplib_trick.cpp +++ b/src/preprocessing/passes/miplib_trick.cpp @@ -522,8 +522,10 @@ PreprocessingPassResult MipLibTrick::applyInternal( Node n = Rewriter::rewrite(geq.andNode(leq)); assertionsToPreprocess->push_back(n); - PROOF(ProofManager::currentPM()->addDependence(n, Node::null())); - + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, Node::null()); + } SubstitutionMap nullMap(&fakeContext); Theory::PPAssertStatus status CVC4_UNUSED; // just for assertions status = te->solve(geq, nullMap); @@ -591,9 +593,11 @@ PreprocessingPassResult MipLibTrick::applyInternal( Debug("miplib") << " " << newAssertion << endl; assertionsToPreprocess->push_back(newAssertion); - PROOF(ProofManager::currentPM()->addDependence(newAssertion, - Node::null())); - + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(newAssertion, + Node::null()); + } Debug("miplib") << " assertions to remove: " << endl; for (vector<TNode>::const_iterator k = asserts[pos_var].begin(), k_end = asserts[pos_var].end(); diff --git a/src/preprocessing/passes/non_clausal_simp.cpp b/src/preprocessing/passes/non_clausal_simp.cpp index 6d2482a0e..24c1ac67b 100644 --- a/src/preprocessing/passes/non_clausal_simp.cpp +++ b/src/preprocessing/passes/non_clausal_simp.cpp @@ -19,7 +19,6 @@ #include <vector> #include "context/cdo.h" -#include "options/proof_options.h" #include "smt/smt_statistics_registry.h" #include "theory/theory_model.h" @@ -54,7 +53,7 @@ NonClausalSimp::NonClausalSimp(PreprocessingPassContext* preprocContext) PreprocessingPassResult NonClausalSimp::applyInternal( AssertionPipeline* assertionsToPreprocess) { - Assert(!options::unsatCores() && !options::fewerPreprocessingHoles()); + Assert(!options::unsatCores()); d_preprocContext->spendResource(ResourceManager::Resource::PreprocessStep); @@ -98,11 +97,14 @@ PreprocessingPassResult NonClausalSimp::applyInternal( // If in conflict, just return false Trace("non-clausal-simplify") << "conflict in non-clausal propagation" << std::endl; - Assert(!options::unsatCores() && !options::fewerPreprocessingHoles()); + Assert(!options::unsatCores()); assertionsToPreprocess->clear(); Node n = NodeManager::currentNM()->mkConst<bool>(false); assertionsToPreprocess->push_back(n); - PROOF(ProofManager::currentPM()->addDependence(n, Node::null())); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, Node::null()); + } propagator->setNeedsFinish(true); return PreprocessingPassResult::CONFLICT; } @@ -164,7 +166,10 @@ PreprocessingPassResult NonClausalSimp::applyInternal( assertionsToPreprocess->clear(); Node n = NodeManager::currentNM()->mkConst<bool>(false); assertionsToPreprocess->push_back(n); - PROOF(ProofManager::currentPM()->addDependence(n, Node::null())); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, Node::null()); + } propagator->setNeedsFinish(true); return PreprocessingPassResult::CONFLICT; } @@ -207,7 +212,10 @@ PreprocessingPassResult NonClausalSimp::applyInternal( assertionsToPreprocess->clear(); Node n = NodeManager::currentNM()->mkConst<bool>(false); assertionsToPreprocess->push_back(n); - PROOF(ProofManager::currentPM()->addDependence(n, Node::null())); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, Node::null()); + } propagator->setNeedsFinish(true); return PreprocessingPassResult::CONFLICT; } @@ -241,7 +249,6 @@ PreprocessingPassResult NonClausalSimp::applyInternal( // equations[0].second); assertionsToPreprocess->clear(); // Node n = NodeManager::currentNM()->mkConst<bool>(false); // assertionsToPreprocess->push_back(n); - // PROOF(ProofManager::currentPM()->addDependence(n, Node::null())); // false); return; // } // top_level_substs.simplifyRHS(constantPropagations); diff --git a/src/preprocessing/passes/quantifier_macros.cpp b/src/preprocessing/passes/quantifier_macros.cpp index a4d8454a0..f4bc43542 100644 --- a/src/preprocessing/passes/quantifier_macros.cpp +++ b/src/preprocessing/passes/quantifier_macros.cpp @@ -19,7 +19,6 @@ #include <vector> #include "options/quantifiers_options.h" -#include "proof/proof_manager.h" #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "theory/arith/arith_msum.h" @@ -79,11 +78,14 @@ bool QuantifierMacros::simplify( std::vector< Node >& assertions, bool doRewrite for( int i=0; i<(int)assertions.size(); i++ ){ Trace("macros-debug") << " process assertion " << assertions[i] << std::endl; if( processAssertion( assertions[i] ) ){ - PROOF( - if( std::find( macro_assertions.begin(), macro_assertions.end(), assertions[i] )==macro_assertions.end() ){ - macro_assertions.push_back( assertions[i] ); - } - ); + if (options::unsatCores() + && std::find(macro_assertions.begin(), + macro_assertions.end(), + assertions[i]) + == macro_assertions.end()) + { + macro_assertions.push_back(assertions[i]); + } //process this assertion again i--; } @@ -98,17 +100,22 @@ bool QuantifierMacros::simplify( std::vector< Node >& assertions, bool doRewrite if( curr!=assertions[i] ){ curr = Rewriter::rewrite( curr ); Trace("macros-rewrite") << "Rewrite " << assertions[i] << " to " << curr << std::endl; - //for now, it is dependent upon all assertions involving macros, this is an over-approximation. - //a more fine-grained unsat core computation would require caching dependencies for each subterm of the formula, - // which is expensive. - PROOF(ProofManager::currentPM()->addDependence(curr, assertions[i]); - for (unsigned j = 0; j < macro_assertions.size(); j++) { - if (macro_assertions[j] != assertions[i]) - { - ProofManager::currentPM()->addDependence( - curr, macro_assertions[j]); - } - }); + // for now, it is dependent upon all assertions involving macros, this + // is an over-approximation. a more fine-grained unsat core + // computation would require caching dependencies for each subterm of + // the formula, which is expensive. + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(curr, assertions[i]); + for (unsigned j = 0; j < macro_assertions.size(); j++) + { + if (macro_assertions[j] != assertions[i]) + { + ProofManager::currentPM()->addDependence(curr, + macro_assertions[j]); + } + } + } assertions[i] = curr; retVal = true; } @@ -432,9 +439,9 @@ Node QuantifierMacros::simplify( Node n ){ for( unsigned i=0; i<children.size(); i++ ){ Node etc = TypeNode::getEnsureTypeCondition( children[i], tno[i] ); if( etc.isNull() ){ - //if this does fail, we are incomplete, since we are eliminating quantified formula corresponding to op, + // if this does fail, we are incomplete, since we are eliminating + // quantified formula corresponding to op, // and not ensuring it applies to n when its types are correct. - //Assert( false ); success = false; break; }else if( !etc.isConst() ){ diff --git a/src/printer/ast/ast_printer.cpp b/src/printer/ast/ast_printer.cpp index d4f28c186..f235721f1 100644 --- a/src/printer/ast/ast_printer.cpp +++ b/src/printer/ast/ast_printer.cpp @@ -26,6 +26,7 @@ #include "options/language.h" // for LANG_AST #include "printer/dagification_visitor.h" #include "smt/command.h" +#include "smt/node_command.h" #include "theory/substitutions.h" using namespace std; @@ -156,7 +157,7 @@ void AstPrinter::toStream(std::ostream& out, const Model& m) const void AstPrinter::toStream(std::ostream& out, const Model& m, - const Command* c) const + const NodeCommand* c) const { // shouldn't be called; only the non-Command* version above should be Unreachable(); diff --git a/src/printer/ast/ast_printer.h b/src/printer/ast/ast_printer.h index 17e052037..969240930 100644 --- a/src/printer/ast/ast_printer.h +++ b/src/printer/ast/ast_printer.h @@ -175,7 +175,7 @@ class AstPrinter : public CVC4::Printer void toStream(std::ostream& out, TNode n, int toDepth, bool types) const; void toStream(std::ostream& out, const Model& m, - const Command* c) const override; + const NodeCommand* c) const override; }; /* class AstPrinter */ } // namespace ast diff --git a/src/printer/cvc/cvc_printer.cpp b/src/printer/cvc/cvc_printer.cpp index 89b516511..b94977cfe 100644 --- a/src/printer/cvc/cvc_printer.cpp +++ b/src/printer/cvc/cvc_printer.cpp @@ -33,6 +33,7 @@ #include "options/smt_options.h" #include "printer/dagification_visitor.h" #include "smt/command.h" +#include "smt/node_command.h" #include "smt/smt_engine.h" #include "theory/arrays/theory_arrays_rewriter.h" #include "theory/substitutions.h" @@ -1059,11 +1060,11 @@ void CvcPrinter::toStream(std::ostream& out, const CommandStatus* s) const namespace { -void DeclareTypeCommandToStream(std::ostream& out, - const theory::TheoryModel& model, - const DeclareTypeCommand& command) +void DeclareTypeNodeCommandToStream(std::ostream& out, + const theory::TheoryModel& model, + const DeclareTypeNodeCommand& command) { - TypeNode type_node = TypeNode::fromType(command.getType()); + TypeNode type_node = command.getType(); const std::vector<Node>* type_reps = model.getRepSet()->getTypeRepsOrNull(type_node); if (options::modelUninterpDtEnum() && type_node.isSort() @@ -1104,11 +1105,12 @@ void DeclareTypeCommandToStream(std::ostream& out, } } -void DeclareFunctionCommandToStream(std::ostream& out, - const theory::TheoryModel& model, - const DeclareFunctionCommand& command) +void DeclareFunctionNodeCommandToStream( + std::ostream& out, + const theory::TheoryModel& model, + const DeclareFunctionNodeCommand& command) { - Node n = Node::fromExpr(command.getFunction()); + Node n = command.getFunction(); if (n.getKind() == kind::SKOLEM) { // don't print out internal stuff @@ -1172,23 +1174,23 @@ void CvcPrinter::toStream(std::ostream& out, const Model& m) const void CvcPrinter::toStream(std::ostream& out, const Model& model, - const Command* command) const + const NodeCommand* command) const { const auto* theory_model = dynamic_cast<const theory::TheoryModel*>(&model); AlwaysAssert(theory_model != nullptr); if (const auto* declare_type_command = - dynamic_cast<const DeclareTypeCommand*>(command)) + dynamic_cast<const DeclareTypeNodeCommand*>(command)) { - DeclareTypeCommandToStream(out, *theory_model, *declare_type_command); + DeclareTypeNodeCommandToStream(out, *theory_model, *declare_type_command); } else if (const auto* dfc = - dynamic_cast<const DeclareFunctionCommand*>(command)) + dynamic_cast<const DeclareFunctionNodeCommand*>(command)) { - DeclareFunctionCommandToStream(out, *theory_model, *dfc); + DeclareFunctionNodeCommandToStream(out, *theory_model, *dfc); } else { - out << command << std::endl; + out << *command << std::endl; } } diff --git a/src/printer/cvc/cvc_printer.h b/src/printer/cvc/cvc_printer.h index 0fd3d3a49..3c61fb74f 100644 --- a/src/printer/cvc/cvc_printer.h +++ b/src/printer/cvc/cvc_printer.h @@ -177,7 +177,7 @@ class CvcPrinter : public CVC4::Printer std::ostream& out, TNode n, int toDepth, bool types, bool bracket) const; void toStream(std::ostream& out, const Model& m, - const Command* c) const override; + const NodeCommand* c) const override; bool d_cvc3Mode; }; /* class CvcPrinter */ diff --git a/src/printer/printer.cpp b/src/printer/printer.cpp index 0e7550518..d13fc55f1 100644 --- a/src/printer/printer.cpp +++ b/src/printer/printer.cpp @@ -23,6 +23,7 @@ #include "printer/cvc/cvc_printer.h" #include "printer/smt2/smt2_printer.h" #include "printer/tptp/tptp_printer.h" +#include "smt/node_command.h" using namespace std; @@ -72,9 +73,10 @@ unique_ptr<Printer> Printer::makePrinter(OutputLanguage lang) void Printer::toStream(std::ostream& out, const Model& m) const { for(size_t i = 0; i < m.getNumCommands(); ++i) { - const Command* cmd = m.getCommand(i); - const DeclareFunctionCommand* dfc = dynamic_cast<const DeclareFunctionCommand*>(cmd); - if (dfc != NULL && !m.isModelCoreSymbol(dfc->getFunction())) + const NodeCommand* cmd = m.getCommand(i); + const DeclareFunctionNodeCommand* dfc = + dynamic_cast<const DeclareFunctionNodeCommand*>(cmd); + if (dfc != NULL && !m.isModelCoreSymbol(dfc->getFunction().toExpr())) { continue; } diff --git a/src/printer/printer.h b/src/printer/printer.h index 3b737ec5f..8c95e3e9b 100644 --- a/src/printer/printer.h +++ b/src/printer/printer.h @@ -30,6 +30,8 @@ namespace CVC4 { +class NodeCommand; + class Printer { public: @@ -271,13 +273,13 @@ class Printer /** write model response to command */ virtual void toStream(std::ostream& out, const Model& m, - const Command* c) const = 0; + const NodeCommand* c) const = 0; /** write model response to command using another language printer */ void toStreamUsing(OutputLanguage lang, std::ostream& out, const Model& m, - const Command* c) const + const NodeCommand* c) const { getPrinter(lang)->toStream(out, m, c); } diff --git a/src/printer/smt2/smt2_printer.cpp b/src/printer/smt2/smt2_printer.cpp index 3d76c81dc..da0423956 100644 --- a/src/printer/smt2/smt2_printer.cpp +++ b/src/printer/smt2/smt2_printer.cpp @@ -31,11 +31,12 @@ #include "options/printer_options.h" #include "options/smt_options.h" #include "printer/dagification_visitor.h" +#include "smt/node_command.h" #include "smt/smt_engine.h" #include "smt_util/boolean_simplification.h" #include "theory/arrays/theory_arrays_rewriter.h" -#include "theory/quantifiers/quantifiers_attributes.h" #include "theory/datatypes/sygus_datatype_utils.h" +#include "theory/quantifiers/quantifiers_attributes.h" #include "theory/substitutions.h" #include "theory/theory_model.h" #include "util/smt2_quote_string.h" @@ -1331,23 +1332,23 @@ void Smt2Printer::toStream(std::ostream& out, const Model& m) const void Smt2Printer::toStream(std::ostream& out, const Model& model, - const Command* command) const + const NodeCommand* command) const { const theory::TheoryModel* theory_model = dynamic_cast<const theory::TheoryModel*>(&model); AlwaysAssert(theory_model != nullptr); - if (const DeclareTypeCommand* dtc = - dynamic_cast<const DeclareTypeCommand*>(command)) + if (const DeclareTypeNodeCommand* dtc = + dynamic_cast<const DeclareTypeNodeCommand*>(command)) { // print out the DeclareTypeCommand - Type t = (*dtc).getType(); - if (!t.isSort()) + TypeNode tn = dtc->getType(); + if (!tn.isSort()) { out << (*dtc) << endl; } else { - std::vector<Expr> elements = theory_model->getDomainElements(t); + std::vector<Expr> elements = theory_model->getDomainElements(tn.toType()); if (options::modelUninterpDtEnum()) { if (isVariant_2_6(d_variant)) @@ -1367,7 +1368,7 @@ void Smt2Printer::toStream(std::ostream& out, else { // print the cardinality - out << "; cardinality of " << t << " is " << elements.size() << endl; + out << "; cardinality of " << tn << " is " << elements.size() << endl; out << (*dtc) << endl; // print the representatives for (const Expr& type_ref : elements) @@ -1375,7 +1376,7 @@ void Smt2Printer::toStream(std::ostream& out, Node trn = Node::fromExpr(type_ref); if (trn.isVar()) { - out << "(declare-fun " << quoteSymbol(trn) << " () " << t << ")" + out << "(declare-fun " << quoteSymbol(trn) << " () " << tn << ")" << endl; } else @@ -1386,11 +1387,11 @@ void Smt2Printer::toStream(std::ostream& out, } } } - else if (const DeclareFunctionCommand* dfc = - dynamic_cast<const DeclareFunctionCommand*>(command)) + else if (const DeclareFunctionNodeCommand* dfc = + dynamic_cast<const DeclareFunctionNodeCommand*>(command)) { // print out the DeclareFunctionCommand - Node n = Node::fromExpr((*dfc).getFunction()); + Node n = dfc->getFunction(); if ((*dfc).getPrintInModelSetByUser()) { if (!(*dfc).getPrintInModel()) @@ -1432,10 +1433,10 @@ void Smt2Printer::toStream(std::ostream& out, out << ")" << endl; } } - else if (const DatatypeDeclarationCommand* datatype_declaration_command = - dynamic_cast<const DatatypeDeclarationCommand*>(command)) + else if (const DeclareDatatypeNodeCommand* declare_datatype_command = + dynamic_cast<const DeclareDatatypeNodeCommand*>(command)) { - out << datatype_declaration_command; + out << *declare_datatype_command; } else { diff --git a/src/printer/smt2/smt2_printer.h b/src/printer/smt2/smt2_printer.h index 6b57823a4..0cf06dd6b 100644 --- a/src/printer/smt2/smt2_printer.h +++ b/src/printer/smt2/smt2_printer.h @@ -19,8 +19,6 @@ #ifndef CVC4__PRINTER__SMT2_PRINTER_H #define CVC4__PRINTER__SMT2_PRINTER_H -#include <iostream> - #include "printer/printer.h" namespace CVC4 { @@ -234,7 +232,7 @@ class Smt2Printer : public CVC4::Printer std::ostream& out, TNode n, int toDepth, bool types, TypeNode nt) const; void toStream(std::ostream& out, const Model& m, - const Command* c) const override; + const NodeCommand* c) const override; void toStream(std::ostream& out, const SExpr& sexpr) const; void toStream(std::ostream& out, const DType& dt) const; diff --git a/src/printer/tptp/tptp_printer.cpp b/src/printer/tptp/tptp_printer.cpp index c4623f76a..d25666d70 100644 --- a/src/printer/tptp/tptp_printer.cpp +++ b/src/printer/tptp/tptp_printer.cpp @@ -20,12 +20,13 @@ #include <typeinfo> #include <vector> -#include "expr/expr.h" // for ExprSetDepth etc.. -#include "expr/node_manager.h" // for VarNameAttr -#include "options/language.h" // for LANG_AST -#include "options/smt_options.h" // for unsat cores -#include "smt/smt_engine.h" +#include "expr/expr.h" // for ExprSetDepth etc.. +#include "expr/node_manager.h" // for VarNameAttr +#include "options/language.h" // for LANG_AST +#include "options/smt_options.h" // for unsat cores #include "smt/command.h" +#include "smt/node_command.h" +#include "smt/smt_engine.h" using namespace std; @@ -59,7 +60,7 @@ void TptpPrinter::toStream(std::ostream& out, const Model& m) const void TptpPrinter::toStream(std::ostream& out, const Model& m, - const Command* c) const + const NodeCommand* c) const { // shouldn't be called; only the non-Command* version above should be Unreachable(); diff --git a/src/printer/tptp/tptp_printer.h b/src/printer/tptp/tptp_printer.h index 6682b495e..9377a8895 100644 --- a/src/printer/tptp/tptp_printer.h +++ b/src/printer/tptp/tptp_printer.h @@ -47,7 +47,7 @@ class TptpPrinter : public CVC4::Printer private: void toStream(std::ostream& out, const Model& m, - const Command* c) const override; + const NodeCommand* c) const override; }; /* class TptpPrinter */ diff --git a/src/proof/arith_proof.cpp b/src/proof/arith_proof.cpp deleted file mode 100644 index 635767b97..000000000 --- a/src/proof/arith_proof.cpp +++ /dev/null @@ -1,1207 +0,0 @@ -/********************* */ -/*! \file arith_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Liana Hadarean, Guy Katz - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ -#include "proof/arith_proof.h" - -#include <memory> -#include <stack> - -#include "base/check.h" -#include "expr/node.h" -#include "expr/type_checker_util.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" -#include "theory/arith/constraint_forward.h" -#include "theory/arith/normal_form.h" -#include "theory/arith/theory_arith.h" - -#define CVC4_ARITH_VAR_TERM_PREFIX "term." - -namespace CVC4 { - -inline static Node eqNode(TNode n1, TNode n2) { - return NodeManager::currentNM()->mkNode(kind::EQUAL, n1, n2); -} - -// congrence matching term helper -inline static bool match(TNode n1, TNode n2) { - Debug("pf::arith") << "match " << n1 << " " << n2 << std::endl; - if(ProofManager::currentPM()->hasOp(n1)) { - n1 = ProofManager::currentPM()->lookupOp(n1); - } - if(ProofManager::currentPM()->hasOp(n2)) { - n2 = ProofManager::currentPM()->lookupOp(n2); - } - Debug("pf::arith") << "+ match " << n1 << " " << n2 << std::endl; - if(n1 == n2) { - return true; - } - if(n1.getType().isFunction() && n2.hasOperator()) { - if(ProofManager::currentPM()->hasOp(n2.getOperator())) { - return n1 == ProofManager::currentPM()->lookupOp(n2.getOperator()); - } else { - return n1 == n2.getOperator(); - } - } - if(n2.getType().isFunction() && n1.hasOperator()) { - if(ProofManager::currentPM()->hasOp(n1.getOperator())) { - return n2 == ProofManager::currentPM()->lookupOp(n1.getOperator()); - } else { - return n2 == n1.getOperator(); - } - } - if(n1.hasOperator() && n2.hasOperator() && n1.getOperator() != n2.getOperator()) { - return false; - } - for(size_t i = 0; i < n1.getNumChildren() && i < n2.getNumChildren(); ++i) { - if(n1[i] != n2[i]) { - return false; - } - } - return true; -} - -void ProofArith::toStream(std::ostream& out) const -{ - Trace("theory-proof-debug") << "; Print Arith proof..." << std::endl; - //AJR : carry this further? - ProofLetMap map; - toStreamLFSC(out, ProofManager::getArithProof(), *d_proof, map); -} - -void ProofArith::toStreamLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - const ProofLetMap& map) -{ - Debug("lfsc-arith") << "Printing arith proof in LFSC : " << std::endl; - pf.debug_print("lfsc-arith"); - Debug("lfsc-arith") << std::endl; - toStreamRecLFSC(out, tp, pf, 0, map); -} - -Node ProofArith::toStreamRecLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - unsigned tb, - const ProofLetMap& map) -{ - Debug("pf::arith") << std::endl - << std::endl - << "toStreamRecLFSC called. tb = " << tb - << " . proof:" << std::endl; - pf.debug_print("pf::arith"); - Debug("pf::arith") << std::endl; - - if(tb == 0) { - Assert(pf.d_id == theory::eq::MERGED_THROUGH_TRANS); - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.size() >= 2); - - int neg = -1; - std::shared_ptr<theory::eq::EqProof> subTrans = - std::make_shared<theory::eq::EqProof>(); - subTrans->d_id = theory::eq::MERGED_THROUGH_TRANS; - subTrans->d_node = pf.d_node; - - size_t i = 0; - while (i < pf.d_children.size()) { - // Look for the negative clause, with which we will form a contradiction. - if(!pf.d_children[i]->d_node.isNull() && pf.d_children[i]->d_node.getKind() == kind::NOT) { - Assert(neg < 0); - neg = i; - ++i; - } - - // Handle congruence closures over equalities. - else if (pf.d_children[i]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && pf.d_children[i]->d_node.isNull()) { - Debug("pf::arith") << "Handling congruence over equalities" << std::endl; - - // Gather the sequence of consecutive congruence closures. - std::vector<std::shared_ptr<const theory::eq::EqProof>> congruenceClosures; - unsigned count; - Debug("pf::arith") << "Collecting congruence sequence" << std::endl; - for (count = 0; - i + count < pf.d_children.size() && - pf.d_children[i + count]->d_id==theory::eq::MERGED_THROUGH_CONGRUENCE && - pf.d_children[i + count]->d_node.isNull(); - ++count) { - Debug("pf::arith") << "Found a congruence: " << std::endl; - pf.d_children[i+count]->debug_print("pf::arith"); - congruenceClosures.push_back(pf.d_children[i+count]); - } - - Debug("pf::arith") << "Total number of congruences found: " << congruenceClosures.size() << std::endl; - - // Determine if the "target" of the congruence sequence appears right before or right after the sequence. - bool targetAppearsBefore = true; - bool targetAppearsAfter = true; - - if ((i == 0) || (i == 1 && neg == 0)) { - Debug("pf::arith") << "Target does not appear before" << std::endl; - targetAppearsBefore = false; - } - - if ((i + count >= pf.d_children.size()) || - (!pf.d_children[i + count]->d_node.isNull() && - pf.d_children[i + count]->d_node.getKind() == kind::NOT)) { - Debug("pf::arith") << "Target does not appear after" << std::endl; - targetAppearsAfter = false; - } - - // Assert that we have precisely one target clause. - Assert(targetAppearsBefore != targetAppearsAfter); - - // Begin breaking up the congruences and ordering the equalities correctly. - std::vector<std::shared_ptr<theory::eq::EqProof>> orderedEqualities; - - - // Insert target clause first. - if (targetAppearsBefore) { - orderedEqualities.push_back(pf.d_children[i - 1]); - // The target has already been added to subTrans; remove it. - subTrans->d_children.pop_back(); - } else { - orderedEqualities.push_back(pf.d_children[i + count]); - } - - // Start with the congruence closure closest to the target clause, and work our way back/forward. - if (targetAppearsBefore) { - for (unsigned j = 0; j < count; ++j) { - if (pf.d_children[i + j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert(orderedEqualities.begin(), pf.d_children[i + j]->d_children[0]); - if (pf.d_children[i + j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert(orderedEqualities.end(), pf.d_children[i + j]->d_children[1]); - } - } else { - for (unsigned j = 0; j < count; ++j) { - if (pf.d_children[i + count - 1 - j]->d_children[0]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert(orderedEqualities.begin(), pf.d_children[i + count - 1 - j]->d_children[0]); - if (pf.d_children[i + count - 1 - j]->d_children[1]->d_id != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert(orderedEqualities.end(), pf.d_children[i + count - 1 - j]->d_children[1]); - } - } - - // Copy the result into the main transitivity proof. - subTrans->d_children.insert(subTrans->d_children.end(), orderedEqualities.begin(), orderedEqualities.end()); - - // Increase i to skip over the children that have been processed. - i += count; - if (targetAppearsAfter) { - ++i; - } - } - - // Else, just copy the child proof as is - else { - subTrans->d_children.push_back(pf.d_children[i]); - ++i; - } - } - Assert(neg >= 0); - - Node n1; - std::stringstream ss; - //Assert(subTrans->d_children.size() == pf.d_children.size() - 1); - Debug("pf::arith") << "\nsubtrans has " << subTrans->d_children.size() << " children\n"; - if(pf.d_children.size() > 2) { - n1 = toStreamRecLFSC(ss, tp, *subTrans, 1, map); - } else { - n1 = toStreamRecLFSC(ss, tp, *(subTrans->d_children[0]), 1, map); - Debug("pf::arith") << "\nsubTrans unique child " << subTrans->d_children[0]->d_id << " was proven\ngot: " << n1 << std::endl; - } - - Node n2 = pf.d_children[neg]->d_node; - Assert(n2.getKind() == kind::NOT); - out << "(clausify_false (contra _ "; - Debug("pf::arith") << "\nhave proven: " << n1 << std::endl; - Debug("pf::arith") << "n2 is " << n2[0] << std::endl; - - if (n2[0].getNumChildren() > 0) { Debug("pf::arith") << "\nn2[0]: " << n2[0][0] << std::endl; } - if (n1.getNumChildren() > 1) { Debug("pf::arith") << "n1[1]: " << n1[1] << std::endl; } - - if(n2[0].getKind() == kind::APPLY_UF) { - out << "(trans _ _ _ _ "; - out << "(symm _ _ _ "; - out << ss.str(); - out << ") (pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; - } else { - Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) - || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); - if(n1[1] == n2[0][0]) { - out << "(symm _ _ _ " << ss.str() << ")"; - } else { - out << ss.str(); - } - out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; - } - return Node(); - } - - switch(pf.d_id) { - case theory::eq::MERGED_THROUGH_CONGRUENCE: { - Debug("pf::arith") << "\nok, looking at congruence:\n"; - pf.debug_print("pf::arith"); - std::stack<const theory::eq::EqProof*> stk; - for (const theory::eq::EqProof* pf2 = &pf; - pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; - pf2 = pf2->d_children[0].get()) { - Assert(!pf2->d_node.isNull()); - Assert(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF - || pf2->d_node.getKind() == kind::BUILTIN - || pf2->d_node.getKind() == kind::APPLY_UF - || pf2->d_node.getKind() == kind::SELECT - || pf2->d_node.getKind() == kind::STORE); - Assert(pf2->d_children.size() == 2); - out << "(cong _ _ _ _ _ _ "; - stk.push(pf2); - } - Assert(stk.top()->d_children[0]->d_id - != theory::eq::MERGED_THROUGH_CONGRUENCE); - NodeBuilder<> b1(kind::PARTIAL_APPLY_UF), b2(kind::PARTIAL_APPLY_UF); - const theory::eq::EqProof* pf2 = stk.top(); - stk.pop(); - Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); - Node n1 = toStreamRecLFSC(out, tp, *(pf2->d_children[0]), tb + 1, map); - out << " "; - std::stringstream ss; - Node n2 = toStreamRecLFSC(ss, tp, *(pf2->d_children[1]), tb + 1, map); - Debug("pf::arith") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; - pf2->debug_print("pf::arith"); - Debug("pf::arith") << "looking at " << pf2->d_node << "\n"; - Debug("pf::arith") << " " << n1 << "\n"; - Debug("pf::arith") << " " << n2 << "\n"; - int side = 0; - if(match(pf2->d_node, n1[0])) { - //if(tb == 1) { - Debug("pf::arith") << "SIDE IS 0\n"; - //} - side = 0; - } else { - //if(tb == 1) { - Debug("pf::arith") << "SIDE IS 1\n"; - //} - if(!match(pf2->d_node, n1[1])) { - Debug("pf::arith") << "IN BAD CASE, our first subproof is\n"; - pf2->d_children[0]->debug_print("pf::arith"); - } - Assert(match(pf2->d_node, n1[1])); - side = 1; - } - if(n1[side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::PARTIAL_APPLY_UF || n1[side].getKind() == kind::SELECT || n1[side].getKind() == kind::STORE) { - if(n1[side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::PARTIAL_APPLY_UF) { - b1 << n1[side].getOperator(); - } else { - b1 << ProofManager::currentPM()->mkOp(n1[side].getOperator()); - } - b1.append(n1[side].begin(), n1[side].end()); - } else { - b1 << n1[side]; - } - if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || n1[1-side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::SELECT || n1[side].getKind() == kind::STORE) { - if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || n1[1-side].getKind() == kind::APPLY_UF) { - b2 << n1[1-side].getOperator(); - } else { - b2 << ProofManager::currentPM()->mkOp(n1[1-side].getOperator()); - } - b2.append(n1[1-side].begin(), n1[1-side].end()); - } else { - b2 << n1[1-side]; - } - Debug("pf::arith") << "pf2->d_node " << pf2->d_node << std::endl; - Debug("pf::arith") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; - Debug("pf::arith") << "n1 " << n1 << std::endl; - Debug("pf::arith") << "n2 " << n2 << std::endl; - Debug("pf::arith") << "side " << side << std::endl; - if(pf2->d_node[b1.getNumChildren() - (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[side]) { - b1 << n2[side]; - b2 << n2[1-side]; - out << ss.str(); - } else { - Assert(pf2->d_node[b1.getNumChildren() - - (pf2->d_node.getMetaKind() - == kind::metakind::PARAMETERIZED - ? 0 - : 1)] - == n2[1 - side]); - b1 << n2[1-side]; - b2 << n2[side]; - out << "(symm _ _ _ " << ss.str() << ")"; - } - out << ")"; - while(!stk.empty()) { - if(tb == 1) { - Debug("pf::arith") << "\nMORE TO DO\n"; - } - pf2 = stk.top(); - stk.pop(); - Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); - out << " "; - ss.str(""); - n2 = toStreamRecLFSC(ss, tp, *(pf2->d_children[1]), tb + 1, map); - Debug("pf::arith") << "\nok, in cong[" << stk.size() << "]" << "\n"; - Debug("pf::arith") << "looking at " << pf2->d_node << "\n"; - Debug("pf::arith") << " " << n1 << "\n"; - Debug("pf::arith") << " " << n2 << "\n"; - Debug("pf::arith") << " " << b1 << "\n"; - Debug("pf::arith") << " " << b2 << "\n"; - if(pf2->d_node[b1.getNumChildren()] == n2[side]) { - b1 << n2[side]; - b2 << n2[1-side]; - out << ss.str(); - } else { - Assert(pf2->d_node[b1.getNumChildren()] == n2[1 - side]); - b1 << n2[1-side]; - b2 << n2[side]; - out << "(symm _ _ _ " << ss.str() << ")"; - } - out << ")"; - } - n1 = b1; - n2 = b2; - Debug("pf::arith") << "at end assert, got " << pf2->d_node << " and " << n1 << std::endl; - if(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF) { - Assert(n1 == pf2->d_node); - } - if(n1.getOperator().getType().getNumChildren() == n1.getNumChildren() + 1) { - if(ProofManager::currentPM()->hasOp(n1.getOperator())) { - b1.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst<Kind>()); - } else { - b1.clear(kind::APPLY_UF); - b1 << n1.getOperator(); - } - b1.append(n1.begin(), n1.end()); - n1 = b1; - Debug("pf::arith") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; - if(pf2->d_node.getKind() == kind::APPLY_UF) { - Assert(n1 == pf2->d_node); - } - } - if(n2.getOperator().getType().getNumChildren() == n2.getNumChildren() + 1) { - if(ProofManager::currentPM()->hasOp(n2.getOperator())) { - b2.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst<Kind>()); - } else { - b2.clear(kind::APPLY_UF); - b2 << n2.getOperator(); - } - b2.append(n2.begin(), n2.end()); - n2 = b2; - } - Node n = (side == 0 ? eqNode(n1, n2) : eqNode(n2, n1)); - if(tb == 1) { - Debug("pf::arith") << "\ncong proved: " << n << "\n"; - } - return n; - } - - case theory::eq::MERGED_THROUGH_REFLEXIVITY: - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - out << "(refl _ "; - tp->printTerm(NodeManager::currentNM()->toExpr(pf.d_node), out, map); - out << ")"; - return eqNode(pf.d_node, pf.d_node); - - case theory::eq::MERGED_THROUGH_EQUALITY: - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - out << ProofManager::getLitName(pf.d_node.negate()); - return pf.d_node; - - case theory::eq::MERGED_THROUGH_TRANS: { - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.size() >= 2); - std::stringstream ss; - Debug("pf::arith") << "\ndoing trans proof[[\n"; - pf.debug_print("pf::arith"); - Debug("pf::arith") << "\n"; - Node n1 = toStreamRecLFSC(ss, tp, *(pf.d_children[0]), tb + 1, map); - Debug("pf::arith") << "\ndoing trans proof, got n1 " << n1 << "\n"; - if(tb == 1) { - Debug("pf::arith") << "\ntrans proof[0], got n1 " << n1 << "\n"; - } - - bool identicalEqualities = false; - bool evenLengthSequence; - Node nodeAfterEqualitySequence; - - std::map<size_t, Node> childToStream; - - for(size_t i = 1; i < pf.d_children.size(); ++i) { - std::stringstream ss1(ss.str()), ss2; - ss.str(""); - - // It is possible that we've already converted the i'th child to stream. If so, - // use previously stored result. Otherwise, convert and store. - Node n2; - if (childToStream.find(i) != childToStream.end()) - n2 = childToStream[i]; - else { - n2 = toStreamRecLFSC(ss2, tp, *(pf.d_children[i]), tb + 1, map); - childToStream[i] = n2; - } - - // The following branch is dedicated to handling sequences of identical equalities, - // i.e. trans[ a=b, a=b, a=b ]. - // - // There are two cases: - // 1. The number of equalities is odd. Then, the sequence can be collapsed to just one equality, - // i.e. a=b. - // 2. The number of equalities is even. Now, we have two options: a=a or b=b. To determine this, - // we look at the node after the equality sequence. If it needs a, we go for a=a; and if it needs - // b, we go for b=b. If there is no following node, we look at the goal of the transitivity proof, - // and use it to determine which option we need. - if(n2.getKind() == kind::EQUAL) { - if (((n1[0] == n2[0]) && (n1[1] == n2[1])) || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) { - // We are in a sequence of identical equalities - - Debug("pf::arith") << "Detected identical equalities: " << std::endl << "\t" << n1 << std::endl; - - if (!identicalEqualities) { - // The sequence of identical equalities has started just now - identicalEqualities = true; - - Debug("pf::arith") << "The sequence is just beginning. Determining length..." << std::endl; - - // Determine whether the length of this sequence is odd or even. - evenLengthSequence = true; - bool sequenceOver = false; - size_t j = i + 1; - - while (j < pf.d_children.size() && !sequenceOver) { - std::stringstream dontCare; - nodeAfterEqualitySequence = toStreamRecLFSC( - dontCare, tp, *(pf.d_children[j]), tb + 1, map); - - if (((nodeAfterEqualitySequence[0] == n1[0]) && (nodeAfterEqualitySequence[1] == n1[1])) || - ((nodeAfterEqualitySequence[0] == n1[1]) && (nodeAfterEqualitySequence[1] == n1[0]))) { - evenLengthSequence = !evenLengthSequence; - } else { - sequenceOver = true; - } - - ++j; - } - - if (evenLengthSequence) { - // If the length is even, we need to apply transitivity for the "correct" hand of the equality. - - Debug("pf::arith") << "Equality sequence of even length" << std::endl; - Debug("pf::arith") << "n1 is: " << n1 << std::endl; - Debug("pf::arith") << "n2 is: " << n2 << std::endl; - Debug("pf::arith") << "pf-d_node is: " << pf.d_node << std::endl; - Debug("pf::arith") << "Next node is: " << nodeAfterEqualitySequence << std::endl; - - ss << "(trans _ _ _ _ "; - - // If the sequence is at the very end of the transitivity proof, use pf.d_node to guide us. - if (!sequenceOver) { - if (match(n1[0], pf.d_node[0])) { - n1 = eqNode(n1[0], n1[0]); - ss << ss1.str() << " (symm _ _ _ " << ss1.str() << ")"; - } else if (match(n1[1], pf.d_node[1])) { - n1 = eqNode(n1[1], n1[1]); - ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); - } else { - Debug("pf::arith") << "Error: identical equalities over, but hands don't match what we're proving." - << std::endl; - Assert(false); - } - } else { - // We have a "next node". Use it to guide us. - - Assert(nodeAfterEqualitySequence.getKind() == kind::EQUAL); - - if ((n1[0] == nodeAfterEqualitySequence[0]) || (n1[0] == nodeAfterEqualitySequence[1])) { - - // Eliminate n1[1] - ss << ss1.str() << " (symm _ _ _ " << ss1.str() << ")"; - n1 = eqNode(n1[0], n1[0]); - - } else if ((n1[1] == nodeAfterEqualitySequence[0]) || (n1[1] == nodeAfterEqualitySequence[1])) { - - // Eliminate n1[0] - ss << " (symm _ _ _ " << ss1.str() << ")" << ss1.str(); - n1 = eqNode(n1[1], n1[1]); - - } else { - Debug("pf::arith") << "Error: even length sequence, but I don't know which hand to keep!" << std::endl; - Assert(false); - } - } - - ss << ")"; - - } else { - Debug("pf::arith") << "Equality sequence length is odd!" << std::endl; - ss.str(ss1.str()); - } - - Debug("pf::arith") << "Have proven: " << n1 << std::endl; - } else { - ss.str(ss1.str()); - } - - // Ignore the redundancy. - continue; - } - } - - if (identicalEqualities) { - // We were in a sequence of identical equalities, but it has now ended. Resume normal operation. - identicalEqualities = false; - } - - Debug("pf::arith") << "\ndoing trans proof, got n2 " << n2 << "\n"; - if(tb == 1) { - Debug("pf::arith") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; - Debug("pf::arith") << (n2.getKind() == kind::EQUAL) << "\n"; - - if ((n1.getNumChildren() >= 2) && (n2.getNumChildren() >= 2)) { - Debug("pf::arith") << n1[0].getId() << " " << n1[1].getId() << " / " << n2[0].getId() << " " << n2[1].getId() << "\n"; - Debug("pf::arith") << n1[0].getId() << " " << n1[0] << "\n"; - Debug("pf::arith") << n1[1].getId() << " " << n1[1] << "\n"; - Debug("pf::arith") << n2[0].getId() << " " << n2[0] << "\n"; - Debug("pf::arith") << n2[1].getId() << " " << n2[1] << "\n"; - Debug("pf::arith") << (n1[0] == n2[0]) << "\n"; - Debug("pf::arith") << (n1[1] == n2[1]) << "\n"; - Debug("pf::arith") << (n1[0] == n2[1]) << "\n"; - Debug("pf::arith") << (n1[1] == n2[0]) << "\n"; - } - } - ss << "(trans _ _ _ _ "; - - if((n2.getKind() == kind::EQUAL) && (n1.getKind() == kind::EQUAL)) - // Both elements of the transitivity rule are equalities/iffs - { - if(n1[0] == n2[0]) { - if(tb == 1) { Debug("pf::arith") << "case 1\n"; } - n1 = eqNode(n1[1], n2[1]); - ss << "(symm _ _ _ " << ss1.str() << ") " << ss2.str(); - } else if(n1[1] == n2[1]) { - if(tb == 1) { Debug("pf::arith") << "case 2\n"; } - n1 = eqNode(n1[0], n2[0]); - ss << ss1.str() << " (symm _ _ _ " << ss2.str() << ")"; - } else if(n1[0] == n2[1]) { - if(tb == 1) { Debug("pf::arith") << "case 3\n"; } - n1 = eqNode(n2[0], n1[1]); - ss << ss2.str() << " " << ss1.str(); - if(tb == 1) { Debug("pf::arith") << "++ proved " << n1 << "\n"; } - } else if(n1[1] == n2[0]) { - if(tb == 1) { Debug("pf::arith") << "case 4\n"; } - n1 = eqNode(n1[0], n2[1]); - ss << ss1.str() << " " << ss2.str(); - } else { - Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; - Warning() << "0 proves " << n1 << "\n"; - Warning() << "1 proves " << n2 << "\n\n"; - pf.debug_print("pf::arith",0); - //toStreamRec(Warning.getStream(), pf, 0); - Warning() << "\n\n"; - Unreachable(); - } - Debug("pf::arith") << "++ trans proof[" << i << "], now have " << n1 << std::endl; - } else if(n1.getKind() == kind::EQUAL) { - // n1 is an equality/iff, but n2 is a predicate - if(n1[0] == n2) { - n1 = n1[1]; - ss << "(symm _ _ _ " << ss1.str() << ") (pred_eq_t _ " << ss2.str() << ")"; - } else if(n1[1] == n2) { - n1 = n1[0]; - ss << ss1.str() << " (pred_eq_t _ " << ss2.str() << ")"; - } else { - Unreachable(); - } - } else if(n2.getKind() == kind::EQUAL) { - // n2 is an equality/iff, but n1 is a predicate - if(n2[0] == n1) { - n1 = n2[1]; - ss << "(symm _ _ _ " << ss2.str() << ") (pred_eq_t _ " << ss1.str() << ")"; - } else if(n2[1] == n1) { - n1 = n2[0]; - ss << ss2.str() << " (pred_eq_t _ " << ss1.str() << ")"; - } else { - Unreachable(); - } - } else { - // Both n1 and n2 are prediacates. Don't know what to do... - Unreachable(); - } - - ss << ")"; - } - out << ss.str(); - Debug("pf::arith") << "\n++ trans proof done, have proven " << n1 << std::endl; - return n1; - } - - default: - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - Debug("pf::arith") << "theory proof: " << pf.d_node << " by rule " << int(pf.d_id) << std::endl; - AlwaysAssert(false); - return pf.d_node; - } -} - -ArithProof::ArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* pe) - : TheoryProof(arith, pe), d_recorder() -{ - arith->setProofRecorder(&d_recorder); -} - -theory::TheoryId ArithProof::getTheoryId() { return theory::THEORY_ARITH; } -void ArithProof::registerTerm(Expr term) { - Debug("pf::arith") << "Arith register term: " << term << ". Kind: " << term.getKind() - << ". Type: " << term.getType() << std::endl; - - if (term.getType().isReal() && !term.getType().isInteger()) { - Debug("pf::arith") << "Entering real mode" << std::endl; - } - - if (term.isVariable() && !ProofManager::getSkolemizationManager()->isSkolem(term)) { - d_declarations.insert(term); - } - - // recursively declare all other terms - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - // could belong to other theories - d_proofEngine->registerTerm(term[i]); - } -} - -void LFSCArithProof::printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - Debug("pf::arith") << "Arith print term: " << term << ". Kind: " << term.getKind() - << ". Type: " << term.getType() - << ". Number of children: " << term.getNumChildren() << std::endl; - - // !d_realMode <--> term.getType().isInteger() - - Assert (theory::Theory::theoryOf(term) == theory::THEORY_ARITH); - std::ostringstream closing; - if (!expectedType.isNull() && !expectedType.isInteger() && term.getType().isInteger()) { - os << "(term_int_to_real "; - closing << ")"; - } - switch (term.getKind()) - { - case kind::CONST_RATIONAL: - { - Assert(term.getNumChildren() == 0); - Assert(term.getType().isInteger() || term.getType().isReal()); - - const Rational& r = term.getConst<Rational>(); - bool neg = (r < 0); - - os << (term.getType().isInteger() ? "(a_int " : "(a_real "); - closing << ") "; - - if (neg) - { - os << "(~ "; - closing << ")"; - } - - if (term.getType().isInteger()) - { - os << r.abs(); - } - else - { - printRational(os, r.abs()); - } - - break; - } - - case kind::PLUS: - case kind::MINUS: - case kind::MULT: - case kind::DIVISION: - case kind::DIVISION_TOTAL: - { - Assert(term.getNumChildren() >= 1); - TypeNode ty = Node::fromExpr(term).getType(); - - std::string lfscFunction = getLfscFunction(term); - for (unsigned i = 0; i < term.getNumChildren() - 1; ++i) - { - os << "(" << lfscFunction << " "; - closing << ")"; - d_proofEngine->printBoundTerm(term[i], os, map, ty); - os << " "; - } - - d_proofEngine->printBoundTerm(term[term.getNumChildren() - 1], os, map, ty); - break; - } - - case kind::UMINUS: - { - Assert(term.getNumChildren() == 1); - os << "(" << getLfscFunction(term) << " "; - closing << ")"; - d_proofEngine->printBoundTerm(term[0], os, map, Node::fromExpr(term).getType()); - break; - } - - case kind::GT: - case kind::GEQ: - case kind::LT: - case kind::LEQ: - { - Assert(term.getNumChildren() == 2); - Assert(term.getType().isBoolean()); - - std::string lfscFunction = getLfscFunction(term); - TypeNode realType = NodeManager::currentNM()->realType(); - - os << "(" << lfscFunction << " "; - closing << ")"; - - d_proofEngine->printBoundTerm(term[0], os, map); - os << " "; - d_proofEngine->printBoundTerm(term[1], os, map, realType); - break; - } - case kind::EQUAL: - { - Assert(term.getType().isBoolean()); - Assert(term.getNumChildren() == 2); - - TypeNode eqType = equalityType(term[0], term[1]); - - os << "(= " << eqType << " "; - closing << ")"; - - d_proofEngine->printBoundTerm(term[0], os, map, eqType); - d_proofEngine->printBoundTerm(term[1], os, map, eqType); - break; - } - - case kind::VARIABLE: - case kind::SKOLEM: - os << CVC4_ARITH_VAR_TERM_PREFIX << ProofManager::sanitize(term); - break; - - default: - Debug("pf::arith") << "Default printing of term: " << term << std::endl; - os << term; - break; - } - os << closing.str(); -} - -void LFSCArithProof::printOwnedSort(Type type, std::ostream& os) { - Debug("pf::arith") << "Arith print sort: " << type << std::endl; - os << type; -} - -std::string LFSCArithProof::getLfscFunction(const Node & n) { - Assert(n.getType().isInteger() || n.getType().isReal() || n.getType().isBoolean()); - std::string opString; - switch (n.getKind()) { - case kind::UMINUS: - opString = "u-_"; - break; - case kind::PLUS: - opString = "+_"; - break; - case kind::MINUS: - opString = "-_"; - break; - case kind::MULT: - opString = "*_"; - break; - case kind::DIVISION: - case kind::DIVISION_TOTAL: - opString = "/_"; - break; - case kind::GT: - opString = ">_"; - break; - case kind::GEQ: - opString = ">=_"; - break; - case kind::LT: - opString = "<_"; - break; - case kind::LEQ: - opString = "<=_"; - break; - default: - Unreachable() << "Tried to get the operator for a non-operator kind: " << n.getKind(); - } - std::string typeString; - if (n.getType().isInteger()) { - typeString = "Int"; - } else if (n.getType().isReal()) { - typeString = "Real"; - } else { // Boolean - if (n[0].getType().isInteger()) { - typeString = "IntReal"; - } else { - typeString = "Real"; - } - } - return opString + typeString; -} - -void LFSCArithProof::printRational(std::ostream& o, const Rational& r) -{ - if (r.sgn() < 0) - { - o << "(~ " << r.getNumerator().abs() << "/" << r.getDenominator().abs() - << ")"; - } - else - { - o << r.getNumerator() << "/" << r.getDenominator(); - } -} - -void LFSCArithProof::printInteger(std::ostream& o, const Integer& i) -{ - if (i.sgn() < 0) - { - o << "(~ " << i.abs() << ")"; - } - else - { - o << i; - } -} - -void LFSCArithProof::printLinearPolynomialNormalizer(std::ostream& o, - const Node& n) -{ - switch (n.getKind()) - { - case kind::PLUS: - { - // Since our axioms are binary, but n may be n-ary, we rig up - // a right-associative tree. - size_t nchildren = n.getNumChildren(); - for (size_t i = 0; i < nchildren; ++i) - { - if (i < nchildren - 1) - { - o << "\n (is_aff_+ _ _ _ _ _ "; - } - printLinearMonomialNormalizer(o, n[i]); - } - std::fill_n(std::ostream_iterator<char>(o), nchildren - 1, ')'); - break; - } - case kind::MULT: - case kind::VARIABLE: - case kind::CONST_RATIONAL: - case kind::SKOLEM: - { - printLinearMonomialNormalizer(o, n); - break; - } - default: - Unreachable() << "Invalid operation " << n.getKind() - << " in linear polynomial"; - break; - } -} - -void LFSCArithProof::printLinearMonomialNormalizer(std::ostream& o, - const Node& n) -{ - switch (n.getKind()) - { - case kind::MULT: { - Assert((n[0].getKind() == kind::CONST_RATIONAL - && (n[1].getKind() == kind::VARIABLE - || n[1].getKind() == kind::SKOLEM))) - << "node " << n << " is not a linear monomial" - << " " << n[0].getKind() << " " << n[1].getKind(); - - o << "\n (is_aff_mul_c_L _ _ _ "; - printConstRational(o, n[0]); - o << " "; - printVariableNormalizer(o, n[1]); - o << ")"; - break; - } - case kind::CONST_RATIONAL: - { - o << "\n (is_aff_const "; - printConstRational(o, n); - o << ")"; - break; - } - case kind::VARIABLE: - case kind::SKOLEM: - { - o << "\n "; - printVariableNormalizer(o, n); - break; - } - default: - Unreachable() << "Invalid operation " << n.getKind() - << " in linear monomial"; - break; - } -} - -void LFSCArithProof::printConstRational(std::ostream& o, const Node& n) -{ - Assert(n.getKind() == kind::CONST_RATIONAL); - const Rational value = n.getConst<Rational>(); - printRational(o, value); -} - -void LFSCArithProof::printVariableNormalizer(std::ostream& o, const Node& n) -{ - std::ostringstream msg; - Assert(n.getKind() == kind::VARIABLE || n.getKind() == kind::SKOLEM) - << "Invalid variable kind " << n.getKind() << " in linear monomial"; - if (n.getType().isInteger()) { - o << "(is_aff_var_int "; - } else if (n.getType().isReal()) { - o << "(is_aff_var_real "; - } else { - Unreachable(); - } - o << n << ")"; -} - -void LFSCArithProof::printLinearPolynomialPredicateNormalizer(std::ostream& o, - const Node& n) -{ - Assert(n.getKind() == kind::GEQ) - << "can only print normalization witnesses for (>=) nodes"; - Assert(n[1].getKind() == kind::CONST_RATIONAL); - o << "\n (is_aff_- _ _ _ _ _ "; - printLinearPolynomialNormalizer(o, n[0]); - o << "\n (is_aff_const "; - printConstRational(o, n[1]); - o << "))"; -} - -std::pair<Node, std::string> LFSCArithProof::printProofAndMaybeTighten( - const Node& bound) -{ - const Node & nonNegBound = bound.getKind() == kind::NOT ? bound[0] : bound; - std::ostringstream pfOfPossiblyTightenedPredicate; - if (nonNegBound[0].getType().isInteger()) { - switch(bound.getKind()) - { - case kind::NOT: - { - // Tighten ~[i >= r] to [i < r] to [i <= {r}] to [-i >= -{r}] - // where - // * i is an integer - // * r is a real - // * {r} denotes the greatest int less than r - // it is equivalent to (ceil(r) - 1) - Assert(nonNegBound[1].getKind() == kind::CONST_RATIONAL); - Rational oldBound = nonNegBound[1].getConst<Rational>(); - Integer newBound = -(oldBound.ceiling() - 1); - // Since the arith theory rewrites bounds to be purely integral or - // purely real, mixed bounds should not appear in proofs - AlwaysAssert(oldBound.isIntegral()) << "Mixed int/real bound in arith proof"; - pfOfPossiblyTightenedPredicate - << "(tighten_not_>=_IntInt" - << " _ _ _ _ (" - << "check_neg_of_greatest_integer_below_int "; - printInteger(pfOfPossiblyTightenedPredicate, newBound); - pfOfPossiblyTightenedPredicate << " "; - printInteger(pfOfPossiblyTightenedPredicate, oldBound.ceiling()); - pfOfPossiblyTightenedPredicate << ") " << ProofManager::getLitName(bound.negate(), "") << ")"; - Node newLeft = (theory::arith::Polynomial::parsePolynomial(nonNegBound[0]) * -1).getNode(); - Node newRight = NodeManager::currentNM()->mkConst(Rational(newBound)); - Node newTerm = NodeManager::currentNM()->mkNode(kind::GEQ, newLeft, newRight); - return std::make_pair(newTerm, pfOfPossiblyTightenedPredicate.str()); - } - case kind::GEQ: - { - // Tighten [i >= r] to [i >= ceil(r)] - // where - // * i is an integer - // * r is a real - Assert(nonNegBound[1].getKind() == kind::CONST_RATIONAL); - - Rational oldBound = nonNegBound[1].getConst<Rational>(); - // Since the arith theory rewrites bounds to be purely integral or - // purely real, mixed bounds should not appear in proofs - AlwaysAssert(oldBound.isIntegral()) << "Mixed int/real bound in arith proof"; - pfOfPossiblyTightenedPredicate << ProofManager::getLitName(bound.negate(), ""); - return std::make_pair(bound, pfOfPossiblyTightenedPredicate.str()); - } - default: Unreachable(); - } - } else { - return std::make_pair(bound, ProofManager::getLitName(bound.negate(), "")); - } - // Silence compiler warnings about missing a return. - Unreachable(); -} - -void LFSCArithProof::printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) -{ - Debug("pf::arith") << "Printing proof for lemma " << lemma << std::endl; - if (Debug.isOn("pf::arith::printTheoryLemmaProof")) { - Debug("pf::arith::printTheoryLemmaProof") << "Printing proof for lemma:" << std::endl; - for (const auto & conjunct : lemma) { - Debug("pf::arith::printTheoryLemmaProof") << " " << conjunct << std::endl; - } - } - // Prefixes for the names of linearity witnesses - const char* linearizedProofPrefix = "pf_aff"; - std::ostringstream lemmaParen; - - // Construct the set of conflicting literals - std::set<Node> conflictSet; - std::transform(lemma.begin(), - lemma.end(), - std::inserter(conflictSet, conflictSet.begin()), - [](const Expr& e) { - return NodeManager::currentNM()->fromExpr(e).negate(); - }); - - // If we have Farkas coefficients stored for this lemma, use them to write a - // proof. Otherwise, just `trust` the lemma. - if (d_recorder.hasFarkasCoefficients(conflictSet)) - { - // Get farkas coefficients & literal order - const auto& farkasInfo = d_recorder.getFarkasCoefficients(conflictSet); - const Node& conflict = farkasInfo.first; - theory::arith::RationalVectorCP farkasCoefficients = farkasInfo.second; - Assert(farkasCoefficients != theory::arith::RationalVectorCPSentinel); - Assert(conflict.getNumChildren() == farkasCoefficients->size()); - const size_t nAntecedents = conflict.getNumChildren(); - - // Print proof - if (Debug.isOn("pf::arith::printTheoryLemmaProof")) { - os << "Farkas:" << std::endl; - for (const auto & n : *farkasCoefficients) { - os << " " << n << std::endl; - } - } - - // Prove affine function bounds from term bounds - os << "\n;; Farkas Proof ;;" << std::endl; - os << "\n; Linear Polynomial Proof Conversions"; - for (size_t i = 0; i != nAntecedents; ++i) - { - const Node& antecedent = conflict[i]; - os << "\n (@ " - << ProofManager::getLitName(antecedent.negate(), linearizedProofPrefix) - << " "; - lemmaParen << ")"; - const std::pair<Node, std::string> tightened = printProofAndMaybeTighten(antecedent); - switch (tightened.first.getKind()) - { - case kind::NOT: - { - Assert(conflict[i][0].getKind() == kind::GEQ); - os << "(aff_>_from_term _ _ _ _ "; - break; - } - case kind::GEQ: - { - os << "(aff_>=_from_term _ _ _ "; - break; - } - default: Unreachable(); - } - const Node& nonNegTightened = tightened.first.getKind() == kind::NOT ? tightened.first[0] : tightened.first; - printLinearPolynomialPredicateNormalizer(os, nonNegTightened); - os << " (pf_reified_arith_pred _ _ " << tightened.second << "))"; - } - - // Now, print the proof of bottom, from affine function bounds - os << "\n; Farkas Combination"; - os << "\n (clausify_false (bounded_aff_contra _ _"; - lemmaParen << "))"; - for (size_t i = 0; i != nAntecedents; ++i) - { - const Node& lit = conflict[i]; - os << "\n (bounded_aff_add _ _ _ _ _"; - os << "\n (bounded_aff_mul_c _ _ _ "; - printRational(os, (*farkasCoefficients)[i].abs()); - os << " " << ProofManager::getLitName(lit.negate(), linearizedProofPrefix) - << ")" - << " ; " << lit; - lemmaParen << ")"; - } - - os << "\n bounded_aff_ax_0_>=_0"; - os << lemmaParen.str(); // Close lemma proof - } - else - { - os << "\n; Arithmetic proofs which use reasoning more complex than Farkas " - "proofs and bound tightening are currently unsupported\n" - "(clausify_false trust)\n"; - } -} - -void LFSCArithProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { - // Nothing to do here at this point. -} - -void LFSCArithProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { - for (ExprSet::const_iterator it = d_declarations.begin(); - it != d_declarations.end(); - ++it) - { - Expr term = *it; - Assert(term.isVariable()); - bool isInt = term.getType().isInteger(); - const char * var_type = isInt ? "int_var" : "real_var"; - os << "(% " << ProofManager::sanitize(term) << " " << var_type << "\n"; - os << "(@ " << CVC4_ARITH_VAR_TERM_PREFIX << ProofManager::sanitize(term) - << " "; - os << "(term_" << var_type << " " << ProofManager::sanitize(term) << ")\n"; - paren << ")"; - paren << ")"; - } -} - -void LFSCArithProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { - // Nothing to do here at this point. -} - -void LFSCArithProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { - // Nothing to do here at this point. -} - -bool LFSCArithProof::printsAsBool(const Node& n) -{ - // Our boolean variables and constants print as sort Bool. - // All complex booleans print as formulas. - return n.getType().isBoolean() and (n.isVar() or n.isConst()); -} - -TypeNode LFSCArithProof::equalityType(const Expr& left, const Expr& right) -{ - return TypeNode::fromType(!left.getType().isInteger() ? left.getType() : right.getType()); -} - -} /* CVC4 namespace */ diff --git a/src/proof/arith_proof.h b/src/proof/arith_proof.h deleted file mode 100644 index 832afcae0..000000000 --- a/src/proof/arith_proof.h +++ /dev/null @@ -1,217 +0,0 @@ -/********************* */ -/*! \file arith_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Guy Katz, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Arith proof - ** - ** Arith proof - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__ARITH__PROOF_H -#define CVC4__ARITH__PROOF_H - -#include <memory> -#include <unordered_set> - -#include "expr/expr.h" -#include "proof/arith_proof_recorder.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" -#include "theory/uf/equality_engine.h" - -namespace CVC4 { - -//proof object outputted by TheoryArith -class ProofArith : public Proof { - public: - ProofArith(std::shared_ptr<theory::eq::EqProof> pf) : d_proof(pf) {} - void toStream(std::ostream& out) const override; - - private: - static void toStreamLFSC(std::ostream& out, TheoryProof* tp, - const theory::eq::EqProof& pf, - const ProofLetMap& map); - static Node toStreamRecLFSC(std::ostream& out, TheoryProof* tp, - const theory::eq::EqProof& pf, - unsigned tb, const ProofLetMap& map); - // it is simply an equality engine proof - std::shared_ptr<theory::eq::EqProof> d_proof; -}; - -namespace theory { -namespace arith { -class TheoryArith; -} -} - -typedef std::unordered_set<Type, TypeHashFunction > TypeSet; - - -class ArithProof : public TheoryProof { -protected: - // std::map<Expr, std::string> d_constRationalString; // all the variable/function declarations - - // TypeSet d_sorts; // all the uninterpreted sorts in this theory - ExprSet d_declarations; // all the variable/function declarations - - /** - * Where farkas proofs of lemmas are stored. - */ - proof::ArithProofRecorder d_recorder; - - theory::TheoryId getTheoryId() override; - - public: - ArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* proofEngine); - - void registerTerm(Expr term) override; -}; - -class LFSCArithProof : public ArithProof { -public: - LFSCArithProof(theory::arith::TheoryArith* arith, TheoryProofEngine* proofEngine) - : ArithProof(arith, proofEngine) - {} - void printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) override; - void printOwnedSort(Type type, std::ostream& os) override; - - /** - * Returns the LFSC identifier for the operator of this node. - * - * e.g. "+_Real". - * - * Does not include any parens. - * - * Even if the operator is a comparison (e.g. >=) on integers, will not - * return a purely `Int` predicate like ">=_Int". Instead this treats the - * right hand side as a real. - */ - static std::string getLfscFunction(const Node& n); - - /** - * Print a rational number in LFSC format. - * e.g. 5/8 or (~ 1/1) - * - * @param o ostream to print to. - * @param r the rational to print - */ - static void printRational(std::ostream& o, const Rational& r); - - /** - * Print an integer in LFSC format. - * e.g. 5 or (~ 1) - * - * @param o ostream to print to. - * @param i the integer to print - */ - static void printInteger(std::ostream& o, const Integer& i); - - /** - * Print a value of type poly_formula_norm - * - * @param o ostream to print to - * @param n node (asserted to be of the form [linear polynomial >= constant]) - */ - static void printLinearPolynomialPredicateNormalizer(std::ostream& o, - const Node& n); - - /** - * Print a value of type poly_norm - * - * @param o ostream to print to - * @param n node (asserted to be a linear polynomial) - */ - static void printLinearPolynomialNormalizer(std::ostream& o, const Node& n); - - /** - * Print a value of type poly_norm - * - * @param o ostream to print to - * @param n node (asserted to be a linear monomial) - */ - static void printLinearMonomialNormalizer(std::ostream& o, const Node& n); - - /** - * Print a LFSC rational - * - * @param o ostream to print to - * @param n node (asserted to be a const rational) - */ - static void printConstRational(std::ostream& o, const Node& n); - - /** - * print the pn_var normalizer for n (type poly_norm) - * - * @param o the ostream to print to - * @param n the node to print (asserted to be a variable) - */ - static void printVariableNormalizer(std::ostream& o, const Node& n); - /** - * print a proof of the lemma - * - * First, we print linearity witnesses, i.e. witnesses that each literal has - * the form: - * [linear polynomial] >= 0 OR - * [linear polynomial] > 0 - * - * Then we use those witnesses to prove that the above linearized constraints - * hold. - * - * Then we use the farkas coefficients to combine the literals into a - * variable-free contradiction. The literals may be a mix of strict and - * relaxed inequalities. - * - * @param lemma the set of literals disjoined in the lemma - * @param os stream to print the proof to - * @param paren global closing stream (unused) - * @param map let map (unused) - */ - void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; - void printSortDeclarations(std::ostream& os, std::ostream& paren) override; - void printTermDeclarations(std::ostream& os, std::ostream& paren) override; - void printDeferredDeclarations(std::ostream& os, - std::ostream& paren) override; - void printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) override; - - /** - * Given a node that is an arith literal (an arith comparison or negation - * thereof), prints a proof of that literal. - * - * If the node represents a tightenable bound (e.g. [Int] < 3) then it prints - * a proof of the tightening instead. (e.g. [Int] <= 2). - * - * @return a pair comprising: - * * the new node (after tightening) and - * * a string proving it. - */ - std::pair<Node, std::string> printProofAndMaybeTighten(const Node& bound); - - /** - * Return whether this node, when serialized to LFSC, has sort `Bool`. Otherwise, the sort is `formula`. - */ - bool printsAsBool(const Node& n) override; - - TypeNode equalityType(const Expr& left, const Expr& right) override; -}; - - -}/* CVC4 namespace */ - -#endif /* CVC4__ARITH__PROOF_H */ diff --git a/src/proof/arith_proof_recorder.cpp b/src/proof/arith_proof_recorder.cpp deleted file mode 100644 index 01da402c9..000000000 --- a/src/proof/arith_proof_recorder.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/********************* */ -/*! \file arith_proof_recorder.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief A class for recording the skeletons of arithmetic proofs at solve - ** time so they can later be used during proof-production time. - **/ - -#include "proof/arith_proof_recorder.h" - -#include <algorithm> -#include <vector> - -#include "base/map_util.h" - -namespace CVC4 { -namespace proof { - -ArithProofRecorder::ArithProofRecorder() : d_lemmasToFarkasCoefficients() -{ - // Nothing else -} -void ArithProofRecorder::saveFarkasCoefficients( - Node conflict, theory::arith::RationalVectorCP farkasCoefficients) -{ - // Verify that the conflict is a conjuction of (possibly negated) real bounds - // Verify that the conflict is a conjunciton ... - Assert(conflict.getKind() == kind::AND); - Assert(conflict.getNumChildren() == farkasCoefficients->size()); - for (size_t i = 0, nchildren = conflict.getNumChildren(); i < nchildren; ++i) - { - const Node& child = conflict[i]; - // ... of possibly negated ... - const Node& nonNegativeChild = - child.getKind() == kind::NOT ? child[0] : child; - // ... real bounds - Assert(nonNegativeChild.getType().isBoolean() - && nonNegativeChild[0].getType().isReal()); - } - Debug("pf::arith") << "Saved Farkas Coefficients:" << std::endl; - if (Debug.isOn("pf::arith")) - { - for (size_t i = 0, nchildren = conflict.getNumChildren(); i < nchildren; - ++i) - { - const Node& child = conflict[i]; - const Rational& r = (*farkasCoefficients)[i]; - Debug("pf::arith") << " " << std::setw(8) << r; - Debug("pf::arith") << " " << child << std::endl; - } - } - - std::set<Node> lits; - std::copy( - conflict.begin(), conflict.end(), std::inserter(lits, lits.begin())); - - d_lemmasToFarkasCoefficients[lits] = - std::make_pair(std::move(conflict), *farkasCoefficients); -} - -bool ArithProofRecorder::hasFarkasCoefficients( - const std::set<Node>& conflict) const -{ - return d_lemmasToFarkasCoefficients.find(conflict) - != d_lemmasToFarkasCoefficients.end(); -} - -std::pair<Node, theory::arith::RationalVectorCP> -ArithProofRecorder::getFarkasCoefficients(const std::set<Node>& conflict) const -{ - if (auto *p = FindOrNull(d_lemmasToFarkasCoefficients, conflict)) - { - return std::make_pair(p->first, &p->second); - } - else - { - return std::make_pair(Node(), theory::arith::RationalVectorCPSentinel); - } -} - -} // namespace proof -} // namespace CVC4 diff --git a/src/proof/arith_proof_recorder.h b/src/proof/arith_proof_recorder.h deleted file mode 100644 index 0669e5d16..000000000 --- a/src/proof/arith_proof_recorder.h +++ /dev/null @@ -1,107 +0,0 @@ -/********************* */ -/*! \file arith_proof_recorder.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief A class for recording the skeletons of arithmetic proofs at solve - ** time so they can later be used during proof-production time. - ** - ** In particular, we're interested in proving bottom from a conjunction of - ** theory literals. - ** - ** For now, we assume that this can be done using a Farkas combination, and if - ** that doesn't work for some reason, then we give up and "trust" the lemma. - ** In the future we'll build support for more sophisticated reasoning. - ** - ** Given this scope, our task is to... - ** for each lemma (a set of literals) - ** save the Farkas coefficients for those literals - ** which requires we save an ordering of the literals - ** and a parallel ordering of Farkas coefficients. - ** - ** Farkas proofs have the following core structure: - ** For a list of affine bounds: c[i] dot x >= b[i] - ** (x is a vector of variables) - ** (c[i] is a vector of coefficients) - ** and a list of non-negative coefficients: f[i], - ** compute - ** - ** sum_i{ (c[i] dot x) * f[i] } and sum_i{b[i]*f[i]} - ** - ** and then verify that the left is actually < the right, a contradiction - ** - ** To be clear: this code does not check Farkas proofs, it just stores the - ** information needed to write them. - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__ARITH_PROOF_RECORDER_H -#define CVC4__PROOF__ARITH_PROOF_RECORDER_H - -#include <map> -#include <set> - -#include "expr/node.h" -#include "theory/arith/constraint_forward.h" - -namespace CVC4 { -namespace proof { - -class ArithProofRecorder -{ - public: - ArithProofRecorder(); - - /** - * @brief For a set of incompatible literals, save the Farkas coefficients - * demonstrating their incompatibility - * - * @param conflict a conjunction of conflicting literals - * @param farkasCoefficients a list of rational coefficients which the literals - * should be multiplied by (pairwise) to produce a contradiction. - * - * The orders of the two vectors must agree! - */ - void saveFarkasCoefficients( - Node conflict, theory::arith::RationalVectorCP farkasCoefficients); - - /** - * @brief Determine whether some literals have a Farkas proof of their - * incompatibility - * - * @param conflict a conjunction of (putatively) conflicting literals - * - * @return whether or not there is actually a proof for them. - */ - bool hasFarkasCoefficients(const std::set<Node>& conflict) const; - - /** - * @brief Get the Farkas Coefficients object - * - * @param conflict a conjunction of conflicting literals - * @return theory::arith::RationalVectorCP -- the Farkas coefficients - * Node -- a conjunction of the problem literals in coefficient order - * - * theory::arith::RationalVectorCPSentinel if there is no entry for - * these lits - */ - std::pair<Node, theory::arith::RationalVectorCP> getFarkasCoefficients( - const std::set<Node>& conflict) const; - - protected: - // For each lemma, save the Farkas coefficients of that lemma - std::map<std::set<Node>, std::pair<Node, theory::arith::RationalVector>> - d_lemmasToFarkasCoefficients; -}; - -} // namespace proof -} // namespace CVC4 - -#endif diff --git a/src/proof/array_proof.cpp b/src/proof/array_proof.cpp deleted file mode 100644 index 2d42a7489..000000000 --- a/src/proof/array_proof.cpp +++ /dev/null @@ -1,1350 +0,0 @@ -/********************* */ -/*! \file array_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Yoni Zohar, Liana Hadarean - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "proof/array_proof.h" - -#include <stack> - -#include "proof/proof_manager.h" -#include "proof/simplify_boolean_node.h" -#include "proof/theory_proof.h" -#include "theory/arrays/theory_arrays.h" - -namespace CVC4 { - -namespace { - -class ArrayProofPrinter : public theory::eq::EqProof::PrettyPrinter { - public: - ArrayProofPrinter(unsigned row, unsigned row1, unsigned ext) - : d_row(row), d_row1(row1), d_ext(ext) {} - - std::string printTag(unsigned tag) override; - - private: - const unsigned d_row; - const unsigned d_row1; - const unsigned d_ext; -}; // class ArrayProofPrinter - -std::string ArrayProofPrinter::printTag(unsigned tag) { - if (tag == theory::eq::MERGED_THROUGH_CONGRUENCE) return "Congruence"; - if (tag == theory::eq::MERGED_THROUGH_EQUALITY) return "Pure Equality"; - if (tag == theory::eq::MERGED_THROUGH_REFLEXIVITY) return "Reflexivity"; - if (tag == theory::eq::MERGED_THROUGH_CONSTANTS) return "Constants"; - if (tag == theory::eq::MERGED_THROUGH_TRANS) return "Transitivity"; - - if (tag == d_row) return "Read Over Write"; - if (tag == d_row1) return "Read Over Write (1)"; - if (tag == d_ext) return "Extensionality"; - - std::ostringstream result; - result << tag; - return result.str(); -} - -} // namespace - - - -ProofArray::ProofArray(std::shared_ptr<theory::eq::EqProof> pf, - unsigned row, - unsigned row1, - unsigned ext) - : d_proof(pf), d_reasonRow(row), d_reasonRow1(row1), d_reasonExt(ext) -{ -} - -void ProofArray::toStream(std::ostream& out) const -{ - ProofLetMap map; - toStream(out, map); -} - -void ProofArray::toStream(std::ostream& out, const ProofLetMap& map) const -{ - Trace("pf::array") << "; Print Array proof..." << std::endl; - toStreamLFSC(out, ProofManager::getArrayProof(), *d_proof, map); - Debug("pf::array") << "; Print Array proof done!" << std::endl; -} - -void ProofArray::toStreamLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - const ProofLetMap& map) const -{ - Debug("pf::array") << "Printing array proof in LFSC : " << std::endl; - ArrayProofPrinter proofPrinter(d_reasonRow, d_reasonRow1, d_reasonExt); - pf.debug_print("pf::array", 0, &proofPrinter); - Debug("pf::array") << std::endl; - toStreamRecLFSC(out, tp, pf, 0, map); - Debug("pf::array") << "Printing array proof in LFSC DONE" << std::endl; -} - -Node ProofArray::toStreamRecLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - unsigned tb, - const ProofLetMap& map) const -{ - Debug("pf::array") << std::endl - << std::endl - << "toStreamRecLFSC called. tb = " << tb - << " . proof:" << std::endl; - ArrayProofPrinter proofPrinter(d_reasonRow, d_reasonRow1, d_reasonExt); - if(tb == 0) { - std::shared_ptr<theory::eq::EqProof> subTrans = - std::make_shared<theory::eq::EqProof>(); - - int neg = tp->assertAndPrint(pf, map, subTrans, &proofPrinter); - - Node n1; - std::stringstream ss, ss2; - Debug("mgdx") << "\nsubtrans has " << subTrans->d_children.size() << " children\n"; - bool disequalityFound = (neg >= 0); - - if (!disequalityFound || pf.d_children.size() > 2) - { - n1 = toStreamRecLFSC(ss, tp, *subTrans, 1, map); - } else { - n1 = toStreamRecLFSC(ss, tp, *(subTrans->d_children[0]), 1, map); - Debug("mgdx") << "\nsubTrans unique child " - << subTrans->d_children[0]->d_id - << " was proven\ngot: " << n1 << std::endl; - } - - out << "(clausify_false (contra _ "; - if (disequalityFound) { - Node n2 = pf.d_children[neg]->d_node; - Assert(n2.getKind() == kind::NOT); - Debug("mgdx") << "\nhave proven: " << n1 << std::endl; - Debug("mgdx") << "n2 is " << n2 << std::endl; - Debug("mgdx") << "n2->d_id is " << pf.d_children[neg]->d_id << std::endl; - Debug("mgdx") << "n2[0] is " << n2[0] << std::endl; - - if (n2[0].getNumChildren() > 0) { Debug("mgdx") << "\nn2[0]: " << n2[0][0] << std::endl; } - if (n1.getNumChildren() > 1) { Debug("mgdx") << "n1[1]: " << n1[1] << std::endl; } - - if ((pf.d_children[neg]->d_id == d_reasonExt) || - (pf.d_children[neg]->d_id == theory::eq::MERGED_THROUGH_TRANS)) { - // Ext case: The negative node was created by an EXT rule; e.g. it is a[k]!=b[k], due to a!=b. - out << ss.str(); - out << " "; - toStreamRecLFSC(ss2, tp, *pf.d_children[neg], 1, map); - out << ss2.str(); - } else if (n2[0].getKind() == kind::APPLY_UF) { - out << "(trans _ _ _ _ "; - out << "(symm _ _ _ "; - out << ss.str(); - out << ") (pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; - } else { - Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) - || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); - if(n1[1] == n2[0][0]) { - out << "(symm _ _ _ " << ss.str() << ")"; - } else { - out << ss.str(); - } - Debug("pf::array") << "ArrayProof::toStream: getLitName( " << n2[0] << " ) = " << - ProofManager::getLitName(n2[0]) << std::endl; - - out << " " << ProofManager::getLitName(n2[0]); - } - } else { - Node n2 = pf.d_node; - Assert(n2.getKind() == kind::EQUAL); - Assert((n1[0] == n2[0] && n1[1] == n2[1]) - || (n1[1] == n2[0] && n1[0] == n2[1])); - - out << ss.str(); - out << " "; - ProofManager::getTheoryProofEngine()->printConstantDisequalityProof(out, - n1[0].toExpr(), - n1[1].toExpr(), - map); - } - - out << "))" << std::endl; - return Node(); - } - - switch (pf.d_id) - { - case theory::eq::MERGED_THROUGH_CONGRUENCE: - { - Debug("mgd") << "\nok, looking at congruence:\n"; - pf.debug_print("mgd", 0, &proofPrinter); - std::stack<const theory::eq::EqProof*> stk; - for (const theory::eq::EqProof* pf2 = &pf; - pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; - pf2 = pf2->d_children[0].get()) - { - Debug("mgd") << "Looking at pf2 with d_node: " << pf2->d_node - << std::endl; - Assert(!pf2->d_node.isNull()); - Assert(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF - || pf2->d_node.getKind() == kind::BUILTIN - || pf2->d_node.getKind() == kind::APPLY_UF - || pf2->d_node.getKind() == kind::SELECT - || pf2->d_node.getKind() == kind::PARTIAL_SELECT_0 - || pf2->d_node.getKind() == kind::PARTIAL_SELECT_1 - || pf2->d_node.getKind() == kind::STORE); - - Assert(pf2->d_children.size() == 2); - out << "(cong _ _ _ _ _ _ "; - stk.push(pf2); - } - Assert(stk.top()->d_children[0]->d_id - != theory::eq::MERGED_THROUGH_CONGRUENCE); - // NodeBuilder<> b1(kind::PARTIAL_APPLY_UF), - // b2(kind::PARTIAL_APPLY_UF); - NodeBuilder<> b1, b2; - - const theory::eq::EqProof* pf2 = stk.top(); - stk.pop(); - Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); - Node n1 = toStreamRecLFSC(out, tp, *(pf2->d_children[0]), tb + 1, map); - out << " "; - std::stringstream ss; - Node n2 = toStreamRecLFSC(ss, tp, *(pf2->d_children[1]), tb + 1, map); - - Debug("mgd") << "\nok, in FIRST cong[" << stk.size() << "]" - << "\n"; - pf2->debug_print("mgd", 0, &proofPrinter); - // Temp - Debug("mgd") << "n1 is a proof for: " << pf2->d_children[0]->d_node - << ". It is: " << n1 << std::endl; - Debug("mgd") << "n2 is a proof for: " << pf2->d_children[1]->d_node - << ". It is: " << n2 << std::endl; - // - Debug("mgd") << "looking at " << pf2->d_node << "\n"; - Debug("mgd") << " " << n1 << "\n"; - Debug("mgd") << " " << n2 << "\n"; - - int side = 0; - if (tp->match(pf2->d_node, n1[0])) - { - Debug("mgd") << "SIDE IS 0\n"; - side = 0; - } - else - { - Debug("mgd") << "SIDE IS 1\n"; - if (!tp->match(pf2->d_node, n1[1])) - { - Debug("mgd") << "IN BAD CASE, our first subproof is\n"; - pf2->d_children[0]->debug_print("mgd", 0, &proofPrinter); - } - Assert(tp->match(pf2->d_node, n1[1])); - side = 1; - } - - if (n1[side].getKind() == kind::APPLY_UF - || n1[side].getKind() == kind::PARTIAL_APPLY_UF - || n1[side].getKind() == kind::SELECT - || n1[side].getKind() == kind::PARTIAL_SELECT_1 - || n1[side].getKind() == kind::STORE) - { - if (n1[side].getKind() == kind::APPLY_UF - || n1[side].getKind() == kind::PARTIAL_APPLY_UF) - { - b1 << kind::PARTIAL_APPLY_UF; - b1 << n1[side].getOperator(); - } - else if (n1[side].getKind() == kind::SELECT - || n1[side].getKind() == kind::PARTIAL_SELECT_1) - { - // b1 << n1[side].getKind(); - b1 << kind::SELECT; - } else { - b1 << kind::PARTIAL_APPLY_UF; - b1 << ProofManager::currentPM()->mkOp(n1[side].getOperator()); - } - b1.append(n1[side].begin(), n1[side].end()); - } - else if (n1[side].getKind() == kind::PARTIAL_SELECT_0) { - b1 << kind::PARTIAL_SELECT_1; - } else { - b1 << n1[side]; - } - - if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || - n1[1-side].getKind() == kind::APPLY_UF || - n1[1-side].getKind() == kind::SELECT || - n1[1-side].getKind() == kind::PARTIAL_SELECT_1 || - n1[1-side].getKind() == kind::STORE) { - if(n1[1-side].getKind() == kind::APPLY_UF || - n1[1-side].getKind() == kind::PARTIAL_APPLY_UF) { - b2 << kind::PARTIAL_APPLY_UF; - b2 << n1[1-side].getOperator(); - } else if (n1[1-side].getKind() == kind::SELECT || n1[1-side].getKind() == kind::PARTIAL_SELECT_1) { - // b2 << n1[1-side].getKind(); - b2 << kind::SELECT; - } else { - b2 << kind::PARTIAL_APPLY_UF; - b2 << ProofManager::currentPM()->mkOp(n1[1-side].getOperator()); - } - b2.append(n1[1-side].begin(), n1[1-side].end()); - } else if (n1[1-side].getKind() == kind::PARTIAL_SELECT_0) { - b2 << kind::PARTIAL_SELECT_1; - } else { - b2 << n1[1-side]; - } - Debug("mgd") << "pf2->d_node " << pf2->d_node << std::endl; - Debug("mgd") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; - Debug("mgd") << "n1 " << n1 << std::endl; - Debug("mgd") << "n2 " << n2 << std::endl; - // These debug prints can cause a problem if we're constructing a SELECT node and it doesn't have enough - // children yet. - // Debug("mgd") << "b1 " << b1 << std::endl; - // Debug("mgd") << "b2 " << b2 << std::endl; - Debug("mgd") << "side " << side << std::endl; - Debug("mgd") << "pf2->d_node's number of children: " << pf2->d_node.getNumChildren() << std::endl; - Debug("mgd") << "pf2->d_node's meta kind: " << pf2->d_node.getMetaKind() << std::endl; - Debug("mgd") << "Is this meta kind considered parameterized? " << (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED) << std::endl; - - if(pf2->d_node[b1.getNumChildren() + - (n1[side].getKind() == kind::PARTIAL_SELECT_0 ? 1 : 0) + - (n1[side].getKind() == kind::PARTIAL_SELECT_1 ? 1 : 0) - - (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[side]) { - b1 << n2[side]; - b2 << n2[1-side]; - out << ss.str(); - } else { - Assert( - pf2->d_node[b1.getNumChildren() - + (n1[side].getKind() == kind::PARTIAL_SELECT_0 ? 1 : 0) - + (n1[side].getKind() == kind::PARTIAL_SELECT_1 ? 1 : 0) - - (pf2->d_node.getMetaKind() - == kind::metakind::PARAMETERIZED - ? 0 - : 1)] - == n2[1 - side]); - b1 << n2[1-side]; - b2 << n2[side]; - out << "(symm _ _ _ " << ss.str() << ")"; - } - - Debug("mgd") << "After first insertion:" << std::endl; - Debug("mgd") << "b1 " << b1 << std::endl; - Debug("mgd") << "b2 " << b2 << std::endl; - - out << ")"; - while(!stk.empty()) { - - Debug("mgd") << "\nMORE TO DO\n"; - - pf2 = stk.top(); - stk.pop(); - Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); - out << " "; - ss.str(""); - n2 = toStreamRecLFSC(ss, tp, *(pf2->d_children[1]), tb + 1, map); - - Debug("mgd") << "\nok, in cong[" << stk.size() << "]" << "\n"; - Debug("mgd") << "looking at " << pf2->d_node << "\n"; - Debug("mgd") << " " << n1 << "\n"; - Debug("mgd") << " " << n2 << "\n"; - Debug("mgd") << " " << b1 << "\n"; - Debug("mgd") << " " << b2 << "\n"; - if(pf2->d_node[b1.getNumChildren()] == n2[side]) { - b1 << n2[side]; - b2 << n2[1-side]; - out << ss.str(); - } else { - Assert(pf2->d_node[b1.getNumChildren()] == n2[1 - side]); - b1 << n2[1-side]; - b2 << n2[side]; - out << "(symm _ _ _ " << ss.str() << ")"; - } - out << ")"; - } - n1 = b1; - n2 = b2; - - Debug("mgd") << "at end assert!" << std::endl - << "pf2->d_node = " << pf2->d_node << std::endl - << "n1 (assigned from b1) = " << n1 << std::endl - << "n2 (assigned from b2) = " << n2 << std::endl; - - if(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF) { - Assert(n1 == pf2->d_node); - } - - Debug("mgd") << "n1.getOperator().getType().getNumChildren() = " - << n1.getOperator().getType().getNumChildren() << std::endl; - Debug("mgd") << "n1.getNumChildren() + 1 = " - << n1.getNumChildren() + 1 << std::endl; - - Assert(!( - (n1.getKind() == kind::PARTIAL_SELECT_0 && n1.getNumChildren() == 2))); - if (n1.getKind() == kind::PARTIAL_SELECT_1 && n1.getNumChildren() == 2) { - Debug("mgd") << "Finished a SELECT. Updating.." << std::endl; - b1.clear(kind::SELECT); - b1.append(n1.begin(), n1.end()); - n1 = b1; - Debug("mgd") << "New n1: " << n1 << std::endl; - } else if(n1.getOperator().getType().getNumChildren() == n1.getNumChildren() + 1) { - if(ProofManager::currentPM()->hasOp(n1.getOperator())) { - b1.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst<Kind>()); - } else { - b1.clear(kind::APPLY_UF); - b1 << n1.getOperator(); - } - b1.append(n1.begin(), n1.end()); - n1 = b1; - Debug("mgd") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; - if(pf2->d_node.getKind() == kind::APPLY_UF) { - Assert(n1 == pf2->d_node); - } - } - - Debug("mgd") << "n2.getOperator().getType().getNumChildren() = " - << n2.getOperator().getType().getNumChildren() << std::endl; - Debug("mgd") << "n2.getNumChildren() + 1 = " - << n2.getNumChildren() + 1 << std::endl; - - Assert(!( - (n2.getKind() == kind::PARTIAL_SELECT_0 && n2.getNumChildren() == 2))); - if (n2.getKind() == kind::PARTIAL_SELECT_1 && n2.getNumChildren() == 2) { - Debug("mgd") << "Finished a SELECT. Updating.." << std::endl; - b2.clear(kind::SELECT); - b2.append(n2.begin(), n2.end()); - n2 = b2; - Debug("mgd") << "New n2: " << n2 << std::endl; - } else if(n2.getOperator().getType().getNumChildren() == n2.getNumChildren() + 1) { - if(ProofManager::currentPM()->hasOp(n2.getOperator())) { - b2.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst<Kind>()); - } else { - b2.clear(kind::APPLY_UF); - b2 << n2.getOperator(); - } - b2.append(n2.begin(), n2.end()); - n2 = b2; - } - Node n = (side == 0 ? n1.eqNode(n2) : n2.eqNode(n1)); - - Debug("mgdx") << "\ncong proved: " << n << "\n"; - return n; - } - case theory::eq::MERGED_THROUGH_REFLEXIVITY: - { - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - out << "(refl _ "; - tp->printTerm(NodeManager::currentNM()->toExpr(pf.d_node), out, map); - out << ")"; - return pf.d_node.eqNode(pf.d_node); - } - case theory::eq::MERGED_THROUGH_EQUALITY: - { - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - Debug("pf::array") << "ArrayProof::toStream: getLitName( " << pf.d_node.negate() << " ) = " << - ProofManager::getLitName(pf.d_node.negate()) << std::endl; - out << ProofManager::getLitName(pf.d_node.negate()); - return pf.d_node; - } - - case theory::eq::MERGED_THROUGH_TRANS: - { - bool firstNeg = false; - bool secondNeg = false; - - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.size() >= 2); - std::stringstream ss; - Debug("mgd") << "\ndoing trans proof[[\n"; - pf.debug_print("mgd", 0, &proofPrinter); - Debug("mgd") << "\n"; - - pf.d_children[0]->d_node = simplifyBooleanNode(pf.d_children[0]->d_node); - - Node n1 = toStreamRecLFSC(ss, tp, *(pf.d_children[0]), tb + 1, map); - Debug("mgd") << "\ndoing trans proof, got n1 " << n1 << "\n"; - if(tb == 1) { - Debug("mgdx") << "\ntrans proof[0], got n1 " << n1 << "\n"; - } - - bool identicalEqualities = false; - bool evenLengthSequence; - std::stringstream dontCare; - Node nodeAfterEqualitySequence = - toStreamRecLFSC(dontCare, tp, *(pf.d_children[0]), tb + 1, map); - - std::map<size_t, Node> childToStream; - - std::pair<Node, Node> nodePair; - for (size_t i = 1; i < pf.d_children.size(); ++i) - { - std::stringstream ss1(ss.str()), ss2; - ss.str(""); - - // In congruences, we can have something like a[x] - it's important to - // keep these, - // and not turn them into (a[x]=true), because that will mess up the - // congruence application - // later. - - if (pf.d_children[i]->d_id != theory::eq::MERGED_THROUGH_CONGRUENCE) - pf.d_children[i]->d_node = - simplifyBooleanNode(pf.d_children[i]->d_node); - - // It is possible that we've already converted the i'th child to stream. - // If so, - // use previously stored result. Otherwise, convert and store. - Node n2; - if (childToStream.find(i) != childToStream.end()) - n2 = childToStream[i]; - else - { - n2 = toStreamRecLFSC(ss2, tp, *(pf.d_children[i]), tb + 1, map); - childToStream[i] = n2; - } - - Debug("mgd") << "\ndoing trans proof, got (first) n2 " << n2 << "\n"; - - // The following branch is dedicated to handling sequences of identical - // equalities, - // i.e. trans[ a=b, a=b, a=b ]. - // - // There are two cases: - // 1. The number of equalities is odd. Then, the sequence can be - // collapsed to just one equality, - // i.e. a=b. - // 2. The number of equalities is even. Now, we have two options: a=a - // or b=b. To determine this, - // we look at the node after the equality sequence. If it needs a, - // we go for a=a; and if it needs - // b, we go for b=b. If there is no following node, we look at the - // goal of the transitivity proof, - // and use it to determine which option we need. - - if (n2.getKind() == kind::EQUAL) - { - if (((n1[0] == n2[0]) && (n1[1] == n2[1])) - || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) - { - // We are in a sequence of identical equalities - - Debug("pf::array") << "Detected identical equalities: " << std::endl - << "\t" << n1 << std::endl; - - if (!identicalEqualities) - { - // The sequence of identical equalities has started just now - identicalEqualities = true; - - Debug("pf::array") - << "The sequence is just beginning. Determining length..." - << std::endl; - - // Determine whether the length of this sequence is odd or even. - evenLengthSequence = true; - bool sequenceOver = false; - size_t j = i + 1; - - while (j < pf.d_children.size() && !sequenceOver) - { - std::stringstream ignore; - nodeAfterEqualitySequence = - toStreamRecLFSC(ignore, tp, *(pf.d_children[j]), tb + 1, map); - if (((nodeAfterEqualitySequence[0] == n1[0]) - && (nodeAfterEqualitySequence[1] == n1[1])) - || ((nodeAfterEqualitySequence[0] == n1[1]) - && (nodeAfterEqualitySequence[1] == n1[0]))) - { - evenLengthSequence = !evenLengthSequence; - } - else - { - sequenceOver = true; - } - - ++j; - } - - nodePair = - tp->identicalEqualitiesPrinterHelper(evenLengthSequence, - sequenceOver, - pf, - map, - ss1.str(), - &ss, - n1, - nodeAfterEqualitySequence); - n1 = nodePair.first; - nodeAfterEqualitySequence = nodePair.second; - } - else - { - ss.str(ss1.str()); - } - - // Ignore the redundancy. - continue; - } - } - - if (identicalEqualities) - { - // We were in a sequence of identical equalities, but it has now ended. - // Resume normal operation. - identicalEqualities = false; - } - - Debug("mgd") << "\ndoing trans proof, got n2 " << n2 << "\n"; - if (tb == 1) - { - Debug("mgdx") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; - Debug("mgdx") << (n2.getKind() == kind::EQUAL) << "\n"; - - if ((n1.getNumChildren() >= 2) && (n2.getNumChildren() >= 2)) - { - Debug("mgdx") << n1[0].getId() << " " << n1[1].getId() << " / " - << n2[0].getId() << " " << n2[1].getId() << "\n"; - Debug("mgdx") << n1[0].getId() << " " << n1[0] << "\n"; - Debug("mgdx") << n1[1].getId() << " " << n1[1] << "\n"; - Debug("mgdx") << n2[0].getId() << " " << n2[0] << "\n"; - Debug("mgdx") << n2[1].getId() << " " << n2[1] << "\n"; - Debug("mgdx") << (n1[0] == n2[0]) << "\n"; - Debug("mgdx") << (n1[1] == n2[1]) << "\n"; - Debug("mgdx") << (n1[0] == n2[1]) << "\n"; - Debug("mgdx") << (n1[1] == n2[0]) << "\n"; - } - } - - // We can hadnle one of the equalities being negative, but not both - Assert((n1.getKind() != kind::NOT) || (n2.getKind() != kind::NOT)); - - firstNeg = false; - secondNeg = false; - - if (n1.getKind() == kind::NOT) { - Debug("mgdx") << "n1 is negative" << std::endl; - Debug("pf::array") << "n1 = " << n1 << ", n2 = " << n2 << std::endl; - firstNeg = true; - ss << "(negtrans1 _ _ _ _ "; - n1 = n1[0]; - } else if (n2.getKind() == kind::NOT) { - Debug("mgdx") << "n2 is negative" << std::endl; - Debug("pf::array") << "n1 = " << n1 << ", n2 = " << n2 << std::endl; - secondNeg = true; - ss << "(negtrans2 _ _ _ _ "; - n2 = n2[0]; - } else { - ss << "(trans _ _ _ _ "; - } - - if((n2.getKind() == kind::EQUAL) && (n1.getKind() == kind::EQUAL)) - // Both elements of the transitivity rule are equalities/iffs - { - if(n1[0] == n2[0]) { - if(tb == 1) { Debug("mgdx") << "case 1\n"; } - n1 = n1[1].eqNode(n2[1]); - ss << (firstNeg ? "(negsymm _ _ _ " : "(symm _ _ _ ") << ss1.str() << ") " << ss2.str(); - } else if(n1[1] == n2[1]) { - if(tb == 1) { Debug("mgdx") << "case 2\n"; } - n1 = n1[0].eqNode(n2[0]); - ss << ss1.str() << (secondNeg ? " (negsymm _ _ _ " : " (symm _ _ _ " ) << ss2.str() << ")"; - } else if(n1[0] == n2[1]) { - if(tb == 1) { Debug("mgdx") << "case 3\n"; } - if(!firstNeg && !secondNeg) { - n1 = n2[0].eqNode(n1[1]); - ss << ss2.str() << " " << ss1.str(); - } else if (firstNeg) { - n1 = n1[1].eqNode(n2[0]); - ss << " (negsymm _ _ _ " << ss1.str() << ") (symm _ _ _ " << ss2.str() << ")"; - } else { - Assert(secondNeg); - n1 = n1[1].eqNode(n2[0]); - ss << " (symm _ _ _ " << ss1.str() << ") (negsymm _ _ _ " << ss2.str() << ")"; - } - if(tb == 1) { Debug("mgdx") << "++ proved " << n1 << "\n"; } - } else if(n1[1] == n2[0]) { - if(tb == 1) { Debug("mgdx") << "case 4\n"; } - n1 = n1[0].eqNode(n2[1]); - ss << ss1.str() << " " << ss2.str(); - } else { - Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; - Warning() << "0 proves " << n1 << "\n"; - Warning() << "1 proves " << n2 << "\n\n"; - pf.debug_print("mgdx", 0, &proofPrinter); - //toStreamRec(Warning.getStream(), pf, 0); - Warning() << "\n\n"; - Unreachable(); - } - Debug("mgd") << "++ trans proof[" << i << "], now have " << n1 << std::endl; - } else if(n1.getKind() == kind::EQUAL) { - // n1 is an equality/iff, but n2 is a predicate - if(n1[0] == n2) { - n1 = n1[1]; - ss << (firstNeg ? "(negsymm _ _ _ " : "(symm _ _ _ ") - << ss1.str() << ") (pred_eq_t _ " << ss2.str() << ")"; - } else if(n1[1] == n2) { - n1 = n1[0]; - ss << ss1.str() << " (pred_eq_t _ " << ss2.str() << ")"; - } else { - Unreachable(); - } - } else if(n2.getKind() == kind::EQUAL) { - // n2 is an equality/iff, but n1 is a predicate - if(n2[0] == n1) { - n1 = n2[1]; - ss << (secondNeg ? "(negsymm _ _ _ " : "(symm _ _ _ ") - << ss2.str() << ") (pred_eq_t _ " << ss1.str() << ")"; - } else if(n2[1] == n1) { - n1 = n2[0]; - ss << ss2.str() << " (pred_eq_t _ " << ss1.str() << ")"; - } else { - Unreachable(); - } - } else { - // Both n1 and n2 are prediacates. Don't know what to do... - Unreachable(); - } - - ss << ")"; - - if (firstNeg || secondNeg) { - n1 = (n1.getKind() == kind::NOT) ? n1[0] : n1.notNode(); - } - } - - out << ss.str(); - Debug("mgd") << "\n++ trans proof done, have proven " << n1 << std::endl; - //return (firstNeg || secondNeg) ? n1.notNode() : n1; - return n1; - } - - case theory::eq::MERGED_THROUGH_CONSTANTS: - { - Debug("pf::array") << "Proof for: " << pf.d_node << std::endl; - Assert(pf.d_node.getKind() == kind::NOT); - Node n = pf.d_node[0]; - Assert(n.getKind() == kind::EQUAL); - Assert(n.getNumChildren() == 2); - Assert(n[0].isConst() && n[1].isConst()); - - ProofManager::getTheoryProofEngine()->printConstantDisequalityProof( - out, n[0].toExpr(), n[1].toExpr(), map); - return pf.d_node; - } - - default: - { - if (pf.d_id == d_reasonRow) - { - Debug("mgd") << "row lemma: " << pf.d_node << std::endl; - Assert(pf.d_node.getKind() == kind::EQUAL); - - if (pf.d_node[1].getKind() == kind::SELECT) - { - // This is the case where ((a[i]:=t)[k] == a[k]), and the sub-proof - // explains why (i != k). - TNode t1, t2, t3, t4; - Node ret; - if (pf.d_node[1].getKind() == kind::SELECT - && pf.d_node[1][0].getKind() == kind::STORE - && pf.d_node[0].getKind() == kind::SELECT - && pf.d_node[0][0] == pf.d_node[1][0][0] - && pf.d_node[0][1] == pf.d_node[1][1]) - { - t2 = pf.d_node[1][0][1]; - t3 = pf.d_node[1][1]; - t1 = pf.d_node[0][0]; - t4 = pf.d_node[1][0][2]; - ret = pf.d_node[1].eqNode(pf.d_node[0]); - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 - << "\nt4 " << t4 << "\n"; - } - else - { - Assert(pf.d_node[0].getKind() == kind::SELECT - && pf.d_node[0][0].getKind() == kind::STORE - && pf.d_node[1].getKind() == kind::SELECT - && pf.d_node[1][0] == pf.d_node[0][0][0] - && pf.d_node[1][1] == pf.d_node[0][1]); - t2 = pf.d_node[0][0][1]; - t3 = pf.d_node[0][1]; - t1 = pf.d_node[1][0]; - t4 = pf.d_node[0][0][2]; - ret = pf.d_node; - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 - << "\nt4 " << t4 << "\n"; - } - - // inner index != outer index - // t3 is the outer index - - Assert(pf.d_children.size() == 1); - std::stringstream ss; - Node subproof = - toStreamRecLFSC(ss, tp, *(pf.d_children[0]), tb + 1, map); - - out << "(row _ _ "; - tp->printTerm(t2.toExpr(), out, map); - out << " "; - tp->printTerm(t3.toExpr(), out, map); - out << " "; - tp->printTerm(t1.toExpr(), out, map); - out << " "; - tp->printTerm(t4.toExpr(), out, map); - out << " "; - - Debug("pf::array") << "pf.d_children[0]->d_node is: " - << pf.d_children[0]->d_node << ". t3 is: " << t3 - << std::endl - << "subproof is: " << subproof << std::endl; - - Debug("pf::array") << "Subproof is: " << ss.str() << std::endl; - - // The subproof needs to show that t2 != t3. This can either be a direct - // disequality, - // or, if (wlog) t2 is constant, it can show that t3 is equal to another - // constant. - if (subproof.getKind() == kind::NOT) - { - // The subproof is for t2 != t3 (or t3 != t2) - if (subproof[0][1] == t3) - { - Debug("pf::array") << "Dont need symmetry!" << std::endl; - out << ss.str(); - } - else - { - Debug("pf::array") << "Need symmetry!" << std::endl; - out << "(negsymm _ _ _ " << ss.str() << ")"; - } - } - else - { - // Either t2 or t3 is a constant. - Assert(subproof.getKind() == kind::EQUAL); - Assert(subproof[0].isConst() || subproof[1].isConst()); - Assert(t2.isConst() || t3.isConst()); - Assert(!(t2.isConst() && t3.isConst())); - - bool t2IsConst = t2.isConst(); - if (subproof[0].isConst()) - { - if (t2IsConst) - { - // (t3 == subproof[1]) == subproof[0] != t2 - // goal is t2 != t3 - // subproof already shows constant = t3 - Assert(t3 == subproof[1]); - out << "(negtrans _ _ _ _ "; - tp->printConstantDisequalityProof( - out, t2.toExpr(), subproof[0].toExpr(), map); - out << " "; - out << ss.str(); - out << ")"; - } - else - { - Assert(t2 == subproof[1]); - out << "(negsymm _ _ _ "; - out << "(negtrans _ _ _ _ "; - tp->printConstantDisequalityProof( - out, t3.toExpr(), subproof[0].toExpr(), map); - out << " "; - out << ss.str(); - out << "))"; - } - } - else - { - if (t2IsConst) - { - // (t3 == subproof[0]) == subproof[1] != t2 - // goal is t2 != t3 - // subproof already shows constant = t3 - Assert(t3 == subproof[0]); - out << "(negtrans _ _ _ _ "; - tp->printConstantDisequalityProof( - out, t2.toExpr(), subproof[1].toExpr(), map); - out << " "; - out << "(symm _ _ _ " << ss.str() << ")"; - out << ")"; - } - else - { - Assert(t2 == subproof[0]); - out << "(negsymm _ _ _ "; - out << "(negtrans _ _ _ _ "; - tp->printConstantDisequalityProof( - out, t3.toExpr(), subproof[1].toExpr(), map); - out << " "; - out << "(symm _ _ _ " << ss.str() << ")"; - out << "))"; - } - } - } - - out << ")"; - return ret; - } - else - { - Debug("pf::array") << "In the case of NEGATIVE ROW" << std::endl; - - Debug("pf::array") << "pf.d_children[0]->d_node is: " - << pf.d_children[0]->d_node << std::endl; - - // This is the case where (i == k), and the sub-proof explains why - // ((a[i]:=t)[k] != a[k]) - - // If we wanted to remove the need for "negativerow", we would need to - // prove i==k using a new satlem. We would: - // 1. Create a new satlem. - // 2. Assume that i != k - // 3. Apply ROW to show that ((a[i]:=t)[k] == a[k]) - // 4. Contradict this with the fact that ((a[i]:=t)[k] != a[k]), - // obtaining our contradiction - - TNode t1, t2, t3, t4; - Node ret; - - // pf.d_node is an equality, i==k. - t1 = pf.d_node[0]; - t2 = pf.d_node[1]; - - // pf.d_children[0]->d_node will have the form: (not (= (select (store - // a_565 i7 e_566) i1) (select a_565 i1))), - // or its symmetrical version. - - unsigned side; - if (pf.d_children[0]->d_node[0][0].getKind() == kind::SELECT - && pf.d_children[0]->d_node[0][0][0].getKind() == kind::STORE) - { - side = 0; - } - else if (pf.d_children[0]->d_node[0][1].getKind() == kind::SELECT - && pf.d_children[0]->d_node[0][1][0].getKind() == kind::STORE) - { - side = 1; - } - else - { - Unreachable(); - } - - Debug("pf::array") << "Side is: " << side << std::endl; - - // The array's index and element types will come from the subproof... - t3 = pf.d_children[0]->d_node[0][side][0][0]; - t4 = pf.d_children[0]->d_node[0][side][0][2]; - ret = pf.d_node; - - // The order of indices needs to match; we might have to swap t1 and t2 - // and then apply symmetry. - bool swap = (t2 == pf.d_children[0]->d_node[0][side][0][1]); - - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\nt4 " - << t4 << "\n"; - - Assert(pf.d_children.size() == 1); - std::stringstream ss; - Node subproof = - toStreamRecLFSC(ss, tp, *(pf.d_children[0]), tb + 1, map); - - Debug("pf::array") << "Subproof is: " << ss.str() << std::endl; - - if (swap) - { - out << "(symm _ _ _ "; - } - - out << "(negativerow _ _ "; - tp->printTerm(swap ? t2.toExpr() : t1.toExpr(), out, map); - out << " "; - tp->printTerm(swap ? t1.toExpr() : t2.toExpr(), out, map); - out << " "; - tp->printTerm(t3.toExpr(), out, map); - out << " "; - tp->printTerm(t4.toExpr(), out, map); - out << " "; - - if (side != 0) - { - out << "(negsymm _ _ _ " << ss.str() << ")"; - } - else - { - out << ss.str(); - } - - out << ")"; - - if (swap) - { - out << ") "; - } - - return ret; - } - } - else if (pf.d_id == d_reasonRow1) - { - Debug("mgd") << "row1 lemma: " << pf.d_node << std::endl; - Assert(pf.d_node.getKind() == kind::EQUAL); - TNode t1, t2, t3; - Node ret; - if (pf.d_node[1].getKind() == kind::SELECT - && pf.d_node[1][0].getKind() == kind::STORE - && pf.d_node[1][0][1] == pf.d_node[1][1] - && pf.d_node[1][0][2] == pf.d_node[0]) - { - t1 = pf.d_node[1][0][0]; - t2 = pf.d_node[1][0][1]; - t3 = pf.d_node[0]; - ret = pf.d_node[1].eqNode(pf.d_node[0]); - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; - } - else - { - Assert(pf.d_node[0].getKind() == kind::SELECT - && pf.d_node[0][0].getKind() == kind::STORE - && pf.d_node[0][0][1] == pf.d_node[0][1] - && pf.d_node[0][0][2] == pf.d_node[1]); - t1 = pf.d_node[0][0][0]; - t2 = pf.d_node[0][0][1]; - t3 = pf.d_node[1]; - ret = pf.d_node; - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; - } - out << "(row1 _ _ "; - tp->printTerm(t1.toExpr(), out, map); - out << " "; - tp->printTerm(t2.toExpr(), out, map); - out << " "; - tp->printTerm(t3.toExpr(), out, map); - out << ")"; - return ret; - } - else if (pf.d_id == d_reasonExt) { - Assert(pf.d_node.getKind() == kind::NOT); - Assert(pf.d_node[0].getKind() == kind::EQUAL); - Assert(pf.d_children.size() == 1); - std::shared_ptr<theory::eq::EqProof> child_proof = pf.d_children[0]; - Assert(child_proof->d_node.getKind() == kind::NOT); - Assert(child_proof->d_node[0].getKind() == kind::EQUAL); - - Debug("mgd") << "EXT lemma: " << pf.d_node << std::endl; - - TNode t1, t2, t3; - t1 = child_proof->d_node[0][0]; - t2 = child_proof->d_node[0][1]; - t3 = pf.d_node[0][0][1]; - - Debug("mgd") << "t1 " << t1 << "\nt2 " << t2 << "\nt3 " << t3 << "\n"; - - out << "(or_elim_1 _ _ "; - out << ProofManager::getLitName(child_proof->d_node[0]); - out << " "; - out << ProofManager::getArrayProof()->skolemToLiteral(t3.toExpr()); - out << ")"; - - return pf.d_node; - } - - else { - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - Debug("mgd") << "theory proof: " << pf.d_node << " by rule " << int(pf.d_id) << std::endl; - AlwaysAssert(false); - return pf.d_node; - } -} - } -} - -ArrayProof::ArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* pe) - : TheoryProof(arrays, pe) -{} - -theory::TheoryId ArrayProof::getTheoryId() { return theory::THEORY_ARRAYS; } -void ArrayProof::registerTerm(Expr term) { - // already registered - if (d_declarations.find(term) != d_declarations.end()) - return; - - Type type = term.getType(); - if (type.isSort()) { - // declare uninterpreted sorts - d_sorts.insert(type); - } - - if (term.getKind() == kind::APPLY_UF) { - Expr function = term.getOperator(); - d_declarations.insert(function); - } - - if (term.isVariable()) { - d_declarations.insert(term); - } - - if (term.getKind() == kind::SELECT && term.getType().isBoolean()) { - // Ensure cnf literals - Node asNode(term); - ProofManager::currentPM()->ensureLiteral( - asNode.eqNode(NodeManager::currentNM()->mkConst(true))); - ProofManager::currentPM()->ensureLiteral( - asNode.eqNode(NodeManager::currentNM()->mkConst(false))); - } - - // recursively declare all other terms - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - // could belong to other theories - d_proofEngine->registerTerm(term[i]); - } -} - -std::string ArrayProof::skolemToLiteral(Expr skolem) { - Debug("pf::array") << "ArrayProof::skolemToLiteral( " << skolem << ")" << std::endl; - Assert(d_skolemToLiteral.find(skolem) != d_skolemToLiteral.end()); - return d_skolemToLiteral[skolem]; -} - -void LFSCArrayProof::printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - Assert(theory::Theory::theoryOf(term) == theory::THEORY_ARRAYS); - - if (theory::Theory::theoryOf(term) != theory::THEORY_ARRAYS) { - // We can get here, for instance, if there's a (select ite ...), e.g. a non-array term - // hiding as a subterm of an array term. In that case, send it back to the dispatcher. - d_proofEngine->printBoundTerm(term, os, map); - return; - } - - if (term.getKind() == kind::VARIABLE || term.getKind() == kind::SKOLEM) { - os << term; - return; - } - - Assert((term.getKind() == kind::SELECT) - || (term.getKind() == kind::PARTIAL_SELECT_0) - || (term.getKind() == kind::PARTIAL_SELECT_1) - || (term.getKind() == kind::STORE)); - - switch (term.getKind()) { - case kind::SELECT: { - Assert(term.getNumChildren() == 2); - - bool convertToBool = (term[1].getType().isBoolean() && !d_proofEngine->printsAsBool(term[1])); - - os << "(apply _ _ (apply _ _ (read "; - printSort(ArrayType(term[0].getType()).getIndexType(), os); - os << " "; - printSort(ArrayType(term[0].getType()).getConstituentType(), os); - os << ") "; - printTerm(term[0], os, map); - os << ") "; - if (convertToBool) os << "(f_to_b "; - printTerm(term[1], os, map); - if (convertToBool) os << ")"; - os << ") "; - return; - } - - case kind::PARTIAL_SELECT_0: - Assert(term.getNumChildren() == 1); - os << "(read "; - printSort(ArrayType(term[0].getType()).getIndexType(), os); - os << " "; - printSort(ArrayType(term[0].getType()).getConstituentType(), os); - os << ") "; - return; - - case kind::PARTIAL_SELECT_1: - Debug("pf::array") << "This branch has not beed tested yet." << std::endl; - Unreachable(); - - Assert(term.getNumChildren() == 1); - os << "(apply _ _ (read "; - printSort(ArrayType(term[0].getType()).getIndexType(), os); - os << " "; - printSort(ArrayType(term[0].getType()).getConstituentType(), os); - os << ") "; - printTerm(term[0], os, map); - os << ") "; - return; - - case kind::STORE: - os << "(apply _ _ (apply _ _ (apply _ _ (write "; - printSort(ArrayType(term[0].getType()).getIndexType(), os); - os << " "; - printSort(ArrayType(term[0].getType()).getConstituentType(), os); - os << ") "; - printTerm(term[0], os, map); - os << ") "; - printTerm(term[1], os, map); - os << ") "; - printTerm(term[2], os, map); - os << ") "; - return; - - default: - Unreachable(); - return; - } -} - -void LFSCArrayProof::printOwnedSort(Type type, std::ostream& os) { - Debug("pf::array") << std::endl << "(pf::array) LFSCArrayProof::printOwnedSort: type is: " << type << std::endl; - Assert(type.isArray() || type.isSort()); - if (type.isArray()){ - ArrayType array_type(type); - - Debug("pf::array") << "LFSCArrayProof::printOwnedSort: type is an array. Index type: " - << array_type.getIndexType() - << ", element type: " << array_type.getConstituentType() << std::endl; - - os << "(Array "; - printSort(array_type.getIndexType(), os); - os << " "; - printSort(array_type.getConstituentType(), os); - os << ")"; - } else { - os << type; - } -} - -void LFSCArrayProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { - os << " ;; Array Theory Lemma \n;;"; - for (unsigned i = 0; i < lemma.size(); ++i) { - os << lemma[i] <<" "; - } - os <<"\n"; - //os << " (clausify_false trust)"; - ArrayProof::printTheoryLemmaProof(lemma, os, paren, map); -} - -void LFSCArrayProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { - // declaring the sorts - for (TypeSet::const_iterator it = d_sorts.begin(); it != d_sorts.end(); ++it) { - if (!ProofManager::currentPM()->wasPrinted(*it)) { - os << "(% " << *it << " sort\n"; - paren << ")"; - ProofManager::currentPM()->markPrinted(*it); - } - } -} - -void LFSCArrayProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { - Debug("pf::array") << "Arrays declaring terms..." << std::endl; - - for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { - Expr term = *it; - - Assert(term.getType().isArray() || term.isVariable()); - - Debug("pf::array") << "LFSCArrayProof::printDeclarations: term is: " << term - << ". It's type is: " << term.getType() - << std::endl; - - if (term.getType().isArray()){ - ArrayType array_type(term.getType()); - - Debug("pf::array") << "LFSCArrayProof::printDeclarations: term is an array. Index type: " - << array_type.getIndexType() - << ", element type: " << array_type.getConstituentType() << std::endl; - - os << "(% " << ProofManager::sanitize(term) << " "; - os << "(term "; - os << "(Array "; - - printSort(array_type.getIndexType(), os); - os << " "; - printSort(array_type.getConstituentType(), os); - - os << "))\n"; - paren << ")"; - } else { - Assert(term.isVariable()); - if (ProofManager::getSkolemizationManager()->isSkolem(*it)) { - Debug("pf::array") << "This term is a skoelm!" << std::endl; - d_skolemDeclarations.insert(*it); - } else { - os << "(% " << ProofManager::sanitize(term) << " "; - os << "(term "; - os << term.getType() << ")\n"; - paren << ")"; - } - } - } - - Debug("pf::array") << "Declaring terms done!" << std::endl; -} - -void LFSCArrayProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { - Debug("pf::array") << "Array: print deferred declarations called" << std::endl; - - unsigned count = 1; - for (ExprSet::const_iterator it = d_skolemDeclarations.begin(); it != d_skolemDeclarations.end(); ++it) { - Expr term = *it; - Node equality = ProofManager::getSkolemizationManager()->getDisequality(*it); - - Debug("pf::array") << "LFSCArrayProof::printDeferredDeclarations: term is: " << *it << std::endl - << "It is a witness for: " << equality << std::endl; - - std::ostringstream newSkolemLiteral; - newSkolemLiteral << ".sl" << count++; - std::string skolemLiteral = newSkolemLiteral.str(); - - d_skolemToLiteral[*it] = skolemLiteral; - - Debug("pf::array") << "LFSCArrayProof::printDeferredDeclarations: new skolem literal is: " << skolemLiteral << std::endl; - - Assert(equality.getKind() == kind::NOT); - Assert(equality[0].getKind() == kind::EQUAL); - - Node array_one = equality[0][0]; - Node array_two = equality[0][1]; - - ProofLetMap map; - os << "(ext _ _ "; - printTerm(array_one.toExpr(), os, map); - os << " "; - printTerm(array_two.toExpr(), os, map); - os << " (\\ "; - os << ProofManager::sanitize(*it); - os << " (\\ "; - os << skolemLiteral.c_str(); - os << "\n"; - - paren << ")))"; - } -} - -void LFSCArrayProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { - // Nothing to do here at this point. -} - -bool LFSCArrayProof::printsAsBool(const Node &n) -{ - if (n.getKind() == kind::SELECT) - return true; - - return false; -} - -} /* CVC4 namespace */ diff --git a/src/proof/array_proof.h b/src/proof/array_proof.h deleted file mode 100644 index ffcff165a..000000000 --- a/src/proof/array_proof.h +++ /dev/null @@ -1,121 +0,0 @@ -/********************* */ -/*! \file array_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Tim King, Mathias Preiner, Guy Katz - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Arrray proof - ** - ** Arrau proof - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__ARRAY__PROOF_H -#define CVC4__ARRAY__PROOF_H - -#include <memory> -#include <unordered_set> - -#include "expr/expr.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" -#include "theory/arrays/theory_arrays.h" -#include "theory/uf/equality_engine.h" - -namespace CVC4 { - -// Proof object outputted by TheoryARRAY. -class ProofArray : public Proof { - public: - ProofArray(std::shared_ptr<theory::eq::EqProof> pf, unsigned row, - unsigned row1, unsigned ext); - - void registerSkolem(Node equality, Node skolem); - - void toStream(std::ostream& out) const override; - void toStream(std::ostream& out, const ProofLetMap& map) const override; - - private: - void toStreamLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - const ProofLetMap& map) const; - - Node toStreamRecLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - unsigned tb, - const ProofLetMap& map) const; - - // It is simply an equality engine proof. - std::shared_ptr<theory::eq::EqProof> d_proof; - - /** Merge tag for ROW applications */ - unsigned d_reasonRow; - /** Merge tag for ROW1 applications */ - unsigned d_reasonRow1; - /** Merge tag for EXT applications */ - unsigned d_reasonExt; -}; - -namespace theory { -namespace arrays{ -class TheoryArrays; -} /* namespace CVC4::theory::arrays */ -} /* namespace CVC4::theory */ - -typedef std::unordered_set<Type, TypeHashFunction > TypeSet; - -class ArrayProof : public TheoryProof { - // TODO: whatever goes in this theory -protected: - TypeSet d_sorts; // all the uninterpreted sorts in this theory - ExprSet d_declarations; // all the variable/function declarations - ExprSet d_skolemDeclarations; // all the skolem variable declarations - std::map<Expr, std::string> d_skolemToLiteral; - theory::TheoryId getTheoryId() override; - - public: - ArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* proofEngine); - - std::string skolemToLiteral(Expr skolem); - - void registerTerm(Expr term) override; -}; - -class LFSCArrayProof : public ArrayProof { -public: - LFSCArrayProof(theory::arrays::TheoryArrays* arrays, TheoryProofEngine* proofEngine) - : ArrayProof(arrays, proofEngine) - {} - - void printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) override; - void printOwnedSort(Type type, std::ostream& os) override; - void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; - void printSortDeclarations(std::ostream& os, std::ostream& paren) override; - void printTermDeclarations(std::ostream& os, std::ostream& paren) override; - void printDeferredDeclarations(std::ostream& os, - std::ostream& paren) override; - void printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) override; - - bool printsAsBool(const Node& n) override; -}; - - -}/* CVC4 namespace */ - -#endif /* CVC4__ARRAY__PROOF_H */ diff --git a/src/proof/bitvector_proof.cpp b/src/proof/bitvector_proof.cpp deleted file mode 100644 index 98e3300f5..000000000 --- a/src/proof/bitvector_proof.cpp +++ /dev/null @@ -1,792 +0,0 @@ -/********************* */ -/*! \file bitvector_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Liana Hadarean, Guy Katz, Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** Contains implementions (e.g. code for printing bitblasting bindings that is - ** common to all kinds of bitvector proofs. - **/ - -#include "proof/bitvector_proof.h" -#include "options/bv_options.h" -#include "options/proof_options.h" -#include "proof/proof_output_channel.h" -#include "proof/theory_proof.h" -#include "prop/sat_solver_types.h" -#include "theory/bv/bitblast/bitblaster.h" -#include "theory/bv/theory_bv.h" - -namespace CVC4 { - -namespace proof { -BitVectorProof::BitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine) - : TheoryProof(bv, proofEngine), - d_declarations(), - d_seenBBTerms(), - d_bbTerms(), - d_bbAtoms(), - d_bitblaster(nullptr), - d_useConstantLetification(false), - d_cnfProof() -{ -} - -void BitVectorProof::setBitblaster(theory::bv::TBitblaster<Node>* bb) -{ - Assert(d_bitblaster == NULL); - d_bitblaster = bb; -} - -void BitVectorProof::registerTermBB(Expr term) -{ - Debug("pf::bv") << "BitVectorProof::registerTermBB( " << term - << " )" << std::endl; - - if (d_seenBBTerms.find(term) != d_seenBBTerms.end()) return; - - d_seenBBTerms.insert(term); - d_bbTerms.push_back(term); - - // If this term gets used in the final proof, we will want to register it. - // However, we don't know this at this point; and when the theory proof engine - // sees it, if it belongs to another theory, it won't register it with this - // proof. So, we need to tell the engine to inform us. - - if (theory::Theory::theoryOf(term) != theory::THEORY_BV) - { - Debug("pf::bv") << "\tMarking term " << term - << " for future BV registration" << std::endl; - d_proofEngine->markTermForFutureRegistration(term, theory::THEORY_BV); - } -} - -void BitVectorProof::registerAtomBB(Expr atom, Expr atom_bb) { - Debug("pf::bv") << "BitVectorProof::registerAtomBB( " << atom - << ", " << atom_bb << " )" << std::endl; - - Expr def = atom.eqExpr(atom_bb); - d_bbAtoms.insert(std::make_pair(atom, def)); - registerTerm(atom); - - // Register the atom's terms for bitblasting - registerTermBB(atom[0]); - registerTermBB(atom[1]); -} - -void BitVectorProof::registerTerm(Expr term) { - Debug("pf::bv") << "BitVectorProof::registerTerm( " << term << " )" - << std::endl; - - if (options::lfscLetification() && term.isConst() - && term.getType().isBitVector()) - { - if (d_constantLetMap.find(term) == d_constantLetMap.end()) { - std::ostringstream name; - name << "letBvc" << d_constantLetMap.size(); - d_constantLetMap[term] = name.str(); - } - } - - d_usedBB.insert(term); - - if (theory::Theory::isLeafOf(term, theory::THEORY_BV) && !term.isConst()) - { - d_declarations.insert(term); - } - - Debug("pf::bv") << "Going to register children: " << std::endl; - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - Debug("pf::bv") << "\t" << term[i] << std::endl; - } - - // don't care about parametric operators for bv? - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - d_proofEngine->registerTerm(term[i]); - } -} - -std::string BitVectorProof::getBBTermName(Expr expr) -{ - Debug("pf::bv") << "BitVectorProof::getBBTermName( " << expr << " ) = bt" - << expr.getId() << std::endl; - std::ostringstream os; - os << "bt" << expr.getId(); - return os.str(); -} - -void BitVectorProof::printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - Debug("pf::bv") << std::endl - << "(pf::bv) BitVectorProof::printOwnedTerm( " << term - << " ), theory is: " << theory::Theory::theoryOf(term) - << std::endl; - - Assert(theory::Theory::theoryOf(term) == theory::THEORY_BV); - - // peel off eager bit-blasting trick - if (term.getKind() == kind::BITVECTOR_EAGER_ATOM) { - d_proofEngine->printBoundTerm(term[0], os, map); - return; - } - - switch (term.getKind()) { - case kind::CONST_BITVECTOR : { - printConstant(term, os); - return; - } - case kind::BITVECTOR_AND : - case kind::BITVECTOR_OR : - case kind::BITVECTOR_XOR : - case kind::BITVECTOR_NAND : - case kind::BITVECTOR_NOR : - case kind::BITVECTOR_XNOR : - case kind::BITVECTOR_COMP : - case kind::BITVECTOR_MULT : - case kind::BITVECTOR_PLUS : - case kind::BITVECTOR_SUB : - case kind::BITVECTOR_UDIV : - case kind::BITVECTOR_UREM : - case kind::BITVECTOR_UDIV_TOTAL : - case kind::BITVECTOR_UREM_TOTAL : - case kind::BITVECTOR_SDIV : - case kind::BITVECTOR_SREM : - case kind::BITVECTOR_SMOD : - case kind::BITVECTOR_SHL : - case kind::BITVECTOR_LSHR : - case kind::BITVECTOR_ASHR : - case kind::BITVECTOR_CONCAT : { - printOperatorNary(term, os, map); - return; - } - case kind::BITVECTOR_NEG : - case kind::BITVECTOR_NOT : - case kind::BITVECTOR_ROTATE_LEFT : - case kind::BITVECTOR_ROTATE_RIGHT : { - printOperatorUnary(term, os, map); - return; - } - case kind::EQUAL : - case kind::BITVECTOR_ULT : - case kind::BITVECTOR_ULE : - case kind::BITVECTOR_UGT : - case kind::BITVECTOR_UGE : - case kind::BITVECTOR_SLT : - case kind::BITVECTOR_SLE : - case kind::BITVECTOR_SGT : - case kind::BITVECTOR_SGE : { - printPredicate(term, os, map); - return; - } - case kind::BITVECTOR_EXTRACT : - case kind::BITVECTOR_REPEAT : - case kind::BITVECTOR_ZERO_EXTEND : - case kind::BITVECTOR_SIGN_EXTEND : { - printOperatorParametric(term, os, map); - return; - } - case kind::BITVECTOR_BITOF : { - printBitOf(term, os, map); - return; - } - - case kind::VARIABLE: { - os << "(a_var_bv " << utils::getSize(term)<< " " << ProofManager::sanitize(term) << ")"; - return; - } - - case kind::SKOLEM: { - - // TODO: we need to distinguish between "real" skolems (e.g. from array) and "fake" skolems, - // like ITE terms. Is there a more elegant way? - - if (ProofManager::getSkolemizationManager()->isSkolem(term)) { - os << ProofManager::sanitize(term); - } else { - os << "(a_var_bv " << utils::getSize(term)<< " " << ProofManager::sanitize(term) << ")"; - } - return; - } - - default: - Unreachable(); - } -} - -void BitVectorProof::printEmptyClauseProof(std::ostream& os, - std::ostream& paren) -{ - Assert(options::bitblastMode() == options::BitblastMode::EAGER) - << "the BV theory should only be proving bottom directly in the eager " - "bitblasting mode"; -} - -void BitVectorProof::printBitOf(Expr term, - std::ostream& os, - const ProofLetMap& map) -{ - Assert(term.getKind() == kind::BITVECTOR_BITOF); - unsigned bit = term.getOperator().getConst<BitVectorBitOf>().d_bitIndex; - Expr var = term[0]; - - Debug("pf::bv") << "BitVectorProof::printBitOf( " << term << " ), " - << "bit = " << bit << ", var = " << var << std::endl; - - os << "(bitof "; - os << d_exprToVariableName[var]; - os << " " << bit << ")"; -} - -void BitVectorProof::printConstant(Expr term, std::ostream& os) -{ - Assert(term.isConst()); - os << "(a_bv " << utils::getSize(term) << " "; - - if (d_useConstantLetification) { - os << d_constantLetMap[term] << ")"; - } else { - std::ostringstream paren; - int size = utils::getSize(term); - for (int i = size - 1; i >= 0; --i) { - os << "(bvc "; - os << (utils::getBit(term, i) ? "b1" : "b0") <<" "; - paren << ")"; - } - os << " bvn)"; - os << paren.str(); - } -} - -void BitVectorProof::printOperatorNary(Expr term, - std::ostream& os, - const ProofLetMap& map) -{ - std::string op = utils::toLFSCKindTerm(term); - std::ostringstream paren; - std::string holes = term.getKind() == kind::BITVECTOR_CONCAT ? "_ _ " : ""; - unsigned size = term.getKind() == kind::BITVECTOR_CONCAT? utils::getSize(term) : - utils::getSize(term[0]); // cause of COMP - - for (unsigned i = 0; i < term.getNumChildren() - 1; ++i) { - os <<"("<< op <<" " << size <<" " << holes; - } - d_proofEngine->printBoundTerm(term[0], os, map); - os <<" "; - for (unsigned i = 1; i < term.getNumChildren(); ++i) { - d_proofEngine->printBoundTerm(term[i], os, map); - os << ")"; - } -} - -void BitVectorProof::printOperatorUnary(Expr term, - std::ostream& os, - const ProofLetMap& map) -{ - os <<"("; - os << utils::toLFSCKindTerm(term) << " " << utils::getSize(term) <<" "; - os << " "; - d_proofEngine->printBoundTerm(term[0], os, map); - os <<")"; -} - -void BitVectorProof::printPredicate(Expr term, - std::ostream& os, - const ProofLetMap& map) -{ - os <<"("; - os << utils::toLFSCKindTerm(term) << " " << utils::getSize(term[0]) <<" "; - os << " "; - d_proofEngine->printBoundTerm(term[0], os, map); - os << " "; - d_proofEngine->printBoundTerm(term[1], os, map); - os <<")"; -} - -void BitVectorProof::printOperatorParametric(Expr term, - std::ostream& os, - const ProofLetMap& map) -{ - os <<"("; - os << utils::toLFSCKindTerm(term) << " " << utils::getSize(term) <<" "; - os <<" "; - if (term.getKind() == kind::BITVECTOR_REPEAT) { - unsigned amount = - term.getOperator().getConst<BitVectorRepeat>().d_repeatAmount; - os << amount <<" _ "; - } - if (term.getKind() == kind::BITVECTOR_SIGN_EXTEND) { - unsigned amount = - term.getOperator().getConst<BitVectorSignExtend>().d_signExtendAmount; - os << amount <<" _ "; - } - - if (term.getKind() == kind::BITVECTOR_ZERO_EXTEND) { - unsigned amount = - term.getOperator().getConst<BitVectorZeroExtend>().d_zeroExtendAmount; - os << amount<<" _ "; - } - if (term.getKind() == kind::BITVECTOR_EXTRACT) { - unsigned low = utils::getExtractLow(term); - unsigned high = utils::getExtractHigh(term); - os << high <<" " << low << " " << utils::getSize(term[0]); - } - os <<" "; - Assert(term.getNumChildren() == 1); - d_proofEngine->printBoundTerm(term[0], os, map); - os <<")"; -} - -void BitVectorProof::printOwnedSort(Type type, std::ostream& os) -{ - Debug("pf::bv") << std::endl - << "(pf::bv) BitVectorProof::printOwnedSort( " << type << " )" - << std::endl; - Assert(type.isBitVector()); - unsigned width = utils::getSize(type); - os << "(BitVec " << width << ")"; -} - -void BitVectorProof::printSortDeclarations(std::ostream& os, - std::ostream& paren) -{ - // Nothing to do here at this point. -} - -void BitVectorProof::printTermDeclarations(std::ostream& os, - std::ostream& paren) -{ - ExprSet::const_iterator it = d_declarations.begin(); - ExprSet::const_iterator end = d_declarations.end(); - for (; it != end; ++it) { - if ((it->isVariable() || it->isConst()) && !ProofManager::getSkolemizationManager()->isSkolem(*it)) { - d_exprToVariableName[*it] = ProofManager::sanitize(*it); - } else { - std::string newAlias = assignAlias(*it); - d_exprToVariableName[*it] = newAlias; - } - - os << "(% " << d_exprToVariableName[*it] <<" var_bv" << "\n"; - paren <<")"; - } -} - -void BitVectorProof::printDeferredDeclarations(std::ostream& os, - std::ostream& paren) -{ - if (options::lfscLetification()) { - os << std::endl << ";; BV const letification\n" << std::endl; - std::map<Expr,std::string>::const_iterator it; - for (it = d_constantLetMap.begin(); it != d_constantLetMap.end(); ++it) { - os << "\n(@ " << it->second << " "; - std::ostringstream localParen; - int size = utils::getSize(it->first); - for (int i = size - 1; i >= 0; --i) { - os << "(bvc "; - os << (utils::getBit(it->first, i) ? "b1" : "b0") << " "; - localParen << ")"; - } - os << "bvn"; - os << localParen.str(); - paren << ")"; - } - os << std::endl; - - d_useConstantLetification = true; - } -} - -void BitVectorProof::printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) -{ - // Print "trust" statements to bind complex bv variables to their associated terms - - ExprToString::const_iterator it = d_assignedAliases.begin(); - ExprToString::const_iterator end = d_assignedAliases.end(); - - for (; it != end; ++it) { - Debug("pf::bv") << "Printing aliasing declaration for: " << *it << std::endl; - std::stringstream declaration; - declaration << ".fbvd" << d_aliasToBindDeclaration.size(); - d_aliasToBindDeclaration[it->second] = declaration.str(); - - os << "(th_let_pf _ "; - - os << "(trust_f "; - os << "(= (BitVec " << utils::getSize(it->first) << ") "; - os << "(a_var_bv " << utils::getSize(it->first) << " " << it->second << ") "; - d_proofEngine->printBoundTerm(it->first, os, globalLetMap); - os << ")) "; - os << "(\\ "<< d_aliasToBindDeclaration[it->second] << "\n"; - paren << "))"; - } - - os << "\n"; -} - -void BitVectorProof::printTermBitblasting(Expr term, std::ostream& os) -{ - // TODO: once we have the operator elimination rules remove those that we - // eliminated - Assert(term.getType().isBitVector()); - Kind kind = term.getKind(); - - if (theory::Theory::isLeafOf(term, theory::THEORY_BV) && !term.isConst()) - { - // A term is a leaf if it has no children, or if it belongs to another theory - os << "(bv_bbl_var " << utils::getSize(term) << " " << d_exprToVariableName[term]; - os << " _)"; - return; - } - - switch(kind) { - case kind::CONST_BITVECTOR : { - os << "(bv_bbl_const "<< utils::getSize(term) <<" _ "; - std::ostringstream paren; - int size = utils::getSize(term); - if (d_useConstantLetification) { - os << d_constantLetMap[term] << ")"; - } else { - for (int i = size - 1; i>= 0; --i) { - os << "(bvc "; - os << (utils::getBit(term, i) ? "b1" : "b0") <<" "; - paren << ")"; - } - os << " bvn)"; - os << paren.str(); - } - return; - } - - case kind::BITVECTOR_AND : - case kind::BITVECTOR_OR : - case kind::BITVECTOR_XOR : - case kind::BITVECTOR_NAND : - case kind::BITVECTOR_NOR : - case kind::BITVECTOR_XNOR : - case kind::BITVECTOR_COMP : - case kind::BITVECTOR_MULT : - case kind::BITVECTOR_PLUS : - case kind::BITVECTOR_SUB : - case kind::BITVECTOR_CONCAT : { - Debug("pf::bv") << "Bitblasing kind = " << kind << std::endl; - - for (int i = term.getNumChildren() - 1; i > 0; --i) { - os <<"(bv_bbl_"<< utils::toLFSCKind(kind); - - if (kind == kind::BITVECTOR_CONCAT) { - os << " " << utils::getSize(term) << " _"; - } - os << " _ _ _ _ _ _ "; - } - - os << getBBTermName(term[0]) << " "; - - for (unsigned i = 1; i < term.getNumChildren(); ++i) { - os << getBBTermName(term[i]); - os << ") "; - } - return; - } - - case kind::BITVECTOR_NEG : - case kind::BITVECTOR_NOT : - case kind::BITVECTOR_ROTATE_LEFT : - case kind::BITVECTOR_ROTATE_RIGHT : { - os << "(bv_bbl_"<<utils::toLFSCKind(kind); - os << " _ _ _ _ "; - os << getBBTermName(term[0]); - os << ")"; - return; - } - case kind::BITVECTOR_EXTRACT : { - os <<"(bv_bbl_"<<utils::toLFSCKind(kind); - - os << " " << utils::getSize(term) << " "; - os << utils::getExtractHigh(term) << " "; - os << utils::getExtractLow(term) << " "; - os << " _ _ _ _ "; - - os << getBBTermName(term[0]); - os <<")"; - return; - } - case kind::BITVECTOR_REPEAT : - case kind::BITVECTOR_ZERO_EXTEND : - case kind::BITVECTOR_SIGN_EXTEND : { - os << "(bv_bbl_" << utils::toLFSCKind(kind) << " "; - os << utils::getSize(term) << " "; - if (term.getKind() == kind::BITVECTOR_REPEAT) { - unsigned amount = - term.getOperator().getConst<BitVectorRepeat>().d_repeatAmount; - os << amount; - } - if (term.getKind() == kind::BITVECTOR_SIGN_EXTEND) { - unsigned amount = - term.getOperator().getConst<BitVectorSignExtend>().d_signExtendAmount; - os << amount; - } - - if (term.getKind() == kind::BITVECTOR_ZERO_EXTEND) { - unsigned amount = - term.getOperator().getConst<BitVectorZeroExtend>().d_zeroExtendAmount; - os << amount; - } - - os <<" _ _ _ _ "; - os << getBBTermName(term[0]); - os <<")"; - return; - } - case kind::BITVECTOR_UDIV : - case kind::BITVECTOR_UREM : - case kind::BITVECTOR_UDIV_TOTAL : - case kind::BITVECTOR_UREM_TOTAL : - case kind::BITVECTOR_SDIV : - case kind::BITVECTOR_SREM : - case kind::BITVECTOR_SMOD : - case kind::BITVECTOR_SHL : - case kind::BITVECTOR_LSHR : - case kind::BITVECTOR_ASHR : { - // these are terms for which bit-blasting is not supported yet - std::ostringstream paren; - os <<"(trust_bblast_term _ "; - paren <<")"; - d_proofEngine->printLetTerm(term, os); - os <<" "; - std::vector<Node> bits; - d_bitblaster->bbTerm(term, bits); - - for (int i = utils::getSize(term) - 1; i >= 0; --i) { - os << "(bbltc "; - d_proofEngine->printLetTerm((bits[i]).toExpr(), os); - paren << ")"; - } - os << "bbltn" << paren.str(); - return; - } - - default: Unreachable() << "BitVectorProof Unknown operator"; - } -} - -void BitVectorProof::printAtomBitblasting(Expr atom, - std::ostream& os, - bool swap) -{ - Kind kind = atom.getKind(); - switch(kind) { - case kind::BITVECTOR_ULT : - case kind::BITVECTOR_ULE : - case kind::BITVECTOR_UGT : - case kind::BITVECTOR_UGE : - case kind::BITVECTOR_SLT : - case kind::BITVECTOR_SLE : - case kind::BITVECTOR_SGT : - case kind::BITVECTOR_SGE : - case kind::EQUAL: { - Debug("pf::bv") << "Bitblasing kind = " << kind << std::endl; - - os << "(bv_bbl_" << utils::toLFSCKindTerm(atom); - - if (swap) {os << "_swap";} - - os << " _ _ _ _ _ _ "; - os << getBBTermName(atom[0]); - os << " "; - os << getBBTermName(atom[1]); - os << ")"; - - return; - } - default: Unreachable() << "BitVectorProof Unknown atom kind"; - } -} - -void BitVectorProof::printAtomBitblastingToFalse(Expr atom, std::ostream& os) -{ - Assert(atom.getKind() == kind::EQUAL); - - os << "(bv_bbl_=_false"; - os << " _ _ _ _ _ _ "; - os << getBBTermName(atom[0]); - - os << " "; - - os << getBBTermName(atom[1]); - - os << ")"; -} - -void BitVectorProof::printBitblasting(std::ostream& os, std::ostream& paren) -{ - // bit-blast terms - { - Debug("pf::bv") - << "BitVectorProof::printBitblasting: the bitblasted terms are: " - << std::endl; - std::vector<Expr>::const_iterator it = d_bbTerms.begin(); - std::vector<Expr>::const_iterator end = d_bbTerms.end(); - - for (; it != end; ++it) { - if (d_usedBB.find(*it) == d_usedBB.end()) { - Debug("pf::bv") << "\t" << *it << "\t(UNUSED)" << std::endl; - } else { - Debug("pf::bv") << "\t" << *it << std::endl; - } - } - - Debug("pf::bv") << std::endl; - } - - std::vector<Expr>::const_iterator it = d_bbTerms.begin(); - std::vector<Expr>::const_iterator end = d_bbTerms.end(); - for (; it != end; ++it) { - if (d_usedBB.find(*it) == d_usedBB.end() - && options::bitblastMode() != options::BitblastMode::EAGER) - continue; - - // Is this term has an alias, we inject it through the decl_bblast statement - if (hasAlias(*it)) { - os << "(decl_bblast_with_alias _ _ _ _ "; - printTermBitblasting(*it, os); - os << " " << d_aliasToBindDeclaration[d_assignedAliases[*it]] << " "; - os << "(\\ "<< getBBTermName(*it); - os << "\n"; - paren <<"))"; - } else { - os << "(decl_bblast _ _ _ "; - printTermBitblasting(*it, os); - os << "(\\ "<< getBBTermName(*it); - os << "\n"; - paren <<"))"; - } - } - // bit-blast atoms - ExprToExpr::const_iterator ait = d_bbAtoms.begin(); - ExprToExpr::const_iterator aend = d_bbAtoms.end(); - for (; ait != aend; ++ait) { - if (d_usedBB.find(ait->first) == d_usedBB.end() - && options::bitblastMode() != options::BitblastMode::EAGER) - continue; - - os << "(th_let_pf _ "; - if (ait->first.getKind() == kind::CONST_BOOLEAN) { - bool val = ait->first.getConst<bool>(); - os << "(iff_symm " << (val ? "true" : "false" ) << ")"; - } else { - Assert(ait->first == ait->second[0]); - - bool swap = false; - if (ait->first.getKind() == kind::EQUAL) { - Expr bitwiseEquivalence = ait->second[1]; - if ((bitwiseEquivalence.getKind() == kind::CONST_BOOLEAN) && !bitwiseEquivalence.getConst<bool>()) { - printAtomBitblastingToFalse(ait->first, os); - } else { - if (bitwiseEquivalence.getKind() != kind::AND) { - // Just one bit - if (bitwiseEquivalence.getNumChildren() > 0 && bitwiseEquivalence[0].getKind() == kind::BITVECTOR_BITOF) { - swap = (ait->first[1] == bitwiseEquivalence[0][0]); - } - } else { - // Multiple bits - if (bitwiseEquivalence[0].getNumChildren() > 0 && - bitwiseEquivalence[0][0].getKind() == kind::BITVECTOR_BITOF) { - swap = (ait->first[1] == bitwiseEquivalence[0][0][0]); - } else if (bitwiseEquivalence[0].getNumChildren() > 0 && - bitwiseEquivalence[0][1].getKind() == kind::BITVECTOR_BITOF) { - swap = (ait->first[0] == bitwiseEquivalence[0][1][0]); - } - } - - printAtomBitblasting(ait->first, os, swap); - } - } else { - printAtomBitblasting(ait->first, os, swap); - } - } - - os <<"(\\ " << ProofManager::getPreprocessedAssertionName(ait->second) <<"\n"; - paren <<"))"; - } -} - -theory::TheoryId BitVectorProof::getTheoryId() { return theory::THEORY_BV; } - -const std::set<Node>* BitVectorProof::getAtomsInBitblastingProof() -{ - return &d_atomsInBitblastingProof; -} - -std::string BitVectorProof::assignAlias(Expr expr) -{ - Assert(d_exprToVariableName.find(expr) == d_exprToVariableName.end()); - - std::stringstream ss; - ss << "fbv" << d_assignedAliases.size(); - Debug("pf::bv") << "assignAlias( " << expr << ") = " << ss.str() << std::endl; - d_assignedAliases[expr] = ss.str(); - return ss.str(); -} - -bool BitVectorProof::hasAlias(Expr expr) -{ - return d_assignedAliases.find(expr) != d_assignedAliases.end(); -} - -void BitVectorProof::printConstantDisequalityProof( - std::ostream& os, Expr c1, Expr c2, const ProofLetMap& globalLetMap) -{ - Assert(c1.isConst()); - Assert(c2.isConst()); - Assert(utils::getSize(c1) == utils::getSize(c2)); - - os << "(bv_disequal_constants " << utils::getSize(c1) << " "; - - std::ostringstream paren; - - for (int i = utils::getSize(c1) - 1; i >= 0; --i) { - os << "(bvc "; - os << (utils::getBit(c1, i) ? "b1" : "b0") << " "; - paren << ")"; - } - os << "bvn"; - os << paren.str(); - - os << " "; - - for (int i = utils::getSize(c2) - 1; i >= 0; --i) { - os << "(bvc "; - os << (utils::getBit(c2, i) ? "b1" : "b0") << " "; - - } - os << "bvn"; - os << paren.str(); - - os << ")"; -} - -void BitVectorProof::printRewriteProof(std::ostream& os, - const Node& n1, - const Node& n2) -{ - ProofLetMap emptyMap; - os << "(rr_bv_default "; - d_proofEngine->printBoundTerm(n2.toExpr(), os, emptyMap); - os << " "; - d_proofEngine->printBoundTerm(n1.toExpr(), os, emptyMap); - os << ")"; -} - -} // namespace proof - -} // namespace CVC4 diff --git a/src/proof/bitvector_proof.h b/src/proof/bitvector_proof.h deleted file mode 100644 index 9a9071e58..000000000 --- a/src/proof/bitvector_proof.h +++ /dev/null @@ -1,280 +0,0 @@ -/********************* */ -/*! \file bitvector_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner, Liana Hadarean - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Bitvector proof base class - ** - ** Contains code (e.g. proof printing code) which is common to all bitvector - *proofs. - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__BITVECTOR_PROOF_H -#define CVC4__BITVECTOR_PROOF_H - -#include <set> -#include <unordered_map> -#include <unordered_set> -#include <vector> - -#include "expr/expr.h" -#include "proof/cnf_proof.h" -#include "proof/theory_proof.h" -#include "prop/sat_solver.h" -#include "theory/bv/theory_bv.h" - -// Since TBitblaster and BitVectorProof are cyclically dependent, we need this -// forward declaration -namespace CVC4 { -namespace theory { -namespace bv { -template <class T> -class TBitblaster; -} -} // namespace theory -} // namespace CVC4 - -namespace CVC4 { - -namespace proof { - -typedef std::unordered_set<Expr, ExprHashFunction> ExprSet; -typedef std::unordered_map<Expr, ClauseId, ExprHashFunction> ExprToClauseId; -typedef std::unordered_map<Expr, unsigned, ExprHashFunction> ExprToId; -typedef std::unordered_map<Expr, Expr, ExprHashFunction> ExprToExpr; -typedef std::unordered_map<Expr, std::string, ExprHashFunction> ExprToString; - -/** - * A bitvector proof is best understood as having - * - * 1. A declaration of a "bitblasted formulas" -- boolean formulas - * that are each translations of a BV-literal (a comparison between BVs). - * - * (and a proof that each "bitblasted formula" is implied by the - * corresponding BV literal) - * - * 2. A declaration of a cnf formula equisatisfiable to the bitblasted - * formula - * - * (and a proof that each clause is implied by some bitblasted formula) - * - * 3. A proof of UNSAT from the clauses. - * - * This class is responsible for 1 & 2. The proof of UNSAT is delegated to a - * subclass. - */ -class BitVectorProof : public TheoryProof -{ - protected: - BitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine); - virtual ~BitVectorProof(){}; - - // Set of BV variables in the input. (e.g. "a" in [ a = 000 ] ^ [ a == 001 ]) - ExprSet d_declarations; - - // terms and formulas that are actually relevant to the proof - ExprSet d_usedBB; - - ExprSet d_seenBBTerms; // terms that need to be bit-blasted - std::vector<Expr> d_bbTerms; // order of bit-blasting - - /** atoms that need to be bit-blasted, - * BV-literals -> (BV-literals <=> bool formula) - * where a BV literal is a signed or unsigned comparison. - */ - ExprToExpr d_bbAtoms; - - // map from Expr representing normalized lemma to ClauseId in SAT solver - ExprToClauseId d_bbConflictMap; - - theory::bv::TBitblaster<Node>* d_bitblaster; - - /** In an LFSC proof the manifestation of this expression bit-level - * representation will have a string name. This method returns that name. - */ - std::string getBBTermName(Expr expr); - - /** A mapping from constant BV terms to identifiers that will refer to them in - * an LFSC proof, if constant-letification is enabled. - */ - std::map<Expr, std::string> d_constantLetMap; - - /** Should we introduced identifiers to refer to BV constant terms? It may - * reduce the textual size of a proof! - */ - bool d_useConstantLetification; - - /** Temporary storage for the set of nodes in the bitblasted formula which - * correspond to CNF variables eventually used in the proof of unsat on the - * CNF formula - */ - std::set<Node> d_atomsInBitblastingProof; - - /** - * Prints out - * (a) a declaration of bit-level interpretations corresponding to bits in - * the input BV terms. - * (b) a proof that the each BV literal entails a boolean formula on - * bitof expressions. - */ - void printBitblasting(std::ostream& os, std::ostream& paren); - - /** - * The proof that the bit-blasted SAT formula is correctly converted to CNF - */ - std::unique_ptr<CnfProof> d_cnfProof; - - theory::TheoryId getTheoryId() override; - - public: - void printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) override; - - void printOwnedSort(Type type, std::ostream& os) override; - - /** - * Populate the d_atomsInBitblastingProof member. - * See its documentation - */ - virtual void calculateAtomsInBitblastingProof() = 0; - - /** - * Prints out a declaration of the bit-blasting, and the subsequent - * conversion of the result to CNF - * - * @param os the stream to print to - * @param paren a stream that will be placed at the back of the proof (for - * closing parens) - * @param letMap The let-map, which contains information about LFSC - * identifiers and the values they reference. - */ - virtual void printBBDeclarationAndCnf(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) = 0; - - /** - * Prints a proof of the empty clause. - * - * @param os the stream to print to - * @param paren any parentheses to add to the end of the global proof - */ - virtual void printEmptyClauseProof(std::ostream& os, std::ostream& paren); - - /** - * Read the d_atomsInBitblastingProof member. - * See its documentation. - */ - const std::set<Node>* getAtomsInBitblastingProof(); - - void registerTermBB(Expr term); - - /** - * Informs the proof that the `atom` predicate was bitblasted into the - * `atom_bb` term. - * - * The `atom` term must be a comparison of bitvectors, and the `atom_bb` term - * a boolean formula on bitof expressions - */ - void registerAtomBB(Expr atom, Expr atom_bb); - - void registerTerm(Expr term) override; - - /** - * This must be done before registering any terms or atoms, since the CNF - * proof must reflect the result of bitblasting those - * - * Feeds the SAT solver's true and false variables into the CNF stream. - */ - virtual void initCnfProof(prop::CnfStream* cnfStream, - context::Context* cnf, - prop::SatVariable trueVar, - prop::SatVariable falseVar) = 0; - - CnfProof* getCnfProof() { return d_cnfProof.get(); } - - /** - * Attaches this BVP to the given SAT solver, initializing a SAT proof. - * - * This must be invoked before `initCnfProof` because a SAT proof must already - * exist to initialize a CNF proof. - */ - virtual void attachToSatSolver(prop::SatSolver& sat_solver) = 0; - - void setBitblaster(theory::bv::TBitblaster<Node>* bb); - - /** - * Kind of a mess. Used for resulution-based BVP's, where in eager mode this - * must be invoked before printing a proof of the empty clause. In lazy mode - * the behavior and purpose are both highly unclear. - * - * This exists as a virtual method of BitVectorProof, and not - * ResolutionBitVectorProof, because the machinery that invokes it is - * high-level enough that it doesn't know the difference between clausal and - * resolution proofs. - * - * TODO(aozdemir) figure out what is going on and clean this up - * Issue: https://github.com/CVC4/CVC4/issues/2789 - */ - virtual void finalizeConflicts(std::vector<Expr>& conflicts){}; - - private: - ExprToString d_exprToVariableName; - - ExprToString d_assignedAliases; - std::map<std::string, std::string> d_aliasToBindDeclaration; - std::string assignAlias(Expr expr); - bool hasAlias(Expr expr); - - // Functions for printing various BV terms. Helpers for BV's `printOwnedTerm` - void printConstant(Expr term, std::ostream& os); - void printOperatorNary(Expr term, std::ostream& os, const ProofLetMap& map); - void printOperatorUnary(Expr term, std::ostream& os, const ProofLetMap& map); - void printPredicate(Expr term, std::ostream& os, const ProofLetMap& map); - void printOperatorParametric(Expr term, std::ostream& os, const ProofLetMap& map); - void printBitOf(Expr term, std::ostream& os, const ProofLetMap& map); - - /** - * Prints the LFSC construction of a bblast_term for `term` - */ - void printTermBitblasting(Expr term, std::ostream& os); - - /** - * For a given BV-atom (a comparison), prints a proof that that comparison - * holds iff the bitblasted equivalent of it holds. - * Uses a side-condidition to do the bit-blasting. - */ - void printAtomBitblasting(Expr term, std::ostream& os, bool swap); - void printAtomBitblastingToFalse(Expr term, std::ostream& os); - - void printSortDeclarations(std::ostream& os, std::ostream& paren) override; - void printTermDeclarations(std::ostream& os, std::ostream& paren) override; - void printDeferredDeclarations(std::ostream& os, - std::ostream& paren) override; - void printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) override; - - void printConstantDisequalityProof(std::ostream& os, - Expr c1, - Expr c2, - const ProofLetMap& globalLetMap) override; - void printRewriteProof(std::ostream& os, - const Node& n1, - const Node& n2) override; -}; - -} // namespace proof - -}/* CVC4 namespace */ - -#endif /* CVC4__BITVECTOR__PROOF_H */ diff --git a/src/proof/clausal_bitvector_proof.cpp b/src/proof/clausal_bitvector_proof.cpp deleted file mode 100644 index 21e8056ca..000000000 --- a/src/proof/clausal_bitvector_proof.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/********************* */ -/*! \file clausal_bitvector_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner, Andres Noetzli - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Bitvector proof using the DRAT proof format - ** - ** Contains DRAT-specific printing logic. - **/ - -#include "cvc4_private.h" - -#include <algorithm> -#include <iostream> -#include <iterator> -#include <map> - -#include "options/bv_options.h" -#include "proof/clausal_bitvector_proof.h" -#include "proof/dimacs.h" -#include "proof/drat/drat_proof.h" -#include "proof/er/er_proof.h" -#include "proof/lfsc_proof_printer.h" -#include "proof/lrat/lrat_proof.h" -#include "prop/sat_solver_types.h" -#include "smt/smt_statistics_registry.h" -#include "theory/bv/theory_bv.h" - -#if CVC4_USE_DRAT2ER -#include "drat2er_options.h" -#include "drat_trim_interface.h" -#endif - -namespace CVC4 { - -namespace proof { - -ClausalBitVectorProof::ClausalBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine) - : BitVectorProof(bv, proofEngine), - d_clauses(), - d_originalClauseIndices(), - d_binaryDratProof(), - d_coreClauseIndices(), - d_dratTranslationStatistics(), - d_dratOptimizationStatistics() -{ -} - -void ClausalBitVectorProof::attachToSatSolver(prop::SatSolver& sat_solver) -{ - sat_solver.setClausalProofLog(this); -} - -void ClausalBitVectorProof::initCnfProof(prop::CnfStream* cnfStream, - context::Context* cnf, - prop::SatVariable trueVar, - prop::SatVariable falseVar) -{ - Assert(d_cnfProof == nullptr); - d_cnfProof.reset(new LFSCCnfProof(cnfStream, cnf, "bb")); - - // Create a clause which forces the true variable to be true, and register it - int trueClauseId = ClauseId(ProofManager::currentPM()->nextId()); - // with the CNF proof - d_cnfProof->registerTrueUnitClause(trueClauseId); - // and with (this) bit-vector proof - prop::SatClause c{prop::SatLiteral(trueVar, false)}; - registerUsedClause(trueClauseId, c); - - // The same for false. - int falseClauseId = ClauseId(ProofManager::currentPM()->nextId()); - d_cnfProof->registerFalseUnitClause(falseClauseId); - c[0] = prop::SatLiteral(falseVar, true); - registerUsedClause(falseClauseId, c); -} - -void ClausalBitVectorProof::registerUsedClause(ClauseId id, - prop::SatClause& clause) -{ - prop::SatClause& emplaced_clause = - d_clauses.emplace(id, clause).first->second; - canonicalizeClause(emplaced_clause); - d_originalClauseIndices.push_back(id); -}; - -void ClausalBitVectorProof::calculateAtomsInBitblastingProof() -{ - optimizeDratProof(); - - // Debug dump of DRAT Proof - if (Debug.isOn("bv::clausal")) - { - std::string serializedDratProof = d_binaryDratProof.str(); - Debug("bv::clausal") << "option: " << options::bvOptimizeSatProof() - << std::endl; - Debug("bv::clausal") << "binary DRAT proof byte count: " - << serializedDratProof.size() << std::endl; - Debug("bv::clausal") << "clause count: " << d_coreClauseIndices.size() - << std::endl; - } - - // Empty any old record of which atoms were used - d_atomsInBitblastingProof.clear(); - Assert(d_atomsInBitblastingProof.size() == 0); - - // For each used clause, ask the CNF proof which atoms are used in it - for (const ClauseId usedIdx : d_coreClauseIndices) - { - d_cnfProof->collectAtoms(&d_clauses.at(usedIdx), d_atomsInBitblastingProof); - } -} - -struct SatClausePointerComparator -{ - inline bool operator()(const prop::SatClause* const& l, - const prop::SatClause* const& r) const - { - prop::SatClauseLessThan cmp; - return cmp(*l, *r); - } -}; - -void ClausalBitVectorProof::optimizeDratProof() -{ - TimerStat::CodeTimer optimizeDratProofTimer{ - d_dratOptimizationStatistics.d_totalTime}; - if (options::bvOptimizeSatProof() == options::BvOptimizeSatProof::PROOF - || options::bvOptimizeSatProof() == options::BvOptimizeSatProof::FORMULA) - { - Debug("bv::clausal") << "Optimizing DRAT" << std::endl; - std::string formulaFilename("cvc4-dimacs-XXXXXX"); - std::string dratFilename("cvc4-drat-XXXXXX"); - std::string optDratFilename("cvc4-optimized-drat-XXXXXX"); - std::string optFormulaFilename("cvc4-optimized-formula-XXXXXX"); - - { - std::unique_ptr<std::fstream> formStream = openTmpFile(&formulaFilename); - const int64_t startPos = static_cast<int64_t>(formStream->tellp()); - printDimacs(*formStream, d_clauses, d_originalClauseIndices); - d_dratOptimizationStatistics.d_initialFormulaSize.setData( - static_cast<int64_t>(formStream->tellp()) - startPos); - formStream->close(); - } - - { - std::unique_ptr<std::fstream> dratStream = openTmpFile(&dratFilename); - const int64_t startPos = static_cast<int64_t>(dratStream->tellp()); - (*dratStream) << d_binaryDratProof.str(); - d_dratOptimizationStatistics.d_initialDratSize.setData( - static_cast<int64_t>(dratStream->tellp()) - startPos); - dratStream->close(); - } - - std::unique_ptr<std::fstream> optDratStream = openTmpFile(&optDratFilename); - std::unique_ptr<std::fstream> optFormulaStream = - openTmpFile(&optFormulaFilename); - -#if CVC4_USE_DRAT2ER - { - TimerStat::CodeTimer runDratTimeOptimizationTimer{ - d_dratOptimizationStatistics.d_toolTime}; - int dratTrimExitCode = - drat2er::drat_trim::OptimizeWithDratTrim(formulaFilename, - dratFilename, - optFormulaFilename, - optDratFilename, - drat2er::options::QUIET); - AlwaysAssert(dratTrimExitCode == 0) - << "drat-trim exited with " << dratTrimExitCode; - } -#else - Unimplemented() - << "Proof production when using CryptoMiniSat requires drat2er.\n" - << "Run contrib/get-drat2er, reconfigure with --drat2er, and rebuild"; -#endif - - { - d_binaryDratProof.str(""); - Assert(d_binaryDratProof.str().size() == 0); - - const int64_t startPos = static_cast<int64_t>(d_binaryDratProof.tellp()); - std::ifstream lratStream(optDratFilename); - std::copy(std::istreambuf_iterator<char>(lratStream), - std::istreambuf_iterator<char>(), - std::ostreambuf_iterator<char>(d_binaryDratProof)); - d_dratOptimizationStatistics.d_optimizedDratSize.setData( - static_cast<int64_t>(d_binaryDratProof.tellp()) - startPos); - } - - if (options::bvOptimizeSatProof() == options::BvOptimizeSatProof::FORMULA) - { - std::ifstream optFormulaInStream{optFormulaFilename}; - const int64_t startPos = static_cast<int64_t>(optFormulaInStream.tellg()); - std::vector<prop::SatClause> core = parseDimacs(optFormulaInStream); - d_dratOptimizationStatistics.d_optimizedFormulaSize.setData( - static_cast<int64_t>(optFormulaInStream.tellg()) - startPos); - - CodeTimer clauseMatchingTimer{ - d_dratOptimizationStatistics.d_clauseMatchingTime}; - - // Now we need to compute the clause indices for the UNSAT core. This is a - // bit difficult because drat-trim may have reordered clauses, and/or - // removed duplicate literals. We use literal sets as the canonical clause - // form. - // - // TODO (aozdemir) It may be better to use a hash map instead of a tree - // map here. - std::map<const prop::SatClause*, ClauseId, SatClausePointerComparator> - cannonicalClausesToIndices; - for (const auto& kv : d_clauses) - { - cannonicalClausesToIndices[&kv.second] = kv.first; - } - - d_coreClauseIndices.clear(); - - for (prop::SatClause& coreClause : core) - { - canonicalizeClause(coreClause); - d_coreClauseIndices.push_back( - cannonicalClausesToIndices.at(&coreClause)); - } - Debug("bv::clausal") << "Optimizing the DRAT proof and the formula" - << std::endl; - } - else - { - Debug("bv::clausal") << "Optimizing the DRAT proof but not the formula" - << std::endl; - d_coreClauseIndices = d_originalClauseIndices; - } - - optFormulaStream->close(); - - Assert(d_coreClauseIndices.size() > 0); - remove(formulaFilename.c_str()); - remove(dratFilename.c_str()); - remove(optDratFilename.c_str()); - remove(optFormulaFilename.c_str()); - Debug("bv::clausal") << "Optimized DRAT" << std::endl; - } - else - { - Debug("bv::clausal") << "Not optimizing the formula or the DRAT proof" - << std::endl; - d_coreClauseIndices = d_originalClauseIndices; - } -} - -void ClausalBitVectorProof::canonicalizeClause(prop::SatClause& clause) -{ - std::sort(clause.begin(), clause.end()); - clause.erase(std::unique(clause.begin(), clause.end()), clause.end()); -} - -ClausalBitVectorProof::DratTranslationStatistics::DratTranslationStatistics() - : d_totalTime("proof::bv::dratTranslation::totalTime"), - d_toolTime("proof::bv::dratTranslation::toolTime") -{ - smtStatisticsRegistry()->registerStat(&d_totalTime); - smtStatisticsRegistry()->registerStat(&d_toolTime); -} - -ClausalBitVectorProof::DratTranslationStatistics::~DratTranslationStatistics() -{ - smtStatisticsRegistry()->unregisterStat(&d_totalTime); - smtStatisticsRegistry()->unregisterStat(&d_toolTime); -} - -ClausalBitVectorProof::DratOptimizationStatistics::DratOptimizationStatistics() - : d_totalTime("proof::bv::dratOptimization::totalTime"), - d_toolTime("proof::bv::dratOptimization::toolTime"), - d_clauseMatchingTime("proof::bv::dratOptimization::clauseMatchingTime"), - d_initialDratSize("proof::bv::dratOptimization::initialDratSize", 0), - d_optimizedDratSize("proof::bv::dratOptimization::optimizedDratSize", 0), - d_initialFormulaSize("proof::bv::dratOptimization::initialFormulaSize", - 0), - d_optimizedFormulaSize( - "proof::bv::dratOptimization::optimizedFormulaSize", 0) -{ - smtStatisticsRegistry()->registerStat(&d_totalTime); - smtStatisticsRegistry()->registerStat(&d_toolTime); - smtStatisticsRegistry()->registerStat(&d_clauseMatchingTime); - smtStatisticsRegistry()->registerStat(&d_initialDratSize); - smtStatisticsRegistry()->registerStat(&d_optimizedDratSize); - smtStatisticsRegistry()->registerStat(&d_initialFormulaSize); - smtStatisticsRegistry()->registerStat(&d_optimizedFormulaSize); -} - -ClausalBitVectorProof::DratOptimizationStatistics::~DratOptimizationStatistics() -{ - smtStatisticsRegistry()->unregisterStat(&d_totalTime); - smtStatisticsRegistry()->unregisterStat(&d_toolTime); - smtStatisticsRegistry()->unregisterStat(&d_clauseMatchingTime); - smtStatisticsRegistry()->unregisterStat(&d_initialDratSize); - smtStatisticsRegistry()->unregisterStat(&d_optimizedDratSize); - smtStatisticsRegistry()->unregisterStat(&d_initialFormulaSize); - smtStatisticsRegistry()->unregisterStat(&d_optimizedFormulaSize); -} - -void LfscClausalBitVectorProof::printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) -{ - Unreachable() << "Clausal bit-vector proofs should only be used in " - "combination with eager " - "bitblasting, which **does not use theory lemmas**"; -} - -void LfscClausalBitVectorProof::printBBDeclarationAndCnf(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) -{ - os << "\n;; Bitblasting mappings\n"; - printBitblasting(os, paren); - - os << "\n;; BB-CNF mappings\n"; - d_cnfProof->printAtomMapping(d_atomsInBitblastingProof, os, paren, letMap); - - os << "\n;; BB-CNF proofs\n"; - for (const ClauseId id : d_coreClauseIndices) - { - d_cnfProof->printCnfProofForClause(id, &d_clauses.at(id), os, paren); - } -} - -void LfscDratBitVectorProof::printEmptyClauseProof(std::ostream& os, - std::ostream& paren) -{ - Assert(options::bitblastMode() == options::BitblastMode::EAGER) - << "the BV theory should only be proving bottom directly in the eager " - "bitblasting mode"; - - os << "\n;; Proof of input to SAT solver\n"; - os << "(@ proofOfSatInput "; - paren << ")"; - - LFSCProofPrinter::printSatInputProof(d_coreClauseIndices, os, "bb"); - - os << "\n;; DRAT Proof Value\n"; - os << "(@ dratProof "; - paren << ")"; - d_dratTranslationStatistics.d_totalTime.start(); - drat::DratProof pf = drat::DratProof::fromBinary(d_binaryDratProof.str()); - d_dratTranslationStatistics.d_totalTime.stop(); - pf.outputAsLfsc(os, 2); - os << "\n"; - - os << "\n;; Verification of DRAT Proof\n"; - os << "(drat_proof_of_bottom _ proofOfSatInput dratProof " - << "\n)"; -} - -void LfscLratBitVectorProof::printEmptyClauseProof(std::ostream& os, - std::ostream& paren) -{ - Assert(options::bitblastMode() == options::BitblastMode::EAGER) - << "the BV theory should only be proving bottom directly in the eager " - "bitblasting mode"; - - os << "\n;; Proof of input to SAT solver\n"; - os << "(@ proofOfCMap "; - paren << ")"; - LFSCProofPrinter::printCMapProof(d_coreClauseIndices, os, "bb"); - - os << "\n;; DRAT Proof Value\n"; - os << "(@ lratProof "; - paren << ")"; - d_dratTranslationStatistics.d_totalTime.start(); - lrat::LratProof pf = - lrat::LratProof::fromDratProof(d_clauses, - d_coreClauseIndices, - d_binaryDratProof.str(), - d_dratTranslationStatistics.d_toolTime); - d_dratTranslationStatistics.d_totalTime.stop(); - pf.outputAsLfsc(os); - os << "\n"; - - os << "\n;; Verification of DRAT Proof\n"; - os << "(lrat_proof_of_bottom _ proofOfCMap lratProof " - << "\n)"; -} - -void LfscErBitVectorProof::printEmptyClauseProof(std::ostream& os, - std::ostream& paren) -{ - Assert(options::bitblastMode() == options::BitblastMode::EAGER) - << "the BV theory should only be proving bottom directly in the eager " - "bitblasting mode"; - - d_dratTranslationStatistics.d_totalTime.start(); - er::ErProof pf = - er::ErProof::fromBinaryDratProof(d_clauses, - d_coreClauseIndices, - d_binaryDratProof.str(), - d_dratTranslationStatistics.d_toolTime); - d_dratTranslationStatistics.d_totalTime.stop(); - - pf.outputAsLfsc(os); -} - -} // namespace proof - -}; // namespace CVC4 diff --git a/src/proof/clausal_bitvector_proof.h b/src/proof/clausal_bitvector_proof.h deleted file mode 100644 index 28a53c90c..000000000 --- a/src/proof/clausal_bitvector_proof.h +++ /dev/null @@ -1,189 +0,0 @@ -/********************* */ -/*! \file clausal_bitvector_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Bitvector proof for clausal (DRAT/LRAT) formats - ** - ** An internal string stream is hooked up to CryptoMiniSat, which spits out a - ** binary DRAT proof. Depending on which kind of proof we're going to turn - ** that into, we process it in different ways. - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__CLAUSAL_BITVECTOR_PROOF_H -#define CVC4__PROOF__CLAUSAL_BITVECTOR_PROOF_H - -#include <iostream> -#include <sstream> -#include <unordered_map> - -#include "expr/expr.h" -#include "proof/bitvector_proof.h" -#include "proof/drat/drat_proof.h" -#include "proof/lrat/lrat_proof.h" -#include "proof/theory_proof.h" -#include "prop/cnf_stream.h" -#include "prop/sat_solver_types.h" -#include "theory/bv/theory_bv.h" -#include "util/statistics_registry.h" - -namespace CVC4 { - -namespace proof { - -class ClausalBitVectorProof : public BitVectorProof -{ - public: - ClausalBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine); - - ~ClausalBitVectorProof() = default; - - void attachToSatSolver(prop::SatSolver& sat_solver) override; - - void initCnfProof(prop::CnfStream* cnfStream, - context::Context* cnf, - prop::SatVariable trueVar, - prop::SatVariable falseVar) override; - - std::ostream& getDratOstream() { return d_binaryDratProof; } - - void registerUsedClause(ClauseId id, prop::SatClause& clause); - - void calculateAtomsInBitblastingProof() override; - - protected: - // A list of all clauses and their ids which are passed into the SAT solver - std::unordered_map<ClauseId, prop::SatClause> d_clauses{}; - std::vector<ClauseId> d_originalClauseIndices{}; - // Stores the proof recieved from the SAT solver. - std::ostringstream d_binaryDratProof{}; - std::vector<ClauseId> d_coreClauseIndices{}; - - struct DratTranslationStatistics - { - DratTranslationStatistics(); - ~DratTranslationStatistics(); - - // Total time spent doing translation (optimized binary DRAT -> in memory - // target format including IO, postprocessing, etc.) - TimerStat d_totalTime; - // Time that the external tool actually spent - TimerStat d_toolTime; - }; - - DratTranslationStatistics d_dratTranslationStatistics; - - private: - // Optimizes the DRAT proof stored in `d_binaryDratProof` and returns a list - // of clause actually needed to check that proof (a smaller UNSAT core) - void optimizeDratProof(); - - // Given reference to a SAT clause encoded as a vector of literals, puts the - // literals into a canonical order - static void canonicalizeClause(prop::SatClause& clause); - - struct DratOptimizationStatistics - { - DratOptimizationStatistics(); - ~DratOptimizationStatistics(); - - // Total time spent using drat-trim to optimize the DRAT proof/formula - // (including IO, etc.) - TimerStat d_totalTime; - // Time that drat-trim actually spent optimizing the DRAT proof/formula - TimerStat d_toolTime; - // Time that was spent matching clauses in drat-trim's output to clauses in - // its input - TimerStat d_clauseMatchingTime; - // Bytes in binary DRAT proof before optimization - IntStat d_initialDratSize; - // Bytes in binary DRAT proof after optimization - IntStat d_optimizedDratSize; - // Bytes in textual DIMACS bitblasted formula before optimization - IntStat d_initialFormulaSize; - // Bytes in textual DIMACS bitblasted formula after optimization - IntStat d_optimizedFormulaSize; - }; - - DratOptimizationStatistics d_dratOptimizationStatistics; -}; - -/** - * A representation of a clausal proof of a bitvector problem's UNSAT nature - */ -class LfscClausalBitVectorProof : public ClausalBitVectorProof -{ - public: - LfscClausalBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine) - : ClausalBitVectorProof(bv, proofEngine) - { - } - - void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; - void printBBDeclarationAndCnf(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) override; -}; - -/** - * A DRAT proof for a bit-vector problem - */ -class LfscDratBitVectorProof : public LfscClausalBitVectorProof -{ - public: - LfscDratBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine) - : LfscClausalBitVectorProof(bv, proofEngine) - { - } - - void printEmptyClauseProof(std::ostream& os, std::ostream& paren) override; -}; - -/** - * An LRAT proof for a bit-vector problem - */ -class LfscLratBitVectorProof : public LfscClausalBitVectorProof -{ - public: - LfscLratBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine) - : LfscClausalBitVectorProof(bv, proofEngine) - { - } - - void printEmptyClauseProof(std::ostream& os, std::ostream& paren) override; -}; - -/** - * An Extended Resolution proof for a bit-vector problem - */ -class LfscErBitVectorProof : public LfscClausalBitVectorProof -{ - public: - LfscErBitVectorProof(theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) - : LfscClausalBitVectorProof(bv, proofEngine) - { - } - - void printEmptyClauseProof(std::ostream& os, std::ostream& paren) override; -}; - -} // namespace proof - -} // namespace CVC4 - -#endif /* CVC4__PROOF__CLAUSAL_BITVECTOR_PROOF_H */ diff --git a/src/proof/clause_id.h b/src/proof/clause_id.h index b2d36c9cb..5d7ec94f6 100644 --- a/src/proof/clause_id.h +++ b/src/proof/clause_id.h @@ -28,6 +28,13 @@ namespace CVC4 { */ typedef unsigned ClauseId; +/** Reserved clauseId values used in the resolution proof. The represent, + * respectively, the empty clause, that adding the clause to the SAT solver was + * a no-op, and that an error occurred when trying to add. */ +const ClauseId ClauseIdEmpty(-1); +const ClauseId ClauseIdUndef(-2); +const ClauseId ClauseIdError(-3); + }/* CVC4 namespace */ #endif /* CVC4__PROOF__CLAUSE_ID_H */ diff --git a/src/proof/cnf_proof.cpp b/src/proof/cnf_proof.cpp index 677bf2f8c..258e2fdb2 100644 --- a/src/proof/cnf_proof.cpp +++ b/src/proof/cnf_proof.cpp @@ -19,8 +19,6 @@ #include "proof/clause_id.h" #include "proof/proof_manager.h" -#include "proof/proof_utils.h" -#include "proof/theory_proof.h" #include "prop/cnf_stream.h" #include "prop/minisat/minisat.h" #include "prop/sat_solver_types.h" @@ -32,11 +30,7 @@ CnfProof::CnfProof(prop::CnfStream* stream, const std::string& name) : d_cnfStream(stream) , d_clauseToAssertion(ctx) - , d_assertionToProofRule(ctx) , d_currentAssertionStack() - , d_currentDefinitionStack() - , d_clauseToDefinition(ctx) - , d_definitions() , d_cnfDeps() , d_name(name) { @@ -46,86 +40,22 @@ CnfProof::CnfProof(prop::CnfStream* stream, CnfProof::~CnfProof() {} -bool CnfProof::isAssertion(Node node) { - return d_assertionToProofRule.find(node) != - d_assertionToProofRule.end(); -} - -bool CnfProof::isDefinition(Node node) { - return d_definitions.find(node) != - d_definitions.end(); -} - -ProofRule CnfProof::getProofRule(Node node) { - Assert(isAssertion(node)); - NodeToProofRule::iterator it = d_assertionToProofRule.find(node); - return (*it).second; -} - -ProofRule CnfProof::getProofRule(ClauseId clause) { - TNode assertion = getAssertionForClause(clause); - return getProofRule(assertion); -} - Node CnfProof::getAssertionForClause(ClauseId clause) { ClauseIdToNode::const_iterator it = d_clauseToAssertion.find(clause); Assert(it != d_clauseToAssertion.end() && !(*it).second.isNull()); return (*it).second; } -Node CnfProof::getDefinitionForClause(ClauseId clause) { - ClauseIdToNode::const_iterator it = d_clauseToDefinition.find(clause); - Assert(it != d_clauseToDefinition.end()); - return (*it).second; -} - -void CnfProof::registerConvertedClause(ClauseId clause, bool explanation) { +void CnfProof::registerConvertedClause(ClauseId clause) +{ Assert(clause != ClauseIdUndef && clause != ClauseIdError && clause != ClauseIdEmpty); - - // Explanations do not need a CNF conversion proof since they are in CNF - // (they will only need a theory proof as they are theory valid) - if (explanation) { - Debug("proof:cnf") << "CnfProof::registerConvertedClause " - << clause << " explanation? " << explanation << std::endl; - Assert(d_explanations.find(clause) == d_explanations.end()); - d_explanations.insert(clause); - return; - } - Node current_assertion = getCurrentAssertion(); - Node current_expr = getCurrentDefinition(); - Debug("proof:cnf") << "CnfProof::registerConvertedClause " - << clause << " assertion = " << current_assertion - << clause << " definition = " << current_expr << std::endl; + Debug("proof:cnf") << "CnfProof::registerConvertedClause " << clause + << " assertion = " << current_assertion << std::endl; setClauseAssertion(clause, current_assertion); - setClauseDefinition(clause, current_expr); -} - -void CnfProof::registerTrueUnitClause(ClauseId clauseId) -{ - Node trueNode = NodeManager::currentNM()->mkConst<bool>(true); - pushCurrentAssertion(trueNode); - pushCurrentDefinition(trueNode); - registerConvertedClause(clauseId); - popCurrentAssertion(); - popCurrentDefinition(); - d_cnfStream->ensureLiteral(trueNode); - d_trueUnitClause = clauseId; -} - -void CnfProof::registerFalseUnitClause(ClauseId clauseId) -{ - Node falseNode = NodeManager::currentNM()->mkConst<bool>(false).notNode(); - pushCurrentAssertion(falseNode); - pushCurrentDefinition(falseNode); - registerConvertedClause(clauseId); - popCurrentAssertion(); - popCurrentDefinition(); - d_cnfStream->ensureLiteral(falseNode); - d_falseUnitClause = clauseId; } void CnfProof::setClauseAssertion(ClauseId clause, Node expr) { @@ -145,56 +75,15 @@ void CnfProof::setClauseAssertion(ClauseId clause, Node expr) { return; } - d_clauseToAssertion.insert (clause, expr); + d_clauseToAssertion.insert(clause, expr); } -void CnfProof::setClauseDefinition(ClauseId clause, Node definition) { - Debug("proof:cnf") << "CnfProof::setClauseDefinition " - << clause << " definition " << definition << std::endl; - // We keep the first definition - if (d_clauseToDefinition.find(clause) != d_clauseToDefinition.end()) - return; - - d_clauseToDefinition.insert(clause, definition); - d_definitions.insert(definition); -} - -void CnfProof::registerAssertion(Node assertion, ProofRule reason) { - Debug("proof:cnf") << "CnfProof::registerAssertion " - << assertion << " reason " << reason << std::endl; - // We can obtain the assertion from different reasons (e.g. if the - // assertion is a lemma over shared terms both theories can generate - // the same lemma) We only need to prove the lemma in one way, so we - // keep the first reason. - if (isAssertion(assertion)) { - return; - } - d_assertionToProofRule.insert(assertion, reason); -} - -LemmaProofRecipe CnfProof::getProofRecipe(const std::set<Node> &lemma) { - Assert(d_lemmaToProofRecipe.find(lemma) != d_lemmaToProofRecipe.end()); - return d_lemmaToProofRecipe[lemma]; -} - -bool CnfProof::haveProofRecipe(const std::set<Node> &lemma) { - return d_lemmaToProofRecipe.find(lemma) != d_lemmaToProofRecipe.end(); -} - -void CnfProof::setCnfDependence(Node from, Node to) { - Debug("proof:cnf") << "CnfProof::setCnfDependence " - << "from " << from << std::endl - << " to " << to << std::endl; - - Assert(from != to); - d_cnfDeps.insert(std::make_pair(from, to)); -} - -void CnfProof::pushCurrentAssertion(Node assertion) { +void CnfProof::pushCurrentAssertion(Node assertion, bool isInput) +{ Debug("proof:cnf") << "CnfProof::pushCurrentAssertion " << assertion << std::endl; - d_currentAssertionStack.push_back(assertion); + d_currentAssertionStack.push_back(std::pair<Node, bool>(assertion, isInput)); Debug("proof:cnf") << "CnfProof::pushCurrentAssertion " << "new stack size = " << d_currentAssertionStack.size() @@ -205,7 +94,7 @@ void CnfProof::popCurrentAssertion() { Assert(d_currentAssertionStack.size()); Debug("proof:cnf") << "CnfProof::popCurrentAssertion " - << d_currentAssertionStack.back() << std::endl; + << d_currentAssertionStack.back().first << std::endl; d_currentAssertionStack.pop_back(); @@ -216,740 +105,12 @@ void CnfProof::popCurrentAssertion() { Node CnfProof::getCurrentAssertion() { Assert(d_currentAssertionStack.size()); - return d_currentAssertionStack.back(); + return d_currentAssertionStack.back().first; } -void CnfProof::setProofRecipe(LemmaProofRecipe* proofRecipe) { - Assert(proofRecipe); - Assert(proofRecipe->getNumSteps() > 0); - d_lemmaToProofRecipe[proofRecipe->getBaseAssertions()] = *proofRecipe; -} - -void CnfProof::pushCurrentDefinition(Node definition) { - Debug("proof:cnf") << "CnfProof::pushCurrentDefinition " - << definition << std::endl; - - d_currentDefinitionStack.push_back(definition); -} - -void CnfProof::popCurrentDefinition() { - Assert(d_currentDefinitionStack.size()); - - Debug("proof:cnf") << "CnfProof::popCurrentDefinition " - << d_currentDefinitionStack.back() << std::endl; - - d_currentDefinitionStack.pop_back(); -} - -Node CnfProof::getCurrentDefinition() { - Assert(d_currentDefinitionStack.size()); - return d_currentDefinitionStack.back(); -} - -Node CnfProof::getAtom(prop::SatVariable var) { - prop::SatLiteral lit (var); - Node node = d_cnfStream->getNode(lit); - return node; -} - -void CnfProof::collectAtoms(const prop::SatClause* clause, - std::set<Node>& atoms) { - for (unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = clause->operator[](i); - prop::SatVariable var = lit.getSatVariable(); - TNode atom = getAtom(var); - if (atoms.find(atom) == atoms.end()) { - atoms.insert(atom); - } - } -} - -prop::SatLiteral CnfProof::getLiteral(TNode atom) { - return d_cnfStream->getLiteral(atom); -} - -bool CnfProof::hasLiteral(TNode atom) { - return d_cnfStream->hasLiteral(atom); -} - -void CnfProof::ensureLiteral(TNode atom, bool noPreregistration) { - d_cnfStream->ensureLiteral(atom, noPreregistration); -} - -void CnfProof::collectAtomsForClauses(const IdToSatClause& clauses, - std::set<Node>& atoms) { - IdToSatClause::const_iterator it = clauses.begin(); - for (; it != clauses.end(); ++it) { - const prop::SatClause* clause = it->second; - collectAtoms(clause, atoms); - } -} - -void CnfProof::collectAtomsAndRewritesForLemmas(const IdToSatClause& lemmaClauses, - std::set<Node>& atoms, - NodePairSet& rewrites) { - IdToSatClause::const_iterator it = lemmaClauses.begin(); - for (; it != lemmaClauses.end(); ++it) { - const prop::SatClause* clause = it->second; - - // TODO: just calculate the map from ID to recipe once, - // instead of redoing this over and over again - std::vector<Expr> clause_expr; - std::set<Node> clause_expr_nodes; - for(unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = (*clause)[i]; - Node node = getAtom(lit.getSatVariable()); - Expr atom = node.toExpr(); - if (atom.isConst()) { - Assert(atom == utils::mkTrue()); - continue; - } - clause_expr_nodes.insert(lit.isNegated() ? node.notNode() : node); - } - - LemmaProofRecipe recipe = getProofRecipe(clause_expr_nodes); - - for (unsigned i = 0; i < recipe.getNumSteps(); ++i) { - const LemmaProofRecipe::ProofStep* proofStep = recipe.getStep(i); - Node atom = proofStep->getLiteral(); - - if (atom == Node()) { - // The last proof step always has the empty node as its target... - continue; - } - - if (atom.getKind() == kind::NOT) { - atom = atom[0]; - } - - atoms.insert(atom); - } - - LemmaProofRecipe::RewriteIterator rewriteIt; - for (rewriteIt = recipe.rewriteBegin(); rewriteIt != recipe.rewriteEnd(); ++rewriteIt) { - rewrites.insert(NodePair(rewriteIt->first, rewriteIt->second)); - - // The unrewritten terms also need to have literals, so insert them into atoms - Node rewritten = rewriteIt->first; - if (rewritten.getKind() == kind::NOT) { - rewritten = rewritten[0]; - } - atoms.insert(rewritten); - } - } -} - -void CnfProof::collectAssertionsForClauses(const IdToSatClause& clauses, - NodeSet& assertions) { - IdToSatClause::const_iterator it = clauses.begin(); - for (; it != clauses.end(); ++it) { - TNode used_assertion = getAssertionForClause(it->first); - assertions.insert(used_assertion); - // it can be the case that a definition for a clause is an assertion - // but it is not the assertion for the clause - // e.g. the assertions [(and a b), a] - TNode used_definition = getDefinitionForClause(it->first); - if (isAssertion(used_definition)) { - assertions.insert(used_definition); - } - } -} - -// Detects whether a clause has x v ~x for some x -// If so, returns the positive occurence's idx first, then the negative's -Maybe<std::pair<size_t, size_t>> CnfProof::detectTrivialTautology( - const prop::SatClause& clause) +bool CnfProof::getCurrentAssertionKind() { - // a map from a SatVariable to its previous occurence's polarity and location - std::map<prop::SatVariable, std::pair<bool, size_t>> varsToPolsAndIndices; - for (size_t i = 0; i < clause.size(); ++i) - { - prop::SatLiteral lit = clause[i]; - prop::SatVariable var = lit.getSatVariable(); - bool polarity = !lit.isNegated(); - - // Check if this var has already occured w/ opposite polarity - auto iter = varsToPolsAndIndices.find(var); - if (iter != varsToPolsAndIndices.end() && iter->second.first != polarity) - { - if (iter->second.first) - { - return Maybe<std::pair<size_t, size_t>>{ - std::make_pair(iter->second.second, i)}; - } - else - { - return Maybe<std::pair<size_t, size_t>>{ - std::make_pair(i, iter->second.second)}; - } - } - varsToPolsAndIndices[var] = std::make_pair(polarity, i); - } - return Maybe<std::pair<size_t, size_t>>{}; -} - -void LFSCCnfProof::printAtomMapping(const std::set<Node>& atoms, - std::ostream& os, - std::ostream& paren) { - std::set<Node>::const_iterator it = atoms.begin(); - std::set<Node>::const_iterator end = atoms.end(); - - for (;it != end; ++it) { - os << "(decl_atom "; - Node atom = *it; - prop::SatVariable var = getLiteral(atom).getSatVariable(); - //FIXME hideous - LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); - pe->printLetTerm(atom.toExpr(), os); - - os << " (\\ " << ProofManager::getVarName(var, d_name); - os << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; - paren << ")))"; - } -} - -void LFSCCnfProof::printAtomMapping(const std::set<Node>& atoms, - std::ostream& os, - std::ostream& paren, - ProofLetMap &letMap) { - std::set<Node>::const_iterator it = atoms.begin(); - std::set<Node>::const_iterator end = atoms.end(); - - for (;it != end; ++it) { - os << "(decl_atom "; - Node atom = *it; - prop::SatVariable var = getLiteral(atom).getSatVariable(); - //FIXME hideous - LFSCTheoryProofEngine* pe = (LFSCTheoryProofEngine*)ProofManager::currentPM()->getTheoryProofEngine(); - if (pe->printsAsBool(atom.toExpr())) os << "(p_app "; - pe->printBoundTerm(atom.toExpr(), os, letMap); - if (pe->printsAsBool(atom.toExpr())) os << ")"; - - os << " (\\ " << ProofManager::getVarName(var, d_name); - os << " (\\ " << ProofManager::getAtomName(var, d_name) << "\n"; - paren << ")))"; - } -} - -// maps each expr to the position it had in the clause and the polarity it had -Node LFSCCnfProof::clauseToNode(const prop::SatClause& clause, - std::map<Node, unsigned>& childIndex, - std::map<Node, bool>& childPol ) { - std::vector< Node > children; - for (unsigned i = 0; i < clause.size(); ++i) { - prop::SatLiteral lit = clause[i]; - prop::SatVariable var = lit.getSatVariable(); - Node atom = getAtom(var); - children.push_back( lit.isNegated() ? atom.negate() : atom ); - childIndex[atom] = i; - childPol[atom] = !lit.isNegated(); - } - return children.size()==1 ? children[0] : - NodeManager::currentNM()->mkNode(kind::OR, children ); -} - -void LFSCCnfProof::printCnfProofForClause(ClauseId id, - const prop::SatClause* clause, - std::ostream& os, - std::ostream& paren) { - Debug("cnf-pf") << std::endl << std::endl << "LFSCCnfProof::printCnfProofForClause( " << id << " ) starting " - << std::endl; - - os << "(satlem _ _ "; - std::ostringstream clause_paren; - printClause(*clause, os, clause_paren); - os << "(clausify_false "; - - // FIXMEEEEEEEEEEEE - // os <<"trust)"; - // os << clause_paren.str() - // << " (\\ " << ProofManager::getInputClauseName(id, d_name) << "\n"; - // paren << "))"; - - // return; - - Assert(clause->size() > 0); - - // If the clause contains x v ~x, it's easy! - // - // It's important to check for this case, because our other logic for - // recording the location of variables in the clause assumes the clause is - // not tautological - Maybe<std::pair<size_t, size_t>> isTrivialTaut = - detectTrivialTautology(*clause); - if (isTrivialTaut.just()) - { - size_t posIndexInClause = isTrivialTaut.value().first; - size_t negIndexInClause = isTrivialTaut.value().second; - Trace("cnf-pf") << "; Indices " << posIndexInClause << " (+) and " - << negIndexInClause << " (-) make this clause a tautology" - << std::endl; - - std::string proofOfPos = - ProofManager::getLitName((*clause)[negIndexInClause], d_name); - std::string proofOfNeg = - ProofManager::getLitName((*clause)[posIndexInClause], d_name); - os << "(contra _ " << proofOfPos << " " << proofOfNeg << ")"; - } - else - { - Node base_assertion = getDefinitionForClause(id); - - // get the assertion for the clause id - std::map<Node, unsigned> childIndex; - std::map<Node, bool> childPol; - Node assertion = clauseToNode(*clause, childIndex, childPol); - // if there is no reason, construct assertion directly. This can happen - // for unit clauses. - if (base_assertion.isNull()) - { - base_assertion = assertion; - } - // os_base is proof of base_assertion - std::stringstream os_base; - - // checks if tautological definitional clause or top-level clause - // and prints the proof of the top-level formula - bool is_input = printProofTopLevel(base_assertion, os_base); - - if (is_input) - { - Debug("cnf-pf") << std::endl - << "; base assertion is input. proof: " << os_base.str() - << std::endl; - } - - // get base assertion with polarity - bool base_pol = base_assertion.getKind() != kind::NOT; - base_assertion = base_assertion.getKind() == kind::NOT ? base_assertion[0] - : base_assertion; - - std::map<Node, unsigned>::iterator itci = childIndex.find(base_assertion); - bool is_in_clause = itci != childIndex.end(); - unsigned base_index = is_in_clause ? itci->second : 0; - Trace("cnf-pf") << std::endl; - Trace("cnf-pf") << "; input = " << is_input - << ", is_in_clause = " << is_in_clause << ", id = " << id - << ", assertion = " << assertion - << ", base assertion = " << base_assertion << std::endl; - if (!is_input) - { - Assert(is_in_clause); - prop::SatLiteral blit = (*clause)[base_index]; - os_base << ProofManager::getLitName(blit, d_name); - base_pol = !childPol[base_assertion]; // WHY? if the case is => - } - Trace("cnf-pf") << "; polarity of base assertion = " << base_pol - << std::endl; - Trace("cnf-pf") << "; proof of base : " << os_base.str() << std::endl; - - bool success = false; - if (is_input && is_in_clause && childPol[base_assertion] == base_pol) - { - // if both in input and in clause, the proof is trivial. this is the case - // for unit clauses. - Trace("cnf-pf") << "; trivial" << std::endl; - os << "(contra _ "; - success = true; - prop::SatLiteral lit = (*clause)[itci->second]; - if (base_pol) - { - os << os_base.str() << " " << ProofManager::getLitName(lit, d_name); - } - else - { - os << ProofManager::getLitName(lit, d_name) << " " << os_base.str(); - } - os << ")"; - } else if ((base_assertion.getKind()==kind::AND && !base_pol) || - ((base_assertion.getKind()==kind::OR || - base_assertion.getKind()==kind::IMPLIES) && base_pol)) { - Trace("cnf-pf") << "; and/or case 1" << std::endl; - success = true; - std::stringstream os_main; - std::stringstream os_paren; - //eliminate each one - for (int j = base_assertion.getNumChildren()-2; j >= 0; j--) { - Trace("cnf-pf-debug") << "; base_assertion[" << j << "] is: " << base_assertion[j] - << ", and its kind is: " << base_assertion[j].getKind() << std::endl ; - - Node child_base = base_assertion[j].getKind()==kind::NOT ? - base_assertion[j][0] : base_assertion[j]; - bool child_pol = base_assertion[j].getKind()!=kind::NOT; - - Trace("cnf-pf-debug") << "; child " << j << " " - << ", child base: " << child_base - << ", child pol: " << child_pol - << ", childPol[child_base] " - << childPol[child_base] << ", base pol: " << base_pol << std::endl; - - std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); - - if( itcic!=childIndex.end() ){ - //Assert( child_pol==childPol[child_base] ); - os_main << "(or_elim_1 _ _ "; - prop::SatLiteral lit = (*clause)[itcic->second]; - // Should be if in the original formula it was negated - // if( childPol[child_base] && base_pol ){ - - // Adding the below to catch a specific case where the first child of an IMPLIES is negative, - // in which case we need not_not introduction. - if (base_assertion.getKind() == kind::IMPLIES && !child_pol && base_pol) { - os_main << "(not_not_intro _ " << ProofManager::getLitName(lit, d_name) << ") "; - } else if (childPol[child_base] && base_pol) { - os_main << ProofManager::getLitName(lit, d_name) << " "; - }else{ - os_main << "(not_not_intro _ " << ProofManager::getLitName(lit, d_name) << ") "; - } - if( base_assertion.getKind()==kind::AND ){ - os_main << "(not_and_elim _ _ "; - os_paren << ")"; - } - os_paren << ")"; - }else{ - success = false; - } - } - - if( success ){ - if( base_assertion.getKind()==kind::IMPLIES ){ - os_main << "(impl_elim _ _ "; - } - os_main << os_base.str(); - if( base_assertion.getKind()==kind::IMPLIES ){ - os_main << ")"; - } - os_main << os_paren.str(); - int last_index = base_assertion.getNumChildren()-1; - Node child_base = base_assertion[last_index].getKind()==kind::NOT ? base_assertion[last_index][0] : base_assertion[last_index]; - //bool child_pol = base_assertion[last_index].getKind()!=kind::NOT; - std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); - if( itcic!=childIndex.end() ){ - os << "(contra _ "; - prop::SatLiteral lit = (*clause)[itcic->second]; - if( childPol[child_base] && base_pol){ - os << os_main.str() << " " << ProofManager::getLitName(lit, d_name); - }else{ - os << ProofManager::getLitName(lit, d_name) << " " << os_main.str(); - } - os << ")"; - }else{ - success = false; - } - } - }else if ((base_assertion.getKind()==kind::AND && base_pol) || - ((base_assertion.getKind()==kind::OR || - base_assertion.getKind()==kind::IMPLIES) && !base_pol)) { - - std::stringstream os_main; - - Node iatom; - if (is_in_clause) { - Assert(assertion.getNumChildren() == 2); - iatom = assertion[ base_index==0 ? 1 : 0]; - } else { - Assert(assertion.getNumChildren() == 1); - iatom = assertion[0]; - } - - Trace("cnf-pf") << "; and/or case 2, iatom = " << iatom << std::endl; - Node e_base = iatom.getKind()==kind::NOT ? iatom[0] : iatom; - bool e_pol = iatom.getKind()!=kind::NOT; - std::map< Node, unsigned >::iterator itcic = childIndex.find( e_base ); - if( itcic!=childIndex.end() ){ - prop::SatLiteral lit = (*clause)[itcic->second]; - //eliminate until we find iatom - for( unsigned j=0; j<base_assertion.getNumChildren(); j++ ){ - Node child_base = base_assertion[j].getKind()==kind::NOT ? base_assertion[j][0] : base_assertion[j]; - bool child_pol = base_assertion[j].getKind()!=kind::NOT; - if( j==0 && base_assertion.getKind()==kind::IMPLIES ){ - child_pol = !child_pol; - } - if( e_base==child_base && (e_pol==child_pol)==(base_assertion.getKind()==kind::AND) ){ - success = true; - bool elimNn =( ( base_assertion.getKind()==kind::OR || ( base_assertion.getKind()==kind::IMPLIES && j==1 ) ) && e_pol ); - if( elimNn ){ - os_main << "(not_not_elim _ "; - } - std::stringstream os_paren; - if( j+1<base_assertion.getNumChildren() ){ - os_main << "(and_elim_1 _ _ "; - if( base_assertion.getKind()==kind::OR || base_assertion.getKind()==kind::IMPLIES ){ - os_main << "(not_" << ( base_assertion.getKind()==kind::OR ? "or" : "impl" ) << "_elim _ _ "; - os_paren << ")"; - } - os_paren << ")"; - } - for( unsigned k=0; k<j; k++ ){ - os_main << "(and_elim_2 _ _ "; - if( base_assertion.getKind()==kind::OR || base_assertion.getKind()==kind::IMPLIES ){ - os_main << "(not_" << ( base_assertion.getKind()==kind::OR ? "or" : "impl" ) << "_elim _ _ "; - os_paren << ")"; - } - os_paren << ")"; - } - os_main << os_base.str() << os_paren.str(); - if( elimNn ){ - os_main << ")"; - } - break; - } - } - if( success ){ - os << "(contra _ "; - if( !e_pol ){ - os << ProofManager::getLitName(lit, d_name) << " " << os_main.str(); - }else{ - os << os_main.str() << " " << ProofManager::getLitName(lit, d_name); - } - os << ")"; - } - } - }else if( base_assertion.getKind()==kind::XOR || ( base_assertion.getKind()==kind::EQUAL && base_assertion[0].getType().isBoolean() ) ){ - //eliminate negation - int num_nots_2 = 0; - int num_nots_1 = 0; - Kind k; - if( !base_pol ){ - if( base_assertion.getKind()==kind::EQUAL ){ - num_nots_2 = 1; - } - k = kind::EQUAL; - }else{ - k = base_assertion.getKind(); - } - std::vector< unsigned > indices; - std::vector< bool > pols; - success = true; - int elimNum = 0; - for( unsigned i=0; i<2; i++ ){ - Node child_base = base_assertion[i].getKind()==kind::NOT ? base_assertion[i][0] : base_assertion[i]; - bool child_pol = base_assertion[i].getKind()!=kind::NOT; - std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); - if( itcic!=childIndex.end() ){ - indices.push_back( itcic->second ); - pols.push_back( childPol[child_base] ); - if( i==0 ){ - //figure out which way to elim - elimNum = child_pol==childPol[child_base] ? 2 : 1; - if( (elimNum==2)==(k==kind::EQUAL) ){ - num_nots_2++; - } - if( elimNum==1 ){ - num_nots_1++; - } - } - }else{ - success = false; - break; - } - } - Trace("cnf-pf") << std::endl << "; num nots = " << num_nots_2 << std::endl; - if( success ){ - os << "(contra _ "; - std::stringstream os_base_n; - if( num_nots_2==2 ){ - os_base_n << "(not_not_elim _ "; - } - os_base_n << "(or_elim_1 _ _ "; - prop::SatLiteral lit1 = (*clause)[indices[0]]; - if( !pols[0] || num_nots_1==1 ){ - os_base_n << "(not_not_intro _ " << ProofManager::getLitName(lit1, d_name) << ") "; - }else{ - Trace("cnf-pf-debug") << "CALLING getlitname" << std::endl; - os_base_n << ProofManager::getLitName(lit1, d_name) << " "; - } - Assert(elimNum != 0); - os_base_n << "(" << ( k==kind::EQUAL ? "iff" : "xor" ) << "_elim_" << elimNum << " _ _ "; - if( !base_pol ){ - os_base_n << "(not_" << ( base_assertion.getKind()==kind::EQUAL ? "iff" : "xor" ) << "_elim _ _ " << os_base.str() << ")"; - }else{ - os_base_n << os_base.str(); - } - os_base_n << "))"; - if( num_nots_2==2 ){ - os_base_n << ")"; - num_nots_2 = 0; - } - prop::SatLiteral lit2 = (*clause)[indices[1]]; - if( pols[1]==(num_nots_2==0) ){ - os << os_base_n.str() << " "; - if( num_nots_2==1 ){ - os << "(not_not_intro _ " << ProofManager::getLitName(lit2, d_name) << ")"; - }else{ - os << ProofManager::getLitName(lit2, d_name); - } - }else{ - os << ProofManager::getLitName(lit2, d_name) << " " << os_base_n.str(); - } - os << ")"; - } - }else if( base_assertion.getKind()==kind::ITE ){ - std::map< unsigned, unsigned > appears; - std::map< unsigned, Node > appears_expr; - unsigned appears_count = 0; - for( unsigned r=0; r<3; r++ ){ - Node child_base = base_assertion[r].getKind()==kind::NOT ? base_assertion[r][0] : base_assertion[r]; - std::map< Node, unsigned >::iterator itcic = childIndex.find( child_base ); - if( itcic!=childIndex.end() ){ - appears[r] = itcic->second; - appears_expr[r] = child_base; - appears_count++; - } - } - if( appears_count==2 ){ - success = true; - int elimNum = 1; - unsigned index1 = 0; - unsigned index2 = 1; - if( appears.find( 0 )==appears.end() ){ - elimNum = 3; - index1 = 1; - index2 = 2; - }else if( appears.find( 1 )==appears.end() ){ - elimNum = 2; - index1 = 0; - index2 = 2; - } - std::stringstream os_main; - os_main << "(or_elim_1 _ _ "; - prop::SatLiteral lit1 = (*clause)[appears[index1]]; - if( !childPol[appears_expr[index1]] || elimNum==1 || ( elimNum==3 && !base_pol ) ){ - os_main << "(not_not_intro _ " << ProofManager::getLitName(lit1, d_name) << ") "; - }else{ - os_main << ProofManager::getLitName(lit1, d_name) << " "; - } - os_main << "(" << ( base_pol ? "" : "not_" ) << "ite_elim_" << elimNum << " _ _ _ "; - os_main << os_base.str() << "))"; - os << "(contra _ "; - prop::SatLiteral lit2 = (*clause)[appears[index2]]; - if( !childPol[appears_expr[index2]] || !base_pol ){ - os << ProofManager::getLitName(lit2, d_name) << " " << os_main.str(); - }else{ - os << os_main.str() << " " << ProofManager::getLitName(lit2, d_name); - } - os << ")"; - } - }else if( base_assertion.isConst() ){ - bool pol = base_assertion==NodeManager::currentNM()->mkConst( true ); - if( pol!=base_pol ){ - success = true; - if( pol ){ - os << "(contra _ truth " << os_base.str() << ")"; - }else{ - os << os_base.str(); - } - } - } - - if( !success ){ - Trace("cnf-pf") << std::endl; - Trace("cnf-pf") << ";!!!!!!!!! CnfProof : Can't process " << assertion << ", base = " << base_assertion << ", id = " << id << std::endl; - Trace("cnf-pf") << ";!!!!!!!!! Clause is : "; - for (unsigned i = 0; i < clause->size(); ++i) { - Trace("cnf-pf") << (*clause)[i] << " "; - } - Trace("cnf-pf") << std::endl; - os << "trust-bad"; - } - } - - os << ")" << clause_paren.str() - << " (\\ " << ProofManager::getInputClauseName(id, d_name) << "\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(); - if (lit.isNegated()) { - os << "(ast _ _ _ " << ProofManager::getAtomName(var, d_name) <<" (\\ " << ProofManager::getLitName(lit, d_name) << " "; - paren << "))"; - } else { - os << "(asf _ _ _ " << ProofManager::getAtomName(var, d_name) <<" (\\ " << ProofManager::getLitName(lit, d_name) << " "; - paren << "))"; - } - } -} - -// print a proof of the top-level formula e, based on the input assertions -bool LFSCCnfProof::printProofTopLevel(Node e, std::ostream& out) { - if (!isAssertion(e)) { - // check if deduced by CNF - // dependence on top level fact i.e. a depends on (a and b) - NodeToNode::const_iterator itd = d_cnfDeps.find(e); - if (itd != d_cnfDeps.end()) { - TNode parent = itd->second; - //check if parent is an input assertion - std::stringstream out_parent; - if (printProofTopLevel(parent, out_parent)) { - if(parent.getKind()==kind::AND || - (parent.getKind()==kind::NOT && (parent[0].getKind()==kind::IMPLIES || - parent[0].getKind()==kind::OR))) { - Node parent_base = parent.getKind()==kind::NOT ? parent[0] : parent; - Node e_base = e.getKind()==kind::NOT ? e[0] : e; - bool e_pol = e.getKind()!=kind::NOT; - for( unsigned i=0; i<parent_base.getNumChildren(); i++ ){ - Node child_base = parent_base[i].getKind()==kind::NOT ? parent_base[i][0] : parent_base[i]; - bool child_pol = parent_base[i].getKind()!=kind::NOT; - if( parent_base.getKind()==kind::IMPLIES && i==0 ){ - child_pol = !child_pol; - } - if (e_base==child_base && - (e_pol==child_pol)==(parent_base.getKind()==kind::AND)) { - bool elimNn = ((parent_base.getKind()==kind::OR || - (parent_base.getKind()==kind::IMPLIES && i==1)) && e_pol); - if (elimNn) { - out << "(not_not_elim _ "; - } - std::stringstream out_paren; - if (i+1 < parent_base.getNumChildren()) { - out << "(and_elim_1 _ _ "; - if( parent_base.getKind()==kind::OR || - parent_base.getKind()==kind::IMPLIES ){ - out << "(not_" << ( parent_base.getKind()==kind::OR ? "or" : "impl" ) - << "_elim _ _ "; - out_paren << ")"; - } - out_paren << ")"; - } - for( unsigned j=0; j<i; j++ ){ - out << "(and_elim_2 _ _ "; - if( parent_base.getKind()==kind::OR || parent_base.getKind()==kind::IMPLIES ){ - out << "(not_" << ( parent_base.getKind()==kind::OR ? "or" : "impl" ) << "_elim _ _ "; - out_paren << ")"; - } - out_paren << ")"; - } - out << out_parent.str(); - out << out_paren.str(); - if( elimNn ){ - out << ")"; - } - return true; - } - } - } else { - Trace("cnf-pf-debug") << "; isInputAssertion : parent of " << e - << " is not correct type (" << parent << ")" << std::endl; - } - } else { - Trace("cnf-pf-debug") << "; isInputAssertion : parent of " << e - << " is not input" << std::endl; - } - } else { - Trace("cnf-pf-debug") << "; isInputAssertion : " << e - << " has no parent" << std::endl; - } - return false; - } else { - out << ProofManager::getPreprocessedAssertionName(e); - return true; - } + return d_currentAssertionStack.back().second; } } /* CVC4 namespace */ diff --git a/src/proof/cnf_proof.h b/src/proof/cnf_proof.h index 56993583e..e437ef722 100644 --- a/src/proof/cnf_proof.h +++ b/src/proof/cnf_proof.h @@ -27,10 +27,8 @@ #include "context/cdhashmap.h" #include "proof/clause_id.h" -#include "proof/lemma_proof.h" #include "proof/sat_proof.h" #include "util/maybe.h" -#include "util/proof.h" namespace CVC4 { namespace prop { @@ -44,11 +42,11 @@ typedef std::unordered_map<Node, Node, NodeHashFunction> NodeToNode; typedef std::unordered_set<ClauseId> ClauseIdSet; typedef context::CDHashMap<ClauseId, Node> ClauseIdToNode; -typedef context::CDHashMap<Node, ProofRule, NodeHashFunction> NodeToProofRule; -typedef std::map<std::set<Node>, LemmaProofRecipe> LemmaToRecipe; typedef std::pair<Node, Node> NodePair; typedef std::set<NodePair> NodePairSet; +typedef std::unordered_set<Node, NodeHashFunction> NodeSet; + class CnfProof { protected: CVC4::prop::CnfStream* d_cnfStream; @@ -56,23 +54,9 @@ protected: /** Map from ClauseId to the assertion that lead to adding this clause **/ ClauseIdToNode d_clauseToAssertion; - /** Map from assertion to reason for adding assertion **/ - NodeToProofRule d_assertionToProofRule; - - /** Map from lemma to the recipe for proving it **/ - LemmaToRecipe d_lemmaToProofRecipe; - - /** Top of stack is assertion currently being converted to CNF **/ - std::vector<Node> d_currentAssertionStack; - - /** Top of stack is top-level fact currently being converted to CNF **/ - std::vector<Node> d_currentDefinitionStack; - - /** Map from ClauseId to the top-level fact that lead to adding this clause **/ - ClauseIdToNode d_clauseToDefinition; - - /** Top-level facts that follow from assertions during convertAndAssert **/ - NodeSet d_definitions; + /** Top of stack is assertion currently being converted to CNF. Also saves + * whether it is input (rather than a lemma). **/ + std::vector<std::pair<Node, bool>> d_currentAssertionStack; /** Map from top-level fact to facts/assertion that it follows from **/ NodeToNode d_cnfDeps; @@ -84,132 +68,36 @@ protected: // The clause ID of the unit clause defining the false SAT literal. ClauseId d_falseUnitClause; - bool isDefinition(Node node); - - Node getDefinitionForClause(ClauseId clause); - std::string d_name; public: CnfProof(CVC4::prop::CnfStream* cnfStream, context::Context* ctx, const std::string& name); - - - Node getAtom(prop::SatVariable var); - prop::SatLiteral getLiteral(TNode node); - bool hasLiteral(TNode node); - void ensureLiteral(TNode node, bool noPreregistration = false); - - void collectAtoms(const prop::SatClause* clause, - std::set<Node>& atoms); - void collectAtomsForClauses(const IdToSatClause& clauses, - std::set<Node>& atoms); - void collectAtomsAndRewritesForLemmas(const IdToSatClause& lemmaClauses, - std::set<Node>& atoms, - NodePairSet& rewrites); - void collectAssertionsForClauses(const IdToSatClause& clauses, - NodeSet& assertions); + ~CnfProof(); /** Methods for logging what the CnfStream does **/ // map the clause back to the current assertion where it came from - // if it is an explanation, it does not have a CNF proof since it is - // already in CNF - void registerConvertedClause(ClauseId clause, bool explanation=false); - - // The CNF proof has a special relationship to true and false. - // In particular, it need to know the identity of clauses defining - // canonical true and false literals in the underlying SAT solver. - void registerTrueUnitClause(ClauseId clauseId); - void registerFalseUnitClause(ClauseId clauseId); - inline ClauseId getTrueUnitClause() { return d_trueUnitClause; }; - inline ClauseId getFalseUnitClause() { return d_falseUnitClause; }; - - /** Clause is one of the clauses defining the node expression*/ - void setClauseDefinition(ClauseId clause, Node node); + void registerConvertedClause(ClauseId clause); /** Clause is one of the clauses defining top-level assertion node*/ void setClauseAssertion(ClauseId clause, Node node); - void registerAssertion(Node assertion, ProofRule reason); - void setCnfDependence(Node from, Node to); - - void pushCurrentAssertion(Node assertion); // the current assertion being converted + /** Current assertion being converted and whether it is an input (rather than + * a lemma) */ + void pushCurrentAssertion(Node assertion, bool isInput = false); void popCurrentAssertion(); Node getCurrentAssertion(); - - void pushCurrentDefinition(Node assertion); // the current Tseitin definition being converted - void popCurrentDefinition(); - Node getCurrentDefinition(); + bool getCurrentAssertionKind(); /** * Checks whether the assertion stack is empty. */ bool isAssertionStackEmpty() const { return d_currentAssertionStack.empty(); } - void setProofRecipe(LemmaProofRecipe* proofRecipe); - LemmaProofRecipe getProofRecipe(const std::set<Node> &lemma); - bool haveProofRecipe(const std::set<Node> &lemma); - // accessors for the leaf assertions that are being converted to CNF - bool isAssertion(Node node); - ProofRule getProofRule(Node assertion); - ProofRule getProofRule(ClauseId clause); Node getAssertionForClause(ClauseId clause); - - /** Virtual methods for printing things **/ - virtual void printAtomMapping(const std::set<Node>& atoms, - std::ostream& os, - std::ostream& paren) = 0; - virtual void printAtomMapping(const std::set<Node>& atoms, - std::ostream& os, - std::ostream& paren, - ProofLetMap &letMap) = 0; - - // Detects whether a clause has x v ~x for some x - // If so, returns the positive occurence's idx first, then the negative's - static Maybe<std::pair<size_t, size_t>> detectTrivialTautology( - const prop::SatClause& clause); - virtual void printClause(const prop::SatClause& clause, - std::ostream& os, - std::ostream& paren) = 0; - virtual void printCnfProofForClause(ClauseId id, - const prop::SatClause* clause, - std::ostream& os, - std::ostream& paren) = 0; - virtual ~CnfProof(); };/* class CnfProof */ -class LFSCCnfProof : public CnfProof { - Node clauseToNode( const prop::SatClause& clause, - std::map<Node, unsigned>& childIndex, - std::map<Node, bool>& childPol ); - bool printProofTopLevel(Node e, std::ostream& out); -public: - LFSCCnfProof(CVC4::prop::CnfStream* cnfStream, - context::Context* ctx, - const std::string& name) - : CnfProof(cnfStream, ctx, name) - {} - ~LFSCCnfProof() {} - - void printAtomMapping(const std::set<Node>& atoms, - std::ostream& os, - std::ostream& paren) override; - - void printAtomMapping(const std::set<Node>& atoms, - std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) override; - - void printClause(const prop::SatClause& clause, - std::ostream& os, - std::ostream& paren) override; - void printCnfProofForClause(ClauseId id, - const prop::SatClause* clause, - std::ostream& os, - std::ostream& paren) override; -};/* class LFSCCnfProof */ - } /* CVC4 namespace */ #endif /* CVC4__CNF_PROOF_H */ diff --git a/src/proof/dimacs.cpp b/src/proof/dimacs.cpp deleted file mode 100644 index d9d9f8c1c..000000000 --- a/src/proof/dimacs.cpp +++ /dev/null @@ -1,120 +0,0 @@ -/********************* */ -/*! \file dimacs.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief DIMACS SAT Problem Format - ** - ** Defines serialization for SAT problems as DIMACS - **/ - -#include "proof/dimacs.h" - -#include "base/check.h" - -#include <iostream> - -namespace CVC4 { -namespace proof { - -// Prints the literal as a (+) or (-) int -// Not operator<< b/c that represents negation as ~ -std::ostream& textOut(std::ostream& o, const prop::SatLiteral& l) -{ - if (l.isNegated()) - { - o << "-"; - } - return o << l.getSatVariable() + 1; -} - -// Prints the clause as a space-separated list of ints -// Not operator<< b/c that represents negation as ~ -std::ostream& textOut(std::ostream& o, const prop::SatClause& c) -{ - for (const auto& l : c) - { - textOut(o, l) << " "; - } - return o << "0"; -} - -void printDimacs(std::ostream& o, - const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId>& usedIndices) -{ - size_t maxVar = 0; - for (const ClauseId i : usedIndices) - { - const prop::SatClause& c = clauses.at(i); - for (const auto& l : c) - { - if (l.getSatVariable() + 1 > maxVar) - { - maxVar = l.getSatVariable() + 1; - } - } - } - o << "p cnf " << maxVar << " " << usedIndices.size() << '\n'; - for (const ClauseId i : usedIndices) - { - const prop::SatClause& c = clauses.at(i); - for (const auto& l : c) - { - if (l.isNegated()) - { - o << '-'; - } - o << l.getSatVariable() + 1 << " "; - } - o << "0\n"; - } -} - -std::vector<prop::SatClause> parseDimacs(std::istream& in) -{ - std::string tag; - uint64_t nVars; - uint64_t nClauses; - - in >> tag; - Assert(in.good()); - Assert(tag == "p"); - - in >> tag; - Assert(in.good()); - Assert(tag == "cnf"); - - in >> nVars; - Assert(nVars >= 0); - - in >> nClauses; - Assert(nClauses >= 0); - - std::vector<prop::SatClause> cnf; - for (uint64_t i = 0; i < nClauses; ++i) - { - cnf.emplace_back(); - int64_t lit; - in >> lit; - Assert(in.good()); - while (lit != 0) - { - cnf.back().emplace_back(std::abs(lit) - 1, lit < 0); - in >> lit; - Assert(static_cast<uint64_t>(std::abs(lit)) <= nVars); - Assert(in.good()); - } - } - - return cnf; -} - -} // namespace proof -} // namespace CVC4 diff --git a/src/proof/dimacs.h b/src/proof/dimacs.h deleted file mode 100644 index 405b33208..000000000 --- a/src/proof/dimacs.h +++ /dev/null @@ -1,69 +0,0 @@ -/********************* */ -/*! \file dimacs.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief DIMACS SAT Problem Format - ** - ** Defines serialization/deserialization for SAT problems as DIMACS - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__DIMACS_H -#define CVC4__PROOF__DIMACS_H - -#include <iosfwd> -#include <memory> -#include <unordered_map> - -#include "proof/clause_id.h" -#include "prop/sat_solver_types.h" - -namespace CVC4 { -namespace proof { - -/** - * Prints the literal as a (+) or (-) int - * Not operator<< b/c that represents negation as ~ - * - * @param o where to print - * @param l the literal to print - * - * @return the original stream - */ -std::ostream& textOut(std::ostream& o, const prop::SatLiteral& l); - -/** - * Prints the clause as a space-separated list of ints - * Not operator<< b/c that represents literal negation as ~ - * - * @param o where to print - * @param c the clause to print - * - * @return the original stream - */ -std::ostream& textOut(std::ostream& o, const prop::SatClause& c); - -/** - * Prints a CNF formula in DIMACS format - * - * @param o where to print to - * @param usedClauses the CNF formula - */ -void printDimacs(std::ostream& o, - const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId>& usedIndices); - -std::vector<prop::SatClause> parseDimacs(std::istream& i); - -} // namespace proof -} // namespace CVC4 - -#endif // CVC4__PROOF__DIMACS_H diff --git a/src/proof/drat/drat_proof.cpp b/src/proof/drat/drat_proof.cpp deleted file mode 100644 index ee9c42d77..000000000 --- a/src/proof/drat/drat_proof.cpp +++ /dev/null @@ -1,291 +0,0 @@ -/********************* */ -/*! \file drat_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief DRAT Proof Format - ** - ** Defines deserialization for DRAT proofs. - **/ - -#include "proof/drat/drat_proof.h" - -#include <algorithm> -#include <bitset> -#include <iostream> - -#include "proof/proof_manager.h" - -namespace CVC4 { -namespace proof { -namespace drat { - -// helper functions for parsing the binary DRAT format. - -/** - * Parses a binary literal which starts at `start` and must not go beyond `end` - * - * Leaves the iterator one past the last byte that is a part of the clause. - * - * If the literal overruns `end`, then raises a `InvalidDratProofException`. - */ -SatLiteral parse_binary_literal(std::string::const_iterator& start, - const std::string::const_iterator& proof_end) -{ - // lit is encoded as uint represented by a variable-length byte sequence - uint64_t literal_represented_as_uint = 0; - for (uint8_t shift = 0; start != proof_end; ++start, shift += 7) - { - // Check whether shift is so large that we're going to lose some - // information - if (shift + 7 >= 64) - { - throw InvalidDratProofException( - "While parsing a DRAT proof, encountered a literal that was too " - "long"); - } - unsigned char byte = *start; - // The MSB of the byte is an indicator of whether the sequence continues - bool continued = (byte >> 7) & 1; - uint64_t numeric_part = byte & 0x7f; - literal_represented_as_uint |= numeric_part << shift; - if (!continued) - { - // LSB of `literal_represented_as_uint` indicates negation. - bool negated = literal_represented_as_uint & 1; - // Rest is the literal number - SatVariable var_number = literal_represented_as_uint >> 1; - ++start; - // Internal clauses start at 0, external ones start at 1. - return SatLiteral(var_number - 1, negated); - } - } - throw InvalidDratProofException( - "Literal in DRAT proof was not done when " - "EOF was encountered"); -} - -/** - * Parses a binary clause which starts at `start` and must not go beyond `end` - * - * Leaves the iterator one past the last byte that is a part of the clause. - * That is, one past the null byte. - * - * If the clause overruns `end`, then raises a `InvalidDratProofException`. - */ -SatClause parse_binary_clause(std::string::const_iterator& start, - const std::string::const_iterator& proof_end) -{ - SatClause clause; - // A clause is a 0-terminated sequence of literals - while (start != proof_end) - { - // Is the clause done? - if (*start == 0) - { - ++start; - return clause; - } - else - { - // If not, parse another literal - clause.emplace_back(parse_binary_literal(start, proof_end)); - } - } - // We've overrun the end of the byte stream. - throw InvalidDratProofException( - "Clause in DRAT proof was not done when " - "EOF was encountered"); -} - -/** - * Writes this SAT literal in the textual DIMACS format. i.e. as a non-zero - * integer. - * - * Since internally +0 and -0 are valid literals, we add one to each - * literal's number (SAT variable) when outputtting it. - * - * @param os the stream to write to - * @param l the literal to write - */ -void outputLiteralAsDimacs(std::ostream& os, SatLiteral l) -{ - if (l.isNegated()) - { - os << '-'; - } - // add 1 to convert between internal literals and their DIMACS - // representaations. - os << l.getSatVariable() + 1; -} - -// DratInstruction implementation - -DratInstruction::DratInstruction(DratInstructionKind kind, SatClause clause) - : d_kind(kind), d_clause(clause) -{ - // All intialized -} - -void DratInstruction::outputAsText(std::ostream& os) const -{ - switch (d_kind) - { - case DratInstructionKind::ADDITION: - { - for (const SatLiteral& l : d_clause) - { - outputLiteralAsDimacs(os, l); - os << ' '; - } - os << '0' << std::endl; - break; - } - case DratInstructionKind::DELETION: - { - os << "d "; - for (const SatLiteral& l : d_clause) - { - outputLiteralAsDimacs(os, l); - os << ' '; - } - os << '0' << std::endl; - break; - } - default: - { - Unreachable() << "Unknown DRAT instruction kind"; - } - } -} - -// DratProof implementation - -DratProof::DratProof() : d_instructions() {} - -// See the "binary format" section of -// https://www.cs.utexas.edu/~marijn/drat-trim/ -DratProof DratProof::fromBinary(const std::string& s) -{ - DratProof proof; - if (Debug.isOn("pf::drat")) - { - Debug("pf::drat") << "Parsing binary DRAT proof" << std::endl; - Debug("pf::drat") << "proof length: " << s.length() << " bytes" - << std::endl; - Debug("pf::drat") << "proof as bytes: "; - for (char i : s) - { - if (i == 'a' || i == 'd') - { - Debug("pf::drat") << std::endl << " " << std::bitset<8>(i); - } - else - { - Debug("pf::drat") << " " << std::bitset<8>(i); - } - } - Debug("pf::drat") << std::endl << "parsing proof..." << std::endl; - } - - // For each instruction - for (auto i = s.cbegin(), end = s.cend(); i != end;) - { - switch (*i) - { - case 'a': - { - ++i; - proof.d_instructions.emplace_back(ADDITION, - parse_binary_clause(i, end)); - break; - } - case 'd': - { - ++i; - proof.d_instructions.emplace_back(DELETION, - parse_binary_clause(i, end)); - break; - } - default: - { - std::ostringstream errmsg; - errmsg << "Invalid instruction in Drat proof. Instruction bits: " - << std::bitset<8>(*i) - << ". Expected 'a' (01100001) or 'd' " - "(01100100)."; - throw InvalidDratProofException(errmsg.str()); - } - } - } - - if (Debug.isOn("pf::drat")) - { - Debug("pf::drat") << "Printing out DRAT in textual format:" << std::endl; - proof.outputAsText(Debug("pf::drat")); - } - - return proof; -}; - -const std::vector<DratInstruction>& DratProof::getInstructions() const -{ - return d_instructions; -}; - -void DratProof::outputAsText(std::ostream& os) const -{ - for (const DratInstruction& instruction : d_instructions) - { - instruction.outputAsText(os); - os << "\n"; - } -}; - -void DratProof::outputAsLfsc(std::ostream& os, uint8_t indentation) const -{ - for (const DratInstruction& i : d_instructions) - { - if (indentation > 0) - { - std::fill_n(std::ostream_iterator<char>(os), indentation, ' '); - } - os << "("; - switch (i.d_kind) - { - case ADDITION: - { - os << "DRATProofa "; - break; - } - case DELETION: - { - os << "DRATProofd "; - break; - } - default: - { - Unreachable() << "Unrecognized DRAT instruction kind"; - } - } - for (const SatLiteral& l : i.d_clause) - { - os << "(clc (" << (l.isNegated() ? "neg " : "pos ") - << ProofManager::getVarName(l.getSatVariable(), "bb") << ") "; - } - os << "cln"; - std::fill_n(std::ostream_iterator<char>(os), i.d_clause.size(), ')'); - os << "\n"; - } - os << "DRATProofn"; - std::fill_n(std::ostream_iterator<char>(os), d_instructions.size(), ')'); -} -} // namespace drat -} // namespace proof -} // namespace CVC4 diff --git a/src/proof/drat/drat_proof.h b/src/proof/drat/drat_proof.h deleted file mode 100644 index 1213c80c7..000000000 --- a/src/proof/drat/drat_proof.h +++ /dev/null @@ -1,140 +0,0 @@ -/********************* */ -/*! \file drat_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief DRAT Proof Format - ** - ** Declares C++ types that represent a DRAT proof. - ** Defines serialization for these types. - ** - ** You can find an introduction to DRAT in the drat-trim paper: - ** http://www.cs.utexas.edu/~marijn/publications/drat-trim.pdf - ** - **/ - -#ifndef CVC4__PROOF__DRAT__DRAT_PROOF_H -#define CVC4__PROOF__DRAT__DRAT_PROOF_H - -#include "cvc4_private.h" -#include "prop/sat_solver.h" -#include "prop/sat_solver_types.h" - -namespace CVC4 { -namespace proof { -namespace drat { - -using CVC4::prop::SatClause; -using CVC4::prop::SatLiteral; -using CVC4::prop::SatVariable; - -class CVC4_PUBLIC InvalidDratProofException : public CVC4::Exception -{ - public: - InvalidDratProofException() : Exception("Invalid DRAT Proof") {} - - InvalidDratProofException(const std::string& msg) : Exception(msg) {} - - InvalidDratProofException(const char* msg) : Exception(msg) {} -}; /* class InvalidDratProofException */ - -enum DratInstructionKind -{ - ADDITION, - DELETION -}; - -struct DratInstruction -{ - DratInstruction(DratInstructionKind kind, SatClause clause); - - /** - * Write the DRAT instruction in textual format. - * The format is described in: - * http://www.cs.utexas.edu/~marijn/publications/drat-trim.pdf - * - * @param os the stream to write to - */ - void outputAsText(std::ostream& os) const; - - DratInstructionKind d_kind; - SatClause d_clause; -}; - -class DratProof -{ - public: - DratProof(const DratProof&) = default; - - DratProof(DratProof&&) = default; - - ~DratProof() = default; - - /** - * Parses a DRAT proof from the **binary format**. - * The format is described at: - * https://www.cs.utexas.edu/~marijn/drat-trim/#contact - * - * What do the standard authors mean by the format being "binary"? - * They just mean that proofs in this format should be understood as - * sequences of bytes, not sequences of ASCII/Unicode/your favorite character - * set characters. - * - * @param binaryProof a string containing the bytes of the binary proof. - * Even though the proof isn't text, it's safe to store it in a string - * because C++ strings don't make any gaurantees about the encoding of - * their contents. This makes them (effectively) just byte sequences. - * - * @return the parsed proof - */ - static DratProof fromBinary(const std::string& binaryProof); - - /** - * @return The instructions in this proof - */ - const std::vector<DratInstruction>& getInstructions() const; - - /** - * Write the DRAT proof in textual format. - * The format is described in: - * http://www.cs.utexas.edu/~marijn/publications/drat-trim.pdf - * - * @param os the stream to write to - */ - void outputAsText(std::ostream& os) const; - - /** - * Write the DRAT proof as an LFSC value - * The format is from the LFSC signature drat.plf - * - * Reads the current `ProofManager` to determine what the variables should be - * named. - * - * @param os the stream to write to - * @param indentation the number of spaces to indent each proof instruction - */ - void outputAsLfsc(std::ostream& os, uint8_t indentation) const; - - private: - /** - * Create an DRAT proof with no instructions. - */ - DratProof(); - - /** - * The instructions of the DRAT proof. - */ - std::vector<DratInstruction> d_instructions; -}; - -} // namespace drat -} // namespace proof -} // namespace CVC4 - -#endif // CVC4__PROOF__DRAT__DRAT_PROOF_H diff --git a/src/proof/er/er_proof.cpp b/src/proof/er/er_proof.cpp deleted file mode 100644 index 54b0fd879..000000000 --- a/src/proof/er/er_proof.cpp +++ /dev/null @@ -1,399 +0,0 @@ -/********************* */ -/*! \file er_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Andres Noetzli, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief ER Proof Format - ** - ** Declares C++ types that represent an ER/TRACECHECK proof. - ** Defines serialization for these types. - ** - ** You can find details about the way ER is encoded in the TRACECHECK - ** format at these locations: - ** https://github.com/benjaminkiesl/drat2er - ** http://www.cs.utexas.edu/users/marijn/publications/ijcar18.pdf - **/ - -#include "proof/er/er_proof.h" - -#include <unistd.h> -#include <algorithm> -#include <fstream> -#include <iostream> -#include <iterator> -#include <sstream> -#include <unordered_set> - -#include "base/check.h" -#include "base/map_util.h" -#include "proof/dimacs.h" -#include "proof/lfsc_proof_printer.h" -#include "proof/proof_manager.h" - -#if CVC4_USE_DRAT2ER -#include "drat2er.h" -#include "drat2er_options.h" -#endif - -namespace CVC4 { -namespace proof { -namespace er { - -TraceCheckProof TraceCheckProof::fromText(std::istream& in) -{ - TraceCheckProof pf; - TraceCheckIdx idx = 0; - int64_t token = 0; - - // For each line of the proof, start with the idx - // If there is no idx, then you're done! - in >> idx; - for (; !in.eof(); in >> idx) - { - Assert(in.good()); - - // Then parse the clause (it's 0-terminated) - std::vector<prop::SatLiteral> clause; - in >> token; - for (; token != 0; in >> token) - { - clause.emplace_back(std::abs(token) - 1, token < 0); - } - - // Then parse the chain of literals (it's also 0-terminated) - std::vector<TraceCheckIdx> chain; - in >> token; - for (; token != 0; in >> token) - { - Assert(token > 0); - chain.push_back(token); - } - - // Add the line to the proof - pf.d_lines.emplace_back(idx, std::move(clause), std::move(chain)); - } - return pf; -} - -ErProof ErProof::fromBinaryDratProof( - const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId>& usedIds, - const std::string& dratBinary, - TimerStat& toolTimer) -{ - std::string formulaFilename("cvc4-dimacs-XXXXXX"); - std::string dratFilename("cvc4-drat-XXXXXX"); - std::string tracecheckFilename("cvc4-tracecheck-er-XXXXXX"); - - // Write the formula - std::unique_ptr<std::fstream> formStream = openTmpFile(&formulaFilename); - printDimacs(*formStream, clauses, usedIds); - formStream->close(); - - // Write the (binary) DRAT proof - std::unique_ptr<std::fstream> dratStream = openTmpFile(&dratFilename); - (*dratStream) << dratBinary; - dratStream->close(); - - std::unique_ptr<std::fstream> tracecheckStream = - openTmpFile(&tracecheckFilename); - - // Invoke drat2er - { - CodeTimer blockTimer{toolTimer}; -#if CVC4_USE_DRAT2ER - drat2er::TransformDRATToExtendedResolution(formulaFilename, - dratFilename, - tracecheckFilename, - false, - drat2er::options::QUIET, - false); -#else - Unimplemented() - << "ER proof production requires drat2er.\n" - << "Run contrib/get-drat2er, reconfigure with --drat2er, and rebuild"; -#endif - } - - // Parse the resulting TRACECHECK proof into an ER proof. - TraceCheckProof pf = TraceCheckProof::fromText(*tracecheckStream); - ErProof proof(clauses, usedIds, std::move(pf)); - tracecheckStream->close(); - - remove(formulaFilename.c_str()); - remove(dratFilename.c_str()); - remove(tracecheckFilename.c_str()); - - return proof; -} - -ErProof::ErProof(const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId>& usedIds, - TraceCheckProof&& tracecheck) - : d_inputClauseIds(), d_definitions(), d_tracecheck(tracecheck) -{ - // Step zero, save input clause Ids for future printing - d_inputClauseIds = usedIds; - - // Make a list of (idx, clause pairs), the used ones. - std::vector<std::pair<ClauseId, prop::SatClause>> usedClauses; - std::transform( - usedIds.begin(), - usedIds.end(), - std::back_inserter(usedClauses), - [&](const ClauseId& i) { return make_pair(i, clauses.at(i)); }); - - // Step one, verify the formula starts the proof - if (Configuration::isAssertionBuild()) - { - for (size_t i = 0, n = usedClauses.size(); i < n; ++i) - { - Assert(d_tracecheck.d_lines[i].d_idx = i + 1); - Assert(d_tracecheck.d_lines[i].d_chain.size() == 0); - std::unordered_set<prop::SatLiteral, prop::SatLiteralHashFunction> - traceCheckClause{d_tracecheck.d_lines[i].d_clause.begin(), - d_tracecheck.d_lines[i].d_clause.end()}; - std::unordered_set<prop::SatLiteral, prop::SatLiteralHashFunction> - originalClause{usedClauses[i].second.begin(), - usedClauses[i].second.end()}; - Assert(traceCheckClause == originalClause); - } - } - - // Step two, identify definitions. They correspond to lines that follow the - // input lines, are in bounds, and have no justifying chain. - for (size_t i = usedClauses.size(), n = d_tracecheck.d_lines.size(); - i < n && d_tracecheck.d_lines[i].d_chain.size() == 0;) - { - prop::SatClause c = d_tracecheck.d_lines[i].d_clause; - Assert(c.size() > 0); - Assert(!c[0].isNegated()); - - // Get the new variable of the definition -- the first variable of the - // first clause - prop::SatVariable newVar = c[0].getSatVariable(); - - // The rest of the literals in the clause of the 'other literals' of the def - std::vector<prop::SatLiteral> otherLiterals{++c.begin(), c.end()}; - - size_t nLinesForThisDef = 2 + otherLiterals.size(); - // Look at the negation of the second literal in the second clause to get - // the old literal - AlwaysAssert(d_tracecheck.d_lines.size() > i + 1) - << "Malformed definition in TRACECHECK proof from drat2er"; - d_definitions.emplace_back(newVar, - ~d_tracecheck.d_lines[i + 1].d_clause[1], - std::move(otherLiterals)); - - // Advance over the lines for this definition - i += nLinesForThisDef; - } -} - -void ErProof::outputAsLfsc(std::ostream& os) const -{ - // How many parens to close? - size_t parenCount = 0; - std::unordered_set<prop::SatVariable> newVariables; - - // Print Definitions - for (const ErDefinition& def : d_definitions) - { - os << "\n (decl_definition (" - << (def.d_oldLiteral.isNegated() ? "neg " : "pos ") - << ProofManager::getVarName(def.d_oldLiteral.getSatVariable(), "bb") - << ") "; - LFSCProofPrinter::printSatClause(def.d_otherLiterals, os, "bb"); - os << " (\\ er.v" << def.d_newVariable << " (\\ er.def" - << def.d_newVariable; - newVariables.insert(def.d_newVariable); - } - parenCount += 3 * d_definitions.size(); - - // Clausify Definitions - TraceCheckIdx firstDefClause = d_inputClauseIds.size() + 1; - for (const ErDefinition& def : d_definitions) - { - os << "\n (clausify_definition _ _ _ " - << "er.def" << def.d_newVariable << " _ (\\ er.c" << firstDefClause - << " (\\ er.c" << (firstDefClause + 1) << " (\\ er.cnf" - << def.d_newVariable; - - firstDefClause += 2 + def.d_otherLiterals.size(); - } - parenCount += 4 * d_definitions.size(); - - // Unroll proofs of CNF to proofs of clauses - firstDefClause = d_inputClauseIds.size() + 1; - for (const ErDefinition& def : d_definitions) - { - for (size_t i = 0, n = def.d_otherLiterals.size(); i < n; ++i) - { - // Compute the name of the CNF proof we're unrolling in this step - std::ostringstream previousCnfProof; - previousCnfProof << "er.cnf" << def.d_newVariable; - if (i != 0) - { - // For all but the first unrolling, the previous CNF has an unrolling - // number attached - previousCnfProof << ".u" << i; - } - - // Prove the first clause in the CNF - os << "\n (@ "; - os << "er.c" << (firstDefClause + 2 + i); - os << " (common_tail_cnf_prove_head _ _ _ " << previousCnfProof.str() - << ")"; - - // Prove the rest of the CNF - os << "\n (@ "; - os << "er.cnf" << def.d_newVariable << ".u" << (i + 1); - os << " (common_tail_cnf_prove_tail _ _ _ " << previousCnfProof.str() - << ")"; - } - parenCount += 2 * def.d_otherLiterals.size(); - - firstDefClause += 2 + def.d_otherLiterals.size(); - } - - // NB: At this point `firstDefClause` points to the first clause resulting - // from a resolution chain - - // Now, elaborate each resolution chain - for (size_t cId = firstDefClause, nLines = d_tracecheck.d_lines.size(); - cId <= nLines; - ++cId) - { - const std::vector<TraceCheckIdx>& chain = - d_tracecheck.d_lines[cId - 1].d_chain; - const std::vector<prop::SatLiteral> pivots = computePivotsForChain(chain); - Assert(chain.size() > 0); - Assert(chain.size() == pivots.size() + 1); - - os << "\n (satlem_simplify _ _ _ "; - parenCount += 1; - - // Print resolution openings (reverse order) - for (int64_t i = pivots.size() - 1; i >= 0; --i) - { - prop::SatLiteral pivot = pivots[i]; - os << "(" << (pivot.isNegated() ? 'Q' : 'R') << " _ _ "; - } - - // Print resolution start - writeIdForClauseProof(os, chain[0]); - os << " "; - - // Print resolution closings (forward order) - for (size_t i = 0, n = pivots.size(); i < n; ++i) - { - prop::SatVariable pivotVar = pivots[i].getSatVariable(); - TraceCheckIdx clauseId = chain[i + 1]; - writeIdForClauseProof(os, clauseId); - os << " "; - if (ContainsKey(newVariables, pivotVar)) - { - // This is a defined variable - os << "er.v" << pivotVar; - } - else - { - os << ProofManager::getVarName(pivotVar, "bb"); - } - os << ") "; - } - os << "(\\ er.c" << cId; - parenCount += 1; - } - - // Write proof of bottom - Assert(d_tracecheck.d_lines.back().d_clause.size() == 0) - << "The TRACECHECK proof from drat2er did not prove bottom."; - os << "\n er.c" << d_tracecheck.d_lines.back().d_idx - << " ; (holds cln)\n"; - - // Finally, close the parentheses! - std::fill_n(std::ostream_iterator<char>(os), parenCount, ')'); -} - -namespace { -/** - * Resolves two clauses - * - * @param dest one of the inputs, and the output too. **This is an input and - * output** - * @param src the other input - * - * @return the unique literal that was resolved on, with the polarization that - * it originally had in `dest`. - * - * For example, if dest = (1 3 -4 5) and src = (1 -3 5), then 3 is returned and - * after the call dest = (1 -4 5). - */ -prop::SatLiteral resolveModify( - std::unordered_set<prop::SatLiteral, prop::SatLiteralHashFunction>& dest, - const prop::SatClause& src) -{ - CVC4_UNUSED bool foundPivot = false; - prop::SatLiteral pivot(0, false); - - for (prop::SatLiteral lit : src) - { - auto negationLocation = dest.find(~lit); - if (negationLocation != dest.end()) - { -#ifdef CVC4_ASSERTIONS - Assert(!foundPivot); - foundPivot = true; -#endif - dest.erase(negationLocation); - pivot = ~lit; - } - dest.insert(lit); - } - - Assert(foundPivot); - return pivot; -} -} // namespace - -std::vector<prop::SatLiteral> ErProof::computePivotsForChain( - const std::vector<TraceCheckIdx>& chain) const -{ - std::vector<prop::SatLiteral> pivots; - - const prop::SatClause& first = d_tracecheck.d_lines[chain[0] - 1].d_clause; - std::unordered_set<prop::SatLiteral, prop::SatLiteralHashFunction> - runningClause{first.begin(), first.end()}; - - for (auto idx = ++chain.cbegin(), end = chain.cend(); idx != end; ++idx) - { - pivots.push_back( - resolveModify(runningClause, d_tracecheck.d_lines[*idx - 1].d_clause)); - } - return pivots; -} - -void ErProof::writeIdForClauseProof(std::ostream& o, TraceCheckIdx i) const -{ - if (i <= d_inputClauseIds.size()) - { - // This clause is an input clause! Ask the ProofManager for its name - o << ProofManager::getInputClauseName(d_inputClauseIds[i - 1], "bb"); - } - else - { - // This clause was introduced by a definition or resolution chain - o << "er.c" << i; - } -} - -} // namespace er -} // namespace proof -} // namespace CVC4 diff --git a/src/proof/er/er_proof.h b/src/proof/er/er_proof.h deleted file mode 100644 index 6f7239ef2..000000000 --- a/src/proof/er/er_proof.h +++ /dev/null @@ -1,218 +0,0 @@ -/********************* */ -/*! \file er_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief ER Proof Format - ** - ** Declares C++ types that represent an ER/TRACECHECK proof. - ** Defines serialization for these types. - ** - ** You can find details about the way ER is encoded in the TRACECHECK - ** format at these locations: - ** https://github.com/benjaminkiesl/drat2er - ** http://www.cs.utexas.edu/users/marijn/publications/ijcar18.pdf - ** - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__ER__ER_PROOF_H -#define CVC4__PROOF__ER__ER_PROOF_H - -#include <memory> -#include <unordered_map> -#include <vector> - -#include "proof/clause_id.h" -#include "prop/sat_solver_types.h" -#include "util/statistics_registry.h" - -namespace CVC4 { -namespace proof { -namespace er { - -/** - * A definition of the form: - * newVar <-> p v (~x_1 ^ ~x_2 ^ ... ^ ~x_n) - */ -struct ErDefinition -{ - ErDefinition(prop::SatVariable newVariable, - prop::SatLiteral oldLiteral, - std::vector<prop::SatLiteral>&& otherLiterals) - : d_newVariable(newVariable), - d_oldLiteral(oldLiteral), - d_otherLiterals(otherLiterals) - { - } - - // newVar - prop::SatVariable d_newVariable; - // p - prop::SatLiteral d_oldLiteral; - // A list of the x_i's - std::vector<prop::SatLiteral> d_otherLiterals; -}; - -// For representing a clause's index within a TRACECHECK proof. -using TraceCheckIdx = size_t; - -/** - * A single line in a TRACECHECK proof. - * - * Consists of the index of a new clause, the literals of that clause, and the - * indices for preceding clauses which can be combined in a resolution chain to - * produce this new clause. - */ -struct TraceCheckLine -{ - TraceCheckLine(TraceCheckIdx idx, - std::vector<prop::SatLiteral>&& clause, - std::vector<TraceCheckIdx>&& chain) - : d_idx(idx), d_clause(clause), d_chain(chain) - { - } - - // The index of the new clause - TraceCheckIdx d_idx; - // The new clause - std::vector<prop::SatLiteral> d_clause; - /** - * Indices of clauses which must be resolved to produce this new clause. - * While the TRACECHECK format does not specify the order, we require them to - * be in resolution-order. - */ - std::vector<TraceCheckIdx> d_chain; -}; - -/** - * A TRACECHECK proof -- just a list of lines - */ -struct TraceCheckProof -{ - static TraceCheckProof fromText(std::istream& in); - TraceCheckProof() : d_lines() {} - - // The lines of this proof. - std::vector<TraceCheckLine> d_lines; -}; - -/** - * An extended resolution proof. - * It supports resolution, along with extensions of the form - * - * newVar <-> p v (~x_1 ^ ~x_2 ^ ... ^ ~x_n) - */ -class ErProof -{ - public: - /** - * Construct an ER proof from a DRAT proof, using drat2er - * - * @param clauses A store of clauses that might be in our formula - * @param usedIds the ids of clauses that are actually in our formula - * @param dratBinary The DRAT proof from the SAT solver, as a binary stream - * - * @return the Er proof and a timer of the execution of drat2er - */ - static ErProof fromBinaryDratProof( - const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId>& usedIds, - const std::string& dratBinary, - TimerStat& toolTimer - ); - - /** - * Construct an ER proof from a TRACECHECK ER proof - * - * This basically just identifies groups of lines which correspond to - * definitions, and extracts them. - * - * @param clauses A store of clauses that might be in our formula - * @param usedIds the ids of clauses that are actually in our formula - * @param tracecheck The TRACECHECK proof, as a stream. - */ - ErProof(const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId>& usedIds, - TraceCheckProof&& tracecheck); - - /** - * Write the ER proof as an LFSC value of type (holds cln). - * The format is from the LFSC signature er.plf - * - * Reads the current `ProofManager` to determine what the variables should be - * named. - * - * @param os the stream to write to - */ - void outputAsLfsc(std::ostream& os) const; - - const std::vector<ClauseId>& getInputClauseIds() const - { - return d_inputClauseIds; - } - - const std::vector<ErDefinition>& getDefinitions() const - { - return d_definitions; - } - - const TraceCheckProof& getTraceCheckProof() const { return d_tracecheck; } - - private: - /** - * Creates an empty ErProof. - */ - ErProof() : d_inputClauseIds(), d_definitions(), d_tracecheck() {} - - /** - * Computes the pivots on the basis of which an in-order resolution chain is - * resolved. - * - * c0 c1 - * \ / Clauses c_i being resolved in a chain around - * v1 c2 pivots v_i. - * \ / - * v2 c3 - * \ / - * v3 c4 - * \ / - * v4 - * - * - * @param chain the chain, of N clause indices - * - * @return a list of N - 1 variables, the list ( v_i ) from i = 1 to N - 1 - */ - std::vector<prop::SatLiteral> computePivotsForChain( - const std::vector<TraceCheckIdx>& chain) const; - - /** - * Write the LFSC identifier for the proof of a clause - * - * @param o where to write to - * @param i the TRACECHECK index for the clause whose proof identifier to - * print - */ - void writeIdForClauseProof(std::ostream& o, TraceCheckIdx i) const; - - // A list of the Ids for the input clauses, in order. - std::vector<ClauseId> d_inputClauseIds; - // A list of new variable definitions, in order. - std::vector<ErDefinition> d_definitions; - // The underlying TRACECHECK proof. - TraceCheckProof d_tracecheck; -}; - -} // namespace er -} // namespace proof -} // namespace CVC4 - -#endif // CVC4__PROOF__ER__ER_PROOF_H diff --git a/src/proof/lemma_proof.cpp b/src/proof/lemma_proof.cpp deleted file mode 100644 index bdebb6cfc..000000000 --- a/src/proof/lemma_proof.cpp +++ /dev/null @@ -1,254 +0,0 @@ -/********************* */ -/*! \file lemma_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** A class for recoding the steps required in order to prove a theory lemma. - -**/ - -#include "proof/lemma_proof.h" -#include "theory/rewriter.h" - -namespace CVC4 { - -LemmaProofRecipe::ProofStep::ProofStep(theory::TheoryId theory, Node literalToProve) : - d_theory(theory), d_literalToProve(literalToProve) { -} - -theory::TheoryId LemmaProofRecipe::ProofStep::getTheory() const { - return d_theory; -} - -Node LemmaProofRecipe::ProofStep::getLiteral() const { - return d_literalToProve; -} - -void LemmaProofRecipe::ProofStep::addAssertion(const Node& assertion) { - d_assertions.insert(assertion); -} - -std::set<Node> LemmaProofRecipe::ProofStep::getAssertions() const { - return d_assertions; -} - -void LemmaProofRecipe::addStep(ProofStep& proofStep) { - d_proofSteps.push_back(proofStep); -} - -std::set<Node> LemmaProofRecipe::getMissingAssertionsForStep(unsigned index) const { - Assert(index < d_proofSteps.size()); - - std::set<Node> existingAssertions = getBaseAssertions(); - - // The literals for all the steps "before" (i.e. behind) the step indicated - // by the index are considered "existing" - size_t revIndex = d_proofSteps.size() - 1 - index; - for (size_t i = d_proofSteps.size() - 1; i != revIndex; --i) - { - existingAssertions.insert(d_proofSteps[i].getLiteral().negate()); - } - - std::set<Node> neededAssertions = d_proofSteps[revIndex].getAssertions(); - - std::set<Node> result; - std::set_difference(neededAssertions.begin(), neededAssertions.end(), - existingAssertions.begin(), existingAssertions.end(), - std::inserter(result, result.begin())); - return result; -} - -void LemmaProofRecipe::dump(const char *tag) const { - - if (d_proofSteps.size() == 1) { - Debug(tag) << std::endl << "[Simple lemma]" << std::endl << std::endl; - } - - if (d_originalLemma != Node()) { - Debug(tag) << std::endl << "Original lemma: " << d_originalLemma << std::endl << std::endl; - } - - unsigned count = 1; - Debug(tag) << "Base assertions:" << std::endl; - for (std::set<Node>::iterator baseIt = d_baseAssertions.begin(); - baseIt != d_baseAssertions.end(); - ++baseIt) { - Debug(tag) << "\t#" << count << ": " << "\t" << *baseIt << std::endl; - ++count; - } - - Debug(tag) << std::endl << std::endl << "Proof steps:" << std::endl; - - count = 1; - for (const auto& step : (*this)) { - Debug(tag) << "\tStep #" << count << ": " << "\t[" << step.getTheory() << "] "; - if (step.getLiteral() == Node()) { - Debug(tag) << "Contradiction"; - } else { - Debug(tag) << step.getLiteral(); - } - - Debug(tag) << std::endl; - - std::set<Node> missingAssertions = getMissingAssertionsForStep(count - 1); - for (std::set<Node>::const_iterator it = missingAssertions.begin(); it != missingAssertions.end(); ++it) { - Debug(tag) << "\t\t\tMissing assertion for step: " << *it << std::endl; - } - - Debug(tag) << std::endl; - ++count; - } - - if (!d_assertionToExplanation.empty()) { - Debug(tag) << std::endl << "Rewrites used:" << std::endl; - count = 1; - for (std::map<Node, Node>::const_iterator rewrite = d_assertionToExplanation.begin(); - rewrite != d_assertionToExplanation.end(); - ++rewrite) { - Debug(tag) << "\tRewrite #" << count << ":" << std::endl - << "\t\t" << rewrite->first - << std::endl << "\t\trewritten into" << std::endl - << "\t\t" << rewrite->second - << std::endl << std::endl; - ++count; - } - } -} - -void LemmaProofRecipe::addBaseAssertion(Node baseAssertion) { - d_baseAssertions.insert(baseAssertion); -} - -std::set<Node> LemmaProofRecipe::getBaseAssertions() const { - return d_baseAssertions; -} - -theory::TheoryId LemmaProofRecipe::getTheory() const { - Assert(d_proofSteps.size() > 0); - return d_proofSteps.back().getTheory(); -} - -void LemmaProofRecipe::addRewriteRule(Node assertion, Node explanation) { - if (d_assertionToExplanation.find(assertion) != d_assertionToExplanation.end()) { - Assert(d_assertionToExplanation[assertion] == explanation); - } - - d_assertionToExplanation[assertion] = explanation; -} - -bool LemmaProofRecipe::wasRewritten(Node assertion) const { - return d_assertionToExplanation.find(assertion) != d_assertionToExplanation.end(); -} - -Node LemmaProofRecipe::getExplanation(Node assertion) const { - Assert(d_assertionToExplanation.find(assertion) - != d_assertionToExplanation.end()); - return d_assertionToExplanation.find(assertion)->second; -} - -LemmaProofRecipe::RewriteIterator LemmaProofRecipe::rewriteBegin() const { - return d_assertionToExplanation.begin(); -} - -LemmaProofRecipe::RewriteIterator LemmaProofRecipe::rewriteEnd() const { - return d_assertionToExplanation.end(); -} - -LemmaProofRecipe::iterator LemmaProofRecipe::begin() { - return d_proofSteps.rbegin(); -} - -LemmaProofRecipe::iterator LemmaProofRecipe::end() { - return d_proofSteps.rend(); -} - -LemmaProofRecipe::const_iterator LemmaProofRecipe::begin() const { - return d_proofSteps.crbegin(); -} - -LemmaProofRecipe::const_iterator LemmaProofRecipe::end() const { - return d_proofSteps.crend(); -} - -bool LemmaProofRecipe::operator<(const LemmaProofRecipe& other) const { - return d_baseAssertions < other.d_baseAssertions; - } - -bool LemmaProofRecipe::simpleLemma() const { - return d_proofSteps.size() == 1; -} - -bool LemmaProofRecipe::compositeLemma() const { - return !simpleLemma(); -} - -const LemmaProofRecipe::ProofStep* LemmaProofRecipe::getStep(unsigned index) const { - Assert(index < d_proofSteps.size()); - - size_t revIndex = d_proofSteps.size() - 1 - index; - - return &d_proofSteps[revIndex]; -} - -LemmaProofRecipe::ProofStep* LemmaProofRecipe::getStep(unsigned index) { - Assert(index < d_proofSteps.size()); - - size_t revIndex = d_proofSteps.size() - 1 - index; - - return &d_proofSteps[revIndex]; -} - -unsigned LemmaProofRecipe::getNumSteps() const { - return d_proofSteps.size(); -} - -void LemmaProofRecipe::setOriginalLemma(Node lemma) { - d_originalLemma = lemma; -} - -Node LemmaProofRecipe::getOriginalLemma() const { - return d_originalLemma; -} - -std::ostream& operator<<(std::ostream& out, - const LemmaProofRecipe::ProofStep& step) -{ - out << "Proof Step("; - out << " lit = " << step.getLiteral() << ","; - out << " assertions = " << step.getAssertions() << ","; - out << " theory = " << step.getTheory(); - out << " )"; - return out; -} - -std::ostream& operator<<(std::ostream& out, const LemmaProofRecipe& recipe) -{ - out << "LemmaProofRecipe("; - out << "\n original lemma = " << recipe.getOriginalLemma(); - out << "\n actual clause = " << recipe.getBaseAssertions(); - out << "\n theory = " << recipe.getTheory(); - out << "\n steps = "; - for (const auto& step : recipe) - { - out << "\n " << step; - } - out << "\n rewrites = "; - for (LemmaProofRecipe::RewriteIterator i = recipe.rewriteBegin(), - end = recipe.rewriteEnd(); - i != end; - ++i) - { - out << "\n Rewrite(" << i->first << ", explanation = " << i->second - << ")"; - } - out << "\n)"; - return out; -} - -} /* namespace CVC4 */ diff --git a/src/proof/lemma_proof.h b/src/proof/lemma_proof.h deleted file mode 100644 index ffc6655a6..000000000 --- a/src/proof/lemma_proof.h +++ /dev/null @@ -1,115 +0,0 @@ -/********************* */ -/*! \file lemma_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** A class for recoding the steps required in order to prove a theory lemma. - -**/ - -#include "cvc4_private.h" - -#ifndef CVC4__LEMMA_PROOF_H -#define CVC4__LEMMA_PROOF_H - -#include "expr/expr.h" -#include "proof/clause_id.h" -#include "prop/sat_solver_types.h" -#include "util/proof.h" -#include "expr/node.h" -#include <iosfwd> - -namespace CVC4 { - -class LemmaProofRecipe { -public: - class ProofStep { - public: - ProofStep(theory::TheoryId theory, Node literalToProve); - theory::TheoryId getTheory() const; - Node getLiteral() const; - void addAssertion(const Node& assertion); - std::set<Node> getAssertions() const; - - private: - theory::TheoryId d_theory; - Node d_literalToProve; - std::set<Node> d_assertions; - }; - - //* The lemma assertions and owner */ - void addBaseAssertion(Node baseAssertion); - std::set<Node> getBaseAssertions() const; - theory::TheoryId getTheory() const; - - //* Rewrite rules */ - using RewriteIterator = std::map<Node, Node>::const_iterator; - RewriteIterator rewriteBegin() const; - RewriteIterator rewriteEnd() const; - - // Steps iterator - // The default iterator for a LemmaProofRecipe - using iterator = std::vector<ProofStep>::reverse_iterator; - std::vector<ProofStep>::reverse_iterator begin(); - std::vector<ProofStep>::reverse_iterator end(); - - using const_iterator = std::vector<ProofStep>::const_reverse_iterator; - std::vector<ProofStep>::const_reverse_iterator begin() const; - std::vector<ProofStep>::const_reverse_iterator end() const; - - using difference_type = ptrdiff_t; - using size_type = size_t; - using value_type = ProofStep; - using pointer = ProofStep *; - using const_pointer = const ProofStep *; - using reference = ProofStep &; - using const_reference = const ProofStep &; - - void addRewriteRule(Node assertion, Node explanation); - bool wasRewritten(Node assertion) const; - Node getExplanation(Node assertion) const; - - //* Original lemma */ - void setOriginalLemma(Node lemma); - Node getOriginalLemma() const; - - //* Proof Steps */ - void addStep(ProofStep& proofStep); - const ProofStep* getStep(unsigned index) const; - ProofStep* getStep(unsigned index); - unsigned getNumSteps() const; - std::set<Node> getMissingAssertionsForStep(unsigned index) const; - bool simpleLemma() const; - bool compositeLemma() const; - - void dump(const char *tag) const; - bool operator<(const LemmaProofRecipe& other) const; - -private: - //* The list of assertions for this lemma */ - std::set<Node> d_baseAssertions; - - //* The various steps needed to derive the empty clause */ - // The "first" step is actually at the back. - std::vector<ProofStep> d_proofSteps; - - //* A map from assertions to their rewritten explanations (toAssert --> toExplain) */ - std::map<Node, Node> d_assertionToExplanation; - - //* The original lemma, as asserted by the owner theory solver */ - Node d_originalLemma; -}; - -std::ostream& operator<<(std::ostream & out, const LemmaProofRecipe::ProofStep & step); - -std::ostream& operator<<(std::ostream & out, const LemmaProofRecipe & recipe); - -} /* CVC4 namespace */ - -#endif /* CVC4__LEMMA_PROOF_H */ diff --git a/src/proof/lfsc_proof_printer.cpp b/src/proof/lfsc_proof_printer.cpp deleted file mode 100644 index 464083841..000000000 --- a/src/proof/lfsc_proof_printer.cpp +++ /dev/null @@ -1,217 +0,0 @@ -/********************* */ -/*! \file lfsc_proof_printer.cpp - ** \verbatim - ** Top contributors (to current version): - ** Andres Noetzli, Alex Ozdemir, Liana Hadarean - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Prints proofs in the LFSC format - ** - ** Prints proofs in the LFSC format. - **/ - -#include "proof/lfsc_proof_printer.h" - -#include <algorithm> -#include <iostream> -#include <iterator> - -#include "prop/bvminisat/core/Solver.h" -#include "prop/minisat/core/Solver.h" - -namespace CVC4 { -namespace proof { - -template <class Solver> -std::string LFSCProofPrinter::clauseName(TSatProof<Solver>* satProof, - ClauseId id) -{ - std::ostringstream os; - if (satProof->isInputClause(id)) - { - os << ProofManager::getInputClauseName(id, satProof->getName()); - } - else if (satProof->isLemmaClause(id)) - { - os << ProofManager::getLemmaClauseName(id, satProof->getName()); - } - else - { - os << ProofManager::getLearntClauseName(id, satProof->getName()); - } - return os.str(); -} - -template <class Solver> -void LFSCProofPrinter::printResolution(TSatProof<Solver>* satProof, - ClauseId id, - std::ostream& out, - std::ostream& paren) -{ - out << "(satlem_simplify _ _ _"; - paren << ")"; - - const ResChain<Solver>& res = satProof->getResolutionChain(id); - const typename ResChain<Solver>::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(); - out << " " << clauseName(satProof, start_id); - - for (unsigned i = 0; i < steps.size(); i++) - { - prop::SatVariable v = - prop::MinisatSatSolver::toSatVariable(var(steps[i].lit)); - out << " " << clauseName(satProof, steps[i].id) << " " - << ProofManager::getVarName(v, satProof->getName()) << ")"; - } - - if (id == satProof->getEmptyClauseId()) - { - out << " (\\ empty empty)"; - return; - } - - out << " (\\ " << clauseName(satProof, id) << "\n"; // bind to lemma name - paren << ")"; -} - -template <class Solver> -void LFSCProofPrinter::printAssumptionsResolution(TSatProof<Solver>* satProof, - ClauseId id, - std::ostream& out, - std::ostream& paren) -{ - Assert(satProof->isAssumptionConflict(id)); - // print the resolution proving the assumption conflict - printResolution(satProof, id, out, paren); - // resolve out assumptions to prove empty clause - out << "(satlem_simplify _ _ _ "; - const std::vector<typename Solver::TLit>& confl = - *(satProof->getAssumptionConflicts().at(id)); - - Assert(confl.size()); - - for (unsigned i = 0; i < confl.size(); ++i) - { - prop::SatLiteral lit = toSatLiteral<Solver>(confl[i]); - out << "("; - out << (lit.isNegated() ? "Q" : "R") << " _ _ "; - } - - out << clauseName(satProof, id) << " "; - for (int i = confl.size() - 1; i >= 0; --i) - { - prop::SatLiteral lit = toSatLiteral<Solver>(confl[i]); - prop::SatVariable v = lit.getSatVariable(); - out << "unit" << v << " "; - out << ProofManager::getVarName(v, satProof->getName()) << ")"; - } - out << "(\\ e e)\n"; - paren << ")"; -} - -template <class Solver> -void LFSCProofPrinter::printResolutions(TSatProof<Solver>* satProof, - std::ostream& out, - std::ostream& paren) -{ - Debug("bv-proof") << "; print resolutions" << std::endl; - std::set<ClauseId>::iterator it = satProof->getSeenLearnt().begin(); - for (; it != satProof->getSeenLearnt().end(); ++it) - { - if (*it != satProof->getEmptyClauseId()) - { - Debug("bv-proof") << "; print resolution for " << *it << std::endl; - printResolution(satProof, *it, out, paren); - } - } - Debug("bv-proof") << "; done print resolutions" << std::endl; -} - -template <class Solver> -void LFSCProofPrinter::printResolutionEmptyClause(TSatProof<Solver>* satProof, - std::ostream& out, - std::ostream& paren) -{ - printResolution(satProof, satProof->getEmptyClauseId(), out, paren); -} - -void LFSCProofPrinter::printSatInputProof(const std::vector<ClauseId>& clauses, - std::ostream& out, - const std::string& namingPrefix) -{ - for (auto i = clauses.begin(), end = clauses.end(); i != end; ++i) - { - out << "\n (cnfc_proof _ _ _ " - << ProofManager::getInputClauseName(*i, namingPrefix) << " "; - } - out << "cnfn_proof"; - std::fill_n(std::ostream_iterator<char>(out), clauses.size(), ')'); -} - -void LFSCProofPrinter::printCMapProof(const std::vector<ClauseId>& clauses, - std::ostream& out, - const std::string& namingPrefix) -{ - for (size_t i = 0, n = clauses.size(); i < n; ++i) - { - out << "\n (CMapc_proof " << (i + 1) << " _ _ _ " - << ProofManager::getInputClauseName(clauses[i], namingPrefix) << " "; - } - out << "CMapn_proof"; - std::fill_n(std::ostream_iterator<char>(out), clauses.size(), ')'); -} - -void LFSCProofPrinter::printSatClause(const prop::SatClause& clause, - std::ostream& out, - const std::string& namingPrefix) -{ - for (auto i = clause.cbegin(); i != clause.cend(); ++i) - { - out << "(clc " << (i->isNegated() ? "(neg " : "(pos ") - << ProofManager::getVarName(i->getSatVariable(), namingPrefix) << ") "; - } - out << "cln"; - std::fill_n(std::ostream_iterator<char>(out), clause.size(), ')'); -} - -// Template specializations -template void LFSCProofPrinter::printAssumptionsResolution( - TSatProof<CVC4::Minisat::Solver>* satProof, - ClauseId id, - std::ostream& out, - std::ostream& paren); -template void LFSCProofPrinter::printResolutions( - TSatProof<CVC4::Minisat::Solver>* satProof, - std::ostream& out, - std::ostream& paren); -template void LFSCProofPrinter::printResolutionEmptyClause( - TSatProof<CVC4::Minisat::Solver>* satProof, - std::ostream& out, - std::ostream& paren); - -template void LFSCProofPrinter::printAssumptionsResolution( - TSatProof<CVC4::BVMinisat::Solver>* satProof, - ClauseId id, - std::ostream& out, - std::ostream& paren); -template void LFSCProofPrinter::printResolutions( - TSatProof<CVC4::BVMinisat::Solver>* satProof, - std::ostream& out, - std::ostream& paren); -template void LFSCProofPrinter::printResolutionEmptyClause( - TSatProof<CVC4::BVMinisat::Solver>* satProof, - std::ostream& out, - std::ostream& paren); -} // namespace proof -} // namespace CVC4 diff --git a/src/proof/lfsc_proof_printer.h b/src/proof/lfsc_proof_printer.h deleted file mode 100644 index 62547676f..000000000 --- a/src/proof/lfsc_proof_printer.h +++ /dev/null @@ -1,154 +0,0 @@ -/********************* */ -/*! \file lfsc_proof_printer.h - ** \verbatim - ** Top contributors (to current version): - ** Andres Noetzli, Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Prints proofs in the LFSC format - ** - ** Prints proofs in the LFSC format. - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__LFSC_PROOF_PRINTER_H -#define CVC4__PROOF__LFSC_PROOF_PRINTER_H - -#include <iosfwd> -#include <string> -#include <vector> - -#include "proof/clause_id.h" -#include "proof/proof_manager.h" -#include "proof/sat_proof.h" -#include "proof/sat_proof_implementation.h" -#include "util/proof.h" - -namespace CVC4 { -namespace proof { - -class LFSCProofPrinter -{ - public: - /** - * Prints the resolution proof for an assumption conflict. - * - * @param satProof The record of the reasoning done by the SAT solver - * @param id The clause to print a proof for - * @param out The stream to print to - * @param paren A stream for the closing parentheses - */ - template <class Solver> - static void printAssumptionsResolution(TSatProof<Solver>* satProof, - ClauseId id, - std::ostream& out, - std::ostream& paren); - - /** - * Prints the resolution proofs for learned clauses that have been used to - * deduce unsat. - * - * @param satProof The record of the reasoning done by the SAT solver - * @param out The stream to print to - * @param paren A stream for the closing parentheses - */ - template <class Solver> - static void printResolutions(TSatProof<Solver>* satProof, - std::ostream& out, - std::ostream& paren); - - /** - * Prints the resolution proof for the empty clause. - * - * @param satProof The record of the reasoning done by the SAT solver - * @param out The stream to print to - * @param paren A stream for the closing parentheses - */ - template <class Solver> - static void printResolutionEmptyClause(TSatProof<Solver>* satProof, - std::ostream& out, - std::ostream& paren); - - /** - * The SAT solver is given a list of clauses. - * Assuming that each clause has alreay been individually proven, - * defines a proof of the input to the SAT solver. - * - * Prints an LFSC value corresponding to the proof, i.e. a value of type - * (cnf_holds ...) - * - * @param clauses The clauses to print a proof of - * @param out The stream to print to - * @param namingPrefix The prefix for LFSC names - */ - static void printSatInputProof(const std::vector<ClauseId>& clauses, - std::ostream& out, - const std::string& namingPrefix); - - /** - * The LRAT proof signature uses the concept of a _clause map_ (CMap), which - * represents an indexed collection of (conjoined) clauses. - * - * Specifically, the signatures rely on a proof that a CMap containing the - * clauses given to the SAT solver hold. - * - * Assuming that the individual clauses already have proofs, this function - * prints a proof of the CMap mapping 1 to the first clause, 2 to the second, - * and so on. - * - * That is, it prints a value of type (CMap_holds ...) - * - * @param clauses The clauses to print a proof of - * @param out The stream to print to - * @param namingPrefix The prefix for LFSC names - */ - static void printCMapProof(const std::vector<ClauseId>& clauses, - std::ostream& out, - const std::string& namingPrefix); - - /** - * Prints a clause - * - * @param clause The clause to print - * @param out The stream to print to - * @param namingPrefix The prefix for LFSC names - */ - static void printSatClause(const prop::SatClause& clause, - std::ostream& out, - const std::string& namingPrefix); - - private: - - /** - * Maps a clause id to a string identifier used in the LFSC proof. - * - * @param satProof The record of the reasoning done by the SAT solver - * @param id The clause to map to a string - */ - template <class Solver> - static std::string clauseName(TSatProof<Solver>* satProof, ClauseId id); - - /** - * Prints the resolution proof for a given clause. - * - * @param satProof The record of the reasoning done by the SAT solver - * @param id The clause to print a proof for - * @param out The stream to print to - * @param paren A stream for the closing parentheses - */ - template <class Solver> - static void printResolution(TSatProof<Solver>* satProof, - ClauseId id, - std::ostream& out, - std::ostream& paren); -}; - -} // namespace proof -} // namespace CVC4 - -#endif /* CVC4__PROOF__LFSC_PROOF_PRINTER_H */ diff --git a/src/proof/lrat/lrat_proof.cpp b/src/proof/lrat/lrat_proof.cpp deleted file mode 100644 index 69ffa623a..000000000 --- a/src/proof/lrat/lrat_proof.cpp +++ /dev/null @@ -1,343 +0,0 @@ -/********************* */ -/*! \file lrat_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Andres Noetzli, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief DRAT Proof Format - ** - ** Defines deserialization for DRAT proofs. - **/ - -#include "proof/lrat/lrat_proof.h" - -#include <algorithm> -#include <cstdlib> -#include <fstream> -#include <iostream> -#include <memory> -#include <sstream> -#include <unordered_map> - -#include "base/check.h" -#include "base/output.h" -#include "proof/dimacs.h" -#include "proof/lfsc_proof_printer.h" -#include "util/utility.h" - -#if CVC4_USE_DRAT2ER -#include "drat2er_options.h" -#include "drat_trim_interface.h" -#endif - -namespace CVC4 { -namespace proof { -namespace lrat { - -using prop::SatClause; -using prop::SatLiteral; -using prop::SatVariable; - -namespace { - -// Prints the trace as a space-separated list of (+) ints with a space at the -// end. -std::ostream& operator<<(std::ostream& o, const LratUPTrace& trace) -{ - for (const auto& i : trace) - { - o << i << " "; - } - return o; -} - -/** - * Print a list of clause indices to go to while doing UP. - * - * i.e. a value of type Trace - * - * @param o where to print to - * @param trace the trace (list of clauses) to print - */ -void printTrace(std::ostream& o, const LratUPTrace& trace) -{ - for (ClauseIdx idx : trace) - { - o << "(Tracec " << idx << " "; - } - o << "Tracen"; - std::fill_n(std::ostream_iterator<char>(o), trace.size(), ')'); -} - -/** - * Print the RAT hints for a clause addition. - * - * i.e. prints an LFSC value of type RATHints - * - * @param o where to print to - * @param hints the RAT hints to print - */ -void printHints(std::ostream& o, - const std::vector<std::pair<ClauseIdx, LratUPTrace>>& hints) -{ - for (auto& hint : hints) - { - o << "\n (RATHintsc " << hint.first << " "; - printTrace(o, hint.second); - o << " "; - } - o << "RATHintsn"; - std::fill_n(std::ostream_iterator<char>(o), hints.size(), ')'); -} - -/** - * Print an index list - * - * i.e. prints an LFSC value of type CIList - * - * @param o where to print to - * @param indices the list of indices to print - */ -void printIndices(std::ostream& o, const std::vector<ClauseIdx>& indices) -{ - Assert(indices.size() > 0); - // Verify that the indices are sorted! - for (size_t i = 0, n = indices.size() - 1; i < n; ++i) - { - Assert(indices[i] < indices[i + 1]); - } - - for (ClauseIdx idx : indices) - { - o << "(CIListc " << idx << " "; - } - o << "CIListn"; - std::fill_n(std::ostream_iterator<char>(o), indices.size(), ')'); -} - -} // namespace - -// Prints the LRAT addition line in textual format - -LratProof LratProof::fromDratProof( - const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId> usedIds, - const std::string& dratBinary, - TimerStat& toolTimer) -{ - std::ostringstream cmd; - std::string formulaFilename("cvc4-dimacs-XXXXXX"); - std::string dratFilename("cvc4-drat-XXXXXX"); - std::string lratFilename("cvc4-lrat-XXXXXX"); - - std::unique_ptr<std::fstream> formStream = openTmpFile(&formulaFilename); - printDimacs(*formStream, clauses, usedIds); - formStream->close(); - - std::unique_ptr<std::fstream> dratStream = openTmpFile(&dratFilename); - (*dratStream) << dratBinary; - dratStream->close(); - - std::unique_ptr<std::fstream> lratStream = openTmpFile(&lratFilename); - - { - CodeTimer blockTimer{toolTimer}; -#if CVC4_USE_DRAT2ER - drat2er::drat_trim::CheckAndConvertToLRAT( - formulaFilename, dratFilename, lratFilename, drat2er::options::QUIET); -#else - Unimplemented() - << "LRAT proof production requires drat2er.\n" - << "Run contrib/get-drat2er, reconfigure with --drat2er, and rebuild"; -#endif - } - - LratProof lrat(*lratStream); - remove(formulaFilename.c_str()); - remove(dratFilename.c_str()); - remove(lratFilename.c_str()); - return lrat; -} - -std::istream& operator>>(std::istream& in, SatLiteral& l) -{ - int64_t i; - in >> i; - l = SatLiteral(std::abs(i), i < 0); - return in; -} - -// This parser is implemented to parse the textual RAT format found in -// "Efficient Certified RAT Verification", by Cruz-Filipe et. All -LratProof::LratProof(std::istream& textualProof) -{ - for (size_t line = 0;; ++line) - { - // Read beginning of instruction. EOF indicates that we're done. - size_t clauseIdx; - textualProof >> clauseIdx; - if (textualProof.eof()) - { - return; - } - - // Read the first word of the instruction. A 'd' indicates deletion. - std::string first; - textualProof >> first; - Trace("pf::lrat") << "First word: " << first << std::endl; - Assert(textualProof.good()); - if (first == "d") - { - std::vector<ClauseIdx> clauses; - while (true) - { - ClauseIdx di; - textualProof >> di; - Assert(textualProof.good()); - if (di == 0) - { - break; - } - clauses.push_back(di); - } - if (clauses.size() > 0) - { - std::sort(clauses.begin(), clauses.end()); - std::unique_ptr<LratInstruction> instr( - new LratDeletion(clauseIdx, std::move(clauses))); - d_instructions.push_back(std::move(instr)); - } - } - else - { - // We need to reparse the first word as a literal to read the clause - // we're parsing. It ends with a 0; - std::istringstream firstS(first); - SatLiteral lit; - firstS >> lit; - Trace("pf::lrat") << "First lit: " << lit << std::endl; - Assert(!firstS.fail()) - << "Couldn't parse first literal from addition line"; - - SatClause clause; - for (; lit != 0; textualProof >> lit) - { - Assert(textualProof.good()); - clause.emplace_back(lit.getSatVariable() - 1, lit.isNegated()); - } - - // Now we read the AT UP trace. It ends at the first non-(+) # - std::vector<ClauseIdx> atTrace; - int64_t i; - textualProof >> i; - for (; i > 0; textualProof >> i) - { - Assert(textualProof.good()); - atTrace.push_back(i); - } - - // For each RAT hint... (each RAT hint starts with a (-)). - std::vector<std::pair<ClauseIdx, LratUPTrace>> resolvants; - for (; i<0; textualProof>> i) - { - Assert(textualProof.good()); - // Create an entry in the RAT hint list - resolvants.emplace_back(-i, std::vector<ClauseIdx>()); - - // Record the UP trace. It ends with a (-) or 0. - textualProof >> i; - for (; i > 0; textualProof >> i) - { - resolvants.back().second.push_back(i); - } - } - // Pairs compare based on the first element, so this sorts by the - // resolution target index - std::sort(resolvants.begin(), resolvants.end()); - std::unique_ptr<LratInstruction> instr( - new LratAddition(clauseIdx, - std::move(clause), - std::move(atTrace), - std::move(resolvants))); - d_instructions.push_back(std::move(instr)); - } - } -} - -void LratProof::outputAsLfsc(std::ostream& o) const -{ - std::ostringstream closeParen; - for (const auto& i : d_instructions) - { - i->outputAsLfsc(o, closeParen); - } - o << "LRATProofn"; - o << closeParen.str(); -} - -void LratAddition::outputAsText(std::ostream& o) const -{ - o << d_idxOfClause << " "; - textOut(o, d_clause) << " "; - o << d_atTrace; // Inludes a space at the end. - for (const auto& rat : d_resolvants) - { - o << "-" << rat.first << " "; - o << rat.second; // Includes a space at the end. - } - o << "0\n"; -} - -void LratAddition::outputAsLfsc(std::ostream& o, std::ostream& closeParen) const -{ - o << "\n (LRATProofa " << d_idxOfClause << " "; - closeParen << ")"; - LFSCProofPrinter::printSatClause(d_clause, o, "bb"); - o << " "; - printTrace(o, d_atTrace); - o << " "; - printHints(o, d_resolvants); - o << " "; -} - -void LratDeletion::outputAsText(std::ostream& o) const -{ - o << d_idxOfClause << " d "; - for (const auto& idx : d_clauses) - { - o << idx << " "; - } - o << "0\n"; -} - -void LratDeletion::outputAsLfsc(std::ostream& o, std::ostream& closeParen) const -{ - o << "\n (LRATProofd "; - closeParen << ")"; - printIndices(o, d_clauses); - o << " "; -} - -std::ostream& operator<<(std::ostream& o, const LratProof& p) -{ - for (const auto& instr : p.getInstructions()) - { - o << *instr; - } - return o; -} - -std::ostream& operator<<(std::ostream& o, const LratInstruction& i) -{ - i.outputAsText(o); - return o; -} - -} // namespace lrat -} // namespace proof -} // namespace CVC4 diff --git a/src/proof/lrat/lrat_proof.h b/src/proof/lrat/lrat_proof.h deleted file mode 100644 index 1c065a08e..000000000 --- a/src/proof/lrat/lrat_proof.h +++ /dev/null @@ -1,184 +0,0 @@ -/********************* */ -/*! \file lrat_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief LRAT Proof Format - ** - ** Declares C++ types that represent a LRAT proof. - ** Defines serialization for these types. - ** - ** Represents an **abstract** LRAT proof. - ** Does **not** represent an LFSC LRAT proof, or an LRAT proof being used to - ** prove things about bit-vectors. - ** - ** Paper on LRAT: https://www.cs.utexas.edu/~marijn/publications/lrat.pdf - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__LRAT__LRAT_PROOF_H -#define CVC4__PROOF__LRAT__LRAT_PROOF_H - -#include <iosfwd> -#include <string> -#include <unordered_map> -#include <utility> -#include <vector> - -#include "proof/clause_id.h" -// Included because we need operator<< for the SAT types -#include "prop/sat_solver.h" -#include "util/statistics_registry.h" - -namespace CVC4 { -namespace proof { -namespace lrat { - -// Refers to clause position within an LRAT proof -using ClauseIdx = size_t; - -// This is conceptually an Either<Addition,Deletion> -class LratInstruction -{ - public: - /** - * Write this LRAT instruction in textual format - * - * @param out the stream to write to - */ - virtual void outputAsText(std::ostream& out) const = 0; - /** - * Write this LRAT instruction as an LFSC value - * - * @param out the stream to write to - * @param closeParen the stream to write any closing parentheses to - * - */ - virtual void outputAsLfsc(std::ostream& o, - std::ostream& closeParen) const = 0; - virtual ~LratInstruction() = default; -}; - -class LratDeletion : public LratInstruction -{ - public: - LratDeletion(ClauseIdx idxOfClause, std::vector<ClauseIdx>&& clauses) - : d_idxOfClause(idxOfClause), d_clauses(clauses) - { - // Nothing left to do - } - - LratDeletion() = default; - - void outputAsText(std::ostream& out) const override; - void outputAsLfsc(std::ostream& o, std::ostream& closeParen) const override; - - private: - // This idx doesn't really matter, but it's in the format anyway, so we parse - // it. - ClauseIdx d_idxOfClause; - - // Clauses to delete - std::vector<ClauseIdx> d_clauses; -}; - -// A sequence of locations that will contain unit clauses during unit -// propegation -using LratUPTrace = std::vector<ClauseIdx>; - -class LratAddition : public LratInstruction -{ - public: - LratAddition(ClauseIdx idxOfClause, - prop::SatClause&& clause, - LratUPTrace&& atTrace, - std::vector<std::pair<ClauseIdx, LratUPTrace>> resolvants) - : d_idxOfClause(idxOfClause), - d_clause(clause), - d_atTrace(atTrace), - d_resolvants(resolvants) - { - // Nothing left to do - } - - void outputAsText(std::ostream& out) const override; - void outputAsLfsc(std::ostream& o, std::ostream& closeParen) const override; - - private: - // The idx for the new clause - ClauseIdx d_idxOfClause; - // The new clause - prop::SatClause d_clause; - // UP trace based on the negation of that clause - LratUPTrace d_atTrace; - - // Clauses that can resolve with `clause` on its first variable, - // together with a UP trace after that resolution. - // Used for RAT checks. - std::vector<std::pair<ClauseIdx, LratUPTrace>> d_resolvants; -}; - -class LratProof -{ - public: - /** - * @brief Construct an LRAT proof from a DRAT proof, using drat-trim - * - * @param clauses A store of clauses that might be in our formula - * @param usedIds the ids of clauses that are actually in our formula - * @param dratBinary The DRAT proof from the SAT solver, as a binary stream. - * - * @return an LRAT proof an a timer for how long it took to run drat-trim - */ - static LratProof fromDratProof( - const std::unordered_map<ClauseId, prop::SatClause>& clauses, - const std::vector<ClauseId> usedIds, - const std::string& dratBinary, - TimerStat& toolTimer); - /** - * @brief Construct an LRAT proof from its textual representation - * - * @param textualProof the textual encoding of the LRAT proof. See the paper - * in the file's header comment. - */ - LratProof(std::istream& textualProof); - - /** - * Construct a LRAT proof from an explicit instruction list - * - * @param instructions - */ - LratProof(std::vector<std::unique_ptr<LratInstruction>>&& instructions) - : d_instructions(std::move(instructions)) - { - // Nothing else - } - - const std::vector<std::unique_ptr<LratInstruction>>& getInstructions() const - { - return d_instructions; - } - - void outputAsLfsc(std::ostream& o) const; - - private: - // The instructions in the proof. Each is a deletion or addition. - std::vector<std::unique_ptr<LratInstruction>> d_instructions; -}; - -// Prints the LRAT proof in textual format -std::ostream& operator<<(std::ostream& o, const LratProof& p); -std::ostream& operator<<(std::ostream& o, const LratInstruction& i); - -} // namespace lrat -} // namespace proof -} // namespace CVC4 - -#endif diff --git a/src/proof/proof.h b/src/proof/proof.h deleted file mode 100644 index be5af32db..000000000 --- a/src/proof/proof.h +++ /dev/null @@ -1,70 +0,0 @@ -/********************* */ -/*! \file proof.h - ** \verbatim - ** Top contributors (to current version): - ** Tim King, Liana Hadarean, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Proof macros - ** - ** Proof macros - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__PROOF_H -#define CVC4__PROOF__PROOF_H - -#include "options/smt_options.h" - - -/* Do NOT use #ifdef CVC4_PROOF to check if proofs are enabled. - * We cannot assume users will use -DCVC4_PROOFS if they have a proofs build. - * The preferred way of checking that proofs are enabled is to use: - * #if IS_PROOFS_BUILD - * ... - * #endif - * - * The macro IS_PROOFS_BUILD is defined in base/configuration_private.h - * - * This has the effect of forcing that location to have included this header - * *before* performing this test. This includes C preprocessing expansion. - * This forces the inclusion of "cvc4_private.h". This is intentional! - * - * See bug 688 for more details: - * https://github.com/CVC4/CVC4/issues/907 - * - * If you want to check CVC4_PROOF, you should have a very good reason - * and should list the exceptions here: - * - Makefile.am - * - proof/proofs.h - * - base/configuration_private.h - */ - -#ifdef CVC4_PROOF -# define PROOF(x) if(CVC4::options::proof() || CVC4::options::unsatCores()) { x; } -# define NULLPROOF(x) (CVC4::options::proof() || CVC4::options::unsatCores()) ? x : NULL -# define PROOF_ON() (CVC4::options::proof() || CVC4::options::unsatCores()) -# define THEORY_PROOF(x) if(CVC4::options::proof()) { x; } -# define THEORY_NULLPROOF(x) CVC4::options::proof() ? x : NULL -# define THEORY_PROOF_ON() CVC4::options::proof() -#else /* CVC4_PROOF */ -# define PROOF(x) -# define NULLPROOF(x) NULL -# define PROOF_ON() false -# define THEORY_PROOF(x) -# define THEORY_NULLPROOF(x) NULL -# define THEORY_PROOF_ON() false -#endif /* CVC4_PROOF */ - -#ifdef CVC4_PROOF_STATS /* CVC4_PROOF_STATS */ -# define PSTATS(x) { x; } -#else -# define PSTATS(x) -#endif /* CVC4_PROOF_STATS */ - -#endif /* CVC4__PROOF__PROOF_H */ diff --git a/src/proof/proof_manager.cpp b/src/proof/proof_manager.cpp index 99e3010b4..3e6cc9c69 100644 --- a/src/proof/proof_manager.cpp +++ b/src/proof/proof_manager.cpp @@ -21,14 +21,10 @@ #include "context/context.h" #include "expr/node_visitor.h" #include "options/bv_options.h" -#include "options/proof_options.h" +#include "options/smt_options.h" #include "proof/clause_id.h" #include "proof/cnf_proof.h" -#include "proof/lfsc_proof_printer.h" -#include "proof/proof_utils.h" -#include "proof/resolution_bitvector_proof.h" #include "proof/sat_proof_implementation.h" -#include "proof/theory_proof.h" #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "smt/smt_statistics_registry.h" @@ -39,36 +35,16 @@ #include "theory/uf/theory_uf.h" #include "theory/valuation.h" #include "util/hash.h" -#include "util/proof.h" namespace CVC4 { -std::string nodeSetToString(const std::set<Node>& nodes) { - std::ostringstream os; - std::set<Node>::const_iterator it; - for (it = nodes.begin(); it != nodes.end(); ++it) { - os << *it << " "; - } - return os.str(); -} - -std::string append(const std::string& str, uint64_t num) { - std::ostringstream os; - os << str << num; - return os.str(); -} - -ProofManager::ProofManager(context::Context* context, ProofFormat format) +ProofManager::ProofManager(context::Context* context) : d_context(context), d_satProof(nullptr), d_cnfProof(nullptr), - d_theoryProof(nullptr), - d_inputFormulas(), d_inputCoreFormulas(context), d_outputCoreFormulas(context), d_nextId(0), - d_fullProof(), - d_format(format), d_deps(context) { } @@ -78,19 +54,6 @@ ProofManager::~ProofManager() {} ProofManager* ProofManager::currentPM() { return smt::currentProofManager(); } -const Proof& ProofManager::getProof(SmtEngine* smt) -{ - if (!currentPM()->d_fullProof) - { - Assert(currentPM()->d_format == LFSC); - currentPM()->d_fullProof.reset(new LFSCProof( - smt, - getSatProof(), - static_cast<LFSCCnfProof*>(getCnfProof()), - static_cast<LFSCTheoryProofEngine*>(getTheoryProofEngine()))); - } - return *(currentPM()->d_fullProof); -} CoreSatProof* ProofManager::getSatProof() { @@ -104,45 +67,8 @@ CnfProof* ProofManager::getCnfProof() return currentPM()->d_cnfProof.get(); } -TheoryProofEngine* ProofManager::getTheoryProofEngine() -{ - Assert(currentPM()->d_theoryProof != NULL); - return currentPM()->d_theoryProof.get(); -} - -UFProof* ProofManager::getUfProof() { - Assert(options::proof()); - TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_UF); - return (UFProof*)pf; -} - -proof::ResolutionBitVectorProof* ProofManager::getBitVectorProof() -{ - Assert(options::proof()); - TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_BV); - return static_cast<proof::ResolutionBitVectorProof*>(pf); -} - -ArrayProof* ProofManager::getArrayProof() { - Assert(options::proof()); - TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_ARRAYS); - return (ArrayProof*)pf; -} - -ArithProof* ProofManager::getArithProof() { - Assert(options::proof()); - TheoryProof* pf = getTheoryProofEngine()->getTheoryProof(theory::THEORY_ARITH); - return (ArithProof*)pf; -} - -SkolemizationManager* ProofManager::getSkolemizationManager() { - Assert(options::proof() || options::unsatCores()); - return &(currentPM()->d_skolemizationManager); -} - void ProofManager::initSatProof(Minisat::Solver* solver) { - Assert(d_format == LFSC); // Destroy old instance before initializing new one to avoid issues with // registering stats d_satProof.reset(); @@ -153,150 +79,22 @@ void ProofManager::initCnfProof(prop::CnfStream* cnfStream, context::Context* ctx) { Assert(d_satProof != nullptr); - Assert(d_format == LFSC); - d_cnfProof.reset(new LFSCCnfProof(cnfStream, ctx, "")); + d_cnfProof.reset(new CnfProof(cnfStream, ctx, "")); // true and false have to be setup in a special way Node true_node = NodeManager::currentNM()->mkConst<bool>(true); Node false_node = NodeManager::currentNM()->mkConst<bool>(false).notNode(); d_cnfProof->pushCurrentAssertion(true_node); - d_cnfProof->pushCurrentDefinition(true_node); d_cnfProof->registerConvertedClause(d_satProof->getTrueUnit()); d_cnfProof->popCurrentAssertion(); - d_cnfProof->popCurrentDefinition(); d_cnfProof->pushCurrentAssertion(false_node); - d_cnfProof->pushCurrentDefinition(false_node); d_cnfProof->registerConvertedClause(d_satProof->getFalseUnit()); d_cnfProof->popCurrentAssertion(); - d_cnfProof->popCurrentDefinition(); -} - -void ProofManager::initTheoryProofEngine() -{ - Assert(d_theoryProof == NULL); - Assert(d_format == LFSC); - d_theoryProof.reset(new LFSCTheoryProofEngine()); -} - -std::string ProofManager::getInputClauseName(ClauseId id, - const std::string& prefix) { - return append(prefix+".pb", id); -} - -std::string ProofManager::getLemmaClauseName(ClauseId id, - const std::string& prefix) { - return append(prefix+".lemc", id); } -std::string ProofManager::getLemmaName(ClauseId id, const std::string& prefix) { - return append(prefix+"lem", id); -} - -std::string ProofManager::getLearntClauseName(ClauseId id, - const std::string& prefix) { - return append(prefix+".cl", id); -} -std::string ProofManager::getVarName(prop::SatVariable var, - const std::string& prefix) { - return append(prefix+".v", var); -} -std::string ProofManager::getAtomName(prop::SatVariable var, - const std::string& prefix) { - return append(prefix+".a", var); -} -std::string ProofManager::getLitName(prop::SatLiteral lit, - const std::string& prefix) { - return append(prefix+".l", lit.toInt()); -} - -std::string ProofManager::getPreprocessedAssertionName(Node node, - const std::string& prefix) { - if (currentPM()->d_assertionFilters.find(node) != currentPM()->d_assertionFilters.end()) { - return currentPM()->d_assertionFilters[node]; - } - - node = node.getKind() == kind::BITVECTOR_EAGER_ATOM ? node[0] : node; - return append(prefix+".PA", node.getId()); -} -std::string ProofManager::getAssertionName(Node node, - const std::string& prefix) { - return append(prefix+".A", node.getId()); -} -std::string ProofManager::getInputFormulaName(const Expr& expr) { - return currentPM()->d_inputFormulaToName[expr]; -} - -std::string ProofManager::getAtomName(TNode atom, - const std::string& prefix) { - prop::SatLiteral lit = currentPM()->d_cnfProof->getLiteral(atom); - Assert(!lit.isNegated()); - return getAtomName(lit.getSatVariable(), prefix); -} - -std::string ProofManager::getLitName(TNode lit, - const std::string& prefix) { - std::string litName = getLitName(currentPM()->d_cnfProof->getLiteral(lit), prefix); - if (currentPM()->d_rewriteFilters.find(litName) != currentPM()->d_rewriteFilters.end()) { - return currentPM()->d_rewriteFilters[litName]; - } - - return litName; -} - -bool ProofManager::hasLitName(TNode lit) { - return currentPM()->d_cnfProof->hasLiteral(lit); -} - -std::string ProofManager::sanitize(TNode node) { - Assert(node.isVar() || node.isConst()); - - std::string name = node.toString(); - if (node.isVar()) { - std::replace(name.begin(), name.end(), ' ', '_'); - } else if (node.isConst()) { - name.erase(std::remove(name.begin(), name.end(), '('), name.end()); - name.erase(std::remove(name.begin(), name.end(), ')'), name.end()); - name.erase(std::remove(name.begin(), name.end(), ' '), name.end()); - name = "const" + name; - } - - return name; -} - -void ProofManager::traceDeps(TNode n, ExprSet* coreAssertions) { - Debug("cores") << "trace deps " << n << std::endl; - if ((n.isConst() && n == NodeManager::currentNM()->mkConst<bool>(true)) || - (n.getKind() == kind::NOT && n[0] == NodeManager::currentNM()->mkConst<bool>(false))) { - return; - } - if(d_inputCoreFormulas.find(n.toExpr()) != d_inputCoreFormulas.end()) { - // originating formula was in core set - Debug("cores") << " -- IN INPUT CORE LIST!" << std::endl; - coreAssertions->insert(n.toExpr()); - } else { - Debug("cores") << " -- NOT IN INPUT CORE LIST!" << std::endl; - if(d_deps.find(n) == d_deps.end()) { - if (options::allowEmptyDependencies()) { - Debug("cores") << " -- Could not track cause assertion. Failing silently." << std::endl; - return; - } - InternalError() - << "Cannot trace dependence information back to input assertion:\n`" - << n << "'"; - } - Assert(d_deps.find(n) != d_deps.end()); - std::vector<Node> deps = (*d_deps.find(n)).second; - for(std::vector<Node>::const_iterator i = deps.begin(); i != deps.end(); ++i) { - Debug("cores") << " + tracing deps: " << n << " -deps-on- " << *i << std::endl; - if( !(*i).isNull() ){ - traceDeps(*i, coreAssertions); - } - } - } -} void ProofManager::traceDeps(TNode n, CDExprSet* coreAssertions) { Debug("cores") << "trace deps " << n << std::endl; @@ -311,10 +109,6 @@ void ProofManager::traceDeps(TNode n, CDExprSet* coreAssertions) { } else { Debug("cores") << " -- NOT IN INPUT CORE LIST!" << std::endl; if(d_deps.find(n) == d_deps.end()) { - if (options::allowEmptyDependencies()) { - Debug("cores") << " -- Could not track cause assertion. Failing silently." << std::endl; - return; - } InternalError() << "Cannot trace dependence information back to input assertion:\n`" << n << "'"; @@ -345,16 +139,11 @@ void ProofManager::traceUnsatCore() { IdToSatClause::const_iterator it = used_inputs.begin(); for(; it != used_inputs.end(); ++it) { Node node = d_cnfProof->getAssertionForClause(it->first); - ProofRule rule = d_cnfProof->getProofRule(node); - Debug("cores") << "core input assertion " << node << std::endl; - Debug("cores") << "with proof rule " << rule << std::endl; - if (rule == RULE_TSEITIN || - rule == RULE_GIVEN) { - // trace dependences back to actual assertions - // (this adds them to the unsat core) - traceDeps(node, &d_outputCoreFormulas); - } + Debug("cores") << "core input assertion " << node << "\n"; + // trace dependences back to actual assertions + // (this adds them to the unsat core) + traceDeps(node, &d_outputCoreFormulas); } } @@ -373,150 +162,32 @@ std::vector<Expr> ProofManager::extractUnsatCore() { return result; } -void ProofManager::constructSatProof() { - if (!d_satProof->proofConstructed()) { +void ProofManager::constructSatProof() +{ + if (!d_satProof->proofConstructed()) + { d_satProof->constructProof(); } } -void ProofManager::getLemmasInUnsatCore(theory::TheoryId theory, std::vector<Node> &lemmas) { - Assert(PROOF_ON()) << "Cannot compute unsat core when proofs are off"; - Assert(unsatCoreAvailable()) - << "Cannot get unsat core at this time. Mabye the input is SAT?"; - - constructSatProof(); - - IdToSatClause used_lemmas; - IdToSatClause used_inputs; - d_satProof->collectClausesUsed(used_inputs, used_lemmas); - - IdToSatClause::const_iterator it; - std::set<Node> seen; - - Debug("pf::lemmasUnsatCore") << "Dumping all lemmas in unsat core" << std::endl; - for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) { - std::set<Node> lemma = satClauseToNodeSet(it->second); - Debug("pf::lemmasUnsatCore") << nodeSetToString(lemma); - - // TODO: we should be able to drop the "haveProofRecipe" check. - // however, there are some rewrite issues with the arith solver, resulting - // in non-registered recipes. For now we assume no one is requesting arith lemmas. - LemmaProofRecipe recipe; - if (!getCnfProof()->haveProofRecipe(lemma)) { - Debug("pf::lemmasUnsatCore") << "\t[no recipe]" << std::endl; - continue; - } - - recipe = getCnfProof()->getProofRecipe(lemma); - Debug("pf::lemmasUnsatCore") << "\t[owner = " << recipe.getTheory() - << ", original = " << recipe.getOriginalLemma() << "]" << std::endl; - if (recipe.simpleLemma() && recipe.getTheory() == theory && seen.find(recipe.getOriginalLemma()) == seen.end()) { - lemmas.push_back(recipe.getOriginalLemma()); - seen.insert(recipe.getOriginalLemma()); - } - } -} - -std::set<Node> ProofManager::satClauseToNodeSet(prop::SatClause* clause) { - std::set<Node> result; - for (unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = (*clause)[i]; - Node node = getCnfProof()->getAtom(lit.getSatVariable()); - Expr atom = node.toExpr(); - if (atom != utils::mkTrue()) - result.insert(lit.isNegated() ? node.notNode() : node); - } - - return result; -} - -Node ProofManager::getWeakestImplicantInUnsatCore(Node lemma) { - Assert(PROOF_ON()) << "Cannot compute unsat core when proofs are off"; +void ProofManager::getLemmasInUnsatCore(std::vector<Node>& lemmas) +{ + Assert(options::unsatCores()) + << "Cannot compute unsat core when proofs are off"; Assert(unsatCoreAvailable()) << "Cannot get unsat core at this time. Mabye the input is SAT?"; - - // If we're doing aggressive minimization, work on all lemmas, not just conjunctions. - if (!options::aggressiveCoreMin() && (lemma.getKind() != kind::AND)) - return lemma; - constructSatProof(); - - NodeBuilder<> builder(kind::AND); - IdToSatClause used_lemmas; IdToSatClause used_inputs; d_satProof->collectClausesUsed(used_inputs, used_lemmas); - + Debug("pf::lemmasUnsatCore") << "Retrieving all lemmas in unsat core\n"; IdToSatClause::const_iterator it; - std::set<Node>::iterator lemmaIt; - - if (!options::aggressiveCoreMin()) { - for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) { - std::set<Node> currentLemma = satClauseToNodeSet(it->second); - - // TODO: we should be able to drop the "haveProofRecipe" check. - // however, there are some rewrite issues with the arith solver, resulting - // in non-registered recipes. For now we assume no one is requesting arith lemmas. - LemmaProofRecipe recipe; - if (!getCnfProof()->haveProofRecipe(currentLemma)) - continue; - - recipe = getCnfProof()->getProofRecipe(currentLemma); - if (recipe.getOriginalLemma() == lemma) { - for (lemmaIt = currentLemma.begin(); lemmaIt != currentLemma.end(); ++lemmaIt) { - builder << *lemmaIt; - - // Check that each conjunct appears in the original lemma. - bool found = false; - for (unsigned i = 0; i < lemma.getNumChildren(); ++i) { - if (lemma[i] == *lemmaIt) - found = true; - } - - if (!found) - return lemma; - } - } - } - } else { - // Aggressive mode - for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) { - std::set<Node> currentLemma = satClauseToNodeSet(it->second); - - // TODO: we should be able to drop the "haveProofRecipe" check. - // however, there are some rewrite issues with the arith solver, resulting - // in non-registered recipes. For now we assume no one is requesting arith lemmas. - LemmaProofRecipe recipe; - if (!getCnfProof()->haveProofRecipe(currentLemma)) - continue; - - recipe = getCnfProof()->getProofRecipe(currentLemma); - if (recipe.getOriginalLemma() == lemma) { - NodeBuilder<> disjunction(kind::OR); - for (lemmaIt = currentLemma.begin(); lemmaIt != currentLemma.end(); ++lemmaIt) { - disjunction << *lemmaIt; - } - - Node conjunct = (disjunction.getNumChildren() == 1) ? disjunction[0] : disjunction; - builder << conjunct; - } - } + for (it = used_lemmas.begin(); it != used_lemmas.end(); ++it) + { + Node lemma = d_cnfProof->getAssertionForClause(it->first); + Debug("pf::lemmasUnsatCore") << "Retrieved lemma " << lemma << "\n"; + lemmas.push_back(lemma); } - - AlwaysAssert(builder.getNumChildren() != 0); - - if (builder.getNumChildren() == 1) - return builder[0]; - - return builder; -} - -void ProofManager::addAssertion(Expr formula) { - Debug("proof:pm") << "assert: " << formula << std::endl; - d_inputFormulas.insert(formula); - std::ostringstream name; - name << "A" << d_inputFormulaToName.size(); - d_inputFormulaToName[formula] = name.str(); } void ProofManager::addCoreAssertion(Expr formula) { @@ -542,665 +213,4 @@ void ProofManager::addUnsatCore(Expr formula) { d_outputCoreFormulas.insert(formula); } -void ProofManager::addAssertionFilter(const Node& node, const std::string& rewritten) { - d_assertionFilters[node] = rewritten; -} - -void ProofManager::setLogic(const LogicInfo& logic) { - d_logic = logic; -} - -LFSCProof::LFSCProof(SmtEngine* smtEngine, - CoreSatProof* sat, - LFSCCnfProof* cnf, - LFSCTheoryProofEngine* theory) - : d_satProof(sat), - d_cnfProof(cnf), - d_theoryProof(theory), - d_smtEngine(smtEngine) -{} - -void LFSCProof::toStream(std::ostream& out, const ProofLetMap& map) const -{ - Unreachable(); -} - -void collectAtoms(TNode node, std::set<Node>& seen, CnfProof* cnfProof) -{ - Debug("pf::pm::atoms") << "collectAtoms: Colleting atoms from " << node - << "\n"; - if (seen.find(node) != seen.end()) - { - Debug("pf::pm::atoms") << "collectAtoms:\t already seen\n"; - return; - } - // if I have a SAT literal for a node, save it, unless this node is a - // negation, in which case its underlying will be collected downstream - if (cnfProof->hasLiteral(node) && node.getKind() != kind::NOT) - { - Debug("pf::pm::atoms") << "collectAtoms: has SAT literal, save\n"; - seen.insert(node); - } - for (unsigned i = 0; i < node.getNumChildren(); ++i) - { - Debug("pf::pm::atoms") << push; - collectAtoms(node[i], seen, cnfProof); - Debug("pf::pm::atoms") << pop; - } -} - -void LFSCProof::toStream(std::ostream& out) const -{ - TimerStat::CodeTimer proofProductionTimer( - ProofManager::currentPM()->getStats().d_proofProductionTime); - - IdToSatClause used_lemmas; - IdToSatClause used_inputs; - std::set<Node> atoms; - NodePairSet rewrites; - NodeSet used_assertions; - - { - CodeTimer skeletonProofTimer{ - ProofManager::currentPM()->getStats().d_skeletonProofTraceTime}; - Assert(!d_satProof->proofConstructed()); - - // Here we give our SAT solver a chance to flesh out the resolution proof. - // It proves bottom from a set of clauses. - d_satProof->constructProof(); - - // We ask the SAT solver which clauses are used in that proof. - // For a resolution proof, these are the leaves of the tree. - d_satProof->collectClausesUsed(used_inputs, used_lemmas); - - IdToSatClause::iterator it2; - Debug("pf::pm") << std::endl << "Used inputs: " << std::endl; - for (it2 = used_inputs.begin(); it2 != used_inputs.end(); ++it2) - { - Debug("pf::pm") << "\t input = " << *(it2->second) << std::endl; - } - Debug("pf::pm") << std::endl; - - Debug("pf::pm") << std::endl << "Used lemmas: " << std::endl; - for (it2 = used_lemmas.begin(); it2 != used_lemmas.end(); ++it2) - { - std::vector<Expr> clause_expr; - for (unsigned i = 0; i < it2->second->size(); ++i) - { - prop::SatLiteral lit = (*(it2->second))[i]; - Expr atom = d_cnfProof->getAtom(lit.getSatVariable()).toExpr(); - if (atom.isConst()) - { - Assert(atom == utils::mkTrue()); - continue; - } - Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; - clause_expr.push_back(expr_lit); - } - - Debug("pf::pm") << "\t lemma " << it2->first << " = " << *(it2->second) - << std::endl; - Debug("pf::pm") << "\t"; - for (unsigned i = 0; i < clause_expr.size(); ++i) - { - Debug("pf::pm") << clause_expr[i] << " "; - } - Debug("pf::pm") << std::endl; - } - Debug("pf::pm") << std::endl; - - // collecting assertions that lead to the clauses being asserted - d_cnfProof->collectAssertionsForClauses(used_inputs, used_assertions); - - NodeSet::iterator it3; - Debug("pf::pm") << std::endl << "Used assertions: " << std::endl; - for (it3 = used_assertions.begin(); it3 != used_assertions.end(); ++it3) - Debug("pf::pm") << "\t assertion = " << *it3 << std::endl; - - // collects the atoms in the clauses - d_cnfProof->collectAtomsAndRewritesForLemmas(used_lemmas, atoms, rewrites); - - if (!rewrites.empty()) - { - Debug("pf::pm") << std::endl << "Rewrites used in lemmas: " << std::endl; - NodePairSet::const_iterator rewriteIt; - for (rewriteIt = rewrites.begin(); rewriteIt != rewrites.end(); - ++rewriteIt) - { - Debug("pf::pm") << "\t" << rewriteIt->first << " --> " - << rewriteIt->second << std::endl; - } - Debug("pf::pm") << std::endl << "Rewrite printing done" << std::endl; - } - else - { - Debug("pf::pm") << "No rewrites in lemmas found" << std::endl; - } - - // The derived/unrewritten atoms may not have CNF literals required later - // on. If they don't, add them. - std::set<Node>::const_iterator it; - for (it = atoms.begin(); it != atoms.end(); ++it) - { - Debug("pf::pm") << "Ensure literal for atom: " << *it << std::endl; - if (!d_cnfProof->hasLiteral(*it)) - { - // For arithmetic: these literals are not normalized, causing an error - // in Arith. - if (theory::Theory::theoryOf(*it) == theory::THEORY_ARITH) - { - d_cnfProof->ensureLiteral( - *it, - true); // This disables preregistration with the theory solver. - } - else - { - d_cnfProof->ensureLiteral( - *it); // Normal method, with theory solver preregisteration. - } - } - } - - // From the clauses, compute the atoms (atomic theory predicates in - // assertions and lemmas). - d_cnfProof->collectAtomsForClauses(used_inputs, atoms); - d_cnfProof->collectAtomsForClauses(used_lemmas, atoms); - - // collects the atoms in the assertions - Debug("pf::pm") << std::endl - << "LFSCProof::toStream: Colleting atoms from assertions " - << used_assertions << "\n" - << push; - for (TNode used_assertion : used_assertions) - { - collectAtoms(used_assertion, atoms, d_cnfProof); - } - Debug("pf::pm") << pop; - - std::set<Node>::iterator atomIt; - Debug("pf::pm") << std::endl - << "Dumping atoms from lemmas, inputs and assertions: " - << std::endl - << std::endl; - for (atomIt = atoms.begin(); atomIt != atoms.end(); ++atomIt) - { - Debug("pf::pm") << "\tAtom: " << *atomIt << std::endl; - } - } - - smt::SmtScope scope(d_smtEngine); - ProofLetMap globalLetMap; - std::ostringstream paren; - { - CodeTimer declTimer{ - ProofManager::currentPM()->getStats().d_proofDeclarationsTime}; - out << "(check\n"; - paren << ")"; - out << " ;; Declarations\n"; - - // declare the theory atoms - Debug("pf::pm") << "LFSCProof::toStream: registering terms:" << std::endl; - for (std::set<Node>::const_iterator it = atoms.begin(); it != atoms.end(); ++it) - { - Debug("pf::pm") << "\tTerm: " << (*it).toExpr() << std::endl; - d_theoryProof->registerTerm((*it).toExpr()); - } - - Debug("pf::pm") << std::endl - << "Term registration done!" << std::endl - << std::endl; - - Debug("pf::pm") << std::endl - << "LFSCProof::toStream: starting to print assertions" - << std::endl; - - // print out all the original assertions - d_theoryProof->registerTermsFromAssertions(); - d_theoryProof->printSortDeclarations(out, paren); - d_theoryProof->printTermDeclarations(out, paren); - d_theoryProof->printAssertions(out, paren); - - Debug("pf::pm") << std::endl - << "LFSCProof::toStream: print assertions DONE" - << std::endl; - - out << "(: (holds cln)\n\n"; - paren << ")"; - - // Have the theory proofs print deferred declarations, e.g. for skolem - // variables. - out << " ;; Printing deferred declarations \n\n"; - d_theoryProof->printDeferredDeclarations(out, paren); - - out << "\n ;; Printing the global let map"; - d_theoryProof->finalizeBvConflicts(used_lemmas, out); - ProofManager::getBitVectorProof()->calculateAtomsInBitblastingProof(); - if (options::lfscLetification()) - { - ProofManager::currentPM()->printGlobalLetMap( - atoms, globalLetMap, out, paren); - } - - out << " ;; Printing aliasing declarations \n\n"; - d_theoryProof->printAliasingDeclarations(out, paren, globalLetMap); - - out << " ;; Rewrites for Lemmas \n"; - d_theoryProof->printLemmaRewrites(rewrites, out, paren); - - // print trust that input assertions are their preprocessed form - printPreprocessedAssertions(used_assertions, out, paren, globalLetMap); - } - - { - CodeTimer cnfProofTimer{ - ProofManager::currentPM()->getStats().d_cnfProofTime}; - // print mapping between theory atoms and internal SAT variables - out << ";; Printing mapping from preprocessed assertions into atoms \n"; - d_cnfProof->printAtomMapping(atoms, out, paren, globalLetMap); - - Debug("pf::pm") << std::endl - << "Printing cnf proof for clauses" << std::endl; - - IdToSatClause::const_iterator cl_it = used_inputs.begin(); - // print CNF conversion proof for each clause - for (; cl_it != used_inputs.end(); ++cl_it) - { - d_cnfProof->printCnfProofForClause( - cl_it->first, cl_it->second, out, paren); - } - } - - { - CodeTimer theoryLemmaTimer{ - ProofManager::currentPM()->getStats().d_theoryLemmaTime}; - Debug("pf::pm") << std::endl - << "Printing cnf proof for clauses DONE" << std::endl; - - Debug("pf::pm") << "Proof manager: printing theory lemmas" << std::endl; - d_theoryProof->printTheoryLemmas(used_lemmas, out, paren, globalLetMap); - Debug("pf::pm") << "Proof manager: printing theory lemmas DONE!" - << std::endl; - } - - { - CodeTimer finalProofTimer{ - ProofManager::currentPM()->getStats().d_finalProofTime}; - out << ";; Printing final unsat proof \n"; - if (options::bitblastMode() == options::BitblastMode::EAGER - && ProofManager::getBitVectorProof()) - { - ProofManager::getBitVectorProof()->printEmptyClauseProof(out, paren); - } - else - { - // print actual resolution proof - proof::LFSCProofPrinter::printResolutions(d_satProof, out, paren); - proof::LFSCProofPrinter::printResolutionEmptyClause( - d_satProof, out, paren); - } - } - - out << paren.str(); - out << "\n;;\n"; -} - -void LFSCProof::printPreprocessedAssertions(const NodeSet& assertions, - std::ostream& os, - std::ostream& paren, - ProofLetMap& globalLetMap) const -{ - os << "\n ;; In the preprocessor we trust \n"; - NodeSet::const_iterator it = assertions.begin(); - NodeSet::const_iterator end = assertions.end(); - - Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions starting" << std::endl; - - if (options::fewerPreprocessingHoles()) { - // Check for assertions that did not get rewritten, and update the printing filter. - checkUnrewrittenAssertion(assertions); - - // For the remaining assertions, bind them to input assertions. - for (; it != end; ++it) { - // Rewrite preprocessing step if it cannot be eliminated - if (!ProofManager::currentPM()->have_input_assertion((*it).toExpr())) { - os << "(th_let_pf _ (trust_f (iff "; - - Expr inputAssertion; - - if (((*it).isConst() && *it == NodeManager::currentNM()->mkConst<bool>(true)) || - ((*it).getKind() == kind::NOT && (*it)[0] == NodeManager::currentNM()->mkConst<bool>(false))) { - inputAssertion = NodeManager::currentNM()->mkConst<bool>(true).toExpr(); - } else { - // Figure out which input assertion led to this assertion - ExprSet inputAssertions; - ProofManager::currentPM()->traceDeps(*it, &inputAssertions); - - Debug("pf::pm") << "Original assertions for " << *it << " are: " << std::endl; - - ProofManager::assertions_iterator assertionIt; - for (assertionIt = inputAssertions.begin(); assertionIt != inputAssertions.end(); ++assertionIt) { - Debug("pf::pm") << "\t" << *assertionIt << std::endl; - } - - if (inputAssertions.size() == 0) { - Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions: Count NOT find the assertion that caused this PA. Picking an arbitrary one..." << std::endl; - // For now just use the first assertion... - inputAssertion = *(ProofManager::currentPM()->begin_assertions()); - } else { - if (inputAssertions.size() != 1) { - Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions: Attention: more than one original assertion was found. Picking just one." << std::endl; - } - inputAssertion = *inputAssertions.begin(); - } - } - - if (!ProofManager::currentPM()->have_input_assertion(inputAssertion)) { - // The thing returned by traceDeps does not appear in the input assertions... - Debug("pf::pm") << "LFSCProof::printPreprocessedAssertions: Count NOT find the assertion that caused this PA. Picking an arbitrary one..." << std::endl; - // For now just use the first assertion... - inputAssertion = *(ProofManager::currentPM()->begin_assertions()); - } - - Debug("pf::pm") << "Original assertion for " << *it - << " is: " - << inputAssertion - << ", AKA " - << ProofManager::currentPM()->getInputFormulaName(inputAssertion) - << std::endl; - - ProofManager::currentPM()->getTheoryProofEngine()->printTheoryTerm(inputAssertion, os, globalLetMap); - os << " "; - ProofManager::currentPM()->printTrustedTerm(*it, os, globalLetMap); - os << "))"; - os << "(\\ "<< ProofManager::getPreprocessedAssertionName(*it, "") << "\n"; - paren << "))"; - - std::ostringstream rewritten; - - rewritten << "(or_elim_1 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << ProofManager::currentPM()->getInputFormulaName(inputAssertion); - rewritten << ") (iff_elim_1 _ _ "; - rewritten << ProofManager::getPreprocessedAssertionName(*it, ""); - rewritten << "))"; - - ProofManager::currentPM()->addAssertionFilter(*it, rewritten.str()); - } - } - } else { - for (; it != end; ++it) { - os << "(th_let_pf _ "; - - //TODO - os << "(trust_f "; - ProofManager::currentPM()->printTrustedTerm(*it, os, globalLetMap); - os << ") "; - - os << "(\\ "<< ProofManager::getPreprocessedAssertionName(*it, "") << "\n"; - paren << "))"; - } - } - - os << "\n"; -} - -void LFSCProof::checkUnrewrittenAssertion(const NodeSet& rewrites) const -{ - Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion starting" << std::endl; - - NodeSet::const_iterator rewrite; - for (rewrite = rewrites.begin(); rewrite != rewrites.end(); ++rewrite) { - Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion: handling " << *rewrite << std::endl; - if (ProofManager::currentPM()->have_input_assertion((*rewrite).toExpr())) { - Assert( - ProofManager::currentPM()->have_input_assertion((*rewrite).toExpr())); - Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion: this assertion was NOT rewritten!" << std::endl - << "\tAdding filter: " - << ProofManager::getPreprocessedAssertionName(*rewrite, "") - << " --> " - << ProofManager::currentPM()->getInputFormulaName((*rewrite).toExpr()) - << std::endl; - ProofManager::currentPM()->addAssertionFilter(*rewrite, - ProofManager::currentPM()->getInputFormulaName((*rewrite).toExpr())); - } else { - Debug("pf::pm") << "LFSCProof::checkUnrewrittenAssertion: this assertion WAS rewritten! " << *rewrite << std::endl; - } - } -} - -//---from Morgan--- - -bool ProofManager::hasOp(TNode n) const { - return d_bops.find(n) != d_bops.end(); -} - -Node ProofManager::lookupOp(TNode n) const { - std::map<Node, Node>::const_iterator i = d_bops.find(n); - Assert(i != d_bops.end()); - return (*i).second; -} - -Node ProofManager::mkOp(TNode n) { - Trace("mgd-pm-mkop") << "MkOp : " << n << " " << n.getKind() << std::endl; - if(n.getKind() != kind::BUILTIN) { - return n; - } - - Node& op = d_ops[n]; - if(op.isNull()) { - Assert((n.getConst<Kind>() == kind::SELECT) - || (n.getConst<Kind>() == kind::STORE)); - - Debug("mgd-pm-mkop") << "making an op for " << n << "\n"; - - std::stringstream ss; - ss << n; - std::string s = ss.str(); - Debug("mgd-pm-mkop") << " : " << s << std::endl; - std::vector<TypeNode> v; - v.push_back(NodeManager::currentNM()->integerType()); - if(n.getConst<Kind>() == kind::SELECT) { - v.push_back(NodeManager::currentNM()->integerType()); - v.push_back(NodeManager::currentNM()->integerType()); - } else if(n.getConst<Kind>() == kind::STORE) { - v.push_back(NodeManager::currentNM()->integerType()); - v.push_back(NodeManager::currentNM()->integerType()); - v.push_back(NodeManager::currentNM()->integerType()); - } - TypeNode type = NodeManager::currentNM()->mkFunctionType(v); - Debug("mgd-pm-mkop") << "typenode is: " << type << "\n"; - op = NodeManager::currentNM()->mkSkolem(s, type, " ignore", NodeManager::SKOLEM_NO_NOTIFY); - d_bops[op] = n; - } - Debug("mgd-pm-mkop") << "returning the op: " << op << "\n"; - return op; -} -//---end from Morgan--- - -bool ProofManager::wasPrinted(const Type& type) const { - return d_printedTypes.find(type) != d_printedTypes.end(); -} - -void ProofManager::markPrinted(const Type& type) { - d_printedTypes.insert(type); -} - -void ProofManager::addRewriteFilter(const std::string &original, const std::string &substitute) { - d_rewriteFilters[original] = substitute; -} - -bool ProofManager::haveRewriteFilter(TNode lit) { - std::string litName = getLitName(currentPM()->d_cnfProof->getLiteral(lit)); - return d_rewriteFilters.find(litName) != d_rewriteFilters.end(); -} - -void ProofManager::clearRewriteFilters() { - d_rewriteFilters.clear(); -} - -std::ostream& operator<<(std::ostream& out, CVC4::ProofRule k) { - switch(k) { - case RULE_GIVEN: - out << "RULE_GIVEN"; - break; - case RULE_DERIVED: - out << "RULE_DERIVED"; - break; - case RULE_RECONSTRUCT: - out << "RULE_RECONSTRUCT"; - break; - case RULE_TRUST: - out << "RULE_TRUST"; - break; - case RULE_INVALID: - out << "RULE_INVALID"; - break; - case RULE_CONFLICT: - out << "RULE_CONFLICT"; - break; - case RULE_TSEITIN: - out << "RULE_TSEITIN"; - break; - case RULE_SPLIT: - out << "RULE_SPLIT"; - break; - case RULE_ARRAYS_EXT: - out << "RULE_ARRAYS"; - break; - case RULE_ARRAYS_ROW: - out << "RULE_ARRAYS"; - break; - default: - out << "ProofRule Unknown! [" << unsigned(k) << "]"; - } - - return out; -} - -void ProofManager::registerRewrite(unsigned ruleId, Node original, Node result){ - Assert(currentPM()->d_theoryProof != NULL); - currentPM()->d_rewriteLog.push_back(RewriteLogEntry(ruleId, original, result)); -} - -void ProofManager::clearRewriteLog() { - Assert(currentPM()->d_theoryProof != NULL); - currentPM()->d_rewriteLog.clear(); -} - -std::vector<RewriteLogEntry> ProofManager::getRewriteLog() { - return currentPM()->d_rewriteLog; -} - -void ProofManager::dumpRewriteLog() const { - Debug("pf::rr") << "Dumpign rewrite log:" << std::endl; - - for (unsigned i = 0; i < d_rewriteLog.size(); ++i) { - Debug("pf::rr") << "\tRule " << d_rewriteLog[i].getRuleId() - << ": " - << d_rewriteLog[i].getOriginal() - << " --> " - << d_rewriteLog[i].getResult() << std::endl; - } -} - -void bind(Expr term, ProofLetMap& map, Bindings& letOrder) { - ProofLetMap::iterator it = map.find(term); - if (it != map.end()) - return; - - for (unsigned i = 0; i < term.getNumChildren(); ++i) - bind(term[i], map, letOrder); - - // Special case: chain operators. If we have and(a,b,c), it will be prineted as and(a,and(b,c)). - // The subterm and(b,c) may repeat elsewhere, so we need to bind it, too. - Kind k = term.getKind(); - if (((k == kind::OR) || (k == kind::AND)) && term.getNumChildren() > 2) { - Node currentExpression = term[term.getNumChildren() - 1]; - for (int i = term.getNumChildren() - 2; i >= 0; --i) { - NodeBuilder<> builder(k); - builder << term[i]; - builder << currentExpression.toExpr(); - currentExpression = builder; - bind(currentExpression.toExpr(), map, letOrder); - } - } else { - unsigned newId = ProofLetCount::newId(); - ProofLetCount letCount(newId); - map[term] = letCount; - letOrder.push_back(LetOrderElement(term, newId)); - } -} - -void ProofManager::printGlobalLetMap(std::set<Node>& atoms, - ProofLetMap& letMap, - std::ostream& out, - std::ostringstream& paren) { - Bindings letOrder; - std::set<Node>::const_iterator atom; - for (atom = atoms.begin(); atom != atoms.end(); ++atom) { - bind(atom->toExpr(), letMap, letOrder); - } - - // TODO: give each theory a chance to add atoms. For now, just query BV directly... - const std::set<Node>* additionalAtoms = ProofManager::getBitVectorProof()->getAtomsInBitblastingProof(); - for (atom = additionalAtoms->begin(); atom != additionalAtoms->end(); ++atom) { - bind(atom->toExpr(), letMap, letOrder); - } - - for (unsigned i = 0; i < letOrder.size(); ++i) { - Expr currentExpr = letOrder[i].expr; - unsigned letId = letOrder[i].id; - ProofLetMap::iterator it = letMap.find(currentExpr); - Assert(it != letMap.end()); - out << "\n(@ let" << letId << " "; - d_theoryProof->printBoundTerm(currentExpr, out, letMap); - paren << ")"; - it->second.increment(); - } - - out << std::endl << std::endl; -} - -void ProofManager::ensureLiteral(Node node) { - d_cnfProof->ensureLiteral(node); -} -void ProofManager::printTrustedTerm(Node term, - std::ostream& os, - ProofLetMap& globalLetMap) -{ - TheoryProofEngine* tpe = ProofManager::currentPM()->getTheoryProofEngine(); - if (tpe->printsAsBool(term)) os << "(p_app "; - tpe->printTheoryTerm(term.toExpr(), os, globalLetMap); - if (tpe->printsAsBool(term)) os << ")"; -} - -ProofManager::ProofManagerStatistics::ProofManagerStatistics() - : d_proofProductionTime("proof::ProofManager::proofProductionTime"), - d_theoryLemmaTime( - "proof::ProofManager::proofProduction::theoryLemmaTime"), - d_skeletonProofTraceTime( - "proof::ProofManager::proofProduction::skeletonProofTraceTime"), - d_proofDeclarationsTime( - "proof::ProofManager::proofProduction::proofDeclarationsTime"), - d_cnfProofTime("proof::ProofManager::proofProduction::cnfProofTime"), - d_finalProofTime("proof::ProofManager::proofProduction::finalProofTime") -{ - smtStatisticsRegistry()->registerStat(&d_proofProductionTime); - smtStatisticsRegistry()->registerStat(&d_theoryLemmaTime); - smtStatisticsRegistry()->registerStat(&d_skeletonProofTraceTime); - smtStatisticsRegistry()->registerStat(&d_proofDeclarationsTime); - smtStatisticsRegistry()->registerStat(&d_cnfProofTime); - smtStatisticsRegistry()->registerStat(&d_finalProofTime); -} - -ProofManager::ProofManagerStatistics::~ProofManagerStatistics() -{ - smtStatisticsRegistry()->unregisterStat(&d_proofProductionTime); - smtStatisticsRegistry()->unregisterStat(&d_theoryLemmaTime); - smtStatisticsRegistry()->unregisterStat(&d_skeletonProofTraceTime); - smtStatisticsRegistry()->unregisterStat(&d_proofDeclarationsTime); - smtStatisticsRegistry()->unregisterStat(&d_cnfProofTime); - smtStatisticsRegistry()->unregisterStat(&d_finalProofTime); -} - } /* CVC4 namespace */ diff --git a/src/proof/proof_manager.h b/src/proof/proof_manager.h index 45bde4dcb..3013c9b55 100644 --- a/src/proof/proof_manager.h +++ b/src/proof/proof_manager.h @@ -28,59 +28,27 @@ #include "context/cdhashset.h" #include "expr/node.h" #include "proof/clause_id.h" -#include "proof/proof.h" -#include "proof/proof_utils.h" -#include "proof/skolemization_manager.h" -#include "theory/logic_info.h" -#include "theory/substitutions.h" -#include "util/proof.h" #include "util/statistics_registry.h" namespace CVC4 { -class SmtGlobals; - // forward declarations namespace Minisat { class Solver; }/* Minisat namespace */ -namespace BVMinisat { - class Solver; -}/* BVMinisat namespace */ - namespace prop { class CnfStream; }/* CVC4::prop namespace */ class SmtEngine; -const ClauseId ClauseIdEmpty(-1); -const ClauseId ClauseIdUndef(-2); -const ClauseId ClauseIdError(-3); - template <class Solver> class TSatProof; typedef TSatProof< CVC4::Minisat::Solver> CoreSatProof; class CnfProof; -class RewriterProof; -class TheoryProofEngine; -class TheoryProof; -class UFProof; -class ArithProof; -class ArrayProof; - -namespace proof { -class ResolutionBitVectorProof; -} - -template <class Solver> class LFSCSatProof; -typedef TSatProof<CVC4::Minisat::Solver> CoreSatProof; -class LFSCCnfProof; -class LFSCTheoryProofEngine; -class LFSCUFProof; -class LFSCRewriterProof; +typedef TSatProof<CVC4::Minisat::Solver> CoreSatProof; namespace prop { typedef uint64_t SatVariable; @@ -88,291 +56,72 @@ namespace prop { typedef std::vector<SatLiteral> SatClause; }/* CVC4::prop namespace */ -// different proof modes -enum ProofFormat { - LFSC, - NATIVE -};/* enum ProofFormat */ - -std::string append(const std::string& str, uint64_t num); - typedef std::unordered_map<ClauseId, prop::SatClause*> IdToSatClause; typedef context::CDHashSet<Expr, ExprHashFunction> CDExprSet; -typedef std::unordered_map<Node, std::vector<Node>, NodeHashFunction> NodeToNodes; typedef context::CDHashMap<Node, std::vector<Node>, NodeHashFunction> CDNodeToNodes; typedef std::unordered_set<ClauseId> IdHashSet; -enum ProofRule { - RULE_GIVEN, /* input assertion */ - RULE_DERIVED, /* a "macro" rule */ - RULE_RECONSTRUCT, /* prove equivalence using another method */ - RULE_TRUST, /* trust without evidence (escape hatch until proofs are fully supported) */ - RULE_INVALID, /* assert-fail if this is ever needed in proof; use e.g. for split lemmas */ - RULE_CONFLICT, /* re-construct as a conflict */ - RULE_TSEITIN, /* Tseitin CNF transformation */ - RULE_SPLIT, /* A splitting lemma of the form a v ~ a*/ - - RULE_ARRAYS_EXT, /* arrays, extensional */ - RULE_ARRAYS_ROW, /* arrays, read-over-write */ -};/* enum ProofRules */ - -class RewriteLogEntry { -public: - RewriteLogEntry(unsigned ruleId, Node original, Node result) - : d_ruleId(ruleId), d_original(original), d_result(result) { - } - - unsigned getRuleId() const { - return d_ruleId; - } - - Node getOriginal() const { - return d_original; - } - - Node getResult() const { - return d_result; - } - -private: - unsigned d_ruleId; - Node d_original; - Node d_result; -}; - class ProofManager { context::Context* d_context; std::unique_ptr<CoreSatProof> d_satProof; std::unique_ptr<CnfProof> d_cnfProof; - std::unique_ptr<TheoryProofEngine> d_theoryProof; // information that will need to be shared across proofs - ExprSet d_inputFormulas; - std::map<Expr, std::string> d_inputFormulaToName; CDExprSet d_inputCoreFormulas; CDExprSet d_outputCoreFormulas; - SkolemizationManager d_skolemizationManager; - int d_nextId; - std::unique_ptr<Proof> d_fullProof; - ProofFormat d_format; // used for now only in debug builds - CDNodeToNodes d_deps; - std::set<Type> d_printedTypes; - - std::map<std::string, std::string> d_rewriteFilters; - std::map<Node, std::string> d_assertionFilters; - - std::vector<RewriteLogEntry> d_rewriteLog; - -protected: - LogicInfo d_logic; - public: - ProofManager(context::Context* context, ProofFormat format = LFSC); - ~ProofManager(); - - static ProofManager* currentPM(); - - // initialization - void initSatProof(Minisat::Solver* solver); - void initCnfProof(CVC4::prop::CnfStream* cnfStream, context::Context* ctx); - void initTheoryProofEngine(); - - // getting various proofs - static const Proof& getProof(SmtEngine* smt); - static CoreSatProof* getSatProof(); - static CnfProof* getCnfProof(); - static TheoryProofEngine* getTheoryProofEngine(); - static TheoryProof* getTheoryProof( theory::TheoryId id ); - static UFProof* getUfProof(); - static proof::ResolutionBitVectorProof* getBitVectorProof(); - static ArrayProof* getArrayProof(); - static ArithProof* getArithProof(); - - static SkolemizationManager *getSkolemizationManager(); - - // iterators over data shared by proofs - typedef ExprSet::const_iterator assertions_iterator; - - // iterate over the assertions (these are arbitrary boolean formulas) - assertions_iterator begin_assertions() const { - return d_inputFormulas.begin(); - } - assertions_iterator end_assertions() const { return d_inputFormulas.end(); } - size_t num_assertions() const { return d_inputFormulas.size(); } - bool have_input_assertion(const Expr& assertion) { - return d_inputFormulas.find(assertion) != d_inputFormulas.end(); - } - - void ensureLiteral(Node node); - -//---from Morgan--- - Node mkOp(TNode n); - Node lookupOp(TNode n) const; - bool hasOp(TNode n) const; - - std::map<Node, Node> d_ops; - std::map<Node, Node> d_bops; -//---end from Morgan--- - - - // variable prefixes - static std::string getInputClauseName(ClauseId id, const std::string& prefix = ""); - static std::string getLemmaClauseName(ClauseId id, const std::string& prefix = ""); - static std::string getLemmaName(ClauseId id, const std::string& prefix = ""); - static std::string getLearntClauseName(ClauseId id, const std::string& prefix = ""); - static std::string getPreprocessedAssertionName(Node node, const std::string& prefix = ""); - static std::string getAssertionName(Node node, const std::string& prefix = ""); - static std::string getInputFormulaName(const Expr& expr); - - static std::string getVarName(prop::SatVariable var, const std::string& prefix = ""); - static std::string getAtomName(prop::SatVariable var, const std::string& prefix = ""); - static std::string getAtomName(TNode atom, const std::string& prefix = ""); - static std::string getLitName(prop::SatLiteral lit, const std::string& prefix = ""); - static std::string getLitName(TNode lit, const std::string& prefix = ""); - static bool hasLitName(TNode lit); - - // for SMT variable names that have spaces and other things - static std::string sanitize(TNode var); - - // wrap term with (p_app ... ) if the term is printed as a boolean, and print - // used for "trust" assertions - static void printTrustedTerm(Node term, - std::ostream& os, - ProofLetMap& globalLetMap); - - /** Add proof assertion - unlike addCoreAssertion this is post definition expansion **/ - void addAssertion(Expr formula); - - /** Public unsat core methods **/ - void addCoreAssertion(Expr formula); - - void addDependence(TNode n, TNode dep); - void addUnsatCore(Expr formula); - - // trace dependences back to unsat core - void traceDeps(TNode n, ExprSet* coreAssertions); - void traceDeps(TNode n, CDExprSet* coreAssertions); - void traceUnsatCore(); - - typedef CDExprSet::const_iterator output_core_iterator; - - output_core_iterator begin_unsat_core() const { return d_outputCoreFormulas.begin(); } - output_core_iterator end_unsat_core() const { return d_outputCoreFormulas.end(); } - size_t size_unsat_core() const { return d_outputCoreFormulas.size(); } - std::vector<Expr> extractUnsatCore(); - - bool unsatCoreAvailable() const; - void getLemmasInUnsatCore(theory::TheoryId theory, std::vector<Node> &lemmas); - Node getWeakestImplicantInUnsatCore(Node lemma); - - int nextId() { return d_nextId++; } - - void setLogic(const LogicInfo& logic); - const std::string getLogic() const { return d_logic.getLogicString(); } - LogicInfo & getLogicInfo() { return d_logic; } - - void markPrinted(const Type& type); - bool wasPrinted(const Type& type) const; - - void addRewriteFilter(const std::string &original, const std::string &substitute); - void clearRewriteFilters(); - bool haveRewriteFilter(TNode lit); - - void addAssertionFilter(const Node& node, const std::string& rewritten); - - static void registerRewrite(unsigned ruleId, Node original, Node result); - static void clearRewriteLog(); - - std::vector<RewriteLogEntry> getRewriteLog(); - void dumpRewriteLog() const; + ProofManager(context::Context* context); + ~ProofManager(); - void printGlobalLetMap(std::set<Node>& atoms, - ProofLetMap& letMap, - std::ostream& out, - std::ostringstream& paren); - - struct ProofManagerStatistics - { - ProofManagerStatistics(); - ~ProofManagerStatistics(); + static ProofManager* currentPM(); - /** - * Time spent producing proofs (i.e. generating the proof from the logging - * information) - */ - TimerStat d_proofProductionTime; + // initialization + void initSatProof(Minisat::Solver* solver); + void initCnfProof(CVC4::prop::CnfStream* cnfStream, context::Context* ctx); - /** - * Time spent printing proofs of theory lemmas - */ - TimerStat d_theoryLemmaTime; + // getting various proofs + static CoreSatProof* getSatProof(); + static CnfProof* getCnfProof(); - /** - * Time spent tracing the proof of the boolean skeleton - * (e.g. figuring out which assertions are needed, etc.) - */ - TimerStat d_skeletonProofTraceTime; + /** Public unsat core methods **/ + void addCoreAssertion(Expr formula); - /** - * Time spent processing and printing declarations in the proof - */ - TimerStat d_proofDeclarationsTime; + void addDependence(TNode n, TNode dep); + void addUnsatCore(Expr formula); - /** - * Time spent printing the CNF proof - */ - TimerStat d_cnfProofTime; + // trace dependences back to unsat core + void traceDeps(TNode n, CDExprSet* coreAssertions); + void traceUnsatCore(); - /** - * Time spent printing the final proof of UNSAT - */ - TimerStat d_finalProofTime; + typedef CDExprSet::const_iterator output_core_iterator; - }; /* struct ProofManagerStatistics */ + output_core_iterator begin_unsat_core() const + { + return d_outputCoreFormulas.begin(); + } + output_core_iterator end_unsat_core() const + { + return d_outputCoreFormulas.end(); + } + size_t size_unsat_core() const { return d_outputCoreFormulas.size(); } + std::vector<Expr> extractUnsatCore(); - ProofManagerStatistics& getStats() { return d_stats; } + bool unsatCoreAvailable() const; + void getLemmasInUnsatCore(std::vector<Node>& lemmas); - private: - void constructSatProof(); - std::set<Node> satClauseToNodeSet(prop::SatClause* clause); + int nextId() { return d_nextId++; } - ProofManagerStatistics d_stats; +private: + void constructSatProof(); };/* class ProofManager */ -class LFSCProof : public Proof -{ - public: - LFSCProof(SmtEngine* smtEngine, - CoreSatProof* sat, - LFSCCnfProof* cnf, - LFSCTheoryProofEngine* theory); - ~LFSCProof() override {} - void toStream(std::ostream& out) const override; - void toStream(std::ostream& out, const ProofLetMap& map) const override; - - private: - // FIXME: hack until we get preprocessing - void printPreprocessedAssertions(const NodeSet& assertions, - std::ostream& os, - std::ostream& paren, - ProofLetMap& globalLetMap) const; - - void checkUnrewrittenAssertion(const NodeSet& assertions) const; - - CoreSatProof* d_satProof; - LFSCCnfProof* d_cnfProof; - LFSCTheoryProofEngine* d_theoryProof; - SmtEngine* d_smtEngine; -}; /* class LFSCProof */ - -std::ostream& operator<<(std::ostream& out, CVC4::ProofRule k); }/* CVC4 namespace */ diff --git a/src/proof/proof_output_channel.cpp b/src/proof/proof_output_channel.cpp deleted file mode 100644 index 88467aea6..000000000 --- a/src/proof/proof_output_channel.cpp +++ /dev/null @@ -1,102 +0,0 @@ -/********************* */ -/*! \file proof_output_channel.cpp - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Tim King, Liana Hadarean - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "proof/proof_output_channel.h" - -#include "base/check.h" -#include "theory/term_registration_visitor.h" -#include "theory/valuation.h" - -namespace CVC4 { - -ProofOutputChannel::ProofOutputChannel() : d_conflict(), d_proof(nullptr) {} -const Proof& ProofOutputChannel::getConflictProof() const -{ - Assert(hasConflict()); - return *d_proof; -} - -void ProofOutputChannel::conflict(TNode n, std::unique_ptr<Proof> pf) -{ - Trace("pf::tp") << "ProofOutputChannel: CONFLICT: " << n << std::endl; - Assert(!hasConflict()); - Assert(!d_proof); - d_conflict = n; - d_proof = std::move(pf); - Assert(hasConflict()); - Assert(d_proof); -} - -bool ProofOutputChannel::propagate(TNode x) { - Trace("pf::tp") << "ProofOutputChannel: got a propagation: " << x - << std::endl; - d_propagations.insert(x); - return true; -} - -theory::LemmaStatus ProofOutputChannel::lemma(TNode n, - ProofRule rule, - theory::LemmaProperty p) -{ - Trace("pf::tp") << "ProofOutputChannel: new lemma: " << n << std::endl; - // TODO(#1231): We should transition to supporting multiple lemmas. The - // following assertion cannot be enabled due to - // "test/regress/regress0/arrays/swap_t1_np_nf_ai_00005_007.cvc.smt". - // Assert( - // d_lemma.isNull()) << - // "Multiple calls to ProofOutputChannel::lemma() are not supported."; - d_lemma = n; - return theory::LemmaStatus(TNode::null(), 0); -} - -theory::LemmaStatus ProofOutputChannel::splitLemma(TNode, bool) { - AlwaysAssert(false); - return theory::LemmaStatus(TNode::null(), 0); -} - -void ProofOutputChannel::requirePhase(TNode n, bool b) { - Debug("pf::tp") << "ProofOutputChannel::requirePhase called" << std::endl; - Trace("pf::tp") << "requirePhase " << n << " " << b << std::endl; -} - -void ProofOutputChannel::setIncomplete() { - Debug("pf::tp") << "ProofOutputChannel::setIncomplete called" << std::endl; - AlwaysAssert(false); -} - - -MyPreRegisterVisitor::MyPreRegisterVisitor(theory::Theory* theory) - : d_theory(theory) - , d_visited() { -} - -bool MyPreRegisterVisitor::alreadyVisited(TNode current, TNode parent) { - return d_visited.find(current) != d_visited.end(); -} - -void MyPreRegisterVisitor::visit(TNode current, TNode parent) { - d_theory->preRegisterTerm(current); - d_visited.insert(current); -} - -void MyPreRegisterVisitor::start(TNode node) { -} - -void MyPreRegisterVisitor::done(TNode node) { -} - -} /* namespace CVC4 */ diff --git a/src/proof/proof_output_channel.h b/src/proof/proof_output_channel.h deleted file mode 100644 index b68abd44b..000000000 --- a/src/proof/proof_output_channel.h +++ /dev/null @@ -1,79 +0,0 @@ -/********************* */ -/*! \file proof_output_channel.h - ** \verbatim - ** Top contributors (to current version): - ** Tim King, Guy Katz, Liana Hadarean - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF_OUTPUT_CHANNEL_H -#define CVC4__PROOF_OUTPUT_CHANNEL_H - -#include <memory> -#include <set> -#include <unordered_set> - -#include "expr/node.h" -#include "theory/output_channel.h" -#include "theory/theory.h" -#include "util/proof.h" - -namespace CVC4 { - -class ProofOutputChannel : public theory::OutputChannel { - public: - ProofOutputChannel(); - ~ProofOutputChannel() override {} - - /** - * This may be called at most once per ProofOutputChannel. - * Requires that `n` and `pf` are non-null. - */ - void conflict(TNode n, std::unique_ptr<Proof> pf) override; - bool propagate(TNode x) override; - theory::LemmaStatus lemma(TNode n, - ProofRule rule, - theory::LemmaProperty p) override; - theory::LemmaStatus splitLemma(TNode, bool) override; - void requirePhase(TNode n, bool b) override; - void setIncomplete() override; - - /** Has conflict() has been called? */ - bool hasConflict() const { return !d_conflict.isNull(); } - - /** - * Returns the proof passed into the conflict() call. - * Requires hasConflict() to hold. - */ - const Proof& getConflictProof() const; - Node getLastLemma() const { return d_lemma; } - - private: - Node d_conflict; - std::unique_ptr<Proof> d_proof; - Node d_lemma; - std::set<Node> d_propagations; -}; /* class ProofOutputChannel */ - -class MyPreRegisterVisitor { - theory::Theory* d_theory; - std::unordered_set<TNode, TNodeHashFunction> d_visited; -public: - typedef void return_type; - MyPreRegisterVisitor(theory::Theory* theory); - bool alreadyVisited(TNode current, TNode parent); - void visit(TNode current, TNode parent); - void start(TNode node); - void done(TNode node); -}; /* class MyPreRegisterVisitor */ - -} /* CVC4 namespace */ - -#endif /* CVC4__PROOF_OUTPUT_CHANNEL_H */ diff --git a/src/proof/proof_utils.cpp b/src/proof/proof_utils.cpp deleted file mode 100644 index cad56db6a..000000000 --- a/src/proof/proof_utils.cpp +++ /dev/null @@ -1,126 +0,0 @@ -/********************* */ -/*! \file proof_utils.cpp - ** \verbatim - ** Top contributors (to current version): - ** Liana Hadarean, Andrew Reynolds, Guy Katz - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "proof/proof_utils.h" -#include "theory/theory.h" - -namespace CVC4 { -namespace utils { - -std::string toLFSCKind(Kind kind) { - switch(kind) { - // core kinds - case kind::OR : return "or"; - case kind::AND: return "and"; - case kind::XOR: return "xor"; - case kind::EQUAL: return "="; - case kind::IMPLIES: return "impl"; - case kind::NOT: return "not"; - - // bit-vector kinds - case kind::BITVECTOR_AND : - return "bvand"; - case kind::BITVECTOR_OR : - return "bvor"; - case kind::BITVECTOR_XOR : - return "bvxor"; - case kind::BITVECTOR_NAND : - return "bvnand"; - case kind::BITVECTOR_NOR : - return "bvnor"; - case kind::BITVECTOR_XNOR : - return "bvxnor"; - case kind::BITVECTOR_COMP : - return "bvcomp"; - case kind::BITVECTOR_MULT : - return "bvmul"; - case kind::BITVECTOR_PLUS : - return "bvadd"; - case kind::BITVECTOR_SUB : - return "bvsub"; - case kind::BITVECTOR_UDIV : - case kind::BITVECTOR_UDIV_TOTAL : - return "bvudiv"; - case kind::BITVECTOR_UREM : - case kind::BITVECTOR_UREM_TOTAL : - return "bvurem"; - case kind::BITVECTOR_SDIV : - return "bvsdiv"; - case kind::BITVECTOR_SREM : - return "bvsrem"; - case kind::BITVECTOR_SMOD : - return "bvsmod"; - case kind::BITVECTOR_SHL : - return "bvshl"; - case kind::BITVECTOR_LSHR : - return "bvlshr"; - case kind::BITVECTOR_ASHR : - return "bvashr"; - case kind::BITVECTOR_CONCAT : - return "concat"; - case kind::BITVECTOR_NEG : - return "bvneg"; - case kind::BITVECTOR_NOT : - return "bvnot"; - case kind::BITVECTOR_ROTATE_LEFT : - return "rotate_left"; - case kind::BITVECTOR_ROTATE_RIGHT : - return "rotate_right"; - case kind::BITVECTOR_ULT : - return "bvult"; - case kind::BITVECTOR_ULE : - return "bvule"; - case kind::BITVECTOR_UGT : - return "bvugt"; - case kind::BITVECTOR_UGE : - return "bvuge"; - case kind::BITVECTOR_SLT : - return "bvslt"; - case kind::BITVECTOR_SLE : - return "bvsle"; - case kind::BITVECTOR_SGT : - return "bvsgt"; - case kind::BITVECTOR_SGE : - return "bvsge"; - case kind::BITVECTOR_EXTRACT : - return "extract"; - case kind::BITVECTOR_REPEAT : - return "repeat"; - case kind::BITVECTOR_ZERO_EXTEND : - return "zero_extend"; - case kind::BITVECTOR_SIGN_EXTEND : - return "sign_extend"; - default: - Unreachable(); - } -} - -std::string toLFSCKindTerm(Expr node) { - Kind k = node.getKind(); - if( k==kind::EQUAL ){ - if( node[0].getType().isBoolean() ){ - return "iff"; - }else{ - return "="; - } - }else{ - return toLFSCKind( k ); - } -} - -} /* namespace CVC4::utils */ -} /* namespace CVC4 */ diff --git a/src/proof/proof_utils.h b/src/proof/proof_utils.h deleted file mode 100644 index e54edd8b7..000000000 --- a/src/proof/proof_utils.h +++ /dev/null @@ -1,229 +0,0 @@ -/********************* */ -/*! \file proof_utils.h - ** \verbatim - ** Top contributors (to current version): - ** Liana Hadarean, Guy Katz, Dejan Jovanovic - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "cvc4_private.h" - -#pragma once - -#include <set> -#include <sstream> -#include <unordered_set> -#include <vector> - -#include "expr/node_manager.h" - -namespace CVC4 { - -typedef std::unordered_set<Expr, ExprHashFunction> ExprSet; -typedef std::unordered_set<Node, NodeHashFunction> NodeSet; - -typedef std::pair<Node, Node> NodePair; -typedef std::set<NodePair> NodePairSet; - - -class ProofLetCount { -public: - static unsigned counter; - static void resetCounter() { counter = 0; } - static unsigned newId() { return ++counter; } - - unsigned count; - unsigned id; - ProofLetCount() - : count(0) - , id(-1) - {} - - void increment() { ++count; } - ProofLetCount(unsigned i) - : count(1) - , id(i) - {} - - ProofLetCount(const ProofLetCount& other) - : count(other.count) - , id (other.id) - {} - - bool operator==(const ProofLetCount &other) const { - return other.id == id && other.count == count; - } - - ProofLetCount& operator=(const ProofLetCount &rhs) { - if (&rhs == this) return *this; - id = rhs.id; - count = rhs.count; - return *this; - } -}; - -struct LetOrderElement { - Expr expr; - unsigned id; - LetOrderElement(Expr e, unsigned i) - : expr(e) - , id(i) - {} - - LetOrderElement() - : expr() - , id(-1) - {} -}; - -typedef std::vector<LetOrderElement> Bindings; - -namespace utils { - -std::string toLFSCKind(Kind kind); -std::string toLFSCKindTerm(Expr node); - -inline unsigned getExtractHigh(Expr node) { - return node.getOperator().getConst<BitVectorExtract>().d_high; -} - -inline unsigned getExtractLow(Expr node) { - return node.getOperator().getConst<BitVectorExtract>().d_low; -} - -inline unsigned getSize(Type type) { - BitVectorType bv(type); - return bv.getSize(); -} - - -inline unsigned getSize(Expr node) { - Assert(node.getType().isBitVector()); - return getSize(node.getType()); -} - -inline Expr mkTrue() { - return NodeManager::currentNM()->toExprManager()->mkConst<bool>(true); -} - -inline Expr mkFalse() { - return NodeManager::currentNM()->toExprManager()->mkConst<bool>(false); -} - -inline Expr mkExpr(Kind k , Expr expr) { - return NodeManager::currentNM()->toExprManager()->mkExpr(k, expr); -} -inline Expr mkExpr(Kind k , Expr e1, Expr e2) { - return NodeManager::currentNM()->toExprManager()->mkExpr(k, e1, e2); -} -inline Expr mkExpr(Kind k , std::vector<Expr>& children) { - return NodeManager::currentNM()->toExprManager()->mkExpr(k, children); -} - - -inline Expr mkOnes(unsigned size) { - BitVector val = BitVector::mkOnes(size); - return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(val); -} - -inline Expr mkConst(unsigned size, unsigned int value) { - BitVector val(size, value); - return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(val); -} - -inline Expr mkConst(const BitVector& value) { - return NodeManager::currentNM()->toExprManager()->mkConst<BitVector>(value); -} - -inline Expr mkOr(const std::vector<Expr>& nodes) { - std::set<Expr> all; - all.insert(nodes.begin(), nodes.end()); - Assert(all.size() != 0); - - if (all.size() == 1) { - // All the same, or just one - return nodes[0]; - } - - - NodeBuilder<> disjunction(kind::OR); - std::set<Expr>::const_iterator it = all.begin(); - std::set<Expr>::const_iterator it_end = all.end(); - while (it != it_end) { - disjunction << Node::fromExpr(*it); - ++ it; - } - - Node res = disjunction; - return res.toExpr(); -}/* mkOr() */ - - -inline Expr mkAnd(const std::vector<Expr>& conjunctions) { - std::set<Expr> all; - all.insert(conjunctions.begin(), conjunctions.end()); - - if (all.size() == 0) { - return mkTrue(); - } - - if (all.size() == 1) { - // All the same, or just one - return conjunctions[0]; - } - - - NodeBuilder<> conjunction(kind::AND); - std::set<Expr>::const_iterator it = all.begin(); - std::set<Expr>::const_iterator it_end = all.end(); - while (it != it_end) { - conjunction << Node::fromExpr(*it); - ++ it; - } - - Node res = conjunction; - return res.toExpr(); -}/* mkAnd() */ - -inline Expr mkSortedExpr(Kind kind, const std::vector<Expr>& children) { - std::set<Expr> all; - all.insert(children.begin(), children.end()); - - if (all.size() == 0) { - return mkTrue(); - } - - if (all.size() == 1) { - // All the same, or just one - return children[0]; - } - - - NodeBuilder<> res(kind); - std::set<Expr>::const_iterator it = all.begin(); - std::set<Expr>::const_iterator it_end = all.end(); - while (it != it_end) { - res << Node::fromExpr(*it); - ++ it; - } - - return ((Node)res).toExpr(); -}/* mkSortedNode() */ - -inline const bool getBit(Expr expr, unsigned i) { - Assert(i < utils::getSize(expr) && expr.isConst()); - Integer bit = expr.getConst<BitVector>().extract(i, i).getValue(); - return (bit == 1u); -} - -} -} diff --git a/src/proof/resolution_bitvector_proof.cpp b/src/proof/resolution_bitvector_proof.cpp deleted file mode 100644 index d48789f71..000000000 --- a/src/proof/resolution_bitvector_proof.cpp +++ /dev/null @@ -1,523 +0,0 @@ -/********************* */ -/*! \file resolution_bitvector_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Liana Hadarean, Guy Katz - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "proof/resolution_bitvector_proof.h" -#include "options/bv_options.h" -#include "options/proof_options.h" -#include "proof/array_proof.h" -#include "proof/bitvector_proof.h" -#include "proof/clause_id.h" -#include "proof/lfsc_proof_printer.h" -#include "proof/proof_output_channel.h" -#include "proof/proof_utils.h" -#include "proof/sat_proof_implementation.h" -#include "prop/bvminisat/bvminisat.h" -#include "prop/sat_solver_types.h" -#include "theory/bv/bitblast/bitblaster.h" -#include "theory/bv/theory_bv.h" -#include "theory/bv/theory_bv_rewrite_rules.h" - -#include <iostream> -#include <sstream> - -using namespace CVC4::theory; -using namespace CVC4::theory::bv; - -namespace CVC4 { - -namespace proof { - -ResolutionBitVectorProof::ResolutionBitVectorProof( - theory::bv::TheoryBV* bv, TheoryProofEngine* proofEngine) - : BitVectorProof(bv, proofEngine), - d_resolutionProof(), - d_isAssumptionConflict(false) -{ -} - -void ResolutionBitVectorProof::initSatProof(CVC4::BVMinisat::Solver* solver) -{ - Assert(d_resolutionProof == NULL); - d_resolutionProof.reset(new BVSatProof(solver, &d_fakeContext, "bb", true)); -} - -void ResolutionBitVectorProof::initCnfProof(prop::CnfStream* cnfStream, - context::Context* cnf, - prop::SatVariable trueVar, - prop::SatVariable falseVar) -{ - Assert(d_resolutionProof != NULL); - Assert(d_cnfProof == nullptr); - d_cnfProof.reset(new LFSCCnfProof(cnfStream, cnf, "bb")); - - d_cnfProof->registerTrueUnitClause(d_resolutionProof->getTrueUnit()); - d_cnfProof->registerFalseUnitClause(d_resolutionProof->getFalseUnit()); -} - -void ResolutionBitVectorProof::attachToSatSolver(prop::SatSolver& sat_solver) -{ - sat_solver.setResolutionProofLog(this); -} - -BVSatProof* ResolutionBitVectorProof::getSatProof() -{ - Assert(d_resolutionProof != NULL); - return d_resolutionProof.get(); -} - -void ResolutionBitVectorProof::startBVConflict( - CVC4::BVMinisat::Solver::TCRef cr) -{ - d_resolutionProof->startResChain(cr); -} - -void ResolutionBitVectorProof::startBVConflict( - CVC4::BVMinisat::Solver::TLit lit) -{ - d_resolutionProof->startResChain(lit); -} - -void ResolutionBitVectorProof::endBVConflict( - const CVC4::BVMinisat::Solver::TLitVec& confl) -{ - Debug("pf::bv") << "ResolutionBitVectorProof::endBVConflict called" - << std::endl; - - std::vector<Expr> expr_confl; - for (int i = 0; i < confl.size(); ++i) - { - prop::SatLiteral lit = prop::BVMinisatSatSolver::toSatLiteral(confl[i]); - Expr atom = d_cnfProof->getAtom(lit.getSatVariable()).toExpr(); - Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; - expr_confl.push_back(expr_lit); - } - - Expr conflict = utils::mkSortedExpr(kind::OR, expr_confl); - Debug("pf::bv") << "Make conflict for " << conflict << std::endl; - - if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) - { - Debug("pf::bv") << "Abort...already conflict for " << conflict << std::endl; - // This can only happen when we have eager explanations in the bv solver - // if we don't get to propagate p before ~p is already asserted - d_resolutionProof->cancelResChain(); - return; - } - - // we don't need to check for uniqueness in the sat solver then - ClauseId clause_id = d_resolutionProof->registerAssumptionConflict(confl); - d_bbConflictMap[conflict] = clause_id; - d_resolutionProof->endResChain(clause_id); - Debug("pf::bv") << "ResolutionBitVectorProof::endBVConflict id" << clause_id - << " => " << conflict << "\n"; - d_isAssumptionConflict = false; -} - -void ResolutionBitVectorProof::finalizeConflicts(std::vector<Expr>& conflicts) -{ - if (options::bitblastMode() == options::BitblastMode::EAGER) - { - Debug("pf::bv") << "Construct full proof." << std::endl; - d_resolutionProof->constructProof(); - return; - } - - for (unsigned i = 0; i < conflicts.size(); ++i) - { - Expr confl = conflicts[i]; - Debug("pf::bv") << "Finalize conflict #" << i << ": " << confl << std::endl; - - // Special case: if the conflict has a (true) or a (not false) in it, it is - // trivial... - bool ignoreConflict = false; - if ((confl.isConst() && confl.getConst<bool>()) - || (confl.getKind() == kind::NOT && confl[0].isConst() - && !confl[0].getConst<bool>())) - { - ignoreConflict = true; - } - else if (confl.getKind() == kind::OR) - { - for (unsigned k = 0; k < confl.getNumChildren(); ++k) - { - if ((confl[k].isConst() && confl[k].getConst<bool>()) - || (confl[k].getKind() == kind::NOT && confl[k][0].isConst() - && !confl[k][0].getConst<bool>())) - { - ignoreConflict = true; - } - } - } - if (ignoreConflict) - { - Debug("pf::bv") << "Ignoring conflict due to (true) or (not false)" - << std::endl; - continue; - } - - if (d_bbConflictMap.find(confl) != d_bbConflictMap.end()) - { - ClauseId id = d_bbConflictMap[confl]; - d_resolutionProof->collectClauses(id); - } - else - { - // There is no exact match for our conflict, but maybe it is a subset of - // another conflict - ExprToClauseId::const_iterator it; - bool matchFound = false; - for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) - { - Expr possibleMatch = it->first; - if (possibleMatch.getKind() != kind::OR) - { - // This is a single-node conflict. If this node is in the conflict - // we're trying to prove, we have a match. - for (unsigned k = 0; k < confl.getNumChildren(); ++k) - { - if (confl[k] == possibleMatch) - { - matchFound = true; - d_resolutionProof->collectClauses(it->second); - break; - } - } - } - else - { - if (possibleMatch.getNumChildren() > confl.getNumChildren()) continue; - - unsigned k = 0; - bool matching = true; - for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) - { - // j is the index in possibleMatch - // k is the index in confl - while (k < confl.getNumChildren() && confl[k] != possibleMatch[j]) - { - ++k; - } - if (k == confl.getNumChildren()) - { - // We couldn't find a match for possibleMatch[j], so not a match - matching = false; - break; - } - } - - if (matching) - { - Debug("pf::bv") - << "Collecting info from a sub-conflict" << std::endl; - d_resolutionProof->collectClauses(it->second); - matchFound = true; - break; - } - } - } - - if (!matchFound) - { - Debug("pf::bv") << "Do not collect clauses for " << confl << std::endl - << "Dumping existing conflicts:" << std::endl; - - i = 0; - for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) - { - ++i; - Debug("pf::bv") << "\tConflict #" << i << ": " << it->first - << std::endl; - } - - Unreachable(); - } - } - } -} - -void LfscResolutionBitVectorProof::printTheoryLemmaProof( - std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) -{ - Debug("pf::bv") - << "(pf::bv) LfscResolutionBitVectorProof::printTheoryLemmaProof called" - << std::endl; - Expr conflict = utils::mkSortedExpr(kind::OR, lemma); - Debug("pf::bv") << "\tconflict = " << conflict << std::endl; - - if (d_bbConflictMap.find(conflict) != d_bbConflictMap.end()) - { - std::ostringstream lemma_paren; - for (unsigned i = 0; i < lemma.size(); ++i) - { - Expr lit = lemma[i]; - - if (lit.getKind() == kind::NOT) - { - os << "(intro_assump_t _ _ _ "; - } - else - { - os << "(intro_assump_f _ _ _ "; - } - lemma_paren << ")"; - // print corresponding literal in main sat solver - ProofManager* pm = ProofManager::currentPM(); - CnfProof* cnf = pm->getCnfProof(); - prop::SatLiteral main_lit = cnf->getLiteral(lit); - os << pm->getLitName(main_lit); - os << " "; - // print corresponding literal in bv sat solver - prop::SatVariable bb_var = d_cnfProof->getLiteral(lit).getSatVariable(); - os << pm->getAtomName(bb_var, "bb"); - os << "(\\ unit" << bb_var << "\n"; - lemma_paren << ")"; - } - Expr lem = utils::mkOr(lemma); - Assert(d_bbConflictMap.find(lem) != d_bbConflictMap.end()); - ClauseId lemma_id = d_bbConflictMap[lem]; - proof::LFSCProofPrinter::printAssumptionsResolution( - d_resolutionProof.get(), lemma_id, os, lemma_paren); - os << lemma_paren.str(); - } - else - { - Debug("pf::bv") << "Found a non-recorded conflict. Looking for a matching " - "sub-conflict..." - << std::endl; - - bool matching; - - ExprToClauseId::const_iterator it; - unsigned i = 0; - for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) - { - // Our conflict is sorted, and the records are also sorted. - ++i; - Expr possibleMatch = it->first; - - if (possibleMatch.getKind() != kind::OR) - { - // This is a single-node conflict. If this node is in the conflict we're - // trying to prove, we have a match. - matching = false; - - for (unsigned k = 0; k < conflict.getNumChildren(); ++k) - { - if (conflict[k] == possibleMatch) - { - matching = true; - break; - } - } - } - else - { - if (possibleMatch.getNumChildren() > conflict.getNumChildren()) - continue; - - unsigned k = 0; - - matching = true; - for (unsigned j = 0; j < possibleMatch.getNumChildren(); ++j) - { - // j is the index in possibleMatch - // k is the index in conflict - while (k < conflict.getNumChildren() - && conflict[k] != possibleMatch[j]) - { - ++k; - } - if (k == conflict.getNumChildren()) - { - // We couldn't find a match for possibleMatch[j], so not a match - matching = false; - break; - } - } - } - - if (matching) - { - Debug("pf::bv") << "Found a match with conflict #" << i << ": " - << std::endl - << possibleMatch << std::endl; - // The rest is just a copy of the usual handling, if a precise match is - // found. We only use the literals that appear in the matching conflict, - // though, and not in the original lemma - as these may not have even - // been bit blasted! - std::ostringstream lemma_paren; - - if (possibleMatch.getKind() == kind::OR) - { - for (const Expr& lit : possibleMatch) - { - if (lit.getKind() == kind::NOT) - { - os << "(intro_assump_t _ _ _ "; - } - else - { - os << "(intro_assump_f _ _ _ "; - } - lemma_paren << ")"; - // print corresponding literal in main sat solver - ProofManager* pm = ProofManager::currentPM(); - CnfProof* cnf = pm->getCnfProof(); - prop::SatLiteral main_lit = cnf->getLiteral(lit); - os << pm->getLitName(main_lit); - os << " "; - // print corresponding literal in bv sat solver - prop::SatVariable bb_var = - d_cnfProof->getLiteral(lit).getSatVariable(); - os << pm->getAtomName(bb_var, "bb"); - os << "(\\ unit" << bb_var << "\n"; - lemma_paren << ")"; - } - } - else - { - // The conflict only consists of one node, either positive or - // negative. - Expr lit = possibleMatch; - if (lit.getKind() == kind::NOT) - { - os << "(intro_assump_t _ _ _ "; - } - else - { - os << "(intro_assump_f _ _ _ "; - } - lemma_paren << ")"; - // print corresponding literal in main sat solver - ProofManager* pm = ProofManager::currentPM(); - CnfProof* cnf = pm->getCnfProof(); - prop::SatLiteral main_lit = cnf->getLiteral(lit); - os << pm->getLitName(main_lit); - os << " "; - // print corresponding literal in bv sat solver - prop::SatVariable bb_var = - d_cnfProof->getLiteral(lit).getSatVariable(); - os << pm->getAtomName(bb_var, "bb"); - os << "(\\ unit" << bb_var << "\n"; - lemma_paren << ")"; - } - - ClauseId lemma_id = it->second; - proof::LFSCProofPrinter::printAssumptionsResolution( - d_resolutionProof.get(), lemma_id, os, lemma_paren); - os << lemma_paren.str(); - - return; - } - } - - // We failed to find a matching sub conflict. The last hope is that the - // conflict has a FALSE assertion in it; this can happen in some corner - // cases, where the FALSE is the result of a rewrite. - - for (const Expr& lit : lemma) - { - if (lit.getKind() == kind::NOT && lit[0] == utils::mkFalse()) - { - Debug("pf::bv") << "Lemma has a (not false) literal" << std::endl; - os << "(clausify_false "; - os << ProofManager::getLitName(lit); - os << ")"; - return; - } - } - - Debug("pf::bv") << "Failed to find a matching sub-conflict..." << std::endl - << "Dumping existing conflicts:" << std::endl; - - i = 0; - for (it = d_bbConflictMap.begin(); it != d_bbConflictMap.end(); ++it) - { - ++i; - Debug("pf::bv") << "\tConflict #" << i << ": " << it->first << std::endl; - } - - Unreachable(); - } -} - -void LfscResolutionBitVectorProof::calculateAtomsInBitblastingProof() -{ - // Collect the input clauses used - IdToSatClause used_lemmas; - IdToSatClause used_inputs; - d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); - d_cnfProof->collectAtomsForClauses(used_inputs, d_atomsInBitblastingProof); - Assert(used_lemmas.empty()); -} - -void LfscResolutionBitVectorProof::printBBDeclarationAndCnf(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) -{ - // print mapping between theory atoms and internal SAT variables - os << std::endl << ";; BB atom mapping\n" << std::endl; - - std::set<Node>::iterator atomIt; - Debug("pf::bv") << std::endl - << "BV Dumping atoms from inputs: " << std::endl - << std::endl; - for (atomIt = d_atomsInBitblastingProof.begin(); - atomIt != d_atomsInBitblastingProof.end(); - ++atomIt) - { - Debug("pf::bv") << "\tAtom: " << *atomIt << std::endl; - } - Debug("pf::bv") << std::endl; - - // first print bit-blasting - printBitblasting(os, paren); - - // print CNF conversion proof for bit-blasted facts - IdToSatClause used_lemmas; - IdToSatClause used_inputs; - d_resolutionProof->collectClausesUsed(used_inputs, used_lemmas); - - d_cnfProof->printAtomMapping(d_atomsInBitblastingProof, os, paren, letMap); - os << std::endl << ";; Bit-blasting definitional clauses \n" << std::endl; - for (IdToSatClause::iterator it = used_inputs.begin(); - it != used_inputs.end(); - ++it) - { - d_cnfProof->printCnfProofForClause(it->first, it->second, os, paren); - } - - os << std::endl << " ;; Bit-blasting learned clauses \n" << std::endl; - proof::LFSCProofPrinter::printResolutions(d_resolutionProof.get(), os, paren); -} - -void LfscResolutionBitVectorProof::printEmptyClauseProof(std::ostream& os, - std::ostream& paren) -{ - Assert(options::bitblastMode() == options::BitblastMode::EAGER) - << "the BV theory should only be proving bottom directly in the eager " - "bitblasting mode"; - proof::LFSCProofPrinter::printResolutionEmptyClause( - d_resolutionProof.get(), os, paren); -} - -} // namespace proof - -} /* namespace CVC4 */ diff --git a/src/proof/resolution_bitvector_proof.h b/src/proof/resolution_bitvector_proof.h deleted file mode 100644 index a1b0b0d59..000000000 --- a/src/proof/resolution_bitvector_proof.h +++ /dev/null @@ -1,113 +0,0 @@ -/********************* */ -/*! \file resolution_bitvector_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Mathias Preiner, Liana Hadarean - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Bitvector proof - ** - ** Bitvector proof - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__PROOF__RESOLUTION_BITVECTOR_PROOF_H -#define CVC4__PROOF__RESOLUTION_BITVECTOR_PROOF_H - -#include <iosfwd> - -#include "context/context.h" -#include "expr/expr.h" -#include "proof/bitvector_proof.h" -#include "proof/sat_proof.h" -#include "proof/theory_proof.h" -#include "prop/bvminisat/core/Solver.h" -#include "prop/cnf_stream.h" -#include "prop/sat_solver_types.h" -#include "theory/bv/bitblast/bitblaster.h" -#include "theory/bv/theory_bv.h" - -namespace CVC4 { - -typedef TSatProof<CVC4::BVMinisat::Solver> BVSatProof; - -namespace proof { - -/** - * Represents a bitvector proof which is backed by - * (a) bitblasting and - * (b) a resolution unsat proof. - * - * Contains tools for constructing BV conflicts - */ -class ResolutionBitVectorProof : public BitVectorProof -{ - public: - ResolutionBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine); - - /** - * Create an (internal) SAT proof object - * Must be invoked before manipulating BV conflicts, - * or initializing a BNF proof - */ - void initSatProof(CVC4::BVMinisat::Solver* solver); - - BVSatProof* getSatProof(); - - void finalizeConflicts(std::vector<Expr>& conflicts) override; - - void startBVConflict(CVC4::BVMinisat::Solver::TCRef cr); - void startBVConflict(CVC4::BVMinisat::Solver::TLit lit); - void endBVConflict(const BVMinisat::Solver::TLitVec& confl); - - void markAssumptionConflict() { d_isAssumptionConflict = true; } - bool isAssumptionConflict() const { return d_isAssumptionConflict; } - - void initCnfProof(prop::CnfStream* cnfStream, - context::Context* cnf, - prop::SatVariable trueVar, - prop::SatVariable falseVar) override; - - protected: - void attachToSatSolver(prop::SatSolver& sat_solver) override; - - context::Context d_fakeContext; - - // The CNF formula that results from bit-blasting will need a proof. - // This is that proof. - std::unique_ptr<BVSatProof> d_resolutionProof; - - bool d_isAssumptionConflict; - -}; - -class LfscResolutionBitVectorProof : public ResolutionBitVectorProof -{ - public: - LfscResolutionBitVectorProof(theory::bv::TheoryBV* bv, - TheoryProofEngine* proofEngine) - : ResolutionBitVectorProof(bv, proofEngine) - { - } - void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; - void printBBDeclarationAndCnf(std::ostream& os, - std::ostream& paren, - ProofLetMap& letMap) override; - void printEmptyClauseProof(std::ostream& os, std::ostream& paren) override; - void calculateAtomsInBitblastingProof() override; -}; - -} // namespace proof - -} // namespace CVC4 - -#endif /* CVC4__PROOF__RESOLUTIONBITVECTORPROOF_H */ diff --git a/src/proof/sat_proof.h b/src/proof/sat_proof.h index 83e4d3930..38aea0673 100644 --- a/src/proof/sat_proof.h +++ b/src/proof/sat_proof.h @@ -32,7 +32,6 @@ #include "expr/expr.h" #include "proof/clause_id.h" #include "proof/proof_manager.h" -#include "util/proof.h" #include "util/statistics_registry.h" // Forward declarations. diff --git a/src/proof/sat_proof_implementation.h b/src/proof/sat_proof_implementation.h index 897a5c452..7ce18ae4a 100644 --- a/src/proof/sat_proof_implementation.h +++ b/src/proof/sat_proof_implementation.h @@ -20,10 +20,7 @@ #define CVC4__SAT__PROOF_IMPLEMENTATION_H #include "proof/clause_id.h" -#include "proof/cnf_proof.h" #include "proof/sat_proof.h" -#include "prop/bvminisat/bvminisat.h" -#include "prop/bvminisat/core/Solver.h" #include "prop/minisat/core/Solver.h" #include "prop/minisat/minisat.h" #include "prop/sat_solver_types.h" @@ -712,11 +709,6 @@ void TSatProof<Solver>::registerResolution(ClauseId id, ResChain<Solver>* res) { Assert(checkResolution(id)); } - PSTATS(uint64_t resolutionSteps = - static_cast<uint64_t>(res.getSteps().size()); - d_statistics.d_resChainLengths << resolutionSteps; - d_statistics.d_avgChainLength.addEntry(resolutionSteps); - ++(d_statistics.d_numLearnedClauses);) } /// recording resolutions @@ -912,14 +904,6 @@ void TSatProof<Solver>::markDeleted(typename Solver::TCRef clause) { } } -// template<> -// void toSatClause< ::BVMinisat::Solver> (const BVMinisat::Solver::TClause& -// minisat_cl, -// prop::SatClause& sat_cl) { - -// prop::BVMinisatSatSolver::toSatClause(minisat_cl, sat_cl); -// } - template <class Solver> void TSatProof<Solver>::constructProof(ClauseId conflict) { d_satProofConstructed = true; @@ -1002,9 +986,6 @@ void TSatProof<Solver>::collectClauses(ClauseId id) { const ResolutionChain& res = getResolutionChain(id); const typename ResolutionChain::ResSteps& steps = res.getSteps(); - PSTATS(d_statistics.d_usedResChainLengths - << ((uint64_t)steps.size()); - d_statistics.d_usedClauseGlue << ((uint64_t)d_glueMap[id]);); ClauseId start = res.getStart(); collectClauses(start); @@ -1018,8 +999,6 @@ void TSatProof<Solver>::collectClausesUsed(IdToSatClause& inputs, IdToSatClause& lemmas) { inputs = d_seenInputs; lemmas = d_seenLemmas; - PSTATS(d_statistics.d_numLearnedInProof.setData(d_seenLearnt.size()); - d_statistics.d_numLemmasInProof.setData(d_seenLemmas.size());); } template <class Solver> diff --git a/src/proof/simplify_boolean_node.cpp b/src/proof/simplify_boolean_node.cpp deleted file mode 100644 index 5f1943654..000000000 --- a/src/proof/simplify_boolean_node.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/********************* */ -/*! \file simplify_boolean_node.cpp - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Liana Hadarean, Andrew Reynolds - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Simplifying a boolean node, needed for constructing LFSC proofs. - ** - **/ - -#include "cvc4_private.h" - -#include "proof_manager.h" - -namespace CVC4 { - -inline static Node eqNode(TNode n1, TNode n2) { - return NodeManager::currentNM()->mkNode(kind::EQUAL, n1, n2); -} - -Node simplifyBooleanNode(const Node &n) { - if (n.isNull()) - return n; - - // Only simplify boolean nodes - if (!n.getType().isBoolean()) - return n; - - // Sometimes we get sent intermediate nodes that we shouldn't simplify. - // If a node doesn't have a literal, it's clearly intermediate - ignore. - if (!ProofManager::hasLitName(n)) - return n; - - // If we already simplified the node, ignore. - if (ProofManager::currentPM()->haveRewriteFilter(n.negate())) - return n; - - - std::string litName = ProofManager::getLitName(n.negate()); - Node falseNode = NodeManager::currentNM()->mkConst(false); - Node trueNode = NodeManager::currentNM()->mkConst(true); - Node simplified = n; - - // (not (= false b)), (not (= true b))) - if ((n.getKind() == kind::NOT) && (n[0].getKind() == kind::EQUAL) && - (n[0][0].getKind() == kind::BOOLEAN_TERM_VARIABLE || n[0][1].getKind() == kind::BOOLEAN_TERM_VARIABLE)) { - Node lhs = n[0][0]; - Node rhs = n[0][1]; - - if (lhs == falseNode) { - Assert(rhs != falseNode); - Assert(rhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (not (= false b)) --> true = b - - simplified = eqNode(trueNode, rhs); - - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_not_iff_f _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - - } else if (rhs == falseNode) { - Assert(lhs != falseNode); - Assert(lhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (not (= b false)) --> b = true - - simplified = eqNode(lhs, trueNode); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_not_iff_f_2 _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - - } else if (lhs == trueNode) { - Assert(rhs != trueNode); - Assert(rhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (not (= true b)) --> b = false - - simplified = eqNode(falseNode, rhs); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_not_iff_t _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - - } else if (rhs == trueNode) { - Assert(lhs != trueNode); - Assert(lhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (not (= b true)) --> b = false - - simplified = eqNode(lhs, falseNode); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_not_iff_t_2 _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - } - - } else if ((n.getKind() == kind::EQUAL) && - (n[0].getKind() == kind::BOOLEAN_TERM_VARIABLE || n[1].getKind() == kind::BOOLEAN_TERM_VARIABLE)) { - Node lhs = n[0]; - Node rhs = n[1]; - - if (lhs == falseNode) { - Assert(rhs != falseNode); - Assert(rhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (= false b) - - std::stringstream newLitName; - newLitName << "(pred_iff_f _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(litName, newLitName.str()); - - } else if (rhs == falseNode) { - Assert(lhs != falseNode); - Assert(lhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (= b false)) - - std::stringstream newLitName; - newLitName << "(pred_iff_f_2 _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(litName, newLitName.str()); - - } else if (lhs == trueNode) { - Assert(rhs != trueNode); - Assert(rhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (= true b) - - std::stringstream newLitName; - newLitName << "(pred_iff_t _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(litName, newLitName.str()); - - } else if (rhs == trueNode) { - Assert(lhs != trueNode); - Assert(lhs.getKind() == kind::BOOLEAN_TERM_VARIABLE); - // (= b true) - - - std::stringstream newLitName; - newLitName << "(pred_iff_t_2 _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(litName, newLitName.str()); - } - - } else if ((n.getKind() == kind::NOT) && (n[0].getKind() == kind::BOOLEAN_TERM_VARIABLE)) { - // (not b) --> b = false - simplified = eqNode(n[0], falseNode); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_eq_f _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - - } else if (n.getKind() == kind::BOOLEAN_TERM_VARIABLE) { - // (b) --> b = true - simplified = eqNode(n, trueNode); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_eq_t _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - - } else if ((n.getKind() == kind::NOT) && (n[0].getKind() == kind::SELECT)) { - // not(a[x]) --> a[x] = false - simplified = eqNode(n[0], falseNode); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_eq_f _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - - } else if (n.getKind() == kind::SELECT) { - // a[x] --> a[x] = true - simplified = eqNode(n, trueNode); - std::string simplifiedLitName = ProofManager::getLitName(simplified.negate()); - std::stringstream newLitName; - newLitName << "(pred_eq_t _ " << litName << ")"; - ProofManager::currentPM()->addRewriteFilter(simplifiedLitName, newLitName.str()); - } - - if (simplified != n) - Debug("pf::simplify") << "simplifyBooleanNode: " << n << " --> " << simplified << std::endl; - - return simplified; -} - -}/* CVC4 namespace */ diff --git a/src/proof/skolemization_manager.cpp b/src/proof/skolemization_manager.cpp deleted file mode 100644 index 1bb14598d..000000000 --- a/src/proof/skolemization_manager.cpp +++ /dev/null @@ -1,69 +0,0 @@ -/********************* */ -/*! \file skolemization_manager.cpp - ** \verbatim - ** Top contributors (to current version): - ** Paul Meng, Tim King, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - - **/ - -#include "proof/skolemization_manager.h" - -namespace CVC4 { - -void SkolemizationManager::registerSkolem(Node disequality, Node skolem) { - Debug("pf::pm") << "SkolemizationManager: registerSkolem: disequality = " << disequality << ", skolem = " << skolem << std::endl; - - if (isSkolem(skolem)) { - Assert(d_skolemToDisequality[skolem] == disequality); - return; - } - - d_disequalityToSkolem[disequality] = skolem; - d_skolemToDisequality[skolem] = disequality; -} - -bool SkolemizationManager::hasSkolem(Node disequality) { - return (d_disequalityToSkolem.find(disequality) != d_disequalityToSkolem.end()); -} - -Node SkolemizationManager::getSkolem(Node disequality) { - Debug("pf::pm") << "SkolemizationManager: getSkolem( "; - Assert(d_disequalityToSkolem.find(disequality) - != d_disequalityToSkolem.end()); - Debug("pf::pm") << disequality << " ) = " << d_disequalityToSkolem[disequality] << std::endl; - return d_disequalityToSkolem[disequality]; -} - -Node SkolemizationManager::getDisequality(Node skolem) { - Assert(d_skolemToDisequality.find(skolem) != d_skolemToDisequality.end()); - return d_skolemToDisequality[skolem]; -} - -bool SkolemizationManager::isSkolem(Node skolem) { - return (d_skolemToDisequality.find(skolem) != d_skolemToDisequality.end()); -} - -void SkolemizationManager::clear() { - Debug("pf::pm") << "SkolemizationManager: clear" << std::endl; - d_disequalityToSkolem.clear(); - d_skolemToDisequality.clear(); -} - -std::unordered_map<Node, Node, NodeHashFunction>::const_iterator SkolemizationManager::begin() { - return d_disequalityToSkolem.begin(); -} - -std::unordered_map<Node, Node, NodeHashFunction>::const_iterator SkolemizationManager::end() { - return d_disequalityToSkolem.end(); -} - -} /* CVC4 namespace */ diff --git a/src/proof/skolemization_manager.h b/src/proof/skolemization_manager.h deleted file mode 100644 index a2c61db4d..000000000 --- a/src/proof/skolemization_manager.h +++ /dev/null @@ -1,55 +0,0 @@ -/********************* */ -/*! \file skolemization_manager.h - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Tim King, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__SKOLEMIZATION_MANAGER_H -#define CVC4__SKOLEMIZATION_MANAGER_H - -#include <iostream> -#include <unordered_map> - -#include "proof/proof.h" -#include "util/proof.h" -#include "expr/node.h" -#include "theory/logic_info.h" -#include "theory/substitutions.h" - -namespace CVC4 { - -class SkolemizationManager { -public: - void registerSkolem(Node disequality, Node skolem); - bool hasSkolem(Node disequality); - Node getSkolem(Node disequality); - Node getDisequality(Node skolem); - bool isSkolem(Node skolem); - void clear(); - - std::unordered_map<Node, Node, NodeHashFunction>::const_iterator begin(); - std::unordered_map<Node, Node, NodeHashFunction>::const_iterator end(); - -private: - std::unordered_map<Node, Node, NodeHashFunction> d_disequalityToSkolem; - std::unordered_map<Node, Node, NodeHashFunction> d_skolemToDisequality; -}; - -}/* CVC4 namespace */ - - - -#endif /* CVC4__SKOLEMIZATION_MANAGER_H */ diff --git a/src/proof/theory_proof.cpp b/src/proof/theory_proof.cpp deleted file mode 100644 index b47fd6a1e..000000000 --- a/src/proof/theory_proof.cpp +++ /dev/null @@ -1,1756 +0,0 @@ -/********************* */ -/*! \file theory_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Liana Hadarean, Yoni Zohar - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ -#include "proof/theory_proof.h" - -#include "base/check.h" -#include "context/context.h" -#include "expr/node_visitor.h" -#include "options/bv_options.h" -#include "options/proof_options.h" -#include "proof/arith_proof.h" -#include "proof/array_proof.h" -#include "proof/clausal_bitvector_proof.h" -#include "proof/clause_id.h" -#include "proof/cnf_proof.h" -#include "proof/proof_manager.h" -#include "proof/proof_output_channel.h" -#include "proof/proof_utils.h" -#include "proof/resolution_bitvector_proof.h" -#include "proof/sat_proof.h" -#include "proof/simplify_boolean_node.h" -#include "proof/uf_proof.h" -#include "prop/sat_solver_types.h" -#include "smt/smt_engine.h" -#include "smt/smt_engine_scope.h" -#include "theory/arrays/theory_arrays.h" -#include "theory/bv/theory_bv.h" -#include "theory/output_channel.h" -#include "theory/term_registration_visitor.h" -#include "theory/uf/theory_uf.h" -#include "theory/valuation.h" -#include "util/hash.h" -#include "util/proof.h" - -namespace CVC4 { - -using proof::LfscResolutionBitVectorProof; -using proof::ResolutionBitVectorProof; - -unsigned CVC4::ProofLetCount::counter = 0; -static unsigned LET_COUNT = 1; - -TheoryProofEngine::TheoryProofEngine() - : d_registrationCache() - , d_theoryProofTable() -{ - d_theoryProofTable[theory::THEORY_BOOL] = new LFSCBooleanProof(this); -} - -TheoryProofEngine::~TheoryProofEngine() { - TheoryProofTable::iterator it = d_theoryProofTable.begin(); - TheoryProofTable::iterator end = d_theoryProofTable.end(); - for (; it != end; ++it) { - delete it->second; - } -} - -void TheoryProofEngine::registerTheory(theory::Theory* th) { - if (th) { - theory::TheoryId id = th->getId(); - if(d_theoryProofTable.find(id) == d_theoryProofTable.end()) { - - Trace("pf::tp") << "TheoryProofEngine::registerTheory: " << id << std::endl; - - if (id == theory::THEORY_UF) { - d_theoryProofTable[id] = new LFSCUFProof((theory::uf::TheoryUF*)th, this); - return; - } - - if (id == theory::THEORY_BV) { - auto thBv = static_cast<theory::bv::TheoryBV*>(th); - if (options::bitblastMode() == options::BitblastMode::EAGER - && options::bvSatSolver() == options::SatSolverMode::CRYPTOMINISAT) - { - proof::BitVectorProof* bvp = nullptr; - switch (options::bvProofFormat()) - { - case options::BvProofFormat::DRAT: - { - bvp = new proof::LfscDratBitVectorProof(thBv, this); - break; - } - case options::BvProofFormat::LRAT: - { - bvp = new proof::LfscLratBitVectorProof(thBv, this); - break; - } - case options::BvProofFormat::ER: - { - bvp = new proof::LfscErBitVectorProof(thBv, this); - break; - } - default: - { - Unreachable() << "Invalid BvProofFormat"; - } - }; - d_theoryProofTable[id] = bvp; - } - else - { - proof::BitVectorProof* bvp = - new proof::LfscResolutionBitVectorProof(thBv, this); - d_theoryProofTable[id] = bvp; - } - return; - } - - if (id == theory::THEORY_ARRAYS) { - d_theoryProofTable[id] = new LFSCArrayProof((theory::arrays::TheoryArrays*)th, this); - return; - } - - if (id == theory::THEORY_ARITH) { - d_theoryProofTable[id] = new LFSCArithProof((theory::arith::TheoryArith*)th, this); - return; - } - - // TODO other theories - } - } -} - -void TheoryProofEngine::finishRegisterTheory(theory::Theory* th) { - if (th) { - theory::TheoryId id = th->getId(); - if (id == theory::THEORY_BV) { - theory::bv::TheoryBV* bv_th = static_cast<theory::bv::TheoryBV*>(th); - Assert(d_theoryProofTable.find(id) != d_theoryProofTable.end()); - proof::BitVectorProof* bvp = - static_cast<proof::BitVectorProof*>(d_theoryProofTable[id]); - bv_th->setProofLog(bvp); - return; - } - } -} - -TheoryProof* TheoryProofEngine::getTheoryProof(theory::TheoryId id) { - // The UF theory handles queries for the Builtin theory. - if (id == theory::THEORY_BUILTIN) { - Debug("pf::tp") << "TheoryProofEngine::getTheoryProof: BUILTIN --> UF" << std::endl; - id = theory::THEORY_UF; - } - - if (d_theoryProofTable.find(id) == d_theoryProofTable.end()) { - InternalError() - << "Error! Proofs not yet supported for the following theory: " << id - << std::endl; - } - - return d_theoryProofTable[id]; -} - -void TheoryProofEngine::markTermForFutureRegistration(Expr term, theory::TheoryId id) { - d_exprToTheoryIds[term].insert(id); -} - -void TheoryProofEngine::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { - Assert(c1.isConst()); - Assert(c2.isConst()); - - Assert(theory::Theory::theoryOf(c1) == theory::Theory::theoryOf(c2)); - getTheoryProof(theory::Theory::theoryOf(c1))->printConstantDisequalityProof(os, c1, c2, globalLetMap); -} - -void TheoryProofEngine::printTheoryTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - this->printTheoryTermAsType(term, os, map, expectedType); -} - -TypeNode TheoryProofEngine::equalityType(const Expr& left, const Expr& right) -{ - // Ask the two theories what they think.. - TypeNode leftType = getTheoryProof(theory::Theory::theoryOf(left))->equalityType(left, right); - TypeNode rightType = getTheoryProof(theory::Theory::theoryOf(right))->equalityType(left, right); - - // Error if the disagree. - Assert(leftType.isNull() || rightType.isNull() || leftType == rightType) - << "TheoryProofEngine::equalityType(" << left << ", " << right << "):" << std::endl - << "theories disagree about the type of an equality:" << std::endl - << "\tleft: " << leftType << std::endl - << "\tright:" << rightType; - - return leftType.isNull() ? rightType : leftType; -} - -void TheoryProofEngine::registerTerm(Expr term) { - Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering term: " << term << std::endl; - - if (d_registrationCache.count(term)) { - return; - } - - Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering NEW term: " << term << std::endl; - - theory::TheoryId theory_id = theory::Theory::theoryOf(term); - - Debug("pf::tp::register") << "Term's theory( " << term << " ) = " << theory_id << std::endl; - - // don't need to register boolean terms - if (theory_id == theory::THEORY_BUILTIN || - term.getKind() == kind::ITE) { - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - registerTerm(term[i]); - } - d_registrationCache.insert(term); - return; - } - - if (!supportedTheory(theory_id)) return; - - // Register the term with its owner theory - getTheoryProof(theory_id)->registerTerm(term); - - // A special case: the array theory needs to know of every skolem, even if - // it belongs to another theory (e.g., a BV skolem) - if (ProofManager::getSkolemizationManager()->isSkolem(term) && theory_id != theory::THEORY_ARRAYS) { - Debug("pf::tp::register") << "TheoryProofEngine::registerTerm: registering a non-array skolem: " << term << std::endl; - getTheoryProof(theory::THEORY_ARRAYS)->registerTerm(term); - } - - d_registrationCache.insert(term); -} - -theory::TheoryId TheoryProofEngine::getTheoryForLemma(const prop::SatClause* clause) { - ProofManager* pm = ProofManager::currentPM(); - - std::set<Node> nodes; - for(unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = (*clause)[i]; - Node node = pm->getCnfProof()->getAtom(lit.getSatVariable()); - Expr atom = node.toExpr(); - if (atom.isConst()) { - Assert(atom == utils::mkTrue()); - continue; - } - - nodes.insert(lit.isNegated() ? node.notNode() : node); - } - - // Ensure that the lemma is in the database. - Assert(pm->getCnfProof()->haveProofRecipe(nodes)); - return pm->getCnfProof()->getProofRecipe(nodes).getTheory(); -} - -void LFSCTheoryProofEngine::bind(Expr term, ProofLetMap& map, Bindings& let_order) { - ProofLetMap::iterator it = map.find(term); - if (it != map.end()) { - ProofLetCount& count = it->second; - count.increment(); - return; - } - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - bind(term[i], map, let_order); - } - unsigned new_id = ProofLetCount::newId(); - map[term] = ProofLetCount(new_id); - let_order.push_back(LetOrderElement(term, new_id)); -} - -void LFSCTheoryProofEngine::printLetTerm(Expr term, std::ostream& os) { - ProofLetMap map; - Bindings let_order; - bind(term, map, let_order); - std::ostringstream paren; - for (unsigned i = 0; i < let_order.size(); ++i) { - Expr current_expr = let_order[i].expr; - unsigned let_id = let_order[i].id; - ProofLetMap::const_iterator it = map.find(current_expr); - Assert(it != map.end()); - unsigned let_count = it->second.count; - Assert(let_count); - // skip terms that only appear once - if (let_count <= LET_COUNT) { - continue; - } - - os << "(@ let" <<let_id << " "; - printTheoryTerm(current_expr, os, map); - paren <<")"; - } - unsigned last_let_id = let_order.back().id; - Expr last = let_order.back().expr; - unsigned last_count = map.find(last)->second.count; - if (last_count <= LET_COUNT) { - printTheoryTerm(last, os, map); - } - else { - os << " let" << last_let_id; - } - os << paren.str(); -} - -void LFSCTheoryProofEngine::printTheoryTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - theory::TheoryId theory_id = theory::Theory::theoryOf(term); - - // boolean terms and ITEs are special because they - // are common to all theories - if (theory_id == theory::THEORY_BUILTIN || - term.getKind() == kind::ITE || - term.getKind() == kind::EQUAL) { - printCoreTerm(term, os, map, expectedType); - return; - } - // dispatch to proper theory - getTheoryProof(theory_id)->printOwnedTerm(term, os, map, expectedType); -} - -void LFSCTheoryProofEngine::printSort(Type type, std::ostream& os) { - if (type.isSort()) { - getTheoryProof(theory::THEORY_UF)->printOwnedSort(type, os); - return; - } - if (type.isBitVector()) { - getTheoryProof(theory::THEORY_BV)->printOwnedSort(type, os); - return; - } - - if (type.isArray()) { - getTheoryProof(theory::THEORY_ARRAYS)->printOwnedSort(type, os); - return; - } - - if (type.isInteger() || type.isReal()) { - getTheoryProof(theory::THEORY_ARITH)->printOwnedSort(type, os); - return; - } - - if (type.isBoolean()) { - getTheoryProof(theory::THEORY_BOOL)->printOwnedSort(type, os); - return; - } - - Unreachable(); -} - -void LFSCTheoryProofEngine::performExtraRegistrations() { - ExprToTheoryIds::const_iterator it; - for (it = d_exprToTheoryIds.begin(); it != d_exprToTheoryIds.end(); ++it) { - if (d_registrationCache.count(it->first)) { // Only register if the term appeared - TheoryIdSet::const_iterator theoryIt; - for (theoryIt = it->second.begin(); theoryIt != it->second.end(); ++theoryIt) { - Debug("pf::tp") << "\tExtra registration of term " << it->first - << " with theory: " << *theoryIt << std::endl; - Assert(supportedTheory(*theoryIt)); - getTheoryProof(*theoryIt)->registerTerm(it->first); - } - } - } -} - -void LFSCTheoryProofEngine::registerTermsFromAssertions() { - ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); - ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); - - for(; it != end; ++it) { - registerTerm(*it); - } - - performExtraRegistrations(); -} - -void LFSCTheoryProofEngine::printAssertions(std::ostream& os, std::ostream& paren) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions called" << std::endl << std::endl; - - ProofManager::assertions_iterator it = ProofManager::currentPM()->begin_assertions(); - ProofManager::assertions_iterator end = ProofManager::currentPM()->end_assertions(); - - for (; it != end; ++it) { - Debug("pf::tp") << "printAssertions: assertion is: " << *it << std::endl; - os << "(% " << ProofManager::currentPM()->getInputFormulaName(*it) << " (th_holds "; - - // Assertions appear before the global let map, so we use a dummpMap to avoid letification here. - ProofLetMap dummyMap; - - bool convertFromBool = (it->getType().isBoolean() && printsAsBool(*it)); - if (convertFromBool) os << "(p_app "; - printBoundTerm(*it, os, dummyMap); - if (convertFromBool) os << ")"; - - os << ")\n"; - paren << ")"; - } - - Debug("pf::tp") << "LFSCTheoryProofEngine::printAssertions done" << std::endl << std::endl; -} - -void LFSCTheoryProofEngine::printLemmaRewrites(NodePairSet& rewrites, - std::ostream& os, - std::ostream& paren) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printLemmaRewrites called" << std::endl << std::endl; - - NodePairSet::const_iterator it; - - for (it = rewrites.begin(); it != rewrites.end(); ++it) { - Debug("pf::tp") << "printLemmaRewrites: " << it->first << " --> " << it->second << std::endl; - - Node n1 = it->first; - Node n2 = it->second; - Assert(n1.toExpr() == utils::mkFalse() - || theory::Theory::theoryOf(n1) == theory::Theory::theoryOf(n2)); - - std::ostringstream rewriteRule; - rewriteRule << ".lrr" << d_assertionToRewrite.size(); - - os << "(th_let_pf _ "; - getTheoryProof(theory::Theory::theoryOf(n1))->printRewriteProof(os, n1, n2); - os << "(\\ " << rewriteRule.str() << "\n"; - - d_assertionToRewrite[it->first] = rewriteRule.str(); - Debug("pf::tp") << "d_assertionToRewrite[" << it->first << "] = " << rewriteRule.str() << std::endl; - paren << "))"; - } - - Debug("pf::tp") << "LFSCTheoryProofEngine::printLemmaRewrites done" << std::endl << std::endl; -} - -void LFSCTheoryProofEngine::printSortDeclarations(std::ostream& os, std::ostream& paren) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations called" << std::endl << std::endl; - - TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); - TheoryProofTable::const_iterator end = d_theoryProofTable.end(); - for (; it != end; ++it) { - it->second->printSortDeclarations(os, paren); - } - - Debug("pf::tp") << "LFSCTheoryProofEngine::printSortDeclarations done" << std::endl << std::endl; -} - -void LFSCTheoryProofEngine::printTermDeclarations(std::ostream& os, std::ostream& paren) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printTermDeclarations called" << std::endl << std::endl; - - TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); - TheoryProofTable::const_iterator end = d_theoryProofTable.end(); - for (; it != end; ++it) { - it->second->printTermDeclarations(os, paren); - } - - Debug("pf::tp") << "LFSCTheoryProofEngine::printTermDeclarations done" << std::endl << std::endl; -} - -void LFSCTheoryProofEngine::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printDeferredDeclarations called" << std::endl; - - TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); - TheoryProofTable::const_iterator end = d_theoryProofTable.end(); - for (; it != end; ++it) { - it->second->printDeferredDeclarations(os, paren); - } -} - -void LFSCTheoryProofEngine::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { - Debug("pf::tp") << "LFSCTheoryProofEngine::printAliasingDeclarations called" << std::endl; - - TheoryProofTable::const_iterator it = d_theoryProofTable.begin(); - TheoryProofTable::const_iterator end = d_theoryProofTable.end(); - for (; it != end; ++it) { - it->second->printAliasingDeclarations(os, paren, globalLetMap); - } -} - -void LFSCTheoryProofEngine::dumpTheoryLemmas(const IdToSatClause& lemmas) { - Debug("pf::dumpLemmas") << "Dumping ALL theory lemmas" << std::endl << std::endl; - - ProofManager* pm = ProofManager::currentPM(); - for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) { - ClauseId id = it->first; - Debug("pf::dumpLemmas") << "**** \tLemma ID = " << id << std::endl; - const prop::SatClause* clause = it->second; - std::set<Node> nodes; - for(unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = (*clause)[i]; - Node node = pm->getCnfProof()->getAtom(lit.getSatVariable()); - if (node.isConst()) { - Assert(node.toExpr() == utils::mkTrue()); - continue; - } - nodes.insert(lit.isNegated() ? node.notNode() : node); - } - - LemmaProofRecipe recipe = pm->getCnfProof()->getProofRecipe(nodes); - recipe.dump("pf::dumpLemmas"); - } - - Debug("pf::dumpLemmas") << "Theory lemma printing DONE" << std::endl << std::endl; -} - -// TODO: this function should be moved into the BV prover. -void LFSCTheoryProofEngine::finalizeBvConflicts(const IdToSatClause& lemmas, std::ostream& os) { - // BitVector theory is special case: must know all conflicts needed - // ahead of time for resolution proof lemmas - std::vector<Expr> bv_lemmas; - - for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) { - const prop::SatClause* clause = it->second; - - std::vector<Expr> conflict; - std::set<Node> conflictNodes; - for(unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = (*clause)[i]; - Node node = ProofManager::currentPM()->getCnfProof()->getAtom(lit.getSatVariable()); - Expr atom = node.toExpr(); - - // The literals (true) and (not false) are omitted from conflicts - if (atom.isConst()) { - Assert(atom == utils::mkTrue() - || (atom == utils::mkFalse() && lit.isNegated())); - continue; - } - - Expr expr_lit = lit.isNegated() ? atom.notExpr() : atom; - conflict.push_back(expr_lit); - conflictNodes.insert(lit.isNegated() ? node.notNode() : node); - } - - LemmaProofRecipe recipe = ProofManager::currentPM()->getCnfProof()->getProofRecipe(conflictNodes); - - unsigned numberOfSteps = recipe.getNumSteps(); - - prop::SatClause currentClause = *clause; - std::vector<Expr> currentClauseExpr = conflict; - - for (unsigned i = 0; i < numberOfSteps; ++i) { - const LemmaProofRecipe::ProofStep* currentStep = recipe.getStep(i); - - if (currentStep->getTheory() != theory::THEORY_BV) { - continue; - } - - // If any rewrites took place, we need to update the conflict clause accordingly - std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(i); - std::map<Node, Node> explanationToMissingAssertion; - std::set<Node>::iterator assertionIt; - for (assertionIt = missingAssertions.begin(); - assertionIt != missingAssertions.end(); - ++assertionIt) { - Node negated = (*assertionIt).negate(); - explanationToMissingAssertion[recipe.getExplanation(negated)] = negated; - } - - currentClause = *clause; - currentClauseExpr = conflict; - - for (unsigned j = 0; j < i; ++j) { - // Literals already used in previous steps need to be negated - Node previousLiteralNode = recipe.getStep(j)->getLiteral(); - - // If this literal is the result of a rewrite, we need to translate it - if (explanationToMissingAssertion.find(previousLiteralNode) != - explanationToMissingAssertion.end()) { - previousLiteralNode = explanationToMissingAssertion[previousLiteralNode]; - } - - Node previousLiteralNodeNegated = previousLiteralNode.negate(); - prop::SatLiteral previousLiteralNegated = - ProofManager::currentPM()->getCnfProof()->getLiteral(previousLiteralNodeNegated); - - currentClause.push_back(previousLiteralNegated); - currentClauseExpr.push_back(previousLiteralNodeNegated.toExpr()); - } - - // If we're in the final step, the last literal is Null and should not be added. - // Otherwise, the current literal does NOT need to be negated - Node currentLiteralNode = currentStep->getLiteral(); - - if (currentLiteralNode != Node()) { - prop::SatLiteral currentLiteral = - ProofManager::currentPM()->getCnfProof()->getLiteral(currentLiteralNode); - - currentClause.push_back(currentLiteral); - currentClauseExpr.push_back(currentLiteralNode.toExpr()); - } - - bv_lemmas.push_back(utils::mkSortedExpr(kind::OR, currentClauseExpr)); - } - } - - proof::BitVectorProof* bv = ProofManager::getBitVectorProof(); - bv->finalizeConflicts(bv_lemmas); -} - -void LFSCTheoryProofEngine::printTheoryLemmas(const IdToSatClause& lemmas, - std::ostream& os, - std::ostream& paren, - ProofLetMap& map) { - os << " ;; Theory Lemmas \n"; - Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: starting" << std::endl; - - if (Debug.isOn("pf::dumpLemmas")) { - dumpTheoryLemmas(lemmas); - } - - // finalizeBvConflicts(lemmas, os, paren, map); - ProofManager::getBitVectorProof()->printBBDeclarationAndCnf(os, paren, map); - - if (options::bitblastMode() == options::BitblastMode::EAGER) - { - Assert(lemmas.size() == 1); - // nothing more to do (no combination with eager so far) - return; - } - - ProofManager* pm = ProofManager::currentPM(); - Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemmas..." << std::endl; - - for (IdToSatClause::const_iterator it = lemmas.begin(); it != lemmas.end(); ++it) { - ClauseId id = it->first; - const prop::SatClause* clause = it->second; - - Debug("pf::tp") << "LFSCTheoryProofEngine::printTheoryLemmas: printing lemma. ID = " - << id << std::endl; - - std::vector<Expr> clause_expr; - std::set<Node> clause_expr_nodes; - for(unsigned i = 0; i < clause->size(); ++i) { - prop::SatLiteral lit = (*clause)[i]; - Node node = pm->getCnfProof()->getAtom(lit.getSatVariable()); - Expr atom = node.toExpr(); - if (atom.isConst()) { - Assert(atom == utils::mkTrue()); - continue; - } - Expr expr_lit = lit.isNegated() ? atom.notExpr(): atom; - clause_expr.push_back(expr_lit); - clause_expr_nodes.insert(lit.isNegated() ? node.notNode() : node); - } - - LemmaProofRecipe recipe = pm->getCnfProof()->getProofRecipe(clause_expr_nodes); - - if (recipe.simpleLemma()) { - // In a simple lemma, there will be no propositional resolution in the end - - Debug("pf::tp") << "Simple lemma" << std::endl; - // Printing the clause as it appears in resolution proof - os << "(satlem _ _ "; - std::ostringstream clause_paren; - pm->getCnfProof()->printClause(*clause, os, clause_paren); - - // Find and handle missing assertions, due to rewrites - std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(0); - if (!missingAssertions.empty()) { - Debug("pf::tp") << "Have missing assertions for this simple lemma!" << std::endl; - } - - std::set<Node>::const_iterator missingAssertion; - for (missingAssertion = missingAssertions.begin(); - missingAssertion != missingAssertions.end(); - ++missingAssertion) { - - Debug("pf::tp") << "Working on missing assertion: " << *missingAssertion << std::endl; - Assert(recipe.wasRewritten(missingAssertion->negate())); - Node explanation = recipe.getExplanation(missingAssertion->negate()).negate(); - Debug("pf::tp") << "Found explanation: " << explanation << std::endl; - - // We have a missing assertion. - // rewriteIt->first is the assertion after the rewrite (the explanation), - // rewriteIt->second is the original assertion that needs to be fed into the theory. - - bool found = false; - unsigned k; - for (k = 0; k < clause_expr.size(); ++k) { - if (clause_expr[k] == explanation.toExpr()) { - found = true; - break; - } - } - - AlwaysAssert(found); - Debug("pf::tp") << "Replacing theory assertion " - << clause_expr[k] - << " with " - << *missingAssertion - << std::endl; - - clause_expr[k] = missingAssertion->toExpr(); - - std::ostringstream rewritten; - - if (missingAssertion->getKind() == kind::NOT && (*missingAssertion)[0].toExpr() == utils::mkFalse()) { - rewritten << "(or_elim_2 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << pm->getLitName(explanation); - rewritten << ") (iff_elim_2 _ _ "; - rewritten << d_assertionToRewrite[missingAssertion->negate()]; - rewritten << "))"; - } - else { - rewritten << "(or_elim_1 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << pm->getLitName(explanation); - rewritten << ") (iff_elim_1 _ _ "; - rewritten << d_assertionToRewrite[missingAssertion->negate()]; - rewritten << "))"; - } - - Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl - << pm->getLitName(*missingAssertion) << " --> " << rewritten.str() - << ", explanation = " << explanation - << std::endl << std::endl; - - pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str()); - } - - // Query the appropriate theory for a proof of this clause - theory::TheoryId theory_id = getTheoryForLemma(clause); - Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl; - getTheoryProof(theory_id)->printTheoryLemmaProof(clause_expr, os, paren, map); - - // Turn rewrite filter OFF - pm->clearRewriteFilters(); - - Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl; - os << clause_paren.str(); - os << "( \\ " << pm->getLemmaClauseName(id) <<"\n"; - paren << "))"; - } else { // This is a composite lemma - - unsigned numberOfSteps = recipe.getNumSteps(); - prop::SatClause currentClause = *clause; - std::vector<Expr> currentClauseExpr = clause_expr; - - for (unsigned i = 0; i < numberOfSteps; ++i) { - const LemmaProofRecipe::ProofStep* currentStep = recipe.getStep(i); - - currentClause = *clause; - currentClauseExpr = clause_expr; - - for (unsigned j = 0; j < i; ++j) { - // Literals already used in previous steps need to be negated - Node previousLiteralNode = recipe.getStep(j)->getLiteral(); - Node previousLiteralNodeNegated = previousLiteralNode.negate(); - prop::SatLiteral previousLiteralNegated = - ProofManager::currentPM()->getCnfProof()->getLiteral(previousLiteralNodeNegated); - currentClause.push_back(previousLiteralNegated); - currentClauseExpr.push_back(previousLiteralNodeNegated.toExpr()); - } - - // If the current literal is NULL, can ignore (final step) - // Otherwise, the current literal does NOT need to be negated - Node currentLiteralNode = currentStep->getLiteral(); - if (currentLiteralNode != Node()) { - prop::SatLiteral currentLiteral = - ProofManager::currentPM()->getCnfProof()->getLiteral(currentLiteralNode); - - currentClause.push_back(currentLiteral); - currentClauseExpr.push_back(currentLiteralNode.toExpr()); - } - - os << "(satlem _ _ "; - std::ostringstream clause_paren; - - pm->getCnfProof()->printClause(currentClause, os, clause_paren); - - // query appropriate theory for proof of clause - theory::TheoryId theory_id = currentStep->getTheory(); - Debug("pf::tp") << "Get theory lemma from " << theory_id << "..." << std::endl; - - std::set<Node> missingAssertions = recipe.getMissingAssertionsForStep(i); - if (!missingAssertions.empty()) { - Debug("pf::tp") << "Have missing assertions for this step!" << std::endl; - } - - // Turn rewrite filter ON - std::set<Node>::const_iterator missingAssertion; - for (missingAssertion = missingAssertions.begin(); - missingAssertion != missingAssertions.end(); - ++missingAssertion) { - - Debug("pf::tp") << "Working on missing assertion: " << *missingAssertion << std::endl; - - Assert(recipe.wasRewritten(missingAssertion->negate())); - Node explanation = recipe.getExplanation(missingAssertion->negate()).negate(); - - Debug("pf::tp") << "Found explanation: " << explanation << std::endl; - - // We have a missing assertion. - // rewriteIt->first is the assertion after the rewrite (the explanation), - // rewriteIt->second is the original assertion that needs to be fed into the theory. - - bool found = false; - unsigned k; - for (k = 0; k < currentClauseExpr.size(); ++k) { - if (currentClauseExpr[k] == explanation.toExpr()) { - found = true; - break; - } - } - - AlwaysAssert(found); - - Debug("pf::tp") << "Replacing theory assertion " - << currentClauseExpr[k] - << " with " - << *missingAssertion - << std::endl; - - currentClauseExpr[k] = missingAssertion->toExpr(); - - std::ostringstream rewritten; - - if (missingAssertion->getKind() == kind::NOT && (*missingAssertion)[0].toExpr() == utils::mkFalse()) { - rewritten << "(or_elim_2 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << pm->getLitName(explanation); - rewritten << ") (iff_elim_2 _ _ "; - rewritten << d_assertionToRewrite[missingAssertion->negate()]; - rewritten << "))"; - } - else { - rewritten << "(or_elim_1 _ _ "; - rewritten << "(not_not_intro _ "; - rewritten << pm->getLitName(explanation); - rewritten << ") (iff_elim_1 _ _ "; - rewritten << d_assertionToRewrite[missingAssertion->negate()]; - rewritten << "))"; - } - - Debug("pf::tp") << "Setting a rewrite filter for this proof: " << std::endl - << pm->getLitName(*missingAssertion) << " --> " << rewritten.str() - << "explanation = " << explanation - << std::endl << std::endl; - - pm->addRewriteFilter(pm->getLitName(*missingAssertion), rewritten.str()); - } - - getTheoryProof(theory_id)->printTheoryLemmaProof(currentClauseExpr, os, paren, map); - - // Turn rewrite filter OFF - pm->clearRewriteFilters(); - - Debug("pf::tp") << "Get theory lemma from " << theory_id << "... DONE!" << std::endl; - os << clause_paren.str(); - os << "( \\ " << pm->getLemmaClauseName(id) << "s" << i <<"\n"; - paren << "))"; - } - - Assert(numberOfSteps >= 2); - - os << "(satlem_simplify _ _ _ "; - for (unsigned i = 0; i < numberOfSteps - 1; ++i) { - // Resolve step i with step i + 1 - if (recipe.getStep(i)->getLiteral().getKind() == kind::NOT) { - os << "(Q _ _ "; - } else { - os << "(R _ _ "; - } - - os << pm->getLemmaClauseName(id) << "s" << i; - os << " "; - } - - os << pm->getLemmaClauseName(id) << "s" << numberOfSteps - 1 << " "; - - prop::SatLiteral v; - for (int i = numberOfSteps - 2; i >= 0; --i) { - v = ProofManager::currentPM()->getCnfProof()->getLiteral(recipe.getStep(i)->getLiteral()); - os << ProofManager::getVarName(v.getSatVariable(), "") << ") "; - } - - os << "( \\ " << pm->getLemmaClauseName(id) << "\n"; - paren << "))"; - } - } -} - -void LFSCTheoryProofEngine::printBoundTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - Debug("pf::tp") << "LFSCTheoryProofEngine::printBoundTerm( " << term << " ) " << std::endl; - - // Since let-abbreviated terms are abbreviated with their default type, only - // use the let map if there is no expectedType or the expectedType matches - // the default. - if (expectedType.isNull() - || TypeNode::fromType(term.getType()) == expectedType) - { - ProofLetMap::const_iterator it = map.find(term); - if (it != map.end()) - { - unsigned id = it->second.id; - unsigned count = it->second.count; - - if (count > LET_COUNT) - { - os << "let" << id; - Debug("pf::tp::letmap") << "Using let map for " << term << std::endl; - return; - } - } - } - Debug("pf::tp::letmap") << "Skipping let map for " << term << std::endl; - - printTheoryTerm(term, os, map, expectedType); -} - -void LFSCTheoryProofEngine::printBoundFormula(Expr term, - std::ostream& os, - const ProofLetMap& map) -{ - Assert(term.getType().isBoolean() or term.getType().isPredicate()); - bool wrapWithBoolToPred = term.getType().isBoolean() and printsAsBool(term); - if (wrapWithBoolToPred) - { - os << "(p_app "; - } - printBoundTerm(term, os, map); - if (wrapWithBoolToPred) - { - os << ")"; - } -} - -void LFSCTheoryProofEngine::printCoreTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - if (term.isVariable()) { - os << ProofManager::sanitize(term); - return; - } - - Kind k = term.getKind(); - - switch(k) { - case kind::ITE: { - TypeNode armType = expectedType.isNull() - ? TypeNode::fromType(term.getType()) - : expectedType; - bool useFormulaType = term.getType().isBoolean(); - Assert(term[1].getType().isSubtypeOf(term.getType())); - Assert(term[2].getType().isSubtypeOf(term.getType())); - os << (useFormulaType ? "(ifte " : "(ite _ "); - - printBoundFormula(term[0], os, map); - os << " "; - if (useFormulaType) - { - printBoundFormula(term[1], os, map); - } - else - { - printBoundTerm(term[1], os, map, armType); - } - os << " "; - if (useFormulaType) - { - printBoundFormula(term[2], os, map); - } - else - { - printBoundTerm(term[2], os, map, armType); - } - os << ")"; - return; - } - - case kind::EQUAL: { - bool booleanCase = term[0].getType().isBoolean(); - TypeNode armType = equalityType(term[0], term[1]); - - os << "("; - if (booleanCase) { - os << "iff "; - } else { - os << "= "; - printSort(term[0].getType(), os); - os << " "; - } - - if (booleanCase && printsAsBool(term[0])) os << "(p_app "; - printBoundTerm(term[0], os, map, armType); - if (booleanCase && printsAsBool(term[0])) os << ")"; - - os << " "; - - if (booleanCase && printsAsBool(term[1])) os << "(p_app "; - printBoundTerm(term[1], os, map, armType); - if (booleanCase && printsAsBool(term[1])) os << ") "; - os << ")"; - - return; - } - - case kind::DISTINCT: - { - // Distinct nodes can have any number of chidlren. - Assert(term.getNumChildren() >= 2); - TypeNode armType = equalityType(term[0], term[1]); - - if (term.getNumChildren() == 2) { - os << "(not (= "; - printSort(term[0].getType(), os); - os << " "; - printBoundTerm(term[0], os, map, armType); - os << " "; - printBoundTerm(term[1], os, map, armType); - os << "))"; - } else { - unsigned numOfPairs = term.getNumChildren() * (term.getNumChildren() - 1) / 2; - for (unsigned i = 1; i < numOfPairs; ++i) { - os << "(and "; - } - - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - for (unsigned j = i + 1; j < term.getNumChildren(); ++j) { - armType = equalityType(term[i], term[j]); - if ((i != 0) || (j != 1)) { - os << "(not (= "; - printSort(term[0].getType(), os); - os << " "; - printBoundTerm(term[i], os, map, armType); - os << " "; - printBoundTerm(term[j], os, map, armType); - os << ")))"; - } else { - os << "(not (= "; - printSort(term[0].getType(), os); - os << " "; - printBoundTerm(term[0], os, map, armType); - os << " "; - printBoundTerm(term[1], os, map, armType); - os << "))"; - } - } - } - } - return; - } - - default: Unhandled() << k; - } -} - -void TheoryProof::printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) { - // Default method for replaying proofs: assert (negated) literals back to a fresh copy of the theory - Assert(d_theory != NULL); - - context::UserContext fakeContext; - ProofOutputChannel oc; - theory::Valuation v(NULL); - //make new copy of theory - theory::Theory* th; - Trace("pf::tp") << ";; Print theory lemma proof, theory id = " << d_theory->getId() << std::endl; - - if (d_theory->getId()==theory::THEORY_UF) { - th = new theory::uf::TheoryUF(&fakeContext, - &fakeContext, - oc, - v, - ProofManager::currentPM()->getLogicInfo(), - nullptr, - "replay::"); - } else if (d_theory->getId()==theory::THEORY_ARRAYS) { - th = new theory::arrays::TheoryArrays( - &fakeContext, - &fakeContext, - oc, - v, - ProofManager::currentPM()->getLogicInfo(), - nullptr, - "replay::"); - } else if (d_theory->getId() == theory::THEORY_ARITH) { - Trace("theory-proof-debug") << "Arith proofs currently not supported. Use 'trust'" << std::endl; - os << " (clausify_false trust)"; - return; - } else { - InternalError() << "can't generate theory-proof for " - << ProofManager::currentPM()->getLogic(); - } - // must perform initialization on the theory - if (th != nullptr) - { - // finish init, standalone version - th->finishInitStandalone(); - } - - Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->ProduceProofs()" << std::endl; - th->produceProofs(); - Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->ProduceProofs() DONE" << std::endl; - - MyPreRegisterVisitor preRegVisitor(th); - for (unsigned i=0; i<lemma.size(); i++) { - Node strippedLit = (lemma[i].getKind() == kind::NOT) ? lemma[i][0] : lemma[i]; - if (strippedLit.getKind() == kind::EQUAL || - d_theory->getId() == theory::Theory::theoryOf(strippedLit)) { - Node lit = Node::fromExpr( lemma[i] ).negate(); - Trace("pf::tp") << "; preregistering and asserting " << lit << std::endl; - NodeVisitor<MyPreRegisterVisitor>::run(preRegVisitor, lit); - th->assertFact(lit, false); - } - } - - Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - calling th->check()" << std::endl; - th->check(theory::Theory::EFFORT_FULL); - Debug("pf::tp") << "TheoryProof::printTheoryLemmaProof - th->check() DONE" << std::endl; - - if(!oc.hasConflict()) { - Trace("pf::tp") << "; conflict is null" << std::endl; - Node lastLemma = oc.getLastLemma(); - Assert(!lastLemma.isNull()); - Trace("pf::tp") << "; ++ but got lemma: " << lastLemma << std::endl; - - if (lastLemma.getKind() == kind::OR) { - Debug("pf::tp") << "OR lemma. Negating each child separately" << std::endl; - for (unsigned i = 0; i < lastLemma.getNumChildren(); ++i) { - if (lastLemma[i].getKind() == kind::NOT) { - Trace("pf::tp") << "; asserting fact: " << lastLemma[i][0] << std::endl; - th->assertFact(lastLemma[i][0], false); - } - else { - Trace("pf::tp") << "; asserting fact: " << lastLemma[i].notNode() << std::endl; - th->assertFact(lastLemma[i].notNode(), false); - } - } - } else { - Unreachable(); - - Assert(oc.getLastLemma().getKind() == kind::NOT); - Debug("pf::tp") << "NOT lemma" << std::endl; - Trace("pf::tp") << "; asserting fact: " << oc.getLastLemma()[0] - << std::endl; - th->assertFact(oc.getLastLemma()[0], false); - } - - // Trace("pf::tp") << "; ++ but got lemma: " << oc.d_lemma << std::endl; - // Trace("pf::tp") << "; asserting " << oc.d_lemma[1].negate() << std::endl; - // th->assertFact(oc.d_lemma[1].negate(), false); - - // - th->check(theory::Theory::EFFORT_FULL); - } else { - Debug("pf::tp") << "Calling oc.d_proof->toStream(os)" << std::endl; - oc.getConflictProof().toStream(os, map); - Debug("pf::tp") << "Calling oc.d_proof->toStream(os) -- DONE!" << std::endl; - } - - Debug("pf::tp") << "About to delete the theory solver used for proving the lemma... " << std::endl; - delete th; - Debug("pf::tp") << "About to delete the theory solver used for proving the lemma: DONE! " << std::endl; -} - -bool TheoryProofEngine::supportedTheory(theory::TheoryId id) { - return (id == theory::THEORY_ARRAYS || - id == theory::THEORY_ARITH || - id == theory::THEORY_BV || - id == theory::THEORY_UF || - id == theory::THEORY_BOOL); -} - -bool TheoryProofEngine::printsAsBool(const Node &n) { - if (!n.getType().isBoolean()) { - return false; - } - - theory::TheoryId theory_id = theory::Theory::theoryOf(n); - return getTheoryProof(theory_id)->printsAsBool(n); -} - -BooleanProof::BooleanProof(TheoryProofEngine* proofEngine) - : TheoryProof(NULL, proofEngine) -{} - -void BooleanProof::registerTerm(Expr term) { - Assert(term.getType().isBoolean()); - - if (term.isVariable() && d_declarations.find(term) == d_declarations.end()) { - d_declarations.insert(term); - return; - } - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - d_proofEngine->registerTerm(term[i]); - } -} - -theory::TheoryId BooleanProof::getTheoryId() { return theory::THEORY_BOOL; } -void LFSCBooleanProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { - Node falseNode = NodeManager::currentNM()->mkConst(false); - Node trueNode = NodeManager::currentNM()->mkConst(true); - - Assert(c1 == falseNode.toExpr() || c1 == trueNode.toExpr()); - Assert(c2 == falseNode.toExpr() || c2 == trueNode.toExpr()); - Assert(c1 != c2); - - if (c1 == trueNode.toExpr()) - os << "t_t_neq_f"; - else - os << "(negsymm _ _ _ t_t_neq_f)"; -} - -void LFSCBooleanProof::printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - Assert(term.getType().isBoolean()); - if (term.isVariable()) { - os << ProofManager::sanitize(term); - return; - } - - Kind k = term.getKind(); - switch(k) { - case kind::OR: - case kind::AND: - if (options::lfscLetification() && term.getNumChildren() > 2) { - // If letification is on, the entire term is probably a let expression. - // However, we need to transform it from (and a b c) into (and a (and b c)) form first. - Node currentExpression = term[term.getNumChildren() - 1]; - for (int i = term.getNumChildren() - 2; i >= 0; --i) { - NodeBuilder<> builder(k); - builder << term[i]; - builder << currentExpression.toExpr(); - currentExpression = builder; - } - - // The let map should already have the current expression. - ProofLetMap::const_iterator it = map.find(currentExpression.toExpr()); - if (it != map.end()) { - unsigned id = it->second.id; - unsigned count = it->second.count; - - if (count > LET_COUNT) { - os << "let" << id; - break; - } - } - } - - // If letification is off or there were 2 children, same treatment as the other cases. - CVC4_FALLTHROUGH; - case kind::XOR: - case kind::IMPLIES: - case kind::NOT: - // print the Boolean operators - os << "(" << utils::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 < term.getNumChildren(); ++i) { - - if (printsAsBool(term[i])) os << "(p_app "; - d_proofEngine->printBoundTerm(term[i], os, map); - if (printsAsBool(term[i])) os << ")"; - - os << " "; - if(i < term.getNumChildren() - 2) { - os << "(" << utils::toLFSCKind(k) << " "; - paren << ")"; - } - } - os << paren.str() << ")"; - } else { - // this is for binary and unary operators - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - os << " "; - if (printsAsBool(term[i])) os << "(p_app "; - d_proofEngine->printBoundTerm(term[i], os, map); - if (printsAsBool(term[i])) os << ")"; - } - os << ")"; - } - return; - - case kind::CONST_BOOLEAN: - os << (term.getConst<bool>() ? "true" : "false"); - return; - - default: Unhandled() << k; - } -} - -void LFSCBooleanProof::printOwnedSort(Type type, std::ostream& os) { - Assert(type.isBoolean()); - os << "Bool"; -} - -void LFSCBooleanProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { - // Nothing to do here at this point. -} - -void LFSCBooleanProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { - for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { - Expr term = *it; - - os << "(% " << ProofManager::sanitize(term) << " (term "; - printSort(term.getType(), os); - os <<")\n"; - paren <<")"; - } -} - -void LFSCBooleanProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { - // Nothing to do here at this point. -} - -void LFSCBooleanProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { - // Nothing to do here at this point. -} - -void LFSCBooleanProof::printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) { - Unreachable() << "No boolean lemmas yet!"; -} - -bool LFSCBooleanProof::printsAsBool(const Node &n) -{ - Kind k = n.getKind(); - switch (k) { - case kind::BOOLEAN_TERM_VARIABLE: - case kind::VARIABLE: - return true; - - default: - return false; - } -} - -void TheoryProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { - // By default, we just print a trust statement. Specific theories can implement - // better proofs. - - os << "(trust_f (not (= _ "; - d_proofEngine->printBoundTerm(c1, os, globalLetMap); - os << " "; - d_proofEngine->printBoundTerm(c2, os, globalLetMap); - os << ")))"; -} - -void TheoryProof::printRewriteProof(std::ostream& os, const Node &n1, const Node &n2) { - // This is the default for a rewrite proof: just a trust statement. - ProofLetMap emptyMap; - os << "(trust_f (iff "; - d_proofEngine->printBoundTerm(n1.toExpr(), os, emptyMap); - os << " "; - d_proofEngine->printBoundTerm(n2.toExpr(), os, emptyMap); - os << "))"; -} - -void TheoryProof::printOwnedTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - this->printOwnedTermAsType(term, os, map, expectedType); -} - -TypeNode TheoryProof::equalityType(const Expr& left, const Expr& right) -{ - Assert(left.getType() == right.getType()) - << "TheoryProof::equalityType(" << left << ", " << right << "):" << std::endl - << "types disagree:" << std::endl - << "\tleft: " << left.getType() << std::endl - << "\tright:" << right.getType(); - return TypeNode::fromType(left.getType()); -} - -bool TheoryProof::match(TNode n1, TNode n2) -{ - theory::TheoryId theoryId = this->getTheoryId(); - ProofManager* pm = ProofManager::currentPM(); - bool ufProof = (theoryId == theory::THEORY_UF); - Debug(ufProof ? "pf::uf" : "mgd") << "match " << n1 << " " << n2 << std::endl; - if (pm->hasOp(n1)) - { - n1 = pm->lookupOp(n1); - } - if (pm->hasOp(n2)) - { - n2 = pm->lookupOp(n2); - } - Debug(ufProof ? "pf::uf" : "mgd") << "+ match " << n1 << " " << n2 - << std::endl; - if (!ufProof) - { - Debug("pf::array") << "+ match: step 1" << std::endl; - } - if (n1 == n2) - { - return true; - } - - if (n1.getType().isFunction() && n2.hasOperator()) - { - if (pm->hasOp(n2.getOperator())) - { - return n1 == pm->lookupOp(n2.getOperator()); - } - else - { - return n1 == n2.getOperator(); - } - } - - if (n2.getType().isFunction() && n1.hasOperator()) - { - if (pm->hasOp(n1.getOperator())) - { - return n2 == pm->lookupOp(n1.getOperator()); - } - else - { - return n2 == n1.getOperator(); - } - } - - if (n1.hasOperator() && n2.hasOperator() - && n1.getOperator() != n2.getOperator()) - { - if (ufProof - || !((n1.getKind() == kind::SELECT - && n2.getKind() == kind::PARTIAL_SELECT_0) - || (n1.getKind() == kind::SELECT - && n2.getKind() == kind::PARTIAL_SELECT_1) - || (n1.getKind() == kind::PARTIAL_SELECT_1 - && n2.getKind() == kind::SELECT) - || (n1.getKind() == kind::PARTIAL_SELECT_1 - && n2.getKind() == kind::PARTIAL_SELECT_0) - || (n1.getKind() == kind::PARTIAL_SELECT_0 - && n2.getKind() == kind::SELECT) - || (n1.getKind() == kind::PARTIAL_SELECT_0 - && n2.getKind() == kind::PARTIAL_SELECT_1))) - { - return false; - } - } - - for (size_t i = 0; i < n1.getNumChildren() && i < n2.getNumChildren(); ++i) - { - if (n1[i] != n2[i]) - { - return false; - } - } - - return true; -} - -int TheoryProof::assertAndPrint( - const theory::eq::EqProof& pf, - const ProofLetMap& map, - std::shared_ptr<theory::eq::EqProof> subTrans, - theory::eq::EqProof::PrettyPrinter* pPrettyPrinter) -{ - theory::TheoryId theoryId = getTheoryId(); - int neg = -1; - Assert(theoryId == theory::THEORY_UF || theoryId == theory::THEORY_ARRAYS); - bool ufProof = (theoryId == theory::THEORY_UF); - std::string theoryName = theory::getStatsPrefix(theoryId); - pf.debug_print(("pf::" + theoryName).c_str(), 0, pPrettyPrinter); - Debug("pf::" + theoryName) << std::endl; - - Assert(pf.d_id == theory::eq::MERGED_THROUGH_TRANS); - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.size() >= 2); - - subTrans->d_id = theory::eq::MERGED_THROUGH_TRANS; - subTrans->d_node = pf.d_node; - - size_t i = 0; - while (i < pf.d_children.size()) - { - // special treatment for uf and not for array - if (ufProof - || pf.d_children[i]->d_id != theory::eq::MERGED_THROUGH_CONGRUENCE) - { - pf.d_children[i]->d_node = simplifyBooleanNode(pf.d_children[i]->d_node); - } - - // Look for the negative clause, with which we will form a contradiction. - if (!pf.d_children[i]->d_node.isNull() - && pf.d_children[i]->d_node.getKind() == kind::NOT) - { - Assert(neg < 0); - (neg) = i; - ++i; - } - - // Handle congruence closures over equalities. - else if (pf.d_children[i]->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE - && pf.d_children[i]->d_node.isNull()) - { - Debug("pf::" + theoryName) << "Handling congruence over equalities" - << std::endl; - - // Gather the sequence of consecutive congruence closures. - std::vector<std::shared_ptr<const theory::eq::EqProof>> - congruenceClosures; - unsigned count; - Debug("pf::" + theoryName) << "Collecting congruence sequence" - << std::endl; - for (count = 0; i + count < pf.d_children.size() - && pf.d_children[i + count]->d_id - == theory::eq::MERGED_THROUGH_CONGRUENCE - && pf.d_children[i + count]->d_node.isNull(); - ++count) - { - Debug("pf::" + theoryName) << "Found a congruence: " << std::endl; - pf.d_children[i + count]->debug_print( - ("pf::" + theoryName).c_str(), 0, pPrettyPrinter); - congruenceClosures.push_back(pf.d_children[i + count]); - } - - Debug("pf::" + theoryName) - << "Total number of congruences found: " << congruenceClosures.size() - << std::endl; - - // Determine if the "target" of the congruence sequence appears right - // before or right after the sequence. - bool targetAppearsBefore = true; - bool targetAppearsAfter = true; - - if ((i == 0) || (i == 1 && neg == 0)) - { - Debug("pf::" + theoryName) << "Target does not appear before" - << std::endl; - targetAppearsBefore = false; - } - - if ((i + count >= pf.d_children.size()) - || (!pf.d_children[i + count]->d_node.isNull() - && pf.d_children[i + count]->d_node.getKind() == kind::NOT)) - { - Debug("pf::" + theoryName) << "Target does not appear after" - << std::endl; - targetAppearsAfter = false; - } - - // Flow changes between uf and array - if (ufProof) - { - // Assert that we have precisely at least one possible clause. - Assert(targetAppearsBefore || targetAppearsAfter); - - // If both are valid, assume the one after the sequence is correct - if (targetAppearsAfter && targetAppearsBefore) - { - targetAppearsBefore = false; - } - } - else - { // not a uf proof - // Assert that we have precisely one target clause. - Assert(targetAppearsBefore != targetAppearsAfter); - } - - // Begin breaking up the congruences and ordering the equalities - // correctly. - std::vector<std::shared_ptr<theory::eq::EqProof>> orderedEqualities; - - // Insert target clause first. - if (targetAppearsBefore) - { - orderedEqualities.push_back(pf.d_children[i - 1]); - // The target has already been added to subTrans; remove it. - subTrans->d_children.pop_back(); - } - else - { - orderedEqualities.push_back(pf.d_children[i + count]); - } - - // Start with the congruence closure closest to the target clause, and - // work our way back/forward. - if (targetAppearsBefore) - { - for (unsigned j = 0; j < count; ++j) - { - if (pf.d_children[i + j]->d_children[0]->d_id - != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert(orderedEqualities.begin(), - pf.d_children[i + j]->d_children[0]); - if (pf.d_children[i + j]->d_children[1]->d_id - != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert(orderedEqualities.end(), - pf.d_children[i + j]->d_children[1]); - } - } - else - { - for (unsigned j = 0; j < count; ++j) - { - if (pf.d_children[i + count - 1 - j]->d_children[0]->d_id - != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert( - orderedEqualities.begin(), - pf.d_children[i + count - 1 - j]->d_children[0]); - if (pf.d_children[i + count - 1 - j]->d_children[1]->d_id - != theory::eq::MERGED_THROUGH_REFLEXIVITY) - orderedEqualities.insert( - orderedEqualities.end(), - pf.d_children[i + count - 1 - j]->d_children[1]); - } - } - - // Copy the result into the main transitivity proof. - subTrans->d_children.insert(subTrans->d_children.end(), - orderedEqualities.begin(), - orderedEqualities.end()); - - // Increase i to skip over the children that have been processed. - i += count; - if (targetAppearsAfter) - { - ++i; - } - } - - // Else, just copy the child proof as is - else - { - subTrans->d_children.push_back(pf.d_children[i]); - ++i; - } - } - - bool disequalityFound = (neg >= 0); - if (!disequalityFound) - { - Debug("pf::" + theoryName) - << "A disequality was NOT found. UNSAT due to merged constants" - << std::endl; - Debug("pf::" + theoryName) << "Proof for: " << pf.d_node << std::endl; - Assert(pf.d_node.getKind() == kind::EQUAL); - Assert(pf.d_node.getNumChildren() == 2); - Assert(pf.d_node[0].isConst() && pf.d_node[1].isConst()); - } - return neg; -} - -std::pair<Node, Node> TheoryProof::identicalEqualitiesPrinterHelper( - bool evenLengthSequence, - bool sequenceOver, - const theory::eq::EqProof& pf, - const ProofLetMap& map, - const std::string subproofStr, - std::stringstream* outStream, - Node n, - Node nodeAfterEqualitySequence) -{ - theory::TheoryId theoryId = getTheoryId(); - Assert(theoryId == theory::THEORY_UF || theoryId == theory::THEORY_ARRAYS); - bool ufProof = (theoryId == theory::THEORY_UF); - std::string theoryName = theory::getStatsPrefix(theoryId); - if (evenLengthSequence) - { - // If the length is even, we need to apply transitivity for the "correct" - // hand of the equality. - - Debug("pf::" + theoryName) << "Equality sequence of even length" - << std::endl; - Debug("pf::" + theoryName) << "n1 is: " << n << std::endl; - Debug("pf::" + theoryName) << "pf-d_node is: " << pf.d_node << std::endl; - Debug("pf::" + theoryName) << "Next node is: " << nodeAfterEqualitySequence - << std::endl; - - (*outStream) << "(trans _ _ _ _ "; - - // If the sequence is at the very end of the transitivity proof, use - // pf.d_node to guide us. - if (!sequenceOver) - { - if (match(n[0], pf.d_node[0])) - { - n = n[0].eqNode(n[0]); - (*outStream) << subproofStr << " (symm _ _ _ " << subproofStr << ")"; - } - else if (match(n[1], pf.d_node[1])) - { - n = n[1].eqNode(n[1]); - (*outStream) << " (symm _ _ _ " << subproofStr << ")" << subproofStr; - } - else - { - Debug("pf::" + theoryName) << "Error: identical equalities over, but " - "hands don't match what we're proving." - << std::endl; - Assert(false); - } - } - else - { - // We have a "next node". Use it to guide us. - if (!ufProof && nodeAfterEqualitySequence.getKind() == kind::NOT) - { - nodeAfterEqualitySequence = nodeAfterEqualitySequence[0]; - } - - Assert(nodeAfterEqualitySequence.getKind() == kind::EQUAL); - - if ((n[0] == nodeAfterEqualitySequence[0]) - || (n[0] == nodeAfterEqualitySequence[1])) - { - // Eliminate n[1] - (*outStream) << subproofStr << " (symm _ _ _ " << subproofStr << ")"; - n = n[0].eqNode(n[0]); - } - else if ((n[1] == nodeAfterEqualitySequence[0]) - || (n[1] == nodeAfterEqualitySequence[1])) - { - // Eliminate n[0] - (*outStream) << " (symm _ _ _ " << subproofStr << ")" << subproofStr; - n = n[1].eqNode(n[1]); - } - else - { - Debug("pf::" + theoryName) << "Error: even length sequence, but I " - "don't know which hand to keep!" - << std::endl; - Assert(false); - } - } - - (*outStream) << ")"; - } - else - { - Debug("pf::" + theoryName) << "Equality sequence length is odd!" - << std::endl; - (*outStream).str(subproofStr); - } - - Debug("pf::" + theoryName) << "Have proven: " << n << std::endl; - return std::make_pair<Node&, Node&>(n, nodeAfterEqualitySequence); -} - -} /* namespace CVC4 */ diff --git a/src/proof/theory_proof.h b/src/proof/theory_proof.h deleted file mode 100644 index dd5fe0326..000000000 --- a/src/proof/theory_proof.h +++ /dev/null @@ -1,510 +0,0 @@ -/********************* */ -/*! \file theory_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Liana Hadarean, Guy Katz, Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "cvc4_private.h" - -#ifndef CVC4__THEORY_PROOF_H -#define CVC4__THEORY_PROOF_H - -#include <iosfwd> -#include <unordered_map> -#include <unordered_set> - -#include "expr/expr.h" -#include "expr/type_node.h" -#include "proof/clause_id.h" -#include "proof/proof_utils.h" -#include "prop/sat_solver_types.h" -#include "theory/uf/equality_engine.h" -#include "util/proof.h" -namespace CVC4 { - -namespace theory { -class Theory; -} /* namespace CVC4::theory */ - -typedef std::unordered_map < ClauseId, prop::SatClause* > IdToSatClause; - -class TheoryProof; - -typedef std::unordered_set<Expr, ExprHashFunction > ExprSet; -typedef std::map<theory::TheoryId, TheoryProof* > TheoryProofTable; - -typedef std::set<theory::TheoryId> TheoryIdSet; -typedef std::map<Expr, TheoryIdSet> ExprToTheoryIds; - -class TheoryProofEngine { -protected: - ExprSet d_registrationCache; - TheoryProofTable d_theoryProofTable; - ExprToTheoryIds d_exprToTheoryIds; - - /** - * Returns whether the theory is currently supported in proof - * production mode. - */ - bool supportedTheory(theory::TheoryId id); -public: - - TheoryProofEngine(); - virtual ~TheoryProofEngine(); - - /** - * Print the theory term (could be an atom) by delegating to the proper theory. - * - * @param term - * @param os - */ - virtual void printLetTerm(Expr term, std::ostream& os) = 0; - - /** - * Print a term in some (core or non-core) theory - * - * @param term expression representing term - * @param os output stream - * @param expectedType The type that this is expected to have in a parent - * node. Null if there are no such requirements. This is useful for requesting - * type conversions from the theory. e.g. in (5.5 == 4) the right-hand-side - * should be converted to a real. - * - * The first version of this function has a default value for expectedType - * (null) The second version is virtual. - * - * They are split to avoid mixing virtual function and default argument - * values, which behave weirdly when combined. - */ - void printBoundTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType = TypeNode()) - { - this->printBoundTermAsType(term, os, map, expectedType); - } - virtual void printBoundTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) = 0; - - /** - * Print the proof representation of the given sort. - * - * @param os - */ - virtual void printSort(Type type, std::ostream& os) = 0; - - /** - * Go over the assertions and register all terms with the theories. - * - * @param os - * @param paren closing parenthesis - */ - virtual void registerTermsFromAssertions() = 0; - - /** - * Print the theory assertions (arbitrary formulas over - * theory atoms) - * - * @param os - * @param paren closing parenthesis - */ - virtual void printAssertions(std::ostream& os, std::ostream& paren) = 0; - /** - * Print variable declarations that need to appear within the proof, - * e.g. skolemized variables. - * - * @param os - * @param paren closing parenthesis - */ - virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; - - /** - * Print aliasing declarations. - * - * @param os - * @param paren closing parenthesis - */ - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) = 0; - - /** - * Print proofs of all the theory lemmas (must prove - * actual clause used in resolution proof). - * - * @param os - * @param paren - */ - virtual void printTheoryLemmas(const IdToSatClause& lemmas, std::ostream& os, - std::ostream& paren, ProofLetMap& map) = 0; - - /** - * Register theory atom (ensures all terms and atoms are declared). - * - * @param atom - */ - void registerTerm(Expr atom); - - /** - * Ensures that a theory proof class for the given theory is created. - * This method can be invoked regardless of whether the "proof" option - * has been set. - * - * @param theory - */ - void registerTheory(theory::Theory* theory); - /** - * Additional configuration of the theory proof class for the given theory. - * This method should only be invoked when the "proof" option has been set. - * - * @param theory - */ - void finishRegisterTheory(theory::Theory* theory); - - theory::TheoryId getTheoryForLemma(const prop::SatClause* clause); - TheoryProof* getTheoryProof(theory::TheoryId id); - - void markTermForFutureRegistration(Expr term, theory::TheoryId id); - - void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap); - - /** - * Print a term in some non-core theory - * - * @param term expression representing term - * @param os output stream - * @param expectedType The type that this is expected to have in a parent - * node. Null if there are no such requirements. This is useful for requesting - * type conversions from the theory. e.g. in (5.5 == 4) the right-hand-side - * should be converted to a real. - * - * The first version of this function has a default value for expectedType - * (null) The second version is virtual. - * - * They are split to avoid mixing virtual function and default argument - * values, which behave weirdly when combined. - */ - void printTheoryTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType = TypeNode()); - virtual void printTheoryTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) = 0; - /** - * Calls `TheoryProof::equalityType` on the appropriate theory. - */ - TypeNode equalityType(const Expr& left, const Expr& right); - - bool printsAsBool(const Node &n); -}; - -class LFSCTheoryProofEngine : public TheoryProofEngine { - void bind(Expr term, ProofLetMap& map, Bindings& let_order); -public: - LFSCTheoryProofEngine() - : TheoryProofEngine() {} - - void printTheoryTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) override; - - void registerTermsFromAssertions() override; - void printSortDeclarations(std::ostream& os, std::ostream& paren); - void printTermDeclarations(std::ostream& os, std::ostream& paren); - void printCoreTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType = TypeNode()); - void printLetTerm(Expr term, std::ostream& os) override; - void printBoundTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) override; - void printAssertions(std::ostream& os, std::ostream& paren) override; - void printLemmaRewrites(NodePairSet& rewrites, - std::ostream& os, - std::ostream& paren); - void printDeferredDeclarations(std::ostream& os, - std::ostream& paren) override; - void printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) override; - void printTheoryLemmas(const IdToSatClause& lemmas, - std::ostream& os, - std::ostream& paren, - ProofLetMap& map) override; - void printSort(Type type, std::ostream& os) override; - - void performExtraRegistrations(); - - void finalizeBvConflicts(const IdToSatClause& lemmas, std::ostream& os); - -private: - static void dumpTheoryLemmas(const IdToSatClause& lemmas); - - // Prints this boolean term as a formula. - // If necessary, it prints a wrapper converting a `Bool`-sorted term to a - // formula. - void printBoundFormula(Expr term, std::ostream& os, const ProofLetMap& map); - - // TODO: this function should be moved into the BV prover. - - std::map<Node, std::string> d_assertionToRewrite; -}; - -class TheoryProof { -protected: - // Pointer to the theory for this proof - theory::Theory* d_theory; - TheoryProofEngine* d_proofEngine; - virtual theory::TheoryId getTheoryId() = 0; - - public: - TheoryProof(theory::Theory* th, TheoryProofEngine* proofEngine) - : d_theory(th) - , d_proofEngine(proofEngine) - {} - virtual ~TheoryProof() {}; - /** - * Print a term belonging some theory, not necessarily this one. - * - * @param term expresion representing term - * @param os output stream - */ - void printTerm(Expr term, std::ostream& os, const ProofLetMap& map) { - d_proofEngine->printBoundTerm(term, os, map); - } - /** - * Print a term belonging to THIS theory. - * - * @param term expression representing term - * @param os output stream - * @param expectedType The type that this is expected to have in a parent - * node. Null if there are no such requirements. This is useful for requesting - * type conversions from the theory. e.g. in (5.5 == 4) the right-hand-side - * should be converted to a real. - * - * The first version of this function has a default value for expectedType - * (null) The second version is virtual. - * - * They are split to avoid mixing virtual function and default argument - * values, which behave weirdly when combined. - */ - void printOwnedTerm(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType = TypeNode()); - - virtual void printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) = 0; - - /** - * Return the type (at the SMT level, the sort) of an equality or disequality - * between `left` and `right`. - * - * The default implementation asserts that the two have the same type, and - * returns it. - * - * A theory may want to do something else. - * - * For example, the theory of arithmetic allows equalities between Reals and - * Integers. In this case the integer is upcast to a real, and the equality - * is over reals. - */ - virtual TypeNode equalityType(const Expr& left, const Expr& right); - - /** - * Print the proof representation of the given type that belongs to some theory. - * - * @param type - * @param os - */ - void printSort(Type type, std::ostream& os) { - d_proofEngine->printSort(type, os); - } - - // congrence matching term helper - bool match(TNode n1, TNode n2); - - /** - * Helper function for ProofUF::toStreamRecLFSC and - * ProofArray::toStreamRecLFSC - * Inputs: - * - pf: equality engine proof - * - map: A map for the let-expressions in the proof - * - subTrans: main transitivity proof part - * - pPrettyPrinter: optional pretty printer for sub-proofs - * returns: - * - the index of the contradicting node in pf. - * */ - int assertAndPrint( - const theory::eq::EqProof& pf, - const ProofLetMap& map, - std::shared_ptr<theory::eq::EqProof> subTrans, - theory::eq::EqProof::PrettyPrinter* pPrettyPrinter = nullptr); - - /** - * Helper function for ProofUF::toStreamRecLFSC and - * ProofArray::toStreamRecLFSC - * Inputs: - * - evenLengthSequence: true iff the length of the sequence - * of the identical equalities is even. - * - sequenceOver: have we reached the last equality of this sequence? - * - pf: equality engine proof - * - map: A map for the let-expressions in the proof - * - subproofStr: current stringstream content - * - outStream: output stream to which the proof is printed - * - n: transitivity sub-proof - * - nodeAfterEqualitySequence: The node after the identical sequence. - * Returns: - * A pair of nodes, that are the updated nodes n and nodeAfterEqualitySequence - * - */ - std::pair<Node, Node> identicalEqualitiesPrinterHelper( - bool evenLengthSequence, - bool sequenceOver, - const theory::eq::EqProof& pf, - const ProofLetMap& map, - const std::string subproofStr, - std::stringstream* outStream, - Node n, - Node nodeAfterEqualitySequence); - - /** - * Print the proof representation of the given type that belongs to THIS theory. - * - * @param type - * @param os - */ - virtual void printOwnedSort(Type type, std::ostream& os) = 0; - /** - * Print a proof for the theory lemmas. Must prove - * clause representing lemmas to be used in resolution proof. - * - * @param os output stream - */ - virtual void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map); - /** - * Print the sorts declarations for this theory. - * - * @param os - * @param paren - */ - virtual void printSortDeclarations(std::ostream& os, std::ostream& paren) = 0; - /** - * Print the term declarations for this theory. - * - * @param os - * @param paren - */ - virtual void printTermDeclarations(std::ostream& os, std::ostream& paren) = 0; - /** - * Print any deferred variable/sorts declarations for this theory - * (those that need to appear inside the actual proof). - * - * @param os - * @param paren - */ - virtual void printDeferredDeclarations(std::ostream& os, std::ostream& paren) = 0; - /** - * Print any aliasing declarations. - * - * @param os - * @param paren - */ - virtual void printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) = 0; - /** - * Register a term of this theory that appears in the proof. - * - * @param term - */ - virtual void registerTerm(Expr term) = 0; - /** - * Print a proof for the disequality of two constants that belong to this theory. - * - * @param term - */ - virtual void printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap); - /** - * Print a proof for the equivalence of n1 and n2. - * - * @param term - */ - virtual void printRewriteProof(std::ostream& os, const Node &n1, const Node &n2); - - /** - * Return whether this node, when serialized as an LFSC proof, has sort `Bool`. - * - * This is virtual because it ultimately, theories control the serialization - * of their proofs, so a theory will need to override this appropriately. - * - * This should only be called on nodes of type `Bool`. - */ - virtual bool printsAsBool(const Node &n) { - // Most nodes print as formulas, so this is the default. - return false; - } -}; - -class BooleanProof : public TheoryProof { -protected: - ExprSet d_declarations; // all the boolean variables - theory::TheoryId getTheoryId() override; - - public: - BooleanProof(TheoryProofEngine* proofEngine); - - void registerTerm(Expr term) override; -}; - -class LFSCBooleanProof : public BooleanProof { -public: - LFSCBooleanProof(TheoryProofEngine* proofEngine) - : BooleanProof(proofEngine) - {} - void printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode ty) override; - void printOwnedSort(Type type, std::ostream& os) override; - void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; - void printSortDeclarations(std::ostream& os, std::ostream& paren) override; - void printTermDeclarations(std::ostream& os, std::ostream& paren) override; - void printDeferredDeclarations(std::ostream& os, - std::ostream& paren) override; - void printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) override; - - bool printsAsBool(const Node& n) override; - void printConstantDisequalityProof(std::ostream& os, - Expr c1, - Expr c2, - const ProofLetMap& globalLetMap) override; -}; - -} /* CVC4 namespace */ - -#endif /* CVC4__THEORY_PROOF_H */ diff --git a/src/proof/uf_proof.cpp b/src/proof/uf_proof.cpp deleted file mode 100644 index 74990ff44..000000000 --- a/src/proof/uf_proof.cpp +++ /dev/null @@ -1,759 +0,0 @@ -/********************* */ -/*! \file uf_proof.cpp - ** \verbatim - ** Top contributors (to current version): - ** Liana Hadarean, Guy Katz, Yoni Zohar - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ -#include "proof/uf_proof.h" - -#include <stack> - -#include "proof/proof_manager.h" -#include "proof/simplify_boolean_node.h" -#include "theory/uf/theory_uf.h" - -namespace CVC4 { - -void ProofUF::toStream(std::ostream& out) const -{ - ProofLetMap map; - toStream(out, map); -} - -void ProofUF::toStream(std::ostream& out, const ProofLetMap& map) const -{ - Trace("theory-proof-debug") << "; Print UF proof..." << std::endl; - //AJR : carry this further? - toStreamLFSC(out, ProofManager::getUfProof(), *d_proof, map); -} - -void ProofUF::toStreamLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - const ProofLetMap& map) -{ - Debug("pf::uf") << "ProofUF::toStreamLFSC starting" << std::endl; - Debug("lfsc-uf") << "Printing uf proof in LFSC : " << std::endl; - pf.debug_print("lfsc-uf"); - Debug("lfsc-uf") << std::endl; - toStreamRecLFSC( out, tp, pf, 0, map ); -} - -Node ProofUF::toStreamRecLFSC(std::ostream& out, - TheoryProof* tp, - const theory::eq::EqProof& pf, - unsigned tb, - const ProofLetMap& map) -{ - Debug("pf::uf") << std::endl - << std::endl - << "toStreamRecLFSC called. tb = " << tb - << " . proof:" << std::endl; - if (tb == 0) - { - // Special case: false was an input, so the proof is just "false". - if (pf.d_id == theory::eq::MERGED_THROUGH_EQUALITY && - pf.d_node == NodeManager::currentNM()->mkConst(false)) { - out << "(clausify_false "; - out << ProofManager::getLitName(NodeManager::currentNM()->mkConst(false).notNode()); - out << ")" << std::endl; - return Node(); - } - - std::shared_ptr<theory::eq::EqProof> subTrans = - std::make_shared<theory::eq::EqProof>(); - - int neg = tp->assertAndPrint(pf, map, subTrans); - - Node n1; - std::stringstream ss, ss2; - Debug("pf::uf") << "\nsubtrans has " << subTrans->d_children.size() << " children\n"; - bool disequalityFound = (neg >= 0); - - if(!disequalityFound || subTrans->d_children.size() >= 2) { - n1 = toStreamRecLFSC(ss, tp, *subTrans, 1, map); - } else { - n1 = toStreamRecLFSC(ss, tp, *(subTrans->d_children[0]), 1, map); - Debug("pf::uf") << "\nsubTrans unique child " - << subTrans->d_children[0]->d_id - << " was proven\ngot: " << n1 << std::endl; - } - - Debug("pf::uf") << "\nhave proven: " << n1 << std::endl; - - out << "(clausify_false (contra _ "; - if (disequalityFound) { - Node n2 = pf.d_children[neg]->d_node; - Assert(n2.getKind() == kind::NOT); - - Debug("pf::uf") << "n2 is " << n2[0] << std::endl; - - if (n2[0].getNumChildren() > 0) - { - Debug("pf::uf") << "\nn2[0]: " << n2[0][0] << std::endl; - } - if (n1.getNumChildren() > 1) { Debug("pf::uf") << "n1[1]: " << n1[1] << std::endl; } - - if(n2[0].getKind() == kind::APPLY_UF) { - out << "(trans _ _ _ _ "; - - if (n1[0] == n2[0]) { - out << "(symm _ _ _ "; - out << ss.str(); - out << ") "; - } else { - Assert(n1[1] == n2[0]); - out << ss.str(); - } - out << "(pred_eq_f _ " << ProofManager::getLitName(n2[0]) << ")) t_t_neq_f))" << std::endl; - } else if (n2[0].getKind() == kind::BOOLEAN_TERM_VARIABLE) { - out << ss.str() << " " << ProofManager::getLitName(n2[0]) << "))"; - } else { - Assert((n1[0] == n2[0][0] && n1[1] == n2[0][1]) - || (n1[1] == n2[0][0] && n1[0] == n2[0][1])); - if(n1[1] == n2[0][0]) { - out << "(symm _ _ _ " << ss.str() << ")"; - } else { - out << ss.str(); - } - out << " " << ProofManager::getLitName(n2[0]) << "))" << std::endl; - } - } else { - Node n2 = pf.d_node; - Assert(n2.getKind() == kind::EQUAL); - Assert((n1[0] == n2[0] && n1[1] == n2[1]) - || (n1[1] == n2[0] && n1[0] == n2[1])); - - out << ss.str(); - out << " "; - ProofManager::getTheoryProofEngine()->printConstantDisequalityProof( - out, n1[0].toExpr(), n1[1].toExpr(), map); - out << "))" << std::endl; - } - - return Node(); - } - // TODO (#2965): improve this code, which is highly complicated. - switch(pf.d_id) { - case theory::eq::MERGED_THROUGH_CONGRUENCE: { - Debug("pf::uf") << "\nok, looking at congruence:\n"; - pf.debug_print("pf::uf"); - std::stack<const theory::eq::EqProof*> stk; - for (const theory::eq::EqProof* pf2 = &pf; - pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE; - pf2 = pf2->d_children[0].get()) { - Assert(!pf2->d_node.isNull()); - Assert(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF - || pf2->d_node.getKind() == kind::BUILTIN - || pf2->d_node.getKind() == kind::APPLY_UF - || pf2->d_node.getKind() == kind::SELECT - || pf2->d_node.getKind() == kind::STORE); - Assert(pf2->d_children.size() == 2); - out << "(cong _ _ _ _ _ _ "; - stk.push(pf2); - } - Assert(stk.top()->d_children[0]->d_id - != theory::eq::MERGED_THROUGH_CONGRUENCE); - NodeBuilder<> b1(kind::PARTIAL_APPLY_UF), b2(kind::PARTIAL_APPLY_UF); - const theory::eq::EqProof* pf2 = stk.top(); - stk.pop(); - Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); - Node n1 = toStreamRecLFSC(out, tp, *(pf2->d_children[0]), tb + 1, map); - out << " "; - std::stringstream ss; - Node n2 = toStreamRecLFSC(ss, tp, *(pf2->d_children[1]), tb + 1, map); - Debug("pf::uf") << "\nok, in FIRST cong[" << stk.size() << "]" << "\n"; - pf2->debug_print("pf::uf"); - Debug("pf::uf") << "looking at " << pf2->d_node << "\n"; - Debug("pf::uf") << " " << n1 << "\n"; - Debug("pf::uf") << " " << n2 << "\n"; - int side = 0; - if (tp->match(pf2->d_node, n1[0])) - { - //if(tb == 1) { - Debug("pf::uf") << "SIDE IS 0\n"; - //} - side = 0; - } else { - //if(tb == 1) { - Debug("pf::uf") << "SIDE IS 1\n"; - //} - if (!tp->match(pf2->d_node, n1[1])) - { - Debug("pf::uf") << "IN BAD CASE, our first subproof is\n"; - pf2->d_children[0]->debug_print("pf::uf"); - } - Assert(tp->match(pf2->d_node, n1[1])); - side = 1; - } - if (n1[side].getKind() == kind::APPLY_UF - || n1[side].getKind() == kind::PARTIAL_APPLY_UF - || n1[side].getKind() == kind::SELECT - || n1[side].getKind() == kind::STORE) - { - if (n1[side].getKind() == kind::APPLY_UF - || n1[side].getKind() == kind::PARTIAL_APPLY_UF) - { - b1 << n1[side].getOperator(); - } else { - b1 << ProofManager::currentPM()->mkOp(n1[side].getOperator()); - } - b1.append(n1[side].begin(), n1[side].end()); - } else { - b1 << n1[side]; - } - if(n1[1-side].getKind() == kind::PARTIAL_APPLY_UF || n1[1-side].getKind() == kind::APPLY_UF || n1[side].getKind() == kind::SELECT || n1[side].getKind() == kind::STORE) { - if (n1[1 - side].getKind() == kind::PARTIAL_APPLY_UF - || n1[1 - side].getKind() == kind::APPLY_UF) - { - b2 << n1[1-side].getOperator(); - } else { - b2 << ProofManager::currentPM()->mkOp(n1[1-side].getOperator()); - } - b2.append(n1[1-side].begin(), n1[1-side].end()); - } else { - b2 << n1[1-side]; - } - Debug("pf::uf") << "pf2->d_node " << pf2->d_node << std::endl; - Debug("pf::uf") << "b1.getNumChildren() " << b1.getNumChildren() << std::endl; - Debug("pf::uf") << "n1 " << n1 << std::endl; - Debug("pf::uf") << "n2 " << n2 << std::endl; - Debug("pf::uf") << "side " << side << std::endl; - if(pf2->d_node[b1.getNumChildren() - (pf2->d_node.getMetaKind() == kind::metakind::PARAMETERIZED ? 0 : 1)] == n2[side]) { - b1 << n2[side]; - b2 << n2[1-side]; - out << ss.str(); - } else { - Assert(pf2->d_node[b1.getNumChildren() - - (pf2->d_node.getMetaKind() - == kind::metakind::PARAMETERIZED - ? 0 - : 1)] - == n2[1 - side]); - b1 << n2[1-side]; - b2 << n2[side]; - out << "(symm _ _ _ " << ss.str() << ")"; - } - out << ")"; - while(!stk.empty()) { - if(tb == 1) { - Debug("pf::uf") << "\nMORE TO DO\n"; - } - pf2 = stk.top(); - stk.pop(); - Assert(pf2->d_id == theory::eq::MERGED_THROUGH_CONGRUENCE); - out << " "; - ss.str(""); - n2 = toStreamRecLFSC(ss, tp, *(pf2->d_children[1]), tb + 1, map); - Debug("pf::uf") << "\nok, in cong[" << stk.size() << "]" << "\n"; - Debug("pf::uf") << "looking at " << pf2->d_node << "\n"; - Debug("pf::uf") << " " << n1 << "\n"; - Debug("pf::uf") << " " << n2 << "\n"; - Debug("pf::uf") << " " << b1 << "\n"; - Debug("pf::uf") << " " << b2 << "\n"; - if(pf2->d_node[b1.getNumChildren()] == n2[side]) { - b1 << n2[side]; - b2 << n2[1-side]; - out << ss.str(); - } else { - Assert(pf2->d_node[b1.getNumChildren()] == n2[1 - side]); - b1 << n2[1-side]; - b2 << n2[side]; - out << "(symm _ _ _ " << ss.str() << ")"; - } - out << ")"; - } - n1 = b1; - n2 = b2; - Debug("pf::uf") << "at end assert, got " << pf2->d_node << " and " << n1 << std::endl; - if(pf2->d_node.getKind() == kind::PARTIAL_APPLY_UF) { - Assert(n1 == pf2->d_node); - } - if(n1.getOperator().getType().getNumChildren() == n1.getNumChildren() + 1) { - if(ProofManager::currentPM()->hasOp(n1.getOperator())) { - b1.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst<Kind>()); - } else { - b1.clear(kind::APPLY_UF); - b1 << n1.getOperator(); - } - b1.append(n1.begin(), n1.end()); - n1 = b1; - Debug("pf::uf") << "at[2] end assert, got " << pf2->d_node << " and " << n1 << std::endl; - if(pf2->d_node.getKind() == kind::APPLY_UF) { - Assert(n1 == pf2->d_node); - } - } - if(n2.getOperator().getType().getNumChildren() == n2.getNumChildren() + 1) { - if(ProofManager::currentPM()->hasOp(n2.getOperator())) { - b2.clear(ProofManager::currentPM()->lookupOp(n2.getOperator()).getConst<Kind>()); - } else { - b2.clear(kind::APPLY_UF); - b2 << n2.getOperator(); - } - b2.append(n2.begin(), n2.end()); - n2 = b2; - } - Node n = (side == 0 ? n1.eqNode(n2) : n2.eqNode(n1)); - if(tb == 1) { - Debug("pf::uf") << "\ncong proved: " << n << "\n"; - } - return n; - } - - case theory::eq::MERGED_THROUGH_REFLEXIVITY: - { - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - out << "(refl _ "; - tp->printTerm(NodeManager::currentNM()->toExpr(pf.d_node), out, map); - out << ")"; - return pf.d_node.eqNode(pf.d_node); - } - case theory::eq::MERGED_THROUGH_EQUALITY: - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - out << ProofManager::getLitName(pf.d_node.negate()); - return pf.d_node; - - case theory::eq::MERGED_THROUGH_TRANS: { - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.size() >= 2); - std::stringstream ss; - Debug("pf::uf") << "\ndoing trans proof[[\n"; - pf.debug_print("pf::uf"); - Debug("pf::uf") << "\n"; - - pf.d_children[0]->d_node = simplifyBooleanNode(pf.d_children[0]->d_node); - - Node n1 = toStreamRecLFSC(ss, tp, *(pf.d_children[0]), tb + 1, map); - Debug("pf::uf") << "\ndoing trans proof, got n1 " << n1 << "\n"; - if(tb == 1) { - Debug("pf::uf") << "\ntrans proof[0], got n1 " << n1 << "\n"; - } - - bool identicalEqualities = false; - bool evenLengthSequence; - std::stringstream dontCare; - Node nodeAfterEqualitySequence = - toStreamRecLFSC(dontCare, tp, *(pf.d_children[0]), tb + 1, map); - - std::map<size_t, Node> childToStream; - std::pair<Node, Node> nodePair; - for(size_t i = 1; i < pf.d_children.size(); ++i) { - std::stringstream ss1(ss.str()), ss2; - ss.str(""); - - pf.d_children[i]->d_node = simplifyBooleanNode(pf.d_children[i]->d_node); - - // It is possible that we've already converted the i'th child to stream. - // If so, - // use previously stored result. Otherwise, convert and store. - Node n2; - if (childToStream.find(i) != childToStream.end()) - n2 = childToStream[i]; - else - { - n2 = toStreamRecLFSC(ss2, tp, *(pf.d_children[i]), tb + 1, map); - childToStream[i] = n2; - } - - // The following branch is dedicated to handling sequences of identical - // equalities, - // i.e. trans[ a=b, a=b, a=b ]. - // - // There are two cases: - // 1. The number of equalities is odd. Then, the sequence can be - // collapsed to just one equality, - // i.e. a=b. - // 2. The number of equalities is even. Now, we have two options: a=a - // or b=b. To determine this, - // we look at the node after the equality sequence. If it needs a, - // we go for a=a; and if it needs - // b, we go for b=b. If there is no following node, we look at the - // goal of the transitivity proof, - // and use it to determine which option we need. - if (n2.getKind() == kind::EQUAL) - { - if (((n1[0] == n2[0]) && (n1[1] == n2[1])) - || ((n1[0] == n2[1]) && (n1[1] == n2[0]))) - { - // We are in a sequence of identical equalities - - Debug("pf::uf") << "Detected identical equalities: " << std::endl - << "\t" << n1 << std::endl; - - if (!identicalEqualities) - { - // The sequence of identical equalities has started just now - identicalEqualities = true; - - Debug("pf::uf") - << "The sequence is just beginning. Determining length..." - << std::endl; - - // Determine whether the length of this sequence is odd or even. - evenLengthSequence = true; - bool sequenceOver = false; - size_t j = i + 1; - - while (j < pf.d_children.size() && !sequenceOver) - { - std::stringstream ignore; - nodeAfterEqualitySequence = - toStreamRecLFSC(ignore, tp, *(pf.d_children[j]), tb + 1, map); - - if (((nodeAfterEqualitySequence[0] == n1[0]) - && (nodeAfterEqualitySequence[1] == n1[1])) - || ((nodeAfterEqualitySequence[0] == n1[1]) - && (nodeAfterEqualitySequence[1] == n1[0]))) - { - evenLengthSequence = !evenLengthSequence; - } - else - { - sequenceOver = true; - } - - ++j; - } - - nodePair = - tp->identicalEqualitiesPrinterHelper(evenLengthSequence, - sequenceOver, - pf, - map, - ss1.str(), - &ss, - n1, - nodeAfterEqualitySequence); - n1 = nodePair.first; - nodeAfterEqualitySequence = nodePair.second; - } else { - ss.str(ss1.str()); - } - - // Ignore the redundancy. - continue; - } - } - - if (identicalEqualities) { - // We were in a sequence of identical equalities, but it has now ended. Resume normal operation. - identicalEqualities = false; - } - - Debug("pf::uf") << "\ndoing trans proof, got n2 " << n2 << "\n"; - if(tb == 1) { - Debug("pf::uf") << "\ntrans proof[" << i << "], got n2 " << n2 << "\n"; - Debug("pf::uf") << (n2.getKind() == kind::EQUAL) << "\n"; - - if ((n1.getNumChildren() >= 2) && (n2.getNumChildren() >= 2)) { - Debug("pf::uf") << n1[0].getId() << " " << n1[1].getId() << " / " << n2[0].getId() << " " << n2[1].getId() << "\n"; - Debug("pf::uf") << n1[0].getId() << " " << n1[0] << "\n"; - Debug("pf::uf") << n1[1].getId() << " " << n1[1] << "\n"; - Debug("pf::uf") << n2[0].getId() << " " << n2[0] << "\n"; - Debug("pf::uf") << n2[1].getId() << " " << n2[1] << "\n"; - Debug("pf::uf") << (n1[0] == n2[0]) << "\n"; - Debug("pf::uf") << (n1[1] == n2[1]) << "\n"; - Debug("pf::uf") << (n1[0] == n2[1]) << "\n"; - Debug("pf::uf") << (n1[1] == n2[0]) << "\n"; - } - } - - ss << "(trans _ _ _ _ "; - - if(n2.getKind() == kind::EQUAL && n1.getKind() == kind::EQUAL) - // Both elements of the transitivity rule are equalities/iffs - { - if(n1[0] == n2[0]) { - if(tb == 1) { Debug("pf::uf") << "case 1\n"; } - n1 = n1[1].eqNode(n2[1]); - ss << "(symm _ _ _ " << ss1.str() << ") " << ss2.str(); - } else if(n1[1] == n2[1]) { - if(tb == 1) { Debug("pf::uf") << "case 2\n"; } - n1 = n1[0].eqNode(n2[0]); - ss << ss1.str() << " (symm _ _ _ " << ss2.str() << ")"; - } else if(n1[0] == n2[1]) { - if(tb == 1) { Debug("pf::uf") << "case 3\n"; } - n1 = n2[0].eqNode(n1[1]); - ss << ss2.str() << " " << ss1.str(); - if(tb == 1) { Debug("pf::uf") << "++ proved " << n1 << "\n"; } - } else if(n1[1] == n2[0]) { - if(tb == 1) { Debug("pf::uf") << "case 4\n"; } - n1 = n1[0].eqNode(n2[1]); - ss << ss1.str() << " " << ss2.str(); - } else { - Warning() << "\n\ntrans proof failure at step " << i << "\n\n"; - Warning() << "0 proves " << n1 << "\n"; - Warning() << "1 proves " << n2 << "\n\n"; - pf.debug_print("pf::uf",0); - //toStreamRec(Warning.getStream(), pf, 0); - Warning() << "\n\n"; - Unreachable(); - } - Debug("pf::uf") << "++ trans proof[" << i << "], now have " << n1 << std::endl; - } else if(n1.getKind() == kind::EQUAL) { - // n1 is an equality/iff, but n2 is a predicate - if(n1[0] == n2) { - n1 = n1[1].eqNode(NodeManager::currentNM()->mkConst(true)); - ss << "(symm _ _ _ " << ss1.str() << ") (pred_eq_t _ " << ss2.str() << ")"; - } else if(n1[1] == n2) { - n1 = n1[0].eqNode(NodeManager::currentNM()->mkConst(true)); - ss << ss1.str() << " (pred_eq_t _ " << ss2.str() << ")"; - } else { - Unreachable(); - } - } else if(n2.getKind() == kind::EQUAL) { - // n2 is an equality/iff, but n1 is a predicate - if(n2[0] == n1) { - n1 = n2[1].eqNode(NodeManager::currentNM()->mkConst(true)); - ss << "(symm _ _ _ " << ss2.str() << ") (pred_eq_t _ " << ss1.str() << ")"; - } else if(n2[1] == n1) { - n1 = n2[0].eqNode(NodeManager::currentNM()->mkConst(true)); - ss << ss2.str() << " (pred_eq_t _ " << ss1.str() << ")"; - } else { - Unreachable(); - } - } else { - // Both n1 and n2 are predicates. - // We want to prove b1 = b2, and we know that ((b1), (b2)) or ((not b1), (not b2)) - if (n1.getKind() == kind::NOT) { - Assert(n2.getKind() == kind::NOT); - Assert(pf.d_node[0] == n1[0] || pf.d_node[0] == n2[0]); - Assert(pf.d_node[1] == n1[0] || pf.d_node[1] == n2[0]); - Assert(n1[0].getKind() == kind::BOOLEAN_TERM_VARIABLE); - Assert(n2[0].getKind() == kind::BOOLEAN_TERM_VARIABLE); - - if (pf.d_node[0] == n1[0]) { - ss << "(false_preds_equal _ _ " << ss1.str() << " " << ss2.str() << ") "; - ss << "(pred_refl_neg _ " << ss2.str() << ")"; - } else { - ss << "(false_preds_equal _ _ " << ss2.str() << " " << ss1.str() << ") "; - ss << "(pred_refl_neg _ " << ss1.str() << ")"; - } - n1 = pf.d_node; - - } else if (n1.getKind() == kind::BOOLEAN_TERM_VARIABLE) { - Assert(n2.getKind() == kind::BOOLEAN_TERM_VARIABLE); - Assert(pf.d_node[0] == n1 || pf.d_node[0] == n2); - Assert(pf.d_node[1] == n1 || pf.d_node[2] == n2); - - if (pf.d_node[0] == n1) { - ss << "(true_preds_equal _ _ " << ss1.str() << " " << ss2.str() << ") "; - ss << "(pred_refl_pos _ " << ss2.str() << ")"; - } else { - ss << "(true_preds_equal _ _ " << ss2.str() << " " << ss1.str() << ") "; - ss << "(pred_refl_pos _ " << ss1.str() << ")"; - } - n1 = pf.d_node; - - } else { - - Unreachable(); - } - } - - ss << ")"; - } - out << ss.str(); - Debug("pf::uf") << "\n++ trans proof done, have proven " << n1 << std::endl; - return n1; - } - - default: - Assert(!pf.d_node.isNull()); - Assert(pf.d_children.empty()); - Debug("pf::uf") << "theory proof: " << pf.d_node << " by rule " << int(pf.d_id) << std::endl; - AlwaysAssert(false); - return pf.d_node; - } -} - -UFProof::UFProof(theory::uf::TheoryUF* uf, TheoryProofEngine* pe) - : TheoryProof(uf, pe) -{} - -theory::TheoryId UFProof::getTheoryId() { return theory::THEORY_UF; } -void UFProof::registerTerm(Expr term) { - // already registered - if (d_declarations.find(term) != d_declarations.end()) - return; - - Type type = term.getType(); - if (type.isSort()) { - // declare uninterpreted sorts - d_sorts.insert(type); - } - - if (term.getKind() == kind::APPLY_UF) { - Expr function = term.getOperator(); - d_declarations.insert(function); - } - - if (term.isVariable()) { - d_declarations.insert(term); - - - if (term.getKind() == kind::BOOLEAN_TERM_VARIABLE) { - // Ensure cnf literals - Node asNode(term); - ProofManager::currentPM()->ensureLiteral( - asNode.eqNode(NodeManager::currentNM()->mkConst(true))); - ProofManager::currentPM()->ensureLiteral( - asNode.eqNode(NodeManager::currentNM()->mkConst(false))); - } - } - - // recursively declare all other terms - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - // could belong to other theories - d_proofEngine->registerTerm(term[i]); - } -} - -void LFSCUFProof::printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) -{ - Node node = Node::fromExpr(term); - Debug("pf::uf") << std::endl << "(pf::uf) LFSCUfProof::printOwnedTerm: term = " << node << std::endl; - - Assert(theory::Theory::theoryOf(node) == theory::THEORY_UF); - - if (node.getKind() == kind::VARIABLE || - node.getKind() == kind::SKOLEM || - node.getKind() == kind::BOOLEAN_TERM_VARIABLE) { - os << node; - return; - } - - Assert(node.getKind() == kind::APPLY_UF); - - if(node.getType().isBoolean()) { - os << "(p_app "; - } - Node func = node.getOperator(); - for (unsigned i = 0; i < term.getNumChildren(); ++i) { - os << "(apply _ _ "; - } - os << func << " "; - Assert(func.getType().isFunction()); - std::vector<TypeNode> argsTypes = node.getOperator().getType().getArgTypes(); - for (unsigned i = 0; i < node.getNumChildren(); ++i) { - - bool convertToBool = (node[i].getType().isBoolean() && !d_proofEngine->printsAsBool(node[i])); - if (convertToBool) os << "(f_to_b "; - d_proofEngine->printBoundTerm(term[i], os, map, argsTypes[i]); - if (convertToBool) os << ")"; - os << ")"; - } - if(term.getType().isBoolean()) { - os << ")"; - } -} - -void LFSCUFProof::printOwnedSort(Type type, std::ostream& os) { - Debug("pf::uf") << std::endl << "(pf::uf) LFSCArrayProof::printOwnedSort: type is: " << type << std::endl; - - Assert(type.isSort()); - os << type; -} - -void LFSCUFProof::printTheoryLemmaProof(std::vector<Expr>& lemma, std::ostream& os, std::ostream& paren, const ProofLetMap& map) { - os << " ;; UF Theory Lemma \n;;"; - for (unsigned i = 0; i < lemma.size(); ++i) { - os << lemma[i] <<" "; - } - os <<"\n"; - //os << " (clausify_false trust)"; - UFProof::printTheoryLemmaProof(lemma, os, paren, map); -} - -void LFSCUFProof::printSortDeclarations(std::ostream& os, std::ostream& paren) { - for (TypeSet::const_iterator it = d_sorts.begin(); it != d_sorts.end(); ++it) { - if (!ProofManager::currentPM()->wasPrinted(*it)) { - os << "(% " << *it << " sort\n"; - paren << ")"; - ProofManager::currentPM()->markPrinted(*it); - } - } -} - -void LFSCUFProof::printTermDeclarations(std::ostream& os, std::ostream& paren) { - // declaring the terms - Debug("pf::uf") << "LFSCUFProof::printTermDeclarations called" << std::endl; - - for (ExprSet::const_iterator it = d_declarations.begin(); it != d_declarations.end(); ++it) { - Expr term = *it; - - os << "(% " << ProofManager::sanitize(term) << " "; - os << "(term "; - - Type type = term.getType(); - if (type.isFunction()) { - std::ostringstream fparen; - FunctionType ftype = (FunctionType)type; - std::vector<Type> args = ftype.getArgTypes(); - args.push_back(ftype.getRangeType()); - os << "(arrow"; - for (unsigned i = 0; i < args.size(); i++) { - Type arg_type = args[i]; - os << " "; - d_proofEngine->printSort(arg_type, os); - if (i < args.size() - 2) { - os << " (arrow"; - fparen << ")"; - } - } - os << fparen.str() << "))\n"; - } else { - Assert(term.isVariable()); - os << type << ")\n"; - } - paren << ")"; - } - - Debug("pf::uf") << "LFSCUFProof::printTermDeclarations done" << std::endl; -} - -void LFSCUFProof::printDeferredDeclarations(std::ostream& os, std::ostream& paren) { - // Nothing to do here at this point. -} - -void LFSCUFProof::printAliasingDeclarations(std::ostream& os, std::ostream& paren, const ProofLetMap &globalLetMap) { - // Nothing to do here at this point. -} - -bool LFSCUFProof::printsAsBool(const Node &n) { - if (n.getKind() == kind::BOOLEAN_TERM_VARIABLE) - return true; - - return false; -} - -void LFSCUFProof::printConstantDisequalityProof(std::ostream& os, Expr c1, Expr c2, const ProofLetMap &globalLetMap) { - Node falseNode = NodeManager::currentNM()->mkConst(false); - Node trueNode = NodeManager::currentNM()->mkConst(true); - - Assert(c1 == falseNode.toExpr() || c1 == trueNode.toExpr()); - Assert(c2 == falseNode.toExpr() || c2 == trueNode.toExpr()); - Assert(c1 != c2); - - if (c1 == trueNode.toExpr()) - os << "t_t_neq_f"; - else - os << "(symm _ _ _ t_t_neq_f)"; -} - -} /* namespace CVC4 */ diff --git a/src/proof/uf_proof.h b/src/proof/uf_proof.h deleted file mode 100644 index 647359a87..000000000 --- a/src/proof/uf_proof.h +++ /dev/null @@ -1,106 +0,0 @@ -/********************* */ -/*! \file uf_proof.h - ** \verbatim - ** Top contributors (to current version): - ** Mathias Preiner, Liana Hadarean, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief UF proof - ** - ** UF proof - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__UF__PROOF_H -#define CVC4__UF__PROOF_H - -#include <memory> -#include <unordered_set> - -#include "expr/expr.h" -#include "proof/theory_proof.h" -#include "theory/uf/equality_engine.h" -#include "util/proof.h" - -namespace CVC4 { - -// proof object outputted by TheoryUF -class ProofUF : public Proof -{ - public: - ProofUF(std::shared_ptr<theory::eq::EqProof> pf) : d_proof(pf) {} - void toStream(std::ostream& out) const override; - void toStream(std::ostream& out, const ProofLetMap& map) const override; - - private: - static void toStreamLFSC(std::ostream& out, TheoryProof* tp, - const theory::eq::EqProof& pf, - const ProofLetMap& map); - static Node toStreamRecLFSC(std::ostream& out, TheoryProof* tp, - const theory::eq::EqProof& pf, unsigned tb, - const ProofLetMap& map); - - // it is simply an equality engine proof - std::shared_ptr<theory::eq::EqProof> d_proof; -}; - -namespace theory { -namespace uf { -class TheoryUF; -} -} - -typedef std::unordered_set<Type, TypeHashFunction > TypeSet; - - -class UFProof : public TheoryProof { -protected: - TypeSet d_sorts; // all the uninterpreted sorts in this theory - ExprSet d_declarations; // all the variable/function declarations - theory::TheoryId getTheoryId() override; - - public: - UFProof(theory::uf::TheoryUF* uf, TheoryProofEngine* proofEngine); - - void registerTerm(Expr term) override; -}; - -class LFSCUFProof : public UFProof { -public: - LFSCUFProof(theory::uf::TheoryUF* uf, TheoryProofEngine* proofEngine) - : UFProof(uf, proofEngine) - {} - void printOwnedTermAsType(Expr term, - std::ostream& os, - const ProofLetMap& map, - TypeNode expectedType) override; - void printOwnedSort(Type type, std::ostream& os) override; - void printTheoryLemmaProof(std::vector<Expr>& lemma, - std::ostream& os, - std::ostream& paren, - const ProofLetMap& map) override; - void printSortDeclarations(std::ostream& os, std::ostream& paren) override; - void printTermDeclarations(std::ostream& os, std::ostream& paren) override; - void printDeferredDeclarations(std::ostream& os, - std::ostream& paren) override; - void printAliasingDeclarations(std::ostream& os, - std::ostream& paren, - const ProofLetMap& globalLetMap) override; - - bool printsAsBool(const Node& n) override; - - void printConstantDisequalityProof(std::ostream& os, - Expr c1, - Expr c2, - const ProofLetMap& globalLetMap) override; -}; - - -}/* CVC4 namespace */ - -#endif /* CVC4__UF__PROOF_H */ diff --git a/src/prop/bvminisat/bvminisat.cpp b/src/prop/bvminisat/bvminisat.cpp index c1aac33be..0b531c498 100644 --- a/src/prop/bvminisat/bvminisat.cpp +++ b/src/prop/bvminisat/bvminisat.cpp @@ -18,7 +18,6 @@ #include "prop/bvminisat/simp/SimpSolver.h" #include "proof/clause_id.h" -#include "proof/sat_proof.h" #include "util/statistics_registry.h" namespace CVC4 { @@ -66,7 +65,6 @@ ClauseId BVMinisatSatSolver::addClause(SatClause& clause, // } ClauseId clause_id = ClauseIdError; d_minisat->addClause(minisat_clause, clause_id); - THEORY_PROOF(Assert(clause_id != ClauseIdError);); return clause_id; } @@ -76,14 +74,14 @@ SatValue BVMinisatSatSolver::propagate() { void BVMinisatSatSolver::addMarkerLiteral(SatLiteral lit) { d_minisat->addMarkerLiteral(BVMinisat::var(toMinisatLit(lit))); - markUnremovable(lit); + markUnremovable(lit); } void BVMinisatSatSolver::explain(SatLiteral lit, std::vector<SatLiteral>& explanation) { std::vector<BVMinisat::Lit> minisat_explanation; d_minisat->explain(toMinisatLit(lit), minisat_explanation); for (unsigned i = 0; i < minisat_explanation.size(); ++i) { - explanation.push_back(toSatLiteral(minisat_explanation[i])); + explanation.push_back(toSatLiteral(minisat_explanation[i])); } } @@ -104,12 +102,6 @@ void BVMinisatSatSolver::popAssumption() { d_minisat->popAssumption(); } -void BVMinisatSatSolver::setResolutionProofLog( - proof::ResolutionBitVectorProof* bvp) -{ - d_minisat->setProofLog( bvp ); -} - SatVariable BVMinisatSatSolver::newVar(bool isTheoryAtom, bool preRegister, bool canErase){ return d_minisat->newVar(true, true, !canErase); } @@ -148,9 +140,7 @@ SatValue BVMinisatSatSolver::solve(long unsigned int& resource){ return result; } -bool BVMinisatSatSolver::ok() const { - return d_minisat->okay(); -} +bool BVMinisatSatSolver::ok() const { return d_minisat->okay(); } void BVMinisatSatSolver::getUnsatCore(SatClause& unsatCore) { // TODO add assertion to check the call was after an unsat call @@ -160,11 +150,11 @@ void BVMinisatSatSolver::getUnsatCore(SatClause& unsatCore) { } SatValue BVMinisatSatSolver::value(SatLiteral l){ - return toSatLiteralValue(d_minisat->value(toMinisatLit(l))); + return toSatLiteralValue(d_minisat->value(toMinisatLit(l))); } SatValue BVMinisatSatSolver::modelValue(SatLiteral l){ - return toSatLiteralValue(d_minisat->modelValue(toMinisatLit(l))); + return toSatLiteralValue(d_minisat->modelValue(toMinisatLit(l))); } void BVMinisatSatSolver::unregisterVar(SatLiteral lit) { @@ -309,17 +299,3 @@ void BVMinisatSatSolver::Statistics::init(BVMinisat::SimpSolver* minisat){ } /* namespace CVC4::prop */ } /* namespace CVC4 */ - -namespace CVC4 { -template<> -prop::SatLiteral toSatLiteral< BVMinisat::Solver>(BVMinisat::Solver::TLit lit) { - return prop::BVMinisatSatSolver::toSatLiteral(lit); -} - -template<> -void toSatClause< BVMinisat::Solver> (const BVMinisat::Solver::TClause& minisat_cl, - prop::SatClause& sat_cl) { - prop::BVMinisatSatSolver::toSatClause(minisat_cl, sat_cl); -} - -} diff --git a/src/prop/bvminisat/bvminisat.h b/src/prop/bvminisat/bvminisat.h index 01a0a518e..f93dc8048 100644 --- a/src/prop/bvminisat/bvminisat.h +++ b/src/prop/bvminisat/bvminisat.h @@ -21,8 +21,6 @@ #include <memory> #include "context/cdo.h" -#include "proof/clause_id.h" -#include "proof/resolution_bitvector_proof.h" #include "prop/bv_sat_solver_notify.h" #include "prop/bvminisat/simp/SimpSolver.h" #include "prop/sat_solver.h" @@ -57,8 +55,8 @@ class BVMinisatSatSolver : public BVSatSolverInterface, } }; - std::unique_ptr<BVMinisat::SimpSolver> d_minisat; - std::unique_ptr<MinisatNotify> d_minisatNotify; + std::unique_ptr<BVMinisat::SimpSolver> d_minisat; + std::unique_ptr<MinisatNotify> d_minisatNotify; unsigned d_assertionsCount; context::CDO<unsigned> d_assertionsRealCount; @@ -79,6 +77,7 @@ public: ClauseId addXorClause(SatClause& clause, bool rhs, bool removable) override { Unreachable() << "Minisat does not support native XOR reasoning"; + return ClauseIdError; } SatValue propagate() override; @@ -123,8 +122,6 @@ public: void popAssumption() override; - void setResolutionProofLog(proof::ResolutionBitVectorProof* bvp) override; - private: /* Disable the default constructor. */ BVMinisatSatSolver() = delete; diff --git a/src/prop/bvminisat/core/Solver.cc b/src/prop/bvminisat/core/Solver.cc index 84ab62fd8..f7ba14acd 100644 --- a/src/prop/bvminisat/core/Solver.cc +++ b/src/prop/bvminisat/core/Solver.cc @@ -29,11 +29,6 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "base/output.h" #include "options/bv_options.h" #include "options/smt_options.h" -#include "proof/clause_id.h" -#include "proof/proof_manager.h" -#include "proof/resolution_bitvector_proof.h" -#include "proof/sat_proof.h" -#include "proof/sat_proof_implementation.h" #include "prop/bvminisat/mtl/Sort.h" #include "theory/interrupted.h" #include "util/utility.h" @@ -170,8 +165,7 @@ Solver::Solver(CVC4::context::Context* context) , conflict_budget(-1), propagation_budget(-1), - asynch_interrupt(false), - d_bvp(NULL) + asynch_interrupt(false) { // Create the constant variables varTrue = newVar(true, false); @@ -220,7 +214,7 @@ bool Solver::addClause_(vec<Lit>& ps, ClauseId& id) if (decisionLevel() > 0) { cancelUntil(0); } - + if (!ok) { id = ClauseIdUndef; return false; @@ -231,7 +225,7 @@ bool Solver::addClause_(vec<Lit>& ps, ClauseId& id) sort(ps); Lit p; int i, j; int falseLiteralsCount = 0; - + for (i = j = 0, p = lit_Undef; i < ps.size(); i++) { // tautologies are ignored if (value(ps[i]) == l_True || ps[i] == ~p) { @@ -245,82 +239,30 @@ bool Solver::addClause_(vec<Lit>& ps, ClauseId& id) } if (value(ps[i]) == l_False) { - if (!THEORY_PROOF_ON()) - continue; - ++falseLiteralsCount; + continue; } ps[j++] = p = ps[i]; } - + ps.shrink(i - j); clause_added = true; - Assert(falseLiteralsCount == 0 || THEORY_PROOF_ON()); - if(falseLiteralsCount == 0) { if (ps.size() == 0) { - Assert(!THEORY_PROOF_ON()); return ok = false; } else if (ps.size() == 1){ - if(d_bvp){ id = d_bvp->getSatProof()->registerUnitClause(ps[0], INPUT);} uncheckedEnqueue(ps[0]); CRef confl_ref = propagate(); ok = (confl_ref == CRef_Undef); - if(d_bvp){ if (!ok) d_bvp->getSatProof()->finalizeProof(confl_ref); } return ok; } else { CRef cr = ca.alloc(ps, false); clauses.push(cr); attachClause(cr); - if(d_bvp){ id = d_bvp->getSatProof()->registerClause(cr, INPUT);} - } - return ok; - } - - if (falseLiteralsCount != 0 && THEORY_PROOF_ON()) { - // we are in a conflicting state - if (ps.size() == falseLiteralsCount && falseLiteralsCount == 1) { - if(d_bvp){ id = d_bvp->getSatProof()->storeUnitConflict(ps[0], INPUT); } - if(d_bvp){ d_bvp->getSatProof()->finalizeProof(CVC4::BVMinisat::CRef_Lazy); } - return ok = false; - } - - assign_lt lt(*this); - sort(ps, lt); - - CRef cr = ca.alloc(ps, false); - clauses.push(cr); - attachClause(cr); - - if(d_bvp){id = d_bvp->getSatProof()->registerClause(cr, INPUT);} - - if(ps.size() == falseLiteralsCount) { - if(d_bvp){ d_bvp->getSatProof()->finalizeProof(cr); } - return ok = false; - } - - // Check if it propagates - if (ps.size() == falseLiteralsCount + 1) { - Clause& cl = ca[cr]; - - Assert(value(cl[0]) == l_Undef); - uncheckedEnqueue(cl[0], cr); - Assert(cl.size() > 1); - CRef confl = propagate(); - ok = (confl == CRef_Undef); - if(!ok) { - if(d_bvp){ - if(ca[confl].size() == 1) { - id = d_bvp->getSatProof()->storeUnitConflict(ca[confl][0], LEARNT); - d_bvp->getSatProof()->finalizeProof(CVC4::BVMinisat::CRef_Lazy); - } else { - d_bvp->getSatProof()->finalizeProof(confl); - } - } - } } + return ok; } return ok; } @@ -338,9 +280,6 @@ void Solver::attachClause(CRef cr) { void Solver::detachClause(CRef cr, bool strict) { const Clause& clause = ca[cr]; - if (d_bvp) - { - d_bvp->getSatProof()->markDeleted(cr); } assert(clause.size() > 1); @@ -425,23 +364,24 @@ Lit Solver::pickBranchLit() return next == var_Undef ? lit_Undef : mkLit(next, rnd_pol ? drand(random_seed) < 0.5 : polarity[next]); } - /*_________________________________________________________________________________________________ | -| analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> [void] -| +| analyze : (confl : Clause*) (out_learnt : vec<Lit>&) (out_btlevel : int&) -> +[void] +| | Description: | Analyze conflict and produce a reason clause. -| +| | Pre-conditions: | * 'out_learnt' is assumed to be cleared. | * Current decision level must be greater than root level. -| +| | Post-conditions: | * 'out_learnt[0]' is the asserting literal at level 'out_btlevel'. -| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision level of the -| rest of literals. There may be others from the same level though. -| +| * If out_learnt.size() > 1 then 'out_learnt[1]' has the greatest decision +level of the | rest of literals. There may be others from the same level +though. +| |________________________________________________________________________________________________@*/ void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel, UIP uip) { @@ -454,8 +394,6 @@ void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel, UIP uip int index = trail.size() - 1; bool done = false; - - if(d_bvp){ d_bvp->getSatProof()->startResChain(confl); } do{ assert(confl != CRef_Undef); // (otherwise should be UIP) @@ -477,13 +415,6 @@ void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel, UIP uip out_learnt.push(q); } - if (level(var(q)) == 0) - { - if (d_bvp) - { - d_bvp->getSatProof()->resolveOutUnit(q); - } - } } // Select next clause to look at: @@ -493,10 +424,6 @@ void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel, UIP uip seen[var(p)] = 0; pathC--; - if ( pathC > 0 && confl != CRef_Undef ) { - if(d_bvp){ d_bvp->getSatProof()->addResolutionStep(p, confl, sign(p));} - } - switch (uip) { case UIP_FIRST: done = pathC == 0; @@ -536,13 +463,6 @@ void Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel, UIP uip // Literal is not redundant out_learnt[j++] = out_learnt[i1]; } - else - { - if (d_bvp) - { - d_bvp->getSatProof()->storeLitRedundant(out_learnt[i1]); - } - } } } }else if (ccmin_mode == 1){ @@ -640,10 +560,10 @@ bool Solver::litRedundant(Lit p, uint32_t abstract_levels) return true; } -/** +/** * Specialized analyzeFinal procedure where we test the consistency * of the assumptions before backtracking bellow the assumption level. - * + * * @param p the original uip (may be unit) * @param confl_clause the conflict clause * @param out_conflict the conflict in terms of assumptions we are building @@ -653,14 +573,14 @@ void Solver::analyzeFinal2(Lit p, CRef confl_clause, vec<Lit>& out_conflict) { assert (decisionLevel() == assumptions.size()); assert (level(var(p)) == assumptions.size()); - out_conflict.clear(); - + out_conflict.clear(); + Clause& cl = ca[confl_clause]; for (int i = 0; i < cl.size(); ++i) { seen[var(cl[i])] = 1; } - int end = options::proof() ? 0 : trail_lim[0]; + int end = trail_lim[0]; for (int i = trail.size() - 1; i >= end; i--) { Var x = var(trail[i]); if (seen[x]) { @@ -670,44 +590,31 @@ void Solver::analyzeFinal2(Lit p, CRef confl_clause, vec<Lit>& out_conflict) { if (marker[x] == 2) { assert (level(x) > 0); out_conflict.push(~trail[i]); - } else { - if(d_bvp){d_bvp->getSatProof()->resolveOutUnit(~(trail[i])); } } - } else { - if(d_bvp){d_bvp->getSatProof()->resolveOutUnit(~p);} } } else { Clause& clause = ca[reason(x)]; - if(d_bvp){d_bvp->getSatProof()->addResolutionStep(trail[i],reason(x), sign(trail[i]));} for (int j = 1; j < clause.size(); j++) { if (level(var(clause[j])) > 0) seen[var(clause[j])] = 1; - if(d_bvp){ - if (level(var(clause[j])) == 0) - { - d_bvp->getSatProof()->resolveOutUnit(clause[j]); - seen[var(clause[j])] = - 0; // we don't need to resolve it out again - } - } } } seen[x] = 0; } - assert (seen[x] == 0); + assert(seen[x] == 0); } - assert (out_conflict.size()); + assert(out_conflict.size()); } /*_________________________________________________________________________________________________ | | analyzeFinal : (p : Lit) -> [void] -| +| | Description: -| Specialized analysis procedure to express the final conflict in terms of assumptions. -| Calculates the (possibly empty) set of assumptions that led to the assignment of 'p', and -| stores the result in 'out_conflict'. +| Specialized analysis procedure to express the final conflict in terms of +assumptions. | Calculates the (possibly empty) set of assumptions that led to +the assignment of 'p', and | stores the result in 'out_conflict'. |________________________________________________________________________________________________@*/ void Solver::analyzeFinal(Lit p, vec<Lit>& out_conflict) { @@ -716,22 +623,14 @@ void Solver::analyzeFinal(Lit p, vec<Lit>& out_conflict) out_conflict.push(p); } - if(d_bvp){ - if (level(var(p)) == 0 && d_bvp->isAssumptionConflict()) { - Assert(marker[var(p)] == 2); - if (reason(var(p)) == CRef_Undef) { - d_bvp->startBVConflict(p); - } - } - } - - if (decisionLevel() == 0 && !options::proof()) { + if (decisionLevel() == 0) + { return; } seen[var(p)] = 1; - int end = options::proof() ? 0 : trail_lim[0]; - + int end = trail_lim[0]; + for (int i = trail.size()-1; i >= end; i--){ Var x = var(trail[i]); if (seen[x]) { @@ -744,26 +643,12 @@ void Solver::analyzeFinal(Lit p, vec<Lit>& out_conflict) } } else { Clause& clause = ca[reason(x)]; - if(d_bvp){ - if (d_bvp->isAssumptionConflict() && - trail[i] == p) { - d_bvp->startBVConflict(reason(x)); - } else { - d_bvp->getSatProof()->addResolutionStep(trail[i], reason(x), sign(trail[i])); - } - } for (int j = 1; j < clause.size(); j++) { if (level(var(clause[j])) > 0) { seen[var(clause[j])] = 1; } - if(d_bvp){ - if (level(var(clause[j])) == 0) - { - d_bvp->getSatProof()->resolveOutUnit(clause[j]); - } - } } } seen[x] = 0; @@ -805,10 +690,9 @@ lbool Solver::propagateAssumptions() { lbool Solver::assertAssumption(Lit p, bool propagate) { // TODO need to somehow mark the assumption as unit in the current context? // it's not always unit though, but this would be useful for debugging - + // assert(marker[var(p)] == 1); - if (decisionLevel() > assumptions.size()) { cancelUntil(assumptions.size()); } @@ -829,9 +713,9 @@ lbool Solver::assertAssumption(Lit p, bool propagate) { // run the propagation if (propagate) { only_bcp = true; - ccmin_mode = 0; + ccmin_mode = 0; lbool result = search(-1); - return result; + return result; } else { return l_True; } @@ -841,18 +725,16 @@ void Solver::addMarkerLiteral(Var var) { // make sure it wasn't already marked Assert(marker[var] == 0); marker[var] = 1; - if(d_bvp){d_bvp->getSatProof()->registerAssumption(var);} } - /*_________________________________________________________________________________________________ | | propagate : [void] -> [Clause*] -| +| | Description: -| Propagates all enqueued facts. If a conflict arises, the conflicting clause is returned, -| otherwise CRef_Undef. -| +| Propagates all enqueued facts. If a conflict arises, the conflicting clause +is returned, | otherwise CRef_Undef. +| | Post-conditions: | * the propagation queue is empty, even if there was a conflict. |________________________________________________________________________________________________@*/ @@ -920,20 +802,24 @@ CRef Solver::propagate() return confl; } - /*_________________________________________________________________________________________________ | | reduceDB : () -> [void] -| +| | Description: -| Remove half of the learnt clauses, minus the clauses locked by the current assignment. Locked -| clauses are clauses that are reason to some assignment. Binary clauses are never removed. +| Remove half of the learnt clauses, minus the clauses locked by the current +assignment. Locked | clauses are clauses that are reason to some assignment. +Binary clauses are never removed. |________________________________________________________________________________________________@*/ -struct reduceDB_lt { - ClauseAllocator& ca; - reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} - bool operator () (CRef x, CRef y) { - return ca[x].size() > 2 && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); } +struct reduceDB_lt +{ + ClauseAllocator& ca; + reduceDB_lt(ClauseAllocator& ca_) : ca(ca_) {} + bool operator()(CRef x, CRef y) + { + return ca[x].size() > 2 + && (ca[y].size() == 2 || ca[x].activity() < ca[y].activity()); + } }; void Solver::reduceDB() { @@ -963,14 +849,6 @@ void Solver::removeSatisfied(vec<CRef>& cs) Clause& clause = ca[cs[i]]; if (satisfied(clause)) { - if (locked(clause)) - { - // store a resolution of the literal clause propagated - if (d_bvp) - { - d_bvp->getSatProof()->storeUnitResolution(clause[0]); - } - } removeClause(cs[i]); } else @@ -989,14 +867,14 @@ void Solver::rebuildOrderHeap() order_heap.build(vs); } - /*_________________________________________________________________________________________________ | | simplify : [void] -> [bool] -| +| | Description: -| Simplify the clause database according to the current top-level assigment. Currently, the only -| thing done here is the removal of satisfied clauses, but more things can be put here. +| Simplify the clause database according to the current top-level assigment. +Currently, the only | thing done here is the removal of satisfied clauses, +but more things can be put here. |________________________________________________________________________________________________@*/ bool Solver::simplify() { @@ -1021,19 +899,19 @@ bool Solver::simplify() return true; } - /*_________________________________________________________________________________________________ | | search : (nof_conflicts : int) (params : const SearchParams&) -> [lbool] -| +| | Description: -| Search for a model the specified number of conflicts. +| Search for a model the specified number of conflicts. | NOTE! Use negative value for 'nof_conflicts' indicate infinity. -| +| | Output: -| 'l_True' if a partial assigment that is consistent with respect to the clauseset is found. If -| all variables are decision variables, this means that the clause set is satisfiable. 'l_False' -| if the clause set is unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. +| 'l_True' if a partial assigment that is consistent with respect to the +clauseset is found. If | all variables are decision variables, this means +that the clause set is satisfiable. 'l_False' | if the clause set is +unsatisfiable. 'l_Undef' if the bound on number of conflicts is reached. |________________________________________________________________________________________________@*/ lbool Solver::search(int nof_conflicts, UIP uip) { @@ -1051,7 +929,6 @@ lbool Solver::search(int nof_conflicts, UIP uip) if (decisionLevel() == 0) { // can this happen for bv? - if(d_bvp){ d_bvp->getSatProof()->finalizeProof(confl);} return l_False; } @@ -1067,59 +944,34 @@ lbool Solver::search(int nof_conflicts, UIP uip) learnts.push(cr); attachClause(cr); claBumpActivity(ca[cr]); - if(d_bvp){ - ClauseId id = d_bvp->getSatProof()->registerClause(cr, LEARNT); - PSTATS( - std::unordered_set<int> cl_levels; - for (int i = 0; i < learnt_clause.size(); ++i) { - cl_levels.insert(level(var(learnt_clause[i]))); - } - if( d_bvp ){ d_bvp->getSatProof()->storeClauseGlue(id, cl_levels.size()); } - ) - d_bvp->getSatProof()->endResChain(id); - } } - + if (learnt_clause.size() == 1) { // learning a unit clause - if(d_bvp){ d_bvp->getSatProof()->endResChain(learnt_clause[0]);} } - + // if the uip was an assumption we are unsat if (level(var(p)) <= assumptions.size()) { for (int i = 0; i < learnt_clause.size(); ++i) { - assert (level(var(learnt_clause[i])) <= decisionLevel()); + assert(level(var(learnt_clause[i])) <= decisionLevel()); seen[var(learnt_clause[i])] = 1; } - // Starting new resolution chain for bit-vector proof - if( d_bvp ){ - if (cr == CRef_Undef) { - d_bvp->startBVConflict(learnt_clause[0]); - } - else { - d_bvp->startBVConflict(cr); - } - } analyzeFinal(p, conflict); - if(d_bvp){ d_bvp->endBVConflict(conflict); } Debug("bvminisat::search") << OUTPUT_TAG << " conflict on assumptions " << std::endl; return l_False; } if (!CVC4::options::bvEagerExplanations()) { - // check if uip leads to a conflict + // check if uip leads to a conflict if (backtrack_level < assumptions.size()) { cancelUntil(assumptions.size()); uncheckedEnqueue(p, cr); - + CRef new_confl = propagate(); if (new_confl != CRef_Undef) { // we have a conflict we now need to explain it - // TODO: proof for analyzeFinal2 - if(d_bvp){ d_bvp->startBVConflict(new_confl); } analyzeFinal2(p, new_confl, conflict); - if(d_bvp){ d_bvp->endBVConflict(conflict); } return l_False; } } @@ -1127,8 +979,7 @@ lbool Solver::search(int nof_conflicts, UIP uip) cancelUntil(backtrack_level); uncheckedEnqueue(p, cr); - - + varDecayActivity(); claDecayActivity(); @@ -1138,10 +989,17 @@ lbool Solver::search(int nof_conflicts, UIP uip) max_learnts *= learntsize_inc; if (verbosity >= 1) - printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", - (int)conflicts, - (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]), nClauses(), (int)clauses_literals, - (int)max_learnts, nLearnts(), (double)learnts_literals/nLearnts(), progressEstimate()*100); + printf("| %9d | %7d %8d %8d | %8d %8d %6.0f | %6.3f %% |\n", + (int)conflicts, + (int)dec_vars + - (trail_lim.size() == 0 ? trail.size() + : trail_lim[0]), + nClauses(), + (int)clauses_literals, + (int)max_learnts, + nLearnts(), + (double)learnts_literals / nLearnts(), + progressEstimate() * 100); } }else{ @@ -1152,9 +1010,9 @@ lbool Solver::search(int nof_conflicts, UIP uip) withinBudget(ResourceManager::Resource::BvSatConflictsStep); } catch (const CVC4::theory::Interrupted& e) { - // do some clean-up and rethrow - cancelUntil(assumptions.size()); - throw e; + // do some clean-up and rethrow + cancelUntil(assumptions.size()); + throw e; } if ((decisionLevel() > assumptions.size() && nof_conflicts >= 0 @@ -1192,10 +1050,8 @@ lbool Solver::search(int nof_conflicts, UIP uip) newDecisionLevel(); }else if (value(p) == l_False){ marker[var(p)] = 2; - - if(d_bvp){ d_bvp->markAssumptionConflict(); } + analyzeFinal(~p, conflict); - if(d_bvp){ d_bvp->endBVConflict(conflict); } Debug("bvminisat::search") << OUTPUT_TAG << " assumption false, we're unsat" << std::endl; return l_False; }else{ @@ -1283,7 +1139,7 @@ lbool Solver::solve_() conflict.clear(); ccmin_mode = 0; - + if (!ok) return l_False; solves++; @@ -1324,31 +1180,20 @@ lbool Solver::solve_() //================================================================================================= // Bitvector propagations -// +// void Solver::explain(Lit p, std::vector<Lit>& explanation) { Debug("bvminisat::explain") << OUTPUT_TAG << "starting explain of " << p << std::endl; // top level fact, no explanation necessary if (level(var(p)) == 0) { - if(d_bvp){ - // the only way a marker variable is - if (reason(var(p)) == CRef_Undef) { - d_bvp->startBVConflict(p); - vec<Lit> confl; - confl.push(p); - d_bvp->endBVConflict(confl); - return; - } - } - if (!THEORY_PROOF_ON()) - return; + return; } - + seen[var(p)] = 1; // if we are called at decisionLevel = 0 trail_lim is empty - int bottom = options::proof() ? 0 : trail_lim[0]; + int bottom = trail_lim[0]; for (int i = trail.size()-1; i >= bottom; i--){ Var x = var(trail[i]); if (seen[x]) { @@ -1358,21 +1203,12 @@ void Solver::explain(Lit p, std::vector<Lit>& explanation) { explanation.push_back(trail[i]); } else { Assert(level(x) == 0); - if(d_bvp){ d_bvp->getSatProof()->resolveOutUnit(~(trail[i])); } } - } else { Clause& clause = ca[reason(x)]; - if(d_bvp){ - if (p == trail[i]) { - d_bvp->startBVConflict(reason(var(p))); - } else { - d_bvp->getSatProof()->addResolutionStep(trail[i], reason(x), sign(trail[i])); - } - } for (int j = 1; j < clause.size(); j++) { - if (level(var(clause[j])) > 0 || options::proof()) + if (level(var(clause[j])) > 0) { seen[var(clause[j])] = 1; } @@ -1382,28 +1218,11 @@ void Solver::explain(Lit p, std::vector<Lit>& explanation) { } } seen[var(p)] = 0; - - if(d_bvp){ - vec<Lit> conflict_clause; - conflict_clause.push(p); - for(unsigned i = 0; i < explanation.size(); ++i) { - conflict_clause.push(~explanation[i]); - } - d_bvp->endBVConflict(conflict_clause); - } -} - -void Solver::setProofLog(proof::ResolutionBitVectorProof* bvp) -{ - d_bvp = bvp; - d_bvp->initSatProof(this); - d_bvp->getSatProof()->registerTrueLit(mkLit(varTrue, false)); - d_bvp->getSatProof()->registerFalseLit(mkLit(varFalse, true)); } //================================================================================================= // Writing CNF to DIMACS: -// +// // FIXME: this needs to be rewritten completely. static Var mapVar(Var x, vec<Var>& map, Var& max) @@ -1454,7 +1273,7 @@ void Solver::toDimacs(FILE* f, const vec<Lit>& assumps) for (int i = 0; i < clauses.size(); i++) if (!satisfied(ca[clauses[i]])) cnt++; - + for (int i = 0; i < clauses.size(); i++) if (!satisfied(ca[clauses[i]])){ Clause& clause = ca[clauses[i]]; @@ -1494,8 +1313,7 @@ void Solver::relocAll(ClauseAllocator& to) Lit p = mkLit(v, s); // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); vec<Watcher>& ws = watches[p]; - for (int j = 0; j < ws.size(); j++) - ca.reloc(ws[j].cref, to, d_bvp ? d_bvp->getSatProof() : NULL); + for (int j = 0; j < ws.size(); j++) ca.reloc(ws[j].cref, to); } // All reasons: @@ -1504,20 +1322,16 @@ void Solver::relocAll(ClauseAllocator& to) Var v = var(trail[i]); if (reason(v) != CRef_Undef && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) - ca.reloc(vardata[v].reason, to, d_bvp ? d_bvp->getSatProof() : NULL); + ca.reloc(vardata[v].reason, to); } // All learnt: // - for (int i = 0; i < learnts.size(); i++) - ca.reloc(learnts[i], to, d_bvp ? d_bvp->getSatProof() : NULL); + for (int i = 0; i < learnts.size(); i++) ca.reloc(learnts[i], to); // All original: // - for (int i = 0; i < clauses.size(); i++) - ca.reloc(clauses[i], to, d_bvp ? d_bvp->getSatProof() : NULL); - - if(d_bvp){ d_bvp->getSatProof()->finishUpdateCRef(); } + for (int i = 0; i < clauses.size(); i++) ca.reloc(clauses[i], to); } @@ -1525,33 +1339,28 @@ void Solver::garbageCollect() { // Initialize the next region to a size corresponding to the estimated utilization degree. This // is not precise but should avoid some unnecessary reallocations for the new region: - ClauseAllocator to(ca.size() - ca.wasted()); - Debug("bvminisat") << " BVMinisat::Garbage collection \n"; + ClauseAllocator to(ca.size() - ca.wasted()); + Debug("bvminisat") << " BVMinisat::Garbage collection \n"; relocAll(to); if (verbosity >= 2) - printf("| Garbage collection: %12d bytes => %12d bytes |\n", - ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + printf( + "| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, + to.size() * ClauseAllocator::Unit_Size); to.moveTo(ca); } -void ClauseAllocator::reloc(CRef& cr, - ClauseAllocator& to, - CVC4::TSatProof<Solver>* proof) +void ClauseAllocator::reloc(CRef& cr, ClauseAllocator& to) { - CRef old = cr; // save the old reference - Clause& c = operator[](cr); if (c.reloced()) { cr = c.relocation(); return; } - + cr = to.alloc(c, c.learnt()); c.relocate(cr); - if (proof) - { - proof->updateCRef(old, cr); - } - - // Copy extra data-fields: - // (This could be cleaned-up. Generalize Clause-constructor to be applicable here instead?) + + // Copy extra data-fields: + // (This could be cleaned-up. Generalize Clause-constructor to be applicable + // here instead?) to[cr].mark(c.mark()); if (to[cr].learnt()) to[cr].activity() = c.activity(); else if (to[cr].has_extra()) to[cr].calcAbstraction(); diff --git a/src/prop/bvminisat/core/Solver.h b/src/prop/bvminisat/core/Solver.h index 7fad72d6d..f2721c88d 100644 --- a/src/prop/bvminisat/core/Solver.h +++ b/src/prop/bvminisat/core/Solver.h @@ -25,7 +25,6 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "context/context.h" #include "proof/clause_id.h" -#include "proof/sat_proof.h" #include "prop/bvminisat/core/SolverTypes.h" #include "prop/bvminisat/mtl/Alg.h" #include "prop/bvminisat/mtl/Heap.h" @@ -39,11 +38,6 @@ namespace BVMinisat { class Solver; } -// TODO (aozdemir) replace this forward declaration with an include -namespace proof { -class ResolutionBitVectorProof; -} - namespace BVMinisat { /** Interface for minisat callbacks */ @@ -71,11 +65,10 @@ public: //================================================================================================= // Solver -- the main class: class Solver { - friend class CVC4::TSatProof< CVC4::BVMinisat::Solver>; public: typedef Var TVar; typedef Lit TLit; - typedef Clause TClause; + typedef Clause TClause; typedef CRef TCRef; typedef vec<Lit> TLitVec; @@ -109,12 +102,17 @@ public: Var trueVar() const { return varTrue; } Var falseVar() const { return varFalse; } - - bool addClause (const vec<Lit>& ps, ClauseId& id); // Add a clause to the solver. + bool addClause(const vec<Lit>& ps, + ClauseId& id); // Add a clause to the solver. bool addEmptyClause(); // Add the empty clause, making the solver contradictory. - bool addClause (Lit p, ClauseId& id); // Add a unit clause to the solver. - bool addClause (Lit p, Lit q, ClauseId& id); // Add a binary clause to the solver. - bool addClause (Lit p, Lit q, Lit r, ClauseId& id); // Add a ternary clause to the solver. + bool addClause(Lit p, ClauseId& id); // Add a unit clause to the solver. + bool addClause(Lit p, + Lit q, + ClauseId& id); // Add a binary clause to the solver. + bool addClause(Lit p, + Lit q, + Lit r, + ClauseId& id); // Add a ternary clause to the solver. bool addClause_( vec<Lit>& ps, ClauseId& id); // Add a clause to the solver without making superflous internal copy. Will // change the passed vector 'ps'. @@ -141,9 +139,9 @@ public: void toDimacs (const char* file, Lit p); void toDimacs (const char* file, Lit p, Lit q); void toDimacs (const char* file, Lit p, Lit q, Lit r); - + // Variable mode: - // + // void setPolarity (Var v, bool b); // Declare which polarity the decision heuristic should use for a variable. Requires mode 'polarity_user'. void setDecisionVar (Var v, bool b); // Declare if a variable should be eligible for selection in the decision heuristic. @@ -211,13 +209,12 @@ public: void addMarkerLiteral(Var var); - bool need_to_propagate; // true if we added new clauses, set to true in propagation + bool need_to_propagate; // true if we added new clauses, set to true in + // propagation bool only_bcp; // solving mode in which only boolean constraint propagation is done void setOnlyBCP (bool val) { only_bcp = val;} void explain(Lit l, std::vector<Lit>& explanation); - void setProofLog(CVC4::proof::ResolutionBitVectorProof* bvp); - protected: // has a clause been added @@ -293,9 +290,6 @@ public: int64_t conflict_budget; // -1 means no budget. int64_t propagation_budget; // -1 means no budget. bool asynch_interrupt; - - //proof log - CVC4::proof::ResolutionBitVectorProof* d_bvp; // Main internal methods: // @@ -458,13 +452,15 @@ inline int Solver::nLearnts () const { return learnts.size(); } inline int Solver::nVars () const { return vardata.size(); } inline int Solver::nFreeVars () const { return (int)dec_vars - (trail_lim.size() == 0 ? trail.size() : trail_lim[0]); } inline void Solver::setPolarity (Var v, bool b) { polarity[v] = b; } -inline void Solver::setDecisionVar(Var v, bool b) -{ - if ( b && !decision[v]) dec_vars++; - else if (!b && decision[v]) dec_vars--; +inline void Solver::setDecisionVar(Var v, bool b) +{ + if (b && !decision[v]) + dec_vars++; + else if (!b && decision[v]) + dec_vars--; - decision[v] = b; - insertVarOrder(v); + decision[v] = b; + insertVarOrder(v); } inline void Solver::setConfBudget(int64_t x){ conflict_budget = conflicts + x; } inline void Solver::setPropBudget(int64_t x){ propagation_budget = propagations + x; } diff --git a/src/prop/bvminisat/core/SolverTypes.h b/src/prop/bvminisat/core/SolverTypes.h index 302db104f..cf9ce7e15 100644 --- a/src/prop/bvminisat/core/SolverTypes.h +++ b/src/prop/bvminisat/core/SolverTypes.h @@ -86,15 +86,14 @@ struct LitHashFunction { const Lit lit_Undef = { -2 }; // }- Useful special constants. const Lit lit_Error = { -1 }; // } - //================================================================================================= // Lifted booleans: // -// NOTE: this implementation is optimized for the case when comparisons between values are mostly -// between one variable and one constant. Some care had to be taken to make sure that gcc -// does enough constant propagation to produce sensible code, and this appears to be somewhat -// fragile unfortunately. - +// NOTE: this implementation is optimized for the case when comparisons between +// values are mostly +// between one variable and one constant. Some care had to be taken to +// make sure that gcc does enough constant propagation to produce sensible +// code, and this appears to be somewhat fragile unfortunately. #ifndef l_True #define l_True (lbool((uint8_t)0)) // gcc does not do constant propagation if these are real constants. @@ -121,10 +120,12 @@ public: bool operator != (lbool b) const { return !(*this == b); } lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } - lbool operator && (lbool b) const { - uint8_t sel = (this->value << 1) | (b.value << 3); - uint8_t v = (0xF7F755F4 >> sel) & 3; - return lbool(v); } + lbool operator&&(lbool b) const + { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); + } lbool operator || (lbool b) const { uint8_t sel = (this->value << 1) | (b.value << 3); @@ -163,14 +164,14 @@ class Clause { header.reloced = 0; header.size = ps.size(); - for (int i = 0; i < ps.size(); i++) - data[i].lit = ps[i]; + for (int i = 0; i < ps.size(); i++) data[i].lit = ps[i]; if (header.has_extra){ if (header.learnt) - data[header.size].act = 0; - else - calcAbstraction(); } + data[header.size].act = 0; + else + calcAbstraction(); + } } public: @@ -256,9 +257,7 @@ class ClauseAllocator : public RegionAllocator<uint32_t> RegionAllocator<uint32_t>::free(clauseWord32Size(c.size(), c.has_extra())); } - void reloc(CRef& cr, - ClauseAllocator& to, - CVC4::TSatProof<Solver>* proof = NULL); + void reloc(CRef& cr, ClauseAllocator& to); }; @@ -275,7 +274,7 @@ class OccLists public: OccLists(const Deleted& d) : deleted(d) {} - + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } @@ -334,13 +333,12 @@ class CMap typedef Map<CRef, T, CRefHash> HashTable; HashTable map; - + public: // Size-operations: void clear () { map.clear(); } int size () const { return map.elems(); } - // Insert/Remove/Test mapping: void insert (CRef cr, const T& t){ map.insert(cr, t); } void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility @@ -363,15 +361,14 @@ class CMap printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } }; - /*_________________________________________________________________________________________________ | | subsumes : (other : const Clause&) -> Lit -| +| | Description: -| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' -| by subsumption resolution. -| +| Checks if clause subsumes 'other', and at the same time, if it can be +used to simplify 'other' | by subsumption resolution. +| | Result: | lit_Error - No subsumption or simplification | lit_Undef - Clause subsumes 'other' diff --git a/src/prop/bvminisat/simp/SimpSolver.cc b/src/prop/bvminisat/simp/SimpSolver.cc index b003342c6..9429e4ced 100644 --- a/src/prop/bvminisat/simp/SimpSolver.cc +++ b/src/prop/bvminisat/simp/SimpSolver.cc @@ -23,7 +23,6 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "options/bv_options.h" #include "options/smt_options.h" #include "proof/clause_id.h" -#include "proof/proof.h" #include "prop/bvminisat/mtl/Sort.h" #include "prop/bvminisat/utils/System.h" @@ -64,7 +63,7 @@ SimpSolver::SimpSolver(CVC4::context::Context* context) asymm_lits(0), eliminated_vars(0), elimorder(1), - use_simplification(!PROOF_ON()), + use_simplification(true), occurs(ClauseDeleted(ca)), elim_heap(ElimLt(n_occ)), bwdsub_assigns(0), @@ -94,7 +93,7 @@ SimpSolver::SimpSolver(CVC4::context::Context* context) SimpSolver::~SimpSolver() { - // CVC4::StatisticsRegistry::unregisterStat(&total_eliminate_time); + // CVC4::StatisticsRegistry::unregisterStat(&total_eliminate_time); } @@ -111,7 +110,7 @@ Var SimpSolver::newVar(bool sign, bool dvar, bool freeze) { touched .push(0); elim_heap .insert(v); if (freeze) { - setFrozen(v, true); + setFrozen(v, true); } } return v; @@ -122,7 +121,7 @@ Var SimpSolver::newVar(bool sign, bool dvar, bool freeze) { lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) { only_bcp = false; - + vec<Var> extra_frozen; lbool result = l_True; @@ -210,14 +209,15 @@ void SimpSolver::removeClause(CRef cr) const Clause& clause = ca[cr]; if (use_simplification) + { for (int i = 0; i < clause.size(); i++) { n_occ[toInt(clause[i])]--; updateElimHeap(var(clause[i])); occurs.smudge(var(clause[i])); } - - Solver::removeClause(cr); + } + Solver::removeClause(cr); } @@ -538,7 +538,7 @@ bool SimpSolver::eliminateVar(Var v) for (int i = 0; i < cls.size(); i++) (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); - // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // Check whether the increase in number of clauses stays within the allowed ('grow'). Moreover, no // clause must exceed the limit on the maximal clause size (if it is set): // int cnt = 0; @@ -546,9 +546,10 @@ bool SimpSolver::eliminateVar(Var v) for (int i = 0; i < pos.size(); i++) for (int j = 0; j < neg.size(); j++) - if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && - (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) - return true; + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) + && (++cnt > cls.size() + grow + || (clause_lim != -1 && clause_size > clause_lim))) + return true; // Delete and store old clauses: eliminated[v] = true; @@ -565,8 +566,7 @@ bool SimpSolver::eliminateVar(Var v) mkElimClause(elimclauses, ~mkLit(v)); } - for (int i = 0; i < cls.size(); i++) - removeClause(cls[i]); + for (int i = 0; i < cls.size(); i++) removeClause(cls[i]); // Produce clauses in cross product: vec<Lit>& resolvent = add_tmp; @@ -580,7 +580,7 @@ bool SimpSolver::eliminateVar(Var v) // Free occurs list for this variable: occurs[v].clear(true); - + // Free watchers lists for this variable, if possible: if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); @@ -600,7 +600,7 @@ bool SimpSolver::substitute(Var v, Lit x) eliminated[v] = true; setDecisionVar(v, false); const vec<CRef>& cls = occurs.lookup(v); - + vec<Lit>& subst_clause = add_tmp; for (int i = 0; i < cls.size(); i++){ Clause& clause = ca[cls[i]]; @@ -643,7 +643,7 @@ bool SimpSolver::eliminate(bool turn_off_elim) { // CVC4::TimerStat::CodeTimer codeTimer(total_eliminate_time); - + if (!simplify()) return false; else if (!use_simplification) @@ -655,9 +655,12 @@ bool SimpSolver::eliminate(bool turn_off_elim) gatherTouchedClauses(); // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); - if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && - !backwardSubsumptionCheck(true)){ - ok = false; goto cleanup; } + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) + && !backwardSubsumptionCheck(true)) + { + ok = false; + goto cleanup; + } // Empty elim_heap and return immediately on user-interrupt: if (asynch_interrupt){ @@ -670,7 +673,7 @@ bool SimpSolver::eliminate(bool turn_off_elim) // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); for (int cnt = 0; !elim_heap.empty(); cnt++){ Var elim = elim_heap.removeMin(); - + if (asynch_interrupt) break; if (isEliminated(elim) || value(elim) != l_Undef) continue; @@ -720,8 +723,10 @@ bool SimpSolver::eliminate(bool turn_off_elim) } if (verbosity >= 1 && elimclauses.size() > 0) - printf("| Eliminated clauses: %10.2f Mb |\n", - double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + printf( + "| Eliminated clauses: %10.2f Mb " + " |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024 * 1024)); return ok; @@ -772,15 +777,17 @@ void SimpSolver::garbageCollect() { // Initialize the next region to a size corresponding to the estimated utilization degree. This // is not precise but should avoid some unnecessary reallocations for the new region: - ClauseAllocator to(ca.size() - ca.wasted()); + ClauseAllocator to(ca.size() - ca.wasted()); cleanUpClauses(); to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. relocAll(to); Solver::relocAll(to); if (verbosity >= 2) - printf("| Garbage collection: %12d bytes => %12d bytes |\n", - ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + printf( + "| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, + to.size() * ClauseAllocator::Unit_Size); to.moveTo(ca); } diff --git a/src/prop/bvminisat/simp/SimpSolver.h b/src/prop/bvminisat/simp/SimpSolver.h index 9d3a51c02..9907b8d72 100644 --- a/src/prop/bvminisat/simp/SimpSolver.h +++ b/src/prop/bvminisat/simp/SimpSolver.h @@ -54,7 +54,7 @@ class SimpSolver : public Solver { Lit r, ClauseId& id); // Add a ternary clause to the solver. bool addClause_(vec<Lit>& ps, ClauseId& id); - bool substitute(Var v, Lit x); // Replace all occurences of v with x (may + bool substitute(Var v, Lit x); // Replace all occurrences of v with x (may // cause a contradiction). // Variable mode: diff --git a/src/prop/cadical.cpp b/src/prop/cadical.cpp index ca447cea8..7cc5b16cd 100644 --- a/src/prop/cadical.cpp +++ b/src/prop/cadical.cpp @@ -19,7 +19,6 @@ #ifdef CVC4_USE_CADICAL #include "base/check.h" -#include "proof/sat_proof.h" namespace CVC4 { namespace prop { diff --git a/src/prop/cnf_stream.cpp b/src/prop/cnf_stream.cpp index a6a4b6859..c46cd5136 100644 --- a/src/prop/cnf_stream.cpp +++ b/src/prop/cnf_stream.cpp @@ -83,22 +83,13 @@ void CnfStream::assertClause(TNode node, SatClause& c) { } } - if (PROOF_ON() && d_cnfProof) - { - d_cnfProof->pushCurrentDefinition(node); - } - ClauseId clause_id = d_satSolver->addClause(c, d_removable); if (clause_id == ClauseIdUndef) return; // nothing to store (no clause was added) - if (PROOF_ON() && d_cnfProof) + if (d_cnfProof && clause_id != ClauseIdError) { - if (clause_id != ClauseIdError) - { - d_cnfProof->registerConvertedClause(clause_id); - } - d_cnfProof->popCurrentDefinition(); - }; + d_cnfProof->registerConvertedClause(clause_id); + } } void CnfStream::assertClause(TNode node, SatLiteral a) { @@ -171,11 +162,17 @@ void TseitinCnfStream::ensureLiteral(TNode n, bool noPreregistration) { // non-empty and that we are not associating a bogus assertion with the // clause. This should be ok because we use the mapping back to assertions // for clauses from input assertions only. - PROOF(if (d_cnfProof) { d_cnfProof->pushCurrentAssertion(Node::null()); }); + if (d_cnfProof) + { + d_cnfProof->pushCurrentAssertion(Node::null()); + } lit = toCNF(n, false); - PROOF(if (d_cnfProof) { d_cnfProof->popCurrentAssertion(); }); + if (d_cnfProof) + { + d_cnfProof->popCurrentAssertion(); + } // Store backward-mappings // These may already exist @@ -547,7 +544,6 @@ void TseitinCnfStream::convertAndAssertAnd(TNode node, bool negated) { // If the node is a conjunction, we handle each conjunct separately for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); conjunct != node_end; ++conjunct ) { - PROOF(if (d_cnfProof) d_cnfProof->setCnfDependence(*conjunct, node);); convertAndAssert(*conjunct, false); } } else { @@ -581,7 +577,6 @@ void TseitinCnfStream::convertAndAssertOr(TNode node, bool negated) { // If the node is a conjunction, we handle each conjunct separately for(TNode::const_iterator conjunct = node.begin(), node_end = node.end(); conjunct != node_end; ++conjunct ) { - PROOF(if (d_cnfProof) d_cnfProof->setCnfDependence((*conjunct).negate(), node.negate());); convertAndAssert(*conjunct, true); } } @@ -658,8 +653,6 @@ void TseitinCnfStream::convertAndAssertImplies(TNode node, bool negated) { clause[1] = q; assertClause(node, clause); } else {// Construct the - PROOF(if (d_cnfProof) d_cnfProof->setCnfDependence(node[0], node.negate());); - PROOF(if (d_cnfProof) d_cnfProof->setCnfDependence(node[1].negate(), node.negate());); // !(p => q) is the same as (p && ~q) convertAndAssert(node[0], false); convertAndAssert(node[1], true); @@ -693,32 +686,23 @@ void TseitinCnfStream::convertAndAssertIte(TNode node, bool negated) { void TseitinCnfStream::convertAndAssert(TNode node, bool removable, bool negated, - ProofRule proof_id, - TNode from) { + bool input) +{ Debug("cnf") << "convertAndAssert(" << node << ", removable = " << (removable ? "true" : "false") << ", negated = " << (negated ? "true" : "false") << ")" << endl; d_removable = removable; - PROOF - (if (d_cnfProof) { - Node assertion = negated ? node.notNode() : (Node)node; - Node from_assertion = negated? from.notNode() : (Node) from; - - if (proof_id != RULE_INVALID) { - d_cnfProof->pushCurrentAssertion(from.isNull() ? assertion : from_assertion); - d_cnfProof->registerAssertion(from.isNull() ? assertion : from_assertion, proof_id); - } - else { - d_cnfProof->pushCurrentAssertion(assertion); - d_cnfProof->registerAssertion(assertion, proof_id); - } - }); + if (d_cnfProof) + { + d_cnfProof->pushCurrentAssertion(negated ? node.notNode() : (Node)node, + input); + } convertAndAssert(node, negated); - PROOF - (if (d_cnfProof) { - d_cnfProof->popCurrentAssertion(); - }); + if (d_cnfProof) + { + d_cnfProof->popCurrentAssertion(); + } } void TseitinCnfStream::convertAndAssert(TNode node, bool negated) { diff --git a/src/prop/cnf_stream.h b/src/prop/cnf_stream.h index 40243e5b9..aec4257f2 100644 --- a/src/prop/cnf_stream.h +++ b/src/prop/cnf_stream.h @@ -186,10 +186,13 @@ class CnfStream { * @param node node to convert and assert * @param removable whether the sat solver can choose to remove the clauses * @param negated whether we are asserting the node negated + * @param input whether it is an input assertion (rather than a lemma). This + * information is only relevant for unsat core tracking. */ - virtual void convertAndAssert(TNode node, bool removable, bool negated, - ProofRule proof_id, - TNode from = TNode::null()) = 0; + virtual void convertAndAssert(TNode node, + bool removable, + bool negated, + bool input = false) = 0; /** * Get the node that is represented by the given SatLiteral. @@ -269,12 +272,13 @@ class TseitinCnfStream : public CnfStream { * @param node the formula to assert * @param removable is this something that can be erased * @param negated true if negated + * @param input whether it is an input assertion (rather than a lemma). This + * information is only relevant for unsat core tracking. */ void convertAndAssert(TNode node, bool removable, bool negated, - ProofRule rule, - TNode from = TNode::null()) override; + bool input = false) override; private: /** diff --git a/src/prop/cryptominisat.cpp b/src/prop/cryptominisat.cpp index 9dc04c274..9927172be 100644 --- a/src/prop/cryptominisat.cpp +++ b/src/prop/cryptominisat.cpp @@ -19,8 +19,6 @@ #include "prop/cryptominisat.h" #include "base/check.h" -#include "proof/clause_id.h" -#include "proof/sat_proof.h" #include <cryptominisat5/cryptominisat.h> @@ -86,8 +84,9 @@ void CryptoMinisatSolver::init() CryptoMinisatSolver::~CryptoMinisatSolver() {} ClauseId CryptoMinisatSolver::addXorClause(SatClause& clause, - bool rhs, - bool removable) { + bool rhs, + bool removable) +{ Debug("sat::cryptominisat") << "Add xor clause " << clause <<" = " << rhs << "\n"; if (!d_okay) { @@ -96,7 +95,7 @@ ClauseId CryptoMinisatSolver::addXorClause(SatClause& clause, } ++(d_statistics.d_xorClausesAdded); - + // ensure all sat literals have positive polarity by pushing // the negation on the result std::vector<CMSatVar> xor_clause; @@ -118,36 +117,19 @@ ClauseId CryptoMinisatSolver::addClause(SatClause& clause, bool removable){ } ++(d_statistics.d_clausesAdded); - + std::vector<CMSat::Lit> internal_clause; toInternalClause(clause, internal_clause); bool nowOkay = d_solver->add_clause(internal_clause); ClauseId freshId; - if (THEORY_PROOF_ON()) - { - freshId = ClauseId(ProofManager::currentPM()->nextId()); - // If this clause results in a conflict, then `nowOkay` may be false, but - // we still need to register this clause as used. Thus, we look at - // `d_okay` instead - if (d_bvp && d_okay) - { - d_bvp->registerUsedClause(freshId, clause); - } - } - else - { - freshId = ClauseIdError; - } + freshId = ClauseIdError; d_okay &= nowOkay; return freshId; } -bool CryptoMinisatSolver::ok() const { - return d_okay; -} - +bool CryptoMinisatSolver::ok() const { return d_okay; } SatVariable CryptoMinisatSolver::newVar(bool isTheoryAtom, bool preRegister, bool canErase){ d_solver->new_var(); @@ -207,19 +189,11 @@ SatValue CryptoMinisatSolver::value(SatLiteral l){ return toSatLiteralValue(value); } -SatValue CryptoMinisatSolver::modelValue(SatLiteral l){ - return value(l); -} +SatValue CryptoMinisatSolver::modelValue(SatLiteral l) { return value(l); } unsigned CryptoMinisatSolver::getAssertionLevel() const { Unreachable() << "No interface to get assertion level in Cryptominisat"; - return -1; -} - -void CryptoMinisatSolver::setClausalProofLog(proof::ClausalBitVectorProof* bvp) -{ - d_bvp = bvp; - d_solver->set_drat(&bvp->getDratOstream(), false); + return -1; } // Satistics for CryptoMinisatSolver diff --git a/src/prop/cryptominisat.h b/src/prop/cryptominisat.h index d60855654..6d3b351b0 100644 --- a/src/prop/cryptominisat.h +++ b/src/prop/cryptominisat.h @@ -21,7 +21,6 @@ #ifdef CVC4_USE_CRYPTOMINISAT -#include "proof/clausal_bitvector_proof.h" #include "prop/sat_solver.h" // Cryptominisat has name clashes with the other Minisat implementations since @@ -68,7 +67,6 @@ class CryptoMinisatSolver : public SatSolver SatValue modelValue(SatLiteral l) override; unsigned getAssertionLevel() const override; - void setClausalProofLog(proof::ClausalBitVectorProof* bvp) override; private: class Statistics @@ -97,7 +95,6 @@ class CryptoMinisatSolver : public SatSolver void init(); std::unique_ptr<CMSat::SATSolver> d_solver; - proof::ClausalBitVectorProof* d_bvp; unsigned d_numVariables; bool d_okay; SatVariable d_true; diff --git a/src/prop/kissat.cpp b/src/prop/kissat.cpp index 0440fcd4e..544a37a3e 100644 --- a/src/prop/kissat.cpp +++ b/src/prop/kissat.cpp @@ -19,7 +19,6 @@ #ifdef CVC4_USE_KISSAT #include "base/check.h" -#include "proof/sat_proof.h" namespace CVC4 { namespace prop { diff --git a/src/prop/minisat/core/Solver.cc b/src/prop/minisat/core/Solver.cc index f56f6a447..311224a03 100644 --- a/src/prop/minisat/core/Solver.cc +++ b/src/prop/minisat/core/Solver.cc @@ -30,6 +30,7 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "options/prop_options.h" #include "options/smt_options.h" #include "proof/clause_id.h" +#include "proof/cnf_proof.h" #include "proof/proof_manager.h" #include "proof/sat_proof.h" #include "proof/sat_proof_implementation.h" @@ -217,7 +218,10 @@ Solver::Solver(CVC4::prop::TheoryProxy* proxy, propagation_budget(-1), asynch_interrupt(false) { - PROOF(ProofManager::currentPM()->initSatProof(this);) + if (options::unsatCores()) + { + ProofManager::currentPM()->initSatProof(this); + } // Create the constant variables varTrue = newVar(true, false, false); @@ -227,11 +231,11 @@ Solver::Solver(CVC4::prop::TheoryProxy* proxy, uncheckedEnqueue(mkLit(varTrue, false)); uncheckedEnqueue(mkLit(varFalse, true)); // FIXME: these should be axioms I believe - PROOF - ( - ProofManager::getSatProof()->registerTrueLit(mkLit(varTrue, false)); - ProofManager::getSatProof()->registerFalseLit(mkLit(varFalse, true)); - ); + if (options::unsatCores()) + { + ProofManager::getSatProof()->registerTrueLit(mkLit(varTrue, false)); + ProofManager::getSatProof()->registerFalseLit(mkLit(varFalse, true)); + } } @@ -390,15 +394,20 @@ CRef Solver::reason(Var x) { // FIXME: at some point will need more information about where this explanation // came from (ie. the theory/sharing) Debug("pf::sat") << "Minisat::Solver registering a THEORY_LEMMA (1)" << std::endl; - PROOF(ClauseId id = ProofManager::getSatProof()->registerClause( - real_reason, THEORY_LEMMA); - ProofManager::getCnfProof()->registerConvertedClause(id, true); - // explainPropagation() pushes the explanation on the assertion stack - // in CnfProof, so we need to pop it here. This is important because - // reason() may be called indirectly while adding a clause, which can - // lead to a wrong assertion being associated with the clause being - // added (see issue #2137). - ProofManager::getCnfProof()->popCurrentAssertion();); + if (options::unsatCores()) + { + ClauseId id = ProofManager::getSatProof()->registerClause(real_reason, + THEORY_LEMMA); + // map id to assertion, which may be required if looking for + // lemmas in unsat core + ProofManager::getCnfProof()->registerConvertedClause(id); + // explainPropagation() pushes the explanation on the assertion stack + // in CnfProof, so we need to pop it here. This is important because + // reason() may be called indirectly while adding a clause, which can + // lead to a wrong assertion being associated with the clause being + // added (see issue #2137). + ProofManager::getCnfProof()->popCurrentAssertion(); + } vardata[x] = VarData(real_reason, level(x), user_level(x), intro_level(x), trail_index(x)); clauses_removable.push(real_reason); attachClause(real_reason); @@ -441,9 +450,13 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) } // If a literal is false at 0 level (both sat and user level) we also ignore it if (value(ps[i]) == l_False) { - if (!PROOF_ON() && level(var(ps[i])) == 0 && user_level(var(ps[i])) == 0) { + if (!options::unsatCores() && level(var(ps[i])) == 0 + && user_level(var(ps[i])) == 0) + { continue; - } else { + } + else + { // If we decide to keep it, we count it into the false literals falseLiteralsCount ++; } @@ -467,34 +480,46 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) lemmas.push(); ps.copyTo(lemmas.last()); lemmas_removable.push(removable); - PROOF( - // Store the expression being converted to CNF until - // the clause is actually created - Node assertion = ProofManager::getCnfProof()->getCurrentAssertion(); - Node def = ProofManager::getCnfProof()->getCurrentDefinition(); - lemmas_cnf_assertion.push_back(std::make_pair(assertion, def)); - id = ClauseIdUndef; - ); - // does it have to always be a lemma? - // PROOF(id = ProofManager::getSatProof()->registerUnitClause(ps[0], THEORY_LEMMA);); - // PROOF(id = ProofManager::getSatProof()->registerTheoryLemma(ps);); - // Debug("cores") << "lemma push " << proof_id << " " << (proof_id & 0xffffffff) << std::endl; - // lemmas_proof_id.push(proof_id); + if (options::unsatCores()) + { + // Store the expression being converted to CNF until + // the clause is actually created + lemmas_cnf_assertion.push_back( + ProofManager::getCnfProof()->getCurrentAssertion()); + id = ClauseIdUndef; + } } else { assert(decisionLevel() == 0); // If all false, we're in conflict if (ps.size() == falseLiteralsCount) { - if(PROOF_ON()) { + if (options::unsatCores()) + { // 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( id = ProofManager::getSatProof()->storeUnitConflict(ps[0], INPUT); ) - PROOF( ProofManager::getSatProof()->finalizeProof(CVC4::Minisat::CRef_Lazy); ) + if (options::unsatCores()) + { + ClauseKind ck = + ProofManager::getCnfProof()->getCurrentAssertionKind() + ? INPUT + : THEORY_LEMMA; + id = ProofManager::getSatProof()->storeUnitConflict(ps[0], ck); + // map id to assertion, which may be required if looking for + // lemmas in unsat core + if (ck == THEORY_LEMMA) + { + ProofManager::getCnfProof()->registerConvertedClause(id); + } + ProofManager::getSatProof()->finalizeProof( + CVC4::Minisat::CRef_Lazy); + } return ok = false; } - } else { + } + else + { return ok = false; } } @@ -509,14 +534,23 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) cr = ca.alloc(clauseLevel, ps, false); clauses_persistent.push(cr); - attachClause(cr); - - if(PROOF_ON()) { - PROOF( - id = ProofManager::getSatProof()->registerClause(cr, INPUT); - ) - if(ps.size() == falseLiteralsCount) { - PROOF( ProofManager::getSatProof()->finalizeProof(cr); ) + attachClause(cr); + + if (options::unsatCores()) + { + ClauseKind ck = ProofManager::getCnfProof()->getCurrentAssertionKind() + ? INPUT + : THEORY_LEMMA; + id = ProofManager::getSatProof()->registerClause(cr, ck); + // map id to assertion, which may be required if looking for + // lemmas in unsat core + if (ck == THEORY_LEMMA) + { + ProofManager::getCnfProof()->registerConvertedClause(id); + } + if (ps.size() == falseLiteralsCount) + { + ProofManager::getSatProof()->finalizeProof(cr); return ok = false; } } @@ -527,15 +561,25 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) if(assigns[var(ps[0])] == l_Undef) { assert(assigns[var(ps[0])] != l_False); uncheckedEnqueue(ps[0], cr); - Debug("cores") << "i'm registering a unit clause, input" << std::endl; - PROOF( - if(ps.size() == 1) { - id = ProofManager::getSatProof()->registerUnitClause(ps[0], INPUT); - } - ); + Debug("cores") << "i'm registering a unit clause, maybe input" + << std::endl; + if (options::unsatCores() && ps.size() == 1) + { + ClauseKind ck = + ProofManager::getCnfProof()->getCurrentAssertionKind() + ? INPUT + : THEORY_LEMMA; + id = ProofManager::getSatProof()->registerUnitClause(ps[0], ck); + // map id to assertion, which may be required if looking for + // lemmas in unsat core + if (ck == THEORY_LEMMA) + { + ProofManager::getCnfProof()->registerConvertedClause(id); + } + } CRef confl = propagate(CHECK_WITHOUT_THEORY); if(! (ok = (confl == CRef_Undef)) ) { - if (PROOF_ON()) + if (options::unsatCores()) { if (ca[confl].size() == 1) { @@ -552,7 +596,10 @@ bool Solver::addClause_(vec<Lit>& ps, bool removable, ClauseId& id) } return ok; } else { - PROOF(id = ClauseIdUndef;); + if (options::unsatCores()) + { + id = ClauseIdUndef; + } return ok; } } @@ -575,7 +622,10 @@ void Solver::attachClause(CRef cr) { void Solver::detachClause(CRef cr, bool strict) { const Clause& c = ca[cr]; - PROOF( ProofManager::getSatProof()->markDeleted(cr); ); + if (options::unsatCores()) + { + ProofManager::getSatProof()->markDeleted(cr); + } Debug("minisat") << "Solver::detachClause(" << c << ")" << std::endl; assert(c.size() > 1); @@ -826,7 +876,10 @@ int Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel) int max_resolution_level = 0; // Maximal level of the resolved clauses - PROOF( ProofManager::getSatProof()->startResChain(confl); ) + if (options::unsatCores()) + { + ProofManager::getSatProof()->startResChain(confl); + } do{ assert(confl != CRef_Undef); // (otherwise should be UIP) @@ -867,9 +920,9 @@ int Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel) } // FIXME: can we do it lazily if we actually need the proof? - if (level(var(q)) == 0) + if (options::unsatCores() && level(var(q)) == 0) { - PROOF(ProofManager::getSatProof()->resolveOutUnit(q);) + ProofManager::getSatProof()->resolveOutUnit(q); } } } @@ -881,8 +934,9 @@ int Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel) seen[var(p)] = 0; pathC--; - if ( pathC > 0 && confl != CRef_Undef ) { - PROOF( ProofManager::getSatProof()->addResolutionStep(p, confl, sign(p)); ) + if (options::unsatCores() && pathC > 0 && confl != CRef_Undef) + { + ProofManager::getSatProof()->addResolutionStep(p, confl, sign(p)); } }while (pathC > 0); @@ -905,7 +959,10 @@ int Solver::analyze(CRef confl, vec<Lit>& out_learnt, int& out_btlevel) // Literal is not redundant out_learnt[j++] = out_learnt[i]; } else { - PROOF( ProofManager::getSatProof()->storeLitRedundant(out_learnt[i]); ) + if (options::unsatCores()) + { + ProofManager::getSatProof()->storeLitRedundant(out_learnt[i]); + } // Literal is redundant, to be safe, mark the level as current assertion level // TODO: maybe optimize max_resolution_level = std::max(max_resolution_level, user_level(var(out_learnt[i]))); @@ -1169,7 +1226,10 @@ void Solver::propagateTheory() { addClause(explanation, true, id); // explainPropagation() pushes the explanation on the assertion // stack in CnfProof, so we need to pop it here. - PROOF(ProofManager::getCnfProof()->popCurrentAssertion();) + if (options::unsatCores()) + { + ProofManager::getCnfProof()->popCurrentAssertion(); + } } } } @@ -1310,9 +1370,10 @@ void Solver::removeSatisfied(vec<CRef>& cs) for (i = j = 0; i < cs.size(); i++){ Clause& c = ca[cs[i]]; if (satisfied(c)) { - if (locked(c)) { + if (options::unsatCores() && locked(c)) + { // store a resolution of the literal c propagated - PROOF( ProofManager::getSatProof()->storeUnitResolution(c[0]); ) + ProofManager::getSatProof()->storeUnitResolution(c[0]); } removeClause(cs[i]); } @@ -1412,8 +1473,11 @@ lbool Solver::search(int nof_conflicts) conflicts++; conflictC++; if (decisionLevel() == 0) { - PROOF( ProofManager::getSatProof()->finalizeProof(confl); ) - return l_False; + if (options::unsatCores()) + { + ProofManager::getSatProof()->finalizeProof(confl); + } + return l_False; } // Analyze the conflict @@ -1425,8 +1489,10 @@ lbool Solver::search(int nof_conflicts) if (learnt_clause.size() == 1) { uncheckedEnqueue(learnt_clause[0]); - PROOF( ProofManager::getSatProof()->endResChain(learnt_clause[0]); ) - + if (options::unsatCores()) + { + ProofManager::getSatProof()->endResChain(learnt_clause[0]); + } } else { CRef cr = ca.alloc(assertionLevelOnly() ? assertionLevel : max_level, @@ -1436,15 +1502,12 @@ lbool Solver::search(int nof_conflicts) attachClause(cr); claBumpActivity(ca[cr]); uncheckedEnqueue(learnt_clause[0], cr); - PROOF(ClauseId id = - ProofManager::getSatProof()->registerClause(cr, LEARNT); - PSTATS(std::unordered_set<int> cl_levels; - for (int i = 0; i < learnt_clause.size(); ++i) { - cl_levels.insert(level(var(learnt_clause[i]))); - } ProofManager::getSatProof() - ->storeClauseGlue(id, cl_levels.size());) - ProofManager::getSatProof() - ->endResChain(id);); + if (options::unsatCores()) + { + ClauseId id = + ProofManager::getSatProof()->registerClause(cr, LEARNT); + ProofManager::getSatProof()->endResChain(id); + } } varDecayActivity(); @@ -1469,25 +1532,33 @@ lbool Solver::search(int nof_conflicts) } } else { - - // If this was a final check, we are satisfiable - if (check_type == CHECK_FINAL) { - bool decisionEngineDone = d_proxy->isDecisionEngineDone(); - // Unless a lemma has added more stuff to the queues - if (!decisionEngineDone && - (!order_heap.empty() || qhead < trail.size()) ) { - check_type = CHECK_WITH_THEORY; - continue; - } else if (recheck) { - // There some additional stuff added, so we go for another full-check - continue; - } else { - // Yes, we're truly satisfiable - return l_True; - } - } else if (check_type == CHECK_FINAL_FAKE) { + // If this was a final check, we are satisfiable + if (check_type == CHECK_FINAL) + { + bool decisionEngineDone = d_proxy->isDecisionEngineDone(); + // Unless a lemma has added more stuff to the queues + if (!decisionEngineDone + && (!order_heap.empty() || qhead < trail.size())) + { check_type = CHECK_WITH_THEORY; + continue; + } + else if (recheck) + { + // There some additional stuff added, so we go for another + // full-check + continue; } + else + { + // Yes, we're truly satisfiable + return l_True; + } + } + else if (check_type == CHECK_FINAL_FAKE) + { + check_type = CHECK_WITH_THEORY; + } if ((nof_conflicts >= 0 && conflictC >= nof_conflicts) || !withinBudget(ResourceManager::Resource::SatConflictStep)) @@ -1744,7 +1815,10 @@ void Solver::relocAll(ClauseAllocator& to) // printf(" >>> RELOCING: %s%d\n", sign(p)?"-":"", var(p)+1); vec<Watcher>& ws = watches[p]; for (int j = 0; j < ws.size(); j++) - ca.reloc(ws[j].cref, to, NULLPROOF(ProofManager::getSatProof())); + ca.reloc(ws[j].cref, + to, + CVC4::options::unsatCores() ? ProofManager::getSatProof() + : nullptr); } // All reasons: @@ -1753,22 +1827,31 @@ void Solver::relocAll(ClauseAllocator& to) Var v = var(trail[i]); if (hasReasonClause(v) && (ca[reason(v)].reloced() || locked(ca[reason(v)]))) - ca.reloc( - vardata[v].d_reason, to, NULLPROOF(ProofManager::getSatProof())); + ca.reloc(vardata[v].d_reason, + to, + CVC4::options::unsatCores() ? ProofManager::getSatProof() + : nullptr); } // All learnt: // for (int i = 0; i < clauses_removable.size(); i++) ca.reloc( - clauses_removable[i], to, NULLPROOF(ProofManager::getSatProof())); + clauses_removable[i], + to, + CVC4::options::unsatCores() ? ProofManager::getSatProof() : nullptr); // All original: // for (int i = 0; i < clauses_persistent.size(); i++) ca.reloc( - clauses_persistent[i], to, NULLPROOF(ProofManager::getSatProof())); + clauses_persistent[i], + to, + CVC4::options::unsatCores() ? ProofManager::getSatProof() : nullptr); - PROOF(ProofManager::getSatProof()->finishUpdateCRef();) + if (options::unsatCores()) + { + ProofManager::getSatProof()->finishUpdateCRef(); + } } @@ -1874,7 +1957,7 @@ CRef Solver::updateLemmas() { // If it's an empty lemma, we have a conflict at zero level if (lemma.size() == 0) { - Assert(!PROOF_ON()); + Assert(!options::unsatCores()); conflict = CRef_Lazy; backtrackLevel = 0; Debug("minisat::lemmas") << "Solver::updateLemmas(): found empty clause" << std::endl; @@ -1904,7 +1987,8 @@ CRef Solver::updateLemmas() { // Last index in the trail int backtrack_index = trail.size(); - PROOF(Assert(lemmas.size() == (int)lemmas_cnf_assertion.size());); + Assert(!options::unsatCores() + || lemmas.size() == (int)lemmas_cnf_assertion.size()); // Attach all the clauses and enqueue all the propagations for (int j = 0; j < lemmas.size(); ++j) @@ -1928,15 +2012,16 @@ CRef Solver::updateLemmas() { } lemma_ref = ca.alloc(clauseLevel, lemma, removable); - PROOF(TNode cnf_assertion = lemmas_cnf_assertion[j].first; - TNode cnf_def = lemmas_cnf_assertion[j].second; - - Debug("pf::sat") - << "Minisat::Solver registering a THEORY_LEMMA (2)" << std::endl; - ClauseId id = ProofManager::getSatProof()->registerClause( - lemma_ref, THEORY_LEMMA); - ProofManager::getCnfProof()->setClauseAssertion(id, cnf_assertion); - ProofManager::getCnfProof()->setClauseDefinition(id, cnf_def);); + if (options::unsatCores()) + { + TNode cnf_assertion = lemmas_cnf_assertion[j]; + + Debug("pf::sat") << "Minisat::Solver registering a THEORY_LEMMA (2)" + << std::endl; + ClauseId id = ProofManager::getSatProof()->registerClause(lemma_ref, + THEORY_LEMMA); + ProofManager::getCnfProof()->setClauseAssertion(id, cnf_assertion); + } if (removable) { clauses_removable.push(lemma_ref); } else { @@ -1948,17 +2033,15 @@ CRef Solver::updateLemmas() { // If the lemma is propagating enqueue its literal (or set the conflict) if (conflict == CRef_Undef && value(lemma[0]) != l_True) { if (lemma.size() == 1 || (value(lemma[1]) == l_False && trail_index(var(lemma[1])) < backtrack_index)) { - if (PROOF_ON() && lemma.size() == 1) + if (options::unsatCores() && lemma.size() == 1) { - Node cnf_assertion = lemmas_cnf_assertion[j].first; - Node cnf_def = lemmas_cnf_assertion[j].second; + Node cnf_assertion = lemmas_cnf_assertion[j]; Debug("pf::sat") << "Minisat::Solver registering a THEORY_LEMMA (3) " << cnf_assertion << value(lemma[0]) << std::endl; ClauseId id = ProofManager::getSatProof()->registerUnitClause( lemma[0], THEORY_LEMMA); ProofManager::getCnfProof()->setClauseAssertion(id, cnf_assertion); - ProofManager::getCnfProof()->setClauseDefinition(id, cnf_def); } if (value(lemma[0]) == l_False) { @@ -1969,7 +2052,10 @@ 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], LEARNT); ); + if (options::unsatCores()) + { + ProofManager::getSatProof()->storeUnitConflict(lemma[0], LEARNT); + } } } else { Debug("minisat::lemmas") << "lemma size is " << lemma.size() << std::endl; @@ -1979,7 +2065,8 @@ CRef Solver::updateLemmas() { } } - PROOF(Assert(lemmas.size() == (int)lemmas_cnf_assertion.size());); + Assert(!options::unsatCores() + || lemmas.size() == (int)lemmas_cnf_assertion.size()); // Clear the lemmas lemmas.clear(); lemmas_cnf_assertion.clear(); diff --git a/src/prop/minisat/core/Solver.h b/src/prop/minisat/core/Solver.h index 508947456..a5f3664e8 100644 --- a/src/prop/minisat/core/Solver.h +++ b/src/prop/minisat/core/Solver.h @@ -63,7 +63,7 @@ public: typedef Var TVar; typedef Lit TLit; - typedef Clause TClause; + typedef Clause TClause; typedef CRef TCRef; typedef vec<Lit> TLitVec; @@ -98,7 +98,7 @@ public: vec<bool> lemmas_removable; /** Nodes being converted to CNF */ - std::vector<std::pair<CVC4::Node, CVC4::Node> > lemmas_cnf_assertion; + std::vector<CVC4::Node> lemmas_cnf_assertion; /** Do a another check if FULL_EFFORT was the last one */ bool recheck; @@ -203,7 +203,7 @@ public: lbool solve (Lit p, Lit q, Lit r); // Search for a model that respects three assumptions. bool okay () const; // FALSE means solver is in a conflicting state - void toDimacs (); + void toDimacs(); void toDimacs (FILE* f, const vec<Lit>& assumps); // Write CNF to file in DIMACS-format. void toDimacs (const char *file, const vec<Lit>& assumps); void toDimacs (FILE* f, Clause& c, vec<Var>& map, Var& max); diff --git a/src/prop/minisat/core/SolverTypes.h b/src/prop/minisat/core/SolverTypes.h index bbd6e17a2..b30d97aee 100644 --- a/src/prop/minisat/core/SolverTypes.h +++ b/src/prop/minisat/core/SolverTypes.h @@ -73,9 +73,14 @@ inline bool sign (Lit p) { return p.x & 1; } inline int var (Lit p) { return p.x >> 1; } // Mapping Literals to and from compact integers suitable for array indexing: -inline int toInt (Var v) { return v; } -inline int toInt (Lit p) { return p.x; } -inline Lit toLit (int i) { Lit p; p.x = i; return p; } +inline int toInt(Var v) { return v; } +inline int toInt(Lit p) { return p.x; } +inline Lit toLit(int i) +{ + Lit p; + p.x = i; + return p; +} //const Lit lit_Undef = mkLit(var_Undef, false); // }- Useful special constants. //const Lit lit_Error = mkLit(var_Undef, true ); // } @@ -83,20 +88,19 @@ inline Lit toLit (int i) { Lit p; p.x = i; return p; } const Lit lit_Undef = { -2 }; // }- Useful special constants. const Lit lit_Error = { -1 }; // } - //================================================================================================= // Lifted booleans: // -// NOTE: this implementation is optimized for the case when comparisons between values are mostly -// between one variable and one constant. Some care had to be taken to make sure that gcc -// does enough constant propagation to produce sensible code, and this appears to be somewhat -// fragile unfortunately. +// NOTE: this implementation is optimized for the case when comparisons between +// values are mostly +// between one variable and one constant. Some care had to be taken to +// make sure that gcc does enough constant propagation to produce sensible +// code, and this appears to be somewhat fragile unfortunately. /* - This is to avoid multiple definitions of l_True, l_False and l_Undef if using multiple copies of - Minisat. - IMPORTANT: if we you change the value of the constants so that it is not the same in all copies - of Minisat this breaks! + This is to avoid multiple definitions of l_True, l_False and l_Undef if using + multiple copies of Minisat. IMPORTANT: if we you change the value of the + constants so that it is not the same in all copies of Minisat this breaks! */ #ifndef l_True @@ -124,10 +128,12 @@ public: bool operator != (lbool b) const { return !(*this == b); } lbool operator ^ (bool b) const { return lbool((uint8_t)(value^(uint8_t)b)); } - lbool operator && (lbool b) const { - uint8_t sel = (this->value << 1) | (b.value << 3); - uint8_t v = (0xF7F755F4 >> sel) & 3; - return lbool(v); } + lbool operator&&(lbool b) const + { + uint8_t sel = (this->value << 1) | (b.value << 3); + uint8_t v = (0xF7F755F4 >> sel) & 3; + return lbool(v); + } lbool operator || (lbool b) const { uint8_t sel = (this->value << 1) | (b.value << 3); @@ -163,7 +169,7 @@ inline std::ostream& operator <<(std::ostream& out, Minisat::vec<Minisat::Lit>& } inline std::ostream& operator <<(std::ostream& out, Minisat::lbool val) { - std::string val_str; + std::string val_str; if( val == l_False ) { val_str = "0"; } else if (val == l_True ) { @@ -208,14 +214,14 @@ class Clause { header.size = ps.size(); header.level = level; - for (int i = 0; i < ps.size(); i++) - data[i].lit = ps[i]; + for (int i = 0; i < ps.size(); i++) data[i].lit = ps[i]; if (header.has_extra){ if (header.removable) - data[header.size].act = 0; - else - calcAbstraction(); } + data[header.size].act = 0; + else + calcAbstraction(); + } } public: @@ -321,7 +327,7 @@ class OccLists public: OccLists(const Deleted& d) : deleted(d) {} - + void init (const Idx& idx){ occs.growTo(toInt(idx)+1); dirty.growTo(toInt(idx)+1, 0); } void resizeTo (const Idx& idx); // Vec& operator[](const Idx& idx){ return occs[toInt(idx)]; } @@ -394,13 +400,12 @@ class CMap typedef Map<CRef, T, CRefHash> HashTable; HashTable map; - + public: // Size-operations: void clear () { map.clear(); } int size () const { return map.elems(); } - // Insert/Remove/Test mapping: void insert (CRef cr, const T& t){ map.insert(cr, t); } void growTo (CRef cr, const T& t){ map.insert(cr, t); } // NOTE: for compatibility @@ -423,15 +428,14 @@ class CMap printf(" --- size = %d, bucket_count = %d\n", size(), map.bucket_count()); } }; - /*_________________________________________________________________________________________________ | | subsumes : (other : const Clause&) -> Lit -| +| | Description: -| Checks if clause subsumes 'other', and at the same time, if it can be used to simplify 'other' -| by subsumption resolution. -| +| Checks if clause subsumes 'other', and at the same time, if it can be +used to simplify 'other' | by subsumption resolution. +| | Result: | lit_Error - No subsumption or simplification | lit_Undef - Clause subsumes 'other' diff --git a/src/prop/minisat/minisat.cpp b/src/prop/minisat/minisat.cpp index a4d2dce8a..25353e416 100644 --- a/src/prop/minisat/minisat.cpp +++ b/src/prop/minisat/minisat.cpp @@ -154,7 +154,7 @@ ClauseId MinisatSatSolver::addClause(SatClause& clause, bool removable) { return ClauseIdUndef; } d_minisat->addClause(minisat_clause, removable, clause_id); - PROOF(Assert(clause_id != ClauseIdError);); + Assert(!CVC4::options::unsatCores() || clause_id != ClauseIdError); return clause_id; } diff --git a/src/prop/minisat/simp/SimpSolver.cc b/src/prop/minisat/simp/SimpSolver.cc index a101a0c2d..0ec8981ca 100644 --- a/src/prop/minisat/simp/SimpSolver.cc +++ b/src/prop/minisat/simp/SimpSolver.cc @@ -21,8 +21,8 @@ OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWA #include "prop/minisat/simp/SimpSolver.h" #include "options/prop_options.h" +#include "options/smt_options.h" #include "proof/clause_id.h" -#include "proof/proof.h" #include "prop/minisat/mtl/Sort.h" #include "prop/minisat/utils/System.h" @@ -47,25 +47,30 @@ static DoubleOption opt_simp_garbage_frac(_cat, "simp-gc-frac", "The fraction of //================================================================================================= // Constructor/Destructor: - -SimpSolver::SimpSolver(CVC4::prop::TheoryProxy* proxy, CVC4::context::Context* context, bool enableIncremental) : - Solver(proxy, context, enableIncremental) - , grow (opt_grow) - , clause_lim (opt_clause_lim) - , subsumption_lim (opt_subsumption_lim) - , simp_garbage_frac (opt_simp_garbage_frac) - , use_asymm (opt_use_asymm) - , use_rcheck (opt_use_rcheck) - , use_elim (options::minisatUseElim() && !enableIncremental) - , merges (0) - , asymm_lits (0) - , eliminated_vars (0) - , elimorder (1) - , use_simplification (!enableIncremental && !PROOF_ON()) // TODO: turn off simplifications if proofs are on initially - , occurs (ClauseDeleted(ca)) - , elim_heap (ElimLt(n_occ)) - , bwdsub_assigns (0) - , n_touched (0) +SimpSolver::SimpSolver(CVC4::prop::TheoryProxy* proxy, + CVC4::context::Context* context, + bool enableIncremental) + : Solver(proxy, context, enableIncremental), + grow(opt_grow), + clause_lim(opt_clause_lim), + subsumption_lim(opt_subsumption_lim), + simp_garbage_frac(opt_simp_garbage_frac), + use_asymm(opt_use_asymm), + use_rcheck(opt_use_rcheck), + use_elim(options::minisatUseElim() && !enableIncremental), + merges(0), + asymm_lits(0), + eliminated_vars(0), + elimorder(1), + use_simplification( + !enableIncremental + && !options::unsatCores()) // TODO: turn off simplifications if + // proofs are on initially + , + occurs(ClauseDeleted(ca)), + elim_heap(ElimLt(n_occ)), + bwdsub_assigns(0), + n_touched(0) { if(options::minisatUseElim() && options::minisatUseElim.wasSetByUser() && @@ -117,8 +122,8 @@ Var SimpSolver::newVar(bool sign, bool dvar, bool isTheoryAtom, bool preRegister lbool SimpSolver::solve_(bool do_simp, bool turn_off_simp) { if (options::minisatDumpDimacs()) { - toDimacs(); - return l_Undef; + toDimacs(); + return l_Undef; } assert(decisionLevel() == 0); @@ -525,7 +530,7 @@ bool SimpSolver::eliminateVar(Var v) for (int i = 0; i < cls.size(); i++) (find(ca[cls[i]], mkLit(v)) ? pos : neg).push(cls[i]); - // Check wether the increase in number of clauses stays within the allowed ('grow'). Moreover, no + // Check whether the increase in number of clauses stays within the allowed ('grow'). Moreover, no // clause must exceed the limit on the maximal clause size (if it is set): // int cnt = 0; @@ -533,9 +538,10 @@ bool SimpSolver::eliminateVar(Var v) for (int i = 0; i < pos.size(); i++) for (int j = 0; j < neg.size(); j++) - if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) && - (++cnt > cls.size() + grow || (clause_lim != -1 && clause_size > clause_lim))) - return true; + if (merge(ca[pos[i]], ca[neg[j]], v, clause_size) + && (++cnt > cls.size() + grow + || (clause_lim != -1 && clause_size > clause_lim))) + return true; // Delete and store old clauses: eliminated[v] = true; @@ -552,10 +558,9 @@ bool SimpSolver::eliminateVar(Var v) mkElimClause(elimclauses, ~mkLit(v)); } - for (int i = 0; i < cls.size(); i++) - removeClause(cls[i]); + for (int i = 0; i < cls.size(); i++) removeClause(cls[i]); - ClauseId id = ClauseIdUndef; + ClauseId id = ClauseIdUndef; // Produce clauses in cross product: vec<Lit>& resolvent = add_tmp; for (int i = 0; i < pos.size(); i++) @@ -569,7 +574,7 @@ bool SimpSolver::eliminateVar(Var v) // Free occurs list for this variable: occurs[v].clear(true); - + // Free watchers lists for this variable, if possible: if (watches[ mkLit(v)].size() == 0) watches[ mkLit(v)].clear(true); if (watches[~mkLit(v)].size() == 0) watches[~mkLit(v)].clear(true); @@ -589,7 +594,7 @@ bool SimpSolver::substitute(Var v, Lit x) eliminated[v] = true; setDecisionVar(v, false); const vec<CRef>& cls = occurs.lookup(v); - + vec<Lit>& subst_clause = add_tmp; for (int i = 0; i < cls.size(); i++){ Clause& c = ca[cls[i]]; @@ -641,9 +646,12 @@ bool SimpSolver::eliminate(bool turn_off_elim) gatherTouchedClauses(); // printf(" ## (time = %6.2f s) BWD-SUB: queue = %d, trail = %d\n", cpuTime(), subsumption_queue.size(), trail.size() - bwdsub_assigns); - if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) && - !backwardSubsumptionCheck(true)){ - ok = false; goto cleanup; } + if ((subsumption_queue.size() > 0 || bwdsub_assigns < trail.size()) + && !backwardSubsumptionCheck(true)) + { + ok = false; + goto cleanup; + } // Empty elim_heap and return immediately on user-interrupt: if (asynch_interrupt){ @@ -656,7 +664,7 @@ bool SimpSolver::eliminate(bool turn_off_elim) // printf(" ## (time = %6.2f s) ELIM: vars = %d\n", cpuTime(), elim_heap.size()); for (int cnt = 0; !elim_heap.empty(); cnt++){ Var elim = elim_heap.removeMin(); - + if (asynch_interrupt) break; if (isEliminated(elim) || value(elim) != l_Undef) continue; @@ -706,8 +714,10 @@ bool SimpSolver::eliminate(bool turn_off_elim) } if (verbosity >= 1 && elimclauses.size() > 0) - printf("| Eliminated clauses: %10.2f Mb |\n", - double(elimclauses.size() * sizeof(uint32_t)) / (1024*1024)); + printf( + "| Eliminated clauses: %10.2f Mb " + " |\n", + double(elimclauses.size() * sizeof(uint32_t)) / (1024 * 1024)); return ok; } @@ -744,11 +754,11 @@ void SimpSolver::relocAll(ClauseAllocator& to) // for (int i = 0; i < subsumption_queue.size(); i++) ca.reloc(subsumption_queue[i], to); - // TODO reloc now takes the proof form the core solver + // TODO reloc now takes the proof form the core solver // Temporary clause: // ca.reloc(bwdsub_tmpunit, to); - // TODO reloc now takes the proof form the core solver + // TODO reloc now takes the proof form the core solver } @@ -756,15 +766,17 @@ void SimpSolver::garbageCollect() { // Initialize the next region to a size corresponding to the estimated utilization degree. This // is not precise but should avoid some unnecessary reallocations for the new region: - ClauseAllocator to(ca.size() - ca.wasted()); + ClauseAllocator to(ca.size() - ca.wasted()); cleanUpClauses(); to.extra_clause_field = ca.extra_clause_field; // NOTE: this is important to keep (or lose) the extra fields. relocAll(to); Solver::relocAll(to); if (verbosity >= 2) - printf("| Garbage collection: %12d bytes => %12d bytes |\n", - ca.size()*ClauseAllocator::Unit_Size, to.size()*ClauseAllocator::Unit_Size); + printf( + "| Garbage collection: %12d bytes => %12d bytes |\n", + ca.size() * ClauseAllocator::Unit_Size, + to.size() * ClauseAllocator::Unit_Size); to.moveTo(ca); - // TODO: proof.finalizeUpdateId(); + // TODO: proof.finalizeUpdateId(); } diff --git a/src/prop/minisat/simp/SimpSolver.h b/src/prop/minisat/simp/SimpSolver.h index 335075f09..c13ee5583 100644 --- a/src/prop/minisat/simp/SimpSolver.h +++ b/src/prop/minisat/simp/SimpSolver.h @@ -55,7 +55,7 @@ class SimpSolver : public Solver { bool addClause (Lit p, Lit q, bool removable, ClauseId& id); // Add a binary clause to the solver. bool addClause (Lit p, Lit q, Lit r, bool removable, ClauseId& id); // Add a ternary clause to the solver. bool addClause_(vec<Lit>& ps, bool removable, ClauseId& id); - bool substitute(Var v, Lit x); // Replace all occurences of v with x (may cause a contradiction). + bool substitute(Var v, Lit x); // Replace all occurrences of v with x (may cause a contradiction). // Variable mode: // diff --git a/src/prop/prop_engine.cpp b/src/prop/prop_engine.cpp index f74e52509..e71e681e5 100644 --- a/src/prop/prop_engine.cpp +++ b/src/prop/prop_engine.cpp @@ -99,17 +99,17 @@ PropEngine::PropEngine(TheoryEngine* te, d_decisionEngine->setSatSolver(d_satSolver); d_decisionEngine->setCnfStream(d_cnfStream); - PROOF ( - ProofManager::currentPM()->initCnfProof(d_cnfStream, userContext); - ); + if (options::unsatCores()) + { + ProofManager::currentPM()->initCnfProof(d_cnfStream, userContext); + } } void PropEngine::finishInit() { NodeManager* nm = NodeManager::currentNM(); - d_cnfStream->convertAndAssert(nm->mkConst(true), false, false, RULE_GIVEN); - d_cnfStream->convertAndAssert( - nm->mkConst(false).notNode(), false, false, RULE_GIVEN); + d_cnfStream->convertAndAssert(nm->mkConst(true), false, false); + d_cnfStream->convertAndAssert(nm->mkConst(false).notNode(), false, false); } PropEngine::~PropEngine() { @@ -126,18 +126,15 @@ void PropEngine::assertFormula(TNode node) { Assert(!d_inCheckSat) << "Sat solver in solve()!"; Debug("prop") << "assertFormula(" << node << ")" << endl; // Assert as non-removable - d_cnfStream->convertAndAssert(node, false, false, RULE_GIVEN); + d_cnfStream->convertAndAssert(node, false, false, true); } -void PropEngine::assertLemma(TNode node, bool negated, - bool removable, - ProofRule rule, - TNode from) { - //Assert(d_inCheckSat, "Sat solver should be in solve()!"); +void PropEngine::assertLemma(TNode node, bool negated, bool removable) +{ Debug("prop::lemmas") << "assertLemma(" << node << ")" << endl; // Assert as (possibly) removable - d_cnfStream->convertAndAssert(node, removable, negated, rule, from); + d_cnfStream->convertAndAssert(node, removable, negated); } void PropEngine::addAssertionsToDecisionEngine( diff --git a/src/prop/prop_engine.h b/src/prop/prop_engine.h index 9a2daee49..1df862568 100644 --- a/src/prop/prop_engine.h +++ b/src/prop/prop_engine.h @@ -103,11 +103,7 @@ class PropEngine * @param removable whether this lemma can be quietly removed based * on an activity heuristic (or not) */ - void assertLemma(TNode node, - bool negated, - bool removable, - ProofRule rule, - TNode from = TNode::null()); + void assertLemma(TNode node, bool negated, bool removable); /** * Pass a list of assertions from an AssertionPipeline to the decision engine. diff --git a/src/prop/sat_solver.h b/src/prop/sat_solver.h index d4b08ab71..1526e91b9 100644 --- a/src/prop/sat_solver.h +++ b/src/prop/sat_solver.h @@ -33,11 +33,6 @@ namespace CVC4 { -namespace proof { -class ClausalBitVectorProof; -class ResolutionBitVectorProof; -} // namespace proof - namespace prop { class TheoryProxy; @@ -58,7 +53,7 @@ public: /** Add a clause corresponding to rhs = l1 xor .. xor ln */ virtual ClauseId addXorClause(SatClause& clause, bool rhs, bool removable) = 0; - + /** * Create a new boolean variable in the solver. * @param isTheoryAtom is this a theory atom that needs to be asserted to theory @@ -84,6 +79,7 @@ public: virtual SatValue solve(const std::vector<SatLiteral>& assumptions) { Unimplemented() << "Solving under assumptions not implemented"; + return SAT_VALUE_UNKNOWN; }; /** Interrupt the solver */ @@ -101,10 +97,6 @@ public: /** Check if the solver is in an inconsistent state */ virtual bool ok() const = 0; - virtual void setResolutionProofLog(proof::ResolutionBitVectorProof* bvp) {} - - virtual void setClausalProofLog(proof::ClausalBitVectorProof* drat_proof) {} - };/* class SatSolver */ diff --git a/src/prop/theory_proxy.cpp b/src/prop/theory_proxy.cpp index 41da4546e..d0ba4ca71 100644 --- a/src/prop/theory_proxy.cpp +++ b/src/prop/theory_proxy.cpp @@ -76,22 +76,12 @@ void TheoryProxy::explainPropagation(SatLiteral l, SatClause& explanation) { TNode lNode = d_cnfStream->getNode(l); Debug("prop-explain") << "explainPropagation(" << lNode << ")" << std::endl; - LemmaProofRecipe* proofRecipe = NULL; - PROOF(proofRecipe = new LemmaProofRecipe;); + Node theoryExplanation = d_theoryEngine->getExplanation(lNode); - Node theoryExplanation = d_theoryEngine->getExplanationAndRecipe(lNode, proofRecipe); - - PROOF({ - ProofManager::getCnfProof()->pushCurrentAssertion(theoryExplanation); - ProofManager::getCnfProof()->setProofRecipe(proofRecipe); - - Debug("pf::sat") << "TheoryProxy::explainPropagation: setting lemma recipe to: " - << std::endl; - proofRecipe->dump("pf::sat"); - - delete proofRecipe; - proofRecipe = NULL; - }); + if (options::unsatCores()) + { + ProofManager::getCnfProof()->pushCurrentAssertion(theoryExplanation); + } Debug("prop-explain") << "explainPropagation() => " << theoryExplanation << std::endl; if (theoryExplanation.getKind() == kind::AND) { diff --git a/src/smt/assertions.cpp b/src/smt/assertions.cpp index ea3acf2d1..8019c383d 100644 --- a/src/smt/assertions.cpp +++ b/src/smt/assertions.cpp @@ -17,7 +17,6 @@ #include "expr/node_algorithm.h" #include "options/base_options.h" #include "options/language.h" -#include "options/proof_options.h" #include "options/smt_options.h" #include "proof/proof_manager.h" #include "smt/smt_engine.h" @@ -179,18 +178,23 @@ void Assertions::addFormula( } // Give it to proof manager - PROOF(if (inInput) { - // n is an input assertion - if (inUnsatCore || options::unsatCores() || options::dumpUnsatCores() - || options::checkUnsatCores() || options::fewerPreprocessingHoles()) + if (options::unsatCores()) + { + if (inInput) + { // n is an input assertion + if (inUnsatCore || options::unsatCores() || options::dumpUnsatCores() + || options::checkUnsatCores()) + { + ProofManager::currentPM()->addCoreAssertion(n.toExpr()); + } + } + else { - ProofManager::currentPM()->addCoreAssertion(n.toExpr()); + // n is the result of an unknown preprocessing step, add it to dependency + // map to null + ProofManager::currentPM()->addDependence(n, Node::null()); } - } else { - // n is the result of an unknown preprocessing step, add it to dependency - // map to null - ProofManager::currentPM()->addDependence(n, Node::null()); - }); + } // Add the normalized formula to the queue d_assertions.push_back(n, isAssumption); diff --git a/src/smt/assertions.h b/src/smt/assertions.h index c2a16db71..a74c58bd8 100644 --- a/src/smt/assertions.h +++ b/src/smt/assertions.h @@ -122,7 +122,8 @@ class Assertions * formula might be pushed out to the propositional layer * immediately, or it might be simplified and kept, or it might not * even be simplified. - * The arguments isInput and isAssumption are used for bookkeeping for proofs. + * The arguments isInput and isAssumption are used for bookkeeping for unsat + * cores. * The argument maybeHasFv should be set to true if the assertion may have * free variables. By construction, assertions from the smt2 parser are * guaranteed not to have free variables. However, other cases such as diff --git a/src/smt/command.cpp b/src/smt/command.cpp index 88f04f885..cb95cf348 100644 --- a/src/smt/command.cpp +++ b/src/smt/command.cpp @@ -2275,57 +2275,22 @@ void BlockModelValuesCommand::toStream(std::ostream& out, /* class GetProofCommand */ /* -------------------------------------------------------------------------- */ -GetProofCommand::GetProofCommand() : d_smtEngine(nullptr), d_result(nullptr) {} +GetProofCommand::GetProofCommand() {} void GetProofCommand::invoke(SmtEngine* smtEngine) { - try - { - d_smtEngine = smtEngine; - d_result = &smtEngine->getProof(); - d_commandStatus = CommandSuccess::instance(); - } - catch (RecoverableModalException& e) - { - d_commandStatus = new CommandRecoverableFailure(e.what()); - } - catch (UnsafeInterruptException& e) - { - d_commandStatus = new CommandInterrupted(); - } - catch (exception& e) - { - d_commandStatus = new CommandFailure(e.what()); - } -} - -const Proof& GetProofCommand::getResult() const { return *d_result; } -void GetProofCommand::printResult(std::ostream& out, uint32_t verbosity) const -{ - if (!ok()) - { - this->Command::printResult(out, verbosity); - } - else - { - smt::SmtScope scope(d_smtEngine); - d_result->toStream(out); - } + Unimplemented() << "Unimplemented get-proof\n"; } Command* GetProofCommand::exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) { GetProofCommand* c = new GetProofCommand(); - c->d_result = d_result; - c->d_smtEngine = d_smtEngine; return c; } Command* GetProofCommand::clone() const { GetProofCommand* c = new GetProofCommand(); - c->d_result = d_result; - c->d_smtEngine = d_smtEngine; return c; } diff --git a/src/smt/command.h b/src/smt/command.h index a0e591269..9fbd1bf73 100644 --- a/src/smt/command.h +++ b/src/smt/command.h @@ -33,7 +33,6 @@ #include "expr/type.h" #include "expr/variable_type_map.h" #include "proof/unsat_core.h" -#include "util/proof.h" #include "util/result.h" #include "util/sexpr.h" @@ -1131,9 +1130,7 @@ class CVC4_PUBLIC GetProofCommand : public Command public: GetProofCommand(); - const Proof& getResult() const; void invoke(SmtEngine* smtEngine) override; - void printResult(std::ostream& out, uint32_t verbosity = 2) const override; Command* exportTo(ExprManager* exprManager, ExprManagerMapCollection& variableMap) override; Command* clone() const override; @@ -1144,11 +1141,6 @@ class CVC4_PUBLIC GetProofCommand : public Command bool types = false, size_t dag = 1, OutputLanguage language = language::output::LANG_AUTO) const override; - - protected: - SmtEngine* d_smtEngine; - // d_result is owned by d_smtEngine. - const Proof* d_result; }; /* class GetProofCommand */ class CVC4_PUBLIC GetInstantiationsCommand : public Command diff --git a/src/smt/dump.h b/src/smt/dump.h index 050935422..4c0efeb6e 100644 --- a/src/smt/dump.h +++ b/src/smt/dump.h @@ -21,6 +21,7 @@ #include "base/output.h" #include "smt/command.h" +#include "smt/node_command.h" namespace CVC4 { @@ -40,6 +41,20 @@ class CVC4_PUBLIC CVC4dumpstream return *this; } + /** A convenience function for dumping internal commands. + * + * Since Commands are now part of the public API, internal code should use + * NodeCommands and this function (instead of the one above) to dump them. + */ + CVC4dumpstream& operator<<(const NodeCommand& nc) + { + if (d_os != nullptr) + { + (*d_os) << nc << std::endl; + } + return *this; + } + private: std::ostream* d_os; }; /* class CVC4dumpstream */ @@ -56,6 +71,7 @@ class CVC4_PUBLIC CVC4dumpstream CVC4dumpstream() {} CVC4dumpstream(std::ostream& os) {} CVC4dumpstream& operator<<(const Command& c) { return *this; } + CVC4dumpstream& operator<<(const NodeCommand& nc) { return *this; } }; /* class CVC4dumpstream */ #endif /* CVC4_DUMPING && !CVC4_MUZZLE */ diff --git a/src/smt/dump_manager.cpp b/src/smt/dump_manager.cpp index d5fd65c4c..033be405f 100644 --- a/src/smt/dump_manager.cpp +++ b/src/smt/dump_manager.cpp @@ -51,7 +51,7 @@ void DumpManager::finishInit() void DumpManager::resetAssertions() { d_modelGlobalCommands.clear(); } -void DumpManager::addToModelCommandAndDump(const Command& c, +void DumpManager::addToModelCommandAndDump(const NodeCommand& c, uint32_t flags, bool userVisible, const char* dumpTag) @@ -70,14 +70,14 @@ void DumpManager::addToModelCommandAndDump(const Command& c, { if (flags & ExprManager::VAR_FLAG_GLOBAL) { - d_modelGlobalCommands.push_back(std::unique_ptr<Command>(c.clone())); + d_modelGlobalCommands.push_back(std::unique_ptr<NodeCommand>(c.clone())); } else { - Command* cc = c.clone(); + NodeCommand* cc = c.clone(); d_modelCommands.push_back(cc); // also remember for memory management purposes - d_modelCommandsAlloc.push_back(std::unique_ptr<Command>(cc)); + d_modelCommandsAlloc.push_back(std::unique_ptr<NodeCommand>(cc)); } } if (Dump.isOn(dumpTag)) @@ -88,7 +88,7 @@ void DumpManager::addToModelCommandAndDump(const Command& c, } else { - d_dumpCommands.push_back(std::unique_ptr<Command>(c.clone())); + d_dumpCommands.push_back(std::unique_ptr<NodeCommand>(c.clone())); } } } @@ -96,7 +96,7 @@ void DumpManager::addToModelCommandAndDump(const Command& c, void DumpManager::setPrintFuncInModel(Node f, bool p) { Trace("setp-model") << "Set printInModel " << f << " to " << p << std::endl; - for (std::unique_ptr<Command>& c : d_modelGlobalCommands) + for (std::unique_ptr<NodeCommand>& c : d_modelGlobalCommands) { DeclareFunctionCommand* dfc = dynamic_cast<DeclareFunctionCommand*>(c.get()); @@ -109,7 +109,7 @@ void DumpManager::setPrintFuncInModel(Node f, bool p) } } } - for (Command* c : d_modelCommands) + for (NodeCommand* c : d_modelCommands) { DeclareFunctionCommand* dfc = dynamic_cast<DeclareFunctionCommand*>(c); if (dfc != NULL) @@ -128,7 +128,7 @@ size_t DumpManager::getNumModelCommands() const return d_modelCommands.size() + d_modelGlobalCommands.size(); } -const Command* DumpManager::getModelCommand(size_t i) const +const NodeCommand* DumpManager::getModelCommand(size_t i) const { Assert(i < getNumModelCommands()); // index the global commands first, then the locals diff --git a/src/smt/dump_manager.h b/src/smt/dump_manager.h index 6f2ee37a1..2ce0570e4 100644 --- a/src/smt/dump_manager.h +++ b/src/smt/dump_manager.h @@ -22,7 +22,7 @@ #include "context/cdlist.h" #include "expr/node.h" -#include "smt/command.h" +#include "smt/node_command.h" namespace CVC4 { namespace smt { @@ -36,7 +36,7 @@ namespace smt { */ class DumpManager { - typedef context::CDList<Command*> CommandList; + typedef context::CDList<NodeCommand*> CommandList; public: DumpManager(context::UserContext* u); @@ -54,7 +54,7 @@ class DumpManager * Add to Model command. This is used for recording a command * that should be reported during a get-model call. */ - void addToModelCommandAndDump(const Command& c, + void addToModelCommandAndDump(const NodeCommand& c, uint32_t flags = 0, bool userVisible = true, const char* dumpTag = "declarations"); @@ -66,7 +66,7 @@ class DumpManager /** get number of commands to report in a model */ size_t getNumModelCommands() const; /** get model command at index i */ - const Command* getModelCommand(size_t i) const; + const NodeCommand* getModelCommand(size_t i) const; private: /** Fully inited */ @@ -77,7 +77,7 @@ class DumpManager * regardless of push/pop). Only maintained if produce-models option * is on. */ - std::vector<std::unique_ptr<Command>> d_modelGlobalCommands; + std::vector<std::unique_ptr<NodeCommand>> d_modelGlobalCommands; /** * A list of commands that should be in the Model locally (i.e., @@ -89,7 +89,7 @@ class DumpManager * A list of model commands allocated to d_modelCommands at any time. This * is maintained for memory management purposes. */ - std::vector<std::unique_ptr<Command>> d_modelCommandsAlloc; + std::vector<std::unique_ptr<NodeCommand>> d_modelCommandsAlloc; /** * A vector of declaration commands waiting to be dumped out. @@ -97,7 +97,7 @@ class DumpManager * This ensures the declarations come after the set-logic and * any necessary set-option commands are dumped. */ - std::vector<std::unique_ptr<Command>> d_dumpCommands; + std::vector<std::unique_ptr<NodeCommand>> d_dumpCommands; }; } // namespace smt diff --git a/src/smt/listeners.cpp b/src/smt/listeners.cpp index 539d6ba2f..52ddcf156 100644 --- a/src/smt/listeners.cpp +++ b/src/smt/listeners.cpp @@ -18,7 +18,7 @@ #include "expr/expr.h" #include "expr/node_manager_attributes.h" #include "options/smt_options.h" -#include "smt/command.h" +#include "smt/node_command.h" #include "smt/dump.h" #include "smt/dump_manager.h" #include "smt/smt_engine.h" @@ -40,7 +40,7 @@ SmtNodeManagerListener::SmtNodeManagerListener(DumpManager& dm) : d_dm(dm) {} void SmtNodeManagerListener::nmNotifyNewSort(TypeNode tn, uint32_t flags) { - DeclareTypeCommand c(tn.getAttribute(expr::VarNameAttr()), 0, tn.toType()); + DeclareTypeNodeCommand c(tn.getAttribute(expr::VarNameAttr()), 0, tn); if ((flags & ExprManager::SORT_FLAG_PLACEHOLDER) == 0) { d_dm.addToModelCommandAndDump(c, flags); @@ -50,9 +50,9 @@ void SmtNodeManagerListener::nmNotifyNewSort(TypeNode tn, uint32_t flags) void SmtNodeManagerListener::nmNotifyNewSortConstructor(TypeNode tn, uint32_t flags) { - DeclareTypeCommand c(tn.getAttribute(expr::VarNameAttr()), - tn.getAttribute(expr::SortArityAttr()), - tn.toType()); + DeclareTypeNodeCommand c(tn.getAttribute(expr::VarNameAttr()), + tn.getAttribute(expr::SortArityAttr()), + tn); if ((flags & ExprManager::SORT_FLAG_PLACEHOLDER) == 0) { d_dm.addToModelCommandAndDump(c); @@ -68,17 +68,16 @@ void SmtNodeManagerListener::nmNotifyNewDatatypes( for (const TypeNode& dt : dtts) { Assert(dt.isDatatype()); - types.push_back(dt.toType()); } - DatatypeDeclarationCommand c(types); + DeclareDatatypeNodeCommand c(dtts); d_dm.addToModelCommandAndDump(c); } } void SmtNodeManagerListener::nmNotifyNewVar(TNode n, uint32_t flags) { - DeclareFunctionCommand c( - n.getAttribute(expr::VarNameAttr()), n.toExpr(), n.getType().toType()); + DeclareFunctionNodeCommand c( + n.getAttribute(expr::VarNameAttr()), n, n.getType()); if ((flags & ExprManager::VAR_FLAG_DEFINED) == 0) { d_dm.addToModelCommandAndDump(c, flags); @@ -90,7 +89,7 @@ void SmtNodeManagerListener::nmNotifyNewSkolem(TNode n, uint32_t flags) { std::string id = n.getAttribute(expr::VarNameAttr()); - DeclareFunctionCommand c(id, n.toExpr(), n.getType().toType()); + DeclareFunctionNodeCommand c(id, n, n.getType()); if (Dump.isOn("skolems") && comment != "") { Dump("skolems") << CommentCommand(id + " is " + comment); diff --git a/src/smt/model.cpp b/src/smt/model.cpp index 7924698ff..a23b885ff 100644 --- a/src/smt/model.cpp +++ b/src/smt/model.cpp @@ -19,8 +19,8 @@ #include "expr/expr_iomanip.h" #include "options/base_options.h" #include "printer/printer.h" -#include "smt/command.h" #include "smt/dump_manager.h" +#include "smt/node_command.h" #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" @@ -42,7 +42,7 @@ size_t Model::getNumCommands() const return d_smt.getDumpManager()->getNumModelCommands(); } -const Command* Model::getCommand(size_t i) const +const NodeCommand* Model::getCommand(size_t i) const { return d_smt.getDumpManager()->getModelCommand(i); } diff --git a/src/smt/model.h b/src/smt/model.h index 8f4409b07..4c28704c3 100644 --- a/src/smt/model.h +++ b/src/smt/model.h @@ -25,7 +25,7 @@ namespace CVC4 { -class Command; +class NodeCommand; class SmtEngine; class Model; @@ -48,7 +48,7 @@ class Model { /** get number of commands to report */ size_t getNumCommands() const; /** get command */ - const Command* getCommand(size_t i) const; + const NodeCommand* getCommand(size_t i) const; /** get the smt engine that this model is hooked up to */ SmtEngine* getSmtEngine() { return &d_smt; } /** get the smt engine (as a pointer-to-const) that this model is hooked up to */ diff --git a/src/smt/node_command.cpp b/src/smt/node_command.cpp new file mode 100644 index 000000000..265b35b3e --- /dev/null +++ b/src/smt/node_command.cpp @@ -0,0 +1,180 @@ +/********************* */ +/*! \file node_command.cpp + ** \verbatim + ** Top contributors (to current version): + ** Abdalrhman Mohamed + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of NodeCommand functions. + ** + ** Implementation of NodeCommand functions. + **/ + +#include "smt/node_command.h" + +#include "printer/printer.h" + +namespace CVC4 { + +/* -------------------------------------------------------------------------- */ +/* class NodeCommand */ +/* -------------------------------------------------------------------------- */ + +NodeCommand::~NodeCommand() {} + +std::string NodeCommand::toString() const +{ + std::stringstream ss; + toStream(ss); + return ss.str(); +} + +std::ostream& operator<<(std::ostream& out, const NodeCommand& nc) +{ + nc.toStream(out, + Node::setdepth::getDepth(out), + Node::printtypes::getPrintTypes(out), + Node::dag::getDag(out), + Node::setlanguage::getLanguage(out)); + return out; +} + +/* -------------------------------------------------------------------------- */ +/* class DeclareFunctionNodeCommand */ +/* -------------------------------------------------------------------------- */ + +DeclareFunctionNodeCommand::DeclareFunctionNodeCommand(const std::string& id, + Node expr, + TypeNode type) + : d_id(id), + d_fun(expr), + d_type(type), + d_printInModel(true), + d_printInModelSetByUser(false) +{ +} + +void DeclareFunctionNodeCommand::toStream(std::ostream& out, + int toDepth, + bool types, + size_t dag, + OutputLanguage language) const +{ + Printer::getPrinter(language)->toStreamCmdDeclareFunction(out, d_id, d_type); +} + +NodeCommand* DeclareFunctionNodeCommand::clone() const +{ + return new DeclareFunctionNodeCommand(d_id, d_fun, d_type); +} + +const Node& DeclareFunctionNodeCommand::getFunction() const { return d_fun; } + +bool DeclareFunctionNodeCommand::getPrintInModel() const +{ + return d_printInModel; +} + +bool DeclareFunctionNodeCommand::getPrintInModelSetByUser() const +{ + return d_printInModelSetByUser; +} + +void DeclareFunctionNodeCommand::setPrintInModel(bool p) +{ + d_printInModel = p; + d_printInModelSetByUser = true; +} + +/* -------------------------------------------------------------------------- */ +/* class DeclareTypeNodeCommand */ +/* -------------------------------------------------------------------------- */ + +DeclareTypeNodeCommand::DeclareTypeNodeCommand(const std::string& id, + size_t arity, + TypeNode type) + : d_id(id), d_arity(arity), d_type(type) +{ +} + +void DeclareTypeNodeCommand::toStream(std::ostream& out, + int toDepth, + bool types, + size_t dag, + OutputLanguage language) const +{ + Printer::getPrinter(language)->toStreamCmdDeclareType( + out, d_id, d_arity, d_type); +} + +NodeCommand* DeclareTypeNodeCommand::clone() const +{ + return new DeclareTypeNodeCommand(d_id, d_arity, d_type); +} + +const std::string DeclareTypeNodeCommand::getSymbol() const { return d_id; } + +const TypeNode& DeclareTypeNodeCommand::getType() const { return d_type; } + +/* -------------------------------------------------------------------------- */ +/* class DeclareDatatypeNodeCommand */ +/* -------------------------------------------------------------------------- */ + +DeclareDatatypeNodeCommand::DeclareDatatypeNodeCommand( + const std::vector<TypeNode>& datatypes) + : d_datatypes(datatypes) +{ +} + +void DeclareDatatypeNodeCommand::toStream(std::ostream& out, + int toDepth, + bool types, + size_t dag, + OutputLanguage language) const +{ + Printer::getPrinter(language)->toStreamCmdDatatypeDeclaration(out, + d_datatypes); +} + +NodeCommand* DeclareDatatypeNodeCommand::clone() const +{ + return new DeclareDatatypeNodeCommand(d_datatypes); +} + +/* -------------------------------------------------------------------------- */ +/* class DefineFunctionNodeCommand */ +/* -------------------------------------------------------------------------- */ + +DefineFunctionNodeCommand::DefineFunctionNodeCommand( + const std::string& id, + Node fun, + const std::vector<Node>& formals, + Node formula) + : d_id(id), d_fun(fun), d_formals(formals), d_formula(formula) +{ +} + +void DefineFunctionNodeCommand::toStream(std::ostream& out, + int toDepth, + bool types, + size_t dag, + OutputLanguage language) const +{ + Printer::getPrinter(language)->toStreamCmdDefineFunction( + out, + d_fun.toString(), + d_formals, + d_fun.getType().getRangeType(), + d_formula); +} + +NodeCommand* DefineFunctionNodeCommand::clone() const +{ + return new DefineFunctionNodeCommand(d_id, d_fun, d_formals, d_formula); +} + +} // namespace CVC4 diff --git a/src/smt/node_command.h b/src/smt/node_command.h new file mode 100644 index 000000000..2ca166bb6 --- /dev/null +++ b/src/smt/node_command.h @@ -0,0 +1,157 @@ +/********************* */ +/*! \file node_command.h + ** \verbatim + ** Top contributors (to current version): + ** Abdalrhman Mohamed + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Datastructures used for printing commands internally. + ** + ** Datastructures used for printing commands internally. + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__SMT__NODE_COMMAND_H +#define CVC4__SMT__NODE_COMMAND_H + +#include <string> + +#include "expr/node.h" +#include "expr/type_node.h" +#include "options/language.h" + +namespace CVC4 { + +/** + * A node version of Command. DO NOT use this version unless there is a need + * to buffer commands for later use (e.g., printing models). + */ +class NodeCommand +{ + public: + /** Destructor */ + virtual ~NodeCommand(); + + /** Print this NodeCommand to output stream */ + virtual void toStream( + std::ostream& out, + int toDepth = -1, + bool types = false, + size_t dag = 1, + OutputLanguage language = language::output::LANG_AUTO) const = 0; + + /** Get a string representation of this NodeCommand */ + std::string toString() const; + + /** Clone this NodeCommand (make a shallow copy). */ + virtual NodeCommand* clone() const = 0; +}; + +std::ostream& operator<<(std::ostream& out, const NodeCommand& nc); + +/** + * Declare n-ary function symbol. + * SMT-LIB: ( declare-fun <id> ( <type.getArgTypes()> ) <type.getRangeType()> ) + */ +class DeclareFunctionNodeCommand : public NodeCommand +{ + public: + DeclareFunctionNodeCommand(const std::string& id, Node fun, TypeNode type); + void toStream( + std::ostream& out, + int toDepth = -1, + bool types = false, + size_t dag = 1, + OutputLanguage language = language::output::LANG_AUTO) const override; + NodeCommand* clone() const override; + const Node& getFunction() const; + bool getPrintInModel() const; + bool getPrintInModelSetByUser() const; + void setPrintInModel(bool p); + + private: + std::string d_id; + Node d_fun; + TypeNode d_type; + bool d_printInModel; + bool d_printInModelSetByUser; +}; + +/** + * Create datatype sort. + * SMT-LIB: ( declare-datatypes ( <datatype decls>{n+1} ) ( <datatypes>{n+1} ) ) + */ +class DeclareDatatypeNodeCommand : public NodeCommand +{ + public: + DeclareDatatypeNodeCommand(const std::vector<TypeNode>& datatypes); + void toStream( + std::ostream& out, + int toDepth = -1, + bool types = false, + size_t dag = 1, + OutputLanguage language = language::output::LANG_AUTO) const override; + NodeCommand* clone() const override; + + private: + std::vector<TypeNode> d_datatypes; +}; + +/** + * Declare uninterpreted sort. + * SMT-LIB: ( declare-sort <id> <arity> ) + */ +class DeclareTypeNodeCommand : public NodeCommand +{ + public: + DeclareTypeNodeCommand(const std::string& id, size_t arity, TypeNode type); + void toStream( + std::ostream& out, + int toDepth = -1, + bool types = false, + size_t dag = 1, + OutputLanguage language = language::output::LANG_AUTO) const override; + NodeCommand* clone() const override; + const std::string getSymbol() const; + const TypeNode& getType() const; + + private: + std::string d_id; + size_t d_arity; + TypeNode d_type; +}; + +/** + * Define n-ary function. + * SMT-LIB: ( define-fun <id> ( <formals> ) <fun.getType()> <formula> ) + */ +class DefineFunctionNodeCommand : public NodeCommand +{ + public: + DefineFunctionNodeCommand(const std::string& id, + Node fun, + const std::vector<Node>& formals, + Node formula); + void toStream( + std::ostream& out, + int toDepth = -1, + bool types = false, + size_t dag = 1, + OutputLanguage language = language::output::LANG_AUTO) const override; + NodeCommand* clone() const override; + + private: + std::string d_id; + Node d_fun; + std::vector<Node> d_formals; + Node d_formula; +}; + +} // namespace CVC4 + +#endif /* CVC4__SMT__NODE_COMMAND_H */ diff --git a/src/smt/process_assertions.cpp b/src/smt/process_assertions.cpp index a69207512..33d092def 100644 --- a/src/smt/process_assertions.cpp +++ b/src/smt/process_assertions.cpp @@ -21,7 +21,6 @@ #include "options/arith_options.h" #include "options/base_options.h" #include "options/bv_options.h" -#include "options/proof_options.h" #include "options/quantifiers_options.h" #include "options/sep_options.h" #include "options/smt_options.h" @@ -147,12 +146,6 @@ bool ProcessAssertions::apply(Assertions& as) << endl; dumpAssertions("post-definition-expansion", assertions); - // save the assertions now - THEORY_PROOF( - for (size_t i = 0, nasserts = assertions.size(); i < nasserts; ++i) { - ProofManager::currentPM()->addAssertion(assertions[i].toExpr()); - }); - Debug("smt") << " assertions : " << assertions.size() << endl; if (options::globalNegate()) @@ -470,7 +463,7 @@ bool ProcessAssertions::simplifyAssertions(AssertionPipeline& assertions) if (options::simplificationMode() != options::SimplificationMode::NONE) { - if (!options::unsatCores() && !options::fewerPreprocessingHoles()) + if (!options::unsatCores()) { // Perform non-clausal simplification PreprocessingPassResult res = @@ -532,7 +525,7 @@ bool ProcessAssertions::simplifyAssertions(AssertionPipeline& assertions) if (options::repeatSimp() && options::simplificationMode() != options::SimplificationMode::NONE - && !options::unsatCores() && !options::fewerPreprocessingHoles()) + && !options::unsatCores()) { PreprocessingPassResult res = d_passes["non-clausal-simp"]->apply(&assertions); diff --git a/src/smt/set_defaults.cpp b/src/smt/set_defaults.cpp index 130f75894..6f00998d2 100644 --- a/src/smt/set_defaults.cpp +++ b/src/smt/set_defaults.cpp @@ -27,7 +27,6 @@ #include "options/open_ostream.h" #include "options/option_exception.h" #include "options/printer_options.h" -#include "options/proof_options.h" #include "options/prop_options.h" #include "options/quantifiers_options.h" #include "options/sep_options.h" @@ -72,11 +71,6 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) Notice() << "SmtEngine: setting unsatCores" << std::endl; options::unsatCores.set(true); } - if (options::checkProofs() || options::dumpProofs()) - { - Notice() << "SmtEngine: setting proof" << std::endl; - options::proof.set(true); - } if (options::bitvectorAigSimplifications.wasSetByUser()) { Notice() << "SmtEngine: setting bitvectorAig" << std::endl; @@ -254,12 +248,6 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) << std::endl; } } - // !!!!!!!!!!!!!!!! temporary, to support CI check for old proof system - if (options::proof()) - { - options::proofNew.set(false); - } - if (options::arraysExp()) { if (!logic.isQuantified()) @@ -316,11 +304,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) options::produceAssertions.set(true); } - // Disable options incompatible with incremental solving, unsat cores, and - // proofs or output an error if enabled explicitly. It is also currently - // incompatible with arithmetic, force the option off. - if (options::incrementalSolving() || options::unsatCores() - || options::proof()) + // Disable options incompatible with incremental solving, unsat cores or + // output an error if enabled explicitly. It is also currently incompatible + // with arithmetic, force the option off. + if (options::incrementalSolving() || options::unsatCores()) { if (options::unconstrainedSimp()) { @@ -328,10 +315,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) { throw OptionException( "unconstrained simplification not supported with unsat " - "cores/proofs/incremental solving"); + "cores/incremental solving"); } Notice() << "SmtEngine: turning off unconstrained simplification to " - "support unsat cores/proofs/incremental solving" + "support unsat cores/incremental solving" << std::endl; options::unconstrainedSimp.set(false); } @@ -353,17 +340,17 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) } } - if (options::incrementalSolving() || options::proof()) + if (options::incrementalSolving()) { if (options::sygusInference()) { if (options::sygusInference.wasSetByUser()) { throw OptionException( - "sygus inference not supported with proofs/incremental solving"); + "sygus inference not supported with incremental solving"); } Notice() << "SmtEngine: turning off sygus inference to support " - "proofs/incremental solving" + "incremental solving" << std::endl; options::sygusInference.set(false); } @@ -380,19 +367,18 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) options::bitvectorToBool.set(true); } - // Disable options incompatible with unsat cores and proofs or output an - // error if enabled explicitly - if (options::unsatCores() || options::proof()) + // Disable options incompatible with unsat cores or output an error if enabled + // explicitly + if (options::unsatCores()) { if (options::simplificationMode() != options::SimplificationMode::NONE) { if (options::simplificationMode.wasSetByUser()) { - throw OptionException( - "simplification not supported with unsat cores/proofs"); + throw OptionException("simplification not supported with unsat cores"); } Notice() << "SmtEngine: turning off simplification to support unsat " - "cores/proofs" + "cores" << std::endl; options::simplificationMode.set(options::SimplificationMode::NONE); } @@ -402,10 +388,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) if (options::pbRewrites.wasSetByUser()) { throw OptionException( - "pseudoboolean rewrites not supported with unsat cores/proofs"); + "pseudoboolean rewrites not supported with unsat cores"); } Notice() << "SmtEngine: turning off pseudoboolean rewrites to support " - "unsat cores/proofs" + "unsat cores" << std::endl; options::pbRewrites.set(false); } @@ -414,11 +400,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) { if (options::sortInference.wasSetByUser()) { - throw OptionException( - "sort inference not supported with unsat cores/proofs"); + throw OptionException("sort inference not supported with unsat cores"); } Notice() << "SmtEngine: turning off sort inference to support unsat " - "cores/proofs" + "cores" << std::endl; options::sortInference.set(false); } @@ -428,10 +413,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) if (options::preSkolemQuant.wasSetByUser()) { throw OptionException( - "pre-skolemization not supported with unsat cores/proofs"); + "pre-skolemization not supported with unsat cores"); } Notice() << "SmtEngine: turning off pre-skolemization to support unsat " - "cores/proofs" + "cores" << std::endl; options::preSkolemQuant.set(false); } @@ -441,11 +426,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) { if (options::bitvectorToBool.wasSetByUser()) { - throw OptionException( - "bv-to-bool not supported with unsat cores/proofs"); + throw OptionException("bv-to-bool not supported with unsat cores"); } Notice() << "SmtEngine: turning off bitvector-to-bool to support unsat " - "cores/proofs" + "cores" << std::endl; options::bitvectorToBool.set(false); } @@ -455,10 +439,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) if (options::boolToBitvector.wasSetByUser()) { throw OptionException( - "bool-to-bv != off not supported with unsat cores/proofs"); + "bool-to-bv != off not supported with unsat cores"); } Notice() << "SmtEngine: turning off bool-to-bv to support unsat " - "cores/proofs" + "cores" << std::endl; options::boolToBitvector.set(options::BoolToBVMode::OFF); } @@ -467,11 +451,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) { if (options::bvIntroducePow2.wasSetByUser()) { - throw OptionException( - "bv-intro-pow2 not supported with unsat cores/proofs"); + throw OptionException("bv-intro-pow2 not supported with unsat cores"); } Notice() << "SmtEngine: turning off bv-intro-pow2 to support " - "unsat-cores/proofs" + "unsat-cores" << std::endl; options::bvIntroducePow2.set(false); } @@ -480,11 +463,10 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) { if (options::repeatSimp.wasSetByUser()) { - throw OptionException( - "repeat-simp not supported with unsat cores/proofs"); + throw OptionException("repeat-simp not supported with unsat cores"); } Notice() << "SmtEngine: turning off repeat-simp to support unsat " - "cores/proofs" + "cores" << std::endl; options::repeatSimp.set(false); } @@ -493,19 +475,17 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) { if (options::globalNegate.wasSetByUser()) { - throw OptionException( - "global-negate not supported with unsat cores/proofs"); + throw OptionException("global-negate not supported with unsat cores"); } Notice() << "SmtEngine: turning off global-negate to support unsat " - "cores/proofs" + "cores" << std::endl; options::globalNegate.set(false); } if (options::bitvectorAig()) { - throw OptionException( - "bitblast-aig not supported with unsat cores/proofs"); + throw OptionException("bitblast-aig not supported with unsat cores"); } } else @@ -626,7 +606,7 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) if (!options::ufSymmetryBreaker.wasSetByUser()) { bool qf_uf_noinc = logic.isPure(THEORY_UF) && !logic.isQuantified() - && !options::incrementalSolving() && !options::proof() + && !options::incrementalSolving() && !options::unsatCores(); Trace("smt") << "setting uf symmetry breaker to " << qf_uf_noinc << std::endl; @@ -848,7 +828,7 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) // Do we need to track instantiations? // Needed for sygus due to single invocation techniques. if (options::cegqiNestedQE() - || (options::proof() && !options::trackInstLemmas.wasSetByUser()) + || (options::unsatCores() && !options::trackInstLemmas.wasSetByUser()) || is_sygus) { options::trackInstLemmas.set(true); @@ -1323,59 +1303,6 @@ void setDefaults(LogicInfo& logic, bool isInternalSubsolver) options::arraysOptimizeLinear.set(false); } - if (options::proof()) - { - if (options::incrementalSolving()) - { - if (options::incrementalSolving.wasSetByUser()) - { - throw OptionException("--incremental is not supported with proofs"); - } - Warning() - << "SmtEngine: turning off incremental solving mode (not yet " - "supported with --proof, try --tear-down-incremental instead)" - << std::endl; - options::incrementalSolving.set(false); - } - if (logic > LogicInfo("QF_AUFBVLRA")) - { - throw OptionException( - "Proofs are only supported for sub-logics of QF_AUFBVLIA."); - } - if (options::bitvectorAlgebraicSolver()) - { - if (options::bitvectorAlgebraicSolver.wasSetByUser()) - { - throw OptionException( - "--bv-algebraic-solver is not supported with proofs"); - } - Notice() << "SmtEngine: turning off bv algebraic solver to support proofs" - << std::endl; - options::bitvectorAlgebraicSolver.set(false); - } - if (options::bitvectorEqualitySolver()) - { - if (options::bitvectorEqualitySolver.wasSetByUser()) - { - throw OptionException("--bv-eq-solver is not supported with proofs"); - } - Notice() << "SmtEngine: turning off bv eq solver to support proofs" - << std::endl; - options::bitvectorEqualitySolver.set(false); - } - if (options::bitvectorInequalitySolver()) - { - if (options::bitvectorInequalitySolver.wasSetByUser()) - { - throw OptionException( - "--bv-inequality-solver is not supported with proofs"); - } - Notice() << "SmtEngine: turning off bv ineq solver to support proofs" - << std::endl; - options::bitvectorInequalitySolver.set(false); - } - } - if (!options::bitvectorEqualitySolver()) { if (options::bvLazyRewriteExtf()) diff --git a/src/smt/smt_engine.cpp b/src/smt/smt_engine.cpp index 98e865478..81d4f594d 100644 --- a/src/smt/smt_engine.cpp +++ b/src/smt/smt_engine.cpp @@ -62,7 +62,6 @@ #include "options/open_ostream.h" #include "options/option_exception.h" #include "options/printer_options.h" -#include "options/proof_options.h" #include "options/prop_options.h" #include "options/quantifiers_options.h" #include "options/sep_options.h" @@ -75,9 +74,7 @@ #include "preprocessing/preprocessing_pass_context.h" #include "preprocessing/preprocessing_pass_registry.h" #include "printer/printer.h" -#include "proof/proof.h" #include "proof/proof_manager.h" -#include "proof/theory_proof.h" #include "proof/unsat_core.h" #include "smt/abduction_solver.h" #include "smt/abstract_values.h" @@ -113,14 +110,9 @@ #include "theory/theory_model.h" #include "theory/theory_traits.h" #include "util/hash.h" -#include "util/proof.h" #include "util/random.h" #include "util/resource_manager.h" -#if (IS_LFSC_BUILD && IS_PROOFS_BUILD) -#include "lfscc.h" -#endif - using namespace std; using namespace CVC4; using namespace CVC4::smt; @@ -131,10 +123,6 @@ using namespace CVC4::theory; namespace CVC4 { -namespace proof { -extern const char* const plf_signatures; -} // namespace proof - namespace smt { }/* namespace CVC4::smt */ @@ -307,15 +295,6 @@ void SmtEngine::finishInit() d_abductSolver.reset(new AbductionSolver(this)); } - PROOF( ProofManager::currentPM()->setLogic(d_logic); ); - PROOF({ - TheoryEngine* te = d_smtSolver->getTheoryEngine(); - for (TheoryId id = theory::THEORY_FIRST; id < theory::THEORY_LAST; ++id) - { - ProofManager::currentPM()->getTheoryProofEngine()->finishRegisterTheory( - te->theoryOf(id)); - } - }); d_pp->finishInit(); AlwaysAssert(getPropEngine()->getAssertionLevel() == 0) @@ -709,9 +688,18 @@ void SmtEngine::defineFunction(Expr func, ss << language::SetLanguage( language::SetLanguage::getLanguage(Dump.getStream())) << func; - DefineFunctionCommand c(ss.str(), func, formals, formula, global); + std::vector<Node> nFormals; + nFormals.reserve(formals.size()); + + for (const Expr& formal : formals) + { + nFormals.push_back(formal.getNode()); + } + + DefineFunctionNodeCommand nc( + ss.str(), func.getNode(), nFormals, formula.getNode()); d_dumpm->addToModelCommandAndDump( - c, ExprManager::VAR_FLAG_DEFINED, true, "declarations"); + nc, ExprManager::VAR_FLAG_DEFINED, true, "declarations"); // type check body debugCheckFunctionBody(formula, formals, func); @@ -891,7 +879,7 @@ theory::TheoryModel* SmtEngine::getAvailableModel(const char* c) const std::stringstream ss; ss << "Cannot " << c << " since model is not available. Perhaps the most recent call to " - "check-sat was interupted?"; + "check-sat was interrupted?"; throw RecoverableModalException(ss.str().c_str()); } @@ -1005,12 +993,6 @@ Result SmtEngine::checkSatInternal(const vector<Node>& assumptions, checkModel(); } } - // Check that UNSAT results generate a proof correctly. - if(options::checkProofs()) { - if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { - checkProof(); - } - } // Check that UNSAT results generate an unsat core correctly. if(options::checkUnsatCores()) { if(r.asSatisfiabilityResult().isSat() == Result::UNSAT) { @@ -1476,43 +1458,6 @@ Expr SmtEngine::getSepHeapExpr() { return getSepHeapAndNilExpr().first; } Expr SmtEngine::getSepNilExpr() { return getSepHeapAndNilExpr().second; } -void SmtEngine::checkProof() -{ -#if (IS_LFSC_BUILD && IS_PROOFS_BUILD) - - Chat() << "generating proof..." << endl; - - const Proof& pf = getProof(); - - Chat() << "checking proof..." << endl; - - std::string logicString = d_logic.getLogicString(); - - std::stringstream pfStream; - - pfStream << proof::plf_signatures << endl; - int64_t sizeBeforeProof = static_cast<int64_t>(pfStream.tellp()); - - pf.toStream(pfStream); - d_stats->d_proofsSize += - static_cast<int64_t>(pfStream.tellp()) - sizeBeforeProof; - - { - TimerStat::CodeTimer checkProofTimer(d_stats->d_lfscCheckProofTime); - lfscc_init(); - lfscc_check_file(pfStream, false, false, false, false, false, false, false); - } - // FIXME: we should actually call lfscc_cleanup here, but lfscc_cleanup - // segfaults on regress0/bv/core/bitvec7.smt - // lfscc_cleanup(); - -#else /* (IS_LFSC_BUILD && IS_PROOFS_BUILD) */ - Unreachable() - << "This version of CVC4 was built without proof support; cannot check " - "proofs."; -#endif /* (IS_LFSC_BUILD && IS_PROOFS_BUILD) */ -} - UnsatCore SmtEngine::getUnsatCoreInternal() { #if IS_PROOFS_BUILD @@ -1548,7 +1493,6 @@ void SmtEngine::checkUnsatCore() { coreChecker.setIsInternalSubsolver(); coreChecker.setLogic(getLogicInfo()); coreChecker.getOptions().set(options::checkUnsatCores, false); - coreChecker.getOptions().set(options::checkProofs, false); Notice() << "SmtEngine::checkUnsatCore(): pushing core assertions (size == " << core.size() << ")" << endl; for(UnsatCore::iterator i = core.begin(); i != core.end(); ++i) { @@ -1823,32 +1767,6 @@ UnsatCore SmtEngine::getUnsatCore() { return getUnsatCoreInternal(); } -// TODO(#1108): Simplify the error reporting of this method. -const Proof& SmtEngine::getProof() -{ - Trace("smt") << "SMT getProof()" << endl; - SmtScope smts(this); - finishInit(); - if(Dump.isOn("benchmark")) { - Dump("benchmark") << GetProofCommand(); - } -#if IS_PROOFS_BUILD - if(!options::proof()) { - throw ModalException("Cannot get a proof when produce-proofs option is off."); - } - if (d_state->getMode() != SmtMode::UNSAT) - { - throw RecoverableModalException( - "Cannot get a proof unless immediately preceded by UNSAT/ENTAILED " - "response."); - } - - return ProofManager::getProof(this); -#else /* IS_PROOFS_BUILD */ - throw ModalException("This build of CVC4 doesn't have proof support."); -#endif /* IS_PROOFS_BUILD */ -} - void SmtEngine::printInstantiations( std::ostream& out ) { SmtScope smts(this); finishInit(); diff --git a/src/smt/smt_engine.h b/src/smt/smt_engine.h index 99c4a67d3..5aa2ba987 100644 --- a/src/smt/smt_engine.h +++ b/src/smt/smt_engine.h @@ -34,7 +34,6 @@ #include "smt/smt_mode.h" #include "theory/logic_info.h" #include "util/hash.h" -#include "util/proof.h" #include "util/result.h" #include "util/sexpr.h" #include "util/statistics.h" @@ -544,16 +543,6 @@ class CVC4_PUBLIC SmtEngine */ std::vector<std::pair<Expr, Expr> > getAssignment(); - /** - * Get the last proof (only if immediately preceded by an UNSAT or ENTAILED - * query). Only permitted if CVC4 was built with proof support and - * produce-proofs is on. - * - * The Proof object is owned by this SmtEngine until the SmtEngine is - * destroyed. - */ - const Proof& getProof(); - /** Print all instantiations made by the quantifiers module. */ void printInstantiations(std::ostream& out); @@ -566,7 +555,7 @@ class CVC4_PUBLIC SmtEngine /** * Get synth solution. * - * This method returns true if we are in a state immediately preceeded by + * This method returns true if we are in a state immediately preceded by * a successful call to checkSynth. * * This method adds entries to solMap that map functions-to-synthesize with @@ -921,11 +910,6 @@ class CVC4_PUBLIC SmtEngine }; /** - * Check that a generated proof (via getProof()) checks. - */ - void checkProof(); - - /** * Internal method to get an unsatisfiable core (only if immediately preceded * by an UNSAT or ENTAILED query). Only permitted if CVC4 was built with * unsat-core support and produce-unsat-cores is on. Does not dump the diff --git a/src/smt/smt_engine_scope.cpp b/src/smt/smt_engine_scope.cpp index 1e9c91767..cc86ae33c 100644 --- a/src/smt/smt_engine_scope.cpp +++ b/src/smt/smt_engine_scope.cpp @@ -20,7 +20,6 @@ #include "base/check.h" #include "base/configuration_private.h" #include "base/output.h" -#include "proof/proof.h" #include "smt/smt_engine.h" namespace CVC4 { diff --git a/src/smt/smt_engine_stats.cpp b/src/smt/smt_engine_stats.cpp index 9b25580d2..e36284714 100644 --- a/src/smt/smt_engine_stats.cpp +++ b/src/smt/smt_engine_stats.cpp @@ -25,9 +25,7 @@ SmtEngineStatistics::SmtEngineStatistics() d_cnfConversionTime("smt::SmtEngine::cnfConversionTime"), d_numAssertionsPre("smt::SmtEngine::numAssertionsPreITERemoval", 0), d_numAssertionsPost("smt::SmtEngine::numAssertionsPostITERemoval", 0), - d_proofsSize("smt::SmtEngine::proofsSize", 0), d_checkModelTime("smt::SmtEngine::checkModelTime"), - d_lfscCheckProofTime("smt::SmtEngine::lfscCheckProofTime"), d_checkUnsatCoreTime("smt::SmtEngine::checkUnsatCoreTime"), d_solveTime("smt::SmtEngine::solveTime"), d_pushPopTime("smt::SmtEngine::pushPopTime"), @@ -39,9 +37,7 @@ SmtEngineStatistics::SmtEngineStatistics() smtStatisticsRegistry()->registerStat(&d_cnfConversionTime); smtStatisticsRegistry()->registerStat(&d_numAssertionsPre); smtStatisticsRegistry()->registerStat(&d_numAssertionsPost); - smtStatisticsRegistry()->registerStat(&d_proofsSize); smtStatisticsRegistry()->registerStat(&d_checkModelTime); - smtStatisticsRegistry()->registerStat(&d_lfscCheckProofTime); smtStatisticsRegistry()->registerStat(&d_checkUnsatCoreTime); smtStatisticsRegistry()->registerStat(&d_solveTime); smtStatisticsRegistry()->registerStat(&d_pushPopTime); @@ -56,9 +52,7 @@ SmtEngineStatistics::~SmtEngineStatistics() smtStatisticsRegistry()->unregisterStat(&d_cnfConversionTime); smtStatisticsRegistry()->unregisterStat(&d_numAssertionsPre); smtStatisticsRegistry()->unregisterStat(&d_numAssertionsPost); - smtStatisticsRegistry()->unregisterStat(&d_proofsSize); smtStatisticsRegistry()->unregisterStat(&d_checkModelTime); - smtStatisticsRegistry()->unregisterStat(&d_lfscCheckProofTime); smtStatisticsRegistry()->unregisterStat(&d_checkUnsatCoreTime); smtStatisticsRegistry()->unregisterStat(&d_solveTime); smtStatisticsRegistry()->unregisterStat(&d_pushPopTime); diff --git a/src/smt/smt_engine_stats.h b/src/smt/smt_engine_stats.h index 3463a0371..5193d173c 100644 --- a/src/smt/smt_engine_stats.h +++ b/src/smt/smt_engine_stats.h @@ -36,12 +36,8 @@ struct SmtEngineStatistics IntStat d_numAssertionsPre; /** Number of assertions after ite removal */ IntStat d_numAssertionsPost; - /** Size of all proofs generated */ - IntStat d_proofsSize; /** time spent in checkModel() */ TimerStat d_checkModelTime; - /** time spent checking the proof with LFSC */ - TimerStat d_lfscCheckProofTime; /** time spent in checkUnsatCore() */ TimerStat d_checkUnsatCoreTime; /** time spent in PropEngine::checkSat() */ diff --git a/src/smt/smt_solver.cpp b/src/smt/smt_solver.cpp index a31d84587..706c18416 100644 --- a/src/smt/smt_solver.cpp +++ b/src/smt/smt_solver.cpp @@ -14,7 +14,6 @@ #include "smt/smt_solver.h" -#include "proof/theory_proof.h" #include "prop/prop_engine.h" #include "smt/assertions.h" #include "smt/preprocessor.h" @@ -58,11 +57,6 @@ void SmtSolver::finishInit(const LogicInfo& logicInfo) ++id) { theory::TheoryConstructor::addTheory(d_theoryEngine.get(), id); - // register with proof engine if applicable -#ifdef CVC4_PROOF - ProofManager::currentPM()->getTheoryProofEngine()->registerTheory( - d_theoryEngine->theoryOf(id)); -#endif } Trace("smt-debug") << "Making prop engine..." << std::endl; @@ -197,7 +191,7 @@ Result SmtSolver::checkSatisfiability(Assertions& as, // set the filename on the result Result r = Result(result, filename); - + // notify our state of the check-sat result d_state.notifyCheckSatResult(hasAssumptions, r); diff --git a/src/smt/sygus_solver.h b/src/smt/sygus_solver.h index 468535da1..621bea9f3 100644 --- a/src/smt/sygus_solver.h +++ b/src/smt/sygus_solver.h @@ -116,7 +116,7 @@ class SygusSolver /** * Get synth solution. * - * This method returns true if we are in a state immediately preceeded by + * This method returns true if we are in a state immediately preceded by * a successful call to checkSynth. * * This method adds entries to sol_map that map functions-to-synthesize with diff --git a/src/smt/term_formula_removal.cpp b/src/smt/term_formula_removal.cpp index 5da190a3d..74fcda668 100644 --- a/src/smt/term_formula_removal.cpp +++ b/src/smt/term_formula_removal.cpp @@ -19,7 +19,7 @@ #include "expr/node_algorithm.h" #include "expr/skolem_manager.h" -#include "options/proof_options.h" +#include "options/smt_options.h" #include "proof/proof_manager.h" using namespace std; @@ -45,16 +45,21 @@ theory::TrustNode RemoveTermFormulas::run( { Node itesRemoved = run(assertion, newAsserts, newSkolems, false, false); // In some calling contexts, not necessary to report dependence information. - if (reportDeps - && (options::unsatCores() || options::fewerPreprocessingHoles())) + if (reportDeps && options::unsatCores()) { // new assertions have a dependence on the node - PROOF(ProofManager::currentPM()->addDependence(itesRemoved, assertion);) + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(itesRemoved, assertion); + } unsigned n = 0; while (n < newAsserts.size()) { - PROOF(ProofManager::currentPM()->addDependence(newAsserts[n].getProven(), - assertion);) + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(newAsserts[n].getProven(), + assertion); + } ++n; } } @@ -380,7 +385,7 @@ Node RemoveTermFormulas::replace(TNode node, bool inQuant, bool inTerm) const { }else if( !inTerm && hasNestedTermChildren( node ) ){ // Remember if we're inside a term inTerm = true; - } + } vector<Node> newChildren; bool somethingChanged = false; @@ -402,13 +407,14 @@ Node RemoveTermFormulas::replace(TNode node, bool inQuant, bool inTerm) const { } } -// returns true if the children of node should be considered nested terms +// returns true if the children of node should be considered nested terms bool RemoveTermFormulas::hasNestedTermChildren( TNode node ) { - return theory::kindToTheoryId(node.getKind())!=theory::THEORY_BOOL && - node.getKind()!=kind::EQUAL && node.getKind()!=kind::SEP_STAR && - node.getKind()!=kind::SEP_WAND && node.getKind()!=kind::SEP_LABEL && - node.getKind()!=kind::BITVECTOR_EAGER_ATOM; - // dont' worry about FORALL or EXISTS (handled separately) + return theory::kindToTheoryId(node.getKind()) != theory::THEORY_BOOL + && node.getKind() != kind::EQUAL && node.getKind() != kind::SEP_STAR + && node.getKind() != kind::SEP_WAND + && node.getKind() != kind::SEP_LABEL + && node.getKind() != kind::BITVECTOR_EAGER_ATOM; + // dont' worry about FORALL or EXISTS (handled separately) } Node RemoveTermFormulas::getAxiomFor(Node n) diff --git a/src/proof/simplify_boolean_node.h b/src/theory/arith/arith_lemma.cpp index bb4fe2e47..9bd4df255 100644 --- a/src/proof/simplify_boolean_node.h +++ b/src/theory/arith/arith_lemma.cpp @@ -1,27 +1,28 @@ /********************* */ -/*! \file simplify_boolean_node.h +/*! \file arith_lemma.cpp ** \verbatim ** Top contributors (to current version): - ** Mathias Preiner, Guy Katz + ** Gereon Kremer ** This file is part of the CVC4 project. ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS ** in the top-level source directory) and their institutional affiliations. ** All rights reserved. See the file COPYING in the top-level source ** directory for licensing information.\endverbatim ** - ** \brief Simplifying a boolean node, needed for constructing LFSC proofs. - ** + ** \brief ArithLemma class, derived from Lemma. **/ -#include "cvc4_private.h" - -#ifndef CVC4__SIMPLIFY_BOOLEAN_NODE_H -#define CVC4__SIMPLIFY_BOOLEAN_NODE_H +#include "theory/arith/arith_lemma.h" namespace CVC4 { +namespace theory { +namespace arith { -Node simplifyBooleanNode(const Node &n); - -}/* CVC4 namespace */ +std::ostream& operator<<(std::ostream& out, const ArithLemma& al) +{ + return out << al.d_node; +} -#endif /* CVC4__SIMPLIFY_BOOLEAN_NODE_H */ +} // namespace arith +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/arith/arith_lemma.h b/src/theory/arith/arith_lemma.h new file mode 100644 index 000000000..a06cb83f3 --- /dev/null +++ b/src/theory/arith/arith_lemma.h @@ -0,0 +1,61 @@ +/********************* */ +/*! \file arith_lemma.h + ** \verbatim + ** Top contributors (to current version): + ** Gereon Kremer + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief ArithLemma class, derived from Lemma. + **/ + +#ifndef CVC4__THEORY__ARITH__ARITH_LEMMA_H +#define CVC4__THEORY__ARITH__ARITH_LEMMA_H + +#include <tuple> +#include <vector> + +#include "expr/node.h" +#include "theory/arith/nl/inference.h" +#include "theory/inference_manager_buffered.h" +#include "theory/output_channel.h" +#include "theory/theory_inference.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +/** + * The data structure for a single lemma to process by the arithmetic theory, + * derived from theory::SimpleTheoryLemma. + * + * This also includes the inference type that produced this lemma. + */ +class ArithLemma : public SimpleTheoryLemma +{ + public: + ArithLemma(Node n, + LemmaProperty p, + ProofGenerator* pg, + nl::Inference inf = nl::Inference::UNKNOWN) + : SimpleTheoryLemma(n, p, pg), d_inference(inf) + { + } + virtual ~ArithLemma() {} + + /** The inference id for the lemma */ + nl::Inference d_inference; +}; +/** + * Writes an arithmetic lemma to a stream. + */ +std::ostream& operator<<(std::ostream& out, const ArithLemma& al); + +} // namespace arith +} // namespace theory +} // namespace CVC4 + +#endif /* CVC4__THEORY__ARITH__ARITH_LEMMA_H */ diff --git a/src/theory/arith/callbacks.cpp b/src/theory/arith/callbacks.cpp index 758a337ba..f5f8a1a10 100644 --- a/src/theory/arith/callbacks.cpp +++ b/src/theory/arith/callbacks.cpp @@ -16,6 +16,8 @@ **/ #include "theory/arith/callbacks.h" + +#include "theory/arith/proof_macros.h" #include "theory/arith/theory_arith_private.h" namespace CVC4 { @@ -87,17 +89,17 @@ void FarkasConflictBuilder::reset(){ d_consequent = NullConstraint; d_constraints.clear(); d_consequentSet = false; - PROOF(d_farkas.clear()); + ARITH_PROOF(d_farkas.clear()); Assert(!underConstruction()); } /* Adds a constraint to the constraint under construction. */ void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc){ Assert( - !PROOF_ON() + !ARITH_PROOF_ON() || (!underConstruction() && d_constraints.empty() && d_farkas.empty()) || (underConstruction() && d_constraints.size() + 1 == d_farkas.size())); - Assert(PROOF_ON() || d_farkas.empty()); + Assert(ARITH_PROOF_ON() || d_farkas.empty()); Assert(c->isTrue()); if(d_consequent == NullConstraint){ @@ -105,17 +107,20 @@ void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc){ } else { d_constraints.push_back(c); } - PROOF(d_farkas.push_back(fc);); - Assert(!PROOF_ON() || d_constraints.size() + 1 == d_farkas.size()); - Assert(PROOF_ON() || d_farkas.empty()); + ARITH_PROOF(d_farkas.push_back(fc)); + Assert(!ARITH_PROOF_ON() || d_constraints.size() + 1 == d_farkas.size()); + Assert(ARITH_PROOF_ON() || d_farkas.empty()); } void FarkasConflictBuilder::addConstraint(ConstraintCP c, const Rational& fc, const Rational& mult){ Assert(!mult.isZero()); - if(PROOF_ON() && !mult.isOne()){ + if (ARITH_PROOF_ON() && !mult.isOne()) + { Rational prod = fc * mult; addConstraint(c, prod); - }else{ + } + else + { addConstraint(c, fc); } } @@ -132,7 +137,7 @@ void FarkasConflictBuilder::makeLastConsequent(){ ConstraintCP last = d_constraints.back(); d_constraints.back() = d_consequent; d_consequent = last; - PROOF( std::swap( d_farkas.front(), d_farkas.back() ) ); + ARITH_PROOF(std::swap(d_farkas.front(), d_farkas.back())); d_consequentSet = true; } @@ -145,14 +150,14 @@ ConstraintCP FarkasConflictBuilder::commitConflict(){ Assert(underConstruction()); Assert(!d_constraints.empty()); Assert( - !PROOF_ON() + !ARITH_PROOF_ON() || (!underConstruction() && d_constraints.empty() && d_farkas.empty()) || (underConstruction() && d_constraints.size() + 1 == d_farkas.size())); - Assert(PROOF_ON() || d_farkas.empty()); + Assert(ARITH_PROOF_ON() || d_farkas.empty()); Assert(d_consequentSet); ConstraintP not_c = d_consequent->getNegation(); - RationalVectorCP coeffs = NULLPROOF(&d_farkas); + RationalVectorCP coeffs = ARITH_NULLPROOF(&d_farkas); not_c->impliedByFarkas(d_constraints, coeffs, true ); reset(); diff --git a/src/theory/arith/constraint.cpp b/src/theory/arith/constraint.cpp index 6a04e70d1..081bc08a7 100644 --- a/src/theory/arith/constraint.cpp +++ b/src/theory/arith/constraint.cpp @@ -21,7 +21,6 @@ #include <unordered_set> #include "base/output.h" -#include "proof/proof.h" #include "smt/smt_statistics_registry.h" #include "theory/arith/arith_utilities.h" #include "theory/arith/normal_form.h" @@ -551,46 +550,49 @@ bool Constraint::hasTrichotomyProof() const { void Constraint::printProofTree(std::ostream& out, size_t depth) const { -#if IS_PROOFS_BUILD - const ConstraintRule& rule = getConstraintRule(); - out << std::string(2 * depth, ' ') << "* " << getVariable() << " ["; - if (hasLiteral()) + if (ARITH_PROOF_ON()) { - out << getLiteral(); - } - else - { - out << "NOLIT"; - }; - out << "]" << ' ' << getType() << ' ' << getValue() << " (" << getProofType() - << ")"; - if (getProofType() == FarkasAP) - { - out << " ["; - bool first = true; - for (const auto& coeff : *rule.d_farkasCoefficients) + const ConstraintRule& rule = getConstraintRule(); + out << std::string(2 * depth, ' ') << "* " << getVariable() << " ["; + if (hasLiteral()) { - if (not first) + out << getLiteral(); + } + else + { + out << "NOLIT"; + }; + out << "]" << ' ' << getType() << ' ' << getValue() << " (" + << getProofType() << ")"; + if (getProofType() == FarkasAP) + { + out << " ["; + bool first = true; + for (const auto& coeff : *rule.d_farkasCoefficients) { - out << ", "; + if (not first) + { + out << ", "; + } + first = false; + out << coeff; } - first = false; - out << coeff; + out << "]"; } - out << "]"; - } - out << endl; + out << endl; - for (AntecedentId i = rule.d_antecedentEnd; i != AntecedentIdSentinel; --i) { - ConstraintCP antecdent = d_database->getAntecedent(i); - if (antecdent == NullConstraint) { - break; + for (AntecedentId i = rule.d_antecedentEnd; i != AntecedentIdSentinel; --i) + { + ConstraintCP antecdent = d_database->getAntecedent(i); + if (antecdent == NullConstraint) + { + break; + } + antecdent->printProofTree(out, depth + 1); } - antecdent->printProofTree(out, depth + 1); + return; } -#else /* IS_PROOFS_BUILD */ out << "Cannot print proof. This is not a proof build." << endl; -#endif /* IS_PROOFS_BUILD */ } bool Constraint::sanityChecking(Node n) const { @@ -648,8 +650,7 @@ ConstraintCP ConstraintDatabase::getAntecedent (AntecedentId p) const { void ConstraintRule::print(std::ostream& out) const { - - RationalVectorCP coeffs = NULLPROOF(d_farkasCoefficients); + RationalVectorCP coeffs = ARITH_NULLPROOF(d_farkasCoefficients); out << "{ConstraintRule, "; out << d_constraint << std::endl; out << "d_proofType= " << d_proofType << ", " << std::endl; @@ -658,7 +659,7 @@ void ConstraintRule::print(std::ostream& out) const { if (d_constraint != NullConstraint && d_antecedentEnd != AntecedentIdSentinel) { const ConstraintDatabase& database = d_constraint->getDatabase(); - + size_t coeffIterator = (coeffs != RationalVectorCPSentinel) ? coeffs->size()-1 : 0; AntecedentId p = d_antecedentEnd; // must have at least one antecedent @@ -700,9 +701,11 @@ bool Constraint::wellFormedFarkasProof() const { ConstraintCP antecedent = d_database->d_antecedents[p]; if(antecedent == NullConstraint) { return false; } -#if IS_PROOFS_BUILD - if(!PROOF_ON()){ return cr.d_farkasCoefficients == RationalVectorCPSentinel; } - Assert(PROOF_ON()); + if (!ARITH_PROOF_ON()) + { + return cr.d_farkasCoefficients == RationalVectorCPSentinel; + } + Assert(ARITH_PROOF_ON()); if(cr.d_farkasCoefficients == RationalVectorCPSentinel){ return false; } if(cr.d_farkasCoefficients->size() < 2){ return false; } @@ -755,7 +758,7 @@ bool Constraint::wellFormedFarkasProof() const { default: return false; } - + if(coeffIterator == coeffBegin){ return false; } --coeffIterator; --p; @@ -800,10 +803,6 @@ bool Constraint::wellFormedFarkasProof() const { // 0 = lhs <= rhs < 0 return (lhs.isNull() || (Constant::isMember(lhs) && Constant(lhs).isZero())) && rhs.sgn() < 0; - -#else /* IS_PROOFS_BUILD */ - return true; -#endif /* IS_PROOFS_BUILD */ } ConstraintP Constraint::makeNegation(ArithVar v, ConstraintType t, const DeltaRational& r){ @@ -860,7 +859,6 @@ ConstraintDatabase::ConstraintDatabase(context::Context* satContext, context::Co , d_one(1) , d_negOne(-1) { - } SortedConstraintMap& ConstraintDatabase::getVariableSCM(ArithVar v) const{ @@ -1109,7 +1107,7 @@ ConstraintP ConstraintDatabase::addLiteral(TNode literal){ return isNot ? hit->getNegation(): hit; }else{ Comparison negCmp = Comparison::parseNormalForm(negationNode); - + ConstraintType negType = Constraint::constraintTypeOfComparison(negCmp); DeltaRational negDR = negCmp.normalizedDeltaRational(); @@ -1213,7 +1211,8 @@ void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){ AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1; RationalVectorP coeffs; - if(PROOF_ON()){ + if (ARITH_PROOF_ON()) + { std::pair<int, int> sgns = unateFarkasSigns(getNegation(), imp); Rational first(sgns.first); @@ -1222,10 +1221,11 @@ void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){ coeffs = new RationalVector(); coeffs->push_back(first); coeffs->push_back(second); - } else { + } + else + { coeffs = RationalVectorPSentinel; } - // no need to delete coeffs the memory is owned by ConstraintRule d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffs)); @@ -1233,7 +1233,7 @@ void Constraint::impliedByUnate(ConstraintCP imp, bool nowInConflict){ if(Debug.isOn("constraint::conflictCommit") && inConflict()){ Debug("constraint::conflictCommit") << "inConflict@impliedByUnate " << this << std::endl; } - + if(Debug.isOn("constraints::wffp") && !wellFormedFarkasProof()){ getConstraintRule().print(Debug("constraints::wffp")); } @@ -1343,7 +1343,7 @@ void Constraint::impliedByIntHole(const ConstraintCPVec& b, bool nowInConflict){ * coeffs != RationalVectorSentinal, * coeffs->size() = a.size() + 1, * for i in [0,a.size) : coeff[i] corresponds to a[i], and - * coeff.back() corresponds to the current constraint. + * coeff.back() corresponds to the current constraint. */ void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coeffs, bool nowInConflict){ Debug("constraints::pf") << "impliedByFarkas(" << this; @@ -1359,10 +1359,9 @@ void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coef Assert(negationHasProof() == nowInConflict); Assert(allHaveProof(a)); - Assert(PROOF_ON() == (coeffs != RationalVectorCPSentinel)); - // !PROOF_ON() => coeffs == RationalVectorCPSentinel - // PROOF_ON() => coeffs->size() == a.size() + 1 - Assert(!PROOF_ON() || coeffs->size() == a.size() + 1); + Assert(ARITH_PROOF_ON() == (coeffs != RationalVectorCPSentinel)); + Assert(!ARITH_PROOF_ON() || coeffs->size() == a.size() + 1); + Assert(a.size() >= 1); d_database->d_antecedents.push_back(NullConstraint); @@ -1374,10 +1373,13 @@ void Constraint::impliedByFarkas(const ConstraintCPVec& a, RationalVectorCP coef AntecedentId antecedentEnd = d_database->d_antecedents.size() - 1; RationalVectorCP coeffsCopy; - if(PROOF_ON()){ + if (ARITH_PROOF_ON()) + { Assert(coeffs != RationalVectorCPSentinel); coeffsCopy = new RationalVector(*coeffs); - } else { + } + else + { coeffsCopy = RationalVectorCPSentinel; } d_database->pushConstraintRule(ConstraintRule(this, FarkasAP, antecedentEnd, coeffsCopy)); diff --git a/src/theory/arith/constraint.h b/src/theory/arith/constraint.h index b32616a04..3caccdebd 100644 --- a/src/theory/arith/constraint.h +++ b/src/theory/arith/constraint.h @@ -75,9 +75,9 @@ #ifndef CVC4__THEORY__ARITH__CONSTRAINT_H #define CVC4__THEORY__ARITH__CONSTRAINT_H -#include <unordered_map> #include <list> #include <set> +#include <unordered_map> #include <vector> #include "base/configuration_private.h" @@ -85,12 +85,12 @@ #include "context/cdqueue.h" #include "context/context.h" #include "expr/node.h" -#include "proof/proof.h" #include "theory/arith/arithvar.h" #include "theory/arith/callbacks.h" #include "theory/arith/congruence_manager.h" #include "theory/arith/constraint_forward.h" #include "theory/arith/delta_rational.h" +#include "theory/arith/proof_macros.h" namespace CVC4 { namespace theory { @@ -252,11 +252,11 @@ struct PerVariableDatabase{ } }; - /** * If proofs are on, there is a vector of rationals for farkas coefficients. - * This is the owner of the memory for the vector, and calls delete upon cleanup. - * + * This is the owner of the memory for the vector, and calls delete upon + * cleanup. + * */ struct ConstraintRule { ConstraintP d_constraint; @@ -302,17 +302,13 @@ struct ConstraintRule { * We do however use all of the constraints by requiring non-zero * coefficients. */ -#if IS_PROOFS_BUILD RationalVectorCP d_farkasCoefficients; -#endif /* IS_PROOFS_BUILD */ ConstraintRule() : d_constraint(NullConstraint) , d_proofType(NoAP) , d_antecedentEnd(AntecedentIdSentinel) { -#if IS_PROOFS_BUILD d_farkasCoefficients = RationalVectorCPSentinel; -#endif /* IS_PROOFS_BUILD */ } ConstraintRule(ConstraintP con, ArithProofType pt) @@ -320,18 +316,14 @@ struct ConstraintRule { , d_proofType(pt) , d_antecedentEnd(AntecedentIdSentinel) { -#if IS_PROOFS_BUILD d_farkasCoefficients = RationalVectorCPSentinel; -#endif /* IS_PROOFS_BUILD */ } ConstraintRule(ConstraintP con, ArithProofType pt, AntecedentId antecedentEnd) : d_constraint(con) , d_proofType(pt) , d_antecedentEnd(antecedentEnd) { -#if IS_PROOFS_BUILD d_farkasCoefficients = RationalVectorCPSentinel; -#endif /* IS_PROOFS_BUILD */ } ConstraintRule(ConstraintP con, ArithProofType pt, AntecedentId antecedentEnd, RationalVectorCP coeffs) @@ -339,10 +331,8 @@ struct ConstraintRule { , d_proofType(pt) , d_antecedentEnd(antecedentEnd) { - Assert(PROOF_ON() || coeffs == RationalVectorCPSentinel); -#if IS_PROOFS_BUILD + Assert(ARITH_PROOF_ON() || coeffs == RationalVectorCPSentinel); d_farkasCoefficients = coeffs; -#endif /* IS_PROOFS_BUILD */ } void print(std::ostream& out) const; @@ -750,7 +740,7 @@ class Constraint { /** * If the constraint - * canBePropagated() and + * canBePropagated() and * !assertedToTheTheory(), * the constraint is added to the database's propagation queue. * @@ -789,9 +779,11 @@ class Constraint { ConstraintP constraint = crp->d_constraint; Assert(constraint->d_crid != ConstraintRuleIdSentinel); constraint->d_crid = ConstraintRuleIdSentinel; - - PROOF(if (crp->d_farkasCoefficients != RationalVectorCPSentinel) { - delete crp->d_farkasCoefficients; + ARITH_PROOF({ + if (crp->d_farkasCoefficients != RationalVectorCPSentinel) + { + delete crp->d_farkasCoefficients; + } }); } }; @@ -876,10 +868,11 @@ class Constraint { return getConstraintRule().d_antecedentEnd; } - inline RationalVectorCP getFarkasCoefficients() const { - return NULLPROOF(getConstraintRule().d_farkasCoefficients); + inline RationalVectorCP getFarkasCoefficients() const + { + return ARITH_NULLPROOF(getConstraintRule().d_farkasCoefficients); } - + void debugPrint() const; /** @@ -1051,8 +1044,7 @@ private: * The index in this list is the proper ordering of the proofs. */ ConstraintRuleList d_constraintProofs; - - + /** * Contains the exact list of constraints that can be used for propagation. */ @@ -1100,9 +1092,9 @@ private: const Rational d_one; const Rational d_negOne; - + friend class Constraint; - + public: ConstraintDatabase( context::Context* satContext, @@ -1209,7 +1201,7 @@ public: /** AntecendentID must be in range. */ ConstraintCP getAntecedent(AntecedentId p) const; - + private: /** returns true if cons is now in conflict. */ bool handleUnateProp(ConstraintP ant, ConstraintP cons); diff --git a/src/theory/arith/inference_manager.cpp b/src/theory/arith/inference_manager.cpp new file mode 100644 index 000000000..bb0bb494e --- /dev/null +++ b/src/theory/arith/inference_manager.cpp @@ -0,0 +1,144 @@ +/********************* */ +/*! \file inference_manager.cpp + ** \verbatim + ** Top contributors (to current version): + ** Gereon Kremer + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Implementation of the inference manager for the theory of strings. + **/ + +#include "theory/arith/inference_manager.h" + +#include "options/arith_options.h" +#include "theory/arith/theory_arith.h" +#include "theory/rewriter.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +InferenceManager::InferenceManager(TheoryArith& ta, + ArithState& astate, + ProofNodeManager* pnm) + : InferenceManagerBuffered(ta, astate, pnm), d_lemmasPp(ta.getUserContext()) +{ +} + +void InferenceManager::addPendingArithLemma(std::unique_ptr<ArithLemma> lemma, + bool isWaiting) +{ + lemma->d_node = Rewriter::rewrite(lemma->d_node); + if (hasCachedLemma(lemma->d_node, lemma->d_property)) + { + return; + } + if (isEntailedFalse(*lemma)) + { + if (isWaiting) + { + d_waitingLem.clear(); + } + else + { + d_pendingLem.clear(); + d_theoryState.notifyInConflict(); + } + } + if (isWaiting) + { + d_waitingLem.emplace_back(std::move(lemma)); + } + else + { + d_pendingLem.emplace_back(std::move(lemma)); + } +} +void InferenceManager::addPendingArithLemma(const ArithLemma& lemma, + bool isWaiting) +{ + addPendingArithLemma(std::unique_ptr<ArithLemma>(new ArithLemma(lemma)), + isWaiting); +} +void InferenceManager::addPendingArithLemma(const Node& lemma, + nl::Inference inftype, + bool isWaiting) +{ + addPendingArithLemma(std::unique_ptr<ArithLemma>(new ArithLemma( + lemma, LemmaProperty::NONE, nullptr, inftype)), + isWaiting); +} + +void InferenceManager::flushWaitingLemmas() +{ + for (auto& lem : d_waitingLem) + { + d_pendingLem.emplace_back(std::move(lem)); + } + d_waitingLem.clear(); +} + +void InferenceManager::addConflict(const Node& conf, nl::Inference inftype) +{ + conflict(Rewriter::rewrite(conf)); +} + +std::size_t InferenceManager::numWaitingLemmas() const +{ + return d_waitingLem.size(); +} + +bool InferenceManager::hasCachedLemma(TNode lem, LemmaProperty p) +{ + if (isLemmaPropertyPreprocess(p)) + { + return d_lemmasPp.find(lem) != d_lemmasPp.end(); + } + return TheoryInferenceManager::hasCachedLemma(lem, p); +} + +bool InferenceManager::cacheLemma(TNode lem, LemmaProperty p) +{ + if (isLemmaPropertyPreprocess(p)) + { + if (d_lemmasPp.find(lem) != d_lemmasPp.end()) + { + return false; + } + d_lemmasPp.insert(lem); + return true; + } + return TheoryInferenceManager::cacheLemma(lem, p); +} + +bool InferenceManager::isEntailedFalse(const ArithLemma& lem) +{ + if (options::nlExtEntailConflicts()) + { + Node ch_lemma = lem.d_node.negate(); + ch_lemma = Rewriter::rewrite(ch_lemma); + Trace("arith-inf-manager") << "InferenceManager::Check entailment of " + << ch_lemma << "..." << std::endl; + + std::pair<bool, Node> et = d_theory.getValuation().entailmentCheck( + options::TheoryOfMode::THEORY_OF_TYPE_BASED, ch_lemma); + Trace("arith-inf-manager") << "entailment test result : " << et.first << " " + << et.second << std::endl; + if (et.first) + { + Trace("arith-inf-manager") + << "*** Lemma entailed to be in conflict : " << lem.d_node + << std::endl; + return true; + } + } + return false; +} + +} // namespace arith +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/arith/inference_manager.h b/src/theory/arith/inference_manager.h new file mode 100644 index 000000000..1c0678e60 --- /dev/null +++ b/src/theory/arith/inference_manager.h @@ -0,0 +1,116 @@ +/********************* */ +/*! \file inference_manager.h + ** \verbatim + ** Top contributors (to current version): + ** Gereon Kremer + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Customized inference manager for the theory of nonlinear arithmetic + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__ARITH__INFERENCE_MANAGER_H +#define CVC4__THEORY__ARITH__INFERENCE_MANAGER_H + +#include <map> +#include <vector> + +#include "theory/arith/arith_lemma.h" +#include "theory/arith/arith_state.h" +#include "theory/arith/nl/inference.h" +#include "theory/arith/nl/nl_lemma_utils.h" +#include "theory/inference_manager_buffered.h" + +namespace CVC4 { +namespace theory { +namespace arith { + +class TheoryArith; + +/** + * A specialized variant of the InferenceManagerBuffered, tailored to the + * arithmetic theory. + * + * It adds some convenience methods to send ArithLemma and adds a mechanism for + * waiting lemmas that can be flushed into the pending lemmas of the this + * buffered inference manager. + * It also extends the caching mechanism of TheoryInferenceManager to cache + * preprocessing lemmas and non-preprocessing lemmas separately. For the former, + * it uses the cache provided by the TheoryInferenceManager base class. + */ +class InferenceManager : public InferenceManagerBuffered +{ + using NodeSet = context::CDHashSet<Node, NodeHashFunction>; + + public: + InferenceManager(TheoryArith& ta, ArithState& astate, ProofNodeManager* pnm); + + /** + * Add a lemma as pending lemma to the this inference manager. + * If isWaiting is true, the lemma is first stored as waiting lemma and only + * added as pending lemma when calling flushWaitingLemmas. + */ + void addPendingArithLemma(std::unique_ptr<ArithLemma> lemma, + bool isWaiting = false); + /** + * Add a lemma as pending lemma to the this inference manager. + * If isWaiting is true, the lemma is first stored as waiting lemma and only + * added as pending lemma when calling flushWaitingLemmas. + */ + void addPendingArithLemma(const ArithLemma& lemma, bool isWaiting = false); + /** + * Add a lemma as pending lemma to the this inference manager. + * If isWaiting is true, the lemma is first stored as waiting lemma and only + * added as pending lemma when calling flushWaitingLemmas. + */ + void addPendingArithLemma(const Node& lemma, + nl::Inference inftype, + bool isWaiting = false); + + /** + * Flush all waiting lemmas to the this inference manager (as pending + * lemmas). To actually send them, call doPendingLemmas() afterwards. + */ + void flushWaitingLemmas(); + + /** Add a conflict to the this inference manager. */ + void addConflict(const Node& conf, nl::Inference inftype); + + /** Returns the number of pending lemmas. */ + std::size_t numWaitingLemmas() const; + + /** Checks whether the given lemma is already present in the cache. */ + virtual bool hasCachedLemma(TNode lem, LemmaProperty p) override; + + protected: + /** + * Adds the given lemma to the cache. Returns true if it has not been in the + * cache yet. + */ + virtual bool cacheLemma(TNode lem, LemmaProperty p) override; + + private: + /** + * Checks whether the lemma is entailed to be false. In this case, it is a + * conflict. + */ + bool isEntailedFalse(const ArithLemma& lem); + + /** The waiting lemmas. */ + std::vector<std::unique_ptr<ArithLemma>> d_waitingLem; + + /** cache of all preprocessed lemmas sent on the output channel + * (user-context-dependent) */ + NodeSet d_lemmasPp; +}; + +} // namespace arith +} // namespace theory +} // namespace CVC4 + +#endif diff --git a/src/theory/arith/linear_equality.cpp b/src/theory/arith/linear_equality.cpp index 7eb2f3f9e..3c4f678a2 100644 --- a/src/theory/arith/linear_equality.cpp +++ b/src/theory/arith/linear_equality.cpp @@ -510,11 +510,11 @@ void LinearEqualityModule::propagateBasicFromRow(ConstraintP c){ RowIndex ridx = d_tableau.basicToRowIndex(basic); ConstraintCPVec bounds; - RationalVectorP coeffs = NULLPROOF(new RationalVector()); + RationalVectorP coeffs = ARITH_NULLPROOF(new RationalVector()); propagateRow(bounds, ridx, upperBound, c, coeffs); c->impliedByFarkas(bounds, coeffs, false); c->tryToPropagate(); - + if(coeffs != RationalVectorPSentinel) { delete coeffs; } } @@ -524,9 +524,9 @@ void LinearEqualityModule::propagateBasicFromRow(ConstraintP c){ * The proof is in terms of the other constraints and the negation of c, ~c. * * A row has the form: - * sum a_i * x_i = 0 + * sum a_i * x_i = 0 * or - * sx + sum r y + sum q z = 0 + * sx + sum r y + sum q z = 0 * where r > 0 and q < 0. * * If rowUp, we are proving c @@ -555,7 +555,7 @@ void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bo Assert(farkas->empty()); farkas->push_back(Rational(0)); } - + ArithVar v = c->getVariable(); Debug("arith::propagateRow") << "LinearEqualityModule::propagateRow(" << ridx << ", " << rowUp << ", " << v << ") start" << endl; @@ -563,7 +563,7 @@ void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bo const Rational& multiple = rowUp ? d_one : d_negOne; Debug("arith::propagateRow") << "multiple: " << multiple << endl; - + Tableau::RowIterator iter = d_tableau.ridRowIterator(ridx); for(; !iter.atEnd(); ++iter){ const Tableau::Entry& entry = *iter; @@ -595,8 +595,8 @@ void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bo if(farkas != RationalVectorPSentinel){ Assert(farkas->front().isZero()); Rational multAij = multiple * a_ij; - Debug("arith::propagateRow") << "("<<multAij<<") "; - farkas->front() = multAij; + Debug("arith::propagateRow") << "(" << multAij << ") "; + farkas->front() = multAij; } Debug("arith::propagateRow") << c << endl; @@ -605,10 +605,10 @@ void LinearEqualityModule::propagateRow(ConstraintCPVec& into, RowIndex ridx, bo ConstraintCP bound = selectUb ? d_variables.getUpperBoundConstraint(nonbasic) : d_variables.getLowerBoundConstraint(nonbasic); - + if(farkas != RationalVectorPSentinel){ Rational multAij = multiple * a_ij; - Debug("arith::propagateRow") << "("<<multAij<<") "; + Debug("arith::propagateRow") << "(" << multAij << ") "; farkas->push_back(multAij); } Assert(bound != NullConstraint); @@ -678,7 +678,7 @@ ConstraintP LinearEqualityModule::weakestExplanation(bool aboveUpper, DeltaRatio * If !aboveUpper, then the conflict is with the constraint c : x_b >= l_b. * * A row has the form: - * -x_b sum a_i * x_i = 0 + * -x_b sum a_i * x_i = 0 * or * -x_b + sum r y + sum q z = 0, * x_b = sum r y + sum q z @@ -724,7 +724,7 @@ ConstraintCP LinearEqualityModule::minimallyWeakConflict(bool aboveUpper, ArithV Assert(assignment < d_variables.getLowerBound(basicVar)); surplus = d_variables.getLowerBound(basicVar) - assignment; } - + bool anyWeakenings = false; for(Tableau::RowIterator i = d_tableau.basicRowIterator(basicVar); !i.atEnd(); ++i){ const Tableau::Entry& entry = *i; diff --git a/src/theory/arith/nl/cad_solver.cpp b/src/theory/arith/nl/cad_solver.cpp index b1f0894fb..ac939c341 100644 --- a/src/theory/arith/nl/cad_solver.cpp +++ b/src/theory/arith/nl/cad_solver.cpp @@ -95,7 +95,7 @@ std::vector<NlLemma> CadSolver::checkFull() { lems.emplace_back(nm->mkNode(Kind::OR, mis), Inference::CAD_CONFLICT); } - Trace("nl-cad") << "UNSAT with MIS: " << lems.back().d_lemma << std::endl; + Trace("nl-cad") << "UNSAT with MIS: " << lems.back().d_node << std::endl; } return lems; #else diff --git a/src/theory/arith/nl/nl_lemma_utils.cpp b/src/theory/arith/nl/nl_lemma_utils.cpp index 49eec186e..58a552815 100644 --- a/src/theory/arith/nl/nl_lemma_utils.cpp +++ b/src/theory/arith/nl/nl_lemma_utils.cpp @@ -15,20 +15,26 @@ #include "theory/arith/nl/nl_lemma_utils.h" #include "theory/arith/nl/nl_model.h" +#include "theory/arith/nl/nonlinear_extension.h" namespace CVC4 { namespace theory { namespace arith { namespace nl { -LemmaProperty NlLemma::getLemmaProperty() const +bool NlLemma::process(TheoryInferenceManager* im) { - return d_preprocess ? LemmaProperty::PREPROCESS : LemmaProperty::NONE; + bool res = ArithLemma::process(im); + if (d_nlext != nullptr) + { + d_nlext->processSideEffect(*this); + } + return res; } std::ostream& operator<<(std::ostream& out, NlLemma& n) { - out << n.d_lemma; + out << n.d_node; return out; } diff --git a/src/theory/arith/nl/nl_lemma_utils.h b/src/theory/arith/nl/nl_lemma_utils.h index f40857fda..c6c22c3c6 100644 --- a/src/theory/arith/nl/nl_lemma_utils.h +++ b/src/theory/arith/nl/nl_lemma_utils.h @@ -17,7 +17,9 @@ #include <tuple> #include <vector> + #include "expr/node.h" +#include "theory/arith/arith_lemma.h" #include "theory/arith/nl/inference.h" #include "theory/output_channel.h" @@ -27,6 +29,7 @@ namespace arith { namespace nl { class NlModel; +class NonlinearExtension; /** * The data structure for a single lemma to process by the non-linear solver, @@ -39,19 +42,24 @@ class NlModel; * - A set of secant points to record (for transcendental secant plane * inferences). */ -struct NlLemma +class NlLemma : public ArithLemma { - NlLemma(Node lem, Inference id = Inference::UNKNOWN) - : d_id(id), d_lemma(lem), d_preprocess(false) + public: + NlLemma(Node n, + LemmaProperty p, + ProofGenerator* pg, + nl::Inference inf = nl::Inference::UNKNOWN) + : ArithLemma(n, p, pg, inf) + { + } + NlLemma(Node n, nl::Inference inf = nl::Inference::UNKNOWN) + : ArithLemma(n, LemmaProperty::NONE, nullptr, inf) { } ~NlLemma() {} - /** The inference id for the lemma */ - Inference d_id; - /** The lemma */ - Node d_lemma; - /** Whether to preprocess the lemma */ - bool d_preprocess; + + bool process(TheoryInferenceManager* im) override; + /** secant points to add * * A member (tf, d, c) in this vector indicates that point c should be added @@ -62,8 +70,8 @@ struct NlLemma * Cimatti et al., CADE 2017. */ std::vector<std::tuple<Node, unsigned, Node> > d_secantPoint; - /** get lemma property (preprocess or none) */ - LemmaProperty getLemmaProperty() const; + + NonlinearExtension* d_nlext; }; /** * Writes a non-linear lemma to a stream. diff --git a/src/theory/arith/nl/nl_model.cpp b/src/theory/arith/nl/nl_model.cpp index cc10d6659..fbc38fbc2 100644 --- a/src/theory/arith/nl/nl_model.cpp +++ b/src/theory/arith/nl/nl_model.cpp @@ -16,6 +16,7 @@ #include "expr/node_algorithm.h" #include "options/arith_options.h" +#include "options/smt_options.h" #include "options/theory_options.h" #include "theory/arith/arith_msum.h" #include "theory/arith/arith_utilities.h" @@ -325,7 +326,7 @@ bool NlModel::checkModel(const std::vector<Node>& assertions, Node v = cb.first; Node pred = nm->mkNode(AND, nm->mkNode(GEQ, v, l), nm->mkNode(GEQ, u, v)); pred = nm->mkNode(OR, mg.negate(), pred); - lemmas.push_back(pred); + lemmas.emplace_back(pred); } } return true; diff --git a/src/theory/arith/nl/nl_solver.cpp b/src/theory/arith/nl/nl_solver.cpp index 521539674..6c20eec76 100644 --- a/src/theory/arith/nl/nl_solver.cpp +++ b/src/theory/arith/nl/nl_solver.cpp @@ -843,7 +843,7 @@ std::vector<NlLemma> NlSolver::checkMonomialMagnitude(unsigned c) std::vector<NlLemma> nr_lemmas; for (unsigned i = 0; i < lemmas.size(); i++) { - if (r_lemmas.find(lemmas[i].d_lemma) == r_lemmas.end()) + if (r_lemmas.find(lemmas[i].d_node) == r_lemmas.end()) { nr_lemmas.push_back(lemmas[i]); } diff --git a/src/theory/arith/nl/nonlinear_extension.cpp b/src/theory/arith/nl/nonlinear_extension.cpp index 8535396e7..ada6aa11a 100644 --- a/src/theory/arith/nl/nonlinear_extension.cpp +++ b/src/theory/arith/nl/nonlinear_extension.cpp @@ -166,9 +166,9 @@ void NonlinearExtension::sendLemmas(const std::vector<NlLemma>& out) { for (const NlLemma& nlem : out) { - Node lem = nlem.d_lemma; - LemmaProperty p = nlem.getLemmaProperty(); - Trace("nl-ext-lemma") << "NonlinearExtension::Lemma : " << nlem.d_id + Node lem = nlem.d_node; + LemmaProperty p = nlem.d_property; + Trace("nl-ext-lemma") << "NonlinearExtension::Lemma : " << nlem.d_inference << " : " << lem << std::endl; d_containing.getOutputChannel().lemma(lem, p); // process the side effect @@ -182,7 +182,7 @@ void NonlinearExtension::sendLemmas(const std::vector<NlLemma>& out) { d_lemmas.insert(lem); } - d_stats.d_inferences << nlem.d_id; + d_stats.d_inferences << nlem.d_inference; } } @@ -210,14 +210,15 @@ void NonlinearExtension::computeRelevantAssertions( unsigned NonlinearExtension::filterLemma(NlLemma lem, std::vector<NlLemma>& out) { Trace("nl-ext-lemma-debug") - << "NonlinearExtension::Lemma pre-rewrite : " << lem.d_lemma << std::endl; - lem.d_lemma = Rewriter::rewrite(lem.d_lemma); + << "NonlinearExtension::Lemma pre-rewrite : " << lem.d_node << std::endl; + lem.d_node = Rewriter::rewrite(lem.d_node); // get the proper cache - NodeSet& lcache = lem.d_preprocess ? d_lemmasPp : d_lemmas; - if (lcache.find(lem.d_lemma) != lcache.end()) + NodeSet& lcache = + isLemmaPropertyPreprocess(lem.d_property) ? d_lemmasPp : d_lemmas; + if (lcache.find(lem.d_node) != lcache.end()) { Trace("nl-ext-lemma-debug") - << "NonlinearExtension::Lemma duplicate : " << lem.d_lemma << std::endl; + << "NonlinearExtension::Lemma duplicate : " << lem.d_node << std::endl; return 0; } out.emplace_back(lem); @@ -232,7 +233,7 @@ unsigned NonlinearExtension::filterLemmas(std::vector<NlLemma>& lemmas, // check if any are entailed to be false for (const NlLemma& lem : lemmas) { - Node ch_lemma = lem.d_lemma.negate(); + Node ch_lemma = lem.d_node.negate(); ch_lemma = Rewriter::rewrite(ch_lemma); Trace("nl-ext-et-debug") << "Check entailment of " << ch_lemma << "..." << std::endl; @@ -243,7 +244,7 @@ unsigned NonlinearExtension::filterLemmas(std::vector<NlLemma>& lemmas, if (et.first) { Trace("nl-ext-et") << "*** Lemma entailed to be in conflict : " - << lem.d_lemma << std::endl; + << lem.d_node << std::endl; // return just this lemma if (filterLemma(lem, out) > 0) { diff --git a/src/theory/arith/nl/nonlinear_extension.h b/src/theory/arith/nl/nonlinear_extension.h index ee58a9e2e..d035b1056 100644 --- a/src/theory/arith/nl/nonlinear_extension.h +++ b/src/theory/arith/nl/nonlinear_extension.h @@ -172,6 +172,9 @@ class NonlinearExtension */ void presolve(); + /** Process side effect se */ + void processSideEffect(const NlLemma& se); + private: /** Model-based refinement * @@ -274,8 +277,6 @@ class NonlinearExtension * Send lemmas in out on the output channel of theory of arithmetic. */ void sendLemmas(const std::vector<NlLemma>& out); - /** Process side effect se */ - void processSideEffect(const NlLemma& se); /** cache of all lemmas sent on the output channel (user-context-dependent) */ NodeSet d_lemmas; diff --git a/src/theory/arith/nl/transcendental_solver.cpp b/src/theory/arith/nl/transcendental_solver.cpp index 321818b94..b2ef16459 100644 --- a/src/theory/arith/nl/transcendental_solver.cpp +++ b/src/theory/arith/nl/transcendental_solver.cpp @@ -212,8 +212,8 @@ void TranscendentalSolver::initLastCall(const std::vector<Node>& assertions, // note we must do preprocess on this lemma Trace("nl-ext-lemma") << "NonlinearExtension::Lemma : purify : " << lem << std::endl; - NlLemma nlem(lem, Inference::T_PURIFY_ARG); - nlem.d_preprocess = true; + NlLemma nlem( + lem, LemmaProperty::PREPROCESS, nullptr, Inference::T_PURIFY_ARG); lems.emplace_back(nlem); } diff --git a/src/theory/arith/proof_macros.h b/src/theory/arith/proof_macros.h new file mode 100644 index 000000000..6cc1b3b15 --- /dev/null +++ b/src/theory/arith/proof_macros.h @@ -0,0 +1,34 @@ +/********************* */ +/*! \file proof_macros.h + ** \verbatim + ** Top contributors (to current version): + ** Alex Ozdemir + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Macros which run code when the old or new proof system is enabled, + ** or unsat cores are enabled. + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__ARITH__PROOF_MACROS_H +#define CVC4__THEORY__ARITH__PROOF_MACROS_H + +#include "options/smt_options.h" + +#define ARITH_PROOF(x) \ + if (CVC4::options::proofNew()) \ + { \ + x; \ + } +#define ARITH_NULLPROOF(x) \ + (CVC4::options::proofNew()) \ + ? x \ + : NULL +#define ARITH_PROOF_ON() CVC4::options::proofNew() + +#endif // CVC4__THEORY__ARITH__PROOF_MACROS_H diff --git a/src/theory/arith/theory_arith.cpp b/src/theory/arith/theory_arith.cpp index ea751ca74..762634ce7 100644 --- a/src/theory/arith/theory_arith.cpp +++ b/src/theory/arith/theory_arith.cpp @@ -41,7 +41,6 @@ TheoryArith::TheoryArith(context::Context* c, d_internal( new TheoryArithPrivate(*this, c, u, out, valuation, logicInfo, pnm)), d_ppRewriteTimer("theory::arith::ppRewriteTimer"), - d_proofRecorder(nullptr), d_astate(*d_internal, c, u, valuation) { smtStatisticsRegistry()->registerStat(&d_ppRewriteTimer); diff --git a/src/theory/arith/theory_arith.h b/src/theory/arith/theory_arith.h index bfe30db61..6adf8f66a 100644 --- a/src/theory/arith/theory_arith.h +++ b/src/theory/arith/theory_arith.h @@ -18,7 +18,6 @@ #pragma once #include "expr/node.h" -#include "proof/arith_proof_recorder.h" #include "theory/arith/arith_state.h" #include "theory/arith/theory_arith_private_forward.h" #include "theory/theory.h" @@ -41,11 +40,6 @@ class TheoryArith : public Theory { TimerStat d_ppRewriteTimer; - /** - * @brief Where to store Farkas proofs of lemmas - */ - proof::ArithProofRecorder * d_proofRecorder; - public: TheoryArith(context::Context* c, context::UserContext* u, @@ -110,11 +104,6 @@ class TheoryArith : public Theory { std::pair<bool, Node> entailmentCheck(TNode lit) override; - void setProofRecorder(proof::ArithProofRecorder* proofRecorder) - { - d_proofRecorder = proofRecorder; - } - private: /** The state object wrapping TheoryArithPrivate */ ArithState d_astate; diff --git a/src/theory/arith/theory_arith_private.cpp b/src/theory/arith/theory_arith_private.cpp index 8ca99d369..8a780116c 100644 --- a/src/theory/arith/theory_arith_private.cpp +++ b/src/theory/arith/theory_arith_private.cpp @@ -624,7 +624,7 @@ bool TheoryArithPrivate::AssertLower(ConstraintP constraint){ ConstraintP ubc = d_partialModel.getUpperBoundConstraint(x_i); ConstraintP negation = constraint->getNegation(); negation->impliedByUnate(ubc, true); - + raiseConflict(constraint); ++(d_statistics.d_statAssertLowerConflicts); @@ -757,7 +757,7 @@ bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){ if(d_partialModel.greaterThanUpperBound(x_i, c_i) ){ // \upperbound(x_i) <= c_i return false; //sat } - + // cmpToLb = \lowerbound(x_i).cmp(c_i) int cmpToLB = d_partialModel.cmpToLowerBound(x_i, c_i); if( cmpToLB < 0 ){ // \upperbound(x_i) < \lowerbound(x_i) @@ -802,7 +802,7 @@ bool TheoryArithPrivate::AssertUpper(ConstraintP constraint){ ++(d_statistics.d_statDisequalityConflicts); raiseConflict(eq); return true; - } + } } }else if(cmpToLB > 0){ // l <= x <= u and l < u @@ -1291,8 +1291,10 @@ void TheoryArithPrivate::setupVariableList(const VarList& vl){ }else{ if (d_nonlinearExtension == nullptr) { - if( vlNode.getKind()==kind::EXPONENTIAL || vlNode.getKind()==kind::SINE || - vlNode.getKind()==kind::COSINE || vlNode.getKind()==kind::TANGENT ){ + if (vlNode.getKind() == kind::EXPONENTIAL + || vlNode.getKind() == kind::SINE || vlNode.getKind() == kind::COSINE + || vlNode.getKind() == kind::TANGENT) + { d_nlIncomplete = true; } } @@ -1737,7 +1739,6 @@ ConstraintP TheoryArithPrivate::constraintFromFactQueue(){ } else { Debug("arith::constraint") << "already has proof: " << constraint->externalExplainByAssertions() << endl; } - if(Debug.isOn("arith::negatedassumption") && inConflict){ ConstraintP negation = constraint->getNegation(); @@ -1905,7 +1906,7 @@ void TheoryArithPrivate::outputConflicts(){ Debug("arith::conflict") << "outputting conflicts" << std::endl; Assert(anyConflict()); static unsigned int conflicts = 0; - + if(!conflictQueueEmpty()){ Assert(!d_conflicts.empty()); for(size_t i = 0, i_end = d_conflicts.size(); i < i_end; ++i){ @@ -1923,35 +1924,6 @@ void TheoryArithPrivate::outputConflicts(){ ++conflicts; Debug("arith::conflict") << "d_conflicts[" << i << "] " << conflict << " has proof: " << hasProof << endl; - PROOF(if (d_containing.d_proofRecorder && confConstraint->hasFarkasProof() - && pf.d_farkasCoefficients->size() - == conflict.getNumChildren()) { - // The Farkas coefficients and the children of `conflict` seem to be in - // opposite orders... There is some relevant documentation in the - // comment for the d_farkasCoefficients field in "constraint.h" - // - // Anyways, we reverse the children in `conflict` here. - NodeBuilder<> conflictInFarkasCoefficientOrder(kind::AND); - for (size_t j = 0, nchildren = conflict.getNumChildren(); j < nchildren; - ++j) - { - conflictInFarkasCoefficientOrder - << conflict[conflict.getNumChildren() - j - 1]; - } - - if (Debug.isOn("arith::pf::tree")) { - confConstraint->printProofTree(Debug("arith::pf::tree")); - confConstraint->getNegation()->printProofTree(Debug("arith::pf::tree")); - } - - Assert(conflict.getNumChildren() == pf.d_farkasCoefficients->size()); - if (confConstraint->hasSimpleFarkasProof() - && confConstraint->getNegation()->isPossiblyTightenedAssumption()) - { - d_containing.d_proofRecorder->saveFarkasCoefficients( - conflictInFarkasCoefficientOrder, pf.d_farkasCoefficients); - } - }) if(Debug.isOn("arith::normalize::external")){ conflict = flattenAndSort(conflict); Debug("arith::conflict") << "(normalized to) " << conflict << endl; @@ -2190,7 +2162,6 @@ std::pair<ConstraintP, ArithVar> TheoryArithPrivate::replayGetConstraint(const D return make_pair(imp, added); } } - ConstraintP newc = d_constraintDatabase.getConstraint(v, t, dr); d_replayConstraints.push_back(newc); @@ -2337,7 +2308,7 @@ void TheoryArithPrivate::tryBranchCut(ApproximateSimplex* approx, int nid, Branc // ConstraintCPVec& back = conflicts.back(); // back.push_back(conflicting); // back.push_back(negConflicting); - + // // remove the floor/ceiling contraint implied by bcneg // Constraint::assertionFringe(back); } @@ -2375,14 +2346,15 @@ void TheoryArithPrivate::replayAssert(ConstraintP c) { }else{ Debug("approx::replayAssert") << "replayAssert " << c << " has explanation" << endl; } - Debug("approx::replayAssert") << "replayAssertion " << c << endl; + Debug("approx::replayAssert") << "replayAssertion " << c << endl; if(inConflict){ raiseConflict(c); }else{ assertionCases(c); } }else{ - Debug("approx::replayAssert") << "replayAssert " << c << " already asserted" << endl; + Debug("approx::replayAssert") + << "replayAssert " << c << " already asserted" << endl; } } @@ -2551,7 +2523,7 @@ std::vector<ConstraintCPVec> TheoryArithPrivate::replayLogRec(ApproximateSimplex SimplexDecisionProcedure& simplex = selectSimplex(true); simplex.findModel(false); - // can change d_qflraStatus + // can change d_qflraStatus d_linEq.stopTrackingBoundCounts(); d_partialModel.startQueueingBoundCounts(); @@ -3101,13 +3073,13 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ << " " << useApprox << " " << safeToCallApprox() << endl; - + bool noPivotLimitPass1 = noPivotLimit && !useApprox; d_qflraStatus = simplex.findModel(noPivotLimitPass1); Debug("TheoryArithPrivate::solveRealRelaxation") << "solveRealRelaxation()" << " pass1 " << d_qflraStatus << endl; - + if(d_qflraStatus == Result::SAT_UNKNOWN && useApprox && safeToCallApprox()){ // pass2: fancy-final static const int32_t relaxationLimit = 10000; @@ -3275,7 +3247,6 @@ bool TheoryArithPrivate::solveRealRelaxation(Theory::Effort effortLevel){ // if(!useFancyFinal){ // d_qflraStatus = simplex.findModel(noPivotLimit); // }else{ - // if(d_qflraStatus == Result::SAT_UNKNOWN){ // //Message() << "got sat unknown" << endl; @@ -3711,7 +3682,7 @@ Node TheoryArithPrivate::branchIntegerVariable(ArithVar x) const { Integer ceil_d = d.ceiling(); Rational f = r - floor_d; // Multiply by -1 to get abs value. - Rational c = (r - ceil_d) * (-1); + Rational c = (r - ceil_d) * (-1); Integer nearest = (c > f) ? floor_d : ceil_d; // Prioritize trying a simple rounding of the real solution first, @@ -4140,8 +4111,8 @@ bool TheoryArithPrivate::collectModelInfo(TheoryModel* m) Debug("arith::collectModelInfo") << "collectModelInfo() begin " << endl; std::set<Node> termSet; - d_containing.computeRelevantTerms(termSet); - + const std::set<Kind>& irrKinds = m->getIrrelevantKinds(); + d_containing.computeAssertedTerms(termSet, irrKinds, true); // Delta lasts at least the duration of the function call const Rational& delta = d_partialModel.getDelta(); @@ -4372,7 +4343,7 @@ bool TheoryArithPrivate::propagateCandidateBound(ArithVar basic, bool upperBound //We are only going to recreate the functionality for now. //In the future this can be improved to generate a temporary constraint //if none exists. - //Experiment with doing this everytime or only when the new constraint + //Experiment with doing this every time or only when the new constraint //implies an unknown fact. ConstraintType t = upperBound ? UpperBound : LowerBound; @@ -4651,7 +4622,6 @@ bool TheoryArithPrivate::tryToPropagate(RowIndex ridx, bool rowUp, ArithVar v, b ConstraintP implied = d_constraintDatabase.getBestImpliedBound(v, t, bound); if(implied != NullConstraint){ - return rowImplicationCanBeApplied(ridx, rowUp, implied); } } @@ -4699,9 +4669,8 @@ bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, C if( !assertedToTheTheory && canBePropagated && !hasProof ){ ConstraintCPVec explain; - - PROOF(d_farkasBuffer.clear()); - RationalVectorP coeffs = NULLPROOF(&d_farkasBuffer); + ARITH_PROOF(d_farkasBuffer.clear()); + RationalVectorP coeffs = ARITH_NULLPROOF(&d_farkasBuffer); // After invoking `propegateRow`: // * coeffs[0] is for implied @@ -4716,38 +4685,6 @@ bool TheoryArithPrivate::rowImplicationCanBeApplied(RowIndex ridx, bool rowUp, C } Node implication = implied->externalImplication(explain); Node clause = flattenImplication(implication); - PROOF(if (d_containing.d_proofRecorder - && coeffs != RationalVectorCPSentinel - && coeffs->size() == clause.getNumChildren()) { - Debug("arith::prop") << "implied : " << implied << std::endl; - Debug("arith::prop") << "implication: " << implication << std::endl; - Debug("arith::prop") << "coeff len: " << coeffs->size() << std::endl; - Debug("arith::prop") << "exp : " << explain << std::endl; - Debug("arith::prop") << "clause : " << clause << std::endl; - Debug("arith::prop") - << "clause len: " << clause.getNumChildren() << std::endl; - Debug("arith::prop") << "exp len: " << explain.size() << std::endl; - // Using the information from the above comment we assemble a conflict - // AND in coefficient order - NodeBuilder<> conflictInFarkasCoefficientOrder(kind::AND); - conflictInFarkasCoefficientOrder << implication[1].negate(); - for (const Node& antecedent : implication[0]) - { - Debug("arith::prop") << " ante: " << antecedent << std::endl; - conflictInFarkasCoefficientOrder << antecedent; - } - - Assert(coeffs != RationalVectorPSentinel); - Assert(conflictInFarkasCoefficientOrder.getNumChildren() - == coeffs->size()); - if (std::all_of(explain.begin(), explain.end(), [](ConstraintCP c) { - return c->isAssumption() || c->hasIntTightenProof(); - })) - { - d_containing.d_proofRecorder->saveFarkasCoefficients( - conflictInFarkasCoefficientOrder, coeffs); - } - }) outputLemma(clause); }else{ Assert(!implied->negationHasProof()); diff --git a/src/theory/arrays/array_proof_reconstruction.cpp b/src/theory/arrays/array_proof_reconstruction.cpp deleted file mode 100644 index abc4857e8..000000000 --- a/src/theory/arrays/array_proof_reconstruction.cpp +++ /dev/null @@ -1,199 +0,0 @@ -/********************* */ -/*! \file array_proof_reconstruction.cpp - ** \verbatim - ** Top contributors (to current version): - ** Guy Katz, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** [[ Add lengthier description here ]] - - ** \todo document this file - -**/ - -#include "theory/arrays/array_proof_reconstruction.h" - -#include <memory> - -namespace CVC4 { -namespace theory { -namespace arrays { - -ArrayProofReconstruction::ArrayProofReconstruction(const eq::EqualityEngine* equalityEngine) - : d_equalityEngine(equalityEngine) { -} - -void ArrayProofReconstruction::setRowMergeTag(unsigned tag) { - d_reasonRow = tag; -} - -void ArrayProofReconstruction::setRow1MergeTag(unsigned tag) { - d_reasonRow1 = tag; -} - -void ArrayProofReconstruction::setExtMergeTag(unsigned tag) { - d_reasonExt = tag; -} - -void ArrayProofReconstruction::notify( - unsigned reasonType, Node reason, Node a, Node b, - std::vector<TNode>& equalities, eq::EqProof* proof) const { - Debug("pf::array") << "ArrayProofReconstruction::notify( " - << reason << ", " << a << ", " << b << std::endl; - - - if (reasonType == d_reasonExt) { - if (proof) { - // Todo: here we assume that a=b is an assertion. We should probably call - // explain() recursively, to explain this. - std::shared_ptr<eq::EqProof> childProof = std::make_shared<eq::EqProof>(); - childProof->d_node = reason; - proof->d_children.push_back(childProof); - } - } - - else if (reasonType == d_reasonRow) { - // ROW rules mean that (i==k) OR ((a[i]:=t)[k] == a[k]) - // The equality here will be either (i == k) because ((a[i]:=t)[k] != a[k]), - // or ((a[i]:=t)[k] == a[k]) because (i != k). - - if (proof) { - if (a.getKind() == kind::SELECT) { - // This is the case of ((a[i]:=t)[k] == a[k]) because (i != k). - - // The edge is ((a[i]:=t)[k], a[k]), or (a[k], (a[i]:=t)[k]). This flag should be - // false in the first case and true in the second case. - bool currentNodeIsUnchangedArray; - - Assert(a.getNumChildren() == 2); - Assert(b.getNumChildren() == 2); - - if (a[0].getKind() == kind::VARIABLE || a[0].getKind() == kind::SKOLEM) { - currentNodeIsUnchangedArray = true; - } else if (b[0].getKind() == kind::VARIABLE || b[0].getKind() == kind::SKOLEM) { - currentNodeIsUnchangedArray = false; - } else { - Assert(a[0].getKind() == kind::STORE); - Assert(b[0].getKind() == kind::STORE); - - if (a[0][0] == b[0]) { - currentNodeIsUnchangedArray = false; - } else if (b[0][0] == a[0]) { - currentNodeIsUnchangedArray = true; - } else { - Unreachable(); - } - } - - Node indexOne = currentNodeIsUnchangedArray ? a[1] : a[0][1]; - Node indexTwo = currentNodeIsUnchangedArray ? b[0][1] : b[1]; - - // Some assertions to ensure that the theory of arrays behaves as expected - Assert(a[1] == b[1]); - if (currentNodeIsUnchangedArray) { - Assert(a[0] == b[0][0]); - } else { - Assert(a[0][0] == b[0]); - } - - Debug("pf::ee") << "Getting explanation for ROW guard: " - << indexOne << " != " << indexTwo << std::endl; - - std::shared_ptr<eq::EqProof> childProof = - std::make_shared<eq::EqProof>(); - d_equalityEngine->explainEquality(indexOne, indexTwo, false, equalities, - childProof.get()); - - // It could be that the guard condition is a constant disequality. In - // this case, we need to change it to a different format. - bool haveNegChild = false; - for (unsigned i = 0; i < childProof->d_children.size(); ++i) { - if (childProof->d_children[i]->d_node.getKind() == kind::NOT) - haveNegChild = true; - } - - if ((childProof->d_children.size() != 0) && - (childProof->d_id == theory::eq::MERGED_THROUGH_CONSTANTS || !haveNegChild)) { - // The proof has two children, explaining why each index is a (different) constant. - Assert(childProof->d_children.size() == 2); - - Node constantOne, constantTwo; - // Each subproof explains why one of the indices is constant. - - if (childProof->d_children[0]->d_id == theory::eq::MERGED_THROUGH_REFLEXIVITY) { - constantOne = childProof->d_children[0]->d_node; - } else { - Assert(childProof->d_children[0]->d_node.getKind() == kind::EQUAL); - if ((childProof->d_children[0]->d_node[0] == indexOne) || - (childProof->d_children[0]->d_node[0] == indexTwo)) { - constantOne = childProof->d_children[0]->d_node[1]; - } else { - constantOne = childProof->d_children[0]->d_node[0]; - } - } - - if (childProof->d_children[1]->d_id == theory::eq::MERGED_THROUGH_REFLEXIVITY) { - constantTwo = childProof->d_children[1]->d_node; - } else { - Assert(childProof->d_children[1]->d_node.getKind() == kind::EQUAL); - if ((childProof->d_children[1]->d_node[0] == indexOne) || - (childProof->d_children[1]->d_node[0] == indexTwo)) { - constantTwo = childProof->d_children[1]->d_node[1]; - } else { - constantTwo = childProof->d_children[1]->d_node[0]; - } - } - - std::shared_ptr<eq::EqProof> constantDisequalityProof = - std::make_shared<eq::EqProof>(); - constantDisequalityProof->d_id = theory::eq::MERGED_THROUGH_CONSTANTS; - constantDisequalityProof->d_node = - NodeManager::currentNM()->mkNode(kind::EQUAL, constantOne, constantTwo).negate(); - - // Middle is where we need to insert the new disequality - std::vector<std::shared_ptr<eq::EqProof>>::iterator middle = - childProof->d_children.begin(); - ++middle; - - childProof->d_children.insert(middle, constantDisequalityProof); - - childProof->d_id = theory::eq::MERGED_THROUGH_TRANS; - childProof->d_node = - NodeManager::currentNM()->mkNode(kind::EQUAL, indexOne, indexTwo).negate(); - } - - proof->d_children.push_back(childProof); - } else { - // This is the case of (i == k) because ((a[i]:=t)[k] != a[k]), - - Node indexOne = a; - Node indexTwo = b; - - Debug("pf::ee") << "The two indices are: " << indexOne << ", " << indexTwo << std::endl - << "The reason for the edge is: " << reason << std::endl; - - Assert(reason.getNumChildren() == 2); - Debug("pf::ee") << "Getting explanation for ROW guard: " << reason[1] << std::endl; - - std::shared_ptr<eq::EqProof> childProof = - std::make_shared<eq::EqProof>(); - d_equalityEngine->explainEquality(reason[1][0], reason[1][1], false, - equalities, childProof.get()); - proof->d_children.push_back(childProof); - } - } - - } - - else if (reasonType == d_reasonRow1) { - // No special handling required at this time - } -} - -}/* CVC4::theory::arrays namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ diff --git a/src/theory/arrays/array_proof_reconstruction.h b/src/theory/arrays/array_proof_reconstruction.h deleted file mode 100644 index a73b5dd08..000000000 --- a/src/theory/arrays/array_proof_reconstruction.h +++ /dev/null @@ -1,59 +0,0 @@ -/********************* */ -/*! \file array_proof_reconstruction.h - ** \verbatim - ** Top contributors (to current version): - ** Paul Meng, Mathias Preiner, Tim King - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Array-specific proof construction logic to be used during the - ** equality engine's path reconstruction - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__THEORY__ARRAYS__ARRAY_PROOF_RECONSTRUCTION_H -#define CVC4__THEORY__ARRAYS__ARRAY_PROOF_RECONSTRUCTION_H - -#include "theory/uf/equality_engine.h" - -namespace CVC4 { -namespace theory { -namespace arrays { - -/** - * A callback class to be invoked whenever the equality engine traverses - * an "array-owned" edge during path reconstruction. - */ - -class ArrayProofReconstruction : public eq::PathReconstructionNotify { -public: - ArrayProofReconstruction(const eq::EqualityEngine* equalityEngine); - - void notify(unsigned reasonType, Node reason, Node a, Node b, - std::vector<TNode>& equalities, - eq::EqProof* proof) const override; - - void setRowMergeTag(unsigned tag); - void setRow1MergeTag(unsigned tag); - void setExtMergeTag(unsigned tag); - -private: - /** Merge tag for ROW applications */ - unsigned d_reasonRow; - /** Merge tag for ROW1 applications */ - unsigned d_reasonRow1; - /** Merge tag for EXT applications */ - unsigned d_reasonExt; - - const eq::EqualityEngine* d_equalityEngine; -}; /* class ArrayProofReconstruction */ - -}/* CVC4::theory::arrays namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /* CVC4__THEORY__ARRAYS__ARRAY_PROOF_RECONSTRUCTION_H */ diff --git a/src/theory/arrays/static_fact_manager.cpp b/src/theory/arrays/static_fact_manager.cpp deleted file mode 100644 index d2f4b75c9..000000000 --- a/src/theory/arrays/static_fact_manager.cpp +++ /dev/null @@ -1,170 +0,0 @@ -/********************* */ -/*! \file static_fact_manager.cpp - ** \verbatim - ** Top contributors (to current version): - ** Clark Barrett, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Path-compressing, backtrackable union-find using an undo - ** stack. Refactored from the UF union-find. - ** - ** Path-compressing, backtrackable union-find using an undo stack - ** rather than storing items in a CDMap<>. - **/ - -#include <iostream> - -#include "base/check.h" -#include "expr/node.h" -#include "theory/arrays/static_fact_manager.h" - -using namespace std; - -namespace CVC4 { -namespace theory { -namespace arrays { - -bool StaticFactManager::areEq(TNode a, TNode b) { - return (find(a) == find(b)); -} - -bool StaticFactManager::areDiseq(TNode a, TNode b) { - Node af = find(a); - Node bf = find(b); - Node left, right; - unsigned i; - for (i = 0; i < d_diseq.size(); ++i) { - left = find(d_diseq[i][0]); - right = find(d_diseq[i][1]); - if ((left == af && right == bf) || - (left == bf && right == af)) { - return true; - } - } - return false; -} - -void StaticFactManager::addDiseq(TNode eq) { - Assert(eq.getKind() == kind::EQUAL); - d_diseq.push_back(eq); -} - -void StaticFactManager::addEq(TNode eq) { - Assert(eq.getKind() == kind::EQUAL); - Node a = find(eq[0]); - Node b = find(eq[1]); - - if( a == b) { - return; - } - - /* - * take care of the congruence closure part - */ - - // make "a" the one with shorter diseqList - // CNodeTNodesMap::iterator deq_ia = d_disequalities.find(a); - // CNodeTNodesMap::iterator deq_ib = d_disequalities.find(b); - - // if(deq_ia != d_disequalities.end()) { - // if(deq_ib == d_disequalities.end() || - // (*deq_ia).second->size() > (*deq_ib).second->size()) { - // TNode tmp = a; - // a = b; - // b = tmp; - // } - // } - // a = find(a); - // b = find(b); - - - // b becomes the canon of a - setCanon(a, b); - - // deq_ia = d_disequalities.find(a); - // map<TNode, TNode> alreadyDiseqs; - // if(deq_ia != d_disequalities.end()) { - // /* - // * Collecting the disequalities of b, no need to check for conflicts - // * since the representative of b does not change and we check all the things - // * in a's class when we look at the diseq list of find(a) - // */ - - // CNodeTNodesMap::iterator deq_ib = d_disequalities.find(b); - // if(deq_ib != d_disequalities.end()) { - // CTNodeListAlloc* deq = (*deq_ib).second; - // for(CTNodeListAlloc::const_iterator j = deq->begin(); j!=deq->end(); - // j++) { - // TNode deqn = *j; - // TNode s = deqn[0]; - // TNode t = deqn[1]; - // TNode sp = find(s); - // TNode tp = find(t); - // Assert(sp == b || tp == b); - // if(sp == b) { - // alreadyDiseqs[tp] = deqn; - // } else { - // alreadyDiseqs[sp] = deqn; - // } - // } - // } - - // /* - // * Looking for conflicts in the a disequality list. Note - // * that at this point a and b are already merged. Also has - // * the side effect that it adds them to the list of b (which - // * became the canonical representative) - // */ - - // CTNodeListAlloc* deqa = (*deq_ia).second; - // for(CTNodeListAlloc::const_iterator i = deqa->begin(); i!= deqa->end(); - // i++) { - // TNode deqn = (*i); - // Assert(deqn.getKind() == kind::EQUAL || deqn.getKind() == - // kind::IFF); TNode s = deqn[0]; TNode t = deqn[1]; TNode sp = find(s); - // TNode tp = find(t); - - // if(find(s) == find(t)) { - // d_conflict = deqn; - // return; - // } - // Assert( sp == b || tp == b); - - // // make sure not to add duplicates - - // if(sp == b) { - // if(alreadyDiseqs.find(tp) == alreadyDiseqs.end()) { - // appendToDiseqList(b, deqn); - // alreadyDiseqs[tp] = deqn; - // } - // } else { - // if(alreadyDiseqs.find(sp) == alreadyDiseqs.end()) { - // appendToDiseqList(b, deqn); - // alreadyDiseqs[sp] = deqn; - // } - // } - - // } - // } - - // // TODO: check for equality propagations - // // a and b are find(a) and find(b) here - // checkPropagations(a,b); - - // if(a.getType().isArray()) { - // checkRowLemmas(a,b); - // checkRowLemmas(b,a); - // // note the change in order, merge info adds the list of - // // the 2nd argument to the first - // d_infoMap.mergeInfo(b, a); - // } -} - - -}/* CVC4::theory::arrays namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ diff --git a/src/theory/arrays/static_fact_manager.h b/src/theory/arrays/static_fact_manager.h deleted file mode 100644 index 2df4b0fda..000000000 --- a/src/theory/arrays/static_fact_manager.h +++ /dev/null @@ -1,116 +0,0 @@ -/********************* */ -/*! \file static_fact_manager.h - ** \verbatim - ** Top contributors (to current version): - ** Clark Barrett, Mathias Preiner, Morgan Deters - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Path-compressing, backtrackable union-find using an undo - ** stack. Refactored from the UF union-find. - ** - ** Path-compressing, backtrackable union-find using an undo stack - ** rather than storing items in a CDMap<>. - **/ - -#include "cvc4_private.h" - -#ifndef CVC4__THEORY__ARRAYS__STATIC_FACT_MANAGER_H -#define CVC4__THEORY__ARRAYS__STATIC_FACT_MANAGER_H - -#include <utility> -#include <vector> -#include <unordered_map> - -#include "expr/node.h" - -namespace CVC4 { -namespace theory { -namespace arrays { - - class StaticFactManager { - /** Our underlying map type. */ - typedef std::unordered_map<Node, Node, NodeHashFunction> MapType; - - /** - * Our map of Nodes to their canonical representatives. - * If a Node is not present in the map, it is its own - * representative. - */ - MapType d_map; - std::vector<Node> d_diseq; - -public: - StaticFactManager() {} - - /** - * Return a Node's union-find representative, doing path compression. - */ - inline TNode find(TNode n); - - /** - * Return a Node's union-find representative, NOT doing path compression. - * This is useful for Assert() statements, debug checking, and similar - * things that you do NOT want to mutate the structure. - */ - inline TNode debugFind(TNode n) const; - - /** - * Set the canonical representative of n to newParent. They should BOTH - * be their own canonical representatives on entry to this funciton. - */ - inline void setCanon(TNode n, TNode newParent); - - bool areEq(TNode a, TNode b); - bool areDiseq(TNode a, TNode b); - void addDiseq(TNode eq); - void addEq(TNode eq); - -};/* class StaticFactManager<> */ - -inline TNode StaticFactManager::debugFind(TNode n) const { - MapType::const_iterator i = d_map.find(n); - if(i == d_map.end()) { - return n; - } else { - return debugFind((*i).second); - } -} - -inline TNode StaticFactManager::find(TNode n) { - Trace("arraysuf") << "arraysUF find of " << n << std::endl; - MapType::iterator i = d_map.find(n); - if(i == d_map.end()) { - Trace("arraysuf") << "arraysUF it is rep" << std::endl; - return n; - } else { - Trace("arraysuf") << "arraysUF not rep: par is " << (*i).second << std::endl; - std::pair<TNode, TNode> pr = *i; - // our iterator is invalidated by the recursive call to find(), - // since it mutates the map - TNode p = find(pr.second); - if(p == pr.second) { - return p; - } - pr.second = p; - d_map.insert(pr); - return p; - } -} - -inline void StaticFactManager::setCanon(TNode n, TNode newParent) { - Assert(d_map.find(n) == d_map.end()); - Assert(d_map.find(newParent) == d_map.end()); - if(n != newParent) { - d_map[n] = newParent; - } -} - -}/* CVC4::theory::arrays namespace */ -}/* CVC4::theory namespace */ -}/* CVC4 namespace */ - -#endif /*CVC4__THEORY__ARRAYS__STATIC_FACT_MANAGER_H */ diff --git a/src/theory/arrays/theory_arrays.cpp b/src/theory/arrays/theory_arrays.cpp index b4a234748..408f4c682 100644 --- a/src/theory/arrays/theory_arrays.cpp +++ b/src/theory/arrays/theory_arrays.cpp @@ -23,9 +23,6 @@ #include "expr/node_algorithm.h" #include "options/arrays_options.h" #include "options/smt_options.h" -#include "proof/array_proof.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" #include "smt/command.h" #include "smt/logic_exception.h" #include "smt/smt_statistics_registry.h" @@ -46,7 +43,6 @@ namespace arrays { // Use static configuration of options for now const bool d_ccStore = false; -const bool d_useArrTable = false; //const bool d_eagerLemmas = false; const bool d_preprocess = true; const bool d_solveWrite = true; @@ -88,7 +84,6 @@ TheoryArrays::TheoryArrays(context::Context* c, d_isPreRegistered(c), d_mayEqualEqualityEngine(c, name + "theory::arrays::mayEqual", true), d_notify(*this), - d_conflict(c, false), d_backtracker(c), d_infoMap(c, &d_backtracker, name), d_mergeQueue(c), @@ -111,7 +106,6 @@ TheoryArrays::TheoryArrays(context::Context* c, d_readTableContext(new context::Context()), d_arrayMerges(c), d_inCheckModel(false), - d_proofReconstruction(nullptr), d_dstrat(new TheoryArraysDecisionStrategy(this)), d_dstratInit(false) { @@ -179,26 +173,6 @@ void TheoryArrays::finishInit() { d_equalityEngine->addFunctionKind(kind::STORE); } - if (d_useArrTable) - { - d_equalityEngine->addFunctionKind(kind::ARR_TABLE_FUN); - } - - d_proofReconstruction.reset(new ArrayProofReconstruction(d_equalityEngine)); - d_reasonRow = d_equalityEngine->getFreshMergeReasonType(); - d_reasonRow1 = d_equalityEngine->getFreshMergeReasonType(); - d_reasonExt = d_equalityEngine->getFreshMergeReasonType(); - - d_proofReconstruction->setRowMergeTag(d_reasonRow); - d_proofReconstruction->setRow1MergeTag(d_reasonRow1); - d_proofReconstruction->setExtMergeTag(d_reasonExt); - - d_equalityEngine->addPathReconstructionTrigger(d_reasonRow, - d_proofReconstruction.get()); - d_equalityEngine->addPathReconstructionTrigger(d_reasonRow1, - d_proofReconstruction.get()); - d_equalityEngine->addPathReconstructionTrigger(d_reasonExt, - d_proofReconstruction.get()); } ///////////////////////////////////////////////////////////////////////////// @@ -421,7 +395,8 @@ bool TheoryArrays::propagateLit(TNode literal) << std::endl; // If already in conflict, no more propagation - if (d_conflict) { + if (d_state.isInConflict()) + { Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::propagateLit(" << literal << "): already in conflict" << std::endl; @@ -434,43 +409,12 @@ bool TheoryArrays::propagateLit(TNode literal) } bool ok = d_out->propagate(literal); if (!ok) { - d_conflict = true; + d_state.notifyInConflict(); } return ok; }/* TheoryArrays::propagate(TNode) */ -void TheoryArrays::explain(TNode literal, std::vector<TNode>& assumptions, - eq::EqProof* proof) { - // Do the work - bool polarity = literal.getKind() != kind::NOT; - TNode atom = polarity ? literal : literal[0]; - //eq::EqProof * eqp = new eq::EqProof; - // eq::EqProof * eqp = NULL; - if (atom.getKind() == kind::EQUAL) { - d_equalityEngine->explainEquality( - atom[0], atom[1], polarity, assumptions, proof); - } else { - d_equalityEngine->explainPredicate(atom, polarity, assumptions, proof); - } - if (Debug.isOn("pf::array")) - { - if (proof) - { - Debug("pf::array") << " Proof is : " << std::endl; - proof->debug_print("pf::array"); - } - - Debug("pf::array") << "Array: explain( " << literal << " ):" << std::endl - << "\t"; - for (unsigned i = 0; i < assumptions.size(); ++i) - { - Debug("pf::array") << assumptions[i] << " "; - } - Debug("pf::array") << std::endl; - } -} - TNode TheoryArrays::weakEquivGetRep(TNode node) { TNode pointer; while (true) { @@ -695,7 +639,8 @@ void TheoryArrays::checkWeakEquiv(bool arraysMerged) { */ void TheoryArrays::preRegisterTermInternal(TNode node) { - if (d_conflict) { + if (d_state.isInConflict()) + { return; } Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::preRegisterTerm(" << node << ")" << std::endl; @@ -795,7 +740,8 @@ void TheoryArrays::preRegisterTermInternal(TNode node) } // Apply RIntro1 Rule - d_equalityEngine->assertEquality(ni.eqNode(v), true, d_true, d_reasonRow1); + d_equalityEngine->assertEquality( + ni.eqNode(v), true, d_true, theory::eq::MERGED_THROUGH_ROW1); d_infoMap.addStore(node, node); d_infoMap.addInStore(a, node); @@ -838,7 +784,6 @@ void TheoryArrays::preRegisterTermInternal(TNode node) // The may equal needs the node d_mayEqualEqualityEngine.addTerm(node); d_equalityEngine->addTerm(node); - Assert(d_equalityEngine->getSize(node) == 1); } else { d_equalityEngine->addTerm(node); @@ -852,7 +797,6 @@ void TheoryArrays::preRegisterTermInternal(TNode node) // !d_equalityEngine->consistent()); } - void TheoryArrays::preRegisterTerm(TNode node) { preRegisterTermInternal(node); @@ -864,19 +808,32 @@ void TheoryArrays::preRegisterTerm(TNode node) } } -TrustNode TheoryArrays::explain(TNode literal) +void TheoryArrays::explain(TNode literal, Node& explanation) { - Node explanation = explain(literal, NULL); - return TrustNode::mkTrustPropExp(literal, explanation, nullptr); -} - -Node TheoryArrays::explain(TNode literal, eq::EqProof* proof) { ++d_numExplain; Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::explain(" << literal << ")" << std::endl; std::vector<TNode> assumptions; - explain(literal, assumptions, proof); - return mkAnd(assumptions); + // Do the work + bool polarity = literal.getKind() != kind::NOT; + TNode atom = polarity ? literal : literal[0]; + if (atom.getKind() == kind::EQUAL) + { + d_equalityEngine->explainEquality( + atom[0], atom[1], polarity, assumptions, nullptr); + } + else + { + d_equalityEngine->explainPredicate(atom, polarity, assumptions, nullptr); + } + explanation = mkAnd(assumptions); +} + +TrustNode TheoryArrays::explain(TNode literal) +{ + Node explanation; + explain(literal, explanation); + return TrustNode::mkTrustPropExp(literal, explanation, nullptr); } ///////////////////////////////////////////////////////////////////////////// @@ -900,23 +857,6 @@ void TheoryArrays::notifySharedTerm(TNode t) } } - -EqualityStatus TheoryArrays::getEqualityStatus(TNode a, TNode b) { - Assert(d_equalityEngine->hasTerm(a) && d_equalityEngine->hasTerm(b)); - if (d_equalityEngine->areEqual(a, b)) - { - // The terms are implied to be equal - return EQUALITY_TRUE; - } - else if (d_equalityEngine->areDisequal(a, b, false)) - { - // The terms are implied to be dis-equal - return EQUALITY_FALSE; - } - return EQUALITY_UNKNOWN;//FALSE_IN_MODEL; -} - - void TheoryArrays::checkPair(TNode r1, TNode r2) { Debug("arrays::sharing") << "TheoryArrays::computeCareGraph(): checking reads " << r1 << " and " << r2 << std::endl; @@ -1090,22 +1030,15 @@ void TheoryArrays::computeCareGraph() // MODEL GENERATION ///////////////////////////////////////////////////////////////////////////// -bool TheoryArrays::collectModelInfo(TheoryModel* m) +bool TheoryArrays::collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) { - // Compute terms appearing in assertions and shared terms, and also - // include additional reads due to the RIntro1 and RIntro2 rules. - std::set<Node> termSet; - computeRelevantTerms(termSet); - - // Send the equality engine information to the model - if (!m->assertEqualityEngine(d_equalityEngine, &termSet)) - { - return false; - } - + // termSet contains terms appearing in assertions and shared terms, and also + // includes additional reads due to the RIntro1 and RIntro2 rules. NodeManager* nm = NodeManager::currentNM(); // Compute arrays that we need to produce representatives for std::vector<Node> arrays; + eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine); for (; !eqcs_i.isFinished(); ++eqcs_i) { Node eqc = (*eqcs_i); @@ -1158,12 +1091,14 @@ bool TheoryArrays::collectModelInfo(TheoryModel* m) //} // Loop through all array equivalence classes that need a representative computed - for (size_t i=0; i<arrays.size(); ++i) { - TNode n = arrays[i]; - TNode nrep = d_equalityEngine->getRepresentative(n); + for (size_t i = 0; i < arrays.size(); ++i) + { + TNode n = arrays[i]; + TNode nrep = d_equalityEngine->getRepresentative(n); - //if (fullModel) { - // Compute default value for this array - there is one default value for every mayEqual equivalence class + // if (fullModel) { + // Compute default value for this array - there is one default value for + // every mayEqual equivalence class TNode mayRep = d_mayEqualEqualityEngine.getRepresentative(nrep); it = d_defValues.find(mayRep); // If this mayEqual EC doesn't have a default value associated, get the next available default value for the associated array element type @@ -1265,7 +1200,7 @@ Node TheoryArrays::getSkolem(TNode ref, const string& name, const TypeNode& type Node d = skolem.eqNode(ref); Debug("arrays-model-based") << "Asserting skolem equality " << d << endl; d_equalityEngine->assertEquality(d, true, d_true); - Assert(!d_conflict); + Assert(!d_state.isInConflict()); d_skolemAssertions.push_back(d); d_skolemIndex = d_skolemIndex + 1; } @@ -1274,145 +1209,11 @@ Node TheoryArrays::getSkolem(TNode ref, const string& name, const TypeNode& type return skolem; } - -void TheoryArrays::check(Effort e) { - if (done() && !fullEffort(e)) { - return; - } - - getOutputChannel().spendResource(ResourceManager::Resource::TheoryCheckStep); - - TimerStat::CodeTimer checkTimer(d_checkTime); - - while (!done() && !d_conflict) +void TheoryArrays::postCheck(Effort level) +{ + if ((options::arraysEagerLemmas() || fullEffort(level)) + && !d_state.isInConflict() && options::arraysWeakEquivalence()) { - // Get all the assertions - Assertion assertion = get(); - TNode fact = assertion.d_assertion; - - Debug("arrays") << spaces(getSatContext()->getLevel()) << "TheoryArrays::check(): processing " << fact << std::endl; - - bool polarity = fact.getKind() != kind::NOT; - TNode atom = polarity ? fact : fact[0]; - - if (!assertion.d_isPreregistered) - { - if (atom.getKind() == kind::EQUAL) { - if (!d_equalityEngine->hasTerm(atom[0])) - { - Assert(atom[0].isConst()); - d_equalityEngine->addTerm(atom[0]); - } - if (!d_equalityEngine->hasTerm(atom[1])) - { - Assert(atom[1].isConst()); - d_equalityEngine->addTerm(atom[1]); - } - } - } - - // Do the work - switch (fact.getKind()) { - case kind::EQUAL: - d_equalityEngine->assertEquality(fact, true, fact); - break; - case kind::SELECT: - d_equalityEngine->assertPredicate(fact, true, fact); - break; - case kind::NOT: - if (fact[0].getKind() == kind::SELECT) { - d_equalityEngine->assertPredicate(fact[0], false, fact); - } - else if (!d_equalityEngine->areDisequal(fact[0][0], fact[0][1], false)) - { - // Assert the dis-equality - d_equalityEngine->assertEquality(fact[0], false, fact); - - // Apply ArrDiseq Rule if diseq is between arrays - if(fact[0][0].getType().isArray() && !d_conflict) { - if (d_conflict) { Debug("pf::array") << "Entering the skolemization branch" << std::endl; } - - NodeManager* nm = NodeManager::currentNM(); - TypeNode indexType = fact[0][0].getType()[0]; - - TNode k; - // k is the skolem for this disequality. - if (!d_proofsEnabled) { - Debug("pf::array") << "Check: kind::NOT: array theory making a skolem" << std::endl; - - // If not in replay mode, generate a fresh skolem variable - k = getSkolem(fact, - "array_ext_index", - indexType, - "an extensional lemma index variable from the theory of arrays", - false); - - // Register this skolem for the proof replay phase - PROOF(ProofManager::getSkolemizationManager()->registerSkolem(fact, k)); - } else { - if (!ProofManager::getSkolemizationManager()->hasSkolem(fact)) { - // In the solution pass we didn't need this skolem. Therefore, we don't need it - // in this reply pass, either. - break; - } - - // Reuse the same skolem as in the solution pass - k = ProofManager::getSkolemizationManager()->getSkolem(fact); - Debug("pf::array") << "Skolem = " << k << std::endl; - } - - Node ak = nm->mkNode(kind::SELECT, fact[0][0], k); - Node bk = nm->mkNode(kind::SELECT, fact[0][1], k); - Node eq = ak.eqNode(bk); - Node lemma = fact[0].orNode(eq.notNode()); - - // In solve mode we don't care if ak and bk are registered. If they aren't, they'll be registered - // when we output the lemma. However, in replay need the lemma to be propagated, and so we - // preregister manually. - if (d_proofsEnabled) { - if (!d_equalityEngine->hasTerm(ak)) - { - preRegisterTermInternal(ak); - } - if (!d_equalityEngine->hasTerm(bk)) - { - preRegisterTermInternal(bk); - } - } - - if (options::arraysPropagate() > 0 && d_equalityEngine->hasTerm(ak) - && d_equalityEngine->hasTerm(bk)) - { - // Propagate witness disequality - might produce a conflict - d_permRef.push_back(lemma); - Debug("pf::array") << "Asserting to the equality engine:" << std::endl - << "\teq = " << eq << std::endl - << "\treason = " << fact << std::endl; - - d_equalityEngine->assertEquality(eq, false, fact, d_reasonExt); - ++d_numProp; - } - - if (!d_proofsEnabled) { - // If this is the solution pass, generate the lemma. Otherwise, don't generate it - - // as this is the lemma that we're reproving... - Trace("arrays-lem")<<"Arrays::addExtLemma " << lemma <<"\n"; - d_out->lemma(lemma); - ++d_numExt; - } - } else { - Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem" << std::endl; - d_modelConstraints.push_back(fact); - } - } - break; - default: - Unreachable(); - break; - } - } - - if ((options::arraysEagerLemmas() || fullEffort(e)) && !d_conflict && options::arraysWeakEquivalence()) { // Replay all array merges to update weak equivalence data structures context::CDList<Node>::iterator it = d_arrayMerges.begin(), iend = d_arrayMerges.end(); TNode a, b, eq; @@ -1488,7 +1289,7 @@ void TheoryArrays::check(Effort e) { lemma = mkAnd(conjunctions, true); // LSH FIXME: which kind of arrays lemma is this Trace("arrays-lem") << "Arrays::addExtLemma " << lemma <<"\n"; - d_out->lemma(lemma, RULE_INVALID, LemmaProperty::SEND_ATOMS); + d_out->lemma(lemma, LemmaProperty::SEND_ATOMS); d_readTableContext->pop(); Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl; return; @@ -1499,10 +1300,13 @@ void TheoryArrays::check(Effort e) { d_readTableContext->pop(); } - if(!options::arraysEagerLemmas() && fullEffort(e) && !d_conflict && !options::arraysWeakEquivalence()) { + if (!options::arraysEagerLemmas() && fullEffort(level) + && !d_state.isInConflict() && !options::arraysWeakEquivalence()) + { // generate the lemmas on the worklist Trace("arrays-lem")<< "Arrays::discharging lemmas. Number of queued lemmas: " << d_RowQueue.size() << "\n"; - while (d_RowQueue.size() > 0 && !d_conflict) { + while (d_RowQueue.size() > 0 && !d_state.isInConflict()) + { if (dischargeLemmas()) { break; } @@ -1512,6 +1316,84 @@ void TheoryArrays::check(Effort e) { Trace("arrays") << spaces(getSatContext()->getLevel()) << "Arrays::check(): done" << endl; } +bool TheoryArrays::preNotifyFact( + TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) +{ + if (!isPrereg) + { + if (atom.getKind() == kind::EQUAL) + { + if (!d_equalityEngine->hasTerm(atom[0])) + { + Assert(atom[0].isConst()); + d_equalityEngine->addTerm(atom[0]); + } + if (!d_equalityEngine->hasTerm(atom[1])) + { + Assert(atom[1].isConst()); + d_equalityEngine->addTerm(atom[1]); + } + } + } + return false; +} + +void TheoryArrays::notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) +{ + // if a disequality + if (atom.getKind() == kind::EQUAL && !pol) + { + // Apply ArrDiseq Rule if diseq is between arrays + if (fact[0][0].getType().isArray() && !d_state.isInConflict()) + { + NodeManager* nm = NodeManager::currentNM(); + TypeNode indexType = fact[0][0].getType()[0]; + + TNode k; + // k is the skolem for this disequality. + Debug("pf::array") << "Check: kind::NOT: array theory making a skolem" + << std::endl; + + // If not in replay mode, generate a fresh skolem variable + k = getSkolem( + fact, + "array_ext_index", + indexType, + "an extensional lemma index variable from the theory of arrays", + false); + + Node ak = nm->mkNode(kind::SELECT, fact[0][0], k); + Node bk = nm->mkNode(kind::SELECT, fact[0][1], k); + Node eq = ak.eqNode(bk); + Node lemma = fact[0].orNode(eq.notNode()); + + if (options::arraysPropagate() > 0 && d_equalityEngine->hasTerm(ak) + && d_equalityEngine->hasTerm(bk)) + { + // Propagate witness disequality - might produce a conflict + d_permRef.push_back(lemma); + Debug("pf::array") << "Asserting to the equality engine:" << std::endl + << "\teq = " << eq << std::endl + << "\treason = " << fact << std::endl; + + d_equalityEngine->assertEquality(eq, false, fact); + ++d_numProp; + } + + // If this is the solution pass, generate the lemma. Otherwise, don't + // generate it - as this is the lemma that we're reproving... + Trace("arrays-lem") << "Arrays::addExtLemma " << lemma << "\n"; + d_out->lemma(lemma); + ++d_numExt; + } + else + { + Debug("pf::array") << "Check: kind::NOT: array theory NOT making a skolem" + << std::endl; + d_modelConstraints.push_back(fact); + } + } +} Node TheoryArrays::mkAnd(std::vector<TNode>& conjunctions, bool invert, unsigned startIndex) { @@ -1716,7 +1598,8 @@ void TheoryArrays::mergeArrays(TNode a, TNode b) } // If no more to do, break - if (d_conflict || d_mergeQueue.empty()) { + if (d_state.isInConflict() || d_mergeQueue.empty()) + { break; } @@ -1916,7 +1799,8 @@ void TheoryArrays::propagate(RowLemmaType lem) if (!bjExists) { preRegisterTermInternal(bj); } - d_equalityEngine->assertEquality(aj_eq_bj, true, reason, d_reasonRow); + d_equalityEngine->assertEquality( + aj_eq_bj, true, reason, theory::eq::MERGED_THROUGH_ROW); ++d_numProp; return; } @@ -1927,7 +1811,8 @@ void TheoryArrays::propagate(RowLemmaType lem) (aj.isConst() && bj.isConst()) ? d_true : aj.eqNode(bj).notNode(); Node i_eq_j = i.eqNode(j); d_permRef.push_back(reason); - d_equalityEngine->assertEquality(i_eq_j, true, reason, d_reasonRow); + d_equalityEngine->assertEquality( + i_eq_j, true, reason, theory::eq::MERGED_THROUGH_ROW); ++d_numProp; return; } @@ -1938,7 +1823,8 @@ void TheoryArrays::queueRowLemma(RowLemmaType lem) { Debug("pf::array") << "Array solver: queue row lemma called" << std::endl; - if (d_conflict || d_RowAlreadyAdded.contains(lem)) { + if (d_state.isInConflict() || d_RowAlreadyAdded.contains(lem)) + { return; } TNode a, b, i, j; @@ -1965,33 +1851,23 @@ void TheoryArrays::queueRowLemma(RowLemmaType lem) propagate(lem); } - // If equivalent lemma already exists, don't enqueue this one - if (d_useArrTable) { - Node tableEntry = NodeManager::currentNM()->mkNode(kind::ARR_TABLE_FUN, a, b, i, j); - if (d_equalityEngine->getSize(tableEntry) != 1) - { - return; - } - } - // Prefer equality between indexes so as not to introduce new read terms if (options::arraysEagerIndexSplitting() && !bothExist && !d_equalityEngine->areDisequal(i, j, false)) { Node i_eq_j; - if (!d_proofsEnabled) { - i_eq_j = d_valuation.ensureLiteral(i.eqNode(j)); // TODO: think about this - } else { - i_eq_j = i.eqNode(j); - } - + i_eq_j = d_valuation.ensureLiteral(i.eqNode(j)); // TODO: think about this +#if 0 + i_eq_j = i.eqNode(j); +#endif getOutputChannel().requirePhase(i_eq_j, true); d_decisionRequests.push(i_eq_j); } // TODO: maybe add triggers here - if ((options::arraysEagerLemmas() || bothExist) && !d_proofsEnabled) { + if (options::arraysEagerLemmas() || bothExist) + { // Make sure that any terms introduced by rewriting are appropriately stored in the equality database Node aj2 = Rewriter::rewrite(aj); if (aj != aj2) { @@ -2099,7 +1975,8 @@ bool TheoryArrays::dischargeLemmas() int prop = options::arraysPropagate(); if (prop > 0) { propagate(l); - if (d_conflict) { + if (d_state.isInConflict()) + { return true; } } @@ -2170,26 +2047,14 @@ bool TheoryArrays::dischargeLemmas() void TheoryArrays::conflict(TNode a, TNode b) { Debug("pf::array") << "TheoryArrays::Conflict called" << std::endl; - std::shared_ptr<eq::EqProof> proof = d_proofsEnabled ? - std::make_shared<eq::EqProof>() : nullptr; - d_conflictNode = explain(a.eqNode(b), proof.get()); + explain(a.eqNode(b), d_conflictNode); if (!d_inCheckModel) { - std::unique_ptr<ProofArray> proof_array; - - if (d_proofsEnabled) { - proof->debug_print("pf::array"); - proof_array.reset(new ProofArray(proof, - /*row=*/d_reasonRow, - /*row1=*/d_reasonRow1, - /*ext=*/d_reasonExt)); - } - - d_out->conflict(d_conflictNode, std::move(proof_array)); + d_out->conflict(d_conflictNode); } - d_conflict = true; + d_state.notifyInConflict(); } TheoryArrays::TheoryArraysDecisionStrategy::TheoryArraysDecisionStrategy( @@ -2263,13 +2128,8 @@ TrustNode TheoryArrays::expandDefinition(Node node) return TrustNode::null(); } -void TheoryArrays::computeRelevantTerms(std::set<Node>& termSet, - bool includeShared) +void TheoryArrays::computeRelevantTerms(std::set<Node>& termSet) { - // include all standard terms - std::set<Kind> irrKinds; - computeRelevantTermsInternal(termSet, irrKinds, includeShared); - NodeManager* nm = NodeManager::currentNM(); // make sure RIntro1 reads are included in the relevant set of reads eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine); diff --git a/src/theory/arrays/theory_arrays.h b/src/theory/arrays/theory_arrays.h index 8cbf826c1..dea3d4136 100644 --- a/src/theory/arrays/theory_arrays.h +++ b/src/theory/arrays/theory_arrays.h @@ -26,7 +26,6 @@ #include "context/cdhashset.h" #include "context/cdqueue.h" #include "theory/arrays/array_info.h" -#include "theory/arrays/array_proof_reconstruction.h" #include "theory/arrays/theory_arrays_rewriter.h" #include "theory/theory.h" #include "theory/uf/equality_engine.h" @@ -129,15 +128,6 @@ class TheoryArrays : public Theory { /** conflicts in setModelVal */ IntStat d_numSetModelValConflicts; - // Merge reason types - - /** Merge tag for ROW applications */ - unsigned d_reasonRow; - /** Merge tag for ROW1 applications */ - unsigned d_reasonRow1; - /** Merge tag for EXT applications */ - unsigned d_reasonExt; - public: TheoryArrays(context::Context* c, context::UserContext* u, @@ -215,9 +205,8 @@ class TheoryArrays : public Theory { /** Should be called to propagate the literal. */ bool propagateLit(TNode literal); - /** Explain why this literal is true by adding assumptions */ - void explain(TNode literal, std::vector<TNode>& assumptions, - eq::EqProof* proof); + /** Explain why this literal is true by building an explanation */ + void explain(TNode literal, Node& exp); /** For debugging only- checks invariants about when things are preregistered*/ context::CDHashSet<Node, NodeHashFunction > d_isPreRegistered; @@ -227,7 +216,6 @@ class TheoryArrays : public Theory { public: void preRegisterTerm(TNode n) override; - Node explain(TNode n, eq::EqProof* proof); TrustNode explain(TNode n) override; ///////////////////////////////////////////////////////////////////////////// @@ -252,7 +240,6 @@ class TheoryArrays : public Theory { public: void notifySharedTerm(TNode t) override; - EqualityStatus getEqualityStatus(TNode a, TNode b) override; void computeCareGraph() override; bool isShared(TNode t) { @@ -264,7 +251,9 @@ class TheoryArrays : public Theory { ///////////////////////////////////////////////////////////////////////////// public: - bool collectModelInfo(TheoryModel* m) override; + /** Collect model values in m based on the relevant terms given by termSet */ + bool collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) override; ///////////////////////////////////////////////////////////////////////////// // NOTIFICATIONS @@ -278,8 +267,18 @@ class TheoryArrays : public Theory { // MAIN SOLVER ///////////////////////////////////////////////////////////////////////////// - public: - void check(Effort e) override; + //--------------------------------- standard check + /** Post-check, called after the fact queue of the theory is processed. */ + void postCheck(Effort level) override; + /** Pre-notify fact, return true if processed. */ + bool preNotifyFact(TNode atom, + bool pol, + TNode fact, + bool isPrereg, + bool isInternal) override; + /** Notify fact */ + void notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) override; + //--------------------------------- end standard check private: TNode weakEquivGetRep(TNode node); @@ -342,9 +341,6 @@ class TheoryArrays : public Theory { /** The notify class for d_equalityEngine */ NotifyClass d_notify; - /** Are we in conflict? */ - context::CDO<bool> d_conflict; - /** Conflict when merging constants */ void conflict(TNode a, TNode b); @@ -445,9 +441,6 @@ class TheoryArrays : public Theory { bool d_inCheckModel; int d_topLevel; - /** An equality-engine callback for proof reconstruction */ - std::unique_ptr<ArrayProofReconstruction> d_proofReconstruction; - /** * The decision strategy for the theory of arrays, which calls the * getNextDecisionEngineRequest function below. @@ -478,13 +471,11 @@ class TheoryArrays : public Theory { * for the comparison between the indexes that appears in the lemma. */ Node getNextDecisionRequest(); - /** - * Compute relevant terms. This includes additional select nodes for the + * Compute relevant terms. This includes select nodes for the * RIntro1 and RIntro2 rules. */ - void computeRelevantTerms(std::set<Node>& termSet, - bool includeShared = true) override; + void computeRelevantTerms(std::set<Node>& termSet) override; };/* class TheoryArrays */ }/* CVC4::theory::arrays namespace */ diff --git a/src/theory/builtin/proof_checker.cpp b/src/theory/builtin/proof_checker.cpp index 7521d116e..cf27b516e 100644 --- a/src/theory/builtin/proof_checker.cpp +++ b/src/theory/builtin/proof_checker.cpp @@ -16,6 +16,7 @@ #include "expr/skolem_manager.h" #include "smt/term_formula_removal.h" +#include "theory/evaluator.h" #include "theory/rewriter.h" #include "theory/theory.h" @@ -29,6 +30,9 @@ const char* toString(MethodId id) switch (id) { case MethodId::RW_REWRITE: return "RW_REWRITE"; + case MethodId::RW_EXT_REWRITE: return "RW_EXT_REWRITE"; + case MethodId::RW_REWRITE_EQ_EXT: return "RW_REWRITE_EQ_EXT"; + case MethodId::RW_EVALUATE: return "RW_EVALUATE"; case MethodId::RW_IDENTITY: return "RW_IDENTITY"; case MethodId::SB_DEFAULT: return "SB_DEFAULT"; case MethodId::SB_LITERAL: return "SB_LITERAL"; @@ -56,6 +60,7 @@ void BuiltinProofRuleChecker::registerTo(ProofChecker* pc) pc->registerChecker(PfRule::SCOPE, this); pc->registerChecker(PfRule::SUBS, this); pc->registerChecker(PfRule::REWRITE, this); + pc->registerChecker(PfRule::EVALUATE, this); pc->registerChecker(PfRule::MACRO_SR_EQ_INTRO, this); pc->registerChecker(PfRule::MACRO_SR_PRED_INTRO, this); pc->registerChecker(PfRule::MACRO_SR_PRED_ELIM, this); @@ -63,6 +68,7 @@ void BuiltinProofRuleChecker::registerTo(ProofChecker* pc) pc->registerChecker(PfRule::THEORY_REWRITE, this); pc->registerChecker(PfRule::REMOVE_TERM_FORMULA_AXIOM, this); // trusted rules + pc->registerTrustedChecker(PfRule::THEORY_LEMMA, this, 1); pc->registerTrustedChecker(PfRule::PREPROCESS, this, 2); pc->registerTrustedChecker(PfRule::PREPROCESS_LEMMA, this, 2); pc->registerTrustedChecker(PfRule::THEORY_PREPROCESS, this, 2); @@ -70,16 +76,6 @@ void BuiltinProofRuleChecker::registerTo(ProofChecker* pc) pc->registerTrustedChecker(PfRule::WITNESS_AXIOM, this, 2); } -Node BuiltinProofRuleChecker::applyTheoryRewrite(Node n, bool preRewrite) -{ - TheoryId tid = Theory::theoryOf(n); - Rewriter* rewriter = Rewriter::getInstance(); - Node nkr = preRewrite ? rewriter->preRewrite(tid, n).d_node - : rewriter->postRewrite(tid, n).d_node; - return nkr; -} - - Node BuiltinProofRuleChecker::applySubstitutionRewrite( Node n, const std::vector<Node>& exp, MethodId ids, MethodId idr) { @@ -95,7 +91,20 @@ Node BuiltinProofRuleChecker::applyRewrite(Node n, MethodId idr) { return Rewriter::rewrite(n); } - else if (idr == MethodId::RW_IDENTITY) + if (idr == MethodId::RW_EXT_REWRITE) + { + return d_ext_rewriter.extendedRewrite(n); + } + if (idr == MethodId::RW_REWRITE_EQ_EXT) + { + return Rewriter::rewriteEqualityExt(n); + } + if (idr == MethodId::RW_EVALUATE) + { + Evaluator eval; + return eval.eval(n, {}, {}, false); + } + if (idr == MethodId::RW_IDENTITY) { // does nothing return n; @@ -228,7 +237,7 @@ Node BuiltinProofRuleChecker::checkInternal(PfRule id, { exp.push_back(children[i]); } - Node res = applySubstitution(args[0], exp); + Node res = applySubstitution(args[0], exp, ids); return args[0].eqNode(res); } else if (id == PfRule::REWRITE) @@ -243,6 +252,13 @@ Node BuiltinProofRuleChecker::checkInternal(PfRule id, Node res = applyRewrite(args[0], idr); return args[0].eqNode(res); } + else if (id == PfRule::EVALUATE) + { + Assert(children.empty()); + Assert(args.size() == 1); + Node res = applyRewrite(args[0], MethodId::RW_EVALUATE); + return args[0].eqNode(res); + } else if (id == PfRule::MACRO_SR_EQ_INTRO) { Assert(1 <= args.size() && args.size() <= 3); @@ -334,13 +350,14 @@ Node BuiltinProofRuleChecker::checkInternal(PfRule id, Assert(args.size() == 1); return RemoveTermFormulas::getAxiomFor(args[0]); } - else if (id == PfRule::PREPROCESS || id == PfRule::PREPROCESS_LEMMA - || id == PfRule::THEORY_PREPROCESS - || id == PfRule::THEORY_PREPROCESS_LEMMA - || id == PfRule::WITNESS_AXIOM) + else if (id == PfRule::PREPROCESS || id == PfRule::THEORY_PREPROCESS + || id == PfRule::WITNESS_AXIOM || id == PfRule::THEORY_LEMMA + || id == PfRule::PREPROCESS_LEMMA || id == PfRule::THEORY_REWRITE) { + // "trusted" rules Assert(children.empty()); - Assert(args.size() == 1); + Assert(!args.empty()); + Assert(args[0].getType().isBoolean()); return args[0]; } // no rule @@ -390,6 +407,23 @@ void BuiltinProofRuleChecker::addMethodIds(std::vector<Node>& args, } } +bool BuiltinProofRuleChecker::getTheoryId(TNode n, TheoryId& tid) +{ + uint32_t i; + if (!getUInt32(n, i)) + { + return false; + } + tid = static_cast<TheoryId>(i); + return true; +} + +Node BuiltinProofRuleChecker::mkTheoryIdNode(TheoryId tid) +{ + return NodeManager::currentNM()->mkConst( + Rational(static_cast<uint32_t>(tid))); +} + } // namespace builtin } // namespace theory } // namespace CVC4 diff --git a/src/theory/builtin/proof_checker.h b/src/theory/builtin/proof_checker.h index 7e46587b7..3251b4e9e 100644 --- a/src/theory/builtin/proof_checker.h +++ b/src/theory/builtin/proof_checker.h @@ -20,11 +20,12 @@ #include "expr/node.h" #include "expr/proof_checker.h" #include "expr/proof_node.h" +#include "theory/quantifiers/extended_rewrite.h" namespace CVC4 { namespace theory { -/** +/** * Identifiers for rewriters and substitutions, which we abstractly * classify as "methods". Methods have a unique identifier in the internal * proof calculus implemented by the checker below. @@ -41,6 +42,12 @@ enum class MethodId : uint32_t //---------------------------- Rewriters // Rewriter::rewrite(n) RW_REWRITE, + // d_ext_rew.extendedRewrite(n); + RW_EXT_REWRITE, + // Rewriter::rewriteExtEquality(n) + RW_REWRITE_EQ_EXT, + // Evaluator::evaluate(n) + RW_EVALUATE, // identity RW_IDENTITY, //---------------------------- Substitutions @@ -75,21 +82,10 @@ class BuiltinProofRuleChecker : public ProofRuleChecker * specifying a call to Rewriter::rewrite. * @return The rewritten form of n. */ - static Node applyRewrite(Node n, MethodId idr = MethodId::RW_REWRITE); - /** - * Apply small-step rewrite on n in skolem form (either pre- or - * post-rewrite). This encapsulates the exact behavior of a THEORY_REWRITE - * step in a proof. - * - * @param n The node to rewrite - * @param preRewrite If true, performs a pre-rewrite or a post-rewrite - * otherwise - * @return The rewritten form of n - */ - static Node applyTheoryRewrite(Node n, bool preRewrite); + Node applyRewrite(Node n, MethodId idr = MethodId::RW_REWRITE); /** * Get substitution. Updates vars/subs to the substitution specified by - * exp (e.g. as an equality) for the substitution method ids. + * exp for the substitution method ids. */ static bool getSubstitution(Node exp, TNode& var, @@ -123,10 +119,10 @@ class BuiltinProofRuleChecker : public ProofRuleChecker * @param idr The method identifier of the rewriter. * @return The substituted, rewritten form of n. */ - static Node applySubstitutionRewrite(Node n, - const std::vector<Node>& exp, - MethodId ids = MethodId::SB_DEFAULT, - MethodId idr = MethodId::RW_REWRITE); + Node applySubstitutionRewrite(Node n, + const std::vector<Node>& exp, + MethodId ids = MethodId::SB_DEFAULT, + MethodId idr = MethodId::RW_REWRITE); /** get a method identifier from a node, return false if we fail */ static bool getMethodId(TNode n, MethodId& i); /** @@ -144,6 +140,11 @@ class BuiltinProofRuleChecker : public ProofRuleChecker */ static void addMethodIds(std::vector<Node>& args, MethodId ids, MethodId idr); + /** get a TheoryId from a node, return false if we fail */ + static bool getTheoryId(TNode n, TheoryId& tid); + /** Make a TheoryId into a node */ + static Node mkTheoryIdNode(TheoryId tid); + /** Register all rules owned by this rule checker into pc. */ void registerTo(ProofChecker* pc) override; protected: @@ -151,6 +152,9 @@ class BuiltinProofRuleChecker : public ProofRuleChecker Node checkInternal(PfRule id, const std::vector<Node>& children, const std::vector<Node>& args) override; + + /** extended rewriter object */ + quantifiers::ExtendedRewriter d_ext_rewriter; }; } // namespace builtin diff --git a/src/theory/bv/bitblast/aig_bitblaster.h b/src/theory/bv/bitblast/aig_bitblaster.h index 1e1b5bab4..fef45cdf5 100644 --- a/src/theory/bv/bitblast/aig_bitblaster.h +++ b/src/theory/bv/bitblast/aig_bitblaster.h @@ -89,12 +89,6 @@ class AigBitblaster : public TBitblaster<Abc_Obj_t*> prop::SatSolver* getSatSolver() override { return d_satSolver.get(); } - void setProofLog(proof::BitVectorProof* bvp) override - { - // Proofs are currently not supported with ABC - Unimplemented(); - } - class Statistics { public: diff --git a/src/theory/bv/bitblast/bitblaster.h b/src/theory/bv/bitblast/bitblaster.h index defc66b74..74e3c3f56 100644 --- a/src/theory/bv/bitblast/bitblaster.h +++ b/src/theory/bv/bitblast/bitblaster.h @@ -24,8 +24,8 @@ #include <vector> #include "expr/node.h" -#include "proof/bitvector_proof.h" #include "prop/bv_sat_solver_notify.h" +#include "prop/sat_solver.h" #include "prop/sat_solver_types.h" #include "smt/smt_engine_scope.h" #include "theory/bv/bitblast/bitblast_strategies_template.h" @@ -64,7 +64,6 @@ class TBitblaster // sat solver used for bitblasting and associated CnfStream std::unique_ptr<context::Context> d_nullContext; std::unique_ptr<prop::CnfStream> d_cnfStream; - proof::BitVectorProof* d_bvp; void initAtomBBStrategies(); void initTermBBStrategies(); @@ -91,7 +90,6 @@ class TBitblaster bool hasBBTerm(TNode node) const; void getBBTerm(TNode node, Bits& bits) const; virtual void storeBBTerm(TNode term, const Bits& bits); - virtual void setProofLog(proof::BitVectorProof* bvp); /** * Return a constant representing the value of a in the model. @@ -186,8 +184,7 @@ TBitblaster<T>::TBitblaster() : d_termCache(), d_modelCache(), d_nullContext(new context::Context()), - d_cnfStream(), - d_bvp(nullptr) + d_cnfStream() { initAtomBBStrategies(); initTermBBStrategies(); @@ -218,20 +215,6 @@ void TBitblaster<T>::invalidateModelCache() } template <class T> -void TBitblaster<T>::setProofLog(proof::BitVectorProof* bvp) -{ - if (THEORY_PROOF_ON()) - { - d_bvp = bvp; - prop::SatSolver* satSolver = getSatSolver(); - bvp->attachToSatSolver(*satSolver); - prop::SatVariable t = satSolver->trueVar(); - prop::SatVariable f = satSolver->falseVar(); - bvp->initCnfProof(d_cnfStream.get(), d_nullContext.get(), t, f); - } -} - -template <class T> Node TBitblaster<T>::getTermModel(TNode node, bool fullModel) { if (d_modelCache.find(node) != d_modelCache.end()) return d_modelCache[node]; diff --git a/src/theory/bv/bitblast/eager_bitblaster.cpp b/src/theory/bv/bitblast/eager_bitblaster.cpp index 4acd1d2f8..627a17bc5 100644 --- a/src/theory/bv/bitblast/eager_bitblaster.cpp +++ b/src/theory/bv/bitblast/eager_bitblaster.cpp @@ -72,7 +72,7 @@ EagerBitblaster::EagerBitblaster(TheoryBV* theory_bv, context::Context* c) d_bitblastingRegistrar.get(), d_nullContext.get(), rm, - options::proof(), + false, "EagerBitblaster")); } @@ -87,8 +87,7 @@ void EagerBitblaster::bbFormula(TNode node) } else { - d_cnfStream->convertAndAssert( - node, false, false, RULE_INVALID, TNode::null()); + d_cnfStream->convertAndAssert(node, false, false); } } @@ -116,10 +115,7 @@ void EagerBitblaster::bbAtom(TNode node) ? d_atomBBStrategies[normalized.getKind()](normalized, this) : normalized; - if (!options::proof()) - { - atom_bb = Rewriter::rewrite(atom_bb); - } + atom_bb = Rewriter::rewrite(atom_bb); // asserting that the atom is true iff the definition holds Node atom_definition = @@ -127,21 +123,14 @@ void EagerBitblaster::bbAtom(TNode node) AlwaysAssert(options::bitblastMode() == options::BitblastMode::EAGER); storeBBAtom(node, atom_bb); - d_cnfStream->convertAndAssert( - atom_definition, false, false, RULE_INVALID, TNode::null()); + d_cnfStream->convertAndAssert(atom_definition, false, false); } void EagerBitblaster::storeBBAtom(TNode atom, Node atom_bb) { - if (d_bvp) { - d_bvp->registerAtomBB(atom.toExpr(), atom_bb.toExpr()); - } d_bbAtoms.insert(atom); } void EagerBitblaster::storeBBTerm(TNode node, const Bits& bits) { - if (d_bvp) { - d_bvp->registerTermBB(node.toExpr()); - } d_termCache.insert(std::make_pair(node, bits)); } diff --git a/src/theory/bv/bitblast/eager_bitblaster.h b/src/theory/bv/bitblast/eager_bitblaster.h index a8b7ccbe5..da9488d43 100644 --- a/src/theory/bv/bitblast/eager_bitblaster.h +++ b/src/theory/bv/bitblast/eager_bitblaster.h @@ -23,8 +23,6 @@ #include "theory/bv/bitblast/bitblaster.h" -#include "proof/bitvector_proof.h" -#include "proof/resolution_bitvector_proof.h" #include "prop/cnf_stream.h" #include "prop/sat_solver.h" diff --git a/src/theory/bv/bitblast/lazy_bitblaster.cpp b/src/theory/bv/bitblast/lazy_bitblaster.cpp index c3a305952..3109d6ed7 100644 --- a/src/theory/bv/bitblast/lazy_bitblaster.cpp +++ b/src/theory/bv/bitblast/lazy_bitblaster.cpp @@ -19,7 +19,6 @@ #include "theory/bv/bitblast/lazy_bitblaster.h" #include "options/bv_options.h" -#include "proof/proof_manager.h" #include "prop/cnf_stream.h" #include "prop/sat_solver.h" #include "prop/sat_solver_factory.h" @@ -84,7 +83,7 @@ TLazyBitblaster::TLazyBitblaster(context::Context* c, d_nullRegistrar.get(), d_nullContext.get(), rm, - options::proof(), + false, "LazyBitblaster")); d_satSolverNotify.reset( @@ -161,8 +160,7 @@ void TLazyBitblaster::bbAtom(TNode node) Assert(!atom_bb.isNull()); Node atom_definition = nm->mkNode(kind::EQUAL, node, atom_bb); storeBBAtom(node, atom_bb); - d_cnfStream->convertAndAssert( - atom_definition, false, false, RULE_INVALID, TNode::null()); + d_cnfStream->convertAndAssert(atom_definition, false, false); return; } @@ -173,28 +171,19 @@ void TLazyBitblaster::bbAtom(TNode node) ? d_atomBBStrategies[normalized.getKind()](normalized, this) : normalized; - if (!options::proof()) - { - atom_bb = Rewriter::rewrite(atom_bb); - } + atom_bb = Rewriter::rewrite(atom_bb); // asserting that the atom is true iff the definition holds Node atom_definition = nm->mkNode(kind::EQUAL, node, atom_bb); storeBBAtom(node, atom_bb); - d_cnfStream->convertAndAssert( - atom_definition, false, false, RULE_INVALID, TNode::null()); + d_cnfStream->convertAndAssert(atom_definition, false, false); } void TLazyBitblaster::storeBBAtom(TNode atom, Node atom_bb) { - // No need to store the definition for the lazy bit-blaster (unless proofs are enabled). - if( d_bvp != NULL ){ - d_bvp->registerAtomBB(atom.toExpr(), atom_bb.toExpr()); - } d_bbAtoms.insert(atom); } void TLazyBitblaster::storeBBTerm(TNode node, const Bits& bits) { - if( d_bvp ){ d_bvp->registerTermBB(node.toExpr()); } d_termCache.insert(std::make_pair(node, bits)); } @@ -540,7 +529,8 @@ Node TLazyBitblaster::getModelFromSatSolver(TNode a, bool fullModel) { bool TLazyBitblaster::collectModelInfo(TheoryModel* m, bool fullModel) { std::set<Node> termSet; - d_bv->computeRelevantTerms(termSet); + const std::set<Kind>& irrKinds = m->getIrrelevantKinds(); + d_bv->computeAssertedTerms(termSet, irrKinds, true); for (std::set<Node>::const_iterator it = termSet.begin(); it != termSet.end(); ++it) { TNode var = *it; diff --git a/src/theory/bv/bitblast/lazy_bitblaster.h b/src/theory/bv/bitblast/lazy_bitblaster.h index a355d42c4..bc930aec4 100644 --- a/src/theory/bv/bitblast/lazy_bitblaster.h +++ b/src/theory/bv/bitblast/lazy_bitblaster.h @@ -19,7 +19,6 @@ #ifndef CVC4__THEORY__BV__BITBLAST__LAZY_BITBLASTER_H #define CVC4__THEORY__BV__BITBLAST__LAZY_BITBLASTER_H -#include "proof/resolution_bitvector_proof.h" #include "theory/bv/bitblast/bitblaster.h" #include "context/cdhashmap.h" diff --git a/src/theory/bv/bv_eager_solver.cpp b/src/theory/bv/bv_eager_solver.cpp index 36aa72da3..d1490374d 100644 --- a/src/theory/bv/bv_eager_solver.cpp +++ b/src/theory/bv/bv_eager_solver.cpp @@ -33,8 +33,7 @@ EagerBitblastSolver::EagerBitblastSolver(context::Context* c, TheoryBV* bv) d_bitblaster(), d_aigBitblaster(), d_useAig(options::bitvectorAig()), - d_bv(bv), - d_bvp(nullptr) + d_bv(bv) { } @@ -55,10 +54,6 @@ void EagerBitblastSolver::initialize() { #endif } else { d_bitblaster.reset(new EagerBitblaster(d_bv, d_context)); - THEORY_PROOF(if (d_bvp) { - d_bitblaster->setProofLog(d_bvp); - d_bvp->setBitblaster(d_bitblaster.get()); - }); } } @@ -127,11 +122,6 @@ bool EagerBitblastSolver::collectModelInfo(TheoryModel* m, bool fullModel) return d_bitblaster->collectModelInfo(m, fullModel); } -void EagerBitblastSolver::setProofLog(proof::BitVectorProof* bvp) -{ - d_bvp = bvp; -} - } // namespace bv } // namespace theory } // namespace CVC4 diff --git a/src/theory/bv/bv_eager_solver.h b/src/theory/bv/bv_eager_solver.h index 6182832e9..e0b55c23b 100644 --- a/src/theory/bv/bv_eager_solver.h +++ b/src/theory/bv/bv_eager_solver.h @@ -23,7 +23,6 @@ #include <vector> #include "expr/node.h" -#include "proof/resolution_bitvector_proof.h" #include "theory/bv/theory_bv.h" #include "theory/theory_model.h" @@ -48,7 +47,6 @@ class EagerBitblastSolver { bool isInitialized(); void initialize(); bool collectModelInfo(theory::TheoryModel* m, bool fullModel); - void setProofLog(proof::BitVectorProof* bvp); private: context::CDHashSet<Node, NodeHashFunction> d_assertionSet; @@ -61,7 +59,6 @@ class EagerBitblastSolver { bool d_useAig; TheoryBV* d_bv; - proof::BitVectorProof* d_bvp; }; // class EagerBitblastSolver } // namespace bv diff --git a/src/theory/bv/bv_subtheory.h b/src/theory/bv/bv_subtheory.h index 725b61f95..f4b88b719 100644 --- a/src/theory/bv/bv_subtheory.h +++ b/src/theory/bv/bv_subtheory.h @@ -25,10 +25,6 @@ namespace CVC4 { -namespace proof { -class BitVectorProof; -} - namespace theory { class TheoryModel; @@ -72,7 +68,6 @@ class SubtheorySolver { SubtheorySolver(context::Context* c, TheoryBV* bv) : d_context(c), d_bv(bv), - d_bvp(nullptr), d_assertionQueue(c), d_assertionIndex(c, 0) {} virtual ~SubtheorySolver() {} @@ -93,7 +88,7 @@ class SubtheorySolver { return res; } virtual void assertFact(TNode fact) { d_assertionQueue.push_back(fact); } - virtual void setProofLog(proof::BitVectorProof* bvp) {} + AssertionQueue::const_iterator assertionsBegin() { return d_assertionQueue.begin(); } @@ -107,8 +102,6 @@ class SubtheorySolver { /** The bit-vector theory */ TheoryBV* d_bv; - /** proof log */ - proof::ResolutionBitVectorProof* d_bvp; AssertionQueue d_assertionQueue; context::CDO<uint32_t> d_assertionIndex; }; /* class SubtheorySolver */ diff --git a/src/theory/bv/bv_subtheory_algebraic.cpp b/src/theory/bv/bv_subtheory_algebraic.cpp index e5a416a1b..80a6aeb86 100644 --- a/src/theory/bv/bv_subtheory_algebraic.cpp +++ b/src/theory/bv/bv_subtheory_algebraic.cpp @@ -715,7 +715,8 @@ bool AlgebraicSolver::collectModelInfo(TheoryModel* model, bool fullModel) Debug("bitvector-model") << "AlgebraicSolver::collectModelInfo\n"; AlwaysAssert(!d_quickSolver->inConflict()); set<Node> termSet; - d_bv->computeRelevantTerms(termSet); + const std::set<Kind>& irrKinds = model->getIrrelevantKinds(); + d_bv->computeAssertedTerms(termSet, irrKinds, true); // collect relevant terms that the bv theory abstracts to variables // (variables and parametric terms such as select apply_uf) diff --git a/src/theory/bv/bv_subtheory_bitblast.cpp b/src/theory/bv/bv_subtheory_bitblast.cpp index 8f87bc4b8..28c70a5b8 100644 --- a/src/theory/bv/bv_subtheory_bitblast.cpp +++ b/src/theory/bv/bv_subtheory_bitblast.cpp @@ -18,7 +18,6 @@ #include "decision/decision_attributes.h" #include "options/bv_options.h" #include "options/decision_options.h" -#include "proof/proof_manager.h" #include "smt/smt_statistics_registry.h" #include "theory/bv/abstraction.h" #include "theory/bv/bitblast/lazy_bitblaster.h" @@ -276,12 +275,6 @@ void BitblastSolver::setConflict(TNode conflict) { d_bv->setConflict(final_conflict); } -void BitblastSolver::setProofLog(proof::BitVectorProof* bvp) -{ - d_bitblaster->setProofLog( bvp ); - bvp->setBitblaster(d_bitblaster.get()); -} - }/* namespace CVC4::theory::bv */ }/* namespace CVC4::theory */ }/* namespace CVC4 */ diff --git a/src/theory/bv/bv_subtheory_bitblast.h b/src/theory/bv/bv_subtheory_bitblast.h index a2b099609..60ef08d93 100644 --- a/src/theory/bv/bv_subtheory_bitblast.h +++ b/src/theory/bv/bv_subtheory_bitblast.h @@ -24,10 +24,6 @@ namespace CVC4 { -namespace proof { -class ResolutionBitVectorProof; -} - namespace theory { namespace bv { @@ -79,7 +75,6 @@ public: void bitblastQueue(); void setAbstraction(AbstractionModule* module); uint64_t computeAtomWeight(TNode atom); - void setProofLog(proof::BitVectorProof* bvp) override; }; } /* namespace CVC4::theory::bv */ diff --git a/src/theory/bv/bv_subtheory_core.cpp b/src/theory/bv/bv_subtheory_core.cpp index d8f376a74..38c5cb482 100644 --- a/src/theory/bv/bv_subtheory_core.cpp +++ b/src/theory/bv/bv_subtheory_core.cpp @@ -354,7 +354,8 @@ bool CoreSolver::collectModelInfo(TheoryModel* m, bool fullModel) } } set<Node> termSet; - d_bv->computeRelevantTerms(termSet); + const std::set<Kind>& irrKinds = m->getIrrelevantKinds(); + d_bv->computeAssertedTerms(termSet, irrKinds, true); if (!m->assertEqualityEngine(d_equalityEngine, &termSet)) { return false; diff --git a/src/theory/bv/theory_bv.cpp b/src/theory/bv/theory_bv.cpp index 1696d6185..d6492f177 100644 --- a/src/theory/bv/theory_bv.cpp +++ b/src/theory/bv/theory_bv.cpp @@ -18,8 +18,6 @@ #include "expr/node_algorithm.h" #include "options/bv_options.h" #include "options/smt_options.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" #include "smt/smt_statistics_registry.h" #include "theory/bv/abstraction.h" #include "theory/bv/bv_eager_solver.h" @@ -83,19 +81,19 @@ TheoryBV::TheoryBV(context::Context* c, return; } - if (options::bitvectorEqualitySolver() && !options::proof()) + if (options::bitvectorEqualitySolver()) { d_subtheories.emplace_back(new CoreSolver(c, this, d_extTheory.get())); d_subtheoryMap[SUB_CORE] = d_subtheories.back().get(); } - if (options::bitvectorInequalitySolver() && !options::proof()) + if (options::bitvectorInequalitySolver()) { d_subtheories.emplace_back(new InequalitySolver(c, u, this)); d_subtheoryMap[SUB_INEQUALITY] = d_subtheories.back().get(); } - if (options::bitvectorAlgebraicSolver() && !options::proof()) + if (options::bitvectorAlgebraicSolver()) { d_subtheories.emplace_back(new AlgebraicSolver(c, this)); d_subtheoryMap[SUB_ALGEBRAIC] = d_subtheories.back().get(); @@ -230,8 +228,11 @@ TrustNode TheoryBV::expandDefinition(Node node) TNode num = node[0], den = node[1]; Node den_eq_0 = nm->mkNode(kind::EQUAL, den, utils::mkZero(width)); - Node divTotalNumDen = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV ? kind::BITVECTOR_UDIV_TOTAL : - kind::BITVECTOR_UREM_TOTAL, num, den); + Node divTotalNumDen = nm->mkNode(node.getKind() == kind::BITVECTOR_UDIV + ? kind::BITVECTOR_UDIV_TOTAL + : kind::BITVECTOR_UREM_TOTAL, + num, + den); Node divByZero = getBVDivByZero(node.getKind(), width); Node divByZeroNum = nm->mkNode(kind::APPLY_UF, divByZero, num); ret = nm->mkNode(kind::ITE, den_eq_0, divByZeroNum, divTotalNumDen); @@ -327,7 +328,7 @@ void TheoryBV::check(Effort e) if (done() && e<Theory::EFFORT_FULL) { return; } - + //last call : do reductions on extended bitvector functions if (e == Theory::EFFORT_LAST_CALL) { std::vector<Node> nred = d_extTheory->getActive(); @@ -410,7 +411,7 @@ void TheoryBV::check(Effort e) break; } } - + //check extended functions if (Theory::fullEffort(e)) { //do inferences (adds external lemmas) TODO: this can be improved to add internal inferences @@ -431,7 +432,9 @@ void TheoryBV::check(Effort e) if( doExtfReductions( nred ) ){ return; } - }else{ + } + else + { d_needsLastCallCheck = true; } } @@ -726,11 +729,13 @@ TrustNode TheoryBV::ppRewrite(TNode t) } else if (RewriteRule<UltPlusOne>::applies(t)) { Node result = RewriteRule<UltPlusOne>::run<false>(t); res = Rewriter::rewrite(result); - } else if( res.getKind() == kind::EQUAL && - ((res[0].getKind() == kind::BITVECTOR_PLUS && - RewriteRule<ConcatToMult>::applies(res[1])) || - (res[1].getKind() == kind::BITVECTOR_PLUS && - RewriteRule<ConcatToMult>::applies(res[0])))) { + } + else if (res.getKind() == kind::EQUAL + && ((res[0].getKind() == kind::BITVECTOR_PLUS + && RewriteRule<ConcatToMult>::applies(res[1])) + || (res[1].getKind() == kind::BITVECTOR_PLUS + && RewriteRule<ConcatToMult>::applies(res[0])))) + { Node mult = RewriteRule<ConcatToMult>::applies(res[0])? RewriteRule<ConcatToMult>::run<false>(res[0]) : RewriteRule<ConcatToMult>::run<true>(res[1]); @@ -743,9 +748,13 @@ TrustNode TheoryBV::ppRewrite(TNode t) } else { res = t; } - } else if (RewriteRule<SignExtendEqConst>::applies(t)) { + } + else if (RewriteRule<SignExtendEqConst>::applies(t)) + { res = RewriteRule<SignExtendEqConst>::run<false>(t); - } else if (RewriteRule<ZeroExtendEqConst>::applies(t)) { + } + else if (RewriteRule<ZeroExtendEqConst>::applies(t)) + { res = RewriteRule<ZeroExtendEqConst>::run<false>(t); } else if (RewriteRule<NormalizeEqPlusNeg>::applies(t)) @@ -960,20 +969,6 @@ bool TheoryBV::applyAbstraction(const std::vector<Node>& assertions, std::vector return changed; } -void TheoryBV::setProofLog(proof::BitVectorProof* bvp) -{ - if (options::bitblastMode() == options::BitblastMode::EAGER) - { - d_eagerSolver->setProofLog(bvp); - } - else - { - for( unsigned i=0; i< d_subtheories.size(); i++ ){ - d_subtheories[i]->setProofLog( bvp ); - } - } -} - void TheoryBV::setConflict(Node conflict) { if (options::bvAbstraction()) diff --git a/src/theory/bv/theory_bv.h b/src/theory/bv/theory_bv.h index c6e9282f4..2f63f1a52 100644 --- a/src/theory/bv/theory_bv.h +++ b/src/theory/bv/theory_bv.h @@ -32,12 +32,7 @@ #include "util/hash.h" #include "util/statistics_registry.h" -// Forward declarations, needed because the BV theory and the BV Proof classes -// are cyclically dependent namespace CVC4 { -namespace proof { -class BitVectorProof; -} namespace theory { @@ -123,8 +118,6 @@ class TheoryBV : public Theory { bool applyAbstraction(const std::vector<Node>& assertions, std::vector<Node>& new_assertions); - void setProofLog(proof::BitVectorProof* bvp); - private: class Statistics { @@ -197,7 +190,7 @@ class TheoryBV : public Theory { std::unique_ptr<EagerBitblastSolver> d_eagerSolver; std::unique_ptr<AbstractionModule> d_abstractionModule; bool d_calledPreregister; - + //for extended functions bool d_needsLastCallCheck; context::CDHashSet<Node, NodeHashFunction> d_extf_range_infer; @@ -225,7 +218,7 @@ class TheoryBV : public Theory { * (ite ((_ extract 1 0) x) 1 0) */ bool doExtfReductions( std::vector< Node >& terms ); - + bool wasPropagatedBySubtheory(TNode literal) const { return d_propagatedBy.find(literal) != d_propagatedBy.end(); } @@ -266,7 +259,11 @@ class TheoryBV : public Theory { void sendConflict(); - void lemma(TNode node) { d_out->lemma(node, RULE_CONFLICT); d_lemmasAdded = true; } + void lemma(TNode node) + { + d_out->lemma(node); + d_lemmasAdded = true; + } void checkForLemma(TNode node); diff --git a/src/theory/combination_engine.cpp b/src/theory/combination_engine.cpp index e1317cf29..f1e977fe3 100644 --- a/src/theory/combination_engine.cpp +++ b/src/theory/combination_engine.cpp @@ -113,7 +113,7 @@ eq::EqualityEngineNotify* CombinationEngine::getModelEqualityEngineNotify() void CombinationEngine::sendLemma(TrustNode trn, TheoryId atomsTo) { - d_te.lemma(trn.getNode(), RULE_INVALID, false, LemmaProperty::NONE, atomsTo); + d_te.lemma(trn.getNode(), false, LemmaProperty::NONE, atomsTo); } void CombinationEngine::resetRound() diff --git a/src/theory/datatypes/inference_manager.cpp b/src/theory/datatypes/inference_manager.cpp new file mode 100644 index 000000000..1faf71aa9 --- /dev/null +++ b/src/theory/datatypes/inference_manager.cpp @@ -0,0 +1,126 @@ +/********************* */ +/*! \file inference_manager.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Datatypes inference manager + **/ + +#include "theory/datatypes/inference_manager.h" + +#include "expr/dtype.h" +#include "options/datatypes_options.h" +#include "theory/theory.h" + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { +namespace datatypes { + +DatatypesInference::DatatypesInference(Node conc, Node exp, ProofGenerator* pg) + : SimpleTheoryInternalFact(conc, exp, pg) +{ + // false is not a valid explanation + Assert(d_exp.isNull() || !d_exp.isConst() || d_exp.getConst<bool>()); +} + +bool DatatypesInference::mustCommunicateFact(Node n, Node exp) +{ + Trace("dt-lemma-debug") << "Compute for " << exp << " => " << n << std::endl; + bool addLemma = false; + if (options::dtInferAsLemmas() && !exp.isConst()) + { + // all units are lemmas + addLemma = true; + } + else if (n.getKind() == EQUAL) + { + TypeNode tn = n[0].getType(); + if (!tn.isDatatype()) + { + addLemma = true; + } + else + { + const DType& dt = tn.getDType(); + addLemma = dt.involvesExternalType(); + } + } + else if (n.getKind() == LEQ || n.getKind() == OR) + { + addLemma = true; + } + if (addLemma) + { + Trace("dt-lemma-debug") << "Communicate " << n << std::endl; + return true; + } + Trace("dt-lemma-debug") << "Do not need to communicate " << n << std::endl; + return false; +} + +bool DatatypesInference::process(TheoryInferenceManager* im) +{ + // check to see if we have to communicate it to the rest of the system + if (mustCommunicateFact(d_conc, d_exp)) + { + // send it as an (explained) lemma + std::vector<Node> exp; + if (!d_exp.isNull() && !d_exp.isConst()) + { + exp.push_back(d_exp); + } + return im->lemmaExp(d_conc, exp, {}); + } + // assert the internal fact + bool polarity = d_conc.getKind() != NOT; + TNode atom = polarity ? d_conc : d_conc[0]; + im->assertInternalFact(atom, polarity, d_exp); + return true; +} + +InferenceManager::InferenceManager(Theory& t, + TheoryState& state, + ProofNodeManager* pnm) + : InferenceManagerBuffered(t, state, pnm) +{ +} + +void InferenceManager::addPendingInference(Node conc, + Node exp, + ProofGenerator* pg) +{ + d_pendingFact.emplace_back(new DatatypesInference(conc, exp, pg)); +} + +void InferenceManager::process() +{ + // process pending lemmas, used infrequently, only for definitional lemmas + doPendingLemmas(); + // now process the pending facts + doPendingFacts(); +} + +bool InferenceManager::sendLemmas(const std::vector<Node>& lemmas) +{ + bool ret = false; + for (const Node& lem : lemmas) + { + if (lemma(lem)) + { + ret = true; + } + } + return ret; +} + +} // namespace datatypes +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/datatypes/inference_manager.h b/src/theory/datatypes/inference_manager.h new file mode 100644 index 000000000..0dfdfb281 --- /dev/null +++ b/src/theory/datatypes/inference_manager.h @@ -0,0 +1,95 @@ +/********************* */ +/*! \file inference_manager.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief Datatypes inference manager + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__DATATYPES__INFERENCE_MANAGER_H +#define CVC4__THEORY__DATATYPES__INFERENCE_MANAGER_H + +#include "context/cdhashmap.h" +#include "expr/node.h" +#include "theory/inference_manager_buffered.h" + +namespace CVC4 { +namespace theory { +namespace datatypes { + +/** + * A custom inference class. The main feature of this class is that it + * dynamically decides whether to process itself as a fact or as a lemma, + * based on the mustCommunicateFact method below. + */ +class DatatypesInference : public SimpleTheoryInternalFact +{ + public: + DatatypesInference(Node conc, Node exp, ProofGenerator* pg); + /** + * Must communicate fact method. + * The datatypes decision procedure makes "internal" inferences : + * (1) Unification : C( t1...tn ) = C( s1...sn ) => ti = si + * (2) Label : ~is_C1(t) ... ~is_C{i-1}(t) ~is_C{i+1}(t) ... ~is_Cn(t) => + * is_Ci( t ) + * (3) Instantiate : is_C( t ) => t = C( sel_1( t ) ... sel_n( t ) ) + * (4) collapse selector : S( C( t1...tn ) ) = t' + * (5) collapse term size : size( C( t1...tn ) ) = 1 + size( t1 ) + ... + + * size( tn ) + * (6) non-negative size : 0 <= size(t) + * This method returns true if the fact must be sent out as a lemma. If it + * returns false, then we assert the fact internally. We may need to + * communicate outwards if the conclusions involve other theories. Also + * communicate (6) and OR conclusions. + */ + static bool mustCommunicateFact(Node n, Node exp); + /** + * Process this fact, possibly as a fact or as a lemma, depending on the + * above method. + */ + bool process(TheoryInferenceManager* im) override; +}; + +/** + * The datatypes inference manager, which uses the above class for + * inferences. + */ +class InferenceManager : public InferenceManagerBuffered +{ + typedef context::CDHashSet<Node, NodeHashFunction> NodeSet; + + public: + InferenceManager(Theory& t, TheoryState& state, ProofNodeManager* pnm); + ~InferenceManager() {} + /** + * Add pending inference, which may be processed as either a fact or + * a lemma based on mustCommunicateFact in DatatypesInference above. + */ + void addPendingInference(Node conc, Node exp, ProofGenerator* pg = nullptr); + /** + * Process the current lemmas and facts. This is a custom method that can + * be seen as overriding the behavior of calling both doPendingLemmas and + * doPendingFacts. It determines whether facts should be sent as lemmas + * or processed internally. + */ + void process(); + /** + * Send lemmas with property NONE on the output channel immediately. + * Returns true if any lemma was sent. + */ + bool sendLemmas(const std::vector<Node>& lemmas); +}; + +} // namespace datatypes +} // namespace theory +} // namespace CVC4 + +#endif diff --git a/src/theory/datatypes/theory_datatypes.cpp b/src/theory/datatypes/theory_datatypes.cpp index a98c77a5d..5253414a9 100644 --- a/src/theory/datatypes/theory_datatypes.cpp +++ b/src/theory/datatypes/theory_datatypes.cpp @@ -47,22 +47,18 @@ TheoryDatatypes::TheoryDatatypes(Context* c, const LogicInfo& logicInfo, ProofNodeManager* pnm) : Theory(THEORY_DATATYPES, c, u, out, valuation, logicInfo, pnm), - d_infer(c), - d_infer_exp(c), d_term_sk(u), d_notify(*this), d_labels(c), d_selector_apps(c), - d_conflict(c, false), - d_addedLemma(false), - d_addedFact(false), d_collectTermsCache(c), d_collectTermsCacheU(u), d_functionTerms(c), d_singleton_eq(u), d_lemmas_produced_c(u), d_sygusExtension(nullptr), - d_state(c, u, valuation) + d_state(c, u, valuation), + d_im(*this, d_state, pnm) { d_true = NodeManager::currentNM()->mkConst( true ); @@ -71,6 +67,7 @@ TheoryDatatypes::TheoryDatatypes(Context* c, // indicate we are using the default theory state object d_theoryState = &d_state; + d_inferManager = &d_im; } TheoryDatatypes::~TheoryDatatypes() { @@ -156,64 +153,43 @@ TNode TheoryDatatypes::getEqcConstructor( TNode r ) { } } -void TheoryDatatypes::check(Effort e) { - if (done() && e<EFFORT_FULL) { - return; - } - Assert(d_pending.empty()); - d_addedLemma = false; - - if( e == EFFORT_LAST_CALL ){ +bool TheoryDatatypes::preCheck(Effort level) +{ + d_im.reset(); + return false; +} + +void TheoryDatatypes::postCheck(Effort level) +{ + if (level == EFFORT_LAST_CALL) + { Assert(d_sygusExtension != nullptr); - std::vector< Node > lemmas; + std::vector<Node> lemmas; d_sygusExtension->check(lemmas); - doSendLemmas( lemmas ); + d_im.sendLemmas(lemmas); return; } - - TimerStat::CodeTimer checkTimer(d_checkTime); - - Trace("datatypes-check") << "Check effort " << e << std::endl; - while(!done() && !d_conflict) { - // Get all the assertions - Assertion assertion = get(); - TNode fact = assertion.d_assertion; - Trace("datatypes-assert") << "Assert " << fact << std::endl; - - TNode atom CVC4_UNUSED = fact.getKind() == kind::NOT ? fact[0] : fact; - - // extra debug check to make sure that the rewriter did its job correctly - Assert(atom.getKind() != kind::EQUAL - || (atom[0].getKind() != kind::TUPLE_UPDATE - && atom[1].getKind() != kind::TUPLE_UPDATE - && atom[0].getKind() != kind::RECORD_UPDATE - && atom[1].getKind() != kind::RECORD_UPDATE)) - << "tuple/record escaped into datatypes decision procedure; should " - "have been rewritten away"; - - //assert the fact - assertFact( fact, fact ); - flushPendingFacts(); - } - - if( e == EFFORT_FULL && !d_conflict && !d_addedLemma && !d_valuation.needCheck() ) { + else if (level == EFFORT_FULL && !d_state.isInConflict() + && !d_im.hasSentLemma() && !d_valuation.needCheck()) + { //check for cycles - Assert(d_pending.empty()); + Assert(!d_im.hasPendingFact()); do { - d_addedFact = false; + d_im.reset(); Trace("datatypes-proc") << "Check cycles..." << std::endl; checkCycles(); Trace("datatypes-proc") << "...finish check cycles" << std::endl; - flushPendingFacts(); - if( d_conflict || d_addedLemma ){ + d_im.process(); + if (d_state.isInConflict() || d_im.hasSentLemma()) + { return; } - }while( d_addedFact ); - + } while (d_im.hasSentFact()); + //check for splits - Trace("datatypes-debug") << "Check for splits " << e << endl; + Trace("datatypes-debug") << "Check for splits " << endl; do { - d_addedFact = false; + d_im.reset(); std::map< TypeNode, Node > rec_singletons; eq::EqClassesIterator eqcs_i = eq::EqClassesIterator(d_equalityEngine); while( !eqcs_i.isFinished() ){ @@ -265,7 +241,7 @@ void TheoryDatatypes::check(Effort e) { assumptions.push_back(assumption); Node lemma = assumptions.size()==1 ? assumptions[0] : NodeManager::currentNM()->mkNode( OR, assumptions ); Trace("dt-singleton") << "*************Singleton equality lemma " << lemma << std::endl; - doSendLemma( lemma ); + d_im.lemma(lemma); } } }else{ @@ -279,8 +255,6 @@ void TheoryDatatypes::check(Effort e) { //all other cases std::vector< bool > pcons; getPossibleCons( eqc, n, pcons ); - //std::map< int, bool > sel_apps; - //getSelectorsForCons( n, sel_apps ); //check if we do not need to resolve the constructor type for this equivalence class. // this is if there are no selectors for this equivalence class, and its possible values are infinite, // then do not split. @@ -322,10 +296,8 @@ void TheoryDatatypes::check(Effort e) { //this may not be necessary? //if only one constructor, then this term must be this constructor Node t = utils::mkTester(n, 0, dt); - d_pending.push_back( t ); - d_pending_exp[ t ] = d_true; + d_im.addPendingInference(t, d_true); Trace("datatypes-infer") << "DtInfer : 1-cons (full) : " << t << std::endl; - d_infer.push_back( t ); }else{ Assert(consIndex != -1 || dt.isSygus()); if( options::dtBinarySplit() && consIndex!=-1 ){ @@ -335,14 +307,13 @@ void TheoryDatatypes::check(Effort e) { NodeBuilder<> nb(kind::OR); nb << test << test.notNode(); Node lemma = nb; - doSendLemma( lemma ); + d_im.lemma(lemma); d_out->requirePhase( test, true ); }else{ Trace("dt-split") << "*************Split for constructors on " << n << endl; Node lemma = utils::mkSplit(n, dt); Trace("dt-split-debug") << "Split lemma is : " << lemma << std::endl; - d_out->lemma(lemma, LemmaProperty::SEND_ATOMS); - d_addedLemma = true; + d_im.lemma(lemma, LemmaProperty::SEND_ATOMS, false); } if( !options::dtBlastSplits() ){ break; @@ -358,29 +329,32 @@ void TheoryDatatypes::check(Effort e) { } ++eqcs_i; } - if (d_addedLemma) + if (d_im.hasSentLemma()) { // clear pending facts: we added a lemma, so internal inferences are // no longer necessary - d_pending.clear(); - d_pending_exp.clear(); + d_im.clearPendingFacts(); } else { // we did not add a lemma, process internal inferences. This loop // will repeat. Trace("datatypes-debug") << "Flush pending facts..." << std::endl; - flushPendingFacts(); + d_im.process(); } - }while( !d_conflict && !d_addedLemma && d_addedFact ); - Trace("datatypes-debug") << "Finished, conflict=" << d_conflict << ", lemmas=" << d_addedLemma << std::endl; - if( !d_conflict ){ + } while (!d_state.isInConflict() && !d_im.hasSentLemma() + && d_im.hasSentFact()); + Trace("datatypes-debug") + << "Finished, conflict=" << d_state.isInConflict() + << ", lemmas=" << d_im.hasSentLemma() << std::endl; + if (!d_state.isInConflict()) + { Trace("dt-model-debug") << std::endl; printModelDebug("dt-model-debug"); } } - Trace("datatypes-check") << "Finished check effort " << e << std::endl; + Trace("datatypes-check") << "Finished check effort " << level << std::endl; if( Debug.isOn("datatypes") || Debug.isOn("datatypes-split") ) { Notice() << "TheoryDatatypes::check(): done" << endl; } @@ -390,100 +364,17 @@ bool TheoryDatatypes::needsCheckLastEffort() { return d_sygusExtension != nullptr; } -void TheoryDatatypes::flushPendingFacts(){ - //pending lemmas: used infrequently, only for definitional lemmas - if( !d_pending_lem.empty() ){ - int i = 0; - while( i<(int)d_pending_lem.size() ){ - doSendLemma( d_pending_lem[i] ); - i++; - } - d_pending_lem.clear(); - } - int i = 0; - while( !d_conflict && i<(int)d_pending.size() ){ - Node fact = d_pending[i]; - Node exp = d_pending_exp[ fact ]; - Trace("datatypes-debug") << "Assert fact (#" << (i+1) << "/" << d_pending.size() << ") " << fact << " with explanation " << exp << std::endl; - //check to see if we have to communicate it to the rest of the system - if( mustCommunicateFact( fact, exp ) ){ - Node lem = fact; - if( exp.isNull() || exp==d_true ){ - Trace("dt-lemma-debug") << "Trivial explanation." << std::endl; - }else{ - Trace("dt-lemma-debug") << "Get explanation..." << std::endl; - std::vector< TNode > assumptions; - //if( options::dtRExplainLemmas() ){ - explain( exp, assumptions ); - //}else{ - // ee_exp = exp; - //} - //Trace("dt-lemma-debug") << "Explanation : " << ee_exp << std::endl; - if( assumptions.empty() ){ - lem = fact; - }else{ - std::vector< Node > children; - for (const TNode& assumption : assumptions) - { - children.push_back(assumption.negate()); - } - children.push_back( fact ); - lem = NodeManager::currentNM()->mkNode( OR, children ); - } - } - Trace("dt-lemma") << "Datatypes lemma : " << lem << std::endl; - doSendLemma( lem ); - }else{ - assertFact( fact, exp ); - d_addedFact = true; - } - Trace("datatypes-debug") << "Finished fact " << fact << ", now = " << d_conflict << " " << d_pending.size() << std::endl; - i++; - } - d_pending.clear(); - d_pending_exp.clear(); -} - -bool TheoryDatatypes::doSendLemma( Node lem ) { - if( d_lemmas_produced_c.find( lem )==d_lemmas_produced_c.end() ){ - Trace("dt-lemma-send") << "TheoryDatatypes::doSendLemma : " << lem << std::endl; - d_lemmas_produced_c[lem] = true; - d_out->lemma( lem ); - d_addedLemma = true; - return true; - }else{ - Trace("dt-lemma-send") << "TheoryDatatypes::doSendLemma : duplicate : " - << lem << std::endl; - return false; - } -} -bool TheoryDatatypes::doSendLemmas( std::vector< Node >& lemmas ){ - bool ret = false; - for (const Node& lem : lemmas) - { - bool cret = doSendLemma(lem); - ret = ret || cret; - } - lemmas.clear(); - return ret; -} - -void TheoryDatatypes::assertFact( Node fact, Node exp) +void TheoryDatatypes::notifyFact(TNode atom, + bool polarity, + TNode fact, + bool isInternal) { - Trace("datatypes-debug") << "TheoryDatatypes::assertFact : " << fact << std::endl; - bool polarity = fact.getKind() != kind::NOT; - TNode atom = polarity ? fact : fact[0]; - if (atom.getKind() == kind::EQUAL) { - d_equalityEngine->assertEquality(atom, polarity, exp); - }else{ - d_equalityEngine->assertPredicate(atom, polarity, exp); - } // could be sygus-specific if (d_sygusExtension) { std::vector< Node > lemmas; d_sygusExtension->assertFact(atom, polarity, lemmas); - doSendLemmas( lemmas ); + d_im.sendLemmas(lemmas); } //add to tester if applicable Node t_arg; @@ -493,28 +384,37 @@ void TheoryDatatypes::assertFact( Node fact, Node exp) Trace("dt-tester") << "Assert tester : " << atom << " for " << t_arg << std::endl; Node rep = getRepresentative( t_arg ); EqcInfo* eqc = getOrMakeEqcInfo( rep, true ); - addTester( tindex, fact, eqc, rep, t_arg ); + Node tst = + isInternal ? (polarity ? Node(atom) : atom.notNode()) : Node(fact); + addTester(tindex, tst, eqc, rep, t_arg); Trace("dt-tester") << "Done assert tester." << std::endl; Trace("dt-tester") << "Done pending merges." << std::endl; - if( !d_conflict && polarity ){ + if (!d_state.isInConflict() && polarity) + { if (d_sygusExtension) { Trace("dt-tester") << "Assert tester to sygus : " << atom << std::endl; - //Assert( !d_sygus_util->d_conflict ); std::vector< Node > lemmas; d_sygusExtension->assertTester(tindex, t_arg, atom, lemmas); Trace("dt-tester") << "Done assert tester to sygus." << std::endl; - doSendLemmas( lemmas ); + d_im.sendLemmas(lemmas); } } }else{ Trace("dt-tester-debug") << "Assert (non-tester) : " << atom << std::endl; } Trace("datatypes-debug") << "TheoryDatatypes::assertFact : finished " << fact << std::endl; + // now, flush pending facts if this wasn't an internal call + if (!isInternal) + { + d_im.process(); + } } -void TheoryDatatypes::preRegisterTerm(TNode n) { - Debug("datatypes-prereg") << "TheoryDatatypes::preRegisterTerm() " << n << endl; +void TheoryDatatypes::preRegisterTerm(TNode n) +{ + Debug("datatypes-prereg") + << "TheoryDatatypes::preRegisterTerm() " << n << endl; collectTerms( n ); switch (n.getKind()) { case kind::EQUAL: @@ -530,12 +430,11 @@ void TheoryDatatypes::preRegisterTerm(TNode n) { { std::vector< Node > lemmas; d_sygusExtension->preRegisterTerm(n, lemmas); - doSendLemmas( lemmas ); + d_im.sendLemmas(lemmas); } - // d_equalityEngine->addTriggerTerm(n, THEORY_DATATYPES); break; } - flushPendingFacts(); + d_im.process(); } TrustNode TheoryDatatypes::expandDefinition(Node n) @@ -699,21 +598,13 @@ TrustNode TheoryDatatypes::ppRewrite(TNode in) return TrustNode::null(); } -void TheoryDatatypes::notifySharedTerm(TNode t) -{ - Debug("datatypes") << "TheoryDatatypes::notifySharedTerm(): " << t << " " - << t.getType().isBoolean() << endl; - d_equalityEngine->addTriggerTerm(t, THEORY_DATATYPES); - Debug("datatypes") << "TheoryDatatypes::notifySharedTerm() finished" - << std::endl; -} - bool TheoryDatatypes::propagateLit(TNode literal) { Debug("dt::propagate") << "TheoryDatatypes::propagateLit(" << literal << ")" << std::endl; // If already in conflict, no more propagation - if (d_conflict) { + if (d_state.isInConflict()) + { Debug("dt::propagate") << "TheoryDatatypes::propagateLit(" << literal << "): already in conflict" << std::endl; return false; @@ -723,7 +614,7 @@ bool TheoryDatatypes::propagateLit(TNode literal) bool ok = d_out->propagate(literal); if (!ok) { Trace("dt-conflict") << "CONFLICT: Eq engine propagate conflict " << std::endl; - d_conflict = true; + d_state.notifyInConflict(); } return ok; } @@ -805,7 +696,7 @@ void TheoryDatatypes::conflict(TNode a, TNode b){ d_conflictNode = explainLit(eq); Trace("dt-conflict") << "CONFLICT: Eq engine conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); - d_conflict = true; + d_state.notifyInConflict(); } /** called when a new equivalance class is created */ @@ -826,7 +717,8 @@ void TheoryDatatypes::eqNotifyMerge(TNode t1, TNode t2) } void TheoryDatatypes::merge( Node t1, Node t2 ){ - if( !d_conflict ){ + if (!d_state.isInConflict()) + { TNode trep1 = t1; TNode trep2 = t2; Trace("datatypes-debug") << "Merge " << t1 << " " << t2 << std::endl; @@ -855,7 +747,7 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ d_conflictNode = explainLit(unifEq); Trace("dt-conflict") << "CONFLICT: Clash conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); - d_conflict = true; + d_state.notifyInConflict(); return; } else @@ -864,22 +756,10 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ for( int i=0; i<(int)cons1.getNumChildren(); i++ ) { if( !areEqual( cons1[i], cons2[i] ) ){ Node eq = cons1[i].eqNode( cons2[i] ); - d_pending.push_back( eq ); - d_pending_exp[ eq ] = unifEq; + d_im.addPendingInference(eq, unifEq); Trace("datatypes-infer") << "DtInfer : cons-inj : " << eq << " by " << unifEq << std::endl; - d_infer.push_back( eq ); - d_infer_exp.push_back( unifEq ); } } -/* - for( unsigned i=0; i<rew.size(); i++ ){ - d_pending.push_back( rew[i] ); - d_pending_exp[ rew[i] ] = unifEq; - Trace("datatypes-infer") << "DtInfer : cons-inj : " << rew[i] << " by " << unifEq << std::endl; - d_infer.push_back( rew[i] ); - d_infer_exp.push_back( unifEq ); - } -*/ } } Trace("datatypes-debug") << " instantiated : " << eqc1->d_inst << " " << eqc2->d_inst << std::endl; @@ -889,7 +769,8 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ Trace("datatypes-debug") << " must check if it is okay to set the constructor." << std::endl; checkInst = true; addConstructor( eqc2->d_constructor.get(), eqc1, t1 ); - if( d_conflict ){ + if (d_state.isInConflict()) + { return; } } @@ -916,7 +797,8 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ Node t_arg = d_labels_args[t2][i]; unsigned tindex = d_labels_tindex[t2][i]; addTester( tindex, t, eqc1, t1, t_arg ); - if( d_conflict ){ + if (d_state.isInConflict()) + { Trace("datatypes-debug") << " conflict!" << std::endl; return; } @@ -940,7 +822,8 @@ void TheoryDatatypes::merge( Node t1, Node t2 ){ if( checkInst ){ Trace("datatypes-debug") << " checking instantiate" << std::endl; instantiate( eqc1, t1 ); - if( d_conflict ){ + if (d_state.isInConflict()) + { return; } } @@ -979,6 +862,8 @@ int TheoryDatatypes::getLabelIndex( EqcInfo* eqc, Node n ){ return -1; }else{ int tindex = utils::isTester(lbl); + Trace("datatypes-debug") << "Label of " << n << " is " << lbl + << " with tindex " << tindex << std::endl; Assert(tindex != -1); return tindex; } @@ -1033,9 +918,7 @@ Node TheoryDatatypes::getTermSkolemFor( Node n ) { d_term_sk[n] = k; Node eq = k.eqNode( n ); Trace("datatypes-infer") << "DtInfer : ref : " << eq << std::endl; - d_pending_lem.push_back( eq ); - //doSendLemma( eq ); - //d_pending_exp[ eq ] = d_true; + d_im.addPendingLemma(eq); return k; }else{ return (*it).second; @@ -1051,6 +934,7 @@ void TheoryDatatypes::addTester( Trace("datatypes-debug") << "Add tester : " << t << " to eqc(" << n << ")" << std::endl; Debug("datatypes-labels") << "Add tester " << t << " " << n << " " << eqc << std::endl; bool tpolarity = t.getKind()!=NOT; + Assert((tpolarity ? t : t[0]).getKind() == APPLY_TESTER); Node j, jt; bool makeConflict = false; int prevTIndex = getLabelIndex(eqc, n); @@ -1068,7 +952,7 @@ void TheoryDatatypes::addTester( d_conflictNode = mkAnd( assumptions ); Trace("dt-conflict") << "CONFLICT: Tester eq conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); - d_conflict = true; + d_state.notifyInConflict(); return; }else{ makeConflict = true; @@ -1122,16 +1006,8 @@ void TheoryDatatypes::addTester( Debug("datatypes-labels") << "Labels at " << n_lbl << " / " << dt.getNumConstructors() << std::endl; if( tpolarity ){ instantiate( eqc, n ); - for (unsigned i = 0, ncons = dt.getNumConstructors(); i < ncons; i++) - { - if( i!=ttindex && neg_testers.find( i )==neg_testers.end() ){ - Assert(n.getKind() != APPLY_CONSTRUCTOR); - Node infer = utils::mkTester(n, i, dt).negate(); - Trace("datatypes-infer") << "DtInfer : neg label : " << infer << " by " << t << std::endl; - d_infer.push_back( infer ); - d_infer_exp.push_back( t ); - } - } + // We could propagate is-C1(x) => not is-C2(x) here for all other + // constructors, but empirically this hurts performance. }else{ //check if we have reached the maximum number of testers // in this case, add the positive tester @@ -1167,18 +1043,15 @@ void TheoryDatatypes::addTester( ? NodeManager::currentNM()->mkConst(false) : utils::mkTester(t_arg, testerIndex, dt); Node t_concl_exp = ( nb.getNumChildren() == 1 ) ? nb.getChild( 0 ) : nb; - d_pending.push_back( t_concl ); - d_pending_exp[ t_concl ] = t_concl_exp; + d_im.addPendingInference(t_concl, t_concl_exp); Trace("datatypes-infer") << "DtInfer : label : " << t_concl << " by " << t_concl_exp << std::endl; - d_infer.push_back( t_concl ); - d_infer_exp.push_back( t_concl_exp ); return; } } } } if( makeConflict ){ - d_conflict = true; + d_state.notifyInConflict(); Debug("datatypes-labels") << "Explain " << j << " " << t << std::endl; std::vector< TNode > assumptions; explain( j, assumptions ); @@ -1245,7 +1118,7 @@ void TheoryDatatypes::addConstructor( Node c, EqcInfo* eqc, Node n ){ d_conflictNode = mkAnd( assumptions ); Trace("dt-conflict") << "CONFLICT: Tester merge eq conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); - d_conflict = true; + d_state.notifyInConflict(); return; } } @@ -1332,10 +1205,7 @@ void TheoryDatatypes::collapseSelector( Node s, Node c ) { Trace("datatypes-infer") << "DtInfer : collapse sel"; //Trace("datatypes-infer") << ( wrong ? " wrong" : ""); Trace("datatypes-infer") << " : " << eq << " by " << peq << std::endl; - d_pending.push_back( eq ); - d_pending_exp[eq] = peq; - d_infer.push_back( eq ); - d_infer_exp.push_back(peq); + d_im.addPendingInference(eq, peq); } } } @@ -1483,25 +1353,15 @@ void TheoryDatatypes::computeCareGraph(){ Trace("dt-cg-summary") << "...done, # pairs = " << n_pairs << std::endl; } -bool TheoryDatatypes::collectModelInfo(TheoryModel* m) +bool TheoryDatatypes::collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) { - Trace("dt-cmi") << "Datatypes : Collect model info " + Trace("dt-cmi") << "Datatypes : Collect model values " << d_equalityEngine->consistent() << std::endl; Trace("dt-model") << std::endl; printModelDebug( "dt-model" ); Trace("dt-model") << std::endl; - std::set<Node> termSet; - - // Compute terms appearing in assertions and shared terms, and in inferred equalities - computeRelevantTerms(termSet); - - //combine the equality engine - if (!m->assertEqualityEngine(d_equalityEngine, &termSet)) - { - return false; - } - //get all constructors eq::EqClassesIterator eqccs_i = eq::EqClassesIterator(d_equalityEngine); std::vector< Node > cons; @@ -1649,7 +1509,7 @@ Node TheoryDatatypes::getSingletonLemma( TypeNode tn, bool pol ) { Node v2 = NodeManager::currentNM()->mkSkolem( "k2", tn ); a = v1.eqNode( v2 ).negate(); //send out immediately as lemma - doSendLemma( a ); + d_im.lemma(a); Trace("dt-singleton") << "******** assert " << a << " to avoid singleton cardinality for type " << tn << std::endl; } d_singleton_lemma[index][tn] = a; @@ -1707,7 +1567,7 @@ void TheoryDatatypes::collectTerms( Node n ) { Node lem = nm->mkNode(LEQ, d_zero, n); Trace("datatypes-infer") << "DtInfer : size geq zero : " << lem << std::endl; - d_pending_lem.push_back(lem); + d_im.addPendingLemma(lem); } else if (nk == DT_HEIGHT_BOUND && n[1].getConst<Rational>().isZero()) { @@ -1732,7 +1592,7 @@ void TheoryDatatypes::collectTerms( Node n ) { : nm->mkNode(OR, children)); } Trace("datatypes-infer") << "DtInfer : zero height : " << lem << std::endl; - d_pending_lem.push_back(lem); + d_im.addPendingLemma(lem); } } @@ -1761,6 +1621,7 @@ Node TheoryDatatypes::getInstantiateCons(Node n, const DType& dt, int index) } void TheoryDatatypes::instantiate( EqcInfo* eqc, Node n ){ + Trace("datatypes-debug") << "Instantiate: " << n << std::endl; //add constructor to equivalence class if not done so already int index = getLabelIndex( eqc, n ); if (index == -1 || eqc->d_inst) @@ -1791,13 +1652,10 @@ void TheoryDatatypes::instantiate( EqcInfo* eqc, Node n ){ eq = tt.eqNode(tt_cons); Debug("datatypes-inst") << "DtInstantiate : " << eqc << " " << eq << std::endl; - d_pending.push_back(eq); - d_pending_exp[eq] = exp; + d_im.addPendingInference(eq, exp); Trace("datatypes-infer-debug") << "inst : " << eqc << " " << n << std::endl; Trace("datatypes-infer") << "DtInfer : instantiate : " << eq << " by " << exp << std::endl; - d_infer.push_back(eq); - d_infer_exp.push_back(exp); } void TheoryDatatypes::checkCycles() { @@ -1832,7 +1690,7 @@ void TheoryDatatypes::checkCycles() { d_conflictNode = mkAnd( expl ); Trace("dt-conflict") << "CONFLICT: Cycle conflict : " << d_conflictNode << std::endl; d_out->conflict( d_conflictNode ); - d_conflict = true; + d_state.notifyInConflict(); return; } } @@ -1882,11 +1740,8 @@ void TheoryDatatypes::checkCycles() { Trace("dt-cdt") << std::endl; Node eq = part_out[i][0].eqNode( part_out[i][j] ); Node eqExp = mkAnd( exp ); - d_pending.push_back( eq ); - d_pending_exp[ eq ] = eqExp; + d_im.addPendingInference(eq, eqExp); Trace("datatypes-infer") << "DtInfer : cdt-bisimilar : " << eq << " by " << eqExp << std::endl; - d_infer.push_back( eq ); - d_infer_exp.push_back( eqExp ); } } } @@ -2061,39 +1916,6 @@ Node TheoryDatatypes::searchForCycle( TNode n, TNode on, } } -bool TheoryDatatypes::mustCommunicateFact( Node n, Node exp ){ - //the datatypes decision procedure makes "internal" inferences apart from the equality engine : - // (1) Unification : C( t1...tn ) = C( s1...sn ) => ti = si - // (2) Label : ~is_C1( t ) ... ~is_C{i-1}( t ) ~is_C{i+1}( t ) ... ~is_Cn( t ) => is_Ci( t ) - // (3) Instantiate : is_C( t ) => t = C( sel_1( t ) ... sel_n( t ) ) - // (4) collapse selector : S( C( t1...tn ) ) = t' - // (5) collapse term size : size( C( t1...tn ) ) = 1 + size( t1 ) + ... + size( tn ) - // (6) non-negative size : 0 <= size( t ) - //We may need to communicate outwards if the conclusions involve other theories. Also communicate (6) and OR conclusions. - Trace("dt-lemma-debug") << "Compute for " << exp << " => " << n << std::endl; - bool addLemma = false; - if( options::dtInferAsLemmas() && exp!=d_true ){ - addLemma = true; - }else if( n.getKind()==EQUAL ){ - TypeNode tn = n[0].getType(); - if( !tn.isDatatype() ){ - addLemma = true; - }else{ - const DType& dt = tn.getDType(); - addLemma = dt.involvesExternalType(); - } - }else if( n.getKind()==LEQ || n.getKind()==OR ){ - addLemma = true; - } - if( addLemma ){ - Trace("dt-lemma-debug") << "Communicate " << n << std::endl; - return true; - }else{ - Trace("dt-lemma-debug") << "Do not need to communicate " << n << std::endl; - return false; - } -} - bool TheoryDatatypes::hasTerm(TNode a) { return d_equalityEngine->hasTerm(a); } bool TheoryDatatypes::areEqual( TNode a, TNode b ){ @@ -2218,15 +2040,8 @@ Node TheoryDatatypes::mkAnd( std::vector< TNode >& assumptions ) { } } -void TheoryDatatypes::computeRelevantTerms(std::set<Node>& termSet, - bool includeShared) +void TheoryDatatypes::computeRelevantTerms(std::set<Node>& termSet) { - // Compute terms appearing in assertions and shared terms - std::set<Kind> irrKinds; - // testers are not relevant for model construction - irrKinds.insert(APPLY_TESTER); - computeRelevantTermsInternal(termSet, irrKinds, includeShared); - Trace("dt-cmi") << "Have " << termSet.size() << " relevant terms..." << std::endl; @@ -2267,8 +2082,6 @@ void TheoryDatatypes::computeRelevantTerms(std::set<Node>& termSet, } ++eqcs_i; } - Trace("dt-cmi") << "After adding non-singletons, has " << termSet.size() - << " relevant terms..." << std::endl; } std::pair<bool, Node> TheoryDatatypes::entailmentCheck(TNode lit) diff --git a/src/theory/datatypes/theory_datatypes.h b/src/theory/datatypes/theory_datatypes.h index 0d5df098d..37a4f81f7 100644 --- a/src/theory/datatypes/theory_datatypes.h +++ b/src/theory/datatypes/theory_datatypes.h @@ -26,6 +26,7 @@ #include "expr/attribute.h" #include "expr/node_trie.h" #include "theory/datatypes/datatypes_rewriter.h" +#include "theory/datatypes/inference_manager.h" #include "theory/datatypes/sygus_extension.h" #include "theory/theory.h" #include "theory/uf/equality_engine.h" @@ -43,9 +44,6 @@ class TheoryDatatypes : public Theory { typedef context::CDHashMap<Node, bool, NodeHashFunction> BoolMap; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeMap; - /** inferences */ - NodeList d_infer; - NodeList d_infer_exp; Node d_true; Node d_zero; /** mkAnd */ @@ -70,6 +68,7 @@ class TheoryDatatypes : public Theory { TNode t2, bool value) override { + AlwaysAssert(tag == THEORY_DATATYPES); Debug("dt") << "NotifyClass::eqNotifyTriggerTermMerge(" << tag << ", " << t1 << ", " << t2 << ")" << std::endl; if (value) { return d_dt.propagateLit(t1.eqNode(t2)); @@ -169,20 +168,6 @@ private: /** selector apps for eqch equivalence class */ NodeUIntMap d_selector_apps; std::map< Node, std::vector< Node > > d_selector_apps_data; - /** Are we in conflict */ - context::CDO<bool> d_conflict; - /** added lemma - * - * This flag is set to true during a full effort check if this theory - * called d_out->lemma(...). - */ - bool d_addedLemma; - /** added fact - * - * This flag is set to true during a full effort check if this theory - * added an internal fact to its equality engine. - */ - bool d_addedFact; /** The conflict node */ Node d_conflictNode; /** @@ -195,10 +180,6 @@ private: * collectTerms(...) on. */ BoolMap d_collectTermsCacheU; - /** pending assertions/merges */ - std::vector< Node > d_pending_lem; - std::vector< Node > d_pending; - std::map< Node, Node > d_pending_exp; /** All the function terms that the theory has seen */ context::CDList<TNode> d_functionTerms; /** counter for forcing assignments (ensures fairness) */ @@ -218,12 +199,6 @@ private: /** assert fact */ void assertFact( Node fact, Node exp ); - /** flush pending facts */ - void flushPendingFacts(); - - /** do send lemma */ - bool doSendLemma( Node lem ); - bool doSendLemmas( std::vector< Node >& lem ); /** get or make eqc info */ EqcInfo* getOrMakeEqcInfo( TNode n, bool doMake = false ); @@ -263,9 +238,10 @@ private: /** finish initialization */ void finishInit() override; //--------------------------------- end initialization - /** propagate */ bool propagateLit(TNode literal); + /** Conflict when merging two constants */ + void conflict(TNode a, TNode b); /** explain */ void addAssumptions( std::vector<TNode>& assumptions, std::vector<TNode>& tassumptions ); void explainEquality( TNode a, TNode b, bool polarity, std::vector<TNode>& assumptions ); @@ -274,22 +250,25 @@ private: TrustNode explain(TNode literal) override; Node explainLit(TNode literal); Node explain( std::vector< Node >& lits ); - /** Conflict when merging two constants */ - void conflict(TNode a, TNode b); /** called when a new equivalance class is created */ void eqNotifyNewClass(TNode t); /** called when two equivalance classes have merged */ void eqNotifyMerge(TNode t1, TNode t2); - void check(Effort e) override; + //--------------------------------- standard check + /** Do we need a check call at last call effort? */ bool needsCheckLastEffort() override; + /** Pre-check, called before the fact queue of the theory is processed. */ + bool preCheck(Effort level) override; + /** Post-check, called after the fact queue of the theory is processed. */ + void postCheck(Effort level) override; + /** Notify fact */ + void notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) override; + //--------------------------------- end standard check void preRegisterTerm(TNode n) override; TrustNode expandDefinition(Node n) override; TrustNode ppRewrite(TNode n) override; - void notifySharedTerm(TNode t) override; EqualityStatus getEqualityStatus(TNode a, TNode b) override; - bool collectModelInfo(TheoryModel* m) override; - void shutdown() override {} std::string identify() const override { return std::string("TheoryDatatypes"); @@ -336,8 +315,6 @@ private: Node getInstantiateCons(Node n, const DType& dt, int index); /** check instantiate */ void instantiate( EqcInfo* eqc, Node n ); - /** must communicate fact */ - bool mustCommunicateFact( Node n, Node exp ); private: //equality queries bool hasTerm( TNode a ); @@ -346,12 +323,14 @@ private: bool areCareDisequal( TNode x, TNode y ); TNode getRepresentative( TNode a ); + /** Collect model values in m based on the relevant terms given by termSet */ + bool collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) override; /** - * Compute relevant terms. In addition to all terms in assertions and shared - * terms, this includes datatypes in non-singleton equivalence classes. + * Compute relevant terms. This includes datatypes in non-singleton + * equivalence classes. */ - void computeRelevantTerms(std::set<Node>& termSet, - bool includeShared = true) override; + void computeRelevantTerms(std::set<Node>& termSet) override; /** sygus symmetry breaking utility */ std::unique_ptr<SygusExtension> d_sygusExtension; @@ -360,6 +339,8 @@ private: DatatypesRewriter d_rewriter; /** A (default) theory state object */ TheoryState d_state; + /** The inference manager */ + InferenceManager d_im; };/* class TheoryDatatypes */ }/* CVC4::theory::datatypes namespace */ diff --git a/src/theory/engine_output_channel.cpp b/src/theory/engine_output_channel.cpp index a271d6d9c..b6d9a19db 100644 --- a/src/theory/engine_output_channel.cpp +++ b/src/theory/engine_output_channel.cpp @@ -14,10 +14,6 @@ #include "theory/engine_output_channel.h" -#include "proof/cnf_proof.h" -#include "proof/lemma_proof.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" #include "prop/prop_engine.h" #include "smt/smt_statistics_registry.h" #include "theory/theory_engine.h" @@ -71,9 +67,7 @@ void EngineOutputChannel::safePoint(ResourceManager::Resource r) } } -theory::LemmaStatus EngineOutputChannel::lemma(TNode lemma, - ProofRule rule, - LemmaProperty p) +theory::LemmaStatus EngineOutputChannel::lemma(TNode lemma, LemmaProperty p) { Debug("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" << lemma << ")" @@ -81,151 +75,15 @@ theory::LemmaStatus EngineOutputChannel::lemma(TNode lemma, ++d_statistics.lemmas; d_engine->d_outputChannelUsed = true; - PROOF({ - bool preprocess = isLemmaPropertyPreprocess(p); - registerLemmaRecipe(lemma, lemma, preprocess, d_theory); - }); - TrustNode tlem = TrustNode::mkTrustLemma(lemma); theory::LemmaStatus result = d_engine->lemma( tlem.getNode(), - rule, false, p, isLemmaPropertySendAtoms(p) ? d_theory : theory::THEORY_LAST); return result; } -void EngineOutputChannel::registerLemmaRecipe(Node lemma, - Node originalLemma, - bool preprocess, - theory::TheoryId theoryId) -{ - // During CNF conversion, conjunctions will be broken down into - // multiple lemmas. In order for the recipes to match, we have to do - // the same here. - NodeManager* nm = NodeManager::currentNM(); - - if (preprocess) lemma = d_engine->preprocess(lemma); - - bool negated = (lemma.getKind() == NOT); - Node nnLemma = negated ? lemma[0] : lemma; - - switch (nnLemma.getKind()) - { - case AND: - if (!negated) - { - for (unsigned i = 0; i < nnLemma.getNumChildren(); ++i) - registerLemmaRecipe(nnLemma[i], originalLemma, false, theoryId); - } - else - { - NodeBuilder<> builder(OR); - for (unsigned i = 0; i < nnLemma.getNumChildren(); ++i) - builder << nnLemma[i].negate(); - - Node disjunction = - (builder.getNumChildren() == 1) ? builder[0] : builder; - registerLemmaRecipe(disjunction, originalLemma, false, theoryId); - } - break; - - case EQUAL: - if (nnLemma[0].getType().isBoolean()) - { - if (!negated) - { - registerLemmaRecipe(nm->mkNode(OR, nnLemma[0], nnLemma[1].negate()), - originalLemma, - false, - theoryId); - registerLemmaRecipe(nm->mkNode(OR, nnLemma[0].negate(), nnLemma[1]), - originalLemma, - false, - theoryId); - } - else - { - registerLemmaRecipe(nm->mkNode(OR, nnLemma[0], nnLemma[1]), - originalLemma, - false, - theoryId); - registerLemmaRecipe( - nm->mkNode(OR, nnLemma[0].negate(), nnLemma[1].negate()), - originalLemma, - false, - theoryId); - } - } - break; - - case ITE: - if (!negated) - { - registerLemmaRecipe(nm->mkNode(OR, nnLemma[0].negate(), nnLemma[1]), - originalLemma, - false, - theoryId); - registerLemmaRecipe(nm->mkNode(OR, nnLemma[0], nnLemma[2]), - originalLemma, - false, - theoryId); - } - else - { - registerLemmaRecipe( - nm->mkNode(OR, nnLemma[0].negate(), nnLemma[1].negate()), - originalLemma, - false, - theoryId); - registerLemmaRecipe(nm->mkNode(OR, nnLemma[0], nnLemma[2].negate()), - originalLemma, - false, - theoryId); - } - break; - - default: break; - } - - // Theory lemmas have one step that proves the empty clause - LemmaProofRecipe proofRecipe; - Node emptyNode; - LemmaProofRecipe::ProofStep proofStep(theoryId, emptyNode); - - // Remember the original lemma, so we can report this later when asked to - proofRecipe.setOriginalLemma(originalLemma); - - // Record the assertions and rewrites - Node rewritten; - if (lemma.getKind() == OR) - { - for (unsigned i = 0; i < lemma.getNumChildren(); ++i) - { - rewritten = theory::Rewriter::rewrite(lemma[i]); - if (rewritten != lemma[i]) - { - proofRecipe.addRewriteRule(lemma[i].negate(), rewritten.negate()); - } - proofStep.addAssertion(lemma[i]); - proofRecipe.addBaseAssertion(rewritten); - } - } - else - { - rewritten = theory::Rewriter::rewrite(lemma); - if (rewritten != lemma) - { - proofRecipe.addRewriteRule(lemma.negate(), rewritten.negate()); - } - proofStep.addAssertion(lemma); - proofRecipe.addBaseAssertion(rewritten); - } - proofRecipe.addStep(proofStep); - ProofManager::getCnfProof()->setProofRecipe(&proofRecipe); -} - theory::LemmaStatus EngineOutputChannel::splitLemma(TNode lemma, bool removable) { Debug("theory::lemma") << "EngineOutputChannel<" << d_theory << ">::lemma(" @@ -238,7 +96,7 @@ theory::LemmaStatus EngineOutputChannel::splitLemma(TNode lemma, bool removable) TrustNode tlem = TrustNode::mkTrustLemma(lemma); LemmaProperty p = removable ? LemmaProperty::REMOVABLE : LemmaProperty::NONE; theory::LemmaStatus result = - d_engine->lemma(tlem.getNode(), RULE_SPLIT, false, p, d_theory); + d_engine->lemma(tlem.getNode(), false, p, d_theory); return result; } @@ -251,13 +109,11 @@ bool EngineOutputChannel::propagate(TNode literal) return d_engine->propagate(literal, d_theory); } -void EngineOutputChannel::conflict(TNode conflictNode, - std::unique_ptr<Proof> proof) +void EngineOutputChannel::conflict(TNode conflictNode) { Trace("theory::conflict") << "EngineOutputChannel<" << d_theory << ">::conflict(" << conflictNode << ")" << std::endl; - Assert(!proof); // Theory shouldn't be producing proofs yet ++d_statistics.conflicts; d_engine->d_outputChannelUsed = true; TrustNode tConf = TrustNode::mkTrustConflict(conflictNode); @@ -274,7 +130,7 @@ void EngineOutputChannel::demandRestart() Trace("theory::restart") << "EngineOutputChannel<" << d_theory << ">::restart(" << restartVar << ")" << std::endl; ++d_statistics.restartDemands; - lemma(restartVar, RULE_INVALID, LemmaProperty::REMOVABLE); + lemma(restartVar, LemmaProperty::REMOVABLE); } void EngineOutputChannel::requirePhase(TNode n, bool phase) @@ -329,7 +185,6 @@ LemmaStatus EngineOutputChannel::trustedLemma(TrustNode plem, LemmaProperty p) // now, call the normal interface for lemma return d_engine->lemma( plem.getNode(), - RULE_INVALID, false, p, isLemmaPropertySendAtoms(p) ? d_theory : theory::THEORY_LAST); diff --git a/src/theory/engine_output_channel.h b/src/theory/engine_output_channel.h index 3e959898f..99f812ed4 100644 --- a/src/theory/engine_output_channel.h +++ b/src/theory/engine_output_channel.h @@ -45,12 +45,10 @@ class EngineOutputChannel : public theory::OutputChannel void safePoint(ResourceManager::Resource r) override; - void conflict(TNode conflictNode, - std::unique_ptr<Proof> pf = nullptr) override; + void conflict(TNode conflictNode) override; bool propagate(TNode literal) override; theory::LemmaStatus lemma(TNode lemma, - ProofRule rule, LemmaProperty p = LemmaProperty::NONE) override; theory::LemmaStatus splitLemma(TNode lemma, bool removable = false) override; diff --git a/src/theory/ext_theory.h b/src/theory/ext_theory.h index 420932bfe..2721bc89e 100644 --- a/src/theory/ext_theory.h +++ b/src/theory/ext_theory.h @@ -36,6 +36,7 @@ #include <map> #include <set> +#include "context/cdhashmap.h" #include "context/cdhashset.h" #include "context/context.h" #include "expr/node.h" diff --git a/src/theory/fp/theory_fp.cpp b/src/theory/fp/theory_fp.cpp index 4c59b1c06..5eb9e576d 100644 --- a/src/theory/fp/theory_fp.cpp +++ b/src/theory/fp/theory_fp.cpp @@ -1023,7 +1023,8 @@ bool TheoryFp::collectModelInfo(TheoryModel* m) { std::set<Node> relevantTerms; // Work out which variables are needed - computeRelevantTerms(relevantTerms); + const std::set<Kind>& irrKinds = m->getIrrelevantKinds(); + computeAssertedTerms(relevantTerms, irrKinds); // this override behavior to not assert equality engine return collectModelValues(m, relevantTerms); } diff --git a/src/theory/inference_manager_buffered.cpp b/src/theory/inference_manager_buffered.cpp index adbcc3033..8a7713121 100644 --- a/src/theory/inference_manager_buffered.cpp +++ b/src/theory/inference_manager_buffered.cpp @@ -49,24 +49,34 @@ void InferenceManagerBuffered::addPendingLemma(Node lem, LemmaProperty p, ProofGenerator* pg) { - d_pendingLem.push_back(std::make_shared<Lemma>(lem, p, pg)); + // make the simple theory lemma + d_pendingLem.emplace_back(new SimpleTheoryLemma(lem, p, pg)); } -void InferenceManagerBuffered::addPendingLemma(std::shared_ptr<Lemma> lemma) +void InferenceManagerBuffered::addPendingLemma( + std::unique_ptr<TheoryInference> lemma) { d_pendingLem.emplace_back(std::move(lemma)); } -void InferenceManagerBuffered::addPendingFact(Node fact, Node exp) +void InferenceManagerBuffered::addPendingFact(Node conc, + Node exp, + ProofGenerator* pg) { - Assert(fact.getKind() != AND && fact.getKind() != OR); - d_pendingFact.push_back(std::pair<Node, Node>(fact, exp)); + // make a simple theory internal fact + Assert(conc.getKind() != AND && conc.getKind() != OR); + d_pendingFact.emplace_back(new SimpleTheoryInternalFact(conc, exp, pg)); +} + +void InferenceManagerBuffered::addPendingFact( + std::unique_ptr<TheoryInference> fact) +{ + d_pendingFact.emplace_back(std::move(fact)); } void InferenceManagerBuffered::addPendingPhaseRequirement(Node lit, bool pol) { - // must ensure rewritten - lit = Rewriter::rewrite(lit); + // it is the responsibility of the caller to ensure lit is rewritten d_pendingReqPhase[lit] = pol; } @@ -75,14 +85,9 @@ void InferenceManagerBuffered::doPendingFacts() size_t i = 0; while (!d_theoryState.isInConflict() && i < d_pendingFact.size()) { - std::pair<Node, Node>& pfact = d_pendingFact[i]; - Node fact = pfact.first; - Node exp = pfact.second; - bool polarity = fact.getKind() != NOT; - TNode atom = polarity ? fact : fact[0]; - // no double negation or conjunctive conclusions - Assert(atom.getKind() != NOT && atom.getKind() != AND); - assertInternalFact(atom, polarity, exp); + // process this fact, which notice may enqueue more pending facts in this + // loop. + d_pendingFact[i]->process(this); i++; } d_pendingFact.clear(); @@ -90,20 +95,10 @@ void InferenceManagerBuffered::doPendingFacts() void InferenceManagerBuffered::doPendingLemmas() { - // process all the pending lemmas - for (const std::shared_ptr<Lemma>& plem : d_pendingLem) + for (const std::unique_ptr<TheoryInference>& plem : d_pendingLem) { - if (!plem->notifySend()) - { - // the lemma indicated that it should not be sent after all - continue; - } - Node lem = plem->d_node; - LemmaProperty p = plem->d_property; - ProofGenerator* pg = plem->d_pg; - Assert(!lem.isNull()); - // send (trusted) lemma on the output channel with property p - trustedLemma(TrustNode::mkTrustLemma(lem, pg), p); + // process this lemma + plem->process(this); } d_pendingLem.clear(); } @@ -113,10 +108,24 @@ void InferenceManagerBuffered::doPendingPhaseRequirements() // process the pending require phase calls for (const std::pair<const Node, bool>& prp : d_pendingReqPhase) { - d_out.requirePhase(prp.first, prp.second); + requirePhase(prp.first, prp.second); } d_pendingReqPhase.clear(); } +void InferenceManagerBuffered::clearPendingFacts() { d_pendingFact.clear(); } +void InferenceManagerBuffered::clearPendingLemmas() { d_pendingLem.clear(); } +void InferenceManagerBuffered::clearPendingPhaseRequirements() +{ + d_pendingReqPhase.clear(); +} + + + std::size_t InferenceManagerBuffered::numPendingLemmas() const { + return d_pendingLem.size(); + } + std::size_t InferenceManagerBuffered::numPendingFacts() const { + return d_pendingFact.size(); + } } // namespace theory } // namespace CVC4 diff --git a/src/theory/inference_manager_buffered.h b/src/theory/inference_manager_buffered.h index bb46ef566..62c3c9b55 100644 --- a/src/theory/inference_manager_buffered.h +++ b/src/theory/inference_manager_buffered.h @@ -19,45 +19,13 @@ #include "context/cdhashmap.h" #include "expr/node.h" +#include "theory/theory_inference.h" #include "theory/theory_inference_manager.h" namespace CVC4 { namespace theory { /** - * A lemma base class. This class is an abstract data structure for storing - * pending lemmas in the inference manager below. - */ -class Lemma -{ - public: - Lemma(Node n, LemmaProperty p, ProofGenerator* pg) - : d_node(n), d_property(p), d_pg(pg) - { - } - virtual ~Lemma() {} - /** - * Called just before this lemma is sent on the output channel. The purpose - * of this callback is to do any specific process of the lemma, e.g. take - * debug statistics, cache, etc. - * - * @return true if the lemma should be sent on the output channel. - */ - virtual bool notifySend() { return true; } - /** The lemma to send */ - Node d_node; - /** The lemma property (see OutputChannel::lemma) */ - LemmaProperty d_property; - /** - * The proof generator for this lemma, which if non-null, is wrapped in a - * TrustNode to be set on the output channel via trustedLemma at the time - * the lemma is sent. This proof generator must be able to provide a proof - * for d_node in the remainder of the user context. - */ - ProofGenerator* d_pg; -}; - -/** * The buffered inference manager. This class implements standard methods * for buffering facts, lemmas and phase requirements. */ @@ -67,7 +35,7 @@ class InferenceManagerBuffered : public TheoryInferenceManager InferenceManagerBuffered(Theory& t, TheoryState& state, ProofNodeManager* pnm); - ~InferenceManagerBuffered() {} + virtual ~InferenceManagerBuffered() {} /** * Have we processed an inference during this call to check? In particular, * this returns true if we have a pending fact or lemma, or have encountered @@ -92,21 +60,25 @@ class InferenceManagerBuffered : public TheoryInferenceManager ProofGenerator* pg = nullptr); /** * Add pending lemma, where lemma can be a (derived) class of the - * above one. Pending lemmas are sent to the output channel using - * doPendingLemmas. + * theory inference base class. */ - void addPendingLemma(std::shared_ptr<Lemma> lemma); + void addPendingLemma(std::unique_ptr<TheoryInference> lemma); /** * Add pending fact, which adds a fact on the pending fact queue. It must * be the case that: - * (1) exp => fact is valid, + * (1) exp => conc is valid, * (2) exp is a literal (or conjunction of literals) that holds in the * equality engine of the theory. * * Pending facts are sent to the equality engine of this class using * doPendingFacts. */ - void addPendingFact(Node fact, Node exp); + void addPendingFact(Node conc, Node exp, ProofGenerator* pg = nullptr); + /** + * Add pending fact, where fact can be a (derived) class of the + * theory inference base class. + */ + void addPendingFact(std::unique_ptr<TheoryInference> fact); /** Add pending phase requirement * * This method is called to indicate this class should send a phase @@ -145,12 +117,23 @@ class InferenceManagerBuffered : public TheoryInferenceManager * phase requirements and clears d_pendingReqPhase. */ void doPendingPhaseRequirements(); + /** Clear pending facts, without processing */ + void clearPendingFacts(); + /** Clear pending lemmas, without processing */ + void clearPendingLemmas(); + /** Clear pending phase requirements, without processing */ + void clearPendingPhaseRequirements(); + + /** Returns the number of pending lemmas. */ + std::size_t numPendingLemmas() const; + /** Returns the number of pending facts. */ + std::size_t numPendingFacts() const; protected: - /** A set of pending lemmas */ - std::vector<std::shared_ptr<Lemma>> d_pendingLem; - /** A set of pending facts, paired with their explanations */ - std::vector<std::pair<Node, Node>> d_pendingFact; + /** A set of pending inferences to be processed as lemmas */ + std::vector<std::unique_ptr<TheoryInference>> d_pendingLem; + /** A set of pending inferences to be processed as facts */ + std::vector<std::unique_ptr<TheoryInference>> d_pendingFact; /** A map from literals to their pending phase requirement */ std::map<Node, bool> d_pendingReqPhase; }; diff --git a/src/theory/logic_info.cpp b/src/theory/logic_info.cpp index 9805f602e..878815811 100644 --- a/src/theory/logic_info.cpp +++ b/src/theory/logic_info.cpp @@ -23,6 +23,7 @@ #include <string> #include "base/check.h" +#include "base/configuration.h" #include "expr/kind.h" using namespace std; @@ -43,7 +44,12 @@ LogicInfo::LogicInfo() d_higherOrder(true), d_locked(false) { - for(TheoryId id = THEORY_FIRST; id < THEORY_LAST; ++id) { + for (TheoryId id = THEORY_FIRST; id < THEORY_LAST; ++id) + { + if (id == THEORY_FP && !Configuration::isBuiltWithSymFPU()) + { + continue; + } enableTheory(id); } } diff --git a/src/theory/mkrewriter b/src/theory/mkrewriter index 3c27f1b53..871927760 100755 --- a/src/theory/mkrewriter +++ b/src/theory/mkrewriter @@ -243,10 +243,6 @@ check_builtin_theory_seen ## output -# generate warnings about incorrect #line annotations in templates -nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | - awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 - text=$(cat "$template") for var in \ rewriter_includes \ diff --git a/src/theory/mktheorytraits b/src/theory/mktheorytraits index a87203015..1b1350abe 100755 --- a/src/theory/mktheorytraits +++ b/src/theory/mktheorytraits @@ -214,20 +214,15 @@ function enumerator { lineno=${BASH_LINENO[0]} check_theory_seen type_enumerator_includes="${type_enumerator_includes} -#line $lineno \"$kf\" #include \"$3\"" if expr "$type_constants" : '.* '"$1"' ' &>/dev/null; then mk_type_enumerator_type_constant_cases="${mk_type_enumerator_type_constant_cases} -#line $lineno \"$kf\" case $1: -#line $lineno \"$kf\" return new $2(type, tep); " elif expr "$type_kinds" : '.* '"$1"' ' &>/dev/null; then mk_type_enumerator_cases="${mk_type_enumerator_cases} -#line $lineno \"$kf\" case kind::$1: -#line $lineno \"$kf\" return new $2(type, tep); " else @@ -395,10 +390,6 @@ check_builtin_theory_seen eval "theory_constructors=\"$theory_constructors\"" -# generate warnings about incorrect #line annotations in templates -nl -ba -s' ' "$template" | grep '^ *[0-9][0-9]* # *line' | - awk '{OFS="";if($1+1!=$3) print "'"$template"':",$1,": warning: incorrect annotation \"#line ",$3,"\" (it should be \"#line ",($1+1),"\")"}' >&2 - text=$(cat "$template") for var in \ theory_traits \ diff --git a/src/theory/output_channel.cpp b/src/theory/output_channel.cpp index c918438ee..ad60dbe0e 100644 --- a/src/theory/output_channel.cpp +++ b/src/theory/output_channel.cpp @@ -93,11 +93,6 @@ TNode LemmaStatus::getRewrittenLemma() const { return d_rewrittenLemma; } unsigned LemmaStatus::getLevel() const { return d_level; } -LemmaStatus OutputChannel::lemma(TNode n, LemmaProperty p) -{ - return lemma(n, RULE_INVALID, p); -} - LemmaStatus OutputChannel::split(TNode n) { return splitLemma(n.orNode(n.notNode())); diff --git a/src/theory/output_channel.h b/src/theory/output_channel.h index 0fd610c58..23d7d8784 100644 --- a/src/theory/output_channel.h +++ b/src/theory/output_channel.h @@ -22,11 +22,9 @@ #include <memory> #include "expr/proof_node.h" -#include "proof/proof_manager.h" #include "smt/logic_exception.h" #include "theory/interrupted.h" #include "theory/trust_node.h" -#include "util/proof.h" #include "util/resource_manager.h" namespace CVC4 { @@ -135,10 +133,8 @@ class OutputChannel { * assigned false), or else a literal by itself (in the case of a * unit conflict) which is assigned TRUE (and T-conflicting) in the * current assignment. - * @param pf - a proof of the conflict. This is only non-null if proofs - * are enabled. */ - virtual void conflict(TNode n, std::unique_ptr<Proof> pf = nullptr) = 0; + virtual void conflict(TNode n) = 0; /** * Propagate a theory literal. @@ -153,19 +149,11 @@ class OutputChannel { * been detected. (This requests a split.) * * @param n - a theory lemma valid at decision level 0 - * @param rule - the proof rule for this lemma * @param p The properties of the lemma * @return the "status" of the lemma, including user level at which * the lemma resides; the lemma will be removed when this user level pops */ - virtual LemmaStatus lemma(TNode n, - ProofRule rule, - LemmaProperty p = LemmaProperty::NONE) = 0; - - /** - * Variant of the lemma function that does not require providing a proof rule. - */ - virtual LemmaStatus lemma(TNode n, LemmaProperty p = LemmaProperty::NONE); + virtual LemmaStatus lemma(TNode n, LemmaProperty p = LemmaProperty::NONE) = 0; /** * Request a split on a new theory atom. This is equivalent to diff --git a/src/theory/quantifiers/fun_def_process.cpp b/src/theory/quantifiers/fun_def_process.cpp index 2c5eab94c..eb87db4de 100644 --- a/src/theory/quantifiers/fun_def_process.cpp +++ b/src/theory/quantifiers/fun_def_process.cpp @@ -14,13 +14,15 @@ ** This class implements pre-process steps for admissible recursive function definitions (Reynolds et al IJCAR2016) **/ +#include "theory/quantifiers/fun_def_process.h" + #include <vector> -#include "theory/quantifiers/fun_def_process.h" +#include "options/smt_options.h" +#include "proof/proof_manager.h" #include "theory/quantifiers/quantifiers_attributes.h" #include "theory/quantifiers/term_database.h" #include "theory/quantifiers/term_util.h" -#include "proof/proof_manager.h" using namespace CVC4; using namespace std; @@ -86,7 +88,10 @@ void FunDefFmf::simplify( std::vector< Node >& assertions ) { Trace("fmf-fun-def") << " to " << std::endl; Node new_q = NodeManager::currentNM()->mkNode( FORALL, bvl, bd ); new_q = Rewriter::rewrite( new_q ); - PROOF( ProofManager::currentPM()->addDependence(new_q, assertions[i]); ); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(new_q, assertions[i]); + } assertions[i] = new_q; Trace("fmf-fun-def") << " " << assertions[i] << std::endl; fd_assertions.push_back( i ); @@ -120,7 +125,10 @@ void FunDefFmf::simplify( std::vector< Node >& assertions ) { << std::endl; Trace("fmf-fun-def-rewrite") << " to " << std::endl; Trace("fmf-fun-def-rewrite") << " " << n << std::endl; - PROOF(ProofManager::currentPM()->addDependence(n, assertions[i]);); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(n, assertions[i]); + } assertions[i] = n; } } @@ -175,8 +183,12 @@ Node FunDefFmf::simplifyFormula( Node n, bool pol, bool hasPol, std::vector< Nod c = simplifyFormula( n[i], newPol, newHasPol, cconstraints, hd, false, visited, visited_cons ); if( branch_pos ){ // if at a branching position, the other constraints don't matter if this is satisfied - Node bcons = cconstraints.empty() ? NodeManager::currentNM()->mkConst( true ) : - ( cconstraints.size()==1 ? cconstraints[0] : NodeManager::currentNM()->mkNode( AND, cconstraints ) ); + Node bcons = cconstraints.empty() + ? NodeManager::currentNM()->mkConst(true) + : (cconstraints.size() == 1 + ? cconstraints[0] + : NodeManager::currentNM()->mkNode( + AND, cconstraints)); branch_constraints.push_back( bcons ); Trace("fmf-fun-def-debug2") << "Branching constraint at arg " << i << " is " << bcons << std::endl; } @@ -201,10 +213,14 @@ Node FunDefFmf::simplifyFormula( Node n, bool pol, bool hasPol, std::vector< Nod // in the default case, we care about all conditions branch_cond = constraints.size()==1 ? constraints[0] : NodeManager::currentNM()->mkNode( AND, constraints ); for( unsigned i=0; i<n.getNumChildren(); i++ ){ - // if this child holds with forcing polarity (true child of OR or false child of AND), - // then we only care about its associated recursive conditions - branch_cond = NodeManager::currentNM()->mkNode( kind::ITE, - ( n.getKind()==OR ? n[i] : n[i].negate() ), branch_constraints[i], branch_cond ); + // if this child holds with forcing polarity (true child of OR or + // false child of AND), then we only care about its associated + // recursive conditions + branch_cond = NodeManager::currentNM()->mkNode( + kind::ITE, + (n.getKind() == OR ? n[i] : n[i].negate()), + branch_constraints[i], + branch_cond); } } Trace("fmf-fun-def-debug2") << "Made branching condition " << branch_cond << std::endl; diff --git a/src/theory/quantifiers/instantiate.cpp b/src/theory/quantifiers/instantiate.cpp index 77b61e9dd..04b6e0dda 100644 --- a/src/theory/quantifiers/instantiate.cpp +++ b/src/theory/quantifiers/instantiate.cpp @@ -16,6 +16,8 @@ #include "expr/node_algorithm.h" #include "options/quantifiers_options.h" +#include "options/smt_options.h" +#include "proof/proof_manager.h" #include "smt/smt_statistics_registry.h" #include "theory/quantifiers/cegqi/inst_strategy_cegqi.h" #include "theory/quantifiers/first_order_model.h" @@ -577,18 +579,21 @@ void Instantiate::getInstantiatedQuantifiedFormulas(std::vector<Node>& qs) bool Instantiate::getUnsatCoreLemmas(std::vector<Node>& active_lemmas) { // only if unsat core available - if (options::proof()) + if (options::unsatCores()) { if (!ProofManager::currentPM()->unsatCoreAvailable()) { return false; } } + else + { + return false; + } Trace("inst-unsat-core") << "Get instantiations in unsat core..." << std::endl; - ProofManager::currentPM()->getLemmasInUnsatCore(theory::THEORY_QUANTIFIERS, - active_lemmas); + ProofManager::currentPM()->getLemmasInUnsatCore(active_lemmas); if (Trace.isOn("inst-unsat-core")) { Trace("inst-unsat-core") << "Quantifiers lemmas in unsat core: " @@ -602,27 +607,6 @@ bool Instantiate::getUnsatCoreLemmas(std::vector<Node>& active_lemmas) return true; } -bool Instantiate::getUnsatCoreLemmas(std::vector<Node>& active_lemmas, - std::map<Node, Node>& weak_imp) -{ - if (getUnsatCoreLemmas(active_lemmas)) - { - for (unsigned i = 0, size = active_lemmas.size(); i < size; ++i) - { - Node n = ProofManager::currentPM()->getWeakestImplicantInUnsatCore( - active_lemmas[i]); - if (n != active_lemmas[i]) - { - Trace("inst-unsat-core") << " weaken : " << active_lemmas[i] << " -> " - << n << std::endl; - } - weak_imp[active_lemmas[i]] = n; - } - return true; - } - return false; -} - void Instantiate::getInstantiationTermVectors( Node q, std::vector<std::vector<Node> >& tvecs) { diff --git a/src/theory/quantifiers/instantiate.h b/src/theory/quantifiers/instantiate.h index cb40bbbbc..8b1e0f7fc 100644 --- a/src/theory/quantifiers/instantiate.h +++ b/src/theory/quantifiers/instantiate.h @@ -268,21 +268,6 @@ class Instantiate : public QuantifiersUtil * This method returns false if the unsat core is not available. */ bool getUnsatCoreLemmas(std::vector<Node>& active_lemmas); - /** get unsat core lemmas - * - * If this method returns true, then it appends to active_lemmas all lemmas - * that are in the unsat core that originated from the theory of quantifiers. - * This method returns false if the unsat core is not available. - * - * It also computes a weak implicant for each of these lemmas. For each lemma - * L in active_lemmas, this is a formula L' such that: - * L => L' - * and replacing L by L' in the unsat core results in a set that is still - * unsatisfiable. The map weak_imp stores this formula for each formula in - * active_lemmas. - */ - bool getUnsatCoreLemmas(std::vector<Node>& active_lemmas, - std::map<Node, Node>& weak_imp); /** get explanation for instantiation lemmas * * diff --git a/src/theory/quantifiers_engine.cpp b/src/theory/quantifiers_engine.cpp index dd59628c1..2e69080e7 100644 --- a/src/theory/quantifiers_engine.cpp +++ b/src/theory/quantifiers_engine.cpp @@ -217,7 +217,7 @@ QuantifiersEngine::QuantifiersEngine(context::Context* c, if (options::sygus() || options::sygusInst()) { d_sygus_tdb.reset(new quantifiers::TermDbSygus(c, this)); - } + } d_util.push_back(d_instantiate.get()); @@ -575,12 +575,12 @@ void QuantifiersEngine::check( Theory::Effort e ){ Trace("quant-engine-debug2") << "Quantifiers Engine call to check, level = " << e << ", needsCheck=" << needsCheck << std::endl; if( needsCheck ){ - //flush previous lemmas (for instance, if was interupted), or other lemmas to process + //flush previous lemmas (for instance, if was interrupted), or other lemmas to process flushLemmas(); if( d_hasAddedLemma ){ return; } - + double clSet = 0; if( Trace.isOn("quant-engine") ){ clSet = double(clock())/double(CLOCKS_PER_SEC); @@ -805,7 +805,7 @@ void QuantifiersEngine::check( Theory::Effort e ){ Trace("quant-engine") << ", added lemma = " << d_hasAddedLemma; Trace("quant-engine") << std::endl; } - + Trace("quant-engine-debug2") << "Finished quantifiers engine check." << std::endl; }else{ Trace("quant-engine-debug2") << "Quantifiers Engine does not need check." << std::endl; @@ -1035,7 +1035,7 @@ bool QuantifiersEngine::removeLemma( Node lem ) { void QuantifiersEngine::addRequirePhase( Node lit, bool req ){ d_phase_req_waiting[lit] = req; } - + void QuantifiersEngine::markRelevant( Node q ) { d_model->markRelevant( q ); } @@ -1048,9 +1048,10 @@ bool QuantifiersEngine::theoryEngineNeedsCheck() const return d_te->needCheck(); } -void QuantifiersEngine::setConflict() { - d_conflict = true; - d_conflict_c = true; +void QuantifiersEngine::setConflict() +{ + d_conflict = true; + d_conflict_c = true; } bool QuantifiersEngine::getInstWhenNeedsCheck( Theory::Effort e ) { @@ -1123,10 +1124,6 @@ bool QuantifiersEngine::getUnsatCoreLemmas( std::vector< Node >& active_lemmas ) return d_instantiate->getUnsatCoreLemmas(active_lemmas); } -bool QuantifiersEngine::getUnsatCoreLemmas( std::vector< Node >& active_lemmas, std::map< Node, Node >& weak_imp ) { - return d_instantiate->getUnsatCoreLemmas(active_lemmas, weak_imp); -} - void QuantifiersEngine::getInstantiationTermVectors( Node q, std::vector< std::vector< Node > >& tvecs ) { d_instantiate->getInstantiationTermVectors(q, tvecs); } diff --git a/src/theory/quantifiers_engine.h b/src/theory/quantifiers_engine.h index eca108587..6afc4f925 100644 --- a/src/theory/quantifiers_engine.h +++ b/src/theory/quantifiers_engine.h @@ -265,8 +265,6 @@ public: Node getInstantiatedConjunction(Node q); /** get unsat core lemmas */ bool getUnsatCoreLemmas(std::vector<Node>& active_lemmas); - bool getUnsatCoreLemmas(std::vector<Node>& active_lemmas, - std::map<Node, Node>& weak_imp); /** get explanation for instantiation lemmas */ void getExplanationForInstLemmas(const std::vector<Node>& lems, std::map<Node, Node>& quant, @@ -317,7 +315,7 @@ public: ~Statistics(); };/* class QuantifiersEngine::Statistics */ Statistics d_statistics; - + private: /** reference to theory engine object */ TheoryEngine* d_te; diff --git a/src/theory/rewriter.cpp b/src/theory/rewriter.cpp index f2e13d1e0..9238525dc 100644 --- a/src/theory/rewriter.cpp +++ b/src/theory/rewriter.cpp @@ -21,6 +21,7 @@ #include "smt/smt_engine.h" #include "smt/smt_engine_scope.h" #include "smt/smt_statistics_registry.h" +#include "theory/builtin/proof_checker.h" #include "theory/rewriter_tables.h" #include "theory/theory.h" #include "util/resource_manager.h" @@ -115,13 +116,18 @@ TrustNode Rewriter::rewriteWithProof(TNode node, return TrustNode::mkTrustRewrite(node, ret, d_tpg.get()); } -void Rewriter::setProofChecker(ProofChecker* pc) +void Rewriter::setProofNodeManager(ProofNodeManager* pnm) { // if not already initialized with proof support if (d_tpg == nullptr) { - d_pnm.reset(new ProofNodeManager(pc)); - d_tpg.reset(new TConvProofGenerator(d_pnm.get())); + // the rewriter is staticly determinstic, thus use static cache policy + // for the term conversion proof generator + d_tpg.reset(new TConvProofGenerator(pnm, + nullptr, + TConvPolicy::FIXPOINT, + TConvCachePolicy::STATIC, + "Rewriter::TConvProofGenerator")); } } @@ -391,7 +397,7 @@ RewriteResponse Rewriter::preRewrite(theory::TheoryId theoryId, d_theoryRewriters[theoryId]->preRewriteWithProof(n); // process the trust rewrite response: store the proof step into // tcpg if necessary and then convert to rewrite response. - return processTrustRewriteResponse(tresponse, true, tcpg); + return processTrustRewriteResponse(theoryId, tresponse, true, tcpg); } return d_theoryRewriters[theoryId]->preRewrite(n); } @@ -412,7 +418,7 @@ RewriteResponse Rewriter::postRewrite(theory::TheoryId theoryId, // same as above, for post-rewrite TrustRewriteResponse tresponse = d_theoryRewriters[theoryId]->postRewriteWithProof(n); - return processTrustRewriteResponse(tresponse, false, tcpg); + return processTrustRewriteResponse(theoryId, tresponse, false, tcpg); } return d_theoryRewriters[theoryId]->postRewrite(n); } @@ -420,6 +426,7 @@ RewriteResponse Rewriter::postRewrite(theory::TheoryId theoryId, } RewriteResponse Rewriter::processTrustRewriteResponse( + theory::TheoryId theoryId, const TrustRewriteResponse& tresponse, bool isPre, TConvProofGenerator* tcpg) @@ -433,13 +440,14 @@ RewriteResponse Rewriter::processTrustRewriteResponse( ProofGenerator* pg = trn.getGenerator(); if (pg == nullptr) { + Node tidn = builtin::BuiltinProofRuleChecker::mkTheoryIdNode(theoryId); // add small step trusted rewrite NodeManager* nm = NodeManager::currentNM(); tcpg->addRewriteStep(proven[0], proven[1], PfRule::THEORY_REWRITE, {}, - {proven[0], nm->mkConst(isPre)}); + {proven, tidn, nm->mkConst(isPre)}); } else { diff --git a/src/theory/rewriter.h b/src/theory/rewriter.h index c57844f23..113749a75 100644 --- a/src/theory/rewriter.h +++ b/src/theory/rewriter.h @@ -80,7 +80,7 @@ class Rewriter { /** * Rewrite with proof production, which is managed by the term conversion * proof generator managed by this class (d_tpg). This method requires a call - * to setProofChecker prior to this call. + * to setProofNodeManager prior to this call. * * @param node The node to rewrite. * @param elimTheoryRewrite Whether we also want fine-grained proofs for @@ -94,8 +94,8 @@ class Rewriter { bool elimTheoryRewrite = false, bool isExtEq = false); - /** Set proof checker */ - void setProofChecker(ProofChecker* pc); + /** Set proof node manager */ + void setProofNodeManager(ProofNodeManager* pnm); /** * Garbage collects the rewrite caches. @@ -189,6 +189,7 @@ class Rewriter { TConvProofGenerator* tcpg = nullptr); /** processes a trust rewrite response */ RewriteResponse processTrustRewriteResponse( + theory::TheoryId theoryId, const TrustRewriteResponse& tresponse, bool isPre, TConvProofGenerator* tcpg); @@ -226,8 +227,6 @@ class Rewriter { RewriteEnvironment d_re; - /** The proof node manager */ - std::unique_ptr<ProofNodeManager> d_pnm; /** The proof generator */ std::unique_ptr<TConvProofGenerator> d_tpg; #ifdef CVC4_ASSERTIONS diff --git a/src/theory/sets/theory_sets.cpp b/src/theory/sets/theory_sets.cpp index 63ebacc23..cb42f09d5 100644 --- a/src/theory/sets/theory_sets.cpp +++ b/src/theory/sets/theory_sets.cpp @@ -67,12 +67,12 @@ void TheorySets::finishInit() d_valuation.setUnevaluatedKind(WITNESS); // functions we are doing congruence over - d_equalityEngine->addFunctionKind(kind::SINGLETON); - d_equalityEngine->addFunctionKind(kind::UNION); - d_equalityEngine->addFunctionKind(kind::INTERSECTION); - d_equalityEngine->addFunctionKind(kind::SETMINUS); - d_equalityEngine->addFunctionKind(kind::MEMBER); - d_equalityEngine->addFunctionKind(kind::SUBSET); + d_equalityEngine->addFunctionKind(SINGLETON); + d_equalityEngine->addFunctionKind(UNION); + d_equalityEngine->addFunctionKind(INTERSECTION); + d_equalityEngine->addFunctionKind(SETMINUS); + d_equalityEngine->addFunctionKind(MEMBER); + d_equalityEngine->addFunctionKind(SUBSET); // relation operators d_equalityEngine->addFunctionKind(PRODUCT); d_equalityEngine->addFunctionKind(JOIN); @@ -88,19 +88,26 @@ void TheorySets::finishInit() d_internal->finishInit(); } -void TheorySets::notifySharedTerm(TNode n) { d_internal->addSharedTerm(n); } +void TheorySets::postCheck(Effort level) { d_internal->postCheck(level); } -void TheorySets::check(Effort e) { - if (done() && e < Theory::EFFORT_FULL) { - return; - } - TimerStat::CodeTimer checkTimer(d_checkTime); - d_internal->check(e); +bool TheorySets::preNotifyFact( + TNode atom, bool polarity, TNode fact, bool isPrereg, bool isInternal) +{ + return d_internal->preNotifyFact(atom, polarity, fact); +} + +void TheorySets::notifyFact(TNode atom, + bool polarity, + TNode fact, + bool isInternal) +{ + d_internal->notifyFact(atom, polarity, fact); } -bool TheorySets::collectModelInfo(TheoryModel* m) +bool TheorySets::collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) { - return d_internal->collectModelInfo(m); + return d_internal->collectModelValues(m, termSet); } void TheorySets::computeCareGraph() { @@ -113,15 +120,12 @@ TrustNode TheorySets::explain(TNode node) return TrustNode::mkTrustPropExp(node, exp, nullptr); } -EqualityStatus TheorySets::getEqualityStatus(TNode a, TNode b) { - return d_internal->getEqualityStatus(a, b); -} - Node TheorySets::getModelValue(TNode node) { return Node::null(); } -void TheorySets::preRegisterTerm(TNode node) { +void TheorySets::preRegisterTerm(TNode node) +{ d_internal->preRegisterTerm(node); } @@ -157,7 +161,7 @@ Theory::PPAssertStatus TheorySets::ppAssert(TNode in, SubstitutionMap& outSubsti Theory::PPAssertStatus status = Theory::PP_ASSERT_STATUS_UNSOLVED; // this is based off of Theory::ppAssert - if (in.getKind() == kind::EQUAL) + if (in.getKind() == EQUAL) { if (in[0].isVar() && isLegalElimination(in[0], in[1])) { diff --git a/src/theory/sets/theory_sets.h b/src/theory/sets/theory_sets.h index a7fb31dab..7787c0f9b 100644 --- a/src/theory/sets/theory_sets.h +++ b/src/theory/sets/theory_sets.h @@ -58,12 +58,23 @@ class TheorySets : public Theory void finishInit() override; //--------------------------------- end initialization - void notifySharedTerm(TNode) override; - void check(Effort) override; - bool collectModelInfo(TheoryModel* m) override; + //--------------------------------- standard check + /** Post-check, called after the fact queue of the theory is processed. */ + void postCheck(Effort level) override; + /** Pre-notify fact, return true if processed. */ + bool preNotifyFact(TNode atom, + bool pol, + TNode fact, + bool isPrereg, + bool isInternal) override; + /** Notify fact */ + void notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) override; + //--------------------------------- end standard check + /** Collect model values in m based on the relevant terms given by termSet */ + bool collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) override; void computeCareGraph() override; TrustNode explain(TNode) override; - EqualityStatus getEqualityStatus(TNode a, TNode b) override; Node getModelValue(TNode) override; std::string identify() const override { return "THEORY_SETS"; } void preRegisterTerm(TNode node) override; @@ -71,6 +82,7 @@ class TheorySets : public Theory PPAssertStatus ppAssert(TNode in, SubstitutionMap& outSubstitutions) override; void presolve() override; bool isEntailed(Node n, bool pol); + private: /** Functions to handle callbacks from equality engine */ class NotifyClass : public eq::EqualityEngineNotify diff --git a/src/theory/sets/theory_sets_private.cpp b/src/theory/sets/theory_sets_private.cpp index 3c9414606..c432c8039 100644 --- a/src/theory/sets/theory_sets_private.cpp +++ b/src/theory/sets/theory_sets_private.cpp @@ -276,74 +276,21 @@ bool TheorySetsPrivate::assertFact(Node fact, Node exp) << ", exp = " << exp << std::endl; bool polarity = fact.getKind() != kind::NOT; TNode atom = polarity ? fact : fact[0]; - if (!d_state.isEntailed(atom, polarity)) + if (d_state.isEntailed(atom, polarity)) { - if (atom.getKind() == kind::EQUAL) - { - d_equalityEngine->assertEquality(atom, polarity, exp); - } - else - { - d_equalityEngine->assertPredicate(atom, polarity, exp); - } - if (!d_state.isInConflict()) - { - if (atom.getKind() == kind::MEMBER && polarity) - { - // check if set has a value, if so, we can propagate - Node r = d_equalityEngine->getRepresentative(atom[1]); - EqcInfo* e = getOrMakeEqcInfo(r, true); - if (e) - { - Node s = e->d_singleton; - if (!s.isNull()) - { - Node pexp = NodeManager::currentNM()->mkNode( - kind::AND, atom, atom[1].eqNode(s)); - d_keep.insert(pexp); - if (s.getKind() == kind::SINGLETON) - { - if (s[0] != atom[0]) - { - Trace("sets-prop") - << "Propagate mem-eq : " << pexp << std::endl; - Node eq = s[0].eqNode(atom[0]); - d_keep.insert(eq); - assertFact(eq, pexp); - } - } - else - { - Trace("sets-prop") - << "Propagate mem-eq conflict : " << pexp << std::endl; - d_im.conflict(pexp); - } - } - } - // add to membership list - NodeIntMap::iterator mem_i = d_members.find(r); - int n_members = 0; - if (mem_i != d_members.end()) - { - n_members = (*mem_i).second; - } - d_members[r] = n_members + 1; - if (n_members < (int)d_members_data[r].size()) - { - d_members_data[r][n_members] = atom; - } - else - { - d_members_data[r].push_back(atom); - } - } - } - return true; + return false; + } + if (atom.getKind() == kind::EQUAL) + { + d_equalityEngine->assertEquality(atom, polarity, exp); } else { - return false; + d_equalityEngine->assertPredicate(atom, polarity, exp); } + // call the notify new fact method + notifyFact(atom, polarity, exp); + return true; } void TheorySetsPrivate::fullEffortReset() @@ -950,26 +897,10 @@ void TheorySetsPrivate::checkReduceComprehensions() } } -/**************************** TheorySetsPrivate *****************************/ -/**************************** TheorySetsPrivate *****************************/ -/**************************** TheorySetsPrivate *****************************/ +//--------------------------------- standard check -void TheorySetsPrivate::check(Theory::Effort level) +void TheorySetsPrivate::postCheck(Theory::Effort level) { - Trace("sets-check") << "Sets check effort " << level << std::endl; - if (level == Theory::EFFORT_LAST_CALL) - { - return; - } - while (!d_external.done() && !d_state.isInConflict()) - { - // Get all the assertions - Assertion assertion = d_external.get(); - TNode fact = assertion.d_assertion; - Trace("sets-assert") << "Assert from input " << fact << std::endl; - // assert the fact - assertFact(fact, fact); - } Trace("sets-check") << "Sets finished assertions effort " << level << std::endl; // invoke full effort check, relations check @@ -989,18 +920,75 @@ void TheorySetsPrivate::check(Theory::Effort level) } } Trace("sets-check") << "Sets finish Check effort " << level << std::endl; -} /* TheorySetsPrivate::check() */ +} -/************************ Sharing ************************/ -/************************ Sharing ************************/ -/************************ Sharing ************************/ +bool TheorySetsPrivate::preNotifyFact(TNode atom, bool polarity, TNode fact) +{ + // use entailment check (is this necessary?) + return d_state.isEntailed(atom, polarity); +} -void TheorySetsPrivate::addSharedTerm(TNode n) +void TheorySetsPrivate::notifyFact(TNode atom, bool polarity, TNode fact) { - Debug("sets") << "[sets] TheorySetsPrivate::addSharedTerm( " << n << ")" - << std::endl; - d_equalityEngine->addTriggerTerm(n, THEORY_SETS); + if (d_state.isInConflict()) + { + return; + } + if (atom.getKind() == kind::MEMBER && polarity) + { + // check if set has a value, if so, we can propagate + Node r = d_equalityEngine->getRepresentative(atom[1]); + EqcInfo* e = getOrMakeEqcInfo(r, true); + if (e) + { + Node s = e->d_singleton; + if (!s.isNull()) + { + Node pexp = NodeManager::currentNM()->mkNode( + kind::AND, atom, atom[1].eqNode(s)); + d_keep.insert(pexp); + if (s.getKind() == kind::SINGLETON) + { + if (s[0] != atom[0]) + { + Trace("sets-prop") << "Propagate mem-eq : " << pexp << std::endl; + Node eq = s[0].eqNode(atom[0]); + d_keep.insert(eq); + // triggers an internal inference + assertFact(eq, pexp); + } + } + else + { + Trace("sets-prop") + << "Propagate mem-eq conflict : " << pexp << std::endl; + d_im.conflict(pexp); + } + } + } + // add to membership list + NodeIntMap::iterator mem_i = d_members.find(r); + int n_members = 0; + if (mem_i != d_members.end()) + { + n_members = (*mem_i).second; + } + d_members[r] = n_members + 1; + if (n_members < (int)d_members_data[r].size()) + { + d_members_data[r][n_members] = atom; + } + else + { + d_members_data[r].push_back(atom); + } + } } +//--------------------------------- end standard check + +/************************ Sharing ************************/ +/************************ Sharing ************************/ +/************************ Sharing ************************/ void TheorySetsPrivate::addCarePairs(TNodeTrie* t1, TNodeTrie* t2, @@ -1193,37 +1181,6 @@ bool TheorySetsPrivate::isCareArg(Node n, unsigned a) } } -EqualityStatus TheorySetsPrivate::getEqualityStatus(TNode a, TNode b) -{ - Assert(d_equalityEngine->hasTerm(a) && d_equalityEngine->hasTerm(b)); - if (d_equalityEngine->areEqual(a, b)) - { - // The terms are implied to be equal - return EQUALITY_TRUE; - } - if (d_equalityEngine->areDisequal(a, b, false)) - { - // The terms are implied to be dis-equal - return EQUALITY_FALSE; - } - return EQUALITY_UNKNOWN; - /* - Node aModelValue = d_external.d_valuation.getModelValue(a); - if(aModelValue.isNull()) { return EQUALITY_UNKNOWN; } - Node bModelValue = d_external.d_valuation.getModelValue(b); - if(bModelValue.isNull()) { return EQUALITY_UNKNOWN; } - if( aModelValue == bModelValue ) { - // The term are true in current model - return EQUALITY_TRUE_IN_MODEL; - } else { - return EQUALITY_FALSE_IN_MODEL; - } - */ - // } - // //TODO: can we be more precise sometimes? - // return EQUALITY_UNKNOWN; -} - /******************** Model generation ********************/ /******************** Model generation ********************/ /******************** Model generation ********************/ @@ -1258,18 +1215,10 @@ std::string traceElements(const Node& set) } // namespace -bool TheorySetsPrivate::collectModelInfo(TheoryModel* m) +bool TheorySetsPrivate::collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) { - Trace("sets-model") << "Set collect model info" << std::endl; - set<Node> termSet; - // Compute terms appearing in assertions and shared terms - d_external.computeRelevantTerms(termSet); - - // Assert equalities and disequalities to the model - if (!m->assertEqualityEngine(d_equalityEngine, &termSet)) - { - return false; - } + Trace("sets-model") << "Set collect model values" << std::endl; NodeManager* nm = NodeManager::currentNM(); std::map<Node, Node> mvals; diff --git a/src/theory/sets/theory_sets_private.h b/src/theory/sets/theory_sets_private.h index af780eadc..0df1eabc5 100644 --- a/src/theory/sets/theory_sets_private.h +++ b/src/theory/sets/theory_sets_private.h @@ -172,18 +172,23 @@ class TheorySetsPrivate { */ void finishInit(); + //--------------------------------- standard check + /** Post-check, called after the fact queue of the theory is processed. */ + void postCheck(Theory::Effort level); + /** Preprocess fact, return true if processed. */ + bool preNotifyFact(TNode atom, bool polarity, TNode fact); + /** Notify new fact */ + void notifyFact(TNode atom, bool polarity, TNode fact); + //--------------------------------- end standard check + + /** Collect model values in m based on the relevant terms given by termSet */ void addSharedTerm(TNode); - - void check(Theory::Effort); - - bool collectModelInfo(TheoryModel* m); + bool collectModelValues(TheoryModel* m, const std::set<Node>& termSet); void computeCareGraph(); Node explain(TNode); - EqualityStatus getEqualityStatus(TNode a, TNode b); - void preRegisterTerm(TNode node); /** expandDefinition diff --git a/src/theory/sort_inference.cpp b/src/theory/sort_inference.cpp index 144f5b54d..feb266a20 100644 --- a/src/theory/sort_inference.cpp +++ b/src/theory/sort_inference.cpp @@ -24,7 +24,6 @@ #include "options/quantifiers_options.h" #include "options/smt_options.h" #include "options/uf_options.h" -#include "proof/proof_manager.h" #include "theory/rewriter.h" #include "theory/quantifiers/quant_util.h" diff --git a/src/theory/strings/inference_manager.cpp b/src/theory/strings/inference_manager.cpp index dce038fbf..811c040f3 100644 --- a/src/theory/strings/inference_manager.cpp +++ b/src/theory/strings/inference_manager.cpp @@ -28,19 +28,17 @@ namespace CVC4 { namespace theory { namespace strings { -InferenceManager::InferenceManager(context::Context* c, - context::UserContext* u, +InferenceManager::InferenceManager(Theory& t, SolverState& s, TermRegistry& tr, ExtTheory& e, - OutputChannel& out, - SequencesStatistics& statistics) - : d_state(s), + SequencesStatistics& statistics, + ProofNodeManager* pnm) + : TheoryInferenceManager(t, s, pnm), + d_state(s), d_termReg(tr), d_extt(e), - d_out(out), - d_statistics(statistics), - d_keep(c) + d_statistics(statistics) { NodeManager* nm = NodeManager::currentNM(); d_zero = nm->mkConst(Rational(0)); @@ -49,14 +47,6 @@ InferenceManager::InferenceManager(context::Context* c, d_false = nm->mkConst(false); } -void InferenceManager::sendAssumption(TNode lit) -{ - bool polarity = lit.getKind() != kind::NOT; - TNode atom = polarity ? lit : lit[0]; - // assert pending fact - assertPendingFact(atom, polarity, lit); -} - bool InferenceManager::sendInternalInference(std::vector<Node>& exp, Node conc, Inference infer) @@ -299,7 +289,7 @@ void InferenceManager::doPendingFacts() TNode atom = polarity ? fact : fact[0]; // no double negation or double (conjunctive) conclusions Assert(atom.getKind() != NOT && atom.getKind() != AND); - assertPendingFact(atom, polarity, exp); + assertInternalFact(atom, polarity, exp); } } else @@ -308,13 +298,8 @@ void InferenceManager::doPendingFacts() TNode atom = polarity ? facts : facts[0]; // no double negation or double (conjunctive) conclusions Assert(atom.getKind() != NOT && atom.getKind() != AND); - assertPendingFact(atom, polarity, exp); + assertInternalFact(atom, polarity, exp); } - // Must reference count the equality and its explanation, which is not done - // by the equality engine. Notice that we do not need to do this for - // external assertions, which enter as facts through sendAssumption. - d_keep.insert(facts); - d_keep.insert(exp); i++; } d_pending.clear(); @@ -392,68 +377,6 @@ void InferenceManager::doPendingLemmas() d_pendingReqPhase.clear(); } -void InferenceManager::assertPendingFact(Node atom, bool polarity, Node exp) -{ - eq::EqualityEngine* ee = d_state.getEqualityEngine(); - Trace("strings-pending") << "Assert pending fact : " << atom << " " - << polarity << " from " << exp << std::endl; - Assert(atom.getKind() != OR) << "Infer error: a split."; - if (atom.getKind() == EQUAL) - { - // we must ensure these terms are registered - Trace("strings-pending-debug") << " Register term" << std::endl; - for (const Node& t : atom) - { - // terms in the equality engine are already registered, hence skip - // currently done for only string-like terms, but this could potentially - // be avoided. - if (!ee->hasTerm(t) && t.getType().isStringLike()) - { - d_termReg.registerTerm(t, 0); - } - } - Trace("strings-pending-debug") << " Now assert equality" << std::endl; - ee->assertEquality(atom, polarity, exp); - Trace("strings-pending-debug") << " Finished assert equality" << std::endl; - } - else - { - ee->assertPredicate(atom, polarity, exp); - if (atom.getKind() == STRING_IN_REGEXP) - { - if (polarity && atom[1].getKind() == REGEXP_CONCAT) - { - Node eqc = ee->getRepresentative(atom[0]); - d_state.addEndpointsToEqcInfo(atom, atom[1], eqc); - } - } - } - // process the conflict - if (!d_state.isInConflict()) - { - Node pc = d_state.getPendingConflict(); - if (!pc.isNull()) - { - std::vector<Node> a; - a.push_back(pc); - Trace("strings-pending") - << "Process pending conflict " << pc << std::endl; - Node conflictNode = mkExplain(a); - d_state.notifyInConflict(); - Trace("strings-conflict") - << "CONFLICT: Eager prefix : " << conflictNode << std::endl; - ++(d_statistics.d_conflictsEagerPrefix); - d_out.conflict(conflictNode); - } - } - Trace("strings-pending-debug") << " Now collect terms" << std::endl; - // Collect extended function terms in the atom. Notice that we must register - // all extended functions occurring in assertions and shared terms. We - // make a similar call to registerTermRec in TheoryStrings::addSharedTerm. - d_extt.registerTermRec(atom); - Trace("strings-pending-debug") << " Finished collect terms" << std::endl; -} - bool InferenceManager::hasProcessed() const { return d_state.isInConflict() || !d_pendingLem.empty() || !d_pending.empty(); @@ -475,7 +398,6 @@ Node InferenceManager::mkExplain(const std::vector<Node>& a, { utils::flattenOp(AND, ac, aconj); } - eq::EqualityEngine* ee = d_state.getEqualityEngine(); for (const Node& apc : aconj) { if (std::find(noExplain.begin(), noExplain.end(), apc) != noExplain.end()) @@ -492,12 +414,12 @@ Node InferenceManager::mkExplain(const std::vector<Node>& a, Debug("strings-explain") << "Add to explanation " << apc << std::endl; if (apc.getKind() == NOT && apc[0].getKind() == EQUAL) { - Assert(ee->hasTerm(apc[0][0])); - Assert(ee->hasTerm(apc[0][1])); + Assert(d_ee->hasTerm(apc[0][0])); + Assert(d_ee->hasTerm(apc[0][1])); // ensure that we are ready to explain the disequality - AlwaysAssert(ee->areDisequal(apc[0][0], apc[0][1], true)); + AlwaysAssert(d_ee->areDisequal(apc[0][0], apc[0][1], true)); } - Assert(apc.getKind() != EQUAL || ee->areEqual(apc[0], apc[1])); + Assert(apc.getKind() != EQUAL || d_ee->areEqual(apc[0], apc[1])); // now, explain explain(apc, antec_exp); } @@ -522,7 +444,6 @@ void InferenceManager::explain(TNode literal, { Debug("strings-explain") << "Explain " << literal << " " << d_state.isInConflict() << std::endl; - eq::EqualityEngine* ee = d_state.getEqualityEngine(); bool polarity = literal.getKind() != NOT; TNode atom = polarity ? literal : literal[0]; std::vector<TNode> tassumptions; @@ -530,14 +451,14 @@ void InferenceManager::explain(TNode literal, { if (atom[0] != atom[1]) { - Assert(ee->hasTerm(atom[0])); - Assert(ee->hasTerm(atom[1])); - ee->explainEquality(atom[0], atom[1], polarity, tassumptions); + Assert(d_ee->hasTerm(atom[0])); + Assert(d_ee->hasTerm(atom[1])); + d_ee->explainEquality(atom[0], atom[1], polarity, tassumptions); } } else { - ee->explainPredicate(atom, polarity, tassumptions); + d_ee->explainPredicate(atom, polarity, tassumptions); } for (const TNode a : tassumptions) { diff --git a/src/theory/strings/inference_manager.h b/src/theory/strings/inference_manager.h index 016891737..dc46f1683 100644 --- a/src/theory/strings/inference_manager.h +++ b/src/theory/strings/inference_manager.h @@ -29,6 +29,7 @@ #include "theory/strings/sequences_stats.h" #include "theory/strings/solver_state.h" #include "theory/strings/term_registry.h" +#include "theory/theory_inference_manager.h" #include "theory/uf/equality_engine.h" namespace CVC4 { @@ -65,28 +66,20 @@ namespace strings { * theory of strings, e.g. sendPhaseRequirement, setIncomplete, and * with the extended theory object e.g. markCongruent. */ -class InferenceManager +class InferenceManager : public TheoryInferenceManager { typedef context::CDHashSet<Node, NodeHashFunction> NodeSet; typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; public: - InferenceManager(context::Context* c, - context::UserContext* u, + InferenceManager(Theory& t, SolverState& s, TermRegistry& tr, ExtTheory& e, - OutputChannel& out, - SequencesStatistics& statistics); + SequencesStatistics& statistics, + ProofNodeManager* pnm); ~InferenceManager() {} - /** send assumption - * - * This is called when a fact is asserted to TheoryStrings. It adds lit - * to the equality engine maintained by this class immediately. - */ - void sendAssumption(TNode lit); - /** send internal inferences * * This is called when we have inferred exp => conc, where exp is a set @@ -292,23 +285,12 @@ class InferenceManager // ------------------------------------------------- end extended theory private: - /** assert pending fact - * - * This asserts atom with polarity to the equality engine of this class, - * where exp is the explanation of why (~) atom holds. - * - * This call may trigger further initialization steps involving the terms - * of atom, including calls to registerTerm. - */ - void assertPendingFact(Node atom, bool polarity, Node exp); /** Reference to the solver state of the theory of strings. */ SolverState& d_state; /** Reference to the term registry of theory of strings */ TermRegistry& d_termReg; /** the extended theory object for the theory of strings */ ExtTheory& d_extt; - /** A reference to the output channel of the theory of strings. */ - OutputChannel& d_out; /** Reference to the statistics for the theory of strings/sequences. */ SequencesStatistics& d_statistics; @@ -326,13 +308,6 @@ class InferenceManager std::map<Node, bool> d_pendingReqPhase; /** A list of pending lemmas to be sent on the output channel. */ std::vector<InferInfo> d_pendingLem; - /** - * The keep set of this class. This set is maintained to ensure that - * facts and their explanations are ref-counted. Since facts and their - * explanations are SAT-context-dependent, this set is also - * SAT-context-dependent. - */ - NodeSet d_keep; }; } // namespace strings diff --git a/src/theory/strings/theory_strings.cpp b/src/theory/strings/theory_strings.cpp index 0de0cc33c..3e60cbc44 100644 --- a/src/theory/strings/theory_strings.cpp +++ b/src/theory/strings/theory_strings.cpp @@ -46,7 +46,7 @@ TheoryStrings::TheoryStrings(context::Context* c, d_state(c, u, d_valuation), d_termReg(d_state, out, d_statistics, nullptr), d_extTheory(this), - d_im(c, u, d_state, d_termReg, d_extTheory, out, d_statistics), + d_im(*this, d_state, d_termReg, d_extTheory, d_statistics, pnm), d_rewriter(&d_statistics.d_rewrites), d_bsolver(d_state, d_im), d_csolver(d_state, d_im, d_termReg, d_bsolver), @@ -83,6 +83,8 @@ TheoryStrings::TheoryStrings(context::Context* c, } // use the state object as the official theory state d_theoryState = &d_state; + // use the inference manager as the official inference manager + d_inferManager = &d_im; } TheoryStrings::~TheoryStrings() { @@ -165,23 +167,6 @@ void TheoryStrings::notifySharedTerm(TNode t) Debug("strings") << "TheoryStrings::notifySharedTerm() finished" << std::endl; } -EqualityStatus TheoryStrings::getEqualityStatus(TNode a, TNode b) { - if (d_equalityEngine->hasTerm(a) && d_equalityEngine->hasTerm(b)) - { - if (d_equalityEngine->areEqual(a, b)) - { - // The terms are implied to be equal - return EQUALITY_TRUE; - } - if (d_equalityEngine->areDisequal(a, b, false)) - { - // The terms are implied to be dis-equal - return EQUALITY_FALSE; - } - } - return EQUALITY_UNKNOWN; -} - bool TheoryStrings::propagateLit(TNode literal) { Debug("strings-propagate") @@ -252,23 +237,10 @@ void TheoryStrings::presolve() { // MODEL GENERATION ///////////////////////////////////////////////////////////////////////////// -bool TheoryStrings::collectModelInfo(TheoryModel* m) +bool TheoryStrings::collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) { - Trace("strings-model") << "TheoryStrings : Collect model info" << std::endl; - Trace("strings-model") << "TheoryStrings : assertEqualityEngine." << std::endl; - - std::set<Node> termSet; - - // Compute terms appearing in assertions and shared terms - computeRelevantTerms(termSet); - // assert the (relevant) portion of the equality engine to the model - if (!m->assertEqualityEngine(d_equalityEngine, &termSet)) - { - Unreachable() - << "TheoryStrings::collectModelInfo: failed to assert equality engine" - << std::endl; - return false; - } + Trace("strings-model") << "TheoryStrings : Collect model values" << std::endl; std::map<TypeNode, std::unordered_set<Node, NodeHashFunction> > repSet; // Generate model @@ -653,25 +625,68 @@ TrustNode TheoryStrings::expandDefinition(Node node) return TrustNode::null(); } -void TheoryStrings::check(Effort e) { - if (done() && e<EFFORT_FULL) { - return; +bool TheoryStrings::preNotifyFact( + TNode atom, bool pol, TNode fact, bool isPrereg, bool isInternal) +{ + // this is only required for internal facts, others are already registered + if (isInternal && atom.getKind() == EQUAL) + { + // we must ensure these terms are registered + for (const Node& t : atom) + { + // terms in the equality engine are already registered, hence skip + // currently done for only string-like terms, but this could potentially + // be avoided. + if (!d_equalityEngine->hasTerm(t) && t.getType().isStringLike()) + { + d_termReg.registerTerm(t, 0); + } + } } + return false; +} - TimerStat::CodeTimer checkTimer(d_checkTime); - - // Trace("strings-process") << "Theory of strings, check : " << e << std::endl; - Trace("strings-check-debug") - << "Theory of strings, check : " << e << std::endl; - while (!done() && !d_state.isInConflict()) +void TheoryStrings::notifyFact(TNode atom, + bool polarity, + TNode fact, + bool isInternal) +{ + if (atom.getKind() == STRING_IN_REGEXP) { - // Get all the assertions - Assertion assertion = get(); - TNode fact = assertion.d_assertion; - - Trace("strings-assertion") << "get assertion: " << fact << endl; - d_im.sendAssumption(fact); + if (polarity && atom[1].getKind() == REGEXP_CONCAT) + { + Node eqc = d_equalityEngine->getRepresentative(atom[0]); + d_state.addEndpointsToEqcInfo(atom, atom[1], eqc); + } } + // process pending conflicts due to reasoning about endpoints + if (!d_state.isInConflict()) + { + Node pc = d_state.getPendingConflict(); + if (!pc.isNull()) + { + std::vector<Node> a; + a.push_back(pc); + Trace("strings-pending") + << "Process pending conflict " << pc << std::endl; + Node conflictNode = d_im.mkExplain(a); + Trace("strings-conflict") + << "CONFLICT: Eager prefix : " << conflictNode << std::endl; + ++(d_statistics.d_conflictsEagerPrefix); + d_im.conflict(conflictNode); + return; + } + } + Trace("strings-pending-debug") << " Now collect terms" << std::endl; + // Collect extended function terms in the atom. Notice that we must register + // all extended functions occurring in assertions and shared terms. We + // make a similar call to registerTermRec in TheoryStrings::addSharedTerm. + d_extTheory.registerTermRec(atom); + Trace("strings-pending-debug") << " Finished collect terms" << std::endl; +} + +void TheoryStrings::postCheck(Effort e) +{ d_im.doPendingFacts(); Assert(d_strat.isStrategyInit()); @@ -680,8 +695,10 @@ void TheoryStrings::check(Effort e) { { Trace("strings-check-debug") << "Theory of strings " << e << " effort check " << std::endl; - if(Trace.isOn("strings-eqc")) { - for( unsigned t=0; t<2; t++ ) { + if (Trace.isOn("strings-eqc")) + { + for (unsigned t = 0; t < 2; t++) + { eq::EqClassesIterator eqcs2_i = eq::EqClassesIterator(d_equalityEngine); Trace("strings-eqc") << (t==0 ? "STRINGS:" : "OTHER:") << std::endl; while( !eqcs2_i.isFinished() ){ @@ -725,7 +742,9 @@ void TheoryStrings::check(Effort e) { ++(d_statistics.d_strategyRuns); Trace("strings-check") << " * Run strategy..." << std::endl; runStrategy(e); - // flush the facts + // Send the facts *and* the lemmas. We send lemmas regardless of whether + // we send facts since some lemmas cannot be dropped. Other lemmas are + // otherwise avoided by aborting the strategy when a fact is ready. addedFact = d_im.hasPendingFact(); addedLemma = d_im.hasPendingLemma(); d_im.doPendingFacts(); diff --git a/src/theory/strings/theory_strings.h b/src/theory/strings/theory_strings.h index f4aa0675c..0f59e73dc 100644 --- a/src/theory/strings/theory_strings.h +++ b/src/theory/strings/theory_strings.h @@ -97,27 +97,33 @@ class TheoryStrings : public Theory { void shutdown() override {} /** add shared term */ void notifySharedTerm(TNode n) override; - /** get equality status */ - EqualityStatus getEqualityStatus(TNode a, TNode b) override; /** preregister term */ void preRegisterTerm(TNode n) override; /** Expand definition */ TrustNode expandDefinition(Node n) override; - /** Check at effort e */ - void check(Effort e) override; - /** needs check last effort */ + //--------------------------------- standard check + /** Do we need a check call at last call effort? */ bool needsCheckLastEffort() override; + bool preNotifyFact(TNode atom, + bool pol, + TNode fact, + bool isPrereg, + bool isInternal) override; + void notifyFact(TNode atom, bool pol, TNode fact, bool isInternal) override; + /** Post-check, called after the fact queue of the theory is processed. */ + void postCheck(Effort level) override; + //--------------------------------- end standard check + /** propagate method */ + bool propagateLit(TNode literal); /** Conflict when merging two constants */ void conflict(TNode a, TNode b); /** called when a new equivalence class is created */ void eqNotifyNewClass(TNode t); /** preprocess rewrite */ TrustNode ppRewrite(TNode atom) override; - /** - * Get all relevant information in this theory regarding the current - * model. Return false if a contradiction is discovered. - */ - bool collectModelInfo(TheoryModel* m) override; + /** Collect model values in m based on the relevant terms given by termSet */ + bool collectModelValues(TheoryModel* m, + const std::set<Node>& termSet) override; private: /** NotifyClass for equality engine */ @@ -171,8 +177,6 @@ class TheoryStrings : public Theory { /** The solver state of the theory of strings */ SolverState& d_state; };/* class TheoryStrings::NotifyClass */ - /** propagate method */ - bool propagateLit(TNode literal); /** compute care graph */ void computeCareGraph() override; /** diff --git a/src/theory/strings/theory_strings_preprocess.cpp b/src/theory/strings/theory_strings_preprocess.cpp index a752958b2..084e2ac91 100644 --- a/src/theory/strings/theory_strings_preprocess.cpp +++ b/src/theory/strings/theory_strings_preprocess.cpp @@ -19,6 +19,7 @@ #include <stdint.h> #include "expr/kind.h" +#include "options/smt_options.h" #include "options/strings_options.h" #include "proof/proof_manager.h" #include "smt/logic_exception.h" @@ -972,7 +973,10 @@ void StringsPreprocess::processAssertions( std::vector< Node > &vec_node ){ : NodeManager::currentNM()->mkNode(kind::AND, asserts); if( res!=vec_node[i] ){ res = Rewriter::rewrite( res ); - PROOF( ProofManager::currentPM()->addDependence( res, vec_node[i] ); ); + if (options::unsatCores()) + { + ProofManager::currentPM()->addDependence(res, vec_node[i]); + } vec_node[i] = res; } } diff --git a/src/theory/theory.cpp b/src/theory/theory.cpp index 66541a63e..3c603051c 100644 --- a/src/theory/theory.cpp +++ b/src/theory/theory.cpp @@ -23,6 +23,7 @@ #include "base/check.h" #include "expr/node_algorithm.h" +#include "options/smt_options.h" #include "options/theory_options.h" #include "smt/smt_statistics_registry.h" #include "theory/ext_theory.h" @@ -81,8 +82,7 @@ Theory::Theory(TheoryId id, d_equalityEngine(nullptr), d_allocEqualityEngine(nullptr), d_theoryState(nullptr), - d_inferManager(nullptr), - d_proofsEnabled(false) + d_inferManager(nullptr) { smtStatisticsRegistry()->registerStat(&d_checkTime); smtStatisticsRegistry()->registerStat(&d_computeCareGraphTime); @@ -296,12 +296,12 @@ void Theory::computeCareGraph() { switch (d_valuation.getEqualityStatus(a, b)) { case EQUALITY_TRUE_AND_PROPAGATED: case EQUALITY_FALSE_AND_PROPAGATED: - // If we know about it, we should have propagated it, so we can skip - break; + // If we know about it, we should have propagated it, so we can skip + break; default: - // Let's split on it - addCarePair(a, b); - break; + // Let's split on it + addCarePair(a, b); + break; } } } @@ -359,8 +359,15 @@ std::unordered_set<TNode, TNodeHashFunction> Theory::currentlySharedTerms() cons bool Theory::collectModelInfo(TheoryModel* m) { + // NOTE: the computation of termSet will be moved to model manager + // and passed as an argument to collectModelInfo. std::set<Node> termSet; // Compute terms appearing in assertions and shared terms + TheoryModel* tm = d_valuation.getModel(); + Assert(tm != nullptr); + const std::set<Kind>& irrKinds = tm->getIrrelevantKinds(); + computeAssertedTerms(termSet, irrKinds, true); + // Compute additional relevant terms (theory-specific) computeRelevantTerms(termSet); // if we are using an equality engine, assert it to the model if (d_equalityEngine != nullptr) @@ -375,7 +382,7 @@ bool Theory::collectModelInfo(TheoryModel* m) } void Theory::collectTerms(TNode n, - set<Kind>& irrKinds, + const std::set<Kind>& irrKinds, set<Node>& termSet) const { if (termSet.find(n) != termSet.end()) { @@ -396,13 +403,11 @@ void Theory::collectTerms(TNode n, } } -void Theory::computeRelevantTermsInternal(std::set<Node>& termSet, - std::set<Kind>& irrKinds, - bool includeShared) const +void Theory::computeAssertedTerms(std::set<Node>& termSet, + const std::set<Kind>& irrKinds, + bool includeShared) const { // Collect all terms appearing in assertions - irrKinds.insert(kind::EQUAL); - irrKinds.insert(kind::NOT); context::CDList<Assertion>::const_iterator assert_it = facts_begin(), assert_it_end = facts_end(); for (; assert_it != assert_it_end; ++assert_it) @@ -424,10 +429,9 @@ void Theory::computeRelevantTermsInternal(std::set<Node>& termSet, } } -void Theory::computeRelevantTerms(std::set<Node>& termSet, bool includeShared) +void Theory::computeRelevantTerms(std::set<Node>& termSet) { - std::set<Kind> irrKinds; - computeRelevantTermsInternal(termSet, irrKinds, includeShared); + // by default, there are no additional relevant terms } bool Theory::collectModelValues(TheoryModel* m, const std::set<Node>& termSet) diff --git a/src/theory/theory.h b/src/theory/theory.h index c5fcf362c..176d4b672 100644 --- a/src/theory/theory.h +++ b/src/theory/theory.h @@ -183,26 +183,10 @@ class Theory { //---------------------------------- private collect model info /** - * Scans the current set of assertions and shared terms top-down - * until a theory-leaf is reached, and adds all terms found to - * termSet. This is used by collectModelInfo to delimit the set of - * terms that should be used when constructing a model. - * - * irrKinds: The kinds of terms that appear in assertions that should *not* - * be included in termSet. Note that the kinds EQUAL and NOT are always - * treated as irrelevant kinds. - * - * includeShared: Whether to include shared terms in termSet. Notice that - * shared terms are not influenced by irrKinds. - */ - void computeRelevantTermsInternal(std::set<Node>& termSet, - std::set<Kind>& irrKinds, - bool includeShared = true) const; - /** * Helper function for computeRelevantTerms */ void collectTerms(TNode n, - std::set<Kind>& irrKinds, + const std::set<Kind>& irrKinds, std::set<Node>& termSet) const; //---------------------------------- end private collect model info @@ -262,11 +246,6 @@ class Theory { * the equality engine are used properly. */ TheoryInferenceManager* d_inferManager; - /** - * Whether proofs are enabled - * - */ - bool d_proofsEnabled; /** * Returns the next assertion in the assertFact() queue. @@ -597,6 +576,7 @@ class Theory { Unimplemented() << "Theory " << identify() << " propagated a node but doesn't implement the " "Theory::explain() interface!"; + return TrustNode::null(); } //--------------------------------- check @@ -688,13 +668,29 @@ class Theory { */ virtual bool collectModelInfo(TheoryModel* m); /** - * Same as above, but with empty irrKinds. This version can be overridden - * by the theory, e.g. by restricting or extending the set of terms returned - * by computeRelevantTermsInternal, which is called by default with no - * irrKinds. + * Scans the current set of assertions and shared terms top-down + * until a theory-leaf is reached, and adds all terms found to + * termSet. This is used by collectModelInfo to delimit the set of + * terms that should be used when constructing a model. + * + * @param irrKinds The kinds of terms that appear in assertions that should *not* + * be included in termSet. Note that the kinds EQUAL and NOT are always + * treated as irrelevant kinds. + * + * @param includeShared Whether to include shared terms in termSet. Notice that + * shared terms are not influenced by irrKinds. + * + * TODO (project #39): this method will be deleted. The version in + * model manager will be used. + */ + void computeAssertedTerms(std::set<Node>& termSet, + const std::set<Kind>& irrKinds, + bool includeShared = true) const; + /** + * Compute terms that are not necessarily part of the assertions or + * shared terms that should be considered relevant, add them to termSet. */ - virtual void computeRelevantTerms(std::set<Node>& termSet, - bool includeShared = true); + virtual void computeRelevantTerms(std::set<Node>& termSet); /** * Collect model values, after equality information is added to the model. * The argument termSet is the set of relevant terms returned by @@ -824,15 +820,13 @@ class Theory { * * @return true iff facts have been asserted to this theory. */ - bool hasFacts() { - return !d_facts.empty(); - } + bool hasFacts() { return !d_facts.empty(); } /** Return total number of facts asserted to this theory */ size_t numAssertions() { return d_facts.size(); } - + typedef context::CDList<TNode>::const_iterator shared_terms_iterator; /** @@ -917,7 +911,7 @@ class Theory { /* is extended function reduced */ virtual bool isExtfReduced( int effort, Node n, Node on, std::vector< Node >& exp ) { return n.isConst(); } - + /** * Get reduction for node * If return value is not 0, then n is reduced. @@ -927,9 +921,6 @@ class Theory { * and return value should be <0. */ virtual int getReduction( int effort, Node n, Node& nr ) { return 0; } - - /** Turn on proof-production mode. */ - void produceProofs() { d_proofsEnabled = true; } };/* class Theory */ std::ostream& operator<<(std::ostream& os, theory::Theory::Effort level); diff --git a/src/theory/theory_engine.cpp b/src/theory/theory_engine.cpp index c61879b6d..6837d4be5 100644 --- a/src/theory/theory_engine.cpp +++ b/src/theory/theory_engine.cpp @@ -28,14 +28,9 @@ #include "expr/node_visitor.h" #include "options/bv_options.h" #include "options/options.h" -#include "options/proof_options.h" #include "options/quantifiers_options.h" #include "options/theory_options.h" #include "preprocessing/assertion_pipeline.h" -#include "proof/cnf_proof.h" -#include "proof/lemma_proof.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" #include "smt/logic_exception.h" #include "smt/term_formula_removal.h" #include "theory/arith/arith_ite_utils.h" @@ -256,10 +251,6 @@ TheoryEngine::TheoryEngine(context::Context* context, d_true = NodeManager::currentNM()->mkConst<bool>(true); d_false = NodeManager::currentNM()->mkConst<bool>(false); -#ifdef CVC4_PROOF - ProofManager::currentPM()->initTheoryProofEngine(); -#endif - smtStatisticsRegistry()->registerStat(&d_arithSubstitutionsAdded); } @@ -1150,23 +1141,6 @@ bool TheoryEngine::propagate(TNode literal, theory::TheoryId theory) { assertToTheory(literal, literal, /* to */ THEORY_BUILTIN, /* from */ theory); } } else { - // We could be propagating a unit-clause lemma. In this case, we need to provide a - // recipe. - // TODO: Consider putting this someplace else? This is the only refence to the proof - // manager in this class. - - PROOF({ - LemmaProofRecipe proofRecipe; - proofRecipe.addBaseAssertion(literal); - - Node emptyNode; - LemmaProofRecipe::ProofStep proofStep(theory, emptyNode); - proofStep.addAssertion(literal); - proofRecipe.addStep(proofStep); - - ProofManager::getCnfProof()->setProofRecipe(&proofRecipe); - }); - // Just send off to the SAT solver Assert(d_propEngine->isSatLiteral(literal)); assertToTheory(literal, literal, /* to */ THEORY_SAT_SOLVER, /* from */ theory); @@ -1309,65 +1283,31 @@ static Node mkExplanation(const std::vector<NodeTheoryPair>& explanation) { return conjunction; } -Node TheoryEngine::getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe) { - Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << "): current propagation index = " << d_propagationMapTimestamp << endl; +Node TheoryEngine::getExplanation(TNode node) +{ + Debug("theory::explain") << "TheoryEngine::getExplanation(" << node + << "): current propagation index = " + << d_propagationMapTimestamp << endl; bool polarity = node.getKind() != kind::NOT; TNode atom = polarity ? node : node[0]; // If we're not in shared mode, explanations are simple - if (!d_logicInfo.isSharingEnabled()) { - Debug("theory::explain") << "TheoryEngine::getExplanation: sharing is NOT enabled. " - << " Responsible theory is: " - << theoryOf(atom)->getId() << std::endl; + if (!d_logicInfo.isSharingEnabled()) + { + Debug("theory::explain") + << "TheoryEngine::getExplanation: sharing is NOT enabled. " + << " Responsible theory is: " << theoryOf(atom)->getId() << std::endl; TrustNode texplanation = theoryOf(atom)->explain(node); Node explanation = texplanation.getNode(); - Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; - PROOF({ - if(proofRecipe) { - Node emptyNode; - LemmaProofRecipe::ProofStep proofStep(theoryOf(atom)->getId(), emptyNode); - proofStep.addAssertion(node); - proofRecipe->addBaseAssertion(node); - - if (explanation.getKind() == kind::AND) { - // If the explanation is a conjunction, the recipe for the corresponding lemma is - // the negation of its conjuncts. - Node flat = flattenAnd(explanation); - for (unsigned i = 0; i < flat.getNumChildren(); ++i) { - if (flat[i].isConst() && flat[i].getConst<bool>()) { - ++ i; - continue; - } - if (flat[i].getKind() == kind::NOT && - flat[i][0].isConst() && !flat[i][0].getConst<bool>()) { - ++ i; - continue; - } - Debug("theory::explain") << "TheoryEngine::getExplanationAndRecipe: adding recipe assertion: " - << flat[i].negate() << std::endl; - proofStep.addAssertion(flat[i].negate()); - proofRecipe->addBaseAssertion(flat[i].negate()); - } - } else { - // The recipe for proving it is by negating it. "True" is not an acceptable reason. - if (!((explanation.isConst() && explanation.getConst<bool>()) || - (explanation.getKind() == kind::NOT && - explanation[0].isConst() && !explanation[0].getConst<bool>()))) { - proofStep.addAssertion(explanation.negate()); - proofRecipe->addBaseAssertion(explanation.negate()); - } - } - - proofRecipe->addStep(proofStep); - } - }); - + Debug("theory::explain") << "TheoryEngine::getExplanation(" << node + << ") => " << explanation << endl; return explanation; } - Debug("theory::explain") << "TheoryEngine::getExplanation: sharing IS enabled" << std::endl; + Debug("theory::explain") << "TheoryEngine::getExplanation: sharing IS enabled" + << std::endl; // Initial thing to explain NodeTheoryPair toExplain(node, THEORY_SAT_SOLVER, d_propagationMapTimestamp); @@ -1378,33 +1318,20 @@ Node TheoryEngine::getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRe << "TheoryEngine::getExplanation: explainer for node " << nodeExplainerPair.d_node << " is theory: " << nodeExplainerPair.d_theory << std::endl; - TheoryId explainer = nodeExplainerPair.d_theory; // Create the workplace for explanations std::vector<NodeTheoryPair> explanationVector; explanationVector.push_back(d_propagationMap[toExplain]); // Process the explanation - if (proofRecipe) { - Node emptyNode; - LemmaProofRecipe::ProofStep proofStep(explainer, emptyNode); - proofStep.addAssertion(node); - proofRecipe->addStep(proofStep); - proofRecipe->addBaseAssertion(node); - } - - getExplanation(explanationVector, proofRecipe); + getExplanation(explanationVector); Node explanation = mkExplanation(explanationVector); - Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " << explanation << endl; + Debug("theory::explain") << "TheoryEngine::getExplanation(" << node << ") => " + << explanation << endl; return explanation; } -Node TheoryEngine::getExplanation(TNode node) { - LemmaProofRecipe *dontCareRecipe = NULL; - return getExplanationAndRecipe(node, dontCareRecipe); -} - struct AtomsCollect { std::vector<TNode> d_atoms; @@ -1504,7 +1431,6 @@ void TheoryEngine::ensureLemmaAtoms(const std::vector<TNode>& atoms, theory::The } theory::LemmaStatus TheoryEngine::lemma(TNode node, - ProofRule rule, bool negated, theory::LemmaProperty p, theory::TheoryId atomsTo) @@ -1567,8 +1493,7 @@ theory::LemmaStatus TheoryEngine::lemma(TNode node, // assert lemmas to prop engine for (size_t i = 0, lsize = lemmas.size(); i < lsize; ++i) { - d_propEngine->assertLemma( - lemmas[i], i == 0 && negated, removable, rule, node); + d_propEngine->assertLemma(lemmas[i], i == 0 && negated, removable); } // WARNING: Below this point don't assume lemmas[0] to be not negated. @@ -1611,23 +1536,6 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { << CheckSatCommand(conflict.toExpr()); } - LemmaProofRecipe* proofRecipe = NULL; - PROOF({ - proofRecipe = new LemmaProofRecipe; - Node emptyNode; - LemmaProofRecipe::ProofStep proofStep(theoryId, emptyNode); - - if (conflict.getKind() == kind::AND) { - for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { - proofStep.addAssertion(conflict[i].negate()); - } - } else { - proofStep.addAssertion(conflict.negate()); - } - - proofRecipe->addStep(proofStep); - }); - // In the multiple-theories case, we need to reconstruct the conflict if (d_logicInfo.isSharingEnabled()) { // Create the workplace for explanations @@ -1635,67 +1543,29 @@ void TheoryEngine::conflict(TNode conflict, TheoryId theoryId) { explanationVector.push_back(NodeTheoryPair(conflict, theoryId, d_propagationMapTimestamp)); // Process the explanation - getExplanation(explanationVector, proofRecipe); - PROOF(ProofManager::getCnfProof()->setProofRecipe(proofRecipe)); + getExplanation(explanationVector); Node fullConflict = mkExplanation(explanationVector); Debug("theory::conflict") << "TheoryEngine::conflict(" << conflict << ", " << theoryId << "): full = " << fullConflict << endl; Assert(properConflict(fullConflict)); lemma(fullConflict, - RULE_CONFLICT, true, LemmaProperty::REMOVABLE, THEORY_LAST); } else { // When only one theory, the conflict should need no processing Assert(properConflict(conflict)); - PROOF({ - if (conflict.getKind() == kind::AND) { - // If the conflict is a conjunction, the corresponding lemma is derived by negating - // its conjuncts. - for (unsigned i = 0; i < conflict.getNumChildren(); ++i) { - if (conflict[i].isConst() && conflict[i].getConst<bool>()) { - ++ i; - continue; - } - if (conflict[i].getKind() == kind::NOT && - conflict[i][0].isConst() && !conflict[i][0].getConst<bool>()) { - ++ i; - continue; - } - proofRecipe->getStep(0)->addAssertion(conflict[i].negate()); - proofRecipe->addBaseAssertion(conflict[i].negate()); - } - } else { - proofRecipe->getStep(0)->addAssertion(conflict.negate()); - proofRecipe->addBaseAssertion(conflict.negate()); - } - - ProofManager::getCnfProof()->setProofRecipe(proofRecipe); - }); - - lemma(conflict, RULE_CONFLICT, true, LemmaProperty::REMOVABLE, THEORY_LAST); + lemma(conflict, true, LemmaProperty::REMOVABLE, THEORY_LAST); } - - PROOF({ - delete proofRecipe; - proofRecipe = NULL; - }); } -void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* proofRecipe) { +void TheoryEngine::getExplanation( + std::vector<NodeTheoryPair>& explanationVector) +{ Assert(explanationVector.size() > 0); unsigned i = 0; // Index of the current literal we are processing unsigned j = 0; // Index of the last literal we are keeping - std::unique_ptr<std::set<Node>> inputAssertions = nullptr; - PROOF({ - if (proofRecipe) - { - inputAssertions.reset( - new std::set<Node>(proofRecipe->getStep(0)->getAssertions())); - } - }); // cache of nodes we have already explained by some theory std::unordered_map<Node, size_t, NodeHashFunction> cache; @@ -1768,22 +1638,6 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector explanationVector.push_back((*find).second); ++i; - PROOF({ - if (toExplain.d_node != (*find).second.d_node) - { - Debug("pf::explain") - << "TheoryEngine::getExplanation: Rewrite alert! toAssert = " - << toExplain.d_node << ", toExplain = " << (*find).second.d_node - << std::endl; - - if (proofRecipe) - { - proofRecipe->addRewriteRule(toExplain.d_node, - (*find).second.d_node); - } - } - }) - continue; } } @@ -1814,59 +1668,10 @@ void TheoryEngine::getExplanation(std::vector<NodeTheoryPair>& explanationVector explanationVector.push_back(newExplain); ++ i; - - PROOF({ - if (proofRecipe && inputAssertions) - { - // If we're expanding the target node of the explanation (this is the - // first expansion...), we don't want to add it as a separate proof - // step. It is already part of the assertions. - if (!ContainsKey(*inputAssertions, toExplain.d_node)) - { - LemmaProofRecipe::ProofStep proofStep(toExplain.d_theory, - toExplain.d_node); - if (explanation.getKind() == kind::AND) - { - Node flat = flattenAnd(explanation); - for (unsigned k = 0; k < flat.getNumChildren(); ++k) - { - // If a true constant or a negation of a false constant we can - // ignore it - if (!((flat[k].isConst() && flat[k].getConst<bool>()) - || (flat[k].getKind() == kind::NOT && flat[k][0].isConst() - && !flat[k][0].getConst<bool>()))) - { - proofStep.addAssertion(flat[k].negate()); - } - } - } - else - { - if (!((explanation.isConst() && explanation.getConst<bool>()) - || (explanation.getKind() == kind::NOT - && explanation[0].isConst() - && !explanation[0].getConst<bool>()))) - { - proofStep.addAssertion(explanation.negate()); - } - } - proofRecipe->addStep(proofStep); - } - } - }); } // Keep only the relevant literals explanationVector.resize(j); - - PROOF({ - if (proofRecipe) { - // The remaining literals are the base of the proof - for (unsigned k = 0; k < explanationVector.size(); ++k) { - proofRecipe->addBaseAssertion(explanationVector[k].d_node.negate()); - } - } - }); } void TheoryEngine::setUserAttribute(const std::string& attr, diff --git a/src/theory/theory_engine.h b/src/theory/theory_engine.h index b1543ad0b..167bd6d75 100644 --- a/src/theory/theory_engine.h +++ b/src/theory/theory_engine.h @@ -53,7 +53,6 @@ namespace CVC4 { class ResourceManager; -class LemmaProofRecipe; /** * A pair of a theory and a node. This is used to mark the flow of @@ -292,7 +291,6 @@ class TheoryEngine { * @param p the properties of the lemma. */ theory::LemmaStatus lemma(TNode node, - ProofRule rule, bool negated, theory::LemmaProperty p, theory::TheoryId atomsTo); @@ -442,14 +440,13 @@ class TheoryEngine { bool markPropagation(TNode assertion, TNode originalAssertions, theory::TheoryId toTheoryId, theory::TheoryId fromTheoryId); /** - * Computes the explanation by travarsing the propagation graph and + * Computes the explanation by traversing the propagation graph and * asking relevant theories to explain the propagations. Initially * the explanation vector should contain only the element (node, theory) * where the node is the one to be explained, and the theory is the - * theory that sent the literal. The lemmaProofRecipe will contain a list - * of the explanation steps required to produce the original node. + * theory that sent the literal. */ - void getExplanation(std::vector<NodeTheoryPair>& explanationVector, LemmaProofRecipe* lemmaProofRecipe); + void getExplanation(std::vector<NodeTheoryPair>& explanationVector); public: /** @@ -570,12 +567,6 @@ class TheoryEngine { Node getExplanation(TNode node); /** - * Returns an explanation of the node propagated to the SAT solver and the theory - * that propagated it. - */ - Node getExplanationAndRecipe(TNode node, LemmaProofRecipe* proofRecipe); - - /** * Get the pointer to the model object used by this theory engine. */ theory::TheoryModel* getModel(); @@ -687,14 +678,15 @@ class TheoryEngine { /** * Get instantiation methods * first inputs forall x.q[x] and returns ( q[a], ..., q[z] ) - * second inputs forall x.q[x] and returns ( a, ..., z ) - * third and fourth return mappings e.g. forall x.q1[x] -> ( q1[a]...q1[z] ) , ... , forall x.qn[x] -> ( qn[a]...qn[z] ) + * second inputs forall x.q[x] and returns ( a, ..., z ) + * third and fourth return mappings e.g. forall x.q1[x] -> ( q1[a]...q1[z] ) + * , ... , forall x.qn[x] -> ( qn[a]...qn[z] ) */ void getInstantiations( Node q, std::vector< Node >& insts ); void getInstantiationTermVectors( Node q, std::vector< std::vector< Node > >& tvecs ); void getInstantiations( std::map< Node, std::vector< Node > >& insts ); void getInstantiationTermVectors( std::map< Node, std::vector< std::vector< Node > > >& insts ); - + /** * Get instantiated conjunction, returns q[t1] ^ ... ^ q[tn] where t1...tn are current set of instantiations for q. * Can be used for quantifier elimination when satisfiable and q[t1] ^ ... ^ q[tn] |= q @@ -726,7 +718,7 @@ private: public: /** Set user attribute. - * + * * This function is called when an attribute is set by a user. In SMT-LIBv2 * this is done via the syntax (! n :attr) */ @@ -736,7 +728,7 @@ private: const std::string& str_value); /** Handle user attribute. - * + * * Associates theory t with the attribute attr. Theory t will be * notified whenever an attribute of name attr is set. */ diff --git a/src/theory/theory_inference.cpp b/src/theory/theory_inference.cpp new file mode 100644 index 000000000..618dc640b --- /dev/null +++ b/src/theory/theory_inference.cpp @@ -0,0 +1,63 @@ +/********************* */ +/*! \file theory_inference.cpp + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief The theory inference utility + **/ + +#include "theory/theory_inference.h" + +#include "theory/theory_inference_manager.h" + +using namespace CVC4::kind; + +namespace CVC4 { +namespace theory { + +SimpleTheoryLemma::SimpleTheoryLemma(Node n, + LemmaProperty p, + ProofGenerator* pg) + : d_node(n), d_property(p), d_pg(pg) +{ +} + +bool SimpleTheoryLemma::process(TheoryInferenceManager* im) +{ + Assert(!d_node.isNull()); + // send (trusted) lemma on the output channel with property p + return im->trustedLemma(TrustNode::mkTrustLemma(d_node, d_pg), d_property); +} + +SimpleTheoryInternalFact::SimpleTheoryInternalFact(Node conc, + Node exp, + ProofGenerator* pg) + : d_conc(conc), d_exp(exp), d_pg(pg) +{ +} + +bool SimpleTheoryInternalFact::process(TheoryInferenceManager* im) +{ + bool polarity = d_conc.getKind() != NOT; + TNode atom = polarity ? d_conc : d_conc[0]; + // no double negation or conjunctive conclusions + Assert(atom.getKind() != NOT && atom.getKind() != AND); + if (d_pg != nullptr) + { + im->assertInternalFact(atom, polarity, {d_exp}, d_pg); + } + else + { + im->assertInternalFact(atom, polarity, d_exp); + } + return true; +} + +} // namespace theory +} // namespace CVC4 diff --git a/src/theory/theory_inference.h b/src/theory/theory_inference.h new file mode 100644 index 000000000..8d98051bf --- /dev/null +++ b/src/theory/theory_inference.h @@ -0,0 +1,106 @@ +/********************* */ +/*! \file theory_inference.h + ** \verbatim + ** Top contributors (to current version): + ** Andrew Reynolds + ** This file is part of the CVC4 project. + ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS + ** in the top-level source directory) and their institutional affiliations. + ** All rights reserved. See the file COPYING in the top-level source + ** directory for licensing information.\endverbatim + ** + ** \brief The theory inference utility + **/ + +#include "cvc4_private.h" + +#ifndef CVC4__THEORY__THEORY_INFERENCE_H +#define CVC4__THEORY__THEORY_INFERENCE_H + +#include "expr/node.h" +#include "theory/output_channel.h" + +namespace CVC4 { +namespace theory { + +class TheoryInferenceManager; + +/** + * A theory inference base class. This class is an abstract data structure for + * storing pending lemmas or facts in the buffered inference manager. It can + * be seen a single use object capturing instructions for making a single + * call to TheoryInferenceManager for lemmas or facts. + */ +class TheoryInference +{ + public: + virtual ~TheoryInference() {} + /** + * Called by the provided inference manager to process this inference. This + * method should make call(s) to inference manager to process this + * inference, as well as processing any specific side effects. This method + * typically makes a single call to the provided inference manager e.g. + * d_im->trustedLemma or d_im->assertFactInternal. Notice it is the sole + * responsibility of this class to make this call; the inference manager + * does not call itself otherwise when processing pending inferences. + * + * @return true if the inference was successfully processed by the inference + * manager. This method for instance returns false if it corresponds to a + * lemma that was already cached by im and hence was discarded. + */ + virtual bool process(TheoryInferenceManager* im) = 0; +}; + +/** + * A simple theory lemma with no side effects. Makes a single call to + * trustedLemma in its process method. + */ +class SimpleTheoryLemma : public TheoryInference +{ + public: + SimpleTheoryLemma(Node n, LemmaProperty p, ProofGenerator* pg); + virtual ~SimpleTheoryLemma() {} + /** + * Send the lemma using inference manager im, return true if the lemma was + * sent. + */ + virtual bool process(TheoryInferenceManager* im) override; + /** The lemma to send */ + Node d_node; + /** The lemma property (see OutputChannel::lemma) */ + LemmaProperty d_property; + /** + * The proof generator for this lemma, which if non-null, is wrapped in a + * TrustNode to be set on the output channel via trustedLemma at the time + * the lemma is sent. This proof generator must be able to provide a proof + * for d_node in the remainder of the user context. + */ + ProofGenerator* d_pg; +}; + +/** + * A simple internal fact with no side effects. Makes a single call to + * assertFactInternal in its process method. + */ +class SimpleTheoryInternalFact : public TheoryInference +{ + public: + SimpleTheoryInternalFact(Node conc, Node exp, ProofGenerator* pg); + virtual ~SimpleTheoryInternalFact() {} + /** + * Send the lemma using inference manager im, return true if the lemma was + * sent. + */ + virtual bool process(TheoryInferenceManager* im) override; + /** The lemma to send */ + Node d_conc; + /** The explanation */ + Node d_exp; + /** The proof generator */ + ProofGenerator* d_pg; +}; + +} // namespace theory +} // namespace CVC4 + +#endif diff --git a/src/theory/theory_inference_manager.cpp b/src/theory/theory_inference_manager.cpp index a42c33814..9405a8162 100644 --- a/src/theory/theory_inference_manager.cpp +++ b/src/theory/theory_inference_manager.cpp @@ -30,7 +30,10 @@ TheoryInferenceManager::TheoryInferenceManager(Theory& t, d_out(t.getOutputChannel()), d_ee(nullptr), d_pnm(pnm), - d_keep(t.getSatContext()) + d_keep(t.getSatContext()), + d_lemmasSent(t.getUserContext()), + d_numCurrentLemmas(0), + d_numCurrentFacts(0) { } @@ -47,6 +50,12 @@ void TheoryInferenceManager::setEqualityEngine(eq::EqualityEngine* ee) } } +void TheoryInferenceManager::reset() +{ + d_numCurrentLemmas = 0; + d_numCurrentFacts = 0; +} + void TheoryInferenceManager::conflictEqConstantMerge(TNode a, TNode b) { if (!d_theoryState.isInConflict()) @@ -75,6 +84,27 @@ void TheoryInferenceManager::trustedConflict(TrustNode tconf) } } +void TheoryInferenceManager::conflictExp(PfRule id, + const std::vector<Node>& exp, + const std::vector<Node>& args) +{ + if (!d_theoryState.isInConflict()) + { + if (d_pfee != nullptr) + { + // use proof equality engine to construct the trust node + TrustNode tconf = d_pfee->assertConflict(id, exp, args); + d_out.trustedConflict(tconf); + } + else + { + // version without proofs + Node conf = mkExplainPartial(exp, {}); + conflict(conf); + } + } +} + bool TheoryInferenceManager::propagateLit(TNode lit) { // If already in conflict, no more propagation @@ -114,7 +144,7 @@ TrustNode TheoryInferenceManager::explainConflictEqConstantMerge(TNode a, Node lit = a.eqNode(b); if (d_pfee != nullptr) { - return d_pfee->explain(lit); + return d_pfee->assertConflict(lit); } if (d_ee != nullptr) { @@ -125,42 +155,107 @@ TrustNode TheoryInferenceManager::explainConflictEqConstantMerge(TNode a, << " mkTrustedConflictEqConstantMerge"; } -LemmaStatus TheoryInferenceManager::lemma(TNode lem, LemmaProperty p) +bool TheoryInferenceManager::lemma(TNode lem, LemmaProperty p, bool doCache) +{ + TrustNode tlem = TrustNode::mkTrustLemma(lem, nullptr); + return trustedLemma(tlem, p, doCache); +} + +bool TheoryInferenceManager::trustedLemma(const TrustNode& tlem, + LemmaProperty p, + bool doCache) +{ + if (doCache) + { + if (!cacheLemma(tlem.getNode(), p)) + { + return false; + } + } + d_numCurrentLemmas++; + d_out.trustedLemma(tlem, p); + return true; +} + +bool TheoryInferenceManager::lemmaExp(Node conc, + PfRule id, + const std::vector<Node>& exp, + const std::vector<Node>& noExplain, + const std::vector<Node>& args, + LemmaProperty p, + bool doCache) +{ + if (d_pfee != nullptr) + { + // make the trust node from the proof equality engine + TrustNode trn = d_pfee->assertLemma(conc, id, exp, noExplain, args); + return trustedLemma(trn, p, doCache); + } + // otherwise, not using proofs, explain and send lemma + Node ant = mkExplainPartial(exp, noExplain); + Node lem = NodeManager::currentNM()->mkNode(kind::IMPLIES, ant, conc); + return lemma(lem, p, doCache); +} + +bool TheoryInferenceManager::lemmaExp(Node conc, + const std::vector<Node>& exp, + const std::vector<Node>& noExplain, + ProofGenerator* pg, + LemmaProperty p, + bool doCache) { - return d_out.lemma(lem, p); + if (d_pfee != nullptr) + { + // make the trust node from the proof equality engine + TrustNode trn = d_pfee->assertLemma(conc, exp, noExplain, pg); + return trustedLemma(trn, p, doCache); + } + // otherwise, not using proofs, explain and send lemma + Node ant = mkExplainPartial(exp, noExplain); + Node lem = NodeManager::currentNM()->mkNode(kind::IMPLIES, ant, conc); + return lemma(lem, p, doCache); +} + +bool TheoryInferenceManager::hasCachedLemma(TNode lem, LemmaProperty p) +{ + return d_lemmasSent.find(lem) != d_lemmasSent.end(); +} + +uint32_t TheoryInferenceManager::numSentLemmas() const +{ + return d_numCurrentLemmas; } -LemmaStatus TheoryInferenceManager::trustedLemma(const TrustNode& tlem, - LemmaProperty p) +bool TheoryInferenceManager::hasSentLemma() const { - return d_out.trustedLemma(tlem, p); + return d_numCurrentLemmas != 0; } -void TheoryInferenceManager::assertInternalFact(TNode atom, bool pol, TNode exp) +bool TheoryInferenceManager::assertInternalFact(TNode atom, bool pol, TNode exp) { - processInternalFact(atom, pol, PfRule::UNKNOWN, {exp}, {}, nullptr); + return processInternalFact(atom, pol, PfRule::UNKNOWN, {exp}, {}, nullptr); } -void TheoryInferenceManager::assertInternalFact(TNode atom, +bool TheoryInferenceManager::assertInternalFact(TNode atom, bool pol, PfRule id, const std::vector<Node>& exp, const std::vector<Node>& args) { Assert(id != PfRule::UNKNOWN); - processInternalFact(atom, pol, id, exp, args, nullptr); + return processInternalFact(atom, pol, id, exp, args, nullptr); } -void TheoryInferenceManager::assertInternalFact(TNode atom, +bool TheoryInferenceManager::assertInternalFact(TNode atom, bool pol, const std::vector<Node>& exp, ProofGenerator* pg) { Assert(pg != nullptr); - processInternalFact(atom, pol, PfRule::ASSUME, exp, {}, pg); + return processInternalFact(atom, pol, PfRule::ASSUME, exp, {}, pg); } -void TheoryInferenceManager::processInternalFact(TNode atom, +bool TheoryInferenceManager::processInternalFact(TNode atom, bool pol, PfRule id, const std::vector<Node>& exp, @@ -172,23 +267,26 @@ void TheoryInferenceManager::processInternalFact(TNode atom, // call the pre-notify fact method with preReg = false, isInternal = true if (d_theory.preNotifyFact(atom, pol, expn, false, true)) { - // handled in a theory-specific way that doesn't require equality engine - return; + // Handled in a theory-specific way that doesn't require equality engine, + // notice we return true, indicating that the fact was processed. + return true; } Assert(d_ee != nullptr); Trace("infer-manager") << "TheoryInferenceManager::assertInternalFact: " << expn << std::endl; + d_numCurrentFacts++; // Now, assert the fact. How to do so depends on whether proofs are enabled. // If no proof production, or no proof rule was given + bool ret = false; if (d_pfee == nullptr || id == PfRule::UNKNOWN) { if (atom.getKind() == kind::EQUAL) { - d_ee->assertEquality(atom, pol, expn); + ret = d_ee->assertEquality(atom, pol, expn); } else { - d_ee->assertPredicate(atom, pol, expn); + ret = d_ee->assertPredicate(atom, pol, expn); } // Must reference count the equality and its explanation, which is not done // by the equality engine. Notice that we do *not* need to do this for @@ -208,18 +306,19 @@ void TheoryInferenceManager::processInternalFact(TNode atom, if (pg != nullptr) { // use the proof generator interface - d_pfee->assertFact(lit, expn, pg); + ret = d_pfee->assertFact(lit, expn, pg); } else { // use the explict proof step interface - d_pfee->assertFact(lit, id, expn, args); + ret = d_pfee->assertFact(lit, id, expn, args); } } // call the notify fact method with isInternal = true d_theory.notifyFact(atom, pol, expn, true); Trace("infer-manager") << "TheoryInferenceManager::finished assertInternalFact" << std::endl; + return ret; } void TheoryInferenceManager::explain(TNode n, std::vector<TNode>& assumptions) @@ -244,5 +343,51 @@ Node TheoryInferenceManager::mkExplain(TNode n) return NodeManager::currentNM()->mkAnd(assumptions); } +Node TheoryInferenceManager::mkExplainPartial( + const std::vector<Node>& exp, const std::vector<Node>& noExplain) +{ + std::vector<TNode> assumps; + for (const Node& e : exp) + { + if (std::find(noExplain.begin(), noExplain.end(), e) != noExplain.end()) + { + if (std::find(assumps.begin(), assumps.end(), e) == assumps.end()) + { + // a non-explained literal + assumps.push_back(e); + } + continue; + } + // otherwise, explain it + explain(e, assumps); + } + return NodeManager::currentNM()->mkAnd(assumps); +} + +uint32_t TheoryInferenceManager::numSentFacts() const +{ + return d_numCurrentFacts; +} + +bool TheoryInferenceManager::hasSentFact() const +{ + return d_numCurrentFacts != 0; +} + +bool TheoryInferenceManager::cacheLemma(TNode lem, LemmaProperty p) +{ + if (d_lemmasSent.find(lem) != d_lemmasSent.end()) + { + return false; + } + d_lemmasSent.insert(lem); + return true; +} + +void TheoryInferenceManager::requirePhase(TNode n, bool pol) +{ + return d_out.requirePhase(n, pol); +} + } // namespace theory } // namespace CVC4 diff --git a/src/theory/theory_inference_manager.h b/src/theory/theory_inference_manager.h index af8e817b4..7e5ef6dec 100644 --- a/src/theory/theory_inference_manager.h +++ b/src/theory/theory_inference_manager.h @@ -71,13 +71,23 @@ class TheoryInferenceManager */ TheoryInferenceManager(Theory& t, TheoryState& state, ProofNodeManager* pnm); virtual ~TheoryInferenceManager() {} - //--------------------------------------- initialization /** * Set equality engine, ee is a pointer to the official equality engine * of theory. */ void setEqualityEngine(eq::EqualityEngine* ee); - //--------------------------------------- end initialization + /** + * Reset, which resets counters regarding the number of added lemmas and + * internal facts. This method should be manually called by the theory at + * the appropriate time for the purpose of tracking the usage of this + * inference manager. + * + * For example, some theories implement an internal checking loop that + * repeats while new facts are added. The theory should call reset at the + * beginning of this loop and repeat its strategy while hasAddedFact is true. + */ + void reset(); + //--------------------------------------- propagations /** * T-propagate literal lit, possibly encountered by equality engine, * returns false if we are in conflict. @@ -94,6 +104,7 @@ class TheoryInferenceManager * Theory, if it exists. */ virtual TrustNode explainLit(TNode lit); + //--------------------------------------- conflicts /** * Raise conflict, called when constants a and b merge. Sends the conflict * on the output channel corresponding to the equality engine's explanation @@ -114,11 +125,99 @@ class TheoryInferenceManager * been provided in a custom way. */ void trustedConflict(TrustNode tconf); - /** Send lemma lem with property p on the output channel. */ - LemmaStatus lemma(TNode lem, LemmaProperty p = LemmaProperty::NONE); - /** Send (trusted) lemma lem with property p on the output channel. */ - LemmaStatus trustedLemma(const TrustNode& tlem, - LemmaProperty p = LemmaProperty::NONE); + /** + * Explain and send conflict from contradictory facts. This method is called + * when the proof rule id with premises exp and arguments args concludes + * false. This method sends a trusted conflict corresponding to the official + * equality engine's explanation of literals in exp, with the proof equality + * engine as the proof generator (if it exists). + */ + void conflictExp(PfRule id, + const std::vector<Node>& exp, + const std::vector<Node>& args); + //--------------------------------------- lemmas + /** + * Send (trusted) lemma lem with property p on the output channel. + * + * @param tlem The trust node containing the lemma and its proof generator. + * @param p The property of the lemma + * @param doCache If true, we send the lemma only if it has not already been + * cached (see cacheLemma), and add it to the cache during this call. + * @return true if the lemma was sent on the output channel. + */ + bool trustedLemma(const TrustNode& tlem, + LemmaProperty p = LemmaProperty::NONE, + bool doCache = true); + /** + * Send lemma lem with property p on the output channel. Same as above, with + * a node instead of a trust node. + */ + bool lemma(TNode lem, + LemmaProperty p = LemmaProperty::NONE, + bool doCache = true); + /** + * Explained lemma. This should be called when + * ( exp => conc ) + * is a valid theory lemma. This method adds a lemma where part of exp + * is replaced by its explanation according to the official equality engine + * of the theory. + * + * In particular, this method adds a lemma on the output channel of the form + * ( ^_{e in exp \ noExplain} EXPLAIN(e) ^ noExplain ) => conc + * where EXPLAIN(e) returns the explanation of literal e according to the + * official equality engine of the theory. Note that noExplain is the *subset* + * of exp that should not be explained. + * + * @param conc The conclusion of the lemma + * @param id The proof rule concluding conc + * @param exp The set of (all) literals that imply conc + * @param noExplain The subset of exp that should not be explained by the + * equality engine + * @param args The arguments to the proof step concluding conc + * @param p The property of the lemma + * @param doCache Whether to check and add the lemma to the cache + * @return true if the lemma was sent on the output channel. + */ + bool lemmaExp(Node conc, + PfRule id, + const std::vector<Node>& exp, + const std::vector<Node>& noExplain, + const std::vector<Node>& args, + LemmaProperty p = LemmaProperty::NONE, + bool doCache = true); + /** + * Same as above, but where pg can provide a proof of conc from free + * assumptions in exp. It is required to do so in the remainder of the user + * context when this method returns true. + * + * @param conc The conclusion of the lemma + * @param exp The set of (all) literals that imply conc + * @param noExplain The subset of exp that should not be explained by the + * equality engine + * @param pg If non-null, the proof generator who can provide a proof of conc. + * @param p The property of the lemma + * @param doCache Whether to check and add the lemma to the cache + * @return true if the lemma was sent on the output channel. + */ + bool lemmaExp(Node conc, + const std::vector<Node>& exp, + const std::vector<Node>& noExplain, + ProofGenerator* pg = nullptr, + LemmaProperty p = LemmaProperty::NONE, + bool doCache = true); + /** + * Has this inference manager cached and sent the given lemma (in this user + * context)? This method can be overridden by the particular manager. If not, + * this returns true if lem is in the cache d_lemmasSent maintained by this + * class. Notice that the default cache in this base class is not dependent + * on the lemma property. + */ + virtual bool hasCachedLemma(TNode lem, LemmaProperty p); + /** The number of lemmas we have sent since the last call to reset */ + uint32_t numSentLemmas() const; + /** Have we added a lemma since the last call to reset? */ + bool hasSentLemma() const; + //--------------------------------------- internal facts /** * Assert internal fact. This is recommended method for asserting "internal" * facts into the equality engine of the theory. In particular, this method @@ -130,8 +229,10 @@ class TheoryInferenceManager * @param atom The atom of the fact to assert * @param pol Its polarity * @param exp Its explanation, i.e. ( exp => (~) atom ) is valid. + * @return true if the fact was processed, i.e. it was asserted to the + * equality engine or preNotifyFact returned true. */ - void assertInternalFact(TNode atom, bool pol, TNode exp); + bool assertInternalFact(TNode atom, bool pol, TNode exp); /** * Assert internal fact, with a proof step justification. Notice that if * proofs are not enabled in this inference manager, then this asserts @@ -142,8 +243,10 @@ class TheoryInferenceManager * @param id The proof rule identifier of the proof step * @param exp Its explanation, interpreted as a conjunction * @param args The arguments of the proof step + * @return true if the fact was processed, i.e. it was asserted to the + * equality engine or preNotifyFact returned true. */ - void assertInternalFact(TNode atom, + bool assertInternalFact(TNode atom, bool pol, PfRule id, const std::vector<Node>& exp, @@ -155,21 +258,32 @@ class TheoryInferenceManager * @param atom The atom of the fact to assert * @param pol Its polarity * @param exp Its explanation, interpreted as a conjunction - * @param pg The proof generator for this step. It must be the case that pf - * can provide a proof concluding (~) atom from free asumptions in exp in + * @param pg The proof generator for this step. If non-null, pg must be able + * to provide a proof concluding (~) atom from free asumptions in exp in * the remainder of the current SAT context. + * @return true if the fact was processed, i.e. it was asserted to the + * equality engine or preNotifyFact returned true. */ - void assertInternalFact(TNode atom, + bool assertInternalFact(TNode atom, bool pol, const std::vector<Node>& exp, ProofGenerator* pg); - + /** The number of internal facts we have added since the last call to reset */ + uint32_t numSentFacts() const; + /** Have we added a internal fact since the last call to reset? */ + bool hasSentFact() const; + //--------------------------------------- phase requirements + /** + * Set that literal n has SAT phase requirement pol, that is, it should be + * decided with polarity pol, for details see OutputChannel::requirePhase. + */ + void requirePhase(TNode n, bool pol); protected: /** * Process internal fact. This is a common helper method for the - * assertInternalFact variants above. + * assertInternalFact variants above. Returns true if the fact was processed. */ - void processInternalFact(TNode atom, + bool processInternalFact(TNode atom, bool pol, PfRule id, const std::vector<Node>& exp, @@ -192,6 +306,23 @@ class TheoryInferenceManager * conjunctions), return the explanation as a conjunction. */ Node mkExplain(TNode n); + /** + * Explain the set of formulas in exp using the official equality engine of + * the theory. We ask the equality engine to explain literals in exp + * that do not occur in noExplain, and return unchanged those that occur in + * noExplain. Note the vector noExplain should be a subset of exp. + */ + Node mkExplainPartial(const std::vector<Node>& exp, + const std::vector<Node>& noExplain); + /** + * Cache that lemma lem is being sent with property p. Return true if it + * did not already exist in the cache maintained by this class. If this + * method is not overridden, then we use the d_lemmasSent cache maintained + * by this class, which notice is not dependent on lemma property. If + * the lemma property should be taken into account, the manager should + * override this method to take the lemma property into account as needed. + */ + virtual bool cacheLemma(TNode lem, LemmaProperty p); /** The theory object */ Theory& d_theory; /** Reference to the state of theory */ @@ -211,6 +342,15 @@ class TheoryInferenceManager * SAT-context-dependent. */ NodeSet d_keep; + /** + * A cache of all lemmas sent, which is a user-context-dependent set of + * nodes. Notice that this cache does not depedent on lemma property. + */ + NodeSet d_lemmasSent; + /** The number of lemmas sent since the last call to reset. */ + uint32_t d_numCurrentLemmas; + /** The number of internal facts added since the last call to reset. */ + uint32_t d_numCurrentFacts; }; } // namespace theory diff --git a/src/theory/theory_test_utils.h b/src/theory/theory_test_utils.h index 2593b11a6..965e99338 100644 --- a/src/theory/theory_test_utils.h +++ b/src/theory/theory_test_utils.h @@ -27,7 +27,6 @@ #include "expr/node.h" #include "theory/interrupted.h" #include "theory/output_channel.h" -#include "util/proof.h" #include "util/resource_manager.h" #include "util/unsafe_interrupt_exception.h" @@ -69,17 +68,14 @@ public: ~TestOutputChannel() override {} void safePoint(ResourceManager::Resource r) override {} - void conflict(TNode n, std::unique_ptr<Proof> pf) override - { - push(CONFLICT, n); - } + void conflict(TNode n) override { push(CONFLICT, n); } bool propagate(TNode n) override { push(PROPAGATE, n); return true; } - LemmaStatus lemma(TNode n, ProofRule rule, LemmaProperty p) override + LemmaStatus lemma(TNode n, LemmaProperty p) override { push(LEMMA, n); return LemmaStatus(Node::null(), 0); diff --git a/src/theory/theory_traits_template.h b/src/theory/theory_traits_template.h index 775f42a46..d591affba 100644 --- a/src/theory/theory_traits_template.h +++ b/src/theory/theory_traits_template.h @@ -34,8 +34,6 @@ struct TheoryTraits; ${theory_traits} -#line 38 "${template}" - struct TheoryConstructor { static void addTheory(TheoryEngine* engine, TheoryId id) { switch(id) { diff --git a/src/theory/type_enumerator_template.cpp b/src/theory/type_enumerator_template.cpp index e9fdc6a86..47405f74e 100644 --- a/src/theory/type_enumerator_template.cpp +++ b/src/theory/type_enumerator_template.cpp @@ -22,7 +22,6 @@ ${type_enumerator_includes} -#line 26 "${template}" using namespace std; @@ -42,7 +41,6 @@ TypeEnumeratorInterface* TypeEnumerator::mkTypeEnumerator( } Unreachable(); ${mk_type_enumerator_cases} -#line 46 "${template}" default: Unhandled() << "No type enumerator for type `" << type << "'"; } Unreachable(); diff --git a/src/theory/uf/eq_proof.cpp b/src/theory/uf/eq_proof.cpp index 513cf2f39..d7b615ffa 100644 --- a/src/theory/uf/eq_proof.cpp +++ b/src/theory/uf/eq_proof.cpp @@ -21,33 +21,20 @@ namespace CVC4 { namespace theory { namespace eq { -void EqProof::debug_print(const char* c, - unsigned tb, - PrettyPrinter* prettyPrinter) const +void EqProof::debug_print(const char* c, unsigned tb) const { std::stringstream ss; - debug_print(ss, tb, prettyPrinter); + debug_print(ss, tb); Debug(c) << ss.str(); } -void EqProof::debug_print(std::ostream& os, - unsigned tb, - PrettyPrinter* prettyPrinter) const +void EqProof::debug_print(std::ostream& os, unsigned tb) const { for (unsigned i = 0; i < tb; i++) { os << " "; } - - if (prettyPrinter) - { - os << prettyPrinter->printTag(d_id); - } - else - { - os << static_cast<MergeReasonType>(d_id); - } - os << "("; + os << d_id << "("; if (d_children.empty() && d_node.isNull()) { os << ")"; @@ -66,7 +53,7 @@ void EqProof::debug_print(std::ostream& os, for (unsigned i = 0; i < size; ++i) { os << std::endl; - d_children[i]->debug_print(os, tb + 1, prettyPrinter); + d_children[i]->debug_print(os, tb + 1); if (i < size - 1) { for (unsigned j = 0; j < tb + 1; ++j) @@ -850,8 +837,7 @@ Node EqProof::addToProof( << ", returning " << it->second << "\n"; return it->second; } - Trace("eqproof-conv") << "EqProof::addToProof: adding step for " - << static_cast<MergeReasonType>(d_id) + Trace("eqproof-conv") << "EqProof::addToProof: adding step for " << d_id << " with conclusion " << d_node << "\n"; // Assumption if (d_id == MERGED_THROUGH_EQUALITY) @@ -976,12 +962,10 @@ Node EqProof::addToProof( { Assert(!d_node.isNull() && d_node.getKind() == kind::EQUAL && d_node[1].isConst()) - << ". Conclusion " << d_node << " from " - << static_cast<MergeReasonType>(d_id) + << ". Conclusion " << d_node << " from " << d_id << " was expected to be (= (f t1 ... tn) c)\n"; Assert(!assumptions.count(d_node)) - << "Conclusion " << d_node << " from " - << static_cast<MergeReasonType>(d_id) << " is an assumption\n"; + << "Conclusion " << d_node << " from " << d_id << " is an assumption\n"; // The step has the form // [(= t1 c1)] ... [(= tn cn)] // ------------------------ diff --git a/src/theory/uf/eq_proof.h b/src/theory/uf/eq_proof.h index 492252baa..72368c8c9 100644 --- a/src/theory/uf/eq_proof.h +++ b/src/theory/uf/eq_proof.h @@ -35,36 +35,21 @@ namespace eq { class EqProof { public: - /** A custom pretty printer used for custom rules being those in - * MergeReasonType. */ - class PrettyPrinter - { - public: - virtual ~PrettyPrinter() {} - virtual std::string printTag(unsigned tag) = 0; - }; - EqProof() : d_id(MERGED_THROUGH_REFLEXIVITY) {} /** The proof rule for concluding d_node */ - unsigned d_id; + MergeReasonType d_id; /** The conclusion of this EqProof */ Node d_node; /** The proofs of the premises for deriving d_node with d_id */ std::vector<std::shared_ptr<EqProof>> d_children; /** - * Debug print this proof on debug trace c with tabulation tb and pretty - * printer prettyPrinter. + * Debug print this proof on debug trace c with tabulation tb. */ - void debug_print(const char* c, - unsigned tb = 0, - PrettyPrinter* prettyPrinter = nullptr) const; + void debug_print(const char* c, unsigned tb = 0) const; /** - * Debug print this proof on output stream os with tabulation tb and pretty - * printer prettyPrinter. + * Debug print this proof on output stream os with tabulation tb. */ - void debug_print(std::ostream& os, - unsigned tb = 0, - PrettyPrinter* prettyPrinter = nullptr) const; + void debug_print(std::ostream& os, unsigned tb = 0) const; /** Add to proof * diff --git a/src/theory/uf/equality_engine.cpp b/src/theory/uf/equality_engine.cpp index dd142edf4..c97c99776 100644 --- a/src/theory/uf/equality_engine.cpp +++ b/src/theory/uf/equality_engine.cpp @@ -103,8 +103,6 @@ void EqualityEngine::init() { d_trueId = getNodeId(d_true); d_falseId = getNodeId(d_false); - - d_freshMergeReasonType = eq::NUMBER_OF_MERGE_REASONS; } EqualityEngine::~EqualityEngine() { @@ -1100,7 +1098,7 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, getExplanation(t1Id, t2Id, equalities, cache, eqp); } else { if (eqp) { - eqp->d_id = eq::MERGED_THROUGH_TRANS; + eqp->d_id = MERGED_THROUGH_TRANS; eqp->d_node = d_nodes[t1Id].eqNode(d_nodes[t2Id]).notNode(); } @@ -1137,12 +1135,13 @@ void EqualityEngine::explainEquality(TNode t1, TNode t2, bool polarity, Debug("pf::ee") << "Child proof is:" << std::endl; eqpc->debug_print("pf::ee", 1); } - if (eqpc->d_id == eq::MERGED_THROUGH_TRANS) { + if (eqpc->d_id == MERGED_THROUGH_TRANS) + { std::vector<std::shared_ptr<EqProof>> orderedChildren; bool nullCongruenceFound = false; for (const auto& child : eqpc->d_children) { - if (child->d_id == eq::MERGED_THROUGH_CONGRUENCE + if (child->d_id == MERGED_THROUGH_CONGRUENCE && child->d_node.isNull()) { nullCongruenceFound = true; @@ -1382,35 +1381,24 @@ void EqualityEngine::getExplanation( #endif // If the nodes are the same, we're done - if (t1Id == t2Id){ - if( eqp ) { - if (options::proofNew()) - { - // ignore equalities between function symbols, i.e. internal nullary - // non-constant nodes. - // - // Note that this is robust for HOL because in that case function - // symbols are not internal nodes - if (d_isInternal[t1Id] && d_nodes[t1Id].getNumChildren() == 0 - && !d_isConstant[t1Id]) - { - eqp->d_node = Node::null(); - } - else - { - Assert(d_nodes[t1Id].getKind() != kind::BUILTIN); - eqp->d_node = d_nodes[t1Id].eqNode(d_nodes[t1Id]); - } - } - else if ((d_nodes[t1Id].getKind() == kind::BUILTIN) - && (d_nodes[t1Id].getConst<Kind>() == kind::SELECT)) + if (t1Id == t2Id) + { + if (eqp) + { + // ignore equalities between function symbols, i.e. internal nullary + // non-constant nodes. + // + // Note that this is robust for HOL because in that case function + // symbols are not internal nodes + if (d_isInternal[t1Id] && d_nodes[t1Id].getNumChildren() == 0 + && !d_isConstant[t1Id]) { - std::vector<Node> no_children; - eqp->d_node = NodeManager::currentNM()->mkNode(kind::PARTIAL_SELECT_0, no_children); + eqp->d_node = Node::null(); } else { - eqp->d_node = ProofManager::currentPM()->mkOp(d_nodes[t1Id]); + Assert(d_nodes[t1Id].getKind() != kind::BUILTIN); + eqp->d_node = d_nodes[t1Id].eqNode(d_nodes[t1Id]); } } return; @@ -1466,7 +1454,8 @@ void EqualityEngine::getExplanation( // The current node currentNode = bfsQueue[currentIndex].d_nodeId; EqualityNodeId edgeNode = d_equalityEdges[currentEdge].getNodeId(); - unsigned reasonType = d_equalityEdges[currentEdge].getReasonType(); + MergeReasonType reasonType = static_cast<MergeReasonType>( + d_equalityEdges[currentEdge].getReasonType()); Node reason = d_equalityEdges[currentEdge].getReason(); Debug("equality") @@ -1482,8 +1471,9 @@ void EqualityEngine::getExplanation( << edge.getNodeId() << "} " << d_nodes[edge.getNodeId()] << ")" << std::endl; Debug("equality") - << d_name << " reason type = " - << static_cast<MergeReasonType>(reasonType) << std::endl; + << d_name + << " reason type = " << reasonType + << "\n"; std::shared_ptr<EqProof> eqpc;; // Make child proof if a proof is being constructed @@ -1518,63 +1508,21 @@ void EqualityEngine::getExplanation( { eqpc->d_children.push_back(eqpc1); eqpc->d_children.push_back(eqpc2); - if (options::proofNew()) + // build conclusion if ids correspond to non-internal nodes or + // if non-internal nodes can be retrieved from them (in the + // case of n-ary applications), otherwise leave conclusion as + // null. This is only done for congruence kinds, since + // congruence is not used otherwise. + Kind k = d_nodes[currentNode].getKind(); + if (d_congruenceKinds[k]) { - // build conclusion if ids correspond to non-internal nodes or - // if non-internal nodes can be retrieved from them (in the - // case of n-ary applications), otherwise leave conclusion as - // null. This is only done for congruence kinds, since - // congruence is not used otherwise. - Kind k = d_nodes[currentNode].getKind(); - if (d_congruenceKinds[k]) - { - buildEqConclusion(currentNode, edgeNode, eqpc.get()); - } - else - { - Assert(k == kind::EQUAL) - << "not an internal node " << d_nodes[currentNode] - << " with non-congruence with " << k << "\n"; - } - } - else if (d_nodes[currentNode].getKind() == kind::EQUAL) - { - //leave node null for now - eqpc->d_node = Node::null(); + buildEqConclusion(currentNode, edgeNode, eqpc.get()); } else { - if (d_nodes[f1.d_a].getKind() == kind::APPLY_UF - || d_nodes[f1.d_a].getKind() == kind::SELECT - || d_nodes[f1.d_a].getKind() == kind::STORE) - { - eqpc->d_node = d_nodes[f1.d_a]; - } - else - { - if (d_nodes[f1.d_a].getKind() == kind::BUILTIN - && d_nodes[f1.d_a].getConst<Kind>() == kind::SELECT) - { - eqpc->d_node = NodeManager::currentNM()->mkNode( - kind::PARTIAL_SELECT_1, d_nodes[f1.d_b]); - // The first child is a PARTIAL_SELECT_0. - // Give it a child so that we know what kind of (read) it is, when we dump to LFSC. - Assert(eqpc->d_children[0]->d_node.getKind() - == kind::PARTIAL_SELECT_0); - Assert(eqpc->d_children[0]->d_children.size() == 0); - - eqpc->d_children[0]->d_node = - NodeManager::currentNM()->mkNode( - kind::PARTIAL_SELECT_0, d_nodes[f1.d_b]); - } - else - { - eqpc->d_node = NodeManager::currentNM()->mkNode( - kind::PARTIAL_APPLY_UF, - ProofManager::currentPM()->mkOp(d_nodes[f1.d_a]), - d_nodes[f1.d_b]); - } - } + Assert(k == kind::EQUAL) + << "not an internal node " << d_nodes[currentNode] + << " with non-congruence with " << k << "\n"; } } Debug("equality") << pop; @@ -1608,7 +1556,7 @@ void EqualityEngine::getExplanation( // Get the node we interpreted TNode interpreted; - if (eqpc && options::proofNew()) + if (eqpc) { // build the conclusion f(c1, ..., cn) = c if (d_nodes[currentNode].isConst()) @@ -1661,24 +1609,19 @@ void EqualityEngine::getExplanation( Debug("equality") << d_name << "::eq::getExplanation(): adding: " << reason << std::endl; Debug("equality") - << d_name << "::eq::getExplanation(): reason type = " - << static_cast<MergeReasonType>(reasonType) << std::endl; + << d_name + << "::eq::getExplanation(): reason type = " << reasonType + << "\n"; Node a = d_nodes[currentNode]; Node b = d_nodes[d_equalityEdges[currentEdge].getNodeId()]; if (eqpc) { - //apply proof reconstruction processing (when eqpc is non-null) - if (d_pathReconstructionTriggers.find(reasonType) != d_pathReconstructionTriggers.end()) { - d_pathReconstructionTriggers.find(reasonType) - ->second->notify(reasonType, reason, a, b, equalities, - eqpc.get()); - } if (reasonType == MERGED_THROUGH_EQUALITY) { // in the new proof infrastructure we can assume that "theory // assumptions", which are a consequence of theory reasoning // on other assumptions, are externally justified. In this // case we can use (= a b) directly as the conclusion here. - eqpc->d_node = !options::proofNew() ? reason : b.eqNode(a); + eqpc->d_node = b.eqNode(a); } else { // The LFSC translator prefers (not (= a b)) over (= (= a b) false) @@ -1722,20 +1665,12 @@ void EqualityEngine::getExplanation( } else { eqp->d_id = MERGED_THROUGH_TRANS; eqp->d_children.insert( eqp->d_children.end(), eqp_trans.begin(), eqp_trans.end() ); - if (options::proofNew()) - { - // build conclusion in case of equality between non-internal - // nodes or of n-ary congruence kinds, otherwise leave as - // null. The latter is necessary for the overall handling of - // congruence proofs involving n-ary kinds, see - // EqProof::reduceNestedCongruence for more details. - buildEqConclusion(t1Id, t2Id, eqp); - } - else - { - eqp->d_node = NodeManager::currentNM()->mkNode( - kind::EQUAL, d_nodes[t1Id], d_nodes[t2Id]); - } + // build conclusion in case of equality between non-internal + // nodes or of n-ary congruence kinds, otherwise leave as + // null. The latter is necessary for the overall handling of + // congruence proofs involving n-ary kinds, see + // EqProof::reduceNestedCongruence for more details. + buildEqConclusion(t1Id, t2Id, eqp); } if (Debug.isOn("pf::ee")) { @@ -2218,18 +2153,6 @@ size_t EqualityEngine::getSize(TNode t) { return getEqualityNode(getEqualityNode(t).getFind()).getSize(); } - -void EqualityEngine::addPathReconstructionTrigger(unsigned trigger, const PathReconstructionNotify* notify) { - // Currently we can only inform one callback per trigger - Assert(d_pathReconstructionTriggers.find(trigger) - == d_pathReconstructionTriggers.end()); - d_pathReconstructionTriggers[trigger] = notify; -} - -unsigned EqualityEngine::getFreshMergeReasonType() { - return d_freshMergeReasonType++; -} - std::string EqualityEngine::identify() const { return d_name; } void EqualityEngine::addTriggerTerm(TNode t, TheoryId tag) diff --git a/src/theory/uf/equality_engine.h b/src/theory/uf/equality_engine.h index 19a10eba8..f8444965f 100644 --- a/src/theory/uf/equality_engine.h +++ b/src/theory/uf/equality_engine.h @@ -43,26 +43,10 @@ namespace CVC4 { namespace theory { namespace eq { - -class EqProof; class EqClassesIterator; class EqClassIterator; /** - * An interface for equality engine notifications during equality path reconstruction. - * Can be used to add theory-specific logic for, e.g., proof construction. - */ -class PathReconstructionNotify { -public: - - virtual ~PathReconstructionNotify() {} - - virtual void notify(unsigned reasonType, Node reason, Node a, Node b, - std::vector<TNode>& equalities, - EqProof* proof) const = 0; -}; - -/** * Class for keeping an incremental congruence closure over a set of terms. It provides * notifications via an EqualityEngineNotify object. */ @@ -152,9 +136,6 @@ private: /** The map of kinds with operators to be considered external (for higher-order) */ KindMap d_congruenceKindsExtOperators; - /** Objects that need to be notified during equality path reconstruction */ - std::map<unsigned, const PathReconstructionNotify*> d_pathReconstructionTriggers; - /** Map from nodes to their ids */ std::unordered_map<TNode, EqualityNodeId, TNodeHashFunction> d_nodeIds; @@ -196,9 +177,6 @@ private: /** Memory for the use-list nodes */ std::vector<UseListNode> d_useListNodes; - /** A fresh merge reason type to return upon request */ - unsigned d_freshMergeReasonType; - /** * We keep a list of asserted equalities. Not among original terms, but * among the class representatives. @@ -861,16 +839,6 @@ private: */ bool consistent() const { return !d_done; } - /** - * Marks an object for merge type based notification during equality path reconstruction. - */ - void addPathReconstructionTrigger(unsigned trigger, const PathReconstructionNotify* notify); - - /** - * Returns a fresh merge reason type tag for the client to use. - */ - unsigned getFreshMergeReasonType(); - /** Identify this equality engine (for debugging, etc..) */ std::string identify() const; }; diff --git a/src/theory/uf/equality_engine_types.h b/src/theory/uf/equality_engine_types.h index 14cd80436..cceffa51d 100644 --- a/src/theory/uf/equality_engine_types.h +++ b/src/theory/uf/equality_engine_types.h @@ -63,20 +63,26 @@ static const EqualityEdgeId null_edge = (EqualityEdgeId)(-1); * or a merge of an equality to false due to both sides being * (different) constants. */ -enum MergeReasonType { - /** Terms were merged due to application of congruence closure */ +enum MergeReasonType +{ + /** Terms were merged due to congruence */ MERGED_THROUGH_CONGRUENCE, - /** Terms were merged due to application of pure equality */ + /** Terms were merged due to an assumption */ MERGED_THROUGH_EQUALITY, - /** Equality was merged to true, due to both sides of equality being in the same class */ + /** Terms were merged due to reflexivity */ MERGED_THROUGH_REFLEXIVITY, - /** Equality was merged to false, due to both sides of equality being a constant */ + /** Terms were merged due to theory reasoning */ MERGED_THROUGH_CONSTANTS, - /** (for proofs only) Equality was merged due to transitivity */ + /** Terms were merged due to transitivity */ MERGED_THROUGH_TRANS, - - /** Reason types beyond this constant are theory specific reasons */ - NUMBER_OF_MERGE_REASONS + // TEMPORARY RULES WHILE WE DON'T MIGRATE TO PROOF_NEW + + /** Terms were merged due to arrays read-over-write */ + MERGED_THROUGH_ROW, + /** Terms were merged due to arrays read-over-write (1) */ + MERGED_THROUGH_ROW1, + /** Terms were merged due to extensionality */ + MERGED_THROUGH_EXT, }; inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { @@ -90,13 +96,13 @@ inline std::ostream& operator << (std::ostream& out, MergeReasonType reason) { case MERGED_THROUGH_REFLEXIVITY: out << "reflexivity"; break; - case MERGED_THROUGH_CONSTANTS: - out << "constants disequal"; - break; + case MERGED_THROUGH_CONSTANTS: out << "theory constants"; break; case MERGED_THROUGH_TRANS: out << "transitivity"; break; - + case MERGED_THROUGH_ROW: out << "read-over-write"; break; + case MERGED_THROUGH_ROW1: out << "read-over-write (1)"; break; + case MERGED_THROUGH_EXT: out << "extensionality"; break; default: out << "[theory]"; break; diff --git a/src/theory/uf/ho_extension.cpp b/src/theory/uf/ho_extension.cpp index 11b872e72..2a57cde5e 100644 --- a/src/theory/uf/ho_extension.cpp +++ b/src/theory/uf/ho_extension.cpp @@ -18,7 +18,6 @@ #include "expr/node_algorithm.h" #include "options/uf_options.h" #include "theory/theory_model.h" -#include "theory/uf/theory_uf.h" #include "theory/uf/theory_uf_rewriter.h" using namespace std; @@ -28,9 +27,9 @@ namespace CVC4 { namespace theory { namespace uf { -HoExtension::HoExtension(TheoryUF& p, TheoryState& state) - : d_parent(p), - d_state(state), +HoExtension::HoExtension(TheoryState& state, TheoryInferenceManager& im) + : d_state(state), + d_im(im), d_extensionality(state.getUserContext()), d_uf_std_skolem(state.getUserContext()) { @@ -108,7 +107,7 @@ unsigned HoExtension::applyExtensionality(TNode deq) Node lem = NodeManager::currentNM()->mkNode(OR, deq[0], conc); Trace("uf-ho-lemma") << "uf-ho-lemma : extensionality : " << lem << std::endl; - d_parent.getOutputChannel().lemma(lem); + d_im.lemma(lem); return 1; } return 0; @@ -168,7 +167,7 @@ Node HoExtension::getApplyUfForHoApply(Node node) Trace("uf-ho-lemma") << "uf-ho-lemma : Skolem definition for apply-conversion : " << lem << std::endl; - d_parent.getOutputChannel().lemma(lem); + d_im.lemma(lem); d_uf_std_skolem[f] = new_f; } else @@ -257,7 +256,7 @@ unsigned HoExtension::checkExtensionality(TheoryModel* m) Node lem = nm->mkNode(OR, deq.negate(), eq); Trace("uf-ho") << "HoExtension: cmi extensionality lemma " << lem << std::endl; - d_parent.getOutputChannel().lemma(lem); + d_im.lemma(lem); return 1; } } @@ -282,10 +281,10 @@ unsigned HoExtension::applyAppCompletion(TNode n) Node ret = TheoryUfRewriter::getHoApplyForApplyUf(n); if (!ee->hasTerm(ret) || !ee->areEqual(ret, n)) { - Node eq = ret.eqNode(n); + Node eq = n.eqNode(ret); Trace("uf-ho-lemma") << "uf-ho-lemma : infer, by apply-expand : " << eq << std::endl; - ee->assertEquality(eq, true, d_true); + d_im.assertInternalFact(eq, true, PfRule::HO_APP_ENCODE, {}, {n}); return 1; } Trace("uf-ho-debug") << " ...already have " << ret << " == " << n << "." @@ -442,7 +441,7 @@ bool HoExtension::collectModelInfoHoTerm(Node n, TheoryModel* m) Node eq = n.eqNode(hn); Trace("uf-ho") << "HoExtension: cmi app completion lemma " << eq << std::endl; - d_parent.getOutputChannel().lemma(eq); + d_im.lemma(eq); return false; } } diff --git a/src/theory/uf/ho_extension.h b/src/theory/uf/ho_extension.h index 25cb3623b..ceb8e9c12 100644 --- a/src/theory/uf/ho_extension.h +++ b/src/theory/uf/ho_extension.h @@ -21,6 +21,7 @@ #include "context/cdhashset.h" #include "context/cdo.h" #include "expr/node.h" +#include "theory/theory_inference_manager.h" #include "theory/theory_model.h" #include "theory/theory_state.h" @@ -51,7 +52,7 @@ class HoExtension typedef context::CDHashMap<Node, Node, NodeHashFunction> NodeNodeMap; public: - HoExtension(TheoryUF& p, TheoryState& state); + HoExtension(TheoryState& state, TheoryInferenceManager& im); /** expand definition * @@ -181,10 +182,10 @@ class HoExtension private: /** common constants */ Node d_true; - /** the parent of this extension */ - TheoryUF& d_parent; /** Reference to the state object */ TheoryState& d_state; + /** Reference to the inference manager */ + TheoryInferenceManager& d_im; /** extensionality has been applied to these disequalities */ NodeSet d_extensionality; diff --git a/src/theory/uf/proof_checker.cpp b/src/theory/uf/proof_checker.cpp index b010b6d17..ea95c1f24 100644 --- a/src/theory/uf/proof_checker.cpp +++ b/src/theory/uf/proof_checker.cpp @@ -14,6 +14,8 @@ #include "theory/uf/proof_checker.h" +#include "theory/uf/theory_uf_rewriter.h" + using namespace CVC4::kind; namespace CVC4 { @@ -31,6 +33,8 @@ void UfProofRuleChecker::registerTo(ProofChecker* pc) pc->registerChecker(PfRule::TRUE_ELIM, this); pc->registerChecker(PfRule::FALSE_INTRO, this); pc->registerChecker(PfRule::FALSE_ELIM, this); + pc->registerChecker(PfRule::HO_CONG, this); + pc->registerChecker(PfRule::HO_APP_ENCODE, this); } Node UfProofRuleChecker::checkInternal(PfRule id, @@ -171,6 +175,32 @@ Node UfProofRuleChecker::checkInternal(PfRule id, } return children[0][0].notNode(); } + if (id == PfRule::HO_CONG) + { + Assert(children.size() > 0); + std::vector<Node> lchildren; + std::vector<Node> rchildren; + for (size_t i = 0, nchild = children.size(); i < nchild; ++i) + { + Node eqp = children[i]; + if (eqp.getKind() != EQUAL) + { + return Node::null(); + } + lchildren.push_back(eqp[0]); + rchildren.push_back(eqp[1]); + } + NodeManager* nm = NodeManager::currentNM(); + Node l = nm->mkNode(kind::APPLY_UF, lchildren); + Node r = nm->mkNode(kind::APPLY_UF, rchildren); + return l.eqNode(r); + } + else if (id == PfRule::HO_APP_ENCODE) + { + Assert(args.size() == 1); + Node ret = TheoryUfRewriter::getHoApplyForApplyUf(args[0]); + return args[0].eqNode(ret); + } // no rule return Node::null(); } diff --git a/src/theory/uf/theory_uf.cpp b/src/theory/uf/theory_uf.cpp index f94cc36af..a58834891 100644 --- a/src/theory/uf/theory_uf.cpp +++ b/src/theory/uf/theory_uf.cpp @@ -25,9 +25,6 @@ #include "options/smt_options.h" #include "options/theory_options.h" #include "options/uf_options.h" -#include "proof/proof_manager.h" -#include "proof/theory_proof.h" -#include "proof/uf_proof.h" #include "theory/theory_model.h" #include "theory/type_enumerator.h" #include "theory/uf/cardinality_extension.h" @@ -56,7 +53,8 @@ TheoryUF::TheoryUF(context::Context* c, d_ho(nullptr), d_functionsTerms(c), d_symb(u, instanceName), - d_state(c, u, valuation) + d_state(c, u, valuation), + d_im(*this, d_state, pnm) { d_true = NodeManager::currentNM()->mkConst( true ); @@ -65,8 +63,9 @@ TheoryUF::TheoryUF(context::Context* c, { d_ufProofChecker.registerTo(pc); } - // indicate we are using the default theory state object + // indicate we are using the default theory state and inference managers d_theoryState = &d_state; + d_inferManager = &d_im; } TheoryUF::~TheoryUF() { @@ -99,7 +98,7 @@ void TheoryUF::finishInit() { if (options::ufHo()) { d_equalityEngine->addFunctionKind(kind::HO_APPLY); - d_ho.reset(new HoExtension(*this, d_state)); + d_ho.reset(new HoExtension(d_state, d_im)); } } @@ -288,40 +287,26 @@ bool TheoryUF::propagateLit(TNode literal) return ok; }/* TheoryUF::propagate(TNode) */ -void TheoryUF::explain(TNode literal, std::vector<TNode>& assumptions, eq::EqProof* pf) { +void TheoryUF::explain(TNode literal, Node& exp) +{ + Debug("uf") << "TheoryUF::explain(" << literal << ")" << std::endl; + std::vector<TNode> assumptions; // Do the work bool polarity = literal.getKind() != kind::NOT; TNode atom = polarity ? literal : literal[0]; - if (atom.getKind() == kind::EQUAL) { + if (atom.getKind() == kind::EQUAL) + { d_equalityEngine->explainEquality( - atom[0], atom[1], polarity, assumptions, pf); - } else { - d_equalityEngine->explainPredicate(atom, polarity, assumptions, pf); + atom[0], atom[1], polarity, assumptions, nullptr); } - if( pf ){ - Debug("pf::uf") << std::endl; - pf->debug_print("pf::uf"); - } - - Debug("pf::uf") << "UF: explain( " << literal << " ):" << std::endl << "\t"; - for (unsigned i = 0; i < assumptions.size(); ++i) { - Debug("pf::uf") << assumptions[i] << " "; + else + { + d_equalityEngine->explainPredicate(atom, polarity, assumptions, nullptr); } - Debug("pf::uf") << std::endl; + exp = mkAnd(assumptions); } -TrustNode TheoryUF::explain(TNode literal) -{ - Node exp = explain(literal, NULL); - return TrustNode::mkTrustPropExp(literal, exp, nullptr); -} - -Node TheoryUF::explain(TNode literal, eq::EqProof* pf) { - Debug("uf") << "TheoryUF::explain(" << literal << ")" << std::endl; - std::vector<TNode> assumptions; - explain(literal, assumptions, pf); - return mkAnd(assumptions); -} +TrustNode TheoryUF::explain(TNode literal) { return d_im.explainLit(literal); } bool TheoryUF::collectModelValues(TheoryModel* m, const std::set<Node>& termSet) { @@ -350,7 +335,8 @@ void TheoryUF::presolve() { i != newClauses.end(); ++i) { Debug("uf") << "uf: generating a lemma: " << *i << std::endl; - d_out->lemma(*i); + // no proof generator provided + d_im.lemma(*i); } } if( d_thss ){ @@ -662,13 +648,12 @@ void TheoryUF::computeCareGraph() { << std::endl; }/* TheoryUF::computeCareGraph() */ -void TheoryUF::conflict(TNode a, TNode b) { - std::shared_ptr<eq::EqProof> pf = - d_proofsEnabled ? std::make_shared<eq::EqProof>() : nullptr; - Node conf = explain(a.eqNode(b), pf.get()); - std::unique_ptr<ProofUF> puf(d_proofsEnabled ? new ProofUF(pf) : nullptr); - d_out->conflict(conf, std::move(puf)); - d_state.notifyInConflict(); +void TheoryUF::conflict(TNode a, TNode b) +{ + // call the inference manager, which will construct the conflict (possibly + // with proofs from the underlying proof equality engine), and notify the + // state object. + d_im.conflictEqConstantMerge(a, b); } void TheoryUF::eqNotifyNewClass(TNode t) { diff --git a/src/theory/uf/theory_uf.h b/src/theory/uf/theory_uf.h index 2bfd7e16c..4a8369483 100644 --- a/src/theory/uf/theory_uf.h +++ b/src/theory/uf/theory_uf.h @@ -26,6 +26,7 @@ #include "theory/theory.h" #include "theory/uf/equality_engine.h" #include "theory/uf/proof_checker.h" +#include "theory/uf/proof_equality_engine.h" #include "theory/uf/symmetry_breaker.h" #include "theory/uf/theory_uf_rewriter.h" @@ -112,17 +113,6 @@ private: */ bool propagateLit(TNode literal); - /** - * Explain why this literal is true by adding assumptions - * with proof (if "pf" is non-NULL). - */ - void explain(TNode literal, std::vector<TNode>& assumptions, eq::EqProof* pf); - - /** - * Explain a literal, with proof (if "pf" is non-NULL). - */ - Node explain(TNode literal, eq::EqProof* pf); - /** All the function terms that the theory has seen */ context::CDList<TNode> d_functionsTerms; @@ -202,6 +192,9 @@ private: CardinalityExtension* getCardinalityExtension() const { return d_thss.get(); } private: + /** Explain why this literal is true by building an explanation */ + void explain(TNode literal, Node& exp); + bool areCareDisequal(TNode x, TNode y); void addCarePairs(const TNodeTrie* t1, const TNodeTrie* t2, @@ -213,6 +206,8 @@ private: UfProofRuleChecker d_ufProofChecker; /** A (default) theory state object */ TheoryState d_state; + /** A (default) inference manager */ + TheoryInferenceManager d_im; };/* class TheoryUF */ }/* CVC4::theory::uf namespace */ diff --git a/src/theory/uf/theory_uf_rewriter.h b/src/theory/uf/theory_uf_rewriter.h index e651edb51..5d301cf9e 100644 --- a/src/theory/uf/theory_uf_rewriter.h +++ b/src/theory/uf/theory_uf_rewriter.h @@ -22,6 +22,7 @@ #include "expr/node_algorithm.h" #include "options/uf_options.h" +#include "theory/rewriter.h" #include "theory/substitutions.h" #include "theory/theory_rewriter.h" diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 09bbfc518..0977714c2 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -26,7 +26,6 @@ libcvc4_add_sources( ostream_util.h poly_util.cpp poly_util.h - proof.h random.cpp random.h resource_manager.cpp diff --git a/src/util/proof.h b/src/util/proof.h deleted file mode 100644 index c89a3b0a6..000000000 --- a/src/util/proof.h +++ /dev/null @@ -1,44 +0,0 @@ -/********************* */ -/*! \file proof.h - ** \verbatim - ** Top contributors (to current version): - ** Tim King, Morgan Deters, Mathias Preiner - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief [[ Add one-line brief description here ]] - ** - ** [[ Add lengthier description here ]] - ** \todo document this file - **/ - -#include "cvc4_public.h" - -#ifndef CVC4__PROOF_H -#define CVC4__PROOF_H - -#include <iosfwd> -#include <unordered_map> - -namespace CVC4 { - -class Expr; -class ProofLetCount; -struct ExprHashFunction; - -typedef std::unordered_map<Expr, ProofLetCount, ExprHashFunction> ProofLetMap; - -class CVC4_PUBLIC Proof -{ - public: - virtual ~Proof() {} - virtual void toStream(std::ostream& out) const = 0; - virtual void toStream(std::ostream& out, const ProofLetMap& map) const = 0; -};/* class Proof */ - -}/* CVC4 namespace */ - -#endif /* CVC4__PROOF_H */ diff --git a/src/util/string.cpp b/src/util/string.cpp index 44c4d3e4b..a1a40df8a 100644 --- a/src/util/string.cpp +++ b/src/util/string.cpp @@ -131,7 +131,7 @@ std::vector<unsigned> String::toInternal(const std::string& s, ++i; // are we an escape sequence? bool isEscapeSequence = true; - // the string corresponding to the hexidecimal code point + // the string corresponding to the hexadecimal code point std::stringstream hexString; // is the slash followed by a 'u'? Could be last character. if (i >= s.size() || s[i] != 'u') @@ -195,7 +195,7 @@ std::vector<unsigned> String::toInternal(const std::string& s, } if (!isEnd) { - // if we were interupted before ending, then this is not a valid + // if we were interrupted before ending, then this is not a valid // escape sequence isEscapeSequence = false; } @@ -210,7 +210,7 @@ std::vector<unsigned> String::toInternal(const std::string& s, if (val > num_codes()) { // Failed due to being out of range. This can happen for strings of - // the form \ u { d_4 d_3 d_2 d_1 d_0 } where d_4 is a hexidecimal not + // the form \ u { d_4 d_3 d_2 d_1 d_0 } where d_4 is a hexadecimal not // in the range [0-2]. isEscapeSequence = false; } diff --git a/src/util/string.h b/src/util/string.h index ca458232f..fb4a1208c 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -58,7 +58,7 @@ class CVC4_PUBLIC String { * \u{d_2 d_1 d_0} * \u{d_3 d_2 d_1 d_0} * \u{d_4 d_3 d_2 d_1 d_0} - * where d_0 ... d_4 are hexidecimal digits, to the appropriate character. + * where d_0 ... d_4 are hexadecimal digits, to the appropriate character. * * If useEscSequences is false, then the characters of the constructed * CVC4::String correspond one-to-one with the input string. @@ -213,7 +213,7 @@ class CVC4_PUBLIC String { * This is true for code points between 48 ('0') and 57 ('9'). */ static bool isDigit(unsigned character); - /** is the unsigned a hexidecimal digit? + /** is the unsigned a hexadecimal digit? * * This is true for code points between 48 ('0') and 57 ('9'), code points * between 65 ('A') and 70 ('F) and code points between 97 ('a') and 102 ('f). diff --git a/test/regress/CMakeLists.txt b/test/regress/CMakeLists.txt index 0caeafb36..1a33ee3a5 100644 --- a/test/regress/CMakeLists.txt +++ b/test/regress/CMakeLists.txt @@ -59,6 +59,7 @@ set(regress_0_tests regress0/arrays/bug272.minimized.smtv1.smt2 regress0/arrays/bug272.smtv1.smt2 regress0/arrays/bug3020.smt2 + regress0/arrays/bug4957.smt2 regress0/arrays/bug637.delta.smt2 regress0/arrays/constarr.cvc regress0/arrays/constarr.smt2 @@ -621,7 +622,6 @@ set(regress_0_tests regress0/nl/very-easy-sat.smt2 regress0/nl/very-simple-unsat.smt2 regress0/options/invalid_dump.smt2 - regress0/options/invalid_option_inc_proofs.smt2 regress0/opt-abd-no-use.smt2 regress0/parallel-let.smt2 regress0/parser/as.smt2 @@ -871,7 +871,7 @@ set(regress_0_tests regress0/seq/seq-nth.smt2 regress0/seq/seq-nth-uf.smt2 regress0/seq/seq-nth-uf-z.smt2 - regress0/seq/seq-nth-undef.smt2 + regress0/seq/seq-nth-undef.smt2 regress0/seq/seq-rewrites.smt2 regress0/sets/abt-min.smt2 regress0/sets/abt-te-exh.smt2 diff --git a/test/regress/regress0/arrays/bug4957.smt2 b/test/regress/regress0/arrays/bug4957.smt2 new file mode 100644 index 000000000..f82ae1932 --- /dev/null +++ b/test/regress/regress0/arrays/bug4957.smt2 @@ -0,0 +1,6 @@ +; COMMAND-LINE: --ackermann --no-check-unsat-cores +; EXPECT: unsat +(set-logic QF_ALIA) +(declare-fun a () (Array Int Int)) +(assert (distinct (select a 0) (select (ite false a a) 0))) +(check-sat) diff --git a/test/regress/regress0/bug217.smt2 b/test/regress/regress0/bug217.smt2 index 30c87333e..4d2e828b5 100644 --- a/test/regress/regress0/bug217.smt2 +++ b/test/regress/regress0/bug217.smt2 @@ -1,4 +1,3 @@ -; COMMAND-LINE: --fewer-preprocessing-holes ; EXPECT: unsat (set-logic QF_UF) (set-info :status unsat) diff --git a/test/regress/regress0/options/invalid_option_inc_proofs.smt2 b/test/regress/regress0/options/invalid_option_inc_proofs.smt2 deleted file mode 100644 index f63dbd27f..000000000 --- a/test/regress/regress0/options/invalid_option_inc_proofs.smt2 +++ /dev/null @@ -1,6 +0,0 @@ -; REQUIRES: proof -; COMMAND-LINE: --incremental --proof -; EXPECT: (error "Error in option parsing: --incremental is not supported with proofs") -; EXIT: 1 -(set-logic QF_BV) -(check-sat) diff --git a/test/regress/regress1/bv/bench_38.delta.smt2 b/test/regress/regress1/bv/bench_38.delta.smt2 index 760614348..3f809716a 100644 --- a/test/regress/regress1/bv/bench_38.delta.smt2 +++ b/test/regress/regress1/bv/bench_38.delta.smt2 @@ -1,4 +1,4 @@ -; COMMAND-LINE: --fewer-preprocessing-holes --check-proof --quiet +; COMMAND-LINE: --quiet ; EXPECT: unsat (set-logic QF_BV) (declare-fun x () (_ BitVec 4)) diff --git a/test/regress/regress1/non-fatal-errors.smt2 b/test/regress/regress1/non-fatal-errors.smt2 index 1e1865883..ec3d02927 100644 --- a/test/regress/regress1/non-fatal-errors.smt2 +++ b/test/regress/regress1/non-fatal-errors.smt2 @@ -2,11 +2,10 @@ ; EXPECT: success ; EXPECT: success ; EXPECT: success +; EXPECT: unsupported ; EXPECT: success ; EXPECT: success ; EXPECT: success -; EXPECT: success -; EXPECT: (error "") ; EXPECT: (error "") ; EXPECT: (error "") ; EXPECT: (error "") @@ -22,7 +21,6 @@ (declare-fun p () Bool) (get-unsat-core) (get-value (p)) -(get-proof) (get-model) (get-assignment) (assert true) diff --git a/test/regress/regress1/quantifiers/dump-inst-proof.smt2 b/test/regress/regress1/quantifiers/dump-inst-proof.smt2 index 9edc4df2b..f900e78a9 100644 --- a/test/regress/regress1/quantifiers/dump-inst-proof.smt2 +++ b/test/regress/regress1/quantifiers/dump-inst-proof.smt2 @@ -1,5 +1,5 @@ ; REQUIRES: proof -; COMMAND-LINE: --dump-instantiations --proof --print-inst-full +; COMMAND-LINE: --dump-instantiations --produce-unsat-cores --print-inst-full ; EXPECT: unsat ; EXPECT: (instantiations (forall ((x Int)) (or (P x) (Q x)) ) ; EXPECT: ( 2 ) @@ -21,7 +21,7 @@ (assert (forall ((x Int)) (or (not (S x)) (not (Q x))))) (assert (and (not (R 0)) (not (R 10)) (not (S 1)) (not (P 2)))) (assert (S 2)) -; This tests that --proof minimizes the instantiations printed out. -; This regression should require only the 2 instantiations above, but -; may try more. +; This tests that --produce-unsat-cores minimizes the instantiations +; printed out. This regression should require only the 2 +; instantiations above, but may try more. (check-sat) diff --git a/test/regress/regress1/quantifiers/issue3481-unsat1.smt2 b/test/regress/regress1/quantifiers/issue3481-unsat1.smt2 index fb7ff5485..9cf535dc7 100644 --- a/test/regress/regress1/quantifiers/issue3481-unsat1.smt2 +++ b/test/regress/regress1/quantifiers/issue3481-unsat1.smt2 @@ -2,7 +2,7 @@ ; EXPECT: unsat ;; produced by cvc4_16.drv ;; -(set-logic AUFBVFPDTNIRA) +(set-logic AUFBVDTNIRA) (set-info :smt-lib-version 2.6) ;;; generated by SMT-LIB2 driver ;;; SMT-LIB2 driver: bit-vectors, common part diff --git a/test/regress/regress1/quantifiers/issue3481.smt2 b/test/regress/regress1/quantifiers/issue3481.smt2 index fe8c84d62..3d9bfe981 100644 --- a/test/regress/regress1/quantifiers/issue3481.smt2 +++ b/test/regress/regress1/quantifiers/issue3481.smt2 @@ -3,7 +3,7 @@ ;; produced by cvc4_16.drv ;; (set-info :smt-lib-version 2.6) -(set-logic AUFBVFPDTNIRA) +(set-logic AUFBVDTNIRA) ;;; generated by SMT-LIB2 driver ;;; SMT-LIB2 driver: bit-vectors, common part ;;; SMT-LIB2: integer arithmetic diff --git a/test/unit/CMakeLists.txt b/test/unit/CMakeLists.txt index bd7c1ea22..bd7029c54 100644 --- a/test/unit/CMakeLists.txt +++ b/test/unit/CMakeLists.txt @@ -107,7 +107,6 @@ add_subdirectory(expr) add_subdirectory(main) add_subdirectory(parser) add_subdirectory(prop) -add_subdirectory(proof) add_subdirectory(theory) add_subdirectory(preprocessing) add_subdirectory(util) diff --git a/test/unit/api/python/test_datatype_api.py b/test/unit/api/python/test_datatype_api.py new file mode 100644 index 000000000..a5499ffd6 --- /dev/null +++ b/test/unit/api/python/test_datatype_api.py @@ -0,0 +1,171 @@ +import pytest + +import pycvc4 +from pycvc4 import kinds + + +def test_datatype_simply_rec(): + solver = pycvc4.Solver() + + # Create mutual datatypes corresponding to this definition block: + # + # DATATYPE + # wlist = leaf(data: list), + # list = cons(car: wlist, cdr: list) | nil, + # ns = elem(ndata: set(wlist)) | elemArray(ndata2: array(list, list)) + # END; + + # Make unresolved types as placeholders + unres_wlist = solver.mkUninterpretedSort('wlist') + unres_list = solver.mkUninterpretedSort('list') + unres_ns = solver.mkUninterpretedSort('ns') + unres_types = set([unres_wlist, unres_list, unres_ns]) + + wlist = solver.mkDatatypeDecl('wlist') + leaf = solver.mkDatatypeConstructorDecl('leaf') + leaf.addSelector('data', unres_list) + wlist.addConstructor(leaf) + + dlist = solver.mkDatatypeDecl('list') + cons = solver.mkDatatypeConstructorDecl('cons') + cons.addSelector('car', unres_wlist) + cons.addSelector('cdr', unres_list) + dlist.addConstructor(cons) + nil = solver.mkDatatypeConstructorDecl("nil") + dlist.addConstructor(nil) + + ns = solver.mkDatatypeDecl('ns') + elem = solver.mkDatatypeConstructorDecl('elem') + elem.addSelector('ndata', solver.mkSetSort(unres_wlist)) + ns.addConstructor(elem) + elem_array = solver.mkDatatypeConstructorDecl('elemArray') + elem_array.addSelector('ndata', solver.mkArraySort(unres_list, unres_list)) + ns.addConstructor(elem_array) + + # this is well-founded and has no nested recursion + dtdecls = [wlist, dlist, ns] + dtsorts = solver.mkDatatypeSorts(dtdecls, unres_types) + assert len(dtsorts) == 3 + assert dtsorts[0].getDatatype().isWellFounded() + assert dtsorts[1].getDatatype().isWellFounded() + assert dtsorts[2].getDatatype().isWellFounded() + assert not dtsorts[0].getDatatype().hasNestedRecursion() + assert not dtsorts[1].getDatatype().hasNestedRecursion() + assert not dtsorts[2].getDatatype().hasNestedRecursion() + + # Create mutual datatypes corresponding to this definition block: + # DATATYPE + # ns2 = elem2(ndata: array(int,ns2)) | nil2 + # END; + unres_ns2 = solver.mkUninterpretedSort('ns2') + unres_types = set([unres_ns2]) + + ns2 = solver.mkDatatypeDecl('ns2') + elem2 = solver.mkDatatypeConstructorDecl('elem2') + elem2.addSelector('ndata', + solver.mkArraySort(solver.getIntegerSort(), unres_ns2)) + ns2.addConstructor(elem2) + nil2 = solver.mkDatatypeConstructorDecl('nil2') + ns2.addConstructor(nil2) + + # this is not well-founded due to non-simple recursion + dtdecls = [ns2] + dtsorts = solver.mkDatatypeSorts(dtdecls, unres_types) + assert len(dtsorts) == 1 + assert dtsorts[0].getDatatype()[0][0].getRangeSort().isArray() + elem_sort = dtsorts[0].getDatatype()[0][0].getRangeSort().getArrayElementSort() + assert elem_sort == dtsorts[0] + assert dtsorts[0].getDatatype().isWellFounded() + assert dtsorts[0].getDatatype().hasNestedRecursion() + + # Create mutual datatypes corresponding to this definition block: + # DATATYPE + # list3 = cons3(car: ns3, cdr: list3) | nil3, + # ns3 = elem3(ndata: set(list3)) + # END + unres_ns3 = solver.mkUninterpretedSort('ns3') + unres_list3 = solver.mkUninterpretedSort('list3') + unres_types = set([unres_ns3, unres_list3]) + + list3 = solver.mkDatatypeDecl('list3') + cons3 = solver.mkDatatypeConstructorDecl('cons3') + cons3.addSelector('car', unres_ns3) + cons3.addSelector('cdr', unres_list3) + list3.addConstructor(cons3) + nil3 = solver.mkDatatypeConstructorDecl('nil3') + list3.addConstructor(nil3) + + ns3 = solver.mkDatatypeDecl('ns3') + elem3 = solver.mkDatatypeConstructorDecl('elem3') + elem3.addSelector('ndata', solver.mkSetSort(unres_list3)) + ns3.addConstructor(elem3) + + # both are well-founded and have nested recursion + dtdecls = [list3, ns3] + dtsorts = solver.mkDatatypeSorts(dtdecls, unres_types) + assert len(dtsorts) == 2 + assert dtsorts[0].getDatatype().isWellFounded() + assert dtsorts[1].getDatatype().isWellFounded() + assert dtsorts[0].getDatatype().hasNestedRecursion() + assert dtsorts[1].getDatatype().hasNestedRecursion() + + # Create mutual datatypes corresponding to this definition block: + # DATATYPE + # list4 = cons(car: set(ns4), cdr: list4) | nil, + # ns4 = elem(ndata: list4) + # END + unres_ns4 = solver.mkUninterpretedSort('ns4') + unres_list4 = solver.mkUninterpretedSort('list4') + unres_types = set([unres_ns4, unres_list4]) + + list4 = solver.mkDatatypeDecl('list4') + cons4 = solver.mkDatatypeConstructorDecl('cons4') + cons4.addSelector('car', solver.mkSetSort(unres_ns4)) + cons4.addSelector('cdr', unres_list4) + list4.addConstructor(cons4) + nil4 = solver.mkDatatypeConstructorDecl('nil4') + list4.addConstructor(nil4) + + ns4 = solver.mkDatatypeDecl('ns4') + elem4 = solver.mkDatatypeConstructorDecl('elem3') + elem4.addSelector('ndata', unres_list4) + ns4.addConstructor(elem4) + + # both are well-founded and have nested recursion + dtdecls = [list4, ns4] + dtsorts = solver.mkDatatypeSorts(dtdecls, unres_types) + assert len(dtsorts) == 2 + assert dtsorts[0].getDatatype().isWellFounded() + assert dtsorts[1].getDatatype().isWellFounded() + assert dtsorts[0].getDatatype().hasNestedRecursion() + assert dtsorts[1].getDatatype().hasNestedRecursion() + + # Create mutual datatypes corresponding to this definition block: + # DATATYPE + # list5[X] = cons(car: X, cdr: list5[list5[X]]) | nil + # END + unres_list5 = solver.mkSortConstructorSort('list5', 1) + unres_types = set([unres_list5]) + + x = solver.mkParamSort('X') + v = [x] + list5 = solver.mkDatatypeDecl('list5', v) + + args = [x] + ur_list_x = unres_list5.instantiate(args) + args = [ur_list_x] + ur_list_list_x = unres_list5.instantiate(args) + + cons5 = solver.mkDatatypeConstructorDecl('cons5') + cons5.addSelector('car', x) + cons5.addSelector('cdr', ur_list_list_x) + list5.addConstructor(cons5) + nil5 = solver.mkDatatypeConstructorDecl('nil5') + list5.addConstructor(nil5) + + # well-founded and has nested recursion + dtdecls = [list5] + dtsorts = solver.mkDatatypeSorts(dtdecls, unres_types) + assert len(dtsorts) == 1 + assert dtsorts[0].getDatatype().isWellFounded() + assert dtsorts[0].getDatatype().hasNestedRecursion() diff --git a/test/unit/api/python/test_sort.py b/test/unit/api/python/test_sort.py index cd40fc807..5fdb49f48 100644 --- a/test/unit/api/python/test_sort.py +++ b/test/unit/api/python/test_sort.py @@ -223,21 +223,31 @@ def testGetBVSize(): def testGetFPExponentSize(): solver = pycvc4.Solver() - fpSort = solver.mkFloatingPointSort(4, 8) - fpSort.getFPExponentSize() - setSort = solver.mkSetSort(solver.getIntegerSort()) - with pytest.raises(Exception): - setSort.getFPExponentSize() + if solver.supportsFloatingPoint(): + fpSort = solver.mkFloatingPointSort(4, 8) + fpSort.getFPExponentSize() + setSort = solver.mkSetSort(solver.getIntegerSort()) + + with pytest.raises(Exception): + setSort.getFPExponentSize() + else: + with pytest.raises(Exception): + solver.mkFloatingPointSort(4, 8) def testGetFPSignificandSize(): solver = pycvc4.Solver() - fpSort = solver.mkFloatingPointSort(4, 8) - fpSort.getFPSignificandSize() - setSort = solver.mkSetSort(solver.getIntegerSort()) - with pytest.raises(Exception): - setSort.getFPSignificandSize() + if solver.supportsFloatingPoint(): + fpSort = solver.mkFloatingPointSort(4, 8) + fpSort.getFPSignificandSize() + setSort = solver.mkSetSort(solver.getIntegerSort()) + + with pytest.raises(Exception): + setSort.getFPSignificandSize() + else: + with pytest.raises(Exception): + solver.mkFloatingPointSort(4, 8) def testGetDatatypeParamSorts(): solver = pycvc4.Solver() diff --git a/test/unit/api/python/test_term.py b/test/unit/api/python/test_term.py index b135e4510..ca8d4c741 100644 --- a/test/unit/api/python/test_term.py +++ b/test/unit/api/python/test_term.py @@ -4,6 +4,17 @@ import pycvc4 from pycvc4 import kinds +def test_getitem(): + solver = pycvc4.Solver() + intsort = solver.getIntegerSort() + x = solver.mkConst(intsort, 'x') + y = solver.mkConst(intsort, 'y') + xpy = solver.mkTerm(kinds.Plus, x, y) + + assert xpy[0] == x + assert xpy[1] == y + + def test_get_kind(): solver = pycvc4.Solver() intsort = solver.getIntegerSort() diff --git a/test/unit/api/solver_black.h b/test/unit/api/solver_black.h index 9837d6b00..11dbbb7ae 100644 --- a/test/unit/api/solver_black.h +++ b/test/unit/api/solver_black.h @@ -27,12 +27,14 @@ class SolverBlack : public CxxTest::TestSuite void setUp() override; void tearDown() override; + void testSupportsFloatingPoint(); + void testGetBooleanSort(); void testGetIntegerSort(); void testGetNullSort(); void testGetRealSort(); void testGetRegExpSort(); - void testGetRoundingmodeSort(); + void testGetRoundingModeSort(); void testGetStringSort(); void testMkArraySort(); @@ -169,6 +171,20 @@ void SolverBlack::setUp() { d_solver.reset(new Solver()); } void SolverBlack::tearDown() { d_solver.reset(nullptr); } +void SolverBlack::testSupportsFloatingPoint() +{ + if (d_solver->supportsFloatingPoint()) + { + TS_ASSERT_THROWS_NOTHING( + d_solver->mkRoundingMode(ROUND_NEAREST_TIES_TO_EVEN)); + } + else + { + TS_ASSERT_THROWS(d_solver->mkRoundingMode(ROUND_NEAREST_TIES_TO_EVEN), + CVC4ApiException&); + } +} + void SolverBlack::testGetBooleanSort() { TS_ASSERT_THROWS_NOTHING(d_solver->getBooleanSort()); @@ -199,9 +215,16 @@ void SolverBlack::testGetStringSort() TS_ASSERT_THROWS_NOTHING(d_solver->getStringSort()); } -void SolverBlack::testGetRoundingmodeSort() +void SolverBlack::testGetRoundingModeSort() { - TS_ASSERT_THROWS_NOTHING(d_solver->getRoundingmodeSort()); + if (d_solver->supportsFloatingPoint()) + { + TS_ASSERT_THROWS_NOTHING(d_solver->getRoundingModeSort()); + } + else + { + TS_ASSERT_THROWS(d_solver->getRoundingModeSort(), CVC4ApiException&); + } } void SolverBlack::testMkArraySort() @@ -210,15 +233,20 @@ void SolverBlack::testMkArraySort() Sort intSort = d_solver->getIntegerSort(); Sort realSort = d_solver->getRealSort(); Sort bvSort = d_solver->mkBitVectorSort(32); - Sort fpSort = d_solver->mkFloatingPointSort(3, 5); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(boolSort, boolSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(intSort, intSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(realSort, realSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(bvSort, bvSort)); - TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(fpSort, fpSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(boolSort, intSort)); TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(realSort, bvSort)); - TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(bvSort, fpSort)); + + if (d_solver->supportsFloatingPoint()) + { + Sort fpSort = d_solver->mkFloatingPointSort(3, 5); + TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(fpSort, fpSort)); + TS_ASSERT_THROWS_NOTHING(d_solver->mkArraySort(bvSort, fpSort)); + } + Solver slv; TS_ASSERT_THROWS(slv.mkArraySort(boolSort, boolSort), CVC4ApiException&); } @@ -231,9 +259,16 @@ void SolverBlack::testMkBitVectorSort() void SolverBlack::testMkFloatingPointSort() { - TS_ASSERT_THROWS_NOTHING(d_solver->mkFloatingPointSort(4, 8)); - TS_ASSERT_THROWS(d_solver->mkFloatingPointSort(0, 8), CVC4ApiException&); - TS_ASSERT_THROWS(d_solver->mkFloatingPointSort(4, 0), CVC4ApiException&); + if (d_solver->supportsFloatingPoint()) + { + TS_ASSERT_THROWS_NOTHING(d_solver->mkFloatingPointSort(4, 8)); + TS_ASSERT_THROWS(d_solver->mkFloatingPointSort(0, 8), CVC4ApiException&); + TS_ASSERT_THROWS(d_solver->mkFloatingPointSort(4, 0), CVC4ApiException&); + } + else + { + TS_ASSERT_THROWS(d_solver->mkFloatingPointSort(4, 8), CVC4ApiException&); + } } void SolverBlack::testMkDatatypeSort() @@ -480,8 +515,16 @@ void SolverBlack::testMkBoolean() void SolverBlack::testMkRoundingMode() { - TS_ASSERT_THROWS_NOTHING( - d_solver->mkRoundingMode(RoundingMode::ROUND_TOWARD_ZERO)); + if (CVC4::Configuration::isBuiltWithSymFPU()) + { + TS_ASSERT_THROWS_NOTHING( + d_solver->mkRoundingMode(RoundingMode::ROUND_TOWARD_ZERO)); + } + else + { + TS_ASSERT_THROWS(d_solver->mkRoundingMode(RoundingMode::ROUND_TOWARD_ZERO), + CVC4ApiException&); + } } void SolverBlack::testMkUninterpretedConst() diff --git a/test/unit/proof/CMakeLists.txt b/test/unit/proof/CMakeLists.txt deleted file mode 100644 index 315c78d6f..000000000 --- a/test/unit/proof/CMakeLists.txt +++ /dev/null @@ -1,7 +0,0 @@ -#-----------------------------------------------------------------------------# -# Add unit tests - -cvc4_add_unit_test_black(drat_proof_black proof) -cvc4_add_unit_test_black(er_proof_black proof) -cvc4_add_unit_test_black(lrat_proof_black proof) -cvc4_add_unit_test_black(lfsc_proof_printer_black proof) diff --git a/test/unit/proof/drat_proof_black.h b/test/unit/proof/drat_proof_black.h deleted file mode 100644 index 4b593a588..000000000 --- a/test/unit/proof/drat_proof_black.h +++ /dev/null @@ -1,187 +0,0 @@ -/********************* */ -/*! \file drat_proof_black.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Andres Noetzli - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Black box testing of the DRAT proof class - ** - ** In particular, tests DRAT binary parsing. - **/ - -#include <cxxtest/TestSuite.h> - -#include <cctype> - -#include "proof/drat/drat_proof.h" - -using namespace CVC4::proof::drat; - -class DratProofBlack : public CxxTest::TestSuite -{ - public: - void setUp() override {} - void tearDown() override {} - - void testParseOneAdd(); - void testParseOneMediumAdd(); - void testParseOneBigAdd(); - void testParseLiteralIsTooBig(); - void testParseLiteralOverflow(); - void testParseClauseOverflow(); - - void testParseTwo(); - - void testOutputTwoAsText(); - void testOutputTwoAsLfsc(); -}; - -void DratProofBlack::testParseOneAdd() -{ - // a 1; - std::string input("a\x02\x00", 3); - DratProof proof = DratProof::fromBinary(input); - - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_kind, ADDITION); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause.size(), 1); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause[0], - SatLiteral(0, false)); -} - -void DratProofBlack::testParseOneMediumAdd() -{ - // a -255; - std::string input("a\xff\x01\x00", 4); - DratProof proof = DratProof::fromBinary(input); - - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_kind, ADDITION); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause.size(), 1); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause[0], - SatLiteral(126, true)); -} - -void DratProofBlack::testParseOneBigAdd() -{ - // a -2199023255551; - std::string input("a\xff\xff\xff\xff\xff\x7f\x00", 8); - DratProof proof = DratProof::fromBinary(input); - - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_kind, ADDITION); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause.size(), 1); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause[0], - SatLiteral(2199023255550, true)); -} - -void DratProofBlack::testParseLiteralIsTooBig() -{ - std::string input("a\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\xff\x7f\x00", - 14); - TS_ASSERT_THROWS(DratProof::fromBinary(input), InvalidDratProofException&); -} - -void DratProofBlack::testParseLiteralOverflow() -{ - std::string input("a\x80", 2); - TS_ASSERT_THROWS(DratProof::fromBinary(input), InvalidDratProofException&); -} - -void DratProofBlack::testParseClauseOverflow() -{ - std::string input("a\x80\x01", 3); - TS_ASSERT_THROWS(DratProof::fromBinary(input), InvalidDratProofException&); -} - -void DratProofBlack::testParseTwo() -{ - // d -63 -8193 - // 129 -8191 - std::string input("\x64\x7f\x83\x80\x01\x00\x61\x82\x02\xff\x7f\x00", 12); - DratProof proof = DratProof::fromBinary(input); - - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_kind, DELETION); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause.size(), 2); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause[0], - SatLiteral(62, true)); - TS_ASSERT_EQUALS(proof.getInstructions()[0].d_clause[1], - SatLiteral(8192, true)); - - TS_ASSERT_EQUALS(proof.getInstructions()[1].d_kind, ADDITION); - TS_ASSERT_EQUALS(proof.getInstructions()[1].d_clause.size(), 2); - TS_ASSERT_EQUALS(proof.getInstructions()[1].d_clause[0], - SatLiteral(128, false)); - TS_ASSERT_EQUALS(proof.getInstructions()[1].d_clause[1], - SatLiteral(8190, true)); -} - -void DratProofBlack::testOutputTwoAsText() -{ - // d -63 -8193 - // 129 -8191 - std::string input("\x64\x7f\x83\x80\x01\x00\x61\x82\x02\xff\x7f\x00", 12); - DratProof proof = DratProof::fromBinary(input); - - std::ostringstream output; - proof.outputAsText(output); - - std::istringstream tokens(output.str()); - std::string token; - - tokens >> token; - TS_ASSERT_EQUALS(token, "d"); - - tokens >> token; - TS_ASSERT_EQUALS(token, "-63"); - - tokens >> token; - TS_ASSERT_EQUALS(token, "-8193"); - - tokens >> token; - TS_ASSERT_EQUALS(token, "0"); - - tokens >> token; - TS_ASSERT_EQUALS(token, "129"); - - tokens >> token; - TS_ASSERT_EQUALS(token, "-8191"); - - tokens >> token; - TS_ASSERT_EQUALS(token, "0"); -} - -void DratProofBlack::testOutputTwoAsLfsc() -{ - // d -63 -8193 - // 129 -8191 - std::string input("\x64\x7f\x83\x80\x01\x00\x61\x82\x02\xff\x7f\x00", 12); - DratProof proof = DratProof::fromBinary(input); - std::ostringstream lfsc; - proof.outputAsLfsc(lfsc, 2); - std::ostringstream lfscWithoutWhitespace; - for (char c : lfsc.str()) - { - if (!std::isspace(c)) - { - lfscWithoutWhitespace << c; - } - } - std::string expectedLfsc = - "(DRATProofd (clc (neg bb.v62) (clc (neg bb.v8192) cln))" - "(DRATProofa (clc (pos bb.v128) (clc (neg bb.v8190) cln))" - "DRATProofn))"; - std::ostringstream expectedLfscWithoutWhitespace; - for (char c : expectedLfsc) - { - if (!std::isspace(c)) - { - expectedLfscWithoutWhitespace << c; - } - } - - TS_ASSERT_EQUALS(lfscWithoutWhitespace.str(), - expectedLfscWithoutWhitespace.str()); -} diff --git a/test/unit/proof/er_proof_black.h b/test/unit/proof/er_proof_black.h deleted file mode 100644 index d9178e34e..000000000 --- a/test/unit/proof/er_proof_black.h +++ /dev/null @@ -1,464 +0,0 @@ -/********************* */ -/*! \file er_proof_black.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Black box testing of the ER proof class - ** - ** In particular, tests TRACECHECK parsing and ER LFSC output. - **/ - -#include <cxxtest/TestSuite.h> - -#include <algorithm> -#include <cctype> -#include <iostream> -#include <iterator> -#include <string> -#include <unordered_map> -#include <vector> - -#include "base/configuration_private.h" -#include "proof/clause_id.h" -#include "proof/er/er_proof.h" -#include "prop/sat_solver_types.h" -#include "utils.h" - -#if IS_LFSC_BUILD -#include "lfscc.h" - -namespace CVC4 { -namespace proof { -extern const char* const plf_signatures; -} // namespace proof -} // namespace CVC4 -#endif - - -using namespace CVC4; -using namespace CVC4::proof::er; -using namespace CVC4::prop; - -class ErProofBlack : public CxxTest::TestSuite -{ - public: - void setUp() override {} - void tearDown() override {} - - void testTraceCheckParse1Line(); - void testTraceCheckParse5Lines(); - void testErTraceCheckParse(); - void testErTraceCheckOutput(); - void testErTraceCheckOutputMedium(); -}; - -/** - * @brief Add a new clause to the clause store and list of used clauses - * - * @param clauses the clause store - * @param usedIds the used clauses - * @param id the id of the new clause - * @param clause the clause itself - */ -void addClause(std::unordered_map<ClauseId, SatClause>& clauses, - std::vector<ClauseId>& usedIds, - ClauseId id, - SatClause&& clause) -{ - clauses.emplace(id, std::move(clause)); - usedIds.push_back(id); -} - -void ErProofBlack::testTraceCheckParse1Line() -{ - std::string tracecheckText = "1 -2 3 0 4 2 0\n"; - std::istringstream stream(tracecheckText); - TraceCheckProof pf = TraceCheckProof::fromText(stream); - TS_ASSERT_EQUALS(pf.d_lines.size(), 1); - - TS_ASSERT_EQUALS(pf.d_lines[0].d_idx, 1); - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause.size(), 2); - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause[0], SatLiteral(1, true)); - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause[1], SatLiteral(2, false)); - TS_ASSERT_EQUALS(pf.d_lines[0].d_chain.size(), 2); - TS_ASSERT_EQUALS(pf.d_lines[0].d_chain[0], 4); - TS_ASSERT_EQUALS(pf.d_lines[0].d_chain[1], 2); -} - -void ErProofBlack::testTraceCheckParse5Lines() -{ - std::string tracecheckText = - "1 1 -2 3 0 0\n" - "2 -1 0 0\n" - "3 2 0 0\n" - "4 -3 0 0\n" - "5 0 1 2 4 3 0\n"; - std::istringstream stream(tracecheckText); - TraceCheckProof pf = TraceCheckProof::fromText(stream); - TS_ASSERT_EQUALS(pf.d_lines.size(), 5); - - TS_ASSERT_EQUALS(pf.d_lines[0].d_idx, 1); - TS_ASSERT_EQUALS(pf.d_lines[4].d_idx, 5); - - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause.size(), 3); - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause[0], SatLiteral(0, false)); - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause[1], SatLiteral(1, true)); - TS_ASSERT_EQUALS(pf.d_lines[0].d_clause[2], SatLiteral(2, false)); - TS_ASSERT_EQUALS(pf.d_lines[0].d_chain.size(), 0); - - TS_ASSERT_EQUALS(pf.d_lines[4].d_chain.size(), 4); - TS_ASSERT_EQUALS(pf.d_lines[4].d_chain[0], 1); - TS_ASSERT_EQUALS(pf.d_lines[4].d_chain[1], 2); - TS_ASSERT_EQUALS(pf.d_lines[4].d_chain[2], 4); - TS_ASSERT_EQUALS(pf.d_lines[4].d_chain[3], 3); - TS_ASSERT_EQUALS(pf.d_lines[4].d_clause.size(), 0); -} - -void ErProofBlack::testErTraceCheckParse() -{ - std::string tracecheckText = - "1 1 2 -3 0 0\n" - "2 -1 -2 3 0 0\n" - "3 2 3 -4 0 0\n" - "4 -2 -3 4 0 0\n" - "5 -1 -3 -4 0 0\n" - "6 1 3 4 0 0\n" - "7 -1 2 4 0 0\n" - "8 1 -2 -4 0 0\n" - "9 5 0 0\n" - "10 5 1 0 0\n" - "11 4 5 2 0 10 7 0\n" - "12 -4 5 -3 0 10 5 0\n" - "13 3 5 -2 0 10 2 0\n" - "14 -2 -4 0 2 5 8 0\n" - "15 4 3 0 7 2 6 0\n" - "16 2 -3 0 7 5 1 0\n" - "17 2 0 3 15 16 0\n" - "18 0 4 15 14 17 0\n"; - std::istringstream stream(tracecheckText); - TraceCheckProof tc = TraceCheckProof::fromText(stream); - - std::unordered_map<ClauseId, SatClause> clauses; - std::vector<ClauseId> usedIds; - addClause( - clauses, - usedIds, - 1, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(1, false), SatLiteral(2, true)}); - addClause( - clauses, - usedIds, - 2, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(1, true), SatLiteral(2, false)}); - addClause( - clauses, - usedIds, - 3, - std::vector<SatLiteral>{ - SatLiteral(1, false), SatLiteral(2, false), SatLiteral(3, true)}); - addClause( - clauses, - usedIds, - 4, - std::vector<SatLiteral>{ - SatLiteral(1, true), SatLiteral(2, true), SatLiteral(3, false)}); - addClause(clauses, - usedIds, - 5, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(2, true), SatLiteral(3, true)}); - addClause( - clauses, - usedIds, - 6, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(2, false), SatLiteral(3, false)}); - addClause( - clauses, - usedIds, - 7, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(1, false), SatLiteral(3, false)}); - addClause( - clauses, - usedIds, - 8, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(1, true), SatLiteral(3, true)}); - ErProof pf(clauses, usedIds, std::move(tc)); - - TS_ASSERT_EQUALS(pf.getInputClauseIds()[0], 1); - TS_ASSERT_EQUALS(pf.getInputClauseIds()[7], 8); - - TS_ASSERT_EQUALS(pf.getDefinitions().size(), 1) - TS_ASSERT_EQUALS(pf.getDefinitions()[0].d_newVariable, SatVariable(4)); - TS_ASSERT_EQUALS(pf.getDefinitions()[0].d_oldLiteral, SatLiteral(0, true)); - TS_ASSERT_EQUALS(pf.getDefinitions()[0].d_otherLiterals.size(), 0); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines.size(), 18); - - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[0].d_idx, 1); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_idx, 17); - - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[0].d_clause.size(), 3); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[0].d_clause[0], - SatLiteral(0, false)); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[0].d_clause[1], - SatLiteral(1, false)); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[0].d_clause[2], - SatLiteral(2, true)); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[0].d_chain.size(), 0); - - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_clause.size(), 1); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_clause[0], - SatLiteral(1, false)); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_chain.size(), 3); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_chain[0], 3); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_chain[1], 15); - TS_ASSERT_EQUALS(pf.getTraceCheckProof().d_lines[16].d_chain[2], 16); -} - -void ErProofBlack::testErTraceCheckOutput() -{ - std::string tracecheckText = - "1 1 2 -3 0 0\n" - "2 -1 -2 3 0 0\n" - "3 2 3 -4 0 0\n" - "4 -2 -3 4 0 0\n" - "5 -1 -3 -4 0 0\n" - "6 1 3 4 0 0\n" - "7 -1 2 4 0 0\n" - "8 1 -2 -4 0 0\n" - "9 5 0 0\n" - "10 5 1 0 0\n" - "11 4 5 2 0 10 7 0\n" - "12 -4 5 -3 0 10 5 0\n" - "13 3 5 -2 0 10 2 0\n" - "14 -2 -4 0 2 5 8 0\n" - "15 4 3 0 7 2 6 0\n" - "16 2 -3 0 7 5 1 0\n" - "17 2 0 3 15 16 0\n" - "18 0 4 15 14 17 0\n"; - std::istringstream stream(tracecheckText); - TraceCheckProof tc = TraceCheckProof::fromText(stream); - - std::unordered_map<ClauseId, SatClause> clauses; - std::vector<ClauseId> usedIds; - addClause( - clauses, - usedIds, - 1, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(1, false), SatLiteral(2, true)}); - addClause( - clauses, - usedIds, - 2, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(1, true), SatLiteral(2, false)}); - addClause( - clauses, - usedIds, - 3, - std::vector<SatLiteral>{ - SatLiteral(1, false), SatLiteral(2, false), SatLiteral(3, true)}); - addClause( - clauses, - usedIds, - 4, - std::vector<SatLiteral>{ - SatLiteral(1, true), SatLiteral(2, true), SatLiteral(3, false)}); - addClause(clauses, - usedIds, - 5, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(2, true), SatLiteral(3, true)}); - addClause( - clauses, - usedIds, - 6, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(2, false), SatLiteral(3, false)}); - addClause( - clauses, - usedIds, - 7, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(1, false), SatLiteral(3, false)}); - addClause( - clauses, - usedIds, - 8, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(1, true), SatLiteral(3, true)}); - ErProof pf(clauses, usedIds, std::move(tc)); - - std::ostringstream lfsc; - pf.outputAsLfsc(lfsc); - - std::string out = R"EOF( - (decl_definition - (neg bb.v0) - cln - (\ er.v4 - (\ er.def4 - (clausify_definition _ _ _ er.def4 _ - (\ er.c9 - (\ er.c10 - (\ er.cnf4 - (satlem_simplify _ _ _ - (R _ _ er.c10 bb.pb7 bb.v0) (\ er.c11 - (satlem_simplify _ _ _ - (R _ _ er.c10 bb.pb5 bb.v0) (\ er.c12 - (satlem_simplify _ _ _ - (R _ _ er.c10 bb.pb2 bb.v0) (\ er.c13 - (satlem_simplify _ _ _ - (Q _ _ (R _ _ bb.pb2 bb.pb5 bb.v2) bb.pb8 bb.v0) (\ er.c14 - (satlem_simplify _ _ _ - (Q _ _ (R _ _ bb.pb7 bb.pb2 bb.v1) bb.pb6 bb.v0) (\ er.c15 - (satlem_simplify _ _ _ - (Q _ _ (R _ _ bb.pb7 bb.pb5 bb.v3) bb.pb1 bb.v0) (\ er.c16 - (satlem_simplify _ _ _ - (R _ _ (Q _ _ bb.pb3 er.c15 bb.v3) er.c16 bb.v2) (\ er.c17 - (satlem_simplify _ _ _ - (Q _ _ (R _ _ (Q _ _ bb.pb4 er.c15 bb.v2) er.c14 bb.v3) - er.c17 bb.v1) (\ er.c18 - er.c18 ; (holds cln) - )))))))))))))))) - ))) - ) - )) - ) - )EOF"; - - TS_ASSERT_EQUALS(filterWhitespace(lfsc.str()), filterWhitespace(out)); -} - -/** - * This proof has been specially constructed to stress-test the proof - * machinery, while still being short. It's a bit meandering... - */ -void ErProofBlack::testErTraceCheckOutputMedium() -{ - std::string tracecheckText = - "1 1 2 -3 0 0\n" - "2 -1 -2 3 0 0\n" - "3 2 3 -4 0 0\n" - "4 -2 -3 4 0 0\n" - "5 -1 -3 -4 0 0\n" - "6 1 3 4 0 0\n" - "7 -1 2 4 0 0\n" - "8 1 -2 -4 0 0\n" - - "9 5 2 4 0 0\n" // Definition with 2 other variables - "10 5 1 0 0\n" - "11 2 -5 -1 0 0\n" - "12 4 -5 -1 0 0\n" - - "13 6 0 0\n" // Definition with no other variables - "14 6 -3 0 0\n" - - "15 -3 4 0 11 1 10 7 4 0\n" // Chain w/ both def. and input clauses - - "16 -2 -4 0 2 5 8 0\n" // The useful bit of the proof - "17 4 3 0 7 2 6 0\n" - "18 2 -3 0 7 5 1 0\n" - "19 2 0 3 17 18 0\n" - "20 0 4 17 16 19 0\n"; - - std::istringstream stream(tracecheckText); - TraceCheckProof tc = TraceCheckProof::fromText(stream); - - std::unordered_map<ClauseId, SatClause> clauses; - std::vector<ClauseId> usedIds; - addClause( - clauses, - usedIds, - 1, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(1, false), SatLiteral(2, true)}); - addClause( - clauses, - usedIds, - 2, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(1, true), SatLiteral(2, false)}); - addClause( - clauses, - usedIds, - 3, - std::vector<SatLiteral>{ - SatLiteral(1, false), SatLiteral(2, false), SatLiteral(3, true)}); - addClause( - clauses, - usedIds, - 4, - std::vector<SatLiteral>{ - SatLiteral(1, true), SatLiteral(2, true), SatLiteral(3, false)}); - addClause(clauses, - usedIds, - 5, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(2, true), SatLiteral(3, true)}); - addClause( - clauses, - usedIds, - 6, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(2, false), SatLiteral(3, false)}); - addClause( - clauses, - usedIds, - 7, - std::vector<SatLiteral>{ - SatLiteral(0, true), SatLiteral(1, false), SatLiteral(3, false)}); - addClause( - clauses, - usedIds, - 8, - std::vector<SatLiteral>{ - SatLiteral(0, false), SatLiteral(1, true), SatLiteral(3, true)}); - ErProof pf(clauses, usedIds, std::move(tc)); - - std::ostringstream actual_pf_body; - pf.outputAsLfsc(actual_pf_body); - -#if IS_LFSC_BUILD - std::string pf_header = R"EOF( - (check - (% bb.v0 var - (% bb.v1 var - (% bb.v2 var - (% bb.v3 var - (% bb.pb1 (holds (clc (pos bb.v0) (clc (pos bb.v1) (clc (neg bb.v2) cln)))) - (% bb.pb2 (holds (clc (neg bb.v0) (clc (neg bb.v1) (clc (pos bb.v2) cln)))) - (% bb.pb3 (holds (clc (pos bb.v1) (clc (pos bb.v2) (clc (neg bb.v3) cln)))) - (% bb.pb4 (holds (clc (neg bb.v1) (clc (neg bb.v2) (clc (pos bb.v3) cln)))) - (% bb.pb5 (holds (clc (neg bb.v0) (clc (neg bb.v2) (clc (neg bb.v3) cln)))) - (% bb.pb6 (holds (clc (pos bb.v0) (clc (pos bb.v2) (clc (pos bb.v3) cln)))) - (% bb.pb7 (holds (clc (neg bb.v0) (clc (pos bb.v1) (clc (pos bb.v3) cln)))) - (% bb.pb8 (holds (clc (pos bb.v0) (clc (neg bb.v1) (clc (neg bb.v3) cln)))) - (: (holds cln) - )EOF"; - - std::string pf_footer = R"EOF( - ) - )))))))) - )))) - ) - )EOF"; - - std::stringstream actual_pf; - actual_pf << proof::plf_signatures << pf_header << actual_pf_body.str() << pf_footer; - - lfscc_init(); - lfscc_check_file(actual_pf, false, false, false, false, false, false, false); -#endif -} diff --git a/test/unit/proof/lfsc_proof_printer_black.h b/test/unit/proof/lfsc_proof_printer_black.h deleted file mode 100644 index 74fda4996..000000000 --- a/test/unit/proof/lfsc_proof_printer_black.h +++ /dev/null @@ -1,118 +0,0 @@ -/********************* */ -/*! \file lfsc_proof_printer_black.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Black box testing of LFSC proof printing - **/ - -#include <cxxtest/TestSuite.h> - -#include "proof/lfsc_proof_printer.h" -#include "prop/sat_solver_types.h" -#include "proof/clause_id.h" - -using namespace CVC4::proof; -using namespace CVC4::prop; - -class LfscProofPrinterBlack : public CxxTest::TestSuite -{ - public: - void setUp() override {} - void tearDown() override {} - - void testPrintClause(); - void testPrintSatInputProof(); - void testPrintCMapProof(); -}; - -void LfscProofPrinterBlack::testPrintClause() -{ - SatClause clause{ - SatLiteral(0, false), SatLiteral(1, true), SatLiteral(3, false)}; - std::ostringstream lfsc; - - LFSCProofPrinter::printSatClause(clause, lfsc, ""); - - std::string expectedLfsc = - "(clc (pos .v0) " - "(clc (neg .v1) " - "(clc (pos .v3) " - "cln)))"; - - TS_ASSERT_EQUALS(lfsc.str(), expectedLfsc); -} - -void LfscProofPrinterBlack::testPrintSatInputProof() -{ - std::vector<CVC4::ClauseId> ids{2, 40, 3}; - std::ostringstream lfsc; - - LFSCProofPrinter::printSatInputProof(ids, lfsc, ""); - - std::string expectedLfsc = - "(cnfc_proof _ _ _ .pb2 " - "(cnfc_proof _ _ _ .pb40 " - "(cnfc_proof _ _ _ .pb3 " - "cnfn_proof)))"; - - std::ostringstream lfscWithoutWhitespace; - for (char c : lfsc.str()) - { - if (!std::isspace(c)) - { - lfscWithoutWhitespace << c; - } - } - std::ostringstream expectedLfscWithoutWhitespace; - for (char c : expectedLfsc) - { - if (!std::isspace(c)) - { - expectedLfscWithoutWhitespace << c; - } - } - - TS_ASSERT_EQUALS(lfscWithoutWhitespace.str(), - expectedLfscWithoutWhitespace.str()); -} - -void LfscProofPrinterBlack::testPrintCMapProof() -{ - std::vector<CVC4::ClauseId> ids{2, 40, 3}; - std::ostringstream lfsc; - - LFSCProofPrinter::printCMapProof(ids, lfsc, ""); - - std::string expectedLfsc = - "(CMapc_proof 1 _ _ _ .pb2 " - "(CMapc_proof 2 _ _ _ .pb40 " - "(CMapc_proof 3 _ _ _ .pb3 " - "CMapn_proof)))"; - - std::ostringstream lfscWithoutWhitespace; - for (char c : lfsc.str()) - { - if (!std::isspace(c)) - { - lfscWithoutWhitespace << c; - } - } - std::ostringstream expectedLfscWithoutWhitespace; - for (char c : expectedLfsc) - { - if (!std::isspace(c)) - { - expectedLfscWithoutWhitespace << c; - } - } - - TS_ASSERT_EQUALS(lfscWithoutWhitespace.str(), - expectedLfscWithoutWhitespace.str()); -} diff --git a/test/unit/proof/lrat_proof_black.h b/test/unit/proof/lrat_proof_black.h deleted file mode 100644 index 8d91fee33..000000000 --- a/test/unit/proof/lrat_proof_black.h +++ /dev/null @@ -1,97 +0,0 @@ -/********************* */ -/*! \file lrat_proof_black.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir, Andres Noetzli - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Black box testing of the LRAT proof class - ** - ** In particular, tests LRAT LFSC output. - **/ - -#include <cxxtest/TestSuite.h> - -#include <iostream> - -#include "proof/lrat/lrat_proof.h" -#include "prop/sat_solver_types.h" -#include "utils.h" - -using namespace CVC4; -using namespace CVC4::proof::lrat; -using namespace CVC4::prop; - -class LratProofBlack : public CxxTest::TestSuite -{ - public: - void setUp() override {} - void tearDown() override {} - - void testOutputAsLfsc(); -}; - -void LratProofBlack::testOutputAsLfsc() -{ - std::vector<std::unique_ptr<LratInstruction>> instructions; - - // 6 d 1 2 - std::vector<ClauseIdx> clausesToDelete{1, 2}; - std::unique_ptr<LratDeletion> deletion = std::unique_ptr<LratDeletion>( - new LratDeletion(6, std::move(clausesToDelete))); - instructions.push_back(std::move(deletion)); - - // 7 1 2 0 5 2 0 - std::vector<SatLiteral> firstAddedClause{SatLiteral(1, false), - SatLiteral(2, false)}; - LratUPTrace firstTrace{5, 2}; - std::vector<std::pair<ClauseIdx, LratUPTrace>> firstHints; - std::unique_ptr<LratAddition> add1 = - std::unique_ptr<LratAddition>(new LratAddition( - 7, std::move(firstAddedClause), std::move(firstTrace), firstHints)); - instructions.push_back(std::move(add1)); - - // 8 2 0 -1 3 -5 2 0 - std::vector<SatLiteral> secondAddedClause{SatLiteral(2, false)}; - LratUPTrace secondTrace; - std::vector<std::pair<ClauseIdx, LratUPTrace>> secondHints; - LratUPTrace secondHints0Trace{3}; - secondHints.emplace_back(1, secondHints0Trace); - LratUPTrace secondHints1Trace{2}; - secondHints.emplace_back(5, secondHints1Trace); - std::unique_ptr<LratAddition> add2 = std::unique_ptr<LratAddition>( - new LratAddition(8, - std::move(secondAddedClause), - std::move(secondTrace), - secondHints)); - instructions.push_back(std::move(add2)); - - LratProof proof(std::move(instructions)); - - std::ostringstream lfsc; - proof.outputAsLfsc(lfsc); - - // 6 d 1 2 - // 7 1 2 0 5 2 0 - // 8 2 0 -1 3 -5 2 0 - std::string expectedLfsc = - "(LRATProofd (CIListc 1 (CIListc 2 CIListn)) " - "(LRATProofa 7 " - " (clc (pos bb.v1) (clc (pos bb.v2) cln))" - " (Tracec 5 (Tracec 2 Tracen))" - " RATHintsn " - "(LRATProofa 8 " - " (clc (pos bb.v2) cln)" - " Tracen " - " (RATHintsc 1 (Tracec 3 Tracen)" - " (RATHintsc 5 (Tracec 2 Tracen)" - " RATHintsn)) " - "LRATProofn)))"; - - TS_ASSERT_EQUALS(filterWhitespace(lfsc.str()), - filterWhitespace(expectedLfsc)); -} diff --git a/test/unit/proof/utils.h b/test/unit/proof/utils.h deleted file mode 100644 index 3db6e2171..000000000 --- a/test/unit/proof/utils.h +++ /dev/null @@ -1,34 +0,0 @@ -/********************* */ -/*! \file utils.h - ** \verbatim - ** Top contributors (to current version): - ** Alex Ozdemir - ** This file is part of the CVC4 project. - ** Copyright (c) 2009-2020 by the authors listed in the file AUTHORS - ** in the top-level source directory) and their institutional affiliations. - ** All rights reserved. See the file COPYING in the top-level source - ** directory for licensing information.\endverbatim - ** - ** \brief Utilities for proof testing - **/ - -#include <algorithm> -#include <string> -#include <cctype> -#include <iterator> - -/** - * Creates a new stream with whitespace removed. - * - * @param s the source string - * - * @return a string without whitespace - */ -std::string filterWhitespace(const std::string& s) -{ - std::string out; - std::copy_if(s.cbegin(), s.cend(), std::inserter(out, out.end()), [](char c) { - return !std::isspace(c); - }); - return out; -} diff --git a/test/unit/prop/cnf_stream_white.h b/test/unit/prop/cnf_stream_white.h index f0253fdbf..33fc15674 100644 --- a/test/unit/prop/cnf_stream_white.h +++ b/test/unit/prop/cnf_stream_white.h @@ -174,8 +174,8 @@ class CnfStreamWhite : public CxxTest::TestSuite { Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node b = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node c = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(d_nodeManager->mkNode(kind::AND, a, b, c), - false, false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert( + d_nodeManager->mkNode(kind::AND, a, b, c), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } @@ -189,26 +189,27 @@ class CnfStreamWhite : public CxxTest::TestSuite { Node f = d_nodeManager->mkVar(d_nodeManager->booleanType()); d_cnfStream->convertAndAssert( d_nodeManager->mkNode( - kind::IMPLIES, d_nodeManager->mkNode(kind::AND, a, b), + kind::IMPLIES, + d_nodeManager->mkNode(kind::AND, a, b), d_nodeManager->mkNode( - kind::EQUAL, d_nodeManager->mkNode(kind::OR, c, d), + kind::EQUAL, + d_nodeManager->mkNode(kind::OR, c, d), d_nodeManager->mkNode(kind::NOT, d_nodeManager->mkNode(kind::XOR, e, f)))), - false, false, RULE_INVALID, Node::null()); + false, + false); TS_ASSERT(d_satSolver->addClauseCalled()); } void testTrue() { NodeManagerScope nms(d_nodeManager); - d_cnfStream->convertAndAssert(d_nodeManager->mkConst(true), false, false, - RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert(d_nodeManager->mkConst(true), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } void testFalse() { NodeManagerScope nms(d_nodeManager); - d_cnfStream->convertAndAssert(d_nodeManager->mkConst(false), false, false, - RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert(d_nodeManager->mkConst(false), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } @@ -216,8 +217,8 @@ class CnfStreamWhite : public CxxTest::TestSuite { NodeManagerScope nms(d_nodeManager); Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node b = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(d_nodeManager->mkNode(kind::EQUAL, a, b), false, - false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert( + d_nodeManager->mkNode(kind::EQUAL, a, b), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } @@ -225,33 +226,16 @@ class CnfStreamWhite : public CxxTest::TestSuite { NodeManagerScope nms(d_nodeManager); Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node b = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(d_nodeManager->mkNode(kind::IMPLIES, a, b), - false, false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert( + d_nodeManager->mkNode(kind::IMPLIES, a, b), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } - // ITEs should be removed before going to CNF - // void testIte() { - // NodeManagerScope nms(d_nodeManager); - // d_cnfStream->convertAndAssert( - // d_nodeManager->mkNode( - // kind::EQUAL, - // d_nodeManager->mkNode( - // kind::ITE, - // d_nodeManager->mkVar(d_nodeManager->booleanType()), - // d_nodeManager->mkVar(d_nodeManager->integerType()), - // d_nodeManager->mkVar(d_nodeManager->integerType()) - // ), - // d_nodeManager->mkVar(d_nodeManager->integerType()) - // ), false, false, RULE_INVALID, Node::null()); - // - //} - void testNot() { NodeManagerScope nms(d_nodeManager); Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(d_nodeManager->mkNode(kind::NOT, a), false, - false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert( + d_nodeManager->mkNode(kind::NOT, a), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } @@ -260,8 +244,8 @@ class CnfStreamWhite : public CxxTest::TestSuite { Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node b = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node c = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(d_nodeManager->mkNode(kind::OR, a, b, c), - false, false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert( + d_nodeManager->mkNode(kind::OR, a, b, c), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } @@ -269,10 +253,10 @@ class CnfStreamWhite : public CxxTest::TestSuite { NodeManagerScope nms(d_nodeManager); Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node b = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(a, false, false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert(a, false, false); TS_ASSERT(d_satSolver->addClauseCalled()); d_satSolver->reset(); - d_cnfStream->convertAndAssert(b, false, false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert(b, false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } @@ -280,8 +264,8 @@ class CnfStreamWhite : public CxxTest::TestSuite { NodeManagerScope nms(d_nodeManager); Node a = d_nodeManager->mkVar(d_nodeManager->booleanType()); Node b = d_nodeManager->mkVar(d_nodeManager->booleanType()); - d_cnfStream->convertAndAssert(d_nodeManager->mkNode(kind::XOR, a, b), false, - false, RULE_INVALID, Node::null()); + d_cnfStream->convertAndAssert( + d_nodeManager->mkNode(kind::XOR, a, b), false, false); TS_ASSERT(d_satSolver->addClauseCalled()); } diff --git a/test/unit/theory/theory_engine_white.h b/test/unit/theory/theory_engine_white.h index a67d0aeb2..ae4264aa2 100644 --- a/test/unit/theory/theory_engine_white.h +++ b/test/unit/theory/theory_engine_white.h @@ -41,7 +41,6 @@ #include "theory/theory_rewriter.h" #include "theory/valuation.h" #include "util/integer.h" -#include "util/proof.h" #include "util/rational.h" using namespace CVC4; @@ -55,13 +54,9 @@ using namespace CVC4::theory::bv; using namespace std; class FakeOutputChannel : public OutputChannel { - void conflict(TNode n, std::unique_ptr<Proof> pf) override - { - Unimplemented(); - } + void conflict(TNode n) override { Unimplemented(); } bool propagate(TNode n) override { Unimplemented(); } LemmaStatus lemma(TNode n, - ProofRule rule, LemmaProperty p = LemmaProperty::NONE) override { Unimplemented(); diff --git a/test/unit/theory/theory_white.h b/test/unit/theory/theory_white.h index 9693000a3..e90bd56a2 100644 --- a/test/unit/theory/theory_white.h +++ b/test/unit/theory/theory_white.h @@ -26,7 +26,6 @@ #include "smt/smt_engine_scope.h" #include "theory/theory.h" #include "theory/theory_engine.h" -#include "util/proof.h" #include "util/resource_manager.h" using namespace CVC4; @@ -48,10 +47,7 @@ class TestOutputChannel : public OutputChannel { ~TestOutputChannel() override {} void safePoint(ResourceManager::Resource r) override {} - void conflict(TNode n, std::unique_ptr<Proof> pf) override - { - push(CONFLICT, n); - } + void conflict(TNode n) override { push(CONFLICT, n); } bool propagate(TNode n) override { push(PROPAGATE, n); @@ -59,7 +55,6 @@ class TestOutputChannel : public OutputChannel { } LemmaStatus lemma(TNode n, - ProofRule rule, LemmaProperty p = LemmaProperty::NONE) override { push(LEMMA, n); @@ -298,7 +293,7 @@ class TheoryBlack : public CxxTest::TestSuite { void testOutputChannel() { Node n = atom0.orNode(atom1); - d_outputChannel.lemma(n, RULE_INVALID); + d_outputChannel.lemma(n); d_outputChannel.split(atom0); Node s = atom0.orNode(atom0.notNode()); TS_ASSERT_EQUALS(d_outputChannel.d_callHistory.size(), 2u); |