mirror of
https://github.com/Z3Prover/z3
synced 2025-08-05 10:50:24 +00:00
overhaul of proof format for new solver
This commit overhauls the proof format (in development) for the new core. NOTE: this functionality is work in progress with a long way to go. It is shielded by the sat.euf option, which is off by default and in pre-release state. It is too early to fuzz or use it. It is pushed into master to shed light on road-map for certifying inferences of sat.euf. It retires the ad-hoc extension of DRUP used by the SAT solver. Instead it relies on SMT with ad-hoc extensions for proof terms. It adds the following commands (consumed by proof_cmds.cpp): - assume - for input clauses - learn - when a clause is learned (or redundant clause is added) - del - when a clause is deleted. The commands take a list of expressions of type Bool and the last argument can optionally be of type Proof. When the last argument is of type Proof it is provided as a hint to justify the learned clause. Proof hints can be checked using a self-contained proof checker. The sat/smt/euf_proof_checker.h class provides a plugin dispatcher for checkers. It is instantiated with a checker for arithmetic lemmas, so far for Farkas proofs. Use example: ``` (set-option :sat.euf true) (set-option :tactic.default_tactic smt) (set-option :sat.smt.proof f.proof) (declare-const x Int) (declare-const y Int) (declare-const z Int) (declare-const u Int) (assert (< x y)) (assert (< y z)) (assert (< z x)) (check-sat) ``` Run z3 on a file with above content. Then run z3 on f.proof ``` (verified-smt) (verified-smt) (verified-smt) (verified-farkas) (verified-smt) ```
This commit is contained in:
parent
9922c766b9
commit
e2f4fc2307
37 changed files with 809 additions and 1078 deletions
|
@ -52,8 +52,7 @@ namespace sat {
|
|||
void drat::updt_config() {
|
||||
m_check_unsat = s.get_config().m_drat_check_unsat;
|
||||
m_check_sat = s.get_config().m_drat_check_sat;
|
||||
m_trim = s.get_config().m_drup_trim;
|
||||
m_check = m_check_unsat || m_check_sat || m_trim;
|
||||
m_check = m_check_unsat || m_check_sat;
|
||||
m_activity = s.get_config().m_drat_activity;
|
||||
}
|
||||
|
||||
|
@ -130,14 +129,6 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
buffer[len++] = '0';
|
||||
if (st.get_hint()) {
|
||||
buffer[len++] = ' ';
|
||||
buffer[len++] = 'p';
|
||||
buffer[len++] = ' ';
|
||||
auto* ps = st.get_hint();
|
||||
for (auto ch : ps->to_string())
|
||||
buffer[len++] = ch;
|
||||
}
|
||||
buffer[len++] = '\n';
|
||||
m_out->write(buffer, len);
|
||||
}
|
||||
|
@ -210,8 +201,6 @@ namespace sat {
|
|||
if (st.is_redundant() && st.is_sat())
|
||||
verify(1, &l);
|
||||
|
||||
if (m_trim)
|
||||
m_proof.push_back({mk_clause(1, &l, st.is_redundant()), st});
|
||||
|
||||
if (st.is_deleted())
|
||||
return;
|
||||
|
@ -230,8 +219,7 @@ namespace sat {
|
|||
|
||||
IF_VERBOSE(20, trace(verbose_stream(), 2, lits, st););
|
||||
if (st.is_deleted()) {
|
||||
if (m_trim)
|
||||
m_proof.push_back({mk_clause(2, lits, true), st});
|
||||
;
|
||||
}
|
||||
else {
|
||||
if (st.is_redundant() && st.is_sat())
|
||||
|
@ -658,7 +646,7 @@ namespace sat {
|
|||
verify(0, nullptr);
|
||||
SASSERT(m_inconsistent);
|
||||
}
|
||||
if (m_print_clause) m_print_clause->on_clause(0, nullptr, status::redundant());
|
||||
if (m_clause_eh) m_clause_eh->on_clause(0, nullptr, status::redundant());
|
||||
}
|
||||
void drat::add(literal l, bool learned) {
|
||||
++m_stats.m_num_add;
|
||||
|
@ -666,7 +654,8 @@ namespace sat {
|
|||
if (m_out) dump(1, &l, st);
|
||||
if (m_bout) bdump(1, &l, st);
|
||||
if (m_check) append(l, st);
|
||||
if (m_print_clause) m_print_clause->on_clause(1, &l, st);
|
||||
TRACE("sat", tout << "add " << m_clause_eh << "\n");
|
||||
if (m_clause_eh) m_clause_eh->on_clause(1, &l, st);
|
||||
}
|
||||
void drat::add(literal l1, literal l2, status st) {
|
||||
if (st.is_deleted())
|
||||
|
@ -677,7 +666,7 @@ namespace sat {
|
|||
if (m_out) dump(2, ls, st);
|
||||
if (m_bout) bdump(2, ls, st);
|
||||
if (m_check) append(l1, l2, st);
|
||||
if (m_print_clause) m_print_clause->on_clause(2, ls, st);
|
||||
if (m_clause_eh) m_clause_eh->on_clause(2, ls, st);
|
||||
}
|
||||
void drat::add(clause& c, status st) {
|
||||
if (st.is_deleted())
|
||||
|
@ -687,7 +676,7 @@ namespace sat {
|
|||
if (m_out) dump(c.size(), c.begin(), st);
|
||||
if (m_bout) bdump(c.size(), c.begin(), st);
|
||||
if (m_check) append(mk_clause(c), st);
|
||||
if (m_print_clause) m_print_clause->on_clause(c.size(), c.begin(), st);
|
||||
if (m_clause_eh) m_clause_eh->on_clause(c.size(), c.begin(), st);
|
||||
}
|
||||
|
||||
void drat::add(literal_vector const& lits, status st) {
|
||||
|
@ -709,8 +698,8 @@ namespace sat {
|
|||
if (m_out)
|
||||
dump(sz, lits, st);
|
||||
|
||||
if (m_print_clause)
|
||||
m_print_clause->on_clause(sz, lits, st);
|
||||
if (m_clause_eh)
|
||||
m_clause_eh->on_clause(sz, lits, st);
|
||||
}
|
||||
|
||||
void drat::add(literal_vector const& c) {
|
||||
|
@ -730,8 +719,8 @@ namespace sat {
|
|||
}
|
||||
}
|
||||
}
|
||||
if (m_print_clause)
|
||||
m_print_clause->on_clause(c.size(), c.data(), status::redundant());
|
||||
if (m_clause_eh)
|
||||
m_clause_eh->on_clause(c.size(), c.data(), status::redundant());
|
||||
}
|
||||
|
||||
void drat::del(literal l) {
|
||||
|
@ -739,6 +728,7 @@ namespace sat {
|
|||
if (m_out) dump(1, &l, status::deleted());
|
||||
if (m_bout) bdump(1, &l, status::deleted());
|
||||
if (m_check) append(l, status::deleted());
|
||||
if (m_clause_eh) m_clause_eh->on_clause(1, &l, status::deleted());
|
||||
}
|
||||
|
||||
void drat::del(literal l1, literal l2) {
|
||||
|
@ -747,6 +737,7 @@ namespace sat {
|
|||
if (m_out) dump(2, ls, status::deleted());
|
||||
if (m_bout) bdump(2, ls, status::deleted());
|
||||
if (m_check) append(l1, l2, status::deleted());
|
||||
if (m_clause_eh) m_clause_eh->on_clause(2, ls, status::deleted());
|
||||
}
|
||||
|
||||
void drat::del(clause& c) {
|
||||
|
@ -764,7 +755,8 @@ namespace sat {
|
|||
++m_stats.m_num_del;
|
||||
if (m_out) dump(c.size(), c.begin(), status::deleted());
|
||||
if (m_bout) bdump(c.size(), c.begin(), status::deleted());
|
||||
if (m_check) append(mk_clause(c), status::deleted());
|
||||
if (m_check) append(mk_clause(c), status::deleted());
|
||||
if (m_clause_eh) m_clause_eh->on_clause(c.size(), c.begin(), status::deleted());
|
||||
}
|
||||
|
||||
clause& drat::mk_clause(clause& c) {
|
||||
|
@ -780,23 +772,9 @@ namespace sat {
|
|||
if (m_out) dump(c.size(), c.begin(), status::deleted());
|
||||
if (m_bout) bdump(c.size(), c.begin(), status::deleted());
|
||||
if (m_check) append(mk_clause(c.size(), c.begin(), true), status::deleted());
|
||||
if (m_clause_eh) m_clause_eh->on_clause(c.size(), c.begin(), status::deleted());
|
||||
}
|
||||
|
||||
//
|
||||
// placeholder for trim function.
|
||||
// 1. trail contains justification for the empty clause.
|
||||
// 2. backward pass to prune.
|
||||
//
|
||||
svector<std::pair<clause&, status>> drat::trim() {
|
||||
SASSERT(m_units.empty());
|
||||
svector<std::pair<clause&, status>> proof;
|
||||
for (auto const& [c, st] : m_proof)
|
||||
if (!st.is_deleted())
|
||||
proof.push_back({c,st});
|
||||
return proof;
|
||||
}
|
||||
|
||||
|
||||
void drat::check_model(model const& m) {
|
||||
}
|
||||
|
||||
|
@ -828,196 +806,4 @@ namespace sat {
|
|||
return out;
|
||||
}
|
||||
|
||||
|
||||
std::string proof_hint::to_string() const {
|
||||
std::ostringstream ous;
|
||||
switch (m_ty) {
|
||||
case hint_type::null_h:
|
||||
return std::string();
|
||||
case hint_type::farkas_h:
|
||||
ous << "farkas ";
|
||||
break;
|
||||
case hint_type::bound_h:
|
||||
ous << "bound ";
|
||||
break;
|
||||
case hint_type::implied_eq_h:
|
||||
ous << "implied_eq ";
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
for (auto const& [q, l] : m_literals)
|
||||
ous << rational(q) << " * " << l << " ";
|
||||
for (auto const& [a, b] : m_eqs)
|
||||
ous << " = " << a << " " << b << " ";
|
||||
for (auto const& [a, b] : m_diseqs)
|
||||
ous << " != " << a << " " << b << " ";
|
||||
return ous.str();
|
||||
}
|
||||
|
||||
void proof_hint::from_string(char const* s) {
|
||||
proof_hint& h = *this;
|
||||
h.reset();
|
||||
h.m_ty = hint_type::null_h;
|
||||
if (!s)
|
||||
return;
|
||||
auto ws = [&]() {
|
||||
while (*s == ' ' || *s == '\n' || *s == '\t')
|
||||
++s;
|
||||
};
|
||||
|
||||
auto parse_type = [&]() {
|
||||
if (0 == strncmp(s, "farkas", 6)) {
|
||||
h.m_ty = hint_type::farkas_h;
|
||||
s += 6;
|
||||
return true;
|
||||
}
|
||||
if (0 == strncmp(s, "bound", 5)) {
|
||||
h.m_ty = hint_type::bound_h;
|
||||
s += 5;
|
||||
return true;
|
||||
}
|
||||
if (0 == strncmp(s, "implied_eq", 10)) {
|
||||
h.m_ty = hint_type::implied_eq_h;
|
||||
s += 10;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
sbuffer<char> buff;
|
||||
auto parse_coeff = [&]() {
|
||||
buff.reset();
|
||||
while (*s && *s != ' ') {
|
||||
buff.push_back(*s);
|
||||
++s;
|
||||
}
|
||||
buff.push_back(0);
|
||||
return rational(buff.data());
|
||||
};
|
||||
|
||||
auto parse_literal = [&]() {
|
||||
rational r = parse_coeff();
|
||||
if (!r.is_int())
|
||||
return sat::null_literal;
|
||||
if (r < 0)
|
||||
return sat::literal((-r).get_unsigned(), true);
|
||||
return sat::literal(r.get_unsigned(), false);
|
||||
};
|
||||
auto parse_coeff_literal = [&]() {
|
||||
if (*s == '=') {
|
||||
++s;
|
||||
ws();
|
||||
unsigned a = parse_coeff().get_unsigned();
|
||||
ws();
|
||||
unsigned b = parse_coeff().get_unsigned();
|
||||
h.m_eqs.push_back(std::make_pair(a, b));
|
||||
return true;
|
||||
}
|
||||
if (*s == '!' && *(s + 1) == '=') {
|
||||
s += 2;
|
||||
ws();
|
||||
unsigned a = parse_coeff().get_unsigned();
|
||||
ws();
|
||||
unsigned b = parse_coeff().get_unsigned();
|
||||
h.m_diseqs.push_back(std::make_pair(a, b));
|
||||
return true;
|
||||
}
|
||||
rational coeff = parse_coeff();
|
||||
ws();
|
||||
if (*s == '*') {
|
||||
++s;
|
||||
ws();
|
||||
sat::literal lit = parse_literal();
|
||||
h.m_literals.push_back(std::make_pair(coeff, lit));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
ws();
|
||||
if (!parse_type())
|
||||
return;
|
||||
ws();
|
||||
while (*s) {
|
||||
if (!parse_coeff_literal())
|
||||
return;
|
||||
ws();
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
// debugging code
|
||||
bool drat::is_clause(clause& c, literal l1, literal l2, literal l3, drat::status st1, drat::status st2) {
|
||||
//if (st1 != st2) return false;
|
||||
if (c.size() != 3) return false;
|
||||
if (l1 == c[0]) {
|
||||
if (l2 == c[1] && l3 == c[2]) return true;
|
||||
if (l2 == c[2] && l3 == c[1]) return true;
|
||||
}
|
||||
if (l2 == c[0]) {
|
||||
if (l1 == c[1] && l3 == c[2]) return true;
|
||||
if (l1 == c[2] && l3 == c[1]) return true;
|
||||
}
|
||||
if (l3 == c[0]) {
|
||||
if (l1 == c[1] && l2 == c[2]) return true;
|
||||
if (l1 == c[2] && l2 == c[1]) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
#if 0
|
||||
if (!m_inconsistent) {
|
||||
literal_vector lits(n, c);
|
||||
IF_VERBOSE(0, verbose_stream() << "not drup " << lits << "\n");
|
||||
for (unsigned v = 0; v < m_assignment.size(); ++v) {
|
||||
lbool val = m_assignment[v];
|
||||
if (val != l_undef) {
|
||||
IF_VERBOSE(0, verbose_stream() << literal(v, false) << " |-> " << val << "\n");
|
||||
}
|
||||
}
|
||||
for (clause* cp : s.m_clauses) {
|
||||
clause& cl = *cp;
|
||||
bool found = false;
|
||||
for (literal l : cl) {
|
||||
if (m_assignment[l.var()] != (l.sign() ? l_true : l_false)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
IF_VERBOSE(0, verbose_stream() << "Clause is false under assignment: " << cl << "\n");
|
||||
}
|
||||
}
|
||||
for (clause* cp : s.m_learned) {
|
||||
clause& cl = *cp;
|
||||
bool found = false;
|
||||
for (literal l : cl) {
|
||||
if (m_assignment[l.var()] != (l.sign() ? l_true : l_false)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
IF_VERBOSE(0, verbose_stream() << "Clause is false under assignment: " << cl << "\n");
|
||||
}
|
||||
}
|
||||
svector<sat::solver::bin_clause> bin;
|
||||
s.collect_bin_clauses(bin, true);
|
||||
for (auto& b : bin) {
|
||||
bool found = false;
|
||||
if (m_assignment[b.first.var()] != (b.first.sign() ? l_true : l_false)) found = true;
|
||||
if (m_assignment[b.second.var()] != (b.second.sign() ? l_true : l_false)) found = true;
|
||||
if (!found) {
|
||||
IF_VERBOSE(0, verbose_stream() << "Bin clause is false under assignment: " << b.first << " " << b.second << "\n");
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(0, s.display(verbose_stream()));
|
||||
exit(0);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue