diff --git a/contrib/get-carcara-checker b/contrib/get-carcara-checker new file mode 100755 index 00000000000..43bf2f6f28b --- /dev/null +++ b/contrib/get-carcara-checker @@ -0,0 +1,70 @@ +#!/usr/bin/env bash + +# utility function to download a file +function download { + if [ -x "$(command -v wget)" ]; then + wget -c -O "$2" "$1" + elif [ -x "$(command -v curl)" ]; then + curl -L "$1" >"$2" + else + echo "Can't figure out how to download from web. Please install wget or curl." >&2 + exit 1 + fi +} + +CVC_DIR=$(dirname $(dirname "$0")) +mkdir -p $CVC_DIR/deps +pushd $CVC_DIR/deps + +BASE_DIR=`pwd` +mkdir -p $BASE_DIR/tmp/ + +###### Carcara +CARCARA_DIR="$BASE_DIR/carcara" +mkdir -p $CARCARA_DIR + +# download and unpack Carcara +CARCARA_VERSION="e770994b5d981257d53a83d38a679fa98c144005" +download "https://github.com/hanielb/carcara/archive/$CARCARA_VERSION.tar.gz" $BASE_DIR/tmp/carcara.tgz +tar --strip 1 -xzf $BASE_DIR/tmp/carcara.tgz -C $CARCARA_DIR + +# build carcara +pushd $CARCARA_DIR +cargo build --release +mkdir -p $BASE_DIR/bin +cp ./target/release/carcara $BASE_DIR/bin/carcara +popd + +cat << EOF > $BASE_DIR/bin/cvc5-carcara-check.sh +#!/usr/bin/env bash +echo "=== Generate proof: \$@" +echo "> \$2.alethe" +$BASE_DIR/bin/cvc5-alethe-proof.sh \$@ > \$2.alethe +echo "=== Check proof with Carcara" +$BASE_DIR/bin/carcara-check.sh \$2.alethe \$2 +EOF +chmod +x $BASE_DIR/bin/cvc5-carcara-check.sh + +cat << EOF > $BASE_DIR/bin/cvc5-alethe-proof.sh +#!/usr/bin/env bash +# call cvc5 and remove the first line of the output (should be "unsat") +\$@ --dump-proofs --proof-format=alethe --proof-alethe-experimental | tail -n +2 +EOF +chmod +x $BASE_DIR/bin/cvc5-alethe-proof.sh + +cat << EOF > $BASE_DIR/bin/carcara-check.sh +#!/usr/bin/env bash +$BASE_DIR/bin/carcara check --allow-int-real-subtyping --expand-let-bindings --ignore-unknown-rules \$@ > carcara.out +cat carcara.out +rm carcara.out +EOF +chmod +x $BASE_DIR/bin/carcara-check.sh + +echo "" +echo "========== How to use Carcara ==========" +echo "Generate an Alethe proof with cvc5 (for terms printed without sharing, use --dag-thresh=0):" +echo " $CVC_DIR/deps/bin/cvc5-alethe-proof.sh cvc5 " +echo "Check a proof:" +echo " $CVC_DIR/deps/bin/carcara-check.sh " +echo "Run cvc5 and check the generated proof:" +echo " $CVC_DIR/deps/bin/cvc5-carcara-check.sh cvc5 " \ No newline at end of file diff --git a/test/regress/cli/CMakeLists.txt b/test/regress/cli/CMakeLists.txt index 390f8ce98c3..d3c41297063 100644 --- a/test/regress/cli/CMakeLists.txt +++ b/test/regress/cli/CMakeLists.txt @@ -3971,6 +3971,7 @@ macro(cvc5_add_regression_test level file) ${RUN_REGRESSION_ARGS} --lfsc-binary ${CMAKE_SOURCE_DIR}/deps/bin/lfscc --lfsc-sig-dir ${CMAKE_SOURCE_DIR}/proofs/lfsc/signatures + --carcara-binary ${CMAKE_SOURCE_DIR}/deps/bin/carcara --ethos-binary ${CMAKE_SOURCE_DIR}/deps/bin/ethos --cpc-sig-dir ${CMAKE_SOURCE_DIR}/proofs/eo ${wrapper} diff --git a/test/regress/cli/run_regression.py b/test/regress/cli/run_regression.py index 7c55c6e1343..521125cd4bb 100755 --- a/test/regress/cli/run_regression.py +++ b/test/regress/cli/run_regression.py @@ -262,6 +262,64 @@ def run_internal(self, benchmark_info): print_ok("OK") return exit_code +class AletheTester(Tester): + def __init__(self): + super().__init__("alethe") + + def applies(self, benchmark_info): + return ( + benchmark_info.benchmark_ext != ".sy" + and benchmark_info.expected_output.strip() == "unsat" + ) + + def run_internal(self, benchmark_info): + exit_code = EXIT_OK + with tempfile.NamedTemporaryFile(suffix=".smt2.proof") as tmpf: + cvc5_args = benchmark_info.command_line_args + [ + "--dump-proofs", + "--proof-format=alethe", + "--proof-alethe-experimental" + ] + # remove duplicates + cvc5_args = list(dict.fromkeys(cvc5_args)) + output, error, exit_status = run_process( + [benchmark_info.cvc5_binary] + + cvc5_args + + [benchmark_info.benchmark_basename], + benchmark_info.benchmark_dir, + benchmark_info.timeout, + ) + tmpf.write(output.strip("unsat\n".encode())) + tmpf.flush() + output, error = output.decode(), error.decode() + exit_code = self.check_exit_status(EXIT_OK, exit_status, output, + error, cvc5_args) + if exit_code != EXIT_OK: + return exit_code + original_file = benchmark_info.benchmark_dir + '/' + benchmark_info.benchmark_basename + carcara_args = [ + "--allow-int-real-subtyping", + "--expand-let-bindings", + "--ignore-unknown-rules" + ] + output, error, exit_status = run_process( + [benchmark_info.carcara_binary] + ["check"] + + carcara_args + [tmpf.name] + [original_file], + benchmark_info.benchmark_dir, + timeout=benchmark_info.timeout, + ) + output, error = output.decode(), error.decode() + exit_code = self.check_exit_status(EXIT_OK, exit_status, output, + error, cvc5_args) + if "valid" not in output and "holey" not in output: + print_error("Invalid proof") + print() + print_outputs(output, error) + return EXIT_FAILURE + if exit_code == EXIT_OK: + print_ok("OK") + return exit_code + class CpcTester(Tester): def __init__(self): @@ -478,6 +536,7 @@ def run_internal(self, benchmark_info): "cvc5_binary", "lfsc_binary", "lfsc_sigs", + "carcara_binary", "ethos_binary", "benchmark_dir", "benchmark_basename", @@ -683,6 +742,7 @@ def run_regression( cvc5_binary, lfsc_binary, lfsc_sigs, + carcara_binary, ethos_binary, benchmark_path, timeout, @@ -762,6 +822,8 @@ def run_regression( testers.remove("lfsc") if "dsl-proof" in testers: testers.remove("dsl-proof") + if "alethe" in testers: + testers.remove("alethe") if "cpc" in testers: testers.remove("cpc") @@ -828,6 +890,7 @@ def run_regression( cvc5_binary=cvc5_binary, lfsc_binary=lfsc_binary, lfsc_sigs=lfsc_sigs, + carcara_binary=carcara_binary, ethos_binary=ethos_binary, benchmark_dir=benchmark_dir, benchmark_basename=benchmark_basename, @@ -875,6 +938,7 @@ def main(): parser.add_argument("--tester", choices=tester_choices, action="append") parser.add_argument("--lfsc-binary", default="") parser.add_argument("--lfsc-sig-dir", default="") + parser.add_argument("--carcara-binary", default="") parser.add_argument("--ethos-binary", default="") parser.add_argument("--cpc-sig-dir", default="") parser.add_argument("wrapper", nargs="*") @@ -890,6 +954,7 @@ def main(): cvc5_binary = os.path.abspath(g_args.cvc5_binary) lfsc_binary = os.path.abspath(g_args.lfsc_binary) + carcara_binary = os.path.abspath(g_args.carcara_binary) ethos_binary = os.path.abspath(g_args.ethos_binary) wrapper = g_args.wrapper @@ -922,6 +987,7 @@ def main(): cvc5_binary, lfsc_binary, lfsc_sigs, + carcara_binary, ethos_binary, g_args.benchmark, timeout,