3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-22 16:45:31 +00:00

updated sls-datatype

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2024-10-15 16:59:34 -07:00
parent 295be7579c
commit af687532aa
3 changed files with 134 additions and 39 deletions

View file

@ -21,6 +21,7 @@ Author:
#include "ast/sls/sls_array_plugin.h"
#include "ast/sls/sls_bv_plugin.h"
#include "ast/sls/sls_basic_plugin.h"
#include "ast/sls/sls_datatype_plugin.h"
#include "ast/ast_ll_pp.h"
#include "ast/ast_pp.h"
#include "smt/params/smt_params_helper.hpp"
@ -65,6 +66,8 @@ namespace sls {
register_plugin(alloc(bv_plugin, *this));
else if (fid == array_util(m).get_family_id())
register_plugin(alloc(array_plugin, *this));
else if (fid == datatype_util(m).get_family_id())
register_plugin(alloc(datatype_plugin, *this));
else
verbose_stream() << "did not find plugin for " << fid << "\n";
}
@ -306,10 +309,21 @@ namespace sls {
}
else if (sign && m.is_or(f)) {
for (auto arg : *to_app(f)) {
expr_ref fml(m.mk_not(arg), m);;
expr_ref fml(m.mk_not(arg), m);
add_clause(fml);
}
}
else if (!sign && m.is_implies(f, g, h)) {
clause.reset();
clause.push_back(~mk_literal(g));
clause.push_back(mk_literal(h));
s.add_clause(clause.size(), clause.data());
}
else if (sign && m.is_implies(f, g, h)) {
expr_ref fml(m.mk_not(h), m);
add_clause(fml);
add_clause(g);
}
else if (sign && m.is_and(f)) {
clause.reset();
for (auto arg : *to_app(f))

View file

@ -15,7 +15,7 @@ Author:
Notes:
Axioms:
Eager reduction to EUF:
is-c(c(t)) for each c(t) in T
f_i(c(t_i)) = t_i for each c(..t_i..) in T
is-c(t) => t = c(...acc_j(t)..) for each acc_j(t) in T
@ -46,7 +46,24 @@ Axioms:
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.
So the can-be-equal alias approximation is too strong.
We therefore add an occurs check during propagation and lazily add missed axioms.
Model-repair based:
1. Initialize uninterpreted datatype nodes to hold arbitrary values.
2. Initialize datatype nodes by induced evaluation.
3. Atomic constraints are of the form for datatype terms
x = y, x = t, x != y, x != t; s = t, s != t
violated x = y: x <- eval(y), y <- eval(x) or x, y <- fresh
violated x = t: x <- eval(t), repair t using the shape of x
violated x != y: x <- fresh, y <- fresh
violated x != t: x <- fresh, subterm y of t: y <- fresh
model::get_fresh_value(s)
model::get_some_value(s)
--*/
@ -107,6 +124,7 @@ namespace sls {
}
void datatype_plugin::add_edge(expr* child, expr* parent, sat::literal lit) {
TRACE("dt", tout << mk_bounded_pp(child, m) << " <- " << mk_bounded_pp(parent, m) << " " << lit << "\n");
m_parents.insert_if_not_there(child, svector<parent_t>()).push_back({parent, lit});
}
@ -135,6 +153,7 @@ namespace sls {
}
if (children[0]->get_sort() == parent->get_sort()) {
lits.push_back(~ctx.mk_literal(m.mk_eq(children[0], parent)));
TRACE("dt", tout << lits << "\n");
ctx.add_clause(lits);
lits.pop_back();
}
@ -164,7 +183,7 @@ namespace sls {
auto f = ta->get_decl();
if (dt.is_constructor(t)) {
auto r = dt.get_constructor_recognizer(f);
auto r = dt.get_constructor_is(f);
m_axioms.push_back(m.mk_app(r, t));
auto& acc = *dt.get_constructor_accessors(f);
for (unsigned i = 0; i < ta->get_num_args(); ++i) {
@ -174,42 +193,41 @@ namespace sls {
auto& cns = *dt.get_datatype_constructors(s);
for (auto c : cns) {
if (c != f) {
auto r2 = dt.get_constructor_recognizer(c);
auto r2 = dt.get_constructor_is(c);
m_axioms.push_back(m.mk_not(m.mk_app(r2, t)));
}
}
continue;
}
if (dt.is_accessor(t, u) && !dt.is_constructor(u)) {
auto c = dt.get_accessor_constructor(f);
auto r = dt.get_constructor_recognizer(c);
auto& acc = *dt.get_constructor_accessors(f);
expr_ref_vector args(m);
for (auto a : acc)
args.push_back(m.mk_app(a, u));
m_axioms.push_back(m.mk_implies(m.mk_app(r, u), m.mk_eq(u, m.mk_app(c, args))));
if (dt.is_recognizer0(f)) {
auto u = ta->get_arg(0);
auto c = dt.get_recognizer_constructor(f);
m_axioms.push_back(m.mk_iff(t, m.mk_app(dt.get_constructor_is(c), u)));
}
if (dt.is_datatype(s)) {
auto& cns = *dt.get_datatype_constructors(s);
expr_ref_vector ors(m);
for (auto c : cns) {
auto r = dt.get_constructor_recognizer(c);
auto r = dt.get_constructor_is(c);
ors.push_back(m.mk_app(r, t));
}
m_axioms.push_back(m.mk_or(ors));
for (unsigned i = 0; i < cns.size(); ++i) {
auto r1 = dt.get_constructor_recognizer(cns[i]);
auto r1 = dt.get_constructor_is(cns[i]);
for (unsigned j = i + 1; j < cns.size(); ++j) {
auto r2 = dt.get_constructor_recognizer(cns[j]);
auto r2 = dt.get_constructor_is(cns[j]);
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) {
if (c->get_arity() == 0) {
auto r = dt.get_constructor_recognizer(c);
m_axioms.push_back(m.mk_iff(m.mk_app(r, t), m.mk_eq(t, m.mk_const(c))));
}
auto r = dt.get_constructor_is(c);
auto& acc = *dt.get_constructor_accessors(c);
expr_ref_vector args(m);
for (auto a : acc)
args.push_back(m.mk_app(a, t));
m_axioms.push_back(m.mk_implies(m.mk_app(r, t), m.mk_eq(t, m.mk_app(c, args))));
}
//
@ -222,6 +240,11 @@ namespace sls {
}
}
collect_path_axioms();
TRACE("dt", for (auto a : m_axioms) tout << mk_pp(a, m) << "\n";);
for (auto a : m_axioms)
ctx.add_constraint(a);
}
void datatype_plugin::initialize() {
@ -238,6 +261,8 @@ namespace sls {
void datatype_plugin::init_values() {
if (!m_values.empty())
return;
TRACE("dt", g->display(tout));
m_model = alloc(model, m);
// 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
@ -259,39 +284,94 @@ namespace sls {
if (!dt.is_datatype(e))
continue;
euf::enode* con = get_constructor(n);
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()));
if (!con)
continue;
auto f = con->get_decl();
args.reset();
bool has_null = false;
for (auto arg : euf::enode_args(con)) {
if (dt.is_datatype(arg->get_sort())) {
auto val_arg = m_values.get(arg->get_root_id());
if (!val_arg)
has_null = true;
args.push_back(val_arg);
}
m_values.setx(n->get_id(), m.mk_app(f, args));
else
args.push_back(ctx.get_value(arg->get_expr()));
}
else {
NOT_IMPLEMENTED_YET();
}
}
if (!has_null) {
m_values.setx(id, m.mk_app(f, args));
m_model->register_value(m_values.get(id));
TRACE("dt", tout << "Set interpretation " << g->bpp(n) << " <- " << mk_bounded_pp(m_values.get(id), m) << "\n");
}
}
// assign fresh values to uninterpeted 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;
auto con = get_constructor(n);
if (con)
continue;
m_values.setx(id, m_model->get_fresh_value(e->get_sort()));
TRACE("dt", tout << "Fresh interpretation " << g->bpp(n) << " <- " << mk_bounded_pp(m_values.get(id), m) << "\n");
}
// finally populate values to other 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;
if (m_values.get(id, nullptr))
continue;
euf::enode* con = get_constructor(n);
if (!con)
continue;
auto f = con->get_decl();
args.reset();
for (auto arg : euf::enode_args(con)) {
if (dt.is_datatype(arg->get_sort())) {
auto val_arg = m_values.get(arg->get_root_id());
SASSERT(val_arg);
args.push_back(val_arg);
}
else
args.push_back(ctx.get_value(arg->get_expr()));
}
m_values.setx(id, m.mk_app(f, args));
TRACE("dt", tout << "Patched interpretation " << g->bpp(n) << " <- " << mk_bounded_pp(m_values.get(id), m) << "\n");
}
}
void datatype_plugin::add_dep(euf::enode* n, top_sort<euf::enode>& dep) {
if (!dt.is_datatype(n->get_expr()))
return;
euf::enode* con = get_constructor(n);
TRACE("dt", display(tout) << g->bpp(n) << " con: " << g->bpp(con) << "\n";);
TRACE("dt", 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());
else
for (euf::enode* arg : euf::enode_args(con))
dep.add(n, arg->get_root());
}
void datatype_plugin::start_propagation() {
m_values.reset();
m_model = nullptr;
}
euf::enode* datatype_plugin::get_constructor(euf::enode* n) {
@ -330,7 +410,7 @@ namespace sls {
while (!todo.empty()) {
auto [depth, n, parent_idx] = todo.back();
unsigned id = n->get_root_id();
c = color[id];
c = color.get(id, white);
euf::enode* con;
unsigned idx;
@ -355,14 +435,14 @@ namespace sls {
verbose_stream() << "cycle\n";
for (auto e : diseqs)
verbose_stream() << mk_pp(e, m) << "\n";
ctx.add_clause(m.mk_or(diseqs));
ctx.add_constraint(m.mk_or(diseqs));
return true;
}
color[id] = black;
todo.pop_back();
break;
case white:
color[id] = grey;
color.setx(id, grey, white);
dfsnum.setx(id, depth, UINT_MAX);
con = get_constructor(n);
idx = todo.size() - 1;

View file

@ -37,6 +37,7 @@ namespace sls {
datatype_util dt;
expr_ref_vector m_axioms, m_values;
model_ref m_model;
stats m_stats;
void collect_path_axioms();