3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-30 04:15:51 +00:00

wip - updates to proof logging and self-checking

move self-checking functionality to inside sat/smt so it can be used on-line and not just off-line.

when self-validation fails, use vs, not clause, to check. It allows self-validation without checking and maintaining RUP validation.

new options sat.smt.proof.check_rup, sat.smt.proof.check for online validation.

z3 sat.smt.proof.check=true sat.euf=true /v:1 sat.smt.proof.check_rup=true /st file.smt2 sat.smt.proof=p.smt2
This commit is contained in:
Nikolaj Bjorner 2022-10-16 23:33:30 +02:00
parent 993ff40826
commit ac1552d194
40 changed files with 539 additions and 419 deletions

View file

@ -20,11 +20,14 @@ Author:
#include "ast/ast_util.h"
#include "ast/ast_ll_pp.h"
#include "ast/arith_decl_plugin.h"
#include "smt/smt_solver.h"
#include "sat/sat_params.hpp"
#include "sat/smt/euf_proof_checker.h"
#include "sat/smt/arith_proof_checker.h"
#include "sat/smt/q_proof_checker.h"
#include "sat/smt/arith_theory_checker.h"
#include "sat/smt/q_theory_checker.h"
#include "sat/smt/tseitin_proof_checker.h"
namespace euf {
/**
@ -57,7 +60,7 @@ namespace euf {
* union-find checker.
*/
class eq_proof_checker : public proof_checker_plugin {
class eq_theory_checker : public theory_checker_plugin {
ast_manager& m;
arith_util m_arith;
expr_ref_vector m_trail;
@ -133,7 +136,7 @@ namespace euf {
}
public:
eq_proof_checker(ast_manager& m): m(m), m_arith(m), m_trail(m) {}
eq_theory_checker(ast_manager& m): m(m), m_arith(m), m_trail(m) {}
expr_ref_vector clause(app* jst) override {
expr_ref_vector result(m);
@ -208,7 +211,7 @@ namespace euf {
return false;
}
void register_plugins(proof_checker& pc) override {
void register_plugins(theory_checker& pc) override {
pc.register_plugin(symbol("euf"), this);
}
};
@ -219,12 +222,12 @@ namespace euf {
The pivot occurs with opposite signs in proof1 and proof2
*/
class res_proof_checker : public proof_checker_plugin {
class res_checker : public theory_checker_plugin {
ast_manager& m;
proof_checker& pc;
theory_checker& pc;
public:
res_proof_checker(ast_manager& m, proof_checker& pc): m(m), pc(pc) {}
res_checker(ast_manager& m, theory_checker& pc): m(m), pc(pc) {}
bool check(app* jst) override {
if (jst->get_num_args() != 3)
@ -273,46 +276,46 @@ namespace euf {
return result;
}
void register_plugins(proof_checker& pc) override {
void register_plugins(theory_checker& pc) override {
pc.register_plugin(symbol("res"), this);
}
};
proof_checker::proof_checker(ast_manager& m):
theory_checker::theory_checker(ast_manager& m):
m(m) {
add_plugin(alloc(arith::proof_checker, m));
add_plugin(alloc(eq_proof_checker, m));
add_plugin(alloc(res_proof_checker, m, *this));
add_plugin(alloc(q::proof_checker, m));
add_plugin(alloc(smt_proof_checker_plugin, m, symbol("datatype"))); // no-op datatype proof checker
add_plugin(alloc(tseitin::proof_checker, m));
add_plugin(alloc(arith::theory_checker, m));
add_plugin(alloc(eq_theory_checker, m));
add_plugin(alloc(res_checker, m, *this));
add_plugin(alloc(q::theory_checker, m));
add_plugin(alloc(smt_theory_checker_plugin, m, symbol("datatype"))); // no-op datatype proof checker
add_plugin(alloc(tseitin::theory_checker, m));
}
proof_checker::~proof_checker() {
theory_checker::~theory_checker() {
for (auto& [k, v] : m_checked_clauses)
dealloc(v);
}
void proof_checker::add_plugin(proof_checker_plugin* p) {
void theory_checker::add_plugin(theory_checker_plugin* p) {
m_plugins.push_back(p);
p->register_plugins(*this);
}
void proof_checker::register_plugin(symbol const& rule, proof_checker_plugin* p) {
void theory_checker::register_plugin(symbol const& rule, theory_checker_plugin* p) {
m_map.insert(rule, p);
}
bool proof_checker::check(expr* e) {
bool theory_checker::check(expr* e) {
if (!e || !is_app(e))
return false;
if (m_checked_clauses.contains(e))
return true;
app* a = to_app(e);
proof_checker_plugin* p = nullptr;
theory_checker_plugin* p = nullptr;
return m_map.find(a->get_decl()->get_name(), p) && p->check(a);
}
expr_ref_vector proof_checker::clause(expr* e) {
expr_ref_vector theory_checker::clause(expr* e) {
expr_ref_vector* rr;
if (m_checked_clauses.find(e, rr))
return *rr;
@ -322,17 +325,17 @@ namespace euf {
return r;
}
bool proof_checker::vc(expr* e, expr_ref_vector const& clause, expr_ref_vector& v) {
bool theory_checker::vc(expr* e, expr_ref_vector const& clause, expr_ref_vector& v) {
SASSERT(is_app(e));
app* a = to_app(e);
proof_checker_plugin* p = nullptr;
theory_checker_plugin* p = nullptr;
if (m_map.find(a->get_name(), p))
return p->vc(a, clause, v);
IF_VERBOSE(10, verbose_stream() << "there is no proof plugin for " << mk_pp(e, m) << "\n");
return false;
}
bool proof_checker::check(expr_ref_vector const& clause1, expr* e, expr_ref_vector & units) {
bool theory_checker::check(expr_ref_vector const& clause1, expr* e, expr_ref_vector & units) {
if (!check(e))
return false;
units.reset();
@ -358,13 +361,166 @@ namespace euf {
return true;
}
expr_ref_vector smt_proof_checker_plugin::clause(app* jst) {
expr_ref_vector smt_theory_checker_plugin::clause(app* jst) {
expr_ref_vector result(m);
SASSERT(jst->get_name() == m_rule);
for (expr* arg : *jst)
result.push_back(mk_not(m, arg));
return result;
}
smt_proof_checker::smt_proof_checker(ast_manager& m, params_ref const& p):
m(m),
m_params(p),
m_checker(m),
m_sat_solver(m_params, m.limit()),
m_drat(m_sat_solver)
{
m_params.set_bool("drat.check_unsat", true);
m_params.set_bool("euf", false);
m_sat_solver.updt_params(m_params);
m_drat.updt_config();
m_rup = symbol("rup");
sat_params sp(m_params);
m_check_rup = sp.smt_proof_check_rup();
}
void smt_proof_checker::ensure_solver() {
if (!m_solver)
m_solver = mk_smt_solver(m, m_params, symbol());
}
void smt_proof_checker::log_verified(app* proof_hint) {
symbol n = proof_hint->get_name();
if (n == m_last_rule) {
++m_num_last_rules;
return;
}
if (m_num_last_rules > 0)
std::cout << "(verified-" << m_last_rule << "+" << m_num_last_rules << ")\n";
std::cout << "(verified-" << n << ")\n";
m_last_rule = n;
m_num_last_rules = 0;
}
bool smt_proof_checker::check_rup(expr_ref_vector const& clause) {
if (!m_check_rup)
return true;
add_units();
mk_clause(clause);
return m_drat.is_drup(m_clause.size(), m_clause.data(), m_units);
}
bool smt_proof_checker::check_rup(expr* u) {
if (!m_check_rup)
return true;
add_units();
mk_clause(u);
return m_drat.is_drup(m_clause.size(), m_clause.data(), m_units);
}
void smt_proof_checker::infer(expr_ref_vector& clause, app* proof_hint) {
if (is_rup(proof_hint) && check_rup(clause)) {
if (m_check_rup) {
log_verified(proof_hint);
add_clause(clause);
}
return;
}
expr_ref_vector units(m);
if (m_checker.check(clause, proof_hint, units)) {
bool units_are_rup = true;
for (expr* u : units) {
if (!check_rup(u)) {
std::cout << "unit " << mk_bounded_pp(u, m) << " is not rup\n";
units_are_rup = false;
}
}
if (units_are_rup) {
log_verified(proof_hint);
add_clause(clause);
return;
}
}
// extract a simplified verification condition in case proof validation does not work.
// quantifier instantiation can be validated as follows:
// If quantifier instantiation claims that (forall x . phi(x)) => psi using instantiation x -> t
// then check the simplified VC: phi(t) => psi.
// in case psi is the literal instantiation, then the clause is a propositional tautology.
// The VC function is a no-op if the proof hint does not have an associated vc generator.
expr_ref_vector vc(clause);
if (m_checker.vc(proof_hint, clause, vc)) {
log_verified(proof_hint);
add_clause(clause);
return;
}
ensure_solver();
m_solver->push();
for (expr* lit : vc)
m_solver->assert_expr(m.mk_not(lit));
lbool is_sat = m_solver->check_sat();
if (is_sat != l_false) {
std::cout << "did not verify: " << is_sat << " " << clause << "\n";
if (proof_hint)
std::cout << "hint: " << mk_bounded_pp(proof_hint, m, 4) << "\n";
m_solver->display(std::cout);
if (is_sat == l_true) {
model_ref mdl;
m_solver->get_model(mdl);
mdl->evaluate_constants();
std::cout << *mdl << "\n";
}
exit(0);
}
m_solver->pop(1);
std::cout << "(verified-smt";
if (proof_hint) std::cout << "\n" << mk_bounded_pp(proof_hint, m, 4);
for (expr* arg : clause)
std::cout << "\n " << mk_bounded_pp(arg, m);
std::cout << ")\n";
if (is_rup(proof_hint))
diagnose_rup_failure(clause);
add_clause(clause);
}
void smt_proof_checker::diagnose_rup_failure(expr_ref_vector const& clause) {
expr_ref_vector fmls(m), assumptions(m), core(m);
m_solver->get_assertions(fmls);
for (unsigned i = 0; i < fmls.size(); ++i) {
assumptions.push_back(m.mk_fresh_const("a", m.mk_bool_sort()));
fmls[i] = m.mk_implies(assumptions.back(), fmls.get(i));
}
ref<::solver> core_solver = mk_smt_solver(m, m_params, symbol());
// core_solver->assert_expr(fmls);
core_solver->assert_expr(m.mk_not(mk_or(clause)));
lbool ch = core_solver->check_sat(assumptions);
std::cout << "failed to verify\n" << clause << "\n";
if (ch == l_false) {
core_solver->get_unsat_core(core);
std::cout << "core\n";
for (expr* f : core)
std::cout << mk_pp(f, m) << "\n";
}
SASSERT(false);
exit(0);
}
void smt_proof_checker::collect_statistics(statistics& st) const {
if (m_solver)
m_solver->collect_statistics(st);
}
}