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:
commit
08cb8b8de8
74 changed files with 1280 additions and 896 deletions
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)); }
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue