3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2026-02-14 04:41:48 +00:00

merge with master

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2022-04-16 18:30:03 +02:00
commit 3533bf486f
223 changed files with 7175 additions and 2167 deletions

View file

@ -281,14 +281,19 @@ namespace datalog {
return get_max_var(has_var);
}
void del_rule(horn_subsume_model_converter* mc, rule& r, bool unreachable) {
void del_rule(horn_subsume_model_converter* mc, rule& r, lbool unreachable) {
if (mc) {
ast_manager& m = mc->get_manager();
expr_ref_vector body(m);
if (unreachable) {
TRACE("dl", tout << "unreachable: " << unreachable << " " << r.get_decl()->get_name() << "\n");
switch (unreachable) {
case l_true:
body.push_back(m.mk_true());
break;
case l_false:
body.push_back(m.mk_false());
}
else {
break;
default:
for (unsigned i = 0; i < r.get_tail_size(); ++i) {
if (r.is_neg_tail(i)) {
body.push_back(m.mk_not(r.get_tail(i)));
@ -297,11 +302,12 @@ namespace datalog {
body.push_back(r.get_tail(i));
}
}
break;
}
TRACE("dl_dr",
TRACE("dl",
tout << mk_pp(r.get_head(), m) << " :- \n";
for (unsigned i = 0; i < body.size(); ++i) {
tout << mk_pp(body[i].get(), m) << "\n";
tout << mk_pp(body.get(i), m) << "\n";
});
mc->insert(r.get_head(), body.size(), body.data());

View file

@ -353,7 +353,7 @@ namespace datalog {
unsigned get_max_rule_var(const rule& r);
};
void del_rule(horn_subsume_model_converter* mc, rule& r, bool unreachable);
void del_rule(horn_subsume_model_converter* mc, rule& r, lbool unreachable);
void resolve_rule(rule_manager& rm,
replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,

View file

@ -210,19 +210,35 @@ bool rule_properties::check_accessor(app* n) {
SASSERT(m_dt.is_datatype(s));
if (m_dt.get_datatype_constructors(s)->size() <= 1)
return true;
func_decl* f = n->get_decl();
func_decl * c = m_dt.get_accessor_constructor(f);
func_decl* c = m_dt.get_accessor_constructor(f);
unsigned ut_size = m_rule->get_uninterpreted_tail_size();
unsigned t_size = m_rule->get_tail_size();
ptr_vector<func_decl> ctors;
// add recognizer constructor to ctors
auto add_recognizer = [&](expr* r) {
if (!m_dt.is_recognizer(r))
return;
if (n->get_arg(0) != to_app(r)->get_arg(0))
return;
auto* c2 = m_dt.get_recognizer_constructor(to_app(r)->get_decl());
if (c == c2)
return;
ctors.push_back(c2);
};
auto add_not_recognizer = [&](expr* r) {
if (m.is_not(r, r))
add_recognizer(r);
};
// t is a recognizer for n
auto is_recognizer_base = [&](expr* t) {
return m_dt.is_recognizer(t) &&
to_app(t)->get_arg(0) == n->get_arg(0) &&
m_dt.get_recognizer_constructor(to_app(t)->get_decl()) == c;
};
auto is_recognizer = [&](expr* t) {
if (m.is_and(t))
for (expr* arg : *to_app(t))
@ -231,43 +247,78 @@ bool rule_properties::check_accessor(app* n) {
return is_recognizer_base(t);
};
for (unsigned i = ut_size; i < t_size; ++i)
if (is_recognizer(m_rule->get_tail(i)))
for (unsigned i = ut_size; i < t_size; ++i) {
auto* tail = m_rule->get_tail(i);
if (is_recognizer(tail))
return true;
add_not_recognizer(tail);
}
// create parent use list for every sub-expression in the rule
obj_map<expr, ptr_vector<expr>> use_list;
for (unsigned i = ut_size; i < t_size; ++i) {
app* t = m_rule->get_tail(i);
use_list.insert_if_not_there(t, ptr_vector<expr>()).push_back(nullptr); // add marker for top-level expression.
for (expr* sub : subterms::all(expr_ref(t, m)))
for (expr* sub : subterms::all(expr_ref(t, m)))
if (is_app(sub))
for (expr* arg : *to_app(sub))
use_list.insert_if_not_there(arg, ptr_vector<expr>()).push_back(sub);
}
// walk parents of n to check that each path is guarded by a recognizer.
ptr_vector<expr> todo;
todo.push_back(n);
for (unsigned i = 0; i < todo.size(); ++i) {
expr* e = todo[i];
// walk parents of n depth first to check that each path is guarded by a recognizer.
vector<std::tuple<expr *, unsigned int, bool>> todo;
todo.push_back({n, ctors.size(), false});
while(!todo.empty()) {
auto [e, ctors_size, visited] = todo.back();
if (visited) {
todo.pop_back();
while (ctors.size() > ctors_size) ctors.pop_back();
continue;
}
std::get<2>(todo.back()) = true; // set visited
if (!use_list.contains(e))
return false;
for (expr* parent : use_list[e]) {
if (!parent)
return false; // top-level expressions are not guarded
if (is_recognizer(parent))
if (!parent) { // top-level expression
// check if n is an unguarded "else" branch
ptr_vector<func_decl> diff;
for (auto* dtc : *m_dt.get_datatype_constructors(s))
if (!ctors.contains(dtc))
diff.push_back(dtc);
// the only unguarded constructor for s is c:
// all the others are guarded and we are in an "else" branch so the accessor is safe
if (diff.size() == 1 && diff[0] == c)
continue;
return false; // the accessor is not safe
}
if (is_recognizer(parent))
continue;
if (m.is_ite(parent) && to_app(parent)->get_arg(1) == e && is_recognizer(to_app(parent)->get_arg(0)))
continue;
todo.push_back(parent);
expr *cnd, *thn, *els;
if (m.is_ite(parent, cnd, thn, els)) {
if (thn == e) {
if (is_recognizer(cnd) && els != e)
continue; // e is guarded
}
add_recognizer(cnd);
}
if (m.is_and(parent))
for (expr* arg : *to_app(parent))
add_not_recognizer(arg);
if (m.is_or(parent))
for (expr* arg : *to_app(parent)) {
add_recognizer(arg);
// if one branch is not(recognizer) then the accessor is safe
if (m.is_not(arg, arg) && is_recognizer(arg))
goto _continue;
}
todo.push_back({parent, ctors.size(), false});
_continue:;
}
}
return true;
}
void rule_properties::operator()(app* n) {

View file

@ -777,6 +777,7 @@ protected:
// Sym ::= String | NUM | Var
//
dtoken parse_infix(dtoken tok1, char const* td, app_ref& pred) {
std::string td1_(td);
symbol td1(td);
expr_ref v1(m), v2(m);
sort* s = nullptr;
@ -793,12 +794,12 @@ protected:
if (tok1 == TK_ID) {
expr* _v1 = nullptr;
m_vars.find(td1.bare_str(), _v1);
m_vars.find(td1_, _v1);
v1 = _v1;
}
if (tok3 == TK_ID) {
expr* _v2 = nullptr;
m_vars.find(td2.bare_str(), _v2);
m_vars.find(td, _v2);
v2 = _v2;
}
if (!v1 && !v2) {
@ -950,18 +951,19 @@ protected:
break;
}
case TK_ID: {
symbol data (m_lexer->get_token_data());
if (is_var(data.bare_str())) {
char const* d = m_lexer->get_token_data();
symbol data (d);
if (is_var(d)) {
unsigned idx = 0;
expr* v = nullptr;
if (!m_vars.find(data.bare_str(), v)) {
if (!m_vars.find(d, v)) {
idx = m_num_vars++;
v = m.mk_var(idx, s);
m_vars.insert(data.bare_str(), v);
m_vars.insert(d, v);
}
else if (s != v->get_sort()) {
throw default_exception(default_exception::fmt(), "sort: %s expected, but got: %s\n",
s->get_name().bare_str(), v->get_sort()->get_name().bare_str());
s->get_name().str().c_str(), v->get_sort()->get_name().str().c_str());
}
args.push_back(v);
}
@ -1075,21 +1077,21 @@ protected:
}
sort * register_finite_sort(symbol name, uint64_t domain_size, context::sort_kind k) {
if(m_sort_dict.contains(name.bare_str())) {
throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str());
if(m_sort_dict.contains(name.str().c_str())) {
throw default_exception(default_exception::fmt(), "sort %s already declared", name.str().c_str());
}
sort * s = m_decl_util.mk_sort(name, domain_size);
m_context.register_finite_sort(s, k);
m_sort_dict.insert(name.bare_str(), s);
m_sort_dict.insert(name.str(), s);
return s;
}
sort * register_int_sort(symbol name) {
if(m_sort_dict.contains(name.bare_str())) {
throw default_exception(default_exception::fmt(), "sort %s already declared", name.bare_str());
if(m_sort_dict.contains(name.str().c_str())) {
throw default_exception(default_exception::fmt(), "sort %s already declared", name.str().c_str());
}
sort * s = m_arith.mk_int();
m_sort_dict.insert(name.bare_str(), s);
m_sort_dict.insert(name.str(), s);
return s;
}
@ -1105,8 +1107,8 @@ protected:
app * res;
if(m_arith.is_int(s)) {
uint64_t val;
if (!string_to_uint64(name.bare_str(), val)) {
throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.bare_str());
if (!string_to_uint64(name.str().c_str(), val)) {
throw default_exception(default_exception::fmt(), "Invalid integer: \"%s\"", name.str().c_str());
}
res = m_arith.mk_numeral(rational(val, rational::ui64()), s);
}
@ -1288,7 +1290,7 @@ private:
uint64_set & sort_content = *e->get_data().m_value;
if(!sort_content.contains(num)) {
warning_msg("symbol number %I64u on line %d in file %s does not belong to sort %s",
num, m_current_line, m_current_file.c_str(), s->get_name().bare_str());
num, m_current_line, m_current_file.c_str(), s->get_name().str().c_str());
return false;
}
if(!m_use_map_names) {
@ -1366,7 +1368,7 @@ private:
func_decl * pred = m_context.try_get_predicate_decl(predicate_name);
if(!pred) {
throw default_exception(default_exception::fmt(), "tuple file %s for undeclared predicate %s",
m_current_file.c_str(), predicate_name.bare_str());
m_current_file.c_str(), predicate_name.str().c_str());
}
unsigned pred_arity = pred->get_arity();
sort * const * arg_sorts = pred->get_domain();
@ -1531,9 +1533,9 @@ private:
if(m_use_map_names) {
auto const & value = m_number_names.insert_if_not_there(num, el_name);
if (value!=el_name) {
if (value != el_name) {
warning_msg("mismatch of number names on line %d in file %s. old: \"%s\" new: \"%s\"",
m_current_line, fname.c_str(), value.bare_str(), el_name.bare_str());
m_current_line, fname.c_str(), value.str().c_str(), el_name.str().c_str());
}
}
}

View file

@ -241,7 +241,7 @@ class horn_tactic : public tactic {
verify(q, g, result, mc, pc);
}
g->set(pc.get());
g->set(mc.get());
g->add(mc.get());
}
void verify(expr* q,
@ -282,12 +282,11 @@ class horn_tactic : public tactic {
}
case l_false: {
// goal is sat
mc = concat(g->mc(), mc.get());
g->reset();
if (produce_models) {
model_ref md = m_ctx.get_model();
model_converter_ref mc2 = model2model_converter(md.get());
mc = concat(mc.get(), mc2.get());
mc = mc2.get();
TRACE("dl", mc->display(tout << *md << "\n"););
}
break;
@ -345,6 +344,7 @@ class horn_tactic : public tactic {
g->assert_expr(fml);
}
g->set_prec(goal::UNDER_OVER);
mc = g->mc();
}
void check_parameters() {

View file

@ -64,7 +64,7 @@ namespace datalog {
}
symbol finite_product_relation_plugin::get_name(relation_plugin & inner_plugin) {
std::string str = std::string("fpr_")+inner_plugin.get_name().bare_str();
std::string str = std::string("fpr_")+inner_plugin.get_name().str();
return symbol(str.c_str());
}

View file

@ -213,10 +213,10 @@ namespace datalog {
return true;
}
void make_annotations(execution_context & ctx) override {
ctx.set_register_annotation(m_reg, m_pred->get_name().bare_str());
ctx.set_register_annotation(m_reg, m_pred->get_name().str().c_str());
}
std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override {
const char * rel_name = m_pred->get_name().bare_str();
auto rel_name = m_pred->get_name();
if (m_store) {
return out << "store " << m_reg << " into " << rel_name;
}
@ -378,7 +378,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported join operation on relations of kinds %s and %s",
r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str());
r1.get_plugin().get_name().str().c_str(), r2.get_plugin().get_name().str().c_str());
}
store_fn(r1, r2, fn);
}
@ -441,7 +441,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported filter_equal operation on a relation of kind %s",
r.get_plugin().get_name().bare_str());
r.get_plugin().get_name().str().c_str());
}
store_fn(r, fn);
}
@ -490,7 +490,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported filter_identical operation on a relation of kind %s",
r.get_plugin().get_name().bare_str());
r.get_plugin().get_name().str().c_str());
}
store_fn(r, fn);
}
@ -537,7 +537,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported filter_interpreted operation on a relation of kind %s",
r.get_plugin().get_name().bare_str());
r.get_plugin().get_name().str().c_str());
}
store_fn(r, fn);
}
@ -594,7 +594,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported filter_interpreted_and_project operation on a relation of kind %s",
reg.get_plugin().get_name().bare_str());
reg.get_plugin().get_name().str().c_str());
}
store_fn(reg, fn);
}
@ -837,7 +837,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported join-project operation on relations of kinds %s and %s",
r1.get_plugin().get_name().bare_str(), r2.get_plugin().get_name().bare_str());
r1.get_plugin().get_name().str().c_str(), r2.get_plugin().get_name().str().c_str());
}
store_fn(r1, r2, fn);
}
@ -910,7 +910,7 @@ namespace datalog {
if (!fn) {
throw default_exception(default_exception::fmt(),
"trying to perform unsupported select_equal_and_project operation on a relation of kind %s",
r.get_plugin().get_name().bare_str());
r.get_plugin().get_name().str().c_str());
}
store_fn(r, fn);
}
@ -1076,7 +1076,7 @@ namespace datalog {
return true;
}
std::ostream& display_head_impl(execution_context const& ctx, std::ostream & out) const override {
return out << "mark_saturated " << m_pred->get_name().bare_str();
return out << "mark_saturated " << m_pred->get_name();
}
void make_annotations(execution_context & ctx) override {
}

View file

@ -370,7 +370,7 @@ namespace datalog {
rule * one_parent = inf.m_rules.back();
func_decl* parent_head = one_parent->get_decl();
const char * one_parent_name = parent_head->get_name().bare_str();
std::string one_parent_name = parent_head->get_name().str();
std::string parent_name;
if (inf.m_rules.size() > 1) {
parent_name = one_parent_name + std::string("_and_") + to_string(inf.m_rules.size()-1);

View file

@ -33,7 +33,7 @@ namespace datalog {
// -----------------------------------
symbol table_relation_plugin::create_plugin_name(const table_plugin &p) {
std::string name = std::string("tr_") + p.get_name().bare_str();
std::string name = std::string("tr_") + p.get_name().str();
return symbol(name.c_str());
}

View file

@ -157,7 +157,7 @@ namespace datalog {
//IF_VERBOSE(3, m_context.display_smt2(0,0,verbose_stream()););
if (m_context.print_aig().is_non_empty_string()) {
const char *filename = m_context.print_aig().bare_str();
std::string filename = m_context.print_aig().str();
aig_exporter aig(m_context.get_rules(), get_context(), &m_table_facts);
std::ofstream strm(filename, std::ios_base::binary);
aig(strm);

View file

@ -126,7 +126,7 @@ public:
void move_to_front(expr* e) override { m_solver.move_to_front(e); }
expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); }
void get_levels(ptr_vector<expr> const& vars, unsigned_vector& depth) override { m_solver.get_levels(vars, depth); }
expr_ref_vector get_trail() override { return m_solver.get_trail(); }
expr_ref_vector get_trail(unsigned max_level) override { return m_solver.get_trail(max_level); }
void push() override;
void pop(unsigned n) override;

View file

@ -118,7 +118,7 @@ namespace datalog {
}
rule_set * mk_coi_filter::top_down(rule_set const & source) {
func_decl_set pruned_preds;
func_decl_set pruned_preds, seen;
dataflow_engine<reachability_info> engine(source.get_manager(), source);
engine.run_top_down();
scoped_ptr<rule_set> res = alloc(rule_set, m_context);
@ -126,37 +126,51 @@ namespace datalog {
for (rule * r : source) {
func_decl * pred = r->get_decl();
if (engine.get_fact(pred).is_reachable()) {
res->add_rule(r);
}
bool should_keep = false;
if (seen.contains(pred))
continue;
seen.insert(pred);
if (engine.get_fact(pred).is_reachable())
should_keep = true;
else if (m_context.get_model_converter()) {
pruned_preds.insert(pred);
for (rule* pr : source.get_predicate_rules(pred))
for (unsigned i = 0; i < pr->get_uninterpreted_tail_size(); ++i)
if (pr->get_tail(i)->get_decl() != pred)
// don't try to eliminate across predicates
return nullptr;
}
else
continue;
if (should_keep)
for (rule* pr : source.get_predicate_rules(pred))
res->add_rule(pr);
else
pruned_preds.insert(pred);
}
if (res->get_num_rules() == source.get_num_rules()) {
TRACE("dl", tout << "No transformation\n";);
res = nullptr;
}
if (res && m_context.get_model_converter()) {
generic_model_converter* mc0 = alloc(generic_model_converter, m, "dl_coi");
for (func_decl* f : pruned_preds) {
if (res && m_context.get_model_converter() && !pruned_preds.empty()) {
auto* mc0 = alloc(generic_model_converter, m, "dl_coi");
horn_subsume_model_converter hmc(m);
for (func_decl* f : pruned_preds) {
const rule_vector& rules = source.get_predicate_rules(f);
expr_ref_vector fmls(m);
for (rule * r : rules) {
app* head = r->get_head();
expr_ref_vector conj(m);
for (unsigned j = 0; j < head->get_num_args(); ++j) {
expr* arg = head->get_arg(j);
if (!is_var(arg)) {
conj.push_back(m.mk_eq(m.mk_var(j, arg->get_sort()), arg));
}
}
fmls.push_back(mk_and(conj));
for (rule* r : rules) {
expr_ref_vector constraints(m);
expr_ref body_res(m);
func_decl_ref pred(m);
for (unsigned i = r->get_uninterpreted_tail_size(); i < r->get_tail_size(); ++i)
constraints.push_back(r->get_tail(i));
expr_ref body = mk_and(constraints);
VERIFY(hmc.mk_horn(r->get_head(), body, pred, body_res));
fmls.push_back(body_res);
}
expr_ref fml(m);
fml = m.mk_or(fmls.size(), fmls.data());
mc0->add(f, fml);
mc0->add(f, mk_or(fmls));
}
m_context.add_model_converter(mc0);
}

View file

@ -44,6 +44,11 @@ Subsumption transformation (remove rule):
P(x) := P(x) or (exists y . Q(y) & phi(x,y))
For plan_inlining:
TODO: order of rule inlining would affect model converter?
so shouldn't model converter process inlined rules in a specific (topopologial) order?
--*/
@ -377,18 +382,15 @@ namespace datalog {
return something_forbidden;
}
void mk_rule_inliner::plan_inlining(rule_set const & orig)
{
void mk_rule_inliner::plan_inlining(rule_set const & orig) {
count_pred_occurrences(orig);
scoped_ptr<rule_set> candidate_inlined_set = create_allowed_rule_set(orig);
while (forbid_preds_from_cycles(*candidate_inlined_set)) {
while (forbid_preds_from_cycles(*candidate_inlined_set))
candidate_inlined_set = create_allowed_rule_set(orig);
}
if (forbid_multiple_multipliers(orig, *candidate_inlined_set)) {
if (forbid_multiple_multipliers(orig, *candidate_inlined_set))
candidate_inlined_set = create_allowed_rule_set(orig);
}
TRACE("dl", tout<<"rules to be inlined:\n" << (*candidate_inlined_set); );
@ -402,16 +404,13 @@ namespace datalog {
for (rule_stratifier::item_set * stratum : comps) {
SASSERT(stratum->size() == 1);
func_decl * pred = *stratum->begin();
for (rule * r : candidate_inlined_set->get_predicate_rules(pred)) {
for (rule * r : candidate_inlined_set->get_predicate_rules(pred))
transform_rule(orig, r, m_inlined_rules);
}
}
TRACE("dl", tout << "inlined rules after mutual inlining:\n" << m_inlined_rules; );
for (rule * r : m_inlined_rules)
datalog::del_rule(m_mc, *r, l_undef);
for (rule * r : m_inlined_rules) {
datalog::del_rule(m_mc, *r, false);
}
}
bool mk_rule_inliner::transform_rule(rule_set const& orig, rule * r0, rule_set& tgt) {
@ -437,20 +436,19 @@ namespace datalog {
tgt.add_rule(r);
continue;
}
modified = true;
func_decl * pred = r->get_decl(i);
const rule_vector& pred_rules = m_inlined_rules.get_predicate_rules(pred);
for (rule * inl_rule : pred_rules) {
rule_ref inl_result(m_rm);
if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result)) {
if (try_to_inline_rule(*r.get(), *inl_rule, i, inl_result))
todo.push_back(inl_result);
}
}
}
if (modified) {
datalog::del_rule(m_mc, *r0, true);
}
if (modified)
datalog::del_rule(m_mc, *r0, l_undef);
return modified;
}
@ -473,7 +471,7 @@ namespace datalog {
if (something_done && m_mc) {
for (rule* r : orig) {
if (inlining_allowed(orig, r->get_decl())) {
datalog::del_rule(m_mc, *r, true);
datalog::del_rule(m_mc, *r, l_undef);
}
}
}
@ -558,7 +556,7 @@ namespace datalog {
// nothing unifies with the tail atom, therefore the rule is unsatisfiable
// (we can say this because relation pred doesn't have any ground facts either)
res = nullptr;
datalog::del_rule(m_mc, *r, false);
datalog::del_rule(m_mc, *r, l_false);
return true;
}
if (!is_oriented_rewriter(inlining_candidate, strat)) {
@ -568,7 +566,7 @@ namespace datalog {
goto process_next_tail;
}
if (!try_to_inline_rule(*r, *inlining_candidate, ti, res)) {
datalog::del_rule(m_mc, *r, false);
datalog::del_rule(m_mc, *r, l_false);
res = nullptr;
}
return true;
@ -768,7 +766,7 @@ namespace datalog {
break;
}
rule* r2 = acc[j].get();
rule* r2 = acc.get(j);
// check that the head of r2 only unifies with this single body position.
TRACE("dl", output_predicate(m_context, r2->get_head(), tout << "unify head: "); tout << "\n";);
@ -801,7 +799,7 @@ namespace datalog {
if (num_tail_unifiers == 1) {
TRACE("dl", tout << "setting invalid: " << j << "\n";);
valid.set(j, false);
datalog::del_rule(m_mc, *r2, true);
datalog::del_rule(m_mc, *r2, l_undef);
del_rule(r2, j);
}