mirror of
https://github.com/Z3Prover/z3
synced 2025-04-24 17:45:32 +00:00
fix another bug uncovered by Dunlop, prepare grounds for equality solving within NNFs
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
parent
eaa80d5b02
commit
0b84c60886
13 changed files with 283 additions and 122 deletions
|
@ -23,7 +23,6 @@ Revision History:
|
|||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "tactic/goal_shared_occs.h"
|
||||
#include "ast/pb_decl_plugin.h"
|
||||
|
||||
namespace {
|
||||
class propagate_values_tactic : public tactic {
|
||||
|
@ -117,23 +116,10 @@ class propagate_values_tactic : public tactic {
|
|||
TRACE("shallow_context_simplifier_bug", tout << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n";);
|
||||
if (new_curr != curr) {
|
||||
m_modified = true;
|
||||
//if (has_pb(curr))
|
||||
// IF_VERBOSE(0, verbose_stream() << mk_ismt2_pp(curr, m) << "\n---->\n" << mk_ismt2_pp(new_curr, m) << "\n");
|
||||
}
|
||||
push_result(new_curr, new_pr);
|
||||
}
|
||||
|
||||
bool has_pb(expr* e) {
|
||||
pb_util pb(m);
|
||||
if (pb.is_ge(e)) return true;
|
||||
if (m.is_or(e)) {
|
||||
for (expr* a : *to_app(e)) {
|
||||
if (pb.is_ge(a)) return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void run(goal_ref const & g, goal_ref_buffer & result) {
|
||||
SASSERT(g->is_well_sorted());
|
||||
tactic_report report("propagate-values", *g);
|
||||
|
|
|
@ -16,14 +16,15 @@ Author:
|
|||
Revision History:
|
||||
|
||||
--*/
|
||||
#include "tactic/tactical.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "ast/occurs.h"
|
||||
#include "util/cooperate.h"
|
||||
#include "tactic/goal_shared_occs.h"
|
||||
#include "ast/occurs.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/pb_decl_plugin.h"
|
||||
#include "tactic/goal_shared_occs.h"
|
||||
#include "tactic/tactical.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
|
||||
class solve_eqs_tactic : public tactic {
|
||||
struct imp {
|
||||
|
@ -384,6 +385,20 @@ class solve_eqs_tactic : public tactic {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
void insert_solution(goal const& g, unsigned idx, expr* f, app* var, expr* def, proof* pr) {
|
||||
m_vars.push_back(var);
|
||||
m_candidates.push_back(f);
|
||||
m_candidate_set.mark(f);
|
||||
m_candidate_vars.mark(var);
|
||||
if (m_produce_proofs) {
|
||||
if (!pr)
|
||||
pr = g.pr(idx);
|
||||
else
|
||||
pr = m().mk_modus_ponens(g.pr(idx), pr);
|
||||
}
|
||||
m_subst->insert(var, def, pr, g.dep(idx));
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Start collecting candidates
|
||||
|
@ -408,17 +423,7 @@ class solve_eqs_tactic : public tactic {
|
|||
checkpoint();
|
||||
expr * f = g.form(idx);
|
||||
if (solve(f, var, def, pr)) {
|
||||
m_vars.push_back(var);
|
||||
m_candidates.push_back(f);
|
||||
m_candidate_set.mark(f);
|
||||
m_candidate_vars.mark(var);
|
||||
if (m_produce_proofs) {
|
||||
if (pr == 0)
|
||||
pr = g.pr(idx);
|
||||
else
|
||||
pr = m().mk_modus_ponens(g.pr(idx), pr);
|
||||
}
|
||||
m_subst->insert(var, def, pr, g.dep(idx));
|
||||
insert_solution(g, idx, f, var, def, pr);
|
||||
}
|
||||
m_num_steps++;
|
||||
}
|
||||
|
@ -430,6 +435,163 @@ class solve_eqs_tactic : public tactic {
|
|||
}
|
||||
tout << "\n";);
|
||||
}
|
||||
|
||||
struct nnf_context {
|
||||
bool m_is_and;
|
||||
expr_ref_vector m_args;
|
||||
unsigned m_index;
|
||||
nnf_context(bool is_and, expr_ref_vector const& args, unsigned idx):
|
||||
m_is_and(is_and),
|
||||
m_args(args),
|
||||
m_index(idx)
|
||||
{}
|
||||
};
|
||||
|
||||
bool is_compatible(goal const& g, unsigned idx, vector<nnf_context> const & path, expr* v, expr* eq) {
|
||||
return is_goal_compatible(g, idx, v, eq) && is_path_compatible(path, v, eq);
|
||||
}
|
||||
|
||||
bool is_goal_compatible(goal const& g, unsigned idx, expr* v, expr* eq) {
|
||||
bool all_e = false;
|
||||
for (unsigned j = 0; j < g.size(); ++j) {
|
||||
if (j != idx && !check_eq_compat(g.form(j), v, eq, all_e)) {
|
||||
TRACE("solve_eqs", tout << "occurs goal " << mk_pp(eq, m()) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
//
|
||||
// all_e := all disjunctions contain eq
|
||||
//
|
||||
// or, all_e -> skip if all disjunctions contain eq
|
||||
// or, all_e -> fail if some disjunction contains v but not eq
|
||||
// or, all_e -> all_e := false if some disjunction does not contain v
|
||||
// and, all_e -> all_e
|
||||
//
|
||||
|
||||
bool is_path_compatible(vector<nnf_context> const & path, expr* v, expr* eq) {
|
||||
bool all_e = true;
|
||||
for (unsigned i = path.size(); i-- > 0; ) {
|
||||
auto const& p = path[i];
|
||||
auto const& args = p.m_args;
|
||||
if (p.m_is_and && !all_e) {
|
||||
for (unsigned j = 0; j < args.size(); ++j) {
|
||||
if (j != p.m_index && occurs(v, args[j])) {
|
||||
TRACE("solve_eqs", tout << "occurs and " << mk_pp(eq, m()) << " " << mk_pp(args[j], m()) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (!p.m_is_and) {
|
||||
for (unsigned j = 0; j < args.size(); ++j) {
|
||||
if (j != p.m_index) {
|
||||
if (occurs(v, args[j])) {
|
||||
if (!check_eq_compat(args[j], v, eq, all_e)) {
|
||||
TRACE("solve_eqs", tout << "occurs or " << mk_pp(eq, m()) << " " << mk_pp(args[j], m()) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
all_e = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_eq_compat(expr* f, expr* v, expr* eq, bool& all) {
|
||||
expr_ref_vector args(m());
|
||||
expr* f1 = nullptr;
|
||||
if (!occurs(v, f)) {
|
||||
all = false;
|
||||
return true;
|
||||
}
|
||||
if (m().is_not(f, f1) && m().is_or(f1)) {
|
||||
flatten_and(f, args);
|
||||
for (expr* arg : args) {
|
||||
if (arg == eq) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m().is_or(f)) {
|
||||
flatten_or(f, args);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (expr* arg : args) {
|
||||
if (!check_eq_compat(arg, v, eq, all)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void hoist_nnf(goal const& g, expr* f, vector<nnf_context> & path, unsigned idx, unsigned depth) {
|
||||
if (depth > 4) {
|
||||
return;
|
||||
}
|
||||
app_ref var(m());
|
||||
expr_ref def(m());
|
||||
proof_ref pr(m());
|
||||
expr_ref_vector args(m());
|
||||
expr* f1 = nullptr;
|
||||
|
||||
if (m().is_not(f, f1) && m().is_or(f1)) {
|
||||
flatten_and(f, args);
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
expr* arg = args.get(i), *lhs = nullptr, *rhs = nullptr;
|
||||
if (m().is_eq(arg, lhs, rhs)) {
|
||||
if (trivial_solve1(lhs, rhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) {
|
||||
insert_solution(g, idx, arg, var, def, pr);
|
||||
}
|
||||
else if (trivial_solve1(rhs, lhs, var, def, pr) && is_compatible(g, idx, path, var, arg)) {
|
||||
insert_solution(g, idx, arg, var, def, pr);
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(0,
|
||||
verbose_stream() << "eq not solved " << mk_pp(arg, m()) << "\n";
|
||||
verbose_stream() << is_uninterp_const(lhs) << " " << !m_candidate_vars.is_marked(lhs) << " "
|
||||
<< !occurs(lhs, rhs) << " " << check_occs(lhs) << "\n";);
|
||||
}
|
||||
}
|
||||
else {
|
||||
path.push_back(nnf_context(true, args, i));
|
||||
hoist_nnf(g, arg, path, idx, depth + 1);
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (m().is_or(f)) {
|
||||
flatten_or(f, args);
|
||||
//std::cout << "hoist or " << args.size() << "\n";
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
path.push_back(nnf_context(false, args, i));
|
||||
hoist_nnf(g, args.get(i), path, idx, depth + 1);
|
||||
path.pop_back();
|
||||
}
|
||||
}
|
||||
else {
|
||||
// std::cout << "no hoist " << mk_pp(f, m()) << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
bool collect_hoist(goal const& g) {
|
||||
bool change = false;
|
||||
unsigned size = g.size();
|
||||
vector<nnf_context> path;
|
||||
for (unsigned idx = 0; idx < size; idx++) {
|
||||
checkpoint();
|
||||
hoist_nnf(g, g.form(idx), path, idx, 0);
|
||||
}
|
||||
return change;
|
||||
}
|
||||
|
||||
void sort_vars() {
|
||||
SASSERT(m_candidates.size() == m_vars.size());
|
||||
|
@ -564,6 +726,10 @@ class solve_eqs_tactic : public tactic {
|
|||
++idx;
|
||||
}
|
||||
|
||||
IF_VERBOSE(10,
|
||||
verbose_stream() << "ordered vars: ";
|
||||
for (app* v : m_ordered_vars) verbose_stream() << mk_pp(v, m()) << " ";
|
||||
verbose_stream() << "\n";);
|
||||
TRACE("solve_eqs",
|
||||
tout << "ordered vars:\n";
|
||||
for (app* v : m_ordered_vars) {
|
||||
|
@ -756,6 +922,8 @@ class solve_eqs_tactic : public tactic {
|
|||
while (true) {
|
||||
collect_num_occs(*g);
|
||||
collect(*g);
|
||||
// TBD Disabled until tested more:
|
||||
// collect_hoist(*g);
|
||||
if (m_subst->empty())
|
||||
break;
|
||||
sort_vars();
|
||||
|
@ -773,6 +941,7 @@ class solve_eqs_tactic : public tactic {
|
|||
g->inc_depth();
|
||||
g->add(mc.get());
|
||||
result.push_back(g.get());
|
||||
//IF_VERBOSE(0, g->display(verbose_stream()));
|
||||
TRACE("solve_eqs", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
}
|
||||
|
|
|
@ -24,13 +24,10 @@ Notes:
|
|||
#include "tactic/bv/max_bv_sharing_tactic.h"
|
||||
#include "tactic/bv/bv_size_reduction_tactic.h"
|
||||
#include "tactic/core/ctx_simplify_tactic.h"
|
||||
#include "sat/tactic/sat_tactic.h"
|
||||
#include "ackermannization/ackermannize_bv_tactic.h"
|
||||
#include "smt/tactic/smt_tactic.h"
|
||||
|
||||
tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) {
|
||||
params_ref main_p;
|
||||
main_p.set_bool("elim_and", true);
|
||||
main_p.set_bool("sort_store", true);
|
||||
static tactic * mk_qfaufbv_preamble(ast_manager & m, params_ref const & p) {
|
||||
|
||||
params_ref simp2_p = p;
|
||||
simp2_p.set_bool("som", true);
|
||||
|
@ -39,26 +36,27 @@ tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) {
|
|||
simp2_p.set_bool("local_ctx", true);
|
||||
simp2_p.set_uint("local_ctx_limit", 10000000);
|
||||
|
||||
params_ref ctx_simp_p;
|
||||
ctx_simp_p.set_uint("max_depth", 32);
|
||||
ctx_simp_p.set_uint("max_steps", 5000000);
|
||||
|
||||
params_ref solver_p;
|
||||
solver_p.set_bool("array.simplify", false); // disable array simplifications at old_simplify module
|
||||
return and_then(mk_simplify_tactic(m),
|
||||
mk_propagate_values_tactic(m),
|
||||
mk_solve_eqs_tactic(m),
|
||||
mk_elim_uncnstr_tactic(m),
|
||||
// sound to use? if_no_proofs(if_no_unsat_cores(mk_reduce_args_tactic(m))),
|
||||
if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))),
|
||||
using_params(mk_simplify_tactic(m), simp2_p),
|
||||
mk_max_bv_sharing_tactic(m),
|
||||
if_no_proofs(if_no_unsat_cores(mk_ackermannize_bv_tactic(m, p)))
|
||||
);
|
||||
}
|
||||
|
||||
tactic * preamble_st = and_then(mk_simplify_tactic(m),
|
||||
mk_propagate_values_tactic(m),
|
||||
// using_params(mk_ctx_simplify_tactic(m), ctx_simp_p),
|
||||
mk_solve_eqs_tactic(m),
|
||||
mk_elim_uncnstr_tactic(m),
|
||||
if_no_proofs(if_no_unsat_cores(mk_bv_size_reduction_tactic(m))),
|
||||
using_params(mk_simplify_tactic(m), simp2_p),
|
||||
mk_max_bv_sharing_tactic(m)
|
||||
);
|
||||
tactic * mk_qfaufbv_tactic(ast_manager & m, params_ref const & p) {
|
||||
params_ref main_p;
|
||||
main_p.set_bool("elim_and", true);
|
||||
main_p.set_bool("sort_store", true);
|
||||
|
||||
tactic * st = using_params(and_then(preamble_st,
|
||||
using_params(mk_smt_tactic(m), solver_p)),
|
||||
main_p);
|
||||
tactic * preamble_st = mk_qfaufbv_preamble(m, p);
|
||||
|
||||
tactic * st = using_params(and_then(preamble_st, mk_smt_tactic(m)), main_p);
|
||||
|
||||
st->updt_params(p);
|
||||
return st;
|
||||
|
|
|
@ -39,13 +39,14 @@ struct tactic_report::imp {
|
|||
~imp() {
|
||||
m_watch.stop();
|
||||
double end_memory = static_cast<double>(memory::get_allocation_size())/static_cast<double>(1024*1024);
|
||||
verbose_stream() << "(" << m_id
|
||||
<< " :num-exprs " << m_goal.num_exprs()
|
||||
<< " :num-asts " << m_goal.m().get_num_asts()
|
||||
<< " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds()
|
||||
<< " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory
|
||||
<< " :after-memory " << std::fixed << std::setprecision(2) << end_memory
|
||||
<< ")" << std::endl;
|
||||
IF_VERBOSE(0,
|
||||
verbose_stream() << "(" << m_id
|
||||
<< " :num-exprs " << m_goal.num_exprs()
|
||||
<< " :num-asts " << m_goal.m().get_num_asts()
|
||||
<< " :time " << std::fixed << std::setprecision(2) << m_watch.get_seconds()
|
||||
<< " :before-memory " << std::fixed << std::setprecision(2) << m_start_memory
|
||||
<< " :after-memory " << std::fixed << std::setprecision(2) << end_memory
|
||||
<< ")" << std::endl);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue