mirror of
https://github.com/Z3Prover/z3
synced 2025-04-22 16:45:31 +00:00
Merge branch 'opt' of https://github.com/Z3Prover/z3 into unstable
This commit is contained in:
commit
ab5022888c
396 changed files with 86387 additions and 3294 deletions
|
@ -24,6 +24,7 @@ Revision History:
|
|||
#include"bv_decl_plugin.h"
|
||||
#include"datatype_decl_plugin.h"
|
||||
#include"array_decl_plugin.h"
|
||||
#include"pb_decl_plugin.h"
|
||||
#include"ast_translation.h"
|
||||
#include"ast_pp.h"
|
||||
#include"ast_ll_pp.h"
|
||||
|
@ -1081,7 +1082,6 @@ extern "C" {
|
|||
case OP_BSREM_I:
|
||||
case OP_BUREM_I:
|
||||
case OP_BSMOD_I:
|
||||
|
||||
return Z3_OP_UNINTERPRETED;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -1090,9 +1090,10 @@ extern "C" {
|
|||
}
|
||||
if (mk_c(c)->get_dt_fid() == _d->get_family_id()) {
|
||||
switch(_d->get_decl_kind()) {
|
||||
case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
|
||||
case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
|
||||
case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
|
||||
case OP_DT_CONSTRUCTOR: return Z3_OP_DT_CONSTRUCTOR;
|
||||
case OP_DT_RECOGNISER: return Z3_OP_DT_RECOGNISER;
|
||||
case OP_DT_ACCESSOR: return Z3_OP_DT_ACCESSOR;
|
||||
case OP_DT_UPDATE_FIELD: return Z3_OP_DT_UPDATE_FIELD;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return Z3_OP_UNINTERPRETED;
|
||||
|
@ -1186,6 +1187,15 @@ extern "C" {
|
|||
}
|
||||
}
|
||||
|
||||
if (mk_c(c)->get_pb_fid() == _d->get_family_id()) {
|
||||
switch(_d->get_decl_kind()) {
|
||||
case OP_PB_LE: return Z3_OP_PB_LE;
|
||||
case OP_PB_GE: return Z3_OP_PB_GE;
|
||||
case OP_AT_MOST_K: return Z3_OP_PB_AT_MOST;
|
||||
default: UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
return Z3_OP_UNINTERPRETED;
|
||||
Z3_CATCH_RETURN(Z3_OP_UNINTERPRETED);
|
||||
}
|
||||
|
|
|
@ -37,9 +37,7 @@ extern "C" {
|
|||
catch (z3_exception & ex) {
|
||||
// The error handler is only available for contexts
|
||||
// Just throw a warning.
|
||||
std::ostringstream buffer;
|
||||
buffer << "Error setting " << param_id << ", " << ex.msg();
|
||||
warning_msg(buffer.str().c_str());
|
||||
warning_msg(ex.msg());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -64,9 +62,7 @@ extern "C" {
|
|||
catch (z3_exception & ex) {
|
||||
// The error handler is only available for contexts
|
||||
// Just throw a warning.
|
||||
std::ostringstream buffer;
|
||||
buffer << "Error setting " << param_id << ": " << ex.msg();
|
||||
warning_msg(buffer.str().c_str());
|
||||
warning_msg(ex.msg());
|
||||
return Z3_FALSE;
|
||||
}
|
||||
}
|
||||
|
@ -92,9 +88,7 @@ extern "C" {
|
|||
catch (z3_exception & ex) {
|
||||
// The error handler is only available for contexts
|
||||
// Just throw a warning.
|
||||
std::ostringstream buffer;
|
||||
buffer << "Error setting " << param_id << ": " << ex.msg();
|
||||
warning_msg(buffer.str().c_str());
|
||||
warning_msg(ex.msg());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,7 @@ namespace api {
|
|||
m_basic_fid = m().get_basic_family_id();
|
||||
m_arith_fid = m().mk_family_id("arith");
|
||||
m_bv_fid = m().mk_family_id("bv");
|
||||
m_pb_fid = m().mk_family_id("pb");
|
||||
m_array_fid = m().mk_family_id("array");
|
||||
m_dt_fid = m().mk_family_id("datatype");
|
||||
m_datalog_fid = m().mk_family_id("datalog_relation");
|
||||
|
|
|
@ -78,6 +78,7 @@ namespace api {
|
|||
family_id m_bv_fid;
|
||||
family_id m_dt_fid;
|
||||
family_id m_datalog_fid;
|
||||
family_id m_pb_fid;
|
||||
family_id m_fpa_fid;
|
||||
datatype_decl_plugin * m_dt_plugin;
|
||||
|
||||
|
@ -127,6 +128,7 @@ namespace api {
|
|||
family_id get_bv_fid() const { return m_bv_fid; }
|
||||
family_id get_dt_fid() const { return m_dt_fid; }
|
||||
family_id get_datalog_fid() const { return m_datalog_fid; }
|
||||
family_id get_pb_fid() const { return m_pb_fid; }
|
||||
family_id get_fpa_fid() const { return m_fpa_fid; }
|
||||
datatype_decl_plugin * get_dt_plugin() const { return m_dt_plugin; }
|
||||
|
||||
|
|
|
@ -125,7 +125,7 @@ namespace api {
|
|||
return "unknown";
|
||||
}
|
||||
}
|
||||
std::string to_string(unsigned num_queries, expr*const* queries) {
|
||||
std::string to_string(unsigned num_queries, expr* const* queries) {
|
||||
std::stringstream str;
|
||||
m_context.display_smt2(num_queries, queries, str);
|
||||
return str.str();
|
||||
|
@ -466,13 +466,16 @@ extern "C" {
|
|||
ast_manager& m = mk_c(c)->m();
|
||||
Z3_ast_vector_ref* v = alloc(Z3_ast_vector_ref, m);
|
||||
mk_c(c)->save_object(v);
|
||||
expr_ref_vector rules(m);
|
||||
expr_ref_vector rules(m), queries(m);
|
||||
svector<symbol> names;
|
||||
|
||||
to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, names);
|
||||
to_fixedpoint_ref(d)->ctx().get_rules_as_formulas(rules, queries, names);
|
||||
for (unsigned i = 0; i < rules.size(); ++i) {
|
||||
v->m_ast_vector.push_back(rules[i].get());
|
||||
}
|
||||
for (unsigned i = 0; i < queries.size(); ++i) {
|
||||
v->m_ast_vector.push_back(m.mk_not(queries[i].get()));
|
||||
}
|
||||
RETURN_Z3(of_ast_vector(v));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@ extern "C" {
|
|||
return 0;
|
||||
}
|
||||
ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
|
||||
if (idx >= decls->size()) {
|
||||
if (!decls || idx >= decls->size()) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
}
|
||||
|
@ -506,9 +506,9 @@ extern "C" {
|
|||
RETURN_Z3(0);
|
||||
}
|
||||
ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
|
||||
if (idx >= decls->size()) {
|
||||
if (!decls || idx >= decls->size()) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
RETURN_Z3(0);
|
||||
}
|
||||
func_decl* decl = (*decls)[idx];
|
||||
decl = dt_util.get_constructor_recognizer(decl);
|
||||
|
@ -529,7 +529,7 @@ extern "C" {
|
|||
RETURN_Z3(0);
|
||||
}
|
||||
ptr_vector<func_decl> const * decls = dt_util.get_datatype_constructors(_t);
|
||||
if (idx_c >= decls->size()) {
|
||||
if (!decls || idx_c >= decls->size()) {
|
||||
SET_ERROR_CODE(Z3_INVALID_ARG);
|
||||
return 0;
|
||||
}
|
||||
|
@ -618,4 +618,25 @@ extern "C" {
|
|||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_ast Z3_datatype_update_field(
|
||||
__in Z3_context c, __in Z3_func_decl f, __in Z3_ast t, __in Z3_ast v) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_datatype_update_field(c, f, t, v);
|
||||
RESET_ERROR_CODE();
|
||||
ast_manager & m = mk_c(c)->m();
|
||||
func_decl* _f = to_func_decl(f);
|
||||
expr* _t = to_expr(t);
|
||||
expr* _v = to_expr(v);
|
||||
expr* args[2] = { _t, _v };
|
||||
sort* domain[2] = { m.get_sort(_t), m.get_sort(_v) };
|
||||
parameter param(_f);
|
||||
func_decl * d = m.mk_func_decl(mk_c(c)->get_array_fid(), OP_DT_UPDATE_FIELD, 1, ¶m, 2, domain);
|
||||
app* r = m.mk_app(d, 2, args);
|
||||
mk_c(c)->save_ast_trail(r);
|
||||
check_sorts(c, r);
|
||||
RETURN_Z3(of_ast(r));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
|
|
@ -290,10 +290,10 @@ extern "C" {
|
|||
}
|
||||
}
|
||||
else {
|
||||
model_ref _m;
|
||||
m_solver.get()->get_model(_m);
|
||||
model_ref mr;
|
||||
m_solver.get()->get_model(mr);
|
||||
Z3_model_ref *tmp_val = alloc(Z3_model_ref);
|
||||
tmp_val->m_model = _m.get();
|
||||
tmp_val->m_model = mr.get();
|
||||
mk_c(c)->save_object(tmp_val);
|
||||
*model = of_model(tmp_val);
|
||||
}
|
||||
|
|
|
@ -65,6 +65,18 @@ extern "C" {
|
|||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_bool Z3_API Z3_model_has_interp(Z3_context c, Z3_model m, Z3_func_decl a) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_model_has_interp(c, m, a);
|
||||
CHECK_NON_NULL(m, 0);
|
||||
if (to_model_ref(m)->has_interpretation(to_func_decl(a))) {
|
||||
return Z3_TRUE;
|
||||
} else {
|
||||
return Z3_FALSE;
|
||||
}
|
||||
Z3_CATCH_RETURN(Z3_FALSE);
|
||||
}
|
||||
|
||||
Z3_func_interp Z3_API Z3_model_get_func_interp(Z3_context c, Z3_model m, Z3_func_decl f) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_model_get_func_interp(c, m, f);
|
||||
|
|
243
src/api/api_opt.cpp
Normal file
243
src/api/api_opt.cpp
Normal file
|
@ -0,0 +1,243 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
api_opt.cpp
|
||||
|
||||
Abstract:
|
||||
API for optimization
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-12-3.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include<iostream>
|
||||
#include"z3.h"
|
||||
#include"api_log_macros.h"
|
||||
#include"api_stats.h"
|
||||
#include"api_context.h"
|
||||
#include"api_util.h"
|
||||
#include"api_model.h"
|
||||
#include"opt_context.h"
|
||||
#include"cancel_eh.h"
|
||||
#include"scoped_timer.h"
|
||||
|
||||
|
||||
extern "C" {
|
||||
|
||||
struct Z3_optimize_ref : public api::object {
|
||||
opt::context* m_opt;
|
||||
Z3_optimize_ref():m_opt(0) {}
|
||||
virtual ~Z3_optimize_ref() { dealloc(m_opt); }
|
||||
};
|
||||
inline Z3_optimize_ref * to_optimize(Z3_optimize o) { return reinterpret_cast<Z3_optimize_ref *>(o); }
|
||||
inline Z3_optimize of_optimize(Z3_optimize_ref * o) { return reinterpret_cast<Z3_optimize>(o); }
|
||||
inline opt::context* to_optimize_ptr(Z3_optimize o) { return to_optimize(o)->m_opt; }
|
||||
|
||||
Z3_optimize Z3_API Z3_mk_optimize(Z3_context c) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_optimize(c);
|
||||
RESET_ERROR_CODE();
|
||||
Z3_optimize_ref * o = alloc(Z3_optimize_ref);
|
||||
o->m_opt = alloc(opt::context,mk_c(c)->m());
|
||||
mk_c(c)->save_object(o);
|
||||
RETURN_Z3(of_optimize(o));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
void Z3_API Z3_optimize_inc_ref(Z3_context c, Z3_optimize o) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_inc_ref(c, o);
|
||||
RESET_ERROR_CODE();
|
||||
to_optimize(o)->inc_ref();
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
void Z3_API Z3_optimize_dec_ref(Z3_context c, Z3_optimize o) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_dec_ref(c, o);
|
||||
RESET_ERROR_CODE();
|
||||
to_optimize(o)->dec_ref();
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_assert(c, o, a);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_FORMULA(a,);
|
||||
to_optimize_ptr(o)->add_hard_constraint(to_expr(a));
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_assert_soft(c, o, a, weight, id);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_FORMULA(a,0);
|
||||
rational w(weight);
|
||||
return to_optimize_ptr(o)->add_soft_constraint(to_expr(a), w, to_symbol(id));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_maximize(c, o, t);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_VALID_AST(t,0);
|
||||
return to_optimize_ptr(o)->add_objective(to_app(t), true);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_minimize(c, o, t);
|
||||
RESET_ERROR_CODE();
|
||||
CHECK_VALID_AST(t,0);
|
||||
return to_optimize_ptr(o)->add_objective(to_app(t), false);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_push(c, d);
|
||||
RESET_ERROR_CODE();
|
||||
to_optimize_ptr(d)->push();
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_pop(c, d);
|
||||
RESET_ERROR_CODE();
|
||||
to_optimize_ptr(d)->pop(1);
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
|
||||
Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_check(c, o);
|
||||
RESET_ERROR_CODE();
|
||||
lbool r = l_undef;
|
||||
cancel_eh<opt::context> eh(*to_optimize_ptr(o));
|
||||
unsigned timeout = to_optimize_ptr(o)->get_params().get_uint("timeout", mk_c(c)->get_timeout());
|
||||
api::context::set_interruptable si(*(mk_c(c)), eh);
|
||||
{
|
||||
scoped_timer timer(timeout, &eh);
|
||||
try {
|
||||
r = to_optimize_ptr(o)->optimize();
|
||||
}
|
||||
catch (z3_exception& ex) {
|
||||
mk_c(c)->handle_exception(ex);
|
||||
r = l_undef;
|
||||
}
|
||||
// to_optimize_ref(d).cleanup();
|
||||
}
|
||||
return of_lbool(r);
|
||||
Z3_CATCH_RETURN(Z3_L_UNDEF);
|
||||
}
|
||||
|
||||
Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_get_model(c, o);
|
||||
RESET_ERROR_CODE();
|
||||
model_ref _m;
|
||||
to_optimize_ptr(o)->get_model(_m);
|
||||
Z3_model_ref * m_ref = alloc(Z3_model_ref);
|
||||
if (_m) {
|
||||
m_ref->m_model = _m;
|
||||
}
|
||||
else {
|
||||
m_ref->m_model = alloc(model, mk_c(c)->m());
|
||||
}
|
||||
mk_c(c)->save_object(m_ref);
|
||||
RETURN_Z3(of_model(m_ref));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_set_params(c, o, p);
|
||||
RESET_ERROR_CODE();
|
||||
param_descrs descrs;
|
||||
to_optimize_ptr(o)->collect_param_descrs(descrs);
|
||||
to_params(p)->m_params.validate(descrs);
|
||||
params_ref pr = to_param_ref(p);
|
||||
to_optimize_ptr(o)->updt_params(pr);
|
||||
Z3_CATCH;
|
||||
}
|
||||
|
||||
Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_get_param_descrs(c, o);
|
||||
RESET_ERROR_CODE();
|
||||
Z3_param_descrs_ref * d = alloc(Z3_param_descrs_ref);
|
||||
mk_c(c)->save_object(d);
|
||||
to_optimize_ptr(o)->collect_param_descrs(d->m_descrs);
|
||||
Z3_param_descrs r = of_param_descrs(d);
|
||||
RETURN_Z3(r);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
// get lower value or current approximation
|
||||
Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_get_lower(c, o, idx);
|
||||
RESET_ERROR_CODE();
|
||||
expr_ref e = to_optimize_ptr(o)->get_lower(idx);
|
||||
mk_c(c)->save_ast_trail(e);
|
||||
RETURN_Z3(of_expr(e));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
// get upper or current approximation
|
||||
Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_get_upper(c, o, idx);
|
||||
RESET_ERROR_CODE();
|
||||
expr_ref e = to_optimize_ptr(o)->get_upper(idx);
|
||||
mk_c(c)->save_ast_trail(e);
|
||||
RETURN_Z3(of_expr(e));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
Z3_string Z3_API Z3_optimize_to_string(Z3_context c, Z3_optimize o) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_to_string(c, o);
|
||||
RESET_ERROR_CODE();
|
||||
return mk_c(c)->mk_external_string(to_optimize_ptr(o)->to_string());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
Z3_string Z3_API Z3_optimize_get_help(Z3_context c, Z3_optimize d) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_get_help(c, d);
|
||||
RESET_ERROR_CODE();
|
||||
std::ostringstream buffer;
|
||||
param_descrs descrs;
|
||||
to_optimize_ptr(d)->collect_param_descrs(descrs);
|
||||
descrs.display(buffer);
|
||||
return mk_c(c)->mk_external_string(buffer.str());
|
||||
Z3_CATCH_RETURN("");
|
||||
}
|
||||
|
||||
Z3_stats Z3_API Z3_optimize_get_statistics(Z3_context c,Z3_optimize d) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_optimize_get_statistics(c, d);
|
||||
RESET_ERROR_CODE();
|
||||
Z3_stats_ref * st = alloc(Z3_stats_ref);
|
||||
to_optimize_ptr(d)->collect_statistics(st->m_stats);
|
||||
mk_c(c)->save_object(st);
|
||||
Z3_stats r = of_stats(st);
|
||||
RETURN_Z3(r);
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
61
src/api/api_pb.cpp
Normal file
61
src/api/api_pb.cpp
Normal file
|
@ -0,0 +1,61 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
api_pb.cpp
|
||||
|
||||
Abstract:
|
||||
API for pb theory
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-11-13.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
#include<iostream>
|
||||
#include"z3.h"
|
||||
#include"api_log_macros.h"
|
||||
#include"api_context.h"
|
||||
#include"api_util.h"
|
||||
#include"pb_decl_plugin.h"
|
||||
|
||||
extern "C" {
|
||||
|
||||
Z3_ast Z3_API Z3_mk_atmost(Z3_context c, unsigned num_args,
|
||||
Z3_ast const args[], unsigned k) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_atmost(c, num_args, args, k);
|
||||
RESET_ERROR_CODE();
|
||||
parameter param(k);
|
||||
pb_util util(mk_c(c)->m());
|
||||
ast* a = util.mk_at_most_k(num_args, to_exprs(args), k);
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
check_sorts(c, a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
Z3_ast Z3_API Z3_mk_pble(Z3_context c, unsigned num_args,
|
||||
Z3_ast const args[], int _coeffs[],
|
||||
int k) {
|
||||
Z3_TRY;
|
||||
LOG_Z3_mk_pble(c, num_args, args, _coeffs, k);
|
||||
RESET_ERROR_CODE();
|
||||
pb_util util(mk_c(c)->m());
|
||||
vector<rational> coeffs;
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
coeffs.push_back(rational(_coeffs[i]));
|
||||
}
|
||||
ast* a = util.mk_le(num_args, coeffs.c_ptr(), to_exprs(args), rational(k));
|
||||
mk_c(c)->save_ast_trail(a);
|
||||
check_sorts(c, a);
|
||||
RETURN_Z3(of_ast(a));
|
||||
Z3_CATCH_RETURN(0);
|
||||
}
|
||||
|
||||
|
||||
};
|
|
@ -1543,6 +1543,62 @@ namespace z3 {
|
|||
}
|
||||
};
|
||||
|
||||
class optimize : public object {
|
||||
Z3_optimize m_opt;
|
||||
public:
|
||||
class handle {
|
||||
unsigned m_h;
|
||||
public:
|
||||
handle(unsigned h): m_h(h) {}
|
||||
unsigned h() const { return m_h; }
|
||||
};
|
||||
optimize(context& c):object(c) { m_opt = Z3_mk_optimize(c); Z3_optimize_inc_ref(c, m_opt); }
|
||||
~optimize() { Z3_optimize_dec_ref(ctx(), m_opt); }
|
||||
operator Z3_optimize() const { return m_opt; }
|
||||
void add(expr const& e) {
|
||||
assert(e.is_bool());
|
||||
Z3_optimize_assert(ctx(), m_opt, e);
|
||||
}
|
||||
handle add(expr const& e, unsigned weight) {
|
||||
assert(e.is_bool());
|
||||
std::stringstream strm;
|
||||
strm << weight;
|
||||
return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, strm.str().c_str(), 0));
|
||||
}
|
||||
handle add(expr const& e, char const* weight) {
|
||||
assert(e.is_bool());
|
||||
return handle(Z3_optimize_assert_soft(ctx(), m_opt, e, weight, 0));
|
||||
}
|
||||
handle maximize(expr const& e) {
|
||||
return handle(Z3_optimize_maximize(ctx(), m_opt, e));
|
||||
}
|
||||
handle minimize(expr const& e) {
|
||||
return handle(Z3_optimize_minimize(ctx(), m_opt, e));
|
||||
}
|
||||
void push() {
|
||||
Z3_optimize_push(ctx(), m_opt);
|
||||
}
|
||||
void pop() {
|
||||
Z3_optimize_pop(ctx(), m_opt);
|
||||
}
|
||||
check_result check() { Z3_lbool r = Z3_optimize_check(ctx(), m_opt); check_error(); return to_check_result(r); }
|
||||
model get_model() const { Z3_model m = Z3_optimize_get_model(ctx(), m_opt); check_error(); return model(ctx(), m); }
|
||||
void set(params const & p) { Z3_optimize_set_params(ctx(), m_opt, p); check_error(); }
|
||||
expr lower(handle const& h) {
|
||||
Z3_ast r = Z3_optimize_get_lower(ctx(), m_opt, h.h());
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
expr upper(handle const& h) {
|
||||
Z3_ast r = Z3_optimize_get_upper(ctx(), m_opt, h.h());
|
||||
check_error();
|
||||
return expr(ctx(), r);
|
||||
}
|
||||
stats statistics() const { Z3_stats r = Z3_optimize_get_statistics(ctx(), m_opt); check_error(); return stats(ctx(), r); }
|
||||
friend std::ostream & operator<<(std::ostream & out, optimize const & s) { out << Z3_optimize_to_string(s.ctx(), s.m_opt); return out; }
|
||||
std::string help() const { char const * r = Z3_optimize_get_help(ctx(), m_opt); check_error(); return r; }
|
||||
};
|
||||
|
||||
inline tactic fail_if(probe const & p) {
|
||||
Z3_tactic r = Z3_tactic_fail_if(p.ctx(), p);
|
||||
p.check_error();
|
||||
|
|
|
@ -449,6 +449,19 @@ namespace Microsoft.Z3
|
|||
return MkDatatypeSorts(MkSymbols(names), c);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Update a datatype field at expression t with value v.
|
||||
/// The function performs a record update at t. The field
|
||||
/// that is passed in as argument is updated with value v,
|
||||
/// the remainig fields of t are unchanged.
|
||||
/// </summary>
|
||||
public Expr MkUpdateField(FuncDecl field, Expr t, Expr v)
|
||||
{
|
||||
return Expr.Create(this, Native.Z3_datatype_update_field(
|
||||
nCtx, field.NativeObject,
|
||||
t.NativeObject, v.NativeObject));
|
||||
}
|
||||
|
||||
#endregion
|
||||
#endregion
|
||||
|
||||
|
@ -2251,6 +2264,36 @@ namespace Microsoft.Z3
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Pseudo-Boolean constraints
|
||||
|
||||
/// <summary>
|
||||
/// Create an at-most-k constraint.
|
||||
/// </summary>
|
||||
public BoolExpr MkAtMost(BoolExpr[] args, uint k)
|
||||
{
|
||||
Contract.Requires(args != null);
|
||||
Contract.Requires(Contract.Result<BoolExpr[]>() != null);
|
||||
CheckContextMatch(args);
|
||||
return new BoolExpr(this, Native.Z3_mk_atmost(nCtx, (uint) args.Length,
|
||||
AST.ArrayToNative(args), k));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a pseudo-Boolean less-or-equal constraint.
|
||||
/// </summary>
|
||||
public BoolExpr MkPBLe(int[] coeffs, BoolExpr[] args, int k)
|
||||
{
|
||||
Contract.Requires(args != null);
|
||||
Contract.Requires(coeffs != null);
|
||||
Contract.Requires(args.Length == coeffs.Length);
|
||||
Contract.Requires(Contract.Result<BoolExpr[]>() != null);
|
||||
CheckContextMatch(args);
|
||||
return new BoolExpr(this, Native.Z3_mk_pble(nCtx, (uint) args.Length,
|
||||
AST.ArrayToNative(args),
|
||||
coeffs, k));
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Numerals
|
||||
|
||||
#region General Numerals
|
||||
|
@ -3438,6 +3481,18 @@ namespace Microsoft.Z3
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Optimization
|
||||
/// <summary>
|
||||
/// Create an Optimization context.
|
||||
/// </summary>
|
||||
public Optimize MkOptimize()
|
||||
{
|
||||
Contract.Ensures(Contract.Result<Optimize>() != null);
|
||||
|
||||
return new Optimize(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Floating-Point Arithmetic
|
||||
|
||||
#region Rounding Modes
|
||||
|
@ -4383,6 +4438,7 @@ namespace Microsoft.Z3
|
|||
Contract.Invariant(m_Statistics_DRQ != null);
|
||||
Contract.Invariant(m_Tactic_DRQ != null);
|
||||
Contract.Invariant(m_Fixedpoint_DRQ != null);
|
||||
Contract.Invariant(m_Optimize_DRQ != null);
|
||||
}
|
||||
|
||||
readonly private AST.DecRefQueue m_AST_DRQ = new AST.DecRefQueue();
|
||||
|
@ -4400,6 +4456,7 @@ namespace Microsoft.Z3
|
|||
readonly private Statistics.DecRefQueue m_Statistics_DRQ = new Statistics.DecRefQueue(10);
|
||||
readonly private Tactic.DecRefQueue m_Tactic_DRQ = new Tactic.DecRefQueue(10);
|
||||
readonly private Fixedpoint.DecRefQueue m_Fixedpoint_DRQ = new Fixedpoint.DecRefQueue(10);
|
||||
readonly private Optimize.DecRefQueue m_Optimize_DRQ = new Optimize.DecRefQueue(10);
|
||||
|
||||
/// <summary>
|
||||
/// AST DRQ
|
||||
|
@ -4476,6 +4533,11 @@ namespace Microsoft.Z3
|
|||
/// </summary>
|
||||
public IDecRefQueue Fixedpoint_DRQ { get { Contract.Ensures(Contract.Result<Fixedpoint.DecRefQueue>() != null); return m_Fixedpoint_DRQ; } }
|
||||
|
||||
/// <summary>
|
||||
/// Optimize DRQ
|
||||
/// </summary>
|
||||
public IDecRefQueue Optimize_DRQ { get { Contract.Ensures(Contract.Result<Optimize.DecRefQueue>() != null); return m_Fixedpoint_DRQ; } }
|
||||
|
||||
|
||||
internal long refCount = 0;
|
||||
|
||||
|
@ -4518,6 +4580,7 @@ namespace Microsoft.Z3
|
|||
Statistics_DRQ.Clear(this);
|
||||
Tactic_DRQ.Clear(this);
|
||||
Fixedpoint_DRQ.Clear(this);
|
||||
Optimize_DRQ.Clear(this);
|
||||
|
||||
m_boolSort = null;
|
||||
m_intSort = null;
|
||||
|
|
111
src/api/dotnet/Deprecated.cs
Normal file
111
src/api/dotnet/Deprecated.cs
Normal file
|
@ -0,0 +1,111 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
Deprecated.cs
|
||||
|
||||
Abstract:
|
||||
|
||||
Expose deprecated features for use from the managed API
|
||||
those who use them for experiments.
|
||||
|
||||
Author:
|
||||
|
||||
Christoph Wintersteiger (cwinter) 2012-03-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace Microsoft.Z3
|
||||
{
|
||||
/// <summary>
|
||||
/// The main interaction with Z3 happens via the Context.
|
||||
/// </summary>
|
||||
[ContractVerification(true)]
|
||||
public class Deprecated
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Creates a backtracking point.
|
||||
/// </summary>
|
||||
/// <seealso cref="Pop"/>
|
||||
public static void Push(Context ctx) {
|
||||
Native.Z3_push(ctx.nCtx);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backtracks <paramref name="n"/> backtracking points.
|
||||
/// </summary>
|
||||
/// <remarks>Note that an exception is thrown if <paramref name="n"/> is not smaller than <c>NumScopes</c></remarks>
|
||||
/// <seealso cref="Push"/>
|
||||
public static void Pop(Context ctx, uint n = 1) {
|
||||
Native.Z3_pop(ctx.nCtx, n);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert a constraint (or multiple) into the solver.
|
||||
/// </summary>
|
||||
public static void Assert(Context ctx, params BoolExpr[] constraints)
|
||||
{
|
||||
Contract.Requires(constraints != null);
|
||||
Contract.Requires(Contract.ForAll(constraints, c => c != null));
|
||||
|
||||
ctx.CheckContextMatch(constraints);
|
||||
foreach (BoolExpr a in constraints)
|
||||
{
|
||||
Native.Z3_assert_cnstr(ctx.nCtx, a.NativeObject);
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// Checks whether the assertions in the context are consistent or not.
|
||||
/// </summary>
|
||||
public static Status Check(Context ctx, List<BoolExpr> core, ref Model model, ref Expr proof, params Expr[] assumptions)
|
||||
{
|
||||
Z3_lbool r;
|
||||
model = null;
|
||||
proof = null;
|
||||
if (assumptions == null || assumptions.Length == 0)
|
||||
r = (Z3_lbool)Native.Z3_check(ctx.nCtx);
|
||||
else {
|
||||
IntPtr mdl = IntPtr.Zero, prf = IntPtr.Zero;
|
||||
uint core_size = 0;
|
||||
IntPtr[] native_core = new IntPtr[assumptions.Length];
|
||||
r = (Z3_lbool)Native.Z3_check_assumptions(ctx.nCtx,
|
||||
(uint)assumptions.Length, AST.ArrayToNative(assumptions),
|
||||
ref mdl, ref prf, ref core_size, native_core);
|
||||
|
||||
for (uint i = 0; i < core_size; i++)
|
||||
core.Add((BoolExpr)Expr.Create(ctx, native_core[i]));
|
||||
if (mdl != IntPtr.Zero) {
|
||||
model = new Model(ctx, mdl);
|
||||
}
|
||||
if (prf != IntPtr.Zero) {
|
||||
proof = Expr.Create(ctx, prf);
|
||||
}
|
||||
|
||||
}
|
||||
switch (r)
|
||||
{
|
||||
case Z3_lbool.Z3_L_TRUE: return Status.SATISFIABLE;
|
||||
case Z3_lbool.Z3_L_FALSE: return Status.UNSATISFIABLE;
|
||||
default: return Status.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves an assignment to atomic propositions for a satisfiable context.
|
||||
/// </summary>
|
||||
public static BoolExpr GetAssignment(Context ctx)
|
||||
{
|
||||
IntPtr x = Native.Z3_get_context_assignment(ctx.nCtx);
|
||||
return (BoolExpr)Expr.Create(ctx, x);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -304,6 +304,19 @@ namespace Microsoft.Z3
|
|||
}
|
||||
|
||||
/// <summary>
|
||||
/// Fixedpoint statistics.
|
||||
/// </summary>
|
||||
public Statistics Statistics
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Ensures(Contract.Result<Statistics>() != null);
|
||||
|
||||
return new Statistics(Context, Native.Z3_fixedpoint_get_statistics(Context.nCtx, NativeObject));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse an SMT-LIB2 file with fixedpoint rules.
|
||||
/// Add the rules to the current fixedpoint context.
|
||||
/// Return the set of queries in the file.
|
||||
|
|
298
src/api/dotnet/Optimize.cs
Normal file
298
src/api/dotnet/Optimize.cs
Normal file
|
@ -0,0 +1,298 @@
|
|||
/*++
|
||||
Copyright (c) 2012 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
Optimize.cs
|
||||
|
||||
Abstract:
|
||||
|
||||
Z3 Managed API: Optimizes
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-12-03
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
|
||||
namespace Microsoft.Z3
|
||||
{
|
||||
/// <summary>
|
||||
/// Object for managing optimizization context
|
||||
/// </summary>
|
||||
[ContractVerification(true)]
|
||||
public class Optimize : Z3Object
|
||||
{
|
||||
/// <summary>
|
||||
/// A string that describes all available optimize solver parameters.
|
||||
/// </summary>
|
||||
public string Help
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Ensures(Contract.Result<string>() != null);
|
||||
return Native.Z3_optimize_get_help(Context.nCtx, NativeObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the optimize solver parameters.
|
||||
/// </summary>
|
||||
public Params Parameters
|
||||
{
|
||||
set
|
||||
{
|
||||
Contract.Requires(value != null);
|
||||
Context.CheckContextMatch(value);
|
||||
Native.Z3_optimize_set_params(Context.nCtx, NativeObject, value.NativeObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieves parameter descriptions for Optimize solver.
|
||||
/// </summary>
|
||||
public ParamDescrs ParameterDescriptions
|
||||
{
|
||||
get { return new ParamDescrs(Context, Native.Z3_optimize_get_param_descrs(Context.nCtx, NativeObject)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert a constraint (or multiple) into the optimize solver.
|
||||
/// </summary>
|
||||
public void Assert(params BoolExpr[] constraints)
|
||||
{
|
||||
Contract.Requires(constraints != null);
|
||||
Contract.Requires(Contract.ForAll(constraints, c => c != null));
|
||||
|
||||
Context.CheckContextMatch(constraints);
|
||||
foreach (BoolExpr a in constraints)
|
||||
{
|
||||
Native.Z3_optimize_assert(Context.nCtx, NativeObject, a.NativeObject);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Alias for Assert.
|
||||
/// </summary>
|
||||
public void Add(params BoolExpr[] constraints)
|
||||
{
|
||||
Assert(constraints);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Handle to objectives returned by objective functions.
|
||||
/// </summary>
|
||||
public class Handle
|
||||
{
|
||||
Optimize opt;
|
||||
uint handle;
|
||||
internal Handle(Optimize opt, uint h)
|
||||
{
|
||||
this.opt = opt;
|
||||
this.handle = h;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a lower bound for the objective handle.
|
||||
/// </summary>
|
||||
public ArithExpr Lower
|
||||
{
|
||||
get { return opt.GetLower(handle); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve an upper bound for the objective handle.
|
||||
/// </summary>
|
||||
public ArithExpr Upper
|
||||
{
|
||||
get { return opt.GetUpper(handle); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve the value of an objective.
|
||||
/// </summary>
|
||||
public ArithExpr Value
|
||||
{
|
||||
get { return Lower; }
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Assert soft constraint
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// Return an objective which associates with the group of constraints.
|
||||
/// </remarks>
|
||||
public Handle AssertSoft(BoolExpr constraint, uint weight, string group)
|
||||
{
|
||||
Context.CheckContextMatch(constraint);
|
||||
Symbol s = Context.MkSymbol(group);
|
||||
return new Handle(this, Native.Z3_optimize_assert_soft(Context.nCtx, NativeObject, constraint.NativeObject, weight.ToString(), s.NativeObject));
|
||||
}
|
||||
|
||||
|
||||
///
|
||||
/// <summary>
|
||||
/// Check satisfiability of asserted constraints.
|
||||
/// Produce a model that (when the objectives are bounded and
|
||||
/// don't use strict inequalities) meets the objectives.
|
||||
/// </summary>
|
||||
///
|
||||
public Status Check() {
|
||||
Z3_lbool r = (Z3_lbool)Native.Z3_optimize_check(Context.nCtx, NativeObject);
|
||||
switch (r)
|
||||
{
|
||||
case Z3_lbool.Z3_L_TRUE:
|
||||
return Status.SATISFIABLE;
|
||||
case Z3_lbool.Z3_L_FALSE:
|
||||
return Status.UNSATISFIABLE;
|
||||
default:
|
||||
return Status.UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a backtracking point.
|
||||
/// </summary>
|
||||
/// <seealso cref="Pop"/>
|
||||
public void Push()
|
||||
{
|
||||
Native.Z3_optimize_push(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Backtrack one backtracking point.
|
||||
/// </summary>
|
||||
/// <remarks>Note that an exception is thrown if Pop is called without a corresponding <c>Push</c></remarks>
|
||||
/// <seealso cref="Push"/>
|
||||
public void Pop()
|
||||
{
|
||||
Native.Z3_optimize_pop(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// The model of the last <c>Check</c>.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The result is <c>null</c> if <c>Check</c> was not invoked before,
|
||||
/// if its results was not <c>SATISFIABLE</c>, or if model production is not enabled.
|
||||
/// </remarks>
|
||||
public Model Model
|
||||
{
|
||||
get
|
||||
{
|
||||
IntPtr x = Native.Z3_optimize_get_model(Context.nCtx, NativeObject);
|
||||
if (x == IntPtr.Zero)
|
||||
return null;
|
||||
else
|
||||
return new Model(Context, x);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare an arithmetical maximization objective.
|
||||
/// Return a handle to the objective. The handle is used as
|
||||
/// to retrieve the values of objectives after calling Check.
|
||||
/// </summary>
|
||||
public Handle MkMaximize(ArithExpr e)
|
||||
{
|
||||
return new Handle(this, Native.Z3_optimize_maximize(Context.nCtx, NativeObject, e.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Declare an arithmetical minimization objective.
|
||||
/// Similar to MkMaximize.
|
||||
/// </summary>
|
||||
public Handle MkMinimize(ArithExpr e)
|
||||
{
|
||||
return new Handle(this, Native.Z3_optimize_minimize(Context.nCtx, NativeObject, e.NativeObject));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve a lower bound for the objective handle.
|
||||
/// </summary>
|
||||
private ArithExpr GetLower(uint index)
|
||||
{
|
||||
return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_lower(Context.nCtx, NativeObject, index));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Retrieve an upper bound for the objective handle.
|
||||
/// </summary>
|
||||
private ArithExpr GetUpper(uint index)
|
||||
{
|
||||
return (ArithExpr)Expr.Create(Context, Native.Z3_optimize_get_upper(Context.nCtx, NativeObject, index));
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Print the context to a string (SMT-LIB parseable benchmark).
|
||||
/// </summary>
|
||||
public override string ToString()
|
||||
{
|
||||
return Native.Z3_optimize_to_string(Context.nCtx, NativeObject);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optimize statistics.
|
||||
/// </summary>
|
||||
public Statistics Statistics
|
||||
{
|
||||
get
|
||||
{
|
||||
Contract.Ensures(Contract.Result<Statistics>() != null);
|
||||
|
||||
return new Statistics(Context, Native.Z3_optimize_get_statistics(Context.nCtx, NativeObject));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#region Internal
|
||||
internal Optimize(Context ctx, IntPtr obj)
|
||||
: base(ctx, obj)
|
||||
{
|
||||
Contract.Requires(ctx != null);
|
||||
}
|
||||
internal Optimize(Context ctx)
|
||||
: base(ctx, Native.Z3_mk_optimize(ctx.nCtx))
|
||||
{
|
||||
Contract.Requires(ctx != null);
|
||||
}
|
||||
|
||||
internal class DecRefQueue : IDecRefQueue
|
||||
{
|
||||
public DecRefQueue() : base() { }
|
||||
public DecRefQueue(uint move_limit) : base(move_limit) { }
|
||||
internal override void IncRef(Context ctx, IntPtr obj)
|
||||
{
|
||||
Native.Z3_optimize_inc_ref(ctx.nCtx, obj);
|
||||
}
|
||||
|
||||
internal override void DecRef(Context ctx, IntPtr obj)
|
||||
{
|
||||
Native.Z3_optimize_dec_ref(ctx.nCtx, obj);
|
||||
}
|
||||
};
|
||||
|
||||
internal override void IncRef(IntPtr o)
|
||||
{
|
||||
Context.Optimize_DRQ.IncAndClear(Context, o);
|
||||
base.IncRef(o);
|
||||
}
|
||||
|
||||
internal override void DecRef(IntPtr o)
|
||||
{
|
||||
Context.Optimize_DRQ.Add(o);
|
||||
base.DecRef(o);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -79,6 +79,7 @@ namespace Microsoft.Z3
|
|||
Native.Z3_params_set_symbol(Context.nCtx, NativeObject, name.NativeObject, value.NativeObject);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Adds a parameter setting.
|
||||
/// </summary>
|
||||
|
@ -118,6 +119,7 @@ namespace Microsoft.Z3
|
|||
/// </summary>
|
||||
public void Add(string name, string value)
|
||||
{
|
||||
Contract.Requires(name != null);
|
||||
Contract.Requires(value != null);
|
||||
|
||||
Native.Z3_params_set_symbol(Context.nCtx, NativeObject, Context.MkSymbol(name).NativeObject, Context.MkSymbol(value).NativeObject);
|
||||
|
|
|
@ -361,6 +361,23 @@ public class Context extends IDisposable
|
|||
return mkDatatypeSorts(mkSymbols(names), c);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a datatype field at expression t with value v.
|
||||
* The function performs a record update at t. The field
|
||||
* that is passed in as argument is updated with value v,
|
||||
* the remainig fields of t are unchanged.
|
||||
**/
|
||||
public Expr MkUpdateField(FuncDecl field, Expr t, Expr v)
|
||||
throws Z3Exception
|
||||
{
|
||||
return Expr.create
|
||||
(this,
|
||||
Native.datatypeUpdateField
|
||||
(nCtx(), field.getNativeObject(),
|
||||
t.getNativeObject(), v.getNativeObject()));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a new function declaration.
|
||||
**/
|
||||
|
|
|
@ -322,7 +322,19 @@ public class Fixedpoint extends Z3Object
|
|||
return res;
|
||||
}
|
||||
|
||||
Fixedpoint(Context ctx, long obj)
|
||||
/**
|
||||
* Fixedpoint statistics.
|
||||
*
|
||||
* @throws Z3Exception
|
||||
**/
|
||||
public Statistics getStatistics() throws Z3Exception
|
||||
{
|
||||
return new Statistics(getContext(), Native.fixedpointGetStatistics(
|
||||
getContext().nCtx(), getNativeObject()));
|
||||
}
|
||||
|
||||
|
||||
Fixedpoint(Context ctx, long obj) throws Z3Exception
|
||||
{
|
||||
super(ctx, obj);
|
||||
}
|
||||
|
|
|
@ -2965,3 +2965,4 @@ let enable_trace ( tag : string ) =
|
|||
let disable_trace ( tag : string ) =
|
||||
(Z3native.enable_trace tag)
|
||||
|
||||
|
||||
|
|
|
@ -3355,3 +3355,5 @@ val enable_trace : string -> unit
|
|||
*)
|
||||
val disable_trace : string -> unit
|
||||
|
||||
|
||||
|
||||
|
|
18972
src/api/ml/z3_stubs.c
Normal file
18972
src/api/ml/z3_stubs.c
Normal file
File diff suppressed because it is too large
Load diff
|
@ -301,7 +301,6 @@ class AstRef(Z3PPObject):
|
|||
"""Return unique identifier for object. It can be used for hash-tables and maps."""
|
||||
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
|
||||
|
||||
|
||||
def ctx_ref(self):
|
||||
"""Return a reference to the C context where this AST node is stored."""
|
||||
return self.ctx.ref()
|
||||
|
@ -455,7 +454,6 @@ class SortRef(AstRef):
|
|||
def get_id(self):
|
||||
return Z3_get_ast_id(self.ctx_ref(), self.as_ast())
|
||||
|
||||
|
||||
def kind(self):
|
||||
"""Return the Z3 internal kind of a sort. This method can be used to test if `self` is one of the Z3 builtin sorts.
|
||||
|
||||
|
@ -555,6 +553,8 @@ def _to_sort_ref(s, ctx):
|
|||
return ArraySortRef(s, ctx)
|
||||
elif k == Z3_DATATYPE_SORT:
|
||||
return DatatypeSortRef(s, ctx)
|
||||
elif k == Z3_FINITE_DOMAIN_SORT:
|
||||
return FiniteDomainSortRef(s, ctx)
|
||||
elif k == Z3_FLOATING_POINT_SORT:
|
||||
return FPSortRef(s, ctx)
|
||||
elif k == Z3_ROUNDING_MODE_SORT:
|
||||
|
@ -6085,8 +6085,6 @@ class Solver(Z3PPObject):
|
|||
e = BoolVal(True, self.ctx).as_ast()
|
||||
return Z3_benchmark_to_smtlib_string(self.ctx.ref(), "benchmark generated from python API", "", "unknown", "", sz1, v, e)
|
||||
|
||||
|
||||
|
||||
def SolverFor(logic, ctx=None):
|
||||
"""Create a solver customized for the given logic.
|
||||
|
||||
|
@ -6347,6 +6345,166 @@ class Fixedpoint(Z3PPObject):
|
|||
else:
|
||||
return Exists(self.vars, fml)
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Finite domain sorts
|
||||
#
|
||||
#########################################
|
||||
|
||||
class FiniteDomainSortRef(SortRef):
|
||||
"""Finite domain sort."""
|
||||
|
||||
def size(self):
|
||||
"""Return the size of the finite domain sort"""
|
||||
r = (ctype.c_ulonglong * 1)()
|
||||
if Z3_get_finite_domain_sort_size(self.ctx_ref(), self.ast(), r):
|
||||
return r[0]
|
||||
else:
|
||||
raise Z3Exception("Failed to retrieve finite domain sort size")
|
||||
|
||||
def FiniteDomainSort(name, sz, ctx=None):
|
||||
"""Create a named finite domain sort of a given size sz"""
|
||||
ctx = _get_ctx(ctx)
|
||||
return FiniteDomainSortRef(Z3_mk_finite_domain_sort(ctx.ref(), name, sz), ctx)
|
||||
|
||||
#########################################
|
||||
#
|
||||
# Optimize
|
||||
#
|
||||
#########################################
|
||||
|
||||
class OptimizeObjective:
|
||||
def __init__(self, opt, value, is_max):
|
||||
self._opt = opt
|
||||
self._value = value
|
||||
self._is_max = is_max
|
||||
|
||||
def lower(self):
|
||||
opt = self._opt
|
||||
return _to_expr_ref(Z3_optimize_get_lower(opt.ctx.ref(), opt.optimize, self._value), opt.ctx)
|
||||
|
||||
def upper(self):
|
||||
opt = self._opt
|
||||
return _to_expr_ref(Z3_optimize_get_upper(opt.ctx.ref(), opt.optimize, self._value), opt.ctx)
|
||||
|
||||
def value(self):
|
||||
if self._is_max:
|
||||
return self.upper()
|
||||
else:
|
||||
return self.lower()
|
||||
|
||||
class Optimize(Z3PPObject):
|
||||
"""Optimize API provides methods for solving using objective functions and weighted soft constraints"""
|
||||
|
||||
def __init__(self, ctx=None):
|
||||
self.ctx = _get_ctx(ctx)
|
||||
self.optimize = Z3_mk_optimize(self.ctx.ref())
|
||||
Z3_optimize_inc_ref(self.ctx.ref(), self.optimize)
|
||||
|
||||
def __del__(self):
|
||||
if self.optimize != None:
|
||||
Z3_optimize_dec_ref(self.ctx.ref(), self.optimize)
|
||||
|
||||
def set(self, *args, **keys):
|
||||
"""Set a configuration option. The method `help()` return a string containing all available options.
|
||||
"""
|
||||
p = args2params(args, keys, self.ctx)
|
||||
Z3_optimize_set_params(self.ctx.ref(), self.optimize, p.params)
|
||||
|
||||
def help(self):
|
||||
"""Display a string describing all available options."""
|
||||
print(Z3_optimize_get_help(self.ctx.ref(), self.optimize))
|
||||
|
||||
def param_descrs(self):
|
||||
"""Return the parameter description set."""
|
||||
return ParamDescrsRef(Z3_optimize_get_param_descrs(self.ctx.ref(), self.optimize), self.ctx)
|
||||
|
||||
def assert_exprs(self, *args):
|
||||
"""Assert constraints as background axioms for the optimize solver."""
|
||||
args = _get_args(args)
|
||||
for arg in args:
|
||||
if isinstance(arg, Goal) or isinstance(arg, AstVector):
|
||||
for f in arg:
|
||||
Z3_optimize_assert(self.ctx.ref(), self.optimize, f.as_ast())
|
||||
else:
|
||||
Z3_optimize_assert(self.ctx.ref(), self.optimize, arg.as_ast())
|
||||
|
||||
def add(self, *args):
|
||||
"""Assert constraints as background axioms for the optimize solver. Alias for assert_expr."""
|
||||
self.assert_exprs(*args)
|
||||
|
||||
def add_soft(self, arg, weight = "1", id = None):
|
||||
"""Add soft constraint with optional weight and optional identifier.
|
||||
If no weight is supplied, then the penalty for violating the soft constraint
|
||||
is 1.
|
||||
Soft constraints are grouped by identifiers. Soft constraints that are
|
||||
added without identifiers are grouped by default.
|
||||
"""
|
||||
if _is_int(weight):
|
||||
weight = "%d" % weight
|
||||
if not isinstance(weight, str):
|
||||
raise Z3Exception("weight should be a string or an integer")
|
||||
if id == None:
|
||||
id = ""
|
||||
id = to_symbol(id, self.ctx)
|
||||
v = Z3_optimize_assert_soft(self.ctx.ref(), self.optimize, arg.as_ast(), weight, id)
|
||||
return OptimizeObjective(self, v, False)
|
||||
|
||||
def maximize(self, arg):
|
||||
"""Add objective function to maximize."""
|
||||
return OptimizeObjective(self, Z3_optimize_maximize(self.ctx.ref(), self.optimize, arg.as_ast()), True)
|
||||
|
||||
def minimize(self, arg):
|
||||
"""Add objective function to minimize."""
|
||||
return OptimizeObjective(self, Z3_optimize_minimize(self.ctx.ref(), self.optimize, arg.as_ast()), False)
|
||||
|
||||
def push(self):
|
||||
"""create a backtracking point for added rules, facts and assertions"""
|
||||
Z3_optimize_push(self.ctx.ref(), self.optimize)
|
||||
|
||||
def pop(self):
|
||||
"""restore to previously created backtracking point"""
|
||||
Z3_optimize_pop(self.ctx.ref(), self.optimize)
|
||||
|
||||
def check(self):
|
||||
"""Check satisfiability while optimizing objective functions."""
|
||||
return CheckSatResult(Z3_optimize_check(self.ctx.ref(), self.optimize))
|
||||
|
||||
def model(self):
|
||||
"""Return a model for the last check()."""
|
||||
try:
|
||||
return ModelRef(Z3_optimize_get_model(self.ctx.ref(), self.optimize), self.ctx)
|
||||
except Z3Exception:
|
||||
raise Z3Exception("model is not available")
|
||||
|
||||
def lower(self, obj):
|
||||
if not isinstance(obj, OptimizeObjective):
|
||||
raise Z3Exception("Expecting objective handle returned by maximize/minimize")
|
||||
return obj.lower()
|
||||
|
||||
def upper(self, obj):
|
||||
if not isinstance(obj, OptimizeObjective):
|
||||
raise Z3Exception("Expecting objective handle returned by maximize/minimize")
|
||||
return obj.upper()
|
||||
|
||||
def __repr__(self):
|
||||
"""Return a formatted string with all added rules and constraints."""
|
||||
return self.sexpr()
|
||||
|
||||
def sexpr(self):
|
||||
"""Return a formatted string (in Lisp-like format) with all added constraints. We say the string is in s-expression format.
|
||||
"""
|
||||
return Z3_optimize_to_string(self.ctx.ref(), self.optimize)
|
||||
|
||||
def statistics(self):
|
||||
"""Return statistics for the last `query()`.
|
||||
"""
|
||||
return Statistics(Z3_optimize_get_statistics(self.ctx.ref(), self.optimize), self.ctx)
|
||||
|
||||
|
||||
|
||||
|
||||
#########################################
|
||||
#
|
||||
# ApplyResult
|
||||
|
|
|
@ -1017,6 +1017,8 @@ class Formatter:
|
|||
return self.pp_seq(a.assertions(), 0, [])
|
||||
elif isinstance(a, z3.Fixedpoint):
|
||||
return a.sexpr()
|
||||
elif isinstance(a, z3.Optimize):
|
||||
return a.sexpr()
|
||||
elif isinstance(a, z3.ApplyResult):
|
||||
return self.pp_seq_seq(a, 0, [])
|
||||
elif isinstance(a, z3.ModelRef):
|
||||
|
|
|
@ -78,6 +78,10 @@ class FixedpointObj(ctypes.c_void_p):
|
|||
def __init__(self, fixedpoint): self._as_parameter_ = fixedpoint
|
||||
def from_param(obj): return obj
|
||||
|
||||
class OptimizeObj(ctypes.c_void_p):
|
||||
def __init__(self, optimize): self._as_parameter_ = optimize
|
||||
def from_param(obj): return obj
|
||||
|
||||
class ModelObj(ctypes.c_void_p):
|
||||
def __init__(self, model): self._as_parameter_ = model
|
||||
def from_param(obj): return obj
|
||||
|
|
468
src/api/python/z3util.py
Normal file
468
src/api/python/z3util.py
Normal file
|
@ -0,0 +1,468 @@
|
|||
"""
|
||||
Usage:
|
||||
import common_z3 as CM_Z3
|
||||
"""
|
||||
|
||||
import common as CM
|
||||
from z3 import *
|
||||
|
||||
def get_z3_version(as_str=False):
|
||||
major = ctypes.c_uint(0)
|
||||
minor = ctypes.c_uint(0)
|
||||
build = ctypes.c_uint(0)
|
||||
rev = ctypes.c_uint(0)
|
||||
Z3_get_version(major,minor,build,rev)
|
||||
rs = map(int,(major.value,minor.value,build.value,rev.value))
|
||||
if as_str:
|
||||
return "{}.{}.{}.{}".format(*rs)
|
||||
else:
|
||||
return rs
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def ehash(v):
|
||||
"""
|
||||
Returns a 'stronger' hash value than the default hash() method.
|
||||
The result from hash() is not enough to distinguish between 2
|
||||
z3 expressions in some cases.
|
||||
|
||||
>>> x1 = Bool('x'); x2 = Bool('x'); x3 = Int('x')
|
||||
>>> print x1.hash(),x2.hash(),x3.hash() #BAD: all same hash values
|
||||
783810685 783810685 783810685
|
||||
>>> print ehash(x1), ehash(x2), ehash(x3)
|
||||
x_783810685_1 x_783810685_1 x_783810685_2
|
||||
|
||||
"""
|
||||
if __debug__:
|
||||
assert is_expr(v)
|
||||
|
||||
return "{}_{}_{}".format(str(v),v.hash(),v.sort_kind())
|
||||
|
||||
|
||||
"""
|
||||
In Z3, variables are caleld *uninterpreted* consts and
|
||||
variables are *interpreted* consts.
|
||||
"""
|
||||
|
||||
def is_expr_var(v):
|
||||
"""
|
||||
EXAMPLES:
|
||||
|
||||
>>> is_expr_var(Int('7'))
|
||||
True
|
||||
>>> is_expr_var(IntVal('7'))
|
||||
False
|
||||
>>> is_expr_var(Bool('y'))
|
||||
True
|
||||
>>> is_expr_var(Int('x') + 7 == Int('y'))
|
||||
False
|
||||
>>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off'])
|
||||
>>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff)
|
||||
>>> is_expr_var(LOnOff)
|
||||
False
|
||||
>>> is_expr_var(On)
|
||||
False
|
||||
>>> is_expr_var(Block)
|
||||
True
|
||||
>>> is_expr_var(SafetyInjection)
|
||||
True
|
||||
"""
|
||||
|
||||
return is_const(v) and v.decl().kind()==Z3_OP_UNINTERPRETED
|
||||
|
||||
def is_expr_val(v):
|
||||
"""
|
||||
EXAMPLES:
|
||||
|
||||
>>> is_expr_val(Int('7'))
|
||||
False
|
||||
>>> is_expr_val(IntVal('7'))
|
||||
True
|
||||
>>> is_expr_val(Bool('y'))
|
||||
False
|
||||
>>> is_expr_val(Int('x') + 7 == Int('y'))
|
||||
False
|
||||
>>> LOnOff, (On,Off) = EnumSort("LOnOff",['On','Off'])
|
||||
>>> Block,Reset,SafetyInjection=Consts("Block Reset SafetyInjection",LOnOff)
|
||||
>>> is_expr_val(LOnOff)
|
||||
False
|
||||
>>> is_expr_val(On)
|
||||
True
|
||||
>>> is_expr_val(Block)
|
||||
False
|
||||
>>> is_expr_val(SafetyInjection)
|
||||
False
|
||||
"""
|
||||
return is_const(v) and v.decl().kind()!=Z3_OP_UNINTERPRETED
|
||||
|
||||
|
||||
|
||||
|
||||
def get_vars(f,rs=[]):
|
||||
"""
|
||||
>>> x,y = Ints('x y')
|
||||
>>> a,b = Bools('a b')
|
||||
>>> get_vars(Implies(And(x+y==0,x*2==10),Or(a,Implies(a,b==False))))
|
||||
[x, y, a, b]
|
||||
|
||||
"""
|
||||
if __debug__:
|
||||
assert is_expr(f)
|
||||
|
||||
if is_const(f):
|
||||
if is_expr_val(f):
|
||||
return rs
|
||||
else: #variable
|
||||
return CM.vset(rs + [f],str)
|
||||
|
||||
else:
|
||||
for f_ in f.children():
|
||||
rs = get_vars(f_,rs)
|
||||
|
||||
return CM.vset(rs,str)
|
||||
|
||||
|
||||
|
||||
def mk_var(name,vsort):
|
||||
if vsort.kind() == Z3_INT_SORT:
|
||||
v = Int(name)
|
||||
elif vsort.kind() == Z3_REAL_SORT:
|
||||
v = Real(name)
|
||||
elif vsort.kind() == Z3_BOOL_SORT:
|
||||
v = Bool(name)
|
||||
elif vsort.kind() == Z3_DATATYPE_SORT:
|
||||
v = Const(name,vsort)
|
||||
|
||||
else:
|
||||
assert False, 'Cannot handle this sort (s: %sid: %d)'\
|
||||
%(vsort,vsort.kind())
|
||||
|
||||
return v
|
||||
|
||||
|
||||
|
||||
def prove(claim,assume=None,verbose=0):
|
||||
"""
|
||||
>>> r,m = prove(BoolVal(True),verbose=0); r,model_str(m,as_str=False)
|
||||
(True, None)
|
||||
|
||||
#infinite counter example when proving contradiction
|
||||
>>> r,m = prove(BoolVal(False)); r,model_str(m,as_str=False)
|
||||
(False, [])
|
||||
|
||||
>>> x,y,z=Bools('x y z')
|
||||
>>> r,m = prove(And(x,Not(x))); r,model_str(m,as_str=True)
|
||||
(False, '[]')
|
||||
|
||||
>>> r,m = prove(True,assume=And(x,Not(x)),verbose=0)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError: Assumption is alway False!
|
||||
|
||||
>>> r,m = prove(Implies(x,x),assume=y,verbose=2); r,model_str(m,as_str=False)
|
||||
assume:
|
||||
y
|
||||
claim:
|
||||
Implies(x, x)
|
||||
to_prove:
|
||||
Implies(y, Implies(x, x))
|
||||
(True, None)
|
||||
|
||||
>>> r,m = prove(And(x,True),assume=y,verbose=0); r,model_str(m,as_str=False)
|
||||
(False, [(x, False), (y, True)])
|
||||
|
||||
>>> r,m = prove(And(x,y),assume=y,verbose=0)
|
||||
>>> print r
|
||||
False
|
||||
>>> print model_str(m,as_str=True)
|
||||
x = False
|
||||
y = True
|
||||
|
||||
>>> a,b = Ints('a b')
|
||||
>>> r,m = prove(a**b == b**a,assume=None,verbose=0)
|
||||
E: cannot solve !
|
||||
>>> r is None and m is None
|
||||
True
|
||||
|
||||
"""
|
||||
|
||||
if __debug__:
|
||||
assert not assume or is_expr(assume)
|
||||
|
||||
|
||||
to_prove = claim
|
||||
if assume:
|
||||
if __debug__:
|
||||
is_proved,_ = prove(Not(assume))
|
||||
|
||||
def _f():
|
||||
emsg = "Assumption is alway False!"
|
||||
if verbose >= 2:
|
||||
emsg = "{}\n{}".format(assume,emsg)
|
||||
return emsg
|
||||
|
||||
assert is_proved==False, _f()
|
||||
|
||||
to_prove = Implies(assume,to_prove)
|
||||
|
||||
|
||||
|
||||
if verbose >= 2:
|
||||
print 'assume: '
|
||||
print assume
|
||||
print 'claim: '
|
||||
print claim
|
||||
print 'to_prove: '
|
||||
print to_prove
|
||||
|
||||
f = Not(to_prove)
|
||||
|
||||
models = get_models(f,k=1)
|
||||
if models is None: #unknown
|
||||
print 'E: cannot solve !'
|
||||
return None, None
|
||||
elif models == False: #unsat
|
||||
return True,None
|
||||
else: #sat
|
||||
if __debug__:
|
||||
assert isinstance(models,list)
|
||||
|
||||
if models:
|
||||
return False, models[0] #the first counterexample
|
||||
else:
|
||||
return False, [] #infinite counterexample,models
|
||||
|
||||
|
||||
def get_models(f,k):
|
||||
"""
|
||||
Returns the first k models satisfiying f.
|
||||
If f is not satisfiable, returns False.
|
||||
If f cannot be solved, returns None
|
||||
If f is satisfiable, returns the first k models
|
||||
Note that if f is a tautology, e.g.\ True, then the result is []
|
||||
|
||||
Based on http://stackoverflow.com/questions/11867611/z3py-checking-all-solutions-for-equation
|
||||
|
||||
EXAMPLES:
|
||||
>>> x, y = Ints('x y')
|
||||
>>> len(get_models(And(0<=x,x <= 4),k=11))
|
||||
5
|
||||
>>> get_models(And(0<=x**y,x <= 1),k=2) is None
|
||||
True
|
||||
>>> get_models(And(0<=x,x <= -1),k=2)
|
||||
False
|
||||
>>> len(get_models(x+y==7,5))
|
||||
5
|
||||
>>> len(get_models(And(x<=5,x>=1),7))
|
||||
5
|
||||
>>> get_models(And(x<=0,x>=5),7)
|
||||
False
|
||||
|
||||
>>> x = Bool('x')
|
||||
>>> get_models(And(x,Not(x)),k=1)
|
||||
False
|
||||
>>> get_models(Implies(x,x),k=1)
|
||||
[]
|
||||
>>> get_models(BoolVal(True),k=1)
|
||||
[]
|
||||
|
||||
|
||||
|
||||
"""
|
||||
|
||||
if __debug__:
|
||||
assert is_expr(f)
|
||||
assert k>=1
|
||||
|
||||
|
||||
|
||||
s = Solver()
|
||||
s.add(f)
|
||||
|
||||
models = []
|
||||
i = 0
|
||||
while s.check() == sat and i < k:
|
||||
i = i + 1
|
||||
|
||||
m = s.model()
|
||||
|
||||
if not m: #if m == []
|
||||
break
|
||||
|
||||
models.append(m)
|
||||
|
||||
|
||||
#create new constraint to block the current model
|
||||
block = Not(And([v() == m[v] for v in m]))
|
||||
s.add(block)
|
||||
|
||||
|
||||
if s.check() == unknown:
|
||||
return None
|
||||
elif s.check() == unsat and i==0:
|
||||
return False
|
||||
else:
|
||||
return models
|
||||
|
||||
def is_tautology(claim,verbose=0):
|
||||
"""
|
||||
>>> is_tautology(Implies(Bool('x'),Bool('x')))
|
||||
True
|
||||
|
||||
>>> is_tautology(Implies(Bool('x'),Bool('y')))
|
||||
False
|
||||
|
||||
>>> is_tautology(BoolVal(True))
|
||||
True
|
||||
|
||||
>>> is_tautology(BoolVal(False))
|
||||
False
|
||||
|
||||
"""
|
||||
return prove(claim=claim,assume=None,verbose=verbose)[0]
|
||||
|
||||
|
||||
def is_contradiction(claim,verbose=0):
|
||||
"""
|
||||
>>> x,y=Bools('x y')
|
||||
>>> is_contradiction(BoolVal(False))
|
||||
True
|
||||
|
||||
>>> is_contradiction(BoolVal(True))
|
||||
False
|
||||
|
||||
>>> is_contradiction(x)
|
||||
False
|
||||
|
||||
>>> is_contradiction(Implies(x,y))
|
||||
False
|
||||
|
||||
>>> is_contradiction(Implies(x,x))
|
||||
False
|
||||
|
||||
>>> is_contradiction(And(x,Not(x)))
|
||||
True
|
||||
"""
|
||||
|
||||
return prove(claim=Not(claim),assume=None,verbose=verbose)[0]
|
||||
|
||||
|
||||
def exact_one_model(f):
|
||||
"""
|
||||
return True if f has exactly 1 model, False otherwise.
|
||||
|
||||
EXAMPLES:
|
||||
|
||||
>>> x, y = Ints('x y')
|
||||
>>> exact_one_model(And(0<=x**y,x <= 0))
|
||||
False
|
||||
|
||||
>>> exact_one_model(And(0<=x,x <= 0))
|
||||
True
|
||||
|
||||
>>> exact_one_model(And(0<=x,x <= 1))
|
||||
False
|
||||
|
||||
>>> exact_one_model(And(0<=x,x <= -1))
|
||||
False
|
||||
"""
|
||||
|
||||
models = get_models(f,k=2)
|
||||
if isinstance(models,list):
|
||||
return len(models)==1
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def myBinOp(op,*L):
|
||||
"""
|
||||
>>> myAnd(*[Bool('x'),Bool('y')])
|
||||
And(x, y)
|
||||
|
||||
>>> myAnd(*[Bool('x'),None])
|
||||
x
|
||||
|
||||
>>> myAnd(*[Bool('x')])
|
||||
x
|
||||
|
||||
>>> myAnd(*[])
|
||||
|
||||
>>> myAnd(Bool('x'),Bool('y'))
|
||||
And(x, y)
|
||||
|
||||
>>> myAnd(*[Bool('x'),Bool('y')])
|
||||
And(x, y)
|
||||
|
||||
>>> myAnd([Bool('x'),Bool('y')])
|
||||
And(x, y)
|
||||
|
||||
>>> myAnd((Bool('x'),Bool('y')))
|
||||
And(x, y)
|
||||
|
||||
>>> myAnd(*[Bool('x'),Bool('y'),True])
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AssertionError
|
||||
"""
|
||||
|
||||
if __debug__:
|
||||
assert op == Z3_OP_OR or op == Z3_OP_AND or op == Z3_OP_IMPLIES
|
||||
|
||||
if len(L)==1 and (isinstance(L[0],list) or isinstance(L[0],tuple)):
|
||||
L = L[0]
|
||||
|
||||
if __debug__:
|
||||
assert all(not isinstance(l,bool) for l in L)
|
||||
|
||||
L = [l for l in L if is_expr(l)]
|
||||
if L:
|
||||
if len(L)==1:
|
||||
return L[0]
|
||||
else:
|
||||
if op == Z3_OP_OR:
|
||||
return Or(L)
|
||||
elif op == Z3_OP_AND:
|
||||
return And(L)
|
||||
else: #IMPLIES
|
||||
return Implies(L[0],L[1])
|
||||
else:
|
||||
return None
|
||||
|
||||
|
||||
def myAnd(*L): return myBinOp(Z3_OP_AND,*L)
|
||||
def myOr(*L): return myBinOp(Z3_OP_OR,*L)
|
||||
def myImplies(a,b):return myBinOp(Z3_OP_IMPLIES,[a,b])
|
||||
|
||||
|
||||
|
||||
Iff = lambda f,g: And(Implies(f,g),Implies(g,f))
|
||||
|
||||
|
||||
|
||||
def model_str(m,as_str=True):
|
||||
"""
|
||||
Returned a 'sorted' model (so that it's easier to see)
|
||||
The model is sorted by its key,
|
||||
e.g. if the model is y = 3 , x = 10, then the result is
|
||||
x = 10, y = 3
|
||||
|
||||
EXAMPLES:
|
||||
see doctest exampels from function prove()
|
||||
|
||||
"""
|
||||
if __debug__:
|
||||
assert m is None or m == [] or isinstance(m,ModelRef)
|
||||
|
||||
if m :
|
||||
vs = [(v,m[v]) for v in m]
|
||||
vs = sorted(vs,key=lambda (a,_): str(a))
|
||||
if as_str:
|
||||
return '\n'.join(['{} = {}'.format(k,v) for (k,v) in vs])
|
||||
else:
|
||||
return vs
|
||||
else:
|
||||
return str(m) if as_str else m
|
||||
|
263
src/api/z3_api.h
263
src/api/z3_api.h
|
@ -47,6 +47,7 @@ DEFINE_TYPE(Z3_func_interp);
|
|||
#define Z3_func_interp_opt Z3_func_interp
|
||||
DEFINE_TYPE(Z3_func_entry);
|
||||
DEFINE_TYPE(Z3_fixedpoint);
|
||||
DEFINE_TYPE(Z3_optimize);
|
||||
DEFINE_TYPE(Z3_rcf_num);
|
||||
DEFINE_VOID(Z3_theory_data);
|
||||
#endif
|
||||
|
@ -85,6 +86,7 @@ DEFINE_VOID(Z3_theory_data);
|
|||
- \c Z3_func_interp: interpretation of a function in a model.
|
||||
- \c Z3_func_entry: representation of the value of a \c Z3_func_interp at a particular point.
|
||||
- \c Z3_fixedpoint: context for the recursive predicate solver.
|
||||
- \c Z3_optimize: context for solving optimization queries.
|
||||
- \c Z3_ast_vector: vector of \c Z3_ast objects.
|
||||
- \c Z3_ast_map: mapping from \c Z3_ast to \c Z3_ast objects.
|
||||
- \c Z3_goal: set of formulas that can be solved and/or transformed using tactics and solvers.
|
||||
|
@ -877,6 +879,17 @@ typedef enum
|
|||
|
||||
- Z3_OP_DT_ACCESSOR: datatype accessor.
|
||||
|
||||
- Z3_OP_DT_UPDATE_FIELD: datatype field update.
|
||||
|
||||
- Z3_OP_PB_AT_MOST: Cardinality constraint.
|
||||
E.g., x + y + z <= 2
|
||||
|
||||
- Z3_OP_PB_LE: Generalized Pseudo-Boolean cardinality constraint.
|
||||
Example 2*x + 3*y <= 4
|
||||
|
||||
- Z3_OP_PB_GE: Generalized Pseudo-Boolean cardinality constraint.
|
||||
Example 2*x + 3*y + 2*z >= 4
|
||||
|
||||
- Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN: Floating-point rounding mode RNE
|
||||
|
||||
- Z3_OP_FPA_RM_NEAREST_TIES_TO_AWAY: Floating-point rounding mode RNA
|
||||
|
@ -1141,6 +1154,12 @@ typedef enum {
|
|||
Z3_OP_DT_CONSTRUCTOR=0x800,
|
||||
Z3_OP_DT_RECOGNISER,
|
||||
Z3_OP_DT_ACCESSOR,
|
||||
Z3_OP_DT_UPDATE_FIELD,
|
||||
|
||||
// Pseudo Booleans
|
||||
Z3_OP_PB_AT_MOST=0x900,
|
||||
Z3_OP_PB_LE,
|
||||
Z3_OP_PB_GE,
|
||||
|
||||
// Floating-Point Arithmetic
|
||||
Z3_OP_FPA_RM_NEAREST_TIES_TO_EVEN,
|
||||
|
@ -1327,6 +1346,7 @@ typedef enum
|
|||
def_Type('FUNC_INTERP', 'Z3_func_interp', 'FuncInterpObj')
|
||||
def_Type('FUNC_ENTRY', 'Z3_func_entry', 'FuncEntryObj')
|
||||
def_Type('FIXEDPOINT', 'Z3_fixedpoint', 'FixedpointObj')
|
||||
def_Type('OPTIMIZE', 'Z3_optimize', 'OptimizeObj')
|
||||
def_Type('PARAM_DESCRS', 'Z3_param_descrs', 'ParamDescrs')
|
||||
def_Type('RCF_NUM', 'Z3_rcf_num', 'RCFNumObj')
|
||||
*/
|
||||
|
@ -3868,6 +3888,28 @@ END_MLAPI_EXCLUDE
|
|||
Z3_func_decl Z3_API Z3_get_datatype_sort_constructor_accessor(
|
||||
__in Z3_context c, __in Z3_sort t, unsigned idx_c, unsigned idx_a);
|
||||
|
||||
/**
|
||||
\brief Update record field with a value.
|
||||
|
||||
This corresponds to the 'with' construct in OCaml.
|
||||
It has the effect of updating a record field with a given value.
|
||||
The remaining fields are left unchanged. It is the record
|
||||
equivalent of an array store (see \sa Z3_mk_store).
|
||||
If the datatype has more than one constructor, then the update function
|
||||
behaves as identity if there is a miss-match between the accessor and
|
||||
constructor. For example ((_ update-field car) nil 1) is nil,
|
||||
while ((_ update-field car) (cons 2 nil) 1) is (cons 1 nil).
|
||||
|
||||
|
||||
\pre Z3_get_sort_kind(Z3_get_sort(c, t)) == Z3_get_domain(c, field_access, 1) == Z3_DATATYPE_SORT
|
||||
\pre Z3_get_sort(c, value) == Z3_get_range(c, field_access)
|
||||
|
||||
|
||||
def_API('Z3_datatype_update_field', AST, (_in(CONTEXT), _in(FUNC_DECL), _in(AST), _in(AST)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_datatype_update_field(
|
||||
__in Z3_context c, __in Z3_func_decl field_access,
|
||||
__in Z3_ast t, __in Z3_ast value);
|
||||
|
||||
/**
|
||||
\brief Return arity of relation.
|
||||
|
@ -3893,6 +3935,29 @@ END_MLAPI_EXCLUDE
|
|||
Z3_sort Z3_API Z3_get_relation_column(__in Z3_context c, __in Z3_sort s, unsigned col);
|
||||
|
||||
|
||||
/**
|
||||
\brief Pseudo-Boolean relations.
|
||||
|
||||
Encode p1 + p2 + ... + pn <= k
|
||||
|
||||
def_API('Z3_mk_atmost', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in(UINT)))
|
||||
*/
|
||||
|
||||
Z3_ast Z3_API Z3_mk_atmost(__in Z3_context c, __in unsigned num_args,
|
||||
__in_ecount(num_args) Z3_ast const args[], __in unsigned k);
|
||||
|
||||
/**
|
||||
\brief Pseudo-Boolean relations.
|
||||
|
||||
Encode k1*p1 + k2*p2 + ... + kn*pn <= k
|
||||
|
||||
def_API('Z3_mk_pble', AST, (_in(CONTEXT), _in(UINT), _in_array(1,AST), _in_array(1,INT), _in(INT)))
|
||||
*/
|
||||
|
||||
Z3_ast Z3_API Z3_mk_pble(__in Z3_context c, __in unsigned num_args,
|
||||
__in_ecount(num_args) Z3_ast const args[], __in_ecount(num_args) int coeffs[],
|
||||
__in int k);
|
||||
|
||||
/**
|
||||
\mlonly {3 {L Function Declarations}} \endmlonly
|
||||
*/
|
||||
|
@ -4659,6 +4724,13 @@ END_MLAPI_EXCLUDE
|
|||
*/
|
||||
Z3_ast_opt Z3_API Z3_model_get_const_interp(__in Z3_context c, __in Z3_model m, __in Z3_func_decl a);
|
||||
|
||||
/**
|
||||
\brief Test if there exists an interpretation (i.e., assignment) for \c a in the model \c m.
|
||||
|
||||
def_API('Z3_model_has_interp', BOOL, (_in(CONTEXT), _in(MODEL), _in(FUNC_DECL)))
|
||||
*/
|
||||
Z3_bool Z3_API Z3_model_has_interp(__in Z3_context c, __in Z3_model m, __in Z3_func_decl a);
|
||||
|
||||
/**
|
||||
\brief Return the interpretation of the function \c f in the model \c m.
|
||||
Return \mlonly [None], \endmlonly \conly \c NULL,
|
||||
|
@ -6039,6 +6111,197 @@ END_MLAPI_EXCLUDE
|
|||
#endif
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifdef CorML4
|
||||
/**
|
||||
@name Optimize facilities
|
||||
*/
|
||||
/*@{*/
|
||||
|
||||
/**
|
||||
\brief Create a new optimize context.
|
||||
|
||||
\conly \remark User must use #Z3_optimize_inc_ref and #Z3_optimize_dec_ref to manage optimize objects.
|
||||
\conly Even if the context was created using #Z3_mk_context instead of #Z3_mk_context_rc.
|
||||
|
||||
def_API('Z3_mk_optimize', OPTIMIZE, (_in(CONTEXT), ))
|
||||
*/
|
||||
Z3_optimize Z3_API Z3_mk_optimize(__in Z3_context c);
|
||||
|
||||
#ifdef Conly
|
||||
/**
|
||||
\brief Increment the reference counter of the given optimize context
|
||||
|
||||
def_API('Z3_optimize_inc_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
void Z3_API Z3_optimize_inc_ref(__in Z3_context c,__in Z3_optimize d);
|
||||
|
||||
/**
|
||||
\brief Decrement the reference counter of the given optimize context.
|
||||
|
||||
def_API('Z3_optimize_dec_ref', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
void Z3_API Z3_optimize_dec_ref(__in Z3_context c,__in Z3_optimize d);
|
||||
#endif
|
||||
|
||||
/**
|
||||
\brief Assert hard constraint to the optimization context.
|
||||
|
||||
def_API('Z3_optimize_assert', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(AST)))
|
||||
*/
|
||||
void Z3_API Z3_optimize_assert(Z3_context c, Z3_optimize o, Z3_ast a);
|
||||
|
||||
|
||||
/**
|
||||
\brief Assert soft constraint to the optimization context.
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
\param a - formula
|
||||
\param weight - a positive weight, penalty for violating soft constraint
|
||||
\param id - optional identifier to group soft constraints
|
||||
|
||||
def_API('Z3_optimize_assert_soft', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST), _in(STRING), _in(SYMBOL)))
|
||||
*/
|
||||
unsigned Z3_API Z3_optimize_assert_soft(Z3_context c, Z3_optimize o, Z3_ast a, Z3_string weight, Z3_symbol id);
|
||||
|
||||
|
||||
/**
|
||||
\brief Add a maximization constraint.
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
\param a - arithmetical term
|
||||
def_API('Z3_optimize_maximize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST)))
|
||||
*/
|
||||
unsigned Z3_API Z3_optimize_maximize(Z3_context c, Z3_optimize o, Z3_ast t);
|
||||
|
||||
/**
|
||||
\brief Add a minimization constraint.
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
\param a - arithmetical term
|
||||
|
||||
def_API('Z3_optimize_minimize', UINT, (_in(CONTEXT), _in(OPTIMIZE), _in(AST)))
|
||||
*/
|
||||
unsigned Z3_API Z3_optimize_minimize(Z3_context c, Z3_optimize o, Z3_ast t);
|
||||
|
||||
|
||||
/**
|
||||
\brief Create a backtracking point.
|
||||
|
||||
The optimize solver contains a set of rules, added facts and assertions.
|
||||
The set of rules, facts and assertions are restored upon calling #Z3_optimize_pop.
|
||||
|
||||
\sa Z3_optimize_pop
|
||||
|
||||
def_API('Z3_optimize_push', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
void Z3_API Z3_optimize_push(Z3_context c,Z3_optimize d);
|
||||
|
||||
/**
|
||||
\brief Backtrack one level.
|
||||
|
||||
\sa Z3_optimize_push
|
||||
|
||||
\pre The number of calls to pop cannot exceed calls to push.
|
||||
|
||||
def_API('Z3_optimize_pop', VOID, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
void Z3_API Z3_optimize_pop(Z3_context c,Z3_optimize d);
|
||||
|
||||
/**
|
||||
\brief Check consistency and produce optimal values.
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
|
||||
def_API('Z3_optimize_check', INT, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
Z3_lbool Z3_API Z3_optimize_check(Z3_context c, Z3_optimize o);
|
||||
|
||||
|
||||
/**
|
||||
\brief Retrieve the model for the last #Z3_optimize_check
|
||||
|
||||
The error handler is invoked if a model is not available because
|
||||
the commands above were not invoked for the given optimization
|
||||
solver, or if the result was \c Z3_L_FALSE.
|
||||
|
||||
def_API('Z3_optimize_get_model', MODEL, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
Z3_model Z3_API Z3_optimize_get_model(Z3_context c, Z3_optimize o);
|
||||
|
||||
/**
|
||||
\brief Set parameters on optimization context.
|
||||
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
\param p - parameters
|
||||
|
||||
def_API('Z3_optimize_set_params', VOID, (_in(CONTEXT), _in(OPTIMIZE), _in(PARAMS)))
|
||||
*/
|
||||
void Z3_API Z3_optimize_set_params(Z3_context c, Z3_optimize o, Z3_params p);
|
||||
|
||||
/**
|
||||
\brief Return the parameter description set for the given optimize object.
|
||||
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
|
||||
def_API('Z3_optimize_get_param_descrs', PARAM_DESCRS, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
Z3_param_descrs Z3_API Z3_optimize_get_param_descrs(Z3_context c, Z3_optimize o);
|
||||
|
||||
/**
|
||||
\brief Retrieve lower bound value or approximation for the i'th optimization objective.
|
||||
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
\param idx - index of optimization objective
|
||||
|
||||
def_API('Z3_optimize_get_lower', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_optimize_get_lower(Z3_context c, Z3_optimize o, unsigned idx);
|
||||
|
||||
/**
|
||||
\brief Retrieve upper bound value or approximation for the i'th optimization objective.
|
||||
|
||||
\param c - context
|
||||
\param o - optimization context
|
||||
\param idx - index of optimization objective
|
||||
|
||||
def_API('Z3_optimize_get_upper', AST, (_in(CONTEXT), _in(OPTIMIZE), _in(UINT)))
|
||||
*/
|
||||
Z3_ast Z3_API Z3_optimize_get_upper(Z3_context c, Z3_optimize o, unsigned idx);
|
||||
|
||||
/**
|
||||
\brief Print the current context as a string.
|
||||
\param c - context.
|
||||
\param o - optimization context.
|
||||
|
||||
def_API('Z3_optimize_to_string', STRING, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
Z3_string Z3_API Z3_optimize_to_string(
|
||||
__in Z3_context c,
|
||||
__in Z3_optimize o);
|
||||
|
||||
|
||||
/**
|
||||
\brief Return a string containing a description of parameters accepted by optimize.
|
||||
|
||||
def_API('Z3_optimize_get_help', STRING, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
Z3_string Z3_API Z3_optimize_get_help(__in Z3_context c, __in Z3_optimize t);
|
||||
|
||||
/**
|
||||
\brief Retrieve statistics information from the last call to #Z3_optimize_check
|
||||
|
||||
def_API('Z3_optimize_get_statistics', STATS, (_in(CONTEXT), _in(OPTIMIZE)))
|
||||
*/
|
||||
Z3_stats Z3_API Z3_optimize_get_statistics(__in Z3_context c,__in Z3_optimize d);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CorML4
|
||||
/*@}*/
|
||||
|
||||
|
|
|
@ -22,12 +22,14 @@ Notes:
|
|||
#include"stream_buffer.h"
|
||||
#include"symbol.h"
|
||||
#include"trace.h"
|
||||
#include<sstream>
|
||||
|
||||
void register_z3_replayer_cmds(z3_replayer & in);
|
||||
|
||||
|
||||
void throw_invalid_reference() {
|
||||
TRACE("z3_replayer", tout << "invalid argument reference\n";);
|
||||
throw z3_replayer_exception("invalid argument reference");
|
||||
throw z3_replayer_exception("invalid argument reference1");
|
||||
}
|
||||
|
||||
struct z3_replayer::imp {
|
||||
|
@ -45,7 +47,38 @@ struct z3_replayer::imp {
|
|||
size_t_map<void *> m_heap;
|
||||
svector<z3_replayer_cmd> m_cmds;
|
||||
|
||||
enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT };
|
||||
enum value_kind { INT64, UINT64, DOUBLE, STRING, SYMBOL, OBJECT, UINT_ARRAY, INT_ARRAY, SYMBOL_ARRAY, OBJECT_ARRAY, FLOAT };
|
||||
|
||||
char const* kind2string(value_kind k) const {
|
||||
switch (k) {
|
||||
case INT64: return "int64";
|
||||
case UINT64: return "uint64";
|
||||
case DOUBLE: return "double";
|
||||
case STRING: return "string";
|
||||
case SYMBOL: return "symbol";
|
||||
case OBJECT: return "object";
|
||||
case UINT_ARRAY: return "uint_array";
|
||||
case INT_ARRAY: return "int_array";
|
||||
case SYMBOL_ARRAY: return "symbol_array";
|
||||
case OBJECT_ARRAY: return "object_array";
|
||||
case FLOAT: return "float";
|
||||
default: UNREACHABLE(); return "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void check_arg(unsigned pos, value_kind k) const {
|
||||
if (pos >= m_args.size()) {
|
||||
TRACE("z3_replayer", tout << "too few arguments " << m_args.size() << " expecting " << kind2string(k) << "\n";);
|
||||
throw z3_replayer_exception("invalid argument reference2");
|
||||
}
|
||||
if (m_args[pos].m_kind != k) {
|
||||
std::stringstream strm;
|
||||
strm << "expecting " << kind2string(k) << " at position "
|
||||
<< pos << " but got " << kind2string(m_args[pos].m_kind);
|
||||
throw z3_replayer_exception(strm.str().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
struct value {
|
||||
value_kind m_kind;
|
||||
|
@ -71,6 +104,7 @@ struct z3_replayer::imp {
|
|||
vector<ptr_vector<void> > m_obj_arrays;
|
||||
vector<svector<Z3_symbol> > m_sym_arrays;
|
||||
vector<unsigned_vector> m_unsigned_arrays;
|
||||
vector<svector<int> > m_int_arrays;
|
||||
|
||||
imp(z3_replayer & o, std::istream & in):
|
||||
m_owner(o),
|
||||
|
@ -321,6 +355,15 @@ struct z3_replayer::imp {
|
|||
v.push_back(static_cast<unsigned>(m_args[i].m_uint));
|
||||
}
|
||||
}
|
||||
if (k == INT64) {
|
||||
aidx = m_int_arrays.size();
|
||||
nk = INT_ARRAY;
|
||||
m_int_arrays.push_back(svector<int>());
|
||||
svector<int> & v = m_int_arrays.back();
|
||||
for (unsigned i = asz - sz; i < asz; i++) {
|
||||
v.push_back(static_cast<int>(m_args[i].m_int));
|
||||
}
|
||||
}
|
||||
else if (k == SYMBOL) {
|
||||
aidx = m_sym_arrays.size();
|
||||
nk = SYMBOL_ARRAY;
|
||||
|
@ -489,8 +532,7 @@ struct z3_replayer::imp {
|
|||
next(); skip_blank(); read_ptr(); skip_blank(); read_uint64();
|
||||
unsigned pos = static_cast<unsigned>(m_uint64);
|
||||
TRACE("z3_replayer", tout << "[" << m_line << "] " << "* " << m_ptr << " " << pos << "\n";);
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, OBJECT);
|
||||
m_heap.insert(m_ptr, m_args[pos].m_obj);
|
||||
break;
|
||||
}
|
||||
|
@ -499,8 +541,7 @@ struct z3_replayer::imp {
|
|||
// @ obj_id array_pos idx
|
||||
next(); skip_blank(); read_ptr(); skip_blank(); read_uint64();
|
||||
unsigned pos = static_cast<unsigned>(m_uint64);
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT_ARRAY)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, OBJECT_ARRAY);
|
||||
unsigned aidx = static_cast<unsigned>(m_args[pos].m_uint);
|
||||
ptr_vector<void> & v = m_obj_arrays[aidx];
|
||||
skip_blank(); read_uint64();
|
||||
|
@ -525,26 +566,22 @@ struct z3_replayer::imp {
|
|||
}
|
||||
|
||||
int get_int(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, INT64);
|
||||
return static_cast<int>(m_args[pos].m_int);
|
||||
}
|
||||
|
||||
__int64 get_int64(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, INT64);
|
||||
return m_args[pos].m_int;
|
||||
}
|
||||
|
||||
unsigned get_uint(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, UINT64);
|
||||
return static_cast<unsigned>(m_args[pos].m_uint);
|
||||
}
|
||||
|
||||
__uint64 get_uint64(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, UINT64);
|
||||
return m_args[pos].m_uint;
|
||||
}
|
||||
|
||||
|
@ -555,46 +592,45 @@ struct z3_replayer::imp {
|
|||
}
|
||||
|
||||
double get_double(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != DOUBLE)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, DOUBLE);
|
||||
return m_args[pos].m_double;
|
||||
}
|
||||
|
||||
Z3_string get_str(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != STRING)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, STRING);
|
||||
return m_args[pos].m_str;
|
||||
}
|
||||
|
||||
Z3_symbol get_symbol(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != SYMBOL)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, SYMBOL);
|
||||
return reinterpret_cast<Z3_symbol>(const_cast<char*>(m_args[pos].m_str));
|
||||
}
|
||||
|
||||
void * get_obj(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, OBJECT);
|
||||
return m_args[pos].m_obj;
|
||||
}
|
||||
|
||||
unsigned * get_uint_array(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != UINT_ARRAY)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, UINT_ARRAY);
|
||||
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
|
||||
return m_unsigned_arrays[idx].c_ptr();
|
||||
}
|
||||
|
||||
int * get_int_array(unsigned pos) const {
|
||||
check_arg(pos, INT_ARRAY);
|
||||
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
|
||||
return m_int_arrays[idx].c_ptr();
|
||||
}
|
||||
|
||||
Z3_symbol * get_symbol_array(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != SYMBOL_ARRAY)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, SYMBOL_ARRAY);
|
||||
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
|
||||
return m_sym_arrays[idx].c_ptr();
|
||||
}
|
||||
|
||||
void ** get_obj_array(unsigned pos) const {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT_ARRAY)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, OBJECT_ARRAY);
|
||||
unsigned idx = static_cast<unsigned>(m_args[pos].m_uint);
|
||||
ptr_vector<void> const & v = m_obj_arrays[idx];
|
||||
TRACE("z3_replayer_bug", tout << "pos: " << pos << ", idx: " << idx << " size(): " << v.size() << "\n";
|
||||
|
@ -603,38 +639,32 @@ struct z3_replayer::imp {
|
|||
}
|
||||
|
||||
int * get_int_addr(unsigned pos) {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, INT64);
|
||||
return reinterpret_cast<int*>(&(m_args[pos].m_int));
|
||||
}
|
||||
|
||||
__int64 * get_int64_addr(unsigned pos) {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != INT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, INT64);
|
||||
return &(m_args[pos].m_int);
|
||||
}
|
||||
|
||||
unsigned * get_uint_addr(unsigned pos) {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, UINT64);
|
||||
return reinterpret_cast<unsigned*>(&(m_args[pos].m_uint));
|
||||
}
|
||||
|
||||
__uint64 * get_uint64_addr(unsigned pos) {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != UINT64)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, UINT64);
|
||||
return &(m_args[pos].m_uint);
|
||||
}
|
||||
|
||||
Z3_string * get_str_addr(unsigned pos) {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != STRING)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, STRING);
|
||||
return &(m_args[pos].m_str);
|
||||
}
|
||||
|
||||
void ** get_obj_addr(unsigned pos) {
|
||||
if (pos >= m_args.size() || m_args[pos].m_kind != OBJECT)
|
||||
throw_invalid_reference();
|
||||
check_arg(pos, OBJECT);
|
||||
return &(m_args[pos].m_obj);
|
||||
}
|
||||
|
||||
|
@ -653,6 +683,7 @@ struct z3_replayer::imp {
|
|||
m_obj_arrays.reset();
|
||||
m_sym_arrays.reset();
|
||||
m_unsigned_arrays.reset();
|
||||
m_int_arrays.reset();
|
||||
}
|
||||
|
||||
|
||||
|
@ -715,6 +746,10 @@ unsigned * z3_replayer::get_uint_array(unsigned pos) const {
|
|||
return m_imp->get_uint_array(pos);
|
||||
}
|
||||
|
||||
int * z3_replayer::get_int_array(unsigned pos) const {
|
||||
return m_imp->get_int_array(pos);
|
||||
}
|
||||
|
||||
Z3_symbol * z3_replayer::get_symbol_array(unsigned pos) const {
|
||||
return m_imp->get_symbol_array(pos);
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ public:
|
|||
void * get_obj(unsigned pos) const;
|
||||
|
||||
unsigned * get_uint_array(unsigned pos) const;
|
||||
int * get_int_array(unsigned pos) const;
|
||||
Z3_symbol * get_symbol_array(unsigned pos) const;
|
||||
void ** get_obj_array(unsigned pos) const;
|
||||
|
||||
|
|
|
@ -417,6 +417,7 @@ inline decl_kind arith_decl_plugin::fix_kind(decl_kind k, unsigned arity) {
|
|||
|
||||
app * arith_decl_plugin::mk_numeral(rational const & val, bool is_int) {
|
||||
if (is_int && !val.is_int()) {
|
||||
SASSERT(false);
|
||||
m_manager->raise_exception("invalid rational value passed as an integer");
|
||||
}
|
||||
if (val.is_unsigned()) {
|
||||
|
|
|
@ -2078,6 +2078,8 @@ app * ast_manager::mk_app(func_decl * decl, unsigned num_args, expr * const * ar
|
|||
return r;
|
||||
}
|
||||
|
||||
|
||||
|
||||
func_decl * ast_manager::mk_fresh_func_decl(symbol const & prefix, symbol const & suffix, unsigned arity,
|
||||
sort * const * domain, sort * range) {
|
||||
func_decl_info info(null_family_id, null_decl_kind);
|
||||
|
|
|
@ -2006,6 +2006,7 @@ public:
|
|||
app * mk_false() { return m_false; }
|
||||
app * mk_interp(expr * arg) { return mk_app(m_basic_family_id, OP_INTERP, arg); }
|
||||
|
||||
|
||||
func_decl* mk_and_decl() {
|
||||
sort* domain[2] = { m_bool_sort, m_bool_sort };
|
||||
return mk_func_decl(m_basic_family_id, OP_AND, 0, 0, 2, domain);
|
||||
|
|
|
@ -77,6 +77,8 @@ bool smt2_pp_environment::is_indexed_fdecl(func_decl * f) const {
|
|||
for (i = 0; i < num; i++) {
|
||||
if (f->get_parameter(i).is_int())
|
||||
continue;
|
||||
if (f->get_parameter(i).is_rational())
|
||||
continue;
|
||||
if (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast()))
|
||||
continue;
|
||||
break;
|
||||
|
@ -105,9 +107,13 @@ format * smt2_pp_environment::pp_fdecl_params(format * fname, func_decl * f) {
|
|||
ptr_buffer<format> fs;
|
||||
fs.push_back(fname);
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
SASSERT(f->get_parameter(i).is_int() || (f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())));
|
||||
SASSERT(f->get_parameter(i).is_int() ||
|
||||
f->get_parameter(i).is_rational() ||
|
||||
(f->get_parameter(i).is_ast() && is_func_decl(f->get_parameter(i).get_ast())));
|
||||
if (f->get_parameter(i).is_int())
|
||||
fs.push_back(mk_int(get_manager(), f->get_parameter(i).get_int()));
|
||||
else if (f->get_parameter(i).is_rational())
|
||||
fs.push_back(mk_string(get_manager(), f->get_parameter(i).get_rational().to_string().c_str()));
|
||||
else
|
||||
fs.push_back(pp_fdecl_ref(to_func_decl(f->get_parameter(i).get_ast())));
|
||||
}
|
||||
|
@ -1159,6 +1165,26 @@ std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p) {
|
|||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, expr_ref const& e) {
|
||||
return out << mk_ismt2_pp(e.get(), e.get_manager());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, app_ref const& e) {
|
||||
return out << mk_ismt2_pp(e.get(), e.get_manager());
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e) {
|
||||
for (unsigned i = 0; i < e.size(); ++i)
|
||||
out << mk_ismt2_pp(e[i], e.get_manager()) << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, app_ref_vector const& e) {
|
||||
for (unsigned i = 0; i < e.size(); ++i)
|
||||
out << mk_ismt2_pp(e[i], e.get_manager()) << "\n";
|
||||
return out;
|
||||
}
|
||||
|
||||
#ifdef Z3DEBUG
|
||||
void pp(expr const * n, ast_manager & m) {
|
||||
std::cout << mk_ismt2_pp(const_cast<expr*>(n), m) << std::endl;
|
||||
|
|
|
@ -110,4 +110,10 @@ struct mk_ismt2_pp {
|
|||
|
||||
std::ostream& operator<<(std::ostream& out, mk_ismt2_pp const & p);
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, expr_ref const& e);
|
||||
std::ostream& operator<<(std::ostream& out, app_ref const& e);
|
||||
|
||||
std::ostream& operator<<(std::ostream& out, expr_ref_vector const& e);
|
||||
std::ostream& operator<<(std::ostream& out, app_ref_vector const& e);
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1058,7 +1058,8 @@ void ast_smt_pp::display_ast_smt2(std::ostream& strm, ast* a, unsigned indent, u
|
|||
|
||||
void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
||||
ptr_vector<quantifier> ql;
|
||||
decl_collector decls(m_manager);
|
||||
ast_manager& m = m_manager;
|
||||
decl_collector decls(m);
|
||||
smt_renaming rn;
|
||||
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i) {
|
||||
|
@ -1069,7 +1070,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
|||
}
|
||||
decls.visit(n);
|
||||
|
||||
if (m_manager.is_proof(n)) {
|
||||
if (m.is_proof(n)) {
|
||||
strm << "(";
|
||||
}
|
||||
if (m_benchmark_name != symbol::null) {
|
||||
|
@ -1078,7 +1079,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
|||
if (m_source_info != symbol::null && m_source_info != symbol("")) {
|
||||
strm << "; :source { " << m_source_info << " }\n";
|
||||
}
|
||||
if (m_manager.is_bool(n)) {
|
||||
if (m.is_bool(n)) {
|
||||
strm << "(set-info :status " << m_status << ")\n";
|
||||
}
|
||||
if (m_category != symbol::null && m_category != symbol("")) {
|
||||
|
@ -1095,7 +1096,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
|||
for (unsigned i = 0; i < decls.get_num_sorts(); ++i) {
|
||||
sort* s = decls.get_sorts()[i];
|
||||
if (!(*m_is_declared)(s)) {
|
||||
smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0);
|
||||
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
|
||||
p.pp_sort_decl(sort_mark, s);
|
||||
}
|
||||
}
|
||||
|
@ -1103,7 +1104,7 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
|||
for (unsigned i = 0; i < decls.get_num_decls(); ++i) {
|
||||
func_decl* d = decls.get_func_decls()[i];
|
||||
if (!(*m_is_declared)(d)) {
|
||||
smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0);
|
||||
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
|
||||
p(d);
|
||||
strm << "\n";
|
||||
}
|
||||
|
@ -1112,34 +1113,36 @@ void ast_smt_pp::display_smt2(std::ostream& strm, expr* n) {
|
|||
for (unsigned i = 0; i < decls.get_num_preds(); ++i) {
|
||||
func_decl* d = decls.get_pred_decls()[i];
|
||||
if (!(*m_is_declared)(d)) {
|
||||
smt_printer p(strm, m_manager, ql, rn, m_logic, true, true, m_simplify_implies, 0);
|
||||
smt_printer p(strm, m, ql, rn, m_logic, true, true, m_simplify_implies, 0);
|
||||
p(d);
|
||||
strm << "\n";
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_assumptions.size(); ++i) {
|
||||
strm << "(assert\n";
|
||||
smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0);
|
||||
p(m_assumptions[i].get());
|
||||
strm << ")\n";
|
||||
smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1);
|
||||
strm << "(assert\n ";
|
||||
p(m_assumptions[i].get());
|
||||
strm << ")\n";
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_assumptions_star.size(); ++i) {
|
||||
strm << "(assert\n";
|
||||
smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0);
|
||||
p(m_assumptions_star[i].get());
|
||||
smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 1);
|
||||
strm << "(assert\n ";
|
||||
p(m_assumptions_star[i].get());
|
||||
strm << ")\n";
|
||||
}
|
||||
|
||||
smt_printer p(strm, m_manager, ql, rn, m_logic, false, true, m_simplify_implies, 0);
|
||||
if (m_manager.is_bool(n)) {
|
||||
strm << "(assert\n";
|
||||
p(n);
|
||||
strm << ")\n";
|
||||
smt_printer p(strm, m, ql, rn, m_logic, false, true, m_simplify_implies, 0);
|
||||
if (m.is_bool(n)) {
|
||||
if (!m.is_true(n)) {
|
||||
strm << "(assert\n ";
|
||||
p(n);
|
||||
strm << ")\n";
|
||||
}
|
||||
strm << "(check-sat)\n";
|
||||
}
|
||||
else if (m_manager.is_proof(n)) {
|
||||
else if (m.is_proof(n)) {
|
||||
strm << "(proof\n";
|
||||
p(n);
|
||||
strm << "))\n";
|
||||
|
|
76
src/ast/ast_trail.h
Normal file
76
src/ast/ast_trail.h
Normal file
|
@ -0,0 +1,76 @@
|
|||
/*++
|
||||
Copyright (c) 2006 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
ast_trail.h
|
||||
|
||||
Abstract:
|
||||
|
||||
<abstract>
|
||||
|
||||
Author:
|
||||
|
||||
Leonardo de Moura (leonardo) 2008-06-02.
|
||||
|
||||
Revision History:
|
||||
|
||||
Extracted AST specific features from trail.h
|
||||
nbjorner 2014-9-28
|
||||
|
||||
--*/
|
||||
#ifndef _AST_TRAIL_H_
|
||||
#define _AST_TRAIL_H_
|
||||
|
||||
#include"ast.h"
|
||||
#include"trail.h"
|
||||
|
||||
|
||||
template<typename S, typename T>
|
||||
class ast2ast_trailmap {
|
||||
ref_vector<S, ast_manager> m_domain;
|
||||
ref_vector<T, ast_manager> m_range;
|
||||
obj_map<S, T*> m_map;
|
||||
public:
|
||||
ast2ast_trailmap(ast_manager& m):
|
||||
m_domain(m),
|
||||
m_range(m),
|
||||
m_map()
|
||||
{}
|
||||
|
||||
bool find(S* s, T*& t) {
|
||||
return m_map.find(s,t);
|
||||
}
|
||||
|
||||
void insert(S* s, T* t) {
|
||||
SASSERT(!m_map.contains(s));
|
||||
m_domain.push_back(s);
|
||||
m_range.push_back(t);
|
||||
m_map.insert(s,t);
|
||||
}
|
||||
|
||||
void pop() {
|
||||
SASSERT(!m_domain.empty());
|
||||
m_map.remove(m_domain.back());
|
||||
m_domain.pop_back();
|
||||
m_range.pop_back();
|
||||
}
|
||||
};
|
||||
|
||||
template<typename Ctx, typename S, typename T>
|
||||
class ast2ast_trail : public trail<Ctx> {
|
||||
ast2ast_trailmap<S,T>& m_map;
|
||||
public:
|
||||
ast2ast_trail(ast2ast_trailmap<S,T>& m, S* s, T* t) :
|
||||
m_map(m) {
|
||||
m.insert(s,t);
|
||||
}
|
||||
|
||||
virtual void undo(Ctx& ctx) {
|
||||
m_map.pop();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif /* _AST_TRAIL_H_ */
|
||||
|
|
@ -422,8 +422,55 @@ static sort * get_type(ast_manager & m, family_id datatype_fid, sort * source_da
|
|||
}
|
||||
}
|
||||
|
||||
func_decl * datatype_decl_plugin::mk_update_field(
|
||||
unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) {
|
||||
decl_kind k = OP_DT_UPDATE_FIELD;
|
||||
ast_manager& m = *m_manager;
|
||||
|
||||
if (num_parameters != 1 || !parameters[0].is_ast()) {
|
||||
m.raise_exception("invalid parameters for datatype field update");
|
||||
return 0;
|
||||
}
|
||||
if (arity != 2) {
|
||||
m.raise_exception("invalid number of arguments for datatype field update");
|
||||
return 0;
|
||||
}
|
||||
func_decl* acc = 0;
|
||||
if (is_func_decl(parameters[0].get_ast())) {
|
||||
acc = to_func_decl(parameters[0].get_ast());
|
||||
}
|
||||
if (acc && !get_util().is_accessor(acc)) {
|
||||
acc = 0;
|
||||
}
|
||||
if (!acc) {
|
||||
m.raise_exception("datatype field update requires a datatype accessor as the second argument");
|
||||
return 0;
|
||||
}
|
||||
sort* dom = acc->get_domain(0);
|
||||
sort* rng = acc->get_range();
|
||||
if (dom != domain[0]) {
|
||||
m.raise_exception("first argument to field update should be a data-type");
|
||||
return 0;
|
||||
}
|
||||
if (rng != domain[1]) {
|
||||
std::ostringstream buffer;
|
||||
buffer << "second argument to field update should be " << mk_ismt2_pp(rng, m)
|
||||
<< " instead of " << mk_ismt2_pp(domain[1], m);
|
||||
m.raise_exception(buffer.str().c_str());
|
||||
return 0;
|
||||
}
|
||||
range = domain[0];
|
||||
func_decl_info info(m_family_id, k, num_parameters, parameters);
|
||||
return m.mk_func_decl(symbol("update_field"), arity, domain, range, info);
|
||||
}
|
||||
|
||||
func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) {
|
||||
|
||||
if (k == OP_DT_UPDATE_FIELD) {
|
||||
return mk_update_field(num_parameters, parameters, arity, domain, range);
|
||||
}
|
||||
if (num_parameters < 2 || !parameters[0].is_ast() || !is_sort(parameters[0].get_ast())) {
|
||||
m_manager->raise_exception("invalid parameters for datatype operator");
|
||||
return 0;
|
||||
|
@ -521,6 +568,9 @@ func_decl * datatype_decl_plugin::mk_func_decl(decl_kind k, unsigned num_paramet
|
|||
return m_manager->mk_func_decl(a_name, arity, domain, a_type, info);
|
||||
}
|
||||
break;
|
||||
case OP_DT_UPDATE_FIELD:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
default:
|
||||
m_manager->raise_exception("invalid datatype operator kind");
|
||||
return 0;
|
||||
|
@ -672,6 +722,13 @@ bool datatype_decl_plugin::is_value(app * e) const {
|
|||
return true;
|
||||
}
|
||||
|
||||
void datatype_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
|
||||
if (logic == symbol::null) {
|
||||
op_names.push_back(builtin_name("update-field", OP_DT_UPDATE_FIELD));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
datatype_util::datatype_util(ast_manager & m):
|
||||
m_manager(m),
|
||||
m_family_id(m.mk_family_id("datatype")),
|
||||
|
|
|
@ -32,6 +32,7 @@ enum datatype_op_kind {
|
|||
OP_DT_CONSTRUCTOR,
|
||||
OP_DT_RECOGNISER,
|
||||
OP_DT_ACCESSOR,
|
||||
OP_DT_UPDATE_FIELD,
|
||||
LAST_DT_OP
|
||||
};
|
||||
|
||||
|
@ -149,8 +150,14 @@ public:
|
|||
|
||||
virtual bool is_unique_value(app * e) const { return is_value(e); }
|
||||
|
||||
virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
|
||||
|
||||
private:
|
||||
bool is_value_visit(expr * arg, ptr_buffer<app> & todo) const;
|
||||
|
||||
func_decl * mk_update_field(
|
||||
unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range);
|
||||
};
|
||||
|
||||
class datatype_util {
|
||||
|
@ -181,9 +188,11 @@ public:
|
|||
bool is_constructor(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_CONSTRUCTOR); }
|
||||
bool is_recognizer(func_decl * f) const { return is_decl_of(f, m_family_id, OP_DT_RECOGNISER); }
|
||||
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_recognizer(app * f) const { return is_app_of(f, m_family_id, OP_DT_RECOGNISER); }
|
||||
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); }
|
||||
ptr_vector<func_decl> const * get_datatype_constructors(sort * ty);
|
||||
unsigned get_datatype_num_constructors(sort * ty) {
|
||||
SASSERT(is_datatype(ty));
|
||||
|
|
|
@ -44,6 +44,10 @@ public:
|
|||
void erase(expr * k);
|
||||
void reset();
|
||||
void flush();
|
||||
void set_store_proofs(bool f) {
|
||||
if (m_store_proofs != f) flush();
|
||||
m_store_proofs = f;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
|
@ -255,9 +255,9 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref
|
|||
app * n = to_app(_n);
|
||||
quantifier * q = 0;
|
||||
func_decl * d = n->get_decl();
|
||||
TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m_manager) << "\nd:\n" << d->get_name() << "\n";);
|
||||
TRACE("macro_manager_bug", tout << "trying to expand:\n" << mk_pp(n, m) << "\nd:\n" << d->get_name() << "\n";);
|
||||
if (m_macro_manager.m_decl2macro.find(d, q)) {
|
||||
TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m_manager) << "\n";);
|
||||
TRACE("macro_manager", tout << "expanding: " << mk_pp(n, m) << "\n";);
|
||||
app * head = 0;
|
||||
expr * def = 0;
|
||||
m_macro_manager.get_head_def(q, d, head, def);
|
||||
|
@ -272,17 +272,17 @@ bool macro_manager::macro_expander::get_subst(expr * _n, expr_ref & r, proof_ref
|
|||
SASSERT(subst_args[nidx] == 0);
|
||||
subst_args[nidx] = n->get_arg(i);
|
||||
}
|
||||
var_subst s(m_manager);
|
||||
var_subst s(m);
|
||||
s(def, num, subst_args.c_ptr(), r);
|
||||
if (m_manager.proofs_enabled()) {
|
||||
expr_ref instance(m_manager);
|
||||
if (m.proofs_enabled()) {
|
||||
expr_ref instance(m);
|
||||
s(q->get_expr(), num, subst_args.c_ptr(), instance);
|
||||
proof * qi_pr = m_manager.mk_quant_inst(m_manager.mk_or(m_manager.mk_not(q), instance), num, subst_args.c_ptr());
|
||||
proof * qi_pr = m.mk_quant_inst(m.mk_or(m.mk_not(q), instance), num, subst_args.c_ptr());
|
||||
proof * q_pr = 0;
|
||||
m_macro_manager.m_decl2macro_pr.find(d, q_pr);
|
||||
SASSERT(q_pr != 0);
|
||||
proof * prs[2] = { qi_pr, q_pr };
|
||||
p = m_manager.mk_unit_resolution(2, prs);
|
||||
p = m.mk_unit_resolution(2, prs);
|
||||
}
|
||||
else {
|
||||
p = 0;
|
||||
|
|
|
@ -489,7 +489,6 @@ void macro_util::normalize_expr(app * head, expr * t, expr_ref & norm_t) const {
|
|||
tout << "#" << i << " -> " << mk_pp(var_mapping[i], m_manager) << "\n";
|
||||
});
|
||||
subst(t, var_mapping.size(), var_mapping.c_ptr(), norm_t);
|
||||
SASSERT(is_well_sorted(m_manager, norm_t));
|
||||
}
|
||||
else {
|
||||
norm_t = t;
|
||||
|
|
|
@ -137,7 +137,6 @@ class skolemizer {
|
|||
}
|
||||
}
|
||||
s(body, substitution.size(), substitution.c_ptr(), r);
|
||||
SASSERT(is_well_sorted(m(), r));
|
||||
p = 0;
|
||||
if (m().proofs_enabled()) {
|
||||
if (q->is_forall())
|
||||
|
|
|
@ -116,7 +116,7 @@ void pattern_inference::collect::operator()(expr * n, unsigned num_bindings) {
|
|||
n = e.m_node;
|
||||
unsigned delta = e.m_delta;
|
||||
TRACE("collect", tout << "processing: " << n->get_id() << " " << delta << " kind: " << n->get_kind() << "\n";);
|
||||
TRACE("collect_info", tout << mk_pp(n, m_manager) << "\n";);
|
||||
TRACE("collect_info", tout << mk_pp(n, m) << "\n";);
|
||||
if (visit_children(n, delta)) {
|
||||
m_todo.pop_back();
|
||||
save_candidate(n, delta);
|
||||
|
@ -170,9 +170,9 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
|
|||
free_vars.insert(idx);
|
||||
info * i = 0;
|
||||
if (delta == 0)
|
||||
i = alloc(info, m_manager, n, free_vars, 1);
|
||||
i = alloc(info, m, n, free_vars, 1);
|
||||
else
|
||||
i = alloc(info, m_manager, m_manager.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
|
||||
i = alloc(info, m, m.mk_var(idx, to_var(n)->get_sort()), free_vars, 1);
|
||||
save(n, delta, i);
|
||||
}
|
||||
else {
|
||||
|
@ -189,7 +189,7 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
|
|||
}
|
||||
|
||||
if (c->get_num_args() == 0) {
|
||||
save(n, delta, alloc(info, m_manager, n, uint_set(), 1));
|
||||
save(n, delta, alloc(info, m, n, uint_set(), 1));
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -219,10 +219,10 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
|
|||
|
||||
app * new_node = 0;
|
||||
if (changed)
|
||||
new_node = m_manager.mk_app(decl, buffer.size(), buffer.c_ptr());
|
||||
new_node = m.mk_app(decl, buffer.size(), buffer.c_ptr());
|
||||
else
|
||||
new_node = to_app(n);
|
||||
save(n, delta, alloc(info, m_manager, new_node, free_vars, size));
|
||||
save(n, delta, alloc(info, m, new_node, free_vars, size));
|
||||
// Remark: arithmetic patterns are only used if they are nested inside other terms.
|
||||
// That is, we never consider x + 1 as pattern. On the other hand, f(x+1) can be a pattern
|
||||
// if arithmetic is not in the forbidden list.
|
||||
|
@ -235,7 +235,7 @@ void pattern_inference::collect::save_candidate(expr * n, unsigned delta) {
|
|||
decl_kind k = c->get_decl_kind();
|
||||
if (!free_vars.empty() &&
|
||||
(fid != m_afid || (fid == m_afid && !m_owner.m_nested_arith_only && (k == OP_DIV || k == OP_IDIV || k == OP_MOD || k == OP_REM || k == OP_MUL)))) {
|
||||
TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m_manager) << "\n";);
|
||||
TRACE("pattern_inference", tout << "potential candidate: \n" << mk_pp(new_node, m) << "\n";);
|
||||
m_owner.add_candidate(new_node, free_vars, size);
|
||||
}
|
||||
return;
|
||||
|
@ -338,7 +338,7 @@ bool pattern_inference::contains_subpattern::operator()(expr * n) {
|
|||
uint_set const & s2 = e->get_data().m_value.m_free_vars;
|
||||
SASSERT(s2.subset_of(s1));
|
||||
if (s1 == s2) {
|
||||
TRACE("pattern_inference", tout << mk_pp(n, m_owner.m_manager) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m_manager) << "\n";);
|
||||
TRACE("pattern_inference", tout << mk_pp(n, m_owner.m) << "\nis bigger than\n" << mk_pp(to_app(curr), m_owner.m) << "\n";);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -411,7 +411,7 @@ void pattern_inference::candidates2unary_patterns(ptr_vector<app> const & candid
|
|||
expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
|
||||
info const & i = e->get_data().m_value;
|
||||
if (i.m_free_vars.num_elems() == m_num_bindings) {
|
||||
app * new_pattern = m_manager.mk_pattern(candidate);
|
||||
app * new_pattern = m.mk_pattern(candidate);
|
||||
result.push_back(new_pattern);
|
||||
}
|
||||
else {
|
||||
|
@ -435,7 +435,7 @@ void pattern_inference::candidates2multi_patterns(unsigned max_num_patterns,
|
|||
for (unsigned j = 0; j < m_pre_patterns.size(); j++) {
|
||||
pre_pattern * curr = m_pre_patterns[j];
|
||||
if (curr->m_free_vars.num_elems() == m_num_bindings) {
|
||||
app * new_pattern = m_manager.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
|
||||
app * new_pattern = m.mk_pattern(curr->m_exprs.size(), curr->m_exprs.c_ptr());
|
||||
result.push_back(new_pattern);
|
||||
if (result.size() >= max_num_patterns)
|
||||
return;
|
||||
|
@ -489,7 +489,7 @@ bool pattern_inference::is_forbidden(app * n) const {
|
|||
// occur outside of the quantifier. That is, Z3 will never match this kind of
|
||||
// pattern.
|
||||
if (m_params.m_pi_avoid_skolems && decl->is_skolem()) {
|
||||
CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m_manager) << "\n";);
|
||||
CTRACE("pattern_inference_skolem", decl->is_skolem(), tout << "ignoring: " << mk_pp(n, m) << "\n";);
|
||||
return true;
|
||||
}
|
||||
if (is_forbidden(decl))
|
||||
|
@ -509,8 +509,8 @@ bool pattern_inference::has_preferred_patterns(ptr_vector<app> & candidate_patte
|
|||
expr2info::obj_map_entry * e = m_candidates_info.find_core(candidate);
|
||||
info const & i = e->get_data().m_value;
|
||||
if (i.m_free_vars.num_elems() == m_num_bindings) {
|
||||
TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m_manager) << "\n";);
|
||||
app * p = m_manager.mk_pattern(candidate);
|
||||
TRACE("pattern_inference", tout << "found preferred pattern:\n" << mk_pp(candidate, m) << "\n";);
|
||||
app * p = m.mk_pattern(candidate);
|
||||
result.push_back(p);
|
||||
found = true;
|
||||
}
|
||||
|
@ -531,11 +531,11 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
|
|||
m_collect(n, num_bindings);
|
||||
|
||||
TRACE("pattern_inference",
|
||||
tout << mk_pp(n, m_manager);
|
||||
tout << mk_pp(n, m);
|
||||
tout << "\ncandidates:\n";
|
||||
unsigned num = m_candidates.size();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
tout << mk_pp(m_candidates.get(i), m_manager) << "\n";
|
||||
tout << mk_pp(m_candidates.get(i), m) << "\n";
|
||||
});
|
||||
|
||||
if (!m_candidates.empty()) {
|
||||
|
@ -543,7 +543,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
|
|||
filter_looping_patterns(m_tmp1);
|
||||
TRACE("pattern_inference",
|
||||
tout << "candidates after removing looping-patterns:\n";
|
||||
dump_app_vector(tout, m_tmp1, m_manager););
|
||||
dump_app_vector(tout, m_tmp1, m););
|
||||
SASSERT(!m_tmp1.empty());
|
||||
if (!has_preferred_patterns(m_tmp1, result)) {
|
||||
// continue if there are no preferred patterns
|
||||
|
@ -552,7 +552,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
|
|||
SASSERT(!m_tmp2.empty());
|
||||
TRACE("pattern_inference",
|
||||
tout << "candidates after removing bigger patterns:\n";
|
||||
dump_app_vector(tout, m_tmp2, m_manager););
|
||||
dump_app_vector(tout, m_tmp2, m););
|
||||
m_tmp1.reset();
|
||||
candidates2unary_patterns(m_tmp2, m_tmp1, result);
|
||||
unsigned num_extra_multi_patterns = m_params.m_pi_max_multi_patterns;
|
||||
|
@ -563,7 +563,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
|
|||
std::stable_sort(m_tmp1.begin(), m_tmp1.end(), m_pattern_weight_lt);
|
||||
TRACE("pattern_inference",
|
||||
tout << "candidates after sorting:\n";
|
||||
dump_app_vector(tout, m_tmp1, m_manager););
|
||||
dump_app_vector(tout, m_tmp1, m););
|
||||
candidates2multi_patterns(num_extra_multi_patterns, m_tmp1, result);
|
||||
}
|
||||
}
|
||||
|
@ -577,7 +577,7 @@ void pattern_inference::mk_patterns(unsigned num_bindings,
|
|||
#include"database.h" // defines g_pattern_database
|
||||
|
||||
void pattern_inference::reduce1_quantifier(quantifier * q) {
|
||||
TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m_manager) << "\n";);
|
||||
TRACE("pattern_inference", tout << "processing:\n" << mk_pp(q, m) << "\n";);
|
||||
if (!q->is_forall()) {
|
||||
simplifier::reduce1_quantifier(q);
|
||||
return;
|
||||
|
@ -587,27 +587,27 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
|
|||
|
||||
if (m_params.m_pi_use_database) {
|
||||
m_database.initialize(g_pattern_database);
|
||||
app_ref_vector new_patterns(m_manager);
|
||||
app_ref_vector new_patterns(m);
|
||||
unsigned new_weight;
|
||||
if (m_database.match_quantifier(q, new_patterns, new_weight)) {
|
||||
#ifdef Z3DEBUG
|
||||
for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m_manager, new_patterns.get(i))); }
|
||||
for (unsigned i = 0; i < new_patterns.size(); i++) { SASSERT(is_well_sorted(m, new_patterns.get(i))); }
|
||||
#endif
|
||||
quantifier_ref new_q(m_manager);
|
||||
quantifier_ref new_q(m);
|
||||
if (q->get_num_patterns() > 0) {
|
||||
// just update the weight...
|
||||
TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m_manager) << "\n";);
|
||||
new_q = m_manager.update_quantifier_weight(q, new_weight);
|
||||
TRACE("pattern_inference", tout << "updating weight to: " << new_weight << "\n" << mk_pp(q, m) << "\n";);
|
||||
new_q = m.update_quantifier_weight(q, new_weight);
|
||||
}
|
||||
else {
|
||||
quantifier_ref tmp(m_manager);
|
||||
tmp = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
|
||||
new_q = m_manager.update_quantifier_weight(tmp, new_weight);
|
||||
TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m_manager) << "\n";);
|
||||
quantifier_ref tmp(m);
|
||||
tmp = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), q->get_expr());
|
||||
new_q = m.update_quantifier_weight(tmp, new_weight);
|
||||
TRACE("pattern_inference", tout << "found patterns in database, weight: " << new_weight << "\n" << mk_pp(new_q, m) << "\n";);
|
||||
}
|
||||
proof * pr = 0;
|
||||
if (m_manager.fine_grain_proofs())
|
||||
pr = m_manager.mk_rewrite(q, new_q);
|
||||
if (m.fine_grain_proofs())
|
||||
pr = m.mk_rewrite(q, new_q);
|
||||
cache_result(q, new_q, pr);
|
||||
return;
|
||||
}
|
||||
|
@ -635,7 +635,7 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
|
|||
new_no_patterns.push_back(new_pattern);
|
||||
}
|
||||
|
||||
app_ref_buffer new_patterns(m_manager);
|
||||
app_ref_buffer new_patterns(m);
|
||||
|
||||
if (m_params.m_pi_arith == AP_CONSERVATIVE)
|
||||
m_forbidden.push_back(m_afid);
|
||||
|
@ -677,26 +677,26 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
|
|||
warning_msg("using non nested arith. pattern (quantifier id: %s), the weight was increased to %d (this value can be modified using PI_NON_NESTED_ARITH_WEIGHT=<val>).",
|
||||
q->get_qid().str().c_str(), weight);
|
||||
}
|
||||
// verbose_stream() << mk_pp(q, m_manager) << "\n";
|
||||
// verbose_stream() << mk_pp(q, m) << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
quantifier_ref new_q(m_manager);
|
||||
new_q = m_manager.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body);
|
||||
quantifier_ref new_q(m);
|
||||
new_q = m.update_quantifier(q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_body);
|
||||
if (weight != q->get_weight())
|
||||
new_q = m_manager.update_quantifier_weight(new_q, weight);
|
||||
proof_ref pr(m_manager);
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
new_q = m.update_quantifier_weight(new_q, weight);
|
||||
proof_ref pr(m);
|
||||
if (m.fine_grain_proofs()) {
|
||||
if (new_body_pr == 0)
|
||||
new_body_pr = m_manager.mk_reflexivity(new_body);
|
||||
pr = m_manager.mk_quant_intro(q, new_q, new_body_pr);
|
||||
new_body_pr = m.mk_reflexivity(new_body);
|
||||
pr = m.mk_quant_intro(q, new_q, new_body_pr);
|
||||
}
|
||||
|
||||
if (new_patterns.empty() && m_params.m_pi_pull_quantifiers) {
|
||||
pull_quant pull(m_manager);
|
||||
expr_ref new_expr(m_manager);
|
||||
proof_ref new_pr(m_manager);
|
||||
pull_quant pull(m);
|
||||
expr_ref new_expr(m);
|
||||
proof_ref new_pr(m);
|
||||
pull(new_q, new_expr, new_pr);
|
||||
quantifier * new_new_q = to_quantifier(new_expr);
|
||||
if (new_new_q != new_q) {
|
||||
|
@ -705,12 +705,12 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
|
|||
if (m_params.m_pi_warnings) {
|
||||
warning_msg("pulled nested quantifier to be able to find an useable pattern (quantifier id: %s)", q->get_qid().str().c_str());
|
||||
}
|
||||
new_q = m_manager.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr());
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
pr = m_manager.mk_transitivity(pr, new_pr);
|
||||
pr = m_manager.mk_transitivity(pr, m_manager.mk_quant_intro(new_new_q, new_q, m_manager.mk_reflexivity(new_q->get_expr())));
|
||||
new_q = m.update_quantifier(new_new_q, new_patterns.size(), (expr**) new_patterns.c_ptr(), new_new_q->get_expr());
|
||||
if (m.fine_grain_proofs()) {
|
||||
pr = m.mk_transitivity(pr, new_pr);
|
||||
pr = m.mk_transitivity(pr, m.mk_quant_intro(new_new_q, new_q, m.mk_reflexivity(new_q->get_expr())));
|
||||
}
|
||||
TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m_manager) << "\n";);
|
||||
TRACE("pattern_inference", tout << "pulled quantifier:\n" << mk_pp(new_q, m) << "\n";);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -719,7 +719,7 @@ void pattern_inference::reduce1_quantifier(quantifier * q) {
|
|||
if (m_params.m_pi_warnings) {
|
||||
warning_msg("failed to find a pattern for quantifier (quantifier id: %s)", q->get_qid().str().c_str());
|
||||
}
|
||||
TRACE("pi_failed", tout << mk_pp(q, m_manager) << "\n";);
|
||||
TRACE("pi_failed", tout << mk_pp(q, m) << "\n";);
|
||||
}
|
||||
|
||||
if (new_patterns.empty() && new_body == q->get_expr()) {
|
||||
|
|
|
@ -38,7 +38,7 @@ Revision History:
|
|||
every instance of f(g(X)) is also an instance of f(X).
|
||||
*/
|
||||
class smaller_pattern {
|
||||
ast_manager & m_manager;
|
||||
ast_manager & m;
|
||||
ptr_vector<expr> m_bindings;
|
||||
|
||||
typedef std::pair<expr *, expr *> expr_pair;
|
||||
|
@ -54,7 +54,7 @@ class smaller_pattern {
|
|||
public:
|
||||
|
||||
smaller_pattern(ast_manager & m):
|
||||
m_manager(m) {
|
||||
m(m) {
|
||||
}
|
||||
|
||||
bool operator()(unsigned num_bindings, expr * p1, expr * p2);
|
||||
|
@ -135,7 +135,7 @@ class pattern_inference : public simplifier {
|
|||
m_node(n, m), m_free_vars(vars), m_size(sz) {}
|
||||
};
|
||||
|
||||
ast_manager & m_manager;
|
||||
ast_manager & m;
|
||||
pattern_inference & m_owner;
|
||||
family_id m_afid;
|
||||
unsigned m_num_bindings;
|
||||
|
@ -150,7 +150,7 @@ class pattern_inference : public simplifier {
|
|||
void save_candidate(expr * n, unsigned delta);
|
||||
void reset();
|
||||
public:
|
||||
collect(ast_manager & m, pattern_inference & o):m_manager(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
|
||||
collect(ast_manager & m, pattern_inference & o):m(m), m_owner(o), m_afid(m.mk_family_id("arith")) {}
|
||||
void operator()(expr * n, unsigned num_bindings);
|
||||
};
|
||||
|
||||
|
|
282
src/ast/pb_decl_plugin.cpp
Normal file
282
src/ast/pb_decl_plugin.cpp
Normal file
|
@ -0,0 +1,282 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb_decl_plugin.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Cardinality Constraints plugin
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-05-11
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#include "pb_decl_plugin.h"
|
||||
|
||||
pb_decl_plugin::pb_decl_plugin():
|
||||
m_at_most_sym("at-most"),
|
||||
m_at_least_sym("at-least"),
|
||||
m_pble_sym("pble"),
|
||||
m_pbge_sym("pbge"),
|
||||
m_pbeq_sym("pbeq")
|
||||
{}
|
||||
|
||||
func_decl * pb_decl_plugin::mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range) {
|
||||
SASSERT(m_manager);
|
||||
ast_manager& m = *m_manager;
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
if (!m.is_bool(domain[i])) {
|
||||
m.raise_exception("invalid non-Boolean sort applied to 'at-most'");
|
||||
}
|
||||
}
|
||||
symbol sym;
|
||||
switch(k) {
|
||||
case OP_AT_LEAST_K: sym = m_at_least_sym; break;
|
||||
case OP_AT_MOST_K: sym = m_at_most_sym; break;
|
||||
case OP_PB_LE: sym = m_pble_sym; break;
|
||||
case OP_PB_GE: sym = m_pbge_sym; break;
|
||||
case OP_PB_EQ: sym = m_pbeq_sym; break;
|
||||
default: break;
|
||||
}
|
||||
switch(k) {
|
||||
case OP_AT_LEAST_K:
|
||||
case OP_AT_MOST_K: {
|
||||
if (num_parameters != 1 || !parameters[0].is_int() || parameters[0].get_int() < 0) {
|
||||
m.raise_exception("function expects one non-negative integer parameter");
|
||||
}
|
||||
func_decl_info info(m_family_id, k, 1, parameters);
|
||||
return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info);
|
||||
}
|
||||
case OP_PB_GE:
|
||||
case OP_PB_LE:
|
||||
case OP_PB_EQ: {
|
||||
if (num_parameters != 1 + arity) {
|
||||
m.raise_exception("function expects arity+1 rational parameters");
|
||||
}
|
||||
vector<parameter> params;
|
||||
for (unsigned i = 0; i < num_parameters; ++i) {
|
||||
parameter const& p = parameters[i];
|
||||
if (p.is_int()) {
|
||||
params.push_back(p);
|
||||
}
|
||||
else if (p.is_rational()) {
|
||||
// HACK: ast pretty printer does not work with rationals.
|
||||
rational r = p.get_rational();
|
||||
if (r.is_int32()) {
|
||||
params.push_back(parameter(r.get_int32()));
|
||||
}
|
||||
else {
|
||||
params.push_back(p);
|
||||
}
|
||||
}
|
||||
else {
|
||||
m.raise_exception("functions 'pble/pbge/pbeq' expect arity+1 integer parameters");
|
||||
}
|
||||
}
|
||||
func_decl_info info(m_family_id, k, num_parameters, params.c_ptr());
|
||||
return m.mk_func_decl(sym, arity, domain, m.mk_bool_sort(), info);
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void pb_decl_plugin::get_op_names(svector<builtin_name> & op_names, symbol const & logic) {
|
||||
if (logic == symbol::null) {
|
||||
op_names.push_back(builtin_name(m_at_most_sym.bare_str(), OP_AT_MOST_K));
|
||||
op_names.push_back(builtin_name(m_at_least_sym.bare_str(), OP_AT_LEAST_K));
|
||||
op_names.push_back(builtin_name(m_pble_sym.bare_str(), OP_PB_LE));
|
||||
op_names.push_back(builtin_name(m_pbge_sym.bare_str(), OP_PB_GE));
|
||||
op_names.push_back(builtin_name(m_pbeq_sym.bare_str(), OP_PB_EQ));
|
||||
}
|
||||
}
|
||||
|
||||
app * pb_util::mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) {
|
||||
vector<parameter> params;
|
||||
params.push_back(parameter(k));
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
params.push_back(parameter(coeffs[i]));
|
||||
}
|
||||
return m.mk_app(m_fid, OP_PB_LE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort());
|
||||
}
|
||||
|
||||
app * pb_util::mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) {
|
||||
vector<parameter> params;
|
||||
params.push_back(parameter(k));
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
params.push_back(parameter(coeffs[i]));
|
||||
}
|
||||
return m.mk_app(m_fid, OP_PB_GE, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort());
|
||||
}
|
||||
|
||||
app * pb_util::mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k) {
|
||||
vector<parameter> params;
|
||||
params.push_back(parameter(k));
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
params.push_back(parameter(coeffs[i]));
|
||||
}
|
||||
return m.mk_app(m_fid, OP_PB_EQ, params.size(), params.c_ptr(), num_args, args, m.mk_bool_sort());
|
||||
}
|
||||
|
||||
// ax + by < k
|
||||
// <=>
|
||||
// -ax - by >= -k + 1
|
||||
// <=>
|
||||
// a(1-x) + b(1-y) >= -k + a + b + 1
|
||||
app * pb_util::mk_lt(unsigned num_args, rational const * _coeffs, expr * const * _args, rational const& _k) {
|
||||
vector<rational> coeffs;
|
||||
rational k(_k);
|
||||
expr_ref_vector args(m);
|
||||
expr* f;
|
||||
rational d(denominator(k));
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
coeffs.push_back(_coeffs[i]);
|
||||
d = lcm(d, denominator(coeffs[i]));
|
||||
if (m.is_not(_args[i], f)) {
|
||||
args.push_back(f);
|
||||
}
|
||||
else {
|
||||
args.push_back(m.mk_not(_args[i]));
|
||||
}
|
||||
}
|
||||
if (!d.is_one()) {
|
||||
k *= d;
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
coeffs[i] *= d;
|
||||
}
|
||||
}
|
||||
k.neg();
|
||||
k += rational::one();
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
k += coeffs[i];
|
||||
}
|
||||
return mk_ge(num_args, coeffs.c_ptr(), args.c_ptr(), k);
|
||||
}
|
||||
|
||||
|
||||
app * pb_util::mk_at_most_k(unsigned num_args, expr * const * args, unsigned k) {
|
||||
parameter param(k);
|
||||
return m.mk_app(m_fid, OP_AT_MOST_K, 1, ¶m, num_args, args, m.mk_bool_sort());
|
||||
}
|
||||
|
||||
bool pb_util::is_at_most_k(func_decl *a) const {
|
||||
return is_decl_of(a, m_fid, OP_AT_MOST_K);
|
||||
}
|
||||
|
||||
bool pb_util::is_at_most_k(expr *a, rational& k) const {
|
||||
if (is_at_most_k(a)) {
|
||||
k = get_k(a);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
app * pb_util::mk_at_least_k(unsigned num_args, expr * const * args, unsigned k) {
|
||||
parameter param(k);
|
||||
return m.mk_app(m_fid, OP_AT_LEAST_K, 1, ¶m, num_args, args, m.mk_bool_sort());
|
||||
}
|
||||
|
||||
bool pb_util::is_at_least_k(func_decl *a) const {
|
||||
return is_decl_of(a, m_fid, OP_AT_LEAST_K);
|
||||
}
|
||||
|
||||
bool pb_util::is_at_least_k(expr *a, rational& k) const {
|
||||
if (is_at_least_k(a)) {
|
||||
k = get_k(a);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rational pb_util::get_k(func_decl *a) const {
|
||||
parameter const& p = a->get_parameter(0);
|
||||
if (is_at_most_k(a) || is_at_least_k(a)) {
|
||||
return to_rational(p);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_le(a) || is_ge(a) || is_eq(a));
|
||||
return to_rational(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool pb_util::is_le(func_decl *a) const {
|
||||
return is_decl_of(a, m_fid, OP_PB_LE);
|
||||
}
|
||||
|
||||
bool pb_util::is_le(expr* a, rational& k) const {
|
||||
if (is_le(a)) {
|
||||
k = get_k(a);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool pb_util::is_ge(func_decl *a) const {
|
||||
return is_decl_of(a, m_fid, OP_PB_GE);
|
||||
}
|
||||
|
||||
bool pb_util::is_ge(expr* a, rational& k) const {
|
||||
if (is_ge(a)) {
|
||||
k = get_k(a);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
bool pb_util::is_eq(func_decl *a) const {
|
||||
return is_decl_of(a, m_fid, OP_PB_EQ);
|
||||
}
|
||||
|
||||
bool pb_util::is_eq(expr* a, rational& k) const {
|
||||
if (is_eq(a)) {
|
||||
k = get_k(a);
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
rational pb_util::get_coeff(func_decl* a, unsigned index) const {
|
||||
if (is_at_most_k(a) || is_at_least_k(a)) {
|
||||
return rational::one();
|
||||
}
|
||||
SASSERT(is_le(a) || is_ge(a) || is_eq(a));
|
||||
SASSERT(1 + index < a->get_num_parameters());
|
||||
return to_rational(a->get_parameter(index + 1));
|
||||
}
|
||||
|
||||
rational pb_util::to_rational(parameter const& p) const {
|
||||
if (p.is_int()) {
|
||||
return rational(p.get_int());
|
||||
}
|
||||
SASSERT(p.is_rational());
|
||||
return p.get_rational();
|
||||
}
|
||||
|
||||
bool pb_util::has_unit_coefficients(func_decl* f) const {
|
||||
if (is_at_most_k(f) || is_at_least_k(f)) return true;
|
||||
unsigned sz = f->get_arity();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (!get_coeff(f, i).is_one()) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
123
src/ast/pb_decl_plugin.h
Normal file
123
src/ast/pb_decl_plugin.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb_decl_plugin.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Pseudo-Boolean and Cardinality Constraints plugin
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-05-11
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
(at-most-k x1 .... x_n) means x1 + ... + x_n <= k
|
||||
|
||||
hence:
|
||||
|
||||
(not (at-most-k x1 .... x_n)) means x1 + ... + x_n >= k + 1
|
||||
|
||||
|
||||
--*/
|
||||
#ifndef _PB_DECL_PLUGIN_H_
|
||||
#define _PB_DECL_PLUGIN_H_
|
||||
|
||||
#include"ast.h"
|
||||
|
||||
enum pb_op_kind {
|
||||
OP_AT_MOST_K, // at most K Booleans are true.
|
||||
OP_AT_LEAST_K, // at least K Booleans are true.
|
||||
OP_PB_LE, // pseudo-Boolean <= (generalizes at_most_k)
|
||||
OP_PB_GE, // pseudo-Boolean >=
|
||||
OP_PB_EQ, // equality
|
||||
LAST_PB_OP
|
||||
};
|
||||
|
||||
|
||||
class pb_decl_plugin : public decl_plugin {
|
||||
symbol m_at_most_sym;
|
||||
symbol m_at_least_sym;
|
||||
symbol m_pble_sym;
|
||||
symbol m_pbge_sym;
|
||||
symbol m_pbeq_sym;
|
||||
func_decl * mk_at_most(unsigned arity, unsigned k);
|
||||
func_decl * mk_at_least(unsigned arity, unsigned k);
|
||||
func_decl * mk_le(unsigned arity, rational const* coeffs, int k);
|
||||
func_decl * mk_ge(unsigned arity, rational const* coeffs, int k);
|
||||
func_decl * mk_eq(unsigned arity, rational const* coeffs, int k);
|
||||
public:
|
||||
pb_decl_plugin();
|
||||
virtual ~pb_decl_plugin() {}
|
||||
|
||||
virtual sort * mk_sort(decl_kind k, unsigned num_parameters, parameter const * parameters) {
|
||||
UNREACHABLE();
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual decl_plugin * mk_fresh() {
|
||||
return alloc(pb_decl_plugin);
|
||||
}
|
||||
|
||||
//
|
||||
// Contract for func_decl:
|
||||
// parameters[0] - integer (at most k elements)
|
||||
// all sorts are Booleans
|
||||
// parameters[1] .. parameters[arity] - coefficients
|
||||
virtual func_decl * mk_func_decl(decl_kind k, unsigned num_parameters, parameter const * parameters,
|
||||
unsigned arity, sort * const * domain, sort * range);
|
||||
virtual void get_op_names(svector<builtin_name> & op_names, symbol const & logic);
|
||||
};
|
||||
|
||||
|
||||
class pb_util {
|
||||
ast_manager & m;
|
||||
family_id m_fid;
|
||||
public:
|
||||
pb_util(ast_manager& m):m(m), m_fid(m.mk_family_id("pb")) {}
|
||||
ast_manager & get_manager() const { return m; }
|
||||
family_id get_family_id() const { return m_fid; }
|
||||
app * mk_at_most_k(unsigned num_args, expr * const * args, unsigned k);
|
||||
app * mk_at_least_k(unsigned num_args, expr * const * args, unsigned k);
|
||||
app * mk_le(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
|
||||
app * mk_ge(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
|
||||
app * mk_eq(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
|
||||
app * mk_lt(unsigned num_args, rational const * coeffs, expr * const * args, rational const& k);
|
||||
bool is_at_most_k(func_decl *a) const;
|
||||
bool is_at_most_k(expr *a) const { return is_app(a) && is_at_most_k(to_app(a)->get_decl()); }
|
||||
bool is_at_most_k(expr *a, rational& k) const;
|
||||
bool is_at_least_k(func_decl *a) const;
|
||||
bool is_at_least_k(expr *a) const { return is_app(a) && is_at_least_k(to_app(a)->get_decl()); }
|
||||
bool is_at_least_k(expr *a, rational& k) const;
|
||||
rational get_k(func_decl *a) const;
|
||||
rational get_k(expr *a) const { return get_k(to_app(a)->get_decl()); }
|
||||
bool is_le(func_decl *a) const;
|
||||
bool is_le(expr *a) const { return is_app(a) && is_le(to_app(a)->get_decl()); }
|
||||
bool is_le(expr* a, rational& k) const;
|
||||
bool is_ge(func_decl* a) const;
|
||||
bool is_ge(expr* a) const { return is_app(a) && is_ge(to_app(a)->get_decl()); }
|
||||
bool is_ge(expr* a, rational& k) const;
|
||||
rational get_coeff(expr* a, unsigned index) const { return get_coeff(to_app(a)->get_decl(), index); }
|
||||
rational get_coeff(func_decl* a, unsigned index) const;
|
||||
bool has_unit_coefficients(func_decl* f) const;
|
||||
bool has_unit_coefficients(expr* f) const { return is_app(f) && has_unit_coefficients(to_app(f)->get_decl()); }
|
||||
|
||||
|
||||
bool is_eq(func_decl* f) const;
|
||||
bool is_eq(expr* e) const { return is_app(e) && is_eq(to_app(e)->get_decl()); }
|
||||
bool is_eq(expr* e, rational& k) const;
|
||||
|
||||
|
||||
private:
|
||||
rational to_rational(parameter const& p) const;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
#endif /* _PB_DECL_PLUGIN_H_ */
|
||||
|
|
@ -10,7 +10,7 @@ def_module_params('pp',
|
|||
('decimal', BOOL, False, 'pretty print real numbers using decimal notation (the output may be truncated). Z3 adds a ? if the value is not precise'),
|
||||
('decimal_precision', UINT, 10, 'maximum number of decimal places to be used when pp.decimal=true'),
|
||||
('bv_literals', BOOL, True, 'use Bit-Vector literals (e.g, #x0F and #b0101) during pretty printing'),
|
||||
('fp_real_literals', BOOL, False, 'use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing'),
|
||||
('fp_real_literals', BOOL, False, 'use real-numbered floating point literals (e.g, +1.0p-1) during pretty printing'),
|
||||
('bv_neg', BOOL, False, 'use bvneg when displaying Bit-Vector literals where the most significant bit is 1'),
|
||||
('flat_assoc', BOOL, True, 'flat associative operators (when pretty printing SMT2 terms/formulas)'),
|
||||
('fixed_indent', BOOL, False, 'use a fixed indentation for applications'),
|
||||
|
|
|
@ -24,6 +24,7 @@ Revision History:
|
|||
#include"datatype_decl_plugin.h"
|
||||
#include"dl_decl_plugin.h"
|
||||
#include"seq_decl_plugin.h"
|
||||
#include"pb_decl_plugin.h"
|
||||
#include"fpa_decl_plugin.h"
|
||||
|
||||
void reg_decl_plugins(ast_manager & m) {
|
||||
|
@ -48,4 +49,7 @@ void reg_decl_plugins(ast_manager & m) {
|
|||
if (!m.get_plugin(m.mk_family_id(symbol("fpa")))) {
|
||||
m.register_plugin(symbol("fpa"), alloc(fpa_decl_plugin));
|
||||
}
|
||||
if (!m.get_plugin(m.mk_family_id(symbol("pb")))) {
|
||||
m.register_plugin(symbol("pb"), alloc(pb_decl_plugin));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,9 @@ Revision History:
|
|||
--*/
|
||||
|
||||
#include "ast_counter.h"
|
||||
#include "var_subst.h"
|
||||
|
||||
void counter::update(unsigned el, int delta) {
|
||||
int & counter = get(el);
|
||||
SASSERT(!m_stay_non_negative || counter>=0);
|
||||
SASSERT(!m_stay_non_negative || static_cast<int>(counter)>=-delta);
|
||||
counter += delta;
|
||||
}
|
||||
|
||||
|
@ -92,16 +89,14 @@ int counter::get_max_counter_value() const {
|
|||
void var_counter::count_vars(ast_manager & m, const app * pred, int coef) {
|
||||
unsigned n = pred->get_num_args();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
m_sorts.reset();
|
||||
m_todo.reset();
|
||||
m_mark.reset();
|
||||
::get_free_vars(m_mark, m_todo, pred->get_arg(i), m_sorts);
|
||||
for (unsigned j = 0; j < m_sorts.size(); ++j) {
|
||||
if (m_sorts[j]) {
|
||||
m_fv(pred->get_arg(i));
|
||||
for (unsigned j = 0; j < m_fv.size(); ++j) {
|
||||
if (m_fv[j]) {
|
||||
update(j, coef);
|
||||
}
|
||||
}
|
||||
}
|
||||
m_fv.reset();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -27,16 +27,16 @@ Revision History:
|
|||
#include "ast.h"
|
||||
#include "map.h"
|
||||
#include "uint_set.h"
|
||||
#include "var_subst.h"
|
||||
|
||||
class counter {
|
||||
protected:
|
||||
typedef u_map<int> map_impl;
|
||||
map_impl m_data;
|
||||
const bool m_stay_non_negative;
|
||||
public:
|
||||
typedef map_impl::iterator iterator;
|
||||
|
||||
counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {}
|
||||
counter() {}
|
||||
|
||||
void reset() { m_data.reset(); }
|
||||
iterator begin() const { return m_data.begin(); }
|
||||
|
@ -69,14 +69,13 @@ public:
|
|||
|
||||
class var_counter : public counter {
|
||||
protected:
|
||||
ptr_vector<sort> m_sorts;
|
||||
expr_fast_mark1 m_visited;
|
||||
expr_free_vars m_fv;
|
||||
ptr_vector<expr> m_todo;
|
||||
ast_mark m_mark;
|
||||
unsigned_vector m_scopes;
|
||||
unsigned get_max_var(bool & has_var);
|
||||
public:
|
||||
var_counter(bool stay_non_negative = true): counter(stay_non_negative) {}
|
||||
var_counter() {}
|
||||
void count_vars(ast_manager & m, const app * t, int coef = 1);
|
||||
unsigned get_max_var(expr* e);
|
||||
unsigned get_next_var(expr* e);
|
||||
|
@ -85,11 +84,10 @@ public:
|
|||
class ast_counter {
|
||||
typedef obj_map<ast, int> map_impl;
|
||||
map_impl m_data;
|
||||
bool m_stay_non_negative;
|
||||
public:
|
||||
typedef map_impl::iterator iterator;
|
||||
|
||||
ast_counter(bool stay_non_negative = true) : m_stay_non_negative(stay_non_negative) {}
|
||||
ast_counter() {}
|
||||
|
||||
iterator begin() const { return m_data.begin(); }
|
||||
iterator end() const { return m_data.end(); }
|
||||
|
@ -99,7 +97,6 @@ class ast_counter {
|
|||
}
|
||||
void update(ast * el, int delta){
|
||||
get(el) += delta;
|
||||
SASSERT(!m_stay_non_negative || get(el) >= 0);
|
||||
}
|
||||
|
||||
void inc(ast * el) { update(el, 1); }
|
||||
|
|
|
@ -1041,6 +1041,11 @@ void bit_blaster_tpl<Cfg>::mk_ext_rotate_left_right(unsigned sz, expr * const *
|
|||
mk_rotate_right(sz, a_bits, static_cast<unsigned>(k.get_uint64()), out_bits);
|
||||
}
|
||||
else {
|
||||
//
|
||||
// Review: a better tuned implementation is possible by using shifts by power of two.
|
||||
// e.g., looping over the bits of b_bits, then rotate by a power of two depending
|
||||
// on the bit-position. This would get rid of the mk_urem.
|
||||
//
|
||||
expr_ref_vector sz_bits(m());
|
||||
expr_ref_vector masked_b_bits(m());
|
||||
expr_ref_vector eqs(m());
|
||||
|
|
|
@ -60,6 +60,32 @@ br_status datatype_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr
|
|||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
case OP_DT_UPDATE_FIELD: {
|
||||
SASSERT(num_args == 2);
|
||||
if (!is_app(args[0]) || !m_util.is_constructor(to_app(args[0])))
|
||||
return BR_FAILED;
|
||||
app * a = to_app(args[0]);
|
||||
func_decl * c_decl = a->get_decl();
|
||||
if (c_decl != m_util.get_accessor_constructor(f)) {
|
||||
result = a;
|
||||
return BR_DONE;
|
||||
}
|
||||
ptr_vector<func_decl> const * acc = m_util.get_constructor_accessors(c_decl);
|
||||
SASSERT(acc && acc->size() == a->get_num_args());
|
||||
unsigned num = acc->size();
|
||||
ptr_buffer<expr> new_args;
|
||||
for (unsigned i = 0; i < num; ++i) {
|
||||
|
||||
if (f == (*acc)[i]) {
|
||||
new_args.push_back(args[1]);
|
||||
}
|
||||
else {
|
||||
new_args.push_back(a->get_arg(i));
|
||||
}
|
||||
}
|
||||
result = m().mk_app(c_decl, num, new_args.c_ptr());
|
||||
return BR_DONE;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -29,43 +29,39 @@ void expr_safe_replace::insert(expr* src, expr* dst) {
|
|||
}
|
||||
|
||||
void expr_safe_replace::operator()(expr* e, expr_ref& res) {
|
||||
obj_map<expr,expr*> cache;
|
||||
ptr_vector<expr> todo, args;
|
||||
expr_ref_vector refs(m);
|
||||
todo.push_back(e);
|
||||
m_todo.push_back(e);
|
||||
expr* a, *b, *d;
|
||||
todo.push_back(e);
|
||||
|
||||
while (!todo.empty()) {
|
||||
a = todo.back();
|
||||
if (cache.contains(a)) {
|
||||
todo.pop_back();
|
||||
while (!m_todo.empty()) {
|
||||
a = m_todo.back();
|
||||
if (m_cache.contains(a)) {
|
||||
m_todo.pop_back();
|
||||
}
|
||||
else if (m_subst.find(a, b)) {
|
||||
cache.insert(a, b);
|
||||
todo.pop_back();
|
||||
m_cache.insert(a, b);
|
||||
m_todo.pop_back();
|
||||
}
|
||||
else if (is_var(a)) {
|
||||
cache.insert(a, a);
|
||||
todo.pop_back();
|
||||
m_cache.insert(a, a);
|
||||
m_todo.pop_back();
|
||||
}
|
||||
else if (is_app(a)) {
|
||||
app* c = to_app(a);
|
||||
unsigned n = c->get_num_args();
|
||||
args.reset();
|
||||
m_args.reset();
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
if (cache.find(c->get_arg(i), d)) {
|
||||
args.push_back(d);
|
||||
if (m_cache.find(c->get_arg(i), d)) {
|
||||
m_args.push_back(d);
|
||||
}
|
||||
else {
|
||||
todo.push_back(c->get_arg(i));
|
||||
m_todo.push_back(c->get_arg(i));
|
||||
}
|
||||
}
|
||||
if (args.size() == n) {
|
||||
b = m.mk_app(c->get_decl(), args.size(), args.c_ptr());
|
||||
refs.push_back(b);
|
||||
cache.insert(a, b);
|
||||
todo.pop_back();
|
||||
if (m_args.size() == n) {
|
||||
b = m.mk_app(c->get_decl(), m_args.size(), m_args.c_ptr());
|
||||
m_refs.push_back(b);
|
||||
m_cache.insert(a, b);
|
||||
m_todo.pop_back();
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -93,12 +89,16 @@ void expr_safe_replace::operator()(expr* e, expr_ref& res) {
|
|||
}
|
||||
replace(q->get_expr(), new_body);
|
||||
b = m.update_quantifier(q, pats.size(), pats.c_ptr(), nopats.size(), nopats.c_ptr(), new_body);
|
||||
refs.push_back(b);
|
||||
cache.insert(a, b);
|
||||
todo.pop_back();
|
||||
m_refs.push_back(b);
|
||||
m_cache.insert(a, b);
|
||||
m_todo.pop_back();
|
||||
}
|
||||
}
|
||||
res = cache.find(e);
|
||||
res = m_cache.find(e);
|
||||
m_cache.reset();
|
||||
m_todo.reset();
|
||||
m_args.reset();
|
||||
m_refs.reset();
|
||||
}
|
||||
|
||||
void expr_safe_replace::reset() {
|
||||
|
|
|
@ -29,9 +29,12 @@ class expr_safe_replace {
|
|||
expr_ref_vector m_src;
|
||||
expr_ref_vector m_dst;
|
||||
obj_map<expr, expr*> m_subst;
|
||||
obj_map<expr,expr*> m_cache;
|
||||
ptr_vector<expr> m_todo, m_args;
|
||||
expr_ref_vector m_refs;
|
||||
|
||||
public:
|
||||
expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m) {}
|
||||
expr_safe_replace(ast_manager& m): m(m), m_src(m), m_dst(m), m_refs(m) {}
|
||||
|
||||
void insert(expr* src, expr* dst);
|
||||
|
||||
|
@ -42,6 +45,8 @@ public:
|
|||
void apply_substitution(expr* s, expr* def, expr_ref& t);
|
||||
|
||||
void reset();
|
||||
|
||||
bool empty() const { return m_subst.empty(); }
|
||||
};
|
||||
|
||||
#endif /* __EXPR_SAFE_REPLACE_H__ */
|
||||
|
|
275
src/ast/rewriter/pb_rewriter.cpp
Normal file
275
src/ast/rewriter/pb_rewriter.cpp
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb_rewriter.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for PB constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-14-12
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include "pb_rewriter.h"
|
||||
#include "pb_rewriter_def.h"
|
||||
#include "ast_pp.h"
|
||||
#include "ast_smt_pp.h"
|
||||
|
||||
|
||||
class pb_ast_rewriter_util {
|
||||
ast_manager& m;
|
||||
expr_ref_vector m_refs;
|
||||
public:
|
||||
|
||||
typedef std::pair<expr*, rational> arg_t;
|
||||
typedef vector<arg_t> args_t;
|
||||
typedef rational numeral;
|
||||
|
||||
pb_ast_rewriter_util(ast_manager& m): m(m), m_refs(m) {}
|
||||
|
||||
expr* negate(expr* e) {
|
||||
if (m.is_true(e)) {
|
||||
return m.mk_false();
|
||||
}
|
||||
if (m.is_false(e)) {
|
||||
return m.mk_true();
|
||||
}
|
||||
if (m.is_not(e, e)) {
|
||||
return e;
|
||||
}
|
||||
m_refs.push_back(m.mk_not(e));
|
||||
return m_refs.back();
|
||||
}
|
||||
|
||||
void display(std::ostream& out, expr* e) {
|
||||
out << mk_pp(e, m);
|
||||
}
|
||||
|
||||
bool is_negated(expr* e) const {
|
||||
return m.is_not(e);
|
||||
}
|
||||
|
||||
bool is_true(expr* e) const {
|
||||
return m.is_true(e);
|
||||
}
|
||||
|
||||
bool is_false(expr* e) const {
|
||||
return m.is_false(e);
|
||||
}
|
||||
|
||||
struct compare {
|
||||
bool operator()(std::pair<expr*,rational> const& a,
|
||||
std::pair<expr*,rational> const& b) {
|
||||
return a.first->get_id() < b.first->get_id();
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
expr_ref pb_rewriter::translate_pb2lia(obj_map<expr,expr*>& vars, expr* fml) {
|
||||
pb_util util(m());
|
||||
arith_util a(m());
|
||||
expr_ref result(m()), tmp(m());
|
||||
expr_ref_vector es(m());
|
||||
expr*const* args = to_app(fml)->get_args();
|
||||
unsigned sz = to_app(fml)->get_num_args();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* e = args[i];
|
||||
if (m().is_not(e, e)) {
|
||||
es.push_back(a.mk_sub(a.mk_numeral(rational(1),true),vars.find(e)));
|
||||
}
|
||||
else {
|
||||
es.push_back(vars.find(e));
|
||||
}
|
||||
}
|
||||
|
||||
if (util.is_at_most_k(fml) || util.is_at_least_k(fml)) {
|
||||
if (es.empty()) {
|
||||
tmp = a.mk_numeral(rational(0), true);
|
||||
}
|
||||
else {
|
||||
tmp = a.mk_add(es.size(), es.c_ptr());
|
||||
}
|
||||
if (util.is_at_most_k(fml)) {
|
||||
result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false));
|
||||
}
|
||||
else {
|
||||
result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false));
|
||||
}
|
||||
}
|
||||
else if (util.is_le(fml) || util.is_ge(fml) || util.is_eq(fml)) {
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
es[i] = a.mk_mul(a.mk_numeral(util.get_coeff(fml, i),false), es[i].get());
|
||||
}
|
||||
if (es.empty()) {
|
||||
tmp = a.mk_numeral(rational(0), true);
|
||||
}
|
||||
else {
|
||||
tmp = a.mk_add(es.size(), es.c_ptr());
|
||||
}
|
||||
if (util.is_le(fml)) {
|
||||
result = a.mk_le(tmp, a.mk_numeral(util.get_k(fml), false));
|
||||
}
|
||||
else if (util.is_ge(fml)) {
|
||||
result = a.mk_ge(tmp, a.mk_numeral(util.get_k(fml), false));
|
||||
}
|
||||
else {
|
||||
result = m().mk_eq(tmp, a.mk_numeral(util.get_k(fml), false));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result = fml;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref pb_rewriter::mk_validate_rewrite(app_ref& e1, app_ref& e2) {
|
||||
ast_manager& m = e1.get_manager();
|
||||
arith_util a(m);
|
||||
symbol name;
|
||||
obj_map<expr,expr*> vars;
|
||||
expr_ref_vector trail(m), fmls(m);
|
||||
unsigned sz = to_app(e1)->get_num_args();
|
||||
expr*const*args = to_app(e1)->get_args();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr* e = args[i];
|
||||
if (m.is_true(e)) {
|
||||
if (!vars.contains(e)) {
|
||||
trail.push_back(a.mk_numeral(rational(1), true));
|
||||
vars.insert(e, trail.back());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (m.is_false(e)) {
|
||||
if (!vars.contains(e)) {
|
||||
trail.push_back(a.mk_numeral(rational(0), true));
|
||||
vars.insert(e, trail.back());
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
std::ostringstream strm;
|
||||
strm << "x" << i;
|
||||
name = symbol(strm.str().c_str());
|
||||
trail.push_back(m.mk_const(name, a.mk_int()));
|
||||
expr* x = trail.back();
|
||||
m.is_not(e,e);
|
||||
vars.insert(e, x);
|
||||
fmls.push_back(a.mk_le(a.mk_numeral(rational(0), true), x));
|
||||
fmls.push_back(a.mk_le(x, a.mk_numeral(rational(1), true)));
|
||||
}
|
||||
expr_ref tmp(m);
|
||||
expr_ref fml1 = translate_pb2lia(vars, e1);
|
||||
expr_ref fml2 = translate_pb2lia(vars, e2);
|
||||
tmp = m.mk_not(m.mk_eq(fml1, fml2));
|
||||
fmls.push_back(tmp);
|
||||
tmp = m.mk_and(fmls.size(), fmls.c_ptr());
|
||||
return tmp;
|
||||
}
|
||||
|
||||
static unsigned s_lemma = 0;
|
||||
|
||||
void pb_rewriter::validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml) {
|
||||
ast_manager& m = fml.get_manager();
|
||||
app_ref tmp1(m), tmp2(m);
|
||||
tmp1 = m.mk_app(f, sz, args);
|
||||
tmp2 = to_app(fml);
|
||||
expr_ref tmp = mk_validate_rewrite(tmp1, tmp2);
|
||||
dump_pb_rewrite(tmp);
|
||||
}
|
||||
|
||||
void pb_rewriter::dump_pb_rewrite(expr* fml) {
|
||||
std::ostringstream strm;
|
||||
strm << "pb_rewrite_" << (s_lemma++) << ".smt2";
|
||||
std::ofstream out(strm.str().c_str());
|
||||
ast_smt_pp pp(m());
|
||||
pp.display_smt2(out, fml);
|
||||
out.close();
|
||||
}
|
||||
|
||||
br_status pb_rewriter::mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
ast_manager& m = result.get_manager();
|
||||
rational sum(0), maxsum(0);
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
if (m.is_true(args[i])) {
|
||||
sum += m_util.get_coeff(f, i);
|
||||
maxsum += m_util.get_coeff(f, i);
|
||||
}
|
||||
else if (!m.is_false(args[i])) {
|
||||
maxsum += m_util.get_coeff(f, i);
|
||||
}
|
||||
}
|
||||
rational k = m_util.get_k(f);
|
||||
|
||||
vector<std::pair<expr*,rational> > vec;
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
vec.push_back(std::make_pair(args[i], m_util.get_coeff(f, i)));
|
||||
}
|
||||
|
||||
switch(f->get_decl_kind()) {
|
||||
case OP_AT_MOST_K:
|
||||
case OP_PB_LE:
|
||||
for (unsigned i = 0; i < num_args; ++i) {
|
||||
vec[i].second.neg();
|
||||
}
|
||||
k.neg();
|
||||
break;
|
||||
case OP_AT_LEAST_K:
|
||||
case OP_PB_GE:
|
||||
case OP_PB_EQ:
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
bool is_eq = f->get_decl_kind() == OP_PB_EQ;
|
||||
|
||||
pb_ast_rewriter_util pbu(m);
|
||||
pb_rewriter_util<pb_ast_rewriter_util> util(pbu);
|
||||
|
||||
util.unique(vec, k, is_eq);
|
||||
lbool is_sat = util.normalize(vec, k, is_eq);
|
||||
util.prune(vec, k, is_eq);
|
||||
switch (is_sat) {
|
||||
case l_true:
|
||||
result = m.mk_true();
|
||||
break;
|
||||
case l_false:
|
||||
result = m.mk_false();
|
||||
break;
|
||||
default:
|
||||
m_args.reset();
|
||||
m_coeffs.reset();
|
||||
for (unsigned i = 0; i < vec.size(); ++i) {
|
||||
m_args.push_back(vec[i].first);
|
||||
m_coeffs.push_back(vec[i].second);
|
||||
}
|
||||
if (is_eq) {
|
||||
result = m_util.mk_eq(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k);
|
||||
}
|
||||
else {
|
||||
result = m_util.mk_ge(vec.size(), m_coeffs.c_ptr(), m_args.c_ptr(), k);
|
||||
}
|
||||
break;
|
||||
}
|
||||
TRACE("pb",
|
||||
expr_ref tmp(m);
|
||||
tmp = m.mk_app(f, num_args, args);
|
||||
tout << tmp << "\n";
|
||||
tout << result << "\n";
|
||||
);
|
||||
TRACE("pb_validate",
|
||||
validate_rewrite(f, num_args, args, result););
|
||||
|
||||
return BR_DONE;
|
||||
}
|
||||
|
||||
|
65
src/ast/rewriter/pb_rewriter.h
Normal file
65
src/ast/rewriter/pb_rewriter.h
Normal file
|
@ -0,0 +1,65 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb_rewriter.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for PB constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-14-12
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PB_REWRITER_H_
|
||||
#define _PB_REWRITER_H_
|
||||
|
||||
#include"pb_decl_plugin.h"
|
||||
#include"rewriter_types.h"
|
||||
#include"params.h"
|
||||
#include"lbool.h"
|
||||
|
||||
|
||||
template<typename PBU>
|
||||
class pb_rewriter_util {
|
||||
PBU& m_util;
|
||||
void display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
|
||||
public:
|
||||
pb_rewriter_util(PBU& u) : m_util(u) {}
|
||||
void unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
|
||||
lbool normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
|
||||
void prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq);
|
||||
};
|
||||
|
||||
/**
|
||||
\brief Cheap rewrite rules for PB constraints
|
||||
*/
|
||||
class pb_rewriter {
|
||||
pb_util m_util;
|
||||
vector<rational> m_coeffs;
|
||||
ptr_vector<expr> m_args;
|
||||
|
||||
void validate_rewrite(func_decl* f, unsigned sz, expr*const* args, expr_ref& fml);
|
||||
public:
|
||||
pb_rewriter(ast_manager & m, params_ref const & p = params_ref()):
|
||||
m_util(m) {
|
||||
}
|
||||
ast_manager & m() const { return m_util.get_manager(); }
|
||||
family_id get_fid() const { return m_util.get_family_id(); }
|
||||
|
||||
void updt_params(params_ref const & p) {}
|
||||
static void get_param_descrs(param_descrs & r) {}
|
||||
|
||||
br_status mk_app_core(func_decl * f, unsigned num_args, expr * const * args, expr_ref & result);
|
||||
|
||||
expr_ref translate_pb2lia(obj_map<expr,expr*>& vars, expr* fml);
|
||||
expr_ref mk_validate_rewrite(app_ref& e1, app_ref& e2);
|
||||
void dump_pb_rewrite(expr* fml);
|
||||
};
|
||||
|
||||
#endif
|
298
src/ast/rewriter/pb_rewriter_def.h
Normal file
298
src/ast/rewriter/pb_rewriter_def.h
Normal file
|
@ -0,0 +1,298 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
pb_rewriter_def.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Basic rewriting rules for PB constraints.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2013-14-12
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
#ifndef _PB_REWRITER_DEF_H_
|
||||
#define _PB_REWRITER_DEF_H_
|
||||
|
||||
#include"pb_rewriter.h"
|
||||
|
||||
|
||||
template<typename PBU>
|
||||
void pb_rewriter_util<PBU>::display(std::ostream& out, typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
out << args[i].second << " * ";
|
||||
m_util.display(out, args[i].first);
|
||||
out << " ";
|
||||
if (i+1 < args.size()) out << "+ ";
|
||||
}
|
||||
out << (is_eq?" = ":" >= ") << k << "\n";
|
||||
}
|
||||
|
||||
template<typename PBU>
|
||||
void pb_rewriter_util<PBU>::unique(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
|
||||
|
||||
TRACE("pb_verbose", display(tout << "pre-unique:", args, k, is_eq););
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
if (m_util.is_negated(args[i].first)) {
|
||||
args[i].first = m_util.negate(args[i].first);
|
||||
k -= args[i].second;
|
||||
args[i].second = -args[i].second;
|
||||
}
|
||||
}
|
||||
// remove constants
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
if (m_util.is_true(args[i].first)) {
|
||||
k -= args[i].second;
|
||||
std::swap(args[i], args[args.size()-1]);
|
||||
args.pop_back();
|
||||
--i;
|
||||
}
|
||||
else if (m_util.is_false(args[i].first)) {
|
||||
std::swap(args[i], args[args.size()-1]);
|
||||
args.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
// sort and coalesce arguments:
|
||||
typename PBU::compare cmp;
|
||||
std::sort(args.begin(), args.end(), cmp);
|
||||
|
||||
// coallesce
|
||||
unsigned i, j;
|
||||
for (i = 0, j = 1; j < args.size(); ++j) {
|
||||
if (args[i].first == args[j].first) {
|
||||
args[i].second += args[j].second;
|
||||
}
|
||||
else {
|
||||
++i;
|
||||
args[i] = args[j];
|
||||
}
|
||||
}
|
||||
args.resize(i+1);
|
||||
|
||||
// remove 0s.
|
||||
for (i = 0, j = 0; j < args.size(); ++j) {
|
||||
if (!args[j].second.is_zero()) {
|
||||
if (i != j) {
|
||||
args[i] = args[j];
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
args.resize(i);
|
||||
TRACE("pb_verbose", display(tout << "post-unique:", args, k, is_eq););
|
||||
}
|
||||
|
||||
template<typename PBU>
|
||||
lbool pb_rewriter_util<PBU>::normalize(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
|
||||
TRACE("pb_verbose", display(tout << "pre-normalize:", args, k, is_eq););
|
||||
|
||||
DEBUG_CODE(
|
||||
bool found = false;
|
||||
for (unsigned i = 0; !found && i < args.size(); ++i) {
|
||||
found = args[i].second.is_zero();
|
||||
}
|
||||
if (found) display(verbose_stream(), args, k, is_eq);
|
||||
SASSERT(!found););
|
||||
|
||||
//
|
||||
// Ensure all coefficients are positive:
|
||||
// c*l + y >= k
|
||||
// <=>
|
||||
// c*(1-~l) + y >= k
|
||||
// <=>
|
||||
// c - c*~l + y >= k
|
||||
// <=>
|
||||
// -c*~l + y >= k - c
|
||||
//
|
||||
typename PBU::numeral sum(0);
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
typename PBU::numeral c = args[i].second;
|
||||
if (c.is_neg()) {
|
||||
args[i].second = -c;
|
||||
args[i].first = m_util.negate(args[i].first);
|
||||
k -= c;
|
||||
}
|
||||
sum += args[i].second;
|
||||
}
|
||||
// detect tautologies:
|
||||
if (!is_eq && k <= PBU::numeral::zero()) {
|
||||
args.reset();
|
||||
k = PBU::numeral::zero();
|
||||
return l_true;
|
||||
}
|
||||
if (is_eq && k.is_zero() && args.empty()) {
|
||||
return l_true;
|
||||
}
|
||||
|
||||
// detect infeasible constraints:
|
||||
if (sum < k) {
|
||||
args.reset();
|
||||
k = PBU::numeral::one();
|
||||
return l_false;
|
||||
}
|
||||
|
||||
if (is_eq && k == sum) {
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
args[i].second = PBU::numeral::one();
|
||||
}
|
||||
typename PBU::numeral num(args.size());
|
||||
k = num;
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
bool all_int = true;
|
||||
for (unsigned i = 0; all_int && i < args.size(); ++i) {
|
||||
all_int = args[i].second.is_int();
|
||||
}
|
||||
|
||||
if (!all_int) {
|
||||
// normalize to integers.
|
||||
typename PBU::numeral d(denominator(k));
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
d = lcm(d, denominator(args[i].second));
|
||||
}
|
||||
SASSERT(!d.is_one());
|
||||
k *= d;
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
args[i].second *= d;
|
||||
}
|
||||
}
|
||||
|
||||
if (is_eq) {
|
||||
TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq););
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
// Ensure the largest coefficient is not larger than k:
|
||||
sum = PBU::numeral::zero();
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
typename PBU::numeral c = args[i].second;
|
||||
if (c > k) {
|
||||
args[i].second = k;
|
||||
}
|
||||
sum += args[i].second;
|
||||
}
|
||||
SASSERT(!args.empty());
|
||||
|
||||
// normalize tight inequalities to unit coefficients.
|
||||
if (sum == k) {
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
args[i].second = PBU::numeral::one();
|
||||
}
|
||||
typename PBU::numeral num(args.size());
|
||||
k = num;
|
||||
}
|
||||
|
||||
// apply cutting plane reduction:
|
||||
typename PBU::numeral g(0);
|
||||
for (unsigned i = 0; !g.is_one() && i < args.size(); ++i) {
|
||||
typename PBU::numeral c = args[i].second;
|
||||
if (c != k) {
|
||||
if (g.is_zero()) {
|
||||
g = c;
|
||||
}
|
||||
else {
|
||||
g = gcd(g, c);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (g.is_zero()) {
|
||||
// all coefficients are equal to k.
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
SASSERT(args[i].second == k);
|
||||
args[i].second = PBU::numeral::one();
|
||||
}
|
||||
k = PBU::numeral::one();
|
||||
}
|
||||
else if (g > PBU::numeral::one()) {
|
||||
|
||||
//
|
||||
// Example 5x + 5y + 2z + 2u >= 5
|
||||
// becomes 3x + 3y + z + u >= 3
|
||||
//
|
||||
typename PBU::numeral k_new = div(k, g);
|
||||
if (!(k % g).is_zero()) { // k_new is the ceiling of k / g.
|
||||
k_new++;
|
||||
}
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
SASSERT(args[i].second.is_pos());
|
||||
typename PBU::numeral c = args[i].second;
|
||||
if (c == k) {
|
||||
c = k_new;
|
||||
}
|
||||
else {
|
||||
c = div(c, g);
|
||||
}
|
||||
args[i].second = c;
|
||||
SASSERT(args[i].second.is_pos());
|
||||
}
|
||||
k = k_new;
|
||||
}
|
||||
//
|
||||
// normalize coefficients that fall within a range
|
||||
// k/n <= ... < k/(n-1) for some n = 1,2,...
|
||||
//
|
||||
// e.g, k/n <= min <= max < k/(n-1)
|
||||
// k/min <= n, n-1 < k/max
|
||||
// . floor(k/max) = ceil(k/min) - 1
|
||||
// . floor(k/max) < k/max
|
||||
//
|
||||
// example: k = 5, min = 3, max = 4: 5/3 -> 2 5/4 -> 1, n = 2
|
||||
// replace all coefficients by 1, and k by 2.
|
||||
//
|
||||
if (!k.is_one()) {
|
||||
typename PBU::numeral min = args[0].second, max = args[0].second;
|
||||
for (unsigned i = 1; i < args.size(); ++i) {
|
||||
if (args[i].second < min) min = args[i].second;
|
||||
if (args[i].second > max) max = args[i].second;
|
||||
}
|
||||
SASSERT(min.is_pos());
|
||||
typename PBU::numeral n0 = k/max;
|
||||
typename PBU::numeral n1 = floor(n0);
|
||||
typename PBU::numeral n2 = ceil(k/min) - PBU::numeral::one();
|
||||
if (n1 == n2 && !n0.is_int()) {
|
||||
IF_VERBOSE(3, display(verbose_stream() << "set cardinality\n", args, k, is_eq););
|
||||
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
args[i].second = PBU::numeral::one();
|
||||
}
|
||||
k = n1 + PBU::numeral::one();
|
||||
}
|
||||
}
|
||||
TRACE("pb_verbose", display(tout << "post-normalize:", args, k, is_eq););
|
||||
return l_undef;
|
||||
}
|
||||
|
||||
template<typename PBU>
|
||||
void pb_rewriter_util<PBU>::prune(typename PBU::args_t& args, typename PBU::numeral& k, bool is_eq) {
|
||||
if (is_eq) {
|
||||
return;
|
||||
}
|
||||
typename PBU::numeral nlt(0);
|
||||
unsigned occ = 0;
|
||||
for (unsigned i = 0; nlt < k && i < args.size(); ++i) {
|
||||
if (args[i].second < k) {
|
||||
nlt += args[i].second;
|
||||
++occ;
|
||||
}
|
||||
}
|
||||
if (0 < occ && nlt < k) {
|
||||
for (unsigned i = 0; i < args.size(); ++i) {
|
||||
if (args[i].second < k) {
|
||||
args[i] = args.back();
|
||||
args.pop_back();
|
||||
--i;
|
||||
}
|
||||
}
|
||||
unique(args, k, is_eq);
|
||||
normalize(args, k, is_eq);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
|
@ -31,6 +31,8 @@ void poly_rewriter<Config>::updt_params(params_ref const & _p) {
|
|||
m_hoist_mul = p.hoist_mul();
|
||||
m_hoist_cmul = p.hoist_cmul();
|
||||
m_som_blowup = p.som_blowup();
|
||||
if (!m_flat) m_som = false;
|
||||
if (m_som) m_hoist_mul = false;
|
||||
}
|
||||
|
||||
template<typename Config>
|
||||
|
|
|
@ -25,6 +25,7 @@ Notes:
|
|||
#include"array_rewriter.h"
|
||||
#include"fpa_rewriter.h"
|
||||
#include"dl_rewriter.h"
|
||||
#include"pb_rewriter.h"
|
||||
#include"rewriter_def.h"
|
||||
#include"expr_substitution.h"
|
||||
#include"ast_smt2_pp.h"
|
||||
|
@ -41,6 +42,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
|
|||
datatype_rewriter m_dt_rw;
|
||||
fpa_rewriter m_f_rw;
|
||||
dl_rewriter m_dl_rw;
|
||||
pb_rewriter m_pb_rw;
|
||||
arith_util m_a_util;
|
||||
bv_util m_bv_util;
|
||||
unsigned long long m_max_memory; // in bytes
|
||||
|
@ -196,6 +198,8 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
|
|||
return m_f_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_dl_rw.get_fid())
|
||||
return m_dl_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_pb_rw.get_fid())
|
||||
return m_pb_rw.mk_app_core(f, num, args, result);
|
||||
return BR_FAILED;
|
||||
}
|
||||
|
||||
|
@ -645,6 +649,7 @@ struct th_rewriter_cfg : public default_rewriter_cfg {
|
|||
m_dt_rw(m),
|
||||
m_f_rw(m, p),
|
||||
m_dl_rw(m),
|
||||
m_pb_rw(m),
|
||||
m_a_util(m),
|
||||
m_bv_util(m),
|
||||
m_used_dependencies(m),
|
||||
|
|
|
@ -164,7 +164,7 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref
|
|||
tout << "\n----->\n" << mk_ismt2_pp(result, m) << "\n";);
|
||||
}
|
||||
|
||||
static void get_free_vars_offset(ast_mark& mark, ptr_vector<expr>& todo, unsigned offset, expr* e, ptr_vector<sort>& sorts) {
|
||||
static void get_free_vars_offset(expr_sparse_mark& mark, ptr_vector<expr>& todo, unsigned offset, expr* e, ptr_vector<sort>& sorts) {
|
||||
todo.push_back(e);
|
||||
while (!todo.empty()) {
|
||||
e = todo.back();
|
||||
|
@ -176,7 +176,7 @@ static void get_free_vars_offset(ast_mark& mark, ptr_vector<expr>& todo, unsigne
|
|||
switch(e->get_kind()) {
|
||||
case AST_QUANTIFIER: {
|
||||
quantifier* q = to_quantifier(e);
|
||||
ast_mark mark1;
|
||||
expr_sparse_mark mark1;
|
||||
ptr_vector<expr> todo1;
|
||||
get_free_vars_offset(mark1, todo1, offset+q->get_num_decls(), q->get_expr(), sorts);
|
||||
break;
|
||||
|
@ -210,11 +210,33 @@ static void get_free_vars_offset(ast_mark& mark, ptr_vector<expr>& todo, unsigne
|
|||
|
||||
|
||||
void get_free_vars(expr* e, ptr_vector<sort>& sorts) {
|
||||
ast_mark mark;
|
||||
expr_sparse_mark mark;
|
||||
ptr_vector<expr> todo;
|
||||
get_free_vars_offset(mark, todo, 0, e, sorts);
|
||||
}
|
||||
|
||||
void get_free_vars(ast_mark& mark, ptr_vector<expr>& todo, expr* e, ptr_vector<sort>& sorts) {
|
||||
void get_free_vars(expr_sparse_mark& mark, ptr_vector<expr>& todo, expr* e, ptr_vector<sort>& sorts) {
|
||||
get_free_vars_offset(mark, todo, 0, e, sorts);
|
||||
}
|
||||
|
||||
void expr_free_vars::reset() {
|
||||
m_mark.reset();
|
||||
m_sorts.reset();
|
||||
SASSERT(m_todo.empty());
|
||||
}
|
||||
|
||||
void expr_free_vars::set_default_sort(sort *s) {
|
||||
for (unsigned i = 0; i < m_sorts.size(); ++i) {
|
||||
if (!m_sorts[i]) m_sorts[i] = s;
|
||||
}
|
||||
}
|
||||
|
||||
void expr_free_vars::operator()(expr* e) {
|
||||
reset();
|
||||
get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts);
|
||||
}
|
||||
|
||||
void expr_free_vars::accumulate(expr* e) {
|
||||
SASSERT(m_todo.empty());
|
||||
get_free_vars_offset(m_mark, m_todo, 0, e, m_sorts);
|
||||
}
|
||||
|
|
|
@ -81,9 +81,23 @@ void instantiate(ast_manager & m, quantifier * q, expr * const * exprs, expr_ref
|
|||
|
||||
Return the sorts of the free variables.
|
||||
*/
|
||||
void get_free_vars(expr* e, ptr_vector<sort>& sorts);
|
||||
|
||||
void get_free_vars(ast_mark& mark, ptr_vector<expr>& todo, expr* e, ptr_vector<sort>& sorts);
|
||||
class expr_free_vars {
|
||||
expr_sparse_mark m_mark;
|
||||
ptr_vector<sort> m_sorts;
|
||||
ptr_vector<expr> m_todo;
|
||||
public:
|
||||
void reset();
|
||||
void operator()(expr* e);
|
||||
void accumulate(expr* e);
|
||||
bool empty() const { return m_sorts.empty(); }
|
||||
unsigned size() const { return m_sorts.size(); }
|
||||
sort* operator[](unsigned idx) const { return m_sorts[idx]; }
|
||||
bool contains(unsigned idx) const { return idx < m_sorts.size() && m_sorts[idx] != 0; }
|
||||
void set_default_sort(sort* s);
|
||||
void reverse() { m_sorts.reverse(); }
|
||||
sort*const* c_ptr() const { return m_sorts.c_ptr(); }
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -332,6 +332,7 @@ lbool array_simplifier_plugin::eq_default(expr* def, unsigned arity, unsigned nu
|
|||
for (unsigned i = 0; i < num_st; ++i) {
|
||||
all_eq &= (st[i][arity] == def);
|
||||
all_diseq &= m_manager.is_unique_value(st[i][arity]) && (st[i][arity] != def);
|
||||
TRACE("array_simplifier", tout << m_manager.is_unique_value(st[i][arity]) << " " << mk_pp(st[i][arity], m_manager) << "\n";);
|
||||
}
|
||||
if (all_eq) {
|
||||
return l_true;
|
||||
|
@ -350,6 +351,12 @@ bool array_simplifier_plugin::insert_table(expr* def, unsigned arity, unsigned n
|
|||
return false;
|
||||
}
|
||||
}
|
||||
TRACE("array_simplifier", tout << "inserting: ";
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
tout << mk_pp(st[i][j], m_manager) << " ";
|
||||
}
|
||||
tout << " |-> " << mk_pp(def, m_manager) << "\n";
|
||||
);
|
||||
args_entry e(arity, st[i]);
|
||||
table.insert_if_not_there(e);
|
||||
}
|
||||
|
@ -424,7 +431,8 @@ bool array_simplifier_plugin::reduce_eq(expr * lhs, expr * rhs, expr_ref & resul
|
|||
lbool eq = eq_stores(c1, arity2, st1.size(), st1.c_ptr(), st2.size(), st2.c_ptr());
|
||||
TRACE("array_simplifier",
|
||||
tout << mk_pp(lhs, m_manager) << " = "
|
||||
<< mk_pp(rhs, m_manager) << " := " << eq << "\n";);
|
||||
<< mk_pp(rhs, m_manager) << " := " << eq << "\n";
|
||||
tout << "arity: " << arity1 << "\n";);
|
||||
switch(eq) {
|
||||
case l_false:
|
||||
result = m_manager.mk_false();
|
||||
|
|
|
@ -20,21 +20,32 @@ Notes:
|
|||
#define _BASE_SIMPLIFIER_H_
|
||||
|
||||
#include"expr_map.h"
|
||||
#include"ast_pp.h"
|
||||
|
||||
/**
|
||||
\brief Implements basic functionality used by expression simplifiers.
|
||||
*/
|
||||
class base_simplifier {
|
||||
protected:
|
||||
ast_manager & m_manager;
|
||||
ast_manager & m;
|
||||
expr_map m_cache;
|
||||
ptr_vector<expr> m_todo;
|
||||
|
||||
void cache_result(expr * n, expr * r, proof * p) { m_cache.insert(n, r, p); }
|
||||
void cache_result(expr * n, expr * r, proof * p) {
|
||||
m_cache.insert(n, r, p);
|
||||
CTRACE("simplifier", !is_rewrite_proof(n, r, p),
|
||||
tout << mk_pp(n, m) << "\n";
|
||||
tout << mk_pp(r, m) << "\n";
|
||||
tout << mk_pp(p, m) << "\n";);
|
||||
SASSERT(is_rewrite_proof(n, r, p));
|
||||
}
|
||||
void reset_cache() { m_cache.reset(); }
|
||||
void flush_cache() { m_cache.flush(); }
|
||||
void get_cached(expr * n, expr * & r, proof * & p) const { m_cache.get(n, r, p); }
|
||||
|
||||
void reinitialize() { m_cache.set_store_proofs(m.fine_grain_proofs()); }
|
||||
|
||||
|
||||
void visit(expr * n, bool & visited) {
|
||||
if (!is_cached(n)) {
|
||||
m_todo.push_back(n);
|
||||
|
@ -44,11 +55,22 @@ protected:
|
|||
|
||||
public:
|
||||
base_simplifier(ast_manager & m):
|
||||
m_manager(m),
|
||||
m(m),
|
||||
m_cache(m, m.fine_grain_proofs()) {
|
||||
}
|
||||
bool is_cached(expr * n) const { return m_cache.contains(n); }
|
||||
ast_manager & get_manager() { return m_manager; }
|
||||
ast_manager & get_manager() { return m; }
|
||||
|
||||
bool is_rewrite_proof(expr* n, expr* r, proof* p) {
|
||||
if (p &&
|
||||
!m.is_undef_proof(p) &&
|
||||
!(m.has_fact(p) &&
|
||||
(m.is_eq(m.get_fact(p)) || m.is_oeq(m.get_fact(p)) || m.is_iff(m.get_fact(p))) &&
|
||||
to_app(m.get_fact(p))->get_arg(0) == n &&
|
||||
to_app(m.get_fact(p))->get_arg(1) == r)) return false;
|
||||
|
||||
return (!m.fine_grain_proofs() || p || (n == r));
|
||||
}
|
||||
};
|
||||
|
||||
#endif /* _BASE_SIMPLIFIER_H_ */
|
||||
|
|
|
@ -100,11 +100,11 @@ bool bv_elim_star::visit_quantifier(quantifier* q) {
|
|||
}
|
||||
|
||||
void bv_elim_star::reduce1_quantifier(quantifier* q) {
|
||||
quantifier_ref r(m_manager);
|
||||
proof_ref pr(m_manager);
|
||||
quantifier_ref r(m);
|
||||
proof_ref pr(m);
|
||||
m_bv_elim.elim(q, r);
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
pr = m_manager.mk_rewrite(q, r.get());
|
||||
if (m.fine_grain_proofs()) {
|
||||
pr = m.mk_rewrite(q, r.get());
|
||||
}
|
||||
else {
|
||||
pr = 0;
|
||||
|
|
|
@ -81,6 +81,8 @@ bool datatype_simplifier_plugin::reduce(func_decl * f, unsigned num_args, expr *
|
|||
}
|
||||
UNREACHABLE();
|
||||
}
|
||||
case OP_DT_UPDATE_FIELD:
|
||||
return false;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
|
|
|
@ -203,20 +203,20 @@ void elim_bounds_star::reduce1_quantifier(quantifier * q) {
|
|||
cache_result(q, q, 0);
|
||||
return;
|
||||
}
|
||||
quantifier_ref new_q(m_manager);
|
||||
quantifier_ref new_q(m);
|
||||
expr * new_body = 0;
|
||||
proof * new_pr;
|
||||
get_cached(q->get_expr(), new_body, new_pr);
|
||||
new_q = m_manager.update_quantifier(q, new_body);
|
||||
expr_ref r(m_manager);
|
||||
new_q = m.update_quantifier(q, new_body);
|
||||
expr_ref r(m);
|
||||
m_elim(new_q, r);
|
||||
if (q == r.get()) {
|
||||
cache_result(q, q, 0);
|
||||
return;
|
||||
}
|
||||
proof_ref pr(m_manager);
|
||||
if (m_manager.fine_grain_proofs())
|
||||
pr = m_manager.mk_rewrite(q, r); // TODO: improve justification
|
||||
proof_ref pr(m);
|
||||
if (m.fine_grain_proofs())
|
||||
pr = m.mk_rewrite(q, r); // TODO: improve justification
|
||||
cache_result(q, r, pr);
|
||||
}
|
||||
|
||||
|
|
|
@ -187,7 +187,7 @@ pull_ite_tree_star::pull_ite_tree_star(ast_manager & m, simplifier & s):
|
|||
|
||||
bool pull_ite_tree_star::get_subst(expr * n, expr_ref & r, proof_ref & p) {
|
||||
if (is_app(n) && is_target(to_app(n))) {
|
||||
app_ref tmp(m_manager);
|
||||
app_ref tmp(m);
|
||||
m_proc(to_app(n), tmp, p);
|
||||
r = tmp;
|
||||
return true;
|
||||
|
@ -199,10 +199,10 @@ bool pull_cheap_ite_tree_star::is_target(app * n) const {
|
|||
bool r =
|
||||
n->get_num_args() == 2 &&
|
||||
n->get_family_id() != null_family_id &&
|
||||
m_manager.is_bool(n) &&
|
||||
(m_manager.is_value(n->get_arg(0)) || m_manager.is_value(n->get_arg(1))) &&
|
||||
(m_manager.is_term_ite(n->get_arg(0)) || m_manager.is_term_ite(n->get_arg(1)));
|
||||
TRACE("pull_ite_target", tout << mk_pp(n, m_manager) << "\nresult: " << r << "\n";);
|
||||
m.is_bool(n) &&
|
||||
(m.is_value(n->get_arg(0)) || m.is_value(n->get_arg(1))) &&
|
||||
(m.is_term_ite(n->get_arg(0)) || m.is_term_ite(n->get_arg(1)));
|
||||
TRACE("pull_ite_target", tout << mk_pp(n, m) << "\nresult: " << r << "\n";);
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ push_app_ite::~push_app_ite() {
|
|||
|
||||
int push_app_ite::has_ite_arg(unsigned num_args, expr * const * args) {
|
||||
for (unsigned i = 0; i < num_args; i++)
|
||||
if (m_manager.is_ite(args[i]))
|
||||
if (m.is_ite(args[i]))
|
||||
return i;
|
||||
return -1;
|
||||
}
|
||||
|
@ -53,10 +53,10 @@ void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * arg
|
|||
expr ** args_prime = const_cast<expr**>(args);
|
||||
expr * old = args_prime[ite_arg_idx];
|
||||
args_prime[ite_arg_idx] = t;
|
||||
expr_ref t_new(m_manager);
|
||||
expr_ref t_new(m);
|
||||
apply(decl, num_args, args_prime, t_new);
|
||||
args_prime[ite_arg_idx] = e;
|
||||
expr_ref e_new(m_manager);
|
||||
expr_ref e_new(m);
|
||||
apply(decl, num_args, args_prime, e_new);
|
||||
args_prime[ite_arg_idx] = old;
|
||||
expr * new_args[3] = { c, t_new, e_new };
|
||||
|
@ -67,11 +67,11 @@ void push_app_ite::apply(func_decl * decl, unsigned num_args, expr * const * arg
|
|||
\brief Default (conservative) implementation. Return true if there one and only one ite-term argument.
|
||||
*/
|
||||
bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const * args) {
|
||||
if (m_manager.is_ite(decl))
|
||||
if (m.is_ite(decl))
|
||||
return false;
|
||||
bool found_ite = false;
|
||||
for (unsigned i = 0; i < num_args; i++) {
|
||||
if (m_manager.is_ite(args[i]) && !m_manager.is_bool(args[i])) {
|
||||
if (m.is_ite(args[i]) && !m.is_bool(args[i])) {
|
||||
if (found_ite) {
|
||||
if (m_conservative)
|
||||
return false;
|
||||
|
@ -83,7 +83,7 @@ bool push_app_ite::is_target(func_decl * decl, unsigned num_args, expr * const *
|
|||
}
|
||||
CTRACE("push_app_ite", found_ite, tout << "found target for push app ite:\n";
|
||||
tout << decl->get_name();
|
||||
for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m_manager);
|
||||
for (unsigned i = 0; i < num_args; i++) tout << " " << mk_pp(args[i], m);
|
||||
tout << "\n";);
|
||||
return found_ite;
|
||||
}
|
||||
|
@ -94,19 +94,19 @@ void push_app_ite::operator()(expr * s, expr_ref & r, proof_ref & p) {
|
|||
reduce_core(s);
|
||||
get_cached(s, result, result_proof);
|
||||
r = result;
|
||||
switch (m_manager.proof_mode()) {
|
||||
switch (m.proof_mode()) {
|
||||
case PGM_DISABLED:
|
||||
p = m_manager.mk_undef_proof();
|
||||
p = m.mk_undef_proof();
|
||||
break;
|
||||
case PGM_COARSE:
|
||||
if (result == s)
|
||||
p = m_manager.mk_reflexivity(s);
|
||||
p = m.mk_reflexivity(s);
|
||||
else
|
||||
p = m_manager.mk_rewrite_star(s, result, 0, 0);
|
||||
p = m.mk_rewrite_star(s, result, 0, 0);
|
||||
break;
|
||||
case PGM_FINE:
|
||||
if (result == s)
|
||||
p = m_manager.mk_reflexivity(s);
|
||||
p = m.mk_reflexivity(s);
|
||||
else
|
||||
p = result_proof;
|
||||
break;
|
||||
|
@ -171,24 +171,24 @@ void push_app_ite::reduce1_app(app * n) {
|
|||
m_args.reset();
|
||||
|
||||
func_decl * decl = n->get_decl();
|
||||
proof_ref p1(m_manager);
|
||||
proof_ref p1(m);
|
||||
get_args(n, m_args, p1);
|
||||
|
||||
expr_ref r(m_manager);
|
||||
expr_ref r(m);
|
||||
if (is_target(decl, m_args.size(), m_args.c_ptr()))
|
||||
apply(decl, m_args.size(), m_args.c_ptr(), r);
|
||||
else
|
||||
mk_app(decl, m_args.size(), m_args.c_ptr(), r);
|
||||
|
||||
if (!m_manager.fine_grain_proofs())
|
||||
if (!m.fine_grain_proofs())
|
||||
cache_result(n, r, 0);
|
||||
else {
|
||||
expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
|
||||
expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr());
|
||||
proof * p;
|
||||
if (n == r)
|
||||
p = 0;
|
||||
else if (r != s)
|
||||
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r));
|
||||
p = m.mk_transitivity(p1, m.mk_rewrite(s, r));
|
||||
else
|
||||
p = p1;
|
||||
cache_result(n, r, p);
|
||||
|
@ -200,8 +200,8 @@ void push_app_ite::reduce1_quantifier(quantifier * q) {
|
|||
proof * new_body_pr;
|
||||
get_cached(q->get_expr(), new_body, new_body_pr);
|
||||
|
||||
quantifier * new_q = m_manager.update_quantifier(q, new_body);
|
||||
proof * p = q == new_q ? 0 : m_manager.mk_quant_intro(q, new_q, new_body_pr);
|
||||
quantifier * new_q = m.update_quantifier(q, new_body);
|
||||
proof * p = q == new_q ? 0 : m.mk_quant_intro(q, new_q, new_body_pr);
|
||||
cache_result(q, new_q, p);
|
||||
}
|
||||
|
||||
|
|
|
@ -61,16 +61,18 @@ void simplifier::enable_ac_support(bool flag) {
|
|||
*/
|
||||
void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
|
||||
m_need_reset = true;
|
||||
reinitialize();
|
||||
expr * s_orig = s;
|
||||
expr * old_s;
|
||||
expr * result;
|
||||
proof * result_proof;
|
||||
switch (m_manager.proof_mode()) {
|
||||
switch (m.proof_mode()) {
|
||||
case PGM_DISABLED: // proof generation is disabled.
|
||||
reduce_core(s);
|
||||
// after executing reduce_core, the result of the simplification is in the cache
|
||||
get_cached(s, result, result_proof);
|
||||
r = result;
|
||||
p = m_manager.mk_undef_proof();
|
||||
p = m.mk_undef_proof();
|
||||
break;
|
||||
case PGM_COARSE: // coarse proofs... in this case, we do not produce a step by step (fine grain) proof to show the equivalence (or equisatisfiability) of s an r.
|
||||
m_subst_proofs.reset(); // m_subst_proofs is an auxiliary vector that is used to justify substitutions. See comment on method get_subst.
|
||||
|
@ -78,10 +80,10 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
|
|||
get_cached(s, result, result_proof);
|
||||
r = result;
|
||||
if (result == s)
|
||||
p = m_manager.mk_reflexivity(s);
|
||||
p = m.mk_reflexivity(s);
|
||||
else {
|
||||
remove_duplicates(m_subst_proofs);
|
||||
p = m_manager.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr());
|
||||
p = m.mk_rewrite_star(s, result, m_subst_proofs.size(), m_subst_proofs.c_ptr());
|
||||
}
|
||||
break;
|
||||
case PGM_FINE: // fine grain proofs... in this mode, every proof step (or most of them) is described.
|
||||
|
@ -90,17 +92,20 @@ void simplifier::operator()(expr * s, expr_ref & r, proof_ref & p) {
|
|||
// keep simplyfing until no further simplifications are possible.
|
||||
while (s != old_s) {
|
||||
TRACE("simplifier", tout << "simplification pass... " << s->get_id() << "\n";);
|
||||
TRACE("simplifier_loop", tout << mk_ll_pp(s, m_manager) << "\n";);
|
||||
TRACE("simplifier_loop", tout << mk_ll_pp(s, m) << "\n";);
|
||||
reduce_core(s);
|
||||
get_cached(s, result, result_proof);
|
||||
if (result_proof != 0)
|
||||
SASSERT(is_rewrite_proof(s, result, result_proof));
|
||||
if (result_proof != 0) {
|
||||
m_proofs.push_back(result_proof);
|
||||
}
|
||||
old_s = s;
|
||||
s = result;
|
||||
}
|
||||
SASSERT(s != 0);
|
||||
r = s;
|
||||
p = m_proofs.empty() ? m_manager.mk_reflexivity(s) : m_manager.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
|
||||
p = m_proofs.empty() ? m.mk_reflexivity(s) : m.mk_transitivity(m_proofs.size(), m_proofs.c_ptr());
|
||||
SASSERT(is_rewrite_proof(s_orig, r, p));
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
|
@ -259,9 +264,9 @@ void simplifier::reduce1(expr * n) {
|
|||
specific simplifications via plugins.
|
||||
*/
|
||||
void simplifier::reduce1_app(app * n) {
|
||||
expr_ref r(m_manager);
|
||||
proof_ref p(m_manager);
|
||||
TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m_manager) << "\n";);
|
||||
expr_ref r(m);
|
||||
proof_ref p(m);
|
||||
TRACE("reduce", tout << "reducing...\n" << mk_pp(n, m) << "\n";);
|
||||
if (get_subst(n, r, p)) {
|
||||
TRACE("reduce", tout << "applying substitution...\n";);
|
||||
cache_result(n, r, p);
|
||||
|
@ -279,7 +284,7 @@ void simplifier::reduce1_app(app * n) {
|
|||
void simplifier::reduce1_app_core(app * n) {
|
||||
m_args.reset();
|
||||
func_decl * decl = n->get_decl();
|
||||
proof_ref p1(m_manager);
|
||||
proof_ref p1(m);
|
||||
// Stores the new arguments of n in m_args.
|
||||
// Let n be of the form
|
||||
// (decl arg_0 ... arg_{n-1})
|
||||
|
@ -296,23 +301,23 @@ void simplifier::reduce1_app_core(app * n) {
|
|||
// If none of the arguments have been simplified, and n is not a theory symbol,
|
||||
// Then no simplification is possible, and we can cache the result of the simplification of n as n.
|
||||
if (has_new_args || decl->get_family_id() != null_family_id) {
|
||||
expr_ref r(m_manager);
|
||||
TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m_manager););
|
||||
expr_ref r(m);
|
||||
TRACE("reduce", tout << "reduce1_app\n"; for(unsigned i = 0; i < m_args.size(); i++) tout << mk_ll_pp(m_args[i], m););
|
||||
// the method mk_app invokes get_subst and plugins to simplify
|
||||
// (decl arg_0' ... arg_{n-1}')
|
||||
mk_app(decl, m_args.size(), m_args.c_ptr(), r);
|
||||
if (!m_manager.fine_grain_proofs()) {
|
||||
if (!m.fine_grain_proofs()) {
|
||||
cache_result(n, r, 0);
|
||||
}
|
||||
else {
|
||||
expr * s = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
|
||||
expr * s = m.mk_app(decl, m_args.size(), m_args.c_ptr());
|
||||
proof * p;
|
||||
if (n == r)
|
||||
p = 0;
|
||||
else if (r != s)
|
||||
// we use a "theory rewrite generic proof" to justify the step
|
||||
// s = (decl arg_0' ... arg_{n-1}') --> r
|
||||
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(s, r));
|
||||
p = m.mk_transitivity(p1, m.mk_rewrite(s, r));
|
||||
else
|
||||
p = p1;
|
||||
cache_result(n, r, p);
|
||||
|
@ -354,11 +359,11 @@ bool is_ac_vector(app * n) {
|
|||
}
|
||||
|
||||
void simplifier::reduce1_ac_app_core(app * n) {
|
||||
app_ref n_c(m_manager);
|
||||
proof_ref p1(m_manager);
|
||||
app_ref n_c(m);
|
||||
proof_ref p1(m);
|
||||
mk_ac_congruent_term(n, n_c, p1);
|
||||
TRACE("ac", tout << "expr:\n" << mk_pp(n, m_manager) << "\ncongruent term:\n" << mk_pp(n_c, m_manager) << "\n";);
|
||||
expr_ref r(m_manager);
|
||||
TRACE("ac", tout << "expr:\n" << mk_pp(n, m) << "\ncongruent term:\n" << mk_pp(n_c, m) << "\n";);
|
||||
expr_ref r(m);
|
||||
func_decl * decl = n->get_decl();
|
||||
family_id fid = decl->get_family_id();
|
||||
plugin * p = get_plugin(fid);
|
||||
|
@ -376,7 +381,7 @@ void simplifier::reduce1_ac_app_core(app * n) {
|
|||
// done...
|
||||
}
|
||||
else {
|
||||
r = m_manager.mk_app(decl, m_args.size(), m_args.c_ptr());
|
||||
r = m.mk_app(decl, m_args.size(), m_args.c_ptr());
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -385,7 +390,7 @@ void simplifier::reduce1_ac_app_core(app * n) {
|
|||
get_ac_args(n_c, m_args, m_mults);
|
||||
TRACE("ac", tout << "AC args:\n";
|
||||
for (unsigned i = 0; i < m_args.size(); i++) {
|
||||
tout << mk_pp(m_args[i], m_manager) << " * " << m_mults[i] << "\n";
|
||||
tout << mk_pp(m_args[i], m) << " * " << m_mults[i] << "\n";
|
||||
});
|
||||
if (p != 0 && p->reduce(decl, m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), r)) {
|
||||
// done...
|
||||
|
@ -393,12 +398,12 @@ void simplifier::reduce1_ac_app_core(app * n) {
|
|||
else {
|
||||
ptr_buffer<expr> new_args;
|
||||
expand_args(m_args.size(), m_mults.c_ptr(), m_args.c_ptr(), new_args);
|
||||
r = m_manager.mk_app(decl, new_args.size(), new_args.c_ptr());
|
||||
r = m.mk_app(decl, new_args.size(), new_args.c_ptr());
|
||||
}
|
||||
}
|
||||
TRACE("ac", tout << "AC result:\n" << mk_pp(r, m_manager) << "\n";);
|
||||
TRACE("ac", tout << "AC result:\n" << mk_pp(r, m) << "\n";);
|
||||
|
||||
if (!m_manager.fine_grain_proofs()) {
|
||||
if (!m.fine_grain_proofs()) {
|
||||
cache_result(n, r, 0);
|
||||
}
|
||||
else {
|
||||
|
@ -406,7 +411,7 @@ void simplifier::reduce1_ac_app_core(app * n) {
|
|||
if (n == r.get())
|
||||
p = 0;
|
||||
else if (r.get() != n_c.get())
|
||||
p = m_manager.mk_transitivity(p1, m_manager.mk_rewrite(n_c, r));
|
||||
p = m.mk_transitivity(p1, m.mk_rewrite(n_c, r));
|
||||
else
|
||||
p = p1;
|
||||
cache_result(n, r, p);
|
||||
|
@ -416,8 +421,8 @@ void simplifier::reduce1_ac_app_core(app * n) {
|
|||
static unsigned g_rewrite_lemma_id = 0;
|
||||
|
||||
void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr * const * args, expr* result) {
|
||||
expr_ref arg(m_manager);
|
||||
arg = m_manager.mk_app(decl, num_args, args);
|
||||
expr_ref arg(m);
|
||||
arg = m.mk_app(decl, num_args, args);
|
||||
if (arg.get() != result) {
|
||||
char buffer[128];
|
||||
#ifdef _WINDOWS
|
||||
|
@ -425,11 +430,11 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr *
|
|||
#else
|
||||
sprintf(buffer, "rewrite_lemma_%d.smt", g_rewrite_lemma_id);
|
||||
#endif
|
||||
ast_smt_pp pp(m_manager);
|
||||
ast_smt_pp pp(m);
|
||||
pp.set_benchmark_name("rewrite_lemma");
|
||||
pp.set_status("unsat");
|
||||
expr_ref n(m_manager);
|
||||
n = m_manager.mk_not(m_manager.mk_eq(arg.get(), result));
|
||||
expr_ref n(m);
|
||||
n = m.mk_not(m.mk_eq(arg.get(), result));
|
||||
std::ofstream out(buffer);
|
||||
pp.display(out, n);
|
||||
out.close();
|
||||
|
@ -445,14 +450,14 @@ void simplifier::dump_rewrite_lemma(func_decl * decl, unsigned num_args, expr *
|
|||
*/
|
||||
void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args, expr_ref & result) {
|
||||
m_need_reset = true;
|
||||
if (m_manager.is_eq(decl)) {
|
||||
sort * s = m_manager.get_sort(args[0]);
|
||||
if (m.is_eq(decl)) {
|
||||
sort * s = m.get_sort(args[0]);
|
||||
plugin * p = get_plugin(s->get_family_id());
|
||||
if (p != 0 && p->reduce_eq(args[0], args[1], result))
|
||||
return;
|
||||
}
|
||||
else if (m_manager.is_distinct(decl)) {
|
||||
sort * s = m_manager.get_sort(args[0]);
|
||||
else if (m.is_distinct(decl)) {
|
||||
sort * s = m.get_sort(args[0]);
|
||||
plugin * p = get_plugin(s->get_family_id());
|
||||
if (p != 0 && p->reduce_distinct(num_args, args, result))
|
||||
return;
|
||||
|
@ -464,7 +469,7 @@ void simplifier::mk_app(func_decl * decl, unsigned num_args, expr * const * args
|
|||
//dump_rewrite_lemma(decl, num_args, args, result.get());
|
||||
return;
|
||||
}
|
||||
result = m_manager.mk_app(decl, num_args, args);
|
||||
result = m.mk_app(decl, num_args, args);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -484,7 +489,7 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
get_cached(arg, new_arg, arg_proof);
|
||||
|
||||
CTRACE("simplifier_bug", (arg != new_arg) != (arg_proof != 0),
|
||||
tout << mk_ll_pp(arg, m_manager) << "\n---->\n" << mk_ll_pp(new_arg, m_manager) << "\n";
|
||||
tout << mk_ll_pp(arg, m) << "\n---->\n" << mk_ll_pp(new_arg, m) << "\n";
|
||||
tout << "#" << arg->get_id() << " #" << new_arg->get_id() << "\n";
|
||||
tout << arg << " " << new_arg << "\n";);
|
||||
|
||||
|
@ -500,11 +505,11 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
args.push_back(new_arg);
|
||||
}
|
||||
if (has_new_args) {
|
||||
r = m_manager.mk_app(n->get_decl(), args.size(), args.c_ptr());
|
||||
r = m.mk_app(n->get_decl(), args.size(), args.c_ptr());
|
||||
if (m_use_oeq)
|
||||
p = m_manager.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr());
|
||||
p = m.mk_oeq_congruence(n, r, proofs.size(), proofs.c_ptr());
|
||||
else
|
||||
p = m_manager.mk_congruence(n, r, proofs.size(), proofs.c_ptr());
|
||||
p = m.mk_congruence(n, r, proofs.size(), proofs.c_ptr());
|
||||
}
|
||||
else {
|
||||
r = n;
|
||||
|
@ -523,8 +528,8 @@ void simplifier::mk_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
bool simplifier::get_args(app * n, ptr_vector<expr> & result, proof_ref & p) {
|
||||
bool has_new_args = false;
|
||||
unsigned num = n->get_num_args();
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
app_ref r(m_manager);
|
||||
if (m.fine_grain_proofs()) {
|
||||
app_ref r(m);
|
||||
mk_congruent_term(n, r, p);
|
||||
result.append(r->get_num_args(), r->get_args());
|
||||
SASSERT(n->get_num_args() == result.size());
|
||||
|
@ -582,7 +587,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
new_args.push_back(new_arg);
|
||||
if (arg != new_arg)
|
||||
has_new_arg = true;
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
if (m.fine_grain_proofs()) {
|
||||
proof * pr = 0;
|
||||
m_ac_pr_cache.find(to_app(arg), pr);
|
||||
if (pr != 0)
|
||||
|
@ -601,7 +606,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
new_args.push_back(new_arg);
|
||||
if (arg != new_arg)
|
||||
has_new_arg = true;
|
||||
if (m_manager.fine_grain_proofs() && pr != 0)
|
||||
if (m.fine_grain_proofs() && pr != 0)
|
||||
new_arg_prs.push_back(pr);
|
||||
}
|
||||
}
|
||||
|
@ -610,14 +615,14 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
todo.pop_back();
|
||||
if (!has_new_arg) {
|
||||
m_ac_cache.insert(curr, curr);
|
||||
if (m_manager.fine_grain_proofs())
|
||||
if (m.fine_grain_proofs())
|
||||
m_ac_pr_cache.insert(curr, 0);
|
||||
}
|
||||
else {
|
||||
app * new_curr = m_manager.mk_app(f, new_args.size(), new_args.c_ptr());
|
||||
app * new_curr = m.mk_app(f, new_args.size(), new_args.c_ptr());
|
||||
m_ac_cache.insert(curr, new_curr);
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
proof * p = m_manager.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
|
||||
if (m.fine_grain_proofs()) {
|
||||
proof * p = m.mk_congruence(curr, new_curr, new_arg_prs.size(), new_arg_prs.c_ptr());
|
||||
m_ac_pr_cache.insert(curr, p);
|
||||
}
|
||||
}
|
||||
|
@ -628,7 +633,7 @@ void simplifier::mk_ac_congruent_term(app * n, app_ref & r, proof_ref & p) {
|
|||
app * new_n = 0;
|
||||
m_ac_cache.find(n, new_n);
|
||||
r = new_n;
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
if (m.fine_grain_proofs()) {
|
||||
proof * new_pr = 0;
|
||||
m_ac_pr_cache.find(n, new_pr);
|
||||
p = new_pr;
|
||||
|
@ -719,7 +724,7 @@ void simplifier::get_ac_args(app * n, ptr_vector<expr> & args, vector<rational>
|
|||
SASSERT(!sorted_exprs.empty());
|
||||
SASSERT(sorted_exprs[sorted_exprs.size()-1] == n);
|
||||
|
||||
TRACE("ac", tout << mk_ll_pp(n, m_manager, true, false) << "#" << n->get_id() << "\nsorted expressions...\n";
|
||||
TRACE("ac", tout << mk_ll_pp(n, m, true, false) << "#" << n->get_id() << "\nsorted expressions...\n";
|
||||
for (unsigned i = 0; i < sorted_exprs.size(); i++) {
|
||||
tout << "#" << sorted_exprs[i]->get_id() << " ";
|
||||
}
|
||||
|
@ -754,10 +759,10 @@ void simplifier::get_ac_args(app * n, ptr_vector<expr> & args, vector<rational>
|
|||
void simplifier::reduce1_quantifier(quantifier * q) {
|
||||
expr * new_body;
|
||||
proof * new_body_pr;
|
||||
SASSERT(is_well_sorted(m_manager, q));
|
||||
SASSERT(is_well_sorted(m, q));
|
||||
get_cached(q->get_expr(), new_body, new_body_pr);
|
||||
|
||||
quantifier_ref q1(m_manager);
|
||||
quantifier_ref q1(m);
|
||||
proof * p1 = 0;
|
||||
|
||||
if (is_quantifier(new_body) &&
|
||||
|
@ -774,7 +779,7 @@ void simplifier::reduce1_quantifier(quantifier * q) {
|
|||
sorts.append(nested_q->get_num_decls(), nested_q->get_decl_sorts());
|
||||
names.append(nested_q->get_num_decls(), nested_q->get_decl_names());
|
||||
|
||||
q1 = m_manager.mk_quantifier(q->is_forall(),
|
||||
q1 = m.mk_quantifier(q->is_forall(),
|
||||
sorts.size(),
|
||||
sorts.c_ptr(),
|
||||
names.c_ptr(),
|
||||
|
@ -783,13 +788,13 @@ void simplifier::reduce1_quantifier(quantifier * q) {
|
|||
q->get_qid(),
|
||||
q->get_skid(),
|
||||
0, 0, 0, 0);
|
||||
SASSERT(is_well_sorted(m_manager, q1));
|
||||
SASSERT(is_well_sorted(m, q1));
|
||||
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
quantifier * q0 = m_manager.update_quantifier(q, new_body);
|
||||
proof * p0 = q == q0 ? 0 : m_manager.mk_quant_intro(q, q0, new_body_pr);
|
||||
p1 = m_manager.mk_pull_quant(q0, q1);
|
||||
p1 = m_manager.mk_transitivity(p0, p1);
|
||||
if (m.fine_grain_proofs()) {
|
||||
quantifier * q0 = m.update_quantifier(q, new_body);
|
||||
proof * p0 = q == q0 ? 0 : m.mk_quant_intro(q, q0, new_body_pr);
|
||||
p1 = m.mk_pull_quant(q0, q1);
|
||||
p1 = m.mk_transitivity(p0, p1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -802,7 +807,7 @@ void simplifier::reduce1_quantifier(quantifier * q) {
|
|||
unsigned num = q->get_num_patterns();
|
||||
for (unsigned i = 0; i < num; i++) {
|
||||
get_cached(q->get_pattern(i), new_pattern, new_pattern_pr);
|
||||
if (m_manager.is_pattern(new_pattern)) {
|
||||
if (m.is_pattern(new_pattern)) {
|
||||
new_patterns.push_back(new_pattern);
|
||||
}
|
||||
}
|
||||
|
@ -815,7 +820,7 @@ void simplifier::reduce1_quantifier(quantifier * q) {
|
|||
remove_duplicates(new_patterns);
|
||||
remove_duplicates(new_no_patterns);
|
||||
|
||||
q1 = m_manager.mk_quantifier(q->is_forall(),
|
||||
q1 = m.mk_quantifier(q->is_forall(),
|
||||
q->get_num_decls(),
|
||||
q->get_decl_sorts(),
|
||||
q->get_decl_names(),
|
||||
|
@ -827,26 +832,26 @@ void simplifier::reduce1_quantifier(quantifier * q) {
|
|||
new_patterns.c_ptr(),
|
||||
new_no_patterns.size(),
|
||||
new_no_patterns.c_ptr());
|
||||
SASSERT(is_well_sorted(m_manager, q1));
|
||||
SASSERT(is_well_sorted(m, q1));
|
||||
|
||||
TRACE("simplifier", tout << mk_pp(q, m_manager) << "\n" << mk_pp(q1, m_manager) << "\n";);
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
TRACE("simplifier", tout << mk_pp(q, m) << "\n" << mk_pp(q1, m) << "\n";);
|
||||
if (m.fine_grain_proofs()) {
|
||||
if (q != q1 && !new_body_pr) {
|
||||
new_body_pr = m_manager.mk_rewrite(q->get_expr(), new_body);
|
||||
new_body_pr = m.mk_rewrite(q->get_expr(), new_body);
|
||||
}
|
||||
p1 = q == q1 ? 0 : m_manager.mk_quant_intro(q, q1, new_body_pr);
|
||||
p1 = q == q1 ? 0 : m.mk_quant_intro(q, q1, new_body_pr);
|
||||
}
|
||||
}
|
||||
|
||||
expr_ref r(m_manager);
|
||||
elim_unused_vars(m_manager, q1, r);
|
||||
expr_ref r(m);
|
||||
elim_unused_vars(m, q1, r);
|
||||
|
||||
proof * pr = 0;
|
||||
if (m_manager.fine_grain_proofs()) {
|
||||
if (m.fine_grain_proofs()) {
|
||||
proof * p2 = 0;
|
||||
if (q1.get() != r.get())
|
||||
p2 = m_manager.mk_elim_unused_vars(q1, r);
|
||||
pr = m_manager.mk_transitivity(p1, p2);
|
||||
p2 = m.mk_elim_unused_vars(q1, r);
|
||||
pr = m.mk_transitivity(p1, p2);
|
||||
}
|
||||
|
||||
cache_result(q, r, pr);
|
||||
|
@ -892,7 +897,7 @@ bool subst_simplifier::get_subst(expr * n, expr_ref & r, proof_ref & p) {
|
|||
m_subst_map->get(n, _r, _p);
|
||||
r = _r;
|
||||
p = _p;
|
||||
if (m_manager.coarse_grain_proofs())
|
||||
if (m.coarse_grain_proofs())
|
||||
m_subst_proofs.push_back(p);
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -210,7 +210,7 @@ public:
|
|||
|
||||
plugin * get_plugin(family_id fid) const { return m_plugins.get_plugin(fid); }
|
||||
|
||||
ast_manager & get_manager() { return m_manager; }
|
||||
ast_manager & get_manager() { return m; }
|
||||
|
||||
void borrow_plugins(simplifier const & s);
|
||||
void release_plugins();
|
||||
|
|
|
@ -793,8 +793,10 @@ bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
|
|||
}
|
||||
else {
|
||||
TRACE("st_bug", tout << "found match:\n"; m_subst->display(tout); tout << "m_subst: " << m_subst << "\n";);
|
||||
if (!st(n->m_expr))
|
||||
if (!st(n->m_expr)) {
|
||||
clear_stack();
|
||||
return false;
|
||||
}
|
||||
if (!backtrack())
|
||||
break;
|
||||
}
|
||||
|
@ -806,12 +808,16 @@ bool substitution_tree::visit(expr * e, st_visitor & st, node * r) {
|
|||
else if (!backtrack())
|
||||
break;
|
||||
}
|
||||
clear_stack();
|
||||
return true;
|
||||
}
|
||||
|
||||
void substitution_tree::clear_stack() {
|
||||
while (!m_bstack.empty()) {
|
||||
m_subst->pop_scope();
|
||||
m_bstack.pop_back();
|
||||
}
|
||||
m_subst->pop_scope();
|
||||
return true;
|
||||
}
|
||||
|
||||
template<substitution_tree::st_visit_mode Mode>
|
||||
|
|
|
@ -123,6 +123,8 @@ class substitution_tree {
|
|||
template<st_visit_mode Mode>
|
||||
void visit(expr * e, st_visitor & st, unsigned in_offset, unsigned st_offset, unsigned reg_offset);
|
||||
|
||||
void clear_stack();
|
||||
|
||||
public:
|
||||
substitution_tree(ast_manager & m);
|
||||
~substitution_tree();
|
||||
|
|
|
@ -24,6 +24,7 @@ Notes:
|
|||
#include"array_decl_plugin.h"
|
||||
#include"datatype_decl_plugin.h"
|
||||
#include"seq_decl_plugin.h"
|
||||
#include"pb_decl_plugin.h"
|
||||
#include"fpa_decl_plugin.h"
|
||||
#include"ast_pp.h"
|
||||
#include"var_subst.h"
|
||||
|
@ -336,10 +337,10 @@ cmd_context::~cmd_context() {
|
|||
if (m_main_ctx) {
|
||||
set_verbose_stream(std::cerr);
|
||||
}
|
||||
reset(true);
|
||||
finalize_cmds();
|
||||
finalize_tactic_cmds();
|
||||
finalize_probes();
|
||||
reset(true);
|
||||
m_solver = 0;
|
||||
m_check_sat_result = 0;
|
||||
}
|
||||
|
@ -357,6 +358,18 @@ void cmd_context::set_cancel(bool f) {
|
|||
m().set_cancel(f);
|
||||
}
|
||||
|
||||
opt_wrapper* cmd_context::get_opt() {
|
||||
return m_opt.get();
|
||||
}
|
||||
|
||||
void cmd_context::set_opt(opt_wrapper* opt) {
|
||||
m_opt = opt;
|
||||
for (unsigned i = 0; i < m_scopes.size(); ++i) {
|
||||
m_opt->push();
|
||||
}
|
||||
m_opt->set_logic(m_logic);
|
||||
}
|
||||
|
||||
void cmd_context::global_params_updated() {
|
||||
m_params.updt_params();
|
||||
if (m_params.m_smtlib2_compliant)
|
||||
|
@ -560,6 +573,7 @@ bool cmd_context::logic_has_fpa() const {
|
|||
return !has_logic() || m_logic == "QF_FP" || m_logic == "QF_FPBV";
|
||||
}
|
||||
|
||||
|
||||
bool cmd_context::logic_has_array_core(symbol const & s) const {
|
||||
return
|
||||
s == "QF_AX" ||
|
||||
|
@ -601,6 +615,7 @@ void cmd_context::init_manager_core(bool new_manager) {
|
|||
register_plugin(symbol("array"), alloc(array_decl_plugin), logic_has_array());
|
||||
register_plugin(symbol("datatype"), alloc(datatype_decl_plugin), logic_has_datatype());
|
||||
register_plugin(symbol("seq"), alloc(seq_decl_plugin), logic_has_seq());
|
||||
register_plugin(symbol("pb"), alloc(pb_decl_plugin), !has_logic());
|
||||
register_plugin(symbol("fpa"), alloc(fpa_decl_plugin), logic_has_fpa());
|
||||
}
|
||||
else {
|
||||
|
@ -736,7 +751,7 @@ void cmd_context::insert(symbol const & s, func_decl * f) {
|
|||
if (!m_global_decls) {
|
||||
m_func_decls_stack.push_back(sf_pair(s, f));
|
||||
}
|
||||
TRACE("cmd_context", tout << "new sort decl\n" << mk_pp(f, m()) << "\n";);
|
||||
TRACE("cmd_context", tout << "new function decl\n" << mk_pp(f, m()) << "\n";);
|
||||
}
|
||||
|
||||
void cmd_context::insert(symbol const & s, psort_decl * p) {
|
||||
|
@ -1169,7 +1184,6 @@ void cmd_context::insert_aux_pdecl(pdecl * p) {
|
|||
}
|
||||
|
||||
void cmd_context::reset(bool finalize) {
|
||||
m_check_sat_result = 0;
|
||||
m_logic = symbol::null;
|
||||
m_check_sat_result = 0;
|
||||
m_numeral_as_real = false;
|
||||
|
@ -1186,6 +1200,7 @@ void cmd_context::reset(bool finalize) {
|
|||
if (m_solver)
|
||||
m_solver = 0;
|
||||
m_scopes.reset();
|
||||
m_opt = 0;
|
||||
m_pp_env = 0;
|
||||
m_dt_eh = 0;
|
||||
if (m_manager) {
|
||||
|
@ -1253,6 +1268,8 @@ void cmd_context::push() {
|
|||
s.m_assertions_lim = m_assertions.size();
|
||||
if (m_solver)
|
||||
m_solver->push();
|
||||
if (m_opt)
|
||||
m_opt->push();
|
||||
}
|
||||
|
||||
void cmd_context::push(unsigned n) {
|
||||
|
@ -1348,6 +1365,8 @@ void cmd_context::pop(unsigned n) {
|
|||
if (m_solver) {
|
||||
m_solver->pop(n);
|
||||
}
|
||||
if (m_opt)
|
||||
m_opt->pop(n);
|
||||
unsigned new_lvl = lvl - n;
|
||||
scope & s = m_scopes[new_lvl];
|
||||
restore_func_decls(s.m_func_decls_stack_lim);
|
||||
|
@ -1364,15 +1383,48 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions
|
|||
IF_VERBOSE(100, verbose_stream() << "(started \"check-sat\")" << std::endl;);
|
||||
TRACE("before_check_sat", dump_assertions(tout););
|
||||
init_manager();
|
||||
if (m_solver) {
|
||||
unsigned timeout = m_params.m_timeout;
|
||||
scoped_watch sw(*this);
|
||||
lbool r;
|
||||
|
||||
if (m_opt && !m_opt->empty()) {
|
||||
bool was_pareto = false;
|
||||
m_check_sat_result = get_opt();
|
||||
cancel_eh<opt_wrapper> eh(*get_opt());
|
||||
scoped_ctrl_c ctrlc(eh);
|
||||
scoped_timer timer(timeout, &eh);
|
||||
ptr_vector<expr> cnstr(m_assertions);
|
||||
cnstr.append(num_assumptions, assumptions);
|
||||
get_opt()->set_hard_constraints(cnstr);
|
||||
try {
|
||||
r = get_opt()->optimize();
|
||||
while (r == l_true && get_opt()->is_pareto()) {
|
||||
was_pareto = true;
|
||||
get_opt()->display_assignment(regular_stream());
|
||||
regular_stream() << "\n";
|
||||
r = get_opt()->optimize();
|
||||
}
|
||||
}
|
||||
catch (z3_error & ex) {
|
||||
throw ex;
|
||||
}
|
||||
catch (z3_exception & ex) {
|
||||
throw cmd_exception(ex.msg());
|
||||
}
|
||||
if (was_pareto && r == l_false) {
|
||||
r = l_true;
|
||||
}
|
||||
get_opt()->set_status(r);
|
||||
if (r != l_false && !was_pareto) {
|
||||
get_opt()->display_assignment(regular_stream());
|
||||
}
|
||||
}
|
||||
else if (m_solver) {
|
||||
m_check_sat_result = m_solver.get(); // solver itself stores the result.
|
||||
m_solver->set_progress_callback(this);
|
||||
unsigned timeout = m_params.m_timeout;
|
||||
scoped_watch sw(*this);
|
||||
cancel_eh<solver> eh(*m_solver);
|
||||
scoped_ctrl_c ctrlc(eh);
|
||||
scoped_timer timer(timeout, &eh);
|
||||
lbool r;
|
||||
try {
|
||||
r = m_solver->check_sat(num_assumptions, assumptions);
|
||||
}
|
||||
|
@ -1383,15 +1435,17 @@ void cmd_context::check_sat(unsigned num_assumptions, expr * const * assumptions
|
|||
throw cmd_exception(ex.msg());
|
||||
}
|
||||
m_solver->set_status(r);
|
||||
display_sat_result(r);
|
||||
validate_check_sat_result(r);
|
||||
if (r == l_true)
|
||||
validate_model();
|
||||
}
|
||||
else {
|
||||
// There is no solver installed in the command context.
|
||||
regular_stream() << "unknown" << std::endl;
|
||||
return;
|
||||
}
|
||||
display_sat_result(r);
|
||||
validate_check_sat_result(r);
|
||||
if (r == l_true)
|
||||
validate_model();
|
||||
|
||||
}
|
||||
|
||||
void cmd_context::display_sat_result(lbool r) {
|
||||
|
@ -1568,6 +1622,9 @@ void cmd_context::display_statistics(bool show_total_time, double total_time) {
|
|||
else if (m_solver) {
|
||||
m_solver->collect_statistics(st);
|
||||
}
|
||||
else if (m_opt) {
|
||||
m_opt->collect_statistics(st);
|
||||
}
|
||||
st.display_smt2(regular_stream());
|
||||
}
|
||||
|
||||
|
|
|
@ -111,6 +111,21 @@ struct builtin_decl {
|
|||
builtin_decl(family_id fid, decl_kind k, builtin_decl * n = 0):m_fid(fid), m_decl(k), m_next(n) {}
|
||||
};
|
||||
|
||||
class opt_wrapper : public check_sat_result {
|
||||
public:
|
||||
virtual bool empty() = 0;
|
||||
virtual void push() = 0;
|
||||
virtual void pop(unsigned n) = 0;
|
||||
virtual void set_cancel(bool f) = 0;
|
||||
virtual void reset_cancel() = 0;
|
||||
virtual void cancel() = 0;
|
||||
virtual lbool optimize() = 0;
|
||||
virtual void set_hard_constraints(ptr_vector<expr> & hard) = 0;
|
||||
virtual void display_assignment(std::ostream& out) = 0;
|
||||
virtual bool is_pareto() = 0;
|
||||
virtual void set_logic(symbol const& s) = 0;
|
||||
};
|
||||
|
||||
class cmd_context : public progress_callback, public tactic_manager, public ast_printer_context {
|
||||
public:
|
||||
enum status {
|
||||
|
@ -188,8 +203,9 @@ protected:
|
|||
svector<scope> m_scopes;
|
||||
scoped_ptr<solver_factory> m_solver_factory;
|
||||
scoped_ptr<solver_factory> m_interpolating_solver_factory;
|
||||
ref<solver> m_solver;
|
||||
ref<solver> m_solver;
|
||||
ref<check_sat_result> m_check_sat_result;
|
||||
ref<opt_wrapper> m_opt;
|
||||
|
||||
stopwatch m_watch;
|
||||
|
||||
|
@ -213,7 +229,6 @@ protected:
|
|||
|
||||
void register_builtin_sorts(decl_plugin * p);
|
||||
void register_builtin_ops(decl_plugin * p);
|
||||
void register_plugin(symbol const & name, decl_plugin * p, bool install_names);
|
||||
void load_plugin(symbol const & name, bool install_names, svector<family_id>& fids);
|
||||
void init_manager_core(bool new_manager);
|
||||
void init_manager();
|
||||
|
@ -258,6 +273,8 @@ public:
|
|||
context_params & params() { return m_params; }
|
||||
solver_factory &get_solver_factory() { return *m_solver_factory; }
|
||||
solver_factory &get_interpolating_solver_factory() { return *m_interpolating_solver_factory; }
|
||||
opt_wrapper* get_opt();
|
||||
void set_opt(opt_wrapper* o);
|
||||
void global_params_updated(); // this method should be invoked when global (and module) params are updated.
|
||||
bool set_logic(symbol const & s);
|
||||
bool has_logic() const { return m_logic != symbol::null; }
|
||||
|
@ -306,7 +323,8 @@ public:
|
|||
check_sat_result * get_check_sat_result() const { return m_check_sat_result.get(); }
|
||||
check_sat_state cs_state() const;
|
||||
void validate_model();
|
||||
|
||||
|
||||
void register_plugin(symbol const & name, decl_plugin * p, bool install_names);
|
||||
bool is_func_decl(symbol const & s) const;
|
||||
bool is_sort_decl(symbol const& s) const { return m_psort_decls.contains(s); }
|
||||
void insert(cmd * c);
|
||||
|
|
|
@ -93,13 +93,13 @@ void context_params::set(char const * param, char const * value) {
|
|||
set_bool(m_smtlib2_compliant, param, value);
|
||||
}
|
||||
else {
|
||||
param_descrs d;
|
||||
collect_param_descrs(d);
|
||||
std::stringstream strm;
|
||||
strm << "unknown parameter '" << p << "'\n";
|
||||
strm << "Legal parameters are:\n";
|
||||
d.display(strm, 2, false, false);
|
||||
throw default_exception(strm.str());
|
||||
param_descrs d;
|
||||
collect_param_descrs(d);
|
||||
std::stringstream strm;
|
||||
strm << "unknown parameter '" << p << "'\n";
|
||||
strm << "Legal parameters are:\n";
|
||||
d.display(strm, 2, false, false);
|
||||
throw default_exception(strm.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
0
src/duality/duality.h
Executable file → Normal file
0
src/duality/duality.h
Executable file → Normal file
0
src/duality/duality_solver.cpp
Executable file → Normal file
0
src/duality/duality_solver.cpp
Executable file → Normal file
1
src/duality/duality_wrapper.h
Executable file → Normal file
1
src/duality/duality_wrapper.h
Executable file → Normal file
|
@ -1489,4 +1489,3 @@ namespace std {
|
|||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
|
|
@ -44,6 +44,7 @@ namespace hash_space {
|
|||
|
||||
template <typename T> class hash {};
|
||||
|
||||
|
||||
template <>
|
||||
class hash<int> {
|
||||
public:
|
||||
|
|
200
src/math/simplex/network_flow.h
Normal file
200
src/math/simplex/network_flow.h
Normal file
|
@ -0,0 +1,200 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
network_flow.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Implements Network Simplex algorithm for min cost flow problem
|
||||
|
||||
Author:
|
||||
|
||||
Anh-Dung Phan (t-anphan) 2013-10-24
|
||||
|
||||
Notes:
|
||||
|
||||
This will be used to solve the dual of min cost flow problem
|
||||
i.e. optimization of difference constraint.
|
||||
|
||||
We need a function to reduce DL constraints to min cost flow problem
|
||||
and another function to convert from min cost flow solution to DL solution.
|
||||
|
||||
It remains unclear how to convert DL assignment to a basic feasible solution of Network Simplex.
|
||||
A naive approach is to run an algorithm on max flow in order to get a spanning tree.
|
||||
|
||||
--*/
|
||||
#ifndef _NETWORK_FLOW_H_
|
||||
#define _NETWORK_FLOW_H_
|
||||
|
||||
#include"inf_rational.h"
|
||||
#include"diff_logic.h"
|
||||
#include"spanning_tree.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
enum min_flow_result {
|
||||
// Min cost flow problem is infeasible.
|
||||
// Diff logic optimization could be unbounded or infeasible.
|
||||
INFEASIBLE,
|
||||
// Min cost flow and diff logic optimization are both optimal.
|
||||
OPTIMAL,
|
||||
// Min cost flow problem is unbounded.
|
||||
// Diff logic optimization has to be infeasible.
|
||||
UNBOUNDED,
|
||||
};
|
||||
|
||||
enum pivot_rule {
|
||||
// First eligible edge pivot rule
|
||||
// Edges are traversed in a wraparound fashion
|
||||
FIRST_ELIGIBLE,
|
||||
// Best eligible edge pivot rule
|
||||
// The best edge is selected in every iteration
|
||||
BEST_ELIGIBLE,
|
||||
// Candidate list pivot rule
|
||||
// Major iterations: candidate list is built from eligible edges (in a wraparound way)
|
||||
// Minor iterations: the best edge is selected from the list
|
||||
CANDIDATE_LIST
|
||||
};
|
||||
|
||||
// Solve minimum cost flow problem using Network Simplex algorithm
|
||||
template<typename Ext>
|
||||
class network_flow : private Ext {
|
||||
private:
|
||||
enum edge_state {
|
||||
LOWER = 1,
|
||||
BASIS = 0,
|
||||
};
|
||||
|
||||
typedef dl_var node;
|
||||
typedef dl_edge<Ext> edge;
|
||||
typedef dl_graph<Ext> graph;
|
||||
typedef typename Ext::numeral numeral;
|
||||
typedef typename Ext::fin_numeral fin_numeral;
|
||||
|
||||
class pivot_rule_impl {
|
||||
protected:
|
||||
graph & m_graph;
|
||||
svector<edge_state> & m_states;
|
||||
vector<numeral> & m_potentials;
|
||||
edge_id & m_enter_id;
|
||||
bool edge_in_tree(edge_id id) const { return m_states[id] == BASIS; }
|
||||
public:
|
||||
pivot_rule_impl(graph & g, vector<numeral> & potentials,
|
||||
svector<edge_state> & states, edge_id & enter_id)
|
||||
: m_graph(g),
|
||||
m_potentials(potentials),
|
||||
m_states(states),
|
||||
m_enter_id(enter_id) {
|
||||
}
|
||||
virtual ~pivot_rule_impl() {}
|
||||
virtual bool choose_entering_edge() = 0;
|
||||
virtual pivot_rule rule() const = 0;
|
||||
};
|
||||
|
||||
class first_eligible_pivot : public pivot_rule_impl {
|
||||
edge_id m_next_edge;
|
||||
public:
|
||||
first_eligible_pivot(graph & g, vector<numeral> & potentials,
|
||||
svector<edge_state> & states, edge_id & enter_id) :
|
||||
pivot_rule_impl(g, potentials, states, enter_id),
|
||||
m_next_edge(0) {
|
||||
}
|
||||
virtual bool choose_entering_edge();
|
||||
virtual pivot_rule rule() const { return FIRST_ELIGIBLE; }
|
||||
};
|
||||
|
||||
class best_eligible_pivot : public pivot_rule_impl {
|
||||
public:
|
||||
best_eligible_pivot(graph & g, vector<numeral> & potentials,
|
||||
svector<edge_state> & states, edge_id & enter_id) :
|
||||
pivot_rule_impl(g, potentials, states, enter_id) {
|
||||
}
|
||||
virtual pivot_rule rule() const { return BEST_ELIGIBLE; }
|
||||
virtual bool choose_entering_edge();
|
||||
};
|
||||
|
||||
class candidate_list_pivot : public pivot_rule_impl {
|
||||
private:
|
||||
edge_id m_next_edge;
|
||||
svector<edge_id> m_candidates;
|
||||
unsigned m_num_candidates;
|
||||
unsigned m_minor_step;
|
||||
unsigned m_current_length;
|
||||
static const unsigned NUM_CANDIDATES = 10;
|
||||
static const unsigned MINOR_STEP_LIMIT = 5;
|
||||
|
||||
public:
|
||||
candidate_list_pivot(graph & g, vector<numeral> & potentials,
|
||||
svector<edge_state> & states, edge_id & enter_id) :
|
||||
pivot_rule_impl(g, potentials, states, enter_id),
|
||||
m_next_edge(0),
|
||||
m_minor_step(0),
|
||||
m_current_length(0),
|
||||
m_num_candidates(NUM_CANDIDATES),
|
||||
m_candidates(m_num_candidates) {
|
||||
}
|
||||
|
||||
virtual pivot_rule rule() const { return CANDIDATE_LIST; }
|
||||
|
||||
virtual bool choose_entering_edge();
|
||||
};
|
||||
|
||||
graph m_graph;
|
||||
scoped_ptr<spanning_tree_base> m_tree;
|
||||
scoped_ptr<pivot_rule_impl> m_pivot;
|
||||
vector<fin_numeral> m_balances; // nodes + 1 |-> [b -1b] Denote supply/demand b_i on node i
|
||||
vector<numeral> m_potentials; // nodes + 1 |-> initial: +/- 1
|
||||
// Duals of flows which are convenient to compute dual solutions
|
||||
// become solutions to Dual simplex.
|
||||
vector<numeral> m_flows; // edges + nodes |-> assignemnt Basic feasible flows
|
||||
svector<edge_state> m_states;
|
||||
unsigned m_step;
|
||||
edge_id m_enter_id;
|
||||
edge_id m_leave_id;
|
||||
optional<numeral> m_delta;
|
||||
|
||||
// Initialize the network with a feasible spanning tree
|
||||
void initialize();
|
||||
|
||||
void update_potentials();
|
||||
|
||||
void update_flows();
|
||||
|
||||
bool choose_entering_edge(pivot_rule pr);
|
||||
|
||||
// Send as much flow as possible around the cycle, the first basic edge with flow 0 will leave
|
||||
// Return false if the problem is unbounded
|
||||
bool choose_leaving_edge();
|
||||
|
||||
void update_spanning_tree();
|
||||
|
||||
numeral get_cost() const;
|
||||
|
||||
bool edge_in_tree(edge_id id) const;
|
||||
|
||||
bool is_infeasible();
|
||||
bool check_well_formed();
|
||||
bool check_optimal();
|
||||
|
||||
void display_primal(std::ofstream & os);
|
||||
void display_dual(std::ofstream & os);
|
||||
void display_spanning_tree(std::ofstream & os);
|
||||
void display_system(std::ofstream & os);
|
||||
|
||||
public:
|
||||
|
||||
network_flow(graph & g, vector<fin_numeral> const & balances);
|
||||
|
||||
// Minimize cost flows
|
||||
// Return true if found an optimal solution, and return false if unbounded
|
||||
min_flow_result min_cost(pivot_rule pr = FIRST_ELIGIBLE);
|
||||
|
||||
// Compute the optimal solution
|
||||
numeral get_optimal_solution(vector<numeral> & result, bool is_dual);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
526
src/math/simplex/network_flow_def.h
Normal file
526
src/math/simplex/network_flow_def.h
Normal file
|
@ -0,0 +1,526 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
network_flow_def.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Implements Network Simplex algorithm for min cost flow problem
|
||||
|
||||
Author:
|
||||
|
||||
Anh-Dung Phan (t-anphan) 2013-10-24
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _NETWORK_FLOW_DEF_H_
|
||||
#define _NETWORK_FLOW_DEF_H_
|
||||
|
||||
#include"network_flow.h"
|
||||
#include"uint_set.h"
|
||||
#include"spanning_tree_def.h"
|
||||
|
||||
namespace smt {
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::first_eligible_pivot::choose_entering_edge() {
|
||||
numeral cost = numeral::zero();
|
||||
int num_edges = m_graph.get_num_edges();
|
||||
for (int i = m_next_edge; i < m_next_edge + num_edges; ++i) {
|
||||
edge_id id = i % num_edges;
|
||||
if (edge_in_tree(id)) {
|
||||
continue;
|
||||
}
|
||||
node src = m_graph.get_source(id);
|
||||
node tgt = m_graph.get_target(id);
|
||||
cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id);
|
||||
if (cost.is_pos()) {
|
||||
m_next_edge = m_enter_id = id;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::best_eligible_pivot::choose_entering_edge() {
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
numeral cost = numeral::zero();
|
||||
for (unsigned i = 0; i < num_edges; ++i) {
|
||||
node src = m_graph.get_source(i);
|
||||
node tgt = m_graph.get_target(i);
|
||||
if (!edge_in_tree(i)) {
|
||||
numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(i);
|
||||
if (new_cost > cost) {
|
||||
cost = new_cost;
|
||||
m_enter_id = i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cost.is_pos();
|
||||
};
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::candidate_list_pivot::choose_entering_edge() {
|
||||
numeral cost = numeral::zero();
|
||||
if (m_current_length == 0 || m_minor_step == MINOR_STEP_LIMIT) {
|
||||
// Build the candidate list
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
m_current_length = 0;
|
||||
for (unsigned i = m_next_edge; i < m_next_edge + num_edges; ++i) {
|
||||
edge_id id = (i >= num_edges) ? i - num_edges : i;
|
||||
node src = m_graph.get_source(id);
|
||||
node tgt = m_graph.get_target(id);
|
||||
if (!edge_in_tree(id)) {
|
||||
numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id);
|
||||
if (new_cost.is_pos()) {
|
||||
m_candidates[m_current_length] = id;
|
||||
++m_current_length;
|
||||
if (new_cost > cost) {
|
||||
cost = new_cost;
|
||||
m_enter_id = id;
|
||||
}
|
||||
}
|
||||
if (m_current_length >= m_num_candidates) break;
|
||||
}
|
||||
}
|
||||
m_next_edge = m_enter_id;
|
||||
m_minor_step = 1;
|
||||
return cost.is_pos();
|
||||
}
|
||||
|
||||
++m_minor_step;
|
||||
for (unsigned i = 0; i < m_current_length; ++i) {
|
||||
edge_id id = m_candidates[i];
|
||||
node src = m_graph.get_source(id);
|
||||
node tgt = m_graph.get_target(id);
|
||||
if (!edge_in_tree(id)) {
|
||||
numeral new_cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(id);
|
||||
if (new_cost > cost) {
|
||||
cost = new_cost;
|
||||
m_enter_id = id;
|
||||
}
|
||||
// Remove stale candidates
|
||||
if (!new_cost.is_pos()) {
|
||||
--m_current_length;
|
||||
m_candidates[i] = m_candidates[m_current_length];
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return cost.is_pos();
|
||||
};
|
||||
|
||||
template<typename Ext>
|
||||
network_flow<Ext>::network_flow(graph & g, vector<fin_numeral> const & balances) :
|
||||
m_balances(balances) {
|
||||
// Network flow graph has the edges in the reversed order compared to constraint graph
|
||||
// We only take enabled edges from the original graph
|
||||
for (unsigned i = 0; i < g.get_num_nodes(); ++i) {
|
||||
m_graph.init_var(i);
|
||||
}
|
||||
vector<edge> const & es = g.get_all_edges();
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
edge const & e = es[i];
|
||||
if (e.is_enabled()) {
|
||||
m_graph.add_edge(e.get_target(), e.get_source(), e.get_weight(), explanation());
|
||||
}
|
||||
}
|
||||
TRACE("network_flow", {
|
||||
tout << "Difference logic optimization:" << std::endl;
|
||||
display_dual(tout);
|
||||
tout << "Minimum cost flow:" << std::endl;
|
||||
display_primal(tout);
|
||||
};);
|
||||
|
||||
m_step = 0;
|
||||
m_tree = alloc(basic_spanning_tree<Ext>, m_graph);
|
||||
}
|
||||
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::initialize() {
|
||||
TRACE("network_flow", tout << "initialize...\n";);
|
||||
// Create an artificial root node to construct initial spanning tree
|
||||
unsigned num_nodes = m_graph.get_num_nodes();
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
|
||||
node root = num_nodes;
|
||||
m_graph.init_var(root);
|
||||
|
||||
m_potentials.resize(num_nodes + 1);
|
||||
m_potentials[root] = numeral::zero();
|
||||
|
||||
m_balances.resize(num_nodes + 1);
|
||||
fin_numeral sum_supply = fin_numeral::zero();
|
||||
for (unsigned i = 0; i < num_nodes; ++i) {
|
||||
sum_supply += m_balances[i];
|
||||
}
|
||||
m_balances[root] = -sum_supply;
|
||||
|
||||
m_flows.resize(num_nodes + num_edges);
|
||||
m_flows.fill(numeral::zero());
|
||||
m_states.resize(num_nodes + num_edges);
|
||||
m_states.fill(LOWER);
|
||||
|
||||
// Create artificial edges from/to root node to/from other nodes and initialize the spanning tree
|
||||
svector<edge_id> tree;
|
||||
for (unsigned i = 0; i < num_nodes; ++i) {
|
||||
bool is_forward = !m_balances[i].is_neg();
|
||||
m_states[num_edges + i] = BASIS;
|
||||
node src = is_forward ? i : root;
|
||||
node tgt = is_forward ? root : i;
|
||||
m_flows[num_edges + i] = is_forward ? m_balances[i] : -m_balances[i];
|
||||
m_potentials[i] = is_forward ? numeral::one() : -numeral::one();
|
||||
tree.push_back(m_graph.add_edge(src, tgt, numeral::one(), explanation()));
|
||||
}
|
||||
|
||||
m_tree->initialize(tree);
|
||||
|
||||
TRACE("network_flow",
|
||||
tout << pp_vector("Potentials", m_potentials);
|
||||
tout << pp_vector("Flows", m_flows);
|
||||
tout << "Cost: " << get_cost() << "\n";
|
||||
tout << "Spanning tree:\n";
|
||||
display_spanning_tree(tout);
|
||||
display_primal(tout););
|
||||
SASSERT(check_well_formed());
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::update_potentials() {
|
||||
node src = m_graph.get_source(m_enter_id);
|
||||
node tgt = m_graph.get_target(m_enter_id);
|
||||
numeral cost = m_potentials[src] - m_potentials[tgt] - m_graph.get_weight(m_enter_id);
|
||||
numeral change;
|
||||
node start;
|
||||
if (m_tree->in_subtree_t2(tgt)) {
|
||||
change = cost;
|
||||
start = tgt;
|
||||
}
|
||||
else {
|
||||
change = -cost;
|
||||
start = src;
|
||||
}
|
||||
SASSERT(m_tree->in_subtree_t2(start));
|
||||
TRACE("network_flow", tout << "update_potentials of T_" << start << " with change = " << change << "...\n";);
|
||||
svector<node> descendants;
|
||||
m_tree->get_descendants(start, descendants);
|
||||
SASSERT(descendants.size() >= 1);
|
||||
for (unsigned i = 0; i < descendants.size(); ++i) {
|
||||
node u = descendants[i];
|
||||
m_potentials[u] += change;
|
||||
}
|
||||
TRACE("network_flow", tout << pp_vector("Potentials", m_potentials););
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::update_flows() {
|
||||
m_flows[m_enter_id] += *m_delta;
|
||||
node src = m_graph.get_source(m_enter_id);
|
||||
node tgt = m_graph.get_target(m_enter_id);
|
||||
svector<edge_id> path;
|
||||
svector<bool> against;
|
||||
m_tree->get_path(src, tgt, path, against);
|
||||
SASSERT(path.size() >= 1);
|
||||
for (unsigned i = 0; i < path.size(); ++i) {
|
||||
edge_id e_id = path[i];
|
||||
m_flows[e_id] += against[i] ? - *m_delta : *m_delta;
|
||||
}
|
||||
TRACE("network_flow", tout << pp_vector("Flows", m_flows););
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::choose_leaving_edge() {
|
||||
node src = m_graph.get_source(m_enter_id);
|
||||
node tgt = m_graph.get_target(m_enter_id);
|
||||
m_delta.set_invalid();
|
||||
edge_id leave_id = null_edge_id;
|
||||
svector<edge_id> path;
|
||||
svector<bool> against;
|
||||
m_tree->get_path(src, tgt, path, against);
|
||||
SASSERT(path.size() >= 1);
|
||||
for (unsigned i = 0; i < path.size(); ++i) {
|
||||
edge_id e_id = path[i];
|
||||
if (against[i] && (!m_delta || m_flows[e_id] < *m_delta)) {
|
||||
m_delta = m_flows[e_id];
|
||||
leave_id = e_id;
|
||||
}
|
||||
}
|
||||
m_leave_id = leave_id;
|
||||
|
||||
return m_delta;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::update_spanning_tree() {
|
||||
m_tree->update(m_enter_id, m_leave_id);
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::choose_entering_edge(pivot_rule pr) {
|
||||
if (!m_pivot || pr != m_pivot->rule()) {
|
||||
switch (pr) {
|
||||
case FIRST_ELIGIBLE:
|
||||
m_pivot = alloc(first_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id);
|
||||
break;
|
||||
case BEST_ELIGIBLE:
|
||||
m_pivot = alloc(best_eligible_pivot, m_graph, m_potentials, m_states, m_enter_id);
|
||||
break;
|
||||
case CANDIDATE_LIST:
|
||||
m_pivot = alloc(candidate_list_pivot, m_graph, m_potentials, m_states, m_enter_id);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return m_pivot->choose_entering_edge();
|
||||
}
|
||||
|
||||
// Minimize cost flows
|
||||
template<typename Ext>
|
||||
min_flow_result network_flow<Ext>::min_cost(pivot_rule pr) {
|
||||
initialize();
|
||||
while (choose_entering_edge(pr)) {
|
||||
bool bounded = choose_leaving_edge();
|
||||
if (!bounded) return UNBOUNDED;
|
||||
vector<edge>const& es = m_graph.get_all_edges();
|
||||
TRACE("network_flow",
|
||||
{
|
||||
edge const& e_in = es[m_enter_id];
|
||||
edge const& e_out = es[m_leave_id];
|
||||
node src_in = e_in.get_source();
|
||||
node tgt_in = e_in.get_target();
|
||||
node src_out = e_out.get_source();
|
||||
node tgt_out = e_out.get_target();
|
||||
numeral c1 = m_potentials[src_in] - m_potentials[tgt_in] - m_graph.get_weight(m_enter_id);
|
||||
numeral c2 = m_potentials[src_out] - m_potentials[tgt_out] - m_graph.get_weight(m_leave_id);
|
||||
tout << "new base: y_" << src_in << "_" << tgt_in << " cost: " << c1 << " delta: " << *m_delta << "\n";
|
||||
tout << "old base: y_" << src_out << "_" << tgt_out << " cost: " << c2 << "\n";
|
||||
}
|
||||
);
|
||||
update_flows();
|
||||
if (m_enter_id != m_leave_id) {
|
||||
SASSERT(edge_in_tree(m_leave_id));
|
||||
SASSERT(!edge_in_tree(m_enter_id));
|
||||
m_states[m_enter_id] = BASIS;
|
||||
m_states[m_leave_id] = LOWER;
|
||||
update_spanning_tree();
|
||||
update_potentials();
|
||||
TRACE("network_flow",
|
||||
tout << "Spanning tree:\n";
|
||||
display_spanning_tree(tout);
|
||||
tout << "Cost: " << get_cost() << "\n";
|
||||
display_primal(tout);
|
||||
);
|
||||
SASSERT(check_well_formed());
|
||||
}
|
||||
}
|
||||
TRACE("network_flow",
|
||||
tout << "Spanning tree:\n";
|
||||
display_spanning_tree(tout);
|
||||
tout << "Cost: " << get_cost() << "\n";
|
||||
display_primal(tout);
|
||||
);
|
||||
if (is_infeasible()) return INFEASIBLE;
|
||||
TRACE("network_flow", tout << "Found optimal solution.\n";);
|
||||
SASSERT(check_optimal());
|
||||
return OPTIMAL;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::is_infeasible() {
|
||||
// Flows of artificial arcs should be zero
|
||||
unsigned num_nodes = m_graph.get_num_nodes();
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
SASSERT(m_flows.size() == num_edges);
|
||||
for (unsigned i = 1; i < num_nodes; ++i) {
|
||||
if (m_flows[num_edges - i].is_pos()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the optimal solution
|
||||
template<typename Ext>
|
||||
typename network_flow<Ext>::numeral network_flow<Ext>::get_optimal_solution(vector<numeral> & result, bool is_dual) {
|
||||
numeral objective_value = get_cost();
|
||||
result.reset();
|
||||
if (is_dual) {
|
||||
result.append(m_potentials);
|
||||
}
|
||||
else {
|
||||
result.append(m_flows);
|
||||
}
|
||||
return objective_value;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
typename network_flow<Ext>::numeral network_flow<Ext>::get_cost() const {
|
||||
numeral objective_value = numeral::zero();
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
for (unsigned i = 0; i < num_edges; ++i) {
|
||||
if (edge_in_tree(i)) {
|
||||
objective_value += m_flows[i].get_rational() * m_graph.get_weight(i);
|
||||
}
|
||||
}
|
||||
return objective_value;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::edge_in_tree(edge_id id) const {
|
||||
return m_states[id] == BASIS;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::check_well_formed() {
|
||||
SASSERT(m_tree->check_well_formed());
|
||||
SASSERT(!m_delta || !(*m_delta).is_neg());
|
||||
|
||||
// m_flows are zero on non-basic edges
|
||||
for (unsigned i = 0; i < m_flows.size(); ++i) {
|
||||
SASSERT(!m_flows[i].is_neg());
|
||||
SASSERT(edge_in_tree(i) || m_flows[i].is_zero());
|
||||
}
|
||||
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
for (unsigned i = 0; i < num_edges; ++i) {
|
||||
if (edge_in_tree(i)) {
|
||||
dl_var src = m_graph.get_source(i);
|
||||
dl_var tgt = m_graph.get_target(i);
|
||||
numeral weight = m_graph.get_weight(i);
|
||||
SASSERT(m_potentials[src] - m_potentials[tgt] == weight);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
bool network_flow<Ext>::check_optimal() {
|
||||
numeral total_cost = get_cost();
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
|
||||
for (unsigned i = 0; i < num_edges; ++i) {
|
||||
dl_var src = m_graph.get_source(i);
|
||||
dl_var tgt = m_graph.get_target(i);
|
||||
numeral weight = m_graph.get_weight(i);
|
||||
SASSERT(m_potentials[src] - m_potentials[tgt] <= weight);
|
||||
}
|
||||
|
||||
// m_flows are zero on non-basic edges
|
||||
for (unsigned i = 0; i < m_flows.size(); ++i) {
|
||||
SASSERT(edge_in_tree(i) || m_flows[i].is_zero());
|
||||
}
|
||||
numeral total_balance = numeral::zero();
|
||||
for (unsigned i = 0; i < m_potentials.size(); ++i) {
|
||||
total_balance += m_balances[i] * m_potentials[i];
|
||||
}
|
||||
TRACE("network_flow", tout << "Total balance: " << total_balance << ", total cost: " << total_cost << std::endl;);
|
||||
return total_cost == total_balance;
|
||||
}
|
||||
|
||||
// display methods
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::display_primal(std::ofstream & os) {
|
||||
vector<edge> const & es = m_graph.get_all_edges();
|
||||
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
|
||||
edge const & e = es[i];
|
||||
os << "(declare-fun y_" << e.get_source() << "_" << e.get_target() << " () Real)" << std::endl;
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
|
||||
edge const & e = es[i];
|
||||
os << "(assert (>= y_" << e.get_source() << "_" << e.get_target() << " 0))" << std::endl;
|
||||
};
|
||||
|
||||
for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) {
|
||||
bool initialized = false;
|
||||
for (unsigned j = 0; j < m_graph.get_num_edges(); ++j) {
|
||||
edge const & e = es[j];
|
||||
if (e.get_target() == i || e.get_source() == i) {
|
||||
if (!initialized) {
|
||||
os << "(assert (= (+";
|
||||
}
|
||||
initialized = true;
|
||||
if (e.get_target() == i) {
|
||||
os << " y_" << e.get_source() << "_" << e.get_target();
|
||||
}
|
||||
else {
|
||||
os << " (- y_" << e.get_source() << "_" << e.get_target() << ")";
|
||||
}
|
||||
}
|
||||
}
|
||||
if(initialized) {
|
||||
os << " " << m_balances[i] << ") 0))" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
os << "(minimize (+";
|
||||
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
|
||||
edge const & e = es[i];
|
||||
os << " (* " << e.get_weight() << " y_" << e.get_source() << "_" << e.get_target() << ")";
|
||||
};
|
||||
os << "))" << std::endl;
|
||||
os << "(optimize)" << std::endl;
|
||||
if (!m_flows.empty()) {
|
||||
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
|
||||
edge const & e = es[i];
|
||||
os << "; y_" << e.get_source() << "_" << e.get_target() << " = " << m_flows[i] << "\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::display_dual(std::ofstream & os) {
|
||||
for (unsigned i = 0; i < m_graph.get_num_nodes(); ++i) {
|
||||
os << "(declare-fun v" << i << " () Real)" << std::endl;
|
||||
}
|
||||
vector<edge> const & es = m_graph.get_all_edges();
|
||||
for (unsigned i = 0; i < m_graph.get_num_edges(); ++i) {
|
||||
edge const & e = es[i];
|
||||
os << "(assert (<= (- v" << e.get_source() << " v" << e.get_target() << ") " << e.get_weight() << "))" << std::endl;
|
||||
};
|
||||
os << "(assert (= v0 0))" << std::endl;
|
||||
os << "(maximize (+";
|
||||
for (unsigned i = 0; i < m_balances.size(); ++i) {
|
||||
os << " (* " << m_balances[i] << " v" << i << ")";
|
||||
};
|
||||
os << "))" << std::endl;
|
||||
os << "(optimize)" << std::endl;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void network_flow<Ext>::display_spanning_tree(std::ofstream & os) {
|
||||
++m_step;;
|
||||
std::string prefix = "T";
|
||||
prefix.append(std::to_string(m_step));
|
||||
prefix.append("_");
|
||||
unsigned root = m_graph.get_num_nodes() - 1;
|
||||
for (unsigned i = 0; i < root; ++i) {
|
||||
os << prefix << i << "[shape=circle,label=\"" << prefix << i << " [";
|
||||
os << m_potentials[i] << "/" << m_balances[i] << "]\"];\n";
|
||||
}
|
||||
os << prefix << root << "[shape=doublecircle,label=\"" << prefix << root << " [";
|
||||
os << m_potentials[root] << "/" << m_balances[root] << "]\"];\n";
|
||||
|
||||
unsigned num_edges = m_graph.get_num_edges();
|
||||
for (unsigned i = 0; i < num_edges; ++i) {
|
||||
os << prefix << m_graph.get_source(i) << " -> " << prefix << m_graph.get_target(i);
|
||||
if (edge_in_tree(i)) {
|
||||
os << "[color=red,penwidth=3.0,label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n";
|
||||
}
|
||||
else {
|
||||
os << "[label=\"" << m_flows[i] << "/" << m_graph.get_weight(i) << "\"];\n";
|
||||
}
|
||||
}
|
||||
os << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
26
src/math/simplex/simplex.cpp
Normal file
26
src/math/simplex/simplex.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*++
|
||||
Copyright (c) 2014 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
simplex.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Multi-precision simplex tableau.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2014-01-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#include"simplex.h"
|
||||
#include"sparse_matrix_def.h"
|
||||
#include"simplex_def.h"
|
||||
namespace simplex {
|
||||
template class simplex<mpz_ext>;
|
||||
template class simplex<mpq_ext>;
|
||||
};
|
202
src/math/simplex/simplex.h
Normal file
202
src/math/simplex/simplex.h
Normal file
|
@ -0,0 +1,202 @@
|
|||
/*++
|
||||
Copyright (c) 2014 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
simplex.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Multi-precision simplex tableau.
|
||||
|
||||
- It uses code from theory_arith where applicable.
|
||||
|
||||
- It is detached from the theory class and ASTs.
|
||||
|
||||
- It uses non-shared mpz/mpq's avoiding global locks and operations on rationals.
|
||||
|
||||
- It follows the same sparse tableau layout (no LU yet).
|
||||
|
||||
- It does not include features for non-linear arithmetic.
|
||||
|
||||
- Branch/bound/cuts is external.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2014-01-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SIMPLEX_H_
|
||||
#define _SIMPLEX_H_
|
||||
|
||||
#include "sparse_matrix.h"
|
||||
#include "mpq_inf.h"
|
||||
#include "heap.h"
|
||||
#include "lbool.h"
|
||||
#include "uint_set.h"
|
||||
|
||||
namespace simplex {
|
||||
|
||||
template<typename Ext>
|
||||
class simplex {
|
||||
|
||||
typedef unsigned var_t;
|
||||
typedef typename Ext::eps_numeral eps_numeral;
|
||||
typedef typename Ext::numeral numeral;
|
||||
typedef typename Ext::manager manager;
|
||||
typedef typename Ext::eps_manager eps_manager;
|
||||
typedef typename Ext::scoped_numeral scoped_numeral;
|
||||
typedef _scoped_numeral<eps_manager> scoped_eps_numeral;
|
||||
typedef _scoped_numeral_vector<eps_manager> scoped_eps_numeral_vector;
|
||||
typedef sparse_matrix<Ext> matrix;
|
||||
struct var_lt {
|
||||
bool operator()(var_t v1, var_t v2) const { return v1 < v2; }
|
||||
};
|
||||
typedef heap<var_lt> var_heap;
|
||||
|
||||
struct stats {
|
||||
unsigned m_num_pivots;
|
||||
unsigned m_num_infeasible;
|
||||
unsigned m_num_checks;
|
||||
stats() { reset(); }
|
||||
void reset() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
enum pivot_strategy_t {
|
||||
S_BLAND,
|
||||
S_GREATEST_ERROR,
|
||||
S_LEAST_ERROR,
|
||||
S_DEFAULT
|
||||
};
|
||||
|
||||
struct var_info {
|
||||
unsigned m_base2row:29;
|
||||
unsigned m_is_base:1;
|
||||
unsigned m_lower_valid:1;
|
||||
unsigned m_upper_valid:1;
|
||||
eps_numeral m_value;
|
||||
eps_numeral m_lower;
|
||||
eps_numeral m_upper;
|
||||
numeral m_base_coeff;
|
||||
var_info():
|
||||
m_base2row(0),
|
||||
m_is_base(false),
|
||||
m_lower_valid(false),
|
||||
m_upper_valid(false)
|
||||
{}
|
||||
};
|
||||
|
||||
static const var_t null_var;
|
||||
mutable manager m;
|
||||
mutable eps_manager em;
|
||||
mutable matrix M;
|
||||
unsigned m_max_iterations;
|
||||
volatile bool m_cancel;
|
||||
var_heap m_to_patch;
|
||||
vector<var_info> m_vars;
|
||||
svector<var_t> m_row2base;
|
||||
bool m_bland;
|
||||
unsigned m_blands_rule_threshold;
|
||||
random_gen m_random;
|
||||
uint_set m_left_basis;
|
||||
unsigned m_infeasible_var;
|
||||
unsigned_vector m_base_vars;
|
||||
stats m_stats;
|
||||
|
||||
public:
|
||||
simplex():
|
||||
M(m),
|
||||
m_max_iterations(UINT_MAX),
|
||||
m_cancel(false),
|
||||
m_to_patch(1024),
|
||||
m_bland(false),
|
||||
m_blands_rule_threshold(1000) {}
|
||||
|
||||
typedef typename matrix::row row;
|
||||
typedef typename matrix::row_iterator row_iterator;
|
||||
typedef typename matrix::col_iterator col_iterator;
|
||||
|
||||
void ensure_var(var_t v);
|
||||
row add_row(var_t base, unsigned num_vars, var_t const* vars, numeral const* coeffs);
|
||||
row get_infeasible_row();
|
||||
var_t get_base_var(row const& r) const { return m_row2base[r.id()]; }
|
||||
numeral const& get_base_coeff(row const& r) const { return m_vars[m_row2base[r.id()]].m_base_coeff; }
|
||||
void del_row(var_t base_var);
|
||||
void set_lower(var_t var, eps_numeral const& b);
|
||||
void set_upper(var_t var, eps_numeral const& b);
|
||||
void get_lower(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_lower; }
|
||||
void get_upper(var_t var, scoped_eps_numeral& b) const { b = m_vars[var].m_upper; }
|
||||
bool above_lower(var_t var, eps_numeral const& b) const;
|
||||
bool below_upper(var_t var, eps_numeral const& b) const;
|
||||
bool below_lower(var_t v) const;
|
||||
bool above_upper(var_t v) const;
|
||||
bool lower_valid(var_t var) const { return m_vars[var].m_lower_valid; }
|
||||
bool upper_valid(var_t var) const { return m_vars[var].m_upper_valid; }
|
||||
void unset_lower(var_t var);
|
||||
void unset_upper(var_t var);
|
||||
void set_value(var_t var, eps_numeral const& b);
|
||||
void set_cancel(bool f) { m_cancel = f; }
|
||||
void set_max_iterations(unsigned n) { m_max_iterations = n; }
|
||||
void reset();
|
||||
lbool make_feasible();
|
||||
lbool minimize(var_t var);
|
||||
eps_numeral const& get_value(var_t v);
|
||||
void display(std::ostream& out) const;
|
||||
void display_row(std::ostream& out, row const& r, bool values = true);
|
||||
|
||||
unsigned get_num_vars() const { return m_vars.size(); }
|
||||
|
||||
row_iterator row_begin(row const& r) { return M.row_begin(r); }
|
||||
row_iterator row_end(row const& r) { return M.row_end(r); }
|
||||
|
||||
void collect_statistics(::statistics & st) const;
|
||||
|
||||
private:
|
||||
|
||||
void del_row(row const& r);
|
||||
var_t select_var_to_fix();
|
||||
pivot_strategy_t pivot_strategy();
|
||||
var_t select_smallest_var() { return m_to_patch.empty()?null_var:m_to_patch.erase_min(); }
|
||||
var_t select_error_var(bool least);
|
||||
void check_blands_rule(var_t v, unsigned& num_repeated);
|
||||
bool make_var_feasible(var_t x_i);
|
||||
void update_and_pivot(var_t x_i, var_t x_j, numeral const& a_ij, eps_numeral const& new_value);
|
||||
void update_value(var_t v, eps_numeral const& delta);
|
||||
void update_value_core(var_t v, eps_numeral const& delta);
|
||||
void pivot(var_t x_i, var_t x_j, numeral const& a_ij);
|
||||
void move_to_bound(var_t x, bool to_lower);
|
||||
var_t select_pivot(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
|
||||
var_t select_pivot_blands(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
|
||||
var_t select_pivot_core(var_t x_i, bool is_below, scoped_numeral& out_a_ij);
|
||||
int get_num_non_free_dep_vars(var_t x_j, int best_so_far);
|
||||
|
||||
var_t pick_var_to_leave(var_t x_j, bool is_pos,
|
||||
scoped_eps_numeral& gain, scoped_numeral& new_a_ij, bool& inc);
|
||||
|
||||
|
||||
void select_pivot_primal(var_t v, var_t& x_i, var_t& x_j, scoped_numeral& a_ij, bool& inc_x_i, bool& inc_x_j);
|
||||
|
||||
|
||||
bool at_lower(var_t v) const;
|
||||
bool at_upper(var_t v) const;
|
||||
bool above_lower(var_t v) const;
|
||||
bool below_upper(var_t v) const;
|
||||
bool outside_bounds(var_t v) const { return below_lower(v) || above_upper(v); }
|
||||
bool is_free(var_t v) const { return !m_vars[v].m_lower_valid && !m_vars[v].m_upper_valid; }
|
||||
bool is_non_free(var_t v) const { return !is_free(v); }
|
||||
bool is_base(var_t x) const { return m_vars[x].m_is_base; }
|
||||
void add_patch(var_t v);
|
||||
|
||||
bool well_formed() const;
|
||||
bool well_formed_row(row const& r) const;
|
||||
bool is_feasible() const;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
1032
src/math/simplex/simplex_def.h
Normal file
1032
src/math/simplex/simplex_def.h
Normal file
File diff suppressed because it is too large
Load diff
275
src/math/simplex/sparse_matrix.h
Normal file
275
src/math/simplex/sparse_matrix.h
Normal file
|
@ -0,0 +1,275 @@
|
|||
/*++
|
||||
Copyright (c) 2014 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sparse_matrix.h
|
||||
|
||||
Abstract:
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2014-01-15
|
||||
|
||||
Notes:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPARSE_MATRIX_H_
|
||||
#define _SPARSE_MATRIX_H_
|
||||
|
||||
#include "mpq_inf.h"
|
||||
#include "statistics.h"
|
||||
|
||||
namespace simplex {
|
||||
|
||||
template<typename Ext>
|
||||
class sparse_matrix {
|
||||
public:
|
||||
typedef typename Ext::numeral numeral;
|
||||
typedef typename Ext::scoped_numeral scoped_numeral;
|
||||
typedef typename Ext::manager manager;
|
||||
typedef unsigned var_t;
|
||||
|
||||
struct row_entry {
|
||||
numeral m_coeff;
|
||||
var_t m_var;
|
||||
row_entry(numeral const& c, var_t v): m_coeff(c), m_var(v) {}
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
struct stats {
|
||||
unsigned m_add_rows;
|
||||
stats() { reset(); }
|
||||
void reset() {
|
||||
memset(this, 0, sizeof(*this));
|
||||
}
|
||||
};
|
||||
|
||||
static const int dead_id = -1;
|
||||
|
||||
/**
|
||||
\brief A row_entry is: m_var*m_coeff
|
||||
|
||||
m_col_idx points to the place in the
|
||||
column where the variable occurs.
|
||||
*/
|
||||
struct _row_entry : public row_entry {
|
||||
union {
|
||||
int m_col_idx;
|
||||
int m_next_free_row_entry_idx;
|
||||
};
|
||||
_row_entry(numeral const & c, var_t v): row_entry(c, v), m_col_idx(0) {}
|
||||
_row_entry() : row_entry(numeral(), dead_id), m_col_idx(0) {}
|
||||
bool is_dead() const { return row_entry::m_var == dead_id; }
|
||||
};
|
||||
|
||||
/**
|
||||
\brief A column entry points to the row and the row_entry within the row
|
||||
that has a non-zero coefficient on the variable associated
|
||||
with the column entry.
|
||||
*/
|
||||
struct col_entry {
|
||||
int m_row_id;
|
||||
union {
|
||||
int m_row_idx;
|
||||
int m_next_free_col_entry_idx;
|
||||
};
|
||||
col_entry(int r, int i): m_row_id(r), m_row_idx(i) {}
|
||||
col_entry(): m_row_id(0), m_row_idx(0) {}
|
||||
bool is_dead() const { return m_row_id == dead_id; }
|
||||
};
|
||||
|
||||
struct column;
|
||||
|
||||
/**
|
||||
\brief A row contains a base variable and set of
|
||||
row_entries. The base variable must occur in the set of
|
||||
row_entries with coefficient 1.
|
||||
*/
|
||||
struct _row {
|
||||
vector<_row_entry> m_entries;
|
||||
unsigned m_size; // the real size, m_entries contains dead row_entries.
|
||||
int m_first_free_idx; // first available position.
|
||||
_row();
|
||||
unsigned size() const { return m_size; }
|
||||
unsigned num_entries() const { return m_entries.size(); }
|
||||
void reset(manager& m);
|
||||
_row_entry & add_row_entry(unsigned & pos_idx);
|
||||
void del_row_entry(unsigned idx);
|
||||
void compress(manager& m, vector<column> & cols);
|
||||
void compress_if_needed(manager& _m, vector<column> & cols);
|
||||
void save_var_pos(svector<int> & result_map, unsigned_vector& idxs) const;
|
||||
//bool is_coeff_of(var_t v, numeral const & expected) const;
|
||||
int get_idx_of(var_t v) const;
|
||||
};
|
||||
|
||||
/**
|
||||
\brief A column stores in which rows a variable occurs.
|
||||
The column may have free/dead entries. The field m_first_free_idx
|
||||
is a reference to the first free/dead entry.
|
||||
*/
|
||||
struct column {
|
||||
svector<col_entry> m_entries;
|
||||
unsigned m_size;
|
||||
int m_first_free_idx;
|
||||
mutable unsigned m_refs;
|
||||
|
||||
column():m_size(0), m_first_free_idx(-1), m_refs(0) {}
|
||||
unsigned size() const { return m_size; }
|
||||
unsigned num_entries() const { return m_entries.size(); }
|
||||
void reset();
|
||||
void compress(vector<_row> & rows);
|
||||
void compress_if_needed(vector<_row> & rows);
|
||||
//void compress_singleton(vector<_row> & rows, unsigned singleton_pos);
|
||||
col_entry const * get_first_col_entry() const;
|
||||
col_entry & add_col_entry(int & pos_idx);
|
||||
void del_col_entry(unsigned idx);
|
||||
};
|
||||
|
||||
manager& m;
|
||||
vector<_row> m_rows;
|
||||
svector<unsigned> m_dead_rows; // rows to recycle
|
||||
vector<column> m_columns; // per var
|
||||
svector<int> m_var_pos; // temporary map from variables to positions in row
|
||||
unsigned_vector m_var_pos_idx; // indices in m_var_pos
|
||||
stats m_stats;
|
||||
|
||||
bool well_formed_row(unsigned row_id) const;
|
||||
bool well_formed_column(unsigned column_id) const;
|
||||
void del_row_entry(_row& r, unsigned pos);
|
||||
|
||||
public:
|
||||
|
||||
sparse_matrix(manager& _m): m(_m) {}
|
||||
~sparse_matrix();
|
||||
void reset();
|
||||
|
||||
class row {
|
||||
unsigned m_id;
|
||||
public:
|
||||
explicit row(unsigned r):m_id(r) {}
|
||||
row():m_id(UINT_MAX) {}
|
||||
bool operator!=(row const& other) const {
|
||||
return m_id != other.m_id;
|
||||
}
|
||||
unsigned id() const { return m_id; }
|
||||
};
|
||||
|
||||
void ensure_var(var_t v);
|
||||
|
||||
row mk_row();
|
||||
void add_var(row r, numeral const& n, var_t var);
|
||||
void add(row r, numeral const& n, row src);
|
||||
void mul(row r, numeral const& n);
|
||||
void neg(row r);
|
||||
void del(row r);
|
||||
|
||||
void gcd_normalize(row const& r, scoped_numeral& g);
|
||||
|
||||
class row_iterator {
|
||||
friend class sparse_matrix;
|
||||
unsigned m_curr;
|
||||
_row & m_row;
|
||||
void move_to_used() {
|
||||
while (m_curr < m_row.num_entries() &&
|
||||
m_row.m_entries[m_curr].is_dead()) {
|
||||
++m_curr;
|
||||
}
|
||||
}
|
||||
row_iterator(_row & r, bool begin):
|
||||
m_curr(0), m_row(r) {
|
||||
if (begin) {
|
||||
move_to_used();
|
||||
}
|
||||
else {
|
||||
m_curr = m_row.num_entries();
|
||||
}
|
||||
}
|
||||
public:
|
||||
row_entry & operator*() const { return m_row.m_entries[m_curr]; }
|
||||
row_entry * operator->() const { return &(operator*()); }
|
||||
row_iterator & operator++() { ++m_curr; move_to_used(); return *this; }
|
||||
row_iterator operator++(int) { row_iterator tmp = *this; ++*this; return tmp; }
|
||||
bool operator==(row_iterator const & it) const { return m_curr == it.m_curr; }
|
||||
bool operator!=(row_iterator const & it) const { return m_curr != it.m_curr; }
|
||||
};
|
||||
|
||||
row_iterator row_begin(row const& r) { return row_iterator(m_rows[r.id()], true); }
|
||||
row_iterator row_end(row const& r) { return row_iterator(m_rows[r.id()], false); }
|
||||
|
||||
unsigned column_size(var_t v) const { return m_columns[v].size(); }
|
||||
|
||||
class col_iterator {
|
||||
friend class sparse_matrix;
|
||||
unsigned m_curr;
|
||||
column const& m_col;
|
||||
vector<_row> const& m_rows;
|
||||
void move_to_used() {
|
||||
while (m_curr < m_col.num_entries() && m_col.m_entries[m_curr].is_dead()) {
|
||||
++m_curr;
|
||||
}
|
||||
}
|
||||
col_iterator(column const& c, vector<_row> const& r, bool begin):
|
||||
m_curr(0), m_col(c), m_rows(r) {
|
||||
++m_col.m_refs;
|
||||
if (begin) {
|
||||
move_to_used();
|
||||
}
|
||||
else {
|
||||
m_curr = m_col.num_entries();
|
||||
}
|
||||
}
|
||||
public:
|
||||
~col_iterator() {
|
||||
--m_col.m_refs;
|
||||
}
|
||||
|
||||
row get_row() {
|
||||
return row(m_col.m_entries[m_curr].m_row_id);
|
||||
}
|
||||
row_entry const& get_row_entry() {
|
||||
col_entry const& c = m_col.m_entries[m_curr];
|
||||
int row_id = c.m_row_id;
|
||||
return m_rows[row_id].m_entries[c.m_row_idx];
|
||||
}
|
||||
|
||||
col_iterator & operator++() { ++m_curr; move_to_used(); return *this; }
|
||||
col_iterator operator++(int) { col_iterator tmp = *this; ++*this; return tmp; }
|
||||
bool operator==(col_iterator const & it) const { return m_curr == it.m_curr; }
|
||||
bool operator!=(col_iterator const & it) const { return m_curr != it.m_curr; }
|
||||
};
|
||||
|
||||
col_iterator col_begin(int v) const { return col_iterator(m_columns[v], m_rows, true); }
|
||||
col_iterator col_end(int v) const { return col_iterator(m_columns[v], m_rows, false); }
|
||||
|
||||
void display(std::ostream& out);
|
||||
void display_row(std::ostream& out, row const& r);
|
||||
bool well_formed() const;
|
||||
|
||||
void collect_statistics(::statistics & st) const;
|
||||
|
||||
};
|
||||
|
||||
struct mpz_ext {
|
||||
typedef mpz numeral;
|
||||
typedef scoped_mpz scoped_numeral;
|
||||
typedef unsynch_mpz_manager manager;
|
||||
typedef mpq_inf eps_numeral;
|
||||
typedef unsynch_mpq_inf_manager eps_manager;
|
||||
};
|
||||
|
||||
struct mpq_ext {
|
||||
typedef mpq numeral;
|
||||
typedef scoped_mpq scoped_numeral;
|
||||
typedef unsynch_mpq_manager manager;
|
||||
typedef mpq_inf eps_numeral;
|
||||
typedef unsynch_mpq_inf_manager eps_manager;
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
|
||||
#endif
|
594
src/math/simplex/sparse_matrix_def.h
Normal file
594
src/math/simplex/sparse_matrix_def.h
Normal file
|
@ -0,0 +1,594 @@
|
|||
/*++
|
||||
Copyright (c) 2014 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
sparse_matrix_def.h
|
||||
|
||||
Abstract:
|
||||
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 2014-01-15
|
||||
|
||||
Notes:
|
||||
|
||||
mainly hoisted from theory_arith.h and theory_arith_aux.h
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _SPARSE_MATRIX_DEF_H_
|
||||
#define _SPARSE_MATRIX_DEF_H_
|
||||
|
||||
#include "sparse_matrix.h"
|
||||
#include "uint_set.h"
|
||||
|
||||
namespace simplex {
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Rows
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
template<typename Ext>
|
||||
sparse_matrix<Ext>::_row::_row():
|
||||
m_size(0),
|
||||
m_first_free_idx(-1) {
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::_row::reset(manager& m) {
|
||||
for (unsigned i = 0; i < m_entries.size(); ++i) {
|
||||
m.reset(m_entries[i].m_coeff);
|
||||
}
|
||||
m_entries.reset();
|
||||
m_size = 0;
|
||||
m_first_free_idx = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Add a new row_entry. The result is a reference to the new row_entry.
|
||||
The position of the new row_entry in the
|
||||
row is stored in pos_idx.
|
||||
*/
|
||||
template<typename Ext>
|
||||
typename sparse_matrix<Ext>::_row_entry &
|
||||
sparse_matrix<Ext>::_row::add_row_entry(unsigned & pos_idx) {
|
||||
m_size++;
|
||||
if (m_first_free_idx == -1) {
|
||||
pos_idx = m_entries.size();
|
||||
m_entries.push_back(_row_entry());
|
||||
return m_entries.back();
|
||||
}
|
||||
else {
|
||||
SASSERT(m_first_free_idx >= 0);
|
||||
pos_idx = static_cast<unsigned>(m_first_free_idx);
|
||||
_row_entry & result = m_entries[pos_idx];
|
||||
SASSERT(result.is_dead());
|
||||
m_first_free_idx = result.m_next_free_row_entry_idx;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Delete row_entry at position idx.
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::_row::del_row_entry(unsigned idx) {
|
||||
_row_entry & t = m_entries[idx];
|
||||
SASSERT(!t.is_dead());
|
||||
t.m_next_free_row_entry_idx = m_first_free_idx;
|
||||
t.m_var = dead_id;
|
||||
m_size--;
|
||||
m_first_free_idx = idx;
|
||||
SASSERT(t.is_dead());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Remove holes (i.e., dead entries) from the row.
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::_row::compress(manager& m, vector<column> & cols) {
|
||||
unsigned i = 0;
|
||||
unsigned j = 0;
|
||||
unsigned sz = m_entries.size();
|
||||
for (; i < sz; i++) {
|
||||
_row_entry & t1 = m_entries[i];
|
||||
if (!t1.is_dead()) {
|
||||
if (i != j) {
|
||||
_row_entry & t2 = m_entries[j];
|
||||
t2.m_coeff.swap(t1.m_coeff);
|
||||
t2.m_var = t1.m_var;
|
||||
t2.m_col_idx = t1.m_col_idx;
|
||||
SASSERT(!t2.is_dead());
|
||||
column & col = cols[t2.m_var];
|
||||
col.m_entries[t2.m_col_idx].m_row_idx = j;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
SASSERT(j == m_size);
|
||||
//
|
||||
// alternative: update the free-list to point to the
|
||||
// tail and avoid shrinking.
|
||||
// if m.does not allocate memory (for wrapper around
|
||||
// double), also bypass this step.
|
||||
//
|
||||
for (unsigned i = m_size; i < m_entries.size(); ++i) {
|
||||
m.reset(m_entries[i].m_coeff);
|
||||
}
|
||||
m_entries.shrink(m_size);
|
||||
m_first_free_idx = -1;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::_row::compress_if_needed(manager& m, vector<column> & cols) {
|
||||
if (size() *2 < num_entries()) {
|
||||
compress(m, cols);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Fill the map var -> pos/idx
|
||||
*/
|
||||
template<typename Ext>
|
||||
inline void sparse_matrix<Ext>::_row::save_var_pos(svector<int> & result_map, unsigned_vector& idxs) const {
|
||||
typename vector<_row_entry>::const_iterator it = m_entries.begin();
|
||||
typename vector<_row_entry>::const_iterator end = m_entries.end();
|
||||
unsigned idx = 0;
|
||||
for (; it != end; ++it, ++idx) {
|
||||
if (!it->is_dead()) {
|
||||
result_map[it->m_var] = idx;
|
||||
idxs.push_back(it->m_var);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template<typename Ext>
|
||||
int sparse_matrix<Ext>::_row::get_idx_of(var_t v) const {
|
||||
typename vector<_row_entry>::const_iterator it = m_entries.begin();
|
||||
typename vector<_row_entry>::const_iterator end = m_entries.end();
|
||||
for (unsigned idx = 0; it != end; ++it, ++idx) {
|
||||
if (!it->is_dead() && it->m_var == v)
|
||||
return idx;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Columns
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::column::reset() {
|
||||
m_entries.reset();
|
||||
m_size = 0;
|
||||
m_first_free_idx = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Remove holes (i.e., dead entries) from the column.
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::column::compress(vector<_row> & rows) {
|
||||
unsigned i = 0;
|
||||
unsigned j = 0;
|
||||
unsigned sz = m_entries.size();
|
||||
for (; i < sz; i++) {
|
||||
col_entry & e1 = m_entries[i];
|
||||
if (!e1.is_dead()) {
|
||||
if (i != j) {
|
||||
m_entries[j] = e1;
|
||||
_row & r = rows[e1.m_row_id];
|
||||
r.m_entries[e1.m_row_idx].m_col_idx = j;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
SASSERT(j == m_size);
|
||||
m_entries.shrink(m_size);
|
||||
m_first_free_idx = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Invoke compress if the column contains too many holes (i.e., dead entries).
|
||||
*/
|
||||
template<typename Ext>
|
||||
inline void sparse_matrix<Ext>::column::compress_if_needed(vector<_row> & rows) {
|
||||
if (size() * 2 < num_entries() && m_refs == 0) {
|
||||
compress(rows);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
/**
|
||||
\brief Special version of compress, that is used when the column contain
|
||||
only one entry located at position singleton_pos.
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::column::compress_singleton(vector<_row> & rows, unsigned singleton_pos) {
|
||||
SASSERT(m_size == 1);
|
||||
if (singleton_pos != 0) {
|
||||
col_entry & s = m_entries[singleton_pos];
|
||||
m_entries[0] = s;
|
||||
row & r = rows[s.m_row_id];
|
||||
r[s.m_row_idx].m_col_idx = 0;
|
||||
}
|
||||
m_first_free_idx = -1;
|
||||
m_entries.shrink(1);
|
||||
}
|
||||
#endif
|
||||
template<typename Ext>
|
||||
const typename sparse_matrix<Ext>::col_entry *
|
||||
sparse_matrix<Ext>::column::get_first_col_entry() const {
|
||||
typename svector<col_entry>::const_iterator it = m_entries.begin();
|
||||
typename svector<col_entry>::const_iterator end = m_entries.end();
|
||||
for (; it != end; ++it) {
|
||||
if (!it->is_dead()) {
|
||||
return it;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
typename sparse_matrix<Ext>::col_entry &
|
||||
sparse_matrix<Ext>::column::add_col_entry(int & pos_idx) {
|
||||
m_size++;
|
||||
if (m_first_free_idx == -1) {
|
||||
pos_idx = m_entries.size();
|
||||
m_entries.push_back(col_entry());
|
||||
return m_entries.back();
|
||||
}
|
||||
else {
|
||||
pos_idx = m_first_free_idx;
|
||||
col_entry & result = m_entries[pos_idx];
|
||||
SASSERT(result.is_dead());
|
||||
m_first_free_idx = result.m_next_free_col_entry_idx;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::column::del_col_entry(unsigned idx) {
|
||||
col_entry & c = m_entries[idx];
|
||||
SASSERT(!c.is_dead());
|
||||
c.m_row_id = dead_id;
|
||||
c.m_next_free_col_entry_idx = m_first_free_idx;
|
||||
m_first_free_idx = idx;
|
||||
m_size--;
|
||||
}
|
||||
|
||||
// -----------------------------------
|
||||
//
|
||||
// Matrix
|
||||
//
|
||||
// -----------------------------------
|
||||
|
||||
template<typename Ext>
|
||||
sparse_matrix<Ext>::~sparse_matrix() {
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i) {
|
||||
_row& r = m_rows[i];
|
||||
for (unsigned j = 0; j < r.m_entries.size(); ++j) {
|
||||
m.reset(r.m_entries[j].m_coeff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::reset() {
|
||||
m_rows.reset();
|
||||
m_dead_rows.reset();
|
||||
m_columns.reset();
|
||||
m_var_pos.reset();
|
||||
m_var_pos_idx.reset();
|
||||
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::ensure_var(var_t v) {
|
||||
while (m_columns.size() <= v) {
|
||||
m_columns.push_back(column());
|
||||
m_var_pos.push_back(-1);
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
typename sparse_matrix<Ext>::row
|
||||
sparse_matrix<Ext>::mk_row() {
|
||||
if (m_dead_rows.empty()) {
|
||||
row r(m_rows.size());
|
||||
m_rows.push_back(_row());
|
||||
return r;
|
||||
}
|
||||
else {
|
||||
row r(m_dead_rows.back());
|
||||
m_dead_rows.pop_back();
|
||||
return r;
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::add_var(row dst, numeral const& n, var_t v) {
|
||||
_row& r = m_rows[dst.id()];
|
||||
column& c = m_columns[v];
|
||||
unsigned r_idx;
|
||||
int c_idx;
|
||||
_row_entry & r_entry = r.add_row_entry(r_idx);
|
||||
col_entry& c_entry = c.add_col_entry(c_idx);
|
||||
r_entry.m_var = v;
|
||||
m.set(r_entry.m_coeff, n);
|
||||
r_entry.m_col_idx = c_idx;
|
||||
c_entry.m_row_id = dst.id();
|
||||
c_entry.m_row_idx = r_idx;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Set row1 <- row1 + row2 * n
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::add(row row1, numeral const& n, row row2) {
|
||||
m_stats.m_add_rows++;
|
||||
_row & r1 = m_rows[row1.id()];
|
||||
|
||||
r1.save_var_pos(m_var_pos, m_var_pos_idx);
|
||||
|
||||
//
|
||||
// loop over variables in row2,
|
||||
// add terms in row2 to row1.
|
||||
//
|
||||
|
||||
#define ADD_ROW(_SET_COEFF_, _ADD_COEFF_) \
|
||||
row_iterator it = row_begin(row2); \
|
||||
row_iterator end = row_end(row2); \
|
||||
for (; it != end; ++it) { \
|
||||
var_t v = it->m_var; \
|
||||
int pos = m_var_pos[v]; \
|
||||
if (pos == -1) { \
|
||||
/* variable v is not in row1 */ \
|
||||
unsigned row_idx; \
|
||||
_row_entry & r_entry = r1.add_row_entry(row_idx); \
|
||||
r_entry.m_var = v; \
|
||||
m.set(r_entry.m_coeff, it->m_coeff); \
|
||||
_SET_COEFF_; \
|
||||
column & c = m_columns[v]; \
|
||||
int col_idx; \
|
||||
col_entry & c_entry = c.add_col_entry(col_idx); \
|
||||
r_entry.m_col_idx = col_idx; \
|
||||
c_entry.m_row_id = row1.id(); \
|
||||
c_entry.m_row_idx = row_idx; \
|
||||
} \
|
||||
else { \
|
||||
/* variable v is in row1 */ \
|
||||
_row_entry & r_entry = r1.m_entries[pos]; \
|
||||
SASSERT(r_entry.m_var == v); \
|
||||
_ADD_COEFF_; \
|
||||
if (m.is_zero(r_entry.m_coeff)) { \
|
||||
del_row_entry(r1, pos); \
|
||||
} \
|
||||
} \
|
||||
} \
|
||||
((void) 0)
|
||||
|
||||
if (m.is_one(n)) {
|
||||
ADD_ROW({},
|
||||
m.add(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff));
|
||||
}
|
||||
else if (m.is_minus_one(n)) {
|
||||
ADD_ROW(m.neg(r_entry.m_coeff),
|
||||
m.sub(r_entry.m_coeff, it->m_coeff, r_entry.m_coeff));
|
||||
}
|
||||
else {
|
||||
scoped_numeral tmp(m);
|
||||
ADD_ROW(m.mul(r_entry.m_coeff, n, r_entry.m_coeff),
|
||||
m.mul(it->m_coeff, n, tmp);
|
||||
m.add(r_entry.m_coeff, tmp, r_entry.m_coeff));
|
||||
}
|
||||
|
||||
// reset m_var_pos:
|
||||
for (unsigned i = 0; i < m_var_pos_idx.size(); ++i) {
|
||||
m_var_pos[m_var_pos_idx[i]] = -1;
|
||||
}
|
||||
m_var_pos_idx.reset();
|
||||
r1.compress_if_needed(m, m_columns);
|
||||
}
|
||||
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::del_row_entry(_row& r, unsigned pos) {
|
||||
_row_entry & r_entry = r.m_entries[pos];
|
||||
var_t v = r_entry.m_var;
|
||||
int col_idx = r_entry.m_col_idx;
|
||||
r.del_row_entry(pos);
|
||||
column & c = m_columns[v];
|
||||
c.del_col_entry(col_idx);
|
||||
c.compress_if_needed(m_rows);
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Set row <- -row
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::neg(row r) {
|
||||
row_iterator it = row_begin(r);
|
||||
row_iterator end = row_end(r);
|
||||
for (; it != end; ++it) {
|
||||
m.neg(it->m_coeff);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Set row <- n*row
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::mul(row r, numeral const& n) {
|
||||
SASSERT(!m.is_zero(n));
|
||||
if (m.is_one(n)) {
|
||||
// no op
|
||||
}
|
||||
else if (m.is_minus_one(n)) {
|
||||
neg(r);
|
||||
}
|
||||
else {
|
||||
row_iterator it = row_begin(r);
|
||||
row_iterator end = row_end(r);
|
||||
for (; it != end; ++it) {
|
||||
m.mul(it->m_coeff, n, it->m_coeff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief Delete row.
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::del(row r) {
|
||||
_row& rw = m_rows[r.id()];
|
||||
for (unsigned i = 0; i < rw.m_entries.size(); ++i) {
|
||||
_row_entry& e = rw.m_entries[i];
|
||||
if (!e.is_dead()) {
|
||||
del_row_entry(rw, i);
|
||||
}
|
||||
}
|
||||
SASSERT(rw.m_size == 0);
|
||||
m_dead_rows.push_back(r.id());
|
||||
}
|
||||
|
||||
/**
|
||||
\brief normalize coefficients by dividing with they coefficients.
|
||||
return the gcd.
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::gcd_normalize(row const& r, scoped_numeral& g) {
|
||||
g.reset();
|
||||
row_iterator it = row_begin(r), end = row_end(r);
|
||||
for (; it != end && !m.is_one(g); ++it) {
|
||||
if (m.is_zero(g)) g = it->m_coeff;
|
||||
else m.gcd(g, it->m_coeff, g);
|
||||
}
|
||||
if (m.is_zero(g)) {
|
||||
g = numeral(1);
|
||||
}
|
||||
if (!m.is_one(g)) {
|
||||
row_iterator it2 = row_begin(r);
|
||||
for (; it2 != end; ++it2) {
|
||||
m.div(it2->m_coeff, g, it2->m_coeff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
\brief well_formed check
|
||||
*/
|
||||
template<typename Ext>
|
||||
bool sparse_matrix<Ext>::well_formed() const {
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i) {
|
||||
well_formed_row(i);
|
||||
}
|
||||
for (unsigned i = 0; i < m_columns.size(); ++i) {
|
||||
well_formed_column(i);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief well_formed row check
|
||||
*/
|
||||
template<typename Ext>
|
||||
bool sparse_matrix<Ext>::well_formed_row(unsigned row_id) const {
|
||||
uint_set vars, dead;
|
||||
_row const& r = m_rows[row_id];
|
||||
for (unsigned i = 0; i < r.num_entries(); ++i) {
|
||||
_row_entry const& e = r.m_entries[i];
|
||||
if (e.is_dead()) {
|
||||
dead.insert(i);
|
||||
continue;
|
||||
}
|
||||
SASSERT(!vars.contains(e.m_var));
|
||||
SASSERT(!m.is_zero(e.m_coeff));
|
||||
SASSERT(e.m_var != dead_id);
|
||||
col_entry const& c = m_columns[e.m_var].m_entries[e.m_col_idx];
|
||||
SASSERT(c.m_row_id == row_id);
|
||||
SASSERT(c.m_row_idx == i);
|
||||
vars.insert(e.m_var);
|
||||
}
|
||||
int idx = r.m_first_free_idx;
|
||||
while (idx != -1) {
|
||||
SASSERT(dead.contains(idx));
|
||||
dead.remove(idx);
|
||||
idx = r.m_entries[idx].m_next_free_row_entry_idx;
|
||||
}
|
||||
SASSERT(dead.empty());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief well_formed column check
|
||||
*/
|
||||
template<typename Ext>
|
||||
bool sparse_matrix<Ext>::well_formed_column(var_t v) const {
|
||||
uint_set rows, dead;
|
||||
column const& col = m_columns[v];
|
||||
for (unsigned i = 0; i < col.num_entries(); ++i) {
|
||||
col_entry const& c = col.m_entries[i];
|
||||
if (c.is_dead()) {
|
||||
dead.insert(i);
|
||||
continue;
|
||||
}
|
||||
SASSERT(!rows.contains(c.m_row_id));
|
||||
_row const& row = m_rows[c.m_row_id];
|
||||
_row_entry const& r = row.m_entries[c.m_row_idx];
|
||||
SASSERT(r.m_var == v);
|
||||
SASSERT(r.m_col_idx == i);
|
||||
rows.insert(c.m_row_id);
|
||||
}
|
||||
int idx = col.m_first_free_idx;
|
||||
while (idx != -1) {
|
||||
SASSERT(dead.contains(idx));
|
||||
dead.remove(idx);
|
||||
idx = col.m_entries[idx].m_next_free_col_entry_idx;
|
||||
}
|
||||
SASSERT(dead.empty());
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
\brief statistics
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::collect_statistics(::statistics & st) const {
|
||||
st.update("simplex add rows", m_stats.m_add_rows);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
\brief display method
|
||||
*/
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::display(std::ostream& out) {
|
||||
for (unsigned i = 0; i < m_rows.size(); ++i) {
|
||||
if (m_rows[i].size() == 0) continue;
|
||||
display_row(out, row(i));
|
||||
}
|
||||
}
|
||||
|
||||
template<typename Ext>
|
||||
void sparse_matrix<Ext>::display_row(std::ostream& out, row const& r) {
|
||||
row_iterator it = row_begin(r), end = row_end(r);
|
||||
for (; it != end; ++it) {
|
||||
m.display(out, it->m_coeff);
|
||||
out << "*v" << it->m_var << " ";
|
||||
}
|
||||
out << "\n";
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -159,11 +159,17 @@ void func_interp::insert_entry(expr * const * args, expr * r) {
|
|||
void func_interp::insert_new_entry(expr * const * args, expr * r) {
|
||||
reset_interp_cache();
|
||||
CTRACE("func_interp_bug", get_entry(args) != 0,
|
||||
tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n";
|
||||
tout << "Args:";
|
||||
for (unsigned i = 0; i < m_arity; i++) {
|
||||
tout << mk_ismt2_pp(get_entry(args)->get_arg(i), m_manager) << "\n";
|
||||
}
|
||||
tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";
|
||||
tout << "Args:";
|
||||
for (unsigned i = 0; i < m_arity; i++) {
|
||||
tout << mk_ismt2_pp(args[i], m_manager) << "\n";
|
||||
}
|
||||
tout << "Old: " << mk_ismt2_pp(get_entry(args)->m_result, m_manager) << "\n";
|
||||
tout << "New: " << mk_ismt2_pp(r, m_manager) << "\n";);
|
||||
);
|
||||
SASSERT(get_entry(args) == 0);
|
||||
func_entry * new_entry = func_entry::mk(m_manager, m_arity, args, r);
|
||||
if (!new_entry->args_are_values())
|
||||
|
|
|
@ -23,6 +23,7 @@ Revision History:
|
|||
#include"bool_rewriter.h"
|
||||
#include"arith_rewriter.h"
|
||||
#include"bv_rewriter.h"
|
||||
#include"pb_rewriter.h"
|
||||
#include"datatype_rewriter.h"
|
||||
#include"array_rewriter.h"
|
||||
#include"fpa_rewriter.h"
|
||||
|
@ -36,6 +37,7 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
bv_rewriter m_bv_rw;
|
||||
array_rewriter m_ar_rw;
|
||||
datatype_rewriter m_dt_rw;
|
||||
pb_rewriter m_pb_rw;
|
||||
fpa_rewriter m_f_rw;
|
||||
unsigned long long m_max_memory;
|
||||
unsigned m_max_steps;
|
||||
|
@ -52,6 +54,7 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
// See comment above. We want to allow customers to set :sort-store
|
||||
m_ar_rw(m, p),
|
||||
m_dt_rw(m),
|
||||
m_pb_rw(m),
|
||||
m_f_rw(m) {
|
||||
m_b_rw.set_flat(false);
|
||||
m_a_rw.set_flat(false);
|
||||
|
@ -153,6 +156,8 @@ struct evaluator_cfg : public default_rewriter_cfg {
|
|||
return m_ar_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_dt_rw.get_fid())
|
||||
return m_dt_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_pb_rw.get_fid())
|
||||
return m_pb_rw.mk_app_core(f, num, args, result);
|
||||
if (fid == m_f_rw.get_fid())
|
||||
return m_f_rw.mk_app_core(f, num, args, result);
|
||||
return BR_FAILED;
|
||||
|
|
917
src/model/model_implicant.cpp
Normal file
917
src/model/model_implicant.cpp
Normal file
|
@ -0,0 +1,917 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
model_implicant.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Facility to extract prime implicant from model.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
|
||||
Revision History:
|
||||
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include <sstream>
|
||||
#include "array_decl_plugin.h"
|
||||
#include "ast_pp.h"
|
||||
#include "bool_rewriter.h"
|
||||
#include "for_each_expr.h"
|
||||
#include "model.h"
|
||||
#include "ref_vector.h"
|
||||
#include "rewriter.h"
|
||||
#include "rewriter_def.h"
|
||||
#include "util.h"
|
||||
#include "model_implicant.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "expr_replacer.h"
|
||||
#include "model_smt2_pp.h"
|
||||
#include "poly_rewriter.h"
|
||||
#include "poly_rewriter_def.h"
|
||||
#include "arith_rewriter.h"
|
||||
#include "scoped_proof.h"
|
||||
|
||||
|
||||
|
||||
/////////////////////////
|
||||
// model_implicant
|
||||
//
|
||||
|
||||
|
||||
void model_implicant::assign_value(expr* e, expr* val) {
|
||||
rational r;
|
||||
if (m.is_true(val)) {
|
||||
set_true(e);
|
||||
}
|
||||
else if (m.is_false(val)) {
|
||||
set_false(e);
|
||||
}
|
||||
else if (m_arith.is_numeral(val, r)) {
|
||||
set_number(e, r);
|
||||
}
|
||||
else if (m.is_value(val)) {
|
||||
set_value(e, val);
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(3, verbose_stream() << "Not evaluated " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";);
|
||||
TRACE("pdr", tout << "Variable is not tracked: " << mk_pp(e, m) << " := " << mk_pp(val, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
|
||||
void model_implicant::setup_model(model_ref& model) {
|
||||
m_numbers.reset();
|
||||
m_values.reset();
|
||||
m_model = model;
|
||||
rational r;
|
||||
unsigned sz = model->get_num_constants();
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
func_decl * d = model->get_constant(i);
|
||||
expr* val = model->get_const_interp(d);
|
||||
expr* e = m.mk_const(d);
|
||||
m_refs.push_back(e);
|
||||
assign_value(e, val);
|
||||
}
|
||||
}
|
||||
|
||||
void model_implicant::reset() {
|
||||
m1.reset();
|
||||
m2.reset();
|
||||
m_values.reset();
|
||||
m_visited.reset();
|
||||
m_numbers.reset();
|
||||
m_refs.reset();
|
||||
m_model = 0;
|
||||
}
|
||||
|
||||
expr_ref_vector model_implicant::minimize_model(ptr_vector<expr> const & formulas, model_ref& mdl) {
|
||||
setup_model(mdl);
|
||||
|
||||
TRACE("pdr_verbose",
|
||||
tout << "formulas:\n";
|
||||
for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n";
|
||||
);
|
||||
|
||||
expr_ref_vector model = prune_by_cone_of_influence(formulas);
|
||||
TRACE("pdr_verbose",
|
||||
tout << "pruned model:\n";
|
||||
for (unsigned i = 0; i < model.size(); ++i) tout << mk_pp(model[i].get(), m) << "\n";);
|
||||
|
||||
reset();
|
||||
|
||||
DEBUG_CODE(
|
||||
setup_model(mdl);
|
||||
VERIFY(check_model(formulas));
|
||||
reset(););
|
||||
|
||||
return model;
|
||||
}
|
||||
|
||||
expr_ref_vector model_implicant::minimize_literals(ptr_vector<expr> const& formulas, model_ref& mdl) {
|
||||
|
||||
TRACE("pdr",
|
||||
tout << "formulas:\n";
|
||||
for (unsigned i = 0; i < formulas.size(); ++i) tout << mk_pp(formulas[i], m) << "\n";
|
||||
);
|
||||
|
||||
expr_ref_vector result(m);
|
||||
expr_ref tmp(m);
|
||||
ptr_vector<expr> tocollect;
|
||||
|
||||
setup_model(mdl);
|
||||
collect(formulas, tocollect);
|
||||
for (unsigned i = 0; i < tocollect.size(); ++i) {
|
||||
expr* e = tocollect[i];
|
||||
expr* e1, *e2;
|
||||
SASSERT(m.is_bool(e));
|
||||
SASSERT(is_true(e) || is_false(e));
|
||||
if (is_true(e)) {
|
||||
result.push_back(e);
|
||||
}
|
||||
// hack to break disequalities for arithmetic variables.
|
||||
else if (m.is_eq(e, e1, e2) && m_arith.is_int_real(e1)) {
|
||||
if (get_number(e1) < get_number(e2)) {
|
||||
result.push_back(m_arith.mk_lt(e1,e2));
|
||||
}
|
||||
else {
|
||||
result.push_back(m_arith.mk_lt(e2,e1));
|
||||
}
|
||||
}
|
||||
else {
|
||||
result.push_back(m.mk_not(e));
|
||||
}
|
||||
}
|
||||
reset();
|
||||
TRACE("pdr",
|
||||
tout << "minimized model:\n";
|
||||
for (unsigned i = 0; i < result.size(); ++i) tout << mk_pp(result[i].get(), m) << "\n";
|
||||
);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void model_implicant::process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect) {
|
||||
SASSERT(m.is_bool(e));
|
||||
SASSERT(is_true(e) || is_false(e));
|
||||
unsigned v = is_true(e);
|
||||
unsigned sz = e->get_num_args();
|
||||
expr* const* args = e->get_args();
|
||||
if (e->get_family_id() == m.get_basic_family_id()) {
|
||||
switch(e->get_decl_kind()) {
|
||||
case OP_TRUE:
|
||||
break;
|
||||
case OP_FALSE:
|
||||
break;
|
||||
case OP_EQ:
|
||||
case OP_IFF:
|
||||
if (args[0] == args[1]) {
|
||||
SASSERT(v);
|
||||
// no-op
|
||||
}
|
||||
else if (m.is_bool(args[0])) {
|
||||
todo.append(sz, args);
|
||||
}
|
||||
else {
|
||||
tocollect.push_back(e);
|
||||
}
|
||||
break;
|
||||
case OP_DISTINCT:
|
||||
tocollect.push_back(e);
|
||||
break;
|
||||
case OP_ITE:
|
||||
if (args[1] == args[2]) {
|
||||
tocollect.push_back(args[1]);
|
||||
}
|
||||
else if (is_true(args[1]) && is_true(args[2])) {
|
||||
todo.append(2, args+1);
|
||||
}
|
||||
else if (is_false(args[1]) && is_false(args[2])) {
|
||||
todo.append(2, args+1);
|
||||
}
|
||||
else if (is_true(args[0])) {
|
||||
todo.append(2, args);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_false(args[0]));
|
||||
todo.push_back(args[0]);
|
||||
todo.push_back(args[2]);
|
||||
}
|
||||
break;
|
||||
case OP_AND:
|
||||
if (v) {
|
||||
todo.append(sz, args);
|
||||
}
|
||||
else {
|
||||
unsigned i = 0;
|
||||
for (; !is_false(args[i]) && i < sz; ++i);
|
||||
if (i == sz) {
|
||||
fatal_error(1);
|
||||
}
|
||||
VERIFY(i < sz);
|
||||
todo.push_back(args[i]);
|
||||
}
|
||||
break;
|
||||
case OP_OR:
|
||||
if (v) {
|
||||
unsigned i = 0;
|
||||
for (; !is_true(args[i]) && i < sz; ++i);
|
||||
if (i == sz) {
|
||||
fatal_error(1);
|
||||
}
|
||||
VERIFY(i < sz);
|
||||
todo.push_back(args[i]);
|
||||
}
|
||||
else {
|
||||
todo.append(sz, args);
|
||||
}
|
||||
break;
|
||||
case OP_XOR:
|
||||
case OP_NOT:
|
||||
todo.append(sz, args);
|
||||
break;
|
||||
case OP_IMPLIES:
|
||||
if (v) {
|
||||
if (is_true(args[1])) {
|
||||
todo.push_back(args[1]);
|
||||
}
|
||||
else if (is_false(args[0])) {
|
||||
todo.push_back(args[0]);
|
||||
}
|
||||
else {
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
else {
|
||||
todo.append(sz, args);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
else {
|
||||
tocollect.push_back(e);
|
||||
}
|
||||
}
|
||||
|
||||
void model_implicant::collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect) {
|
||||
ptr_vector<expr> todo;
|
||||
todo.append(formulas);
|
||||
m_visited.reset();
|
||||
|
||||
VERIFY(check_model(formulas));
|
||||
|
||||
while (!todo.empty()) {
|
||||
app* e = to_app(todo.back());
|
||||
todo.pop_back();
|
||||
if (!m_visited.is_marked(e)) {
|
||||
process_formula(e, todo, tocollect);
|
||||
m_visited.mark(e, true);
|
||||
}
|
||||
}
|
||||
m_visited.reset();
|
||||
}
|
||||
|
||||
expr_ref_vector model_implicant::prune_by_cone_of_influence(ptr_vector<expr> const & formulas) {
|
||||
ptr_vector<expr> tocollect;
|
||||
collect(formulas, tocollect);
|
||||
m1.reset();
|
||||
m2.reset();
|
||||
for (unsigned i = 0; i < tocollect.size(); ++i) {
|
||||
TRACE("pdr_verbose", tout << "collect: " << mk_pp(tocollect[i], m) << "\n";);
|
||||
for_each_expr(*this, m_visited, tocollect[i]);
|
||||
}
|
||||
unsigned sz = m_model->get_num_constants();
|
||||
expr_ref e(m), eq(m), val(m);
|
||||
expr_ref_vector model(m);
|
||||
for (unsigned i = 0; i < sz; i++) {
|
||||
e = m.mk_const(m_model->get_constant(i));
|
||||
if (m_visited.is_marked(e)) {
|
||||
val = eval(m_model, e);
|
||||
eq = m.mk_eq(e, val);
|
||||
model.push_back(eq);
|
||||
}
|
||||
}
|
||||
m_visited.reset();
|
||||
TRACE("pdr", tout << sz << " ==> " << model.size() << "\n";);
|
||||
return model;
|
||||
|
||||
}
|
||||
|
||||
void model_implicant::eval_arith(app* e) {
|
||||
rational r, r2;
|
||||
|
||||
#define ARG1 e->get_arg(0)
|
||||
#define ARG2 e->get_arg(1)
|
||||
|
||||
unsigned arity = e->get_num_args();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
expr* arg = e->get_arg(i);
|
||||
if (is_x(arg)) {
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
SASSERT(!is_unknown(arg));
|
||||
}
|
||||
switch(e->get_decl_kind()) {
|
||||
case OP_NUM:
|
||||
VERIFY(m_arith.is_numeral(e, r));
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_IRRATIONAL_ALGEBRAIC_NUM:
|
||||
set_x(e);
|
||||
break;
|
||||
case OP_LE:
|
||||
set_bool(e, get_number(ARG1) <= get_number(ARG2));
|
||||
break;
|
||||
case OP_GE:
|
||||
set_bool(e, get_number(ARG1) >= get_number(ARG2));
|
||||
break;
|
||||
case OP_LT:
|
||||
set_bool(e, get_number(ARG1) < get_number(ARG2));
|
||||
break;
|
||||
case OP_GT:
|
||||
set_bool(e, get_number(ARG1) > get_number(ARG2));
|
||||
break;
|
||||
case OP_ADD:
|
||||
r = rational::zero();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
r += get_number(e->get_arg(i));
|
||||
}
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_SUB:
|
||||
r = get_number(e->get_arg(0));
|
||||
for (unsigned i = 1; i < arity; ++i) {
|
||||
r -= get_number(e->get_arg(i));
|
||||
}
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_UMINUS:
|
||||
SASSERT(arity == 1);
|
||||
set_number(e, get_number(e->get_arg(0)));
|
||||
break;
|
||||
case OP_MUL:
|
||||
r = rational::one();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
r *= get_number(e->get_arg(i));
|
||||
}
|
||||
set_number(e, r);
|
||||
break;
|
||||
case OP_DIV:
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
}
|
||||
else {
|
||||
set_number(e, get_number(ARG1) / r);
|
||||
}
|
||||
break;
|
||||
case OP_IDIV:
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
}
|
||||
else {
|
||||
set_number(e, div(get_number(ARG1), r));
|
||||
}
|
||||
break;
|
||||
case OP_REM:
|
||||
// rem(v1,v2) = if v2 >= 0 then mod(v1,v2) else -mod(v1,v2)
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
}
|
||||
else {
|
||||
r2 = mod(get_number(ARG1), r);
|
||||
if (r.is_neg()) r2.neg();
|
||||
set_number(e, r2);
|
||||
}
|
||||
break;
|
||||
case OP_MOD:
|
||||
SASSERT(arity == 2);
|
||||
r = get_number(ARG2);
|
||||
if (r.is_zero()) {
|
||||
set_x(e);
|
||||
}
|
||||
else {
|
||||
set_number(e, mod(get_number(ARG1), r));
|
||||
}
|
||||
break;
|
||||
case OP_TO_REAL:
|
||||
SASSERT(arity == 1);
|
||||
set_number(e, get_number(ARG1));
|
||||
break;
|
||||
case OP_TO_INT:
|
||||
SASSERT(arity == 1);
|
||||
set_number(e, floor(get_number(ARG1)));
|
||||
break;
|
||||
case OP_IS_INT:
|
||||
SASSERT(arity == 1);
|
||||
set_bool(e, get_number(ARG1).is_int());
|
||||
break;
|
||||
case OP_POWER:
|
||||
set_x(e);
|
||||
break;
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void model_implicant::inherit_value(expr* e, expr* v) {
|
||||
expr* w;
|
||||
SASSERT(!is_unknown(v));
|
||||
SASSERT(m.get_sort(e) == m.get_sort(v));
|
||||
if (is_x(v)) {
|
||||
set_x(e);
|
||||
}
|
||||
else if (m.is_bool(e)) {
|
||||
SASSERT(m.is_bool(v));
|
||||
if (is_true(v)) set_true(e);
|
||||
else if (is_false(v)) set_false(e);
|
||||
else {
|
||||
TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
else if (m_arith.is_int_real(e)) {
|
||||
set_number(e, get_number(v));
|
||||
}
|
||||
else if (m.is_value(v)) {
|
||||
set_value(e, v);
|
||||
}
|
||||
else if (m_values.find(v, w)) {
|
||||
set_value(e, w);
|
||||
}
|
||||
else {
|
||||
TRACE("pdr", tout << "not inherited:\n" << mk_pp(e, m) << "\n" << mk_pp(v, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
|
||||
void model_implicant::eval_exprs(expr_ref_vector& es) {
|
||||
model_ref mr(m_model);
|
||||
for (unsigned j = 0; j < es.size(); ++j) {
|
||||
if (m_array.is_as_array(es[j].get())) {
|
||||
es[j] = eval(mr, es[j].get());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool model_implicant::extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case) {
|
||||
SASSERT(m_array.is_array(a));
|
||||
|
||||
TRACE("pdr", tout << mk_pp(a, m) << "\n";);
|
||||
while (m_array.is_store(a)) {
|
||||
expr_ref_vector store(m);
|
||||
store.append(to_app(a)->get_num_args()-1, to_app(a)->get_args()+1);
|
||||
eval_exprs(store);
|
||||
stores.push_back(store);
|
||||
a = to_app(a)->get_arg(0);
|
||||
}
|
||||
|
||||
if (m_array.is_const(a)) {
|
||||
else_case = to_app(a)->get_arg(0);
|
||||
return true;
|
||||
}
|
||||
|
||||
while (m_array.is_as_array(a)) {
|
||||
func_decl* f = m_array.get_as_array_func_decl(to_app(a));
|
||||
func_interp* g = m_model->get_func_interp(f);
|
||||
unsigned sz = g->num_entries();
|
||||
unsigned arity = f->get_arity();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
expr_ref_vector store(m);
|
||||
func_entry const* fe = g->get_entry(i);
|
||||
store.append(arity, fe->get_args());
|
||||
store.push_back(fe->get_result());
|
||||
for (unsigned j = 0; j < store.size(); ++j) {
|
||||
if (!is_ground(store[j].get())) {
|
||||
TRACE("pdr", tout << "could not extract array interpretation: " << mk_pp(a, m) << "\n" << mk_pp(store[j].get(), m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
eval_exprs(store);
|
||||
stores.push_back(store);
|
||||
}
|
||||
else_case = g->get_else();
|
||||
if (!else_case) {
|
||||
TRACE("pdr", tout << "no else case " << mk_pp(a, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (!is_ground(else_case)) {
|
||||
TRACE("pdr", tout << "non-ground else case " << mk_pp(a, m) << "\n" << mk_pp(else_case, m) << "\n";);
|
||||
return false;
|
||||
}
|
||||
if (m_array.is_as_array(else_case)) {
|
||||
model_ref mr(m_model);
|
||||
else_case = eval(mr, else_case);
|
||||
}
|
||||
TRACE("pdr", tout << "else case: " << mk_pp(else_case, m) << "\n";);
|
||||
return true;
|
||||
}
|
||||
TRACE("pdr", tout << "no translation: " << mk_pp(a, m) << "\n";);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
best effort evaluator of extensional array equality.
|
||||
*/
|
||||
void model_implicant::eval_array_eq(app* e, expr* arg1, expr* arg2) {
|
||||
TRACE("pdr", tout << "array equality: " << mk_pp(e, m) << "\n";);
|
||||
expr_ref v1(m), v2(m);
|
||||
m_model->eval(arg1, v1);
|
||||
m_model->eval(arg2, v2);
|
||||
if (v1 == v2) {
|
||||
set_true(e);
|
||||
return;
|
||||
}
|
||||
sort* s = m.get_sort(arg1);
|
||||
sort* r = get_array_range(s);
|
||||
// give up evaluating finite domain/range arrays
|
||||
if (!r->is_infinite() && !r->is_very_big() && !s->is_infinite() && !s->is_very_big()) {
|
||||
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
vector<expr_ref_vector> store;
|
||||
expr_ref else1(m), else2(m);
|
||||
if (!extract_array_func_interp(v1, store, else1) ||
|
||||
!extract_array_func_interp(v2, store, else2)) {
|
||||
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
|
||||
if (else1 != else2) {
|
||||
if (m.is_value(else1) && m.is_value(else2)) {
|
||||
TRACE("pdr", tout
|
||||
<< "defaults are different: " << mk_pp(e, m) << " "
|
||||
<< mk_pp(else1, m) << " " << mk_pp(else2, m) << "\n";);
|
||||
set_false(e);
|
||||
}
|
||||
else if (m_array.is_array(else1)) {
|
||||
eval_array_eq(e, else1, else2);
|
||||
}
|
||||
else {
|
||||
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
expr_ref s1(m), s2(m), w1(m), w2(m);
|
||||
expr_ref_vector args1(m), args2(m);
|
||||
args1.push_back(v1);
|
||||
args2.push_back(v2);
|
||||
for (unsigned i = 0; i < store.size(); ++i) {
|
||||
args1.resize(1);
|
||||
args2.resize(1);
|
||||
args1.append(store[i].size()-1, store[i].c_ptr());
|
||||
args2.append(store[i].size()-1, store[i].c_ptr());
|
||||
s1 = m_array.mk_select(args1.size(), args1.c_ptr());
|
||||
s2 = m_array.mk_select(args2.size(), args2.c_ptr());
|
||||
m_model->eval(s1, w1);
|
||||
m_model->eval(s2, w2);
|
||||
if (w1 == w2) {
|
||||
continue;
|
||||
}
|
||||
if (m.is_value(w1) && m.is_value(w2)) {
|
||||
TRACE("pdr", tout << "Equality evaluation: " << mk_pp(e, m) << "\n";
|
||||
tout << mk_pp(s1, m) << " |-> " << mk_pp(w1, m) << "\n";
|
||||
tout << mk_pp(s2, m) << " |-> " << mk_pp(w2, m) << "\n";);
|
||||
set_false(e);
|
||||
}
|
||||
else if (m_array.is_array(w1)) {
|
||||
eval_array_eq(e, w1, w2);
|
||||
if (is_true(e)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else {
|
||||
TRACE("pdr", tout << "equality is unknown: " << mk_pp(e, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
return;
|
||||
}
|
||||
set_true(e);
|
||||
}
|
||||
|
||||
void model_implicant::eval_eq(app* e, expr* arg1, expr* arg2) {
|
||||
if (arg1 == arg2) {
|
||||
set_true(e);
|
||||
}
|
||||
else if (m_array.is_array(arg1)) {
|
||||
eval_array_eq(e, arg1, arg2);
|
||||
}
|
||||
else if (is_x(arg1) || is_x(arg2)) {
|
||||
expr_ref eq(m), vl(m);
|
||||
eq = m.mk_eq(arg1, arg2);
|
||||
m_model->eval(eq, vl);
|
||||
if (m.is_true(vl)) {
|
||||
set_bool(e, true);
|
||||
}
|
||||
else if (m.is_false(vl)) {
|
||||
set_bool(e, false);
|
||||
}
|
||||
else {
|
||||
TRACE("pdr", tout << "cannot evaluate: " << mk_pp(vl, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
else if (m.is_bool(arg1)) {
|
||||
bool val = is_true(arg1) == is_true(arg2);
|
||||
SASSERT(val == (is_false(arg1) == is_false(arg2)));
|
||||
if (val) {
|
||||
set_true(e);
|
||||
}
|
||||
else {
|
||||
set_false(e);
|
||||
}
|
||||
}
|
||||
else if (m_arith.is_int_real(arg1)) {
|
||||
set_bool(e, get_number(arg1) == get_number(arg2));
|
||||
}
|
||||
else {
|
||||
expr* e1 = get_value(arg1);
|
||||
expr* e2 = get_value(arg2);
|
||||
if (m.is_value(e1) && m.is_value(e2)) {
|
||||
set_bool(e, e1 == e2);
|
||||
}
|
||||
else if (e1 == e2) {
|
||||
set_bool(e, true);
|
||||
}
|
||||
else {
|
||||
TRACE("pdr", tout << "not value equal:\n" << mk_pp(e1, m) << "\n" << mk_pp(e2, m) << "\n";);
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void model_implicant::eval_basic(app* e) {
|
||||
expr* arg1, *arg2;
|
||||
expr *argCond, *argThen, *argElse, *arg;
|
||||
bool has_x = false;
|
||||
unsigned arity = e->get_num_args();
|
||||
switch(e->get_decl_kind()) {
|
||||
case OP_AND:
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
expr * arg = e->get_arg(j);
|
||||
if (is_false(arg)) {
|
||||
set_false(e);
|
||||
return;
|
||||
}
|
||||
else if (is_x(arg)) {
|
||||
has_x = true;
|
||||
}
|
||||
else {
|
||||
SASSERT(is_true(arg));
|
||||
}
|
||||
}
|
||||
if (has_x) {
|
||||
set_x(e);
|
||||
}
|
||||
else {
|
||||
set_true(e);
|
||||
}
|
||||
break;
|
||||
case OP_OR:
|
||||
for (unsigned j = 0; j < arity; ++j) {
|
||||
expr * arg = e->get_arg(j);
|
||||
if (is_true(arg)) {
|
||||
set_true(e);
|
||||
return;
|
||||
}
|
||||
else if (is_x(arg)) {
|
||||
has_x = true;
|
||||
}
|
||||
else {
|
||||
SASSERT(is_false(arg));
|
||||
}
|
||||
}
|
||||
if (has_x) {
|
||||
set_x(e);
|
||||
}
|
||||
else {
|
||||
set_false(e);
|
||||
}
|
||||
break;
|
||||
case OP_NOT:
|
||||
VERIFY(m.is_not(e, arg));
|
||||
if (is_true(arg)) {
|
||||
set_false(e);
|
||||
}
|
||||
else if (is_false(arg)) {
|
||||
set_true(e);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_x(arg));
|
||||
set_x(e);
|
||||
}
|
||||
break;
|
||||
case OP_IMPLIES:
|
||||
VERIFY(m.is_implies(e, arg1, arg2));
|
||||
if (is_false(arg1) || is_true(arg2)) {
|
||||
set_true(e);
|
||||
}
|
||||
else if (arg1 == arg2) {
|
||||
set_true(e);
|
||||
}
|
||||
else if (is_true(arg1) && is_false(arg2)) {
|
||||
set_false(e);
|
||||
}
|
||||
else {
|
||||
SASSERT(is_x(arg1) || is_x(arg2));
|
||||
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)) {
|
||||
inherit_value(e, argThen);
|
||||
}
|
||||
else if (is_false(argCond)) {
|
||||
inherit_value(e, argElse);
|
||||
}
|
||||
else if (argThen == argElse) {
|
||||
inherit_value(e, argThen);
|
||||
}
|
||||
else if (m.is_bool(e)) {
|
||||
SASSERT(is_x(argCond));
|
||||
if (is_x(argThen) || is_x(argElse)) {
|
||||
set_x(e);
|
||||
}
|
||||
else if (is_true(argThen) == is_true(argElse)) {
|
||||
inherit_value(e, argThen);
|
||||
}
|
||||
else {
|
||||
set_x(e);
|
||||
}
|
||||
}
|
||||
else {
|
||||
set_x(e);
|
||||
}
|
||||
break;
|
||||
case OP_TRUE:
|
||||
set_true(e);
|
||||
break;
|
||||
case OP_FALSE:
|
||||
set_false(e);
|
||||
break;
|
||||
case OP_EQ:
|
||||
VERIFY(m.is_eq(e, arg1, arg2));
|
||||
eval_eq(e, arg1, arg2);
|
||||
break;
|
||||
case OP_DISTINCT: {
|
||||
vector<rational> values;
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
expr* arg = e->get_arg(i);
|
||||
if (is_x(arg)) {
|
||||
set_x(e);
|
||||
return;
|
||||
}
|
||||
values.push_back(get_number(arg));
|
||||
}
|
||||
std::sort(values.begin(), values.end());
|
||||
for (unsigned i = 0; i + 1 < values.size(); ++i) {
|
||||
if (values[i] == values[i+1]) {
|
||||
set_false(e);
|
||||
return;
|
||||
}
|
||||
}
|
||||
set_true(e);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
IF_VERBOSE(0, verbose_stream() << "Term not handled " << mk_pp(e, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
bool model_implicant::check_model(ptr_vector<expr> const& formulas) {
|
||||
ptr_vector<expr> todo(formulas);
|
||||
|
||||
while (!todo.empty()) {
|
||||
expr * curr_e = todo.back();
|
||||
|
||||
if (!is_app(curr_e)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
app * curr = to_app(curr_e);
|
||||
|
||||
if (!is_unknown(curr)) {
|
||||
todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
unsigned arity = curr->get_num_args();
|
||||
for (unsigned i = 0; i < arity; ++i) {
|
||||
if (is_unknown(curr->get_arg(i))) {
|
||||
todo.push_back(curr->get_arg(i));
|
||||
}
|
||||
}
|
||||
if (todo.back() != curr) {
|
||||
continue;
|
||||
}
|
||||
todo.pop_back();
|
||||
if (curr->get_family_id() == m_arith.get_family_id()) {
|
||||
eval_arith(curr);
|
||||
}
|
||||
else if (curr->get_family_id() == m.get_basic_family_id()) {
|
||||
eval_basic(curr);
|
||||
}
|
||||
else {
|
||||
expr_ref vl(m);
|
||||
m_model->eval(curr, vl);
|
||||
assign_value(curr, vl);
|
||||
}
|
||||
|
||||
IF_VERBOSE(35,verbose_stream() << "assigned "<<mk_pp(curr_e,m)
|
||||
<<(is_true(curr_e) ? "true" : is_false(curr_e) ? "false" : "unknown") << "\n";);
|
||||
SASSERT(!is_unknown(curr));
|
||||
}
|
||||
|
||||
bool has_x = false;
|
||||
for (unsigned i = 0; i < formulas.size(); ++i) {
|
||||
expr * form = formulas[i];
|
||||
SASSERT(!is_unknown(form));
|
||||
TRACE("pdr_verbose",
|
||||
tout << "formula is " << (is_true(form) ? "true" : is_false(form) ? "false" : "unknown") << "\n" <<mk_pp(form, m)<< "\n";);
|
||||
|
||||
if (is_false(form)) {
|
||||
IF_VERBOSE(0, verbose_stream() << "formula false in model: " << mk_pp(form, m) << "\n";);
|
||||
UNREACHABLE();
|
||||
}
|
||||
if (is_x(form)) {
|
||||
IF_VERBOSE(0, verbose_stream() << "formula undetermined in model: " << mk_pp(form, m) << "\n";);
|
||||
TRACE("pdr", model_smt2_pp(tout, m, *m_model, 0););
|
||||
has_x = true;
|
||||
}
|
||||
}
|
||||
return !has_x;
|
||||
}
|
||||
|
||||
expr_ref model_implicant::eval(model_ref& model, func_decl* d) {
|
||||
SASSERT(d->get_arity() == 0);
|
||||
expr_ref result(m);
|
||||
if (m_array.is_array(d->get_range())) {
|
||||
expr_ref e(m);
|
||||
e = m.mk_const(d);
|
||||
result = eval(model, e);
|
||||
}
|
||||
else {
|
||||
result = model->get_const_interp(d);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
expr_ref model_implicant::eval(model_ref& model, expr* e) {
|
||||
expr_ref result(m);
|
||||
m_model = model;
|
||||
VERIFY(m_model->eval(e, result, true));
|
||||
if (m_array.is_array(e)) {
|
||||
vector<expr_ref_vector> stores;
|
||||
expr_ref_vector args(m);
|
||||
expr_ref else_case(m);
|
||||
if (extract_array_func_interp(result, stores, else_case)) {
|
||||
result = m_array.mk_const_array(m.get_sort(e), else_case);
|
||||
while (!stores.empty() && stores.back().back() == else_case) {
|
||||
stores.pop_back();
|
||||
}
|
||||
for (unsigned i = stores.size(); i > 0; ) {
|
||||
--i;
|
||||
args.resize(1);
|
||||
args[0] = result;
|
||||
args.append(stores[i]);
|
||||
result = m_array.mk_store(args.size(), args.c_ptr());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
118
src/model/model_implicant.h
Normal file
118
src/model/model_implicant.h
Normal file
|
@ -0,0 +1,118 @@
|
|||
/*++
|
||||
Copyright (c) 2011 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
model_implicant.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Facility to extract prime implicant from model.
|
||||
|
||||
Author:
|
||||
|
||||
Krystof Hoder (t-khoder) 2011-8-19.
|
||||
|
||||
Revision History:
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _MODEL_IMPLICANT_H_
|
||||
#define _MODEL_IMPLICANT_H_
|
||||
|
||||
#include "ast.h"
|
||||
#include "ast_pp.h"
|
||||
#include "obj_hashtable.h"
|
||||
#include "ref_vector.h"
|
||||
#include "trace.h"
|
||||
#include "vector.h"
|
||||
#include "arith_decl_plugin.h"
|
||||
#include "array_decl_plugin.h"
|
||||
|
||||
class model;
|
||||
class model_core;
|
||||
|
||||
class model_implicant {
|
||||
ast_manager& m;
|
||||
arith_util m_arith;
|
||||
array_util m_array;
|
||||
obj_map<expr,rational> m_numbers;
|
||||
expr_ref_vector m_refs;
|
||||
obj_map<expr, expr*> m_values;
|
||||
model_ref m_model;
|
||||
|
||||
//00 -- non-visited
|
||||
//01 -- X
|
||||
//10 -- false
|
||||
//11 -- true
|
||||
expr_mark m1;
|
||||
expr_mark m2;
|
||||
expr_mark m_visited;
|
||||
|
||||
|
||||
void reset();
|
||||
void setup_model(model_ref& model);
|
||||
void assign_value(expr* e, expr* v);
|
||||
void collect(ptr_vector<expr> const& formulas, ptr_vector<expr>& tocollect);
|
||||
void process_formula(app* e, ptr_vector<expr>& todo, ptr_vector<expr>& tocollect);
|
||||
expr_ref_vector prune_by_cone_of_influence(ptr_vector<expr> const & formulas);
|
||||
void eval_arith(app* e);
|
||||
void eval_basic(app* e);
|
||||
void eval_eq(app* e, expr* arg1, expr* arg2);
|
||||
void eval_array_eq(app* e, expr* arg1, expr* arg2);
|
||||
void inherit_value(expr* e, expr* v);
|
||||
|
||||
inline bool is_unknown(expr* x) { return !m1.is_marked(x) && !m2.is_marked(x); }
|
||||
inline void set_unknown(expr* x) { m1.mark(x, false); m2.mark(x, false); }
|
||||
inline bool is_x(expr* x) { return !m1.is_marked(x) && m2.is_marked(x); }
|
||||
inline bool is_false(expr* x) { return m1.is_marked(x) && !m2.is_marked(x); }
|
||||
inline bool is_true(expr* x) { return m1.is_marked(x) && m2.is_marked(x); }
|
||||
inline void set_x(expr* x) { SASSERT(is_unknown(x)); m2.mark(x); }
|
||||
inline void set_v(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
|
||||
inline void set_false(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); }
|
||||
inline void set_true(expr* x) { SASSERT(is_unknown(x)); m1.mark(x); m2.mark(x); }
|
||||
inline void set_bool(expr* x, bool v) { if (v) { set_true(x); } else { set_false(x); } }
|
||||
inline rational const& get_number(expr* x) const { return m_numbers.find(x); }
|
||||
inline void set_number(expr* x, rational const& v) {
|
||||
set_v(x); TRACE("pdr_verbose", tout << mk_pp(x,m) << " " << v << "\n";); m_numbers.insert(x,v);
|
||||
}
|
||||
inline expr* get_value(expr* x) { return m_values.find(x); }
|
||||
inline void set_value(expr* x, expr* v) { set_v(x); m_refs.push_back(v); m_values.insert(x, v); }
|
||||
|
||||
bool check_model(ptr_vector<expr> const & formulas);
|
||||
|
||||
bool extract_array_func_interp(expr* a, vector<expr_ref_vector>& stores, expr_ref& else_case);
|
||||
|
||||
void eval_exprs(expr_ref_vector& es);
|
||||
|
||||
public:
|
||||
model_implicant(ast_manager& m) :
|
||||
m(m), m_arith(m), m_array(m), m_refs(m) {}
|
||||
|
||||
/**
|
||||
\brief extract equalities from model that suffice to satisfy formula.
|
||||
|
||||
\pre model satisfies formulas
|
||||
*/
|
||||
|
||||
expr_ref_vector minimize_model(ptr_vector<expr> const & formulas, model_ref& mdl);
|
||||
|
||||
/**
|
||||
\brief extract literals from model that satisfy formulas.
|
||||
|
||||
\pre model satisfies formulas
|
||||
*/
|
||||
expr_ref_vector minimize_literals(ptr_vector<expr> const & formulas, model_ref& mdl);
|
||||
|
||||
/**
|
||||
for_each_expr visitor.
|
||||
*/
|
||||
void operator()(expr* e) {}
|
||||
|
||||
expr_ref eval(model_ref& mdl, expr* e);
|
||||
|
||||
expr_ref eval(model_ref& mdl, func_decl* d);
|
||||
};
|
||||
|
||||
|
||||
#endif
|
154
src/muz/base/bind_variables.cpp
Normal file
154
src/muz/base/bind_variables.cpp
Normal file
|
@ -0,0 +1,154 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bind_variables.cpp
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility to find constants that are declared as variables.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 9-24-2014
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#include "bind_variables.h"
|
||||
|
||||
bind_variables::bind_variables(ast_manager & m):
|
||||
m(m),
|
||||
m_vars(m),
|
||||
m_pinned(m)
|
||||
{}
|
||||
|
||||
bind_variables::~bind_variables() {
|
||||
}
|
||||
|
||||
expr_ref bind_variables::operator()(expr* fml, bool is_forall) {
|
||||
if (m_vars.empty()) {
|
||||
return expr_ref(fml, m);
|
||||
}
|
||||
SASSERT(m_pinned.empty());
|
||||
expr_ref result = abstract(fml, m_cache, 0);
|
||||
if (!m_names.empty()) {
|
||||
m_bound.reverse();
|
||||
m_names.reverse();
|
||||
result = m.mk_quantifier(is_forall, m_bound.size(), m_bound.c_ptr(), m_names.c_ptr(), result);
|
||||
}
|
||||
m_pinned.reset();
|
||||
m_cache.reset();
|
||||
m_names.reset();
|
||||
m_bound.reset();
|
||||
for (var2bound::iterator it = m_var2bound.begin(); it != m_var2bound.end(); ++it) {
|
||||
it->m_value = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
expr_ref bind_variables::abstract(expr* term, cache_t& cache, unsigned scope) {
|
||||
unsigned sz = m_todo.size();
|
||||
m_todo.push_back(term);
|
||||
m_args.reset();
|
||||
expr* b, *arg;
|
||||
while (m_todo.size() > sz) {
|
||||
expr* e = m_todo.back();
|
||||
if (cache.contains(e)) {
|
||||
m_todo.pop_back();
|
||||
continue;
|
||||
}
|
||||
switch(e->get_kind()) {
|
||||
case AST_VAR: {
|
||||
SASSERT(to_var(e)->get_idx() < scope);
|
||||
// mixing bound variables and free is possible for the caller,
|
||||
// but not proper use.
|
||||
// So we assert here even though we don't check for it.
|
||||
cache.insert(e, e);
|
||||
m_todo.pop_back();
|
||||
break;
|
||||
}
|
||||
case AST_APP: {
|
||||
app* a = to_app(e);
|
||||
var2bound::obj_map_entry* w = m_var2bound.find_core(a);
|
||||
if (w) {
|
||||
var* v = w->get_data().m_value;
|
||||
if (!v) {
|
||||
// allocate a bound index.
|
||||
v = m.mk_var(m_names.size(), m.get_sort(a));
|
||||
m_names.push_back(a->get_decl()->get_name());
|
||||
m_bound.push_back(m.get_sort(a));
|
||||
w->get_data().m_value = v;
|
||||
m_pinned.push_back(v);
|
||||
}
|
||||
if (scope == 0) {
|
||||
cache.insert(e, v);
|
||||
}
|
||||
else {
|
||||
var* v1 = m.mk_var(scope + v->get_idx(), m.get_sort(v));
|
||||
m_pinned.push_back(v1);
|
||||
cache.insert(e, v1);
|
||||
}
|
||||
m_todo.pop_back();
|
||||
break;
|
||||
}
|
||||
bool all_visited = true;
|
||||
bool some_diff = false;
|
||||
m_args.reset();
|
||||
for (unsigned i = 0; i < a->get_num_args(); ++i) {
|
||||
arg = a->get_arg(i);
|
||||
if (!cache.find(arg, b)) {
|
||||
m_todo.push_back(arg);
|
||||
all_visited = false;
|
||||
}
|
||||
else if (all_visited) {
|
||||
m_args.push_back(b);
|
||||
if (b != arg) {
|
||||
some_diff = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (all_visited) {
|
||||
if (some_diff) {
|
||||
b = m.mk_app(a->get_decl(), m_args.size(), m_args.c_ptr());
|
||||
m_pinned.push_back(b);
|
||||
}
|
||||
else {
|
||||
b = a;
|
||||
}
|
||||
cache.insert(e, b);
|
||||
m_todo.pop_back();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case AST_QUANTIFIER: {
|
||||
quantifier* q = to_quantifier(e);
|
||||
expr_ref_buffer patterns(m);
|
||||
expr_ref result1(m);
|
||||
unsigned new_scope = scope + q->get_num_decls();
|
||||
cache_t new_cache;
|
||||
for (unsigned i = 0; i < q->get_num_patterns(); ++i) {
|
||||
patterns.push_back(abstract(q->get_pattern(i), new_cache, new_scope));
|
||||
}
|
||||
result1 = abstract(q->get_expr(), new_cache, new_scope);
|
||||
b = m.update_quantifier(q, patterns.size(), patterns.c_ptr(), result1.get());
|
||||
m_pinned.push_back(b);
|
||||
cache.insert(e, b);
|
||||
m_todo.pop_back();
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
return expr_ref(cache.find(term), m);
|
||||
}
|
||||
|
||||
void bind_variables::add_var(app* v) {
|
||||
m_vars.push_back(v);
|
||||
m_var2bound.insert(v, 0);
|
||||
}
|
51
src/muz/base/bind_variables.h
Normal file
51
src/muz/base/bind_variables.h
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*++
|
||||
Copyright (c) 2013 Microsoft Corporation
|
||||
|
||||
Module Name:
|
||||
|
||||
bind_variables.h
|
||||
|
||||
Abstract:
|
||||
|
||||
Utility to find constants that are declard as varaibles.
|
||||
|
||||
Author:
|
||||
|
||||
Nikolaj Bjorner (nbjorner) 9-24-2014
|
||||
|
||||
Notes:
|
||||
|
||||
|
||||
--*/
|
||||
|
||||
#ifndef _BIND_VARIABLES_H_
|
||||
#define _BIND_VARIABLES_H_
|
||||
|
||||
#include"ast.h"
|
||||
|
||||
class bind_variables {
|
||||
typedef obj_map<app, var*> var2bound;
|
||||
typedef obj_map<expr, expr*> cache_t;
|
||||
ast_manager& m;
|
||||
app_ref_vector m_vars;
|
||||
obj_map<expr, expr*> m_cache;
|
||||
var2bound m_var2bound;
|
||||
expr_ref_vector m_pinned;
|
||||
ptr_vector<sort> m_bound;
|
||||
svector<symbol> m_names;
|
||||
ptr_vector<expr> m_todo;
|
||||
ptr_vector<expr> m_args;
|
||||
|
||||
|
||||
|
||||
expr_ref abstract(expr* fml, cache_t& cache, unsigned scope);
|
||||
public:
|
||||
bind_variables(ast_manager & m);
|
||||
~bind_variables();
|
||||
|
||||
expr_ref operator()(expr* fml, bool is_forall);
|
||||
|
||||
void add_var(app* v);
|
||||
};
|
||||
|
||||
#endif /* _BIND_VARIABLES_H_ */
|
|
@ -210,14 +210,12 @@ namespace datalog {
|
|||
m_rewriter(m),
|
||||
m_var_subst(m),
|
||||
m_rule_manager(*this),
|
||||
m_elim_unused_vars(m),
|
||||
m_abstractor(m),
|
||||
m_contains_p(*this),
|
||||
m_check_pred(m_contains_p, m),
|
||||
m_rule_properties(m, m_rule_manager, *this, m_contains_p),
|
||||
m_transf(*this),
|
||||
m_trail(*this),
|
||||
m_pinned(m),
|
||||
m_vars(m),
|
||||
m_bind_variables(m),
|
||||
m_rule_set(*this),
|
||||
m_transformed_rule_set(*this),
|
||||
m_rule_fmls_head(0),
|
||||
|
@ -228,11 +226,13 @@ namespace datalog {
|
|||
m_engine(0),
|
||||
m_closed(false),
|
||||
m_saturation_was_run(false),
|
||||
m_enable_bind_variables(true),
|
||||
m_last_status(OK),
|
||||
m_last_answer(m),
|
||||
m_engine_type(LAST_ENGINE),
|
||||
m_cancel(false) {
|
||||
re.set_context(this);
|
||||
updt_params(pa);
|
||||
}
|
||||
|
||||
context::~context() {
|
||||
|
@ -272,35 +272,39 @@ namespace datalog {
|
|||
}
|
||||
|
||||
|
||||
bool context::generate_proof_trace() const { return m_params->generate_proof_trace(); }
|
||||
bool context::output_profile() const { return m_params->output_profile(); }
|
||||
bool context::output_tuples() const { return m_params->output_tuples(); }
|
||||
bool context::use_map_names() const { return m_params->use_map_names(); }
|
||||
bool context::fix_unbound_vars() const { return m_params->fix_unbound_vars(); }
|
||||
symbol context::default_table() const { return m_params->default_table(); }
|
||||
symbol context::default_relation() const { return m_params->default_relation(); } // external_relation_plugin::get_name());
|
||||
symbol context::default_table_checker() const { return m_params->default_table_checker(); }
|
||||
bool context::default_table_checked() const { return m_params->default_table_checked(); }
|
||||
bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->dbg_fpr_nonempty_relation_signature(); }
|
||||
unsigned context::dl_profile_milliseconds_threshold() const { return m_params->profile_timeout_milliseconds(); }
|
||||
bool context::all_or_nothing_deltas() const { return m_params->all_or_nothing_deltas(); }
|
||||
bool context::compile_with_widening() const { return m_params->compile_with_widening(); }
|
||||
bool context::unbound_compressor() const { return m_params->unbound_compressor(); }
|
||||
bool context::similarity_compressor() const { return m_params->similarity_compressor(); }
|
||||
unsigned context::similarity_compressor_threshold() const { return m_params->similarity_compressor_threshold(); }
|
||||
unsigned context::timeout() const { return m_fparams.m_timeout; }
|
||||
unsigned context::initial_restart_timeout() const { return m_params->initial_restart_timeout(); }
|
||||
bool context::generate_explanations() const { return m_params->generate_explanations(); }
|
||||
bool context::explanations_on_relation_level() const { return m_params->explanations_on_relation_level(); }
|
||||
bool context::magic_sets_for_queries() const { return m_params->magic_sets_for_queries(); }
|
||||
bool context::eager_emptiness_checking() const { return m_params->eager_emptiness_checking(); }
|
||||
|
||||
bool context::bit_blast() const { return m_params->bit_blast(); }
|
||||
bool context::karr() const { return m_params->karr(); }
|
||||
bool context::scale() const { return m_params->scale(); }
|
||||
bool context::magic() const { return m_params->magic(); }
|
||||
bool context::quantify_arrays() const { return m_params->quantify_arrays(); }
|
||||
bool context::instantiate_quantifiers() const { return m_params->instantiate_quantifiers(); }
|
||||
bool context::generate_proof_trace() const { return m_generate_proof_trace; }
|
||||
bool context::output_profile() const { return m_params->datalog_output_profile(); }
|
||||
bool context::output_tuples() const { return m_params->datalog_print_tuples(); }
|
||||
bool context::use_map_names() const { return m_params->datalog_use_map_names(); }
|
||||
bool context::fix_unbound_vars() const { return m_params->xform_fix_unbound_vars(); }
|
||||
symbol context::default_table() const { return m_params->datalog_default_table(); }
|
||||
symbol context::default_relation() const { return m_default_relation; }
|
||||
void context::set_default_relation(symbol const& s) { m_default_relation = s; }
|
||||
symbol context::print_aig() const { return m_params->print_aig(); }
|
||||
symbol context::check_relation() const { return m_params->datalog_check_relation(); }
|
||||
symbol context::default_table_checker() const { return m_params->datalog_default_table_checker(); }
|
||||
bool context::default_table_checked() const { return m_params->datalog_default_table_checked(); }
|
||||
bool context::dbg_fpr_nonempty_relation_signature() const { return m_params->datalog_dbg_fpr_nonempty_relation_signature(); }
|
||||
unsigned context::dl_profile_milliseconds_threshold() const { return m_params->datalog_profile_timeout_milliseconds(); }
|
||||
bool context::all_or_nothing_deltas() const { return m_params->datalog_all_or_nothing_deltas(); }
|
||||
bool context::compile_with_widening() const { return m_params->datalog_compile_with_widening(); }
|
||||
bool context::unbound_compressor() const { return m_unbound_compressor; }
|
||||
void context::set_unbound_compressor(bool f) { m_unbound_compressor = f; }
|
||||
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(); }
|
||||
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_slice() const { return m_params->xform_slice(); }
|
||||
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(); }
|
||||
bool context::quantify_arrays() const { return m_params->xform_quantify_arrays(); }
|
||||
bool context::instantiate_quantifiers() const { return m_params->xform_instantiate_quantifiers(); }
|
||||
|
||||
|
||||
void context::register_finite_sort(sort * s, sort_kind k) {
|
||||
|
@ -321,54 +325,23 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void context::register_variable(func_decl* var) {
|
||||
m_vars.push_back(m.mk_const(var));
|
||||
m_bind_variables.add_var(m.mk_const(var));
|
||||
}
|
||||
|
||||
expr_ref context::bind_variables(expr* fml, bool is_forall) {
|
||||
expr_ref result(m);
|
||||
app_ref_vector const & vars = m_vars;
|
||||
rule_manager& rm = get_rule_manager();
|
||||
if (vars.empty()) {
|
||||
result = fml;
|
||||
expr_ref context::bind_vars(expr* fml, bool is_forall) {
|
||||
if (m_enable_bind_variables) {
|
||||
return m_bind_variables(fml, is_forall);
|
||||
}
|
||||
else {
|
||||
m_names.reset();
|
||||
m_abstractor(0, vars.size(), reinterpret_cast<expr*const*>(vars.c_ptr()), fml, result);
|
||||
rm.collect_vars(result);
|
||||
ptr_vector<sort>& sorts = rm.get_var_sorts();
|
||||
if (sorts.empty()) {
|
||||
result = fml;
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||
if (!sorts[i]) {
|
||||
if (i < vars.size()) {
|
||||
sorts[i] = vars[i]->get_decl()->get_range();
|
||||
}
|
||||
else {
|
||||
sorts[i] = m.mk_bool_sort();
|
||||
}
|
||||
}
|
||||
if (i < vars.size()) {
|
||||
m_names.push_back(vars[i]->get_decl()->get_name());
|
||||
}
|
||||
else {
|
||||
m_names.push_back(symbol(i));
|
||||
}
|
||||
}
|
||||
quantifier_ref q(m);
|
||||
sorts.reverse();
|
||||
q = m.mk_quantifier(is_forall, sorts.size(), sorts.c_ptr(), m_names.c_ptr(), result);
|
||||
m_elim_unused_vars(q, result);
|
||||
}
|
||||
return expr_ref(fml, m);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void context::register_predicate(func_decl * decl, bool named) {
|
||||
if (!is_predicate(decl)) {
|
||||
m_pinned.push_back(decl);
|
||||
m_preds.insert(decl);
|
||||
TRACE("dl", tout << mk_pp(decl, m) << "\n";);
|
||||
if (named) {
|
||||
m_preds_by_name.insert(decl->get_name(), decl);
|
||||
}
|
||||
|
@ -379,6 +352,7 @@ namespace datalog {
|
|||
m_preds.reset();
|
||||
func_decl_set::iterator it = preds.begin(), end = preds.end();
|
||||
for (; it != end; ++it) {
|
||||
TRACE("dl", tout << mk_pp(*it, m) << "\n";);
|
||||
m_preds.insert(*it);
|
||||
}
|
||||
}
|
||||
|
@ -490,12 +464,7 @@ namespace datalog {
|
|||
rm.mk_rule(fml, p, m_rule_set, m_rule_names[m_rule_fmls_head]);
|
||||
++m_rule_fmls_head;
|
||||
}
|
||||
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
|
||||
rule_ref r(m_rule_manager);
|
||||
for (; it != end; ++it) {
|
||||
r = *it;
|
||||
check_rule(r);
|
||||
}
|
||||
check_rules(m_rule_set);
|
||||
}
|
||||
|
||||
//
|
||||
|
@ -581,147 +550,57 @@ namespace datalog {
|
|||
m_engine->add_cover(level, pred, property);
|
||||
}
|
||||
|
||||
void context::check_uninterpreted_free(rule_ref& r) {
|
||||
func_decl* f = 0;
|
||||
if (r->has_uninterpreted_non_predicates(m, f)) {
|
||||
std::stringstream stm;
|
||||
stm << "Uninterpreted '"
|
||||
<< f->get_name()
|
||||
<< "' in ";
|
||||
r->display(*this, stm);
|
||||
throw default_exception(stm.str());
|
||||
}
|
||||
}
|
||||
|
||||
void context::check_quantifier_free(rule_ref& r) {
|
||||
if (r->has_quantifiers()) {
|
||||
std::stringstream stm;
|
||||
stm << "cannot process quantifiers in rule ";
|
||||
r->display(*this, stm);
|
||||
throw default_exception(stm.str());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void context::check_existential_tail(rule_ref& r) {
|
||||
unsigned ut_size = r->get_uninterpreted_tail_size();
|
||||
unsigned t_size = r->get_tail_size();
|
||||
|
||||
TRACE("dl", r->display_smt2(get_manager(), tout); tout << "\n";);
|
||||
for (unsigned i = ut_size; i < t_size; ++i) {
|
||||
app* t = r->get_tail(i);
|
||||
TRACE("dl", tout << "checking: " << mk_ismt2_pp(t, get_manager()) << "\n";);
|
||||
if (m_check_pred(t)) {
|
||||
std::ostringstream out;
|
||||
out << "interpreted body " << mk_ismt2_pp(t, get_manager()) << " contains recursive predicate";
|
||||
throw default_exception(out.str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void context::check_positive_predicates(rule_ref& r) {
|
||||
ast_mark visited;
|
||||
ptr_vector<expr> todo, tocheck;
|
||||
unsigned ut_size = r->get_uninterpreted_tail_size();
|
||||
unsigned t_size = r->get_tail_size();
|
||||
for (unsigned i = 0; i < ut_size; ++i) {
|
||||
if (r->is_neg_tail(i)) {
|
||||
tocheck.push_back(r->get_tail(i));
|
||||
}
|
||||
}
|
||||
ast_manager& m = get_manager();
|
||||
contains_pred contains_p(*this);
|
||||
check_pred check_pred(contains_p, get_manager());
|
||||
|
||||
for (unsigned i = ut_size; i < t_size; ++i) {
|
||||
todo.push_back(r->get_tail(i));
|
||||
}
|
||||
while (!todo.empty()) {
|
||||
expr* e = todo.back(), *e1, *e2;
|
||||
todo.pop_back();
|
||||
if (visited.is_marked(e)) {
|
||||
continue;
|
||||
}
|
||||
visited.mark(e, true);
|
||||
if (is_predicate(e)) {
|
||||
}
|
||||
else if (m.is_and(e) || m.is_or(e)) {
|
||||
todo.append(to_app(e)->get_num_args(), to_app(e)->get_args());
|
||||
}
|
||||
else if (m.is_implies(e, e1, e2)) {
|
||||
tocheck.push_back(e1);
|
||||
todo.push_back(e2);
|
||||
}
|
||||
else if (is_quantifier(e)) {
|
||||
todo.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)) {
|
||||
todo.push_back(e2);
|
||||
}
|
||||
else if ((m.is_eq(e, e1, e2) || m.is_iff(e, e1, e2)) &&
|
||||
m.is_true(e2)) {
|
||||
todo.push_back(e1);
|
||||
}
|
||||
else {
|
||||
tocheck.push_back(e);
|
||||
}
|
||||
}
|
||||
for (unsigned i = 0; i < tocheck.size(); ++i) {
|
||||
expr* e = tocheck[i];
|
||||
if (check_pred(e)) {
|
||||
std::ostringstream out;
|
||||
out << "recursive predicate " << mk_ismt2_pp(e, get_manager()) << " occurs nested in body";
|
||||
r->display(*this, out << "\n");
|
||||
throw default_exception(out.str());
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void context::check_rule(rule_ref& r) {
|
||||
void context::check_rules(rule_set& r) {
|
||||
m_rule_properties.set_generate_proof(generate_proof_trace());
|
||||
switch(get_engine()) {
|
||||
case DATALOG_ENGINE:
|
||||
check_quantifier_free(r);
|
||||
check_uninterpreted_free(r);
|
||||
check_existential_tail(r);
|
||||
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();
|
||||
break;
|
||||
case PDR_ENGINE:
|
||||
check_existential_tail(r);
|
||||
check_positive_predicates(r);
|
||||
check_uninterpreted_free(r);
|
||||
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:
|
||||
check_positive_predicates(r);
|
||||
check_uninterpreted_free(r);
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
m_rule_properties.check_uninterpreted_free();
|
||||
break;
|
||||
case BMC_ENGINE:
|
||||
check_positive_predicates(r);
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
case QBMC_ENGINE:
|
||||
check_existential_tail(r);
|
||||
check_positive_predicates(r);
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
case TAB_ENGINE:
|
||||
check_existential_tail(r);
|
||||
check_positive_predicates(r);
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
case DUALITY_ENGINE:
|
||||
check_existential_tail(r);
|
||||
check_positive_predicates(r);
|
||||
case DUALITY_ENGINE:
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
case CLP_ENGINE:
|
||||
check_existential_tail(r);
|
||||
check_positive_predicates(r);
|
||||
m_rule_properties.collect(r);
|
||||
m_rule_properties.check_existential_tail();
|
||||
m_rule_properties.check_for_negated_predicates();
|
||||
break;
|
||||
case DDNF_ENGINE:
|
||||
break;
|
||||
case LAST_ENGINE:
|
||||
default:
|
||||
UNREACHABLE();
|
||||
break;
|
||||
}
|
||||
if (generate_proof_trace() && !r->get_proof()) {
|
||||
m_rule_manager.mk_rule_asserted_proof(*r.get());
|
||||
}
|
||||
}
|
||||
|
||||
void context::add_rule(rule_ref& r) {
|
||||
|
@ -807,6 +686,7 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void context::transform_rules(rule_transformer::plugin* plugin) {
|
||||
flet<bool> _enable_bv(m_enable_bind_variables, false);
|
||||
rule_transformer transformer(*this);
|
||||
transformer.register_plugin(plugin);
|
||||
transform_rules(transformer);
|
||||
|
@ -847,6 +727,9 @@ namespace datalog {
|
|||
void context::updt_params(params_ref const& p) {
|
||||
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();
|
||||
}
|
||||
|
||||
expr_ref context::get_background_assertion() {
|
||||
|
@ -908,6 +791,9 @@ namespace datalog {
|
|||
};
|
||||
|
||||
void context::configure_engine() {
|
||||
if (m_engine_type != LAST_ENGINE) {
|
||||
return;
|
||||
}
|
||||
symbol e = m_params->engine();
|
||||
|
||||
if (e == symbol("datalog")) {
|
||||
|
@ -934,6 +820,9 @@ namespace datalog {
|
|||
else if (e == symbol("duality")) {
|
||||
m_engine_type = DUALITY_ENGINE;
|
||||
}
|
||||
else if (e == symbol("ddnf")) {
|
||||
m_engine_type = DDNF_ENGINE;
|
||||
}
|
||||
|
||||
if (m_engine_type == LAST_ENGINE) {
|
||||
expr_fast_mark1 mark;
|
||||
|
@ -959,18 +848,6 @@ namespace datalog {
|
|||
}
|
||||
|
||||
lbool context::query(expr* query) {
|
||||
#if 0
|
||||
// TODO: what?
|
||||
if(get_engine() != DUALITY_ENGINE) {
|
||||
new_query();
|
||||
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
|
||||
rule_ref r(m_rule_manager);
|
||||
for (; it != end; ++it) {
|
||||
r = *it;
|
||||
check_rule(r);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
m_mc = mk_skip_model_converter();
|
||||
m_last_status = OK;
|
||||
m_last_answer = 0;
|
||||
|
@ -982,6 +859,7 @@ namespace datalog {
|
|||
case QBMC_ENGINE:
|
||||
case TAB_ENGINE:
|
||||
case CLP_ENGINE:
|
||||
case DDNF_ENGINE:
|
||||
flush_add_rules();
|
||||
break;
|
||||
case DUALITY_ENGINE:
|
||||
|
@ -1009,6 +887,7 @@ namespace datalog {
|
|||
void context::ensure_engine() {
|
||||
if (!m_engine.get()) {
|
||||
m_engine = m_register_engine.mk_engine(get_engine());
|
||||
m_engine->updt_params();
|
||||
|
||||
// break abstraction.
|
||||
if (get_engine() == DATALOG_ENGINE) {
|
||||
|
@ -1109,23 +988,22 @@ namespace datalog {
|
|||
|
||||
void context::get_raw_rule_formulas(expr_ref_vector& rules, svector<symbol>& names, vector<unsigned> &bounds){
|
||||
for (unsigned i = 0; i < m_rule_fmls.size(); ++i) {
|
||||
expr_ref r = bind_variables(m_rule_fmls[i].get(), true);
|
||||
rules.push_back(r.get());
|
||||
// rules.push_back(m_rule_fmls[i].get());
|
||||
names.push_back(m_rule_names[i]);
|
||||
bounds.push_back(m_rule_bounds[i]);
|
||||
expr_ref r = bind_vars(m_rule_fmls[i].get(), true);
|
||||
rules.push_back(r.get());
|
||||
names.push_back(m_rule_names[i]);
|
||||
bounds.push_back(m_rule_bounds[i]);
|
||||
}
|
||||
}
|
||||
|
||||
void context::get_rules_as_formulas(expr_ref_vector& rules, svector<symbol>& names) {
|
||||
void context::get_rules_as_formulas(expr_ref_vector& rules, expr_ref_vector& queries, svector<symbol>& names) {
|
||||
expr_ref fml(m);
|
||||
datalog::rule_manager& rm = get_rule_manager();
|
||||
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) {
|
||||
ptr_vector<sort> sorts;
|
||||
get_free_vars(m_rule_fmls[i].get(), sorts);
|
||||
if (!sorts.empty()) {
|
||||
m_free_vars(m_rule_fmls[i].get());
|
||||
if (!m_free_vars.empty()) {
|
||||
rm.mk_rule(m_rule_fmls[i].get(), 0, m_rule_set, m_rule_names[i]);
|
||||
m_rule_fmls[i] = m_rule_fmls.back();
|
||||
m_rule_names[i] = m_rule_names.back();
|
||||
|
@ -1137,9 +1015,30 @@ namespace datalog {
|
|||
}
|
||||
rule_set::iterator it = m_rule_set.begin(), end = m_rule_set.end();
|
||||
for (; it != end; ++it) {
|
||||
(*it)->to_formula(fml);
|
||||
rules.push_back(fml);
|
||||
names.push_back((*it)->name());
|
||||
rule* r = *it;
|
||||
rm.to_formula(*r, fml);
|
||||
func_decl* h = r->get_decl();
|
||||
if (m_rule_set.is_output_predicate(h)) {
|
||||
expr* body = fml;
|
||||
expr* e2;
|
||||
if (is_quantifier(body)) {
|
||||
quantifier* q = to_quantifier(body);
|
||||
expr* e = q->get_expr();
|
||||
VERIFY(m.is_implies(e, body, e2));
|
||||
fml = m.mk_quantifier(false, q->get_num_decls(),
|
||||
q->get_decl_sorts(), q->get_decl_names(),
|
||||
body);
|
||||
}
|
||||
else {
|
||||
VERIFY(m.is_implies(body, body, e2));
|
||||
fml = body;
|
||||
}
|
||||
queries.push_back(fml);
|
||||
}
|
||||
else {
|
||||
rules.push_back(fml);
|
||||
names.push_back(r->name());
|
||||
}
|
||||
}
|
||||
for (unsigned i = m_rule_fmls_head; i < m_rule_fmls.size(); ++i) {
|
||||
rules.push_back(m_rule_fmls[i].get());
|
||||
|
@ -1147,10 +1046,7 @@ namespace datalog {
|
|||
}
|
||||
}
|
||||
|
||||
void context::display_smt2(
|
||||
unsigned num_queries,
|
||||
expr* const* queries,
|
||||
std::ostream& out) {
|
||||
void context::display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out) {
|
||||
ast_manager& m = get_manager();
|
||||
free_func_visitor visitor(m);
|
||||
expr_mark visited;
|
||||
|
@ -1158,21 +1054,22 @@ namespace datalog {
|
|||
unsigned num_axioms = m_background.size();
|
||||
expr* const* axioms = m_background.c_ptr();
|
||||
expr_ref fml(m);
|
||||
expr_ref_vector rules(m);
|
||||
expr_ref_vector rules(m), queries(m);
|
||||
svector<symbol> names;
|
||||
bool use_fixedpoint_extensions = m_params->print_with_fixedpoint_extensions();
|
||||
bool use_fixedpoint_extensions = m_params->print_fixedpoint_extensions();
|
||||
bool print_low_level = m_params->print_low_level_smt2();
|
||||
bool do_declare_vars = m_params->print_with_variable_declarations();
|
||||
|
||||
#define PP(_e_) if (print_low_level) out << mk_smt_pp(_e_, m); else ast_smt2_pp(out, _e_, env);
|
||||
|
||||
get_rules_as_formulas(rules, names);
|
||||
get_rules_as_formulas(rules, queries, names);
|
||||
queries.append(num_queries, qs);
|
||||
|
||||
smt2_pp_environment_dbg env(m);
|
||||
mk_fresh_name fresh_names;
|
||||
collect_free_funcs(num_axioms, axioms, visited, visitor, fresh_names);
|
||||
collect_free_funcs(rules.size(), rules.c_ptr(), visited, visitor, fresh_names);
|
||||
collect_free_funcs(num_queries, queries, visited, visitor, fresh_names);
|
||||
collect_free_funcs(queries.size(), queries.c_ptr(), visited, visitor, fresh_names);
|
||||
func_decl_set funcs;
|
||||
func_decl_set::iterator it = visitor.funcs().begin();
|
||||
func_decl_set::iterator end = visitor.funcs().end();
|
||||
|
@ -1258,22 +1155,22 @@ namespace datalog {
|
|||
out << ")\n";
|
||||
}
|
||||
if (use_fixedpoint_extensions) {
|
||||
for (unsigned i = 0; i < num_queries; ++i) {
|
||||
for (unsigned i = 0; i < queries.size(); ++i) {
|
||||
out << "(query ";
|
||||
PP(queries[i]);
|
||||
PP(queries[i].get());
|
||||
out << ")\n";
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (unsigned i = 0; i < num_queries; ++i) {
|
||||
if (num_queries > 1) out << "(push)\n";
|
||||
for (unsigned i = 0; i < queries.size(); ++i) {
|
||||
if (queries.size() > 1) out << "(push)\n";
|
||||
out << "(assert ";
|
||||
expr_ref q(m);
|
||||
q = m.mk_not(queries[i]);
|
||||
q = m.mk_not(queries[i].get());
|
||||
PP(q);
|
||||
out << ")\n";
|
||||
out << "(check-sat)\n";
|
||||
if (num_queries > 1) out << "(pop)\n";
|
||||
if (queries.size() > 1) out << "(pop)\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,9 +39,10 @@ Revision History:
|
|||
#include"model2expr.h"
|
||||
#include"smt_params.h"
|
||||
#include"dl_rule_transformer.h"
|
||||
#include"expr_abstract.h"
|
||||
#include"expr_functors.h"
|
||||
#include"dl_engine_base.h"
|
||||
#include"bind_variables.h"
|
||||
#include"rule_properties.h"
|
||||
|
||||
struct fixedpoint_params;
|
||||
|
||||
|
@ -142,19 +143,6 @@ namespace datalog {
|
|||
SK_UINT64,
|
||||
SK_SYMBOL
|
||||
};
|
||||
|
||||
private:
|
||||
class sort_domain;
|
||||
class symbol_sort_domain;
|
||||
class uint64_sort_domain;
|
||||
class restore_rules;
|
||||
class contains_pred;
|
||||
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
|
||||
typedef map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> sym2decl;
|
||||
typedef obj_map<const func_decl, svector<symbol> > pred2syms;
|
||||
typedef obj_map<const sort, sort_domain*> sort_domain_map;
|
||||
|
||||
class contains_pred : public i_expr_pred {
|
||||
context const& ctx;
|
||||
public:
|
||||
|
@ -167,30 +155,43 @@ namespace datalog {
|
|||
};
|
||||
|
||||
|
||||
private:
|
||||
class sort_domain;
|
||||
class symbol_sort_domain;
|
||||
class uint64_sort_domain;
|
||||
class restore_rules;
|
||||
|
||||
typedef hashtable<symbol, symbol_hash_proc, symbol_eq_proc> symbol_set;
|
||||
typedef map<symbol, func_decl*, symbol_hash_proc, symbol_eq_proc> sym2decl;
|
||||
typedef obj_map<const func_decl, svector<symbol> > pred2syms;
|
||||
typedef obj_map<const sort, sort_domain*> sort_domain_map;
|
||||
|
||||
|
||||
ast_manager & m;
|
||||
register_engine_base& m_register_engine;
|
||||
smt_params & m_fparams;
|
||||
params_ref m_params_ref;
|
||||
fixedpoint_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
|
||||
dl_decl_util m_decl_util;
|
||||
th_rewriter m_rewriter;
|
||||
var_subst m_var_subst;
|
||||
rule_manager m_rule_manager;
|
||||
unused_vars_eliminator m_elim_unused_vars;
|
||||
expr_abstractor m_abstractor;
|
||||
contains_pred m_contains_p;
|
||||
check_pred m_check_pred;
|
||||
rule_properties m_rule_properties;
|
||||
rule_transformer m_transf;
|
||||
trail_stack<context> m_trail;
|
||||
ast_ref_vector m_pinned;
|
||||
app_ref_vector m_vars;
|
||||
svector<symbol> m_names;
|
||||
bind_variables m_bind_variables;
|
||||
sort_domain_map m_sorts;
|
||||
func_decl_set m_preds;
|
||||
sym2decl m_preds_by_name;
|
||||
pred2syms m_argument_var_names;
|
||||
rule_set m_rule_set;
|
||||
rule_set m_transformed_rule_set;
|
||||
expr_free_vars m_free_vars;
|
||||
unsigned m_rule_fmls_head;
|
||||
expr_ref_vector m_rule_fmls;
|
||||
svector<symbol> m_rule_names;
|
||||
|
@ -204,6 +205,7 @@ namespace datalog {
|
|||
|
||||
bool m_closed;
|
||||
bool m_saturation_was_run;
|
||||
bool m_enable_bind_variables;
|
||||
execution_result m_last_status;
|
||||
expr_ref m_last_answer;
|
||||
DL_ENGINE m_engine_type;
|
||||
|
@ -249,27 +251,32 @@ namespace datalog {
|
|||
bool fix_unbound_vars() const;
|
||||
symbol default_table() const;
|
||||
symbol default_relation() const;
|
||||
symbol default_table_checker() const;
|
||||
void set_default_relation(symbol const& s);
|
||||
symbol default_table_checker() const;
|
||||
symbol check_relation() const;
|
||||
bool default_table_checked() const;
|
||||
bool dbg_fpr_nonempty_relation_signature() const;
|
||||
unsigned dl_profile_milliseconds_threshold() const;
|
||||
bool all_or_nothing_deltas() const;
|
||||
bool compile_with_widening() const;
|
||||
bool unbound_compressor() const;
|
||||
void set_unbound_compressor(bool f);
|
||||
bool similarity_compressor() const;
|
||||
symbol print_aig() const;
|
||||
symbol tab_selection() const;
|
||||
unsigned similarity_compressor_threshold() const;
|
||||
unsigned timeout() const;
|
||||
unsigned soft_timeout() const;
|
||||
unsigned initial_restart_timeout() const;
|
||||
bool generate_explanations() const;
|
||||
bool explanations_on_relation_level() const;
|
||||
bool magic_sets_for_queries() const;
|
||||
bool eager_emptiness_checking() const;
|
||||
bool bit_blast() const;
|
||||
bool karr() const;
|
||||
bool scale() const;
|
||||
bool magic() const;
|
||||
bool quantify_arrays() const;
|
||||
bool instantiate_quantifiers() const;
|
||||
bool xform_bit_blast() const;
|
||||
bool xform_slice() const;
|
||||
|
||||
void register_finite_sort(sort * s, sort_kind k);
|
||||
|
||||
|
@ -286,12 +293,14 @@ namespace datalog {
|
|||
universal (if is_forall is true) or existential
|
||||
quantifier.
|
||||
*/
|
||||
expr_ref bind_variables(expr* fml, bool is_forall);
|
||||
expr_ref bind_vars(expr* fml, bool is_forall);
|
||||
|
||||
bool& bind_vars_enabled() { return m_enable_bind_variables; }
|
||||
|
||||
/**
|
||||
Register datalog relation.
|
||||
|
||||
If names 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.
|
||||
*/
|
||||
|
@ -366,7 +375,7 @@ namespace datalog {
|
|||
|
||||
rule_set & get_rules() { flush_add_rules(); return m_rule_set; }
|
||||
|
||||
void get_rules_as_formulas(expr_ref_vector& fmls, svector<symbol>& names);
|
||||
void get_rules_as_formulas(expr_ref_vector& fmls, expr_ref_vector& qs, svector<symbol>& names);
|
||||
void get_raw_rule_formulas(expr_ref_vector& fmls, svector<symbol>& names, vector<unsigned> &bounds);
|
||||
|
||||
void add_fact(app * head);
|
||||
|
@ -420,7 +429,7 @@ namespace datalog {
|
|||
/**
|
||||
\brief Check if rule is well-formed according to engine.
|
||||
*/
|
||||
void check_rule(rule_ref& r);
|
||||
void check_rules(rule_set& r);
|
||||
|
||||
/**
|
||||
\brief Return true if facts to \c pred can be added using the \c add_table_fact() function.
|
||||
|
@ -467,7 +476,7 @@ namespace datalog {
|
|||
|
||||
void display(std::ostream & out) const;
|
||||
|
||||
void display_smt2(unsigned num_queries, expr* const* queries, std::ostream& out);
|
||||
void display_smt2(unsigned num_queries, expr* const* qs, std::ostream& out);
|
||||
|
||||
void display_profile(std::ostream& out) const;
|
||||
|
||||
|
@ -566,11 +575,6 @@ namespace datalog {
|
|||
|
||||
void ensure_engine();
|
||||
|
||||
void check_quantifier_free(rule_ref& r);
|
||||
void check_uninterpreted_free(rule_ref& r);
|
||||
void check_existential_tail(rule_ref& r);
|
||||
void check_positive_predicates(rule_ref& r);
|
||||
|
||||
// auxilary functions for SMT2 pretty-printer.
|
||||
void declare_vars(expr_ref_vector& rules, mk_fresh_name& mk_fresh, std::ostream& out);
|
||||
|
||||
|
|
|
@ -30,8 +30,9 @@ namespace datalog {
|
|||
QBMC_ENGINE,
|
||||
TAB_ENGINE,
|
||||
CLP_ENGINE,
|
||||
LAST_ENGINE,
|
||||
DUALITY_ENGINE
|
||||
DUALITY_ENGINE,
|
||||
DDNF_ENGINE,
|
||||
LAST_ENGINE
|
||||
};
|
||||
|
||||
class engine_base {
|
||||
|
|
|
@ -56,7 +56,8 @@ namespace datalog {
|
|||
m_hnf(m),
|
||||
m_qe(m),
|
||||
m_cfg(m),
|
||||
m_rwr(m, false, m_cfg) {}
|
||||
m_rwr(m, false, m_cfg),
|
||||
m_ufproc(m) {}
|
||||
|
||||
void rule_manager::inc_ref(rule * r) {
|
||||
if (r) {
|
||||
|
@ -111,16 +112,14 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void rule_manager::reset_collect_vars() {
|
||||
m_vars.reset();
|
||||
m_var_idx.reset();
|
||||
m_todo.reset();
|
||||
m_mark.reset();
|
||||
m_free_vars.reset();
|
||||
}
|
||||
|
||||
var_idx_set& rule_manager::finalize_collect_vars() {
|
||||
unsigned sz = m_vars.size();
|
||||
for (unsigned i=0; i<sz; ++i) {
|
||||
if (m_vars[i]) m_var_idx.insert(i);
|
||||
unsigned sz = m_free_vars.size();
|
||||
for (unsigned i = 0; i < sz; ++i) {
|
||||
if (m_free_vars[i]) m_var_idx.insert(i);
|
||||
}
|
||||
return m_var_idx;
|
||||
}
|
||||
|
@ -157,7 +156,7 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void rule_manager::accumulate_vars(expr* e) {
|
||||
::get_free_vars(m_mark, m_todo, e, m_vars);
|
||||
m_free_vars.accumulate(e);
|
||||
}
|
||||
|
||||
|
||||
|
@ -188,8 +187,6 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void rule_manager::mk_rule_core(expr* fml, proof* p, rule_set& rules, symbol const& name) {
|
||||
DEBUG_CODE(ptr_vector<sort> sorts;
|
||||
::get_free_vars(fml, sorts); );
|
||||
expr_ref_vector fmls(m);
|
||||
proof_ref_vector prs(m);
|
||||
m_hnf.reset();
|
||||
|
@ -200,8 +197,6 @@ namespace datalog {
|
|||
m_ctx.register_predicate(m_hnf.get_fresh_predicates()[i], false);
|
||||
}
|
||||
for (unsigned i = 0; i < fmls.size(); ++i) {
|
||||
DEBUG_CODE(ptr_vector<sort> sorts;
|
||||
::get_free_vars(fmls[i].get(), sorts); );
|
||||
mk_horn_rule(fmls[i].get(), prs[i].get(), rules, name);
|
||||
}
|
||||
}
|
||||
|
@ -228,7 +223,7 @@ namespace datalog {
|
|||
|
||||
expr_ref fml1(m);
|
||||
if (p) {
|
||||
r->to_formula(fml1);
|
||||
to_formula(*r, fml1);
|
||||
if (fml1 == fml) {
|
||||
// no-op.
|
||||
}
|
||||
|
@ -246,7 +241,7 @@ namespace datalog {
|
|||
|
||||
if (p) {
|
||||
expr_ref fml2(m);
|
||||
r->to_formula(fml2);
|
||||
to_formula(*r, fml2);
|
||||
if (fml1 != fml2) {
|
||||
p = m.mk_modus_ponens(p, m.mk_rewrite(fml1, fml2));
|
||||
}
|
||||
|
@ -257,17 +252,16 @@ namespace datalog {
|
|||
|
||||
unsigned rule_manager::extract_horn(expr* fml, app_ref_vector& body, app_ref& head) {
|
||||
expr* e1, *e2;
|
||||
unsigned index = m_counter.get_next_var(fml);
|
||||
if (::is_forall(fml)) {
|
||||
index += to_quantifier(fml)->get_num_decls();
|
||||
fml = to_quantifier(fml)->get_expr();
|
||||
}
|
||||
unsigned index = m_counter.get_next_var(fml);
|
||||
if (m.is_implies(fml, e1, e2)) {
|
||||
expr_ref_vector es(m);
|
||||
m_args.reset();
|
||||
head = ensure_app(e2);
|
||||
qe::flatten_and(e1, es);
|
||||
for (unsigned i = 0; i < es.size(); ++i) {
|
||||
body.push_back(ensure_app(es[i].get()));
|
||||
qe::flatten_and(e1, m_args);
|
||||
for (unsigned i = 0; i < m_args.size(); ++i) {
|
||||
body.push_back(ensure_app(m_args[i].get()));
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
@ -299,7 +293,8 @@ namespace datalog {
|
|||
quantifier_hoister qh(m);
|
||||
qh.pull_quantifier(false, q, 0, &names);
|
||||
// retrieve free variables.
|
||||
get_free_vars(q, vars);
|
||||
m_free_vars(q);
|
||||
vars.append(m_free_vars.size(), m_free_vars.c_ptr());
|
||||
if (vars.contains(static_cast<sort*>(0))) {
|
||||
var_subst sub(m, false);
|
||||
expr_ref_vector args(m);
|
||||
|
@ -316,7 +311,8 @@ namespace datalog {
|
|||
}
|
||||
sub(q, args.size(), args.c_ptr(), q);
|
||||
vars.reset();
|
||||
get_free_vars(q, vars);
|
||||
m_free_vars(q);
|
||||
vars.append(m_free_vars.size(), m_free_vars.c_ptr());
|
||||
}
|
||||
SASSERT(!vars.contains(static_cast<sort*>(0)) && "Unused variables have been eliminated");
|
||||
|
||||
|
@ -373,7 +369,7 @@ namespace datalog {
|
|||
}
|
||||
|
||||
void rule_manager::bind_variables(expr* fml, bool is_forall, expr_ref& result) {
|
||||
result = m_ctx.bind_variables(fml, is_forall);
|
||||
result = m_ctx.bind_vars(fml, is_forall);
|
||||
}
|
||||
|
||||
void rule_manager::flatten_body(app_ref_vector& body) {
|
||||
|
@ -498,11 +494,6 @@ namespace datalog {
|
|||
app * * uninterp_tail = r->m_tail; //grows upwards
|
||||
app * * interp_tail = r->m_tail+n; //grows downwards
|
||||
|
||||
DEBUG_CODE(ptr_vector<sort> sorts;
|
||||
::get_free_vars(head, sorts);
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
::get_free_vars(tail[i], sorts);
|
||||
});
|
||||
|
||||
bool has_neg = false;
|
||||
|
||||
|
@ -556,11 +547,6 @@ namespace datalog {
|
|||
if (normalize) {
|
||||
r->norm_vars(*this);
|
||||
}
|
||||
DEBUG_CODE(ptr_vector<sort> sorts;
|
||||
::get_free_vars(head, sorts);
|
||||
for (unsigned i = 0; i < n; ++i) {
|
||||
::get_free_vars(tail[i], sorts);
|
||||
});
|
||||
return r;
|
||||
}
|
||||
|
||||
|
@ -587,6 +573,55 @@ namespace datalog {
|
|||
return r;
|
||||
}
|
||||
|
||||
void rule_manager::to_formula(rule const& r, expr_ref& fml) {
|
||||
ast_manager & m = fml.get_manager();
|
||||
expr_ref_vector body(m);
|
||||
for (unsigned i = 0; i < r.get_tail_size(); i++) {
|
||||
body.push_back(r.get_tail(i));
|
||||
if (r.is_neg_tail(i)) {
|
||||
body[body.size()-1] = m.mk_not(body.back());
|
||||
}
|
||||
}
|
||||
fml = r.get_head();
|
||||
switch (body.size()) {
|
||||
case 0: break;
|
||||
case 1: fml = m.mk_implies(body[0].get(), fml); break;
|
||||
default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), fml); break;
|
||||
}
|
||||
|
||||
m_free_vars(fml);
|
||||
if (m_free_vars.empty()) {
|
||||
return;
|
||||
}
|
||||
svector<symbol> names;
|
||||
used_symbols<> us;
|
||||
m_free_vars.set_default_sort(m.mk_bool_sort());
|
||||
|
||||
us(fml);
|
||||
m_free_vars.reverse();
|
||||
for (unsigned j = 0, i = 0; i < m_free_vars.size(); ++j) {
|
||||
for (char c = 'A'; i < m_free_vars.size() && c <= 'Z'; ++c) {
|
||||
func_decl_ref f(m);
|
||||
std::stringstream _name;
|
||||
_name << c;
|
||||
if (j > 0) _name << j;
|
||||
symbol name(_name.str().c_str());
|
||||
if (!us.contains(name)) {
|
||||
names.push_back(name);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
fml = m.mk_forall(m_free_vars.size(), m_free_vars.c_ptr(), names.c_ptr(), fml);
|
||||
}
|
||||
|
||||
std::ostream& rule_manager::display_smt2(rule const& r, std::ostream & out) {
|
||||
expr_ref fml(m);
|
||||
to_formula(r, fml);
|
||||
return out << mk_ismt2_pp(fml, m);
|
||||
}
|
||||
|
||||
|
||||
void rule_manager::reduce_unbound_vars(rule_ref& r) {
|
||||
unsigned ut_len = r->get_uninterpreted_tail_size();
|
||||
unsigned t_len = r->get_tail_size();
|
||||
|
@ -647,9 +682,7 @@ namespace datalog {
|
|||
svector<bool> tail_neg;
|
||||
app_ref head(r->get_head(), m);
|
||||
|
||||
collect_rule_vars(r);
|
||||
vctr.count_vars(m, head);
|
||||
ptr_vector<sort>& free_rule_vars = m_vars;
|
||||
|
||||
for (unsigned i = 0; i < ut_len; i++) {
|
||||
app * t = r->get_tail(i);
|
||||
|
@ -658,18 +691,16 @@ namespace datalog {
|
|||
tail_neg.push_back(r->is_neg_tail(i));
|
||||
}
|
||||
|
||||
ptr_vector<sort> interp_vars;
|
||||
var_idx_set unbound_vars;
|
||||
expr_ref_vector tails_with_unbound(m);
|
||||
|
||||
for (unsigned i = ut_len; i < t_len; i++) {
|
||||
app * t = r->get_tail(i);
|
||||
interp_vars.reset();
|
||||
::get_free_vars(t, interp_vars);
|
||||
m_free_vars(t);
|
||||
bool has_unbound = false;
|
||||
unsigned iv_size = interp_vars.size();
|
||||
unsigned iv_size = m_free_vars.size();
|
||||
for (unsigned i=0; i<iv_size; i++) {
|
||||
if (!interp_vars[i]) { continue; }
|
||||
if (!m_free_vars[i]) { continue; }
|
||||
if (vctr.get(i)==0) {
|
||||
has_unbound = true;
|
||||
unbound_vars.insert(i);
|
||||
|
@ -693,16 +724,15 @@ namespace datalog {
|
|||
bool_rewriter(m).mk_and(tails_with_unbound.size(), tails_with_unbound.c_ptr(), unbound_tail);
|
||||
|
||||
unsigned q_var_cnt = unbound_vars.num_elems();
|
||||
unsigned max_var = m_counter.get_max_rule_var(*r);
|
||||
|
||||
collect_rule_vars(r);
|
||||
expr_ref_vector subst(m);
|
||||
|
||||
ptr_vector<sort> qsorts;
|
||||
qsorts.resize(q_var_cnt);
|
||||
|
||||
unsigned q_idx = 0;
|
||||
for (unsigned v = 0; v <= max_var; ++v) {
|
||||
sort * v_sort = free_rule_vars[v];
|
||||
for (unsigned v = 0; v < m_free_vars.size(); ++v) {
|
||||
sort * v_sort = m_free_vars[v];
|
||||
if (!v_sort) {
|
||||
//this variable index is not used
|
||||
continue;
|
||||
|
@ -780,7 +810,7 @@ namespace datalog {
|
|||
!new_rule.get_proof() &&
|
||||
old_rule.get_proof()) {
|
||||
expr_ref fml(m);
|
||||
new_rule.to_formula(fml);
|
||||
to_formula(new_rule, fml);
|
||||
scoped_proof _sc(m);
|
||||
proof* p = m.mk_rewrite(m.get_fact(old_rule.get_proof()), fml);
|
||||
new_rule.set_proof(m, m.mk_modus_ponens(old_rule.get_proof(), p));
|
||||
|
@ -791,7 +821,7 @@ namespace datalog {
|
|||
if (m_ctx.generate_proof_trace()) {
|
||||
scoped_proof _scp(m);
|
||||
expr_ref fml(m);
|
||||
r.to_formula(fml);
|
||||
to_formula(r, fml);
|
||||
r.set_proof(m, m.mk_asserted(fml));
|
||||
}
|
||||
}
|
||||
|
@ -881,84 +911,40 @@ namespace datalog {
|
|||
return false;
|
||||
}
|
||||
|
||||
struct uninterpreted_function_finder_proc {
|
||||
ast_manager& m;
|
||||
datatype_util m_dt;
|
||||
dl_decl_util m_dl;
|
||||
bool m_found;
|
||||
func_decl* m_func;
|
||||
uninterpreted_function_finder_proc(ast_manager& m):
|
||||
m(m), m_dt(m), m_dl(m), m_found(false), m_func(0) {}
|
||||
void operator()(var * n) { }
|
||||
void operator()(quantifier * n) { }
|
||||
void operator()(app * n) {
|
||||
if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) {
|
||||
m_found = true;
|
||||
m_func = n->get_decl();
|
||||
}
|
||||
else if (m_dt.is_accessor(n)) {
|
||||
sort* s = m.get_sort(n->get_arg(0));
|
||||
SASSERT(m_dt.is_datatype(s));
|
||||
if (m_dt.get_datatype_constructors(s)->size() > 1) {
|
||||
m_found = true;
|
||||
m_func = n->get_decl();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool found(func_decl*& f) const { f = m_func; return m_found; }
|
||||
};
|
||||
|
||||
//
|
||||
// non-predicates may appear only in the interpreted tail, it is therefore
|
||||
// sufficient only to check the tail.
|
||||
//
|
||||
bool rule::has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const {
|
||||
unsigned sz = get_tail_size();
|
||||
uninterpreted_function_finder_proc proc(m);
|
||||
expr_mark visited;
|
||||
for (unsigned i = get_uninterpreted_tail_size(); i < sz && !proc.found(f); ++i) {
|
||||
for_each_expr(proc, visited, get_tail(i));
|
||||
bool rule_manager::has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const {
|
||||
unsigned sz = r.get_tail_size();
|
||||
m_ufproc.reset();
|
||||
m_visited.reset();
|
||||
for (unsigned i = r.get_uninterpreted_tail_size(); i < sz && !m_ufproc.found(f); ++i) {
|
||||
for_each_expr_core<uninterpreted_function_finder_proc,expr_sparse_mark, true, false>(m_ufproc, m_visited, r.get_tail(i));
|
||||
}
|
||||
return proc.found(f);
|
||||
return m_ufproc.found(f);
|
||||
}
|
||||
|
||||
|
||||
struct quantifier_finder_proc {
|
||||
bool m_exist;
|
||||
bool m_univ;
|
||||
quantifier_finder_proc() : m_exist(false), m_univ(false) {}
|
||||
void operator()(var * n) { }
|
||||
void operator()(quantifier * n) {
|
||||
if (n->is_forall()) {
|
||||
m_univ = true;
|
||||
}
|
||||
else {
|
||||
SASSERT(n->is_exists());
|
||||
m_exist = true;
|
||||
}
|
||||
}
|
||||
void operator()(app * n) { }
|
||||
};
|
||||
|
||||
//
|
||||
// Quantifiers may appear only in the interpreted tail, it is therefore
|
||||
// sufficient only to check the interpreted tail.
|
||||
//
|
||||
void rule::has_quantifiers(bool& existential, bool& universal) const {
|
||||
unsigned sz = get_tail_size();
|
||||
quantifier_finder_proc proc;
|
||||
expr_mark visited;
|
||||
for (unsigned i = get_uninterpreted_tail_size(); i < sz; ++i) {
|
||||
for_each_expr(proc, visited, get_tail(i));
|
||||
void rule_manager::has_quantifiers(rule const& r, bool& existential, bool& universal) const {
|
||||
unsigned sz = r.get_tail_size();
|
||||
m_qproc.reset();
|
||||
m_visited.reset();
|
||||
for (unsigned i = r.get_uninterpreted_tail_size(); i < sz; ++i) {
|
||||
for_each_expr_core<quantifier_finder_proc,expr_sparse_mark, true, false>(m_qproc, m_visited, r.get_tail(i));
|
||||
}
|
||||
existential = proc.m_exist;
|
||||
universal = proc.m_univ;
|
||||
existential = m_qproc.m_exist;
|
||||
universal = m_qproc.m_univ;
|
||||
}
|
||||
|
||||
bool rule::has_quantifiers() const {
|
||||
bool rule_manager::has_quantifiers(rule const& r) const {
|
||||
bool exist, univ;
|
||||
has_quantifiers(exist, univ);
|
||||
has_quantifiers(r, exist, univ);
|
||||
return exist || univ;
|
||||
}
|
||||
|
||||
|
@ -1067,57 +1053,6 @@ namespace datalog {
|
|||
}
|
||||
}
|
||||
|
||||
void rule::to_formula(expr_ref& fml) const {
|
||||
ast_manager & m = fml.get_manager();
|
||||
expr_ref_vector body(m);
|
||||
for (unsigned i = 0; i < m_tail_size; i++) {
|
||||
body.push_back(get_tail(i));
|
||||
if (is_neg_tail(i)) {
|
||||
body[body.size()-1] = m.mk_not(body.back());
|
||||
}
|
||||
}
|
||||
switch(body.size()) {
|
||||
case 0: fml = m_head; break;
|
||||
case 1: fml = m.mk_implies(body[0].get(), m_head); break;
|
||||
default: fml = m.mk_implies(m.mk_and(body.size(), body.c_ptr()), m_head); break;
|
||||
}
|
||||
|
||||
ptr_vector<sort> sorts;
|
||||
get_free_vars(fml, sorts);
|
||||
if (sorts.empty()) {
|
||||
return;
|
||||
}
|
||||
svector<symbol> names;
|
||||
used_symbols<> us;
|
||||
for (unsigned i = 0; i < sorts.size(); ++i) {
|
||||
if (!sorts[i]) {
|
||||
sorts[i] = m.mk_bool_sort();
|
||||
}
|
||||
}
|
||||
|
||||
us(fml);
|
||||
sorts.reverse();
|
||||
for (unsigned j = 0, i = 0; i < sorts.size(); ++j) {
|
||||
for (char c = 'A'; i < sorts.size() && c <= 'Z'; ++c) {
|
||||
func_decl_ref f(m);
|
||||
std::stringstream _name;
|
||||
_name << c;
|
||||
if (j > 0) _name << j;
|
||||
symbol name(_name.str().c_str());
|
||||
if (!us.contains(name)) {
|
||||
names.push_back(name);
|
||||
++i;
|
||||
}
|
||||
}
|
||||
}
|
||||
fml = m.mk_forall(sorts.size(), sorts.c_ptr(), names.c_ptr(), fml);
|
||||
}
|
||||
|
||||
std::ostream& rule::display_smt2(ast_manager& m, std::ostream & out) const {
|
||||
expr_ref fml(m);
|
||||
to_formula(fml);
|
||||
return out << mk_ismt2_pp(fml, m);
|
||||
}
|
||||
|
||||
bool rule_eq_proc::operator()(const rule * r1, const rule * r2) const {
|
||||
if (r1->get_head()!=r2->get_head()) { return false; }
|
||||
|
|
|
@ -30,6 +30,8 @@ Revision History:
|
|||
#include"rewriter.h"
|
||||
#include"hnf.h"
|
||||
#include"qe_lite.h"
|
||||
#include"var_subst.h"
|
||||
#include"datatype_decl_plugin.h"
|
||||
|
||||
namespace datalog {
|
||||
|
||||
|
@ -42,6 +44,56 @@ namespace datalog {
|
|||
typedef obj_ref<rule, rule_manager> rule_ref;
|
||||
typedef ref_vector<rule, rule_manager> rule_ref_vector;
|
||||
typedef ptr_vector<rule> rule_vector;
|
||||
|
||||
|
||||
struct uninterpreted_function_finder_proc {
|
||||
ast_manager& m;
|
||||
datatype_util m_dt;
|
||||
dl_decl_util m_dl;
|
||||
bool m_found;
|
||||
func_decl* m_func;
|
||||
uninterpreted_function_finder_proc(ast_manager& m):
|
||||
m(m), m_dt(m), m_dl(m), m_found(false), m_func(0) {}
|
||||
void operator()(var * n) { }
|
||||
void operator()(quantifier * n) { }
|
||||
void operator()(app * n) {
|
||||
if (is_uninterp(n) && !m_dl.is_rule_sort(n->get_decl()->get_range())) {
|
||||
m_found = true;
|
||||
m_func = n->get_decl();
|
||||
}
|
||||
else if (m_dt.is_accessor(n)) {
|
||||
sort* s = m.get_sort(n->get_arg(0));
|
||||
SASSERT(m_dt.is_datatype(s));
|
||||
if (m_dt.get_datatype_constructors(s)->size() > 1) {
|
||||
m_found = true;
|
||||
m_func = n->get_decl();
|
||||
}
|
||||
}
|
||||
}
|
||||
void reset() { m_found = false; m_func = 0; }
|
||||
|
||||
bool found(func_decl*& f) const { f = m_func; return m_found; }
|
||||
};
|
||||
|
||||
struct quantifier_finder_proc {
|
||||
bool m_exist;
|
||||
bool m_univ;
|
||||
quantifier_finder_proc() : m_exist(false), m_univ(false) {}
|
||||
void operator()(var * n) { }
|
||||
void operator()(quantifier * n) {
|
||||
if (n->is_forall()) {
|
||||
m_univ = true;
|
||||
}
|
||||
else {
|
||||
SASSERT(n->is_exists());
|
||||
m_exist = true;
|
||||
}
|
||||
}
|
||||
void operator()(app * n) { }
|
||||
void reset() { m_exist = m_univ = false; }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
\brief Manager for the \c rule class
|
||||
|
||||
|
@ -64,10 +116,8 @@ namespace datalog {
|
|||
context& m_ctx;
|
||||
rule_counter m_counter;
|
||||
used_vars m_used;
|
||||
ptr_vector<sort> m_vars;
|
||||
var_idx_set m_var_idx;
|
||||
ptr_vector<expr> m_todo;
|
||||
ast_mark m_mark;
|
||||
expr_free_vars m_free_vars;
|
||||
app_ref_vector m_body;
|
||||
app_ref m_head;
|
||||
expr_ref_vector m_args;
|
||||
|
@ -76,6 +126,9 @@ namespace datalog {
|
|||
qe_lite m_qe;
|
||||
remove_label_cfg m_cfg;
|
||||
rewriter_tpl<remove_label_cfg> m_rwr;
|
||||
mutable uninterpreted_function_finder_proc m_ufproc;
|
||||
mutable quantifier_finder_proc m_qproc;
|
||||
mutable expr_sparse_mark m_visited;
|
||||
|
||||
|
||||
// only the context can create a rule_manager
|
||||
|
@ -143,7 +196,7 @@ namespace datalog {
|
|||
|
||||
void accumulate_vars(expr* pred);
|
||||
|
||||
ptr_vector<sort>& get_var_sorts() { return m_vars; }
|
||||
// ptr_vector<sort>& get_var_sorts() { return m_vars; }
|
||||
|
||||
var_idx_set& get_var_idx() { return m_var_idx; }
|
||||
|
||||
|
@ -213,11 +266,18 @@ namespace datalog {
|
|||
*/
|
||||
bool is_fact(app * head) const;
|
||||
|
||||
|
||||
static bool is_forall(ast_manager& m, expr* e, quantifier*& q);
|
||||
|
||||
rule_counter& get_counter() { return m_counter; }
|
||||
|
||||
void to_formula(rule const& r, expr_ref& result);
|
||||
|
||||
std::ostream& display_smt2(rule const& r, std::ostream & out);
|
||||
|
||||
bool has_uninterpreted_non_predicates(rule const& r, func_decl*& f) const;
|
||||
void has_quantifiers(rule const& r, bool& existential, bool& universal) const;
|
||||
bool has_quantifiers(rule const& r) const;
|
||||
|
||||
};
|
||||
|
||||
class rule : public accounted_object {
|
||||
|
@ -293,9 +353,6 @@ namespace datalog {
|
|||
*/
|
||||
bool is_in_tail(const func_decl * p, bool only_positive=false) const;
|
||||
|
||||
bool has_uninterpreted_non_predicates(ast_manager& m, func_decl*& f) const;
|
||||
void has_quantifiers(bool& existential, bool& universal) const;
|
||||
bool has_quantifiers() const;
|
||||
bool has_negation() const;
|
||||
|
||||
/**
|
||||
|
@ -306,12 +363,8 @@ namespace datalog {
|
|||
|
||||
void get_vars(ast_manager& m, ptr_vector<sort>& sorts) const;
|
||||
|
||||
void to_formula(expr_ref& result) const;
|
||||
|
||||
void display(context & ctx, std::ostream & out) const;
|
||||
|
||||
std::ostream& display_smt2(ast_manager& m, std::ostream & out) const;
|
||||
|
||||
symbol const& name() const { return m_name; }
|
||||
|
||||
unsigned hash() const;
|
||||
|
|
|
@ -39,10 +39,10 @@ namespace datalog {
|
|||
Each master object is also present as a key of the map, even if its master set
|
||||
is empty.
|
||||
*/
|
||||
deps_type m_data;
|
||||
context & m_context;
|
||||
deps_type m_data;
|
||||
context & m_context;
|
||||
ptr_vector<expr> m_todo;
|
||||
ast_mark m_visited;
|
||||
expr_sparse_mark m_visited;
|
||||
|
||||
|
||||
//we need to take care with removing to avoid memory leaks
|
||||
|
|
|
@ -56,9 +56,9 @@ namespace datalog {
|
|||
|
||||
|
||||
bool contains_var(expr * trm, unsigned var_idx) {
|
||||
ptr_vector<sort> vars;
|
||||
::get_free_vars(trm, vars);
|
||||
return var_idx < vars.size() && vars[var_idx] != 0;
|
||||
expr_free_vars fv;
|
||||
fv(trm);
|
||||
return fv.contains(var_idx);
|
||||
}
|
||||
|
||||
unsigned count_variable_arguments(app * pred)
|
||||
|
@ -300,14 +300,15 @@ namespace datalog {
|
|||
}
|
||||
|
||||
|
||||
void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
|
||||
void resolve_rule(rule_manager& rm,
|
||||
replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
|
||||
expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res) {
|
||||
if (!pc) return;
|
||||
ast_manager& m = s1.get_manager();
|
||||
expr_ref fml1(m), fml2(m), fml3(m);
|
||||
r1.to_formula(fml1);
|
||||
r2.to_formula(fml2);
|
||||
res.to_formula(fml3);
|
||||
rm.to_formula(r1, fml1);
|
||||
rm.to_formula(r2, fml2);
|
||||
rm.to_formula(res, fml3);
|
||||
vector<expr_ref_vector> substs;
|
||||
svector<std::pair<unsigned, unsigned> > positions;
|
||||
substs.push_back(s1);
|
||||
|
@ -337,7 +338,7 @@ namespace datalog {
|
|||
pc->insert(pr);
|
||||
}
|
||||
|
||||
void resolve_rule(rule const& r1, rule const& r2, unsigned idx,
|
||||
void resolve_rule(rule_manager& rm, rule const& r1, rule const& r2, unsigned idx,
|
||||
expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res) {
|
||||
if (!r1.get_proof()) {
|
||||
return;
|
||||
|
@ -345,7 +346,7 @@ namespace datalog {
|
|||
SASSERT(r2.get_proof());
|
||||
ast_manager& m = s1.get_manager();
|
||||
expr_ref fml(m);
|
||||
res.to_formula(fml);
|
||||
rm.to_formula(res, fml);
|
||||
vector<expr_ref_vector> substs;
|
||||
svector<std::pair<unsigned, unsigned> > positions;
|
||||
substs.push_back(s1);
|
||||
|
|
|
@ -41,12 +41,13 @@ namespace datalog {
|
|||
class pentagon_relation;
|
||||
class relation_fact;
|
||||
class relation_signature;
|
||||
class rule_manager;
|
||||
|
||||
class verbose_action {
|
||||
unsigned m_lvl;
|
||||
class stopwatch* m_sw;
|
||||
public:
|
||||
verbose_action(char const* msg, unsigned lvl = 1);
|
||||
verbose_action(char const* msg, unsigned lvl = 11);
|
||||
~verbose_action();
|
||||
};
|
||||
|
||||
|
@ -345,17 +346,19 @@ namespace datalog {
|
|||
|
||||
class rule_counter : public var_counter {
|
||||
public:
|
||||
rule_counter(bool stay_non_negative = true): var_counter(stay_non_negative) {}
|
||||
rule_counter(){}
|
||||
void count_rule_vars(ast_manager & m, const rule * r, int coef = 1);
|
||||
unsigned get_max_rule_var(const rule& r);
|
||||
};
|
||||
|
||||
void del_rule(horn_subsume_model_converter* mc, rule& r);
|
||||
|
||||
void resolve_rule(replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
|
||||
void resolve_rule(rule_manager& rm,
|
||||
replace_proof_converter* pc, rule const& r1, rule const& r2, unsigned idx,
|
||||
expr_ref_vector const& s1, expr_ref_vector const& s2, rule const& res);
|
||||
|
||||
void resolve_rule(rule const& r1, rule const& r2, unsigned idx,
|
||||
void resolve_rule(rule_manager& rm,
|
||||
rule const& r1, rule const& r2, unsigned idx,
|
||||
expr_ref_vector const& s1, expr_ref_vector const& s2, rule& res);
|
||||
|
||||
model_converter* mk_skip_model_converter();
|
||||
|
|
|
@ -2,83 +2,147 @@ def_module_params('fixedpoint',
|
|||
description='fixedpoint parameters',
|
||||
export=True,
|
||||
params=(('timeout', UINT, UINT_MAX, 'set timeout'),
|
||||
('engine', SYMBOL, 'auto-config', 'Select: auto-config, datalog, pdr, bmc'),
|
||||
('default_table', SYMBOL, 'sparse', 'default table implementation: sparse, hashtable, bitvector, interval'),
|
||||
('default_relation', SYMBOL, 'pentagon', 'default relation implementation: external_relation, pentagon'),
|
||||
('generate_explanations', BOOL, False, '(DATALOG) produce explanations for produced facts when using the datalog engine'),
|
||||
('use_map_names', BOOL, True, "(DATALOG) use names from map files when displaying tuples"),
|
||||
('bit_blast', BOOL, False, '(PDR) bit-blast bit-vectors'),
|
||||
('explanations_on_relation_level', BOOL, False, '(DATALOG) 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)'),
|
||||
('magic_sets_for_queries', BOOL, False, "(DATALOG) magic set transformation will be used for queries"),
|
||||
('magic', BOOL, False, "perform symbolic magic set transformation"),
|
||||
('scale', BOOL, False, "add scaling variable to linear real arithemtic clauses"),
|
||||
('unbound_compressor', BOOL, True, "auxiliary relations will be introduced to avoid unbound variables in rule heads"),
|
||||
('similarity_compressor', BOOL, True, "(DATALOG) rules that differ only in values of constants will be merged into a single rule"),
|
||||
('similarity_compressor_threshold', UINT, 11, "(DATALOG) if similarity_compressor is on, this value determines how many similar rules there must be in order for them to be merged"),
|
||||
('all_or_nothing_deltas', BOOL, False, "(DATALOG) compile rules so that it is enough for the delta relation in union and widening operations to determine only whether the updated relation was modified or not"),
|
||||
('compile_with_widening', BOOL, False, "(DATALOG) widening will be used to compile recursive rules"),
|
||||
('eager_emptiness_checking', BOOL, True, "(DATALOG) emptiness of affected relations will be checked after each instruction, so that we may ommit unnecessary instructions"),
|
||||
('default_table_checked', BOOL, False, "if true, the detault table will be default_table inside a wrapper that checks that its results are the same as of default_table_checker table"),
|
||||
('default_table_checker', SYMBOL, 'null', "see default_table_checked"),
|
||||
|
||||
|
||||
('initial_restart_timeout', UINT, 0, "length of saturation run before the first restart (in ms), zero means no restarts"),
|
||||
('output_profile', BOOL, False, "determines whether profile informations should be output when outputting Datalog rules or instructions"),
|
||||
('inline_linear', BOOL, True, "try linear inlining method"),
|
||||
('inline_eager', BOOL, True, "try eager inlining of rules"),
|
||||
('inline_linear_branch', BOOL, False, "try linear inlining method with potential expansion"),
|
||||
('fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
|
||||
('output_tuples', BOOL, True, "determines whether tuples for output predicates should be output"),
|
||||
('print_with_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 but the result may not be as readable)"),
|
||||
('print_with_variable_declarations', BOOL, True, "use variable declarations when displaying rules (instead of attempting to use original names)"),
|
||||
('bfs_model_search', BOOL, True, "PDR: use BFS strategy for expanding model search"),
|
||||
('use_farkas', BOOL, True, "PDR: use lemma generator based on Farkas (for linear real arithmetic)"),
|
||||
('generate_proof_trace', BOOL, False, "PDR: trace for 'sat' answer as proof object"),
|
||||
('flexible_trace', BOOL, False, "PDR: allow PDR generate long counter-examples "
|
||||
"by extending candidate trace within search area"),
|
||||
('unfold_rules', UINT, 0, "PDR: unfold rules statically using iterative squarring"),
|
||||
('use_model_generalizer', BOOL, False, "PDR: use model for backwards propagation (instead of symbolic simulation)"),
|
||||
('validate_result', BOOL, False, "PDR validate result (by proof checking or model checking)"),
|
||||
('simplify_formulas_pre', BOOL, False, "PDR: simplify derived formulas before inductive propagation"),
|
||||
('simplify_formulas_post', BOOL, False, "PDR: simplify derived formulas after inductive propagation"),
|
||||
('slice', BOOL, True, "PDR: simplify clause set using slicing"),
|
||||
('karr', BOOL, False, "Add linear invariants to clauses using Karr's method"),
|
||||
('quantify_arrays', BOOL, False, "create quantified Horn clauses from clauses with arrays"),
|
||||
('instantiate_quantifiers', BOOL, False, "instantiate quantified Horn clauses using E-matching heuristic"),
|
||||
('coalesce_rules', BOOL, False, "BMC: coalesce rules"),
|
||||
('use_multicore_generalizer', BOOL, False, "PDR: extract multiple cores for blocking states"),
|
||||
('use_inductive_generalizer', BOOL, True, "PDR: generalize lemmas using induction strengthening"),
|
||||
('use_arith_inductive_generalizer', BOOL, False, "PDR: generalize lemmas using arithmetic heuristics for induction strengthening"),
|
||||
('use_convex_closure_generalizer', BOOL, False, "PDR: generalize using convex closures of lemmas"),
|
||||
('use_convex_interior_generalizer', BOOL, False, "PDR: generalize using convex interiors of lemmas"),
|
||||
('cache_mode', UINT, 0, "PDR: use no (0), symbolic (1) or explicit cache (2) for model search"),
|
||||
('inductive_reachability_check', BOOL, False, "PDR: assume negation of the cube on the previous level when "
|
||||
"checking for reachability (not only during cube weakening)"),
|
||||
('max_num_contexts', UINT, 500, "PDR: maximal number of contexts to create"),
|
||||
('try_minimize_core', BOOL, False, "PDR: try to reduce core size (before inductive minimization)"),
|
||||
('profile_timeout_milliseconds', UINT, 0, "instructions and rules that took less than the threshold will not be printed when printed the instruction/rule list"),
|
||||
('dbg_fpr_nonempty_relation_signature', BOOL, False,
|
||||
"if true, finite_product_relation will attempt to avoid creating inner relation with empty signature "
|
||||
"by putting in half of the table columns, if it would have been empty otherwise"),
|
||||
('engine', SYMBOL, 'auto-config',
|
||||
'Select: auto-config, datalog, duality, pdr, bmc'),
|
||||
('datalog.default_table', SYMBOL, 'sparse',
|
||||
'default table implementation: sparse, hashtable, bitvector, interval'),
|
||||
('datalog.default_relation', SYMBOL, 'pentagon',
|
||||
'default relation implementation: external_relation, pentagon'),
|
||||
('datalog.generate_explanations', BOOL, False,
|
||||
'produce explanations for produced facts when using the datalog engine'),
|
||||
('datalog.use_map_names', BOOL, True,
|
||||
"use names from map files when displaying tuples"),
|
||||
('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 ' +
|
||||
'this option to have any effect)'),
|
||||
('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 " +
|
||||
"a single rule"),
|
||||
('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,
|
||||
"compile rules so that it is enough for the delta relation in " +
|
||||
"union and widening operations to determine only whether the " +
|
||||
"updated relation was modified or not"),
|
||||
('datalog.compile_with_widening', BOOL, False,
|
||||
"widening will be used to compile recursive rules"),
|
||||
('datalog.default_table_checked', BOOL, False, "if true, the detault " +
|
||||
'table will be default_table inside a wrapper that checks that its results ' +
|
||||
'are the same as of default_table_checker table'),
|
||||
('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), " +
|
||||
"zero means no restarts"),
|
||||
('datalog.output_profile', BOOL, False,
|
||||
"determines whether profile information should be " +
|
||||
"output when outputting Datalog rules or instructions"),
|
||||
('datalog.print.tuples', BOOL, True,
|
||||
"determines whether tuples for output predicates should be output"),
|
||||
('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,
|
||||
"if true, finite_product_relation will attempt to avoid creating " +
|
||||
"inner relation with empty signature by putting in half of the " +
|
||||
"table columns, if it would have been empty otherwise"),
|
||||
('duality.full_expand', BOOL, False, 'Fully expand derivation trees'),
|
||||
('duality.no_conj', BOOL, False, 'No forced covering (conjectures)'),
|
||||
('duality.feasible_edges', BOOL, True,
|
||||
'Don\'t expand definitley infeasible edges'),
|
||||
('duality.use_underapprox', BOOL, False, 'Use underapproximations'),
|
||||
('duality.stratified_inlining', BOOL, False, 'Use stratified inlining'),
|
||||
('duality.recursion_bound', UINT, UINT_MAX,
|
||||
'Recursion bound for stratified inlining'),
|
||||
('duality.profile', BOOL, False, 'profile run time'),
|
||||
('duality.mbqi', BOOL, True, 'use model-based quantifier instantiation'),
|
||||
('duality.batch_expand', BOOL, False, 'use batch expansion'),
|
||||
('duality.conjecture_file', STRING, '', 'save conjectures to file'),
|
||||
('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.use_model_generalizer', BOOL, False,
|
||||
"use model for backwards propagation (instead of symbolic simulation)"),
|
||||
('pdr.validate_result', 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,
|
||||
"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, " +
|
||||
"when printing rules"),
|
||||
('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,
|
||||
"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 for reachability or non-reachability'),
|
||||
('print_boogie_certificate', BOOL, False, 'print certificate for reachability or non-reachability using a format understood by Boogie'),
|
||||
('print_certificate', BOOL, False,
|
||||
'print certificate for reachability or non-reachability'),
|
||||
('print_boogie_certificate', BOOL, False,
|
||||
'print certificate for reachability or non-reachability using a ' +
|
||||
'format understood by Boogie'),
|
||||
('print_statistics', BOOL, False, 'print statistics'),
|
||||
('use_utvpi', BOOL, True, 'PDR: Enable UTVPI strategy'),
|
||||
('tab_selection', SYMBOL, 'weight', 'selection method for tabular strategy: weight (default), first, var-use'),
|
||||
('full_expand', BOOL, False, 'DUALITY: Fully expand derivation trees'),
|
||||
('no_conj', BOOL, False, 'DUALITY: No forced covering (conjectures)'),
|
||||
('feasible_edges', BOOL, True, 'DUALITY: Don\'t expand definitley infeasible edges'),
|
||||
('use_underapprox', BOOL, False, 'DUALITY: Use underapproximations'),
|
||||
('stratified_inlining', BOOL, False, 'DUALITY: Use stratified inlining'),
|
||||
('recursion_bound', UINT, UINT_MAX, 'DUALITY: Recursion bound for stratified inlining'),
|
||||
('profile', BOOL, False, 'DUALITY: profile run time'),
|
||||
('mbqi', BOOL, True, 'DUALITY: use model-based quantifier instantion'),
|
||||
('batch_expand', BOOL, False, 'DUALITY: use batch expansion'),
|
||||
('dump_aig', SYMBOL, '', 'Dump clauses in AIG text format (AAG) to the given file name'),
|
||||
('conjecture_file', STRING, '', 'DUALITY: save conjectures to file'),
|
||||
('enable_restarts', BOOL, False, 'DUALITY: enable restarts'),
|
||||
('print_aig', SYMBOL, '',
|
||||
'Dump clauses in AIG text format (AAG) to the given file name'),
|
||||
('tab.selection', SYMBOL, 'weight',
|
||||
'selection method for tabular strategy: weight (default), first, var-use'),
|
||||
('xform.bit_blast', BOOL, False,
|
||||
'bit-blast bit-vectors'),
|
||||
('xform.magic', BOOL, False,
|
||||
"perform symbolic magic set transformation"),
|
||||
('xform.scale', BOOL, False,
|
||||
"add scaling variable to linear real arithemtic 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,
|
||||
"try linear inlining method with potential expansion"),
|
||||
('xform.fix_unbound_vars', BOOL, False, "fix unbound variables in tail"),
|
||||
('xform.unfold_rules', UINT, 0,
|
||||
"unfold rules statically using iterative squarring"),
|
||||
('xform.slice', BOOL, True, "simplify clause set using slicing"),
|
||||
('xform.karr', BOOL, False,
|
||||
"Add linear invariants to clauses using Karr's method"),
|
||||
('xform.quantify_arrays', BOOL, False,
|
||||
"create quantified Horn clauses from clauses with arrays"),
|
||||
('xform.instantiate_quantifiers', BOOL, False,
|
||||
"instantiate quantified Horn clauses using E-matching heuristic"),
|
||||
('xform.coalesce_rules', BOOL, False, "coalesce rules"),
|
||||
('duality.enable_restarts', BOOL, False, 'DUALITY: enable restarts'),
|
||||
))
|
||||
|
||||
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue