/*++ Copyright (c) 2006 Microsoft Corporation Module Name: theory_array.cpp Abstract: Author: Leonardo de Moura (leonardo) 2008-06-01. Revision History: --*/ #include"smt_context.h" #include"theory_array.h" #include"ast_ll_pp.h" #include"stats.h" namespace smt { theory_array::theory_array(ast_manager & m, theory_array_params & params): theory_array_base(m), m_params(params), m_find(*this), m_trail_stack(*this), m_final_check_idx(0) { } theory_array::~theory_array() { std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); } void theory_array::init(context * ctx) { theory_array_base::init(ctx); if (!ctx->relevancy()) m_params.m_array_laziness = 0; } void theory_array::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) { // v1 is the new root TRACE("array", tout << "merging v" << v1 << " v" << v2 << "\n"; display_var(tout, v1);); SASSERT(v1 == find(v1)); var_data * d1 = m_var_data[v1]; var_data * d2 = m_var_data[v2]; if (!d1->m_prop_upward && d2->m_prop_upward) set_prop_upward(v1); ptr_vector::iterator it = d2->m_stores.begin(); ptr_vector::iterator end = d2->m_stores.end(); for (; it != end; ++it) add_store(v1, *it); it = d2->m_parent_stores.begin(); end = d2->m_parent_stores.end(); for (; it != end; ++it) add_parent_store(v1, *it); it = d2->m_parent_selects.begin(); end = d2->m_parent_selects.end(); for (; it != end; ++it) add_parent_select(v1, *it); TRACE("array", tout << "after merge\n"; display_var(tout, v1);); } void theory_array::unmerge_eh(theory_var v1, theory_var v2) { // do nothing } theory_var theory_array::mk_var(enode * n) { theory_var r = theory_array_base::mk_var(n); theory_var r2 = m_find.mk_var(); SASSERT(r == r2); SASSERT(r == static_cast(m_var_data.size())); m_var_data.push_back(alloc(var_data)); var_data * d = m_var_data[r]; TRACE("array", tout << mk_bounded_pp(n->get_owner(), get_manager()) << "\nis_array: " << is_array_sort(n) << ", is_select: " << is_select(n) << ", is_store: " << is_store(n) << "\n";); d->m_is_array = is_array_sort(n); if (d->m_is_array) register_sort(get_manager().get_sort(n->get_owner())); d->m_is_select = is_select(n); if (is_store(n)) d->m_stores.push_back(n); get_context().attach_th_var(n, this, r); if (m_params.m_array_laziness <= 1 && is_store(n)) instantiate_axiom1(n); return r; } void theory_array::add_parent_select(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_select(s)); v = find(v); var_data * d = m_var_data[v]; d->m_parent_selects.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_selects)); ptr_vector::iterator it = d->m_stores.begin(); ptr_vector::iterator end = d->m_stores.end(); for (; it != end; ++it) { instantiate_axiom2a(s, *it); } if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { it = d->m_parent_stores.begin(); end = d->m_parent_stores.end(); for (; it != end; ++it) { enode * store = *it; SASSERT(is_store(store)); if (!m_params.m_array_cg || store->is_cgr()) instantiate_axiom2b(s, store); } } } void theory_array::add_parent_store(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_store(s)); v = find(v); var_data * d = m_var_data[v]; d->m_parent_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_parent_stores)); if (!m_params.m_array_weak && !m_params.m_array_delay_exp_axiom && d->m_prop_upward) { ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) if (!m_params.m_array_cg || (*it)->is_cgr()) instantiate_axiom2b(*it, s); } } bool theory_array::instantiate_axiom2b_for(theory_var v) { bool result = false; var_data * d = m_var_data[v]; ptr_vector::iterator it = d->m_parent_stores.begin(); ptr_vector::iterator end = d->m_parent_stores.end(); for (; it != end; ++it) { ptr_vector::iterator it2 = d->m_parent_selects.begin(); ptr_vector::iterator end2 = d->m_parent_selects.end(); for (; it2 != end2; ++it2) if (instantiate_axiom2b(*it2, *it)) result = true; } return result; } /** \brief Mark v for upward propagation. That is, enables the propagation of select(v, i) to store(v,j,k). */ void theory_array::set_prop_upward(theory_var v) { if (m_params.m_array_weak) return; v = find(v); var_data * d = m_var_data[v]; if (!d->m_prop_upward) { TRACE("array", tout << "#" << v << "\n";); m_trail_stack.push(reset_flag_trail(d->m_prop_upward)); d->m_prop_upward = true; if (!m_params.m_array_delay_exp_axiom) instantiate_axiom2b_for(v); ptr_vector::iterator it = d->m_stores.begin(); ptr_vector::iterator end = d->m_stores.end(); for (; it != end; ++it) set_prop_upward(*it); } } void theory_array::set_prop_upward(enode * store) { if (is_store(store)) { theory_var st_v = store->get_arg(0)->get_th_var(get_id()); set_prop_upward(st_v); } } void theory_array::set_prop_upward(theory_var v, var_data* d) { unsigned sz = d->m_stores.size(); for (unsigned i = 0; i < sz; ++i) { set_prop_upward(d->m_stores[i]); } } /** \brief Return the size of the equivalence class for array terms that can be expressed as \lambda i : Index . [.. (select a i) ..] */ unsigned theory_array::get_lambda_equiv_size(theory_var v, var_data* d) { return d->m_stores.size(); } void theory_array::add_store(theory_var v, enode * s) { if (m_params.m_array_cg && !s->is_cgr()) return; SASSERT(is_store(s)); v = find(v); var_data * d = m_var_data[v]; unsigned lambda_equiv_class_size = get_lambda_equiv_size(v, d); if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) { set_prop_upward(v, d); } d->m_stores.push_back(s); m_trail_stack.push(push_back_trail(d->m_stores)); ptr_vector::iterator it = d->m_parent_selects.begin(); ptr_vector::iterator end = d->m_parent_selects.end(); for (; it != end; ++it) { SASSERT(is_select(*it)); instantiate_axiom2a(*it, s); } if (m_params.m_array_always_prop_upward || lambda_equiv_class_size >= 1) set_prop_upward(s); } void theory_array::instantiate_axiom1(enode * store) { TRACE("array", tout << "axiom 1:\n" << mk_bounded_pp(store->get_owner(), get_manager()) << "\n";); SASSERT(is_store(store)); m_stats.m_num_axiom1++; assert_store_axiom1(store); } void theory_array::instantiate_axiom2a(enode * select, enode * store) { TRACE("array", tout << "axiom 2a: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";); SASSERT(is_select(select)); SASSERT(is_store(store)); if (assert_store_axiom2(store, select)) m_stats.m_num_axiom2a++; } bool theory_array::instantiate_axiom2b(enode * select, enode * store) { TRACE("array_axiom2b", tout << "axiom 2b: #" << select->get_owner_id() << " #" << store->get_owner_id() << "\n";); SASSERT(is_select(select)); SASSERT(is_store(store)); if (assert_store_axiom2(store, select)) { m_stats.m_num_axiom2b++; return true; } return false; } void theory_array::instantiate_extensionality(enode * a1, enode * a2) { TRACE("array", tout << "extensionality: #" << a1->get_owner_id() << " #" << a2->get_owner_id() << "\n";); SASSERT(is_array_sort(a1)); SASSERT(is_array_sort(a2)); if (m_params.m_array_extensional && assert_extensionality(a1, a2)) m_stats.m_num_extensionality++; } bool theory_array::internalize_atom(app * atom, bool) { return internalize_term(atom); } // // Internalize the term. If it has already been internalized, return false. // bool theory_array::internalize_term_core(app * n) { TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); context & ctx = get_context(); unsigned num_args = n->get_num_args(); for (unsigned i = 0; i < num_args; i++) ctx.internalize(n->get_arg(i), false); if (ctx.e_internalized(n)) { return false; } enode * e = ctx.mk_enode(n, false, false, true); if (!is_attached_to_var(e)) mk_var(e); if (get_manager().is_bool(n)) { bool_var bv = ctx.mk_bool_var(n); ctx.set_var_theory(bv, get_id()); ctx.set_enode_flag(bv, true); } return true; } bool theory_array::internalize_term(app * n) { if (!is_store(n) && !is_select(n)) { if (!is_array_ext(n)) found_unsupported_op(n); return false; } TRACE("array_bug", tout << mk_bounded_pp(n, get_manager()) << "\n";); if (!internalize_term_core(n)) { return true; } context & ctx = get_context(); enode * arg0 = ctx.get_enode(n->get_arg(0)); if (!is_attached_to_var(arg0)) mk_var(arg0); if (m_params.m_array_laziness == 0) { theory_var v_arg = arg0->get_th_var(get_id()); SASSERT(v_arg != null_theory_var); if (is_select(n)) { add_parent_select(v_arg, ctx.get_enode(n)); } else if (is_store(n)) { add_parent_store(v_arg, ctx.get_enode(n)); } } return true; } void theory_array::apply_sort_cnstr(enode * n, sort * s) { SASSERT(is_array_sort(s)); if (!is_attached_to_var(n)) mk_var(n); } void theory_array::new_eq_eh(theory_var v1, theory_var v2) { m_find.merge(v1, v2); } void theory_array::new_diseq_eh(theory_var v1, theory_var v2) { v1 = find(v1); v2 = find(v2); var_data * d1 = m_var_data[v1]; if (d1->m_is_array) { SASSERT(m_var_data[v2]->m_is_array); TRACE("ext", tout << "extensionality:\n" << mk_bounded_pp(get_enode(v1)->get_owner(), get_manager(), 5) << "\n" << mk_bounded_pp(get_enode(v2)->get_owner(), get_manager(), 5) << "\n";); instantiate_extensionality(get_enode(v1), get_enode(v2)); } } void theory_array::relevant_eh(app * n) { if (m_params.m_array_laziness == 0) return; if (!is_store(n) && !is_select(n)) return; context & ctx = get_context(); enode * arg = ctx.get_enode(n->get_arg(0)); theory_var v_arg = arg->get_th_var(get_id()); SASSERT(v_arg != null_theory_var); if (is_select(n)) { add_parent_select(v_arg, ctx.get_enode(n)); } else { SASSERT(is_store(n)); if (m_params.m_array_laziness > 1) instantiate_axiom1(ctx.get_enode(n)); add_parent_store(v_arg, ctx.get_enode(n)); } } void theory_array::push_scope_eh() { theory_array_base::push_scope_eh(); m_trail_stack.push_scope(); } void theory_array::pop_scope_eh(unsigned num_scopes) { m_trail_stack.pop_scope(num_scopes); unsigned num_old_vars = get_old_num_vars(num_scopes); std::for_each(m_var_data.begin() + num_old_vars, m_var_data.end(), delete_proc()); m_var_data.shrink(num_old_vars); theory_array_base::pop_scope_eh(num_scopes); SASSERT(m_find.get_num_vars() == m_var_data.size()); SASSERT(m_find.get_num_vars() == get_num_vars()); } final_check_status theory_array::final_check_eh() { m_final_check_idx++; final_check_status r; if (m_params.m_array_lazy_ieq) { // Delay the creation of interface equalities... The // motivation is too give other theories and quantifier // instantiation to do something useful during final // check. if (m_final_check_idx % m_params.m_array_lazy_ieq_delay != 0) { assert_delayed_axioms(); r = FC_CONTINUE; } else { if (mk_interface_eqs_at_final_check() == FC_CONTINUE) r = FC_CONTINUE; else r = assert_delayed_axioms(); } } else { if (m_final_check_idx % 2 == 1) { if (assert_delayed_axioms() == FC_CONTINUE) r = FC_CONTINUE; else r = mk_interface_eqs_at_final_check(); } else { if (mk_interface_eqs_at_final_check() == FC_CONTINUE) r = FC_CONTINUE; else r = assert_delayed_axioms(); } } TRACE("as_array", tout << "m_found_unsupported_op: " << m_found_unsupported_op << " " << r << "\n";); if (r == FC_DONE && m_found_unsupported_op) r = FC_GIVEUP; return r; } final_check_status theory_array::assert_delayed_axioms() { if (!m_params.m_array_delay_exp_axiom) return FC_DONE; final_check_status r = FC_DONE; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { var_data * d = m_var_data[v]; if (d->m_prop_upward && instantiate_axiom2b_for(v)) r = FC_CONTINUE; } return r; } final_check_status theory_array::mk_interface_eqs_at_final_check() { unsigned n = mk_interface_eqs(); m_stats.m_num_eq_splits += n; if (n > 0) return FC_CONTINUE; return FC_DONE; } void theory_array::reset_eh() { m_trail_stack.reset(); std::for_each(m_var_data.begin(), m_var_data.end(), delete_proc()); m_var_data.reset(); theory_array_base::reset_eh(); } void theory_array::display(std::ostream & out) const { out << "Theory array:\n"; unsigned num_vars = get_num_vars(); for (unsigned v = 0; v < num_vars; v++) { display_var(out, v); } } // TODO: move to another file void theory_array::display_ids(std::ostream & out, unsigned n, enode * const * v) { for (unsigned i = 0; i < n; i++) { if (i > 0) out << " "; out << "#" << v[i]->get_owner_id(); } } void theory_array::display_var(std::ostream & out, theory_var v) const { var_data const * d = m_var_data[v]; out << "v"; out.width(4); out << std::left << v; out << " #"; out.width(4); out << get_enode(v)->get_owner_id() << " -> #"; out.width(4); out << get_enode(find(v))->get_owner_id(); out << std::right; out << " is_array: " << d->m_is_array << " is_select: " << d->m_is_select << " upward: " << d->m_prop_upward; out << " stores: {"; display_ids(out, d->m_stores.size(), d->m_stores.c_ptr()); out << "} p_stores: {"; display_ids(out, d->m_parent_stores.size(), d->m_parent_stores.c_ptr()); out << "} p_selects: {"; display_ids(out, d->m_parent_selects.size(), d->m_parent_selects.c_ptr()); out << "}"; out << "\n"; } void theory_array::collect_statistics(::statistics & st) const { st.update("array ax1", m_stats.m_num_axiom1); st.update("array ax2", m_stats.m_num_axiom2a); st.update("array exp ax2", m_stats.m_num_axiom2b); st.update("array ext ax", m_stats.m_num_extensionality); st.update("array splits", m_stats.m_num_eq_splits); } };