3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-12 20:18:18 +00:00
z3/src/muz/fp/horn_tactic.cpp
2018-07-02 17:08:02 +01:00

419 lines
13 KiB
C++

/*++
Copyright (c) 2012 Microsoft Corporation
Module Name:
horn_tactic.h
Abstract:
HORN as a tactic to solve Horn clauses.
Author:
Nikolaj Bjorner (nbjorner) 2012-11-16.
Revision History:
--*/
#include "tactic/tactical.h"
#include "tactic/model_converter.h"
#include "tactic/proof_converter.h"
#include "muz/fp/horn_tactic.h"
#include "muz/base/dl_context.h"
#include "muz/fp/dl_register_engine.h"
#include "ast/rewriter/expr_replacer.h"
#include "muz/base/dl_rule_transformer.h"
#include "muz/transforms/dl_mk_slice.h"
#include "tactic/generic_model_converter.h"
#include "muz/transforms/dl_transforms.h"
#include "muz/base/fp_params.hpp"
#include "ast/ast_util.h"
#include "ast/rewriter/var_subst.h"
class horn_tactic : public tactic {
struct imp {
ast_manager& m;
bool m_is_simplify;
datalog::register_engine m_register_engine;
datalog::context m_ctx;
smt_params m_fparams;
expr_free_vars m_free_vars;
imp(bool t, ast_manager & m, params_ref const & p):
m(m),
m_is_simplify(t),
m_ctx(m, m_register_engine, m_fparams) {
updt_params(p);
}
void updt_params(params_ref const & p) {
m_ctx.updt_params(p);
}
void collect_param_descrs(param_descrs & r) {
m_ctx.collect_params(r);
}
void reset_statistics() {
m_ctx.reset_statistics();
}
void collect_statistics(statistics & st) const {
m_ctx.collect_statistics(st);
}
void normalize(expr_ref& f) {
bool is_positive = true;
expr* e = nullptr;
while (true) {
if (is_forall(f) && is_positive) {
f = to_quantifier(f)->get_expr();
}
else if (is_exists(f) && !is_positive) {
f = to_quantifier(f)->get_expr();
}
else if (m.is_not(f, e)) {
is_positive = !is_positive;
f = e;
}
else {
break;
}
}
if (!is_positive) {
f = m.mk_not(f);
}
}
bool is_predicate(expr* a) {
SASSERT(m.is_bool(a));
return is_app(a) && to_app(a)->get_decl()->get_family_id() == null_family_id;
}
void register_predicate(expr* a) {
SASSERT(is_predicate(a));
m_ctx.register_predicate(to_app(a)->get_decl(), false);
}
void check_predicate(ast_mark& mark, expr* a) {
ptr_vector<expr> todo;
todo.push_back(a);
while (!todo.empty()) {
a = todo.back();
todo.pop_back();
if (mark.is_marked(a)) {
continue;
}
mark.mark(a, true);
if (is_quantifier(a)) {
a = to_quantifier(a)->get_expr();
todo.push_back(a);
}
else if (m.is_not(a) || m.is_and(a) || m.is_or(a) || m.is_implies(a)) {
todo.append(to_app(a)->get_num_args(), to_app(a)->get_args());
}
else if (m.is_ite(a)) {
todo.push_back(to_app(a)->get_arg(1));
todo.push_back(to_app(a)->get_arg(2));
}
else if (is_predicate(a)) {
register_predicate(a);
}
}
}
enum formula_kind { IS_RULE, IS_QUERY, IS_NONE };
bool is_implication(expr* f) {
expr* e1;
while (is_forall(f)) {
f = to_quantifier(f)->get_expr();
}
while (m.is_implies(f, e1, f)) ;
return is_predicate(f);
}
formula_kind get_formula_kind(expr_ref& f) {
expr_ref tmp(f);
normalize(tmp);
ast_mark mark;
expr_ref_vector args(m), body(m);
expr_ref head(m);
expr* a = nullptr, *a1 = nullptr;
flatten_or(tmp, args);
for (unsigned i = 0; i < args.size(); ++i) {
a = args[i].get();
check_predicate(mark, a);
if (m.is_not(a, a1)) {
body.push_back(a1);
}
else if (is_predicate(a)) {
if (head) {
return IS_NONE;
}
head = a;
}
else {
body.push_back(m.mk_not(a));
}
}
if (head) {
if (!is_implication(f)) {
f = m.mk_and(body.size(), body.c_ptr());
f = m.mk_implies(f, head);
}
return IS_RULE;
}
else {
f = m.mk_and(body.size(), body.c_ptr());
return IS_QUERY;
}
}
expr_ref mk_rule(expr* body, expr* head) {
return expr_ref(m.mk_implies(body, head), m);
}
void operator()(goal_ref const & g,
goal_ref_buffer & result) {
SASSERT(g->is_well_sorted());
tactic_report report("horn", *g);
bool produce_proofs = g->proofs_enabled();
if (produce_proofs) {
if (!m_ctx.generate_proof_trace()) {
params_ref params = m_ctx.get_params().p;
params.set_bool("generate_proof_trace", true);
updt_params(params);
}
}
unsigned sz = g->size();
expr_ref q(m), f(m);
expr_ref_vector queries(m);
std::stringstream msg;
m_ctx.reset();
m_ctx.ensure_opened();
for (unsigned i = 0; i < sz; i++) {
f = g->form(i);
formula_kind k = get_formula_kind(f);
switch(k) {
case IS_RULE:
m_ctx.add_rule(f, symbol::null);
break;
case IS_QUERY:
queries.push_back(f);
break;
default:
msg << "formula is not in Horn fragment: " << mk_pp(g->form(i), m) << "\n";
TRACE("horn", tout << msg.str(););
throw tactic_exception(msg.str());
}
}
if (queries.size() != 1 || m_is_simplify) {
q = m.mk_fresh_const("query", m.mk_bool_sort());
register_predicate(q);
for (unsigned i = 0; i < queries.size(); ++i) {
f = mk_rule(queries[i].get(), q);
bind_variables(f);
m_ctx.add_rule(f, symbol::null);
}
queries.reset();
queries.push_back(q);
generic_model_converter* mc1 = alloc(generic_model_converter, m, "horn");
mc1->hide(q);
g->add(mc1);
}
SASSERT(queries.size() == 1);
q = queries[0].get();
proof_converter_ref pc = g->pc();
model_converter_ref mc;
if (m_is_simplify) {
simplify(q, g, result, mc, pc);
}
else {
verify(q, g, result, mc, pc);
}
g->set(pc.get());
g->set(mc.get());
}
void verify(expr* q,
goal_ref const& g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc) {
lbool is_reachable = l_undef;
try {
is_reachable = m_ctx.query(q);
}
catch (default_exception& ex) {
IF_VERBOSE(1, verbose_stream() << ex.msg() << "\n";);
throw ex;
}
g->inc_depth();
bool produce_models = g->models_enabled();
bool produce_proofs = g->proofs_enabled();
result.push_back(g.get());
switch (is_reachable) {
case l_true: {
// goal is unsat
if (produce_proofs) {
proof_ref proof = m_ctx.get_proof();
pc = proof2proof_converter(m, proof);
g->assert_expr(m.mk_false(), proof, nullptr);
}
else {
g->assert_expr(m.mk_false());
}
break;
}
case l_false: {
// goal is sat
g->reset();
if (produce_models) {
model_ref md = m_ctx.get_model();
model_converter_ref mc2 = model2model_converter(&*md);
if (mc) {
mc = concat(mc.get(), mc2.get());
}
else {
mc = mc2;
}
}
break;
}
case l_undef:
// subgoal is unchanged.
break;
}
TRACE("horn", g->display(tout););
SASSERT(g->is_well_sorted());
}
void bind_variables(expr_ref& f) {
m_free_vars.reset();
m_free_vars(f);
m_free_vars.set_default_sort(m.mk_bool_sort());
if (!m_free_vars.empty()) {
m_free_vars.reverse();
svector<symbol> names;
for (unsigned i = 0; i < m_free_vars.size(); ++i) {
names.push_back(symbol(m_free_vars.size() - i - 1));
}
f = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), f);
}
}
void simplify(expr* q,
goal_ref const& g,
goal_ref_buffer & result,
model_converter_ref & mc,
proof_converter_ref & pc) {
expr_ref fml(m);
func_decl* query_pred = to_app(q)->get_decl();
m_ctx.set_output_predicate(query_pred);
m_ctx.get_rules(); // flush adding rules.
apply_default_transformation(m_ctx);
if (m_ctx.xform_slice()) {
datalog::rule_transformer transformer(m_ctx);
datalog::mk_slice* slice = alloc(datalog::mk_slice, m_ctx);
transformer.register_plugin(slice);
m_ctx.transform_rules(transformer);
}
expr_substitution sub(m);
sub.insert(q, m.mk_false());
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
rep->set_substitution(&sub);
g->inc_depth();
g->reset();
result.push_back(g.get());
datalog::rule_set const& rules = m_ctx.get_rules();
datalog::rule_set::iterator it = rules.begin(), end = rules.end();
for (; it != end; ++it) {
datalog::rule* r = *it;
m_ctx.get_rule_manager().to_formula(*r, fml);
(*rep)(fml);
g->assert_expr(fml);
}
}
};
bool m_is_simplify;
params_ref m_params;
statistics m_stats;
imp * m_imp;
public:
horn_tactic(bool t, ast_manager & m, params_ref const & p):
m_is_simplify(t),
m_params(p) {
m_imp = alloc(imp, t, m, p);
}
tactic * translate(ast_manager & m) override {
return alloc(horn_tactic, m_is_simplify, m, m_params);
}
~horn_tactic() override {
dealloc(m_imp);
}
void updt_params(params_ref const & p) override {
m_params = p;
m_imp->updt_params(p);
}
void collect_param_descrs(param_descrs & r) override {
m_imp->collect_param_descrs(r);
}
void operator()(goal_ref const & in,
goal_ref_buffer & result) override {
(*m_imp)(in, result);
}
void collect_statistics(statistics & st) const override {
m_imp->collect_statistics(st);
st.copy(m_stats);
}
void reset_statistics() override {
m_stats.reset();
m_imp->reset_statistics();
}
void cleanup() override {
ast_manager & m = m_imp->m;
m_imp->collect_statistics(m_stats);
dealloc(m_imp);
m_imp = alloc(imp, m_is_simplify, m, m_params);
}
};
tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(horn_tactic, false, m, p));
}
tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) {
return clean(alloc(horn_tactic, true, m, p));
}