mirror of
https://github.com/Z3Prover/z3
synced 2025-04-12 12:08:18 +00:00
comments on proof_utils
This commit is contained in:
parent
063b6e9ea5
commit
b269e6b35b
|
@ -12,12 +12,19 @@ Copyright (c) 2015 Microsoft Corporation
|
||||||
class reduce_hypotheses {
|
class reduce_hypotheses {
|
||||||
typedef obj_hashtable<expr> expr_set;
|
typedef obj_hashtable<expr> expr_set;
|
||||||
ast_manager& m;
|
ast_manager& m;
|
||||||
|
// reference for any expression created by the tranformation
|
||||||
expr_ref_vector m_refs;
|
expr_ref_vector m_refs;
|
||||||
|
// currently computed result
|
||||||
obj_map<proof,proof*> m_cache;
|
obj_map<proof,proof*> m_cache;
|
||||||
|
// map conclusions to closed proofs that derive them
|
||||||
obj_map<expr, proof*> m_units;
|
obj_map<expr, proof*> m_units;
|
||||||
|
// currently active units
|
||||||
ptr_vector<expr> m_units_trail;
|
ptr_vector<expr> m_units_trail;
|
||||||
|
// size of m_units_trail at the last push
|
||||||
unsigned_vector m_limits;
|
unsigned_vector m_limits;
|
||||||
|
// map from proofs to active hypotheses
|
||||||
obj_map<proof, expr_set*> m_hypmap;
|
obj_map<proof, expr_set*> m_hypmap;
|
||||||
|
// refernce train for hypotheses sets
|
||||||
ptr_vector<expr_set> m_hyprefs;
|
ptr_vector<expr_set> m_hyprefs;
|
||||||
ptr_vector<expr> m_literals;
|
ptr_vector<expr> m_literals;
|
||||||
|
|
||||||
|
@ -151,19 +158,33 @@ public:
|
||||||
p = result;
|
p = result;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
//SASSERT (p.get () == result);
|
||||||
switch(p->get_decl_kind()) {
|
switch(p->get_decl_kind()) {
|
||||||
case PR_HYPOTHESIS:
|
case PR_HYPOTHESIS:
|
||||||
|
// replace result by m_units[m.get_fact (p)] if defined
|
||||||
|
// AG: This is the main step. Replace a hypothesis by a derivation of its consequence
|
||||||
if (!m_units.find(m.get_fact(p), result)) {
|
if (!m_units.find(m.get_fact(p), result)) {
|
||||||
|
// restore ther result back to p
|
||||||
result = p.get();
|
result = p.get();
|
||||||
}
|
}
|
||||||
|
// compute hypothesis of the result
|
||||||
|
// not clear what 'result' is at this point.
|
||||||
|
// probably the proof at the top of the call
|
||||||
|
// XXX not clear why this is re-computed each time
|
||||||
|
// XXX moreover, m_units are guaranteed to be closed!
|
||||||
|
// XXX so no hypotheses are needed for them
|
||||||
add_hypotheses(result);
|
add_hypotheses(result);
|
||||||
break;
|
break;
|
||||||
case PR_LEMMA: {
|
case PR_LEMMA: {
|
||||||
SASSERT(m.get_num_parents(p) == 1);
|
SASSERT(m.get_num_parents(p) == 1);
|
||||||
tmp = m.get_parent(p, 0);
|
tmp = m.get_parent(p, 0);
|
||||||
|
// eliminate hypothesis recursively in the proof of the lemma
|
||||||
elim(tmp);
|
elim(tmp);
|
||||||
expr_set* hyps = m_hypmap.find(tmp);
|
expr_set* hyps = m_hypmap.find(tmp);
|
||||||
expr_set* new_hyps = 0;
|
expr_set* new_hyps = 0;
|
||||||
|
// XXX if the proof is correct, the hypotheses of the tmp
|
||||||
|
// XXX should be exactly those of the consequence of the lemma
|
||||||
|
// XXX but if this code actually eliminates hypotheses, the set might be a subset
|
||||||
if (hyps) {
|
if (hyps) {
|
||||||
new_hyps = alloc(expr_set, *hyps);
|
new_hyps = alloc(expr_set, *hyps);
|
||||||
}
|
}
|
||||||
|
@ -178,13 +199,19 @@ public:
|
||||||
get_literals(fact);
|
get_literals(fact);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// go over all the literals in the consequence of the lemma
|
||||||
for (unsigned i = 0; i < m_literals.size(); ++i) {
|
for (unsigned i = 0; i < m_literals.size(); ++i) {
|
||||||
expr* e = m_literals[i];
|
expr* e = m_literals[i];
|
||||||
|
// if the literal is not in hypothesis, skip it
|
||||||
if (!in_hypotheses(e, hyps)) {
|
if (!in_hypotheses(e, hyps)) {
|
||||||
m_literals[i] = m_literals.back();
|
m_literals[i] = m_literals.back();
|
||||||
m_literals.pop_back();
|
m_literals.pop_back();
|
||||||
--i;
|
--i;
|
||||||
}
|
}
|
||||||
|
// if the literal is in hypothesis remove it because
|
||||||
|
// it is not in hypothesis set of the lemma
|
||||||
|
// XXX but we assume that lemmas have empty hypothesis set.
|
||||||
|
// XXX eventually every element of new_hyps must be removed!
|
||||||
else {
|
else {
|
||||||
SASSERT(new_hyps);
|
SASSERT(new_hyps);
|
||||||
expr_ref not_e = complement_lit(e);
|
expr_ref not_e = complement_lit(e);
|
||||||
|
@ -192,10 +219,13 @@ public:
|
||||||
new_hyps->remove(not_e);
|
new_hyps->remove(not_e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// killed all hypotheses, so can stop at the lemma since
|
||||||
|
// we have a closed pf of false
|
||||||
if (m_literals.empty()) {
|
if (m_literals.empty()) {
|
||||||
result = tmp;
|
result = tmp;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
// create a new lemma, but might be re-creating existing one
|
||||||
expr_ref clause(m);
|
expr_ref clause(m);
|
||||||
if (m_literals.size() == 1) {
|
if (m_literals.size() == 1) {
|
||||||
clause = m_literals[0];
|
clause = m_literals[0];
|
||||||
|
@ -212,6 +242,7 @@ public:
|
||||||
new_hyps = 0;
|
new_hyps = 0;
|
||||||
}
|
}
|
||||||
m_hypmap.insert(result, new_hyps);
|
m_hypmap.insert(result, new_hyps);
|
||||||
|
// might push 0 into m_hyprefs. No reason for that
|
||||||
m_hyprefs.push_back(new_hyps);
|
m_hyprefs.push_back(new_hyps);
|
||||||
TRACE("proof_utils",
|
TRACE("proof_utils",
|
||||||
tout << "New lemma: " << mk_pp(m.get_fact(p), m)
|
tout << "New lemma: " << mk_pp(m.get_fact(p), m)
|
||||||
|
@ -229,19 +260,27 @@ public:
|
||||||
}
|
}
|
||||||
case PR_UNIT_RESOLUTION: {
|
case PR_UNIT_RESOLUTION: {
|
||||||
proof_ref_vector parents(m);
|
proof_ref_vector parents(m);
|
||||||
|
// get the clause being resolved with
|
||||||
parents.push_back(m.get_parent(p, 0));
|
parents.push_back(m.get_parent(p, 0));
|
||||||
|
// save state
|
||||||
push();
|
push();
|
||||||
bool found_false = false;
|
bool found_false = false;
|
||||||
|
// for every derivation of a unit literal
|
||||||
for (unsigned i = 1; i < m.get_num_parents(p); ++i) {
|
for (unsigned i = 1; i < m.get_num_parents(p); ++i) {
|
||||||
|
// see if it derives false
|
||||||
tmp = m.get_parent(p, i);
|
tmp = m.get_parent(p, i);
|
||||||
elim(tmp);
|
elim(tmp);
|
||||||
if (m.is_false(m.get_fact(tmp))) {
|
if (m.is_false(m.get_fact(tmp))) {
|
||||||
|
// if derived false, the whole pf is false and we can bail out
|
||||||
result = tmp;
|
result = tmp;
|
||||||
found_false = true;
|
found_false = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// -- otherwise, the fact has not changed. nothing to simplify
|
||||||
SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i)));
|
SASSERT(m.get_fact(tmp) == m.get_fact(m.get_parent(p, i)));
|
||||||
parents.push_back(tmp);
|
parents.push_back(tmp);
|
||||||
|
// remember that we have this derivation while we have not poped the trail
|
||||||
|
// but only if the proof is closed (i.e., a real unit)
|
||||||
if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) {
|
if (is_closed(tmp) && !m_units.contains(m.get_fact(tmp))) {
|
||||||
m_units.insert(m.get_fact(tmp), tmp);
|
m_units.insert(m.get_fact(tmp), tmp);
|
||||||
m_units_trail.push_back(m.get_fact(tmp));
|
m_units_trail.push_back(m.get_fact(tmp));
|
||||||
|
@ -251,10 +290,15 @@ public:
|
||||||
pop();
|
pop();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
// look at the clause being resolved with
|
||||||
tmp = m.get_parent(p, 0);
|
tmp = m.get_parent(p, 0);
|
||||||
|
// remember its fact
|
||||||
expr* old_clause = m.get_fact(tmp);
|
expr* old_clause = m.get_fact(tmp);
|
||||||
|
// attempt to reduce its fact
|
||||||
elim(tmp);
|
elim(tmp);
|
||||||
|
// update parents
|
||||||
parents[0] = tmp;
|
parents[0] = tmp;
|
||||||
|
// if the new fact is false, bail out
|
||||||
expr* clause = m.get_fact(tmp);
|
expr* clause = m.get_fact(tmp);
|
||||||
if (m.is_false(clause)) {
|
if (m.is_false(clause)) {
|
||||||
m_refs.push_back(tmp);
|
m_refs.push_back(tmp);
|
||||||
|
@ -264,8 +308,10 @@ public:
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
// case where clause is a literal in the old clause.
|
// case where clause is a literal in the old clause.
|
||||||
|
// i.e., reduce multi-literal clause to a unit
|
||||||
//
|
//
|
||||||
if (is_literal_in_clause(clause, old_clause)) {
|
if (is_literal_in_clause(clause, old_clause)) {
|
||||||
|
// if the resulting literal was resolved, get a pf of false and bail out
|
||||||
bool found = false;
|
bool found = false;
|
||||||
for (unsigned i = 1; !found && i < parents.size(); ++i) {
|
for (unsigned i = 1; !found && i < parents.size(); ++i) {
|
||||||
if (m.is_complement(clause, m.get_fact(parents[i].get()))) {
|
if (m.is_complement(clause, m.get_fact(parents[i].get()))) {
|
||||||
|
@ -277,6 +323,7 @@ public:
|
||||||
found = true;
|
found = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// else if the resulting literal is not resolved, it is the new consequence
|
||||||
if (!found) {
|
if (!found) {
|
||||||
result = parents[0].get();
|
result = parents[0].get();
|
||||||
}
|
}
|
||||||
|
@ -508,6 +555,11 @@ static void permute_unit_resolution(expr_ref_vector& refs, obj_map<proof,proof*>
|
||||||
SASSERT(params[0].is_symbol());
|
SASSERT(params[0].is_symbol());
|
||||||
family_id tid = m.mk_family_id(params[0].get_symbol());
|
family_id tid = m.mk_family_id(params[0].get_symbol());
|
||||||
SASSERT(tid != null_family_id);
|
SASSERT(tid != null_family_id);
|
||||||
|
// AG: This can break a theory lemma. In particular, for Farkas lemmas the coefficients
|
||||||
|
// AG: for the literals propagated from the unit resolution are missing.
|
||||||
|
// AG: Why is this a good thing to do?
|
||||||
|
// AG: This can lead to merging of the units with other terms in interpolation,
|
||||||
|
// AG: but without farkas coefficients this does not make sense
|
||||||
prNew = m.mk_th_lemma(tid, m.get_fact(pr),
|
prNew = m.mk_th_lemma(tid, m.get_fact(pr),
|
||||||
premises.size(), premises.c_ptr(), num_params-1, params+1);
|
premises.size(), premises.c_ptr(), num_params-1, params+1);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue