3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-08-20 10:10:21 +00:00

dt updates

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2024-10-15 09:36:07 -07:00
parent b551f22aca
commit a4275dfb15
6 changed files with 147 additions and 20 deletions

View file

@ -341,8 +341,10 @@ namespace datatype {
ast_manager & get_manager() const { return m; } ast_manager & get_manager() const { return m; }
// sort * mk_datatype_sort(symbol const& name, unsigned n, sort* const* params); // sort * mk_datatype_sort(symbol const& name, unsigned n, sort* const* params);
bool is_datatype(sort const* s) const { return is_sort_of(s, fid(), DATATYPE_SORT); } bool is_datatype(sort const* s) const { return is_sort_of(s, fid(), DATATYPE_SORT); }
bool is_datatype(expr* e) const { return is_datatype(e->get_sort()); }
bool is_enum_sort(sort* s); bool is_enum_sort(sort* s);
bool is_recursive(sort * ty); bool is_recursive(sort * ty);
bool is_recursive(expr* e) { return is_recursive(e->get_sort()); }
bool is_recursive_nested(sort * ty); bool is_recursive_nested(sort * ty);
bool is_constructor(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_CONSTRUCTOR); } bool is_constructor(func_decl * f) const { return is_decl_of(f, fid(), OP_DT_CONSTRUCTOR); }
bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); } bool is_recognizer(func_decl * f) const { return is_recognizer0(f) || is_is(f); }

View file

