3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-04-10 19:27:06 +00:00
This commit is contained in:
Nikolaj Bjorner 2018-06-15 14:58:10 -07:00
commit 6fc08e9c9f
236 changed files with 14093 additions and 16593 deletions

View file

@ -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)

View file

@ -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;

View file

@ -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"
};

View file

@ -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);

View file

@ -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);
/*@}*/
/*@}*/

View file

@ -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),

View file

@ -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_ */

View file

@ -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;

View file

@ -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();

View file

@ -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) {

View file

@ -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 */

View file

@ -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());

View file

@ -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 << "=>";
}

View file

@ -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);
}

View file

@ -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_ */

View file

@ -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); }

View file

@ -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) {

View file

@ -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
*/

View file

@ -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);

View file

@ -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));

View file

@ -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)) {

View file

@ -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)) {

View file

@ -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);

View file

@ -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:

View file

@ -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;
}

View file

@ -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); }
}

View file

@ -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))) ||

View file

@ -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; }

View file

@ -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))

View file

@ -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);

View file

@ -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) {

View file

@ -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) {

View file

@ -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)

View file

@ -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)

View file

@ -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;
}

View file

@ -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;

View file

@ -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));
}

View file

@ -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;
}
}

View file

@ -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; }

View file

@ -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);

View file

@ -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);
}

View file

@ -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;
};

View file

@ -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'),
))

View file

@ -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)) {

View file

@ -18,5 +18,5 @@ z3_add_component(muz
smt
smt2parser
PYG_FILES
fixedpoint_params.pyg
fp_params.pyg
)

View file

@ -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 {
};

View file

@ -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_ */

View file

@ -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() {}

View file

@ -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'),
))

View file

@ -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 {

View file

@ -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 {

View file

@ -9,7 +9,6 @@ z3_add_component(fp
clp
ddnf
muz
pdr
rel
spacer
tab

View file

@ -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));
}

View file

@ -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:

View file

@ -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));
}

View file

@ -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
)

View file

@ -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;
}
}

View file

@ -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

View file

@ -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

View file

@ -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();
}

View file

@ -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

View file

@ -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

View file

@ -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;
}
}
};

View file

@ -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

View file

@ -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;
}
};

View file

@ -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

View file

@ -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() {
}
}

View file

@ -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

View file

@ -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();
}
}

View file

@ -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

View file

@ -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();
}
}
};

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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>;

View file

@ -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

View file

@ -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());

View file

@ -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

View file

@ -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>;

View file

@ -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

View 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);
}
}

View 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

View file

@ -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);
}

View file

@ -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;
};
}

View file

@ -24,12 +24,6 @@ Revision History:
namespace spacer {
class farkas_learner {
typedef obj_hashtable<expr> expr_set;

View file

@ -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);
}
}
};

View file

@ -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

View file

@ -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)); }
}
}

View file

@ -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

View 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;
}
}

View 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_ */

View 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)); }
}
}

View 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

View 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;
}
}

View 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

View file

@ -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