3
0
Fork 0
mirror of https://github.com/Z3Prover/z3 synced 2025-09-03 16:48:06 +00:00

address divergence in the case of shared theory symbols. Codeplex issue 147, thanks to George Karpenkov

Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
Nikolaj Bjorner 2014-12-09 16:04:25 +01:00
commit 08cb8b8de8
74 changed files with 1280 additions and 896 deletions

View file

@ -3939,9 +3939,16 @@ namespace smt {
return th->get_value(n, value);
}
void context::update_model() {
mk_proto_model(l_true);
m_model = m_proto_model->mk_model();
bool context::update_model(bool refinalize) {
final_check_status fcs = FC_DONE;
if (refinalize) {
fcs = final_check();
}
if (fcs == FC_DONE) {
mk_proto_model(l_true);
m_model = m_proto_model->mk_model();
}
return fcs == FC_DONE;
}
void context::mk_proto_model(lbool r) {

View file

@ -54,7 +54,11 @@ Revision History:
// the case that each context only references a few expressions.
// Using a map instead of a vector for the literals can compress space
// consumption.
#ifdef SPARSE_MAP
#define USE_BOOL_VAR_VECTOR 0
#else
#define USE_BOOL_VAR_VECTOR 1
#endif
namespace smt {
@ -69,6 +73,7 @@ namespace smt {
std::string last_failure_as_string() const;
void set_progress_callback(progress_callback *callback);
protected:
ast_manager & m_manager;
smt_params & m_fparams;
@ -106,7 +111,7 @@ namespace smt {
// -----------------------------------
enode * m_true_enode;
enode * m_false_enode;
ptr_vector<enode> m_app2enode; // app -> enode
app2enode_t m_app2enode; // app -> enode
ptr_vector<enode> m_enodes;
plugin_manager<theory> m_theories; // mapping from theory_id -> theory
ptr_vector<theory> m_theory_set; // set of theories for fast traversal
@ -1390,7 +1395,7 @@ namespace smt {
void get_model(model_ref & m) const;
void update_model();
bool update_model(bool refinalize);
void get_proto_model(proto_model_ref & m) const;

View file

@ -24,7 +24,7 @@ namespace smt {
/**
\brief Initialize an enode in the given memory position.
*/
enode * enode::init(ast_manager & m, void * mem, ptr_vector<enode> const & app2enode, app * owner,
enode * enode::init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent) {
SASSERT(m.is_bool(owner) || !merge_tf);
@ -60,7 +60,7 @@ namespace smt {
return n;
}
enode * enode::mk(ast_manager & m, region & r, ptr_vector<enode> const & app2enode, app * owner,
enode * enode::mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent) {
SASSERT(m.is_bool(owner) || !merge_tf);
@ -69,7 +69,7 @@ namespace smt {
return init(m, mem, app2enode, owner, generation, suppress_args, merge_tf, iscope_lvl, cgc_enabled, update_children_parent);
}
enode * enode::mk_dummy(ast_manager & m, ptr_vector<enode> const & app2enode, app * owner) {
enode * enode::mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner) {
unsigned sz = get_enode_size(owner->get_num_args());
void * mem = alloc_svect(char, sz);
return init(m, mem, app2enode, owner, 0, false, false, 0, true, false);

View file

@ -38,6 +38,29 @@ namespace smt {
}
};
/** \ brief Use sparse maps in SMT solver.
Define this to use hash maps rather than vectors over ast
nodes. This is useful in the case there are many solvers, each
referencing few nodes from a large ast manager. There is some
unknown performance penalty for this. */
// #define SPARSE_MAP
#ifndef SPARSE_MAP
typedef ptr_vector<enode> app2enode_t; // app -> enode
#else
class app2enode_t : public u_map<enode *> {
public:
void setx(unsigned x, enode *val, enode *def){
if(val == 0)
erase(x);
else
insert(x,val);
}
};
#endif
class tmp_enode;
/**
@ -115,7 +138,7 @@ namespace smt {
friend class tmp_enode;
static enode * init(ast_manager & m, void * mem, ptr_vector<enode> const & app2enode, app * owner,
static enode * init(ast_manager & m, void * mem, app2enode_t const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent);
public:
@ -124,11 +147,11 @@ namespace smt {
return sizeof(enode) + num_args * sizeof(enode*);
}
static enode * mk(ast_manager & m, region & r, ptr_vector<enode> const & app2enode, app * owner,
static enode * mk(ast_manager & m, region & r, app2enode_t const & app2enode, app * owner,
unsigned generation, bool suppress_args, bool merge_tf, unsigned iscope_lvl,
bool cgc_enabled, bool update_children_parent);
static enode * mk_dummy(ast_manager & m, ptr_vector<enode> const & app2enode, app * owner);
static enode * mk_dummy(ast_manager & m, app2enode_t const & app2enode, app * owner);
static void del_dummy(enode * n) { dealloc_svect(reinterpret_cast<char*>(n)); }

View file

@ -874,13 +874,13 @@ namespace smt {
void add_tmp_row(row & r1, numeral const & coeff, row const & r2);
theory_var pick_var_to_leave(bool has_int, theory_var x_j, bool inc, numeral & a_ij, inf_numeral & gain, bool& skiped_row);
bool is_safe_to_leave(theory_var x, bool& has_int);
bool is_safe_to_leave(theory_var x, bool& has_int, bool& is_shared);
bool move_to_bound(theory_var x_i, bool inc);
template<bool invert>
void add_tmp_row_entry(row & r, numeral const & coeff, theory_var v);
enum max_min_t { UNBOUNDED, AT_BOUND, OPTIMIZED, BEST_EFFORT};
max_min_t max_min(theory_var v, bool max);
max_min_t max_min(row & r, bool max);
max_min_t max_min(theory_var v, bool max, bool& has_shared);
max_min_t max_min(row & r, bool max, bool& has_shared);
bool max_min(svector<theory_var> const & vars);
// -----------------------------------
@ -1016,7 +1016,7 @@ namespace smt {
// Optimization
//
// -----------------------------------
virtual inf_eps_rational<inf_rational> maximize(theory_var v, expr_ref& blocker);
virtual inf_eps_rational<inf_rational> maximize(theory_var v, expr_ref& blocker, bool& has_shared);
virtual inf_eps_rational<inf_rational> value(theory_var v);
virtual theory_var add_objective(app* term);
virtual expr* mk_ge(filter_model_converter& fm, theory_var v, inf_numeral const& val);

View file

@ -928,11 +928,10 @@ namespace smt {
*/
template<typename Ext>
bool theory_arith<Ext>::is_safe_to_leave(theory_var x, bool& has_int) {
bool theory_arith<Ext>::is_safe_to_leave(theory_var x, bool& has_int, bool& shared) {
if (get_context().is_shared(get_enode(x))) {
return false;
}
context& ctx = get_context();
shared |= ctx.is_shared(get_enode(x));
column & c = m_columns[x];
typename svector<col_entry>::iterator it = c.begin_entries();
typename svector<col_entry>::iterator end = c.end_entries();
@ -944,8 +943,9 @@ namespace smt {
numeral const & coeff = r[it->m_row_idx].m_coeff;
if (s != null_theory_var && is_int(s)) has_int = true;
bool is_unsafe = (s != null_theory_var && is_int(s) && !coeff.is_int());
is_unsafe = is_unsafe || (s != null_theory_var && get_context().is_shared(get_enode(s)));
TRACE("opt", tout << "is v" << x << " safe to leave for v" << s << "? " << (is_unsafe?"no":"yes") << " " << (has_int?"int":"real") << "\n";
shared |= (s != null_theory_var && ctx.is_shared(get_enode(s)));
TRACE("opt", tout << "is v" << x << " safe to leave for v" << s
<< "? " << (is_unsafe?"no":"yes") << " " << (has_int?"int":"real") << "\n";
display_row(tout, r, true););
if (is_unsafe) return false;
}
@ -1072,10 +1072,12 @@ namespace smt {
}
template<typename Ext>
inf_eps_rational<inf_rational> theory_arith<Ext>::maximize(theory_var v, expr_ref& blocker) {
inf_eps_rational<inf_rational> theory_arith<Ext>::maximize(theory_var v, expr_ref& blocker, bool& has_shared) {
TRACE("bound_bug", display_var(tout, v); display(tout););
max_min_t r = max_min(v, true);
has_shared = false;
max_min_t r = max_min(v, true, has_shared);
if (r == UNBOUNDED) {
has_shared = false;
blocker = get_manager().mk_false();
return inf_eps_rational<inf_rational>::infinity();
}
@ -1299,10 +1301,11 @@ namespace smt {
Return true if succeeded.
*/
template<typename Ext>
typename theory_arith<Ext>::max_min_t theory_arith<Ext>::max_min(row & r, bool max) {
typename theory_arith<Ext>::max_min_t theory_arith<Ext>::max_min(row & r, bool max, bool& has_shared) {
TRACE("max_min", tout << "max_min...\n";);
m_stats.m_max_min++;
bool skipped_row = false;
has_shared = false;
SASSERT(valid_row_assignment());
SASSERT(satisfy_bounds());
@ -1332,7 +1335,7 @@ namespace smt {
bool has_int = false;
if ((curr_inc && at_upper(curr_x_j)) || (!curr_inc && at_lower(curr_x_j)))
continue; // variable cannot be used for max/min.
if (!is_safe_to_leave(curr_x_j, has_int)) {
if (!is_safe_to_leave(curr_x_j, has_int, has_shared)) {
skipped_row = true;
continue;
}
@ -1536,7 +1539,7 @@ namespace smt {
\brief Maximize/Minimize the given variable. The bounds of v are update if procedure succeeds.
*/
template<typename Ext>
typename theory_arith<Ext>::max_min_t theory_arith<Ext>::max_min(theory_var v, bool max) {
typename theory_arith<Ext>::max_min_t theory_arith<Ext>::max_min(theory_var v, bool max, bool& has_shared) {
expr* e = get_enode(v)->get_owner();
SASSERT(valid_row_assignment());
SASSERT(satisfy_bounds());
@ -1558,7 +1561,7 @@ namespace smt {
add_tmp_row_entry<true>(m_tmp_row, it->m_coeff, it->m_var);
}
}
max_min_t r = max_min(m_tmp_row, max);
max_min_t r = max_min(m_tmp_row, max, has_shared);
if (r == OPTIMIZED) {
TRACE("opt", tout << mk_pp(e, get_manager()) << " " << (max ? "max" : "min") << " value is: " << get_value(v) << "\n";
display_row(tout, m_tmp_row, true); display_row_info(tout, m_tmp_row););
@ -1581,12 +1584,13 @@ namespace smt {
template<typename Ext>
bool theory_arith<Ext>::max_min(svector<theory_var> const & vars) {
bool succ = false;
bool has_shared = false;
svector<theory_var>::const_iterator it = vars.begin();
svector<theory_var>::const_iterator end = vars.end();
for (; it != end; ++it) {
if (max_min(*it, true) == OPTIMIZED)
if (max_min(*it, true, has_shared) == OPTIMIZED && !has_shared)
succ = true;
if (max_min(*it, false) == OPTIMIZED)
if (max_min(*it, false, has_shared) == OPTIMIZED && !has_shared)
succ = true;
}
if (succ) {

View file

@ -376,8 +376,10 @@ namespace smt {
enode_vector::const_iterator end = r->end_parents();
for (; it != end; ++it) {
enode * parent = *it;
#if 0
if (!ctx.is_relevant(parent))
continue;
#endif
unsigned num_args = parent->get_num_args();
if (is_store(parent)) {
SET_ARRAY(parent->get_arg(0));
@ -399,6 +401,7 @@ namespace smt {
return false;
}
#if 0
void theory_array_base::collect_shared_vars(sbuffer<theory_var> & result) {
TRACE("array_shared", tout << "collecting shared vars...\n";);
context & ctx = get_context();
@ -420,6 +423,31 @@ namespace smt {
}
unmark_enodes(to_unmark.size(), to_unmark.c_ptr());
}
#else
void theory_array_base::collect_shared_vars(sbuffer<theory_var> & result) {
TRACE("array_shared", tout << "collecting shared vars...\n";);
context & ctx = get_context();
ptr_buffer<enode> to_unmark;
unsigned num_vars = get_num_vars();
for (unsigned i = 0; i < num_vars; i++) {
enode * n = get_enode(i);
if (ctx.is_relevant(n)) {
enode * r = n->get_root();
if (!r->is_marked()){
if(is_array_sort(r) && ctx.is_shared(r)) {
TRACE("array_shared", tout << "new shared var: #" << r->get_owner_id() << "\n";);
theory_var r_th_var = r->get_th_var(get_id());
SASSERT(r_th_var != null_theory_var);
result.push_back(r_th_var);
}
r->set_mark();
to_unmark.push_back(r);
}
}
}
unmark_enodes(to_unmark.size(), to_unmark.c_ptr());
}
#endif
/**
\brief Create interface variables for shared array variables.

View file

@ -266,7 +266,7 @@ namespace smt {
//
// -----------------------------------
virtual inf_eps_rational<inf_rational> maximize(theory_var v, expr_ref& blocker);
virtual inf_eps_rational<inf_rational> maximize(theory_var v, expr_ref& blocker, bool& has_shared);
virtual inf_eps_rational<inf_rational> value(theory_var v);
virtual theory_var add_objective(app* term);
virtual expr_ref mk_gt(theory_var v, inf_rational const& val);

View file

@ -889,11 +889,12 @@ namespace smt {
}
template<typename Ext>
inf_eps_rational<inf_rational> theory_dense_diff_logic<Ext>::maximize(theory_var v, expr_ref& blocker) {
inf_eps_rational<inf_rational> theory_dense_diff_logic<Ext>::maximize(theory_var v, expr_ref& blocker, bool& has_shared) {
typedef simplex::simplex<simplex::mpq_ext> Simplex;
Simplex S;
ast_manager& m = get_manager();
objective_term const& objective = m_objectives[v];
has_shared = false;
IF_VERBOSE(1,
for (unsigned i = 0; i < objective.size(); ++i) {

View file

@ -320,7 +320,7 @@ namespace smt {
//
// -----------------------------------
virtual inf_eps maximize(theory_var v, expr_ref& blocker);
virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared);
virtual inf_eps value(theory_var v);
virtual theory_var add_objective(app* term);
virtual expr_ref mk_gt(theory_var v, inf_rational const& val);

View file

@ -1187,8 +1187,9 @@ typename theory_diff_logic<Ext>::inf_eps theory_diff_logic<Ext>::value(theory_va
template<typename Ext>
typename theory_diff_logic<Ext>::inf_eps
theory_diff_logic<Ext>::maximize(theory_var v, expr_ref& blocker) {
theory_diff_logic<Ext>::maximize(theory_var v, expr_ref& blocker, bool& has_shared) {
has_shared = false;
Simplex& S = m_S;
ast_manager& m = get_manager();

View file

@ -31,7 +31,7 @@ namespace smt {
public:
typedef inf_eps_rational<inf_rational> inf_eps;
virtual inf_eps value(theory_var) = 0;
virtual inf_eps maximize(theory_var v, expr_ref& blocker) = 0;
virtual inf_eps maximize(theory_var v, expr_ref& blocker, bool& has_shared) = 0;
virtual theory_var add_objective(app* term) = 0;
virtual expr* mk_ge(filter_model_converter& fm, theory_var v, inf_eps const& val) { UNREACHABLE(); return 0; }
bool is_linear(ast_manager& m, expr* term);