3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-24 01:25:31 +00:00

wip - dependent expr simpliifer

- simplify iterator over current indices
- add more simplifiers used by asserted_formulas
- improve diagnostics printing
This commit is contained in:
Nikolaj Bjorner 2022-11-30 13:41:40 +07:00
parent bec3acd146
commit b084821a0c
25 changed files with 553 additions and 158 deletions

View file

@ -125,9 +125,28 @@ void macro_replacer::insert(app* head, expr* def, expr_dependency* dep) {
m_map.insert(f, std::tuple(head, def, dep));
}
void macro_replacer::operator()(expr* t, expr_ref& result, expr_dependency_ref& dep) {
macro_replacer_rw exp(m, *this, dep);
void macro_replacer::operator()(expr* t, expr_dependency* dep_in, expr_ref& result, expr_dependency_ref& dep_out) {
expr_dependency_ref _dep_in(dep_in, m);
macro_replacer_rw exp(m, *this, dep_out);
exp(t, result);
if (!dep_in)
return;
// update dependencies if needed
m_dep_exprs.reset();
m.linearize(dep_in, m_dep_exprs);
unsigned sz = m_trail.size();
for (expr*& d : m_dep_exprs) {
exp(d, result);
if (result != d) {
d = result.get();
m_trail.push_back(result);
}
}
if (sz != m_trail.size()) {
dep_in = m.mk_join(m_dep_exprs.size(), m_dep_exprs.data());
m_trail.shrink(sz);
}
dep_out = m.mk_join(dep_in, dep_out);
}
bool macro_replacer::has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& dep) {

View file

@ -26,6 +26,7 @@ class macro_replacer {
ast_manager& m;
ast_ref_vector m_trail;
expr_dependency_ref_vector m_deps;
ptr_vector<expr> m_dep_exprs;
obj_map<func_decl, std::tuple<app*, expr*, expr_dependency*>> m_map;
struct macro_replacer_cfg;
struct macro_replacer_rw;
@ -35,8 +36,8 @@ public:
macro_replacer(ast_manager& m): m(m), m_trail(m), m_deps(m) {}
void insert(app* head, expr* def, expr_dependency* dep);
void operator()(expr* t, expr_ref& result, expr_dependency_ref& dep);
void operator()(expr* t, expr_ref & result) { expr_dependency_ref dep(m); (*this)(t, result, dep); }
void operator()(expr* t, expr_dependency* d, expr_ref& result, expr_dependency_ref& dep);
void operator()(expr* t, expr_ref & result) { expr_dependency_ref dep(m); (*this)(t, nullptr, result, dep); }
void operator()(expr_ref & t) { expr_ref s(t, m); (*this)(s, t); }
bool has_macro(func_decl* f, app_ref& head, expr_ref& def, expr_dependency_ref& d);

View file

@ -17,4 +17,5 @@ z3_add_component(simplifiers
euf
rewriter
bit_blaster
normal_forms
)

View file

@ -0,0 +1,44 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bit2int.h
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/bit2int.h"
class bit2int_simplifier : public dependent_expr_simplifier {
bit2int m_rewriter;
public:
bit2int_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_rewriter(m) {
}
char const* name() const override { return "bit2int"; }
void reduce() override {
expr_ref r(m);
proof_ref pr(m);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
if (!has_quantifiers(d.fml()))
continue;
m_rewriter(d.fml(), r, pr);
m_fmls.update(idx, dependent_expr(m, r, d.dep()));
}
}
};

View file

@ -0,0 +1,43 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
bv_elim.h
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/bv_elim.h"
namespace bv {
class elim_simplifier : public dependent_expr_simplifier {
bv_elim_rw m_rewriter;
public:
elim_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_rewriter(m) {
}
char const* name() const override { return "bv-elim"; }
void reduce() override {
expr_ref r(m);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
if (!has_quantifiers(d.fml()))
continue;
m_rewriter(d.fml(), r);
m_fmls.update(idx, dependent_expr(m, r, d.dep()));
}
}
};
}

View file

@ -18,6 +18,7 @@ Author:
#pragma once
#include "ast/ast.h"
#include "ast/ast_pp.h"
#include "ast/ast_translation.h"
class dependent_expr {
@ -88,4 +89,20 @@ public:
std::tuple<expr*, expr_dependency*> operator()() const {
return { m_fml, m_dep };
}
std::ostream& display(std::ostream& out) const {
return out << mk_pp(m_fml, m);
if (m_dep) {
out << "\n <- ";
ptr_vector<expr> deps;
m.linearize(m_dep, deps);
for (expr* arg : deps)
out << mk_pp(arg, m) << " ";
}
return out;
}
};
inline std::ostream& operator<<(std::ostream& out, dependent_expr const& d) {
return d.display(out);
}

View file

@ -24,7 +24,7 @@ unsigned dependent_expr_state::num_exprs() {
}
void dependent_expr_state::freeze(func_decl* f) {
if (m_frozen.is_marked(f))
if (m_frozen.is_marked(f) || !is_uninterp(f))
return;
m_frozen_trail.push_back(f);
m_frozen.mark(f, true);
@ -107,3 +107,13 @@ void dependent_expr_state::freeze_suffix() {
freeze_terms(d.fml(), true, visited);
}
}
bool dependent_expr_state::has_quantifiers() {
if (m_has_quantifiers != l_undef)
return m_has_quantifiers == l_true;
bool found = false;
for (unsigned i = qhead(); i < qtail(); ++i)
found |= ::has_quantifiers((*this)[i].fml());
m_has_quantifiers = found ? l_true : l_false;
return m_has_quantifiers == l_true;
}

View file

@ -44,6 +44,7 @@ class dependent_expr_state {
unsigned m_qhead = 0;
bool m_suffix_frozen = false;
bool m_recfun_frozen = false;
lbool m_has_quantifiers = l_undef;
ast_mark m_frozen;
func_decl_ref_vector m_frozen_trail;
void freeze_prefix();
@ -81,7 +82,7 @@ public:
}
void pop(unsigned n) { m_trail.pop_scope(n); }
void advance_qhead() { freeze_prefix(); m_suffix_frozen = false; m_qhead = qtail(); }
void advance_qhead() { freeze_prefix(); m_suffix_frozen = false; m_has_quantifiers = l_undef; m_qhead = qtail(); }
unsigned num_exprs();
/**
@ -90,8 +91,16 @@ public:
bool frozen(func_decl* f) const { return m_frozen.is_marked(f); }
bool frozen(expr* f) const { return is_app(f) && m_frozen.is_marked(to_app(f)->get_decl()); }
void freeze_suffix();
virtual std::ostream& display(std::ostream& out) const { return out; }
bool has_quantifiers();
};
inline std::ostream& operator<<(std::ostream& out, dependent_expr_state& st) {
return st.display(out);
}
/**
Shared interface of simplifiers.
*/
@ -105,6 +114,26 @@ protected:
unsigned qhead() const { return m_fmls.qhead(); }
unsigned qtail() const { return m_fmls.qtail(); }
struct iterator {
dependent_expr_simplifier& s;
unsigned m_index = 0;
bool at_end = false;
unsigned index() const { return at_end ? s.qtail() : m_index; }
iterator(dependent_expr_simplifier& s, unsigned i) : s(s), m_index(i), at_end(i == s.qtail()) {}
bool operator==(iterator const& other) const { return index() == other.index(); }
bool operator!=(iterator const& other) const { return !(*this == other); }
iterator& operator++() { if (!s.m.inc() || s.m_fmls.inconsistent()) at_end = true; else ++m_index; return *this; }
unsigned operator*() const { return m_index; }
};
struct index_set {
dependent_expr_simplifier& s;
iterator begin() { return iterator(s, s.qhead()); }
iterator end() { return iterator(s, s.qtail()); }
index_set(dependent_expr_simplifier& s) : s(s) {}
};
index_set indices() { return index_set(*this); }
public:
dependent_expr_simplifier(ast_manager& m, dependent_expr_state& s) : m(m), m_fmls(s), m_trail(s.m_trail) {}

View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
distribute_forall.h
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/distribute_forall.h"
class distribute_forall_simplifier : public dependent_expr_simplifier {
distribute_forall m_dist;
public:
distribute_forall_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_dist(m) {
}
char const* name() const override { return "distribute-forall"; }
void reduce() override {
if (!m_fmls.has_quantifiers())
return;
expr_ref r(m);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
if (!has_quantifiers(d.fml()))
continue;
m_dist(d.fml(), r);
m_fmls.update(idx, dependent_expr(m, r, d.dep()));
}
}
};

View file

@ -0,0 +1,45 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
elim_bounds.h
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/elim_bounds.h"
class elim_bounds_simplifier : public dependent_expr_simplifier {
elim_bounds_rw m_rewriter;
public:
elim_bounds_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_rewriter(m) {
}
char const* name() const override { return "cheap-fourier-motzkin"; }
void reduce() override {
if (!m_fmls.has_quantifiers())
return;
expr_ref r(m);
for (unsigned idx : indices()) {
auto const& d = m_fmls[idx];
if (!has_quantifiers(d.fml()))
continue;
m_rewriter(d.fml(), r);
m_fmls.update(idx, dependent_expr(m, r, d.dep()));
}
}
};

View file

@ -129,6 +129,38 @@ bool eliminate_predicates::can_be_macro_head(expr* _head, unsigned num_bound) {
return true;
}
/**
* a quasi macro head is of the form
* f(x,x) where x is the only bound variable
* f(x,y,x+y+3,1) where x, y are the only bound variables
*/
bool eliminate_predicates::can_be_quasi_macro_head(expr* head, unsigned num_bound) {
if (!is_app(_head))
return false;
app* head = to_app(_head);
func_decl* f = head->get_decl();
if (m_fmls.frozen(f))
return false;
if (m_is_macro.is_marked(f))
return false;
if (f->is_associative())
return false;
uint_set indices;
for (expr* arg : *head) {
if (!is_var(arg))
return continue;
unsigned idx = to_var(arg)->get_idx();
if (indices.contains(idx))
return continue;
if (idx >= num_bound)
return false;
indices.insert(idx);
}
return indices.size() == num_bound;
}
expr_ref eliminate_predicates::bind_free_variables_in_def(clause& cl, app* head, expr* def) {
unsigned num_bound = cl.m_bound.size();
if (head->get_num_args() == num_bound)
@ -365,7 +397,6 @@ void eliminate_predicates::try_find_macro(clause& cl) {
// (= (+ (f x) s) t)
// becomes (= (f x) (- t s))
//
// TBD:
// (= (+ (* -1 (f x)) x) t)
// becomes (= (f x) (- (- t s)))
@ -473,10 +504,13 @@ void eliminate_predicates::try_find_macro(clause& cl) {
// becomes (= (f x) (- t s (k x))
// add (>= (k x) 0)
// why is this a real improvement?
//
//
//
// To review: quasi-macros
// (= (f x y (+ x y)) s), where x y are all bound variables.
// then ...?
// (= (f x y (+ x y)) s), where x y are all bound variables.
// then replace (f x y z) by (if (= z (+ x y)) s (f' x y))
//
}
@ -501,21 +535,18 @@ void eliminate_predicates::reduce_definitions() {
for (auto const& [k, v] : m_macros)
macro_expander.insert(v->m_head, v->m_def, v->m_dep);
for (unsigned i = qhead(); i < qtail(); ++i) {
for (unsigned i : indices()) {
auto [f, d] = m_fmls[i]();
expr_ref fml(f, m), new_fml(m);
expr_dependency_ref dep(m);
expr_dependency_ref dep(d, m);
while (true) {
macro_expander(fml, new_fml, dep);
macro_expander(fml, dep, new_fml, dep);
if (new_fml == fml)
break;
rewrite(new_fml);
fml = new_fml;
}
if (fml != f) {
dep = m.mk_join(d, dep);
m_fmls.update(i, dependent_expr(m, fml, dep));
}
m_fmls.update(i, dependent_expr(m, fml, dep));
}
reset();
init_clauses();
@ -772,7 +803,7 @@ void eliminate_predicates::init_clauses() {
m_fmls.freeze_suffix();
for (unsigned i = qhead(); i < qtail(); ++i) {
for (unsigned i : indices()) {
clause* cl = init_clause(i);
add_use_list(*cl);
m_clauses.push_back(cl);
@ -816,6 +847,8 @@ void eliminate_predicates::reset() {
void eliminate_predicates::reduce() {
if (!m_fmls.has_quantifiers())
return;
reset();
init_clauses();
find_definitions();

View file

@ -109,6 +109,7 @@ private:
void insert_macro(app* head, expr* def, expr_dependency* dep);
expr_ref bind_free_variables_in_def(clause& cl, app* head, expr* def);
bool can_be_macro_head(expr* head, unsigned num_bound);
bool can_be_quasi_macro_head(expr* head, unsigned num_bound);
bool is_macro_safe(expr* e);
void try_find_macro(clause& cl);
@ -145,4 +146,4 @@ public:
inline std::ostream& operator<<(std::ostream& out, eliminate_predicates::clause const& c) {
return c.display(out);
}
}

View file

@ -68,8 +68,9 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st
auto [f, dep1] = st[i]();
expr_ref g(m);
expr_dependency_ref dep2(m);
mrp(f, g, dep2);
st.update(i, dependent_expr(m, g, m.mk_join(dep1, dep2)));
mrp(f, dep1, g, dep2);
CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n");
st.update(i, dependent_expr(m, g, dep2));
}
continue;
}
@ -77,10 +78,28 @@ void model_reconstruction_trail::replay(unsigned qhead, dependent_expr_state& st
rp->set_substitution(t->m_subst.get());
// rigid entries:
// apply substitution to added in case of rigid model convertions
ptr_vector<expr> dep_exprs;
expr_ref_vector trail(m);
for (unsigned i = qhead; i < st.qtail(); ++i) {
auto [f, dep1] = st[i]();
auto [g, dep2] = rp->replace_with_dep(f);
if (dep1) {
dep_exprs.reset();
trail.reset();
m.linearize(dep1, dep_exprs);
for (auto*& d : dep_exprs) {
auto [h, dep3] = rp->replace_with_dep(d);
if (h != d) {
trail.push_back(h);
d = h;
dep2 = m.mk_join(dep2, dep3);
}
}
if (!trail.empty())
dep1 = m.mk_join(dep_exprs.size(), dep_exprs.data());
}
dependent_expr d(m, g, m.mk_join(dep1, dep2));
CTRACE("simplifier", f != g, tout << "updated " << mk_pp(g, m) << "\n");
add_vars(d, free_vars);
st.update(i, d);
}
@ -121,3 +140,19 @@ void model_reconstruction_trail::append(generic_model_converter& mc) {
m_trail_stack.push(value_trail(m_trail_index));
append(mc, m_trail_index);
}
std::ostream& model_reconstruction_trail::display(std::ostream& out) const {
for (auto* t : m_trail) {
if (!t->m_active)
continue;
else if (t->is_hide())
out << "hide " << t->m_decl->get_name() << "\n";
else if (t->is_def())
out << t->m_decl->get_name() << " <- " << mk_pp(t->m_def, m) << "\n";
else {
for (auto const& [v, def] : t->m_subst->sub())
out << mk_pp(v, m) << " <- " << mk_pp(def, m) << "\n";
}
}
return out;
}

View file

@ -138,5 +138,7 @@ public:
* Append new updates to model converter, update m_trail_index in the process.
*/
void append(generic_model_converter& mc);
std::ostream& display(std::ostream& out) const;
};

View file

@ -83,13 +83,13 @@ void propagate_values::reduce() {
};
unsigned rw = m_stats.m_num_rewrites + 1;
for (unsigned r = 0; r < m_max_rounds && rw != m_stats.m_num_rewrites; ++r) {
for (unsigned r = 0; r < m_max_rounds && m.inc() && rw != m_stats.m_num_rewrites; ++r) {
rw = m_stats.m_num_rewrites;
init_sub();
for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i)
for (unsigned i = qhead(); i < qtail() && m.inc() && !m_fmls.inconsistent(); ++i)
process_fml(i);
init_sub();
for (unsigned i = qtail(); i-- > qhead() && !m_fmls.inconsistent();)
for (unsigned i = qtail(); i-- > qhead() && m.inc() && !m_fmls.inconsistent();)
process_fml(i);
if (subst.empty())
break;

View file

@ -0,0 +1,46 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
pull_nested_quantifiers.h
Abstract:
pull nested quantifiers
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/normal_forms/pull_quant.h"
class pull_nested_quantifiers_simplifier : public dependent_expr_simplifier {
pull_nested_quant m_pull;
public:
pull_nested_quantifiers_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls),
m_pull(m) {
}
char const* name() const override { return "pull-nested-quantifiers"; }
void reduce() override {
if (!m_fmls.has_quantifiers())
return;
expr_ref new_curr(m);
proof_ref new_pr(m);
for (unsigned idx : indices()) {
auto d = m_fmls[idx];
m_pull(d.fml(), new_curr, new_pr);
m_fmls.update(idx, dependent_expr(m, new_curr, d.dep()));
}
}
};

View file

@ -0,0 +1,44 @@
/*++
Copyright (c) 2022 Microsoft Corporation
Module Name:
refine_inj_axiom.h
Abstract:
refine injectivity axiom
Author:
Nikolaj Bjorner (nbjorner) 2022-11-24
--*/
#pragma once
#include "ast/simplifiers/dependent_expr_state.h"
#include "ast/rewriter/inj_axiom.h"
class refine_inj_axiom_simplifier : public dependent_expr_simplifier {
public:
refine_inj_axiom_simplifier(ast_manager& m, params_ref const& p, dependent_expr_state& fmls):
dependent_expr_simplifier(m, fmls) {
}
char const* name() const override { return "refine-injectivity"; }
void reduce() override {
if (!m_fmls.has_quantifiers())
return;
expr_ref r(m);
for (unsigned idx : indices()) {
expr* f = m_fmls[idx].fml();
if (is_quantifier(f) && simplify_inj_axiom(m, to_quantifier(f), r))
m_fmls.update(idx, dependent_expr(m, r, m_fmls[idx].dep()));
}
}
};

View file

@ -40,16 +40,14 @@ public:
m_num_steps = 0;
expr_ref new_curr(m);
proof_ref new_pr(m);
for (unsigned idx = qhead(); idx < qtail(); idx++) {
if (m_fmls.inconsistent())
break;
for (unsigned idx : indices()) {
auto d = m_fmls[idx];
m_rewriter(d.fml(), new_curr, new_pr);
m_num_steps += m_rewriter.get_num_steps();
m_fmls.update(idx, dependent_expr(m, new_curr, d.dep()));
}
}
void collect_statistics(statistics& st) const override { st.update("simplifier", m_num_steps); }
void collect_statistics(statistics& st) const override { st.update("simplifier-steps", m_num_steps); }
void reset_statistics() override { m_num_steps = 0; }
void updt_params(params_ref const& p) override { m_params.append(p); m_rewriter.updt_params(m_params); }
void collect_param_descrs(param_descrs& r) override { th_rewriter::get_param_descrs(r); }

View file

@ -46,7 +46,8 @@ class seq_simplifier : public dependent_expr_simplifier {
<< " :after-memory " << std::fixed << std::setprecision(2) << end_memory
<< ")" << "\n";
s.collect_statistics(st);
verbose_stream() << st);
if (st.size() > 0)
st.display_smt2(verbose_stream()));
}
};
@ -63,14 +64,17 @@ public:
}
void reduce() override {
TRACE("simplifier", tout << m_fmls << "\n");
for (auto* s : m_simplifiers) {
if (m_fmls.inconsistent())
break;
if (!m.inc())
break;
s->reset_statistics();
collect_stats _cs(*s);
s->reduce();
m_fmls.flatten_suffix();
TRACE("simplifier", tout << s->name() << "\n" << m_fmls << "\n");
}
}

View file

@ -34,7 +34,7 @@ namespace euf {
void solve_eqs::get_eqs(dep_eq_vector& eqs) {
for (extract_eq* ex : m_extract_plugins)
for (unsigned i = qhead(); i < qtail(); ++i)
for (unsigned i : indices())
ex->get_eqs(m_fmls[i], eqs);
}
@ -99,6 +99,9 @@ namespace euf {
auto const& [orig, v, t, d] = eq;
SASSERT(j == var2id(v));
bool is_safe = true;
if (m_fmls.frozen(v))
continue;
unsigned todo_sz = todo.size();
// determine if substitution is safe.
@ -187,7 +190,7 @@ namespace euf {
scoped_ptr<expr_replacer> rp = mk_default_expr_replacer(m, false);
rp->set_substitution(m_subst.get());
for (unsigned i = qhead(); i < qtail() && !m_fmls.inconsistent(); ++i) {
for (unsigned i : indices()) {
auto [f, d] = m_fmls[i]();
auto [new_f, new_dep] = rp->replace_with_dep(f);
m_rewriter(new_f);

View file

@ -32,6 +32,10 @@ namespace euf {
struct stats {
unsigned m_num_steps = 0;
unsigned m_num_elim_vars = 0;
void reset() {
m_num_steps = 0;
m_num_elim_vars = 0;
}
};
struct config {
@ -78,5 +82,7 @@ namespace euf {
void collect_statistics(statistics& st) const override;
void reset_statistics() override { m_stats.reset(); }
};
}