@ -69,6 +69,16 @@ namespace sls {
verbose_stream() << "did not find plugin for " << fid << "\n"; verbose_stream() << "did not find plugin for " << fid << "\n";
} }
scoped_ptr<euf::egraph>& context::ensure_euf() {
auto fid = user_sort_family_id;
auto p = m_plugins.get(fid, nullptr);
if (!p) {
p = alloc(euf_plugin, *this);
register_plugin(p);
}
return dynamic_cast<euf_plugin*>(p)->egraph();
}
void context::ensure_plugin(expr* e) { void context::ensure_plugin(expr* e) {
auto fid = get_fid(e); auto fid = get_fid(e);
ensure_plugin(fid); ensure_plugin(fid);

View file

@ -20,6 +20,7 @@ Author:
#include "util/sat_sls.h" #include "util/sat_sls.h"
#include "util/statistics.h" #include "util/statistics.h"
#include "ast/ast.h" #include "ast/ast.h"
#include "ast/euf/euf_egraph.h"
#include "model/model.h" #include "model/model.h"
#include "util/scoped_ptr_vector.h" #include "util/scoped_ptr_vector.h"
#include "util/obj_hashtable.h" #include "util/obj_hashtable.h"
@ -195,6 +196,7 @@ namespace sls {
ast_manager& get_manager() { return m; } ast_manager& get_manager() { return m; }
std::ostream& display(std::ostream& out) const; std::ostream& display(std::ostream& out) const;
std::ostream& display_all(std::ostream& out) const { return s.display(out); } std::ostream& display_all(std::ostream& out) const { return s.display(out); }
scoped_ptr<euf::egraph>& ensure_euf();
void collect_statistics(statistics& st) const; void collect_statistics(statistics& st) const;
void reset_statistics(); void reset_statistics();

View file

@ -33,13 +33,21 @@ Axioms:
a := s = acc(t), a in Atoms => (a => s < t) in P a := s = acc(t), a in Atoms => (a => s < t) in P
s << t if there is a path P with conditions L. s << t if there is a path P with conditions L.
L => s != t L => s != t
This disregards if acc is applied to non-matching constructor. This disregards if acc is applied to non-matching constructor.
In this case we rely on that the interpretation of acc can be In this case we rely on that the interpretation of acc can be
forced. forced.
If this is incorrect, include is-c(t) assumptions in path axioms. If this is incorrect, include is-c(t) assumptions in path axioms.
Is P sufficient? Should we just consider all possible paths of depth at most k to be safe?
Example:
C(acc(t)) == C(s)
triggers equation acc(t) = s, but the equation is implicit, so acc(t) and s are not directly
connected.
Even, the axioms extracted from P don't consider transitivity of =.
So the can-be-equal alias approximation is currently too strong.
--*/ --*/
#include "ast/sls/sls_datatype_plugin.h" #include "ast/sls/sls_datatype_plugin.h"
@ -48,7 +56,10 @@ namespace sls {
datatype_plugin::datatype_plugin(context& c): datatype_plugin::datatype_plugin(context& c):
plugin(c), plugin(c),
dt(m) { g(c.ensure_euf()),
dt(m),
m_axioms(m),
m_values(m) {
m_fid = dt.get_family_id(); m_fid = dt.get_family_id();
} }
@ -56,9 +67,14 @@ namespace sls {
void datatype_plugin::collect_path_axioms() { void datatype_plugin::collect_path_axioms() {
expr* t = nullptr, *z = nullptr; expr* t = nullptr, *z = nullptr;
for (auto s : ctx.subterms()) for (auto s : ctx.subterms()) {
if (dt.is_accessor(s, t) && dt.is_recursive(t->get_sort())) if (dt.is_accessor(s, t) && dt.is_recursive(t) && dt.is_recursive(s))
add_edge(s, t, sat::null_literal); add_edge(s, t, sat::null_literal);
if (dt.is_constructor(s) && dt.is_recursive(s)) {
for (auto arg : *to_app(s))
add_edge(arg, s, sat::null_literal);
}
}
expr* x = nullptr, *y = nullptr; expr* x = nullptr, *y = nullptr;
for (sat::bool_var v = 0; v < ctx.num_bool_vars(); ++v) { for (sat::bool_var v = 0; v < ctx.num_bool_vars(); ++v) {
expr* e = ctx.atom(v); expr* e = ctx.atom(v);
@ -66,8 +82,10 @@ namespace sls {
continue; continue;
if (!m.is_eq(e, x, y)) if (!m.is_eq(e, x, y))
continue; continue;
if (!dt.is_recursive(x))
continue;
sat::literal lp(v, false), ln(v, true); sat::literal lp(v, false), ln(v, true);
if (dt.is_accessor(x, z) && dt.is_recursive(z->get_sort())) { if (dt.is_accessor(x, z) && dt.is_recursive(z)) {
if (ctx.is_unit(lp)) if (ctx.is_unit(lp))
add_edge(y, z, sat::null_literal); add_edge(y, z, sat::null_literal);
else if (ctx.is_unit(ln)) else if (ctx.is_unit(ln))
@ -75,7 +93,7 @@ namespace sls {
else else
add_edge(y, z, lp); add_edge(y, z, lp);
} }
if (dt.is_accessor(y, z) && dt.is_recursive(z->get_sort())) { if (dt.is_accessor(y, z) && dt.is_recursive(z)) {
if (ctx.is_unit(lp)) if (ctx.is_unit(lp))
add_edge(x, z, sat::null_literal); add_edge(x, z, sat::null_literal);
else if (ctx.is_unit(ln)) else if (ctx.is_unit(ln))
@ -107,7 +125,9 @@ namespace sls {
if (lit != sat::null_literal) if (lit != sat::null_literal)
lits.push_back(~lit); lits.push_back(~lit);
if (children.contains(parent)) { if (children.contains(parent)) {
ctx.add_clause(lits); // only assert loop clauses for proper loops
if (parent == children[0])
ctx.add_clause(lits);
if (lit != sat::null_literal) if (lit != sat::null_literal)
lits.pop_back(); lits.pop_back();
continue; continue;
@ -144,17 +164,17 @@ namespace sls {
if (dt.is_constructor(t)) { if (dt.is_constructor(t)) {
auto r = dt.get_constructor_recognizer(f); auto r = dt.get_constructor_recognizer(f);
axioms.push_back(m.mk_app(r, t)); m_axioms.push_back(m.mk_app(r, t));
auto& acc = *dt.get_constructor_accessors(f); auto& acc = *dt.get_constructor_accessors(f);
for (unsigned i = 0; i < ta->get_num_args(); ++i) { for (unsigned i = 0; i < ta->get_num_args(); ++i) {
auto ti = ta->get_arg(i); auto ti = ta->get_arg(i);
axioms.push_back(m.mk_eq(ti, m.mk_app(acc[i], t))); m_axioms.push_back(m.mk_eq(ti, m.mk_app(acc[i], t)));
} }
auto& cns = *dt.get_datatype_constructors(s); auto& cns = *dt.get_datatype_constructors(s);
for (auto c : cns) { for (auto c : cns) {
if (c != f) { if (c != f) {
auto r2 = dt.get_constructor_recognizer(c); auto r2 = dt.get_constructor_recognizer(c);
axioms.push_back(m.mk_not(m.mk_app(r2, t))); m_axioms.push_back(m.mk_not(m.mk_app(r2, t)));
} }
} }
continue; continue;
@ -166,7 +186,7 @@ namespace sls {
expr_ref_vector args(m); expr_ref_vector args(m);
for (auto a : acc) for (auto a : acc)
args.push_back(m.mk_app(a, u)); args.push_back(m.mk_app(a, u));
axioms.push_back(m.mk_implies(m.mk_app(r, u), m.mk_eq(u, m.mk_app(c, args)))); m_axioms.push_back(m.mk_implies(m.mk_app(r, u), m.mk_eq(u, m.mk_app(c, args))));
} }
if (dt.is_datatype(s)) { if (dt.is_datatype(s)) {
@ -176,18 +196,18 @@ namespace sls {
auto r = dt.get_constructor_recognizer(c); auto r = dt.get_constructor_recognizer(c);
ors.push_back(m.mk_app(r, t)); ors.push_back(m.mk_app(r, t));
} }
axioms.push_back(m.mk_or(ors)); m_axioms.push_back(m.mk_or(ors));
for (unsigned i = 0; i < cns.size(); ++i) { for (unsigned i = 0; i < cns.size(); ++i) {
auto r1 = dt.get_constructor_recognizer(cns[i]); auto r1 = dt.get_constructor_recognizer(cns[i]);
for (unsigned j = i + 1; j < cns.size(); ++j) { for (unsigned j = i + 1; j < cns.size(); ++j) {
auto r2 = dt.get_constructor_recognizer(cns[j]); auto r2 = dt.get_constructor_recognizer(cns[j]);
axioms.push_back(m.mk_or(m.mk_not(m.mk_app(r1, t)), m.mk_not(m.mk_app(r2, t)))); m_axioms.push_back(m.mk_or(m.mk_not(m.mk_app(r1, t)), m.mk_not(m.mk_app(r2, t))));
} }
} }
for (auto c : cns) { for (auto c : cns) {
if (c->get_arity() == 0) { if (c->get_arity() == 0) {
auto r = dt.get_constructor_recognizer(c); auto r = dt.get_constructor_recognizer(c);
axioms.push_back(m.mk_iff(m.mk_app(r, t), m.mk_eq(t, m.mk_const(c)))); m_axioms.push_back(m.mk_iff(m.mk_app(r, t), m.mk_eq(t, m.mk_const(c))));
} }
} }
@ -203,19 +223,104 @@ namespace sls {
collect_path_axioms(); collect_path_axioms();
} }
expr_ref datatype_plugin::get_value(expr* e) { return expr_ref(m); } void datatype_plugin::initialize() {
void datatype_plugin::initialize() {} add_axioms();
void datatype_plugin::start_propagation() {} }
expr_ref datatype_plugin::get_value(expr* e) {
if (!dt.is_datatype(e))
return expr_ref(m);
init_values();
return expr_ref(m_values.get(g->find(e)->get_root_id()), m);
}
void datatype_plugin::init_values() {
if (!m_values.empty())
return;
// retrieve e-graph from sls_euf_solver: add bridge in sls_context to share e-graph
SASSERT(g);
// build top_sort<euf::enode> similar to dt_solver.cpp
top_sort<euf::enode> deps;
for (auto* n : g->nodes())
if (n->is_root())
add_dep(n, deps);
deps.topological_sort();
expr_ref_vector args(m);
// walk topological sort in order of leaves to roots, attaching values to nodes.
for (euf::enode* n : deps.top_sorted()) {
SASSERT(n->is_root());
unsigned id = n->get_id();
if (m_values.get(id, nullptr))
continue;
expr* e = n->get_expr();
m_values.reserve(id + 1);
if (!dt.is_datatype(e))
continue;
euf::enode* con = nullptr;
for (auto sib : euf::enode_class(n)) {
if (dt.is_constructor(sib->get_expr())) {
con = sib;
break;
}
}
if (con) {
auto f = con->get_decl();
args.reset();
for (auto arg : euf::enode_args(con)) {
if (dt.is_datatype(arg->get_sort()))
args.push_back(m_values.get(arg->get_root_id()));
else
args.push_back(ctx.get_value(arg->get_expr()));
}
m_values.setx(n->get_id(), m.mk_app(f, args));
}
else {
NOT_IMPLEMENTED_YET();
}
}
}
void datatype_plugin::add_dep(euf::enode* n, top_sort<euf::enode>& dep) {
if (!dt.is_datatype(n->get_expr()))
return;
euf::enode* con = nullptr;
for (auto sib : euf::enode_class(n)) {
if (dt.is_constructor(sib->get_expr())) {
con = sib;
break;
}
}
TRACE("dt", display(tout) << g->bpp(n) << " con: " << g->bpp(con) << "\n";);
if (!con)
dep.insert(n, nullptr);
else if (con->num_args() == 0)
dep.insert(n, nullptr);
for (euf::enode* arg : euf::enode_args(con))
dep.add(n, arg->get_root());
}
void datatype_plugin::start_propagation() {
m_values.reset();
}
void datatype_plugin::propagate_literal(sat::literal lit) {} void datatype_plugin::propagate_literal(sat::literal lit) {}
bool datatype_plugin::propagate() { return false; } bool datatype_plugin::propagate() { return false; }
bool datatype_plugin::is_sat() { return true; } bool datatype_plugin::is_sat() { return true; }
void datatype_plugin::register_term(expr* e) {} void datatype_plugin::register_term(expr* e) {}
std::ostream& datatype_plugin::display(std::ostream& out) const { std::ostream& datatype_plugin::display(std::ostream& out) const {
for (auto a : m_axioms)
out << mk_bounded_pp(a, m, 3) << "\n";
return out; return out;
} }
void datatype_plugin::mk_model(model& mdl) {} void datatype_plugin::mk_model(model& mdl) {
}
void datatype_plugin::collect_statistics(statistics& st) const {} void datatype_plugin::collect_statistics(statistics& st) const {
st.update("sls-dt-axioms", m_axioms.size());
}
void datatype_plugin::reset_statistics() {} void datatype_plugin::reset_statistics() {}
} }

View file

@ -18,6 +18,7 @@ Author:
#include "ast/sls/sls_context.h" #include "ast/sls/sls_context.h"
#include "ast/datatype_decl_plugin.h" #include "ast/datatype_decl_plugin.h"
#include "util/top_sort.h"
namespace sls { namespace sls {
@ -30,10 +31,12 @@ namespace sls {
expr* parent; expr* parent;
sat::literal lit; sat::literal lit;
}; };
scoped_ptr<euf::egraph>& g;
obj_map<sort, ptr_vector<expr>> m_dts; obj_map<sort, ptr_vector<expr>> m_dts;
obj_map<expr, svector<parent_t>> m_parents; obj_map<expr, svector<parent_t>> m_parents;
datatype_util dt; datatype_util dt;
expr_ref_vector m_axioms, m_values;
stats m_stats; stats m_stats;
void collect_path_axioms(); void collect_path_axioms();
@ -41,6 +44,9 @@ namespace sls {
void add_path_axioms(); void add_path_axioms();
void add_path_axioms(ptr_vector<expr>& children, sat::literal_vector& lits, svector<parent_t> const& parents); void add_path_axioms(ptr_vector<expr>& children, sat::literal_vector& lits, svector<parent_t> const& parents);
void add_axioms(); void add_axioms();
void init_values();
void add_dep(euf::enode* n, top_sort<euf::enode>& dep);
public: public:
datatype_plugin(context& c); datatype_plugin(context& c);

View file

@ -87,6 +87,8 @@ namespace sls {
void collect_statistics(statistics& st) const override; void collect_statistics(statistics& st) const override;
void reset_statistics() override; void reset_statistics() override;
scoped_ptr<euf::egraph>& egraph() { return m_g; }
}; };
} }