3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-07-19 10:52:02 +00:00

Cleanup iuc_proof

This commit is contained in:
Arie Gurfinkel 2018-05-16 10:09:26 -07:00
parent ebf6b18821
commit abe67705d3
4 changed files with 211 additions and 260 deletions

View file

@ -6,229 +6,182 @@
namespace spacer { namespace spacer {
/* /*
* ==================================== * ====================================
* init * init
* ==================================== * ====================================
*/ */
iuc_proof::iuc_proof(ast_manager& m, proof* pr, expr_set& b_conjuncts) : m(m), m_pr(pr,m) iuc_proof::iuc_proof(ast_manager& m, proof* pr, expr_set& core_lits) :
{ m(m), m_pr(pr,m) {
// init A-marks and B-marks // init A-marks and B-marks
collect_symbols_b(b_conjuncts); collect_core_symbols(core_lits);
compute_marks(b_conjuncts); compute_marks(core_lits);
} }
proof* iuc_proof::get() /*
{ * ====================================
return m_pr.get(); * methods for computing symbol colors
} * ====================================
*/
class collect_pure_proc {
func_decl_set& m_symbs;
public:
collect_pure_proc(func_decl_set& s):m_symbs(s) {}
/* void operator()(app* a) {
* ==================================== if (a->get_family_id() == null_family_id) {
* methods for computing symbol colors m_symbs.insert(a->get_decl());
* ====================================
*/
class collect_pure_proc {
func_decl_set& m_symbs;
public:
collect_pure_proc(func_decl_set& s):m_symbs(s) {}
void operator()(app* a) {
if (a->get_family_id() == null_family_id) {
m_symbs.insert(a->get_decl());
}
}
void operator()(var*) {}
void operator()(quantifier*) {}
};
void iuc_proof::collect_symbols_b(expr_set& b_conjuncts)
{
expr_mark visited;
collect_pure_proc proc(m_symbols_b);
for (expr_set::iterator it = b_conjuncts.begin(); it != b_conjuncts.end(); ++it)
{
for_each_expr(proc, visited, *it);
} }
} }
void operator()(var*) {}
void operator()(quantifier*) {}
};
class is_pure_expr_proc { void iuc_proof::collect_core_symbols(expr_set& core_lits)
func_decl_set const& m_symbs; {
array_util m_au; expr_mark visited;
public: collect_pure_proc proc(m_core_symbols);
struct non_pure {}; for (expr_set::iterator it = core_lits.begin(); it != core_lits.end(); ++it) {
for_each_expr(proc, visited, *it);
}
}
is_pure_expr_proc(func_decl_set const& s, ast_manager& m): class is_pure_expr_proc {
func_decl_set const& m_symbs;
array_util m_au;
public:
struct non_pure {};
is_pure_expr_proc(func_decl_set const& s, ast_manager& m):
m_symbs(s), m_symbs(s),
m_au (m) m_au (m)
{} {}
void operator()(app* a) { void operator()(app* a) {
if (a->get_family_id() == null_family_id) { if (a->get_family_id() == null_family_id) {
if (!m_symbs.contains(a->get_decl())) { if (!m_symbs.contains(a->get_decl())) {
throw non_pure();
}
}
else if (a->get_family_id () == m_au.get_family_id () &&
a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) {
throw non_pure(); throw non_pure();
} }
} }
void operator()(var*) {} else if (a->get_family_id () == m_au.get_family_id () &&
void operator()(quantifier*) {} a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) {
}; throw non_pure();
// requires that m_symbols_b has already been computed, which is done during initialization.
bool iuc_proof::only_contains_symbols_b(expr* expr) const
{
is_pure_expr_proc proc(m_symbols_b, m);
try {
for_each_expr(proc, expr);
} }
catch (is_pure_expr_proc::non_pure)
{
return false;
}
return true;
} }
void operator()(var*) {}
void operator()(quantifier*) {}
};
/* bool iuc_proof::is_core_pure(expr* e) const
* ==================================== {
* methods for computing which premises is_pure_expr_proc proc(m_core_symbols, m);
* have been used to derive the conclusions try {
* ==================================== for_each_expr(proc, e);
*/ }
catch (is_pure_expr_proc::non_pure)
{return false;}
void iuc_proof::compute_marks(expr_set& b_conjuncts) return true;
}
void iuc_proof::compute_marks(expr_set& core_lits)
{
proof_post_order it(m_pr, m);
while (it.hasNext())
{ {
proof_post_order it(m_pr, m); proof* cur = it.next();
while (it.hasNext()) if (m.get_num_parents(cur) == 0)
{ {
proof* currentNode = it.next(); switch(cur->get_decl_kind())
if (m.get_num_parents(currentNode) == 0)
{ {
switch(currentNode->get_decl_kind()) case PR_ASSERTED:
{ if (core_lits.contains(m.get_fact(cur)))
m_b_mark.mark(cur, true);
case PR_ASSERTED: // currentNode is an axiom else
{ m_a_mark.mark(cur, true);
if (b_conjuncts.contains(m.get_fact(currentNode))) break;
{ case PR_HYPOTHESIS:
m_b_mark.mark(currentNode, true); m_h_mark.mark(cur, true);
} break;
else default:
{ break;
m_a_mark.mark(currentNode, true);
}
break;
}
// currentNode is a hypothesis:
case PR_HYPOTHESIS:
{
m_h_mark.mark(currentNode, true);
break;
}
default:
{
break;
}
}
}
else
{
// collect from parents whether derivation of current node contains A-axioms, B-axioms and hypothesis
bool need_to_mark_a = false;
bool need_to_mark_b = false;
bool need_to_mark_h = false;
for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i)
{
SASSERT(m.is_proof(currentNode->get_arg(i)));
proof* premise = to_app(currentNode->get_arg(i));
need_to_mark_a = need_to_mark_a || m_a_mark.is_marked(premise);
need_to_mark_b = need_to_mark_b || m_b_mark.is_marked(premise);
need_to_mark_h = need_to_mark_h || m_h_mark.is_marked(premise);
}
// if current node is application of lemma, we know that all hypothesis are removed
if(currentNode->get_decl_kind() == PR_LEMMA)
{
need_to_mark_h = false;
}
// save results
m_a_mark.mark(currentNode, need_to_mark_a);
m_b_mark.mark(currentNode, need_to_mark_b);
m_h_mark.mark(currentNode, need_to_mark_h);
} }
} }
} else
bool iuc_proof::is_a_marked(proof* p)
{
return m_a_mark.is_marked(p);
}
bool iuc_proof::is_b_marked(proof* p)
{
return m_b_mark.is_marked(p);
}
bool iuc_proof::is_h_marked(proof* p)
{
return m_h_mark.is_marked(p);
}
/*
* ====================================
* methods for dot printing
* ====================================
*/
void iuc_proof::pp_dot()
{
pp_proof_dot(m, m_pr, this);
}
/*
* ====================================
* statistics
* ====================================
*/
void iuc_proof::print_farkas_stats()
{
unsigned farkas_counter = 0;
unsigned farkas_counter2 = 0;
proof_post_order it3(m_pr, m);
while (it3.hasNext())
{ {
proof* currentNode = it3.next(); // collect from parents whether derivation of current node
// contains A-axioms, B-axioms and hypothesis
bool need_to_mark_a = false;
bool need_to_mark_b = false;
bool need_to_mark_h = false;
// if node is theory lemma for (unsigned i = 0; i < m.get_num_parents(cur); ++i)
if (is_farkas_lemma(m, currentNode))
{ {
farkas_counter++; SASSERT(m.is_proof(cur->get_arg(i)));
proof* premise = to_app(cur->get_arg(i));
// check whether farkas lemma is to be interpolated (could potentially miss farkas lemmas, which are interpolated, because we potentially don't want to use the lowest cut) need_to_mark_a |= m_a_mark.is_marked(premise);
bool has_blue_nonred_parent = false; need_to_mark_b |= m_b_mark.is_marked(premise);
for (unsigned i = 0; i < m.get_num_parents(currentNode); ++i) need_to_mark_h |= m_h_mark.is_marked(premise);
{
proof* premise = to_app(currentNode->get_arg(i));
if (!is_a_marked(premise) && is_b_marked(premise))
{
has_blue_nonred_parent = true;
break;
}
}
if (has_blue_nonred_parent && is_a_marked(currentNode))
{
SASSERT(is_b_marked(currentNode));
farkas_counter2++;
}
} }
}
verbose_stream() << "\nThis proof contains " << farkas_counter << " Farkas lemmas. " << farkas_counter2 << " Farkas lemmas participate in the lowest cut\n"; // if current node is application of a lemma, then all
// active hypotheses are removed
if(cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false;
// save results
m_a_mark.mark(cur, need_to_mark_a);
m_b_mark.mark(cur, need_to_mark_b);
m_h_mark.mark(cur, need_to_mark_h);
}
} }
} }
/*
* ====================================
* statistics
* ====================================
*/
// debug method
void iuc_proof::dump_farkas_stats()
{
unsigned fl_total = 0;
unsigned fl_lowcut = 0;
proof_post_order it(m_pr, m);
while (it.hasNext())
{
proof* cur = it.next();
// if node is theory lemma
if (is_farkas_lemma(m, cur))
{
fl_total++;
// check whether farkas lemma is to be interpolated (could
// potentially miss farkas lemmas, which are interpolated,
// because we potentially don't want to use the lowest
// cut)
bool has_blue_nonred_parent = false;
for (unsigned i = 0; i < m.get_num_parents(cur); ++i) {
proof* premise = to_app(cur->get_arg(i));
if (!is_a_marked(premise) && is_b_marked(premise)) {
has_blue_nonred_parent = true;
break;
}
}
if (has_blue_nonred_parent && is_a_marked(cur))
{
SASSERT(is_b_marked(cur));
fl_lowcut++;
}
}
}
IF_VERBOSE(1, verbose_stream()
<< "\n total farkas lemmas " << fl_total
<< " farkas lemmas in lowest cut " << fl_lowcut << "\n";);
}
}

View file

@ -4,62 +4,58 @@
#include "ast/ast.h" #include "ast/ast.h"
namespace spacer { namespace spacer {
typedef obj_hashtable<expr> expr_set; typedef obj_hashtable<expr> expr_set;
typedef obj_hashtable<func_decl> func_decl_set; typedef obj_hashtable<func_decl> func_decl_set;
/* /*
* an iuc_proof is a proof together with information of the coloring of the axioms. * An iuc_proof is a proof together with information of the
*/ * coloring of the axioms.
class iuc_proof */
{ class iuc_proof
public: {
public:
iuc_proof(ast_manager& m, proof* pr, expr_set& b_conjuncts);
proof* get();
/* // Constructs an iuc_proof given an ast_manager, a proof, and a set of
* returns whether symbol contains only symbols which occur in B. // literals core_lits that might be included in the unsat core
*/ iuc_proof(ast_manager& m, proof* pr, expr_set& core_lits);
bool only_contains_symbols_b(expr* expr) const;
bool is_a_marked(proof* p); // returns the proof object
bool is_b_marked(proof* p); proof* get() {return m_pr.get();}
bool is_h_marked(proof* p);
bool is_b_pure (proof *p) // returns true if all uninterpreted symbols of e are from the core literals
{return !is_h_marked (p) && only_contains_symbols_b (m.get_fact (p));} // requires that m_core_symbols has already been computed
bool is_core_pure(expr* e) const;
/* bool is_a_marked(proof* p) {return m_a_mark.is_marked(p);}
* print dot-representation to file proof.dot bool is_b_marked(proof* p) {return m_b_mark.is_marked(p);}
* use Graphviz's dot with option -Tpdf to convert dot-representation into pdf bool is_h_marked(proof* p) {return m_h_mark.is_marked(p);}
*/
void pp_dot(); bool is_b_pure (proof *p) {
return !is_h_marked (p) && is_core_pure(m.get_fact (p));
void print_farkas_stats(); }
private:
ast_manager& m; // debug method
proof_ref m_pr; void dump_farkas_stats();
private:
ast_mark m_a_mark; ast_manager& m;
ast_mark m_b_mark; proof_ref m_pr;
ast_mark m_h_mark;
ast_mark m_a_mark;
func_decl_set m_symbols_b; // symbols, which occur in any b-asserted formula ast_mark m_b_mark;
ast_mark m_h_mark;
// symbols that occur in any literals in the core
func_decl_set m_core_symbols;
// collect symbols occuring in B (the core)
void collect_core_symbols(expr_set& core_lits);
// compute for each formula of the proof whether it derives
// from A or from B
void compute_marks(expr_set& core_lits);
};
// collect symbols occuring in B
void collect_symbols_b(expr_set& b_conjuncts);
// compute for each formula of the proof whether it derives from A and whether it derives from B
void compute_marks(expr_set& b_conjuncts);
void pp_dot_to_stream(std::ofstream& dotstream);
std::string escape_dot(const std::string &s);
void post_process_dot(std::string dot_filepath, std::ofstream& dotstream);
};
} }
#endif /* IUC_PROOF_H_ */ #endif /* IUC_PROOF_H_ */

View file

@ -289,7 +289,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core)
{ {
iuc_proof iuc_before(m, res.get(), B); iuc_proof iuc_before(m, res.get(), B);
verbose_stream() << "\nStats before transformation:"; verbose_stream() << "\nStats before transformation:";
iuc_before.print_farkas_stats(); iuc_before.dump_farkas_stats();
} }
proof_utils::reduce_hypotheses(res); proof_utils::reduce_hypotheses(res);
@ -299,7 +299,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core)
{ {
iuc_proof iuc_after(m, res.get(), B); iuc_proof iuc_after(m, res.get(), B);
verbose_stream() << "Stats after transformation:"; verbose_stream() << "Stats after transformation:";
iuc_after.print_farkas_stats(); iuc_after.dump_farkas_stats();
} }
} }
else // -- new hypothesis reducer else // -- new hypothesis reducer
@ -309,7 +309,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core)
{ {
iuc_proof iuc_before(m, res.get(), B); iuc_proof iuc_before(m, res.get(), B);
verbose_stream() << "\nStats before transformation:"; verbose_stream() << "\nStats before transformation:";
iuc_before.print_farkas_stats(); iuc_before.dump_farkas_stats();
} }
theory_axiom_reducer ta_reducer(m); theory_axiom_reducer ta_reducer(m);
@ -324,7 +324,7 @@ void iuc_solver::get_iuc(expr_ref_vector &core)
{ {
iuc_proof iuc_after(m, res.get(), B); iuc_proof iuc_after(m, res.get(), B);
verbose_stream() << "Stats after transformation:"; verbose_stream() << "Stats after transformation:";
iuc_after.print_farkas_stats(); iuc_after.dump_farkas_stats();
} }
} }

View file

@ -331,11 +331,13 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
{ {
SASSERT(!m_learner.m_pr.is_a_marked(premise)); SASSERT(!m_learner.m_pr.is_a_marked(premise));
if (m_learner.m_pr.only_contains_symbols_b(m_learner.m.get_fact(premise)) && !m_learner.m_pr.is_h_marked(premise)) if (m_learner.m_pr.is_b_pure(premise))
{ {
rational coefficient; rational coefficient;
VERIFY(params[i].is_rational(coefficient)); VERIFY(params[i].is_rational(coefficient));
linear_combination.push_back(std::make_pair(to_app(m_learner.m.get_fact(premise)), abs(coefficient))); linear_combination.push_back
(std::make_pair(to_app(m_learner.m.get_fact(premise)),
abs(coefficient)));
} }
else else
{ {
@ -529,14 +531,14 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
for (unsigned l=0; l < n; ++l) { for (unsigned l=0; l < n; ++l) {
for (unsigned j=0; j < matrix.num_cols(); ++j) { for (unsigned j=0; j < matrix.num_cols(); ++j) {
expr* s_jn = bounded_vectors[j][l].get(); expr* s_jn = bounded_vectors[j][l].get();
expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m); expr_ref lb(util.mk_le(util.mk_int(0), s_jn), m);
expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m); expr_ref ub(util.mk_le(s_jn, util.mk_int(1)), m);
s->assert_expr(lb); s->assert_expr(lb);
s->assert_expr(ub); s->assert_expr(ub);
} }
} }
// assert: forall i,j: a_ij = sum_k w_ik * s_jk // assert: forall i,j: a_ij = sum_k w_ik * s_jk
for (unsigned i=0; i < matrix.num_rows(); ++i) for (unsigned i=0; i < matrix.num_rows(); ++i)
{ {
@ -563,13 +565,13 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
if (res == lbool::l_true) { if (res == lbool::l_true) {
model_ref model; model_ref model;
s->get_model(model); s->get_model(model);
for (unsigned k=0; k < n; ++k) { for (unsigned k=0; k < n; ++k) {
ptr_vector<app> literals; ptr_vector<app> literals;
vector<rational> coefficients; vector<rational> coefficients;
for (unsigned j=0; j < matrix.num_cols(); ++j) { for (unsigned j=0; j < matrix.num_cols(); ++j) {
expr_ref evaluation(m); expr_ref evaluation(m);
model.get()->eval(bounded_vectors[j][k].get(), evaluation, false); model.get()->eval(bounded_vectors[j][k].get(), evaluation, false);
if (!util.is_zero(evaluation)) { if (!util.is_zero(evaluation)) {
literals.push_back(ordered_basis[j]); literals.push_back(ordered_basis[j]);
@ -579,7 +581,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
SASSERT(!literals.empty()); // since then previous outer loop would have found solution already SASSERT(!literals.empty()); // since then previous outer loop would have found solution already
expr_ref linear_combination(m); expr_ref linear_combination(m);
compute_linear_combination(coefficients, literals, linear_combination); compute_linear_combination(coefficients, literals, linear_combination);
m_learner.add_lemma_to_core(linear_combination); m_learner.add_lemma_to_core(linear_combination);
} }
return; return;
@ -622,7 +624,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
// add an edge from current to each leaf of that subproof // add an edge from current to each leaf of that subproof
// add the leaves to todo // add the leaves to todo
advance_to_lowest_partial_cut(current, todo); advance_to_lowest_partial_cut(current, todo);
m_visited.mark(current, true); m_visited.mark(current, true);
} }
@ -630,7 +632,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
m_learner.set_closed(step, true); m_learner.set_closed(step, true);
} }
void unsat_core_plugin_min_cut::advance_to_lowest_partial_cut(proof* step, ptr_vector<proof>& todo) void unsat_core_plugin_min_cut::advance_to_lowest_partial_cut(proof* step, ptr_vector<proof>& todo)
{ {
bool is_sink = true; bool is_sink = true;
@ -709,7 +711,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
void unsat_core_plugin_min_cut::add_edge(proof* i, proof* j) void unsat_core_plugin_min_cut::add_edge(proof* i, proof* j)
{ {
SASSERT(i != nullptr || j != nullptr); SASSERT(i != nullptr || j != nullptr);
unsigned node_i; unsigned node_i;
unsigned node_j; unsigned node_j;
if (i == nullptr) if (i == nullptr)
@ -777,7 +779,7 @@ void unsat_core_plugin_farkas_lemma::compute_linear_combination(const vector<rat
{ {
m_min_cut.add_edge(node_i, node_j, 1); m_min_cut.add_edge(node_i, node_j, 1);
} }
if (i == nullptr) if (i == nullptr)
{ {
m_connected_to_s.mark(j, true); m_connected_to_s.mark(j, true);