mirror of
https://github.com/Z3Prover/z3
synced 2025-04-10 19:27:06 +00:00
Merge branch 'master' of https://github.com/z3prover/z3
This commit is contained in:
commit
6fc08e9c9f
|
@ -81,7 +81,6 @@ add_subdirectory(muz/base)
|
|||
add_subdirectory(muz/dataflow)
|
||||
add_subdirectory(muz/transforms)
|
||||
add_subdirectory(muz/rel)
|
||||
add_subdirectory(muz/pdr)
|
||||
add_subdirectory(muz/clp)
|
||||
add_subdirectory(muz/tab)
|
||||
add_subdirectory(muz/bmc)
|
||||
|
|
|
@ -190,7 +190,7 @@ extern "C" {
|
|||
MK_UNARY(Z3_mk_not, mk_c(c)->get_basic_fid(), OP_NOT, SKIP);
|
||||
MK_BINARY(Z3_mk_eq, mk_c(c)->get_basic_fid(), OP_EQ, SKIP);
|
||||
MK_NARY(Z3_mk_distinct, mk_c(c)->get_basic_fid(), OP_DISTINCT, SKIP);
|
||||
MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_IFF, SKIP);
|
||||
MK_BINARY(Z3_mk_iff, mk_c(c)->get_basic_fid(), OP_EQ, SKIP);
|
||||
MK_BINARY(Z3_mk_implies, mk_c(c)->get_basic_fid(), OP_IMPLIES, SKIP);
|
||||
MK_BINARY(Z3_mk_xor, mk_c(c)->get_basic_fid(), OP_XOR, SKIP);
|
||||
MK_NARY(Z3_mk_and, mk_c(c)->get_basic_fid(), OP_AND, SKIP);
|
||||
|
@ -894,7 +894,6 @@ extern "C" {
|
|||
case OP_ITE: return Z3_OP_ITE;
|
||||
case OP_AND: return Z3_OP_AND;
|
||||
case OP_OR: return Z3_OP_OR;
|
||||
case OP_IFF: return Z3_OP_IFF;
|
||||
case OP_XOR: return Z3_OP_XOR;
|
||||
case OP_NOT: return Z3_OP_NOT;
|
||||
case OP_IMPLIES: return Z3_OP_IMPLIES;
|
||||
|
|
|
@ -603,7 +603,26 @@ extern "C" {
|
|||
Z3_CATCH;
|
||||
|
||||
}
|
||||
|
||||
|
||||
void Z3_API Z3_fixedpoint_add_callback(Z3_context c, Z3_fixedpoint d,
|
||||
void *state,
|
||||
Z3_fixedpoint_new_lemma_eh new_lemma_eh,
|
||||
Z3_fixedpoint_predecessor_eh predecessor_eh,
|
||||
Z3_fixedpoint_unfold_eh unfold_eh){
|
||||
Z3_TRY;
|
||||
// not logged
|
||||
to_fixedpoint_ref(d)->ctx().add_callback(state,
|
||||
reinterpret_cast<datalog::t_new_lemma_eh>(new_lemma_eh),
|
||||
reinterpret_cast<datalog::t_predecessor_eh>(predecessor_eh),
|
||||
reinterpret_cast<datalog::t_unfold_eh>(unfold_eh));
|
||||
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
void Z3_API Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl){
|
||||
to_fixedpoint_ref(d)->ctx().add_constraint(to_expr(e), lvl);
|
||||
}
|
||||
|
||||
#include "api_datalog_spacer.inc"
|
||||
|
||||
};
|
||||
|
|
|
@ -460,12 +460,12 @@ extern "C" {
|
|||
LOG_Z3_solver_get_unsat_core(c, s);
|
||||
RESET_ERROR_CODE();
|
||||
init_solver(c, s);
|
||||
ptr_vector<expr> core;
|
||||
expr_ref_vector core(mk_c(c)->m());
|
||||
to_solver_ref(s)->get_unsat_core(core);
|
||||
Z3_ast_vector_ref * v = alloc(Z3_ast_vector_ref, *mk_c(c), mk_c(c)->m());
|
||||
mk_c(c)->save_object(v);
|
||||
for (unsigned i = 0; i < core.size(); i++) {
|
||||
v->m_ast_vector.push_back(core[i]);
|
||||
for (expr* e : core) {
|
||||
v->m_ast_vector.push_back(e);
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(v));
|
||||
Z3_CATCH_RETURN(nullptr);
|
||||
|
@ -536,24 +536,22 @@ extern "C" {
|
|||
init_solver(c, s);
|
||||
expr_ref_vector _assumptions(m), _consequences(m), _variables(m);
|
||||
ast_ref_vector const& __assumptions = to_ast_vector_ref(assumptions);
|
||||
unsigned sz = __assumptions.size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!is_expr(__assumptions[i])) {
|
||||
for (ast* e : __assumptions) {
|
||||
if (!is_expr(e)) {
|
||||
_assumptions.finalize(); _consequences.finalize(); _variables.finalize();
|
||||
SET_ERROR_CODE(Z3_INVALID_USAGE);
|
||||
return Z3_L_UNDEF;
|
||||
}
|
||||
_assumptions.push_back(to_expr(__assumptions[i]));
|
||||
_assumptions.push_back(to_expr(e));
|
||||
}
|
||||
ast_ref_vector const& __variables = to_ast_vector_ref(variables);
|
||||
sz = __variables.size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!is_expr(__variables[i])) {
|
||||
for (ast* a : __variables) {
|
||||
if (!is_expr(a)) {
|
||||
_assumptions.finalize(); _consequences.finalize(); _variables.finalize();
|
||||
SET_ERROR_CODE(Z3_INVALID_USAGE);
|
||||
return Z3_L_UNDEF;
|
||||
}
|
||||
_variables.push_back(to_expr(__variables[i]));
|
||||
_variables.push_back(to_expr(a));
|
||||
}
|
||||
lbool result = l_undef;
|
||||
unsigned timeout = to_solver(s)->m_params.get_uint("timeout", mk_c(c)->get_timeout());
|
||||
|
@ -578,8 +576,8 @@ extern "C" {
|
|||
if (result == l_undef) {
|
||||
to_solver_ref(s)->set_reason_unknown(eh);
|
||||
}
|
||||
for (unsigned i = 0; i < _consequences.size(); ++i) {
|
||||
to_ast_vector_ref(consequences).push_back(_consequences[i].get());
|
||||
for (expr* e : _consequences) {
|
||||
to_ast_vector_ref(consequences).push_back(e);
|
||||
}
|
||||
return static_cast<Z3_lbool>(result);
|
||||
Z3_CATCH_RETURN(Z3_L_UNDEF);
|
||||
|
|
|
@ -367,6 +367,18 @@ extern "C" {
|
|||
void Z3_API Z3_fixedpoint_set_reduce_app_callback(
|
||||
Z3_context c, Z3_fixedpoint d, Z3_fixedpoint_reduce_app_callback_fptr cb);
|
||||
|
||||
typedef void (*Z3_fixedpoint_new_lemma_eh)(void *state, Z3_ast lemma, unsigned level);
|
||||
typedef void (*Z3_fixedpoint_predecessor_eh)(void *state);
|
||||
typedef void (*Z3_fixedpoint_unfold_eh)(void *state);
|
||||
|
||||
/** \brief set export callback for lemmas */
|
||||
void Z3_API Z3_fixedpoint_add_callback(Z3_context ctx, Z3_fixedpoint f, void *state,
|
||||
Z3_fixedpoint_new_lemma_eh new_lemma_eh,
|
||||
Z3_fixedpoint_predecessor_eh predecessor_eh,
|
||||
Z3_fixedpoint_unfold_eh unfold_eh);
|
||||
|
||||
void Z3_fixedpoint_add_constraint (Z3_context c, Z3_fixedpoint d, Z3_ast e, unsigned lvl);
|
||||
|
||||
/*@}*/
|
||||
/*@}*/
|
||||
|
||||
|
|
|
@ -81,7 +81,7 @@ app * arith_decl_plugin::mk_numeral(algebraic_numbers::anum const & val, bool is
|
|||
return mk_numeral(rval, is_int);
|
||||
}
|
||||
else {
|
||||
if (is_int) {
|
||||
if (is_int) {
|
||||
m_manager->raise_exception("invalid irrational value passed as an integer");
|
||||
}
|
||||
unsigned idx = aw().mk_id(val);
|
||||
|
@ -638,6 +638,35 @@ bool arith_recognizers::is_numeral(expr const * n, rational & val, bool & is_int
|
|||
return true;
|
||||
}
|
||||
|
||||
#define IS_INT_EXPR_DEPTH_LIMIT 100
|
||||
bool arith_recognizers::is_int_expr(expr const *e) const {
|
||||
if (is_int(e)) return true;
|
||||
if (is_uninterp(e)) return false;
|
||||
ptr_buffer<const expr> todo;
|
||||
todo.push_back(e);
|
||||
rational r;
|
||||
unsigned i = 0;
|
||||
while (!todo.empty()) {
|
||||
++i;
|
||||
if (i > IS_INT_EXPR_DEPTH_LIMIT) {return false;}
|
||||
e = todo.back();
|
||||
todo.pop_back();
|
||||
if (is_to_real(e)) {
|
||||
// pass
|
||||
}
|
||||
else if (is_numeral(e, r) && r.is_int()) {
|
||||
// pass
|
||||
}
|
||||
else if (is_add(e) || is_mul(e)) {
|
||||
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
arith_util::arith_util(ast_manager & m):
|
||||
arith_recognizers(m.mk_family_id("arith")),
|
||||
m_manager(m),
|
||||
|
|
|
@ -244,6 +244,8 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
bool is_int_expr(expr const * e) const;
|
||||
|
||||
bool is_le(expr const * n) const { return is_app_of(n, m_afid, OP_LE); }
|
||||
bool is_ge(expr const * n) const { return is_app_of(n, m_afid, OP_GE); }
|
||||
bool is_lt(expr const * n) const { return is_app_of(n, m_afid, OP_LT); }
|
||||
|
@ -533,4 +535,3 @@ inline app_ref operator>(app_ref const& x, app_ref const& y) {
|
|||
}
|
||||
|
||||
#endif /* ARITH_DECL_PLUGIN_H_ */
|
||||
|
||||
|
|
|
@ -643,7 +643,6 @@ basic_decl_plugin::basic_decl_plugin():
|
|||
m_false_decl(nullptr),
|
||||
m_and_decl(nullptr),
|
||||
m_or_decl(nullptr),
|
||||
m_iff_decl(nullptr),
|
||||
m_xor_decl(nullptr),
|
||||
m_not_decl(nullptr),
|
||||
m_implies_decl(nullptr),
|
||||
|
@ -861,7 +860,6 @@ void basic_decl_plugin::set_manager(ast_manager * m, family_id id) {
|
|||
m_false_decl = mk_bool_op_decl("false", OP_FALSE);
|
||||
m_and_decl = mk_bool_op_decl("and", OP_AND, 2, true, true, true, true);
|
||||
m_or_decl = mk_bool_op_decl("or", OP_OR, 2, true, true, true, true);
|
||||
m_iff_decl = mk_bool_op_decl("iff", OP_IFF, 2, false, true, false, false, true);
|
||||
m_xor_decl = mk_bool_op_decl("xor", OP_XOR, 2, true, true);
|
||||
m_not_decl = mk_bool_op_decl("not", OP_NOT, 1);
|
||||
m_implies_decl = mk_implies_decl();
|
||||
|
@ -892,13 +890,13 @@ void basic_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol co
|
|||
if (logic == symbol::null) {
|
||||
// user friendly aliases
|
||||
op_names.push_back(builtin_name("implies", OP_IMPLIES));
|
||||
op_names.push_back(builtin_name("iff", OP_IFF));
|
||||
op_names.push_back(builtin_name("iff", OP_EQ));
|
||||
op_names.push_back(builtin_name("if_then_else", OP_ITE));
|
||||
op_names.push_back(builtin_name("if", OP_ITE));
|
||||
op_names.push_back(builtin_name("&&", OP_AND));
|
||||
op_names.push_back(builtin_name("||", OP_OR));
|
||||
op_names.push_back(builtin_name("equals", OP_EQ));
|
||||
op_names.push_back(builtin_name("equiv", OP_IFF));
|
||||
op_names.push_back(builtin_name("equiv", OP_EQ));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -919,7 +917,6 @@ void basic_decl_plugin::finalize() {
|
|||
DEC_REF(m_and_decl);
|
||||
DEC_REF(m_or_decl);
|
||||
DEC_REF(m_not_decl);
|
||||
DEC_REF(m_iff_decl);
|
||||
DEC_REF(m_xor_decl);
|
||||
DEC_REF(m_implies_decl);
|
||||
DEC_ARRAY_REF(m_eq_decls);
|
||||
|
@ -1051,7 +1048,6 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
|||
case OP_AND: return m_and_decl;
|
||||
case OP_OR: return m_or_decl;
|
||||
case OP_NOT: return m_not_decl;
|
||||
case OP_IFF: return m_iff_decl;
|
||||
case OP_IMPLIES: return m_implies_decl;
|
||||
case OP_XOR: return m_xor_decl;
|
||||
case OP_ITE: return arity == 3 ? mk_ite_decl(join(domain[1], domain[2])) : nullptr;
|
||||
|
@ -1093,7 +1089,6 @@ func_decl * basic_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters
|
|||
case OP_AND: return m_and_decl;
|
||||
case OP_OR: return m_or_decl;
|
||||
case OP_NOT: return m_not_decl;
|
||||
case OP_IFF: return m_iff_decl;
|
||||
case OP_IMPLIES: return m_implies_decl;
|
||||
case OP_XOR: return m_xor_decl;
|
||||
case OP_ITE: return num_args == 3 ? mk_ite_decl(join(m_manager->get_sort(args[1]), m_manager->get_sort(args[2]))): nullptr;
|
||||
|
@ -2636,10 +2631,10 @@ proof * ast_manager::mk_modus_ponens(proof * p1, proof * p2) {
|
|||
if (!p1 || !p2) return nullptr;
|
||||
SASSERT(has_fact(p1));
|
||||
SASSERT(has_fact(p2));
|
||||
CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2))),
|
||||
CTRACE("mk_modus_ponens", !(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2))),
|
||||
tout << mk_ll_pp(p1, *this) << "\n";
|
||||
tout << mk_ll_pp(p2, *this) << "\n";);
|
||||
SASSERT(is_implies(get_fact(p2)) || is_iff(get_fact(p2)) || is_oeq(get_fact(p2)));
|
||||
SASSERT(is_implies(get_fact(p2)) || is_eq(get_fact(p2)) || is_oeq(get_fact(p2)));
|
||||
CTRACE("mk_modus_ponens", to_app(get_fact(p2))->get_arg(0) != get_fact(p1),
|
||||
tout << mk_pp(get_fact(p1), *this) << "\n" << mk_pp(get_fact(p2), *this) << "\n";);
|
||||
SASSERT(to_app(get_fact(p2))->get_arg(0) == get_fact(p1));
|
||||
|
@ -2717,8 +2712,6 @@ proof * ast_manager::mk_transitivity(proof * p1, proof * p2) {
|
|||
tout << mk_pp(to_app(get_fact(p1))->get_decl(), *this) << "\n";
|
||||
tout << mk_pp(to_app(get_fact(p2))->get_decl(), *this) << "\n";);
|
||||
SASSERT(to_app(get_fact(p1))->get_decl() == to_app(get_fact(p2))->get_decl() ||
|
||||
((is_iff(get_fact(p1)) || is_eq(get_fact(p1))) &&
|
||||
(is_iff(get_fact(p2)) || is_eq(get_fact(p2)))) ||
|
||||
( (is_eq(get_fact(p1)) || is_oeq(get_fact(p1))) &&
|
||||
(is_eq(get_fact(p2)) || is_oeq(get_fact(p2)))));
|
||||
CTRACE("mk_transitivity", to_app(get_fact(p1))->get_arg(1) != to_app(get_fact(p2))->get_arg(0),
|
||||
|
@ -2797,7 +2790,7 @@ proof * ast_manager::mk_quant_intro(quantifier * q1, quantifier * q2, proof * p)
|
|||
if (!p) return nullptr;
|
||||
SASSERT(q1->get_num_decls() == q2->get_num_decls());
|
||||
SASSERT(has_fact(p));
|
||||
SASSERT(is_iff(get_fact(p)));
|
||||
SASSERT(is_eq(get_fact(p)));
|
||||
return mk_app(m_basic_family_id, PR_QUANT_INTRO, p, mk_iff(q1, q2));
|
||||
}
|
||||
|
||||
|
@ -2884,8 +2877,7 @@ bool ast_manager::is_quant_inst(expr const* e, expr*& not_q_or_i, ptr_vector<exp
|
|||
|
||||
bool ast_manager::is_rewrite(expr const* e, expr*& r1, expr*& r2) const {
|
||||
if (is_rewrite(e)) {
|
||||
VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2) ||
|
||||
is_iff(to_app(e)->get_arg(0), r1, r2));
|
||||
VERIFY (is_eq(to_app(e)->get_arg(0), r1, r2));
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
|
@ -2913,7 +2905,7 @@ proof * ast_manager::mk_unit_resolution(unsigned num_proofs, proof * const * pro
|
|||
fact = mk_false();
|
||||
}
|
||||
else {
|
||||
CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_pp(f1, *this) << " " << mk_pp(f2, *this) << "\n";);
|
||||
CTRACE("mk_unit_resolution_bug", !is_or(f1), tout << mk_ll_pp(f1, *this) << "\n" << mk_ll_pp(f2, *this) << "\n";);
|
||||
SASSERT(is_or(f1));
|
||||
ptr_buffer<expr> new_lits;
|
||||
app const * cls = to_app(f1);
|
||||
|
@ -3044,7 +3036,7 @@ proof * ast_manager::mk_iff_oeq(proof * p) {
|
|||
if (!p) return p;
|
||||
|
||||
SASSERT(has_fact(p));
|
||||
SASSERT(is_iff(get_fact(p)) || is_oeq(get_fact(p)));
|
||||
SASSERT(is_eq(get_fact(p)) || is_oeq(get_fact(p)));
|
||||
if (is_oeq(get_fact(p)))
|
||||
return p;
|
||||
|
||||
|
|
|
@ -1041,7 +1041,7 @@ enum basic_sort_kind {
|
|||
};
|
||||
|
||||
enum basic_op_kind {
|
||||
OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_IFF, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP,
|
||||
OP_TRUE, OP_FALSE, OP_EQ, OP_DISTINCT, OP_ITE, OP_AND, OP_OR, OP_XOR, OP_NOT, OP_IMPLIES, OP_OEQ, LAST_BASIC_OP,
|
||||
|
||||
PR_UNDEF, PR_TRUE, PR_ASSERTED, PR_GOAL, PR_MODUS_PONENS, PR_REFLEXIVITY, PR_SYMMETRY, PR_TRANSITIVITY, PR_TRANSITIVITY_STAR, PR_MONOTONICITY, PR_QUANT_INTRO,
|
||||
PR_DISTRIBUTIVITY, PR_AND_ELIM, PR_NOT_OR_ELIM, PR_REWRITE, PR_REWRITE_STAR, PR_PULL_QUANT,
|
||||
|
@ -1060,7 +1060,6 @@ protected:
|
|||
func_decl * m_false_decl;
|
||||
func_decl * m_and_decl;
|
||||
func_decl * m_or_decl;
|
||||
func_decl * m_iff_decl;
|
||||
func_decl * m_xor_decl;
|
||||
func_decl * m_not_decl;
|
||||
func_decl * m_implies_decl;
|
||||
|
@ -1344,9 +1343,9 @@ public:
|
|||
bool is_and(expr const * n) const { return is_app_of(n, m_fid, OP_AND); }
|
||||
bool is_not(expr const * n) const { return is_app_of(n, m_fid, OP_NOT); }
|
||||
bool is_eq(expr const * n) const { return is_app_of(n, m_fid, OP_EQ); }
|
||||
bool is_iff(expr const* n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); }
|
||||
bool is_oeq(expr const * n) const { return is_app_of(n, m_fid, OP_OEQ); }
|
||||
bool is_distinct(expr const * n) const { return is_app_of(n, m_fid, OP_DISTINCT); }
|
||||
bool is_iff(expr const * n) const { return is_app_of(n, m_fid, OP_IFF); }
|
||||
bool is_xor(expr const * n) const { return is_app_of(n, m_fid, OP_XOR); }
|
||||
bool is_ite(expr const * n) const { return is_app_of(n, m_fid, OP_ITE); }
|
||||
bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); }
|
||||
|
@ -1361,7 +1360,6 @@ public:
|
|||
bool is_and(func_decl const * d) const { return is_decl_of(d, m_fid, OP_AND); }
|
||||
bool is_not(func_decl const * d) const { return is_decl_of(d, m_fid, OP_NOT); }
|
||||
bool is_eq(func_decl const * d) const { return is_decl_of(d, m_fid, OP_EQ); }
|
||||
bool is_iff(func_decl const * d) const { return is_decl_of(d, m_fid, OP_IFF); }
|
||||
bool is_xor(func_decl const * d) const { return is_decl_of(d, m_fid, OP_XOR); }
|
||||
bool is_ite(func_decl const * d) const { return is_decl_of(d, m_fid, OP_ITE); }
|
||||
bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); }
|
||||
|
@ -1369,13 +1367,13 @@ public:
|
|||
|
||||
MATCH_UNARY(is_not);
|
||||
MATCH_BINARY(is_eq);
|
||||
MATCH_BINARY(is_iff);
|
||||
MATCH_BINARY(is_implies);
|
||||
MATCH_BINARY(is_and);
|
||||
MATCH_BINARY(is_or);
|
||||
MATCH_BINARY(is_xor);
|
||||
MATCH_TERNARY(is_and);
|
||||
MATCH_TERNARY(is_or);
|
||||
bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); }
|
||||
|
||||
bool is_ite(expr const * n, expr * & t1, expr * & t2, expr * & t3) const;
|
||||
};
|
||||
|
@ -1663,7 +1661,7 @@ public:
|
|||
|
||||
bool is_bool(expr const * n) const;
|
||||
bool is_bool(sort const * s) const { return s == m_bool_sort; }
|
||||
decl_kind get_eq_op(expr const * n) const { return is_bool(n) ? OP_IFF : OP_EQ; }
|
||||
decl_kind get_eq_op(expr const * n) const { return OP_EQ; }
|
||||
|
||||
private:
|
||||
sort * mk_sort(symbol const & name, sort_info * info);
|
||||
|
@ -1746,6 +1744,14 @@ public:
|
|||
arity, domain);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(const char* name, sort * s) {
|
||||
return mk_func_decl(symbol(name), static_cast<unsigned>(0), nullptr, s);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(std::string const& name, sort * s) {
|
||||
return mk_func_decl(symbol(name.c_str()), static_cast<unsigned>(0), nullptr, s);
|
||||
}
|
||||
|
||||
func_decl * mk_const_decl(symbol const & name, sort * s) {
|
||||
return mk_func_decl(name, static_cast<unsigned>(0), nullptr, s);
|
||||
}
|
||||
|
@ -1812,6 +1818,14 @@ public:
|
|||
return mk_const(mk_const_decl(name, s));
|
||||
}
|
||||
|
||||
app * mk_const(std::string const & name, sort * s) {
|
||||
return mk_const(mk_const_decl(name, s));
|
||||
}
|
||||
|
||||
app * mk_const(char const* name, sort * s) {
|
||||
return mk_const(mk_const_decl(name, s));
|
||||
}
|
||||
|
||||
func_decl * mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
|
||||
sort * const * domain, sort * range);
|
||||
|
||||
|
@ -1987,9 +2001,9 @@ public:
|
|||
bool is_and(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_AND); }
|
||||
bool is_not(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_NOT); }
|
||||
bool is_eq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_EQ); }
|
||||
bool is_iff(expr const * n) const { return is_eq(n) && is_bool(to_app(n)->get_arg(0)); }
|
||||
bool is_oeq(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_OEQ); }
|
||||
bool is_distinct(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_DISTINCT); }
|
||||
bool is_iff(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_IFF); }
|
||||
bool is_xor(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_XOR); }
|
||||
bool is_ite(expr const * n) const { return is_app_of(n, m_basic_family_id, OP_ITE); }
|
||||
bool is_term_ite(expr const * n) const { return is_ite(n) && !is_bool(n); }
|
||||
|
@ -2005,7 +2019,7 @@ public:
|
|||
bool is_and(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_AND); }
|
||||
bool is_not(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_NOT); }
|
||||
bool is_eq(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ); }
|
||||
bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_IFF); }
|
||||
bool is_iff(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_EQ) && is_bool(d->get_range()); }
|
||||
bool is_xor(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_XOR); }
|
||||
bool is_ite(func_decl const * d) const { return is_decl_of(d, m_basic_family_id, OP_ITE); }
|
||||
bool is_term_ite(func_decl const * d) const { return is_ite(d) && !is_bool(d->get_range()); }
|
||||
|
@ -2015,7 +2029,6 @@ public:
|
|||
|
||||
MATCH_UNARY(is_not);
|
||||
MATCH_BINARY(is_eq);
|
||||
MATCH_BINARY(is_iff);
|
||||
MATCH_BINARY(is_implies);
|
||||
MATCH_BINARY(is_and);
|
||||
MATCH_BINARY(is_or);
|
||||
|
@ -2023,6 +2036,8 @@ public:
|
|||
MATCH_TERNARY(is_and);
|
||||
MATCH_TERNARY(is_or);
|
||||
|
||||
bool is_iff(expr const* n, expr*& lhs, expr*& rhs) const { return is_eq(n, lhs, rhs) && is_bool(lhs); }
|
||||
|
||||
bool is_ite(expr const* n, expr*& t1, expr*& t2, expr*& t3) const {
|
||||
if (is_ite(n)) {
|
||||
t1 = to_app(n)->get_arg(0);
|
||||
|
@ -2035,7 +2050,7 @@ public:
|
|||
|
||||
public:
|
||||
app * mk_eq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, get_eq_op(lhs), lhs, rhs); }
|
||||
app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_IFF, lhs, rhs); }
|
||||
app * mk_iff(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_EQ, lhs, rhs); }
|
||||
app * mk_oeq(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_OEQ, lhs, rhs); }
|
||||
app * mk_xor(expr * lhs, expr * rhs) { return mk_app(m_basic_family_id, OP_XOR, lhs, rhs); }
|
||||
app * mk_ite(expr * c, expr * t, expr * e) { return mk_app(m_basic_family_id, OP_ITE, c, t, e); }
|
||||
|
@ -2151,6 +2166,23 @@ public:
|
|||
return n > 0 && get_sort(p->get_arg(n - 1)) != m_proof_sort;
|
||||
}
|
||||
expr * get_fact(proof const * p) const { SASSERT(is_proof(p)); SASSERT(has_fact(p)); return p->get_arg(p->get_num_args() - 1); }
|
||||
|
||||
class proof_parents {
|
||||
ast_manager& m;
|
||||
proof * m_proof;
|
||||
public:
|
||||
proof_parents(ast_manager& m, proof * p): m(m), m_proof(p) {}
|
||||
proof * const * begin() const { return (proof* const*)(m_proof->begin()); }
|
||||
proof * const * end() const {
|
||||
unsigned n = m_proof->get_num_args();
|
||||
return (proof* const*)(begin() + (m.has_fact(m_proof) ? n - 1 : n));
|
||||
}
|
||||
};
|
||||
|
||||
proof_parents get_parents(proof* p) {
|
||||
return proof_parents(*this, p);
|
||||
}
|
||||
|
||||
unsigned get_num_parents(proof const * p) const {
|
||||
SASSERT(is_proof(p));
|
||||
unsigned n = p->get_num_args();
|
||||
|
|
|
@ -9,7 +9,8 @@ Abstract: Pretty-printer for proofs in Graphviz format
|
|||
#include "ast/ast_pp_dot.h"
|
||||
|
||||
// string escaping for DOT
|
||||
std::string escape_dot(std::string const & s) {
|
||||
std::string escape_dot(const std::string &s)
|
||||
{
|
||||
std::string res;
|
||||
res.reserve(s.size()); // preallocate
|
||||
for (auto c : s) {
|
||||
|
|
|
@ -4,10 +4,11 @@ Abstract: Pretty-printer for proofs in Graphviz format
|
|||
|
||||
--*/
|
||||
|
||||
#pragma once
|
||||
#ifndef _AST_PP_DOT_
|
||||
#define _AST_PP_DOT_
|
||||
|
||||
#include <iostream>
|
||||
#include "ast_pp.h"
|
||||
#include "ast/ast_pp.h"
|
||||
|
||||
class ast_pp_dot {
|
||||
ast_manager & m_manager;
|
||||
|
@ -21,4 +22,8 @@ class ast_pp_dot {
|
|||
ast_manager & get_manager() const { return m_manager; }
|
||||
};
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p);
|
||||
std::string escape_dot(std::string const & s);
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const ast_pp_dot & p);
|
||||
|
||||
#endif /* AST_PP_DOT */
|
||||
|
|
|
@ -63,10 +63,6 @@ format * smt2_pp_environment::pp_fdecl_name(func_decl * f, unsigned & len) const
|
|||
len = 3;
|
||||
return mk_string(m, "ite");
|
||||
}
|
||||
else if (m.is_iff(f)) {
|
||||
len = 1;
|
||||
return mk_string(m, "=");
|
||||
}
|
||||
else {
|
||||
symbol s = f->get_name();
|
||||
return pp_fdecl_name(s, len, f->is_skolem());
|
||||
|
|
|
@ -225,9 +225,6 @@ class smt_printer {
|
|||
else if (m_manager.is_ite(d)) {
|
||||
m_out << "ite";
|
||||
}
|
||||
else if (m_manager.is_iff(d)) {
|
||||
m_out << "=";
|
||||
}
|
||||
else if (m_manager.is_implies(d)) {
|
||||
m_out << "=>";
|
||||
}
|
||||
|
|
|
@ -98,7 +98,7 @@ bool is_atom(ast_manager & m, expr * n) {
|
|||
return true;
|
||||
SASSERT(is_app(n));
|
||||
if (to_app(n)->get_family_id() != m.get_basic_family_id()) {
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
// the other operators of the basic family are not considered atomic: distinct, ite, and, or, iff, xor, not, implies.
|
||||
return (m.is_eq(n) && !m.is_bool(to_app(n)->get_arg(0))) || m.is_true(n) || m.is_false(n);
|
||||
|
@ -106,7 +106,7 @@ bool is_atom(ast_manager & m, expr * n) {
|
|||
|
||||
|
||||
bool is_literal(ast_manager & m, expr * n) {
|
||||
return
|
||||
return
|
||||
is_atom(m, n) ||
|
||||
(m.is_not(n) && is_atom(m, to_app(n)->get_arg(0)));
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ expr * mk_not(ast_manager & m, expr * arg) {
|
|||
expr * atom;
|
||||
if (m.is_not(arg, atom))
|
||||
return atom;
|
||||
else if (m.is_true(arg))
|
||||
else if (m.is_true(arg))
|
||||
return m.mk_false();
|
||||
else if (m.is_false(arg))
|
||||
return m.mk_true();
|
||||
|
@ -195,6 +195,11 @@ expr * mk_not(ast_manager & m, expr * arg) {
|
|||
return m.mk_not(arg);
|
||||
}
|
||||
|
||||
expr_ref mk_not(const expr_ref& e) {
|
||||
return expr_ref(mk_not(e.m(), e), e.m());
|
||||
}
|
||||
|
||||
|
||||
expr_ref push_not(const expr_ref& e) {
|
||||
ast_manager& m = e.get_manager();
|
||||
if (!is_app(e)) {
|
||||
|
@ -221,7 +226,7 @@ expr_ref push_not(const expr_ref& e) {
|
|||
}
|
||||
return mk_and(args);
|
||||
}
|
||||
return expr_ref(mk_not(m, e), m);
|
||||
return expr_ref(mk_not(m, e), m);
|
||||
}
|
||||
|
||||
expr * expand_distinct(ast_manager & m, unsigned num_args, expr * const * args) {
|
||||
|
@ -277,7 +282,7 @@ void flatten_and(expr_ref_vector& result) {
|
|||
}
|
||||
result[i] = result.back();
|
||||
result.pop_back();
|
||||
--i;
|
||||
--i;
|
||||
}
|
||||
else if (m.is_not(result[i].get(), e1) && m.is_implies(e1,e2,e3)) {
|
||||
result.push_back(e2);
|
||||
|
@ -289,7 +294,7 @@ void flatten_and(expr_ref_vector& result) {
|
|||
m.is_false(e1))) {
|
||||
result[i] = result.back();
|
||||
result.pop_back();
|
||||
--i;
|
||||
--i;
|
||||
}
|
||||
else if (m.is_false(result[i].get()) ||
|
||||
(m.is_not(result[i].get(), e1) &&
|
||||
|
@ -303,10 +308,17 @@ void flatten_and(expr_ref_vector& result) {
|
|||
|
||||
void flatten_and(expr* fml, expr_ref_vector& result) {
|
||||
SASSERT(result.get_manager().is_bool(fml));
|
||||
result.push_back(fml);
|
||||
result.push_back(fml);
|
||||
flatten_and(result);
|
||||
}
|
||||
|
||||
void flatten_and(expr_ref& fml) {
|
||||
expr_ref_vector fmls(fml.get_manager());
|
||||
fmls.push_back(fml);
|
||||
flatten_and(fmls);
|
||||
fml = mk_and(fmls);
|
||||
}
|
||||
|
||||
void flatten_or(expr_ref_vector& result) {
|
||||
ast_manager& m = result.get_manager();
|
||||
expr* e1, *e2, *e3;
|
||||
|
@ -333,7 +345,7 @@ void flatten_or(expr_ref_vector& result) {
|
|||
}
|
||||
result[i] = result.back();
|
||||
result.pop_back();
|
||||
--i;
|
||||
--i;
|
||||
}
|
||||
else if (m.is_implies(result[i].get(),e2,e3)) {
|
||||
result.push_back(e3);
|
||||
|
@ -345,7 +357,7 @@ void flatten_or(expr_ref_vector& result) {
|
|||
m.is_true(e1))) {
|
||||
result[i] = result.back();
|
||||
result.pop_back();
|
||||
--i;
|
||||
--i;
|
||||
}
|
||||
else if (m.is_true(result[i].get()) ||
|
||||
(m.is_not(result[i].get(), e1) &&
|
||||
|
@ -354,12 +366,12 @@ void flatten_or(expr_ref_vector& result) {
|
|||
result.push_back(m.mk_true());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void flatten_or(expr* fml, expr_ref_vector& result) {
|
||||
SASSERT(result.get_manager().is_bool(fml));
|
||||
result.push_back(fml);
|
||||
result.push_back(fml);
|
||||
flatten_or(result);
|
||||
}
|
||||
|
|
|
@ -127,6 +127,8 @@ inline expr_ref mk_or(expr_ref_vector const& args) { return expr_ref(mk_or(args.
|
|||
*/
|
||||
expr * mk_not(ast_manager & m, expr * arg);
|
||||
|
||||
expr_ref mk_not(const expr_ref& e);
|
||||
|
||||
/**
|
||||
Negate and push over conjunction or disjunction.
|
||||
*/
|
||||
|
@ -150,6 +152,8 @@ expr_ref mk_distinct(expr_ref_vector const& args);
|
|||
|
||||
void flatten_and(expr_ref_vector& result);
|
||||
|
||||
void flatten_and(expr_ref& fml);
|
||||
|
||||
void flatten_and(expr* fml, expr_ref_vector& result);
|
||||
|
||||
void flatten_or(expr_ref_vector& result);
|
||||
|
@ -158,4 +162,3 @@ void flatten_or(expr* fml, expr_ref_vector& result);
|
|||
|
||||
|
||||
#endif /* AST_UTIL_H_ */
|
||||
|
||||
|
|
|
@ -358,8 +358,10 @@ namespace datatype {
|
|||
bool is_accessor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_ACCESSOR); }
|
||||
bool is_update_field(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
|
||||
bool is_constructor(app * f) const { return is_app_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
|
||||
bool is_constructor(expr* e) const { return is_app(e) && is_constructor(to_app(e)); }
|
||||
bool is_recognizer0(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER);}
|
||||
bool is_is(app * f) const { return is_app_of(f, m_family_id, OP_DT_IS);}
|
||||
bool is_is(expr * e) const { return is_app(e) && is_is(to_app(e)); }
|
||||
bool is_recognizer(app * f) const { return is_recognizer0(f) || is_is(f); }
|
||||
bool is_accessor(app * f) const { return is_app_of(f, m_family_id, OP_DT_ACCESSOR); }
|
||||
bool is_update_field(app * f) const { return is_app_of(f, m_family_id, OP_DT_UPDATE_FIELD); }
|
||||
|
|
|
@ -27,7 +27,9 @@ Revision History:
|
|||
|
||||
#include "ast/factor_equivs.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
/**
|
||||
Factors input vector v into equivalence classes and the rest
|
||||
*/
|
||||
|
@ -59,8 +61,8 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) {
|
|||
equiv.merge(e1, e2);
|
||||
}
|
||||
else {
|
||||
if (j < i) {
|
||||
v[j] = v.get(i);
|
||||
if (j < i) {
|
||||
v[j] = v.get(i);
|
||||
}
|
||||
j++;
|
||||
}
|
||||
|
@ -68,19 +70,52 @@ void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv) {
|
|||
v.shrink(j);
|
||||
}
|
||||
|
||||
/**
|
||||
* Chooses a representative of an equivalence class
|
||||
*/
|
||||
expr *choose_rep(expr_equiv_class::eq_class &clazz, ast_manager &m) {
|
||||
expr *rep = nullptr;
|
||||
unsigned rep_sz, elem_sz;
|
||||
for (expr *elem : clazz) {
|
||||
if (!m.is_value(elem)) {
|
||||
elem_sz = get_num_exprs(elem);
|
||||
if (!rep || (rep && rep_sz > elem_sz)) {
|
||||
rep = elem;
|
||||
rep_sz = elem_sz;
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("equiv",
|
||||
tout << "Rep: " << mk_pp(rep, m) << "\n";
|
||||
for (expr *el : clazz)
|
||||
tout << mk_pp(el, m) << "\n";
|
||||
tout << "RepEnd\n";);
|
||||
|
||||
return rep;
|
||||
}
|
||||
|
||||
void rewrite_eqs (expr_ref_vector &v, expr_equiv_class &equiv) {
|
||||
ast_manager &m = v.m();
|
||||
expr_safe_replace sub(m);
|
||||
for (auto eq_class : equiv) {
|
||||
expr *rep = choose_rep(eq_class, m);
|
||||
for (expr *el : eq_class) {
|
||||
if (el != rep) {
|
||||
sub.insert (el, rep);
|
||||
}
|
||||
}
|
||||
}
|
||||
sub(v);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* converts equivalence classes to equalities
|
||||
*/
|
||||
void equiv_to_expr(expr_equiv_class &equiv, expr_ref_vector &out) {
|
||||
ast_manager &m = out.get_manager();
|
||||
for (auto eq_class : equiv) {
|
||||
expr *rep = nullptr;
|
||||
for (expr *elem : eq_class) {
|
||||
if (!m.is_value(elem)) {
|
||||
rep = elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
expr *rep = choose_rep(eq_class, m);
|
||||
SASSERT(rep);
|
||||
for (expr *elem : eq_class) {
|
||||
if (rep != elem) {
|
||||
|
|
|
@ -60,6 +60,8 @@ public:
|
|||
|
||||
obj_equiv_class(Manager& m) : m_to_obj(m) {}
|
||||
|
||||
Manager &m() const {return m_to_obj.m();}
|
||||
|
||||
void add_elem(OBJ*o) {
|
||||
SASSERT(!m_to_int.find(o));
|
||||
add_elem_impl(o);
|
||||
|
@ -169,6 +171,11 @@ typedef obj_equiv_class<expr, ast_manager> expr_equiv_class;
|
|||
* Factors input vector v into equivalence classes and the rest
|
||||
*/
|
||||
void factor_eqs(expr_ref_vector &v, expr_equiv_class &equiv);
|
||||
/**
|
||||
* Rewrite expressions in v by choosing a representative from the
|
||||
* equivalence class.
|
||||
*/
|
||||
void rewrite_eqs(expr_ref_vector &v, expr_equiv_class &equiv);
|
||||
/**
|
||||
* converts equivalence classes to equalities
|
||||
*/
|
||||
|
|
|
@ -128,6 +128,20 @@ void for_each_expr(ForEachProc & proc, expr * n) {
|
|||
for_each_expr_core<ForEachProc, expr_mark, false, false>(proc, visited, n);
|
||||
}
|
||||
|
||||
template<typename ForEachProc>
|
||||
void for_each_expr(ForEachProc & proc, unsigned n, expr * const* es) {
|
||||
expr_mark visited;
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
for_each_expr_core<ForEachProc, expr_mark, false, false>(proc, visited, es[i]);
|
||||
}
|
||||
|
||||
template<typename ForEachProc>
|
||||
void for_each_expr(ForEachProc & proc, expr_ref_vector const& es) {
|
||||
expr_mark visited;
|
||||
for (expr* e : es)
|
||||
for_each_expr_core<ForEachProc, expr_mark, false, false>(proc, visited, e);
|
||||
}
|
||||
|
||||
template<typename ForEachProc>
|
||||
void quick_for_each_expr(ForEachProc & proc, expr_fast_mark1 & visited, expr * n) {
|
||||
for_each_expr_core<ForEachProc, expr_fast_mark1, false, false>(proc, visited, n);
|
||||
|
|
|
@ -77,7 +77,7 @@ void macro_substitution::cleanup() {
|
|||
void macro_substitution::insert(func_decl * f, quantifier * q, proof * pr, expr_dependency * dep) {
|
||||
DEBUG_CODE({
|
||||
app * body = to_app(q->get_expr());
|
||||
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
|
||||
SASSERT(m_manager.is_eq(body));
|
||||
expr * lhs = body->get_arg(0);
|
||||
expr * rhs = body->get_arg(1);
|
||||
SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f));
|
||||
|
@ -146,7 +146,7 @@ void macro_substitution::erase(func_decl * f) {
|
|||
|
||||
void macro_substitution::get_head_def(quantifier * q, func_decl * f, app * & head, expr * & def) {
|
||||
app * body = to_app(q->get_expr());
|
||||
SASSERT(m_manager.is_eq(body) || m_manager.is_iff(body));
|
||||
SASSERT(m_manager.is_eq(body));
|
||||
expr * lhs = to_app(body)->get_arg(0);
|
||||
expr * rhs = to_app(body)->get_arg(1);
|
||||
SASSERT(is_app_of(lhs, f) || is_app_of(rhs, f));
|
||||
|
|
|
@ -169,9 +169,8 @@ void macro_manager::mark_forbidden(unsigned n, justified_expr const * exprs) {
|
|||
|
||||
void macro_manager::get_head_def(quantifier * q, func_decl * d, app * & head, expr * & def) const {
|
||||
app * body = to_app(q->get_expr());
|
||||
SASSERT(m.is_eq(body) || m.is_iff(body));
|
||||
expr * lhs = to_app(body)->get_arg(0);
|
||||
expr * rhs = to_app(body)->get_arg(1);
|
||||
expr * lhs = nullptr, *rhs = nullptr;
|
||||
VERIFY(m.is_eq(body, lhs, rhs));
|
||||
SASSERT(is_app_of(lhs, d) || is_app_of(rhs, d));
|
||||
SASSERT(!is_app_of(lhs, d) || !is_app_of(rhs, d));
|
||||
if (is_app_of(lhs, d)) {
|
||||
|
|
|
@ -175,7 +175,7 @@ bool macro_util::is_macro_head(expr * n, unsigned num_decls) const {
|
|||
|
||||
*/
|
||||
bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
|
||||
if (m_manager.is_eq(n) || m_manager.is_iff(n)) {
|
||||
if (m_manager.is_eq(n)) {
|
||||
expr * lhs = to_app(n)->get_arg(0);
|
||||
expr * rhs = to_app(n)->get_arg(1);
|
||||
if (is_macro_head(lhs, num_decls) && !is_forbidden(to_app(lhs)->get_decl()) &&
|
||||
|
@ -207,7 +207,7 @@ bool macro_util::is_left_simple_macro(expr * n, unsigned num_decls, app_ref & he
|
|||
|
||||
*/
|
||||
bool macro_util::is_right_simple_macro(expr * n, unsigned num_decls, app_ref & head, expr_ref & def) const {
|
||||
if (m_manager.is_eq(n) || m_manager.is_iff(n)) {
|
||||
if (m_manager.is_eq(n)) {
|
||||
expr * lhs = to_app(n)->get_arg(0);
|
||||
expr * rhs = to_app(n)->get_arg(1);
|
||||
if (is_macro_head(rhs, num_decls) && !is_forbidden(to_app(rhs)->get_decl()) &&
|
||||
|
@ -339,10 +339,9 @@ bool macro_util::is_pseudo_predicate_macro(expr * n, app_ref & head, app_ref & t
|
|||
TRACE("macro_util", tout << "processing: " << mk_pp(n, m_manager) << "\n";);
|
||||
expr * body = to_quantifier(n)->get_expr();
|
||||
unsigned num_decls = to_quantifier(n)->get_num_decls();
|
||||
if (!m_manager.is_iff(body))
|
||||
expr * lhs, *rhs;
|
||||
if (!m_manager.is_iff(body, lhs, rhs))
|
||||
return false;
|
||||
expr * lhs = to_app(body)->get_arg(0);
|
||||
expr * rhs = to_app(body)->get_arg(1);
|
||||
if (is_pseudo_head(lhs, num_decls, head, t) &&
|
||||
!is_forbidden(head->get_decl()) &&
|
||||
!occurs(head->get_decl(), rhs)) {
|
||||
|
|
|
@ -158,7 +158,7 @@ bool quasi_macros::is_quasi_macro(expr * e, app_ref & a, expr_ref & t) const {
|
|||
if (is_quantifier(e) && to_quantifier(e)->is_forall()) {
|
||||
quantifier * q = to_quantifier(e);
|
||||
expr * qe = q->get_expr();
|
||||
if ((m_manager.is_eq(qe) || m_manager.is_iff(qe))) {
|
||||
if ((m_manager.is_eq(qe))) {
|
||||
expr * lhs = to_app(qe)->get_arg(0);
|
||||
expr * rhs = to_app(qe)->get_arg(1);
|
||||
|
||||
|
|
|
@ -582,7 +582,7 @@ struct nnf::imp {
|
|||
return true;
|
||||
}
|
||||
|
||||
bool is_eq(app * t) const { return m().is_eq(t) || m().is_iff(t); }
|
||||
bool is_eq(app * t) const { return m().is_eq(t); }
|
||||
|
||||
bool process_iff_xor(app * t, frame & fr) {
|
||||
SASSERT(t->get_num_args() == 2);
|
||||
|
@ -630,7 +630,7 @@ struct nnf::imp {
|
|||
}
|
||||
|
||||
bool process_eq(app * t, frame & fr) {
|
||||
if (m().is_bool(t->get_arg(0)))
|
||||
if (m().is_iff(t))
|
||||
return process_iff_xor(t, fr);
|
||||
else
|
||||
return process_default(t, fr);
|
||||
|
@ -725,7 +725,6 @@ struct nnf::imp {
|
|||
return process_implies(t, fr);
|
||||
case OP_ITE:
|
||||
return process_ite(t, fr);
|
||||
case OP_IFF:
|
||||
case OP_XOR:
|
||||
return process_iff_xor(t, fr);
|
||||
case OP_EQ:
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
|
||||
/*++
|
||||
Copyright (c) 2015 Microsoft Corporation
|
||||
|
||||
|
@ -12,11 +11,11 @@ Copyright (c) 2015 Microsoft Corporation
|
|||
#include "ast/rewriter/th_rewriter.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
|
||||
#define IS_EQUIV(_e_) (m.is_eq(_e_) || m.is_iff(_e_))
|
||||
#define IS_EQUIV(_e_) m.is_eq(_e_)
|
||||
|
||||
#define SAME_OP(_d1_, _d2_) ((_d1_ == _d2_) || (IS_EQUIV(_d1_) && IS_EQUIV(_d2_)))
|
||||
|
||||
proof_checker::hyp_decl_plugin::hyp_decl_plugin() :
|
||||
proof_checker::hyp_decl_plugin::hyp_decl_plugin() :
|
||||
m_cons(nullptr),
|
||||
m_atom(nullptr),
|
||||
m_nil(nullptr),
|
||||
|
@ -59,13 +58,13 @@ func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(decl_kind k) {
|
|||
}
|
||||
|
||||
func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(
|
||||
decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) {
|
||||
return mk_func_decl(k);
|
||||
}
|
||||
|
||||
func_decl * proof_checker::hyp_decl_plugin::mk_func_decl(
|
||||
decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned num_args, expr * const * args, sort * range) {
|
||||
return mk_func_decl(k);
|
||||
}
|
||||
|
@ -84,8 +83,8 @@ void proof_checker::hyp_decl_plugin::get_sort_names(svector<builtin_name> & sort
|
|||
}
|
||||
}
|
||||
|
||||
proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m),
|
||||
m_dump_lemmas(false), m_logic("AUFLIA"), m_proof_lemma_id(0) {
|
||||
proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pinned(m), m_nil(m),
|
||||
m_dump_lemmas(false), m_logic("AUFLIRA"), m_proof_lemma_id(0) {
|
||||
symbol fam_name("proof_hypothesis");
|
||||
if (!m.has_plugin(fam_name)) {
|
||||
m.register_plugin(fam_name, alloc(hyp_decl_plugin));
|
||||
|
@ -98,16 +97,16 @@ proof_checker::proof_checker(ast_manager& m) : m(m), m_todo(m), m_marked(), m_pi
|
|||
bool proof_checker::check(proof* p, expr_ref_vector& side_conditions) {
|
||||
proof_ref curr(m);
|
||||
m_todo.push_back(p);
|
||||
|
||||
|
||||
bool result = true;
|
||||
while (result && !m_todo.empty()) {
|
||||
curr = m_todo.back();
|
||||
m_todo.pop_back();
|
||||
result = check1(curr.get(), side_conditions);
|
||||
if (!result) {
|
||||
IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get()););
|
||||
IF_VERBOSE(0, ast_ll_pp(verbose_stream() << "Proof check failed\n", m, curr.get()););
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_hypotheses.reset();
|
||||
|
@ -157,10 +156,10 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
case PR_SPC_REWRITE:
|
||||
case PR_SUPERPOSITION:
|
||||
case PR_SPC_REWRITE:
|
||||
case PR_SUPERPOSITION:
|
||||
case PR_EQUALITY_RESOLUTION:
|
||||
case PR_SPC_RESOLUTION:
|
||||
case PR_SPC_RESOLUTION:
|
||||
case PR_FACTORING:
|
||||
case PR_SPC_DER: {
|
||||
if (match_fact(p, fact)) {
|
||||
|
@ -176,7 +175,7 @@ bool proof_checker::check1_spc(proof* p, expr_ref_vector& side_conditions) {
|
|||
side_conditions.push_back(rewrite_cond.get());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -201,7 +200,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
func_decl_ref f1(m), f2(m);
|
||||
expr_ref_vector terms1(m), terms2(m), terms(m);
|
||||
sort_ref_vector decls1(m), decls2(m);
|
||||
|
||||
|
||||
if (match_proof(p, proofs)) {
|
||||
for (unsigned i = 0; i < proofs.size(); ++i) {
|
||||
add_premise(proofs.get(i));
|
||||
|
@ -211,6 +210,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
switch(k) {
|
||||
case PR_UNDEF:
|
||||
return true;
|
||||
case PR_TRUE:
|
||||
return true;
|
||||
case PR_ASSERTED:
|
||||
return true;
|
||||
case PR_GOAL:
|
||||
|
@ -229,8 +230,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
return false;
|
||||
}
|
||||
case PR_REFLEXIVITY: {
|
||||
if (match_fact(p, fact) &&
|
||||
match_proof(p) &&
|
||||
if (match_fact(p, fact) &&
|
||||
match_proof(p) &&
|
||||
(match_equiv(fact, t1, t2) || match_oeq(fact, t1, t2)) &&
|
||||
(t1.get() == t2.get())) {
|
||||
return true;
|
||||
|
@ -243,7 +244,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
match_proof(p, p1) &&
|
||||
match_fact(p1.get(), fml) &&
|
||||
match_binary(fact.get(), d1, l1, r1) &&
|
||||
match_binary(fml.get(), d2, l2, r2) &&
|
||||
match_binary(fml.get(), d2, l2, r2) &&
|
||||
SAME_OP(d1.get(), d2.get()) &&
|
||||
l1.get() == r2.get() &&
|
||||
r1.get() == l2.get()) {
|
||||
|
@ -271,7 +272,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case PR_TRANSITIVITY_STAR: {
|
||||
if (match_fact(p, fact) &&
|
||||
match_binary(fact.get(), d1, t1, t2)) {
|
||||
|
@ -292,14 +293,14 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
return
|
||||
return
|
||||
vertices.size() == 2 &&
|
||||
vertices.contains(t1->get_id()) &&
|
||||
vertices.contains(t2->get_id());
|
||||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case PR_MONOTONICITY: {
|
||||
TRACE("proof_checker", tout << mk_bounded_pp(p, m, 3) << "\n";);
|
||||
if (match_fact(p, fact) &&
|
||||
|
@ -315,7 +316,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
if (term1 != term2) {
|
||||
bool found = false;
|
||||
for(unsigned j = 0; j < proofs.size() && !found; ++j) {
|
||||
found =
|
||||
found =
|
||||
match_fact(proofs[j].get(), fml) &&
|
||||
match_binary(fml.get(), d2, s1, s2) &&
|
||||
SAME_OP(d1.get(), d2.get()) &&
|
||||
|
@ -351,7 +352,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
for (unsigned i = 0; i < q1->get_num_decls(); ++i) {
|
||||
if (q1->get_decl_sort(i) != q2->get_decl_sort(i)) {
|
||||
// term is not well-typed.
|
||||
UNREACHABLE();
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -408,7 +409,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
side_conditions.push_back(fact.get());
|
||||
return true;
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m););
|
||||
IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m););
|
||||
return false;
|
||||
}
|
||||
case PR_REWRITE_STAR: {
|
||||
|
@ -426,8 +427,8 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
side_conditions.push_back(rewrite_cond.get());
|
||||
return true;
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m););
|
||||
return false;
|
||||
IF_VERBOSE(0, verbose_stream() << "Expected proof of equality:\n" << mk_bounded_pp(p, m););
|
||||
return false;
|
||||
}
|
||||
case PR_PULL_QUANT: {
|
||||
if (match_proof(p) &&
|
||||
|
@ -437,7 +438,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
// TBD: check the enchilada.
|
||||
return true;
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m););
|
||||
IF_VERBOSE(0, verbose_stream() << "Expected proof of equivalence with a quantifier:\n" << mk_bounded_pp(p, m););
|
||||
return false;
|
||||
}
|
||||
case PR_PUSH_QUANT: {
|
||||
|
@ -457,7 +458,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -468,7 +469,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
match_fact(p, fact) &&
|
||||
match_iff(fact.get(), t1, t2)) {
|
||||
// TBD:
|
||||
// match_quantifier(t1.get(), is_forall1, decls1, body1)
|
||||
// match_quantifier(t1.get(), is_forall1, decls1, body1)
|
||||
// filter out decls1 that occur in body1.
|
||||
// if list is empty, then t2 could be just body1.
|
||||
// otherwise t2 is also a quantifier.
|
||||
|
@ -487,7 +488,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
// TBD: check that terms are set of equalities.
|
||||
// t2 is an instance of a predicate in terms1
|
||||
return true;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "does not match last rule: " << mk_pp(p, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
|
@ -509,7 +510,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
get_hypotheses(p1.get(), hypotheses);
|
||||
if (hypotheses.size() == 1 && match_negated(hypotheses.get(0), fact)) {
|
||||
// Suppose fact is (or a b c) and hypothesis is (not (or a b c))
|
||||
// That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause,
|
||||
// That is, (or a b c) should be viewed as a 'quoted expression' and a unary clause,
|
||||
// instead of a clause with three literals.
|
||||
return true;
|
||||
}
|
||||
|
@ -533,7 +534,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
});
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
TRACE("proof_checker", tout << "Matched:\n";
|
||||
ast_ll_pp(tout, m, hypotheses[i].get());
|
||||
ast_ll_pp(tout, m, ors[j-1].get()););
|
||||
|
@ -548,7 +549,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
proofs.size() == 2 &&
|
||||
match_fact(proofs[0].get(), fml1) &&
|
||||
match_fact(proofs[1].get(), fml2) &&
|
||||
match_negated(fml1.get(), fml2.get()) &&
|
||||
m.is_complement(fml1.get(), fml2.get()) &&
|
||||
m.is_false(fact.get())) {
|
||||
return true;
|
||||
}
|
||||
|
@ -562,7 +563,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
bool found = false;
|
||||
for (unsigned j = 0; !found && j < terms1.size(); ++j) {
|
||||
if (match_negated(terms1.get(j), fml2)) {
|
||||
if (m.is_complement(terms1.get(j), fml2)) {
|
||||
found = true;
|
||||
if (j + 1 < terms1.size()) {
|
||||
terms1[j] = terms1.get(terms1.size()-1);
|
||||
|
@ -571,7 +572,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
}
|
||||
if (!found) {
|
||||
TRACE("pr_unit_bug",
|
||||
TRACE("pr_unit_bug",
|
||||
tout << "Parents:\n";
|
||||
for (unsigned i = 0; i < proofs.size(); i++) {
|
||||
expr_ref p(m);
|
||||
|
@ -590,9 +591,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
}
|
||||
switch(terms1.size()) {
|
||||
case 0:
|
||||
case 0:
|
||||
return m.is_false(fact.get());
|
||||
case 1:
|
||||
case 1:
|
||||
return fact.get() == terms1[0].get();
|
||||
default: {
|
||||
if (match_or(fact.get(), terms2)) {
|
||||
|
@ -615,7 +616,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -633,7 +634,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
case PR_IFF_FALSE: {
|
||||
// iff_false(?rule(?p1, (not ?fml)), (iff ?fml false))
|
||||
if (match_proof(p, p1) &&
|
||||
|
@ -676,11 +677,11 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
}
|
||||
case PR_DEF_INTRO: {
|
||||
// def_intro(?fml)
|
||||
//
|
||||
//
|
||||
// ?fml: forall x . ~p(x) or e(x) and forall x . ~e(x) or p(x)
|
||||
// : forall x . ~cond(x) or f(x) = then(x) and forall x . cond(x) or f(x) = else(x)
|
||||
// : forall x . f(x) = e(x)
|
||||
//
|
||||
//
|
||||
if (match_fact(p, fact) &&
|
||||
match_proof(p) &&
|
||||
m.is_bool(fact.get())) {
|
||||
|
@ -710,7 +711,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
return true;
|
||||
}
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
case PR_NNF_POS: {
|
||||
// TBD:
|
||||
|
@ -733,9 +734,9 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
is_forall = true;
|
||||
}
|
||||
if (is_quantifier(e)) {
|
||||
q = to_quantifier(e);
|
||||
q = to_quantifier(e);
|
||||
// TBD check that quantifier is properly instantiated
|
||||
return is_forall == q->is_forall();
|
||||
return is_forall == q->is_forall();
|
||||
}
|
||||
}
|
||||
UNREACHABLE();
|
||||
|
@ -786,7 +787,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
vs(premise, sub.size(), sub.c_ptr(), premise);
|
||||
}
|
||||
fmls.push_back(premise.get());
|
||||
TRACE("proof_checker",
|
||||
TRACE("proof_checker",
|
||||
tout << mk_pp(premise.get(), m) << "\n";
|
||||
for (unsigned j = 0; j < sub.size(); ++j) {
|
||||
tout << mk_pp(sub[j], m) << " ";
|
||||
|
@ -808,7 +809,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
// ok
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" <<
|
||||
IF_VERBOSE(0, verbose_stream() << "Could not establish complementarity for:\n" <<
|
||||
mk_pp(lit1, m) << "\n" << mk_pp(lit2, m) << "\n" << mk_pp(p, m) << "\n";);
|
||||
}
|
||||
fmls[i] = premise1;
|
||||
|
@ -823,7 +824,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
else {
|
||||
premise0 = m.mk_iff(premise0, conclusion);
|
||||
}
|
||||
side_conditions.push_back(premise0);
|
||||
side_conditions.push_back(premise0);
|
||||
return true;
|
||||
}
|
||||
default:
|
||||
|
@ -839,7 +840,7 @@ bool proof_checker::check1_basic(proof* p, expr_ref_vector& side_conditions) {
|
|||
(=> (and ln+1 ln+2 .. ln+m) l0)
|
||||
or in the most general (ground) form:
|
||||
(=> (and ln+1 ln+2 .. ln+m) (or l0 l1 .. ln-1))
|
||||
In other words we use the following (Prolog style) convention for Horn
|
||||
In other words we use the following (Prolog style) convention for Horn
|
||||
implications:
|
||||
The head of a Horn implication is position 0,
|
||||
the first conjunct in the body of an implication is position 1
|
||||
|
@ -851,7 +852,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) {
|
|||
app* a = to_app(e);
|
||||
expr* head, *body;
|
||||
expr_ref_vector args(m);
|
||||
if (m.is_or(e)) {
|
||||
if (m.is_or(e)) {
|
||||
SASSERT(position < a->get_num_args());
|
||||
args.append(a->get_num_args(), a->get_args());
|
||||
lit = args[position].get();
|
||||
|
@ -863,13 +864,13 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) {
|
|||
unsigned num_heads = 1;
|
||||
if (m.is_or(head)) {
|
||||
num_heads = to_app(head)->get_num_args();
|
||||
heads = to_app(head)->get_args();
|
||||
heads = to_app(head)->get_args();
|
||||
}
|
||||
expr*const* bodies = &body;
|
||||
unsigned num_bodies = 1;
|
||||
if (m.is_and(body)) {
|
||||
num_bodies = to_app(body)->get_num_args();
|
||||
bodies = to_app(body)->get_args();
|
||||
bodies = to_app(body)->get_args();
|
||||
}
|
||||
if (position < num_heads) {
|
||||
args.append(num_heads, heads);
|
||||
|
@ -882,7 +883,7 @@ void proof_checker::set_false(expr_ref& e, unsigned position, expr_ref& lit) {
|
|||
args.append(num_bodies, bodies);
|
||||
lit = m.mk_not(args[position].get());
|
||||
args[position] = m.mk_true();
|
||||
e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head);
|
||||
e = m.mk_implies(m.mk_and(args.size(), args.c_ptr()), head);
|
||||
}
|
||||
}
|
||||
else if (position == 0) {
|
||||
|
@ -912,7 +913,7 @@ void proof_checker::add_premise(proof* p) {
|
|||
}
|
||||
|
||||
bool proof_checker::match_proof(proof const* p) const {
|
||||
return
|
||||
return
|
||||
m.is_proof(p) &&
|
||||
m.get_num_parents(p) == 0;
|
||||
}
|
||||
|
@ -925,7 +926,7 @@ bool proof_checker::match_proof(proof const* p, proof_ref& p0) const {
|
|||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
bool proof_checker::match_proof(proof const* p, proof_ref& p0, proof_ref& p1) const {
|
||||
if (m.is_proof(p) &&
|
||||
m.get_num_parents(p) == 2) {
|
||||
|
@ -1032,7 +1033,7 @@ bool proof_checker::match_and(expr const* e, expr_ref_vector& terms) const {
|
|||
}
|
||||
|
||||
bool proof_checker::match_iff(expr const* e, expr_ref& t1, expr_ref& t2) const {
|
||||
return match_op(e, OP_IFF, t1, t2);
|
||||
return match_op(e, OP_EQ, t1, t2) && m.is_bool(t1);
|
||||
}
|
||||
|
||||
bool proof_checker::match_equiv(expr const* e, expr_ref& t1, expr_ref& t2) const {
|
||||
|
@ -1044,7 +1045,7 @@ bool proof_checker::match_implies(expr const* e, expr_ref& t1, expr_ref& t2) con
|
|||
}
|
||||
|
||||
bool proof_checker::match_eq(expr const* e, expr_ref& t1, expr_ref& t2) const {
|
||||
return match_op(e, OP_EQ, t1, t2) || match_iff(e, t1, t2);
|
||||
return match_op(e, OP_EQ, t1, t2);
|
||||
}
|
||||
|
||||
bool proof_checker::match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const {
|
||||
|
@ -1053,7 +1054,7 @@ bool proof_checker::match_oeq(expr const* e, expr_ref& t1, expr_ref& t2) const {
|
|||
|
||||
bool proof_checker::match_negated(expr const* a, expr* b) const {
|
||||
expr_ref t(m);
|
||||
return
|
||||
return
|
||||
(match_not(a, t) && t.get() == b) ||
|
||||
(match_not(b, t) && t.get() == a);
|
||||
}
|
||||
|
@ -1080,7 +1081,7 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) {
|
|||
p = stack.back();
|
||||
SASSERT(m.is_proof(p));
|
||||
if (m_hypotheses.contains(p)) {
|
||||
stack.pop_back();
|
||||
stack.pop_back();
|
||||
continue;
|
||||
}
|
||||
if (is_hypothesis(p) && match_fact(p, hyp)) {
|
||||
|
@ -1122,13 +1123,13 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) {
|
|||
if (!m_hypotheses.find(p, h)) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
|
||||
ptr_buffer<expr> hyps;
|
||||
ptr_buffer<expr> todo;
|
||||
expr_mark mark;
|
||||
todo.push_back(h);
|
||||
expr_ref a(m), b(m);
|
||||
|
||||
|
||||
while (!todo.empty()) {
|
||||
h = todo.back();
|
||||
|
||||
|
@ -1153,14 +1154,14 @@ void proof_checker::get_hypotheses(proof* p, expr_ref_vector& ante) {
|
|||
ast_ll_pp(tout << "Getting hypotheses from: ", m, p);
|
||||
tout << "Found hypotheses:\n";
|
||||
for (unsigned i = 0; i < ante.size(); ++i) {
|
||||
ast_ll_pp(tout, m, ante[i].get());
|
||||
ast_ll_pp(tout, m, ante[i].get());
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
bool proof_checker::match_nil(expr const* e) const {
|
||||
return
|
||||
return
|
||||
is_app(e) &&
|
||||
to_app(e)->get_family_id() == m_hyp_fid &&
|
||||
to_app(e)->get_decl_kind() == OP_NIL;
|
||||
|
@ -1201,7 +1202,7 @@ expr* proof_checker::mk_nil() {
|
|||
}
|
||||
|
||||
bool proof_checker::is_hypothesis(proof const* p) const {
|
||||
return
|
||||
return
|
||||
m.is_proof(p) &&
|
||||
p->get_decl_kind() == PR_HYPOTHESIS;
|
||||
}
|
||||
|
@ -1223,7 +1224,7 @@ expr* proof_checker::mk_hyp(unsigned num_hyps, expr * const * hyps) {
|
|||
}
|
||||
else {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void proof_checker::dump_proof(proof const* pr) {
|
||||
|
@ -1244,9 +1245,9 @@ void proof_checker::dump_proof(proof const* pr) {
|
|||
void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecedents, expr * consequent) {
|
||||
char buffer[128];
|
||||
#ifdef _WINDOWS
|
||||
sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt", m_proof_lemma_id);
|
||||
sprintf_s(buffer, ARRAYSIZE(buffer), "proof_lemma_%d.smt2", m_proof_lemma_id);
|
||||
#else
|
||||
sprintf(buffer, "proof_lemma_%d.smt", m_proof_lemma_id);
|
||||
sprintf(buffer, "proof_lemma_%d.smt2", m_proof_lemma_id);
|
||||
#endif
|
||||
std::ofstream out(buffer);
|
||||
ast_smt_pp pp(m);
|
||||
|
@ -1265,7 +1266,7 @@ void proof_checker::dump_proof(unsigned num_antecedents, expr * const * antecede
|
|||
bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const& coeff, expr_ref& sum, bool& is_strict) {
|
||||
arith_util a(m);
|
||||
app* lit = lit0;
|
||||
|
||||
|
||||
if (m.is_not(lit)) {
|
||||
lit = to_app(lit->get_arg(0));
|
||||
is_pos = !is_pos;
|
||||
|
@ -1277,6 +1278,10 @@ bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const&
|
|||
SASSERT(lit->get_num_args() == 2);
|
||||
sort* s = m.get_sort(lit->get_arg(0));
|
||||
bool is_int = a.is_int(s);
|
||||
if (!is_int && a.is_int_expr(lit->get_arg(0))) {
|
||||
is_int = true;
|
||||
s = a.mk_int();
|
||||
}
|
||||
|
||||
if (!is_int && is_pos && (a.is_gt(lit) || a.is_lt(lit))) {
|
||||
is_strict = true;
|
||||
|
@ -1305,25 +1310,25 @@ bool proof_checker::check_arith_literal(bool is_pos, app* lit0, rational const&
|
|||
}
|
||||
|
||||
//
|
||||
// Multiplying by coefficients over strict
|
||||
// Multiplying by coefficients over strict
|
||||
// and non-strict inequalities:
|
||||
//
|
||||
// (a <= b) * 2
|
||||
// (a - b <= 0) * 2
|
||||
// (2a - 2b <= 0)
|
||||
|
||||
|
||||
// (a < b) * 2 <=>
|
||||
// (a +1 <= b) * 2 <=>
|
||||
// 2a + 2 <= 2b <=>
|
||||
// 2a+2-2b <= 0
|
||||
|
||||
|
||||
bool strict_ineq =
|
||||
is_pos?(a.is_gt(lit) || a.is_lt(lit)):(a.is_ge(lit) || a.is_le(lit));
|
||||
|
||||
|
||||
if (is_int && strict_ineq) {
|
||||
sum = a.mk_add(sum, sign1);
|
||||
}
|
||||
|
||||
|
||||
term = a.mk_mul(sign1, a0);
|
||||
sum = a.mk_add(sum, term);
|
||||
term = a.mk_mul(sign2, a1);
|
||||
|
@ -1355,7 +1360,7 @@ bool proof_checker::check_arith_proof(proof* p) {
|
|||
}
|
||||
expr_ref fact(m);
|
||||
proof_ref_vector proofs(m);
|
||||
|
||||
|
||||
if (!match_fact(p, fact)) {
|
||||
UNREACHABLE();
|
||||
return false;
|
||||
|
@ -1384,7 +1389,7 @@ bool proof_checker::check_arith_proof(proof* p) {
|
|||
coeffs[i] = lc*coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
unsigned num_parents = m.get_num_parents(p);
|
||||
for (unsigned i = 0; i < num_parents; i++) {
|
||||
proof * a = m.get_parent(p, i);
|
||||
|
@ -1393,11 +1398,10 @@ bool proof_checker::check_arith_proof(proof* p) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (m.is_or(fact)) {
|
||||
if (m.is_or(fact)) {
|
||||
app* disj = to_app(fact);
|
||||
unsigned num_args = disj->get_num_args();
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
app* lit = to_app(disj->get_arg(i));
|
||||
if (!check_arith_literal(false, lit, coeffs[offset++], sum, is_strict)) {
|
||||
return false;
|
||||
|
@ -1409,13 +1413,13 @@ bool proof_checker::check_arith_proof(proof* p) {
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!sum.get()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
sort* s = m.get_sort(sum);
|
||||
|
||||
|
||||
|
||||
if (is_strict) {
|
||||
sum = autil.mk_lt(sum, autil.mk_numeral(rational(0), s));
|
||||
|
@ -1433,6 +1437,6 @@ bool proof_checker::check_arith_proof(proof* p) {
|
|||
dump_proof(p);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -110,10 +110,10 @@ public:
|
|||
|
||||
if (m.is_or(decl))
|
||||
{ mk_or_core(args, res); }
|
||||
else if (m.is_iff(decl) && args.size() == 2)
|
||||
else if (m.is_eq(decl) && args.size() == 2)
|
||||
// avoiding simplifying equalities. In particular,
|
||||
// we don't want (= (not a) (not b)) to be reduced to (= a b)
|
||||
{ res = m.mk_iff(args.get(0), args.get(1)); }
|
||||
{ res = m.mk_eq(args.get(0), args.get(1)); }
|
||||
else
|
||||
{ brwr.mk_app(decl, args.size(), args.c_ptr(), res); }
|
||||
}
|
||||
|
|
|
@ -39,7 +39,6 @@ br_status bool_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * co
|
|||
SASSERT(f->get_family_id() == m().get_basic_family_id());
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_EQ:
|
||||
case OP_IFF:
|
||||
SASSERT(num_args == 2);
|
||||
return mk_eq_core(args[0], args[1], result);
|
||||
case OP_DISTINCT:
|
||||
|
@ -428,7 +427,7 @@ bool bool_rewriter::simp_nested_eq_ite(expr * t, expr_fast_mark1 & neg_lits, exp
|
|||
neg = true;
|
||||
t = to_app(t)->get_arg(0);
|
||||
}
|
||||
if (m().is_iff(t) || m().is_eq(t)) {
|
||||
if (m().is_eq(t)) {
|
||||
bool modified = false;
|
||||
expr * new_lhs = simp_arg(to_app(t)->get_arg(0), neg_lits, pos_lits, modified);
|
||||
expr * new_rhs = simp_arg(to_app(t)->get_arg(1), neg_lits, pos_lits, modified);
|
||||
|
@ -708,7 +707,7 @@ br_status bool_rewriter::mk_eq_core(expr * lhs, expr * rhs, expr_ref & result) {
|
|||
|
||||
expr *la, *lb, *ra, *rb;
|
||||
// fold (iff (iff a b) (iff (not a) b)) to false
|
||||
if (m().is_iff(lhs, la, lb) && m().is_iff(rhs, ra, rb)) {
|
||||
if (m().is_eq(lhs, la, lb) && m().is_eq(rhs, ra, rb)) {
|
||||
expr *n;
|
||||
if ((la == ra && ((m().is_not(rb, n) && n == lb) ||
|
||||
(m().is_not(lb, n) && n == rb))) ||
|
||||
|
|
|
@ -81,7 +81,7 @@ public:
|
|||
bool_rewriter(ast_manager & m, params_ref const & p = params_ref()):m_manager(m), m_local_ctx_cost(0) { updt_params(p); }
|
||||
ast_manager & m() const { return m_manager; }
|
||||
family_id get_fid() const { return m().get_basic_family_id(); }
|
||||
bool is_eq(expr * t) const { return m().is_eq(t) || m().is_iff(t); }
|
||||
bool is_eq(expr * t) const { return m().is_eq(t); }
|
||||
|
||||
bool flat() const { return m_flat; }
|
||||
void set_flat(bool f) { m_flat = f; }
|
||||
|
|
|
@ -40,11 +40,8 @@ static bool is_neg_var(ast_manager & m, expr * e, unsigned num_decls) {
|
|||
*/
|
||||
bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) {
|
||||
// (not (= VAR t)) and (not (iff VAR t)) cases
|
||||
if (m_manager.is_not(e) && (m_manager.is_eq(to_app(e)->get_arg(0)) || m_manager.is_iff(to_app(e)->get_arg(0)))) {
|
||||
app * eq = to_app(to_app(e)->get_arg(0));
|
||||
SASSERT(m_manager.is_eq(eq) || m_manager.is_iff(eq));
|
||||
expr * lhs = eq->get_arg(0);
|
||||
expr * rhs = eq->get_arg(1);
|
||||
expr *eq, * lhs, *rhs;
|
||||
if (m_manager.is_not(e, eq) && m_manager.is_eq(eq, lhs, rhs)) {
|
||||
if (!is_var(lhs, num_decls) && !is_var(rhs, num_decls))
|
||||
return false;
|
||||
if (!is_var(lhs, num_decls))
|
||||
|
@ -60,9 +57,7 @@ bool der::is_var_diseq(expr * e, unsigned num_decls, var * & v, expr_ref & t) {
|
|||
return true;
|
||||
}
|
||||
// (iff VAR t) and (iff (not VAR) t) cases
|
||||
else if (m_manager.is_iff(e)) {
|
||||
expr * lhs = to_app(e)->get_arg(0);
|
||||
expr * rhs = to_app(e)->get_arg(1);
|
||||
else if (m_manager.is_eq(e, lhs, rhs) && m_manager.is_bool(lhs)) {
|
||||
// (iff VAR t) case
|
||||
if (is_var(lhs, num_decls) || is_var(rhs, num_decls)) {
|
||||
if (!is_var(lhs, num_decls))
|
||||
|
|
|
@ -259,7 +259,7 @@ private:
|
|||
result = m.mk_ite(t1, tt2, tt3);
|
||||
}
|
||||
}
|
||||
else if ((m.is_eq(fml, t1, t2) && m.is_bool(t1)) || m.is_iff(fml, t1, t2)) {
|
||||
else if (m.is_eq(fml, t1, t2) && m.is_bool(t1)) {
|
||||
expr_ref tt1(m), tt2(m), ntt1(m), ntt2(m), nt1(m), nt2(m);
|
||||
pull_quantifier(t1, qt, vars, tt1, use_fresh, rewrite_ok);
|
||||
pull_quantifier(t2, qt, vars, tt2, use_fresh, rewrite_ok);
|
||||
|
|
|
@ -346,6 +346,8 @@ public:
|
|||
|
||||
void set_bindings(unsigned num_bindings, expr * const * bindings);
|
||||
void set_inv_bindings(unsigned num_bindings, expr * const * bindings);
|
||||
void update_binding_at(unsigned i, expr* binding);
|
||||
void update_inv_binding_at(unsigned i, expr* binding);
|
||||
void operator()(expr * t, expr_ref & result, proof_ref & result_pr);
|
||||
void operator()(expr * t, expr_ref & result) { operator()(t, result, m_pr); }
|
||||
void operator()(expr * n, unsigned num_bindings, expr * const * bindings, expr_ref & result) {
|
||||
|
|
|
@ -639,6 +639,17 @@ void rewriter_tpl<Config>::set_inv_bindings(unsigned num_bindings, expr * const
|
|||
TRACE("rewriter", display_bindings(tout););
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::update_inv_binding_at(unsigned i, expr* binding) {
|
||||
m_bindings[i] = binding;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
void rewriter_tpl<Config>::update_binding_at(unsigned i, expr* binding) {
|
||||
m_bindings[m_bindings.size() - i - 1] = binding;
|
||||
}
|
||||
|
||||
|
||||
template<typename Config>
|
||||
template<bool ProofGen>
|
||||
void rewriter_tpl<Config>::main_loop(expr * t, expr_ref & result, proof_ref & result_pr) {
|
||||
|
|
|
@ -187,7 +187,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
|
|||
if (st != BR_FAILED)
|
||||
return st;
|
||||
}
|
||||
if (k == OP_EQ || k == OP_IFF) {
|
||||
if (k == OP_EQ) {
|
||||
SASSERT(num == 2);
|
||||
st = apply_tamagotchi(args[0], args[1], result);
|
||||
if (st != BR_FAILED)
|
||||
|
|
|
@ -24,6 +24,10 @@ Notes:
|
|||
#include "ast/for_each_expr.h"
|
||||
|
||||
void var_subst::operator()(expr * n, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
if (is_ground(n)) {
|
||||
result = n;
|
||||
return;
|
||||
}
|
||||
SASSERT(is_well_sorted(result.m(), n));
|
||||
m_reducer.reset();
|
||||
if (m_std_order)
|
||||
|
|
|
@ -154,8 +154,10 @@ bool static_features::is_diff_atom(expr const * e) const {
|
|||
bool static_features::is_gate(expr const * e) const {
|
||||
if (is_basic_expr(e)) {
|
||||
switch (to_app(e)->get_decl_kind()) {
|
||||
case OP_ITE: case OP_AND: case OP_OR: case OP_IFF: case OP_XOR: case OP_IMPLIES:
|
||||
case OP_ITE: case OP_AND: case OP_OR: case OP_XOR: case OP_IMPLIES:
|
||||
return true;
|
||||
case OP_EQ:
|
||||
return m_manager.is_bool(e);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -207,7 +209,7 @@ void static_features::update_core(expr * e) {
|
|||
case OP_OR:
|
||||
m_num_ors++;
|
||||
break;
|
||||
case OP_IFF:
|
||||
case OP_EQ:
|
||||
m_num_iffs++;
|
||||
break;
|
||||
}
|
||||
|
@ -418,7 +420,7 @@ void static_features::process(expr * e, bool form_ctx, bool or_and_ctx, bool ite
|
|||
form_ctx_new = true;
|
||||
or_and_ctx_new = true;
|
||||
break;
|
||||
case OP_IFF:
|
||||
case OP_EQ:
|
||||
form_ctx_new = true;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -223,7 +223,7 @@ ATOMIC_CMD(get_proof_graph_cmd, "get-proof-graph", "retrieve proof and print it
|
|||
});
|
||||
|
||||
static void print_core(cmd_context& ctx) {
|
||||
ptr_vector<expr> core;
|
||||
expr_ref_vector core(ctx.m());
|
||||
ctx.get_check_sat_result()->get_unsat_core(core);
|
||||
ctx.regular_stream() << "(";
|
||||
bool first = true;
|
||||
|
|
|
@ -29,7 +29,11 @@ Notes:
|
|||
#include "tactic/arith/bound_manager.h"
|
||||
#include "ast/used_vars.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "util/gparams.h"
|
||||
#include "qe/qe_mbp.h"
|
||||
#include "qe/qe_mbi.h"
|
||||
#include "qe/qe_term_graph.h"
|
||||
|
||||
|
||||
BINARY_SYM_CMD(get_quantifier_body_cmd,
|
||||
|
@ -352,6 +356,184 @@ public:
|
|||
void execute(cmd_context & ctx) override { ctx.display_dimacs(); }
|
||||
};
|
||||
|
||||
class mbp_cmd : public cmd {
|
||||
expr* m_fml;
|
||||
ptr_vector<expr> m_vars;
|
||||
public:
|
||||
mbp_cmd():cmd("mbp") {}
|
||||
char const * get_usage() const override { return "<expr> (<vars>)"; }
|
||||
char const * get_descr(cmd_context & ctx) const override { return "perform model based projection"; }
|
||||
unsigned get_arity() const override { return 2; }
|
||||
cmd_arg_kind next_arg_kind(cmd_context& ctx) const override {
|
||||
if (m_fml == nullptr) return CPK_EXPR;
|
||||
return CPK_EXPR_LIST;
|
||||
}
|
||||
void set_next_arg(cmd_context& ctx, expr * arg) override { m_fml = arg; }
|
||||
void set_next_arg(cmd_context & ctx, unsigned num, expr * const * ts) override {
|
||||
m_vars.append(num, ts);
|
||||
}
|
||||
void prepare(cmd_context & ctx) override { m_fml = nullptr; m_vars.reset(); }
|
||||
void execute(cmd_context & ctx) override {
|
||||
ast_manager& m = ctx.m();
|
||||
app_ref_vector vars(m);
|
||||
model_ref mdl;
|
||||
if (!ctx.is_model_available(mdl) || !ctx.get_check_sat_result()) {
|
||||
throw cmd_exception("model is not available");
|
||||
}
|
||||
for (expr* v : m_vars) {
|
||||
if (!is_uninterp_const(v)) {
|
||||
throw cmd_exception("invalid variable argument. Uninterpreted variable expected");
|
||||
}
|
||||
vars.push_back(to_app(v));
|
||||
}
|
||||
qe::mbp mbp(m);
|
||||
expr_ref fml(m_fml, m);
|
||||
mbp.spacer(vars, *mdl.get(), fml);
|
||||
ctx.regular_stream() << fml << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
class mbi_cmd : public cmd {
|
||||
expr* m_a;
|
||||
expr* m_b;
|
||||
ptr_vector<func_decl> m_vars;
|
||||
public:
|
||||
mbi_cmd():cmd("mbi") {}
|
||||
char const * get_usage() const override { return "<expr> <expr> (vars)"; }
|
||||
char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; }
|
||||
unsigned get_arity() const override { return 3; }
|
||||
cmd_arg_kind next_arg_kind(cmd_context& ctx) const override {
|
||||
if (m_a == nullptr) return CPK_EXPR;
|
||||
if (m_b == nullptr) return CPK_EXPR;
|
||||
return CPK_FUNC_DECL_LIST;
|
||||
}
|
||||
void set_next_arg(cmd_context& ctx, expr * arg) override {
|
||||
if (m_a == nullptr)
|
||||
m_a = arg;
|
||||
else
|
||||
m_b = arg;
|
||||
}
|
||||
void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override {
|
||||
m_vars.append(num, ts);
|
||||
}
|
||||
void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); }
|
||||
void execute(cmd_context & ctx) override {
|
||||
ast_manager& m = ctx.m();
|
||||
func_decl_ref_vector vars(m);
|
||||
for (func_decl* v : m_vars) {
|
||||
vars.push_back(v);
|
||||
}
|
||||
qe::interpolator mbi(m);
|
||||
expr_ref a(m_a, m);
|
||||
expr_ref b(m_b, m);
|
||||
expr_ref itp(m);
|
||||
solver_factory& sf = ctx.get_solver_factory();
|
||||
params_ref p;
|
||||
solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null);
|
||||
solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null);
|
||||
sA->assert_expr(a);
|
||||
sB->assert_expr(b);
|
||||
qe::prop_mbi_plugin pA(sA.get());
|
||||
qe::prop_mbi_plugin pB(sB.get());
|
||||
pA.set_shared(vars);
|
||||
pB.set_shared(vars);
|
||||
lbool res = mbi.pingpong(pA, pB, itp);
|
||||
ctx.regular_stream() << res << " " << itp << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class eufi_cmd : public cmd {
|
||||
expr* m_a;
|
||||
expr* m_b;
|
||||
ptr_vector<func_decl> m_vars;
|
||||
public:
|
||||
eufi_cmd():cmd("eufi") {}
|
||||
char const * get_usage() const override { return "<expr> <expr> (vars)"; }
|
||||
char const * get_descr(cmd_context & ctx) const override { return "perform model based interpolation"; }
|
||||
unsigned get_arity() const override { return 3; }
|
||||
cmd_arg_kind next_arg_kind(cmd_context& ctx) const override {
|
||||
if (m_a == nullptr) return CPK_EXPR;
|
||||
if (m_b == nullptr) return CPK_EXPR;
|
||||
return CPK_FUNC_DECL_LIST;
|
||||
}
|
||||
void set_next_arg(cmd_context& ctx, expr * arg) override {
|
||||
if (m_a == nullptr)
|
||||
m_a = arg;
|
||||
else
|
||||
m_b = arg;
|
||||
}
|
||||
void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override {
|
||||
m_vars.append(num, ts);
|
||||
}
|
||||
void prepare(cmd_context & ctx) override { m_a = nullptr; m_b = nullptr; m_vars.reset(); }
|
||||
void execute(cmd_context & ctx) override {
|
||||
ast_manager& m = ctx.m();
|
||||
func_decl_ref_vector vars(m);
|
||||
for (func_decl* v : m_vars) {
|
||||
vars.push_back(v);
|
||||
}
|
||||
qe::interpolator mbi(m);
|
||||
expr_ref a(m_a, m);
|
||||
expr_ref b(m_b, m);
|
||||
expr_ref itp(m);
|
||||
solver_factory& sf = ctx.get_solver_factory();
|
||||
params_ref p;
|
||||
solver_ref sA = sf(m, p, false /* no proofs */, true, true, symbol::null);
|
||||
solver_ref sB = sf(m, p, false /* no proofs */, true, true, symbol::null);
|
||||
solver_ref sNotA = sf(m, p, false /* no proofs */, true, true, symbol::null);
|
||||
solver_ref sNotB = sf(m, p, false /* no proofs */, true, true, symbol::null);
|
||||
sA->assert_expr(a);
|
||||
sNotA->assert_expr(m.mk_not(a));
|
||||
sB->assert_expr(b);
|
||||
sNotB->assert_expr(m.mk_not(b));
|
||||
qe::euf_arith_mbi_plugin pA(sA.get(), sNotA.get());
|
||||
qe::euf_arith_mbi_plugin pB(sB.get(), sNotB.get());
|
||||
pA.set_shared(vars);
|
||||
pB.set_shared(vars);
|
||||
lbool res = mbi.pogo(pA, pB, itp);
|
||||
ctx.regular_stream() << res << " " << itp << "\n";
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class euf_project_cmd : public cmd {
|
||||
unsigned m_arg_index;
|
||||
ptr_vector<expr> m_lits;
|
||||
ptr_vector<func_decl> m_vars;
|
||||
public:
|
||||
euf_project_cmd():cmd("euf-project") {}
|
||||
char const * get_usage() const override { return "(exprs) (vars)"; }
|
||||
char const * get_descr(cmd_context & ctx) const override { return "perform congruence projection"; }
|
||||
unsigned get_arity() const override { return 2; }
|
||||
cmd_arg_kind next_arg_kind(cmd_context& ctx) const override {
|
||||
if (m_arg_index == 0) return CPK_EXPR_LIST;
|
||||
return CPK_FUNC_DECL_LIST;
|
||||
}
|
||||
void set_next_arg(cmd_context& ctx, unsigned num, expr * const* args) override {
|
||||
m_lits.append(num, args);
|
||||
m_arg_index = 1;
|
||||
}
|
||||
void set_next_arg(cmd_context & ctx, unsigned num, func_decl * const * ts) override {
|
||||
m_vars.append(num, ts);
|
||||
}
|
||||
void prepare(cmd_context & ctx) override { m_arg_index = 0; m_lits.reset(); m_vars.reset(); }
|
||||
void execute(cmd_context & ctx) override {
|
||||
ast_manager& m = ctx.m();
|
||||
func_decl_ref_vector vars(m);
|
||||
expr_ref_vector lits(m);
|
||||
for (func_decl* v : m_vars) vars.push_back(v);
|
||||
for (expr* e : m_lits) lits.push_back(e);
|
||||
flatten_and(lits);
|
||||
qe::term_graph tg(m);
|
||||
tg.set_vars(vars, false);
|
||||
tg.add_lits(lits);
|
||||
expr_ref_vector p = tg.project();
|
||||
ctx.regular_stream() << p << "\n";
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
void install_dbg_cmds(cmd_context & ctx) {
|
||||
ctx.insert(alloc(print_dimacs_cmd));
|
||||
|
@ -377,4 +559,8 @@ void install_dbg_cmds(cmd_context & ctx) {
|
|||
ctx.insert(alloc(instantiate_cmd));
|
||||
ctx.insert(alloc(instantiate_nested_cmd));
|
||||
ctx.insert(alloc(set_next_id));
|
||||
ctx.insert(alloc(mbp_cmd));
|
||||
ctx.insert(alloc(mbi_cmd));
|
||||
ctx.insert(alloc(euf_project_cmd));
|
||||
ctx.insert(alloc(eufi_cmd));
|
||||
}
|
||||
|
|
|
@ -35,11 +35,114 @@ std::ostream& operator<<(std::ostream& out, opt::ineq_type ie) {
|
|||
|
||||
namespace opt {
|
||||
|
||||
/**
|
||||
* Convert a row ax + coeffs + coeff = value into a definition for x
|
||||
* x = (value - coeffs - coeff)/a
|
||||
* as backdrop we have existing assignments to x and other variables that
|
||||
* satisfy the equality with value, and such that value satisfies
|
||||
* the row constraint ( = , <= , < , mod)
|
||||
*/
|
||||
model_based_opt::def::def(row const& r, unsigned x) {
|
||||
for (var const & v : r.m_vars) {
|
||||
if (v.m_id != x) {
|
||||
m_vars.push_back(v);
|
||||
}
|
||||
else {
|
||||
m_div = -v.m_coeff;
|
||||
}
|
||||
}
|
||||
m_coeff = r.m_coeff;
|
||||
if (r.m_type == opt::t_lt) m_coeff += m_div;
|
||||
normalize();
|
||||
SASSERT(m_div.is_pos());
|
||||
}
|
||||
|
||||
model_based_opt::def model_based_opt::def::operator+(def const& other) const {
|
||||
def result;
|
||||
vector<var> const& vs1 = m_vars;
|
||||
vector<var> const& vs2 = other.m_vars;
|
||||
vector<var> & vs = result.m_vars;
|
||||
rational c1(1), c2(1);
|
||||
if (m_div != other.m_div) {
|
||||
c1 = other.m_div;
|
||||
c2 = m_div;
|
||||
}
|
||||
unsigned i = 0, j = 0;
|
||||
while (i < vs1.size() || j < vs2.size()) {
|
||||
unsigned v1 = UINT_MAX, v2 = UINT_MAX;
|
||||
if (i < vs1.size()) v1 = vs1[i].m_id;
|
||||
if (j < vs2.size()) v2 = vs2[j].m_id;
|
||||
if (v1 == v2) {
|
||||
vs.push_back(vs1[i]);
|
||||
vs.back().m_coeff *= c1;
|
||||
vs.back().m_coeff += c2 * vs2[j].m_coeff;
|
||||
++i; ++j;
|
||||
if (vs.back().m_coeff.is_zero()) {
|
||||
vs.pop_back();
|
||||
}
|
||||
}
|
||||
else if (v1 < v2) {
|
||||
vs.push_back(vs1[i]);
|
||||
vs.back().m_coeff *= c1;
|
||||
}
|
||||
else {
|
||||
vs.push_back(vs2[j]);
|
||||
vs.back().m_coeff *= c2;
|
||||
}
|
||||
}
|
||||
result.m_div = c1*m_div;
|
||||
result.m_coeff = (m_coeff*c1) + (other.m_coeff*c2);
|
||||
result.normalize();
|
||||
return result;
|
||||
}
|
||||
|
||||
model_based_opt::def model_based_opt::def::operator/(rational const& r) const {
|
||||
def result(*this);
|
||||
result.m_div *= r;
|
||||
result.normalize();
|
||||
return result;
|
||||
}
|
||||
|
||||
model_based_opt::def model_based_opt::def::operator*(rational const& n) const {
|
||||
def result(*this);
|
||||
for (var& v : result.m_vars) {
|
||||
v.m_coeff *= n;
|
||||
}
|
||||
result.m_coeff *= n;
|
||||
result.normalize();
|
||||
return result;
|
||||
}
|
||||
|
||||
model_based_opt::def model_based_opt::def::operator+(rational const& n) const {
|
||||
def result(*this);
|
||||
result.m_coeff += n * result.m_div;
|
||||
result.normalize();
|
||||
return result;
|
||||
}
|
||||
|
||||
void model_based_opt::def::normalize() {
|
||||
if (m_div.is_one()) return;
|
||||
rational g(m_div);
|
||||
g = gcd(g, m_coeff);
|
||||
for (var const& v : m_vars) {
|
||||
g = gcd(g, abs(v.m_coeff));
|
||||
if (g.is_one()) break;
|
||||
}
|
||||
if (m_div.is_neg()) {
|
||||
g.neg();
|
||||
}
|
||||
if (!g.is_one()) {
|
||||
for (var& v : m_vars) {
|
||||
v.m_coeff /= g;
|
||||
}
|
||||
m_coeff /= g;
|
||||
m_div /= g;
|
||||
}
|
||||
}
|
||||
|
||||
model_based_opt::model_based_opt() {
|
||||
m_rows.push_back(row());
|
||||
}
|
||||
|
||||
|
||||
bool model_based_opt::invariant() {
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i) {
|
||||
|
@ -61,7 +164,7 @@ namespace opt {
|
|||
PASSERT(index == 0 || m_var2row_ids[vars[i].m_id].contains(index));
|
||||
}
|
||||
|
||||
PASSERT(r.m_value == get_row_value(r));
|
||||
PASSERT(r.m_value == eval(r));
|
||||
PASSERT(r.m_type != t_eq || r.m_value.is_zero());
|
||||
// values satisfy constraints
|
||||
PASSERT(index == 0 || r.m_type != t_lt || r.m_value.is_neg());
|
||||
|
@ -105,14 +208,14 @@ namespace opt {
|
|||
if (find_bound(x, bound_row_index, bound_coeff, coeff.is_pos())) {
|
||||
SASSERT(!bound_coeff.is_zero());
|
||||
TRACE("opt", display(tout << "update: " << v << " ", objective());
|
||||
for (unsigned i = 0; i < m_above.size(); ++i) {
|
||||
display(tout << "resolve: ", m_rows[m_above[i]]);
|
||||
for (unsigned above : m_above) {
|
||||
display(tout << "resolve: ", m_rows[above]);
|
||||
});
|
||||
for (unsigned i = 0; i < m_above.size(); ++i) {
|
||||
resolve(bound_row_index, bound_coeff, m_above[i], x);
|
||||
for (unsigned above : m_above) {
|
||||
resolve(bound_row_index, bound_coeff, above, x);
|
||||
}
|
||||
for (unsigned i = 0; i < m_below.size(); ++i) {
|
||||
resolve(bound_row_index, bound_coeff, m_below[i], x);
|
||||
for (unsigned below : m_below) {
|
||||
resolve(bound_row_index, bound_coeff, below, x);
|
||||
}
|
||||
// coeff*x + objective <= ub
|
||||
// a2*x + t2 <= 0
|
||||
|
@ -151,8 +254,7 @@ namespace opt {
|
|||
rational old_val = m_var2value[x];
|
||||
m_var2value[x] = val;
|
||||
unsigned_vector const& row_ids = m_var2row_ids[x];
|
||||
for (unsigned i = 0; i < row_ids.size(); ++i) {
|
||||
unsigned row_id = row_ids[i];
|
||||
for (unsigned row_id : row_ids) {
|
||||
rational coeff = get_coefficient(row_id, x);
|
||||
if (coeff.is_zero()) {
|
||||
continue;
|
||||
|
@ -166,8 +268,7 @@ namespace opt {
|
|||
|
||||
|
||||
void model_based_opt::update_values(unsigned_vector const& bound_vars, unsigned_vector const& bound_trail) {
|
||||
for (unsigned i = bound_trail.size(); i > 0; ) {
|
||||
--i;
|
||||
for (unsigned i = bound_trail.size(); i-- > 0; ) {
|
||||
unsigned x = bound_vars[i];
|
||||
row& r = m_rows[bound_trail[i]];
|
||||
rational val = r.m_coeff;
|
||||
|
@ -175,8 +276,7 @@ namespace opt {
|
|||
rational new_x_val;
|
||||
rational x_coeff, eps(0);
|
||||
vector<var> const& vars = r.m_vars;
|
||||
for (unsigned j = 0; j < vars.size(); ++j) {
|
||||
var const& v = vars[j];
|
||||
for (var const& v : vars) {
|
||||
if (x == v.m_id) {
|
||||
x_coeff = v.m_coeff;
|
||||
}
|
||||
|
@ -218,19 +318,17 @@ namespace opt {
|
|||
<< " eps: " << eps << " ", r); );
|
||||
m_var2value[x] = new_x_val;
|
||||
|
||||
r.m_value = get_row_value(r);
|
||||
r.m_value = eval(r);
|
||||
SASSERT(invariant(bound_trail[i], r));
|
||||
}
|
||||
|
||||
// update and check bounds for all other affected rows.
|
||||
for (unsigned i = bound_trail.size(); i > 0; ) {
|
||||
--i;
|
||||
for (unsigned i = bound_trail.size(); i-- > 0; ) {
|
||||
unsigned x = bound_vars[i];
|
||||
unsigned_vector const& row_ids = m_var2row_ids[x];
|
||||
for (unsigned j = 0; j < row_ids.size(); ++j) {
|
||||
unsigned row_id = row_ids[j];
|
||||
for (unsigned row_id : row_ids) {
|
||||
row & r = m_rows[row_id];
|
||||
r.m_value = get_row_value(r);
|
||||
r.m_value = eval(r);
|
||||
SASSERT(invariant(row_id, r));
|
||||
}
|
||||
}
|
||||
|
@ -245,8 +343,7 @@ namespace opt {
|
|||
uint_set visited;
|
||||
m_above.reset();
|
||||
m_below.reset();
|
||||
for (unsigned i = 0; i < row_ids.size(); ++i) {
|
||||
unsigned row_id = row_ids[i];
|
||||
for (unsigned row_id : row_ids) {
|
||||
SASSERT(row_id != m_objective_id);
|
||||
if (visited.contains(row_id)) {
|
||||
continue;
|
||||
|
@ -289,27 +386,43 @@ namespace opt {
|
|||
m_rows[row_id].m_alive = false;
|
||||
m_retired_rows.push_back(row_id);
|
||||
}
|
||||
|
||||
rational model_based_opt::eval(unsigned x) const {
|
||||
return m_var2value[x];
|
||||
}
|
||||
|
||||
rational model_based_opt::eval(def const& d) const {
|
||||
vector<var> const& vars = d.m_vars;
|
||||
rational val = d.m_coeff;
|
||||
for (var const& v : vars) {
|
||||
val += v.m_coeff * eval(v.m_id);
|
||||
}
|
||||
val /= d.m_div;
|
||||
return val;
|
||||
}
|
||||
|
||||
rational model_based_opt::get_row_value(row const& r) const {
|
||||
rational model_based_opt::eval(row const& r) const {
|
||||
vector<var> const& vars = r.m_vars;
|
||||
rational val = r.m_coeff;
|
||||
for (unsigned i = 0; i < vars.size(); ++i) {
|
||||
var const& v = vars[i];
|
||||
val += v.m_coeff * m_var2value[v.m_id];
|
||||
for (var const& v : vars) {
|
||||
val += v.m_coeff * eval(v.m_id);
|
||||
}
|
||||
return val;
|
||||
}
|
||||
|
||||
rational model_based_opt::get_coefficient(unsigned row_id, unsigned var_id) const {
|
||||
row const& r = m_rows[row_id];
|
||||
if (r.m_vars.empty()) {
|
||||
return m_rows[row_id].get_coefficient(var_id);
|
||||
}
|
||||
|
||||
rational model_based_opt::row::get_coefficient(unsigned var_id) const {
|
||||
if (m_vars.empty()) {
|
||||
return rational::zero();
|
||||
}
|
||||
unsigned lo = 0, hi = r.m_vars.size();
|
||||
unsigned lo = 0, hi = m_vars.size();
|
||||
while (lo < hi) {
|
||||
unsigned mid = lo + (hi - lo)/2;
|
||||
SASSERT(mid < hi);
|
||||
unsigned id = r.m_vars[mid].m_id;
|
||||
unsigned id = m_vars[mid].m_id;
|
||||
if (id == var_id) {
|
||||
lo = mid;
|
||||
break;
|
||||
|
@ -321,12 +434,12 @@ namespace opt {
|
|||
hi = mid;
|
||||
}
|
||||
}
|
||||
if (lo == r.m_vars.size()) {
|
||||
if (lo == m_vars.size()) {
|
||||
return rational::zero();
|
||||
}
|
||||
unsigned id = r.m_vars[lo].m_id;
|
||||
unsigned id = m_vars[lo].m_id;
|
||||
if (id == var_id) {
|
||||
return r.m_vars[lo].m_coeff;
|
||||
return m_vars[lo].m_coeff;
|
||||
}
|
||||
else {
|
||||
return rational::zero();
|
||||
|
@ -368,7 +481,7 @@ namespace opt {
|
|||
tout << a1 << " " << a2 << ": ";
|
||||
display(tout, m_rows[row_dst]);
|
||||
display(tout, m_rows[row_src]););
|
||||
if (a1.is_pos() != a2.is_pos()) {
|
||||
if (a1.is_pos() != a2.is_pos() || m_rows[row_src].m_type == opt::t_eq) {
|
||||
mul_add(x, a1, row_src, a2, row_dst);
|
||||
}
|
||||
else {
|
||||
|
@ -384,6 +497,17 @@ namespace opt {
|
|||
}
|
||||
}
|
||||
|
||||
void model_based_opt::solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x) {
|
||||
SASSERT(a1 == get_coefficient(row_src, x));
|
||||
SASSERT(a1.is_pos());
|
||||
SASSERT(row_src != row_dst);
|
||||
if (!m_rows[row_dst].m_alive) return;
|
||||
rational a2 = get_coefficient(row_dst, x);
|
||||
mul(row_dst, a1);
|
||||
mul_add(false, row_dst, -a2, row_src);
|
||||
SASSERT(get_coefficient(row_dst, x).is_zero());
|
||||
}
|
||||
|
||||
// resolution for integer rows.
|
||||
void model_based_opt::mul_add(
|
||||
unsigned x, rational const& src_c, unsigned row_src, rational const& dst_c, unsigned row_dst) {
|
||||
|
@ -400,18 +524,28 @@ namespace opt {
|
|||
rational slack = (abs_src_c - rational::one()) * (abs_dst_c - rational::one());
|
||||
rational dst_val = dst.m_value - x_val*dst_c;
|
||||
rational src_val = src.m_value - x_val*src_c;
|
||||
bool use_case1 =
|
||||
(src_c * dst_val + dst_c * src_val + slack).is_nonpos()
|
||||
|| abs_src_c.is_one()
|
||||
|| abs_dst_c.is_one();
|
||||
rational distance = src_c * dst_val + dst_c * src_val + slack;
|
||||
bool use_case1 = distance.is_nonpos() || abs_src_c.is_one() || abs_dst_c.is_one();
|
||||
|
||||
#if 0
|
||||
if (distance.is_nonpos() && !abs_src_c.is_one() && !abs_dst_c.is_one()) {
|
||||
unsigned r = copy_row(row_src);
|
||||
mul_add(false, r, rational::one(), row_dst);
|
||||
del_var(r, x);
|
||||
add(r, slack);
|
||||
TRACE("qe", tout << m_rows[r];);
|
||||
SASSERT(!m_rows[r].m_value.is_pos());
|
||||
}
|
||||
#endif
|
||||
|
||||
if (use_case1) {
|
||||
// dst <- abs_src_c*dst + abs_dst_c*src - slack
|
||||
TRACE("opt", tout << "slack: " << slack << " " << src_c << " " << dst_val << " " << dst_c << " " << src_val << "\n";);
|
||||
// dst <- abs_src_c*dst + abs_dst_c*src + slack
|
||||
mul(row_dst, abs_src_c);
|
||||
sub(row_dst, slack);
|
||||
mul_add(false, row_dst, abs_dst_c, row_src);
|
||||
add(row_dst, slack);
|
||||
mul_add(false, row_dst, abs_dst_c, row_src);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// create finite disjunction for |b|.
|
||||
|
@ -432,6 +566,7 @@ namespace opt {
|
|||
// exists z in [0 .. |b|-2] . |b| | (z + s) && a*n_sign(b)(s + z) + |b|t <= 0
|
||||
//
|
||||
|
||||
TRACE("qe", tout << "finite disjunction " << distance << " " << src_c << " " << dst_c << "\n";);
|
||||
vector<var> coeffs;
|
||||
if (abs_dst_c <= abs_src_c) {
|
||||
rational z = mod(dst_val, abs_dst_c);
|
||||
|
@ -457,8 +592,8 @@ namespace opt {
|
|||
}
|
||||
|
||||
void model_based_opt::mk_coeffs_without(vector<var>& dst, vector<var> const src, unsigned x) {
|
||||
for (unsigned i = 0; i < src.size(); ++i) {
|
||||
if (src[i].m_id != x) dst.push_back(src[i]);
|
||||
for (var const & v : src) {
|
||||
if (v.m_id != x) dst.push_back(v);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -469,8 +604,8 @@ namespace opt {
|
|||
void model_based_opt::mul(unsigned dst, rational const& c) {
|
||||
if (c.is_one()) return;
|
||||
row& r = m_rows[dst];
|
||||
for (unsigned i = 0; i < r.m_vars.size(); ++i) {
|
||||
r.m_vars[i].m_coeff *= c;
|
||||
for (auto & v : r.m_vars) {
|
||||
v.m_coeff *= c;
|
||||
}
|
||||
r.m_coeff *= c;
|
||||
r.m_value *= c;
|
||||
|
@ -488,6 +623,21 @@ namespace opt {
|
|||
r.m_value -= c;
|
||||
}
|
||||
|
||||
void model_based_opt::del_var(unsigned dst, unsigned x) {
|
||||
row& r = m_rows[dst];
|
||||
unsigned j = 0;
|
||||
for (var & v : r.m_vars) {
|
||||
if (v.m_id == x) {
|
||||
r.m_value -= eval(x)*r.m_coeff;
|
||||
}
|
||||
else {
|
||||
r.m_vars[j++] = v;
|
||||
}
|
||||
}
|
||||
r.m_vars.shrink(j);
|
||||
}
|
||||
|
||||
|
||||
void model_based_opt::normalize(unsigned row_id) {
|
||||
row& r = m_rows[row_id];
|
||||
if (r.m_vars.empty()) return;
|
||||
|
@ -529,7 +679,7 @@ namespace opt {
|
|||
row& r1 = m_rows[row_id1];
|
||||
row const& r2 = m_rows[row_id2];
|
||||
unsigned i = 0, j = 0;
|
||||
for(; i < r1.m_vars.size() || j < r2.m_vars.size(); ) {
|
||||
while (i < r1.m_vars.size() || j < r2.m_vars.size()) {
|
||||
if (j == r2.m_vars.size()) {
|
||||
m_new_vars.append(r1.m_vars.size() - i, r1.m_vars.c_ptr() + i);
|
||||
break;
|
||||
|
@ -583,40 +733,59 @@ namespace opt {
|
|||
}
|
||||
|
||||
void model_based_opt::display(std::ostream& out) const {
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i) {
|
||||
display(out, m_rows[i]);
|
||||
for (auto const& r : m_rows) {
|
||||
display(out, r);
|
||||
}
|
||||
for (unsigned i = 0; i < m_var2row_ids.size(); ++i) {
|
||||
unsigned_vector const& rows = m_var2row_ids[i];
|
||||
out << i << ": ";
|
||||
for (unsigned j = 0; j < rows.size(); ++j) {
|
||||
out << rows[j] << " ";
|
||||
for (auto const& r : rows) {
|
||||
out << r << " ";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
void model_based_opt::display(std::ostream& out, row const& r) const {
|
||||
vector<var> const& vars = r.m_vars;
|
||||
out << (r.m_alive?"+":"-") << " ";
|
||||
for (unsigned i = 0; i < vars.size(); ++i) {
|
||||
if (i > 0 && vars[i].m_coeff.is_pos()) {
|
||||
void model_based_opt::display(std::ostream& out, vector<var> const& vars, rational const& coeff) {
|
||||
unsigned i = 0;
|
||||
for (var const& v : vars) {
|
||||
if (i > 0 && v.m_coeff.is_pos()) {
|
||||
out << "+ ";
|
||||
}
|
||||
out << vars[i].m_coeff << "* v" << vars[i].m_id << " ";
|
||||
++i;
|
||||
if (v.m_coeff.is_one()) {
|
||||
out << "v" << v.m_id << " ";
|
||||
}
|
||||
else {
|
||||
out << v.m_coeff << "*v" << v.m_id << " ";
|
||||
}
|
||||
}
|
||||
if (r.m_coeff.is_pos()) {
|
||||
out << " + " << r.m_coeff << " ";
|
||||
if (coeff.is_pos()) {
|
||||
out << " + " << coeff << " ";
|
||||
}
|
||||
else if (r.m_coeff.is_neg()) {
|
||||
out << r.m_coeff << " ";
|
||||
}
|
||||
else if (coeff.is_neg()) {
|
||||
out << coeff << " ";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream& model_based_opt::display(std::ostream& out, row const& r) {
|
||||
out << (r.m_alive?"+":"-") << " ";
|
||||
display(out, r.m_vars, r.m_coeff);
|
||||
if (r.m_type == opt::t_mod) {
|
||||
out << r.m_type << " " << r.m_mod << " = 0; value: " << r.m_value << "\n";
|
||||
}
|
||||
else {
|
||||
out << r.m_type << " 0; value: " << r.m_value << "\n";
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& model_based_opt::display(std::ostream& out, def const& r) {
|
||||
display(out, r.m_vars, r.m_coeff);
|
||||
if (!r.m_div.is_one()) {
|
||||
out << " / " << r.m_div;
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
unsigned model_based_opt::add_var(rational const& value, bool is_int) {
|
||||
|
@ -638,10 +807,10 @@ namespace opt {
|
|||
r.m_vars.append(coeffs.size(), coeffs.c_ptr());
|
||||
bool is_int_row = true;
|
||||
std::sort(r.m_vars.begin(), r.m_vars.end(), var::compare());
|
||||
for (unsigned i = 0; i < coeffs.size(); ++i) {
|
||||
val += m_var2value[coeffs[i].m_id] * coeffs[i].m_coeff;
|
||||
SASSERT(!is_int(coeffs[i].m_id) || coeffs[i].m_coeff.is_int());
|
||||
is_int_row &= is_int(coeffs[i].m_id);
|
||||
for (auto const& c : coeffs) {
|
||||
val += m_var2value[c.m_id] * c.m_coeff;
|
||||
SASSERT(!is_int(c.m_id) || c.m_coeff.is_int());
|
||||
is_int_row &= is_int(c.m_id);
|
||||
}
|
||||
r.m_alive = true;
|
||||
r.m_coeff = c;
|
||||
|
@ -674,8 +843,8 @@ namespace opt {
|
|||
unsigned dst = new_row();
|
||||
row const& r = m_rows[src];
|
||||
set_row(dst, r.m_vars, r.m_coeff, r.m_mod, r.m_type);
|
||||
for (unsigned i = 0; i < r.m_vars.size(); ++i) {
|
||||
m_var2row_ids[r.m_vars[i].m_id].push_back(dst);
|
||||
for (auto const& v : r.m_vars) {
|
||||
m_var2row_ids[v.m_id].push_back(dst);
|
||||
}
|
||||
SASSERT(invariant(dst, m_rows[dst]));
|
||||
return dst;
|
||||
|
@ -692,8 +861,8 @@ namespace opt {
|
|||
void model_based_opt::add_constraint(vector<var> const& coeffs, rational const& c, rational const& m, ineq_type rel) {
|
||||
unsigned row_id = new_row();
|
||||
set_row(row_id, coeffs, c, m, rel);
|
||||
for (unsigned i = 0; i < coeffs.size(); ++i) {
|
||||
m_var2row_ids[coeffs[i].m_id].push_back(row_id);
|
||||
for (var const& coeff : coeffs) {
|
||||
m_var2row_ids[coeff.m_id].push_back(row_id);
|
||||
}
|
||||
SASSERT(invariant(row_id, m_rows[row_id]));
|
||||
}
|
||||
|
@ -703,9 +872,9 @@ namespace opt {
|
|||
}
|
||||
|
||||
void model_based_opt::get_live_rows(vector<row>& rows) {
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i) {
|
||||
if (m_rows[i].m_alive) {
|
||||
rows.push_back(m_rows[i]);
|
||||
for (row const& r : m_rows) {
|
||||
if (r.m_alive) {
|
||||
rows.push_back(r);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -728,7 +897,7 @@ namespace opt {
|
|||
// t0 <= s for each s (M inequalities).
|
||||
// If N >= M the construction is symmetric.
|
||||
//
|
||||
void model_based_opt::project(unsigned x) {
|
||||
model_based_opt::def model_based_opt::project(unsigned x, bool compute_def) {
|
||||
unsigned_vector& lub_rows = m_lub;
|
||||
unsigned_vector& glb_rows = m_glb;
|
||||
unsigned_vector& mod_rows = m_mod;
|
||||
|
@ -742,9 +911,9 @@ namespace opt {
|
|||
glb_rows.reset();
|
||||
mod_rows.reset();
|
||||
bool lub_is_unit = false, glb_is_unit = false;
|
||||
unsigned eq_row = UINT_MAX;
|
||||
// select the lub and glb.
|
||||
for (unsigned i = 0; i < row_ids.size(); ++i) {
|
||||
unsigned row_id = row_ids[i];
|
||||
for (unsigned row_id : row_ids) {
|
||||
if (visited.contains(row_id)) {
|
||||
continue;
|
||||
}
|
||||
|
@ -758,8 +927,8 @@ namespace opt {
|
|||
continue;
|
||||
}
|
||||
if (r.m_type == t_eq) {
|
||||
solve_for(row_id, x);
|
||||
return;
|
||||
eq_row = row_id;
|
||||
continue;
|
||||
}
|
||||
if (r.m_type == t_mod) {
|
||||
mod_rows.push_back(row_id);
|
||||
|
@ -792,24 +961,56 @@ namespace opt {
|
|||
}
|
||||
|
||||
if (!mod_rows.empty()) {
|
||||
solve_mod(x, mod_rows);
|
||||
return;
|
||||
return solve_mod(x, mod_rows, compute_def);
|
||||
}
|
||||
|
||||
|
||||
if (eq_row != UINT_MAX) {
|
||||
return solve_for(eq_row, x, compute_def);
|
||||
}
|
||||
|
||||
def result;
|
||||
unsigned lub_size = lub_rows.size();
|
||||
unsigned glb_size = glb_rows.size();
|
||||
unsigned row_index = (lub_size <= glb_size) ? lub_index : glb_index;
|
||||
glb_rows.append(lub_rows);
|
||||
|
||||
// There are only upper or only lower bounds.
|
||||
if (row_index == UINT_MAX) {
|
||||
for (unsigned i = 0; i < glb_rows.size(); ++i) {
|
||||
unsigned row_id = glb_rows[i];
|
||||
SASSERT(m_rows[row_id].m_alive);
|
||||
SASSERT(!get_coefficient(row_id, x).is_zero());
|
||||
retire_row(row_id);
|
||||
if (compute_def) {
|
||||
if (lub_index != UINT_MAX) {
|
||||
result = solve_for(lub_index, x, true);
|
||||
}
|
||||
else if (glb_index != UINT_MAX) {
|
||||
result = solve_for(glb_index, x, true);
|
||||
}
|
||||
else {
|
||||
result = def();
|
||||
m_var2value[x] = rational::zero();
|
||||
}
|
||||
SASSERT(eval(result) == eval(x));
|
||||
}
|
||||
return;
|
||||
else {
|
||||
for (unsigned row_id : lub_rows) retire_row(row_id);
|
||||
for (unsigned row_id : glb_rows) retire_row(row_id);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SASSERT(lub_index != UINT_MAX);
|
||||
SASSERT(glb_index != UINT_MAX);
|
||||
if (compute_def) {
|
||||
#if 0
|
||||
def d1(m_rows[lub_index], x);
|
||||
def d2(m_rows[glb_index], x);
|
||||
result = (d1 + d2)/2;
|
||||
#else
|
||||
if (lub_size <= glb_size) {
|
||||
result = def(m_rows[lub_index], x);
|
||||
}
|
||||
else {
|
||||
result = def(m_rows[glb_index], x);
|
||||
}
|
||||
m_var2value[x] = eval(result);
|
||||
#endif
|
||||
}
|
||||
|
||||
// The number of matching lower and upper bounds is small.
|
||||
|
@ -818,10 +1019,9 @@ namespace opt {
|
|||
(!is_int(x) || lub_is_unit || glb_is_unit)) {
|
||||
for (unsigned i = 0; i < lub_size; ++i) {
|
||||
unsigned row_id1 = lub_rows[i];
|
||||
bool last = i + 1 == lub_rows.size();
|
||||
bool last = i + 1 == lub_size;
|
||||
rational coeff = get_coefficient(row_id1, x);
|
||||
for (unsigned j = 0; j < glb_size; ++j) {
|
||||
unsigned row_id2 = glb_rows[j];
|
||||
for (unsigned row_id2 : glb_rows) {
|
||||
if (last) {
|
||||
resolve(row_id1, coeff, row_id2, x);
|
||||
}
|
||||
|
@ -831,21 +1031,25 @@ namespace opt {
|
|||
}
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < lub_size; ++i) {
|
||||
retire_row(lub_rows[i]);
|
||||
}
|
||||
return;
|
||||
for (unsigned row_id : lub_rows) retire_row(row_id);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// General case.
|
||||
rational coeff = get_coefficient(row_index, x);
|
||||
for (unsigned i = 0; i < glb_rows.size(); ++i) {
|
||||
unsigned row_id = glb_rows[i];
|
||||
for (unsigned row_id : lub_rows) {
|
||||
if (row_id != row_index) {
|
||||
resolve(row_index, coeff, row_id, x);
|
||||
}
|
||||
}
|
||||
for (unsigned row_id : glb_rows) {
|
||||
if (row_id != row_index) {
|
||||
resolve(row_index, coeff, row_id, x);
|
||||
}
|
||||
}
|
||||
retire_row(row_index);
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -863,11 +1067,11 @@ namespace opt {
|
|||
// x := D*x' + u
|
||||
//
|
||||
|
||||
void model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows) {
|
||||
model_based_opt::def model_based_opt::solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def) {
|
||||
SASSERT(!mod_rows.empty());
|
||||
rational D(1);
|
||||
for (unsigned i = 0; i < mod_rows.size(); ++i) {
|
||||
D = lcm(D, m_rows[mod_rows[i]].m_mod);
|
||||
for (unsigned idx : mod_rows) {
|
||||
D = lcm(D, m_rows[idx].m_mod);
|
||||
}
|
||||
if (D.is_zero()) {
|
||||
throw default_exception("modulo 0 is not defined");
|
||||
|
@ -876,9 +1080,9 @@ namespace opt {
|
|||
rational val_x = m_var2value[x];
|
||||
rational u = mod(val_x, D);
|
||||
SASSERT(u.is_nonneg() && u < D);
|
||||
for (unsigned i = 0; i < mod_rows.size(); ++i) {
|
||||
replace_var(mod_rows[i], x, u);
|
||||
SASSERT(invariant(mod_rows[i], m_rows[mod_rows[i]]));
|
||||
for (unsigned idx : mod_rows) {
|
||||
replace_var(idx, x, u);
|
||||
SASSERT(invariant(idx, m_rows[idx]));
|
||||
}
|
||||
//
|
||||
// update inequalities such that u is added to t and
|
||||
|
@ -894,15 +1098,20 @@ namespace opt {
|
|||
unsigned y = add_var(new_val, true);
|
||||
unsigned_vector const& row_ids = m_var2row_ids[x];
|
||||
uint_set visited;
|
||||
for (unsigned i = 0; i < row_ids.size(); ++i) {
|
||||
unsigned row_id = row_ids[i];
|
||||
for (unsigned row_id : row_ids) {
|
||||
if (!visited.contains(row_id)) {
|
||||
// x |-> D*y + u
|
||||
replace_var(row_id, x, D, y, u);
|
||||
visited.insert(row_id);
|
||||
}
|
||||
}
|
||||
project(y);
|
||||
def result = project(y, compute_def);
|
||||
if (compute_def) {
|
||||
result = (result * D) + u;
|
||||
m_var2value[x] = eval(result);
|
||||
}
|
||||
SASSERT(!compute_def || eval(result) == eval(x));
|
||||
return result;
|
||||
}
|
||||
|
||||
// update row with: x |-> C
|
||||
|
@ -949,39 +1158,67 @@ namespace opt {
|
|||
// 3x + t = 0 & 7 | (c*x + s) & ax <= u
|
||||
// 3 | -t & 21 | (-ct + 3s) & a-t <= 3u
|
||||
|
||||
void model_based_opt::solve_for(unsigned row_id1, unsigned x) {
|
||||
model_based_opt::def model_based_opt::solve_for(unsigned row_id1, unsigned x, bool compute_def) {
|
||||
TRACE("opt", tout << "v" << x << "\n" << m_rows[row_id1] << "\n";);
|
||||
rational a = get_coefficient(row_id1, x), b;
|
||||
ineq_type ty = m_rows[row_id1].m_type;
|
||||
SASSERT(!a.is_zero());
|
||||
SASSERT(m_rows[row_id1].m_type == t_eq);
|
||||
SASSERT(m_rows[row_id1].m_alive);
|
||||
if (m_var2is_int[x] && !abs(a).is_one()) {
|
||||
if (a.is_neg()) {
|
||||
a.neg();
|
||||
m_rows[row_id1].neg();
|
||||
}
|
||||
SASSERT(a.is_pos());
|
||||
if (ty == t_lt) {
|
||||
SASSERT(compute_def);
|
||||
m_rows[row_id1].m_coeff += a;
|
||||
}
|
||||
if (m_var2is_int[x] && !a.is_one()) {
|
||||
row& r1 = m_rows[row_id1];
|
||||
vector<var> coeffs;
|
||||
mk_coeffs_without(coeffs, r1.m_vars, x);
|
||||
rational c = r1.m_coeff;
|
||||
add_divides(coeffs, c, abs(a));
|
||||
add_divides(coeffs, c, a);
|
||||
}
|
||||
unsigned_vector const& row_ids = m_var2row_ids[x];
|
||||
uint_set visited;
|
||||
visited.insert(row_id1);
|
||||
for (unsigned i = 0; i < row_ids.size(); ++i) {
|
||||
unsigned row_id2 = row_ids[i];
|
||||
for (unsigned row_id2 : row_ids) {
|
||||
if (!visited.contains(row_id2)) {
|
||||
visited.insert(row_id2);
|
||||
visited.insert(row_id2);
|
||||
b = get_coefficient(row_id2, x);
|
||||
if (!b.is_zero()) {
|
||||
resolve(row_id1, a, row_id2, x);
|
||||
row& dst = m_rows[row_id2];
|
||||
switch (dst.m_type) {
|
||||
case t_eq:
|
||||
case t_lt:
|
||||
case t_le:
|
||||
solve(row_id1, a, row_id2, x);
|
||||
break;
|
||||
case t_mod:
|
||||
// mod reduction already done.
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
def result;
|
||||
if (compute_def) {
|
||||
result = def(m_rows[row_id1], x);
|
||||
m_var2value[x] = eval(result);
|
||||
}
|
||||
retire_row(row_id1);
|
||||
return result;
|
||||
}
|
||||
|
||||
void model_based_opt::project(unsigned num_vars, unsigned const* vars) {
|
||||
|
||||
vector<model_based_opt::def> model_based_opt::project(unsigned num_vars, unsigned const* vars, bool compute_def) {
|
||||
vector<def> result;
|
||||
for (unsigned i = 0; i < num_vars; ++i) {
|
||||
project(vars[i]);
|
||||
result.push_back(project(vars[i], compute_def));
|
||||
TRACE("opt", display(tout << "After projecting: v" << vars[i] << "\n"););
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -58,6 +58,25 @@ namespace opt {
|
|||
rational m_value; // value of m_vars + m_coeff under interpretation of m_var2value.
|
||||
bool m_alive; // rows can be marked dead if they have been processed.
|
||||
void reset() { m_vars.reset(); m_coeff.reset(); m_value.reset(); }
|
||||
|
||||
void neg() { for (var & v : m_vars) v.m_coeff.neg(); m_coeff.neg(); m_value.neg(); }
|
||||
rational get_coefficient(unsigned x) const;
|
||||
};
|
||||
|
||||
// A definition is a linear term of the form (vars + coeff) / div
|
||||
struct def {
|
||||
def(): m_div(1) {}
|
||||
def(row const& r, unsigned x);
|
||||
def(def const& other): m_vars(other.m_vars), m_coeff(other.m_coeff), m_div(other.m_div) {}
|
||||
vector<var> m_vars;
|
||||
rational m_coeff;
|
||||
rational m_div;
|
||||
def operator+(def const& other) const;
|
||||
def operator/(unsigned n) const { return *this / rational(n); }
|
||||
def operator/(rational const& n) const;
|
||||
def operator*(rational const& n) const;
|
||||
def operator+(rational const& n) const;
|
||||
void normalize();
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -81,10 +100,16 @@ namespace opt {
|
|||
|
||||
rational get_coefficient(unsigned row_id, unsigned var_id) const;
|
||||
|
||||
rational get_row_value(row const& r) const;
|
||||
rational eval(row const& r) const;
|
||||
|
||||
rational eval(unsigned x) const;
|
||||
|
||||
rational eval(def const& d) const;
|
||||
|
||||
void resolve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x);
|
||||
|
||||
void solve(unsigned row_src, rational const& a1, unsigned row_dst, unsigned x);
|
||||
|
||||
void mul_add(bool same_sign, unsigned row_id1, rational const& c, unsigned row_id2);
|
||||
|
||||
void mul_add(unsigned x, rational const& a1, unsigned row_src, rational const& a2, unsigned row_dst);
|
||||
|
@ -95,6 +120,8 @@ namespace opt {
|
|||
|
||||
void sub(unsigned dst, rational const& c);
|
||||
|
||||
void del_var(unsigned dst, unsigned x);
|
||||
|
||||
void set_row(unsigned row_id, vector<var> const& coeffs, rational const& c, rational const& m, ineq_type rel);
|
||||
|
||||
void add_constraint(vector<var> const& coeffs, rational const& c, rational const& m, ineq_type r);
|
||||
|
@ -117,12 +144,12 @@ namespace opt {
|
|||
|
||||
void update_value(unsigned x, rational const& val);
|
||||
|
||||
void project(unsigned var);
|
||||
def project(unsigned var, bool compute_def);
|
||||
|
||||
void solve_for(unsigned row_id, unsigned x);
|
||||
|
||||
void solve_mod(unsigned x, unsigned_vector const& mod_rows);
|
||||
def solve_for(unsigned row_id, unsigned x, bool compute_def);
|
||||
|
||||
def solve_mod(unsigned x, unsigned_vector const& mod_rows, bool compute_def);
|
||||
|
||||
bool is_int(unsigned x) const { return m_var2is_int[x]; }
|
||||
|
||||
void retire_row(unsigned row_id);
|
||||
|
@ -159,7 +186,7 @@ namespace opt {
|
|||
//
|
||||
// Project set of variables from inequalities.
|
||||
//
|
||||
void project(unsigned num_vars, unsigned const* vars);
|
||||
vector<def> project(unsigned num_vars, unsigned const* vars, bool compute_def);
|
||||
|
||||
//
|
||||
// Extract current rows (after projection).
|
||||
|
@ -167,13 +194,17 @@ namespace opt {
|
|||
void get_live_rows(vector<row>& rows);
|
||||
|
||||
void display(std::ostream& out) const;
|
||||
void display(std::ostream& out, row const& r) const;
|
||||
static std::ostream& display(std::ostream& out, row const& r);
|
||||
static std::ostream& display(std::ostream& out, def const& r);
|
||||
static void display(std::ostream& out, vector<var> const& vars, rational const& coeff);
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, opt::ineq_type ie);
|
||||
inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::def const& d) { return opt::model_based_opt::display(out, d); }
|
||||
inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::row const& r) { return opt::model_based_opt::display(out, r); }
|
||||
|
||||
inline std::ostream& operator<<(std::ostream& out, opt::model_based_opt::var const v) { return out << "v" << v.m_id; }
|
||||
|
||||
|
|
|
@ -47,8 +47,8 @@ bool model_core::eval(func_decl* f, expr_ref & r) const {
|
|||
|
||||
void model_core::register_decl(func_decl * d, expr * v) {
|
||||
SASSERT(d->get_arity() == 0);
|
||||
decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, 0);
|
||||
if (entry->get_data().m_value == 0) {
|
||||
decl2expr::obj_map_entry * entry = m_interp.insert_if_not_there2(d, nullptr);
|
||||
if (entry->get_data().m_value == nullptr) {
|
||||
// new entry
|
||||
m_decls.push_back(d);
|
||||
m_const_decls.push_back(d);
|
||||
|
@ -67,8 +67,8 @@ void model_core::register_decl(func_decl * d, expr * v) {
|
|||
void model_core::register_decl(func_decl * d, func_interp * fi) {
|
||||
SASSERT(d->get_arity() > 0);
|
||||
SASSERT(&fi->m() == &m_manager);
|
||||
decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, 0);
|
||||
if (entry->get_data().m_value == 0) {
|
||||
decl2finterp::obj_map_entry * entry = m_finterp.insert_if_not_there2(d, nullptr);
|
||||
if (entry->get_data().m_value == nullptr) {
|
||||
// new entry
|
||||
m_decls.push_back(d);
|
||||
m_func_decls.push_back(d);
|
||||
|
|
|
@ -53,6 +53,7 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
bool m_model_completion;
|
||||
bool m_cache;
|
||||
bool m_array_equalities;
|
||||
bool m_array_as_stores;
|
||||
|
||||
evaluator_cfg(ast_manager & m, model_core & md, params_ref const & p):
|
||||
m(m),
|
||||
|
@ -87,6 +88,7 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
m_model_completion = p.completion();
|
||||
m_cache = p.cache();
|
||||
m_array_equalities = p.array_equalities();
|
||||
m_array_as_stores = p.array_as_stores();
|
||||
}
|
||||
|
||||
bool evaluate(func_decl * f, unsigned num, expr * const * args, expr_ref & result) {
|
||||
|
@ -201,15 +203,16 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
return st;
|
||||
}
|
||||
|
||||
void expand_value(expr_ref& val) {
|
||||
void expand_stores(expr_ref& val) {
|
||||
vector<expr_ref_vector> stores;
|
||||
expr_ref else_case(m);
|
||||
bool _unused;
|
||||
if (m_ar.is_array(val) && extract_array_func_interp(val, stores, else_case, _unused)) {
|
||||
if (m_array_as_stores &&
|
||||
m_ar.is_array(val) &&
|
||||
extract_array_func_interp(val, stores, else_case, _unused)) {
|
||||
sort* srt = m.get_sort(val);
|
||||
val = m_ar.mk_const_array(srt, else_case);
|
||||
for (unsigned i = stores.size(); i > 0; ) {
|
||||
--i;
|
||||
for (unsigned i = stores.size(); i-- > 0; ) {
|
||||
expr_ref_vector args(m);
|
||||
args.push_back(val);
|
||||
args.append(stores[i].size(), stores[i].c_ptr());
|
||||
|
@ -288,7 +291,6 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
|
||||
bool cache_results() const { return m_cache; }
|
||||
|
||||
|
||||
br_status mk_array_eq(expr* a, expr* b, expr_ref& result) {
|
||||
TRACE("model_evaluator", tout << "mk_array_eq " << m_array_equalities << "\n";);
|
||||
if (a == b) {
|
||||
|
@ -502,9 +504,6 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
TRACE("model_evaluator", tout << "else case: " << mk_pp(else_case, m) << "\n";);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
template class rewriter_tpl<evaluator_cfg>;
|
||||
|
@ -518,10 +517,7 @@ struct model_evaluator::imp : public rewriter_tpl<evaluator_cfg> {
|
|||
m_cfg(md.get_manager(), md, p) {
|
||||
set_cancel_check(false);
|
||||
}
|
||||
|
||||
void expand_value (expr_ref &val) {
|
||||
m_cfg.expand_value (val);
|
||||
}
|
||||
void expand_stores(expr_ref &val) {m_cfg.expand_stores(val);}
|
||||
};
|
||||
|
||||
model_evaluator::model_evaluator(model_core & md, params_ref const & p) {
|
||||
|
@ -567,10 +563,16 @@ void model_evaluator::reset(params_ref const & p) {
|
|||
updt_params(p);
|
||||
}
|
||||
|
||||
void model_evaluator::reset(model_core &model, params_ref const& p) {
|
||||
dealloc(m_imp);
|
||||
m_imp = alloc(imp, model, p);
|
||||
}
|
||||
|
||||
|
||||
void model_evaluator::operator()(expr * t, expr_ref & result) {
|
||||
TRACE("model_evaluator", tout << mk_ismt2_pp(t, m()) << "\n";);
|
||||
m_imp->operator()(t, result);
|
||||
m_imp->expand_value(result);
|
||||
m_imp->expand_stores(result);
|
||||
}
|
||||
|
||||
expr_ref model_evaluator::operator()(expr * t) {
|
||||
|
@ -579,3 +581,44 @@ expr_ref model_evaluator::operator()(expr * t) {
|
|||
this->operator()(t, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref_vector model_evaluator::operator()(expr_ref_vector const& ts) {
|
||||
expr_ref_vector rs(m());
|
||||
for (expr* t : ts) rs.push_back((*this)(t));
|
||||
return rs;
|
||||
}
|
||||
|
||||
|
||||
bool model_evaluator::is_true(expr* t) {
|
||||
expr_ref tmp(m());
|
||||
return eval(t, tmp, true) && m().is_true(tmp);
|
||||
}
|
||||
|
||||
bool model_evaluator::is_false(expr* t) {
|
||||
expr_ref tmp(m());
|
||||
return eval(t, tmp, true) && m().is_false(tmp);
|
||||
}
|
||||
|
||||
bool model_evaluator::is_true(expr_ref_vector const& ts) {
|
||||
for (expr* t : ts) if (!is_true(t)) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool model_evaluator::eval(expr* t, expr_ref& r, bool model_completion) {
|
||||
set_model_completion(model_completion);
|
||||
try {
|
||||
r = (*this)(t);
|
||||
return true;
|
||||
}
|
||||
catch (model_evaluator_exception &ex) {
|
||||
(void)ex;
|
||||
TRACE("model_evaluator", tout << ex.msg () << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool model_evaluator::eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion) {
|
||||
expr_ref tmp(m());
|
||||
tmp = mk_and(ts);
|
||||
return eval(tmp, r, model_completion);
|
||||
}
|
||||
|
|
|
@ -43,11 +43,25 @@ public:
|
|||
static void get_param_descrs(param_descrs & r);
|
||||
|
||||
void operator()(expr * t, expr_ref & r);
|
||||
|
||||
expr_ref operator()(expr* t);
|
||||
expr_ref_vector operator()(expr_ref_vector const& ts);
|
||||
|
||||
// exception safe
|
||||
bool eval(expr* t, expr_ref& r, bool model_completion = true);
|
||||
bool eval(expr_ref_vector const& ts, expr_ref& r, bool model_completion = true);
|
||||
|
||||
bool is_true(expr * t);
|
||||
bool is_false(expr * t);
|
||||
bool is_true(expr_ref_vector const& ts);
|
||||
|
||||
/**
|
||||
* best effort evaluator of extensional array equality.
|
||||
*/
|
||||
expr_ref eval_array_eq(app* e, expr* arg1, expr* arg2);
|
||||
|
||||
void cleanup(params_ref const & p = params_ref());
|
||||
void reset(params_ref const & p = params_ref());
|
||||
void reset(model_core& model, params_ref const & p = params_ref());
|
||||
|
||||
unsigned get_num_steps() const;
|
||||
};
|
||||
|
|
|
@ -4,6 +4,7 @@ def_module_params('model_evaluator',
|
|||
max_steps_param(),
|
||||
('completion', BOOL, False, 'assigns an interptetation to symbols that do not have one in the current model, when evaluating expressions in the current model'),
|
||||
('cache', BOOL, True, 'cache intermediate results in the model evaluator'),
|
||||
('array_equalities', BOOL, True, 'evaluate array equalities')
|
||||
('array_equalities', BOOL, True, 'evaluate array equalities'),
|
||||
('array_as_stores', BOOL, True, 'return array as a set of stores'),
|
||||
))
|
||||
|
||||
|
|
|
@ -172,7 +172,6 @@ void model_implicant::process_formula(app* e, ptr_vector<expr>& todo, ptr_vector
|
|||
case OP_FALSE:
|
||||
break;
|
||||
case OP_EQ:
|
||||
case OP_IFF:
|
||||
if (args[0] == args[1]) {
|
||||
SASSERT(v);
|
||||
// no-op
|
||||
|
@ -742,10 +741,6 @@ void model_implicant::eval_basic(app* e) {
|
|||
set_x(e);
|
||||
}
|
||||
break;
|
||||
case OP_IFF:
|
||||
VERIFY(m.is_iff(e, arg1, arg2));
|
||||
eval_eq(e, arg1, arg2);
|
||||
break;
|
||||
case OP_ITE:
|
||||
VERIFY(m.is_ite(e, argCond, argThen, argElse));
|
||||
if (is_true(argCond)) {
|
||||
|
|
|
@ -18,5 +18,5 @@ z3_add_component(muz
|
|||
smt
|
||||
smt2parser
|
||||
PYG_FILES
|
||||
fixedpoint_params.pyg
|
||||
fp_params.pyg
|
||||
)
|
||||
|
|
|
@ -27,7 +27,7 @@ Revision History:
|
|||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
#include "muz/base/fp_params.hpp"
|
||||
#include "ast/ast_pp_util.h"
|
||||
|
||||
|
||||
|
@ -152,15 +152,15 @@ namespace datalog {
|
|||
|
||||
class context::restore_rules : public trail<context> {
|
||||
rule_set* m_old_rules;
|
||||
void reset() {
|
||||
dealloc(m_old_rules);
|
||||
void reset() {
|
||||
dealloc(m_old_rules);
|
||||
m_old_rules = nullptr;
|
||||
}
|
||||
public:
|
||||
restore_rules(rule_set& r): m_old_rules(alloc(rule_set, r)) {}
|
||||
|
||||
~restore_rules() override {}
|
||||
|
||||
|
||||
void undo(context& ctx) override {
|
||||
ctx.replace_rules(*m_old_rules);
|
||||
reset();
|
||||
|
@ -188,10 +188,8 @@ namespace datalog {
|
|||
if (m_trail.get_num_scopes() == 0) {
|
||||
throw default_exception("there are no backtracking points to pop to");
|
||||
}
|
||||
if (m_engine.get()) {
|
||||
throw default_exception("pop operation is only supported by duality engine");
|
||||
}
|
||||
m_trail.pop_scope(1);
|
||||
throw default_exception("pop operation is not supported");
|
||||
m_trail.pop_scope(1);
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
|
@ -205,7 +203,7 @@ namespace datalog {
|
|||
m_register_engine(re),
|
||||
m_fparams(fp),
|
||||
m_params_ref(pa),
|
||||
m_params(alloc(fixedpoint_params, m_params_ref)),
|
||||
m_params(alloc(fp_params, m_params_ref)),
|
||||
m_decl_util(m),
|
||||
m_rewriter(m),
|
||||
m_var_subst(m),
|
||||
|
@ -237,7 +235,7 @@ namespace datalog {
|
|||
|
||||
context::~context() {
|
||||
reset();
|
||||
dealloc(m_params);
|
||||
dealloc(m_params);
|
||||
}
|
||||
|
||||
void context::reset() {
|
||||
|
@ -293,14 +291,14 @@ namespace datalog {
|
|||
bool context::similarity_compressor() const { return m_params->datalog_similarity_compressor(); }
|
||||
unsigned context::similarity_compressor_threshold() const { return m_params->datalog_similarity_compressor_threshold(); }
|
||||
unsigned context::soft_timeout() const { return m_fparams.m_timeout; }
|
||||
unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); }
|
||||
unsigned context::initial_restart_timeout() const { return m_params->datalog_initial_restart_timeout(); }
|
||||
bool context::generate_explanations() const { return m_params->datalog_generate_explanations(); }
|
||||
bool context::explanations_on_relation_level() const { return m_params->datalog_explanations_on_relation_level(); }
|
||||
bool context::magic_sets_for_queries() const { return m_params->datalog_magic_sets_for_queries(); }
|
||||
symbol context::tab_selection() const { return m_params->tab_selection(); }
|
||||
bool context::xform_coi() const { return m_params->xform_coi(); }
|
||||
bool context::xform_slice() const { return m_params->xform_slice(); }
|
||||
bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); }
|
||||
bool context::xform_bit_blast() const { return m_params->xform_bit_blast(); }
|
||||
bool context::karr() const { return m_params->xform_karr(); }
|
||||
bool context::scale() const { return m_params->xform_scale(); }
|
||||
bool context::magic() const { return m_params->xform_magic(); }
|
||||
|
@ -430,7 +428,7 @@ namespace datalog {
|
|||
}
|
||||
|
||||
|
||||
void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
|
||||
void context::set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
|
||||
symbol const * relation_names) {
|
||||
if (relation_name_cnt > 0) {
|
||||
ensure_engine();
|
||||
|
@ -440,9 +438,9 @@ namespace datalog {
|
|||
}
|
||||
}
|
||||
|
||||
func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix,
|
||||
func_decl * context::mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix,
|
||||
unsigned arity, sort * const * domain, func_decl* orig_pred) {
|
||||
func_decl* new_pred =
|
||||
func_decl* new_pred =
|
||||
m.mk_fresh_func_decl(prefix, suffix, arity, domain, m.mk_bool_sort());
|
||||
|
||||
register_predicate(new_pred, true);
|
||||
|
@ -475,7 +473,7 @@ namespace datalog {
|
|||
//
|
||||
// Update a rule with a new.
|
||||
// It requires basic subsumption.
|
||||
//
|
||||
//
|
||||
void context::update_rule(expr* rl, symbol const& name) {
|
||||
datalog::rule_manager& rm = get_rule_manager();
|
||||
proof* p = nullptr;
|
||||
|
@ -496,13 +494,13 @@ namespace datalog {
|
|||
rule* old_rule = nullptr;
|
||||
for (unsigned i = 0; i < size_before; ++i) {
|
||||
if (rls[i]->name() == name) {
|
||||
if (old_rule) {
|
||||
if (old_rule) {
|
||||
std::stringstream strm;
|
||||
strm << "Rule " << name << " occurs twice. It cannot be modified";
|
||||
m_rule_set.del_rule(r);
|
||||
throw default_exception(strm.str());
|
||||
}
|
||||
old_rule = rls[i];
|
||||
old_rule = rls[i];
|
||||
}
|
||||
}
|
||||
if (old_rule) {
|
||||
|
@ -558,7 +556,7 @@ namespace datalog {
|
|||
ensure_engine();
|
||||
m_engine->add_cover(level, pred, property);
|
||||
}
|
||||
|
||||
|
||||
void context::add_invariant(func_decl* pred, expr *property)
|
||||
{
|
||||
ensure_engine();
|
||||
|
@ -568,34 +566,28 @@ namespace datalog {
|
|||
void context::check_rules(rule_set& r) {
|
||||
m_rule_properties.set_generate_proof(generate_proof_trace());
|
||||
switch(get_engine()) {
|
||||
case DATALOG_ENGINE:
|
||||
case DATALOG_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_quantifier_free();
|
||||
m_rule_properties.check_uninterpreted_free();
|
||||
m_rule_properties.check_nested_free();
|
||||
m_rule_properties.check_nested_free();
|
||||
m_rule_properties.check_infinite_sorts();
|
||||
break;
|
||||
case SPACER_ENGINE:
|
||||
case PDR_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
m_rule_properties.check_uninterpreted_free();
|
||||
break;
|
||||
case QPDR_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
m_rule_properties.check_uninterpreted_free();
|
||||
break;
|
||||
case BMC_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
break;
|
||||
case QBMC_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
break;
|
||||
case TAB_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
|
@ -658,7 +650,7 @@ namespace datalog {
|
|||
add_fact(pred, rfact);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void context::add_table_fact(func_decl * pred, unsigned num_args, unsigned args[]) {
|
||||
if (pred->get_arity() != num_args) {
|
||||
std::ostringstream out;
|
||||
|
@ -690,7 +682,7 @@ namespace datalog {
|
|||
reopen();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void context::reopen() {
|
||||
SASSERT(m_closed);
|
||||
m_rule_set.reopen();
|
||||
|
@ -703,7 +695,7 @@ namespace datalog {
|
|||
transformer.register_plugin(plugin);
|
||||
transform_rules(transformer);
|
||||
}
|
||||
|
||||
|
||||
void context::transform_rules(rule_transformer& transf) {
|
||||
SASSERT(m_closed); //we must finish adding rules before we start transforming them
|
||||
TRACE("dl", display_rules(tout););
|
||||
|
@ -732,7 +724,7 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void context::collect_params(param_descrs& p) {
|
||||
fixedpoint_params::collect_param_descrs(p);
|
||||
fp_params::collect_param_descrs(p);
|
||||
insert_timeout(p);
|
||||
}
|
||||
|
||||
|
@ -740,8 +732,8 @@ namespace datalog {
|
|||
m_params_ref.copy(p);
|
||||
if (m_engine.get()) m_engine->updt_params();
|
||||
m_generate_proof_trace = m_params->generate_proof_trace();
|
||||
m_unbound_compressor = m_params->datalog_unbound_compressor();
|
||||
m_default_relation = m_params->datalog_default_relation();
|
||||
m_unbound_compressor = m_params->datalog_unbound_compressor();
|
||||
m_default_relation = m_params->datalog_default_relation();
|
||||
}
|
||||
|
||||
expr_ref context::get_background_assertion() {
|
||||
|
@ -756,7 +748,7 @@ namespace datalog {
|
|||
|
||||
void context::assert_expr(expr* e) {
|
||||
TRACE("dl", tout << mk_ismt2_pp(e, m) << "\n";);
|
||||
m_background.push_back(e);
|
||||
m_background.push_back(e);
|
||||
}
|
||||
|
||||
void context::cleanup() {
|
||||
|
@ -776,19 +768,14 @@ namespace datalog {
|
|||
DL_ENGINE get_engine() const { return m_engine_type; }
|
||||
|
||||
void operator()(expr* e) {
|
||||
if (is_quantifier(e)) {
|
||||
m_engine_type = QPDR_ENGINE;
|
||||
}
|
||||
else if (m_engine_type != QPDR_ENGINE) {
|
||||
if (a.is_int_real(e)) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
else if (is_var(e) && m.is_bool(e)) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
else if (dt.is_datatype(m.get_sort(e))) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
}
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -798,19 +785,13 @@ namespace datalog {
|
|||
return;
|
||||
}
|
||||
symbol e = m_params->engine();
|
||||
|
||||
|
||||
if (e == symbol("datalog")) {
|
||||
m_engine_type = DATALOG_ENGINE;
|
||||
}
|
||||
else if (e == symbol("spacer")) {
|
||||
m_engine_type = SPACER_ENGINE;
|
||||
}
|
||||
else if (e == symbol("pdr")) {
|
||||
m_engine_type = PDR_ENGINE;
|
||||
}
|
||||
else if (e == symbol("qpdr")) {
|
||||
m_engine_type = QPDR_ENGINE;
|
||||
}
|
||||
else if (e == symbol("bmc")) {
|
||||
m_engine_type = BMC_ENGINE;
|
||||
}
|
||||
|
@ -830,7 +811,7 @@ namespace datalog {
|
|||
if (m_engine_type == LAST_ENGINE) {
|
||||
expr_fast_mark1 mark;
|
||||
engine_type_proc proc(m);
|
||||
m_engine_type = DATALOG_ENGINE;
|
||||
m_engine_type = DATALOG_ENGINE;
|
||||
for (unsigned i = 0; m_engine_type == DATALOG_ENGINE && i < m_rule_set.get_num_rules(); ++i) {
|
||||
rule * r = m_rule_set.get_rule(i);
|
||||
quick_for_each_expr(proc, mark, r->get_head());
|
||||
|
@ -858,8 +839,6 @@ namespace datalog {
|
|||
switch (get_engine()) {
|
||||
case DATALOG_ENGINE:
|
||||
case SPACER_ENGINE:
|
||||
case PDR_ENGINE:
|
||||
case QPDR_ENGINE:
|
||||
case BMC_ENGINE:
|
||||
case QBMC_ENGINE:
|
||||
case TAB_ENGINE:
|
||||
|
@ -882,8 +861,6 @@ namespace datalog {
|
|||
switch (get_engine()) {
|
||||
case DATALOG_ENGINE:
|
||||
case SPACER_ENGINE:
|
||||
case PDR_ENGINE:
|
||||
case QPDR_ENGINE:
|
||||
case BMC_ENGINE:
|
||||
case QBMC_ENGINE:
|
||||
case TAB_ENGINE:
|
||||
|
@ -916,15 +893,15 @@ namespace datalog {
|
|||
m_rel = dynamic_cast<rel_context_base*>(m_engine.get());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbool context::rel_query(unsigned num_rels, func_decl * const* rels) {
|
||||
lbool context::rel_query(unsigned num_rels, func_decl * const* rels) {
|
||||
m_last_answer = nullptr;
|
||||
ensure_engine();
|
||||
return m_engine->query(num_rels, rels);
|
||||
}
|
||||
|
||||
|
||||
expr* context::get_answer_as_formula() {
|
||||
if (m_last_answer) {
|
||||
return m_last_answer.get();
|
||||
|
@ -977,7 +954,7 @@ namespace datalog {
|
|||
|
||||
void context::display(std::ostream & out) const {
|
||||
display_rules(out);
|
||||
if (m_rel) m_rel->display_facts(out);
|
||||
if (m_rel) m_rel->display_facts(out);
|
||||
}
|
||||
|
||||
void context::display_profile(std::ostream& out) const {
|
||||
|
@ -1013,10 +990,10 @@ namespace datalog {
|
|||
bool context::result_contains_fact(relation_fact const& f) {
|
||||
return m_rel && m_rel->result_contains_fact(f);
|
||||
}
|
||||
|
||||
|
||||
// NB: algebraic data-types declarations will not be printed.
|
||||
|
||||
static void collect_free_funcs(unsigned sz, expr* const* exprs,
|
||||
static void collect_free_funcs(unsigned sz, expr* const* exprs,
|
||||
ast_pp_util& v,
|
||||
mk_fresh_name& fresh_names) {
|
||||
v.collect(sz, exprs);
|
||||
|
@ -1028,7 +1005,7 @@ namespace datalog {
|
|||
fresh_names.add(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void context::get_raw_rule_formulas(expr_ref_vector& rules, svector<symbol>& names, unsigned_vector &bounds) {
|
||||
for (unsigned i = 0; i < m_rule_fmls.size(); ++i) {
|
||||
expr_ref r = bind_vars(m_rule_fmls[i].get(), true);
|
||||
|
@ -1041,7 +1018,7 @@ namespace datalog {
|
|||
void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector<symbol>& names) {
|
||||
expr_ref fml(m);
|
||||
rule_manager& rm = get_rule_manager();
|
||||
|
||||
|
||||
// ensure that rules are all using bound variables.
|
||||
for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) {
|
||||
m_free_vars(m_rule_fmls[i].get());
|
||||
|
@ -1090,7 +1067,7 @@ namespace datalog {
|
|||
}
|
||||
for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) {
|
||||
rules.push_back(m_rule_fmls[i].get());
|
||||
names.push_back(m_rule_names[i]);
|
||||
names.push_back(m_rule_names[i]);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1103,7 +1080,7 @@ namespace datalog {
|
|||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) {
|
||||
ast_manager& m = get_manager();
|
||||
ast_pp_util visitor(m);
|
||||
|
@ -1132,7 +1109,7 @@ namespace datalog {
|
|||
for (unsigned i = 0; i < sz; ++i) {
|
||||
func_decl* f = visitor.coll.get_func_decls()[i];
|
||||
if (f->get_family_id() != null_family_id) {
|
||||
//
|
||||
//
|
||||
}
|
||||
else if (is_predicate(f) && use_fixedpoint_extensions) {
|
||||
rels.insert(f);
|
||||
|
@ -1145,12 +1122,12 @@ namespace datalog {
|
|||
if (!use_fixedpoint_extensions) {
|
||||
out << "(set-logic HORN)\n";
|
||||
}
|
||||
for (func_decl * f : rels)
|
||||
for (func_decl * f : rels)
|
||||
visitor.remove_decl(f);
|
||||
|
||||
visitor.display_decls(out);
|
||||
|
||||
for (func_decl * f : rels)
|
||||
for (func_decl * f : rels)
|
||||
display_rel_decl(out, f);
|
||||
|
||||
if (use_fixedpoint_extensions && do_declare_vars) {
|
||||
|
@ -1166,7 +1143,7 @@ namespace datalog {
|
|||
PP(axioms[i]);
|
||||
out << ")\n";
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
out << (use_fixedpoint_extensions?"(rule ":"(assert ");
|
||||
expr* r = rules[i].get();
|
||||
symbol nm = names[i];
|
||||
|
@ -1179,7 +1156,7 @@ namespace datalog {
|
|||
while (fresh_names.contains(nm)) {
|
||||
std::ostringstream s;
|
||||
s << nm << "!";
|
||||
nm = symbol(s.str().c_str());
|
||||
nm = symbol(s.str().c_str());
|
||||
}
|
||||
fresh_names.add(nm);
|
||||
display_symbol(out, nm) << ")";
|
||||
|
@ -1205,7 +1182,7 @@ namespace datalog {
|
|||
args.push_back(m.mk_var(j, m_free_vars[j]));
|
||||
}
|
||||
qfn = m.mk_implies(q, m.mk_app(fn, args.size(), args.c_ptr()));
|
||||
|
||||
|
||||
out << "(assert ";
|
||||
PP(qfn);
|
||||
out << ")\n";
|
||||
|
@ -1232,7 +1209,7 @@ namespace datalog {
|
|||
smt2_pp_environment_dbg env(m);
|
||||
out << "(declare-rel ";
|
||||
display_symbol(out, f->get_name()) << " (";
|
||||
for (unsigned i = 0; i < f->get_arity(); ++i) {
|
||||
for (unsigned i = 0; i < f->get_arity(); ++i) {
|
||||
ast_smt2_pp(out, f->get_domain(i), env);
|
||||
if (i + 1 < f->get_arity()) {
|
||||
out << " ";
|
||||
|
@ -1262,12 +1239,12 @@ namespace datalog {
|
|||
void context::declare_vars(expr_ref_vector& rules, mk_fresh_name& fresh_names, std::ostream& out) {
|
||||
//
|
||||
// replace bound variables in rules by 'var declarations'
|
||||
// First remove quantifers, then replace bound variables
|
||||
// First remove quantifers, then replace bound variables
|
||||
// by fresh constants.
|
||||
//
|
||||
//
|
||||
smt2_pp_environment_dbg env(m);
|
||||
var_subst vsubst(m, false);
|
||||
|
||||
|
||||
expr_ref_vector fresh_vars(m), subst(m);
|
||||
expr_ref res(m);
|
||||
obj_map<sort, unsigned_vector> var_idxs;
|
||||
|
@ -1280,7 +1257,7 @@ namespace datalog {
|
|||
quantifier* q = to_quantifier(r);
|
||||
if (!q->is_forall()) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (has_quantifiers(q->get_expr())) {
|
||||
continue;
|
||||
}
|
||||
|
@ -1310,7 +1287,7 @@ namespace datalog {
|
|||
fresh_vars.push_back(m.mk_const(name, s));
|
||||
out << "(declare-var " << name << " ";
|
||||
ast_smt2_pp(out, s, env);
|
||||
out << ")\n";
|
||||
out << ")\n";
|
||||
}
|
||||
subst.push_back(fresh_vars[vars[max_var]].get());
|
||||
}
|
||||
|
@ -1322,4 +1299,3 @@ namespace datalog {
|
|||
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ Revision History:
|
|||
#include "muz/base/bind_variables.h"
|
||||
#include "muz/base/rule_properties.h"
|
||||
|
||||
struct fixedpoint_params;
|
||||
struct fp_params;
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
@ -98,7 +98,7 @@ namespace datalog {
|
|||
relation_fact(ast_manager & m) : app_ref_vector(m) {}
|
||||
relation_fact(ast_manager & m, unsigned sz) : app_ref_vector(m) { resize(sz); }
|
||||
relation_fact(context & ctx);
|
||||
|
||||
|
||||
iterator begin() const { return c_ptr(); }
|
||||
iterator end() const { return c_ptr()+size(); }
|
||||
|
||||
|
@ -126,7 +126,7 @@ namespace datalog {
|
|||
virtual bool has_facts(func_decl * pred) const = 0;
|
||||
virtual void store_relation(func_decl * pred, relation_base * rel) = 0;
|
||||
virtual void inherit_predicate_kind(func_decl* new_pred, func_decl* orig_pred) = 0;
|
||||
virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
|
||||
virtual void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
|
||||
symbol const * relation_names) = 0;
|
||||
virtual bool output_profile() const = 0;
|
||||
virtual void collect_non_empty_predicates(func_decl_set& preds) = 0;
|
||||
|
@ -147,7 +147,7 @@ namespace datalog {
|
|||
public:
|
||||
contains_pred(context& ctx): ctx(ctx) {}
|
||||
~contains_pred() override {}
|
||||
|
||||
|
||||
bool operator()(expr* e) override {
|
||||
return ctx.is_predicate(e);
|
||||
}
|
||||
|
@ -170,7 +170,7 @@ namespace datalog {
|
|||
register_engine_base& m_register_engine;
|
||||
smt_params & m_fparams;
|
||||
params_ref m_params_ref;
|
||||
fixedpoint_params* m_params;
|
||||
fp_params* m_params;
|
||||
bool m_generate_proof_trace; // cached configuration parameter
|
||||
bool m_unbound_compressor; // cached configuration parameter
|
||||
symbol m_default_relation; // cached configuration parameter
|
||||
|
@ -227,7 +227,7 @@ namespace datalog {
|
|||
|
||||
void push();
|
||||
void pop();
|
||||
|
||||
|
||||
bool saturation_was_run() const { return m_saturation_was_run; }
|
||||
void notify_saturation_was_run() { m_saturation_was_run = true; }
|
||||
|
||||
|
@ -236,7 +236,7 @@ namespace datalog {
|
|||
ast_manager & get_manager() const { return m; }
|
||||
rule_manager & get_rule_manager() { return m_rule_manager; }
|
||||
smt_params & get_fparams() const { return m_fparams; }
|
||||
fixedpoint_params const& get_params() const { return *m_params; }
|
||||
fp_params const& get_params() const { return *m_params; }
|
||||
DL_ENGINE get_engine() { configure_engine(); return m_engine_type; }
|
||||
register_engine_base& get_register_engine() { return m_register_engine; }
|
||||
th_rewriter& get_rewriter() { return m_rewriter; }
|
||||
|
@ -251,7 +251,7 @@ namespace datalog {
|
|||
symbol default_table() const;
|
||||
symbol default_relation() const;
|
||||
void set_default_relation(symbol const& s);
|
||||
symbol default_table_checker() const;
|
||||
symbol default_table_checker() const;
|
||||
symbol check_relation() const;
|
||||
bool default_table_checked() const;
|
||||
bool dbg_fpr_nonempty_relation_signature() const;
|
||||
|
@ -275,7 +275,7 @@ namespace datalog {
|
|||
bool compress_unbound() const;
|
||||
bool quantify_arrays() const;
|
||||
bool instantiate_quantifiers() const;
|
||||
bool xform_bit_blast() const;
|
||||
bool xform_bit_blast() const;
|
||||
bool xform_slice() const;
|
||||
bool xform_coi() const;
|
||||
bool array_blast() const;
|
||||
|
@ -291,9 +291,9 @@ namespace datalog {
|
|||
void register_variable(func_decl* var);
|
||||
|
||||
/*
|
||||
Replace constants that have been registered as
|
||||
Replace constants that have been registered as
|
||||
variables by de-Bruijn indices and corresponding
|
||||
universal (if is_forall is true) or existential
|
||||
universal (if is_forall is true) or existential
|
||||
quantifier.
|
||||
*/
|
||||
expr_ref bind_vars(expr* fml, bool is_forall);
|
||||
|
@ -303,7 +303,7 @@ namespace datalog {
|
|||
/**
|
||||
Register datalog relation.
|
||||
|
||||
If named is true, we associate the predicate with its name, so that it can be
|
||||
If named is true, we associate the predicate with its name, so that it can be
|
||||
retrieved by the try_get_predicate_decl() function. Auxiliary predicates introduced
|
||||
e.g. by rule transformations do not need to be named.
|
||||
*/
|
||||
|
@ -326,7 +326,7 @@ namespace datalog {
|
|||
/**
|
||||
\brief If a predicate name has a \c func_decl object assigned, return pointer to it;
|
||||
otherwise return 0.
|
||||
|
||||
|
||||
Not all \c func_decl object used as relation identifiers need to be assigned to their
|
||||
names. Generally, the names coming from the parses are registered here.
|
||||
*/
|
||||
|
@ -334,13 +334,13 @@ namespace datalog {
|
|||
func_decl * res = nullptr;
|
||||
m_preds_by_name.find(pred_name, res);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Create a fresh head predicate declaration.
|
||||
|
||||
*/
|
||||
func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix,
|
||||
func_decl * mk_fresh_head_predicate(symbol const & prefix, symbol const & suffix,
|
||||
unsigned arity, sort * const * domain, func_decl* orig_pred=nullptr);
|
||||
|
||||
|
||||
|
@ -365,13 +365,13 @@ namespace datalog {
|
|||
/**
|
||||
\brief Assign names of variables used in the declaration of a predicate.
|
||||
|
||||
These names are used when printing out the relations to make the output conform
|
||||
These names are used when printing out the relations to make the output conform
|
||||
to the one of bddbddb.
|
||||
*/
|
||||
void set_argument_names(const func_decl * pred, const svector<symbol> & var_names);
|
||||
symbol get_argument_name(const func_decl * pred, unsigned arg_index);
|
||||
|
||||
void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
|
||||
void set_predicate_representation(func_decl * pred, unsigned relation_name_cnt,
|
||||
symbol const * relation_names);
|
||||
|
||||
void set_output_predicate(func_decl * pred) { m_rule_set.set_output_predicate(pred); }
|
||||
|
@ -385,9 +385,9 @@ namespace datalog {
|
|||
void add_fact(func_decl * pred, const relation_fact & fact);
|
||||
|
||||
bool has_facts(func_decl * pred) const;
|
||||
|
||||
|
||||
void add_rule(rule_ref& r);
|
||||
|
||||
|
||||
void assert_expr(expr* e);
|
||||
expr_ref get_background_assertion();
|
||||
unsigned get_num_assertions() { return m_background.size(); }
|
||||
|
@ -397,7 +397,7 @@ namespace datalog {
|
|||
Method exposed from API for adding rules.
|
||||
*/
|
||||
void add_rule(expr* rl, symbol const& name, unsigned bound = UINT_MAX);
|
||||
|
||||
|
||||
|
||||
/**
|
||||
Update a named rule.
|
||||
|
@ -421,9 +421,9 @@ namespace datalog {
|
|||
at 'level+1', 'level+2' etc, and include level=-1.
|
||||
*/
|
||||
expr_ref get_cover_delta(int level, func_decl* pred);
|
||||
|
||||
|
||||
/**
|
||||
Add a property of predicate 'pred' at 'level'.
|
||||
Add a property of predicate 'pred' at 'level'.
|
||||
It gets pushed forward when possible.
|
||||
*/
|
||||
void add_cover(int level, func_decl* pred, expr* property);
|
||||
|
@ -432,7 +432,7 @@ namespace datalog {
|
|||
Add an invariant of predicate 'pred'.
|
||||
*/
|
||||
void add_invariant (func_decl *pred, expr *property);
|
||||
|
||||
|
||||
/**
|
||||
\brief Check rule subsumption.
|
||||
*/
|
||||
|
@ -471,15 +471,15 @@ namespace datalog {
|
|||
proof_converter_ref& get_proof_converter() { return m_pc; }
|
||||
void add_proof_converter(proof_converter* pc) { m_pc = concat(m_pc.get(), pc); }
|
||||
|
||||
void transform_rules(rule_transformer& transf);
|
||||
void transform_rules(rule_transformer& transf);
|
||||
void transform_rules(rule_transformer::plugin* plugin);
|
||||
void replace_rules(rule_set const& rs);
|
||||
void record_transformed_rules();
|
||||
|
||||
void apply_default_transformation();
|
||||
void apply_default_transformation();
|
||||
|
||||
void collect_params(param_descrs& r);
|
||||
|
||||
|
||||
void updt_params(params_ref const& p);
|
||||
|
||||
void display_rules(std::ostream & out) const {
|
||||
|
@ -507,7 +507,7 @@ namespace datalog {
|
|||
/**
|
||||
\brief check if query 'q' is satisfied under asserted rules and background.
|
||||
|
||||
If successful, return OK and into \c result assign a relation with all
|
||||
If successful, return OK and into \c result assign a relation with all
|
||||
tuples matching the query. Otherwise return reason for failure and do not modify
|
||||
\c result.
|
||||
|
||||
|
@ -515,7 +515,7 @@ namespace datalog {
|
|||
starting from zero.
|
||||
|
||||
The caller becomes an owner of the relation object returned in \c result. The
|
||||
relation object, however, should not outlive the datalog context since it is
|
||||
relation object, however, should not outlive the datalog context since it is
|
||||
linked to a relation plugin in the context.
|
||||
*/
|
||||
|
||||
|
@ -524,7 +524,7 @@ namespace datalog {
|
|||
lbool query_from_lvl (expr* q, unsigned lvl);
|
||||
/**
|
||||
\brief retrieve model from inductive invariant that shows query is unsat.
|
||||
|
||||
|
||||
\pre engine == 'pdr' || engine == 'duality' - this option is only supported
|
||||
for PDR mode and Duality mode.
|
||||
*/
|
||||
|
@ -532,7 +532,7 @@ namespace datalog {
|
|||
|
||||
/**
|
||||
\brief retrieve proof from derivation of the query.
|
||||
|
||||
|
||||
\pre engine == 'pdr' || engine == 'duality'- this option is only supported
|
||||
for PDR mode and Duality mode.
|
||||
*/
|
||||
|
@ -588,12 +588,25 @@ namespace datalog {
|
|||
|
||||
rel_context_base* get_rel_context() { ensure_engine(); return m_rel; }
|
||||
|
||||
void add_callback(void *state,
|
||||
const datalog::t_new_lemma_eh new_lemma_eh,
|
||||
const datalog::t_predecessor_eh predecessor_eh,
|
||||
const datalog::t_unfold_eh unfold_eh) {
|
||||
ensure_engine();
|
||||
m_engine->add_callback(state, new_lemma_eh, predecessor_eh, unfold_eh);
|
||||
}
|
||||
|
||||
void add_constraint (expr *c, unsigned lvl){
|
||||
ensure_engine();
|
||||
m_engine->add_constraint(c, lvl);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
/**
|
||||
Just reset all tables.
|
||||
*/
|
||||
void reset_tables();
|
||||
void reset_tables();
|
||||
|
||||
|
||||
void flush_add_rules();
|
||||
|
@ -614,4 +627,3 @@ namespace datalog {
|
|||
};
|
||||
|
||||
#endif /* DL_CONTEXT_H_ */
|
||||
|
||||
|
|
|
@ -25,9 +25,7 @@ Revision History:
|
|||
namespace datalog {
|
||||
enum DL_ENGINE {
|
||||
DATALOG_ENGINE,
|
||||
PDR_ENGINE,
|
||||
SPACER_ENGINE,
|
||||
QPDR_ENGINE,
|
||||
BMC_ENGINE,
|
||||
QBMC_ENGINE,
|
||||
TAB_ENGINE,
|
||||
|
@ -36,6 +34,10 @@ namespace datalog {
|
|||
LAST_ENGINE
|
||||
};
|
||||
|
||||
typedef void (*t_new_lemma_eh)(void *state, expr *lemma, unsigned level);
|
||||
typedef void (*t_predecessor_eh)(void *state);
|
||||
typedef void (*t_unfold_eh)(void *state);
|
||||
|
||||
class engine_base {
|
||||
ast_manager& m;
|
||||
std::string m_name;
|
||||
|
@ -102,6 +104,15 @@ namespace datalog {
|
|||
virtual proof_ref get_proof() {
|
||||
return proof_ref(m.mk_asserted(m.mk_true()), m);
|
||||
}
|
||||
virtual void add_callback(void *state,
|
||||
const t_new_lemma_eh new_lemma_eh,
|
||||
const t_predecessor_eh predecessor_eh,
|
||||
const t_unfold_eh unfold_eh) {
|
||||
throw default_exception(std::string("add_lemma_exchange_callbacks is not supported for ") + m_name);
|
||||
}
|
||||
virtual void add_constraint (expr *c, unsigned lvl){
|
||||
throw default_exception(std::string("add_constraint is not supported for ") + m_name);
|
||||
}
|
||||
virtual void updt_params() {}
|
||||
virtual void cancel() {}
|
||||
virtual void cleanup() {}
|
||||
|
|
|
@ -1,37 +1,37 @@
|
|||
def_module_params('fixedpoint',
|
||||
def_module_params('fp',
|
||||
description='fixedpoint parameters',
|
||||
export=True,
|
||||
params=(('timeout', UINT, UINT_MAX, 'set timeout'),
|
||||
('engine', SYMBOL, 'auto-config',
|
||||
'Select: auto-config, datalog, spacer, pdr, bmc'),
|
||||
('datalog.default_table', SYMBOL, 'sparse',
|
||||
('engine', SYMBOL, 'auto-config',
|
||||
'Select: auto-config, datalog, bmc, spacer'),
|
||||
('datalog.default_table', SYMBOL, 'sparse',
|
||||
'default table implementation: sparse, hashtable, bitvector, interval'),
|
||||
('datalog.default_relation', SYMBOL, 'pentagon',
|
||||
('datalog.default_relation', SYMBOL, 'pentagon',
|
||||
'default relation implementation: external_relation, pentagon'),
|
||||
('datalog.generate_explanations', BOOL, False,
|
||||
('datalog.generate_explanations', BOOL, False,
|
||||
'produce explanations for produced facts when using the datalog engine'),
|
||||
('datalog.use_map_names', BOOL, True,
|
||||
('datalog.use_map_names', BOOL, True,
|
||||
"use names from map files when displaying tuples"),
|
||||
('datalog.magic_sets_for_queries', BOOL, False,
|
||||
('datalog.magic_sets_for_queries', BOOL, False,
|
||||
"magic set transformation will be used for queries"),
|
||||
('datalog.explanations_on_relation_level', BOOL, False,
|
||||
'if true, explanations are generated as history of each relation, ' +
|
||||
'rather than per fact (generate_explanations must be set to true for ' +
|
||||
('datalog.explanations_on_relation_level', BOOL, False,
|
||||
'if true, explanations are generated as history of each relation, ' +
|
||||
'rather than per fact (generate_explanations must be set to true for ' +
|
||||
'this option to have any effect)'),
|
||||
('datalog.unbound_compressor', BOOL, True,
|
||||
"auxiliary relations will be introduced to avoid unbound variables " +
|
||||
('datalog.unbound_compressor', BOOL, True,
|
||||
"auxiliary relations will be introduced to avoid unbound variables " +
|
||||
"in rule heads"),
|
||||
('datalog.similarity_compressor', BOOL, True,
|
||||
"rules that differ only in values of constants will be merged into " +
|
||||
('datalog.similarity_compressor', BOOL, True,
|
||||
"rules that differ only in values of constants will be merged into " +
|
||||
"a single rule"),
|
||||
('datalog.similarity_compressor_threshold', UINT, 11,
|
||||
"if similarity_compressor is on, this value determines how many " +
|
||||
('datalog.similarity_compressor_threshold', UINT, 11,
|
||||
"if similarity_compressor is on, this value determines how many " +
|
||||
"similar rules there must be in order for them to be merged"),
|
||||
('datalog.all_or_nothing_deltas', BOOL, False,
|
||||
('datalog.all_or_nothing_deltas', BOOL, False,
|
||||
"compile rules so that it is enough for the delta relation in " +
|
||||
"union and widening operations to determine only whether the " +
|
||||
"union and widening operations to determine only whether the " +
|
||||
"updated relation was modified or not"),
|
||||
('datalog.compile_with_widening', BOOL, False,
|
||||
('datalog.compile_with_widening', BOOL, False,
|
||||
"widening will be used to compile recursive rules"),
|
||||
('datalog.default_table_checked', BOOL, False, "if true, the default " +
|
||||
'table will be default_table inside a wrapper that checks that its results ' +
|
||||
|
@ -39,15 +39,15 @@ def_module_params('fixedpoint',
|
|||
('datalog.default_table_checker', SYMBOL, 'null', "see default_table_checked"),
|
||||
('datalog.check_relation',SYMBOL,'null', "name of default relation to check. " +
|
||||
"operations on the default relation will be verified using SMT solving"),
|
||||
('datalog.initial_restart_timeout', UINT, 0,
|
||||
"length of saturation run before the first restart (in ms), " +
|
||||
('datalog.initial_restart_timeout', UINT, 0,
|
||||
"length of saturation run before the first restart (in ms), " +
|
||||
"zero means no restarts"),
|
||||
('datalog.output_profile', BOOL, False,
|
||||
"determines whether profile information should be " +
|
||||
('datalog.output_profile', BOOL, False,
|
||||
"determines whether profile information should be " +
|
||||
"output when outputting Datalog rules or instructions"),
|
||||
('datalog.print.tuples', BOOL, True,
|
||||
('datalog.print.tuples', BOOL, True,
|
||||
"determines whether tuples for output predicates should be output"),
|
||||
('datalog.profile_timeout_milliseconds', UINT, 0,
|
||||
('datalog.profile_timeout_milliseconds', UINT, 0,
|
||||
"instructions and rules that took less than the threshold " +
|
||||
"will not be printed when printed the instruction/rule list"),
|
||||
('datalog.dbg_fpr_nonempty_relation_signature', BOOL, False,
|
||||
|
@ -56,139 +56,126 @@ def_module_params('fixedpoint',
|
|||
"table columns, if it would have been empty otherwise"),
|
||||
('datalog.subsumption', BOOL, True,
|
||||
"if true, removes/filters predicates with total transitions"),
|
||||
('pdr.bfs_model_search', BOOL, True,
|
||||
"use BFS strategy for expanding model search"),
|
||||
('pdr.farkas', BOOL, True,
|
||||
"use lemma generator based on Farkas (for linear real arithmetic)"),
|
||||
('generate_proof_trace', BOOL, False, "trace for 'sat' answer as proof object"),
|
||||
('pdr.flexible_trace', BOOL, False,
|
||||
"allow PDR generate long counter-examples " +
|
||||
"by extending candidate trace within search area"),
|
||||
('pdr.flexible_trace_depth', UINT, UINT_MAX,
|
||||
'Controls the depth (below the current level) at which flexible trace can be applied'),
|
||||
('pdr.use_model_generalizer', BOOL, False,
|
||||
"use model for backwards propagation (instead of symbolic simulation)"),
|
||||
('pdr.validate_result', BOOL, False,
|
||||
('spacer.push_pob', BOOL, False, "push blocked pobs to higher level"),
|
||||
('spacer.push_pob_max_depth', UINT, UINT_MAX,
|
||||
'Maximum depth at which push_pob is enabled'),
|
||||
('validate', BOOL, False,
|
||||
"validate result (by proof checking or model checking)"),
|
||||
('pdr.simplify_formulas_pre', BOOL, False,
|
||||
"simplify derived formulas before inductive propagation"),
|
||||
('pdr.simplify_formulas_post', BOOL, False,
|
||||
"simplify derived formulas after inductive propagation"),
|
||||
('pdr.use_multicore_generalizer', BOOL, False,
|
||||
"extract multiple cores for blocking states"),
|
||||
('pdr.use_inductive_generalizer', BOOL, True,
|
||||
('spacer.simplify_lemmas_pre', BOOL, False,
|
||||
"simplify derived lemmas before inductive propagation"),
|
||||
('spacer.simplify_lemmas_post', BOOL, False,
|
||||
"simplify derived lemmas after inductive propagation"),
|
||||
('spacer.use_inductive_generalizer', BOOL, True,
|
||||
"generalize lemmas using induction strengthening"),
|
||||
('pdr.use_arith_inductive_generalizer', BOOL, False,
|
||||
"generalize lemmas using arithmetic heuristics for induction strengthening"),
|
||||
('pdr.use_convex_closure_generalizer', BOOL, False,
|
||||
"generalize using convex closures of lemmas"),
|
||||
('pdr.use_convex_interior_generalizer', BOOL, False,
|
||||
"generalize using convex interiors of lemmas"),
|
||||
('pdr.cache_mode', UINT, 0, "use no (0), symbolic (1) or explicit " +
|
||||
"cache (2) for model search"),
|
||||
('pdr.inductive_reachability_check', BOOL, False,
|
||||
"assume negation of the cube on the previous level when " +
|
||||
"checking for reachability (not only during cube weakening)"),
|
||||
('pdr.max_num_contexts', UINT, 500, "maximal number of contexts to create"),
|
||||
('pdr.try_minimize_core', BOOL, False,
|
||||
"try to reduce core size (before inductive minimization)"),
|
||||
('pdr.utvpi', BOOL, True, 'Enable UTVPI strategy'),
|
||||
('print_fixedpoint_extensions', BOOL, True,
|
||||
"use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " +
|
||||
('spacer.max_num_contexts', UINT, 500, "maximal number of contexts to create"),
|
||||
('print_fixedpoint_extensions', BOOL, True,
|
||||
"use SMT-LIB2 fixedpoint extensions, instead of pure SMT2, " +
|
||||
"when printing rules"),
|
||||
('print_low_level_smt2', BOOL, False,
|
||||
"use (faster) low-level SMT2 printer (the printer is scalable " +
|
||||
('print_low_level_smt2', BOOL, False,
|
||||
"use (faster) low-level SMT2 printer (the printer is scalable " +
|
||||
"but the result may not be as readable)"),
|
||||
('print_with_variable_declarations', BOOL, True,
|
||||
('print_with_variable_declarations', BOOL, True,
|
||||
"use variable declarations when displaying rules " +
|
||||
"(instead of attempting to use original names)"),
|
||||
('print_answer', BOOL, False, 'print answer instance(s) to query'),
|
||||
('print_certificate', BOOL, False,
|
||||
('print_certificate', BOOL, False,
|
||||
'print certificate for reachability or non-reachability'),
|
||||
('print_boogie_certificate', BOOL, False,
|
||||
('print_boogie_certificate', BOOL, False,
|
||||
'print certificate for reachability or non-reachability using a ' +
|
||||
'format understood by Boogie'),
|
||||
('print_statistics', BOOL, False, 'print statistics'),
|
||||
('print_aig', SYMBOL, '',
|
||||
('print_aig', SYMBOL, '',
|
||||
'Dump clauses in AIG text format (AAG) to the given file name'),
|
||||
('tab.selection', SYMBOL, 'weight',
|
||||
('tab.selection', SYMBOL, 'weight',
|
||||
'selection method for tabular strategy: weight (default), first, var-use'),
|
||||
('xform.bit_blast', BOOL, False,
|
||||
('xform.bit_blast', BOOL, False,
|
||||
'bit-blast bit-vectors'),
|
||||
('xform.magic', BOOL, False,
|
||||
('xform.magic', BOOL, False,
|
||||
"perform symbolic magic set transformation"),
|
||||
('xform.scale', BOOL, False,
|
||||
('xform.scale', BOOL, False,
|
||||
"add scaling variable to linear real arithmetic clauses"),
|
||||
('xform.inline_linear', BOOL, True, "try linear inlining method"),
|
||||
('xform.inline_eager', BOOL, True, "try eager inlining of rules"),
|
||||
('xform.inline_linear_branch', BOOL, False,
|
||||
('xform.inline_linear_branch', BOOL, False,
|
||||
"try linear inlining method with potential expansion"),
|
||||
('xform.compress_unbound', BOOL, True, "compress tails with unbound variables"),
|
||||
('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
|
||||
('xform.unfold_rules', UINT, 0,
|
||||
"unfold rules statically using iterative squarring"),
|
||||
('xform.unfold_rules', UINT, 0,
|
||||
"unfold rules statically using iterative squaring"),
|
||||
('xform.slice', BOOL, True, "simplify clause set using slicing"),
|
||||
('xform.karr', BOOL, False,
|
||||
('xform.karr', BOOL, False,
|
||||
"Add linear invariants to clauses using Karr's method"),
|
||||
('spacer.use_eqclass', BOOL, False, "Generalizes equalities to equivalence classes"),
|
||||
('xform.transform_arrays', BOOL, False,
|
||||
('spacer.use_euf_gen', BOOL, False, 'Generalize lemmas and pobs using implied equalities'),
|
||||
('xform.transform_arrays', BOOL, False,
|
||||
"Rewrites arrays equalities and applies select over store"),
|
||||
('xform.instantiate_arrays', BOOL, False,
|
||||
('xform.instantiate_arrays', BOOL, False,
|
||||
"Transforms P(a) into P(i, a[i] a)"),
|
||||
('xform.instantiate_arrays.enforce', BOOL, False,
|
||||
('xform.instantiate_arrays.enforce', BOOL, False,
|
||||
"Transforms P(a) into P(i, a[i]), discards a from predicate"),
|
||||
('xform.instantiate_arrays.nb_quantifier', UINT, 1,
|
||||
('xform.instantiate_arrays.nb_quantifier', UINT, 1,
|
||||
"Gives the number of quantifiers per array"),
|
||||
('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing",
|
||||
('xform.instantiate_arrays.slice_technique', SYMBOL, "no-slicing",
|
||||
"<no-slicing>=> GetId(i) = i, <smash> => GetId(i) = true"),
|
||||
('xform.quantify_arrays', BOOL, False,
|
||||
('xform.quantify_arrays', BOOL, False,
|
||||
"create quantified Horn clauses from clauses with arrays"),
|
||||
('xform.instantiate_quantifiers', BOOL, False,
|
||||
('xform.instantiate_quantifiers', BOOL, False,
|
||||
"instantiate quantified Horn clauses using E-matching heuristic"),
|
||||
('xform.coalesce_rules', BOOL, False, "coalesce rules"),
|
||||
('xform.tail_simplifier_pve', BOOL, True, "propagate_variable_equivalences"),
|
||||
('xform.subsumption_checker', BOOL, True, "Enable subsumption checker (no support for model conversion)"),
|
||||
('xform.coi', BOOL, True, "use cone of influence simplification"),
|
||||
('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse)'),
|
||||
('spacer.eager_reach_check', BOOL, True, 'SPACER: eagerly check if a query is reachable using reachability facts of predecessors'),
|
||||
('spacer.order_children', UINT, 0, 'SPACER: order of enqueuing children in non-linear rules : 0 (original), 1 (reverse), 2 (random)'),
|
||||
('spacer.use_lemma_as_cti', BOOL, False, 'SPACER: use a lemma instead of a CTI in flexible_trace'),
|
||||
('spacer.reset_obligation_queue', BOOL, True, 'SPACER: reset obligation queue when entering a new level'),
|
||||
('spacer.init_reach_facts', BOOL, True, 'SPACER: initialize reachability facts with false'),
|
||||
('spacer.reset_pob_queue', BOOL, True, 'SPACER: reset pob obligation queue when entering a new level'),
|
||||
('spacer.use_array_eq_generalizer', BOOL, True, 'SPACER: attempt to generalize lemmas with array equalities'),
|
||||
('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'),
|
||||
('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"),
|
||||
('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"),
|
||||
('spacer.skip_propagate', BOOL, False, "Skip propagate/pushing phase. Turns PDR into a BMC that returns either reachable or unknown"),
|
||||
('spacer.use_derivations', BOOL, True, 'SPACER: using derivation mechanism to cache intermediate results for non-linear rules'),
|
||||
('xform.array_blast', BOOL, False, "try to eliminate local array terms using Ackermannization -- some array terms may remain"),
|
||||
('xform.array_blast_full', BOOL, False, "eliminate all local array variables by QE"),
|
||||
('spacer.propagate', BOOL, True, 'Enable propagate/pushing phase'),
|
||||
('spacer.max_level', UINT, UINT_MAX, "Maximum level to explore"),
|
||||
('spacer.elim_aux', BOOL, True, "Eliminate auxiliary variables in reachability facts"),
|
||||
('spacer.reach_as_init', BOOL, True, "Extend initial rules with computed reachability facts"),
|
||||
('spacer.blast_term_ite', BOOL, True, "Expand non-Boolean ite-terms"),
|
||||
('spacer.nondet_tie_break', BOOL, False, "Break ties in obligation queue non-deterministically"),
|
||||
('spacer.reach_dnf', BOOL, True, "Restrict reachability facts to DNF"),
|
||||
('bmc.linear_unrolling_depth', UINT, UINT_MAX, "Maximal level to explore"),
|
||||
('spacer.split_farkas_literals', BOOL, False, "Split Farkas literals"),
|
||||
('spacer.native_mbp', BOOL, False, "Use native mbp of Z3"),
|
||||
('spacer.iuc.split_farkas_literals', BOOL, False, "Split Farkas literals"),
|
||||
('spacer.native_mbp', BOOL, True, "Use native mbp of Z3"),
|
||||
('spacer.eq_prop', BOOL, True, "Enable equality and bound propagation in arithmetic"),
|
||||
('spacer.weak_abs', BOOL, True, "Weak abstraction"),
|
||||
('spacer.restarts', BOOL, False, "Enable reseting obligation queue"),
|
||||
('spacer.restart_initial_threshold', UINT, 10, "Intial threshold for restarts"),
|
||||
('spacer.random_seed', UINT, 0, "Random seed to be used by SMT solver"),
|
||||
('spacer.ground_cti', BOOL, True, "Require CTI to be ground"),
|
||||
('spacer.vs.dump_benchmarks', BOOL, False, 'dump benchmarks in virtual solver'),
|
||||
('spacer.vs.dump_min_time', DOUBLE, 5.0, 'min time to dump benchmark'),
|
||||
('spacer.vs.recheck', BOOL, False, 're-check locally during benchmark dumping'),
|
||||
('spacer.mbqi', BOOL, True, 'use model-based quantifier instantiation'),
|
||||
|
||||
('spacer.mbqi', BOOL, True, 'Enable mbqi'),
|
||||
('spacer.keep_proxy', BOOL, True, 'keep proxy variables (internal parameter)'),
|
||||
('spacer.instantiate', BOOL, True, 'instantiate quantified lemmas'),
|
||||
('spacer.qlemmas', BOOL, True, 'allow quantified lemmas in frames'),
|
||||
('spacer.new_unsat_core', BOOL, True, 'use the new implementation of unsat-core-generation'),
|
||||
('spacer.minimize_unsat_core', BOOL, False, 'compute unsat-core by min-cut'),
|
||||
('spacer.farkas_optimized', BOOL, True, 'use the optimized farkas plugin, which performs gaussian elimination'),
|
||||
('spacer.farkas_a_const', BOOL, True, 'if the unoptimized farkas plugin is used, use the constants from A while constructing unsat_cores'),
|
||||
('spacer.lemma_sanity_check', BOOL, False, 'check during generalization whether lemma is actually correct'),
|
||||
('spacer.reuse_pobs', BOOL, True, 'reuse POBs'),
|
||||
('spacer.simplify_pob', BOOL, False, 'simplify POBs by removing redundant constraints')
|
||||
('spacer.q3', BOOL, True, 'Allow quantified lemmas in frames'),
|
||||
('spacer.q3.instantiate', BOOL, True, 'Instantiate quantified lemmas'),
|
||||
('spacer.iuc', UINT, 1,
|
||||
'0 = use old implementation of unsat-core-generation, ' +
|
||||
'1 = use new implementation of IUC generation, ' +
|
||||
'2 = use new implementation of IUC + min-cut optimization'),
|
||||
('spacer.iuc.arith', UINT, 1,
|
||||
'0 = use simple Farkas plugin, ' +
|
||||
'1 = use simple Farkas plugin with constant from other partition (like old unsat-core-generation),' +
|
||||
'2 = use Gaussian elimination optimization (broken), 3 = use additive IUC plugin'),
|
||||
('spacer.iuc.old_hyp_reducer', BOOL, False, 'use old hyp reducer instead of new implementation, for debugging only'),
|
||||
('spacer.validate_lemmas', BOOL, False, 'Validate each lemma after generalization'),
|
||||
('spacer.reuse_pobs', BOOL, True, 'Reuse pobs'),
|
||||
('spacer.ground_pobs', BOOL, True, 'Ground pobs by using values from a model'),
|
||||
('spacer.iuc.print_farkas_stats', BOOL, False, 'prints for each proof how many Farkas lemmas it contains and how many of these participate in the cut (for debugging)'),
|
||||
('spacer.iuc.debug_proof', BOOL, False, 'prints proof used by unsat-core-learner for debugging purposes (debugging)'),
|
||||
('spacer.simplify_pob', BOOL, False, 'simplify pobs by removing redundant constraints'),
|
||||
('spacer.q3.use_qgen', BOOL, False, 'use quantified lemma generalizer'),
|
||||
('spacer.q3.qgen.normalize', BOOL, True, 'normalize cube before quantified generalization'),
|
||||
('spacer.p3.share_lemmas', BOOL, False, 'Share frame lemmas'),
|
||||
('spacer.p3.share_invariants', BOOL, False, "Share invariants lemmas"),
|
||||
('spacer.min_level', UINT, 0, 'Minimal level to explore'),
|
||||
('spacer.print_json', SYMBOL, '', 'Print pobs tree in JSON format to a given file'),
|
||||
('spacer.ctp', BOOL, True, 'Enable counterexample-to-pushing'),
|
||||
('spacer.use_inc_clause', BOOL, True, 'Use incremental clause to represent trans'),
|
||||
('spacer.dump_benchmarks', BOOL, False, 'Dump SMT queries as benchmarks'),
|
||||
('spacer.dump_threshold', DOUBLE, 5.0, 'Threshold in seconds on dumping benchmarks'),
|
||||
('spacer.gpdr', BOOL, False, 'Use GPDR solving strategy for non-linear CHC'),
|
||||
('spacer.gpdr.bfs', BOOL, True, 'Use BFS exploration strategy for expanding model search'),
|
||||
|
||||
))
|
||||
|
||||
|
||||
|
|
@ -146,12 +146,10 @@ void rule_properties::check_existential_tail() {
|
|||
else if (is_quantifier(e)) {
|
||||
tocheck.push_back(to_quantifier(e)->get_expr());
|
||||
}
|
||||
else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
|
||||
m.is_true(e1)) {
|
||||
else if (m.is_eq(e, e1, e2) && m.is_true(e1)) {
|
||||
todo.push_back(e2);
|
||||
}
|
||||
else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
|
||||
m.is_true(e2)) {
|
||||
else if (m.is_eq(e, e1, e2) && m.is_true(e2)) {
|
||||
todo.push_back(e1);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -33,7 +33,7 @@ Revision History:
|
|||
#include "muz/transforms/dl_mk_rule_inliner.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
#include "muz/base/fp_params.hpp"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
|
|
@ -9,7 +9,6 @@ z3_add_component(fp
|
|||
clp
|
||||
ddnf
|
||||
muz
|
||||
pdr
|
||||
rel
|
||||
spacer
|
||||
tab
|
||||
|
|
|
@ -30,23 +30,23 @@ Notes:
|
|||
#include "util/scoped_ctrl_c.h"
|
||||
#include "util/scoped_timer.h"
|
||||
#include "util/trail.h"
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
#include "muz/base/fp_params.hpp"
|
||||
#include<iomanip>
|
||||
|
||||
|
||||
struct dl_context {
|
||||
smt_params m_fparams;
|
||||
params_ref m_params_ref;
|
||||
fixedpoint_params m_params;
|
||||
fp_params m_params;
|
||||
cmd_context & m_cmd;
|
||||
datalog::register_engine m_register_engine;
|
||||
dl_collected_cmds* m_collected_cmds;
|
||||
unsigned m_ref_count;
|
||||
datalog::dl_decl_plugin* m_decl_plugin;
|
||||
scoped_ptr<datalog::context> m_context;
|
||||
scoped_ptr<datalog::context> m_context;
|
||||
trail_stack<dl_context> m_trail;
|
||||
|
||||
fixedpoint_params const& get_params() {
|
||||
fp_params const& get_params() {
|
||||
init();
|
||||
return m_context->get_params();
|
||||
}
|
||||
|
@ -58,18 +58,18 @@ struct dl_context {
|
|||
m_ref_count(0),
|
||||
m_decl_plugin(nullptr),
|
||||
m_trail(*this) {}
|
||||
|
||||
|
||||
void inc_ref() {
|
||||
++m_ref_count;
|
||||
}
|
||||
|
||||
|
||||
void dec_ref() {
|
||||
--m_ref_count;
|
||||
if (0 == m_ref_count) {
|
||||
dealloc(this);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void init() {
|
||||
ast_manager& m = m_cmd.m();
|
||||
if (!m_context) {
|
||||
|
@ -83,10 +83,10 @@ struct dl_context {
|
|||
else {
|
||||
m_decl_plugin = alloc(datalog::dl_decl_plugin);
|
||||
m.register_plugin(symbol("datalog_relation"), m_decl_plugin);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void reset() {
|
||||
m_context = nullptr;
|
||||
}
|
||||
|
@ -97,9 +97,9 @@ struct dl_context {
|
|||
m_trail.push(push_back_vector<dl_context, func_decl_ref_vector>(m_collected_cmds->m_rels));
|
||||
}
|
||||
dlctx().register_predicate(pred, false);
|
||||
dlctx().set_predicate_representation(pred, num_kinds, kinds);
|
||||
dlctx().set_predicate_representation(pred, num_kinds, kinds);
|
||||
}
|
||||
|
||||
|
||||
void add_rule(expr * rule, symbol const& name, unsigned bound) {
|
||||
init();
|
||||
if (m_collected_cmds) {
|
||||
|
@ -112,7 +112,7 @@ struct dl_context {
|
|||
else {
|
||||
m_context->add_rule(rule, name, bound);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool collect_query(func_decl* q) {
|
||||
if (m_collected_cmds) {
|
||||
|
@ -127,7 +127,7 @@ struct dl_context {
|
|||
m_collected_cmds->m_queries.push_back(qr);
|
||||
m_trail.push(push_back_vector<dl_context, expr_ref_vector>(m_collected_cmds->m_queries));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
|
@ -142,7 +142,7 @@ struct dl_context {
|
|||
m_trail.pop_scope(1);
|
||||
dlctx().pop();
|
||||
}
|
||||
|
||||
|
||||
datalog::context & dlctx() {
|
||||
init();
|
||||
return *m_context;
|
||||
|
@ -162,7 +162,7 @@ class dl_rule_cmd : public cmd {
|
|||
public:
|
||||
dl_rule_cmd(dl_context * dl_ctx):
|
||||
cmd("rule"),
|
||||
m_dl_ctx(dl_ctx),
|
||||
m_dl_ctx(dl_ctx),
|
||||
m_arg_idx(0),
|
||||
m_t(nullptr),
|
||||
m_bound(UINT_MAX) {}
|
||||
|
@ -210,7 +210,7 @@ public:
|
|||
}
|
||||
char const * get_usage() const override { return "predicate"; }
|
||||
char const * get_main_descr() const override {
|
||||
return "pose a query to a predicate based on the Horn rules.";
|
||||
return "pose a query to a predicate based on the Horn rules.";
|
||||
}
|
||||
|
||||
cmd_arg_kind next_arg_kind(cmd_context & ctx) const override {
|
||||
|
@ -243,9 +243,9 @@ public:
|
|||
return;
|
||||
}
|
||||
datalog::context& dlctx = m_dl_ctx->dlctx();
|
||||
set_background(ctx);
|
||||
set_background(ctx);
|
||||
dlctx.updt_params(m_params);
|
||||
unsigned timeout = m_dl_ctx->get_params().timeout();
|
||||
unsigned timeout = m_dl_ctx->get_params().timeout();
|
||||
cancel_eh<reslimit> eh(ctx.m().limit());
|
||||
bool query_exn = false;
|
||||
lbool status = l_undef;
|
||||
|
@ -271,12 +271,12 @@ public:
|
|||
ctx.regular_stream() << "unsat\n";
|
||||
print_certificate(ctx);
|
||||
break;
|
||||
case l_true:
|
||||
case l_true:
|
||||
ctx.regular_stream() << "sat\n";
|
||||
print_answer(ctx);
|
||||
print_certificate(ctx);
|
||||
break;
|
||||
case l_undef:
|
||||
case l_undef:
|
||||
if (dlctx.get_status() == datalog::BOUNDED){
|
||||
ctx.regular_stream() << "bounded\n";
|
||||
print_certificate(ctx);
|
||||
|
@ -287,7 +287,7 @@ public:
|
|||
case datalog::INPUT_ERROR:
|
||||
ctx.regular_stream() << "input error\n";
|
||||
break;
|
||||
|
||||
|
||||
case datalog::MEMOUT:
|
||||
ctx.regular_stream() << "memory bounds exceeded\n";
|
||||
break;
|
||||
|
@ -295,12 +295,12 @@ public:
|
|||
case datalog::TIMEOUT:
|
||||
ctx.regular_stream() << "timeout\n";
|
||||
break;
|
||||
|
||||
|
||||
case datalog::APPROX:
|
||||
ctx.regular_stream() << "approximated relations\n";
|
||||
break;
|
||||
|
||||
case datalog::OK:
|
||||
case datalog::OK:
|
||||
(void)query_exn;
|
||||
SASSERT(query_exn);
|
||||
break;
|
||||
|
@ -324,7 +324,7 @@ public:
|
|||
void init_pdescrs(cmd_context & ctx, param_descrs & p) override {
|
||||
m_dl_ctx->dlctx().collect_params(p);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private:
|
||||
void set_background(cmd_context& ctx) {
|
||||
|
@ -356,8 +356,8 @@ private:
|
|||
statistics st;
|
||||
datalog::context& dlctx = m_dl_ctx->dlctx();
|
||||
dlctx.collect_statistics(st);
|
||||
st.update("time", ctx.get_seconds());
|
||||
st.display_smt2(ctx.regular_stream());
|
||||
st.update("time", ctx.get_seconds());
|
||||
st.display_smt2(ctx.regular_stream());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,8 +391,8 @@ public:
|
|||
|
||||
void prepare(cmd_context & ctx) override {
|
||||
ctx.m(); // ensure manager is initialized.
|
||||
m_arg_idx = 0;
|
||||
m_query_arg_idx = 0;
|
||||
m_arg_idx = 0;
|
||||
m_query_arg_idx = 0;
|
||||
m_domain.reset();
|
||||
m_kinds.reset();
|
||||
}
|
||||
|
@ -443,21 +443,21 @@ public:
|
|||
m_arg_idx(0),
|
||||
m_dl_ctx(dl_ctx)
|
||||
{}
|
||||
|
||||
|
||||
char const * get_usage() const override { return "<symbol> <sort>"; }
|
||||
char const * get_descr(cmd_context & ctx) const override { return "declare constant as variable"; }
|
||||
unsigned get_arity() const override { return 2; }
|
||||
|
||||
void prepare(cmd_context & ctx) override {
|
||||
ctx.m(); // ensure manager is initialized.
|
||||
m_arg_idx = 0;
|
||||
m_arg_idx = 0;
|
||||
}
|
||||
cmd_arg_kind next_arg_kind(cmd_context & ctx) const override {
|
||||
SASSERT(m_arg_idx <= 1);
|
||||
if (m_arg_idx == 0) {
|
||||
return CPK_SYMBOL;
|
||||
return CPK_SYMBOL;
|
||||
}
|
||||
return CPK_SORT;
|
||||
return CPK_SORT;
|
||||
}
|
||||
|
||||
void set_next_arg(cmd_context & ctx, sort* s) override {
|
||||
|
@ -466,7 +466,7 @@ public:
|
|||
}
|
||||
|
||||
void set_next_arg(cmd_context & ctx, symbol const & s) override {
|
||||
m_var_name = s;
|
||||
m_var_name = s;
|
||||
++m_arg_idx;
|
||||
}
|
||||
|
||||
|
@ -523,7 +523,7 @@ static void install_dl_cmds_aux(cmd_context& ctx, dl_collected_cmds* collected_c
|
|||
ctx.insert(alloc(dl_query_cmd, dl_ctx));
|
||||
ctx.insert(alloc(dl_declare_rel_cmd, dl_ctx));
|
||||
ctx.insert(alloc(dl_declare_var_cmd, dl_ctx));
|
||||
ctx.insert(alloc(dl_push_cmd, dl_ctx));
|
||||
ctx.insert(alloc(dl_push_cmd, dl_ctx));
|
||||
ctx.insert(alloc(dl_pop_cmd, dl_ctx));
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,6 @@ Revision History:
|
|||
#include "muz/clp/clp_context.h"
|
||||
#include "muz/tab/tab_context.h"
|
||||
#include "muz/rel/rel_context.h"
|
||||
#include "muz/pdr/pdr_dl_interface.h"
|
||||
#include "muz/ddnf/ddnf.h"
|
||||
#include "muz/spacer/spacer_dl_interface.h"
|
||||
|
||||
|
@ -30,9 +29,6 @@ namespace datalog {
|
|||
|
||||
engine_base* register_engine::mk_engine(DL_ENGINE engine_type) {
|
||||
switch(engine_type) {
|
||||
case PDR_ENGINE:
|
||||
case QPDR_ENGINE:
|
||||
return alloc(pdr::dl_interface, *m_ctx);
|
||||
case SPACER_ENGINE:
|
||||
return alloc(spacer::dl_interface, *m_ctx);
|
||||
case DATALOG_ENGINE:
|
||||
|
|
|
@ -27,7 +27,7 @@ Revision History:
|
|||
#include "muz/transforms/dl_mk_slice.h"
|
||||
#include "tactic/generic_model_converter.h"
|
||||
#include "muz/transforms/dl_transforms.h"
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
#include "muz/base/fp_params.hpp"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
|
||||
|
@ -71,7 +71,7 @@ class horn_tactic : public tactic {
|
|||
f = to_quantifier(f)->get_expr();
|
||||
}
|
||||
else if (is_exists(f) && !is_positive) {
|
||||
f = to_quantifier(f)->get_expr();
|
||||
f = to_quantifier(f)->get_expr();
|
||||
}
|
||||
else if (m.is_not(f, e)) {
|
||||
is_positive = !is_positive;
|
||||
|
@ -84,7 +84,7 @@ class horn_tactic : public tactic {
|
|||
if (!is_positive) {
|
||||
f = m.mk_not(f);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool is_predicate(expr* a) {
|
||||
|
@ -144,7 +144,7 @@ class horn_tactic : public tactic {
|
|||
expr* a = nullptr, *a1 = nullptr;
|
||||
flatten_or(tmp, args);
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
a = args[i].get();
|
||||
a = args[i].get();
|
||||
check_predicate(mark, a);
|
||||
if (m.is_not(a, a1)) {
|
||||
body.push_back(a1);
|
||||
|
@ -176,13 +176,13 @@ class horn_tactic : public tactic {
|
|||
return expr_ref(m.mk_implies(body, head), m);
|
||||
}
|
||||
|
||||
void operator()(goal_ref const & g,
|
||||
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 (produce_proofs) {
|
||||
if (!m_ctx.generate_proof_trace()) {
|
||||
params_ref params = m_ctx.get_params().p;
|
||||
params.set_bool("generate_proof_trace", true);
|
||||
|
@ -208,7 +208,7 @@ class horn_tactic : public tactic {
|
|||
case IS_QUERY:
|
||||
queries.push_back(f);
|
||||
break;
|
||||
default:
|
||||
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().c_str());
|
||||
|
@ -243,10 +243,10 @@ class horn_tactic : public tactic {
|
|||
g->set(mc.get());
|
||||
}
|
||||
|
||||
void verify(expr* q,
|
||||
void verify(expr* q,
|
||||
goal_ref const& g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc) {
|
||||
|
||||
lbool is_reachable = l_undef;
|
||||
|
@ -275,9 +275,9 @@ class horn_tactic : public tactic {
|
|||
else {
|
||||
g->assert_expr(m.mk_false());
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case l_false: {
|
||||
case l_false: {
|
||||
// goal is sat
|
||||
g->reset();
|
||||
if (produce_models) {
|
||||
|
@ -290,11 +290,11 @@ class horn_tactic : public tactic {
|
|||
mc = mc2;
|
||||
}
|
||||
}
|
||||
break;
|
||||
break;
|
||||
}
|
||||
case l_undef:
|
||||
case l_undef:
|
||||
// subgoal is unchanged.
|
||||
break;
|
||||
break;
|
||||
}
|
||||
TRACE("horn", g->display(tout););
|
||||
SASSERT(g->is_well_sorted());
|
||||
|
@ -314,20 +314,20 @@ class horn_tactic : public tactic {
|
|||
}
|
||||
}
|
||||
|
||||
void simplify(expr* q,
|
||||
void simplify(expr* q,
|
||||
goal_ref const& g,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
goal_ref_buffer & result,
|
||||
model_converter_ref & mc,
|
||||
proof_converter_ref & pc) {
|
||||
|
||||
expr_ref fml(m);
|
||||
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);
|
||||
|
@ -351,7 +351,7 @@ class horn_tactic : public tactic {
|
|||
g->assert_expr(fml);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
bool m_is_simplify;
|
||||
|
@ -368,7 +368,7 @@ public:
|
|||
tactic * translate(ast_manager & m) override {
|
||||
return alloc(horn_tactic, m_is_simplify, m, m_params);
|
||||
}
|
||||
|
||||
|
||||
~horn_tactic() override {
|
||||
dealloc(m_imp);
|
||||
}
|
||||
|
@ -378,16 +378,16 @@ public:
|
|||
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,
|
||||
|
||||
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);
|
||||
|
@ -397,15 +397,15 @@ public:
|
|||
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);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
@ -416,4 +416,3 @@ tactic * mk_horn_tactic(ast_manager & m, params_ref const & p) {
|
|||
tactic * mk_horn_simplify_tactic(ast_manager & m, params_ref const & p) {
|
||||
return clean(alloc(horn_tactic, true, m, p));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,20 +0,0 @@
|
|||
z3_add_component(pdr
|
||||
SOURCES
|
||||
pdr_closure.cpp
|
||||
pdr_context.cpp
|
||||
pdr_dl_interface.cpp
|
||||
pdr_farkas_learner.cpp
|
||||
pdr_generalizers.cpp
|
||||
pdr_manager.cpp
|
||||
pdr_prop_solver.cpp
|
||||
pdr_reachable_cache.cpp
|
||||
pdr_smt_context_manager.cpp
|
||||
pdr_sym_mux.cpp
|
||||
pdr_util.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
arith_tactics
|
||||
core_tactics
|
||||
muz
|
||||
smt_tactic
|
||||
transforms
|
||||
)
|
|
@ -1,177 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_closure.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for computing closures.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-9-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/pdr/pdr_closure.h"
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "ast/ast_util.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
expr_ref scaler::operator()(expr* e, expr* k, obj_map<func_decl, expr*>* translate) {
|
||||
m_cache[0].reset();
|
||||
m_cache[1].reset();
|
||||
m_translate = translate;
|
||||
m_k = k;
|
||||
return scale(e, false);
|
||||
}
|
||||
|
||||
expr_ref scaler::scale(expr* e, bool is_mul) {
|
||||
expr* r;
|
||||
if (m_cache[is_mul].find(e, r)) {
|
||||
return expr_ref(r, m);
|
||||
}
|
||||
if (!is_app(e)) {
|
||||
return expr_ref(e, m);
|
||||
}
|
||||
app* ap = to_app(e);
|
||||
if (m_translate && m_translate->find(ap->get_decl(), r)) {
|
||||
return expr_ref(r, m);
|
||||
}
|
||||
if (!is_mul && a.is_numeral(e)) {
|
||||
return expr_ref(a.mk_mul(m_k, e), m);
|
||||
}
|
||||
expr_ref_vector args(m);
|
||||
bool is_mul_rec = is_mul || a.is_mul(e);
|
||||
for (unsigned i = 0; i < ap->get_num_args(); ++i) {
|
||||
args.push_back(scale(ap->get_arg(i), is_mul_rec));
|
||||
}
|
||||
expr_ref result(m);
|
||||
result = m.mk_app(ap->get_decl(), args.size(), args.c_ptr());
|
||||
m_cache[is_mul].insert(e, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref scaler::undo_k(expr* e, expr* k) {
|
||||
expr_safe_replace sub(m);
|
||||
th_rewriter rw(m);
|
||||
expr_ref result(e, m);
|
||||
sub.insert(k, a.mk_numeral(rational(1), false));
|
||||
sub(result);
|
||||
rw(result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
closure::closure(pred_transformer& p, bool is_closure):
|
||||
m(p.get_manager()), m_pt(p), a(m),
|
||||
m_is_closure(is_closure), m_sigma(m), m_trail(m) {}
|
||||
|
||||
|
||||
void closure::add_variables(unsigned num_vars, expr_ref_vector& fmls) {
|
||||
manager& pm = m_pt.get_pdr_manager();
|
||||
SASSERT(num_vars > 0);
|
||||
while (m_vars.size() < num_vars) {
|
||||
m_vars.resize(m_vars.size()+1);
|
||||
m_sigma.push_back(m.mk_fresh_const("sigma", a.mk_real()));
|
||||
}
|
||||
|
||||
unsigned sz = m_pt.sig_size();
|
||||
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* var;
|
||||
ptr_vector<expr> vars;
|
||||
func_decl* fn0 = m_pt.sig(i);
|
||||
func_decl* fn1 = pm.o2n(fn0, 0);
|
||||
sort* srt = fn0->get_range();
|
||||
if (a.is_int_real(srt)) {
|
||||
for (unsigned j = 0; j < num_vars; ++j) {
|
||||
if (!m_vars[j].find(fn1, var)) {
|
||||
var = m.mk_fresh_const(fn1->get_name().str().c_str(), srt);
|
||||
m_trail.push_back(var);
|
||||
m_vars[j].insert(fn1, var);
|
||||
}
|
||||
vars.push_back(var);
|
||||
}
|
||||
fmls.push_back(m.mk_eq(m.mk_const(fn1), a.mk_add(num_vars, vars.c_ptr())));
|
||||
}
|
||||
}
|
||||
if (m_is_closure) {
|
||||
for (unsigned i = 0; i < num_vars; ++i) {
|
||||
fmls.push_back(a.mk_ge(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real())));
|
||||
}
|
||||
}
|
||||
else {
|
||||
// is interior:
|
||||
for (unsigned i = 0; i < num_vars; ++i) {
|
||||
fmls.push_back(a.mk_gt(m_sigma[i].get(), a.mk_numeral(rational(0), a.mk_real())));
|
||||
}
|
||||
}
|
||||
fmls.push_back(m.mk_eq(a.mk_numeral(rational(1), a.mk_real()), a.mk_add(num_vars, m_sigma.c_ptr())));
|
||||
}
|
||||
|
||||
expr_ref closure::close_fml(expr* e) {
|
||||
expr* e0, *e1, *e2;
|
||||
expr_ref result(m);
|
||||
if (a.is_lt(e, e1, e2)) {
|
||||
result = a.mk_le(e1, e2);
|
||||
}
|
||||
else if (a.is_gt(e, e1, e2)) {
|
||||
result = a.mk_ge(e1, e2);
|
||||
}
|
||||
else if (m.is_not(e, e0) && a.is_ge(e0, e1, e2)) {
|
||||
result = a.mk_le(e1, e2);
|
||||
}
|
||||
else if (m.is_not(e, e0) && a.is_le(e0, e1, e2)) {
|
||||
result = a.mk_ge(e1, e2);
|
||||
}
|
||||
else if (a.is_ge(e) || a.is_le(e) || m.is_eq(e) ||
|
||||
(m.is_not(e, e0) && (a.is_gt(e0) || a.is_lt(e0)))) {
|
||||
result = e;
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(1, verbose_stream() << "Cannot close: " << mk_pp(e, m) << "\n";);
|
||||
result = m.mk_true();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref closure::close_conjunction(expr* fml) {
|
||||
expr_ref_vector fmls(m);
|
||||
flatten_and(fml, fmls);
|
||||
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||
fmls[i] = close_fml(fmls[i].get());
|
||||
}
|
||||
return expr_ref(mk_and(fmls), m);
|
||||
}
|
||||
|
||||
expr_ref closure::relax(unsigned i, expr* fml) {
|
||||
scaler sc(m);
|
||||
expr_ref result = sc(fml, m_sigma[i].get(), &m_vars[i]);
|
||||
return close_conjunction(result);
|
||||
}
|
||||
|
||||
expr_ref closure::operator()(expr_ref_vector const& As) {
|
||||
if (As.empty()) {
|
||||
return expr_ref(m.mk_false(), m);
|
||||
}
|
||||
if (As.size() == 1) {
|
||||
return expr_ref(As[0], m);
|
||||
}
|
||||
expr_ref_vector fmls(m);
|
||||
expr_ref B(m);
|
||||
add_variables(As.size(), fmls);
|
||||
for (unsigned i = 0; i < As.size(); ++i) {
|
||||
fmls.push_back(relax(i, As[i]));
|
||||
}
|
||||
B = mk_and(fmls);
|
||||
return B;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,67 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_closure.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for computing closures.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-9-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_CLOSURE_H_
|
||||
#define PDR_CLOSURE_H_
|
||||
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
// Arithmetic scaling functor.
|
||||
// Variables are replaced using
|
||||
// m_translate. Constants are replaced by
|
||||
// multiplication with a variable 'k' (scale factor).
|
||||
class scaler {
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
obj_map<expr, expr*> m_cache[2];
|
||||
expr* m_k;
|
||||
obj_map<func_decl, expr*>* m_translate;
|
||||
public:
|
||||
scaler(ast_manager& m): m(m), a(m), m_translate(nullptr) {}
|
||||
expr_ref operator()(expr* e, expr* k, obj_map<func_decl, expr*>* translate = nullptr);
|
||||
expr_ref undo_k(expr* e, expr* k);
|
||||
private:
|
||||
expr_ref scale(expr* e, bool is_mul);
|
||||
};
|
||||
|
||||
class pred_transformer;
|
||||
|
||||
class closure {
|
||||
ast_manager& m;
|
||||
pred_transformer& m_pt;
|
||||
arith_util a;
|
||||
bool m_is_closure;
|
||||
expr_ref_vector m_sigma;
|
||||
expr_ref_vector m_trail;
|
||||
vector<obj_map<func_decl, expr*> > m_vars;
|
||||
|
||||
expr_ref relax(unsigned i, expr* fml);
|
||||
expr_ref close_conjunction(expr* fml);
|
||||
expr_ref close_fml(expr* fml);
|
||||
void add_variables(unsigned num_vars, expr_ref_vector& fmls);
|
||||
public:
|
||||
closure(pred_transformer& pt, bool is_closure);
|
||||
expr_ref operator()(expr_ref_vector const& As);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,448 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_context.h
|
||||
|
||||
Abstract:
|
||||
|
||||
PDR for datalog
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_CONTEXT_H_
|
||||
#define PDR_CONTEXT_H_
|
||||
|
||||
#ifdef _CYGWIN
|
||||
#undef min
|
||||
#undef max
|
||||
#endif
|
||||
#include <deque>
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_prop_solver.h"
|
||||
#include "muz/pdr/pdr_reachable_cache.h"
|
||||
#include "muz/base/fixedpoint_params.hpp"
|
||||
|
||||
|
||||
namespace datalog {
|
||||
class rule_set;
|
||||
class context;
|
||||
};
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class pred_transformer;
|
||||
class model_node;
|
||||
class context;
|
||||
|
||||
typedef obj_map<datalog::rule const, app_ref_vector*> rule2inst;
|
||||
typedef obj_map<func_decl, pred_transformer*> decl2rel;
|
||||
|
||||
|
||||
//
|
||||
// Predicate transformer state.
|
||||
// A predicate transformer corresponds to the
|
||||
// set of rules that have the same head predicates.
|
||||
//
|
||||
|
||||
class pred_transformer {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_propagations;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
typedef obj_map<datalog::rule const, expr*> rule2expr;
|
||||
typedef obj_map<datalog::rule const, ptr_vector<app> > rule2apps;
|
||||
|
||||
manager& pm; // pdr-manager
|
||||
ast_manager& m; // manager
|
||||
context& ctx;
|
||||
|
||||
func_decl_ref m_head; // predicate
|
||||
func_decl_ref_vector m_sig; // signature
|
||||
ptr_vector<pred_transformer> m_use; // places where 'this' is referenced.
|
||||
ptr_vector<datalog::rule> m_rules; // rules used to derive transformer
|
||||
prop_solver m_solver; // solver context
|
||||
vector<expr_ref_vector> m_levels; // level formulas
|
||||
expr_ref_vector m_invariants; // properties that are invariant.
|
||||
obj_map<expr, unsigned> m_prop2level; // map property to level where it occurs.
|
||||
obj_map<expr, datalog::rule const*> m_tag2rule; // map tag predicate to rule.
|
||||
rule2expr m_rule2tag; // map rule to predicate tag.
|
||||
rule2inst m_rule2inst; // map rules to instantiations of indices
|
||||
rule2expr m_rule2transition; // map rules to transition
|
||||
rule2apps m_rule2vars; // map rule to auxiliary variables
|
||||
expr_ref m_transition; // transition relation.
|
||||
expr_ref m_initial_state; // initial state.
|
||||
reachable_cache m_reachable;
|
||||
ptr_vector<func_decl> m_predicates;
|
||||
stats m_stats;
|
||||
|
||||
void init_sig();
|
||||
void ensure_level(unsigned level);
|
||||
bool add_property1(expr * lemma, unsigned lvl); // add property 'p' to state at level lvl.
|
||||
void add_child_property(pred_transformer& child, expr* lemma, unsigned lvl);
|
||||
void mk_assumptions(func_decl* head, expr* fml, expr_ref_vector& result);
|
||||
|
||||
// Initialization
|
||||
void init_rules(decl2rel const& pts, expr_ref& init, expr_ref& transition);
|
||||
void init_rule(decl2rel const& pts, datalog::rule const& rule, expr_ref& init,
|
||||
ptr_vector<datalog::rule const>& rules, expr_ref_vector& transition);
|
||||
void init_atom(decl2rel const& pts, app * atom, app_ref_vector& var_reprs, expr_ref_vector& conj, unsigned tail_idx);
|
||||
|
||||
void simplify_formulas(tactic& tac, expr_ref_vector& fmls);
|
||||
|
||||
// Debugging
|
||||
bool check_filled(app_ref_vector const& v) const;
|
||||
|
||||
void add_premises(decl2rel const& pts, unsigned lvl, datalog::rule& rule, expr_ref_vector& r);
|
||||
|
||||
public:
|
||||
pred_transformer(context& ctx, manager& pm, func_decl* head);
|
||||
~pred_transformer();
|
||||
|
||||
void add_rule(datalog::rule* r) { m_rules.push_back(r); }
|
||||
void add_use(pred_transformer* pt) { if (!m_use.contains(pt)) m_use.insert(pt); }
|
||||
void initialize(decl2rel const& pts);
|
||||
|
||||
func_decl* head() const { return m_head; }
|
||||
ptr_vector<datalog::rule> const& rules() const { return m_rules; }
|
||||
func_decl* sig(unsigned i) { init_sig(); return m_sig[i].get(); } // signature
|
||||
func_decl* const* sig() { init_sig(); return m_sig.c_ptr(); }
|
||||
unsigned sig_size() { init_sig(); return m_sig.size(); }
|
||||
expr* transition() const { return m_transition; }
|
||||
expr* initial_state() const { return m_initial_state; }
|
||||
expr* rule2tag(datalog::rule const* r) { return m_rule2tag.find(r); }
|
||||
unsigned get_num_levels() { return m_levels.size(); }
|
||||
expr_ref get_cover_delta(func_decl* p_orig, int level);
|
||||
void add_cover(unsigned level, expr* property);
|
||||
context& get_context() { return ctx; }
|
||||
|
||||
std::ostream& display(std::ostream& strm) const;
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
bool is_reachable(expr* state);
|
||||
void remove_predecessors(expr_ref_vector& literals);
|
||||
void find_predecessors(datalog::rule const& r, ptr_vector<func_decl>& predicates) const;
|
||||
datalog::rule const& find_rule(model_core const& model) const;
|
||||
expr* get_transition(datalog::rule const& r) { return m_rule2transition.find(&r); }
|
||||
ptr_vector<app>& get_aux_vars(datalog::rule const& r) { return m_rule2vars.find(&r); }
|
||||
|
||||
bool propagate_to_next_level(unsigned level);
|
||||
void propagate_to_infinity(unsigned level);
|
||||
void add_property(expr * lemma, unsigned lvl); // add property 'p' to state at level.
|
||||
|
||||
lbool is_reachable(model_node& n, expr_ref_vector* core, bool& uses_level);
|
||||
bool is_invariant(unsigned level, expr* co_state, bool inductive, bool& assumes_level, expr_ref_vector* core = nullptr);
|
||||
bool check_inductive(unsigned level, expr_ref_vector& state, bool& assumes_level);
|
||||
|
||||
expr_ref get_formulas(unsigned level, bool add_axioms);
|
||||
|
||||
void simplify_formulas();
|
||||
|
||||
expr_ref get_propagation_formula(decl2rel const& pts, unsigned level);
|
||||
|
||||
manager& get_pdr_manager() const { return pm; }
|
||||
ast_manager& get_manager() const { return m; }
|
||||
|
||||
void add_premises(decl2rel const& pts, unsigned lvl, expr_ref_vector& r);
|
||||
|
||||
void close(expr* e);
|
||||
|
||||
app_ref_vector& get_inst(datalog::rule const* r) { return *m_rule2inst.find(r);}
|
||||
|
||||
void inherit_properties(pred_transformer& other);
|
||||
|
||||
void ground_free_vars(expr* e, app_ref_vector& vars, ptr_vector<app>& aux_vars);
|
||||
|
||||
prop_solver& get_solver() { return m_solver; }
|
||||
prop_solver const& get_solver() const { return m_solver; }
|
||||
|
||||
void set_use_farkas(bool f) { get_solver().set_use_farkas(f); }
|
||||
bool get_use_farkas() const { return get_solver().get_use_farkas(); }
|
||||
class scoped_farkas {
|
||||
bool m_old;
|
||||
pred_transformer& m_p;
|
||||
public:
|
||||
scoped_farkas(pred_transformer& p, bool v): m_old(p.get_use_farkas()), m_p(p) {
|
||||
p.set_use_farkas(v);
|
||||
}
|
||||
~scoped_farkas() { m_p.set_use_farkas(m_old); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
// structure for counter-example search.
|
||||
class model_node {
|
||||
model_node* m_parent;
|
||||
model_node* m_next;
|
||||
model_node* m_prev;
|
||||
pred_transformer& m_pt;
|
||||
expr_ref m_state;
|
||||
model_ref m_model;
|
||||
ptr_vector<model_node> m_children;
|
||||
unsigned m_level;
|
||||
unsigned m_orig_level;
|
||||
unsigned m_depth;
|
||||
bool m_closed;
|
||||
datalog::rule const* m_rule;
|
||||
public:
|
||||
model_node(model_node* parent, expr_ref& state, pred_transformer& pt, unsigned level):
|
||||
m_parent(parent), m_next(nullptr), m_prev(nullptr), m_pt(pt), m_state(state), m_model(nullptr),
|
||||
m_level(level), m_orig_level(level), m_depth(0), m_closed(false), m_rule(nullptr) {
|
||||
model_node* p = m_parent;
|
||||
if (p) {
|
||||
p->m_children.push_back(this);
|
||||
SASSERT(p->m_level == level+1);
|
||||
SASSERT(p->m_level > 0);
|
||||
m_depth = p->m_depth+1;
|
||||
if (p && p->is_closed()) {
|
||||
p->set_open();
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_model(model_ref& m) { m_model = m; }
|
||||
unsigned level() const { return m_level; }
|
||||
unsigned orig_level() const { return m_orig_level; }
|
||||
unsigned depth() const { return m_depth; }
|
||||
void increase_level() { ++m_level; }
|
||||
expr_ref const& state() const { return m_state; }
|
||||
ptr_vector<model_node> const& children() { return m_children; }
|
||||
pred_transformer& pt() const { return m_pt; }
|
||||
model_node* parent() const { return m_parent; }
|
||||
model* get_model_ptr() const { return m_model.get(); }
|
||||
model const& get_model() const { return *m_model; }
|
||||
unsigned index() const;
|
||||
|
||||
bool is_closed() const { return m_closed; }
|
||||
bool is_open() const { return !is_closed(); }
|
||||
|
||||
bool is_1closed() {
|
||||
if (is_closed()) return true;
|
||||
if (m_children.empty()) return false;
|
||||
for (unsigned i = 0; i < m_children.size(); ++i) {
|
||||
if (m_children[i]->is_open()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void check_pre_closed();
|
||||
void set_closed();
|
||||
void set_open();
|
||||
void set_pre_closed() { TRACE("pdr", tout << state() << "\n";); m_closed = true; }
|
||||
void reset() { m_children.reset(); }
|
||||
|
||||
void set_rule(datalog::rule const* r) { m_rule = r; }
|
||||
datalog::rule* get_rule();
|
||||
|
||||
void mk_instantiate(datalog::rule_ref& r0, datalog::rule_ref& r1, expr_ref_vector& binding);
|
||||
|
||||
std::ostream& display(std::ostream& out, unsigned indent);
|
||||
|
||||
void dequeue(model_node*& root);
|
||||
void enqueue(model_node* n);
|
||||
model_node* next() const { return m_next; }
|
||||
bool is_goal() const { return nullptr != next(); }
|
||||
};
|
||||
|
||||
class model_search {
|
||||
typedef ptr_vector<model_node> model_nodes;
|
||||
bool m_bfs;
|
||||
model_node* m_root;
|
||||
model_node* m_goal;
|
||||
vector<obj_map<expr, model_nodes > > m_cache;
|
||||
obj_map<expr, model_nodes>& cache(model_node const& n);
|
||||
void erase_children(model_node& n, bool backtrack);
|
||||
void remove_node(model_node& n, bool backtrack);
|
||||
void enqueue_leaf(model_node* n); // add leaf to priority queue.
|
||||
void update_models();
|
||||
void set_leaf(model_node& n); // Set node as leaf, remove children.
|
||||
unsigned num_goals() const;
|
||||
|
||||
public:
|
||||
model_search(bool bfs): m_bfs(bfs), m_root(nullptr), m_goal(nullptr) {}
|
||||
~model_search();
|
||||
|
||||
void reset();
|
||||
model_node* next();
|
||||
void add_leaf(model_node& n); // add fresh node.
|
||||
|
||||
void set_root(model_node* n);
|
||||
model_node& get_root() const { return *m_root; }
|
||||
std::ostream& display(std::ostream& out) const;
|
||||
expr_ref get_trace(context const& ctx);
|
||||
proof_ref get_proof_trace(context const& ctx);
|
||||
void backtrack_level(bool uses_level, model_node& n);
|
||||
void remove_goal(model_node& n);
|
||||
|
||||
void well_formed();
|
||||
};
|
||||
|
||||
struct model_exception { };
|
||||
struct inductive_exception {};
|
||||
|
||||
|
||||
// 'state' is unsatisfiable at 'level' with 'core'.
|
||||
// Minimize or weaken core.
|
||||
class core_generalizer {
|
||||
protected:
|
||||
context& m_ctx;
|
||||
public:
|
||||
typedef vector<std::pair<expr_ref_vector,bool> > cores;
|
||||
core_generalizer(context& ctx): m_ctx(ctx) {}
|
||||
virtual ~core_generalizer() {}
|
||||
virtual void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) = 0;
|
||||
virtual void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
new_cores.push_back(std::make_pair(core, uses_level));
|
||||
if (!core.empty()) {
|
||||
(*this)(n, new_cores.back().first, new_cores.back().second);
|
||||
}
|
||||
}
|
||||
virtual void collect_statistics(statistics& st) const {}
|
||||
virtual void reset_statistics() {}
|
||||
};
|
||||
|
||||
class context {
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_nodes;
|
||||
unsigned m_max_depth;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
smt_params& m_fparams;
|
||||
fixedpoint_params const& m_params;
|
||||
ast_manager& m;
|
||||
datalog::context* m_context;
|
||||
manager m_pm;
|
||||
decl2rel m_rels; // Map from relation predicate to fp-operator.
|
||||
decl2rel m_rels_tmp;
|
||||
func_decl_ref m_query_pred;
|
||||
pred_transformer* m_query;
|
||||
mutable model_search m_search;
|
||||
lbool m_last_result;
|
||||
unsigned m_inductive_lvl;
|
||||
unsigned m_expanded_lvl;
|
||||
ptr_vector<core_generalizer> m_core_generalizers;
|
||||
stats m_stats;
|
||||
model_converter_ref m_mc;
|
||||
proof_converter_ref m_pc;
|
||||
|
||||
// Functions used by search.
|
||||
void solve_impl();
|
||||
bool check_reachability(unsigned level);
|
||||
void propagate(unsigned max_prop_lvl);
|
||||
void close_node(model_node& n);
|
||||
void check_pre_closed(model_node& n);
|
||||
void expand_node(model_node& n);
|
||||
lbool expand_state(model_node& n, expr_ref_vector& cube, bool& uses_level);
|
||||
void create_children(model_node& n);
|
||||
expr_ref mk_sat_answer() const;
|
||||
expr_ref mk_unsat_answer();
|
||||
|
||||
// Generate inductive property
|
||||
void get_level_property(unsigned lvl, expr_ref_vector& res, vector<relation_info> & rs);
|
||||
|
||||
|
||||
// Initialization
|
||||
class classifier_proc;
|
||||
void init_core_generalizers(datalog::rule_set& rules);
|
||||
|
||||
bool check_invariant(unsigned lvl);
|
||||
bool check_invariant(unsigned lvl, func_decl* fn);
|
||||
|
||||
void checkpoint();
|
||||
|
||||
void init_rules(datalog::rule_set& rules, decl2rel& transformers);
|
||||
|
||||
void simplify_formulas();
|
||||
|
||||
void reset_core_generalizers();
|
||||
|
||||
void reset(decl2rel& rels);
|
||||
|
||||
void validate();
|
||||
void validate_proof();
|
||||
void validate_search();
|
||||
void validate_model();
|
||||
|
||||
public:
|
||||
|
||||
/**
|
||||
Initial values of predicates are stored in corresponding relations in dctx.
|
||||
|
||||
We check whether there is some reachable state of the relation checked_relation.
|
||||
*/
|
||||
context(
|
||||
smt_params& fparams,
|
||||
fixedpoint_params const& params,
|
||||
ast_manager& m);
|
||||
|
||||
~context();
|
||||
|
||||
smt_params& get_fparams() const { return m_fparams; }
|
||||
fixedpoint_params const& get_params() const { return m_params; }
|
||||
ast_manager& get_manager() const { return m; }
|
||||
manager& get_pdr_manager() { return m_pm; }
|
||||
decl2rel const& get_pred_transformers() const { return m_rels; }
|
||||
pred_transformer& get_pred_transformer(func_decl* p) const { return *m_rels.find(p); }
|
||||
datalog::context& get_context() const { SASSERT(m_context); return *m_context; }
|
||||
expr_ref get_answer();
|
||||
|
||||
bool is_dl() const { return m_fparams.m_arith_mode == AS_DIFF_LOGIC; }
|
||||
bool is_utvpi() const { return m_fparams.m_arith_mode == AS_UTVPI; }
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
|
||||
std::ostream& display(std::ostream& strm) const;
|
||||
|
||||
void display_certificate(std::ostream& strm);
|
||||
|
||||
lbool solve();
|
||||
|
||||
void reset(bool full = true);
|
||||
|
||||
void set_query(func_decl* q) { m_query_pred = q; }
|
||||
|
||||
void set_unsat() { m_last_result = l_false; }
|
||||
|
||||
void set_model_converter(model_converter_ref& mc) { m_mc = mc; }
|
||||
|
||||
model_converter_ref get_model_converter() { return m_mc; }
|
||||
|
||||
void set_proof_converter(proof_converter_ref& pc) { m_pc = pc; }
|
||||
|
||||
void update_rules(datalog::rule_set& rules);
|
||||
|
||||
void set_axioms(expr* axioms) { m_pm.set_background(axioms); }
|
||||
|
||||
unsigned get_num_levels(func_decl* p);
|
||||
|
||||
expr_ref get_cover_delta(int level, func_decl* p_orig, func_decl* p);
|
||||
|
||||
void add_cover(int level, func_decl* pred, expr* property);
|
||||
|
||||
model_ref get_model();
|
||||
|
||||
proof_ref get_proof() const;
|
||||
|
||||
model_node& get_root() const { return m_search.get_root(); }
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,225 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_dl.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog PDR
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/base/dl_context.h"
|
||||
#include "muz/transforms/dl_mk_coi_filter.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
#include "muz/base/dl_rule_transformer.h"
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "muz/pdr/pdr_dl_interface.h"
|
||||
#include "muz/base/dl_rule_set.h"
|
||||
#include "muz/transforms/dl_mk_slice.h"
|
||||
#include "muz/transforms/dl_mk_unfold.h"
|
||||
#include "muz/transforms/dl_mk_coalesce.h"
|
||||
#include "muz/transforms/dl_transforms.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
|
||||
using namespace pdr;
|
||||
|
||||
dl_interface::dl_interface(datalog::context& ctx) :
|
||||
engine_base(ctx.get_manager(), "pdr"),
|
||||
m_ctx(ctx),
|
||||
m_pdr_rules(ctx),
|
||||
m_old_rules(ctx),
|
||||
m_context(nullptr),
|
||||
m_refs(ctx.get_manager()) {
|
||||
m_context = alloc(pdr::context, ctx.get_fparams(), ctx.get_params(), ctx.get_manager());
|
||||
}
|
||||
|
||||
|
||||
dl_interface::~dl_interface() {
|
||||
dealloc(m_context);
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Check if the new rules are weaker so that we can
|
||||
// re-use existing context.
|
||||
//
|
||||
void dl_interface::check_reset() {
|
||||
datalog::rule_set const& new_rules = m_ctx.get_rules();
|
||||
datalog::rule_ref_vector const& old_rules = m_old_rules.get_rules();
|
||||
bool is_subsumed = !old_rules.empty();
|
||||
for (unsigned i = 0; is_subsumed && i < new_rules.get_num_rules(); ++i) {
|
||||
is_subsumed = false;
|
||||
for (unsigned j = 0; !is_subsumed && j < old_rules.size(); ++j) {
|
||||
if (m_ctx.check_subsumes(*old_rules[j], *new_rules.get_rule(i))) {
|
||||
is_subsumed = true;
|
||||
}
|
||||
}
|
||||
if (!is_subsumed) {
|
||||
TRACE("pdr", new_rules.get_rule(i)->display(m_ctx, tout << "Fresh rule "););
|
||||
m_context->reset();
|
||||
}
|
||||
}
|
||||
m_old_rules.replace_rules(new_rules);
|
||||
}
|
||||
|
||||
|
||||
lbool dl_interface::query(expr * query) {
|
||||
//we restore the initial state in the datalog context
|
||||
m_ctx.ensure_opened();
|
||||
m_refs.reset();
|
||||
m_pred2slice.reset();
|
||||
ast_manager& m = m_ctx.get_manager();
|
||||
datalog::rule_manager& rm = m_ctx.get_rule_manager();
|
||||
datalog::rule_set& rules0 = m_ctx.get_rules();
|
||||
|
||||
datalog::rule_set old_rules(rules0);
|
||||
func_decl_ref query_pred(m);
|
||||
rm.mk_query(query, rules0);
|
||||
expr_ref bg_assertion = m_ctx.get_background_assertion();
|
||||
|
||||
check_reset();
|
||||
|
||||
TRACE("pdr",
|
||||
if (!m.is_true(bg_assertion)) {
|
||||
tout << "axioms:\n";
|
||||
tout << mk_pp(bg_assertion, m) << "\n";
|
||||
}
|
||||
tout << "query: " << mk_pp(query, m) << "\n";
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
);
|
||||
|
||||
|
||||
apply_default_transformation(m_ctx);
|
||||
|
||||
if (m_ctx.get_params().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);
|
||||
|
||||
// track sliced predicates.
|
||||
obj_map<func_decl, func_decl*> const& preds = slice->get_predicates();
|
||||
obj_map<func_decl, func_decl*>::iterator it = preds.begin();
|
||||
obj_map<func_decl, func_decl*>::iterator end = preds.end();
|
||||
for (; it != end; ++it) {
|
||||
m_pred2slice.insert(it->m_key, it->m_value);
|
||||
m_refs.push_back(it->m_key);
|
||||
m_refs.push_back(it->m_value);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ctx.get_params().xform_unfold_rules() > 0) {
|
||||
unsigned num_unfolds = m_ctx.get_params().xform_unfold_rules();
|
||||
datalog::rule_transformer transf1(m_ctx), transf2(m_ctx);
|
||||
transf1.register_plugin(alloc(datalog::mk_coalesce, m_ctx));
|
||||
transf2.register_plugin(alloc(datalog::mk_unfold, m_ctx));
|
||||
if (m_ctx.get_params().xform_coalesce_rules()) {
|
||||
m_ctx.transform_rules(transf1);
|
||||
}
|
||||
while (num_unfolds > 0) {
|
||||
m_ctx.transform_rules(transf2);
|
||||
--num_unfolds;
|
||||
}
|
||||
}
|
||||
|
||||
const datalog::rule_set& rules = m_ctx.get_rules();
|
||||
if (rules.get_output_predicates().empty()) {
|
||||
m_context->set_unsat();
|
||||
return l_false;
|
||||
}
|
||||
|
||||
query_pred = rules.get_output_predicate();
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
m_ctx.display_smt2(0, 0, tout);
|
||||
);
|
||||
|
||||
IF_VERBOSE(2, m_ctx.display_rules(verbose_stream()););
|
||||
m_pdr_rules.replace_rules(rules);
|
||||
m_pdr_rules.close();
|
||||
m_ctx.record_transformed_rules();
|
||||
m_ctx.reopen();
|
||||
m_ctx.replace_rules(old_rules);
|
||||
|
||||
|
||||
scoped_restore_proof _sc(m); // update_rules may overwrite the proof mode.
|
||||
|
||||
m_context->set_proof_converter(m_ctx.get_proof_converter());
|
||||
m_context->set_model_converter(m_ctx.get_model_converter());
|
||||
m_context->set_query(query_pred);
|
||||
m_context->set_axioms(bg_assertion);
|
||||
m_context->update_rules(m_pdr_rules);
|
||||
|
||||
if (m_pdr_rules.get_rules().empty()) {
|
||||
m_context->set_unsat();
|
||||
IF_VERBOSE(1, model_smt2_pp(verbose_stream(), m, *m_context->get_model(),0););
|
||||
return l_false;
|
||||
}
|
||||
|
||||
return m_context->solve();
|
||||
|
||||
}
|
||||
|
||||
expr_ref dl_interface::get_cover_delta(int level, func_decl* pred_orig) {
|
||||
func_decl* pred = pred_orig;
|
||||
m_pred2slice.find(pred_orig, pred);
|
||||
SASSERT(pred);
|
||||
return m_context->get_cover_delta(level, pred_orig, pred);
|
||||
}
|
||||
|
||||
void dl_interface::add_cover(int level, func_decl* pred, expr* property) {
|
||||
if (m_ctx.get_params().xform_slice()) {
|
||||
throw default_exception("Covers are incompatible with slicing. Disable slicing before using covers");
|
||||
}
|
||||
m_context->add_cover(level, pred, property);
|
||||
}
|
||||
|
||||
unsigned dl_interface::get_num_levels(func_decl* pred) {
|
||||
m_pred2slice.find(pred, pred);
|
||||
SASSERT(pred);
|
||||
return m_context->get_num_levels(pred);
|
||||
}
|
||||
|
||||
void dl_interface::collect_statistics(statistics& st) const {
|
||||
m_context->collect_statistics(st);
|
||||
}
|
||||
|
||||
void dl_interface::reset_statistics() {
|
||||
m_context->reset_statistics();
|
||||
}
|
||||
|
||||
void dl_interface::display_certificate(std::ostream& out) const {
|
||||
m_context->display_certificate(out);
|
||||
}
|
||||
|
||||
expr_ref dl_interface::get_answer() {
|
||||
return m_context->get_answer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
void dl_interface::updt_params() {
|
||||
dealloc(m_context);
|
||||
m_context = alloc(pdr::context, m_ctx.get_fparams(), m_ctx.get_params(), m_ctx.get_manager());
|
||||
}
|
||||
|
||||
model_ref dl_interface::get_model() {
|
||||
return m_context->get_model();
|
||||
}
|
||||
|
||||
proof_ref dl_interface::get_proof() {
|
||||
return m_context->get_proof();
|
||||
}
|
|
@ -1,78 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_dl_interface.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog PDR
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_DL_INTERFACE_H_
|
||||
#define PDR_DL_INTERFACE_H_
|
||||
|
||||
#include "util/lbool.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
#include "muz/base/dl_rule_set.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "muz/base/dl_engine_base.h"
|
||||
#include "util/statistics.h"
|
||||
|
||||
namespace datalog {
|
||||
class context;
|
||||
}
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class context;
|
||||
|
||||
class dl_interface : public datalog::engine_base {
|
||||
datalog::context& m_ctx;
|
||||
datalog::rule_set m_pdr_rules;
|
||||
datalog::rule_set m_old_rules;
|
||||
context* m_context;
|
||||
obj_map<func_decl, func_decl*> m_pred2slice;
|
||||
ast_ref_vector m_refs;
|
||||
|
||||
void check_reset();
|
||||
|
||||
public:
|
||||
dl_interface(datalog::context& ctx);
|
||||
~dl_interface() override;
|
||||
|
||||
lbool query(expr* query) override;
|
||||
|
||||
void display_certificate(std::ostream& out) const override;
|
||||
|
||||
void collect_statistics(statistics& st) const override;
|
||||
|
||||
void reset_statistics() override;
|
||||
|
||||
expr_ref get_answer() override;
|
||||
|
||||
unsigned get_num_levels(func_decl* pred) override;
|
||||
|
||||
expr_ref get_cover_delta(int level, func_decl* pred) override;
|
||||
|
||||
void add_cover(int level, func_decl* pred, expr* property) override;
|
||||
|
||||
void updt_params() override;
|
||||
|
||||
model_ref get_model() override;
|
||||
|
||||
proof_ref get_proof() override;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
File diff suppressed because it is too large
Load diff
|
@ -1,128 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_farkas_learner.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT2 interface for the datalog PDR
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-11-1.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_FARKAS_LEARNER_H_
|
||||
#define PDR_FARKAS_LEARNER_H_
|
||||
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/ast_translation.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "tactic/tactic.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class farkas_learner {
|
||||
class farkas_collector;
|
||||
class constant_replacer_cfg;
|
||||
class equality_expander_cfg;
|
||||
class constr;
|
||||
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
|
||||
smt_params m_proof_params;
|
||||
ast_manager m_pr;
|
||||
scoped_ptr<smt::kernel> m_ctx;
|
||||
constr* m_constr;
|
||||
|
||||
//
|
||||
// true: produce a combined constraint by applying Farkas coefficients.
|
||||
// false: produce a conjunction of the negated literals from the theory lemmas.
|
||||
//
|
||||
bool m_combine_farkas_coefficients;
|
||||
|
||||
|
||||
static smt_params get_proof_params(smt_params& orig_params);
|
||||
|
||||
//
|
||||
// all ast objects passed to private functions have m_proof_mgs as their ast_manager
|
||||
//
|
||||
|
||||
ast_translation p2o; /** Translate expression from inner ast_manager to outer one */
|
||||
ast_translation o2p; /** Translate expression from outer ast_manager to inner one */
|
||||
|
||||
|
||||
/** All ast opbjects here are in the m_proof_mgs */
|
||||
void get_lemma_guesses_internal(proof * p, expr* A, expr * B, expr_ref_vector& lemmas);
|
||||
|
||||
bool farkas2lemma(proof * fstep, expr* A, expr * B, expr_ref& res);
|
||||
|
||||
void combine_constraints(unsigned cnt, app * const * constrs, rational const * coeffs, expr_ref& res);
|
||||
|
||||
bool try_ensure_lemma_in_language(expr_ref& lemma, expr* A, const func_decl_set& lang);
|
||||
|
||||
bool is_farkas_lemma(ast_manager& m, expr* e);
|
||||
|
||||
void get_asserted(proof* p, expr_set const& bs, ast_mark& b_closed, obj_hashtable<expr>& lemma_set, expr_ref_vector& lemmas);
|
||||
|
||||
bool is_pure_expr(func_decl_set const& symbs, expr* e) const;
|
||||
|
||||
static void test();
|
||||
|
||||
public:
|
||||
farkas_learner(smt_params& params, ast_manager& m);
|
||||
|
||||
~farkas_learner();
|
||||
|
||||
/**
|
||||
All ast objects have the ast_manager which was passed as
|
||||
an argument to the constructor (i.e. m_outer_mgr)
|
||||
|
||||
B is a conjunction of literals.
|
||||
A && B is unsat, equivalently A => ~B is valid
|
||||
Find a weakened B' such that
|
||||
A && B' is unsat and B' uses vocabulary (and constants) in common with A.
|
||||
return lemmas to weaken B.
|
||||
*/
|
||||
|
||||
bool get_lemma_guesses(expr * A, expr * B, expr_ref_vector& lemmas);
|
||||
|
||||
/**
|
||||
Traverse a proof and retrieve lemmas using the vocabulary from bs.
|
||||
*/
|
||||
void get_lemmas(proof* root, expr_set const& bs, expr_ref_vector& lemmas);
|
||||
|
||||
/**
|
||||
Traverse a proof and retrieve consequences of A that are used to establish ~B.
|
||||
The assumption is that:
|
||||
|
||||
A => \/ ~consequences[i] and \/ ~consequences[i] => ~B
|
||||
|
||||
e.g., the second implication can be rewritten as:
|
||||
|
||||
B => /\ consequences[i]
|
||||
*/
|
||||
void get_consequences(proof* root, expr_set const& bs, expr_ref_vector& consequences);
|
||||
|
||||
/**
|
||||
\brief Simplify lemmas using subsumption.
|
||||
*/
|
||||
void simplify_lemmas(expr_ref_vector& lemmas);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,777 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_generalizers.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Generalizers of satisfiable states and unsat cores.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-20.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "muz/pdr/pdr_farkas_learner.h"
|
||||
#include "muz/pdr/pdr_generalizers.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
|
||||
|
||||
namespace pdr {
|
||||
|
||||
|
||||
// ------------------------
|
||||
// core_bool_inductive_generalizer
|
||||
|
||||
// main propositional induction generalizer.
|
||||
// drop literals one by one from the core and check if the core is still inductive.
|
||||
//
|
||||
void core_bool_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
if (core.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
ast_manager& m = core.get_manager();
|
||||
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
|
||||
unsigned num_failures = 0, i = 0, old_core_size = core.size();
|
||||
ptr_vector<expr> processed;
|
||||
|
||||
while (i < core.size() && 1 < core.size() && (!m_failure_limit || num_failures <= m_failure_limit)) {
|
||||
expr_ref lit(m);
|
||||
lit = core[i].get();
|
||||
core[i] = m.mk_true();
|
||||
if (n.pt().check_inductive(n.level(), core, uses_level)) {
|
||||
num_failures = 0;
|
||||
for (i = 0; i < core.size() && processed.contains(core[i].get()); ++i);
|
||||
}
|
||||
else {
|
||||
core[i] = lit;
|
||||
processed.push_back(lit);
|
||||
++num_failures;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "old size: " << old_core_size << " new size: " << core.size() << "\n";);
|
||||
TRACE("pdr", tout << "old size: " << old_core_size << " new size: " << core.size() << "\n";);
|
||||
}
|
||||
|
||||
|
||||
void core_multi_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Find minimal cores.
|
||||
Apply a simple heuristic: find a minimal core, then find minimal cores that exclude at least one
|
||||
literal from each of the literals in the minimal cores.
|
||||
*/
|
||||
void core_multi_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
ast_manager& m = core.get_manager();
|
||||
expr_ref_vector old_core(m), core0(core);
|
||||
bool uses_level1 = uses_level;
|
||||
m_gen(n, core0, uses_level1);
|
||||
new_cores.push_back(std::make_pair(core0, uses_level1));
|
||||
obj_hashtable<expr> core_exprs, core1_exprs;
|
||||
set_union(core_exprs, core0);
|
||||
for (unsigned i = 0; i < old_core.size(); ++i) {
|
||||
expr* lit = old_core[i].get();
|
||||
if (core_exprs.contains(lit)) {
|
||||
expr_ref_vector core1(old_core);
|
||||
core1[i] = core1.back();
|
||||
core1.pop_back();
|
||||
uses_level1 = uses_level;
|
||||
m_gen(n, core1, uses_level1);
|
||||
SASSERT(core1.size() <= old_core.size());
|
||||
if (core1.size() < old_core.size()) {
|
||||
new_cores.push_back(std::make_pair(core1, uses_level1));
|
||||
core1_exprs.reset();
|
||||
set_union(core1_exprs, core1);
|
||||
set_intersection(core_exprs, core1_exprs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// core_farkas_generalizer
|
||||
|
||||
//
|
||||
// for each disjunct of core:
|
||||
// weaken predecessor.
|
||||
//
|
||||
|
||||
core_farkas_generalizer::core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p):
|
||||
core_generalizer(ctx),
|
||||
m_farkas_learner(p, m)
|
||||
{}
|
||||
|
||||
void core_farkas_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
ast_manager& m = n.pt().get_manager();
|
||||
if (core.empty()) return;
|
||||
expr_ref A(m), B(mk_and(core)), C(m);
|
||||
expr_ref_vector Bs(m);
|
||||
flatten_or(B, Bs);
|
||||
A = n.pt().get_propagation_formula(m_ctx.get_pred_transformers(), n.level());
|
||||
|
||||
bool change = false;
|
||||
for (unsigned i = 0; i < Bs.size(); ++i) {
|
||||
expr_ref_vector lemmas(m);
|
||||
C = Bs[i].get();
|
||||
if (m_farkas_learner.get_lemma_guesses(A, B, lemmas)) {
|
||||
TRACE("pdr",
|
||||
tout << "Old core:\n" << mk_pp(B, m) << "\n";
|
||||
tout << "New core:\n" << mk_and(lemmas) << "\n";);
|
||||
Bs[i] = mk_and(lemmas);
|
||||
change = true;
|
||||
}
|
||||
}
|
||||
if (change) {
|
||||
C = mk_or(Bs);
|
||||
TRACE("pdr", tout << "prop:\n" << mk_pp(A,m) << "\ngen:" << mk_pp(B, m) << "\nto: " << mk_pp(C, m) << "\n";);
|
||||
core.reset();
|
||||
flatten_and(C, core);
|
||||
uses_level = true;
|
||||
}
|
||||
}
|
||||
|
||||
void core_farkas_generalizer::collect_statistics(statistics& st) const {
|
||||
m_farkas_learner.collect_statistics(st);
|
||||
}
|
||||
|
||||
|
||||
core_convex_hull_generalizer::core_convex_hull_generalizer(context& ctx, bool is_closure):
|
||||
core_generalizer(ctx),
|
||||
m(ctx.get_manager()),
|
||||
m_is_closure(is_closure) {
|
||||
}
|
||||
|
||||
void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
// method3(n, core, uses_level, new_cores);
|
||||
method1(n, core, uses_level, new_cores);
|
||||
}
|
||||
|
||||
void core_convex_hull_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
||||
// use the entire region as starting point for generalization.
|
||||
//
|
||||
// Constraints:
|
||||
// add_variables: y = y1 + y2
|
||||
// core: Ay <= b -> conv1: A*y1 <= b*sigma1
|
||||
// sigma1 > 0
|
||||
// sigma2 > 0
|
||||
// 1 = sigma1 + sigma2
|
||||
// A'y <= b' -> conv2: A'*y2 <= b'*sigma2
|
||||
//
|
||||
// If Constraints & Transition(y0, y) is unsat, then
|
||||
// update with new core.
|
||||
//
|
||||
void core_convex_hull_generalizer::method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
expr_ref_vector conv2(m), fmls(m), fml1_2(m);
|
||||
bool change = false;
|
||||
|
||||
if (core.empty()) {
|
||||
new_cores.push_back(std::make_pair(core, uses_level));
|
||||
return;
|
||||
}
|
||||
closure cl(n.pt(), m_is_closure);
|
||||
|
||||
expr_ref fml1 = mk_and(core);
|
||||
expr_ref fml2 = n.pt().get_formulas(n.level(), false);
|
||||
fml1_2.push_back(fml1);
|
||||
fml1_2.push_back(nullptr);
|
||||
flatten_and(fml2, fmls);
|
||||
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||
fml2 = m.mk_not(fmls[i].get());
|
||||
fml1_2[1] = fml2;
|
||||
expr_ref state = cl(fml1_2);
|
||||
TRACE("pdr",
|
||||
tout << "Check states:\n" << mk_pp(state, m) << "\n";
|
||||
tout << "Old states:\n" << mk_pp(fml2, m) << "\n";
|
||||
);
|
||||
model_node nd(nullptr, state, n.pt(), n.level());
|
||||
pred_transformer::scoped_farkas sf(n.pt(), true);
|
||||
bool uses_level1 = uses_level;
|
||||
if (l_false == n.pt().is_reachable(nd, &conv2, uses_level1)) {
|
||||
new_cores.push_back(std::make_pair(conv2, uses_level1));
|
||||
change = true;
|
||||
expr_ref state1 = mk_and(conv2);
|
||||
TRACE("pdr",
|
||||
tout << mk_pp(state, m) << "\n";
|
||||
tout << "Generalized to:\n" << mk_pp(state1, m) << "\n";);
|
||||
IF_VERBOSE(0,
|
||||
verbose_stream() << mk_pp(state, m) << "\n";
|
||||
verbose_stream() << "Generalized to:\n" << mk_pp(state1, m) << "\n";);
|
||||
}
|
||||
}
|
||||
if (!m_is_closure || !change) {
|
||||
new_cores.push_back(std::make_pair(core, uses_level));
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Extract the lemmas from the transition relation that were used to establish unsatisfiability.
|
||||
Take convex closures of conbinations of these lemmas.
|
||||
*/
|
||||
void core_convex_hull_generalizer::method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) {
|
||||
TRACE("dl", tout << "method: generalize consequences of F(R)\n";
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
tout << "B:" << mk_pp(core[i], m) << "\n";
|
||||
});
|
||||
bool uses_level1;
|
||||
expr_ref_vector core1(m);
|
||||
core1.append(core);
|
||||
expr_ref_vector consequences(m);
|
||||
{
|
||||
n.pt().get_solver().set_consequences(&consequences);
|
||||
pred_transformer::scoped_farkas sf (n.pt(), true);
|
||||
VERIFY(l_false == n.pt().is_reachable(n, &core1, uses_level1));
|
||||
n.pt().get_solver().set_consequences(nullptr);
|
||||
}
|
||||
IF_VERBOSE(0,
|
||||
verbose_stream() << "Consequences: " << consequences.size() << "\n";
|
||||
for (unsigned i = 0; i < consequences.size(); ++i) {
|
||||
verbose_stream() << mk_pp(consequences[i].get(), m) << "\n";
|
||||
}
|
||||
verbose_stream() << "core: " << core1.size() << "\n";
|
||||
for (unsigned i = 0; i < core1.size(); ++i) {
|
||||
verbose_stream() << mk_pp(core1[i].get(), m) << "\n";
|
||||
});
|
||||
|
||||
expr_ref tmp(m);
|
||||
|
||||
// Check that F(R) => \/ consequences
|
||||
{
|
||||
expr_ref_vector cstate(m);
|
||||
for (unsigned i = 0; i < consequences.size(); ++i) {
|
||||
cstate.push_back(m.mk_not(consequences[i].get()));
|
||||
}
|
||||
tmp = m.mk_and(cstate.size(), cstate.c_ptr());
|
||||
model_node nd(nullptr, tmp, n.pt(), n.level());
|
||||
pred_transformer::scoped_farkas sf (n.pt(), false);
|
||||
VERIFY(l_false == n.pt().is_reachable(nd, &core1, uses_level1));
|
||||
}
|
||||
|
||||
// Create disjunction.
|
||||
tmp = m.mk_and(core.size(), core.c_ptr());
|
||||
|
||||
// Check that \/ consequences => not (core)
|
||||
if (!is_unsat(consequences, tmp)) {
|
||||
IF_VERBOSE(0, verbose_stream() << "Consequences don't contradict the core\n";);
|
||||
return;
|
||||
}
|
||||
IF_VERBOSE(0, verbose_stream() << "Consequences contradict core\n";);
|
||||
|
||||
if (!strengthen_consequences(n, consequences, tmp)) {
|
||||
return;
|
||||
}
|
||||
|
||||
IF_VERBOSE(0, verbose_stream() << "consequences strengthened\n";);
|
||||
// Use the resulting formula to find Farkas lemmas from core.
|
||||
}
|
||||
|
||||
bool core_convex_hull_generalizer::strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B) {
|
||||
expr_ref A(m), tmp(m), convA(m);
|
||||
unsigned sz = As.size();
|
||||
closure cl(n.pt(), m_is_closure);
|
||||
for (unsigned i = 0; i < As.size(); ++i) {
|
||||
expr_ref_vector Hs(m);
|
||||
Hs.push_back(As[i].get());
|
||||
for (unsigned j = i + 1; j < As.size(); ++j) {
|
||||
Hs.push_back(As[j].get());
|
||||
bool unsat = false;
|
||||
A = cl(Hs);
|
||||
tmp = As[i].get();
|
||||
As[i] = A;
|
||||
unsat = is_unsat(As, B);
|
||||
As[i] = tmp;
|
||||
if (unsat) {
|
||||
IF_VERBOSE(0, verbose_stream() << "New convex: " << mk_pp(convA, m) << "\n";);
|
||||
convA = A;
|
||||
As[j] = As.back();
|
||||
As.pop_back();
|
||||
--j;
|
||||
}
|
||||
else {
|
||||
Hs.pop_back();
|
||||
}
|
||||
}
|
||||
if (Hs.size() > 1) {
|
||||
As[i] = convA;
|
||||
}
|
||||
}
|
||||
return sz > As.size();
|
||||
}
|
||||
|
||||
|
||||
bool core_convex_hull_generalizer::is_unsat(expr_ref_vector const& As, expr* B) {
|
||||
smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p);
|
||||
expr_ref disj(m);
|
||||
disj = m.mk_or(As.size(), As.c_ptr());
|
||||
ctx.assert_expr(disj);
|
||||
ctx.assert_expr(B);
|
||||
std::cout << "Checking\n" << mk_pp(disj, m) << "\n" << mk_pp(B, m) << "\n";
|
||||
return l_false == ctx.check();
|
||||
}
|
||||
|
||||
|
||||
// ---------------------------------
|
||||
// core_arith_inductive_generalizer
|
||||
// NB. this is trying out some ideas for generalization in
|
||||
// an ad hoc specialized way. arith_inductive_generalizer should
|
||||
// not be used by default. It is a place-holder for a general purpose
|
||||
// extrapolator of a lattice basis.
|
||||
|
||||
core_arith_inductive_generalizer::core_arith_inductive_generalizer(context& ctx):
|
||||
core_generalizer(ctx),
|
||||
m(ctx.get_manager()),
|
||||
a(m),
|
||||
m_refs(m) {}
|
||||
|
||||
void core_arith_inductive_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
if (core.size() <= 1) {
|
||||
return;
|
||||
}
|
||||
reset();
|
||||
expr_ref e(m), t1(m), t2(m), t3(m);
|
||||
rational r;
|
||||
|
||||
TRACE("pdr", for (unsigned i = 0; i < core.size(); ++i) { tout << mk_pp(core[i].get(), m) << "\n"; });
|
||||
|
||||
svector<eq> eqs;
|
||||
get_eqs(core, eqs);
|
||||
|
||||
if (eqs.empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref_vector new_core(m);
|
||||
new_core.append(core);
|
||||
|
||||
for (unsigned eq = 0; eq < eqs.size(); ++eq) {
|
||||
rational r = eqs[eq].m_value;
|
||||
expr* x = eqs[eq].m_term;
|
||||
unsigned k = eqs[eq].m_i;
|
||||
unsigned l = eqs[eq].m_j;
|
||||
|
||||
new_core[l] = m.mk_true();
|
||||
new_core[k] = m.mk_true();
|
||||
|
||||
for (unsigned i = 0; i < new_core.size(); ++i) {
|
||||
if (substitute_alias(r, x, new_core[i].get(), e)) {
|
||||
new_core[i] = e;
|
||||
}
|
||||
}
|
||||
if (abs(r) >= rational(2) && a.is_int(x)) {
|
||||
new_core[k] = m.mk_eq(a.mk_mod(x, a.mk_numeral(rational(2), true)), a.mk_numeral(rational(0), true));
|
||||
new_core[l] = a.mk_le(x, a.mk_numeral(rational(0), true));
|
||||
}
|
||||
}
|
||||
|
||||
bool inductive = n.pt().check_inductive(n.level(), new_core, uses_level);
|
||||
|
||||
IF_VERBOSE(1,
|
||||
verbose_stream() << (inductive?"":"non") << "inductive\n";
|
||||
verbose_stream() << "old\n";
|
||||
for (unsigned j = 0; j < core.size(); ++j) {
|
||||
verbose_stream() << mk_pp(core[j].get(), m) << "\n";
|
||||
}
|
||||
verbose_stream() << "new\n";
|
||||
for (unsigned j = 0; j < new_core.size(); ++j) {
|
||||
verbose_stream() << mk_pp(new_core[j].get(), m) << "\n";
|
||||
});
|
||||
|
||||
if (inductive) {
|
||||
core.reset();
|
||||
core.append(new_core);
|
||||
}
|
||||
}
|
||||
|
||||
void core_arith_inductive_generalizer::insert_bound(bool is_lower, expr* x, rational const& r, unsigned i) {
|
||||
if (r.is_neg()) {
|
||||
expr_ref e(m);
|
||||
e = a.mk_uminus(x);
|
||||
m_refs.push_back(e);
|
||||
x = e;
|
||||
is_lower = !is_lower;
|
||||
}
|
||||
|
||||
vector<term_loc_t> bound;
|
||||
bound.push_back(std::make_pair(x, i));
|
||||
if (is_lower) {
|
||||
m_lb.insert(abs(r), bound);
|
||||
}
|
||||
else {
|
||||
m_ub.insert(abs(r), bound);
|
||||
}
|
||||
}
|
||||
|
||||
void core_arith_inductive_generalizer::reset() {
|
||||
m_refs.reset();
|
||||
m_lb.reset();
|
||||
m_ub.reset();
|
||||
}
|
||||
|
||||
void core_arith_inductive_generalizer::get_eqs(expr_ref_vector const& core, svector<eq>& eqs) {
|
||||
expr* e1, *x, *y;
|
||||
expr_ref e(m);
|
||||
rational r;
|
||||
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
e = core[i];
|
||||
if (m.is_not(e, e1) && a.is_le(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
|
||||
// not (<= x r) <=> x >= r + 1
|
||||
insert_bound(true, x, r + rational(1), i);
|
||||
}
|
||||
else if (m.is_not(e, e1) && a.is_ge(e1, x, y) && a.is_numeral(y, r) && a.is_int(x)) {
|
||||
// not (>= x r) <=> x <= r - 1
|
||||
insert_bound(false, x, r - rational(1), i);
|
||||
}
|
||||
else if (a.is_le(e, x, y) && a.is_numeral(y, r)) {
|
||||
insert_bound(false, x, r, i);
|
||||
}
|
||||
else if (a.is_ge(e, x, y) && a.is_numeral(y, r)) {
|
||||
insert_bound(true, x, r, i);
|
||||
}
|
||||
}
|
||||
bounds_t::iterator it = m_lb.begin(), end = m_lb.end();
|
||||
for (; it != end; ++it) {
|
||||
rational r = it->m_key;
|
||||
vector<term_loc_t> & terms1 = it->m_value;
|
||||
vector<term_loc_t> terms2;
|
||||
if (r >= rational(2) && m_ub.find(r, terms2)) {
|
||||
for (unsigned i = 0; i < terms1.size(); ++i) {
|
||||
bool done = false;
|
||||
for (unsigned j = 0; !done && j < terms2.size(); ++j) {
|
||||
expr* t1 = terms1[i].first;
|
||||
expr* t2 = terms2[j].first;
|
||||
if (t1 == t2) {
|
||||
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
|
||||
done = true;
|
||||
}
|
||||
else {
|
||||
e = m.mk_eq(t1, t2);
|
||||
th_rewriter rw(m);
|
||||
rw(e);
|
||||
if (m.is_true(e)) {
|
||||
eqs.push_back(eq(t1, r, terms1[i].second, terms2[j].second));
|
||||
done = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool core_arith_inductive_generalizer::substitute_alias(rational const& r, expr* x, expr* e, expr_ref& result) {
|
||||
rational r2;
|
||||
expr* y, *z, *e1;
|
||||
if (m.is_not(e, e1) && substitute_alias(r, x, e1, result)) {
|
||||
result = m.mk_not(result);
|
||||
return true;
|
||||
}
|
||||
if (a.is_le(e, y, z) && a.is_numeral(z, r2)) {
|
||||
if (r == r2) {
|
||||
result = a.mk_le(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r == r2 + rational(1)) {
|
||||
result = a.mk_lt(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r == r2 - rational(1)) {
|
||||
result = a.mk_le(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x))));
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
if (a.is_ge(e, y, z) && a.is_numeral(z, r2)) {
|
||||
if (r == r2) {
|
||||
result = a.mk_ge(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r2 == r + rational(1)) {
|
||||
result = a.mk_gt(y, x);
|
||||
return true;
|
||||
}
|
||||
if (r2 == r - rational(1)) {
|
||||
result = a.mk_ge(y, a.mk_sub(x, a.mk_numeral(rational(1), a.is_int(x))));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// < F, phi, i + 1>
|
||||
// |
|
||||
// < G, psi, i >
|
||||
//
|
||||
// where:
|
||||
//
|
||||
// p(x) <- F(x,y,p,q)
|
||||
// q(x) <- G(x,y)
|
||||
//
|
||||
// Hyp:
|
||||
// Q_k(x) => phi(x) j <= k <= i
|
||||
// Q_k(x) => R_k(x) j <= k <= i + 1
|
||||
// Q_k(x) <=> Trans(Q_{k-1}) j < k <= i + 1
|
||||
// Conclusion:
|
||||
// Q_{i+1}(x) => phi(x)
|
||||
//
|
||||
class core_induction_generalizer::imp {
|
||||
context& m_ctx;
|
||||
manager& pm;
|
||||
ast_manager& m;
|
||||
|
||||
//
|
||||
// Create predicate Q_level
|
||||
//
|
||||
func_decl_ref mk_pred(unsigned level, func_decl* f) {
|
||||
func_decl_ref result(m);
|
||||
std::ostringstream name;
|
||||
name << f->get_name() << "_" << level;
|
||||
symbol sname(name.str().c_str());
|
||||
result = m.mk_func_decl(sname, f->get_arity(), f->get_domain(), f->get_range());
|
||||
return result;
|
||||
}
|
||||
|
||||
//
|
||||
// Create formula exists y . z . F[Q_{level-1}, x, y, z]
|
||||
//
|
||||
expr_ref mk_transition_rule(
|
||||
expr_ref_vector const& reps,
|
||||
unsigned level,
|
||||
datalog::rule const& rule)
|
||||
{
|
||||
expr_ref_vector conj(m), sub(m);
|
||||
expr_ref result(m);
|
||||
svector<symbol> names;
|
||||
unsigned ut_size = rule.get_uninterpreted_tail_size();
|
||||
unsigned t_size = rule.get_tail_size();
|
||||
if (0 == level && 0 < ut_size) {
|
||||
result = m.mk_false();
|
||||
return result;
|
||||
}
|
||||
app* atom = rule.get_head();
|
||||
SASSERT(atom->get_num_args() == reps.size());
|
||||
|
||||
for (unsigned i = 0; i < reps.size(); ++i) {
|
||||
expr* arg = atom->get_arg(i);
|
||||
if (is_var(arg)) {
|
||||
unsigned idx = to_var(arg)->get_idx();
|
||||
if (idx >= sub.size()) sub.resize(idx+1);
|
||||
if (sub[idx].get()) {
|
||||
conj.push_back(m.mk_eq(sub[idx].get(), reps[i]));
|
||||
}
|
||||
else {
|
||||
sub[idx] = reps[i];
|
||||
}
|
||||
}
|
||||
else {
|
||||
conj.push_back(m.mk_eq(arg, reps[i]));
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; 0 < level && i < ut_size; i++) {
|
||||
app* atom = rule.get_tail(i);
|
||||
func_decl* head = atom->get_decl();
|
||||
func_decl_ref fn = mk_pred(level-1, head);
|
||||
conj.push_back(m.mk_app(fn, atom->get_num_args(), atom->get_args()));
|
||||
}
|
||||
for (unsigned i = ut_size; i < t_size; i++) {
|
||||
conj.push_back(rule.get_tail(i));
|
||||
}
|
||||
result = mk_and(conj);
|
||||
if (!sub.empty()) {
|
||||
expr_ref tmp = result;
|
||||
var_subst(m, false)(tmp, sub.size(), sub.c_ptr(), result);
|
||||
}
|
||||
expr_free_vars fv;
|
||||
fv(result);
|
||||
fv.set_default_sort(m.mk_bool_sort());
|
||||
for (unsigned i = 0; i < fv.size(); ++i) {
|
||||
names.push_back(symbol(fv.size() - i - 1));
|
||||
}
|
||||
if (!fv.empty()) {
|
||||
fv.reverse();
|
||||
result = m.mk_exists(fv.size(), fv.c_ptr(), names.c_ptr(), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref bind_head(expr_ref_vector const& reps, expr* fml) {
|
||||
expr_ref result(m);
|
||||
expr_abstract(m, 0, reps.size(), reps.c_ptr(), fml, result);
|
||||
ptr_vector<sort> sorts;
|
||||
svector<symbol> names;
|
||||
unsigned sz = reps.size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
sorts.push_back(m.get_sort(reps[sz-i-1]));
|
||||
names.push_back(symbol(sz-i-1));
|
||||
}
|
||||
if (sz > 0) {
|
||||
result = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref_vector mk_reps(pred_transformer& pt) {
|
||||
expr_ref_vector reps(m);
|
||||
expr_ref rep(m);
|
||||
for (unsigned i = 0; i < pt.head()->get_arity(); ++i) {
|
||||
rep = m.mk_const(pm.o2n(pt.sig(i), 0));
|
||||
reps.push_back(rep);
|
||||
}
|
||||
return reps;
|
||||
}
|
||||
|
||||
//
|
||||
// extract transition axiom:
|
||||
//
|
||||
// forall x . p_lvl(x) <=> exists y z . F[p_{lvl-1}(y), q_{lvl-1}(z), x]
|
||||
//
|
||||
expr_ref mk_transition_axiom(pred_transformer& pt, unsigned level) {
|
||||
expr_ref fml(m.mk_false(), m), tr(m);
|
||||
expr_ref_vector reps = mk_reps(pt);
|
||||
ptr_vector<datalog::rule> const& rules = pt.rules();
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
tr = mk_transition_rule(reps, level, *rules[i]);
|
||||
fml = (i == 0)?tr.get():m.mk_or(fml, tr);
|
||||
}
|
||||
func_decl_ref fn = mk_pred(level, pt.head());
|
||||
fml = m.mk_iff(m.mk_app(fn, reps.size(), reps.c_ptr()), fml);
|
||||
fml = bind_head(reps, fml);
|
||||
return fml;
|
||||
}
|
||||
|
||||
//
|
||||
// Create implication:
|
||||
// Q_level(x) => phi(x)
|
||||
//
|
||||
expr_ref mk_predicate_property(unsigned level, pred_transformer& pt, expr* phi) {
|
||||
expr_ref_vector reps = mk_reps(pt);
|
||||
func_decl_ref fn = mk_pred(level, pt.head());
|
||||
expr_ref fml(m);
|
||||
fml = m.mk_implies(m.mk_app(fn, reps.size(), reps.c_ptr()), phi);
|
||||
fml = bind_head(reps, fml);
|
||||
return fml;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public:
|
||||
imp(context& ctx): m_ctx(ctx), pm(ctx.get_pdr_manager()), m(ctx.get_manager()) {}
|
||||
|
||||
//
|
||||
// not exists y . F(x,y)
|
||||
//
|
||||
expr_ref mk_blocked_transition(pred_transformer& pt, unsigned level) {
|
||||
SASSERT(level > 0);
|
||||
expr_ref fml(m.mk_true(), m);
|
||||
expr_ref_vector reps = mk_reps(pt), fmls(m);
|
||||
ptr_vector<datalog::rule> const& rules = pt.rules();
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
fmls.push_back(m.mk_not(mk_transition_rule(reps, level, *rules[i])));
|
||||
}
|
||||
fml = mk_and(fmls);
|
||||
TRACE("pdr", tout << mk_pp(fml, m) << "\n";);
|
||||
return fml;
|
||||
}
|
||||
|
||||
expr_ref mk_induction_goal(pred_transformer& pt, unsigned level, unsigned depth) {
|
||||
SASSERT(level >= depth);
|
||||
expr_ref_vector conjs(m);
|
||||
ptr_vector<pred_transformer> pts;
|
||||
unsigned_vector levels;
|
||||
// negated goal
|
||||
expr_ref phi = mk_blocked_transition(pt, level);
|
||||
conjs.push_back(m.mk_not(mk_predicate_property(level, pt, phi)));
|
||||
pts.push_back(&pt);
|
||||
levels.push_back(level);
|
||||
// Add I.H.
|
||||
for (unsigned lvl = level-depth; lvl < level; ++lvl) {
|
||||
if (lvl > 0) {
|
||||
expr_ref psi = mk_blocked_transition(pt, lvl);
|
||||
conjs.push_back(mk_predicate_property(lvl, pt, psi));
|
||||
pts.push_back(&pt);
|
||||
levels.push_back(lvl);
|
||||
}
|
||||
}
|
||||
// Transitions:
|
||||
for (unsigned qhead = 0; qhead < pts.size(); ++qhead) {
|
||||
pred_transformer& qt = *pts[qhead];
|
||||
unsigned lvl = levels[qhead];
|
||||
|
||||
// Add transition definition and properties at level.
|
||||
conjs.push_back(mk_transition_axiom(qt, lvl));
|
||||
conjs.push_back(mk_predicate_property(lvl, qt, qt.get_formulas(lvl, true)));
|
||||
|
||||
// Enqueue additional hypotheses
|
||||
ptr_vector<datalog::rule> const& rules = qt.rules();
|
||||
if (lvl + depth < level || lvl == 0) {
|
||||
continue;
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
datalog::rule& r = *rules[i];
|
||||
unsigned ut_size = r.get_uninterpreted_tail_size();
|
||||
for (unsigned j = 0; j < ut_size; ++j) {
|
||||
func_decl* f = r.get_tail(j)->get_decl();
|
||||
pred_transformer* rt = m_ctx.get_pred_transformers().find(f);
|
||||
bool found = false;
|
||||
for (unsigned k = 0; !found && k < levels.size(); ++k) {
|
||||
found = (rt == pts[k] && levels[k] + 1 == lvl);
|
||||
}
|
||||
if (!found) {
|
||||
levels.push_back(lvl-1);
|
||||
pts.push_back(rt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref result = mk_and(conjs);
|
||||
TRACE("pdr", tout << mk_pp(result, m) << "\n";);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Instantiate Peano induction schema.
|
||||
//
|
||||
void core_induction_generalizer::operator()(model_node& n, expr_ref_vector& core, bool& uses_level) {
|
||||
model_node* p = n.parent();
|
||||
if (p == nullptr) {
|
||||
return;
|
||||
}
|
||||
unsigned depth = 2;
|
||||
imp imp(m_ctx);
|
||||
ast_manager& m = core.get_manager();
|
||||
expr_ref goal = imp.mk_induction_goal(p->pt(), p->level(), depth);
|
||||
smt::kernel ctx(m, m_ctx.get_fparams(), m_ctx.get_params().p);
|
||||
ctx.assert_expr(goal);
|
||||
lbool r = ctx.check();
|
||||
TRACE("pdr", tout << r << "\n";
|
||||
for (unsigned i = 0; i < core.size(); ++i) {
|
||||
tout << mk_pp(core[i].get(), m) << "\n";
|
||||
});
|
||||
if (r == l_false) {
|
||||
core.reset();
|
||||
expr_ref phi = imp.mk_blocked_transition(p->pt(), p->level());
|
||||
core.push_back(m.mk_not(phi));
|
||||
uses_level = true;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
@ -1,110 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_generalizers.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Generalizer plugins.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-22.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_GENERALIZERS_H_
|
||||
#define PDR_GENERALIZERS_H_
|
||||
|
||||
#include "muz/pdr/pdr_context.h"
|
||||
#include "muz/pdr/pdr_closure.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class core_bool_inductive_generalizer : public core_generalizer {
|
||||
unsigned m_failure_limit;
|
||||
public:
|
||||
core_bool_inductive_generalizer(context& ctx, unsigned failure_limit) : core_generalizer(ctx), m_failure_limit(failure_limit) {}
|
||||
~core_bool_inductive_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class r_map : public map<rational, T, rational::hash_proc, rational::eq_proc> {
|
||||
};
|
||||
|
||||
class core_arith_inductive_generalizer : public core_generalizer {
|
||||
typedef std::pair<expr*, unsigned> term_loc_t;
|
||||
typedef r_map<vector<term_loc_t> > bounds_t;
|
||||
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
expr_ref_vector m_refs;
|
||||
bounds_t m_lb;
|
||||
bounds_t m_ub;
|
||||
|
||||
struct eq {
|
||||
expr* m_term;
|
||||
rational m_value;
|
||||
unsigned m_i;
|
||||
unsigned m_j;
|
||||
eq(expr* t, rational const& r, unsigned i, unsigned j): m_term(t), m_value(r), m_i(i), m_j(j) {}
|
||||
};
|
||||
void reset();
|
||||
void insert_bound(bool is_lower, expr* x, rational const& r, unsigned i);
|
||||
void get_eqs(expr_ref_vector const& core, svector<eq>& eqs);
|
||||
bool substitute_alias(rational const&r, expr* x, expr* e, expr_ref& result);
|
||||
public:
|
||||
core_arith_inductive_generalizer(context& ctx);
|
||||
~core_arith_inductive_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
|
||||
class core_farkas_generalizer : public core_generalizer {
|
||||
farkas_learner m_farkas_learner;
|
||||
public:
|
||||
core_farkas_generalizer(context& ctx, ast_manager& m, smt_params& p);
|
||||
~core_farkas_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
void collect_statistics(statistics& st) const override;
|
||||
};
|
||||
|
||||
|
||||
class core_convex_hull_generalizer : public core_generalizer {
|
||||
ast_manager& m;
|
||||
obj_map<expr, expr*> m_models;
|
||||
bool m_is_closure;
|
||||
void method1(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
|
||||
void method3(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores);
|
||||
bool strengthen_consequences(model_node& n, expr_ref_vector& As, expr* B);
|
||||
bool is_unsat(expr_ref_vector const& As, expr* B);
|
||||
public:
|
||||
core_convex_hull_generalizer(context& ctx, bool is_closure);
|
||||
~core_convex_hull_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) override;
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
|
||||
class core_multi_generalizer : public core_generalizer {
|
||||
core_bool_inductive_generalizer m_gen;
|
||||
public:
|
||||
core_multi_generalizer(context& ctx, unsigned max_failures): core_generalizer(ctx), m_gen(ctx, max_failures) {}
|
||||
~core_multi_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
void operator()(model_node& n, expr_ref_vector const& core, bool uses_level, cores& new_cores) override;
|
||||
};
|
||||
|
||||
class core_induction_generalizer : public core_generalizer {
|
||||
class imp;
|
||||
public:
|
||||
core_induction_generalizer(context& ctx): core_generalizer(ctx) {}
|
||||
~core_induction_generalizer() override {}
|
||||
void operator()(model_node& n, expr_ref_vector& core, bool& uses_level) override;
|
||||
};
|
||||
};
|
||||
#endif
|
|
@ -1,321 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A manager class for PDR, taking care of creating of AST
|
||||
objects and conversions between them.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/has_free_vars.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "model/model2expr.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
#include "tactic/model_converter.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class collect_decls_proc {
|
||||
func_decl_set& m_bound_decls;
|
||||
func_decl_set& m_aux_decls;
|
||||
public:
|
||||
collect_decls_proc(func_decl_set& bound_decls, func_decl_set& aux_decls):
|
||||
m_bound_decls(bound_decls),
|
||||
m_aux_decls(aux_decls) {
|
||||
}
|
||||
|
||||
void operator()(app* a) {
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
func_decl* f = a->get_decl();
|
||||
if (!m_bound_decls.contains(f)) {
|
||||
m_aux_decls.insert(f);
|
||||
}
|
||||
}
|
||||
}
|
||||
void operator()(var* v) {}
|
||||
void operator()(quantifier* q) {}
|
||||
};
|
||||
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
|
||||
|
||||
expr_ref inductive_property::fixup_clause(expr* fml) const {
|
||||
expr_ref_vector disjs(m);
|
||||
flatten_or(fml, disjs);
|
||||
expr_ref result(m);
|
||||
bool_rewriter(m).mk_or(disjs.size(), disjs.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref inductive_property::fixup_clauses(expr* fml) const {
|
||||
expr_ref_vector conjs(m);
|
||||
expr_ref result(m);
|
||||
flatten_and(fml, conjs);
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
conjs[i] = fixup_clause(conjs[i].get());
|
||||
}
|
||||
bool_rewriter(m).mk_and(conjs.size(), conjs.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string inductive_property::to_string() const {
|
||||
std::stringstream stm;
|
||||
model_ref md;
|
||||
expr_ref result(m);
|
||||
to_model(md);
|
||||
model_smt2_pp(stm, m, *md.get(), 0);
|
||||
return stm.str();
|
||||
}
|
||||
|
||||
void inductive_property::to_model(model_ref& md) const {
|
||||
md = alloc(model, m);
|
||||
vector<relation_info> const& rs = m_relation_info;
|
||||
expr_ref_vector conjs(m);
|
||||
for (unsigned i = 0; i < rs.size(); ++i) {
|
||||
relation_info ri(rs[i]);
|
||||
func_decl * pred = ri.m_pred;
|
||||
expr_ref prop = fixup_clauses(ri.m_body);
|
||||
func_decl_ref_vector const& sig = ri.m_vars;
|
||||
expr_ref q(m);
|
||||
expr_ref_vector sig_vars(m);
|
||||
for (unsigned j = 0; j < sig.size(); ++j) {
|
||||
sig_vars.push_back(m.mk_const(sig[sig.size()-j-1]));
|
||||
}
|
||||
expr_abstract(m, 0, sig_vars.size(), sig_vars.c_ptr(), prop, q);
|
||||
if (sig.empty()) {
|
||||
md->register_decl(pred, q);
|
||||
}
|
||||
else {
|
||||
func_interp* fi = alloc(func_interp, m, sig.size());
|
||||
fi->set_else(q);
|
||||
md->register_decl(pred, fi);
|
||||
}
|
||||
}
|
||||
TRACE("pdr", model_smt2_pp(tout, m, *md, 0););
|
||||
apply(const_cast<model_converter_ref&>(m_mc), md);
|
||||
}
|
||||
|
||||
expr_ref inductive_property::to_expr() const {
|
||||
model_ref md;
|
||||
expr_ref result(m);
|
||||
to_model(md);
|
||||
model2expr(md, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
void inductive_property::display(datalog::rule_manager& rm, ptr_vector<datalog::rule> const& rules, std::ostream& out) const {
|
||||
func_decl_set bound_decls, aux_decls;
|
||||
collect_decls_proc collect_decls(bound_decls, aux_decls);
|
||||
|
||||
for (unsigned i = 0; i < m_relation_info.size(); ++i) {
|
||||
bound_decls.insert(m_relation_info[i].m_pred);
|
||||
func_decl_ref_vector const& sig = m_relation_info[i].m_vars;
|
||||
for (unsigned j = 0; j < sig.size(); ++j) {
|
||||
bound_decls.insert(sig[j]);
|
||||
}
|
||||
for_each_expr(collect_decls, m_relation_info[i].m_body);
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
bound_decls.insert(rules[i]->get_decl());
|
||||
}
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
unsigned u_sz = rules[i]->get_uninterpreted_tail_size();
|
||||
unsigned t_sz = rules[i]->get_tail_size();
|
||||
for (unsigned j = u_sz; j < t_sz; ++j) {
|
||||
for_each_expr(collect_decls, rules[i]->get_tail(j));
|
||||
}
|
||||
}
|
||||
smt2_pp_environment_dbg env(m);
|
||||
func_decl_set::iterator it = aux_decls.begin(), end = aux_decls.end();
|
||||
for (; it != end; ++it) {
|
||||
func_decl* f = *it;
|
||||
ast_smt2_pp(out, f, env);
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
out << to_string() << "\n";
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
out << "(push)\n";
|
||||
out << "(assert (not\n";
|
||||
rm.display_smt2(*rules[i], out);
|
||||
out << "))\n";
|
||||
out << "(check-sat)\n";
|
||||
out << "(pop)\n";
|
||||
}
|
||||
}
|
||||
|
||||
manager::manager(smt_params& fparams, unsigned max_num_contexts, ast_manager& manager) :
|
||||
m(manager),
|
||||
m_fparams(fparams),
|
||||
m_brwr(m),
|
||||
m_mux(m),
|
||||
m_background(m.mk_true(), m),
|
||||
m_contexts(fparams, max_num_contexts, m),
|
||||
m_next_unique_num(0)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void manager::add_new_state(func_decl * s) {
|
||||
SASSERT(s->get_arity()==0); //we currently don't support non-constant states
|
||||
decl_vector vect;
|
||||
SASSERT(o_index(0)==1); //we assume this in the number of retrieved symbols
|
||||
m_mux.create_tuple(s, s->get_arity(), s->get_domain(), s->get_range(), 2, vect);
|
||||
m_o0_preds.push_back(vect[o_index(0)]);
|
||||
}
|
||||
|
||||
func_decl * manager::get_o_pred(func_decl* s, unsigned idx)
|
||||
{
|
||||
func_decl * res = m_mux.try_get_by_prefix(s, o_index(idx));
|
||||
if(res) { return res; }
|
||||
add_new_state(s);
|
||||
res = m_mux.try_get_by_prefix(s, o_index(idx));
|
||||
SASSERT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
func_decl * manager::get_n_pred(func_decl* s)
|
||||
{
|
||||
func_decl * res = m_mux.try_get_by_prefix(s, n_index());
|
||||
if(res) { return res; }
|
||||
add_new_state(s);
|
||||
res = m_mux.try_get_by_prefix(s, n_index());
|
||||
SASSERT(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
void manager::mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res) {
|
||||
m_brwr.mk_and(mdl.size(), mdl.c_ptr(), res);
|
||||
}
|
||||
|
||||
void manager::mk_core_into_cube(const expr_ref_vector & core, expr_ref & res) {
|
||||
m_brwr.mk_and(core.size(), core.c_ptr(), res);
|
||||
}
|
||||
|
||||
void manager::mk_cube_into_lemma(expr * cube, expr_ref & res) {
|
||||
m_brwr.mk_not(cube, res);
|
||||
}
|
||||
|
||||
void manager::mk_lemma_into_cube(expr * lemma, expr_ref & res) {
|
||||
m_brwr.mk_not(lemma, res);
|
||||
}
|
||||
|
||||
expr_ref manager::mk_and(unsigned sz, expr* const* exprs) {
|
||||
expr_ref result(m);
|
||||
m_brwr.mk_and(sz, exprs, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref manager::mk_or(unsigned sz, expr* const* exprs) {
|
||||
expr_ref result(m);
|
||||
m_brwr.mk_or(sz, exprs, result);
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref manager::mk_not_and(expr_ref_vector const& conjs) {
|
||||
expr_ref result(m), e(m);
|
||||
expr_ref_vector es(conjs);
|
||||
flatten_and(es);
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
m_brwr.mk_not(es[i].get(), e);
|
||||
es[i] = e;
|
||||
}
|
||||
m_brwr.mk_or(es.size(), es.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
void manager::get_or(expr* e, expr_ref_vector& result) {
|
||||
result.push_back(e);
|
||||
for (unsigned i = 0; i < result.size(); ) {
|
||||
e = result[i].get();
|
||||
if (m.is_or(e)) {
|
||||
result.append(to_app(e)->get_num_args(), to_app(e)->get_args());
|
||||
result[i] = result.back();
|
||||
result.pop_back();
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool manager::try_get_state_and_value_from_atom(expr * atom0, app *& state, app_ref& value)
|
||||
{
|
||||
if(!is_app(atom0)) {
|
||||
return false;
|
||||
}
|
||||
app * atom = to_app(atom0);
|
||||
expr * arg1;
|
||||
expr * arg2;
|
||||
app * candidate_state;
|
||||
app_ref candidate_value(m);
|
||||
if(m.is_not(atom, arg1)) {
|
||||
if(!is_app(arg1)) {
|
||||
return false;
|
||||
}
|
||||
candidate_state = to_app(arg1);
|
||||
candidate_value = m.mk_false();
|
||||
}
|
||||
else if(m.is_eq(atom, arg1, arg2)) {
|
||||
if(!is_app(arg1) || !is_app(arg2)) {
|
||||
return false;
|
||||
}
|
||||
if(!m_mux.is_muxed(to_app(arg1)->get_decl())) {
|
||||
std::swap(arg1, arg2);
|
||||
}
|
||||
candidate_state = to_app(arg1);
|
||||
candidate_value = to_app(arg2);
|
||||
}
|
||||
else {
|
||||
candidate_state = atom;
|
||||
candidate_value = m.mk_true();
|
||||
}
|
||||
if(!m_mux.is_muxed(candidate_state->get_decl())) {
|
||||
return false;
|
||||
}
|
||||
state = candidate_state;
|
||||
value = candidate_value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool manager::try_get_state_decl_from_atom(expr * atom, func_decl *& state) {
|
||||
app_ref dummy_value_holder(m);
|
||||
app * s;
|
||||
if(try_get_state_and_value_from_atom(atom, s, dummy_value_holder)) {
|
||||
state = s->get_decl();
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool manager::implication_surely_holds(expr * lhs, expr * rhs, expr * bg) {
|
||||
smt::kernel sctx(m, get_fparams());
|
||||
if(bg) {
|
||||
sctx.assert_expr(bg);
|
||||
}
|
||||
sctx.assert_expr(lhs);
|
||||
expr_ref neg_rhs(m.mk_not(rhs),m);
|
||||
sctx.assert_expr(neg_rhs);
|
||||
lbool smt_res = sctx.check();
|
||||
return smt_res==l_false;
|
||||
}
|
||||
|
||||
};
|
|
@ -1,304 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A manager class for PDR, taking care of creating of AST
|
||||
objects and conversions between them.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-25.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_MANAGER_H_
|
||||
#define PDR_MANAGER_H_
|
||||
|
||||
#include <utility>
|
||||
#include <map>
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "ast/expr_substitution.h"
|
||||
#include "util/map.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "muz/pdr/pdr_sym_mux.h"
|
||||
#include "muz/pdr/pdr_farkas_learner.h"
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
#include "muz/base/dl_rule.h"
|
||||
|
||||
|
||||
namespace smt {
|
||||
class context;
|
||||
}
|
||||
|
||||
namespace pdr {
|
||||
|
||||
struct relation_info {
|
||||
func_decl_ref m_pred;
|
||||
func_decl_ref_vector m_vars;
|
||||
expr_ref m_body;
|
||||
relation_info(ast_manager& m, func_decl* pred, ptr_vector<func_decl> const& vars, expr* b):
|
||||
m_pred(pred, m), m_vars(m, vars.size(), vars.c_ptr()), m_body(b, m) {}
|
||||
relation_info(relation_info const& other): m_pred(other.m_pred), m_vars(other.m_vars), m_body(other.m_body) {}
|
||||
};
|
||||
|
||||
class unknown_exception {};
|
||||
|
||||
class inductive_property {
|
||||
ast_manager& m;
|
||||
model_converter_ref m_mc;
|
||||
vector<relation_info> m_relation_info;
|
||||
expr_ref fixup_clauses(expr* property) const;
|
||||
expr_ref fixup_clause(expr* clause) const;
|
||||
public:
|
||||
inductive_property(ast_manager& m, model_converter_ref& mc, vector<relation_info> const& relations):
|
||||
m(m),
|
||||
m_mc(mc),
|
||||
m_relation_info(relations) {}
|
||||
|
||||
std::string to_string() const;
|
||||
|
||||
expr_ref to_expr() const;
|
||||
|
||||
void to_model(model_ref& md) const;
|
||||
|
||||
void display(datalog::rule_manager& rm, ptr_vector<datalog::rule> const& rules, std::ostream& out) const;
|
||||
};
|
||||
|
||||
class manager
|
||||
{
|
||||
ast_manager& m;
|
||||
smt_params& m_fparams;
|
||||
|
||||
mutable bool_rewriter m_brwr;
|
||||
|
||||
sym_mux m_mux;
|
||||
expr_ref m_background;
|
||||
decl_vector m_o0_preds;
|
||||
pdr::smt_context_manager m_contexts;
|
||||
|
||||
/** whenever we need an unique number, we get this one and increase */
|
||||
unsigned m_next_unique_num;
|
||||
|
||||
|
||||
unsigned n_index() const { return 0; }
|
||||
unsigned o_index(unsigned i) const { return i+1; }
|
||||
|
||||
void add_new_state(func_decl * s);
|
||||
|
||||
public:
|
||||
manager(smt_params& fparams, unsigned max_num_contexts, ast_manager & manager);
|
||||
|
||||
ast_manager& get_manager() const { return m; }
|
||||
smt_params& get_fparams() const { return m_fparams; }
|
||||
bool_rewriter& get_brwr() const { return m_brwr; }
|
||||
|
||||
expr_ref mk_and(unsigned sz, expr* const* exprs);
|
||||
expr_ref mk_and(expr_ref_vector const& exprs) {
|
||||
return mk_and(exprs.size(), exprs.c_ptr());
|
||||
}
|
||||
expr_ref mk_and(expr* a, expr* b) {
|
||||
expr* args[2] = { a, b };
|
||||
return mk_and(2, args);
|
||||
}
|
||||
expr_ref mk_or(unsigned sz, expr* const* exprs);
|
||||
expr_ref mk_or(expr_ref_vector const& exprs) {
|
||||
return mk_or(exprs.size(), exprs.c_ptr());
|
||||
}
|
||||
|
||||
expr_ref mk_not_and(expr_ref_vector const& exprs);
|
||||
|
||||
void get_or(expr* e, expr_ref_vector& result);
|
||||
|
||||
//"o" predicates stand for the old states and "n" for the new states
|
||||
func_decl * get_o_pred(func_decl * s, unsigned idx);
|
||||
func_decl * get_n_pred(func_decl * s);
|
||||
|
||||
/**
|
||||
Marks symbol as non-model which means it will not appear in models collected by
|
||||
get_state_cube_from_model function.
|
||||
This is to take care of auxiliary symbols introduced by the disjunction relations
|
||||
to relativize lemmas coming from disjuncts.
|
||||
*/
|
||||
void mark_as_non_model(func_decl * p) {
|
||||
m_mux.mark_as_non_model(p);
|
||||
}
|
||||
|
||||
|
||||
func_decl * const * begin_o0_preds() const { return m_o0_preds.begin(); }
|
||||
func_decl * const * end_o0_preds() const { return m_o0_preds.end(); }
|
||||
|
||||
bool is_state_pred(func_decl * p) const { return m_mux.is_muxed(p); }
|
||||
func_decl * to_o0(func_decl * p) { return m_mux.conv(m_mux.get_primary(p), 0, o_index(0)); }
|
||||
|
||||
bool is_o(func_decl * p, unsigned idx) const {
|
||||
return m_mux.has_index(p, o_index(idx));
|
||||
}
|
||||
bool is_o(expr* e, unsigned idx) const {
|
||||
return is_app(e) && is_o(to_app(e)->get_decl(), idx);
|
||||
}
|
||||
bool is_o(func_decl * p) const {
|
||||
unsigned idx;
|
||||
return m_mux.try_get_index(p, idx) && idx!=n_index();
|
||||
}
|
||||
bool is_o(expr* e) const {
|
||||
return is_app(e) && is_o(to_app(e)->get_decl());
|
||||
}
|
||||
bool is_n(func_decl * p) const {
|
||||
return m_mux.has_index(p, n_index());
|
||||
}
|
||||
bool is_n(expr* e) const {
|
||||
return is_app(e) && is_n(to_app(e)->get_decl());
|
||||
}
|
||||
|
||||
/** true if p should not appead in models propagates into child relations */
|
||||
bool is_non_model_sym(func_decl * p) const
|
||||
{ return m_mux.is_non_model_sym(p); }
|
||||
|
||||
|
||||
/** true if f doesn't contain any n predicates */
|
||||
bool is_o_formula(expr * f) const {
|
||||
return !m_mux.contains(f, n_index());
|
||||
}
|
||||
|
||||
/** true if f contains only o state preds of index o_idx */
|
||||
bool is_o_formula(expr * f, unsigned o_idx) const {
|
||||
return m_mux.is_homogenous_formula(f, o_index(o_idx));
|
||||
}
|
||||
/** true if f doesn't contain any o predicates */
|
||||
bool is_n_formula(expr * f) const {
|
||||
return m_mux.is_homogenous_formula(f, n_index());
|
||||
}
|
||||
|
||||
func_decl * o2n(func_decl * p, unsigned o_idx) {
|
||||
return m_mux.conv(p, o_index(o_idx), n_index());
|
||||
}
|
||||
func_decl * o2o(func_decl * p, unsigned src_idx, unsigned tgt_idx) {
|
||||
return m_mux.conv(p, o_index(src_idx), o_index(tgt_idx));
|
||||
}
|
||||
func_decl * n2o(func_decl * p, unsigned o_idx) {
|
||||
return m_mux.conv(p, n_index(), o_index(o_idx));
|
||||
}
|
||||
|
||||
void formula_o2n(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true)
|
||||
{ m_mux.conv_formula(f, o_index(o_idx), n_index(), result, homogenous); }
|
||||
|
||||
void formula_n2o(expr * f, expr_ref & result, unsigned o_idx, bool homogenous=true)
|
||||
{ m_mux.conv_formula(f, n_index(), o_index(o_idx), result, homogenous); }
|
||||
|
||||
void formula_n2o(unsigned o_idx, bool homogenous, expr_ref & result)
|
||||
{ m_mux.conv_formula(result.get(), n_index(), o_index(o_idx), result, homogenous); }
|
||||
|
||||
void formula_o2o(expr * src, expr_ref & tgt, unsigned src_idx, unsigned tgt_idx, bool homogenous=true)
|
||||
{ m_mux.conv_formula(src, o_index(src_idx), o_index(tgt_idx), tgt, homogenous); }
|
||||
|
||||
/**
|
||||
Return true if all state symbols which e contains are of one kind (either "n" or one of "o").
|
||||
*/
|
||||
bool is_homogenous_formula(expr * e) const {
|
||||
return m_mux.is_homogenous_formula(e);
|
||||
}
|
||||
|
||||
/**
|
||||
Collect indices used in expression.
|
||||
*/
|
||||
void collect_indices(expr* e, unsigned_vector& indices) const {
|
||||
m_mux.collect_indices(e, indices);
|
||||
}
|
||||
|
||||
/**
|
||||
Collect used variables of each index.
|
||||
*/
|
||||
void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const {
|
||||
m_mux.collect_variables(e, vars);
|
||||
}
|
||||
|
||||
/**
|
||||
Return true iff both s1 and s2 are either "n" or "o" of the same index.
|
||||
If one (or both) of them are not state symbol, return false.
|
||||
*/
|
||||
bool have_different_state_kinds(func_decl * s1, func_decl * s2) const {
|
||||
unsigned i1, i2;
|
||||
return m_mux.try_get_index(s1, i1) && m_mux.try_get_index(s2, i2) && i1!=i2;
|
||||
}
|
||||
|
||||
/**
|
||||
Increase indexes of state symbols in formula by dist.
|
||||
The 'N' index becomes 'O' index with number dist-1.
|
||||
*/
|
||||
void formula_shift(expr * src, expr_ref & tgt, unsigned dist) {
|
||||
SASSERT(n_index()==0);
|
||||
SASSERT(o_index(0)==1);
|
||||
m_mux.shift_formula(src, dist, tgt);
|
||||
}
|
||||
|
||||
void mk_model_into_cube(const expr_ref_vector & mdl, expr_ref & res);
|
||||
void mk_core_into_cube(const expr_ref_vector & core, expr_ref & res);
|
||||
void mk_cube_into_lemma(expr * cube, expr_ref & res);
|
||||
void mk_lemma_into_cube(expr * lemma, expr_ref & res);
|
||||
|
||||
/**
|
||||
Remove from vec all atoms that do not have an "o" state.
|
||||
The order of elements in vec may change.
|
||||
An assumption is that atoms having "o" state of given index
|
||||
do not have "o" states of other indexes or "n" states.
|
||||
*/
|
||||
void filter_o_atoms(expr_ref_vector& vec, unsigned o_idx) const
|
||||
{ m_mux.filter_idx(vec, o_index(o_idx)); }
|
||||
void filter_n_atoms(expr_ref_vector& vec) const
|
||||
{ m_mux.filter_idx(vec, n_index()); }
|
||||
|
||||
/**
|
||||
Partition literals into o_lits and others.
|
||||
*/
|
||||
void partition_o_atoms(expr_ref_vector const& lits,
|
||||
expr_ref_vector& o_lits,
|
||||
expr_ref_vector& other,
|
||||
unsigned o_idx) const {
|
||||
m_mux.partition_o_idx(lits, o_lits, other, o_index(o_idx));
|
||||
}
|
||||
|
||||
void filter_out_non_model_atoms(expr_ref_vector& vec) const
|
||||
{ m_mux.filter_non_model_lits(vec); }
|
||||
|
||||
bool try_get_state_and_value_from_atom(expr * atom, app *& state, app_ref& value);
|
||||
bool try_get_state_decl_from_atom(expr * atom, func_decl *& state);
|
||||
|
||||
|
||||
std::string pp_model(const model_core & mdl) const
|
||||
{ return m_mux.pp_model(mdl); }
|
||||
|
||||
|
||||
void set_background(expr* b) { m_background = b; }
|
||||
|
||||
expr* get_background() const { return m_background; }
|
||||
|
||||
|
||||
/**
|
||||
Return true if we can show that lhs => rhs. The function can have false negatives
|
||||
(i.e. when smt::context returns unknown), but no false positives.
|
||||
|
||||
bg is background knowledge and can be null
|
||||
*/
|
||||
bool implication_surely_holds(expr * lhs, expr * rhs, expr * bg=nullptr);
|
||||
|
||||
unsigned get_unique_num() { return m_next_unique_num++; }
|
||||
|
||||
pdr::smt_context* mk_fresh() { return m_contexts.mk_fresh(); }
|
||||
|
||||
void collect_statistics(statistics& st) const { m_contexts.collect_statistics(st); }
|
||||
|
||||
void reset_statistics() { m_contexts.reset_statistics(); }
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,459 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
prop_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SMT solver abstraction for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "model/model.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "muz/pdr/pdr_prop_solver.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "model/model_pp.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "ast/datatype_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
#include "muz/pdr/pdr_farkas_learner.h"
|
||||
#include "ast/ast_smt2_pp.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
|
||||
//
|
||||
// Auxiliary structure to introduce propositional names for assumptions that are not
|
||||
// propositional. It is to work with the smt::context's restriction
|
||||
// that assumptions be propositional literals.
|
||||
//
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class prop_solver::safe_assumptions {
|
||||
prop_solver& s;
|
||||
ast_manager& m;
|
||||
expr_ref_vector m_atoms;
|
||||
expr_ref_vector m_assumptions;
|
||||
obj_map<app,expr *> m_proxies2expr;
|
||||
obj_map<expr, app*> m_expr2proxies;
|
||||
unsigned m_num_proxies;
|
||||
|
||||
app * mk_proxy(expr* literal) {
|
||||
app* res;
|
||||
SASSERT(!is_var(literal)); //it doesn't make sense to introduce names to variables
|
||||
if (m_expr2proxies.find(literal, res)) {
|
||||
return res;
|
||||
}
|
||||
SASSERT(s.m_proxies.size() >= m_num_proxies);
|
||||
if (m_num_proxies == s.m_proxies.size()) {
|
||||
std::stringstream name;
|
||||
name << "pdr_proxy_" << s.m_proxies.size();
|
||||
res = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
|
||||
s.m_proxies.push_back(res);
|
||||
s.m_aux_symbols.insert(res->get_decl());
|
||||
}
|
||||
else {
|
||||
res = s.m_proxies[m_num_proxies].get();
|
||||
}
|
||||
++m_num_proxies;
|
||||
m_expr2proxies.insert(literal, res);
|
||||
m_proxies2expr.insert(res, literal);
|
||||
expr_ref implies(m.mk_or(m.mk_not(res), literal), m);
|
||||
s.m_ctx->assert_expr(implies);
|
||||
m_assumptions.push_back(implies);
|
||||
TRACE("pdr_verbose", tout << "name asserted " << mk_pp(implies, m) << "\n";);
|
||||
return res;
|
||||
}
|
||||
|
||||
void mk_safe(expr_ref_vector& conjs) {
|
||||
flatten_and(conjs);
|
||||
expand_literals(conjs);
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr * lit = conjs[i].get();
|
||||
expr * lit_core = lit;
|
||||
m.is_not(lit, lit_core);
|
||||
SASSERT(!m.is_true(lit));
|
||||
if (!is_uninterp(lit_core) || to_app(lit_core)->get_num_args() != 0) {
|
||||
conjs[i] = mk_proxy(lit);
|
||||
}
|
||||
}
|
||||
m_assumptions.append(conjs);
|
||||
}
|
||||
|
||||
expr* apply_accessor(
|
||||
ptr_vector<func_decl> const& acc,
|
||||
unsigned j,
|
||||
func_decl* f,
|
||||
expr* c) {
|
||||
if (is_app(c) && to_app(c)->get_decl() == f) {
|
||||
return to_app(c)->get_arg(j);
|
||||
}
|
||||
else {
|
||||
return m.mk_app(acc[j], c);
|
||||
}
|
||||
}
|
||||
|
||||
void expand_literals(expr_ref_vector& conjs) {
|
||||
arith_util arith(m);
|
||||
datatype_util dt(m);
|
||||
bv_util bv(m);
|
||||
expr* e1, *e2, *c, *val;
|
||||
rational r;
|
||||
unsigned bv_size;
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "begin expand\n";
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
tout << mk_pp(conjs[i].get(), m) << "\n";
|
||||
});
|
||||
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
expr* e = conjs[i].get();
|
||||
if (m.is_eq(e, e1, e2) && arith.is_int_real(e1)) {
|
||||
conjs[i] = arith.mk_le(e1,e2);
|
||||
if (i+1 == conjs.size()) {
|
||||
conjs.push_back(arith.mk_ge(e1, e2));
|
||||
}
|
||||
else {
|
||||
conjs.push_back(conjs[i+1].get());
|
||||
conjs[i+1] = arith.mk_ge(e1, e2);
|
||||
}
|
||||
++i;
|
||||
}
|
||||
else if ((m.is_eq(e, c, val) && is_app(val) && dt.is_constructor(to_app(val))) ||
|
||||
(m.is_eq(e, val, c) && is_app(val) && dt.is_constructor(to_app(val)))){
|
||||
func_decl* f = to_app(val)->get_decl();
|
||||
func_decl* r = dt.get_constructor_is(f);
|
||||
conjs[i] = m.mk_app(r, c);
|
||||
ptr_vector<func_decl> const& acc = *dt.get_constructor_accessors(f);
|
||||
for (unsigned j = 0; j < acc.size(); ++j) {
|
||||
conjs.push_back(m.mk_eq(apply_accessor(acc, j, f, c), to_app(val)->get_arg(j)));
|
||||
}
|
||||
}
|
||||
else if ((m.is_eq(e, c, val) && bv.is_numeral(val, r, bv_size)) ||
|
||||
(m.is_eq(e, val, c) && bv.is_numeral(val, r, bv_size))) {
|
||||
rational two(2);
|
||||
for (unsigned j = 0; j < bv_size; ++j) {
|
||||
parameter p(j);
|
||||
//expr* e = m.mk_app(bv.get_family_id(), OP_BIT2BOOL, 1, &p, 1, &c);
|
||||
expr* e = m.mk_eq(m.mk_app(bv.get_family_id(), OP_BIT1), bv.mk_extract(j, j, c));
|
||||
if ((r % two).is_zero()) {
|
||||
e = m.mk_not(e);
|
||||
}
|
||||
r = div(r, two);
|
||||
if (j == 0) {
|
||||
conjs[i] = e;
|
||||
}
|
||||
else {
|
||||
conjs.push_back(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
TRACE("pdr",
|
||||
tout << "end expand\n";
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
tout << mk_pp(conjs[i].get(), m) << "\n";
|
||||
});
|
||||
}
|
||||
|
||||
public:
|
||||
safe_assumptions(prop_solver& s, expr_ref_vector const& assumptions):
|
||||
s(s), m(s.m), m_atoms(assumptions), m_assumptions(m), m_num_proxies(0) {
|
||||
mk_safe(m_atoms);
|
||||
}
|
||||
|
||||
~safe_assumptions() {
|
||||
}
|
||||
|
||||
expr_ref_vector const& atoms() const { return m_atoms; }
|
||||
|
||||
unsigned assumptions_size() const { return m_assumptions.size(); }
|
||||
|
||||
expr* assumptions(unsigned i) const { return m_assumptions[i]; }
|
||||
|
||||
void undo_proxies(expr_ref_vector& es) {
|
||||
expr_ref e(m);
|
||||
expr* r;
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
e = es[i].get();
|
||||
if (is_app(e) && m_proxies2expr.find(to_app(e), r)) {
|
||||
es[i] = r;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void elim_proxies(expr_ref_vector& es) {
|
||||
expr_substitution sub(m, false, m.proofs_enabled());
|
||||
proof_ref pr(m);
|
||||
if (m.proofs_enabled()) {
|
||||
pr = m.mk_asserted(m.mk_true());
|
||||
}
|
||||
obj_map<app,expr*>::iterator it = m_proxies2expr.begin(), end = m_proxies2expr.end();
|
||||
for (; it != end; ++it) {
|
||||
sub.insert(it->m_key, m.mk_true(), pr);
|
||||
}
|
||||
scoped_ptr<expr_replacer> rep = mk_default_expr_replacer(m);
|
||||
rep->set_substitution(&sub);
|
||||
replace_proxies(*rep, es);
|
||||
}
|
||||
private:
|
||||
|
||||
void replace_proxies(expr_replacer& rep, expr_ref_vector& es) {
|
||||
expr_ref e(m);
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
e = es[i].get();
|
||||
rep(e);
|
||||
es[i] = e;
|
||||
if (m.is_true(e)) {
|
||||
es[i] = es.back();
|
||||
es.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
prop_solver::prop_solver(manager& pm, symbol const& name) :
|
||||
m_fparams(pm.get_fparams()),
|
||||
m(pm.get_manager()),
|
||||
m_pm(pm),
|
||||
m_name(name),
|
||||
m_ctx(pm.mk_fresh()),
|
||||
m_pos_level_atoms(m),
|
||||
m_neg_level_atoms(m),
|
||||
m_proxies(m),
|
||||
m_core(nullptr),
|
||||
m_model(nullptr),
|
||||
m_consequences(nullptr),
|
||||
m_subset_based_core(false),
|
||||
m_use_farkas(false),
|
||||
m_in_level(false),
|
||||
m_current_level(0)
|
||||
{
|
||||
m_ctx->assert_expr(m_pm.get_background());
|
||||
}
|
||||
|
||||
void prop_solver::add_level() {
|
||||
unsigned idx = level_cnt();
|
||||
std::stringstream name;
|
||||
name << m_name << "#level_" << idx;
|
||||
func_decl * lev_pred = m.mk_fresh_func_decl(name.str().c_str(), 0, nullptr,m.mk_bool_sort());
|
||||
m_aux_symbols.insert(lev_pred);
|
||||
m_level_preds.push_back(lev_pred);
|
||||
|
||||
app_ref pos_la(m.mk_const(lev_pred), m);
|
||||
app_ref neg_la(m.mk_not(pos_la.get()), m);
|
||||
|
||||
m_pos_level_atoms.push_back(pos_la);
|
||||
m_neg_level_atoms.push_back(neg_la);
|
||||
|
||||
m_level_atoms_set.insert(pos_la.get());
|
||||
m_level_atoms_set.insert(neg_la.get());
|
||||
}
|
||||
|
||||
void prop_solver::ensure_level(unsigned lvl) {
|
||||
while (lvl>=level_cnt()) {
|
||||
add_level();
|
||||
}
|
||||
}
|
||||
|
||||
unsigned prop_solver::level_cnt() const {
|
||||
return m_level_preds.size();
|
||||
}
|
||||
|
||||
void prop_solver::push_level_atoms(unsigned level, expr_ref_vector& tgt) const {
|
||||
unsigned lev_cnt = level_cnt();
|
||||
for (unsigned i=0; i<lev_cnt; i++) {
|
||||
bool active = i>=level;
|
||||
app * lev_atom = active ? m_neg_level_atoms[i] : m_pos_level_atoms[i];
|
||||
tgt.push_back(lev_atom);
|
||||
}
|
||||
}
|
||||
|
||||
void prop_solver::add_formula(expr * form) {
|
||||
SASSERT(!m_in_level);
|
||||
m_ctx->assert_expr(form);
|
||||
IF_VERBOSE(21, verbose_stream() << "$ asserted " << mk_pp(form, m) << "\n";);
|
||||
TRACE("pdr", tout << "add_formula: " << mk_pp(form, m) << "\n";);
|
||||
}
|
||||
|
||||
void prop_solver::add_level_formula(expr * form, unsigned level) {
|
||||
ensure_level(level);
|
||||
app * lev_atom = m_pos_level_atoms[level].get();
|
||||
app_ref lform(m.mk_or(form, lev_atom), m);
|
||||
add_formula(lform.get());
|
||||
}
|
||||
|
||||
|
||||
lbool prop_solver::check_safe_assumptions(
|
||||
safe_assumptions& safe,
|
||||
const expr_ref_vector& atoms)
|
||||
{
|
||||
flet<bool> _model(m_fparams.m_model, m_model != nullptr);
|
||||
expr_ref_vector expr_atoms(m);
|
||||
expr_atoms.append(atoms.size(), atoms.c_ptr());
|
||||
|
||||
if (m_in_level) {
|
||||
push_level_atoms(m_current_level, expr_atoms);
|
||||
}
|
||||
|
||||
lbool result = m_ctx->check(expr_atoms);
|
||||
|
||||
TRACE("pdr",
|
||||
tout << mk_pp(m_pm.mk_and(expr_atoms), m) << "\n";
|
||||
tout << result << "\n";);
|
||||
|
||||
if (result == l_true && m_model) {
|
||||
m_ctx->get_model(*m_model);
|
||||
TRACE("pdr_verbose", model_pp(tout, **m_model); );
|
||||
}
|
||||
|
||||
if (result == l_false) {
|
||||
unsigned core_size = m_ctx->get_unsat_core_size();
|
||||
m_assumes_level = false;
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
if (m_level_atoms_set.contains(m_ctx->get_unsat_core_expr(i))) {
|
||||
m_assumes_level = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (result == l_false &&
|
||||
m_core &&
|
||||
m.proofs_enabled() &&
|
||||
m_use_farkas &&
|
||||
!m_subset_based_core) {
|
||||
extract_theory_core(safe);
|
||||
}
|
||||
else if (result == l_false && m_core) {
|
||||
extract_subset_core(safe);
|
||||
SASSERT(expr_atoms.size() >= m_core->size());
|
||||
}
|
||||
m_core = nullptr;
|
||||
m_model = nullptr;
|
||||
m_subset_based_core = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
void prop_solver::extract_subset_core(safe_assumptions& safe) {
|
||||
unsigned core_size = m_ctx->get_unsat_core_size();
|
||||
m_core->reset();
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
expr * core_expr = m_ctx->get_unsat_core_expr(i);
|
||||
SASSERT(is_app(core_expr));
|
||||
|
||||
if (m_level_atoms_set.contains(core_expr)) {
|
||||
continue;
|
||||
}
|
||||
if (m_ctx->is_aux_predicate(core_expr)) {
|
||||
continue;
|
||||
}
|
||||
m_core->push_back(to_app(core_expr));
|
||||
}
|
||||
|
||||
safe.undo_proxies(*m_core);
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "core_exprs: ";
|
||||
for (unsigned i = 0; i < core_size; ++i) {
|
||||
tout << mk_pp(m_ctx->get_unsat_core_expr(i), m) << " ";
|
||||
}
|
||||
tout << "\n";
|
||||
tout << "core: " << mk_pp(m_pm.mk_and(*m_core), m) << "\n";
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
void prop_solver::extract_theory_core(safe_assumptions& safe) {
|
||||
proof_ref pr(m);
|
||||
pr = m_ctx->get_proof();
|
||||
IF_VERBOSE(21, verbose_stream() << mk_ismt2_pp(pr, m) << "\n";);
|
||||
farkas_learner fl(m_fparams, m);
|
||||
expr_ref_vector lemmas(m);
|
||||
obj_hashtable<expr> bs;
|
||||
for (unsigned i = 0; i < safe.assumptions_size(); ++i) {
|
||||
bs.insert(safe.assumptions(i));
|
||||
}
|
||||
fl.get_lemmas(pr, bs, lemmas);
|
||||
safe.elim_proxies(lemmas);
|
||||
fl.simplify_lemmas(lemmas); // redundant?
|
||||
|
||||
bool outside_of_logic =
|
||||
(m_fparams.m_arith_mode == AS_DIFF_LOGIC &&
|
||||
!is_difference_logic(m, lemmas.size(), lemmas.c_ptr())) ||
|
||||
(m_fparams.m_arith_mode == AS_UTVPI &&
|
||||
!is_utvpi_logic(m, lemmas.size(), lemmas.c_ptr()));
|
||||
|
||||
if (outside_of_logic) {
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream() << "not diff\n";
|
||||
for (unsigned i = 0; i < lemmas.size(); ++i) {
|
||||
verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n";
|
||||
});
|
||||
extract_subset_core(safe);
|
||||
}
|
||||
else {
|
||||
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream() << "Lemmas\n";
|
||||
for (unsigned i = 0; i < lemmas.size(); ++i) {
|
||||
verbose_stream() << mk_pp(lemmas[i].get(), m) << "\n";
|
||||
});
|
||||
|
||||
m_core->reset();
|
||||
m_core->append(lemmas);
|
||||
|
||||
if (m_consequences) {
|
||||
fl.get_consequences(pr, bs, *m_consequences);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbool prop_solver::check_assumptions(const expr_ref_vector & atoms) {
|
||||
return check_assumptions_and_formula(atoms, m.mk_true());
|
||||
}
|
||||
|
||||
lbool prop_solver::check_conjunction_as_assumptions(expr * conj) {
|
||||
expr_ref_vector asmp(m);
|
||||
asmp.push_back(conj);
|
||||
return check_assumptions(asmp);
|
||||
}
|
||||
|
||||
lbool prop_solver::check_assumptions_and_formula(const expr_ref_vector & atoms, expr * form)
|
||||
{
|
||||
pdr::smt_context::scoped _scoped(*m_ctx);
|
||||
safe_assumptions safe(*this, atoms);
|
||||
m_ctx->assert_expr(form);
|
||||
CTRACE("pdr", !m.is_true(form), tout << "check with formula: " << mk_pp(form, m) << "\n";);
|
||||
lbool res = check_safe_assumptions(safe, safe.atoms());
|
||||
|
||||
//
|
||||
// we don't have to undo model naming, as from the model
|
||||
// we extract the values for state variables directly
|
||||
//
|
||||
return res;
|
||||
}
|
||||
|
||||
void prop_solver::collect_statistics(statistics& st) const {
|
||||
}
|
||||
|
||||
void prop_solver::reset_statistics() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -1,139 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
prop_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SAT solver abstraction for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-17.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PROP_SOLVER_H_
|
||||
#define PROP_SOLVER_H_
|
||||
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include "ast/ast.h"
|
||||
#include "util/obj_hashtable.h"
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "util/util.h"
|
||||
#include "util/vector.h"
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
|
||||
|
||||
namespace pdr {
|
||||
class prop_solver {
|
||||
|
||||
private:
|
||||
smt_params& m_fparams;
|
||||
ast_manager& m;
|
||||
manager& m_pm;
|
||||
symbol m_name;
|
||||
scoped_ptr<pdr::smt_context> m_ctx;
|
||||
decl_vector m_level_preds;
|
||||
app_ref_vector m_pos_level_atoms; // atoms used to identify level
|
||||
app_ref_vector m_neg_level_atoms; //
|
||||
obj_hashtable<expr> m_level_atoms_set;
|
||||
app_ref_vector m_proxies; // predicates for assumptions
|
||||
expr_ref_vector* m_core;
|
||||
model_ref* m_model;
|
||||
expr_ref_vector* m_consequences;
|
||||
bool m_subset_based_core;
|
||||
bool m_assumes_level;
|
||||
bool m_use_farkas;
|
||||
func_decl_set m_aux_symbols;
|
||||
bool m_in_level;
|
||||
unsigned m_current_level; // set when m_in_level
|
||||
|
||||
/** Add level atoms activating certain level into a vector */
|
||||
void push_level_atoms(unsigned level, expr_ref_vector & tgt) const;
|
||||
|
||||
void ensure_level(unsigned lvl);
|
||||
|
||||
class safe_assumptions;
|
||||
|
||||
void extract_theory_core(safe_assumptions& assumptions);
|
||||
|
||||
void extract_subset_core(safe_assumptions& assumptions);
|
||||
|
||||
lbool check_safe_assumptions(
|
||||
safe_assumptions& assumptions,
|
||||
expr_ref_vector const& atoms);
|
||||
|
||||
|
||||
public:
|
||||
prop_solver(pdr::manager& pm, symbol const& name);
|
||||
|
||||
/** return true is s is a symbol introduced by prop_solver */
|
||||
bool is_aux_symbol(func_decl * s) const {
|
||||
return
|
||||
m_aux_symbols.contains(s) ||
|
||||
m_ctx->is_aux_predicate(s);
|
||||
}
|
||||
|
||||
void set_core(expr_ref_vector* core) { m_core = core; }
|
||||
void set_model(model_ref* mdl) { m_model = mdl; }
|
||||
void set_subset_based_core(bool f) { m_subset_based_core = f; }
|
||||
void set_consequences(expr_ref_vector* consequences) { m_consequences = consequences; }
|
||||
|
||||
bool assumes_level() const { return m_assumes_level; }
|
||||
|
||||
void add_level();
|
||||
unsigned level_cnt() const;
|
||||
|
||||
class scoped_level {
|
||||
bool& m_lev;
|
||||
public:
|
||||
scoped_level(prop_solver& ps, unsigned lvl):m_lev(ps.m_in_level) {
|
||||
SASSERT(!m_lev); m_lev = true; ps.m_current_level = lvl;
|
||||
}
|
||||
~scoped_level() { m_lev = false; }
|
||||
};
|
||||
|
||||
void set_use_farkas(bool f) { m_use_farkas = f; }
|
||||
bool get_use_farkas() const { return m_use_farkas; }
|
||||
|
||||
void add_formula(expr * form);
|
||||
void add_level_formula(expr * form, unsigned level);
|
||||
|
||||
/**
|
||||
* Return true iff conjunction of atoms is consistent with the current state of
|
||||
* the solver.
|
||||
*
|
||||
* If the conjunction of atoms is inconsistent with the solver state and core is non-zero,
|
||||
* core will contain an unsatisfiable core of atoms.
|
||||
*
|
||||
* If the conjunction of atoms is consistent with the solver state and o_model is non-zero,
|
||||
* o_model will contain the "o" literals true in the assignment.
|
||||
*/
|
||||
lbool check_assumptions(const expr_ref_vector & atoms);
|
||||
|
||||
lbool check_conjunction_as_assumptions(expr * conj);
|
||||
|
||||
/**
|
||||
* Like check_assumptions, except it also asserts an extra formula
|
||||
*/
|
||||
lbool check_assumptions_and_formula(
|
||||
const expr_ref_vector & atoms,
|
||||
expr * form);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
void reset_statistics();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
#endif
|
|
@ -1,132 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
reachable_cache.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Object for caching of reachable states.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/pdr/pdr_reachable_cache.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
reachable_cache::reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm)
|
||||
: m(pm.get_manager()),
|
||||
m_pm(pm),
|
||||
m_ctx(nullptr),
|
||||
m_ref_holder(m),
|
||||
m_disj_connector(m),
|
||||
m_cache_mode(cm) {
|
||||
if (m_cache_mode == datalog::CONSTRAINT_CACHE) {
|
||||
m_ctx = pm.mk_fresh();
|
||||
m_ctx->assert_expr(m_pm.get_background());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void reachable_cache::add_disjuncted_formula(expr * f) {
|
||||
app_ref new_connector(m.mk_fresh_const("disj_conn", m.mk_bool_sort()), m);
|
||||
app_ref neg_new_connector(m.mk_not(new_connector), m);
|
||||
app_ref extended_form(m);
|
||||
|
||||
if(m_disj_connector) {
|
||||
extended_form = m.mk_or(m_disj_connector, neg_new_connector, f);
|
||||
}
|
||||
else {
|
||||
extended_form = m.mk_or(neg_new_connector, f);
|
||||
}
|
||||
if (m_ctx) {
|
||||
m_ctx->assert_expr(extended_form);
|
||||
}
|
||||
|
||||
m_disj_connector = new_connector;
|
||||
}
|
||||
|
||||
void reachable_cache::add_reachable(expr * cube) {
|
||||
|
||||
switch (m_cache_mode) {
|
||||
case datalog::NO_CACHE:
|
||||
break;
|
||||
|
||||
case datalog::HASH_CACHE:
|
||||
m_stats.m_inserts++;
|
||||
m_cache.insert(cube);
|
||||
m_ref_holder.push_back(cube);
|
||||
break;
|
||||
|
||||
case datalog::CONSTRAINT_CACHE:
|
||||
m_stats.m_inserts++;
|
||||
TRACE("pdr", tout << mk_pp(cube, m) << "\n";);
|
||||
add_disjuncted_formula(cube);
|
||||
break;
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool reachable_cache::is_reachable(expr * cube) {
|
||||
bool found = false;
|
||||
switch (m_cache_mode) {
|
||||
case datalog::NO_CACHE:
|
||||
return false;
|
||||
|
||||
case datalog::HASH_CACHE:
|
||||
found = m_cache.contains(cube);
|
||||
break;
|
||||
|
||||
case datalog::CONSTRAINT_CACHE: {
|
||||
if(!m_disj_connector) {
|
||||
found = false;
|
||||
break;
|
||||
}
|
||||
expr * connector = m_disj_connector.get();
|
||||
expr_ref_vector assms(m);
|
||||
assms.push_back(connector);
|
||||
m_ctx->push();
|
||||
m_ctx->assert_expr(cube);
|
||||
lbool res = m_ctx->check(assms);
|
||||
m_ctx->pop();
|
||||
|
||||
TRACE("pdr", tout << "is_reachable: " << res << " " << mk_pp(cube, m) << "\n";);
|
||||
|
||||
found = res == l_true;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
if (found) {
|
||||
m_stats.m_hits++;
|
||||
}
|
||||
else {
|
||||
m_stats.m_miss++;
|
||||
}
|
||||
return found;
|
||||
}
|
||||
|
||||
void reachable_cache::collect_statistics(statistics& st) const {
|
||||
st.update("cache inserts", m_stats.m_inserts);
|
||||
st.update("cache miss", m_stats.m_miss);
|
||||
st.update("cache hits", m_stats.m_hits);
|
||||
}
|
||||
|
||||
void reachable_cache::reset_statistics() {
|
||||
m_stats.reset();
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -1,66 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
reachable_cache.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Object for caching of reachable states.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-14.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
|
||||
#ifndef REACHABLE_CACHE_H_
|
||||
#define REACHABLE_CACHE_H_
|
||||
#include "ast/ast.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
|
||||
namespace pdr {
|
||||
class reachable_cache {
|
||||
struct stats {
|
||||
unsigned m_hits;
|
||||
unsigned m_miss;
|
||||
unsigned m_inserts;
|
||||
stats() { reset(); }
|
||||
void reset() { memset(this, 0, sizeof(*this)); }
|
||||
};
|
||||
|
||||
ast_manager & m;
|
||||
manager & m_pm;
|
||||
scoped_ptr<smt_context> m_ctx;
|
||||
ast_ref_vector m_ref_holder;
|
||||
app_ref m_disj_connector;
|
||||
obj_hashtable<expr> m_cache;
|
||||
stats m_stats;
|
||||
datalog::PDR_CACHE_MODE m_cache_mode;
|
||||
|
||||
void add_disjuncted_formula(expr * f);
|
||||
|
||||
public:
|
||||
reachable_cache(pdr::manager & pm, datalog::PDR_CACHE_MODE cm);
|
||||
|
||||
void add_init(app * f) { add_disjuncted_formula(f); }
|
||||
|
||||
/** add cube whose all models are reachable */
|
||||
void add_reachable(expr * cube);
|
||||
|
||||
/** return true if there is a model of cube which is reachable */
|
||||
bool is_reachable(expr * cube);
|
||||
|
||||
void collect_statistics(statistics& st) const;
|
||||
|
||||
void reset_statistics();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,167 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_smt_context_manager.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Manager of smt contexts
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-26.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "muz/pdr/pdr_smt_context_manager.h"
|
||||
#include "ast/has_free_vars.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_smt_pp.h"
|
||||
#include <sstream>
|
||||
#include "smt/params/smt_params.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
smt_context::smt_context(smt_context_manager& p, ast_manager& m, app* pred):
|
||||
m_pred(pred, m),
|
||||
m_parent(p),
|
||||
m_in_delay_scope(false),
|
||||
m_pushed(false)
|
||||
{}
|
||||
|
||||
bool smt_context::is_aux_predicate(func_decl* p) {
|
||||
return m_parent.is_aux_predicate(p);
|
||||
}
|
||||
|
||||
smt_context::scoped::scoped(smt_context& ctx): m_ctx(ctx) {
|
||||
SASSERT(!m_ctx.m_in_delay_scope);
|
||||
SASSERT(!m_ctx.m_pushed);
|
||||
m_ctx.m_in_delay_scope = true;
|
||||
}
|
||||
|
||||
smt_context::scoped::~scoped() {
|
||||
SASSERT(m_ctx.m_in_delay_scope);
|
||||
if (m_ctx.m_pushed) {
|
||||
m_ctx.pop();
|
||||
m_ctx.m_pushed = false;
|
||||
}
|
||||
m_ctx.m_in_delay_scope = false;
|
||||
}
|
||||
|
||||
|
||||
_smt_context::_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred):
|
||||
smt_context(p, ctx.m(), pred),
|
||||
m_context(ctx)
|
||||
{}
|
||||
|
||||
void _smt_context::assert_expr(expr* e) {
|
||||
ast_manager& m = m_context.m();
|
||||
if (m.is_true(e)) {
|
||||
return;
|
||||
}
|
||||
CTRACE("pdr", has_free_vars(e), tout << mk_pp(e, m) << "\n";);
|
||||
SASSERT(!has_free_vars(e));
|
||||
if (m_in_delay_scope && !m_pushed) {
|
||||
m_context.push();
|
||||
m_pushed = true;
|
||||
}
|
||||
expr_ref fml(m);
|
||||
fml = m_pushed?e:m.mk_implies(m_pred, e);
|
||||
m_context.assert_expr(fml);
|
||||
}
|
||||
|
||||
lbool _smt_context::check(expr_ref_vector& assumptions) {
|
||||
ast_manager& m = m_pred.get_manager();
|
||||
if (!m.is_true(m_pred)) {
|
||||
assumptions.push_back(m_pred);
|
||||
}
|
||||
TRACE("pdr_check",
|
||||
{
|
||||
ast_smt_pp pp(m);
|
||||
for (unsigned i = 0; i < m_context.size(); ++i) {
|
||||
pp.add_assumption(m_context.get_formula(i));
|
||||
}
|
||||
for (unsigned i = 0; i < assumptions.size(); ++i) {
|
||||
pp.add_assumption(assumptions[i].get());
|
||||
}
|
||||
|
||||
static unsigned lemma_id = 0;
|
||||
std::ostringstream strm;
|
||||
strm << "pdr-lemma-" << lemma_id << ".smt2";
|
||||
std::ofstream out(strm.str().c_str());
|
||||
pp.display_smt2(out, m.mk_true());
|
||||
out.close();
|
||||
lemma_id++;
|
||||
tout << "pdr_check: " << strm.str() << "\n";
|
||||
});
|
||||
lbool result = m_context.check(assumptions.size(), assumptions.c_ptr());
|
||||
if (!m.is_true(m_pred)) {
|
||||
assumptions.pop_back();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void _smt_context::get_model(model_ref& model) {
|
||||
m_context.get_model(model);
|
||||
}
|
||||
|
||||
proof* _smt_context::get_proof() {
|
||||
return m_context.get_proof();
|
||||
}
|
||||
|
||||
smt_context_manager::smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m):
|
||||
m_fparams(fp),
|
||||
m(m),
|
||||
m_max_num_contexts(max_num_contexts),
|
||||
m_num_contexts(0),
|
||||
m_predicate_list(m) {
|
||||
}
|
||||
|
||||
|
||||
smt_context_manager::~smt_context_manager() {
|
||||
TRACE("pdr",tout << "\n";);
|
||||
std::for_each(m_contexts.begin(), m_contexts.end(), delete_proc<smt::kernel>());
|
||||
}
|
||||
|
||||
smt_context* smt_context_manager::mk_fresh() {
|
||||
++m_num_contexts;
|
||||
app_ref pred(m);
|
||||
smt::kernel * ctx = nullptr;
|
||||
if (m_max_num_contexts == 0) {
|
||||
m_contexts.push_back(alloc(smt::kernel, m, m_fparams));
|
||||
pred = m.mk_true();
|
||||
ctx = m_contexts[m_num_contexts-1];
|
||||
}
|
||||
else {
|
||||
if (m_contexts.size() < m_max_num_contexts) {
|
||||
m_contexts.push_back(alloc(smt::kernel, m, m_fparams));
|
||||
}
|
||||
std::stringstream name;
|
||||
name << "#context" << m_num_contexts;
|
||||
pred = m.mk_const(symbol(name.str().c_str()), m.mk_bool_sort());
|
||||
m_predicate_list.push_back(pred);
|
||||
m_predicate_set.insert(pred->get_decl());
|
||||
ctx = m_contexts[(m_num_contexts-1)%m_max_num_contexts];
|
||||
}
|
||||
return alloc(_smt_context, *ctx, *this, pred);
|
||||
}
|
||||
|
||||
void smt_context_manager::collect_statistics(statistics& st) const {
|
||||
for (unsigned i = 0; i < m_contexts.size(); ++i) {
|
||||
m_contexts[i]->collect_statistics(st);
|
||||
}
|
||||
}
|
||||
|
||||
void smt_context_manager::reset_statistics() {
|
||||
for (unsigned i = 0; i < m_contexts.size(); ++i) {
|
||||
m_contexts[i]->reset_statistics();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
@ -1,92 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_smt_context_manager.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Manager of smt contexts
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2011-11-26.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_SMT_CONTEXT_MANAGER_H_
|
||||
#define PDR_SMT_CONTEXT_MANAGER_H_
|
||||
|
||||
#include "smt/smt_kernel.h"
|
||||
#include "ast/func_decl_dependencies.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
|
||||
namespace pdr {
|
||||
|
||||
class smt_context_manager;
|
||||
|
||||
class smt_context {
|
||||
protected:
|
||||
app_ref m_pred;
|
||||
smt_context_manager& m_parent;
|
||||
bool m_in_delay_scope;
|
||||
bool m_pushed;
|
||||
public:
|
||||
smt_context(smt_context_manager& p, ast_manager& m, app* pred);
|
||||
virtual ~smt_context() {}
|
||||
virtual void assert_expr(expr* e) = 0;
|
||||
virtual lbool check(expr_ref_vector& assumptions) = 0;
|
||||
virtual void get_model(model_ref& model) = 0;
|
||||
virtual proof* get_proof() = 0;
|
||||
virtual unsigned get_unsat_core_size() = 0;
|
||||
virtual expr* get_unsat_core_expr(unsigned i) = 0;
|
||||
virtual void push() = 0;
|
||||
virtual void pop() = 0;
|
||||
bool is_aux_predicate(func_decl* p);
|
||||
bool is_aux_predicate(expr* p) { return is_app(p) && is_aux_predicate(to_app(p)->get_decl()); }
|
||||
class scoped {
|
||||
smt_context& m_ctx;
|
||||
public:
|
||||
scoped(smt_context& ctx);
|
||||
~scoped();
|
||||
};
|
||||
};
|
||||
|
||||
class _smt_context : public smt_context {
|
||||
smt::kernel & m_context;
|
||||
public:
|
||||
_smt_context(smt::kernel & ctx, smt_context_manager& p, app* pred);
|
||||
~_smt_context() override {}
|
||||
void assert_expr(expr* e) override;
|
||||
lbool check(expr_ref_vector& assumptions) override;
|
||||
void get_model(model_ref& model) override;
|
||||
proof* get_proof() override;
|
||||
void push() override { m_context.push(); }
|
||||
void pop() override { m_context.pop(1); }
|
||||
unsigned get_unsat_core_size() override { return m_context.get_unsat_core_size(); }
|
||||
expr* get_unsat_core_expr(unsigned i) override { return m_context.get_unsat_core_expr(i); }
|
||||
};
|
||||
|
||||
class smt_context_manager {
|
||||
smt_params& m_fparams;
|
||||
ast_manager& m;
|
||||
unsigned m_max_num_contexts;
|
||||
ptr_vector<smt::kernel> m_contexts;
|
||||
unsigned m_num_contexts;
|
||||
app_ref_vector m_predicate_list;
|
||||
func_decl_set m_predicate_set;
|
||||
public:
|
||||
smt_context_manager(smt_params& fp, unsigned max_num_contexts, ast_manager& m);
|
||||
~smt_context_manager();
|
||||
smt_context* mk_fresh();
|
||||
void collect_statistics(statistics& st) const;
|
||||
void reset_statistics();
|
||||
bool is_aux_predicate(func_decl* p) const { return m_predicate_set.contains(p); }
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,601 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sym_mux.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A symbol multiplexer that helps with having multiple versions of each of a set of symbols.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-8.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "model/model.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "muz/pdr/pdr_sym_mux.h"
|
||||
|
||||
using namespace pdr;
|
||||
|
||||
sym_mux::sym_mux(ast_manager & m)
|
||||
: m(m), m_ref_holder(m),
|
||||
m_next_sym_suffix_idx(0) {
|
||||
m_suffixes.push_back("_n");
|
||||
size_t suf_sz = m_suffixes.size();
|
||||
for(unsigned i = 0; i < suf_sz; ++i) {
|
||||
symbol suff_sym = symbol(m_suffixes[i].c_str());
|
||||
m_used_suffixes.insert(suff_sym);
|
||||
}
|
||||
}
|
||||
|
||||
std::string sym_mux::get_suffix(unsigned i) {
|
||||
while(m_suffixes.size() <= i) {
|
||||
std::string new_suffix;
|
||||
symbol new_syffix_sym;
|
||||
do {
|
||||
std::stringstream stm;
|
||||
stm<<'_'<<m_next_sym_suffix_idx;
|
||||
m_next_sym_suffix_idx++;
|
||||
new_suffix = stm.str();
|
||||
new_syffix_sym = symbol(new_suffix.c_str());
|
||||
}
|
||||
while (m_used_suffixes.contains(new_syffix_sym));
|
||||
m_used_suffixes.insert(new_syffix_sym);
|
||||
m_suffixes.push_back(new_suffix);
|
||||
}
|
||||
std::string result = m_suffixes[i];
|
||||
return result;
|
||||
}
|
||||
|
||||
void sym_mux::create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range,
|
||||
unsigned tuple_length, decl_vector & tuple)
|
||||
{
|
||||
SASSERT(tuple_length>0);
|
||||
while(tuple.size()<tuple_length) {
|
||||
tuple.push_back(0);
|
||||
}
|
||||
SASSERT(tuple.size()==tuple_length);
|
||||
std::string pre = prefix->get_name().str();
|
||||
for(unsigned i=0; i<tuple_length; i++) {
|
||||
|
||||
if (tuple[i] != 0) {
|
||||
SASSERT(tuple[i]->get_arity()==arity);
|
||||
SASSERT(tuple[i]->get_range()==range);
|
||||
//domain should match as well, but we won't bother checking an array equality
|
||||
}
|
||||
else {
|
||||
std::string name = pre+get_suffix(i);
|
||||
tuple[i] = m.mk_func_decl(symbol(name.c_str()), arity, domain, range);
|
||||
}
|
||||
m_ref_holder.push_back(tuple[i]);
|
||||
m_sym2idx.insert(tuple[i], i);
|
||||
m_sym2prim.insert(tuple[i], tuple[0]);
|
||||
}
|
||||
|
||||
m_prim2all.insert(tuple[0], tuple);
|
||||
m_prefix2prim.insert(prefix, tuple[0]);
|
||||
m_prim2prefix.insert(tuple[0], prefix);
|
||||
m_prim_preds.push_back(tuple[0]);
|
||||
m_ref_holder.push_back(prefix);
|
||||
}
|
||||
|
||||
void sym_mux::ensure_tuple_size(func_decl * prim, unsigned sz) {
|
||||
SASSERT(m_prim2all.contains(prim));
|
||||
decl_vector& tuple = m_prim2all.find_core(prim)->get_data().m_value;
|
||||
SASSERT(tuple[0]==prim);
|
||||
|
||||
if(sz <= tuple.size()) { return; }
|
||||
|
||||
func_decl * prefix;
|
||||
TRUSTME(m_prim2prefix.find(prim, prefix));
|
||||
std::string prefix_name = prefix->get_name().bare_str();
|
||||
for(unsigned i = tuple.size(); i < sz; ++i) {
|
||||
std::string name = prefix_name + get_suffix(i);
|
||||
func_decl * new_sym = m.mk_func_decl(symbol(name.c_str()), prefix->get_arity(),
|
||||
prefix->get_domain(), prefix->get_range());
|
||||
|
||||
tuple.push_back(new_sym);
|
||||
m_ref_holder.push_back(new_sym);
|
||||
m_sym2idx.insert(new_sym, i);
|
||||
m_sym2prim.insert(new_sym, prim);
|
||||
}
|
||||
}
|
||||
|
||||
func_decl * sym_mux::conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx)
|
||||
{
|
||||
if(src_idx==tgt_idx) { return sym; }
|
||||
func_decl * prim = (src_idx==0) ? sym : get_primary(sym);
|
||||
if(tgt_idx>src_idx) {
|
||||
ensure_tuple_size(prim, tgt_idx+1);
|
||||
}
|
||||
decl_vector & sym_vect = m_prim2all.find_core(prim)->get_data().m_value;
|
||||
SASSERT(sym_vect[src_idx]==sym);
|
||||
return sym_vect[tgt_idx];
|
||||
}
|
||||
|
||||
|
||||
func_decl * sym_mux::get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx,
|
||||
unsigned arity, sort * const * domain, sort * range)
|
||||
{
|
||||
func_decl * prim = try_get_primary_by_prefix(prefix);
|
||||
if(prim) {
|
||||
SASSERT(prim->get_arity()==arity);
|
||||
SASSERT(prim->get_range()==range);
|
||||
//domain should match as well, but we won't bother checking an array equality
|
||||
|
||||
return conv(prim, 0, idx);
|
||||
}
|
||||
|
||||
decl_vector syms;
|
||||
create_tuple(prefix, arity, domain, range, idx+1, syms);
|
||||
return syms[idx];
|
||||
}
|
||||
|
||||
bool sym_mux::is_muxed_lit(expr * e, unsigned idx) const
|
||||
{
|
||||
if(!is_app(e)) { return false; }
|
||||
app * a = to_app(e);
|
||||
if(m.is_not(a) && is_app(a->get_arg(0))) {
|
||||
a = to_app(a->get_arg(0));
|
||||
}
|
||||
return is_muxed(a->get_decl());
|
||||
}
|
||||
|
||||
|
||||
struct sym_mux::formula_checker
|
||||
{
|
||||
formula_checker(const sym_mux & parent, bool all, unsigned idx) :
|
||||
m_parent(parent), m_all(all), m_idx(idx),
|
||||
m_found_what_needed(false)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if(m_found_what_needed || !is_app(e)) { return; }
|
||||
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned sym_idx;
|
||||
if(!m_parent.try_get_index(sym, sym_idx)) { return; }
|
||||
|
||||
bool have_idx = sym_idx==m_idx;
|
||||
|
||||
if( m_all ? (!have_idx) : have_idx ) {
|
||||
m_found_what_needed = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
bool all_have_idx() const
|
||||
{
|
||||
SASSERT(m_all); //we were looking for the queried property
|
||||
return !m_found_what_needed;
|
||||
}
|
||||
|
||||
bool some_with_idx() const
|
||||
{
|
||||
SASSERT(!m_all); //we were looking for the queried property
|
||||
return m_found_what_needed;
|
||||
}
|
||||
|
||||
private:
|
||||
const sym_mux & m_parent;
|
||||
bool m_all;
|
||||
unsigned m_idx;
|
||||
|
||||
/**
|
||||
If we check whether all muxed symbols are of given index, we look for
|
||||
counter-examples, checking whether form contains a muxed symbol of an index,
|
||||
we look for symbol of index m_idx.
|
||||
*/
|
||||
bool m_found_what_needed;
|
||||
};
|
||||
|
||||
bool sym_mux::contains(expr * e, unsigned idx) const
|
||||
{
|
||||
formula_checker chck(*this, false, idx);
|
||||
for_each_expr(chck, m_visited, e);
|
||||
m_visited.reset();
|
||||
return chck.some_with_idx();
|
||||
}
|
||||
|
||||
bool sym_mux::is_homogenous_formula(expr * e, unsigned idx) const
|
||||
{
|
||||
formula_checker chck(*this, true, idx);
|
||||
for_each_expr(chck, m_visited, e);
|
||||
m_visited.reset();
|
||||
return chck.all_have_idx();
|
||||
}
|
||||
|
||||
bool sym_mux::is_homogenous(const expr_ref_vector & vect, unsigned idx) const
|
||||
{
|
||||
expr * const * begin = vect.c_ptr();
|
||||
expr * const * end = begin + vect.size();
|
||||
for(expr * const * it = begin; it!=end; it++) {
|
||||
if(!is_homogenous_formula(*it, idx)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class sym_mux::index_collector {
|
||||
sym_mux const& m_parent;
|
||||
svector<bool> m_indices;
|
||||
public:
|
||||
index_collector(sym_mux const& s):
|
||||
m_parent(s) {}
|
||||
|
||||
void operator()(expr * e) {
|
||||
if (is_app(e)) {
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned idx;
|
||||
if (m_parent.try_get_index(sym, idx)) {
|
||||
SASSERT(idx > 0);
|
||||
--idx;
|
||||
if (m_indices.size() <= idx) {
|
||||
m_indices.resize(idx+1, false);
|
||||
}
|
||||
m_indices[idx] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void extract(unsigned_vector& indices) {
|
||||
for (unsigned i = 0; i < m_indices.size(); ++i) {
|
||||
if (m_indices[i]) {
|
||||
indices.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
void sym_mux::collect_indices(expr* e, unsigned_vector& indices) const {
|
||||
indices.reset();
|
||||
index_collector collector(*this);
|
||||
for_each_expr(collector, m_visited, e);
|
||||
m_visited.reset();
|
||||
collector.extract(indices);
|
||||
}
|
||||
|
||||
class sym_mux::variable_collector {
|
||||
sym_mux const& m_parent;
|
||||
vector<ptr_vector<app> >& m_vars;
|
||||
public:
|
||||
variable_collector(sym_mux const& s, vector<ptr_vector<app> >& vars):
|
||||
m_parent(s), m_vars(vars) {}
|
||||
|
||||
void operator()(expr * e) {
|
||||
if (is_app(e)) {
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned idx;
|
||||
if (m_parent.try_get_index(sym, idx)) {
|
||||
SASSERT(idx > 0);
|
||||
--idx;
|
||||
if (m_vars.size() <= idx) {
|
||||
m_vars.resize(idx+1, ptr_vector<app>());
|
||||
}
|
||||
m_vars[idx].push_back(to_app(e));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
void sym_mux::collect_variables(expr* e, vector<ptr_vector<app> >& vars) const {
|
||||
vars.reset();
|
||||
variable_collector collector(*this, vars);
|
||||
for_each_expr(collector, m_visited, e);
|
||||
m_visited.reset();
|
||||
}
|
||||
|
||||
class sym_mux::hmg_checker {
|
||||
const sym_mux & m_parent;
|
||||
|
||||
bool m_found_idx;
|
||||
unsigned m_idx;
|
||||
bool m_multiple_indexes;
|
||||
|
||||
public:
|
||||
hmg_checker(const sym_mux & parent) :
|
||||
m_parent(parent), m_found_idx(false), m_multiple_indexes(false)
|
||||
{
|
||||
}
|
||||
|
||||
void operator()(expr * e)
|
||||
{
|
||||
if(m_multiple_indexes || !is_app(e)) { return; }
|
||||
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
unsigned sym_idx;
|
||||
if(!m_parent.try_get_index(sym, sym_idx)) { return; }
|
||||
|
||||
if(!m_found_idx) {
|
||||
m_found_idx = true;
|
||||
m_idx = sym_idx;
|
||||
return;
|
||||
}
|
||||
if(m_idx==sym_idx) { return; }
|
||||
m_multiple_indexes = true;
|
||||
}
|
||||
|
||||
bool has_multiple_indexes() const
|
||||
{
|
||||
return m_multiple_indexes;
|
||||
}
|
||||
};
|
||||
|
||||
bool sym_mux::is_homogenous_formula(expr * e) const {
|
||||
hmg_checker chck(*this);
|
||||
for_each_expr(chck, m_visited, e);
|
||||
m_visited.reset();
|
||||
return !chck.has_multiple_indexes();
|
||||
}
|
||||
|
||||
|
||||
struct sym_mux::conv_rewriter_cfg : public default_rewriter_cfg
|
||||
{
|
||||
private:
|
||||
ast_manager & m;
|
||||
sym_mux & m_parent;
|
||||
unsigned m_from_idx;
|
||||
unsigned m_to_idx;
|
||||
bool m_homogenous;
|
||||
public:
|
||||
conv_rewriter_cfg(sym_mux & parent, unsigned from_idx, unsigned to_idx, bool homogenous)
|
||||
: m(parent.get_manager()),
|
||||
m_parent(parent),
|
||||
m_from_idx(from_idx),
|
||||
m_to_idx(to_idx),
|
||||
m_homogenous(homogenous) {}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if(!is_app(s)) { return false; }
|
||||
app * a = to_app(s);
|
||||
func_decl * sym = a->get_decl();
|
||||
if(!m_parent.has_index(sym, m_from_idx)) {
|
||||
(void) m_homogenous;
|
||||
SASSERT(!m_homogenous || !m_parent.is_muxed(sym));
|
||||
return false;
|
||||
}
|
||||
func_decl * tgt = m_parent.conv(sym, m_from_idx, m_to_idx);
|
||||
|
||||
t = m.mk_app(tgt, a->get_args());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void sym_mux::conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous)
|
||||
{
|
||||
if(src_idx==tgt_idx) {
|
||||
res = f;
|
||||
return;
|
||||
}
|
||||
conv_rewriter_cfg r_cfg(*this, src_idx, tgt_idx, homogenous);
|
||||
rewriter_tpl<conv_rewriter_cfg> rwr(m, false, r_cfg);
|
||||
rwr(f, res);
|
||||
}
|
||||
|
||||
struct sym_mux::shifting_rewriter_cfg : public default_rewriter_cfg
|
||||
{
|
||||
private:
|
||||
ast_manager & m;
|
||||
sym_mux & m_parent;
|
||||
int m_shift;
|
||||
public:
|
||||
shifting_rewriter_cfg(sym_mux & parent, int shift)
|
||||
: m(parent.get_manager()),
|
||||
m_parent(parent),
|
||||
m_shift(shift) {}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if(!is_app(s)) { return false; }
|
||||
app * a = to_app(s);
|
||||
func_decl * sym = a->get_decl();
|
||||
|
||||
unsigned idx;
|
||||
if(!m_parent.try_get_index(sym, idx)) {
|
||||
return false;
|
||||
}
|
||||
SASSERT(static_cast<int>(idx)+m_shift>=0);
|
||||
func_decl * tgt = m_parent.conv(sym, idx, idx+m_shift);
|
||||
t = m.mk_app(tgt, a->get_args());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
void sym_mux::shift_formula(expr * f, int dist, expr_ref & res)
|
||||
{
|
||||
if(dist==0) {
|
||||
res = f;
|
||||
return;
|
||||
}
|
||||
shifting_rewriter_cfg r_cfg(*this, dist);
|
||||
rewriter_tpl<shifting_rewriter_cfg> rwr(m, false, r_cfg);
|
||||
rwr(f, res);
|
||||
}
|
||||
|
||||
void sym_mux::conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
|
||||
expr_ref_vector & res)
|
||||
{
|
||||
res.reset();
|
||||
expr * const * begin = vect.c_ptr();
|
||||
expr * const * end = begin + vect.size();
|
||||
for(expr * const * it = begin; it!=end; it++) {
|
||||
expr_ref converted(m);
|
||||
conv_formula(*it, src_idx, tgt_idx, converted);
|
||||
res.push_back(converted);
|
||||
}
|
||||
}
|
||||
|
||||
void sym_mux::filter_idx(expr_ref_vector & vect, unsigned idx) const {
|
||||
unsigned i = 0;
|
||||
while (i < vect.size()) {
|
||||
expr* e = vect[i].get();
|
||||
if (contains(e, idx) && is_homogenous_formula(e, idx)) {
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
// we don't allow mixing states inside vector elements
|
||||
SASSERT(!contains(e, idx));
|
||||
vect[i] = vect.back();
|
||||
vect.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void sym_mux::partition_o_idx(
|
||||
expr_ref_vector const& lits,
|
||||
expr_ref_vector& o_lits,
|
||||
expr_ref_vector& other, unsigned idx) const {
|
||||
|
||||
for (unsigned i = 0; i < lits.size(); ++i) {
|
||||
if (contains(lits[i], idx) && is_homogenous_formula(lits[i], idx)) {
|
||||
o_lits.push_back(lits[i]);
|
||||
}
|
||||
else {
|
||||
other.push_back(lits[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class sym_mux::nonmodel_sym_checker {
|
||||
const sym_mux & m_parent;
|
||||
|
||||
bool m_found;
|
||||
public:
|
||||
nonmodel_sym_checker(const sym_mux & parent) :
|
||||
m_parent(parent), m_found(false) {
|
||||
}
|
||||
|
||||
void operator()(expr * e) {
|
||||
if(m_found || !is_app(e)) { return; }
|
||||
|
||||
func_decl * sym = to_app(e)->get_decl();
|
||||
|
||||
if(m_parent.is_non_model_sym(sym)) {
|
||||
m_found = true;
|
||||
}
|
||||
}
|
||||
|
||||
bool found() const {
|
||||
return m_found;
|
||||
}
|
||||
};
|
||||
|
||||
bool sym_mux::has_nonmodel_symbol(expr * e) const {
|
||||
nonmodel_sym_checker chck(*this);
|
||||
for_each_expr(chck, e);
|
||||
return chck.found();
|
||||
}
|
||||
|
||||
void sym_mux::filter_non_model_lits(expr_ref_vector & vect) const {
|
||||
unsigned i = 0;
|
||||
while (i < vect.size()) {
|
||||
if (!has_nonmodel_symbol(vect[i].get())) {
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
vect[i] = vect.back();
|
||||
vect.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class sym_mux::decl_idx_comparator
|
||||
{
|
||||
const sym_mux & m_parent;
|
||||
public:
|
||||
decl_idx_comparator(const sym_mux & parent)
|
||||
: m_parent(parent)
|
||||
{ }
|
||||
|
||||
bool operator()(func_decl * sym1, func_decl * sym2)
|
||||
{
|
||||
unsigned idx1, idx2;
|
||||
if (!m_parent.try_get_index(sym1, idx1)) { idx1 = UINT_MAX; }
|
||||
if (!m_parent.try_get_index(sym2, idx2)) { idx2 = UINT_MAX; }
|
||||
|
||||
if (idx1 != idx2) { return idx1<idx2; }
|
||||
return lt(sym1->get_name(), sym2->get_name());
|
||||
}
|
||||
};
|
||||
|
||||
std::string sym_mux::pp_model(const model_core & mdl) const {
|
||||
decl_vector consts;
|
||||
unsigned sz = mdl.get_num_constants();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
func_decl * d = mdl.get_constant(i);
|
||||
consts.push_back(d);
|
||||
}
|
||||
|
||||
std::sort(consts.begin(), consts.end(), decl_idx_comparator(*this));
|
||||
|
||||
std::stringstream res;
|
||||
|
||||
decl_vector::iterator end = consts.end();
|
||||
for (decl_vector::iterator it = consts.begin(); it!=end; it++) {
|
||||
func_decl * d = *it;
|
||||
std::string name = d->get_name().str();
|
||||
const char * arrow = " -> ";
|
||||
res << name << arrow;
|
||||
unsigned indent = static_cast<unsigned>(name.length() + strlen(arrow));
|
||||
res << mk_pp(mdl.get_const_interp(d), m, indent) << "\n";
|
||||
|
||||
if (it+1 != end) {
|
||||
unsigned idx1, idx2;
|
||||
if (!try_get_index(*it, idx1)) { idx1 = UINT_MAX; }
|
||||
if (!try_get_index(*(it+1), idx2)) { idx2 = UINT_MAX; }
|
||||
if (idx1 != idx2) { res << "\n"; }
|
||||
}
|
||||
}
|
||||
return res.str();
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
|
||||
class sym_mux::index_renamer_cfg : public default_rewriter_cfg{
|
||||
const sym_mux & m_parent;
|
||||
unsigned m_idx;
|
||||
|
||||
public:
|
||||
index_renamer_cfg(const sym_mux & p, unsigned idx) : m_parent(p), m_idx(idx) {}
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if (!is_app(s)) return false;
|
||||
app * a = to_app(s);
|
||||
if (a->get_family_id() != null_family_id) {
|
||||
return false;
|
||||
}
|
||||
func_decl * sym = a->get_decl();
|
||||
unsigned idx;
|
||||
if(!m_parent.try_get_index(sym, idx)) {
|
||||
return false;
|
||||
}
|
||||
if (m_idx == idx) {
|
||||
return false;
|
||||
}
|
||||
ast_manager& m = m_parent.get_manager();
|
||||
symbol name = symbol((sym->get_name().str() + "!").c_str());
|
||||
func_decl * tgt = m.mk_func_decl(name, sym->get_arity(), sym->get_domain(), sym->get_range());
|
||||
t = m.mk_app(tgt, a->get_num_args(), a->get_args());
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
|
@ -1,247 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sym_mux.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A symbol multiplexer that helps with having multiple versions of each of a set of symbols.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-9-8.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef SYM_MUX_H_
|
||||
#define SYM_MUX_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "util/map.h"
|
||||
#include "util/vector.h"
|
||||
#include <vector>
|
||||
|
||||
class model_core;
|
||||
|
||||
namespace pdr {
|
||||
class sym_mux
|
||||
{
|
||||
public:
|
||||
typedef ptr_vector<app> app_vector;
|
||||
typedef ptr_vector<func_decl> decl_vector;
|
||||
private:
|
||||
typedef obj_map<func_decl,unsigned> sym2u;
|
||||
typedef obj_map<func_decl, decl_vector> sym2dv;
|
||||
typedef obj_map<func_decl,func_decl *> sym2sym;
|
||||
typedef obj_map<func_decl, func_decl *> sym2pred;
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbols;
|
||||
|
||||
ast_manager & m;
|
||||
mutable ast_ref_vector m_ref_holder;
|
||||
mutable expr_mark m_visited;
|
||||
|
||||
mutable unsigned m_next_sym_suffix_idx;
|
||||
mutable symbols m_used_suffixes;
|
||||
/** Here we have default suffixes for each of the variants */
|
||||
std::vector<std::string> m_suffixes;
|
||||
|
||||
|
||||
/**
|
||||
Primary symbol is the 0-th variant. This member maps from primary symbol
|
||||
to vector of all its variants (including the primary variant).
|
||||
*/
|
||||
sym2dv m_prim2all;
|
||||
|
||||
/**
|
||||
For each symbol contains its variant index
|
||||
*/
|
||||
mutable sym2u m_sym2idx;
|
||||
/**
|
||||
For each symbol contains its primary variant
|
||||
*/
|
||||
mutable sym2sym m_sym2prim;
|
||||
|
||||
/**
|
||||
Maps prefixes passed to the create_tuple to
|
||||
the primary symbol created from it.
|
||||
*/
|
||||
sym2pred m_prefix2prim;
|
||||
|
||||
/**
|
||||
Maps pripary symbols to prefixes that were used to create them.
|
||||
*/
|
||||
sym2sym m_prim2prefix;
|
||||
|
||||
decl_vector m_prim_preds;
|
||||
|
||||
obj_hashtable<func_decl> m_non_model_syms;
|
||||
|
||||
struct formula_checker;
|
||||
struct conv_rewriter_cfg;
|
||||
struct shifting_rewriter_cfg;
|
||||
class decl_idx_comparator;
|
||||
class hmg_checker;
|
||||
class nonmodel_sym_checker;
|
||||
class index_renamer_cfg;
|
||||
class index_collector;
|
||||
class variable_collector;
|
||||
|
||||
std::string get_suffix(unsigned i);
|
||||
void ensure_tuple_size(func_decl * prim, unsigned sz);
|
||||
|
||||
public:
|
||||
sym_mux(ast_manager & m);
|
||||
|
||||
ast_manager & get_manager() const { return m; }
|
||||
|
||||
bool is_muxed(func_decl * sym) const { return m_sym2idx.contains(sym); }
|
||||
|
||||
bool try_get_index(func_decl * sym, unsigned & idx) const {
|
||||
return m_sym2idx.find(sym,idx);
|
||||
}
|
||||
|
||||
bool has_index(func_decl * sym, unsigned idx) const {
|
||||
unsigned actual_idx;
|
||||
return try_get_index(sym, actual_idx) && idx==actual_idx;
|
||||
}
|
||||
|
||||
/** Return primary symbol. sym must be muxed. */
|
||||
func_decl * get_primary(func_decl * sym) const {
|
||||
func_decl * prim;
|
||||
TRUSTME(m_sym2prim.find(sym, prim));
|
||||
return prim;
|
||||
}
|
||||
|
||||
/**
|
||||
Return primary symbol created from prefix, or 0 if the prefix was never used.
|
||||
*/
|
||||
func_decl * try_get_primary_by_prefix(func_decl* prefix) const {
|
||||
func_decl * res;
|
||||
if(!m_prefix2prim.find(prefix, res)) {
|
||||
return nullptr;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
Return symbol created from prefix, or 0 if the prefix was never used.
|
||||
*/
|
||||
func_decl * try_get_by_prefix(func_decl* prefix, unsigned idx) {
|
||||
func_decl * prim = try_get_primary_by_prefix(prefix);
|
||||
if(!prim) {
|
||||
return nullptr;
|
||||
}
|
||||
return conv(prim, 0, idx);
|
||||
}
|
||||
|
||||
/**
|
||||
Marks symbol as non-model which means it will not appear in models collected by
|
||||
get_muxed_cube_from_model function.
|
||||
This is to take care of auxiliary symbols introduced by the disjunction relations
|
||||
to relativize lemmas coming from disjuncts.
|
||||
*/
|
||||
void mark_as_non_model(func_decl * sym) {
|
||||
SASSERT(is_muxed(sym));
|
||||
m_non_model_syms.insert(get_primary(sym));
|
||||
}
|
||||
|
||||
func_decl * get_or_create_symbol_by_prefix(func_decl* prefix, unsigned idx,
|
||||
unsigned arity, sort * const * domain, sort * range);
|
||||
|
||||
|
||||
|
||||
bool is_muxed_lit(expr * e, unsigned idx) const;
|
||||
|
||||
bool is_non_model_sym(func_decl * s) const {
|
||||
return is_muxed(s) && m_non_model_syms.contains(get_primary(s));
|
||||
}
|
||||
|
||||
/**
|
||||
Create a multiplexed tuple of propositional constants.
|
||||
Symbols may be suplied in the tuple vector,
|
||||
those beyond the size of the array and those with corresponding positions
|
||||
assigned to zero will be created using prefix.
|
||||
Tuple length must be at least one.
|
||||
*/
|
||||
void create_tuple(func_decl* prefix, unsigned arity, sort * const * domain, sort * range,
|
||||
unsigned tuple_length, decl_vector & tuple);
|
||||
|
||||
/**
|
||||
Return true if the only multiplexed symbols which e contains are of index idx.
|
||||
*/
|
||||
bool is_homogenous_formula(expr * e, unsigned idx) const;
|
||||
bool is_homogenous(const expr_ref_vector & vect, unsigned idx) const;
|
||||
|
||||
/**
|
||||
Return true if all multiplexed symbols which e contains are of one index.
|
||||
*/
|
||||
bool is_homogenous_formula(expr * e) const;
|
||||
|
||||
/**
|
||||
Return true if expression e contains a muxed symbol of index idx.
|
||||
*/
|
||||
bool contains(expr * e, unsigned idx) const;
|
||||
|
||||
/**
|
||||
Collect indices used in expression.
|
||||
*/
|
||||
void collect_indices(expr* e, unsigned_vector& indices) const;
|
||||
|
||||
/**
|
||||
Collect used variables of each index.
|
||||
*/
|
||||
void collect_variables(expr* e, vector<ptr_vector<app> >& vars) const;
|
||||
|
||||
/**
|
||||
Convert symbol sym which has to be of src_idx variant into variant tgt_idx.
|
||||
*/
|
||||
func_decl * conv(func_decl * sym, unsigned src_idx, unsigned tgt_idx);
|
||||
|
||||
|
||||
/**
|
||||
Convert src_idx symbols in formula f variant into tgt_idx.
|
||||
If homogenous is true, formula cannot contain symbols of other variants.
|
||||
*/
|
||||
void conv_formula(expr * f, unsigned src_idx, unsigned tgt_idx, expr_ref & res, bool homogenous=true);
|
||||
void conv_formula_vector(const expr_ref_vector & vect, unsigned src_idx, unsigned tgt_idx,
|
||||
expr_ref_vector & res);
|
||||
|
||||
/**
|
||||
Shifts the muxed symbols in f by dist. Dist can be negative, but it should never shift
|
||||
symbol index to a negative value.
|
||||
*/
|
||||
void shift_formula(expr * f, int dist, expr_ref & res);
|
||||
|
||||
/**
|
||||
Remove from vect literals (atoms or negations of atoms) of symbols
|
||||
that contain multiplexed symbols with indexes other than idx.
|
||||
|
||||
Each of the literals can contain only symbols multiplexed with one index
|
||||
(this trivially holds if the literals are propositional).
|
||||
|
||||
Order of elements in vect may be modified by this function
|
||||
*/
|
||||
void filter_idx(expr_ref_vector & vect, unsigned idx) const;
|
||||
|
||||
/**
|
||||
Partition literals into o_literals and others.
|
||||
*/
|
||||
void partition_o_idx(expr_ref_vector const& lits,
|
||||
expr_ref_vector& o_lits,
|
||||
expr_ref_vector& other, unsigned idx) const;
|
||||
|
||||
bool has_nonmodel_symbol(expr * e) const;
|
||||
void filter_non_model_lits(expr_ref_vector & vect) const;
|
||||
|
||||
func_decl * const * begin_prim_preds() const { return m_prim_preds.begin(); }
|
||||
func_decl * const * end_prim_preds() const { return m_prim_preds.end(); }
|
||||
|
||||
std::string pp_model(const model_core & mdl) const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,508 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_util.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "util/util.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/rewriter/expr_replacer.h"
|
||||
#include "ast/rewriter/bool_rewriter.h"
|
||||
#include "ast/rewriter/poly_rewriter.h"
|
||||
#include "ast/rewriter/poly_rewriter_def.h"
|
||||
#include "ast/rewriter/arith_rewriter.h"
|
||||
#include "ast/rewriter/rewriter.h"
|
||||
#include "ast/rewriter/rewriter_def.h"
|
||||
#include "smt/params/smt_params.h"
|
||||
#include "model/model.h"
|
||||
#include "muz/base/dl_util.h"
|
||||
#include "muz/pdr/pdr_manager.h"
|
||||
#include "muz/pdr/pdr_util.h"
|
||||
#include "model/model_smt2_pp.h"
|
||||
|
||||
|
||||
|
||||
namespace pdr {
|
||||
|
||||
unsigned ceil_log2(unsigned u) {
|
||||
if (u == 0) { return 0; }
|
||||
unsigned pow2 = next_power_of_two(u);
|
||||
return get_num_1bits(pow2-1);
|
||||
}
|
||||
|
||||
std::string pp_cube(const ptr_vector<expr>& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(const expr_ref_vector& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(const app_ref_vector& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(const app_vector& model, ast_manager& m) {
|
||||
return pp_cube(model.size(), model.c_ptr(), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(unsigned sz, app * const * lits, ast_manager& m) {
|
||||
return pp_cube(sz, (expr * const *)(lits), m);
|
||||
}
|
||||
|
||||
std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& m) {
|
||||
std::stringstream res;
|
||||
res << "(";
|
||||
expr * const * end = lits+sz;
|
||||
for (expr * const * it = lits; it!=end; it++) {
|
||||
res << mk_pp(*it, m);
|
||||
if (it+1!=end) {
|
||||
res << ", ";
|
||||
}
|
||||
}
|
||||
res << ")";
|
||||
return res.str();
|
||||
}
|
||||
|
||||
void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml) {
|
||||
ast_manager& m = fml.get_manager();
|
||||
expr_ref_vector conjs(m);
|
||||
flatten_and(fml, conjs);
|
||||
obj_map<expr, unsigned> diseqs;
|
||||
expr* n, *lhs, *rhs;
|
||||
for (unsigned i = 0; i < conjs.size(); ++i) {
|
||||
if (m.is_not(conjs[i].get(), n) &&
|
||||
m.is_eq(n, lhs, rhs)) {
|
||||
if (!m.is_value(rhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
if (!m.is_value(rhs)) {
|
||||
continue;
|
||||
}
|
||||
diseqs.insert_if_not_there2(lhs, 0)->get_data().m_value++;
|
||||
}
|
||||
}
|
||||
expr_substitution sub(m);
|
||||
|
||||
unsigned orig_size = conjs.size();
|
||||
unsigned num_deleted = 0;
|
||||
expr_ref val(m), tmp(m);
|
||||
proof_ref pr(m);
|
||||
pr = m.mk_asserted(m.mk_true());
|
||||
obj_map<expr, unsigned>::iterator it = diseqs.begin();
|
||||
obj_map<expr, unsigned>::iterator end = diseqs.end();
|
||||
for (; it != end; ++it) {
|
||||
if (it->m_value >= threshold) {
|
||||
model.eval(it->m_key, val);
|
||||
sub.insert(it->m_key, val, pr);
|
||||
conjs.push_back(m.mk_eq(it->m_key, val));
|
||||
num_deleted += it->m_value;
|
||||
}
|
||||
}
|
||||
if (orig_size < conjs.size()) {
|
||||
scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer(m);
|
||||
rep->set_substitution(&sub);
|
||||
for (unsigned i = 0; i < orig_size; ++i) {
|
||||
tmp = conjs[i].get();
|
||||
(*rep)(tmp);
|
||||
if (m.is_true(tmp)) {
|
||||
conjs[i] = conjs.back();
|
||||
SASSERT(orig_size <= conjs.size());
|
||||
conjs.pop_back();
|
||||
SASSERT(orig_size <= 1 + conjs.size());
|
||||
if (i + 1 == orig_size) {
|
||||
// no-op.
|
||||
}
|
||||
else if (orig_size <= conjs.size()) {
|
||||
// no-op
|
||||
}
|
||||
else {
|
||||
SASSERT(orig_size == 1 + conjs.size());
|
||||
--orig_size;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
else {
|
||||
conjs[i] = tmp;
|
||||
}
|
||||
}
|
||||
IF_VERBOSE(2, verbose_stream() << "Deleted " << num_deleted << " disequalities " << conjs.size() << " conjuncts\n";);
|
||||
}
|
||||
fml = m.mk_and(conjs.size(), conjs.c_ptr());
|
||||
}
|
||||
|
||||
class test_diff_logic {
|
||||
ast_manager& m;
|
||||
arith_util a;
|
||||
bv_util bv;
|
||||
bool m_is_dl;
|
||||
bool m_test_for_utvpi;
|
||||
|
||||
bool is_numeric(expr* e) const {
|
||||
if (a.is_numeral(e)) {
|
||||
return true;
|
||||
}
|
||||
expr* cond, *th, *el;
|
||||
if (m.is_ite(e, cond, th, el)) {
|
||||
return is_numeric(th) && is_numeric(el);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_arith_expr(expr *e) const {
|
||||
return is_app(e) && a.get_family_id() == to_app(e)->get_family_id();
|
||||
}
|
||||
|
||||
bool is_offset(expr* e) const {
|
||||
if (a.is_numeral(e)) {
|
||||
return true;
|
||||
}
|
||||
expr* cond, *th, *el, *e1, *e2;
|
||||
if (m.is_ite(e, cond, th, el)) {
|
||||
return is_offset(th) && is_offset(el);
|
||||
}
|
||||
// recognize offsets.
|
||||
if (a.is_add(e, e1, e2)) {
|
||||
if (is_numeric(e1)) {
|
||||
return is_offset(e2);
|
||||
}
|
||||
if (is_numeric(e2)) {
|
||||
return is_offset(e1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (m_test_for_utvpi) {
|
||||
if (a.is_mul(e, e1, e2)) {
|
||||
if (is_minus_one(e1)) {
|
||||
return is_offset(e2);
|
||||
}
|
||||
if (is_minus_one(e2)) {
|
||||
return is_offset(e1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return !is_arith_expr(e);
|
||||
}
|
||||
|
||||
bool is_minus_one(expr const * e) const {
|
||||
rational r; return a.is_numeral(e, r) && r.is_minus_one();
|
||||
}
|
||||
|
||||
bool test_ineq(expr* e) const {
|
||||
SASSERT(a.is_le(e) || a.is_ge(e) || m.is_eq(e));
|
||||
SASSERT(to_app(e)->get_num_args() == 2);
|
||||
expr * lhs = to_app(e)->get_arg(0);
|
||||
expr * rhs = to_app(e)->get_arg(1);
|
||||
if (is_offset(lhs) && is_offset(rhs))
|
||||
return true;
|
||||
if (!is_numeric(rhs))
|
||||
std::swap(lhs, rhs);
|
||||
if (!is_numeric(rhs))
|
||||
return false;
|
||||
// lhs can be 'x' or '(+ x (* -1 y))'
|
||||
if (is_offset(lhs))
|
||||
return true;
|
||||
expr* arg1, *arg2;
|
||||
if (!a.is_add(lhs, arg1, arg2))
|
||||
return false;
|
||||
// x
|
||||
if (m_test_for_utvpi) {
|
||||
return is_offset(arg1) && is_offset(arg2);
|
||||
}
|
||||
if (is_arith_expr(arg1))
|
||||
std::swap(arg1, arg2);
|
||||
if (is_arith_expr(arg1))
|
||||
return false;
|
||||
// arg2: (* -1 y)
|
||||
expr* m1, *m2;
|
||||
if (!a.is_mul(arg2, m1, m2))
|
||||
return false;
|
||||
return is_minus_one(m1) && is_offset(m2);
|
||||
}
|
||||
|
||||
bool test_eq(expr* e) const {
|
||||
expr* lhs = nullptr, *rhs = nullptr;
|
||||
VERIFY(m.is_eq(e, lhs, rhs));
|
||||
if (!a.is_int_real(lhs)) {
|
||||
return true;
|
||||
}
|
||||
if (a.is_numeral(lhs) || a.is_numeral(rhs)) {
|
||||
return test_ineq(e);
|
||||
}
|
||||
return
|
||||
test_term(lhs) &&
|
||||
test_term(rhs) &&
|
||||
!a.is_mul(lhs) &&
|
||||
!a.is_mul(rhs);
|
||||
}
|
||||
|
||||
bool test_term(expr* e) const {
|
||||
if (m.is_bool(e)) {
|
||||
return true;
|
||||
}
|
||||
if (a.is_numeral(e)) {
|
||||
return true;
|
||||
}
|
||||
if (is_offset(e)) {
|
||||
return true;
|
||||
}
|
||||
expr* lhs, *rhs;
|
||||
if (a.is_add(e, lhs, rhs)) {
|
||||
if (!a.is_numeral(lhs)) {
|
||||
std::swap(lhs, rhs);
|
||||
}
|
||||
return a.is_numeral(lhs) && is_offset(rhs);
|
||||
}
|
||||
if (a.is_mul(e, lhs, rhs)) {
|
||||
return is_minus_one(lhs) || is_minus_one(rhs);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_non_arith_or_basic(expr* e) {
|
||||
if (!is_app(e)) {
|
||||
return false;
|
||||
}
|
||||
family_id fid = to_app(e)->get_family_id();
|
||||
|
||||
if (fid == null_family_id &&
|
||||
!m.is_bool(e) &&
|
||||
to_app(e)->get_num_args() > 0) {
|
||||
return true;
|
||||
}
|
||||
return
|
||||
fid != m.get_basic_family_id() &&
|
||||
fid != null_family_id &&
|
||||
fid != a.get_family_id() &&
|
||||
fid != bv.get_family_id();
|
||||
}
|
||||
|
||||
public:
|
||||
test_diff_logic(ast_manager& m): m(m), a(m), bv(m), m_is_dl(true), m_test_for_utvpi(false) {}
|
||||
|
||||
void test_for_utvpi() { m_test_for_utvpi = true; }
|
||||
|
||||
void operator()(expr* e) {
|
||||
if (!m_is_dl) {
|
||||
return;
|
||||
}
|
||||
if (a.is_le(e) || a.is_ge(e)) {
|
||||
m_is_dl = test_ineq(e);
|
||||
}
|
||||
else if (m.is_eq(e)) {
|
||||
m_is_dl = test_eq(e);
|
||||
}
|
||||
else if (is_non_arith_or_basic(e)) {
|
||||
m_is_dl = false;
|
||||
}
|
||||
else if (is_app(e)) {
|
||||
app* a = to_app(e);
|
||||
for (unsigned i = 0; m_is_dl && i < a->get_num_args(); ++i) {
|
||||
m_is_dl = test_term(a->get_arg(i));
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_is_dl) {
|
||||
char const* msg = "non-diff: ";
|
||||
if (m_test_for_utvpi) {
|
||||
msg = "non-utvpi: ";
|
||||
}
|
||||
IF_VERBOSE(1, verbose_stream() << msg << mk_pp(e, m) << "\n";);
|
||||
}
|
||||
}
|
||||
|
||||
bool is_dl() const { return m_is_dl; }
|
||||
};
|
||||
|
||||
bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) {
|
||||
test_diff_logic test(m);
|
||||
expr_fast_mark1 mark;
|
||||
for (unsigned i = 0; i < num_fmls; ++i) {
|
||||
quick_for_each_expr(test, mark, fmls[i]);
|
||||
}
|
||||
return test.is_dl();
|
||||
}
|
||||
|
||||
bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls) {
|
||||
test_diff_logic test(m);
|
||||
test.test_for_utvpi();
|
||||
expr_fast_mark1 mark;
|
||||
for (unsigned i = 0; i < num_fmls; ++i) {
|
||||
quick_for_each_expr(test, mark, fmls[i]);
|
||||
}
|
||||
return test.is_dl();
|
||||
}
|
||||
|
||||
class arith_normalizer : public poly_rewriter<arith_rewriter_core> {
|
||||
ast_manager& m;
|
||||
arith_util m_util;
|
||||
enum op_kind { LE, GE, EQ };
|
||||
public:
|
||||
arith_normalizer(ast_manager& m, params_ref const& p = params_ref()): poly_rewriter<arith_rewriter_core>(m, p), m(m), m_util(m) {}
|
||||
|
||||
br_status mk_app_core(func_decl* f, unsigned num_args, expr* const* args, expr_ref& result) {
|
||||
br_status st = BR_FAILED;
|
||||
if (m.is_eq(f)) {
|
||||
SASSERT(num_args == 2); return mk_eq_core(args[0], args[1], result);
|
||||
}
|
||||
|
||||
if (f->get_family_id() != get_fid()) {
|
||||
return st;
|
||||
}
|
||||
switch (f->get_decl_kind()) {
|
||||
case OP_NUM: st = BR_FAILED; break;
|
||||
case OP_IRRATIONAL_ALGEBRAIC_NUM: st = BR_FAILED; break;
|
||||
case OP_LE: SASSERT(num_args == 2); st = mk_le_core(args[0], args[1], result); break;
|
||||
case OP_GE: SASSERT(num_args == 2); st = mk_ge_core(args[0], args[1], result); break;
|
||||
case OP_LT: SASSERT(num_args == 2); st = mk_lt_core(args[0], args[1], result); break;
|
||||
case OP_GT: SASSERT(num_args == 2); st = mk_gt_core(args[0], args[1], result); break;
|
||||
default: st = BR_FAILED; break;
|
||||
}
|
||||
return st;
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
br_status mk_eq_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
return mk_le_ge_eq_core(arg1, arg2, EQ, result);
|
||||
}
|
||||
br_status mk_le_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
return mk_le_ge_eq_core(arg1, arg2, LE, result);
|
||||
}
|
||||
br_status mk_ge_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
return mk_le_ge_eq_core(arg1, arg2, GE, result);
|
||||
}
|
||||
br_status mk_lt_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
result = m.mk_not(m_util.mk_ge(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
br_status mk_gt_core(expr* arg1, expr* arg2, expr_ref& result) {
|
||||
result = m.mk_not(m_util.mk_le(arg1, arg2));
|
||||
return BR_REWRITE2;
|
||||
}
|
||||
|
||||
br_status mk_le_ge_eq_core(expr* arg1, expr* arg2, op_kind kind, expr_ref& result) {
|
||||
if (m_util.is_real(arg1)) {
|
||||
numeral g(0);
|
||||
get_coeffs(arg1, g);
|
||||
get_coeffs(arg2, g);
|
||||
if (!g.is_one() && !g.is_zero()) {
|
||||
SASSERT(g.is_pos());
|
||||
expr_ref new_arg1 = rdiv_polynomial(arg1, g);
|
||||
expr_ref new_arg2 = rdiv_polynomial(arg2, g);
|
||||
switch(kind) {
|
||||
case LE: result = m_util.mk_le(new_arg1, new_arg2); return BR_DONE;
|
||||
case GE: result = m_util.mk_ge(new_arg1, new_arg2); return BR_DONE;
|
||||
case EQ: result = m_util.mk_eq(new_arg1, new_arg2); return BR_DONE;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
void update_coeff(numeral const& r, numeral& g) {
|
||||
if (g.is_zero() || abs(r) < g) {
|
||||
g = abs(r);
|
||||
}
|
||||
}
|
||||
|
||||
void get_coeffs(expr* e, numeral& g) {
|
||||
rational r;
|
||||
unsigned sz;
|
||||
expr* const* args = get_monomials(e, sz);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* arg = args[i];
|
||||
if (!m_util.is_numeral(arg, r)) {
|
||||
get_power_product(arg, r);
|
||||
}
|
||||
update_coeff(r, g);
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref rdiv_polynomial(expr* e, numeral const& g) {
|
||||
rational r;
|
||||
SASSERT(g.is_pos());
|
||||
SASSERT(!g.is_one());
|
||||
expr_ref_vector monomes(m);
|
||||
unsigned sz;
|
||||
expr* const* args = get_monomials(e, sz);
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* arg = args[i];
|
||||
if (m_util.is_numeral(arg, r)) {
|
||||
monomes.push_back(m_util.mk_numeral(r/g, false));
|
||||
}
|
||||
else {
|
||||
expr* p = get_power_product(arg, r);
|
||||
r /= g;
|
||||
if (r.is_one()) {
|
||||
monomes.push_back(p);
|
||||
}
|
||||
else {
|
||||
monomes.push_back(m_util.mk_mul(m_util.mk_numeral(r, false), p));
|
||||
}
|
||||
}
|
||||
}
|
||||
expr_ref result(m);
|
||||
mk_add(monomes.size(), monomes.c_ptr(), result);
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct arith_normalizer_cfg: public default_rewriter_cfg {
|
||||
arith_normalizer m_r;
|
||||
bool rewrite_patterns() const { return false; }
|
||||
br_status reduce_app(func_decl * f, unsigned num, expr * const * args, expr_ref & result, proof_ref & result_pr) {
|
||||
return m_r.mk_app_core(f, num, args, result);
|
||||
}
|
||||
arith_normalizer_cfg(ast_manager & m, params_ref const & p):m_r(m,p) {}
|
||||
};
|
||||
|
||||
class arith_normalizer_star : public rewriter_tpl<arith_normalizer_cfg> {
|
||||
arith_normalizer_cfg m_cfg;
|
||||
public:
|
||||
arith_normalizer_star(ast_manager & m, params_ref const & p):
|
||||
rewriter_tpl<arith_normalizer_cfg>(m, false, m_cfg),
|
||||
m_cfg(m, p) {}
|
||||
};
|
||||
|
||||
|
||||
void normalize_arithmetic(expr_ref& t) {
|
||||
ast_manager& m = t.get_manager();
|
||||
scoped_no_proof _sp(m);
|
||||
params_ref p;
|
||||
arith_normalizer_star rw(m, p);
|
||||
expr_ref tmp(m);
|
||||
rw(t, tmp);
|
||||
t = tmp;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template class rewriter_tpl<pdr::arith_normalizer_cfg>;
|
||||
|
||||
|
|
@ -1,81 +0,0 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pdr_util.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility functions for PDR.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef PDR_UTIL_H_
|
||||
#define PDR_UTIL_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
#include "ast/ast_pp.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "util/obj_hashtable.h"
|
||||
#include "util/ref_vector.h"
|
||||
#include "util/trace.h"
|
||||
#include "util/vector.h"
|
||||
#include "ast/arith_decl_plugin.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/bv_decl_plugin.h"
|
||||
|
||||
|
||||
class model;
|
||||
class model_core;
|
||||
|
||||
namespace pdr {
|
||||
|
||||
/**
|
||||
* Return the ceiling of base 2 logarithm of a number,
|
||||
* or zero if the nmber is zero.
|
||||
*/
|
||||
unsigned ceil_log2(unsigned u);
|
||||
|
||||
typedef ptr_vector<app> app_vector;
|
||||
typedef ptr_vector<func_decl> decl_vector;
|
||||
typedef obj_hashtable<func_decl> func_decl_set;
|
||||
|
||||
std::string pp_cube(const ptr_vector<expr>& model, ast_manager& manager);
|
||||
std::string pp_cube(const expr_ref_vector& model, ast_manager& manager);
|
||||
std::string pp_cube(const ptr_vector<app>& model, ast_manager& manager);
|
||||
std::string pp_cube(const app_ref_vector& model, ast_manager& manager);
|
||||
std::string pp_cube(unsigned sz, app * const * lits, ast_manager& manager);
|
||||
std::string pp_cube(unsigned sz, expr * const * lits, ast_manager& manager);
|
||||
|
||||
|
||||
/**
|
||||
\brief replace variables that are used in many disequalities by
|
||||
an equality using the model.
|
||||
|
||||
Assumption: the model satisfies the conjunctions.
|
||||
*/
|
||||
void reduce_disequalities(model& model, unsigned threshold, expr_ref& fml);
|
||||
|
||||
/**
|
||||
\brief normalize coefficients in polynomials so that least coefficient is 1.
|
||||
*/
|
||||
void normalize_arithmetic(expr_ref& t);
|
||||
|
||||
|
||||
/**
|
||||
\brief determine if formulas belong to difference logic or UTVPI fragment.
|
||||
*/
|
||||
bool is_difference_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls);
|
||||
|
||||
bool is_utvpi_logic(ast_manager& m, unsigned num_fmls, expr* const* fmls);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -869,7 +869,7 @@ namespace datalog {
|
|||
dm.set(*d, idx, BIT_1);
|
||||
result.intersect(dm, *d);
|
||||
}
|
||||
else if ((m.is_eq(g, e1, e2) || m.is_iff(g, e1, e2)) && m.is_bool(e1)) {
|
||||
else if (m.is_iff(g, e1, e2)) {
|
||||
udoc diff1, diff2;
|
||||
diff1.push_back(dm.allocateX());
|
||||
diff2.push_back(dm.allocateX());
|
||||
|
|
|
@ -8,18 +8,25 @@ z3_add_component(spacer
|
|||
spacer_generalizers.cpp
|
||||
spacer_manager.cpp
|
||||
spacer_prop_solver.cpp
|
||||
spacer_smt_context_manager.cpp
|
||||
spacer_sym_mux.cpp
|
||||
spacer_util.cpp
|
||||
spacer_itp_solver.cpp
|
||||
spacer_virtual_solver.cpp
|
||||
spacer_iuc_solver.cpp
|
||||
spacer_legacy_mbp.cpp
|
||||
spacer_proof_utils.cpp
|
||||
spacer_unsat_core_learner.cpp
|
||||
spacer_unsat_core_plugin.cpp
|
||||
spacer_matrix.cpp
|
||||
spacer_antiunify.cpp
|
||||
spacer_mev_array.cpp
|
||||
spacer_qe_project.cpp
|
||||
spacer_sem_matcher.cpp
|
||||
spacer_quant_generalizer.cpp
|
||||
spacer_callback.cpp
|
||||
spacer_json.cpp
|
||||
spacer_iuc_proof.cpp
|
||||
spacer_mbc.cpp
|
||||
spacer_pdr.cpp
|
||||
spacer_sat_answer.cpp
|
||||
COMPONENT_DEPENDENCIES
|
||||
arith_tactics
|
||||
core_tactics
|
||||
|
|
|
@ -28,6 +28,7 @@ Revision History:
|
|||
|
||||
namespace spacer {
|
||||
|
||||
|
||||
// Abstracts numeric values by variables
|
||||
struct var_abs_rewriter : public default_rewriter_cfg {
|
||||
ast_manager &m;
|
||||
|
@ -56,8 +57,8 @@ struct var_abs_rewriter : public default_rewriter_cfg {
|
|||
{
|
||||
bool contains_const_child = false;
|
||||
app* a = to_app(t);
|
||||
for (unsigned i=0, sz = a->get_num_args(); i < sz; ++i) {
|
||||
if (m_util.is_numeral(a->get_arg(i))) {
|
||||
for (expr * arg : *a) {
|
||||
if (m_util.is_numeral(arg)) {
|
||||
contains_const_child = true;
|
||||
}
|
||||
}
|
||||
|
@ -102,190 +103,73 @@ struct var_abs_rewriter : public default_rewriter_cfg {
|
|||
|
||||
};
|
||||
|
||||
/*
|
||||
* construct m_g, which is a generalization of t, where every constant
|
||||
* is replaced by a variable for any variable in m_g, remember the
|
||||
* substitution to get back t and save it in m_substitutions
|
||||
*/
|
||||
anti_unifier::anti_unifier(expr* t, ast_manager& man) : m(man), m_pinned(m), m_g(m)
|
||||
{
|
||||
m_pinned.push_back(t);
|
||||
|
||||
obj_map<expr, expr*> substitution;
|
||||
anti_unifier::anti_unifier(ast_manager &manager) : m(manager), m_pinned(m) {}
|
||||
|
||||
var_abs_rewriter var_abs_cfg(m, substitution);
|
||||
rewriter_tpl<var_abs_rewriter> var_abs_rw (m, false, var_abs_cfg);
|
||||
var_abs_rw (t, m_g);
|
||||
|
||||
m_substitutions.push_back(substitution); //TODO: refactor into vector, remove k
|
||||
void anti_unifier::reset() {
|
||||
m_subs.reset();
|
||||
m_cache.reset();
|
||||
m_todo.reset();
|
||||
m_pinned.reset();
|
||||
}
|
||||
|
||||
/* traverses m_g and t in parallel. if they only differ in constants
|
||||
* (i.e. m_g contains a variable, where t contains a constant), then
|
||||
* add the substitutions, which need to be applied to m_g to get t, to
|
||||
* m_substitutions.
|
||||
*/
|
||||
bool anti_unifier::add_term(expr* t) {
|
||||
m_pinned.push_back(t);
|
||||
void anti_unifier::operator()(expr *e1, expr *e2, expr_ref &res,
|
||||
substitution &s1, substitution &s2) {
|
||||
|
||||
ptr_vector<expr> todo;
|
||||
ptr_vector<expr> todo2;
|
||||
todo.push_back(m_g);
|
||||
todo2.push_back(t);
|
||||
reset();
|
||||
if (e1 == e2) {res = e1; s1.reset(); s2.reset(); return;}
|
||||
|
||||
ast_mark visited;
|
||||
m_todo.push_back(expr_pair(e1, e2));
|
||||
while (!m_todo.empty()) {
|
||||
const expr_pair &p = m_todo.back();
|
||||
SASSERT(is_app(p.first));
|
||||
SASSERT(is_app(p.second));
|
||||
|
||||
arith_util util(m);
|
||||
app * n1 = to_app(p.first);
|
||||
app * n2 = to_app(p.second);
|
||||
|
||||
obj_map<expr, expr*> substitution;
|
||||
|
||||
while (!todo.empty()) {
|
||||
expr* current = todo.back();
|
||||
todo.pop_back();
|
||||
expr* current2 = todo2.back();
|
||||
todo2.pop_back();
|
||||
|
||||
if (!visited.is_marked(current)) {
|
||||
visited.mark(current, true);
|
||||
|
||||
if (is_var(current)) {
|
||||
// TODO: for now we don't allow variables in the terms we want to antiunify
|
||||
SASSERT(m_substitutions[0].contains(current));
|
||||
if (util.is_numeral(current2)) {
|
||||
substitution.insert(current, current2);
|
||||
}
|
||||
else {return false;}
|
||||
}
|
||||
else {
|
||||
SASSERT(is_app(current));
|
||||
|
||||
if (is_app(current2) &&
|
||||
to_app(current)->get_decl() == to_app(current2)->get_decl() &&
|
||||
to_app(current)->get_num_args() == to_app(current2)->get_num_args()) {
|
||||
// TODO: what to do for numerals here? E.g. if we
|
||||
// have 1 and 2, do they have the same decl or are
|
||||
// the decls already different?
|
||||
SASSERT (!util.is_numeral(current) || current == current2);
|
||||
for (unsigned i = 0, num_args = to_app(current)->get_num_args();
|
||||
i < num_args; ++i) {
|
||||
todo.push_back(to_app(current)->get_arg(i));
|
||||
todo2.push_back(to_app(current2)->get_arg(i));
|
||||
}
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// we now know that the terms can be anti-unified, so add the cached substitution
|
||||
m_substitutions.push_back(substitution);
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* returns m_g, where additionally any variable, which has only equal
|
||||
* substitutions, is substituted with that substitution
|
||||
*/
|
||||
void anti_unifier::finalize() {
|
||||
ptr_vector<expr> todo;
|
||||
todo.push_back(m_g);
|
||||
|
||||
ast_mark visited;
|
||||
|
||||
obj_map<expr, expr*> generalization;
|
||||
|
||||
arith_util util(m);
|
||||
|
||||
// post-order traversel which ignores constants and handles them
|
||||
// directly when the enclosing term of the constant is handled
|
||||
while (!todo.empty()) {
|
||||
expr* current = todo.back();
|
||||
SASSERT(is_app(current));
|
||||
|
||||
// if we haven't already visited current
|
||||
if (!visited.is_marked(current)) {
|
||||
bool existsUnvisitedParent = false;
|
||||
|
||||
for (unsigned i = 0, sz = to_app(current)->get_num_args(); i < sz; ++i) {
|
||||
expr* argument = to_app(current)->get_arg(i);
|
||||
|
||||
if (!is_var(argument)) {
|
||||
SASSERT(is_app(argument));
|
||||
// if we haven't visited the current parent yet
|
||||
if(!visited.is_marked(argument)) {
|
||||
// add it to the stack
|
||||
todo.push_back(argument);
|
||||
existsUnvisitedParent = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we already visited all parents, we can visit current too
|
||||
if (!existsUnvisitedParent) {
|
||||
visited.mark(current, true);
|
||||
todo.pop_back();
|
||||
|
||||
ptr_buffer<expr> arg_list;
|
||||
for (unsigned i = 0, num_args = to_app(current)->get_num_args();
|
||||
i < num_args; ++i) {
|
||||
expr* argument = to_app(current)->get_arg(i);
|
||||
|
||||
if (is_var(argument)) {
|
||||
// compute whether there are different
|
||||
// substitutions for argument
|
||||
bool containsDifferentSubstitutions = false;
|
||||
|
||||
for (unsigned i=0, sz = m_substitutions.size(); i+1 < sz; ++i) {
|
||||
SASSERT(m_substitutions[i].contains(argument));
|
||||
SASSERT(m_substitutions[i+1].contains(argument));
|
||||
|
||||
// TODO: how to check equality?
|
||||
if (m_substitutions[i][argument] !=
|
||||
m_substitutions[i+1][argument])
|
||||
{
|
||||
containsDifferentSubstitutions = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if yes, use the variable
|
||||
if (containsDifferentSubstitutions) {
|
||||
arg_list.push_back(argument);
|
||||
}
|
||||
// otherwise use the concrete value instead
|
||||
// and remove the substitutions
|
||||
else
|
||||
{
|
||||
arg_list.push_back(m_substitutions[0][argument]);
|
||||
|
||||
for (unsigned i=0, sz = m_substitutions.size(); i < sz; ++i) {
|
||||
SASSERT(m_substitutions[i].contains(argument));
|
||||
m_substitutions[i].remove(argument);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
SASSERT(generalization.contains(argument));
|
||||
arg_list.push_back(generalization[argument]);
|
||||
}
|
||||
}
|
||||
|
||||
SASSERT(to_app(current)->get_num_args() == arg_list.size());
|
||||
expr_ref application(m.mk_app(to_app(current)->get_decl(),
|
||||
to_app(current)->get_num_args(),
|
||||
arg_list.c_ptr()), m);
|
||||
m_pinned.push_back(application);
|
||||
generalization.insert(current, application);
|
||||
}
|
||||
unsigned num_arg1 = n1->get_num_args();
|
||||
unsigned num_arg2 = n2->get_num_args();
|
||||
if (n1->get_decl() != n2->get_decl() || num_arg1 != num_arg2) {
|
||||
expr_ref v(m);
|
||||
v = m.mk_var(m_subs.size(), get_sort(n1));
|
||||
m_pinned.push_back(v);
|
||||
m_subs.push_back(expr_pair(n1, n2));
|
||||
m_cache.insert(n1, n2, v);
|
||||
}
|
||||
else {
|
||||
todo.pop_back();
|
||||
expr *tmp;
|
||||
unsigned todo_sz = m_todo.size();
|
||||
ptr_buffer<expr> kids;
|
||||
for (unsigned i = 0; i < num_arg1; ++i) {
|
||||
expr *arg1 = n1->get_arg(i);
|
||||
expr *arg2 = n2->get_arg(i);
|
||||
if (arg1 == arg2) {kids.push_back(arg1);}
|
||||
else if (m_cache.find(arg1, arg2, tmp)) {kids.push_back(tmp);}
|
||||
else {m_todo.push_back(expr_pair(arg1, arg2));}
|
||||
}
|
||||
if (m_todo.size() > todo_sz) {continue;}
|
||||
|
||||
expr_ref u(m);
|
||||
u = m.mk_app(n1->get_decl(), kids.size(), kids.c_ptr());
|
||||
m_pinned.push_back(u);
|
||||
m_cache.insert(n1, n2, u);
|
||||
}
|
||||
}
|
||||
|
||||
m_g = generalization[m_g];
|
||||
expr *r;
|
||||
VERIFY(m_cache.find(e1, e2, r));
|
||||
res = r;
|
||||
|
||||
// create substitutions
|
||||
s1.reserve(2, m_subs.size());
|
||||
s2.reserve(2, m_subs.size());
|
||||
|
||||
for (unsigned i = 0, sz = m_subs.size(); i < sz; ++i) {
|
||||
expr_pair p = m_subs.get(i);
|
||||
s1.insert(i, 0, expr_offset(p.first, 1));
|
||||
s2.insert(i, 0, expr_offset(p.second, 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -318,6 +202,8 @@ public:
|
|||
*/
|
||||
bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m,
|
||||
expr_ref& result) {
|
||||
NOT_IMPLEMENTED_YET();
|
||||
#if 0
|
||||
arith_util util(m);
|
||||
|
||||
SASSERT(au.get_num_substitutions() > 0);
|
||||
|
@ -411,6 +297,7 @@ bool naive_convex_closure::compute_closure(anti_unifier& au, ast_manager& m,
|
|||
result = expr_ref(m.mk_exists(vars.size(), sorts.c_ptr(), names.c_ptr(), body),m);
|
||||
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool naive_convex_closure::get_range(vector<unsigned int>& v,
|
||||
|
@ -453,7 +340,83 @@ void naive_convex_closure::substitute_vars_by_const(ast_manager& m, expr* t,
|
|||
subs_rw (t, res);
|
||||
}
|
||||
|
||||
|
||||
/// Construct a pattern by abstracting all numbers by variables
|
||||
struct mk_num_pat_rewriter : public default_rewriter_cfg {
|
||||
ast_manager &m;
|
||||
arith_util m_arith;
|
||||
|
||||
// -- mark already seen expressions
|
||||
ast_mark m_seen;
|
||||
// -- true if the expression is known to have a number as a sub-expression
|
||||
ast_mark m_has_num;
|
||||
// -- expressions created during the transformation
|
||||
expr_ref_vector m_pinned;
|
||||
// -- map from introduced variables to expressions they replace
|
||||
app_ref_vector &m_subs;
|
||||
|
||||
|
||||
// -- stack of expressions being processed to have access to expressions
|
||||
// -- before rewriting
|
||||
ptr_buffer<expr> m_stack;
|
||||
|
||||
mk_num_pat_rewriter (ast_manager &manager, app_ref_vector& subs) :
|
||||
m(manager), m_arith(m), m_pinned(m), m_subs(subs) {}
|
||||
|
||||
bool pre_visit(expr * t) {
|
||||
// -- don't touch multiplication
|
||||
if (m_arith.is_mul(t)) return false;
|
||||
|
||||
bool r = (!m_seen.is_marked(t) || m_has_num.is_marked(t));
|
||||
if (r) {m_stack.push_back (t);}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
br_status reduce_app (func_decl * f, unsigned num, expr * const * args,
|
||||
expr_ref & result, proof_ref & result_pr) {
|
||||
expr *s;
|
||||
s = m_stack.back();
|
||||
m_stack.pop_back();
|
||||
if (is_app(s)) {
|
||||
app *a = to_app(s);
|
||||
for (unsigned i = 0, sz = a->get_num_args(); i < sz; ++i) {
|
||||
if (m_has_num.is_marked(a->get_arg(i))) {
|
||||
m_has_num.mark(a, true);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool cache_all_results() const { return false; }
|
||||
bool cache_results() const { return false; }
|
||||
|
||||
bool get_subst(expr * s, expr * & t, proof * & t_pr) {
|
||||
if (m_arith.is_numeral(s)) {
|
||||
t = m.mk_var(m_subs.size(), m.get_sort(s));
|
||||
m_pinned.push_back(t);
|
||||
m_subs.push_back(to_app(s));
|
||||
|
||||
m_has_num.mark(t, true);
|
||||
m_seen.mark(t, true);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs) {
|
||||
SASSERT(subs.empty());
|
||||
mk_num_pat_rewriter rw_cfg(result.m(), subs);
|
||||
rewriter_tpl<mk_num_pat_rewriter> rw(result.m(), false, rw_cfg);
|
||||
rw(e, result);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
template class rewriter_tpl<spacer::var_abs_rewriter>;
|
||||
template class rewriter_tpl<spacer::subs_rewriter_cfg>;
|
||||
template class rewriter_tpl<spacer::mk_num_pat_rewriter>;
|
||||
|
|
|
@ -22,32 +22,36 @@ Revision History:
|
|||
#define _SPACER_ANTIUNIFY_H_
|
||||
|
||||
#include "ast/ast.h"
|
||||
|
||||
#include "ast/substitution/substitution.h"
|
||||
#include "util/obj_pair_hashtable.h"
|
||||
namespace spacer {
|
||||
/**
|
||||
\brief Anti-unifier for ground expressions
|
||||
*/
|
||||
class anti_unifier
|
||||
{
|
||||
public:
|
||||
anti_unifier(expr* t, ast_manager& m);
|
||||
~anti_unifier() {}
|
||||
typedef std::pair<expr *, expr *> expr_pair;
|
||||
typedef pair_hash<obj_ptr_hash<expr>, obj_ptr_hash<expr> > expr_pair_hash;
|
||||
typedef obj_pair_map<expr, expr, expr*> cache_ty;
|
||||
|
||||
bool add_term(expr* t);
|
||||
void finalize();
|
||||
|
||||
expr* get_generalization() {return m_g;}
|
||||
unsigned get_num_substitutions() {return m_substitutions.size();}
|
||||
obj_map<expr, expr*> get_substitution(unsigned index){
|
||||
SASSERT(index < m_substitutions.size());
|
||||
return m_substitutions[index];
|
||||
}
|
||||
|
||||
private:
|
||||
ast_manager& m;
|
||||
// tracking all created expressions
|
||||
ast_manager &m;
|
||||
expr_ref_vector m_pinned;
|
||||
|
||||
expr_ref m_g;
|
||||
svector<expr_pair> m_todo;
|
||||
cache_ty m_cache;
|
||||
svector<expr_pair> m_subs;
|
||||
|
||||
vector<obj_map<expr, expr*>> m_substitutions;
|
||||
public:
|
||||
anti_unifier(ast_manager& m);
|
||||
|
||||
void reset();
|
||||
|
||||
/**
|
||||
\brief Computes anti-unifier of two ground expressions. Returns
|
||||
the anti-unifier and the corresponding substitutions
|
||||
*/
|
||||
void operator() (expr *e1, expr *e2, expr_ref &res,
|
||||
substitution &s1, substitution &s2);
|
||||
};
|
||||
|
||||
class naive_convex_closure
|
||||
|
@ -63,5 +67,8 @@ private:
|
|||
expr_ref& res);
|
||||
};
|
||||
|
||||
/// Abstracts numbers in the given ground expression by variables
|
||||
/// Returns the created pattern and the corresponding substitution.
|
||||
void mk_num_pat(expr *e, expr_ref &result, app_ref_vector &subs);
|
||||
}
|
||||
#endif
|
||||
|
|
38
src/muz/spacer/spacer_callback.cpp
Normal file
38
src/muz/spacer/spacer_callback.cpp
Normal file
|
@ -0,0 +1,38 @@
|
|||
/**++
|
||||
Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_callback.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SPACER plugin for handling events
|
||||
|
||||
Author:
|
||||
|
||||
Matteo Marescotti
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include "spacer_callback.h"
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
|
||||
|
||||
namespace spacer {
|
||||
|
||||
void user_callback::new_lemma_eh(expr *lemma, unsigned level) {
|
||||
m_new_lemma_eh(m_state, lemma, level);
|
||||
}
|
||||
|
||||
void user_callback::predecessor_eh() {
|
||||
m_predecessor_eh(m_state);
|
||||
}
|
||||
|
||||
void user_callback::unfold_eh() {
|
||||
m_unfold_eh(m_state);
|
||||
}
|
||||
|
||||
}
|
65
src/muz/spacer/spacer_callback.h
Normal file
65
src/muz/spacer/spacer_callback.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/**++
|
||||
Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_callback.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SPACER plugin for handling events
|
||||
|
||||
Author:
|
||||
|
||||
Matteo Marescotti
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPACER_CALLBACK_H_
|
||||
#define _SPACER_CALLBACK_H_
|
||||
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/base/dl_engine_base.h"
|
||||
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class user_callback : public spacer_callback {
|
||||
private:
|
||||
void *m_state;
|
||||
const datalog::t_new_lemma_eh m_new_lemma_eh;
|
||||
const datalog::t_predecessor_eh m_predecessor_eh;
|
||||
const datalog::t_unfold_eh m_unfold_eh;
|
||||
|
||||
public:
|
||||
user_callback(context &context,
|
||||
void *state,
|
||||
const datalog::t_new_lemma_eh new_lemma_eh,
|
||||
const datalog::t_predecessor_eh predecessor_eh,
|
||||
const datalog::t_unfold_eh unfold_eh) :
|
||||
spacer_callback(context),
|
||||
m_state(state),
|
||||
m_new_lemma_eh(new_lemma_eh),
|
||||
m_predecessor_eh(predecessor_eh),
|
||||
m_unfold_eh(unfold_eh) {}
|
||||
|
||||
inline bool new_lemma() override { return m_new_lemma_eh != nullptr; }
|
||||
|
||||
void new_lemma_eh(expr *lemma, unsigned level) override;
|
||||
|
||||
inline bool predecessor() override { return m_predecessor_eh != nullptr; }
|
||||
|
||||
void predecessor_eh() override;
|
||||
|
||||
inline bool unfold() override { return m_unfold_eh != nullptr; }
|
||||
|
||||
void unfold_eh() override;
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //_SPACER_CALLBACK_H_
|
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
@ -34,6 +34,7 @@ Revision History:
|
|||
#include "model/model_smt2_pp.h"
|
||||
#include "ast/scoped_proof.h"
|
||||
#include "muz/transforms/dl_transforms.h"
|
||||
#include "muz/spacer/spacer_callback.h"
|
||||
|
||||
using namespace spacer;
|
||||
|
||||
|
@ -92,19 +93,14 @@ lbool dl_interface::query(expr * query)
|
|||
datalog::rule_set old_rules(rules0);
|
||||
func_decl_ref query_pred(m);
|
||||
rm.mk_query(query, m_ctx.get_rules());
|
||||
expr_ref bg_assertion = m_ctx.get_background_assertion();
|
||||
|
||||
check_reset();
|
||||
|
||||
TRACE("spacer",
|
||||
if (!m.is_true(bg_assertion)) {
|
||||
tout << "axioms:\n";
|
||||
tout << mk_pp(bg_assertion, m) << "\n";
|
||||
}
|
||||
tout << "query: " << mk_pp(query, m) << "\n";
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
);
|
||||
tout << "query: " << mk_pp(query, m) << "\n";
|
||||
tout << "rules:\n";
|
||||
m_ctx.display_rules(tout);
|
||||
);
|
||||
|
||||
|
||||
apply_default_transformation(m_ctx);
|
||||
|
@ -160,7 +156,6 @@ lbool dl_interface::query(expr * query)
|
|||
m_context->set_proof_converter(m_ctx.get_proof_converter());
|
||||
m_context->set_model_converter(m_ctx.get_model_converter());
|
||||
m_context->set_query(query_pred);
|
||||
m_context->set_axioms(bg_assertion);
|
||||
m_context->update_rules(m_spacer_rules);
|
||||
|
||||
if (m_spacer_rules.get_rules().empty()) {
|
||||
|
@ -169,7 +164,7 @@ lbool dl_interface::query(expr * query)
|
|||
return l_false;
|
||||
}
|
||||
|
||||
return m_context->solve();
|
||||
return m_context->solve(m_ctx.get_params().spacer_min_level());
|
||||
|
||||
}
|
||||
|
||||
|
@ -254,7 +249,6 @@ lbool dl_interface::query_from_lvl(expr * query, unsigned lvl)
|
|||
m_context->set_proof_converter(m_ctx.get_proof_converter());
|
||||
m_context->set_model_converter(m_ctx.get_model_converter());
|
||||
m_context->set_query(query_pred);
|
||||
m_context->set_axioms(bg_assertion);
|
||||
m_context->update_rules(m_spacer_rules);
|
||||
|
||||
if (m_spacer_rules.get_rules().empty()) {
|
||||
|
@ -352,3 +346,14 @@ proof_ref dl_interface::get_proof()
|
|||
{
|
||||
return m_context->get_proof();
|
||||
}
|
||||
|
||||
void dl_interface::add_callback(void *state,
|
||||
const datalog::t_new_lemma_eh new_lemma_eh,
|
||||
const datalog::t_predecessor_eh predecessor_eh,
|
||||
const datalog::t_unfold_eh unfold_eh){
|
||||
m_context->callbacks().push_back(alloc(user_callback, *m_context, state, new_lemma_eh, predecessor_eh, unfold_eh));
|
||||
}
|
||||
|
||||
void dl_interface::add_constraint (expr *c, unsigned lvl){
|
||||
m_context->add_constraint(c, lvl);
|
||||
}
|
||||
|
|
|
@ -79,6 +79,13 @@ public:
|
|||
|
||||
proof_ref get_proof() override;
|
||||
|
||||
void add_callback(void *state,
|
||||
const datalog::t_new_lemma_eh new_lemma_eh,
|
||||
const datalog::t_predecessor_eh predecessor_eh,
|
||||
const datalog::t_unfold_eh unfold_eh) override;
|
||||
|
||||
void add_constraint (expr *c, unsigned lvl) override;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -24,12 +24,6 @@ Revision History:
|
|||
|
||||
namespace spacer {
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class farkas_learner {
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
|
||||
|
|
|
@ -21,11 +21,16 @@ Revision History:
|
|||
|
||||
#include "muz/spacer/spacer_context.h"
|
||||
#include "muz/spacer/spacer_generalizers.h"
|
||||
#include "ast/ast_util.h"
|
||||
#include "ast/expr_abstract.h"
|
||||
#include "ast/rewriter/var_subst.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/factor_equivs.h"
|
||||
|
||||
#include "ast/rewriter/expr_safe_replace.h"
|
||||
#include "ast/substitution/matcher.h"
|
||||
#include "ast/expr_functors.h"
|
||||
#include "smt/smt_solver.h"
|
||||
#include "qe/qe_term_graph.h"
|
||||
|
||||
namespace spacer {
|
||||
void lemma_sanity_checker::operator()(lemma_ref &lemma) {
|
||||
|
@ -33,9 +38,23 @@ void lemma_sanity_checker::operator()(lemma_ref &lemma) {
|
|||
expr_ref_vector cube(lemma->get_ast_manager());
|
||||
cube.append(lemma->get_cube());
|
||||
ENSURE(lemma->get_pob()->pt().check_inductive(lemma->level(),
|
||||
cube, uses_level));
|
||||
cube, uses_level,
|
||||
lemma->weakness()));
|
||||
}
|
||||
|
||||
namespace{
|
||||
class contains_array_op_proc : public i_expr_pred {
|
||||
ast_manager &m;
|
||||
family_id m_array_fid;
|
||||
public:
|
||||
contains_array_op_proc(ast_manager &manager) :
|
||||
m(manager), m_array_fid(m.mk_family_id("array"))
|
||||
{}
|
||||
virtual bool operator()(expr *e) {
|
||||
return is_app(e) && to_app(e)->get_family_id() == m_array_fid;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// ------------------------
|
||||
// lemma_bool_inductive_generalizer
|
||||
|
@ -50,6 +69,9 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) {
|
|||
pred_transformer &pt = lemma->get_pob()->pt();
|
||||
ast_manager &m = pt.get_ast_manager();
|
||||
|
||||
contains_array_op_proc has_array_op(m);
|
||||
check_pred has_arrays(has_array_op, m);
|
||||
|
||||
expr_ref_vector cube(m);
|
||||
cube.append(lemma->get_cube());
|
||||
|
||||
|
@ -58,14 +80,21 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) {
|
|||
ptr_vector<expr> processed;
|
||||
expr_ref_vector extra_lits(m);
|
||||
|
||||
unsigned weakness = lemma->weakness();
|
||||
|
||||
unsigned i = 0, num_failures = 0;
|
||||
while (i < cube.size() &&
|
||||
(!m_failure_limit || num_failures < m_failure_limit)) {
|
||||
expr_ref lit(m);
|
||||
lit = cube.get(i);
|
||||
if (m_array_only && !has_arrays(lit)) {
|
||||
processed.push_back(lit);
|
||||
++i;
|
||||
continue;
|
||||
}
|
||||
cube[i] = true_expr;
|
||||
if (cube.size() > 1 &&
|
||||
pt.check_inductive(lemma->level(), cube, uses_level)) {
|
||||
pt.check_inductive(lemma->level(), cube, uses_level, weakness)) {
|
||||
num_failures = 0;
|
||||
dirty = true;
|
||||
for (i = 0; i < cube.size() &&
|
||||
|
@ -82,7 +111,7 @@ void lemma_bool_inductive_generalizer::operator()(lemma_ref &lemma) {
|
|||
SASSERT(extra_lits.size() > 1);
|
||||
for (unsigned j = 0, sz = extra_lits.size(); !found && j < sz; ++j) {
|
||||
cube[i] = extra_lits.get(j);
|
||||
if (pt.check_inductive(lemma->level(), cube, uses_level)) {
|
||||
if (pt.check_inductive(lemma->level(), cube, uses_level, weakness)) {
|
||||
num_failures = 0;
|
||||
dirty = true;
|
||||
found = true;
|
||||
|
@ -130,10 +159,11 @@ void unsat_core_generalizer::operator()(lemma_ref &lemma)
|
|||
|
||||
unsigned old_sz = lemma->get_cube().size();
|
||||
unsigned old_level = lemma->level();
|
||||
(void)old_level;
|
||||
|
||||
unsigned uses_level;
|
||||
expr_ref_vector core(m);
|
||||
VERIFY(pt.is_invariant(old_level, lemma->get_expr(), uses_level, &core));
|
||||
VERIFY(pt.is_invariant(lemma->level(), lemma.get(), uses_level, &core));
|
||||
|
||||
CTRACE("spacer", old_sz > core.size(),
|
||||
tout << "unsat core reduced lemma from: "
|
||||
|
@ -176,118 +206,131 @@ public:
|
|||
};
|
||||
}
|
||||
|
||||
bool lemma_array_eq_generalizer::is_array_eq (ast_manager &m, expr* e) {
|
||||
|
||||
expr *e1 = nullptr, *e2 = nullptr;
|
||||
if (m.is_eq(e, e1, e2) && is_app(e1) && is_app(e2)) {
|
||||
app *a1 = to_app(e1);
|
||||
app *a2 = to_app(e2);
|
||||
array_util au(m);
|
||||
if (a1->get_family_id() == null_family_id &&
|
||||
a2->get_family_id() == null_family_id &&
|
||||
au.is_array(a1) && au.is_array(a2))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void lemma_array_eq_generalizer::operator() (lemma_ref &lemma)
|
||||
{
|
||||
TRACE("core_array_eq", tout << "Looking for equalities\n";);
|
||||
|
||||
// -- find array constants
|
||||
ast_manager &m = lemma->get_ast_manager();
|
||||
manager &pm = m_ctx.get_manager();
|
||||
(void)pm;
|
||||
|
||||
|
||||
expr_ref_vector core(m);
|
||||
expr_ref v(m);
|
||||
func_decl_set symb;
|
||||
collect_array_proc cap(m, symb);
|
||||
|
||||
|
||||
// -- find array constants
|
||||
core.append (lemma->get_cube());
|
||||
v = mk_and(core);
|
||||
for_each_expr(cap, v);
|
||||
|
||||
TRACE("core_array_eq",
|
||||
CTRACE("core_array_eq", symb.size() > 1 && symb.size() <= 8,
|
||||
tout << "found " << symb.size() << " array variables in: \n"
|
||||
<< mk_pp(v, m) << "\n";);
|
||||
<< v << "\n";);
|
||||
|
||||
// too few constants
|
||||
if (symb.size() <= 1) { return; }
|
||||
// too many constants, skip this
|
||||
if (symb.size() >= 8) { return; }
|
||||
// too few constants or too many constants
|
||||
if (symb.size() <= 1 || symb.size() > 8) { return; }
|
||||
|
||||
|
||||
// -- for every pair of variables, try an equality
|
||||
typedef func_decl_set::iterator iterator;
|
||||
// -- for every pair of constants (A, B), check whether the
|
||||
// -- equality (A=B) generalizes a literal in the lemma
|
||||
|
||||
ptr_vector<func_decl> vsymbs;
|
||||
for (iterator it = symb.begin(), end = symb.end();
|
||||
it != end; ++it)
|
||||
{ vsymbs.push_back(*it); }
|
||||
for (auto * fdecl : symb) {vsymbs.push_back(fdecl);}
|
||||
|
||||
// create all equalities
|
||||
expr_ref_vector eqs(m);
|
||||
for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i) {
|
||||
for (unsigned j = i + 1; j < sz; ++j) {
|
||||
eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)),
|
||||
m.mk_const(vsymbs.get(j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0, sz = vsymbs.size(); i < sz; ++i)
|
||||
for (unsigned j = i + 1; j < sz; ++j)
|
||||
{ eqs.push_back(m.mk_eq(m.mk_const(vsymbs.get(i)),
|
||||
m.mk_const(vsymbs.get(j)))); }
|
||||
|
||||
smt::kernel solver(m, m_ctx.get_manager().fparams2());
|
||||
// smt-solver to check whether a literal is generalized. using
|
||||
// default params. There has to be a simpler way to approximate
|
||||
// this check
|
||||
ref<solver> sol = mk_smt_solver(m, params_ref::get_empty(), symbol::null);
|
||||
// literals of the new lemma
|
||||
expr_ref_vector lits(m);
|
||||
for (unsigned i = 0, core_sz = core.size(); i < core_sz; ++i) {
|
||||
SASSERT(lits.size() == i);
|
||||
solver.push();
|
||||
solver.assert_expr(core.get(i));
|
||||
for (unsigned j = 0, eqs_sz = eqs.size(); j < eqs_sz; ++j) {
|
||||
solver.push();
|
||||
solver.assert_expr(eqs.get(j));
|
||||
lbool res = solver.check();
|
||||
solver.pop(1);
|
||||
lits.append(core);
|
||||
expr *t = nullptr;
|
||||
bool dirty = false;
|
||||
for (unsigned i = 0, sz = core.size(); i < sz; ++i) {
|
||||
// skip a literal is it is already an array equality
|
||||
if (m.is_not(lits.get(i), t) && is_array_eq(m, t)) continue;
|
||||
solver::scoped_push _pp_(*sol);
|
||||
sol->assert_expr(lits.get(i));
|
||||
for (auto *e : eqs) {
|
||||
solver::scoped_push _p_(*sol);
|
||||
sol->assert_expr(e);
|
||||
lbool res = sol->check_sat(0, nullptr);
|
||||
|
||||
if (res == l_false) {
|
||||
TRACE("core_array_eq",
|
||||
tout << "strengthened " << mk_pp(core.get(i), m)
|
||||
<< " with " << mk_pp(m.mk_not(eqs.get(j)), m) << "\n";);
|
||||
lits.push_back(m.mk_not(eqs.get(j)));
|
||||
tout << "strengthened " << mk_pp(lits.get(i), m)
|
||||
<< " with " << mk_pp(mk_not(m, e), m) << "\n";);
|
||||
lits[i] = mk_not(m, e);
|
||||
dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
solver.pop(1);
|
||||
if (lits.size() == i) { lits.push_back(core.get(i)); }
|
||||
}
|
||||
|
||||
/**
|
||||
HACK: if the first 3 arguments of pt are boolean, assume
|
||||
they correspond to SeaHorn encoding and condition the equality on them.
|
||||
*/
|
||||
// pred_transformer &pt = n.pt ();
|
||||
// if (pt.sig_size () >= 3 &&
|
||||
// m.is_bool (pt.sig (0)->get_range ()) &&
|
||||
// m.is_bool (pt.sig (1)->get_range ()) &&
|
||||
// m.is_bool (pt.sig (2)->get_range ()))
|
||||
// {
|
||||
// lits.push_back (m.mk_const (pm.o2n(pt.sig (0), 0)));
|
||||
// lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (1), 0))));
|
||||
// lits.push_back (m.mk_not (m.mk_const (pm.o2n(pt.sig (2), 0))));
|
||||
// }
|
||||
// nothing changed
|
||||
if (!dirty) return;
|
||||
|
||||
TRACE("core_array_eq", tout << "new possible core "
|
||||
<< mk_pp(pm.mk_and(lits), m) << "\n";);
|
||||
TRACE("core_array_eq",
|
||||
tout << "new possible core " << mk_and(lits) << "\n";);
|
||||
|
||||
|
||||
pred_transformer &pt = lemma->get_pob()->pt();
|
||||
// -- check if it is consistent with the transition relation
|
||||
// -- check if the generalized result is consistent with trans
|
||||
unsigned uses_level1;
|
||||
if (pt.check_inductive(lemma->level(), lits, uses_level1)) {
|
||||
if (pt.check_inductive(lemma->level(), lits, uses_level1, lemma->weakness())) {
|
||||
TRACE("core_array_eq", tout << "Inductive!\n";);
|
||||
lemma->update_cube(lemma->get_pob(),lits);
|
||||
lemma->update_cube(lemma->get_pob(), lits);
|
||||
lemma->set_level(uses_level1);
|
||||
return;
|
||||
} else
|
||||
{ TRACE("core_array_eq", tout << "Not-Inductive!\n";);}
|
||||
}
|
||||
else
|
||||
{TRACE("core_array_eq", tout << "Not-Inductive!\n";);}
|
||||
}
|
||||
|
||||
void lemma_eq_generalizer::operator() (lemma_ref &lemma)
|
||||
{
|
||||
TRACE("core_eq", tout << "Transforming equivalence classes\n";);
|
||||
|
||||
ast_manager &m = m_ctx.get_ast_manager();
|
||||
expr_ref_vector core(m);
|
||||
core.append (lemma->get_cube());
|
||||
if (lemma->get_cube().empty()) return;
|
||||
|
||||
bool dirty;
|
||||
expr_equiv_class eq_classes(m);
|
||||
factor_eqs(core, eq_classes);
|
||||
// create all possible equalities to allow for simple inductive generalization
|
||||
dirty = equiv_to_expr_full(eq_classes, core);
|
||||
if (dirty) {
|
||||
ast_manager &m = m_ctx.get_ast_manager();
|
||||
qe::term_graph egraph(m);
|
||||
egraph.add_lits(lemma->get_cube());
|
||||
|
||||
// -- expand the cube with all derived equalities
|
||||
expr_ref_vector core(m);
|
||||
egraph.to_lits(core, true);
|
||||
|
||||
// -- if the core looks different from the original cube
|
||||
if (core.size() != lemma->get_cube().size() ||
|
||||
core.get(0) != lemma->get_cube().get(0)) {
|
||||
// -- update the lemma
|
||||
lemma->update_cube(lemma->get_pob(), core);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -48,11 +48,14 @@ class lemma_bool_inductive_generalizer : public lemma_generalizer {
|
|||
};
|
||||
|
||||
unsigned m_failure_limit;
|
||||
bool m_array_only;
|
||||
stats m_st;
|
||||
|
||||
public:
|
||||
lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit) :
|
||||
lemma_generalizer(ctx), m_failure_limit(failure_limit) {}
|
||||
lemma_bool_inductive_generalizer(context& ctx, unsigned failure_limit,
|
||||
bool array_only = false) :
|
||||
lemma_generalizer(ctx), m_failure_limit(failure_limit),
|
||||
m_array_only(array_only) {}
|
||||
~lemma_bool_inductive_generalizer() override {}
|
||||
void operator()(lemma_ref &lemma) override;
|
||||
|
||||
|
@ -80,6 +83,8 @@ public:
|
|||
};
|
||||
|
||||
class lemma_array_eq_generalizer : public lemma_generalizer {
|
||||
private:
|
||||
bool is_array_eq(ast_manager &m, expr *e);
|
||||
public:
|
||||
lemma_array_eq_generalizer(context &ctx) : lemma_generalizer(ctx) {}
|
||||
~lemma_array_eq_generalizer() override {}
|
||||
|
@ -94,6 +99,45 @@ public:
|
|||
void operator()(lemma_ref &lemma) override;
|
||||
};
|
||||
|
||||
class lemma_quantifier_generalizer : public lemma_generalizer {
|
||||
struct stats {
|
||||
unsigned count;
|
||||
unsigned num_failures;
|
||||
stopwatch watch;
|
||||
stats() {reset();}
|
||||
void reset() {count = 0; num_failures = 0; watch.reset();}
|
||||
};
|
||||
|
||||
ast_manager &m;
|
||||
arith_util m_arith;
|
||||
stats m_st;
|
||||
expr_ref_vector m_cube;
|
||||
|
||||
bool m_normalize_cube;
|
||||
int m_offset;
|
||||
public:
|
||||
lemma_quantifier_generalizer(context &ctx, bool normalize_cube = true);
|
||||
virtual ~lemma_quantifier_generalizer() {}
|
||||
virtual void operator()(lemma_ref &lemma);
|
||||
|
||||
virtual void collect_statistics(statistics& st) const;
|
||||
virtual void reset_statistics() {m_st.reset();}
|
||||
private:
|
||||
bool generalize(lemma_ref &lemma, app *term);
|
||||
|
||||
void find_candidates(expr *e, app_ref_vector &candidate);
|
||||
bool is_ub(var *var, expr *e);
|
||||
bool is_lb(var *var, expr *e);
|
||||
void mk_abs_cube (lemma_ref &lemma, app *term, var *var,
|
||||
expr_ref_vector &gnd_cube,
|
||||
expr_ref_vector &abs_cube,
|
||||
expr *&lb, expr *&ub, unsigned &stride);
|
||||
|
||||
bool match_sk_idx(expr *e, app_ref_vector const &zks, expr *&idx, app *&sk);
|
||||
void cleanup(expr_ref_vector& cube, app_ref_vector const &zks, expr_ref &bind);
|
||||
|
||||
bool find_stride(expr_ref_vector &c, expr_ref &pattern, unsigned &stride);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,355 +0,0 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_itp_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A solver that produces interpolated unsat cores
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"muz/spacer/spacer_itp_solver.h"
|
||||
#include"ast/ast.h"
|
||||
#include"muz/spacer/spacer_util.h"
|
||||
#include"muz/spacer/spacer_farkas_learner.h"
|
||||
#include"ast/rewriter/expr_replacer.h"
|
||||
#include"muz/spacer/spacer_unsat_core_learner.h"
|
||||
#include"muz/spacer/spacer_unsat_core_plugin.h"
|
||||
|
||||
namespace spacer {
|
||||
void itp_solver::push ()
|
||||
{
|
||||
m_defs.push_back (def_manager (*this));
|
||||
m_solver.push ();
|
||||
}
|
||||
|
||||
void itp_solver::pop (unsigned n)
|
||||
{
|
||||
m_solver.pop (n);
|
||||
unsigned lvl = m_defs.size ();
|
||||
SASSERT (n <= lvl);
|
||||
unsigned new_lvl = lvl-n;
|
||||
while (m_defs.size() > new_lvl) {
|
||||
m_num_proxies -= m_defs.back ().m_defs.size ();
|
||||
m_defs.pop_back ();
|
||||
}
|
||||
}
|
||||
|
||||
app* itp_solver::fresh_proxy ()
|
||||
{
|
||||
if (m_num_proxies == m_proxies.size()) {
|
||||
std::stringstream name;
|
||||
name << "spacer_proxy!" << m_proxies.size ();
|
||||
app_ref res(m);
|
||||
res = m.mk_const (symbol (name.str ().c_str ()),
|
||||
m.mk_bool_sort ());
|
||||
m_proxies.push_back (res);
|
||||
|
||||
// -- add the new proxy to proxy eliminator
|
||||
proof_ref pr(m);
|
||||
pr = m.mk_asserted (m.mk_true ());
|
||||
m_elim_proxies_sub.insert (res, m.mk_true (), pr);
|
||||
|
||||
}
|
||||
return m_proxies.get (m_num_proxies++);
|
||||
}
|
||||
|
||||
app* itp_solver::mk_proxy (expr *v)
|
||||
{
|
||||
{
|
||||
expr *e = v;
|
||||
m.is_not (v, e);
|
||||
if (is_uninterp_const(e)) { return to_app(v); }
|
||||
}
|
||||
|
||||
def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs;
|
||||
return def.mk_proxy (v);
|
||||
}
|
||||
|
||||
bool itp_solver::mk_proxies (expr_ref_vector &v, unsigned from)
|
||||
{
|
||||
bool dirty = false;
|
||||
for (unsigned i = from, sz = v.size(); i < sz; ++i) {
|
||||
app *p = mk_proxy (v.get (i));
|
||||
dirty |= (v.get (i) != p);
|
||||
v[i] = p;
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void itp_solver::push_bg (expr *e)
|
||||
{
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
m_assumptions.push_back (e);
|
||||
m_first_assumption = m_assumptions.size ();
|
||||
}
|
||||
|
||||
void itp_solver::pop_bg (unsigned n)
|
||||
{
|
||||
if (n == 0) { return; }
|
||||
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0;
|
||||
m_assumptions.shrink (m_first_assumption);
|
||||
}
|
||||
|
||||
unsigned itp_solver::get_num_bg () {return m_first_assumption;}
|
||||
|
||||
lbool itp_solver::check_sat (unsigned num_assumptions, expr * const *assumptions)
|
||||
{
|
||||
// -- remove any old assumptions
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
|
||||
// -- replace theory literals in background assumptions with proxies
|
||||
mk_proxies (m_assumptions);
|
||||
// -- in case mk_proxies added new literals, they are all background
|
||||
m_first_assumption = m_assumptions.size ();
|
||||
|
||||
m_assumptions.append (num_assumptions, assumptions);
|
||||
m_is_proxied = mk_proxies (m_assumptions, m_first_assumption);
|
||||
|
||||
lbool res;
|
||||
res = m_solver.check_sat (m_assumptions.size (), m_assumptions.c_ptr ());
|
||||
set_status (res);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
app* itp_solver::def_manager::mk_proxy (expr *v)
|
||||
{
|
||||
app* r;
|
||||
if (m_expr2proxy.find(v, r)) { return r; }
|
||||
|
||||
ast_manager &m = m_parent.m;
|
||||
app_ref proxy(m);
|
||||
app_ref def(m);
|
||||
proxy = m_parent.fresh_proxy ();
|
||||
def = m.mk_or (m.mk_not (proxy), v);
|
||||
m_defs.push_back (def);
|
||||
m_expr2proxy.insert (v, proxy);
|
||||
m_proxy2def.insert (proxy, def);
|
||||
|
||||
m_parent.assert_expr (def.get ());
|
||||
return proxy;
|
||||
}
|
||||
|
||||
bool itp_solver::def_manager::is_proxy (app *k, app_ref &def)
|
||||
{
|
||||
app *r = nullptr;
|
||||
bool found = m_proxy2def.find (k, r);
|
||||
def = r;
|
||||
return found;
|
||||
}
|
||||
|
||||
void itp_solver::def_manager::reset ()
|
||||
{
|
||||
m_expr2proxy.reset ();
|
||||
m_proxy2def.reset ();
|
||||
m_defs.reset ();
|
||||
}
|
||||
|
||||
bool itp_solver::def_manager::is_proxy_def (expr *v)
|
||||
{
|
||||
// XXX This might not be the most robust way to check
|
||||
return m_defs.contains (v);
|
||||
}
|
||||
|
||||
bool itp_solver::is_proxy(expr *e, app_ref &def)
|
||||
{
|
||||
if (!is_uninterp_const(e)) { return false; }
|
||||
|
||||
app *a = to_app (e);
|
||||
|
||||
for (int i = m_defs.size (); i > 0; --i)
|
||||
if (m_defs[i-1].is_proxy (a, def))
|
||||
{ return true; }
|
||||
|
||||
if (m_base_defs.is_proxy (a, def))
|
||||
{ return true; }
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void itp_solver::collect_statistics (statistics &st) const
|
||||
{
|
||||
m_solver.collect_statistics (st);
|
||||
st.update ("time.itp_solver.itp_core", m_itp_watch.get_seconds ());
|
||||
}
|
||||
|
||||
void itp_solver::reset_statistics ()
|
||||
{
|
||||
m_itp_watch.reset ();
|
||||
}
|
||||
|
||||
void itp_solver::get_unsat_core (ptr_vector<expr> &core)
|
||||
{
|
||||
m_solver.get_unsat_core (core);
|
||||
undo_proxies_in_core (core);
|
||||
}
|
||||
void itp_solver::undo_proxies_in_core (ptr_vector<expr> &r)
|
||||
{
|
||||
app_ref e(m);
|
||||
expr_fast_mark1 bg;
|
||||
for (unsigned i = 0; i < m_first_assumption; ++i)
|
||||
{ bg.mark(m_assumptions.get(i)); }
|
||||
|
||||
// expand proxies
|
||||
unsigned j = 0;
|
||||
for (unsigned i = 0, sz = r.size(); i < sz; ++i) {
|
||||
// skip background assumptions
|
||||
if (bg.is_marked(r[i])) { continue; }
|
||||
|
||||
// -- undo proxies, but only if they were introduced in check_sat
|
||||
if (m_is_proxied && is_proxy(r[i], e)) {
|
||||
SASSERT (m.is_or (e));
|
||||
r[j] = e->get_arg (1);
|
||||
} else if (i != j) { r[j] = r[i]; }
|
||||
j++;
|
||||
}
|
||||
r.shrink (j);
|
||||
}
|
||||
|
||||
void itp_solver::undo_proxies (expr_ref_vector &r)
|
||||
{
|
||||
app_ref e(m);
|
||||
// expand proxies
|
||||
for (unsigned i = 0, sz = r.size (); i < sz; ++i)
|
||||
if (is_proxy(r.get(i), e)) {
|
||||
SASSERT (m.is_or (e));
|
||||
r[i] = e->get_arg (1);
|
||||
}
|
||||
}
|
||||
|
||||
void itp_solver::get_unsat_core (expr_ref_vector &_core)
|
||||
{
|
||||
ptr_vector<expr> core;
|
||||
get_unsat_core (core);
|
||||
_core.append (core.size (), core.c_ptr ());
|
||||
}
|
||||
|
||||
void itp_solver::elim_proxies (expr_ref_vector &v)
|
||||
{
|
||||
expr_ref f = mk_and (v);
|
||||
scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer (m);
|
||||
rep->set_substitution (&m_elim_proxies_sub);
|
||||
(*rep) (f);
|
||||
v.reset ();
|
||||
flatten_and (f, v);
|
||||
}
|
||||
|
||||
void itp_solver::get_itp_core (expr_ref_vector &core)
|
||||
{
|
||||
scoped_watch _t_ (m_itp_watch);
|
||||
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
expr_set B;
|
||||
for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) {
|
||||
expr *a = m_assumptions.get (i);
|
||||
app_ref def(m);
|
||||
if (is_proxy(a, def)) { B.insert(def.get()); }
|
||||
B.insert (a);
|
||||
}
|
||||
|
||||
proof_ref pr(m);
|
||||
pr = get_proof ();
|
||||
|
||||
if (!m_new_unsat_core) {
|
||||
// old code
|
||||
farkas_learner learner_old;
|
||||
learner_old.set_split_literals(m_split_literals);
|
||||
|
||||
learner_old.get_lemmas (pr, B, core);
|
||||
elim_proxies (core);
|
||||
simplify_bounds (core); // XXX potentially redundant
|
||||
} else {
|
||||
// new code
|
||||
unsat_core_learner learner(m);
|
||||
|
||||
if (m_farkas_optimized) {
|
||||
if (true) // TODO: proper options
|
||||
{
|
||||
unsat_core_plugin_farkas_lemma_optimized* plugin_farkas_lemma_optimized = alloc(unsat_core_plugin_farkas_lemma_optimized, learner,m);
|
||||
learner.register_plugin(plugin_farkas_lemma_optimized);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsat_core_plugin_farkas_lemma_bounded* plugin_farkas_lemma_bounded = alloc(unsat_core_plugin_farkas_lemma_bounded, learner,m);
|
||||
learner.register_plugin(plugin_farkas_lemma_bounded);
|
||||
}
|
||||
|
||||
} else {
|
||||
unsat_core_plugin_farkas_lemma* plugin_farkas_lemma = alloc(unsat_core_plugin_farkas_lemma, learner, m_split_literals, m_farkas_a_const);
|
||||
learner.register_plugin(plugin_farkas_lemma);
|
||||
}
|
||||
|
||||
if (m_minimize_unsat_core) {
|
||||
unsat_core_plugin_min_cut* plugin_min_cut = alloc(unsat_core_plugin_min_cut, learner, m);
|
||||
learner.register_plugin(plugin_min_cut);
|
||||
} else {
|
||||
unsat_core_plugin_lemma* plugin_lemma = alloc(unsat_core_plugin_lemma, learner);
|
||||
learner.register_plugin(plugin_lemma);
|
||||
}
|
||||
|
||||
learner.compute_unsat_core(pr, B, core);
|
||||
|
||||
elim_proxies (core);
|
||||
simplify_bounds (core); // XXX potentially redundant
|
||||
|
||||
// // debug
|
||||
// expr_ref_vector core2(m);
|
||||
// unsat_core_learner learner2(m);
|
||||
//
|
||||
// unsat_core_plugin_farkas_lemma* plugin_farkas_lemma2 = alloc(unsat_core_plugin_farkas_lemma, learner2, m_split_literals);
|
||||
// learner2.register_plugin(plugin_farkas_lemma2);
|
||||
// unsat_core_plugin_lemma* plugin_lemma2 = alloc(unsat_core_plugin_lemma, learner2);
|
||||
// learner2.register_plugin(plugin_lemma2);
|
||||
// learner2.compute_unsat_core(pr, B, core2);
|
||||
//
|
||||
// elim_proxies (core2);
|
||||
// simplify_bounds (core2);
|
||||
//
|
||||
// IF_VERBOSE(2,
|
||||
// verbose_stream () << "Itp Core:\n"
|
||||
// << mk_pp (mk_and (core), m) << "\n";);
|
||||
// IF_VERBOSE(2,
|
||||
// verbose_stream () << "Itp Core2:\n"
|
||||
// << mk_pp (mk_and (core2), m) << "\n";);
|
||||
//SASSERT(mk_and (core) == mk_and (core2));
|
||||
}
|
||||
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream () << "Itp Core:\n"
|
||||
<< mk_pp (mk_and (core), m) << "\n";);
|
||||
|
||||
}
|
||||
|
||||
void itp_solver::refresh ()
|
||||
{
|
||||
// only refresh in non-pushed state
|
||||
SASSERT (m_defs.size () == 0);
|
||||
expr_ref_vector assertions (m);
|
||||
for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) {
|
||||
expr* a = m_solver.get_assertion (i);
|
||||
if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); }
|
||||
|
||||
}
|
||||
m_base_defs.reset ();
|
||||
NOT_IMPLEMENTED_YET ();
|
||||
// solver interface does not have a reset method. need to introduce it somewhere.
|
||||
// m_solver.reset ();
|
||||
for (unsigned i = 0, e = assertions.size (); i < e; ++i)
|
||||
{ m_solver.assert_expr(assertions.get(i)); }
|
||||
}
|
||||
|
||||
}
|
|
@ -1,172 +0,0 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_itp_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A solver that produces interpolated unsat cores
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef SPACER_ITP_SOLVER_H_
|
||||
#define SPACER_ITP_SOLVER_H_
|
||||
|
||||
#include"solver/solver.h"
|
||||
#include"ast/expr_substitution.h"
|
||||
#include"util/stopwatch.h"
|
||||
namespace spacer {
|
||||
class itp_solver : public solver {
|
||||
private:
|
||||
struct def_manager {
|
||||
itp_solver &m_parent;
|
||||
obj_map<expr, app*> m_expr2proxy;
|
||||
obj_map<app, app*> m_proxy2def;
|
||||
|
||||
expr_ref_vector m_defs;
|
||||
|
||||
def_manager(itp_solver &parent) :
|
||||
m_parent(parent), m_defs(m_parent.m)
|
||||
{}
|
||||
|
||||
bool is_proxy(app *k, app_ref &v);
|
||||
app* mk_proxy(expr *v);
|
||||
void reset();
|
||||
bool is_proxy_def(expr *v);
|
||||
|
||||
};
|
||||
|
||||
friend struct def_manager;
|
||||
ast_manager &m;
|
||||
solver &m_solver;
|
||||
app_ref_vector m_proxies;
|
||||
unsigned m_num_proxies;
|
||||
vector<def_manager> m_defs;
|
||||
def_manager m_base_defs;
|
||||
expr_ref_vector m_assumptions;
|
||||
unsigned m_first_assumption;
|
||||
bool m_is_proxied;
|
||||
|
||||
stopwatch m_itp_watch;
|
||||
|
||||
expr_substitution m_elim_proxies_sub;
|
||||
bool m_split_literals;
|
||||
bool m_new_unsat_core;
|
||||
bool m_minimize_unsat_core;
|
||||
bool m_farkas_optimized;
|
||||
bool m_farkas_a_const;
|
||||
|
||||
bool is_proxy(expr *e, app_ref &def);
|
||||
void undo_proxies_in_core(ptr_vector<expr> &v);
|
||||
app* mk_proxy(expr *v);
|
||||
app* fresh_proxy();
|
||||
void elim_proxies(expr_ref_vector &v);
|
||||
public:
|
||||
itp_solver(solver &solver, bool new_unsat_core, bool minimize_unsat_core, bool farkas_optimized, bool farkas_a_const, bool split_literals = false) :
|
||||
m(solver.get_manager()),
|
||||
m_solver(solver),
|
||||
m_proxies(m),
|
||||
m_num_proxies(0),
|
||||
m_base_defs(*this),
|
||||
m_assumptions(m),
|
||||
m_first_assumption(0),
|
||||
m_is_proxied(false),
|
||||
m_elim_proxies_sub(m, false, true),
|
||||
m_split_literals(split_literals),
|
||||
m_new_unsat_core(new_unsat_core),
|
||||
m_minimize_unsat_core(minimize_unsat_core),
|
||||
m_farkas_optimized(farkas_optimized),
|
||||
m_farkas_a_const(farkas_a_const)
|
||||
{}
|
||||
|
||||
~itp_solver() override {}
|
||||
|
||||
/* itp solver specific */
|
||||
void get_unsat_core(expr_ref_vector &core) override;
|
||||
virtual void get_itp_core(expr_ref_vector &core);
|
||||
void set_split_literals(bool v) {m_split_literals = v;}
|
||||
bool mk_proxies(expr_ref_vector &v, unsigned from = 0);
|
||||
void undo_proxies(expr_ref_vector &v);
|
||||
|
||||
void push_bg(expr *e);
|
||||
void pop_bg(unsigned n);
|
||||
unsigned get_num_bg();
|
||||
|
||||
void get_full_unsat_core(ptr_vector<expr> &core)
|
||||
{m_solver.get_unsat_core(core);}
|
||||
|
||||
/* solver interface */
|
||||
|
||||
solver* translate(ast_manager &m, params_ref const &p) override { return m_solver.translate(m, p);}
|
||||
void updt_params(params_ref const &p) override { m_solver.updt_params(p);}
|
||||
void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r);}
|
||||
void set_produce_models(bool f) override { m_solver.set_produce_models(f);}
|
||||
void assert_expr_core(expr *t) override { m_solver.assert_expr(t);}
|
||||
void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET();}
|
||||
expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); }
|
||||
|
||||
void push() override;
|
||||
void pop(unsigned n) override;
|
||||
unsigned get_scope_level() const override
|
||||
{return m_solver.get_scope_level();}
|
||||
|
||||
lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override;
|
||||
void set_progress_callback(progress_callback *callback) override
|
||||
{m_solver.set_progress_callback(callback);}
|
||||
unsigned get_num_assertions() const override
|
||||
{return m_solver.get_num_assertions();}
|
||||
expr * get_assertion(unsigned idx) const override
|
||||
{return m_solver.get_assertion(idx);}
|
||||
unsigned get_num_assumptions() const override
|
||||
{return m_solver.get_num_assumptions();}
|
||||
expr * get_assumption(unsigned idx) const override
|
||||
{return m_solver.get_assumption(idx);}
|
||||
std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override
|
||||
{ return m_solver.display(out, n, es); }
|
||||
|
||||
/* check_sat_result interface */
|
||||
|
||||
void collect_statistics(statistics &st) const override ;
|
||||
virtual void reset_statistics();
|
||||
|
||||
void get_unsat_core(ptr_vector<expr> &r) override;
|
||||
void get_model_core(model_ref &m) override {m_solver.get_model(m);}
|
||||
proof *get_proof() override {return m_solver.get_proof();}
|
||||
std::string reason_unknown() const override
|
||||
{return m_solver.reason_unknown();}
|
||||
void set_reason_unknown(char const* msg) override
|
||||
{m_solver.set_reason_unknown(msg);}
|
||||
void get_labels(svector<symbol> &r) override
|
||||
{m_solver.get_labels(r);}
|
||||
ast_manager &get_manager() const override {return m;}
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
class scoped_mk_proxy {
|
||||
itp_solver &m_s;
|
||||
expr_ref_vector &m_v;
|
||||
public:
|
||||
scoped_mk_proxy(itp_solver &s, expr_ref_vector &v) : m_s(s), m_v(v)
|
||||
{m_s.mk_proxies(m_v);}
|
||||
~scoped_mk_proxy()
|
||||
{m_s.undo_proxies(m_v);}
|
||||
};
|
||||
|
||||
class scoped_bg {
|
||||
itp_solver &m_s;
|
||||
unsigned m_bg_sz;
|
||||
public:
|
||||
scoped_bg(itp_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {}
|
||||
~scoped_bg()
|
||||
{if (m_s.get_num_bg() > m_bg_sz) { m_s.pop_bg(m_s.get_num_bg() - m_bg_sz); }}
|
||||
};
|
||||
};
|
||||
}
|
||||
#endif
|
280
src/muz/spacer/spacer_iuc_proof.cpp
Normal file
280
src/muz/spacer/spacer_iuc_proof.cpp
Normal file
|
@ -0,0 +1,280 @@
|
|||
#include <unordered_map>
|
||||
#include "ast/ast_pp_dot.h"
|
||||
|
||||
#include "muz/spacer/spacer_iuc_proof.h"
|
||||
#include "ast/for_each_expr.h"
|
||||
#include "ast/array_decl_plugin.h"
|
||||
#include "ast/proofs/proof_utils.h"
|
||||
#include "muz/spacer/spacer_proof_utils.h"
|
||||
#include "muz/spacer/spacer_util.h"
|
||||
namespace spacer {
|
||||
|
||||
/*
|
||||
* ====================================
|
||||
* init
|
||||
* ====================================
|
||||
*/
|
||||
iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits) :
|
||||
m(m), m_pr(pr,m) {
|
||||
for (auto lit : core_lits) m_core_lits.insert(lit);
|
||||
// init A-marks and B-marks
|
||||
collect_core_symbols();
|
||||
compute_marks();
|
||||
}
|
||||
|
||||
iuc_proof::iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector& core_lits) :
|
||||
m(m), m_pr(pr,m) {
|
||||
for (auto lit : core_lits) m_core_lits.insert(lit);
|
||||
// init A-marks and B-marks
|
||||
collect_core_symbols();
|
||||
compute_marks();
|
||||
}
|
||||
/*
|
||||
* ====================================
|
||||
* methods for computing symbol colors
|
||||
* ====================================
|
||||
*/
|
||||
class collect_pure_proc {
|
||||
func_decl_set& m_symbs;
|
||||
public:
|
||||
collect_pure_proc(func_decl_set& s):m_symbs(s) {}
|
||||
|
||||
void operator()(app* a) {
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
m_symbs.insert(a->get_decl());
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
void iuc_proof::collect_core_symbols()
|
||||
{
|
||||
expr_mark visited;
|
||||
collect_pure_proc proc(m_core_symbols);
|
||||
for (auto lit : m_core_lits)
|
||||
for_each_expr(proc, visited, lit);
|
||||
}
|
||||
|
||||
class is_pure_expr_proc {
|
||||
func_decl_set const& m_symbs;
|
||||
array_util m_au;
|
||||
public:
|
||||
struct non_pure {};
|
||||
|
||||
is_pure_expr_proc(func_decl_set const& s, ast_manager& m):
|
||||
m_symbs(s),
|
||||
m_au (m)
|
||||
{}
|
||||
|
||||
void operator()(app* a) {
|
||||
if (a->get_family_id() == null_family_id) {
|
||||
if (!m_symbs.contains(a->get_decl())) {
|
||||
throw non_pure();
|
||||
}
|
||||
}
|
||||
else if (a->get_family_id () == m_au.get_family_id () &&
|
||||
a->is_app_of (a->get_family_id (), OP_ARRAY_EXT)) {
|
||||
throw non_pure();
|
||||
}
|
||||
}
|
||||
void operator()(var*) {}
|
||||
void operator()(quantifier*) {}
|
||||
};
|
||||
|
||||
bool iuc_proof::is_core_pure(expr* e) const
|
||||
{
|
||||
is_pure_expr_proc proc(m_core_symbols, m);
|
||||
try {
|
||||
for_each_expr(proc, e);
|
||||
}
|
||||
catch (is_pure_expr_proc::non_pure)
|
||||
{return false;}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void iuc_proof::compute_marks()
|
||||
{
|
||||
proof_post_order it(m_pr, m);
|
||||
while (it.hasNext())
|
||||
{
|
||||
proof* cur = it.next();
|
||||
if (m.get_num_parents(cur) == 0)
|
||||
{
|
||||
switch(cur->get_decl_kind())
|
||||
{
|
||||
case PR_ASSERTED:
|
||||
if (m_core_lits.contains(m.get_fact(cur)))
|
||||
m_b_mark.mark(cur, true);
|
||||
else
|
||||
m_a_mark.mark(cur, true);
|
||||
break;
|
||||
case PR_HYPOTHESIS:
|
||||
m_h_mark.mark(cur, true);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// collect from parents whether derivation of current node
|
||||
// contains A-axioms, B-axioms and hypothesis
|
||||
bool need_to_mark_a = false;
|
||||
bool need_to_mark_b = false;
|
||||
bool need_to_mark_h = false;
|
||||
|
||||
for (unsigned i = 0; i < m.get_num_parents(cur); ++i)
|
||||
{
|
||||
SASSERT(m.is_proof(cur->get_arg(i)));
|
||||
proof* premise = to_app(cur->get_arg(i));
|
||||
|
||||
need_to_mark_a |= m_a_mark.is_marked(premise);
|
||||
need_to_mark_b |= m_b_mark.is_marked(premise);
|
||||
need_to_mark_h |= m_h_mark.is_marked(premise);
|
||||
}
|
||||
|
||||
// if current node is application of a lemma, then all
|
||||
// active hypotheses are removed
|
||||
if(cur->get_decl_kind() == PR_LEMMA) need_to_mark_h = false;
|
||||
|
||||
// save results
|
||||
m_a_mark.mark(cur, need_to_mark_a);
|
||||
m_b_mark.mark(cur, need_to_mark_b);
|
||||
m_h_mark.mark(cur, need_to_mark_h);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* ====================================
|
||||
* statistics
|
||||
* ====================================
|
||||
*/
|
||||
|
||||
// debug method
|
||||
void iuc_proof::dump_farkas_stats()
|
||||
{
|
||||
unsigned fl_total = 0;
|
||||
unsigned fl_lowcut = 0;
|
||||
|
||||
proof_post_order it(m_pr, m);
|
||||
while (it.hasNext())
|
||||
{
|
||||
proof* cur = it.next();
|
||||
|
||||
// if node is theory lemma
|
||||
if (is_farkas_lemma(m, cur))
|
||||
{
|
||||
fl_total++;
|
||||
|
||||
// check whether farkas lemma is to be interpolated (could
|
||||
// potentially miss farkas lemmas, which are interpolated,
|
||||
// because we potentially don't want to use the lowest
|
||||
// cut)
|
||||
bool has_blue_nonred_parent = false;
|
||||
for (unsigned i = 0; i < m.get_num_parents(cur); ++i) {
|
||||
proof* premise = to_app(cur->get_arg(i));
|
||||
if (!is_a_marked(premise) && is_b_marked(premise)) {
|
||||
has_blue_nonred_parent = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (has_blue_nonred_parent && is_a_marked(cur))
|
||||
{
|
||||
SASSERT(is_b_marked(cur));
|
||||
fl_lowcut++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IF_VERBOSE(1, verbose_stream()
|
||||
<< "\n total farkas lemmas " << fl_total
|
||||
<< " farkas lemmas in lowest cut " << fl_lowcut << "\n";);
|
||||
}
|
||||
|
||||
void iuc_proof::display_dot(std::ostream& out) {
|
||||
out << "digraph proof { \n";
|
||||
|
||||
std::unordered_map<unsigned, unsigned> ids;
|
||||
unsigned last_id = 0;
|
||||
|
||||
proof_post_order it(m_pr, m);
|
||||
while (it.hasNext())
|
||||
{
|
||||
proof* curr = it.next();
|
||||
|
||||
SASSERT(ids.count(curr->get_id()) == 0);
|
||||
ids.insert(std::make_pair(curr->get_id(), last_id));
|
||||
|
||||
std::string color = "white";
|
||||
if (this->is_a_marked(curr) && !this->is_b_marked(curr))
|
||||
color = "red";
|
||||
else if(!this->is_a_marked(curr) && this->is_b_marked(curr))
|
||||
color = "blue";
|
||||
else if(this->is_a_marked(curr) && this->is_b_marked(curr) )
|
||||
color = "purple";
|
||||
|
||||
// compute node label
|
||||
std::ostringstream label_ostream;
|
||||
label_ostream << mk_epp(m.get_fact(curr), m) << "\n";
|
||||
std::string label = escape_dot(label_ostream.str());
|
||||
|
||||
// compute edge-label
|
||||
std::string edge_label = "";
|
||||
if (m.get_num_parents(curr) == 0) {
|
||||
switch (curr->get_decl_kind())
|
||||
{
|
||||
case PR_ASSERTED:
|
||||
edge_label = "asserted:";
|
||||
break;
|
||||
case PR_HYPOTHESIS:
|
||||
edge_label = "hyp:";
|
||||
color = "grey";
|
||||
break;
|
||||
case PR_TH_LEMMA:
|
||||
if (is_farkas_lemma(m, curr))
|
||||
edge_label = "th_axiom(farkas):";
|
||||
else if (is_arith_lemma(m, curr))
|
||||
edge_label = "th_axiom(arith):";
|
||||
else
|
||||
edge_label = "th_axiom:";
|
||||
break;
|
||||
default:
|
||||
edge_label = "unknown axiom:";
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (curr->get_decl_kind() == PR_LEMMA)
|
||||
edge_label = "lemma:";
|
||||
else if (curr->get_decl_kind() == PR_TH_LEMMA) {
|
||||
if (is_farkas_lemma(m, curr))
|
||||
edge_label = "th_lemma(farkas):";
|
||||
else if (is_arith_lemma(m, curr))
|
||||
edge_label = "th_lemma(arith):";
|
||||
else
|
||||
edge_label = "th_lemma(other):";
|
||||
}
|
||||
}
|
||||
|
||||
// generate entry for node in dot-file
|
||||
out << "node_" << last_id << " " << "["
|
||||
<< "shape=box,style=\"filled\","
|
||||
<< "label=\"" << edge_label << " " << label << "\", "
|
||||
<< "fillcolor=\"" << color << "\"" << "]\n";
|
||||
|
||||
// add entry for each edge to that node
|
||||
for (unsigned i = m.get_num_parents(curr); i > 0 ; --i)
|
||||
{
|
||||
proof* premise = to_app(curr->get_arg(i-1));
|
||||
unsigned pid = ids.at(premise->get_id());
|
||||
out << "node_" << pid << " -> " << "node_" << last_id << ";\n";
|
||||
}
|
||||
|
||||
++last_id;
|
||||
}
|
||||
out << "\n}" << std::endl;
|
||||
}
|
||||
}
|
67
src/muz/spacer/spacer_iuc_proof.h
Normal file
67
src/muz/spacer/spacer_iuc_proof.h
Normal file
|
@ -0,0 +1,67 @@
|
|||
#ifndef IUC_PROOF_H_
|
||||
#define IUC_PROOF_H_
|
||||
|
||||
#include <ostream>
|
||||
#include "ast/ast.h"
|
||||
|
||||
namespace spacer {
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
typedef obj_hashtable<func_decl> func_decl_set;
|
||||
|
||||
/*
|
||||
* An iuc_proof is a proof together with information of the
|
||||
* coloring of the axioms.
|
||||
*/
|
||||
class iuc_proof
|
||||
{
|
||||
public:
|
||||
|
||||
// Constructs an iuc_proof given an ast_manager, a proof, and a set of
|
||||
// literals core_lits that might be included in the unsat core
|
||||
iuc_proof(ast_manager& m, proof* pr, const expr_set& core_lits);
|
||||
iuc_proof(ast_manager& m, proof* pr, const expr_ref_vector &core_lits);
|
||||
|
||||
// returns the proof object
|
||||
proof* get() {return m_pr.get();}
|
||||
|
||||
// returns true if all uninterpreted symbols of e are from the core literals
|
||||
// requires that m_core_symbols has already been computed
|
||||
bool is_core_pure(expr* e) const;
|
||||
|
||||
bool is_a_marked(proof* p) {return m_a_mark.is_marked(p);}
|
||||
bool is_b_marked(proof* p) {return m_b_mark.is_marked(p);}
|
||||
bool is_h_marked(proof* p) {return m_h_mark.is_marked(p);}
|
||||
|
||||
bool is_b_pure (proof *p) {
|
||||
return !is_h_marked (p) && is_core_pure(m.get_fact (p));
|
||||
}
|
||||
|
||||
void display_dot(std::ostream &out);
|
||||
// debug method
|
||||
void dump_farkas_stats();
|
||||
private:
|
||||
ast_manager& m;
|
||||
proof_ref m_pr;
|
||||
|
||||
ast_mark m_a_mark;
|
||||
ast_mark m_b_mark;
|
||||
ast_mark m_h_mark;
|
||||
|
||||
// -- literals that are part of the core
|
||||
expr_set m_core_lits;
|
||||
|
||||
// symbols that occur in any literals in the core
|
||||
func_decl_set m_core_symbols;
|
||||
|
||||
// collect symbols occurring in B (the core)
|
||||
void collect_core_symbols();
|
||||
|
||||
// compute for each formula of the proof whether it derives
|
||||
// from A or from B
|
||||
void compute_marks();
|
||||
};
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif /* IUC_PROOF_H_ */
|
435
src/muz/spacer/spacer_iuc_solver.cpp
Normal file
435
src/muz/spacer/spacer_iuc_solver.cpp
Normal file
|
@ -0,0 +1,435 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_iuc_solver.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
A solver that produces interpolated unsat cores (IUCs)
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#include"muz/spacer/spacer_iuc_solver.h"
|
||||
#include"ast/ast.h"
|
||||
#include"muz/spacer/spacer_util.h"
|
||||
#include"ast/proofs/proof_utils.h"
|
||||
#include"muz/spacer/spacer_farkas_learner.h"
|
||||
#include"ast/rewriter/expr_replacer.h"
|
||||
#include"muz/spacer/spacer_unsat_core_learner.h"
|
||||
#include"muz/spacer/spacer_unsat_core_plugin.h"
|
||||
#include "muz/spacer/spacer_iuc_proof.h"
|
||||
|
||||
namespace spacer {
|
||||
void iuc_solver::push ()
|
||||
{
|
||||
m_defs.push_back (def_manager (*this));
|
||||
m_solver.push ();
|
||||
}
|
||||
|
||||
void iuc_solver::pop (unsigned n)
|
||||
{
|
||||
m_solver.pop (n);
|
||||
unsigned lvl = m_defs.size ();
|
||||
SASSERT (n <= lvl);
|
||||
unsigned new_lvl = lvl-n;
|
||||
while (m_defs.size() > new_lvl) {
|
||||
m_num_proxies -= m_defs.back ().m_defs.size ();
|
||||
m_defs.pop_back ();
|
||||
}
|
||||
}
|
||||
|
||||
app* iuc_solver::fresh_proxy ()
|
||||
{
|
||||
if (m_num_proxies == m_proxies.size()) {
|
||||
std::stringstream name;
|
||||
name << "spacer_proxy!" << m_proxies.size ();
|
||||
app_ref res(m);
|
||||
res = m.mk_const (symbol (name.str ().c_str ()),
|
||||
m.mk_bool_sort ());
|
||||
m_proxies.push_back (res);
|
||||
|
||||
// -- add the new proxy to proxy eliminator
|
||||
proof_ref pr(m);
|
||||
pr = m.mk_asserted (m.mk_true ());
|
||||
m_elim_proxies_sub.insert (res, m.mk_true (), pr);
|
||||
|
||||
}
|
||||
return m_proxies.get (m_num_proxies++);
|
||||
}
|
||||
|
||||
app* iuc_solver::mk_proxy (expr *v)
|
||||
{
|
||||
{
|
||||
expr *e = v;
|
||||
m.is_not (v, e);
|
||||
if (is_uninterp_const(e)) { return to_app(v); }
|
||||
}
|
||||
|
||||
def_manager &def = m_defs.size () > 0 ? m_defs.back () : m_base_defs;
|
||||
return def.mk_proxy (v);
|
||||
}
|
||||
|
||||
bool iuc_solver::mk_proxies (expr_ref_vector &v, unsigned from)
|
||||
{
|
||||
bool dirty = false;
|
||||
for (unsigned i = from, sz = v.size(); i < sz; ++i) {
|
||||
app *p = mk_proxy (v.get (i));
|
||||
dirty |= (v.get (i) != p);
|
||||
v[i] = p;
|
||||
}
|
||||
return dirty;
|
||||
}
|
||||
|
||||
void iuc_solver::push_bg (expr *e)
|
||||
{
|
||||
if (m_assumptions.size () > m_first_assumption)
|
||||
{ m_assumptions.shrink(m_first_assumption); }
|
||||
m_assumptions.push_back (e);
|
||||
m_first_assumption = m_assumptions.size ();
|
||||
}
|
||||
|
||||
void iuc_solver::pop_bg (unsigned n)
|
||||
{
|
||||
if (n == 0) { return; }
|
||||
|
||||
if (m_assumptions.size () > m_first_assumption) {
|
||||
m_assumptions.shrink(m_first_assumption);
|
||||
}
|
||||
m_first_assumption = m_first_assumption > n ? m_first_assumption - n : 0;
|
||||
m_assumptions.shrink (m_first_assumption);
|
||||
}
|
||||
|
||||
unsigned iuc_solver::get_num_bg () {return m_first_assumption;}
|
||||
|
||||
lbool iuc_solver::check_sat (unsigned num_assumptions, expr * const *assumptions)
|
||||
{
|
||||
// -- remove any old assumptions
|
||||
m_assumptions.shrink(m_first_assumption);
|
||||
|
||||
// -- replace theory literals in background assumptions with proxies
|
||||
mk_proxies (m_assumptions);
|
||||
// -- in case mk_proxies added new literals, they are all background
|
||||
m_first_assumption = m_assumptions.size ();
|
||||
|
||||
m_assumptions.append (num_assumptions, assumptions);
|
||||
m_is_proxied = mk_proxies (m_assumptions, m_first_assumption);
|
||||
|
||||
return set_status (m_solver.check_sat (m_assumptions));
|
||||
}
|
||||
|
||||
lbool iuc_solver::check_sat_cc(const expr_ref_vector &cube,
|
||||
vector<expr_ref_vector> const & clauses) {
|
||||
if (clauses.empty())
|
||||
return check_sat(cube.size(), cube.c_ptr());
|
||||
|
||||
// -- remove any old assumptions
|
||||
m_assumptions.shrink(m_first_assumption);
|
||||
|
||||
// -- replace theory literals in background assumptions with proxies
|
||||
mk_proxies(m_assumptions);
|
||||
// -- in case mk_proxies added new literals, they are all background
|
||||
m_first_assumption = m_assumptions.size();
|
||||
|
||||
m_assumptions.append(cube);
|
||||
m_is_proxied = mk_proxies(m_assumptions, m_first_assumption);
|
||||
|
||||
return set_status (m_solver.check_sat_cc(m_assumptions, clauses));
|
||||
}
|
||||
|
||||
|
||||
app* iuc_solver::def_manager::mk_proxy (expr *v)
|
||||
{
|
||||
app* r;
|
||||
if (m_expr2proxy.find(v, r))
|
||||
return r;
|
||||
|
||||
ast_manager &m = m_parent.m;
|
||||
app* proxy = m_parent.fresh_proxy ();
|
||||
app* def = m.mk_or (m.mk_not (proxy), v);
|
||||
m_defs.push_back (def);
|
||||
m_expr2proxy.insert (v, proxy);
|
||||
m_proxy2def.insert (proxy, def);
|
||||
|
||||
m_parent.assert_expr (def);
|
||||
return proxy;
|
||||
}
|
||||
|
||||
bool iuc_solver::def_manager::is_proxy (app *k, app_ref &def)
|
||||
{
|
||||
app *r = nullptr;
|
||||
bool found = m_proxy2def.find (k, r);
|
||||
def = r;
|
||||
return found;
|
||||
}
|
||||
|
||||
void iuc_solver::def_manager::reset ()
|
||||
{
|
||||
m_expr2proxy.reset ();
|
||||
m_proxy2def.reset ();
|
||||
m_defs.reset ();
|
||||
}
|
||||
|
||||
bool iuc_solver::def_manager::is_proxy_def (expr *v)
|
||||
{
|
||||
// XXX This might not be the most robust way to check
|
||||
return m_defs.contains (v);
|
||||
}
|
||||
|
||||
bool iuc_solver::is_proxy(expr *e, app_ref &def)
|
||||
{
|
||||
if (!is_uninterp_const(e))
|
||||
return false;
|
||||
|
||||
app* a = to_app (e);
|
||||
|
||||
for (int i = m_defs.size (); i-- > 0; )
|
||||
if (m_defs[i].is_proxy (a, def))
|
||||
return true;
|
||||
|
||||
return m_base_defs.is_proxy (a, def);
|
||||
}
|
||||
|
||||
void iuc_solver::collect_statistics (statistics &st) const
|
||||
{
|
||||
m_solver.collect_statistics (st);
|
||||
st.update ("time.iuc_solver.get_iuc", m_iuc_sw.get_seconds());
|
||||
st.update ("time.iuc_solver.get_iuc.hyp_reduce1", m_hyp_reduce1_sw.get_seconds());
|
||||
st.update ("time.iuc_solver.get_iuc.hyp_reduce2", m_hyp_reduce2_sw.get_seconds());
|
||||
st.update ("time.iuc_solver.get_iuc.learn_core", m_learn_core_sw.get_seconds());
|
||||
|
||||
st.update("iuc_solver.num_proxies", m_proxies.size());
|
||||
}
|
||||
|
||||
void iuc_solver::reset_statistics ()
|
||||
{
|
||||
m_iuc_sw.reset();
|
||||
m_hyp_reduce1_sw.reset();
|
||||
m_hyp_reduce2_sw.reset();
|
||||
m_learn_core_sw.reset();
|
||||
}
|
||||
|
||||
void iuc_solver::get_unsat_core (expr_ref_vector &core) {
|
||||
m_solver.get_unsat_core (core);
|
||||
undo_proxies_in_core (core);
|
||||
}
|
||||
|
||||
void iuc_solver::undo_proxies_in_core (expr_ref_vector &r)
|
||||
{
|
||||
app_ref e(m);
|
||||
expr_fast_mark1 bg;
|
||||
for (unsigned i = 0; i < m_first_assumption; ++i) {
|
||||
bg.mark(m_assumptions.get(i));
|
||||
}
|
||||
|
||||
// expand proxies
|
||||
unsigned j = 0;
|
||||
for (expr* rr : r) {
|
||||
// skip background assumptions
|
||||
if (bg.is_marked(rr))
|
||||
continue;
|
||||
|
||||
// -- undo proxies, but only if they were introduced in check_sat
|
||||
if (m_is_proxied && is_proxy(rr, e)) {
|
||||
SASSERT (m.is_or (e));
|
||||
r[j++] = e->get_arg (1);
|
||||
}
|
||||
else {
|
||||
r[j++] = rr;
|
||||
}
|
||||
}
|
||||
r.shrink (j);
|
||||
}
|
||||
|
||||
void iuc_solver::undo_proxies (expr_ref_vector &r)
|
||||
{
|
||||
app_ref e(m);
|
||||
// expand proxies
|
||||
for (unsigned i = 0, sz = r.size (); i < sz; ++i)
|
||||
if (is_proxy(r.get(i), e)) {
|
||||
SASSERT (m.is_or (e));
|
||||
r[i] = e->get_arg (1);
|
||||
}
|
||||
}
|
||||
|
||||
void iuc_solver::elim_proxies (expr_ref_vector &v)
|
||||
{
|
||||
expr_ref f = mk_and (v);
|
||||
scoped_ptr<expr_replacer> rep = mk_expr_simp_replacer (m);
|
||||
rep->set_substitution (&m_elim_proxies_sub);
|
||||
(*rep)(f);
|
||||
v.reset();
|
||||
flatten_and(f, v);
|
||||
}
|
||||
|
||||
void iuc_solver::get_iuc(expr_ref_vector &core)
|
||||
{
|
||||
scoped_watch _t_ (m_iuc_sw);
|
||||
|
||||
typedef obj_hashtable<expr> expr_set;
|
||||
expr_set core_lits;
|
||||
for (unsigned i = m_first_assumption, sz = m_assumptions.size(); i < sz; ++i) {
|
||||
expr *a = m_assumptions.get (i);
|
||||
app_ref def(m);
|
||||
if (is_proxy(a, def)) { core_lits.insert(def.get()); }
|
||||
core_lits.insert (a);
|
||||
}
|
||||
|
||||
if (m_iuc == 0) {
|
||||
// ORIGINAL PDR CODE
|
||||
// AG: deprecated
|
||||
proof_ref pr(m);
|
||||
pr = get_proof ();
|
||||
|
||||
farkas_learner learner_old;
|
||||
learner_old.set_split_literals(m_split_literals);
|
||||
|
||||
learner_old.get_lemmas (pr, core_lits, core);
|
||||
elim_proxies (core);
|
||||
simplify_bounds (core); // XXX potentially redundant
|
||||
}
|
||||
else {
|
||||
// NEW IUC
|
||||
proof_ref res(get_proof(), m);
|
||||
|
||||
// -- old hypothesis reducer while the new one is broken
|
||||
if (m_old_hyp_reducer) {
|
||||
scoped_watch _t_ (m_hyp_reduce1_sw);
|
||||
// AG: deprecated
|
||||
// pre-process proof in order to get a proof which is
|
||||
// better suited for unsat-core-extraction
|
||||
if (m_print_farkas_stats) {
|
||||
iuc_proof iuc_before(m, res.get(), core_lits);
|
||||
verbose_stream() << "\nOld reduce_hypotheses. Before:";
|
||||
iuc_before.dump_farkas_stats();
|
||||
}
|
||||
|
||||
proof_utils::reduce_hypotheses(res);
|
||||
proof_utils::permute_unit_resolution(res);
|
||||
|
||||
if (m_print_farkas_stats) {
|
||||
iuc_proof iuc_after(m, res.get(), core_lits);
|
||||
verbose_stream() << "Old reduce_hypothesis. After:";
|
||||
iuc_after.dump_farkas_stats();
|
||||
}
|
||||
}
|
||||
// -- new hypothesis reducer
|
||||
else
|
||||
{
|
||||
scoped_watch _t_ (m_hyp_reduce2_sw);
|
||||
|
||||
// pre-process proof for better iuc extraction
|
||||
if (m_print_farkas_stats) {
|
||||
iuc_proof iuc_before(m, res.get(), core_lits);
|
||||
verbose_stream() << "\n New hypothesis_reducer. Before:";
|
||||
iuc_before.dump_farkas_stats();
|
||||
}
|
||||
|
||||
proof_ref pr1(m);
|
||||
{
|
||||
scoped_watch _t_ (m_hyp_reduce1_sw);
|
||||
theory_axiom_reducer ta_reducer(m);
|
||||
pr1 = ta_reducer.reduce (res.get());
|
||||
}
|
||||
|
||||
proof_ref pr2(m);
|
||||
{
|
||||
scoped_watch _t_ (m_hyp_reduce2_sw);
|
||||
hypothesis_reducer hyp_reducer(m);
|
||||
pr2 = hyp_reducer.reduce(pr1);
|
||||
}
|
||||
|
||||
res = pr2;
|
||||
|
||||
if (m_print_farkas_stats) {
|
||||
iuc_proof iuc_after(m, res.get(), core_lits);
|
||||
verbose_stream() << "New hypothesis_reducer. After:";
|
||||
iuc_after.dump_farkas_stats();
|
||||
}
|
||||
}
|
||||
|
||||
iuc_proof iuc_pf(m, res, core_lits);
|
||||
|
||||
unsat_core_learner learner(m, iuc_pf);
|
||||
|
||||
unsat_core_plugin* plugin;
|
||||
// -- register iuc plugins
|
||||
switch (m_iuc_arith) {
|
||||
case 0:
|
||||
case 1:
|
||||
plugin =
|
||||
alloc(unsat_core_plugin_farkas_lemma,
|
||||
learner, m_split_literals,
|
||||
(m_iuc_arith == 1) /* use constants from A */);
|
||||
learner.register_plugin(plugin);
|
||||
break;
|
||||
case 2:
|
||||
SASSERT(false && "Broken");
|
||||
plugin = alloc(unsat_core_plugin_farkas_lemma_optimized, learner, m);
|
||||
learner.register_plugin(plugin);
|
||||
break;
|
||||
case 3:
|
||||
plugin = alloc(unsat_core_plugin_farkas_lemma_bounded, learner, m);
|
||||
learner.register_plugin(plugin);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
switch (m_iuc) {
|
||||
case 1:
|
||||
// -- iuc based on the lowest cut in the proof
|
||||
plugin = alloc(unsat_core_plugin_lemma, learner);
|
||||
learner.register_plugin(plugin);
|
||||
break;
|
||||
case 2:
|
||||
// -- iuc based on the smallest cut in the proof
|
||||
plugin = alloc(unsat_core_plugin_min_cut, learner, m);
|
||||
learner.register_plugin(plugin);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
|
||||
{
|
||||
scoped_watch _t_ (m_learn_core_sw);
|
||||
// compute interpolating unsat core
|
||||
learner.compute_unsat_core(core);
|
||||
}
|
||||
|
||||
elim_proxies (core);
|
||||
// AG: this should be taken care of by minimizing the iuc cut
|
||||
simplify_bounds (core);
|
||||
}
|
||||
|
||||
IF_VERBOSE(2,
|
||||
verbose_stream () << "IUC Core:\n" << core << "\n";);
|
||||
}
|
||||
|
||||
void iuc_solver::refresh ()
|
||||
{
|
||||
// only refresh in non-pushed state
|
||||
SASSERT (m_defs.empty());
|
||||
expr_ref_vector assertions (m);
|
||||
for (unsigned i = 0, e = m_solver.get_num_assertions(); i < e; ++i) {
|
||||
expr* a = m_solver.get_assertion (i);
|
||||
if (!m_base_defs.is_proxy_def(a)) { assertions.push_back(a); }
|
||||
|
||||
}
|
||||
m_base_defs.reset ();
|
||||
NOT_IMPLEMENTED_YET ();
|
||||
// solver interface does not have a reset method. need to introduce it somewhere.
|
||||
// m_solver.reset ();
|
||||
for (unsigned i = 0, e = assertions.size (); i < e; ++i)
|
||||
{ m_solver.assert_expr(assertions.get(i)); }
|
||||
}
|
||||
|
||||
}
|
181
src/muz/spacer/spacer_iuc_solver.h
Normal file
181
src/muz/spacer/spacer_iuc_solver.h
Normal file
|
@ -0,0 +1,181 @@
|
|||
/**
|
||||
Copyright (c) 2017 Arie Gurfinkel
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_iuc_solver.h
|
||||
|
||||
Abstract:
|
||||
|
||||
A solver that produces interpolated unsat cores
|
||||
|
||||
Author:
|
||||
|
||||
Arie Gurfinkel
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef SPACER_IUC_SOLVER_H_
|
||||
#define SPACER_IUC_SOLVER_H_
|
||||
|
||||
#include"solver/solver.h"
|
||||
#include"ast/expr_substitution.h"
|
||||
#include"util/stopwatch.h"
|
||||
namespace spacer {
|
||||
class iuc_solver : public solver {
|
||||
private:
|
||||
struct def_manager {
|
||||
iuc_solver & m_parent;
|
||||
expr_ref_vector m_defs;
|
||||
obj_map<expr, app*> m_expr2proxy;
|
||||
obj_map<app, app*> m_proxy2def;
|
||||
|
||||
def_manager(iuc_solver &parent) :
|
||||
m_parent(parent), m_defs(m_parent.m)
|
||||
{}
|
||||
|
||||
bool is_proxy(app *k, app_ref &v);
|
||||
app* mk_proxy(expr *v);
|
||||
void reset();
|
||||
bool is_proxy_def(expr *v);
|
||||
|
||||
};
|
||||
|
||||
friend struct def_manager;
|
||||
ast_manager& m;
|
||||
solver& m_solver;
|
||||
app_ref_vector m_proxies;
|
||||
unsigned m_num_proxies;
|
||||
vector<def_manager> m_defs;
|
||||
def_manager m_base_defs;
|
||||
expr_ref_vector m_assumptions;
|
||||
unsigned m_first_assumption;
|
||||
bool m_is_proxied;
|
||||
|
||||
stopwatch m_iuc_sw;
|
||||
stopwatch m_hyp_reduce1_sw;
|
||||
stopwatch m_hyp_reduce2_sw;
|
||||
stopwatch m_learn_core_sw;
|
||||
|
||||
expr_substitution m_elim_proxies_sub;
|
||||
bool m_split_literals;
|
||||
unsigned m_iuc;
|
||||
unsigned m_iuc_arith;
|
||||
bool m_print_farkas_stats;
|
||||
bool m_old_hyp_reducer;
|
||||
bool is_proxy(expr *e, app_ref &def);
|
||||
void undo_proxies_in_core(expr_ref_vector &v);
|
||||
app* mk_proxy(expr *v);
|
||||
app* fresh_proxy();
|
||||
void elim_proxies(expr_ref_vector &v);
|
||||
public:
|
||||
iuc_solver(solver &solver, unsigned iuc, unsigned iuc_arith,
|
||||
bool print_farkas_stats, bool old_hyp_reducer,
|
||||
bool split_literals = false) :
|
||||
m(solver.get_manager()),
|
||||
m_solver(solver),
|
||||
m_proxies(m),
|
||||
m_num_proxies(0),
|
||||
m_base_defs(*this),
|
||||
m_assumptions(m),
|
||||
m_first_assumption(0),
|
||||
m_is_proxied(false),
|
||||
m_elim_proxies_sub(m, false, true),
|
||||
m_split_literals(split_literals),
|
||||
m_iuc(iuc),
|
||||
m_iuc_arith(iuc_arith),
|
||||
m_print_farkas_stats(print_farkas_stats),
|
||||
m_old_hyp_reducer(old_hyp_reducer)
|
||||
{}
|
||||
|
||||
~iuc_solver() override {}
|
||||
|
||||
/* iuc solver specific */
|
||||
virtual void get_iuc(expr_ref_vector &core);
|
||||
void set_split_literals(bool v) { m_split_literals = v; }
|
||||
bool mk_proxies(expr_ref_vector &v, unsigned from = 0);
|
||||
void undo_proxies(expr_ref_vector &v);
|
||||
|
||||
void push_bg(expr *e);
|
||||
void pop_bg(unsigned n);
|
||||
unsigned get_num_bg();
|
||||
|
||||
void get_full_unsat_core(ptr_vector<expr> &core) {
|
||||
expr_ref_vector _core(m);
|
||||
m_solver.get_unsat_core(_core);
|
||||
core.append(_core.size(), _core.c_ptr());
|
||||
}
|
||||
|
||||
/* solver interface */
|
||||
|
||||
solver* translate(ast_manager &m, params_ref const &p) override {
|
||||
return m_solver.translate(m, p);
|
||||
}
|
||||
void updt_params(params_ref const &p) override { m_solver.updt_params(p); }
|
||||
void reset_params(params_ref const &p) override { m_solver.reset_params(p); }
|
||||
const params_ref &get_params() const override { return m_solver.get_params(); }
|
||||
void push_params() override { m_solver.push_params(); }
|
||||
void pop_params() override { m_solver.pop_params(); }
|
||||
void collect_param_descrs(param_descrs &r) override { m_solver.collect_param_descrs(r); }
|
||||
void set_produce_models(bool f) override { m_solver.set_produce_models(f); }
|
||||
void assert_expr_core(expr *t) override { m_solver.assert_expr(t); }
|
||||
void assert_expr_core2(expr *t, expr *a) override { NOT_IMPLEMENTED_YET(); }
|
||||
expr_ref_vector cube(expr_ref_vector&, unsigned) override { return expr_ref_vector(m); }
|
||||
|
||||
void push() override;
|
||||
void pop(unsigned n) override;
|
||||
unsigned get_scope_level() const override { return m_solver.get_scope_level(); }
|
||||
|
||||
lbool check_sat(unsigned num_assumptions, expr * const *assumptions) override;
|
||||
lbool check_sat_cc(const expr_ref_vector &cube, vector<expr_ref_vector> const & clauses) override;
|
||||
void set_progress_callback(progress_callback *callback) override {
|
||||
m_solver.set_progress_callback(callback);
|
||||
}
|
||||
unsigned get_num_assertions() const override { return m_solver.get_num_assertions(); }
|
||||
expr * get_assertion(unsigned idx) const override { return m_solver.get_assertion(idx); }
|
||||
unsigned get_num_assumptions() const override { return m_solver.get_num_assumptions(); }
|
||||
expr * get_assumption(unsigned idx) const override { return m_solver.get_assumption(idx); }
|
||||
std::ostream &display(std::ostream &out, unsigned n, expr* const* es) const override {
|
||||
return m_solver.display(out, n, es);
|
||||
}
|
||||
|
||||
/* check_sat_result interface */
|
||||
|
||||
void collect_statistics(statistics &st) const override ;
|
||||
virtual void reset_statistics();
|
||||
|
||||
void get_unsat_core(expr_ref_vector &r) override;
|
||||
void get_model_core(model_ref &m) override {m_solver.get_model(m);}
|
||||
proof *get_proof() override {return m_solver.get_proof();}
|
||||
std::string reason_unknown() const override { return m_solver.reason_unknown(); }
|
||||
void set_reason_unknown(char const* msg) override { m_solver.set_reason_unknown(msg); }
|
||||
void get_labels(svector<symbol> &r) override { m_solver.get_labels(r); }
|
||||
ast_manager& get_manager() const override { return m; }
|
||||
|
||||
virtual void refresh();
|
||||
|
||||
class scoped_mk_proxy {
|
||||
iuc_solver &m_s;
|
||||
expr_ref_vector &m_v;
|
||||
public:
|
||||
scoped_mk_proxy(iuc_solver &s, expr_ref_vector &v) : m_s(s), m_v(v) {
|
||||
m_s.mk_proxies(m_v);
|
||||
}
|
||||
~scoped_mk_proxy() { m_s.undo_proxies(m_v); }
|
||||
};
|
||||
|
||||
class scoped_bg {
|
||||
iuc_solver &m_s;
|
||||
unsigned m_bg_sz;
|
||||
public:
|
||||
scoped_bg(iuc_solver &s) : m_s(s), m_bg_sz(m_s.get_num_bg()) {}
|
||||
~scoped_bg() {
|
||||
if (m_s.get_num_bg() > m_bg_sz) {
|
||||
m_s.pop_bg(m_s.get_num_bg() - m_bg_sz);
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
}
|
||||
#endif
|
191
src/muz/spacer/spacer_json.cpp
Normal file
191
src/muz/spacer/spacer_json.cpp
Normal file
|
@ -0,0 +1,191 @@
|
|||
/**++
|
||||
Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_json.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
SPACER json marshalling support
|
||||
|
||||
Author:
|
||||
|
||||
Matteo Marescotti
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include <iomanip>
|
||||
#include "spacer_context.h"
|
||||
#include "spacer_json.h"
|
||||
#include "spacer_util.h"
|
||||
|
||||
namespace spacer {
|
||||
|
||||
static std::ostream &json_marshal(std::ostream &out, ast *t, ast_manager &m) {
|
||||
|
||||
mk_epp pp = mk_epp(t, m);
|
||||
std::ostringstream ss;
|
||||
ss << pp;
|
||||
out << "\"";
|
||||
for (auto &c:ss.str()) {
|
||||
switch (c) {
|
||||
case '"':
|
||||
out << "\\\"";
|
||||
break;
|
||||
case '\\':
|
||||
out << "\\\\";
|
||||
break;
|
||||
case '\b':
|
||||
out << "\\b";
|
||||
break;
|
||||
case '\f':
|
||||
out << "\\f";
|
||||
break;
|
||||
case '\n':
|
||||
out << "\\n";
|
||||
break;
|
||||
case '\r':
|
||||
out << "\\r";
|
||||
break;
|
||||
case '\t':
|
||||
out << "\\t";
|
||||
break;
|
||||
default:
|
||||
if ('\x00' <= c && c <= '\x1f') {
|
||||
out << "\\u"
|
||||
<< std::hex << std::setw(4) << std::setfill('0') << (int) c;
|
||||
} else {
|
||||
out << c;
|
||||
}
|
||||
}
|
||||
}
|
||||
out << "\"";
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::ostream &json_marshal(std::ostream &out, lemma *l) {
|
||||
out << "{"
|
||||
<< R"("init_level":")" << l->init_level()
|
||||
<< R"(", "level":")" << l->level()
|
||||
<< R"(", "expr":)";
|
||||
json_marshal(out, l->get_expr(), l->get_ast_manager());
|
||||
out << "}";
|
||||
return out;
|
||||
}
|
||||
|
||||
static std::ostream &json_marshal(std::ostream &out, const lemma_ref_vector &lemmas) {
|
||||
|
||||
std::ostringstream ls;
|
||||
for (auto l:lemmas) {
|
||||
ls << ((unsigned)ls.tellp() == 0 ? "" : ",");
|
||||
json_marshal(ls, l);
|
||||
}
|
||||
out << "[" << ls.str() << "]";
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
void json_marshaller::register_lemma(lemma *l) {
|
||||
if (l->has_pob()) {
|
||||
m_relations[&*l->get_pob()][l->get_pob()->depth()].push_back(l);
|
||||
}
|
||||
}
|
||||
|
||||
void json_marshaller::register_pob(pob *p) {
|
||||
m_relations[p];
|
||||
}
|
||||
|
||||
void json_marshaller::marshal_lemmas_old(std::ostream &out) const {
|
||||
unsigned pob_id = 0;
|
||||
for (auto &pob_map:m_relations) {
|
||||
std::ostringstream pob_lemmas;
|
||||
for (auto &depth_lemmas : pob_map.second) {
|
||||
pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",")
|
||||
<< "\"" << depth_lemmas.first << "\":";
|
||||
json_marshal(pob_lemmas, depth_lemmas.second);
|
||||
}
|
||||
if (pob_lemmas.tellp()) {
|
||||
out << ((unsigned)out.tellp() == 0 ? "" : ",\n");
|
||||
out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}";
|
||||
}
|
||||
pob_id++;
|
||||
}
|
||||
}
|
||||
void json_marshaller::marshal_lemmas_new(std::ostream &out) const {
|
||||
unsigned pob_id = 0;
|
||||
for (auto &pob_map:m_relations) {
|
||||
std::ostringstream pob_lemmas;
|
||||
pob *n = pob_map.first;
|
||||
unsigned i = 0;
|
||||
for (auto *l : n->lemmas()) {
|
||||
pob_lemmas << ((unsigned)pob_lemmas.tellp() == 0 ? "" : ",")
|
||||
<< "\"" << i++ << "\":";
|
||||
lemma_ref_vector lemmas_vec;
|
||||
lemmas_vec.push_back(l);
|
||||
json_marshal(pob_lemmas, lemmas_vec);
|
||||
}
|
||||
|
||||
if (pob_lemmas.tellp()) {
|
||||
out << ((unsigned)out.tellp() == 0 ? "" : ",\n");
|
||||
out << "\"" << pob_id << "\":{" << pob_lemmas.str() << "}";
|
||||
}
|
||||
pob_id++;
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &json_marshaller::marshal(std::ostream &out) const {
|
||||
std::ostringstream nodes;
|
||||
std::ostringstream edges;
|
||||
std::ostringstream lemmas;
|
||||
|
||||
if (m_old_style)
|
||||
marshal_lemmas_old(lemmas);
|
||||
else
|
||||
marshal_lemmas_new(lemmas);
|
||||
|
||||
unsigned pob_id = 0;
|
||||
unsigned depth = 0;
|
||||
while (true) {
|
||||
double root_expand_time = m_ctx->get_root().get_expand_time(depth);
|
||||
bool a = false;
|
||||
pob_id = 0;
|
||||
for (auto &pob_map:m_relations) {
|
||||
pob *n = pob_map.first;
|
||||
double expand_time = n->get_expand_time(depth);
|
||||
if (expand_time > 0) {
|
||||
a = true;
|
||||
std::ostringstream pob_expr;
|
||||
json_marshal(pob_expr, n->post(), n->get_ast_manager());
|
||||
|
||||
nodes << ((unsigned)nodes.tellp() == 0 ? "" : ",\n") <<
|
||||
"{\"id\":\"" << depth << n <<
|
||||
"\",\"relative_time\":\"" << expand_time / root_expand_time <<
|
||||
"\",\"absolute_time\":\"" << std::setprecision(2) << expand_time <<
|
||||
"\",\"predicate\":\"" << n->pt().head()->get_name() <<
|
||||
"\",\"expr_id\":\"" << n->post()->get_id() <<
|
||||
"\",\"pob_id\":\"" << pob_id <<
|
||||
"\",\"depth\":\"" << depth <<
|
||||
"\",\"expr\":" << pob_expr.str() << "}";
|
||||
if (n->parent()) {
|
||||
edges << ((unsigned)edges.tellp() == 0 ? "" : ",\n") <<
|
||||
"{\"from\":\"" << depth << n->parent() <<
|
||||
"\",\"to\":\"" << depth << n << "\"}";
|
||||
}
|
||||
}
|
||||
pob_id++;
|
||||
}
|
||||
if (!a) {
|
||||
break;
|
||||
}
|
||||
depth++;
|
||||
}
|
||||
out << "{\n\"nodes\":[\n" << nodes.str() << "\n],\n";
|
||||
out << "\"edges\":[\n" << edges.str() << "\n],\n";
|
||||
out << "\"lemmas\":{\n" << lemmas.str() << "\n}\n}\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
}
|
61
src/muz/spacer/spacer_json.h
Normal file
61
src/muz/spacer/spacer_json.h
Normal file
|
@ -0,0 +1,61 @@
|
|||
/**++
|
||||
Copyright (c) 2017 Microsoft Corporation and Matteo Marescotti
|
||||
|
||||
Module Name:
|
||||
|
||||
spacer_json.h
|
||||
|
||||
Abstract:
|
||||
|
||||
SPACER json marshalling support
|
||||
|
||||
Author:
|
||||
|
||||
Matteo Marescotti
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef Z3_SPACER_JSON_H
|
||||
#define Z3_SPACER_JSON_H
|
||||
|
||||
#include<iostream>
|
||||
#include<map>
|
||||
#include "util/ref.h"
|
||||
#include "util/ref_vector.h"
|
||||
|
||||
class ast;
|
||||
|
||||
class ast_manager;
|
||||
|
||||
namespace spacer {
|
||||
|
||||
class lemma;
|
||||
typedef sref_vector<lemma> lemma_ref_vector;
|
||||
class context;
|
||||
class pob;
|
||||
|
||||
|
||||
class json_marshaller {
|
||||
context *m_ctx;
|
||||
bool m_old_style;
|
||||
std::map<pob*, std::map<unsigned, lemma_ref_vector>> m_relations;
|
||||
|
||||
void marshal_lemmas_old(std::ostream &out) const;
|
||||
void marshal_lemmas_new(std::ostream &out) const;
|
||||
public:
|
||||
json_marshaller(context *ctx, bool old_style = false) :
|
||||
m_ctx(ctx), m_old_style(old_style) {}
|
||||
|
||||
void register_lemma(lemma *l);
|
||||
|
||||
void register_pob(pob *p);
|
||||
|
||||
std::ostream &marshal(std::ostream &out) const;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
#endif //Z3_SPACER_JSON_H
|
|
@ -99,7 +99,7 @@ void qe_project(ast_manager& m, app_ref_vector& vars, expr_ref& fml, model_ref&
|
|||
);
|
||||
{
|
||||
scoped_no_proof _sp(m);
|
||||
qe::arith_project(*M, arith_vars, fml, map);
|
||||
spacer_qe::arith_project(*M, arith_vars, fml, map);
|
||||
}
|
||||
SASSERT(arith_vars.empty());
|
||||
TRACE("spacer",
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue