#include "ast/arith_decl_plugin.h"
#include "ast/reg_decl_plugins.h"
#include "cmd_context/cmd_context.h"
#include "cmd_context/extra_cmds/proof_cmds.h"
#include "parsers/smt2/smt2parser.h"
#include "sat/sat_proof_trim.h"
#include "sat/smt/arith_theory_checker.h"
#include "sat/smt/distinct_theory_checker.h"
#include "sat/smt/tseitin_theory_checker.h"
#include "util/gparams.h"
#include "util/z3_exception.h"
#include <cstdlib>
#include <iostream>
#include <sstream>
static bool g_replaying = false;
static std::ostringstream* g_captured = nullptr;
static void replay_exit_guard() {
if (!g_replaying)
return;
std::cerr << "proof replay exited before completing:\n" << g_captured->str();
std::_Exit(1);
}
struct scoped_replay {
std::streambuf* saved;
scoped_replay(std::ostringstream& captured) : saved(std::cout.rdbuf(captured.rdbuf())) {
g_captured = &captured;
g_replaying = true;
}
~scoped_replay() {
std::cout.rdbuf(saved);
g_replaying = false;
g_captured = nullptr;
}
};
static std::string replay(char const* log) {
static int const registered = std::atexit(replay_exit_guard);
ENSURE(registered == 0);
ast_manager m;
reg_decl_plugins(m);
cmd_context ctx(false, &m);
install_proof_cmds(ctx);
std::istringstream is(log);
std::ostringstream captured;
bool ok;
{
scoped_replay scope(captured);
ok = parse_smt2_commands(ctx, is);
}
VERIFY(ok);
return captured.str();
}
struct scoped_trim {
std::string saved = gparams::get_value("solver.proof.trim");
scoped_trim() { gparams::set("solver.proof.trim", "true"); }
~scoped_trim() { gparams::set("solver.proof.trim", saved.c_str()); }
};
static void check_verified_natively(std::string const& out, char const* hint) {
std::cout << out;
ENSURE(out.find("(verified-smt") == std::string::npos);
ENSURE(out.find("did not verify") == std::string::npos);
ENSURE(out.find("is not rup") == std::string::npos);
ENSURE(out.find(std::string("+") + hint) != std::string::npos);
}
static char const* farkas_real_log =
"(declare-fun k!1 () Real)\n"
"(define-const $24 Bool (>= k!1 (/ 1.0 3.0)))\n"
"(assume $24)\n"
"(define-const $27 Bool (<= k!1 (/ 1.0 4.0)))\n"
"(assume $27)\n"
"(declare-fun farkas (Int Bool Int Bool) Proof)\n"
"(define-const $31 Proof (farkas 1 $27 1 $24))\n"
"(infer $31)\n"
"(declare-fun rup () Proof)\n"
"(infer rup)\n";
static char const* farkas_int_log =
"(declare-fun k!1 () Int)\n"
"(define-const $1 Bool (>= k!1 (- 2)))\n"
"(assume $1)\n"
"(define-const $2 Bool (<= k!1 (- 3)))\n"
"(assume $2)\n"
"(declare-fun farkas (Int Bool Int Bool) Proof)\n"
"(define-const $3 Proof (farkas 1 $2 1 $1))\n"
"(infer $3)\n"
"(declare-fun rup () Proof)\n"
"(infer rup)\n";
static char const* farkas_to_int_log =
"(declare-fun x () Int)\n"
"(define-const $1 Bool (>= x (to_int (/ (- 7.0) 3.0))))\n"
"(assume $1)\n"
"(define-const $2 Bool (<= x (- 4)))\n"
"(assume $2)\n"
"(declare-fun farkas (Int Bool Int Bool) Proof)\n"
"(define-const $3 Proof (farkas 1 $2 1 $1))\n"
"(infer $3)\n"
"(declare-fun rup () Proof)\n"
"(infer rup)\n";
static char const* farkas_nopp_log =
"(declare-fun y () Real)\n"
"(declare-fun x () Real)\n"
"(define-const $16 Real (* (- 1.0) y))\n"
"(define-const $17 Real (+ x $16))\n"
"(define-const $21 Bool (>= $17 (/ 1.0 3.0)))\n"
"(assume $21)\n"
"(define-const $19 Bool (<= $17 (/ 1.0 4.0)))\n"
"(assume $19)\n"
"(declare-fun farkas (Int Bool Int Bool) Proof)\n"
"(define-const $25 Proof (farkas 1 $19 1 $21))\n"
"(infer $25)\n"
"(declare-fun rup () Proof)\n"
"(infer rup)\n";
static void tst_replay_folds_printed_numerals() {
check_verified_natively(replay(farkas_real_log), "farkas 1");
check_verified_natively(replay(farkas_int_log), "farkas 1");
check_verified_natively(replay(farkas_to_int_log), "farkas 1");
check_verified_natively(replay(farkas_nopp_log), "farkas 1");
}
static expr_ref_vector parse_lits(ast_manager& m, char const* text) {
cmd_context ctx(false, &m);
ctx.set_ignore_check(true);
std::istringstream is(text);
VERIFY(parse_smt2_commands(ctx, is));
expr_ref_vector lits(m);
for (expr* e : ctx.assertions())
lits.push_back(e);
return lits;
}
static app_ref mk_arith_hint(ast_manager &m, char const *name, expr_ref_vector const &coeffs,
expr_ref_vector const &lits) {
ENSURE(coeffs.size() == lits.size());
expr_ref_vector args(m);
for (unsigned i = 0; i < lits.size(); ++i) {
args.push_back(coeffs.get(i));
args.push_back(lits.get(i));
}
return app_ref(m.mk_app(symbol(name), args.size(), args.data(), m.mk_proof_sort()), m);
}
static expr_ref_vector ints(ast_manager& m, std::initializer_list<int> ns) {
arith_util a(m);
expr_ref_vector r(m);
for (int n : ns)
r.push_back(a.mk_int(n));
return r;
}
static void tst_checker_nary_mul() {
ast_manager m;
reg_decl_plugins(m);
expr_ref_vector lits = parse_lits(m,
"(declare-const x Real)"
"(assert (>= (* 2.0 3.0 x) 1.0))(assert (<= x 0.0))");
ENSURE(lits.size() == 2);
ENSURE(to_app(to_app(lits.get(0))->get_arg(0))->get_num_args() == 3);
arith::theory_checker checker(m);
ENSURE(checker.check(mk_arith_hint(m, "farkas", ints(m, {1, 6}), lits)));
lits = parse_lits(m,
"(declare-const x Real)(declare-const y Real)"
"(assert (>= (* 2.0 x y) 1.0))(assert (<= x 0.0))");
ENSURE(!checker.check(mk_arith_hint(m, "farkas", ints(m, {1, 2}), lits)));
}
static void tst_checker_printed_numerals() {
ast_manager m;
reg_decl_plugins(m);
arith_util a(m);
expr_ref_vector lits = parse_lits(m,
"(declare-const x Real)"
"(assert (>= x (/ 1.0 3.0)))(assert (<= x (- (/ 1.0 4.0))))");
ENSURE(a.is_div(to_app(lits.get(0))->get_arg(1)));
expr_ref_vector coeffs(m);
coeffs.push_back(a.mk_uminus(a.mk_int(1)));
coeffs.push_back(a.mk_uminus(a.mk_int(1)));
arith::theory_checker checker(m);
ENSURE(checker.check(mk_arith_hint(m, "farkas", coeffs, lits)));
}
static void tst_checker_zero_weight_strictness() {
ast_manager m;
reg_decl_plugins(m);
arith::theory_checker checker(m);
for (char const *atom : {"(< x 0)", "(> x 0)", "(not (<= x 0))", "(not (>= x 0))"}) {
std::string source = std::string("(declare-const x Real)(assert ") + atom + ")";
auto lits = parse_lits(m, source.c_str());
ENSURE(!checker.check(mk_arith_hint(m, "farkas", ints(m, {0}), lits)));
lits.push_back(mk_not(m, lits.get(0)));
ENSURE(checker.check(mk_arith_hint(m, "farkas", ints(m, {1, 1}), lits)));
}
auto lits = parse_lits(m, "(declare-const x Real)(declare-const y Real)"
"(assert (< x 0))(assert (<= y 0))(assert (>= y 0))");
for (char const *name : {"farkas", "bound"})
ENSURE(!checker.check(mk_arith_hint(m, name, ints(m, {0, 1, 1}), lits)));
lits = parse_lits(m, "(declare-const x Real)(declare-const y Real)"
"(assert (< x 0))(assert (< y 0))(assert (>= y 0))");
for (char const *name : {"farkas", "bound"})
ENSURE(checker.check(mk_arith_hint(m, name, ints(m, {0, 1, 1}), lits)));
lits = parse_lits(m, "(declare-const x Real)(assert (<= x 1))(assert (> x 0))");
ENSURE(!checker.check(mk_arith_hint(m, "bound", ints(m, {1, 0}), lits)));
}
static void tst_checker_bound_row_strictness() {
ast_manager m;
reg_decl_plugins(m);
arith::theory_checker checker(m);
auto lits = parse_lits(m, "(declare-const x Real)"
"(assert (< x 0))(assert (<= (- x x) 0))");
ENSURE(!checker.check(mk_arith_hint(m, "bound", ints(m, {1, 1}), lits)));
lits = parse_lits(m, "(declare-const x Real)"
"(assert (< x 0))(assert (not (<= (- x x) 0)))");
ENSURE(checker.check(mk_arith_hint(m, "bound", ints(m, {1, 1}), lits)));
lits = parse_lits(m, "(declare-const x Real)"
"(assert (< (- x x) 0))(assert (<= (- x x) 0))");
ENSURE(checker.check(mk_arith_hint(m, "bound", ints(m, {1, 1}), lits)));
for (char const *source : {"(declare-const x Real)(assert (< x 0))(assert (>= x 0))",
"(declare-const x Real)(assert (<= x 0))(assert (> x 0))",
"(declare-const x Real)(declare-const y Real)"
"(assert (= x y))(assert (< x 0))(assert (>= y 0))"}) {
lits = parse_lits(m, source);
auto coeffs = lits.size() == 2 ? ints(m, {1, 1}) : ints(m, {1, 1, 1});
for (char const *name : {"farkas", "bound"})
ENSURE(checker.check(mk_arith_hint(m, name, coeffs, lits)));
}
}
static void tst_checker_implied_eq_conclusion() {
ast_manager m;
reg_decl_plugins(m);
arith::theory_checker checker(m);
for (char const *sort : {"Real", "Int"}) {
std::string decls = std::string("(declare-const x ") + sort + ")(declare-const y " + sort + ")";
auto lits = parse_lits(m, (decls + "(assert (not (= x y)))").c_str());
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {0}), lits)));
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1}), lits)));
lits = parse_lits(m, (decls + "(assert (<= x 0))").c_str());
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1}), lits)));
lits = parse_lits(m, (decls + "(assert (= x y))").c_str());
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1}), lits)));
lits = parse_lits(m, (decls + "(assert (<= x y))(assert (>= x y))(assert (not (= x y)))").c_str());
for (int coeff : {1, -1})
ENSURE(checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, coeff}), lits)));
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, 0}), lits)));
}
expr_ref_vector empty(m);
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", empty, empty)));
auto args = ints(m, {1});
app_ref incomplete(m.mk_app(symbol("implied-eq"), args.size(), args.data(), m.mk_proof_sort()), m);
ENSURE(!checker.check(incomplete));
}
static void tst_checker_implied_eq_constant_rows() {
ast_manager m;
reg_decl_plugins(m);
arith::theory_checker checker(m);
for (char const *sort : {"Real", "Int"}) {
std::string decls = std::string("(declare-const x ") + sort + ")(declare-const y " + sort +
")(declare-const z " + sort + ")";
for (char const *tautology : {"(<= 0 1)", "(< 0 1)", "(<= 0 0)"}) {
std::string premise = decls + "(assert " + tautology + ")";
auto lits = parse_lits(m, (premise + "(assert (<= x y))(assert (>= x y))"
"(assert (not (= x y)))").c_str());
ENSURE(checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, 1, 1}), lits)));
lits = parse_lits(m, (premise + "(assert (not (= x y)))").c_str());
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1}), lits)));
}
auto lits = parse_lits(m, (decls + "(assert (= z 0))(assert (<= z 1))"
"(assert (<= x y))(assert (>= x y))(assert (not (= x y)))").c_str());
ENSURE(checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, 1, 1, 1}), lits)));
lits = parse_lits(m, (decls + "(assert (<= z 1))(assert (>= z 0))"
"(assert (<= x y))(assert (>= x y))(assert (not (= x y)))").c_str());
ENSURE(checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, 1, 1, 1}), lits)));
for (char const *contradiction : {"(<= 1 0)", "(< 0 0)"}) {
lits = parse_lits(m, (decls + "(assert (<= 0 0))(assert " + contradiction +
")(assert (not (= x y)))").c_str());
ENSURE(checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, 1}), lits)));
lits = parse_lits(m, (decls + "(assert (not (= x y)))").c_str());
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1}), lits)));
}
lits = parse_lits(m, (decls + "(assert (<= z 0))(assert (>= z 1))"
"(assert (not (= x y)))").c_str());
ENSURE(checker.check(mk_arith_hint(m, "implied-eq", ints(m, {1, 1, 1}), lits)));
lits = parse_lits(m, (decls + "(assert (< 0 0))(assert (not (= x y)))").c_str());
ENSURE(!checker.check(mk_arith_hint(m, "implied-eq", ints(m, {0, 1}), lits)));
}
}
static void tst_replay_implied_eq_constant_row() {
check_verified_natively(replay(
"(declare-const x Real)(declare-const y Real)"
"(assume (<= 0.0 1.0))(assume (<= x y))(assume (>= x y))(assume (not (= x y)))"
"(declare-fun implied-eq (Int Bool Int Bool Int Bool Int Bool) Proof)"
"(infer (implied-eq 1 (<= 0.0 1.0) 1 (<= x y) 1 (>= x y) 1 (not (= x y))))"
"(declare-fun rup () Proof)(infer rup)"), "implied-eq 1");
}
static bool check_tseitin(ast_manager& m, expr_ref_vector const& lits) {
app_ref hint(m.mk_app(symbol("tseitin"), lits.size(), lits.data(), m.mk_proof_sort()), m);
tseitin::theory_checker checker(m);
return checker.check(hint);
}
static bool check_tseitin(ast_manager& m, char const* source) {
return check_tseitin(m, parse_lits(m, source));
}
static void tst_checker_tseitin_xor() {
ast_manager m;
reg_decl_plugins(m);
char const* decls = "(declare-const a Bool)(declare-const b Bool)(declare-const c Bool)";
auto check = [&](char const* clause) {
return check_tseitin(m, (std::string(decls) + clause).c_str());
};
ENSURE(check("(assert (xor a b))(assert a)(assert (not b))"));
ENSURE(check("(assert (xor a b))(assert (not a))(assert b)"));
ENSURE(check("(assert (not (xor a b)))(assert a)(assert b)"));
ENSURE(check("(assert (not (xor a b)))(assert (not a))(assert (not b))"));
ENSURE(!check("(assert (xor a b))(assert a)(assert b)"));
ENSURE(!check("(assert (xor a b))(assert (not a))(assert (not b))"));
ENSURE(!check("(assert (not (xor a b)))(assert a)(assert (not b))"));
ENSURE(!check("(assert (not (xor a b)))(assert (not a))(assert b)"));
ENSURE(!check("(assert (not (xor a b)))(assert a)"));
ENSURE(!check("(assert (not (xor a b)))(assert (not a))"));
ENSURE(!check("(assert (xor a b))(assert (not a))"));
ENSURE(!check("(assert (xor a a))(assert a)"));
ENSURE(!check("(assert (xor a a))(assert (not a))"));
ENSURE(check("(assert (not (xor a a)))(assert a)"));
ENSURE(check("(assert (xor a true))(assert a)"));
ENSURE(!check("(assert (xor a true))(assert (not a))"));
ENSURE(check("(assert (xor a false))(assert (not a))"));
ENSURE(!check("(assert (xor a false))(assert a)"));
ENSURE(!check("(assert (xor a b c))(assert a)(assert b)(assert (not c))"));
ENSURE(check("(assert (xor a b c))(assert a)(assert (not (xor b c)))"));
ENSURE(!check("(assert (xor a b c))(assert a)(assert (xor b c))"));
ENSURE(check("(assert (and a b))(assert (not a))(assert (not b))"));
ENSURE(!check("(assert (and a b))(assert (not a))(assert b)"));
}
static void tst_replay_tseitin_xor() {
check_verified_natively(replay(
"(declare-fun a () Bool)(declare-fun b () Bool)(declare-fun p () Bool)"
"(define-const $1 Bool (xor a b))"
"(declare-fun tseitin (Bool Bool Bool) Proof)"
"(infer (not a) (not b) (not $1) (tseitin (not $1) (not a) (not b)))"
"(infer a b (not $1) (tseitin (not $1) a b))"
"(infer a (not b) $1 (tseitin $1 a (not b)))"
"(infer (not a) b $1 (tseitin $1 (not a) b))"
"(assume p $1)(assume (not p))(assume a)(assume b)"
"(declare-fun rup () Proof)(infer $1 rup)(infer rup)"), "tseitin 4");
}
static bool check_alldiff(ast_manager& m, char const* source) {
auto lits = parse_lits(m, source);
app_ref hint(m.mk_app(symbol("alldiff"), lits.size(), lits.data(), m.mk_proof_sort()), m);
distinct::theory_checker checker(m);
return checker.check(hint);
}
static void tst_checker_alldiff() {
ast_manager m;
reg_decl_plugins(m);
char const* decls =
"(declare-sort U 0)(declare-const a U)(declare-const b U)(declare-const c U)(declare-const d U)"
"(declare-const q Bool)(declare-const x Int)";
auto check = [&](char const* clause) {
return check_alldiff(m, (std::string(decls) + clause).c_str());
};
ENSURE(check("(assert (distinct a b c))(assert (or (= a b) (= a c) (= b c)))"));
ENSURE(check("(assert (not (distinct a b c)))(assert (not (or (= a b) (= a c) (= b c))))"));
ENSURE(check("(assert (or (= a b) (= a c) (= b c)))(assert (distinct a b c))"));
ENSURE(check("(assert (distinct a b))(assert (= a b))"));
ENSURE(check("(assert (not (distinct a b)))(assert (not (= a b)))"));
ENSURE(check("(assert (distinct a b c))(assert (or (= b a) (= c a) (= c b)))"));
ENSURE(check("(assert (not (distinct a b c)))(assert (not (or (= b a) (= c a) (= c b))))"));
ENSURE(!check("(assert (distinct a b c))(assert (not (or (= a b) (= a c) (= b c))))"));
ENSURE(!check("(assert (not (distinct a b c)))(assert (or (= a b) (= a c) (= b c)))"));
ENSURE(!check("(assert (distinct a b))(assert (not (= a b)))"));
ENSURE(!check("(assert (distinct a b c))(assert (or (= a b) (= a c)))"));
ENSURE(!check("(assert (distinct a b c))(assert (or (= a b) (= a c) (= b d)))"));
ENSURE(!check("(assert (distinct a b c))(assert (= a b))"));
ENSURE(!check("(assert (not (distinct a b c)))(assert (not (or (= a b) (= a c) (= b c) (= a d))))"));
ENSURE(!check("(assert (not (distinct a b c)))(assert (not (or (= a b) (= a c) (= b c) q)))"));
ENSURE(!check("(assert (not (distinct a b c)))(assert (not (or (= a b) (= a c) (= b c) (= a a))))"));
ENSURE(!check("(assert (not (distinct a b)))(assert (not q))"));
ENSURE(check("(assert (distinct a b c))(assert (or (= a b) (= a c) (= b c) q))"));
ENSURE(check("(assert (not (distinct a b c)))(assert (not (or (= a b) (= a c) (= b c) false)))"));
ENSURE(check("(assert (distinct a b a))(assert (or true (= a b)))"));
ENSURE(check("(assert (not (distinct a b a)))(assert (not (or true (= a b))))"));
ENSURE(!check("(assert (not (distinct a b)))(assert (not (or true (= a b))))"));
ENSURE(!check("(assert (distinct a b a))(assert (= a b))"));
ENSURE(check("(assert (distinct a b a))(assert (or (= a a) (= a b)))"));
ENSURE(check("(assert (distinct 1 2 x))(assert (or (= 1 x) (= 2 x)))"));
ENSURE(!check("(assert (distinct 1 x))(assert (= 2 x))"));
ENSURE(!check("(assert (= a b))(assert (= a c))(assert (= b c))"));
ENSURE(!check("(assert (not (= a b)))"));
ENSURE(!check("(assert (= a b))"));
ENSURE(!check(""));
ENSURE(!check("(assert (distinct a b c))(assert q)(assert (or (= a b) (= a c) (= b c)))"));
ENSURE(!check("(assert q)(assert (or (= a b) (= a c) (= b c)))"));
}
static void tst_replay_alldiff_gates() {
check_verified_natively(replay(
"(declare-sort U 0)(declare-fun a () U)(declare-fun b () U)(declare-fun c () U)"
"(define-const $15 Bool (= a b))(define-const $16 Bool (= b c))(define-const $13 Bool (= a c))"
"(define-const $14 Bool (or $15 $13 $16))"
"(declare-fun tseitin (Bool Bool) Proof)"
"(infer (not $15) $14 (tseitin (not $15) $14))"
"(infer (not $13) $14 (tseitin (not $13) $14))"
"(infer (not $16) $14 (tseitin (not $16) $14))"
"(declare-fun tseitin (Bool Bool Bool Bool) Proof)"
"(infer $15 $13 $16 (not $14) (tseitin $15 $13 $16 (not $14)))"
"(define-const $9 Bool (distinct a b c))"
"(declare-fun alldiff (Bool Bool) Proof)"
"(infer (not $9) (not $14) (alldiff (not $9) (not $14)))"
"(infer $9 $14 (alldiff $9 $14))"
"(declare-fun p () Bool)"
"(assume p $9)(assume (not p) $9)(assume $13)"
"(declare-fun rup () Proof)"
"(infer $14 rup)(infer (not $9) rup)(infer p rup)(infer rup)"), "alldiff 2");
}
static void tst_is_extended_numeral() {
ast_manager m;
reg_decl_plugins(m);
arith_util a(m);
rational n;
expr_ref t(m);
t = a.mk_real(7);
ENSURE(a.is_extended_numeral(t, n) && n == 7);
{
expr_ref lhs(a.mk_uminus(a.mk_real(1)), m);
expr_ref lhs1(a.mk_real(2), m);
expr_ref rhs(a.mk_uminus(a.mk_real(4)), m);
expr_ref rhs1(a.mk_div(lhs1, rhs), m);
t = a.mk_div(lhs, rhs1);
}
ENSURE(a.is_extended_numeral(t, n) && n == 2);
t = a.mk_to_real(a.mk_uminus(a.mk_int(3)));
ENSURE(a.is_extended_numeral(t, n) && n == -3);
{
expr_ref lhs(a.mk_real(-7), m);
expr_ref rhs(a.mk_real(3), m);
t = a.mk_to_int(a.mk_div(lhs, rhs));
}
ENSURE(a.is_extended_numeral(t, n) && n == -3);
{
expr_ref lhs(a.mk_real(7), m);
expr_ref rhs(a.mk_real(3), m);
t = a.mk_to_real(a.mk_to_int(a.mk_div(lhs, rhs)));
}
ENSURE(a.is_extended_numeral(t, n) && n == 2);
{
expr_ref lhs(a.mk_real(1), m);
expr_ref rhs(a.mk_real(0), m);
t = a.mk_div(lhs, rhs);
}
ENSURE(!a.is_extended_numeral(t, n));
{
expr_ref lhs(a.mk_real(1), m);
expr_ref rhs(a.mk_uminus(a.mk_real(0)), m);
t = a.mk_div(lhs, rhs);
}
ENSURE(!a.is_extended_numeral(t, n));
{
expr_ref lhs(a.mk_real(1), m);
expr_ref lhs1(a.mk_real(1), m);
expr_ref rhs(a.mk_real(0), m);
expr_ref rhs1(a.mk_div(lhs1, rhs), m);
t = a.mk_div(lhs, rhs1);
}
ENSURE(!a.is_extended_numeral(t, n));
{
expr_ref lhs(a.mk_real(1), m);
expr_ref lhs1(a.mk_real(1), m);
expr_ref rhs(a.mk_real(-1), m);
expr_ref rhs1(a.mk_add(lhs1, rhs), m);
t = a.mk_div(lhs, rhs1);
}
ENSURE(!a.is_extended_numeral(t, n));
{
expr_ref lhs(a.mk_real(2), m);
expr_ref rhs(a.mk_real(3), m);
expr_ref lhs1(a.mk_mul(lhs, rhs), m);
expr_ref lhs2(a.mk_real(1), m);
expr_ref rhs1(a.mk_real(2), m);
expr_ref rhs2(a.mk_add(lhs2, rhs1), m);
t = a.mk_sub(lhs1, rhs2);
}
ENSURE(a.is_extended_numeral(t, n) && n == 3);
t = a.mk_uminus(m.mk_const(symbol("x"), a.mk_real()));
ENSURE(!a.is_extended_numeral(t, n));
t = a.mk_real(1);
for (unsigned i = 0; i < 200000; ++i)
t = a.mk_uminus(t);
ENSURE(a.is_extended_numeral(t, n) && n == 1);
for (unsigned i = 0; i < 20000; ++i)
t = a.mk_div(t, a.mk_real(1));
ENSURE(a.is_extended_numeral(t, n) && n == 1);
t = a.mk_real(1);
for (unsigned i = 0; i < 20000; ++i) {
expr_ref lhs(a.mk_add(t, a.mk_real(0)), m);
expr_ref rhs(a.mk_real(1), m);
t = a.mk_div(lhs, rhs);
}
ENSURE(a.is_extended_numeral(t, n) && n == 1);
for (unsigned i = 0; i < 20000; ++i)
t = a.mk_to_real(a.mk_to_int(t));
ENSURE(a.is_extended_numeral(t, n) && n == 1);
expr_ref x(m.mk_const(symbol("x"), a.mk_real()), m);
expr_ref_vector lits(m);
{
expr_ref lhs(a.mk_mul(t, x), m);
expr_ref rhs(a.mk_real(1), m);
lits.push_back(a.mk_ge(lhs, rhs));
}
lits.push_back(a.mk_le(x, a.mk_real(0)));
arith::theory_checker checker(m);
ENSURE(checker.check(mk_arith_hint(m, "farkas", ints(m, {1, 1}), lits)));
}
static bool infer_verifies(std::string const& text, unsigned n_assume, unsigned n_clause, char const* hint, std::string& out) {
ast_manager m;
reg_decl_plugins(m);
auto lits = parse_lits(m, text.c_str());
ENSURE(lits.size() >= n_assume + n_clause);
params_ref p;
euf::smt_proof_checker checker(m, p);
expr_ref_vector clause(m), args(m);
unsigned i = 0;
for (; i < n_assume; ++i) {
clause.reset();
clause.push_back(lits.get(i));
checker.assume(clause);
}
clause.reset();
for (; i < n_assume + n_clause; ++i)
clause.push_back(lits.get(i));
for (; i < lits.size(); ++i)
args.push_back(lits.get(i));
app_ref jst(m.mk_app(symbol(hint), args.size(), args.data(), m.mk_proof_sort()), m);
std::ostringstream captured;
bool ok;
{
scoped_replay scope(captured);
try {
ok = checker.infer(clause, jst);
}
catch (default_exception const& ex) {
if (std::string(ex.what()) != "SMT proof verification failed")
throw;
ok = false;
}
}
out = captured.str();
return ok;
}
static void tst_fallback_proves_logged_clause() {
std::string decls = "(declare-sort U 0)(declare-const a U)(declare-const b U)(declare-const c U)";
std::string gate = "(assert (distinct a b c))(assert (or (= a b) (= a c) (= b c)))";
std::string out;
ENSURE(!infer_verifies(decls + "(assert (not (distinct a b c)))(assert (distinct a b c))" + gate, 1, 1, "alldiff", out));
ENSURE(out.find("is not rup") != std::string::npos);
ENSURE(out.find("did not verify") != std::string::npos);
ENSURE(out.find("(verified-smt") == std::string::npos);
ENSURE(!infer_verifies(decls + "(assert (not (distinct a b c)))(assert (distinct a b c))" + gate, 1, 1, "nohint", out));
ENSURE(out.find("did not verify") != std::string::npos);
ENSURE(infer_verifies(decls + "(assert (not (or (= a b) (= a c) (= b c))))(assert (distinct a b c))" + gate, 1, 1, "alldiff", out));
ENSURE(out.find("+alldiff 1") != std::string::npos);
ENSURE(out.find("(verified-smt") == std::string::npos);
ENSURE(infer_verifies(decls + "(assert (= b a))(assert (= a b))(assert (not (= a b)))(assert (= b a))", 1, 1, "array", out));
ENSURE(out.find("(verified-smt") != std::string::npos);
ENSURE(!infer_verifies(decls + "(assert (= a b))(assert (not (= a b)))(assert (= b a))", 0, 1, "array", out));
ENSURE(out.find("is not rup") != std::string::npos);
ENSURE(out.find("did not verify") != std::string::npos);
ENSURE(infer_verifies(decls + "(assert (= a a))(assert (not (= a a)))(assert (= b a))", 0, 1, "array", out));
ENSURE(out.find("(verified-smt") != std::string::npos);
ENSURE(out.find("is not rup") == std::string::npos);
ENSURE(infer_verifies(decls + "(assert (= a b))(assert (= b a))(assert (= a a))", 1, 1, "array", out));
ENSURE(out.find("(verified-smt") != std::string::npos);
ENSURE(!infer_verifies(decls + "(assert (= b a))(assert (= a a))", 0, 1, "array", out));
ENSURE(out.find("did not verify") != std::string::npos);
}
static void tst_trim_satisfied_clause() {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal p(trim.mk_var(), false), q(trim.mk_var(), false), r(trim.mk_var(), false);
auto assume = [&](unsigned id, std::initializer_list<sat::literal> literals) {
trim.init_clause();
for (auto literal : literals)
trim.add_literal(literal.var(), literal.sign());
trim.assume(id);
};
assume(0, {p});
assume(1, {~q});
assume(2, {p, q, r}); assume(3, {~r});
assume(4, {~p});
trim.init_clause();
trim.infer(5);
auto core = trim.trim();
ENSURE(core.size() == 3);
ENSURE(core[0].first == 0);
ENSURE(core[1].first == 4);
ENSURE(core[2].first == 5);
}
static void tst_trim_duplicate_binary_keeps_implied_literal() {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal a(trim.mk_var(), false), b(trim.mk_var(), false), u(trim.mk_var(), false),
c(trim.mk_var(), false), d(trim.mk_var(), false), e(trim.mk_var(), false);
auto clause = [&](std::initializer_list<sat::literal> literals) {
trim.init_clause();
for (auto literal : literals)
trim.add_literal(literal.var(), literal.sign());
};
clause({a}); trim.assume(0);
clause({~a, b}); trim.assume(1); clause({~b, ~a, u}); trim.assume(2); clause({~b, ~a, u}); trim.del(); clause({u}); trim.infer(3); clause({~u, ~d, c, ~e}); trim.assume(4);
clause({~u, ~d, c, e}); trim.assume(5);
clause({~d, c}); trim.infer(6); clause({~a, b}); trim.assume(7); clause({d}); trim.assume(8); clause({~c}); trim.assume(9);
clause({}); trim.infer(10);
auto core = trim.trim();
unsigned_vector ids;
unsigned_vector const* deps6 = nullptr;
for (auto const& [id, deps] : core) {
ids.push_back(id);
if (id == 6)
deps6 = &deps;
}
for (unsigned id : {1u, 2u, 4u, 5u, 6u, 8u, 9u, 10u})
ENSURE(ids.contains(id));
ENSURE(!ids.contains(7));
ENSURE(deps6 && deps6->contains(4) && deps6->contains(5) && deps6->contains(2));
for (unsigned dep : *deps6)
ENSURE(dep < 6);
}
static void tst_trim_rejects_underivable_inference() {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal a(trim.mk_var(), false), b(trim.mk_var(), false);
auto clause = [&](std::initializer_list<sat::literal> literals) {
trim.init_clause();
for (auto literal : literals)
trim.add_literal(literal.var(), literal.sign());
};
clause({a}); trim.assume(0);
clause({b}); trim.infer(1); clause({~b}); trim.assume(2);
clause({}); trim.infer(3);
bool rejected = false;
try {
trim.trim();
}
catch (z3_exception& ex) {
rejected = std::string(ex.what()).find("not derivable by unit propagation") != std::string::npos;
}
ENSURE(rejected);
}
static void tst_trim_ignores_deletions_of_clauses_never_added() {
for (bool before_conflict : {false, true}) {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal a(trim.mk_var(), false), b(trim.mk_var(), false), c(trim.mk_var(), false),
x(trim.mk_var(), false), d(trim.mk_var(), false);
auto clause = [&](std::initializer_list<sat::literal> literals) {
trim.init_clause();
for (auto literal : literals)
trim.add_literal(literal.var(), literal.sign());
};
clause({a}); trim.assume(0);
clause({~a, b}); trim.assume(1); clause({~b, c, x}); trim.assume(2);
clause({~x, d}); trim.assume(3);
clause({~x, ~d}); trim.assume(4);
clause({c}); trim.infer(5); if (before_conflict) {
clause({~b, c, d}); trim.del(); }
clause({~c}); trim.assume(6); if (!before_conflict) {
clause({~b, c, d}); trim.assume(7); clause({~b, c, d}); trim.del();
}
clause({}); trim.infer(8);
auto core = trim.trim();
unsigned_vector ids;
unsigned_vector const* deps5 = nullptr;
for (auto const& [id, deps] : core) {
ids.push_back(id);
if (id == 5)
deps5 = &deps;
}
for (unsigned id : {0u, 1u, 2u, 3u, 4u, 5u, 6u, 8u})
ENSURE(ids.contains(id));
ENSURE(!ids.contains(7));
ENSURE(deps5 && deps5->contains(2) && deps5->contains(3) && deps5->contains(4));
for (unsigned dep : *deps5)
ENSURE(dep < 5);
}
}
static void tst_trim_watches_literals_that_are_not_false() {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal a(trim.mk_var(), false), b(trim.mk_var(), false), c(trim.mk_var(), false),
d(trim.mk_var(), false);
auto clause = [&](std::initializer_list<sat::literal> literals) {
trim.init_clause();
for (auto literal : literals)
trim.add_literal(literal.var(), literal.sign());
};
clause({a}); trim.assume(0);
clause({b}); trim.assume(1);
clause({~a, ~b, c, d}); trim.assume(2);
clause({~c}); trim.assume(3);
clause({~d}); trim.assume(4);
clause({}); trim.infer(5);
auto core = trim.trim();
ENSURE(core.size() == 6);
for (unsigned i = 0; i < core.size(); ++i) {
ENSURE(core[i].first == i);
for (unsigned dep : core[i].second)
ENSURE(dep < i);
}
ENSURE(core[5].second.size() == 5);
sat::proof_trim trim2(params, limit);
sat::literal p(trim2.mk_var(), false), q(trim2.mk_var(), false);
auto clause2 = [&](std::initializer_list<sat::literal> literals) {
trim2.init_clause();
for (auto literal : literals)
trim2.add_literal(literal.var(), literal.sign());
};
clause2({p}); trim2.assume(0);
clause2({~p, q}); trim2.assume(1);
clause2({}); trim2.infer(2); bool rejected = false;
try {
trim2.trim();
}
catch (z3_exception& ex) {
rejected = std::string(ex.what()).find("empty clause is not derivable") != std::string::npos;
}
ENSURE(rejected);
sat::proof_trim trim3(params, limit);
sat::literal r(trim3.mk_var(), false);
trim3.init_clause(); trim3.add_literal(r.var(), r.sign()); trim3.assume(0);
trim3.init_clause(); trim3.assume(1);
trim3.init_clause(); trim3.infer(2);
auto core3 = trim3.trim();
ENSURE(core3.size() == 2);
ENSURE(core3[0].first == 1 && core3[0].second.empty());
ENSURE(core3[1].first == 2 && core3[1].second.size() == 1 && core3[1].second[0] == 1);
}
static void tst_trim_assumed_empty_clauses() {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal x(trim.mk_var(), false), y(trim.mk_var(), false);
trim.init_clause(); trim.add_literal(x.var(), x.sign()); trim.assume(0);
trim.init_clause(); trim.add_literal(y.var(), y.sign()); trim.assume(1);
trim.init_clause(); trim.assume(2); bool rejected = false;
try {
trim.trim();
}
catch (z3_exception& ex) {
rejected = std::string(ex.what()).find("theory axiom whose premises are unknown") != std::string::npos;
}
ENSURE(rejected);
sat::proof_trim trim2(params, limit);
sat::literal a(trim2.mk_var(), false), b(trim2.mk_var(), false);
trim2.init_clause(); trim2.add_literal(a.var(), a.sign()); trim2.assume(0);
trim2.init_clause(); trim2.add_literal(a.var(), !a.sign()); trim2.assume(1);
trim2.init_clause(); trim2.assume(2);
trim2.init_clause(); trim2.add_literal(b.var(), b.sign()); trim2.assume(3);
trim2.init_clause(); trim2.infer(4);
auto core = trim2.trim();
ENSURE(core.size() == 3);
ENSURE(core[0].first == 0 && core[1].first == 1 && core[2].first == 4);
ENSURE(core[2].second.size() == 2);
}
static void tst_trim_double_negation() {
char const* log =
"(declare-fun q () Bool)\n"
"(declare-fun pb (Bool) Bool)\n"
"(assume (not q))\n"
"(assume (not (not q)) (pb true))\n"
"(assume (not (pb true)))\n"
"(declare-fun rup () Proof)\n"
"(infer rup)\n";
std::string out;
{
scoped_trim trim;
out = replay(log);
}
std::cout << out;
ENSURE(out.find("not derivable") == std::string::npos);
for (char const* deps : {"(deps 0)", "(deps 1)", "(deps 2)", "(deps 3 "})
ENSURE(out.find(deps) != std::string::npos);
}
static void tst_trim_deletes_clauses_with_repeated_literals() {
params_ref params;
reslimit limit;
sat::proof_trim trim(params, limit);
sat::literal q(trim.mk_var(), false), a(trim.mk_var(), false);
auto clause = [&](std::initializer_list<sat::literal> literals) {
trim.init_clause();
for (auto literal : literals)
trim.add_literal(literal.var(), literal.sign());
};
clause({q, q, a}); trim.assume(0);
clause({q, q, a}); trim.del();
clause({~q}); trim.assume(1);
clause({~a}); trim.assume(2);
clause({}); trim.infer(3);
bool rejected = false;
try {
trim.trim();
}
catch (z3_exception& ex) {
rejected = std::string(ex.what()).find("empty clause is not derivable") != std::string::npos;
}
ENSURE(rejected);
}
void tst_proof_replay() {
tst_trim_double_negation();
tst_trim_deletes_clauses_with_repeated_literals();
tst_trim_satisfied_clause();
tst_trim_watches_literals_that_are_not_false();
tst_trim_assumed_empty_clauses();
tst_trim_duplicate_binary_keeps_implied_literal();
tst_trim_rejects_underivable_inference();
tst_trim_ignores_deletions_of_clauses_never_added();
tst_is_extended_numeral();
tst_checker_nary_mul();
tst_checker_printed_numerals();
tst_checker_zero_weight_strictness();
tst_checker_bound_row_strictness();
tst_checker_implied_eq_conclusion();
tst_checker_implied_eq_constant_rows();
tst_replay_implied_eq_constant_row();
tst_checker_tseitin_xor();
tst_replay_tseitin_xor();
tst_checker_alldiff();
tst_replay_alldiff_gates();
tst_fallback_proves_logged_clause();
tst_replay_folds_printed_numerals();
}