mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	prepare polysat
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
						commit
						3f5df04dc4
					
				
					 252 changed files with 5792 additions and 2553 deletions
				
			
		|  | @ -38,7 +38,6 @@ z3_add_component(sat_smt | |||
|     q_queue.cpp | ||||
|     q_solver.cpp | ||||
|     recfun_solver.cpp | ||||
|     sat_dual_solver.cpp | ||||
|     sat_th.cpp | ||||
|     user_solver.cpp | ||||
|   COMPONENT_DEPENDENCIES | ||||
|  |  | |||
|  | @ -49,6 +49,14 @@ namespace arith { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void solver::mk_abs_axiom(app* n) { | ||||
|         expr* x = nullptr; | ||||
|         VERIFY(a.is_abs(n, x)); | ||||
|         literal is_nonneg = mk_literal(a.mk_ge(x, a.mk_numeral(rational::zero(), n->get_sort()))); | ||||
|         add_clause(~is_nonneg, eq_internalize(n, x)); | ||||
|         add_clause(is_nonneg, eq_internalize(n, a.mk_uminus(x))); | ||||
|     } | ||||
| 
 | ||||
|     // t = n^0
 | ||||
|     void solver::mk_power0_axioms(app* t, app* n) { | ||||
|         expr_ref p0(a.mk_power0(n, t->get_arg(1)), m); | ||||
|  |  | |||
|  | @ -237,9 +237,10 @@ namespace arith { | |||
|                 if (!is_first) { | ||||
|                     // skip recursive internalization
 | ||||
|                 } | ||||
|                 else if (a.is_to_int(n, n1)) { | ||||
|                     mk_to_int_axiom(t); | ||||
|                 } | ||||
|                 else if (a.is_to_int(n, n1))  | ||||
|                     mk_to_int_axiom(t);                 | ||||
|                 else if (a.is_abs(n))  | ||||
|                     mk_abs_axiom(t);                 | ||||
|                 else if (a.is_idiv(n, n1, n2)) { | ||||
|                     if (!a.is_numeral(n2, r) || r.is_zero()) found_underspecified(n); | ||||
|                     m_idiv_terms.push_back(n); | ||||
|  | @ -268,17 +269,16 @@ namespace arith { | |||
|                 } | ||||
|                 else if (!a.is_div0(n) && !a.is_mod0(n) && !a.is_idiv0(n) && !a.is_rem0(n) && !a.is_power0(n)) { | ||||
|                     found_unsupported(n); | ||||
|                     ensure_arg_vars(to_app(n)); | ||||
|                 } | ||||
|                 else { | ||||
|                     // no-op
 | ||||
|                     ensure_arg_vars(to_app(n)); | ||||
|                 } | ||||
|             } | ||||
|             else { | ||||
|                 if (is_app(n)) { | ||||
|                     internalize_args(to_app(n)); | ||||
|                     for (expr* arg : *to_app(n))  | ||||
|                         if (a.is_arith_expr(arg) && !m.is_bool(arg)) | ||||
|                             internalize_term(arg); | ||||
|                     ensure_arg_vars(to_app(n)); | ||||
|                 } | ||||
|                 theory_var v = mk_evar(n); | ||||
|                 coeffs[vars.size()] = coeffs[index]; | ||||
|  | @ -425,6 +425,12 @@ namespace arith { | |||
|             e_internalize(arg); | ||||
|     } | ||||
| 
 | ||||
|     void solver::ensure_arg_vars(app* n) { | ||||
|         for (expr* arg : *to_app(n)) | ||||
|             if (a.is_real(arg) || a.is_int(arg)) | ||||
|                 internalize_term(arg); | ||||
|     } | ||||
| 
 | ||||
|     theory_var solver::internalize_power(app* t, app* n, unsigned p) { | ||||
|         internalize_args(t, true); | ||||
|         bool _has_var = has_var(t); | ||||
|  | @ -545,11 +551,13 @@ namespace arith { | |||
|         } | ||||
|         m_left_side.clear(); | ||||
|         // reset the coefficients after they have been used.
 | ||||
|         for (unsigned i = 0; i < vars.size(); ++i) { | ||||
|             theory_var var = vars[i]; | ||||
|         for (theory_var var : vars) { | ||||
|             rational const& r = m_columns[var]; | ||||
|             if (!r.is_zero()) { | ||||
|                 m_left_side.push_back(std::make_pair(r, register_theory_var_in_lar_solver(var))); | ||||
|                 auto vi = register_theory_var_in_lar_solver(var); | ||||
|                 if (lp::tv::is_term(vi)) | ||||
|                     vi = lp().map_term_index_to_column_index(vi); | ||||
|                 m_left_side.push_back(std::make_pair(r, vi)); | ||||
|                 m_columns[var].reset(); | ||||
|             } | ||||
|         } | ||||
|  |  | |||
|  | @ -561,6 +561,8 @@ namespace arith { | |||
|     } | ||||
| 
 | ||||
|     void solver::dbg_finalize_model(model& mdl) { | ||||
|         if (m_not_handled) | ||||
|             return; | ||||
|         bool found_bad = false; | ||||
|         for (unsigned v = 0; v < get_num_vars(); ++v) { | ||||
|             if (!is_bool(v)) | ||||
|  | @ -953,6 +955,7 @@ namespace arith { | |||
|             if (n1->get_root() == n2->get_root()) | ||||
|                 continue; | ||||
|             literal eq = eq_internalize(n1, n2); | ||||
|             ctx.mark_relevant(eq); | ||||
|             if (s().value(eq) != l_true) | ||||
|                 return true; | ||||
|         } | ||||
|  | @ -1446,21 +1449,19 @@ namespace arith { | |||
|             TRACE("arith", tout << "canceled\n";); | ||||
|             return l_undef; | ||||
|         } | ||||
|         if (!m_nla) { | ||||
|             TRACE("arith", tout << "no nla\n";); | ||||
|         CTRACE("arith", !m_nla, tout << "no nla\n";); | ||||
|         if (!m_nla)  | ||||
|             return l_true; | ||||
|         } | ||||
|         if (!m_nla->need_check()) | ||||
|             return l_true; | ||||
| 
 | ||||
|         m_a1 = nullptr; m_a2 = nullptr; | ||||
|         lbool r = m_nla->check(m_nla_lemma_vector); | ||||
|         switch (r) { | ||||
|         case l_false: { | ||||
|         case l_false:  | ||||
|             for (const nla::lemma& l : m_nla_lemma_vector) | ||||
|                 false_case_of_check_nla(l); | ||||
|             break; | ||||
|         } | ||||
|         case l_true: | ||||
|             if (assume_eqs()) | ||||
|                 return l_false; | ||||
|  | @ -1477,11 +1478,28 @@ namespace arith { | |||
|     } | ||||
| 
 | ||||
|     bool solver::include_func_interp(func_decl* f) const { | ||||
|         return  | ||||
|             a.is_div0(f) || | ||||
|             a.is_idiv0(f) || | ||||
|             a.is_power0(f) || | ||||
|             a.is_rem0(f) || | ||||
|             a.is_mod0(f);         | ||||
|         SASSERT(f->get_family_id() == get_id()); | ||||
|         switch (f->get_decl_kind()) { | ||||
|         case OP_ADD: | ||||
|         case OP_SUB: | ||||
|         case OP_UMINUS: | ||||
|         case OP_MUL: | ||||
|         case OP_LE: | ||||
|         case OP_LT: | ||||
|         case OP_GE: | ||||
|         case OP_GT: | ||||
|         case OP_MOD: | ||||
|         case OP_REM: | ||||
|         case OP_DIV: | ||||
|         case OP_IDIV: | ||||
|         case OP_POWER: | ||||
|         case OP_IS_INT: | ||||
|         case OP_TO_INT: | ||||
|         case OP_TO_REAL: | ||||
|         case OP_NUM: | ||||
|             return false; | ||||
|         default: | ||||
|             return true;             | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -239,6 +239,7 @@ namespace arith { | |||
|         void add_def_constraint(lp::constraint_index index, theory_var v); | ||||
|         void add_def_constraint_and_equality(lpvar vi, lp::lconstraint_kind kind, const rational& bound); | ||||
|         void internalize_args(app* t, bool force = false); | ||||
|         void ensure_arg_vars(app* t); | ||||
|         theory_var internalize_power(app* t, app* n, unsigned p); | ||||
|         theory_var internalize_mul(app* t); | ||||
|         theory_var internalize_def(expr* term); | ||||
|  | @ -263,6 +264,7 @@ namespace arith { | |||
|         // axioms
 | ||||
|         void mk_div_axiom(expr* p, expr* q); | ||||
|         void mk_to_int_axiom(app* n); | ||||
|         void mk_abs_axiom(app* n); | ||||
|         void mk_is_int_axiom(expr* n); | ||||
|         void mk_idiv_mod_axioms(expr* p, expr* q); | ||||
|         void mk_rem_axiom(expr* dividend, expr* divisor); | ||||
|  | @ -445,6 +447,8 @@ namespace arith { | |||
|         bool is_shared(theory_var v) const override; | ||||
|         lbool get_phase(bool_var v) override; | ||||
|         bool include_func_interp(func_decl* f) const override; | ||||
|         bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_add(n->get_expr()); } | ||||
|         bool has_unhandled() const override { return m_not_handled != nullptr; } | ||||
| 
 | ||||
|         // bounds and equality propagation callbacks
 | ||||
|         lp::lar_solver& lp() { return *m_solver; } | ||||
|  |  | |||
|  | @ -57,8 +57,6 @@ namespace array { | |||
| 
 | ||||
|     bool solver::assert_axiom(unsigned idx) { | ||||
|         axiom_record& r = m_axiom_trail[idx]; | ||||
|         if (!is_relevant(r)) | ||||
|             return false; | ||||
|         switch (r.m_kind) { | ||||
|         case axiom_record::kind_t::is_store: | ||||
|             return assert_store_axiom(to_app(r.n->get_expr())); | ||||
|  | @ -86,35 +84,12 @@ namespace array { | |||
|             return assert_default_const_axiom(to_app(child)); | ||||
|         else if (a.is_store(child)) | ||||
|             return assert_default_store_axiom(to_app(child)); | ||||
|         else if (a.is_map(child)) | ||||
|         else if (is_map_combinator(child)) | ||||
|             return assert_default_map_axiom(to_app(child)); | ||||
|         else | ||||
|             return false;                 | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool solver::is_relevant(axiom_record const& r) const { | ||||
|         return true; | ||||
| #if 0 | ||||
|         // relevancy propagation is currently incomplete on terms
 | ||||
| 
 | ||||
|         expr* child = r.n->get_expr(); | ||||
|         switch (r.m_kind) { | ||||
|         case axiom_record::kind_t::is_select: { | ||||
|             app* select = r.select->get_app(); | ||||
|             for (unsigned i = 1; i < select->get_num_args(); ++i) | ||||
|                 if (!ctx.is_relevant(select->get_arg(i))) | ||||
|                     return false; | ||||
|             return ctx.is_relevant(child);             | ||||
|         } | ||||
|         case axiom_record::kind_t::is_default: | ||||
|             return ctx.is_relevant(child);             | ||||
|         default: | ||||
|             return true; | ||||
|         } | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     bool solver::assert_select(unsigned idx, axiom_record& r) { | ||||
|         expr* child = r.n->get_expr(); | ||||
|         app* select = r.select->get_app(); | ||||
|  | @ -140,7 +115,7 @@ namespace array { | |||
|             return assert_select_as_array_axiom(select, to_app(child)); | ||||
|         else if (a.is_store(child)) | ||||
|             return assert_select_store_axiom(select, to_app(child)); | ||||
|         else if (a.is_map(child)) | ||||
|         else if (is_map_combinator(child)) | ||||
|             return assert_select_map_axiom(select, to_app(child)); | ||||
|         else if (is_lambda(child)) | ||||
|             return assert_select_lambda_axiom(select, child); | ||||
|  | @ -215,10 +190,17 @@ namespace array { | |||
|             return new_prop; | ||||
|          | ||||
|         sat::literal sel_eq = sat::null_literal; | ||||
|         auto ensure_relevant = [&](sat::literal lit) { | ||||
|             if (ctx.is_relevant(lit)) | ||||
|                 return; | ||||
|             new_prop = true; | ||||
|             ctx.mark_relevant(lit);             | ||||
|         }; | ||||
|         auto init_sel_eq = [&]() { | ||||
|             if (sel_eq != sat::null_literal)  | ||||
|                 return true; | ||||
|             sel_eq = mk_literal(sel_eq_e); | ||||
|             ensure_relevant(sel_eq); | ||||
|             return s().value(sel_eq) != l_true; | ||||
|         }; | ||||
| 
 | ||||
|  | @ -235,6 +217,7 @@ namespace array { | |||
|                 break; | ||||
|             } | ||||
|             sat::literal idx_eq = eq_internalize(idx1, idx2); | ||||
|             ensure_relevant(idx_eq); | ||||
|             if (s().value(idx_eq) == l_true) | ||||
|                 continue; | ||||
|             if (s().value(idx_eq) == l_undef) | ||||
|  | @ -253,8 +236,7 @@ namespace array { | |||
|      * Assert | ||||
|      *    select(const(v), i) = v | ||||
|      */ | ||||
|     bool solver::assert_select_const_axiom(app* select, app* cnst) { | ||||
|          | ||||
|     bool solver::assert_select_const_axiom(app* select, app* cnst) {         | ||||
|         ++m_stats.m_num_select_const_axiom; | ||||
|         expr* val = nullptr; | ||||
|         VERIFY(a.is_const(cnst, val)); | ||||
|  | @ -291,16 +273,19 @@ namespace array { | |||
|         return add_clause(lit1, ~lit2); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::is_map_combinator(expr* map) const { | ||||
|         return a.is_map(map) || a.is_union(map) || a.is_intersect(map) || a.is_difference(map) || a.is_complement(map); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * Assert axiom: | ||||
|     * select(map[f](a, ... d), i) = f(select(a,i),...,select(d,i)) | ||||
|     */ | ||||
|     bool solver::assert_select_map_axiom(app* select, app* map) { | ||||
|         ++m_stats.m_num_select_map_axiom; | ||||
|         SASSERT(a.is_map(map)); | ||||
|         SASSERT(a.is_select(select)); | ||||
|         SASSERT(is_map_combinator(map)); | ||||
|         SASSERT(map->get_num_args() > 0); | ||||
|         func_decl* f = a.get_map_func_decl(map); | ||||
|         unsigned num_args = select->get_num_args(); | ||||
|         ptr_buffer<expr> args1, args2; | ||||
|         vector<ptr_vector<expr> > args2l; | ||||
|  | @ -321,7 +306,8 @@ namespace array { | |||
| 
 | ||||
|         expr_ref sel1(m), sel2(m); | ||||
|         sel1 = a.mk_select(args1); | ||||
|         sel2 = m.mk_app(f, args2); | ||||
|         sel2 = apply_map(map, args2.size(), args2.data()); | ||||
| 
 | ||||
|         rewrite(sel2);  | ||||
|         euf::enode* n1 = e_internalize(sel1); | ||||
|         euf::enode* n2 = e_internalize(sel2); | ||||
|  | @ -348,21 +334,44 @@ namespace array { | |||
|         return ctx.propagate(n1, n2, array_axiom()); | ||||
|     } | ||||
| 
 | ||||
|     expr_ref solver::apply_map(app* map, unsigned n, expr* const* args) { | ||||
|         expr_ref result(m); | ||||
|         if (a.is_map(map))  | ||||
|             result = m.mk_app(a.get_map_func_decl(map), n, args); | ||||
|         else if (a.is_union(map))  | ||||
|             result = m.mk_or(n, args); | ||||
|         else if (a.is_intersect(map))  | ||||
|             result = m.mk_and(n, args); | ||||
|         else if (a.is_difference(map)) { | ||||
|             SASSERT(n > 0); | ||||
|             result = args[0]; | ||||
|             for (unsigned i = 1; i < n; ++i) | ||||
|                 result = m.mk_and(result, m.mk_not(args[i])); | ||||
|         } | ||||
|         else if (a.is_complement(map)) { | ||||
|             SASSERT(n == 1); | ||||
|             result = m.mk_not(args[0]); | ||||
|         } | ||||
|         else {             | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|         rewrite(result); | ||||
|         return result; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     /**
 | ||||
|      * Assert: | ||||
|      *    default(map[f](a,..,d)) = f(default(a),..,default(d)) | ||||
|      */ | ||||
|     bool solver::assert_default_map_axiom(app* map) { | ||||
|         ++m_stats.m_num_default_map_axiom; | ||||
|         SASSERT(a.is_map(map)); | ||||
|         func_decl* f = a.get_map_func_decl(map); | ||||
|         SASSERT(map->get_num_args() == f->get_arity()); | ||||
|         SASSERT(is_map_combinator(map)); | ||||
|         expr_ref_vector args2(m); | ||||
|         for (expr* arg : *map) | ||||
|             args2.push_back(a.mk_default(arg)); | ||||
|         expr_ref def1(a.mk_default(map), m); | ||||
|         expr_ref def2(m.mk_app(f, args2), m); | ||||
|         rewrite(def2); | ||||
|         expr_ref def2 = apply_map(map, args2.size(), args2.data()); | ||||
|         return ctx.propagate(e_internalize(def1), e_internalize(def2), array_axiom()); | ||||
|     } | ||||
| 
 | ||||
|  | @ -534,11 +543,14 @@ namespace array { | |||
|         unsigned num_vars = get_num_vars(); | ||||
|         bool change = false; | ||||
|         for (unsigned v = 0; v < num_vars; v++) { | ||||
|             propagate_parent_select_axioms(v); | ||||
|             auto& d = get_var_data(v); | ||||
|             if (!d.m_prop_upward) | ||||
|                 continue; | ||||
|             euf::enode* n = var2enode(v); | ||||
|             if (!ctx.is_relevant(n)) | ||||
|                 continue; | ||||
|             for (euf::enode* lambda : d.m_parent_lambdas) | ||||
|                 propagate_select_axioms(d, lambda); | ||||
|             if (add_as_array_eqs(n)) | ||||
|                 change = true; | ||||
|             bool has_default = false; | ||||
|  | @ -552,8 +564,8 @@ namespace array { | |||
|         m_delay_qhead = 0; | ||||
|          | ||||
|         for (; m_delay_qhead < sz; ++m_delay_qhead)  | ||||
|             if (m_axiom_trail[m_delay_qhead].is_delayed() && assert_axiom(m_delay_qhead)) | ||||
|                 change = true;   | ||||
|             if (m_axiom_trail[m_delay_qhead].is_delayed() && assert_axiom(m_delay_qhead))  | ||||
|                 change = true;               | ||||
|         flet<bool> _enable_delay(m_enable_delay, false); | ||||
|         if (unit_propagate()) | ||||
|             change = true; | ||||
|  | @ -567,6 +579,8 @@ namespace array { | |||
|             return false; | ||||
|         for (unsigned i = 0; i < ctx.get_egraph().enodes_of(f).size(); ++i) { | ||||
|             euf::enode* p = ctx.get_egraph().enodes_of(f)[i]; | ||||
|             if (!ctx.is_relevant(p)) | ||||
|                 continue; | ||||
|             expr_ref_vector select(m); | ||||
|             select.push_back(n->get_expr()); | ||||
|             for (expr* arg : *to_app(p->get_expr())) | ||||
|  | @ -574,7 +588,8 @@ namespace array { | |||
|             expr_ref _e(a.mk_select(select.size(), select.data()), m); | ||||
|             euf::enode* e = e_internalize(_e); | ||||
|             if (e->get_root() != p->get_root()) { | ||||
|                 add_unit(eq_internalize(_e, p->get_expr())); | ||||
|                 sat::literal eq = eq_internalize(_e, p->get_expr()); | ||||
|                 add_unit(eq); | ||||
|                 change = true; | ||||
|             } | ||||
|         } | ||||
|  | @ -594,13 +609,12 @@ namespace array { | |||
|                 expr* e2 = var2expr(v2); | ||||
|                 if (e1->get_sort() != e2->get_sort()) | ||||
|                     continue; | ||||
|                 if (must_have_different_model_values(v1, v2)) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (ctx.get_egraph().are_diseq(var2enode(v1), var2enode(v2))) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 if (must_have_different_model_values(v1, v2))  | ||||
|                     continue;                 | ||||
|                 if (ctx.get_egraph().are_diseq(var2enode(v1), var2enode(v2)))  | ||||
|                     continue;                 | ||||
|                 sat::literal lit = eq_internalize(e1, e2); | ||||
|                 ctx.mark_relevant(lit); | ||||
|                 if (s().value(lit) == l_undef)  | ||||
|                     prop = true; | ||||
|             } | ||||
|  | @ -612,10 +626,11 @@ namespace array { | |||
|         ptr_buffer<euf::enode> to_unmark; | ||||
|         unsigned num_vars = get_num_vars(); | ||||
|         for (unsigned i = 0; i < num_vars; i++) { | ||||
|             euf::enode * n = var2enode(i); | ||||
|              | ||||
|             euf::enode * n = var2enode(i);             | ||||
|             if (!is_array(n))  | ||||
|                 continue;             | ||||
|                 continue; | ||||
|             if (!ctx.is_relevant(n)) | ||||
|                 continue; | ||||
|             euf::enode * r = n->get_root(); | ||||
|             if (r->is_marked1())  | ||||
|                 continue;             | ||||
|  |  | |||
|  | @ -49,7 +49,7 @@ namespace array { | |||
|         if (v == euf::null_theory_var) { | ||||
|             mk_var(n); | ||||
|             if (is_lambda(n->get_expr())) | ||||
|                 internalize_lambda(n); | ||||
|                 internalize_lambda_eh(n); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -57,45 +57,6 @@ namespace array { | |||
|         ensure_var(n); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_store(euf::enode* n) { | ||||
|         add_parent_lambda(n->get_arg(0)->get_th_var(get_id()), n);    | ||||
|         push_axiom(store_axiom(n)); | ||||
|         add_lambda(n->get_th_var(get_id()), n); | ||||
|         SASSERT(!get_var_data(n->get_th_var(get_id())).m_prop_upward); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_map(euf::enode* n) { | ||||
|         for (auto* arg : euf::enode_args(n)) { | ||||
|             add_parent_lambda(arg->get_th_var(get_id()), n); | ||||
|             set_prop_upward(arg); | ||||
|         } | ||||
|         push_axiom(default_axiom(n)); | ||||
|         add_lambda(n->get_th_var(get_id()), n); | ||||
|         SASSERT(!get_var_data(n->get_th_var(get_id())).m_prop_upward); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_lambda(euf::enode* n) { | ||||
|         SASSERT(is_lambda(n->get_expr()) || a.is_const(n->get_expr()) || a.is_as_array(n->get_expr())); | ||||
|         theory_var v = n->get_th_var(get_id()); | ||||
|         push_axiom(default_axiom(n)); | ||||
|         add_lambda(v, n); | ||||
|         set_prop_upward(v); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_select(euf::enode* n) { | ||||
|         add_parent_select(n->get_arg(0)->get_th_var(get_id()), n); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_ext(euf::enode* n) { | ||||
|         SASSERT(is_array(n->get_arg(0))); | ||||
|         push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1))); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_default(euf::enode* n) { | ||||
|         add_parent_default(n->get_arg(0)->get_th_var(get_id()), n); | ||||
|         set_prop_upward(n); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::visited(expr* e) { | ||||
|         euf::enode* n = expr2enode(e); | ||||
|         return n && n->is_attached_to(get_id());         | ||||
|  | @ -116,7 +77,8 @@ namespace array { | |||
| 
 | ||||
|     bool solver::post_visit(expr* e, bool sign, bool root) { | ||||
|         euf::enode* n = expr2enode(e); | ||||
|         app* a = to_app(e);         | ||||
|         app *a = to_app(e); | ||||
|         (void)a; | ||||
|         SASSERT(!n || !n->is_attached_to(get_id())); | ||||
|         if (!n)  | ||||
|             n = mk_enode(e, false); | ||||
|  | @ -124,40 +86,111 @@ namespace array { | |||
|             mk_var(n); | ||||
|         for (auto* arg : euf::enode_args(n)) | ||||
|             ensure_var(arg);   | ||||
|         switch (a->get_decl_kind()) { | ||||
|         case OP_STORE:            | ||||
|             internalize_store(n);  | ||||
|         internalize_eh(n); | ||||
|         if (ctx.is_relevant(n)) | ||||
|             relevant_eh(n); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_lambda_eh(euf::enode* n) { | ||||
|         push_axiom(default_axiom(n)); | ||||
|         auto& d = get_var_data(find(n)); | ||||
|         ctx.push_vec(d.m_lambdas, n); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_eh(euf::enode* n) { | ||||
|         switch (n->get_decl()->get_decl_kind()) { | ||||
|         case OP_STORE:           | ||||
|             ctx.push_vec(get_var_data(find(n)).m_lambdas, n); | ||||
|             push_axiom(store_axiom(n)); | ||||
|             break; | ||||
|         case OP_SELECT:           | ||||
|             internalize_select(n);  | ||||
|         case OP_SELECT: | ||||
|             break; | ||||
|         case OP_AS_ARRAY: | ||||
|         case OP_CONST_ARRAY:      | ||||
|             internalize_lambda(n);  | ||||
|         case OP_CONST_ARRAY: | ||||
|             internalize_lambda_eh(n); | ||||
|             break; | ||||
|         case OP_ARRAY_EXT:        | ||||
|             internalize_ext(n);  | ||||
|         case OP_ARRAY_EXT: | ||||
|             SASSERT(is_array(n->get_arg(0))); | ||||
|             push_axiom(extensionality_axiom(n->get_arg(0), n->get_arg(1))); | ||||
|             break; | ||||
|         case OP_ARRAY_DEFAULT:    | ||||
|             internalize_default(n);  | ||||
|         case OP_ARRAY_DEFAULT: | ||||
|             add_parent_default(find(n->get_arg(0)), n); | ||||
|             break; | ||||
|         case OP_ARRAY_MAP:        | ||||
|             internalize_map(n);  | ||||
|         case OP_ARRAY_MAP: | ||||
|         case OP_SET_UNION: | ||||
|         case OP_SET_INTERSECT: | ||||
|         case OP_SET_DIFFERENCE: | ||||
|         case OP_SET_COMPLEMENT: | ||||
|             for (auto* arg : euf::enode_args(n))  | ||||
|                 add_parent_lambda(find(arg), n); | ||||
|             internalize_lambda_eh(n); | ||||
|             break; | ||||
|         case OP_SET_UNION:        | ||||
|         case OP_SET_INTERSECT:    | ||||
|         case OP_SET_DIFFERENCE:   | ||||
|         case OP_SET_COMPLEMENT:   | ||||
|         case OP_SET_SUBSET:       | ||||
|         case OP_SET_HAS_SIZE:     | ||||
|         case OP_SET_CARD:         | ||||
|             ctx.unhandled_function(a->get_decl());  | ||||
|         case OP_SET_SUBSET: { | ||||
|             expr* x, *y; | ||||
|             VERIFY(a.is_subset(n->get_expr(), x, y)); | ||||
|             expr_ref diff(a.mk_setminus(x, y), m); | ||||
|             expr_ref emp(a.mk_empty_set(x->get_sort()), m); | ||||
|             sat::literal eq = eq_internalize(diff, emp); | ||||
|             sat::literal sub = expr2literal(n->get_expr()); | ||||
|             add_equiv(eq, sub); | ||||
|             break; | ||||
|         }             | ||||
|         case OP_SET_HAS_SIZE: | ||||
|         case OP_SET_CARD: | ||||
|             ctx.unhandled_function(n->get_decl()); | ||||
|             break; | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|             break;             | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void solver::relevant_eh(euf::enode* n) { | ||||
|         if (is_lambda(n->get_expr())) { | ||||
|             set_prop_upward(find(n)); | ||||
|             return; | ||||
|         } | ||||
|         if (!is_app(n->get_expr())) | ||||
|             return; | ||||
|         if (n->get_decl()->get_family_id() != a.get_family_id()) | ||||
|             return; | ||||
|         switch (n->get_decl()->get_decl_kind()) { | ||||
|         case OP_STORE: | ||||
|             add_parent_lambda(find(n->get_arg(0)), n);    | ||||
|             break; | ||||
|         case OP_SELECT: | ||||
|             add_parent_select(find(n->get_arg(0)), n); | ||||
|             break; | ||||
|         case OP_CONST_ARRAY: | ||||
|         case OP_AS_ARRAY: | ||||
|             set_prop_upward(find(n));             | ||||
|             propagate_parent_default(find(n)); | ||||
|             break; | ||||
|         case OP_ARRAY_EXT: | ||||
|             break; | ||||
|         case OP_ARRAY_DEFAULT: | ||||
|             set_prop_upward(find(n->get_arg(0))); | ||||
|             break; | ||||
|         case OP_ARRAY_MAP: | ||||
|         case OP_SET_UNION: | ||||
|         case OP_SET_INTERSECT: | ||||
|         case OP_SET_DIFFERENCE: | ||||
|         case OP_SET_COMPLEMENT: | ||||
|             for (auto* arg : euf::enode_args(n))  | ||||
|                 set_prop_upward_store(arg); | ||||
|             set_prop_upward(find(n)); | ||||
|             break; | ||||
|         case OP_SET_SUBSET: | ||||
|             break; | ||||
|         case OP_SET_HAS_SIZE: | ||||
|         case OP_SET_CARD: | ||||
|             ctx.unhandled_function(n->get_decl()); | ||||
|             break; | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|             break; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  |  | |||
|  | @ -60,6 +60,11 @@ namespace array { | |||
|         ptr_vector<expr> args; | ||||
|         sort* srt = n->get_sort(); | ||||
|         n = n->get_root(); | ||||
|         if (a.is_as_array(n->get_expr())) { | ||||
|             values.set(n->get_expr_id(), n->get_expr()); | ||||
|             return; | ||||
|         } | ||||
|              | ||||
|         unsigned arity = get_array_arity(srt); | ||||
|         func_decl * f    = mk_aux_decl_for_array_sort(m, srt); | ||||
|         func_interp * fi = alloc(func_interp, m, arity); | ||||
|  |  | |||
|  | @ -186,17 +186,16 @@ namespace array { | |||
|         if (should_set_prop_upward(d)) | ||||
|             set_prop_upward(d); | ||||
|         ctx.push_vec(d.m_lambdas, lambda); | ||||
|         if (should_set_prop_upward(d)) { | ||||
|             set_prop_upward(lambda); | ||||
|             propagate_select_axioms(d, lambda); | ||||
|         } | ||||
|         propagate_select_axioms(d, lambda); | ||||
|         if (should_set_prop_upward(d))  | ||||
|             set_prop_upward_store(lambda);                     | ||||
|     } | ||||
| 
 | ||||
|     void solver::add_parent_lambda(theory_var v_child, euf::enode* lambda) { | ||||
|         SASSERT(can_beta_reduce(lambda)); | ||||
|         auto& d = get_var_data(find(v_child)); | ||||
|         ctx.push_vec(d.m_parent_lambdas, lambda); | ||||
|         if (should_set_prop_upward(d)) | ||||
|         if (should_prop_upward(d))         | ||||
|             propagate_select_axioms(d, lambda); | ||||
|     } | ||||
| 
 | ||||
|  | @ -231,6 +230,9 @@ namespace array { | |||
|         for (euf::enode* lambda : d.m_lambdas)  | ||||
|             propagate_select_axioms(d, lambda);         | ||||
|          | ||||
|         if (!should_prop_upward(d)) | ||||
|             return; | ||||
| 
 | ||||
|         for (euf::enode* lambda : d.m_parent_lambdas) | ||||
|             propagate_select_axioms(d, lambda); | ||||
|     } | ||||
|  | @ -246,14 +248,14 @@ namespace array { | |||
|         set_prop_upward(d); | ||||
|     } | ||||
| 
 | ||||
|     void solver::set_prop_upward(euf::enode* n) { | ||||
|     void solver::set_prop_upward_store(euf::enode* n) { | ||||
|         if (a.is_store(n->get_expr())) | ||||
|             set_prop_upward(n->get_arg(0)->get_th_var(get_id())); | ||||
|     } | ||||
| 
 | ||||
|     void solver::set_prop_upward(var_data& d) { | ||||
|         for (auto* p : d.m_lambdas) | ||||
|             set_prop_upward(p); | ||||
|             set_prop_upward_store(p); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|  | @ -273,6 +275,6 @@ namespace array { | |||
|     } | ||||
| 
 | ||||
|     bool solver::can_beta_reduce(expr* c) const { | ||||
|         return a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c) || a.is_map(c); | ||||
|         return a.is_const(c) || a.is_as_array(c) || a.is_store(c) || is_lambda(c) || is_map_combinator(c); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -66,6 +66,7 @@ namespace array { | |||
|         array_union_find                     m_find; | ||||
| 
 | ||||
|         theory_var find(theory_var v) { return m_find.find(v); } | ||||
|         theory_var find(euf::enode* n) { return find(n->get_th_var(get_id())); } | ||||
|         func_decl_ref_vector const& sort2diff(sort* s); | ||||
| 
 | ||||
|         // internalize
 | ||||
|  | @ -73,12 +74,8 @@ namespace array { | |||
|         bool visited(expr* e) override; | ||||
|         bool post_visit(expr* e, bool sign, bool root) override; | ||||
|         void ensure_var(euf::enode* n); | ||||
|         void internalize_store(euf::enode* n); | ||||
|         void internalize_select(euf::enode* n); | ||||
|         void internalize_lambda(euf::enode* n); | ||||
|         void internalize_ext(euf::enode* n); | ||||
|         void internalize_default(euf::enode* n); | ||||
|         void internalize_map(euf::enode* n); | ||||
|         void internalize_eh(euf::enode* n); | ||||
|         void internalize_lambda_eh(euf::enode* n); | ||||
| 
 | ||||
|         // axioms
 | ||||
|         struct axiom_record { | ||||
|  | @ -157,7 +154,6 @@ namespace array { | |||
|         bool assert_axiom(unsigned idx); | ||||
|         bool assert_select(unsigned idx, axiom_record & r); | ||||
|         bool assert_default(axiom_record & r); | ||||
|         bool is_relevant(axiom_record const& r) const; | ||||
|         void set_applied(unsigned idx) { m_axiom_trail[idx].set_applied(); } | ||||
|         bool is_applied(unsigned idx) const { return m_axiom_trail[idx].is_applied(); } | ||||
|         bool is_delayed(unsigned idx) const { return m_axiom_trail[idx].is_delayed(); } | ||||
|  | @ -185,7 +181,9 @@ namespace array { | |||
|         bool assert_congruent_axiom(expr* e1, expr* e2); | ||||
|         bool add_delayed_axioms(); | ||||
|         bool add_as_array_eqs(euf::enode* n); | ||||
|          | ||||
|         expr_ref apply_map(app* map, unsigned n, expr* const* args); | ||||
|         bool is_map_combinator(expr* e) const; | ||||
| 
 | ||||
|         bool has_unitary_domain(app* array_term); | ||||
|         bool has_large_domain(expr* array_term); | ||||
|         std::pair<app*, func_decl*> mk_epsilon(sort* s); | ||||
|  | @ -197,7 +195,7 @@ namespace array { | |||
|         // solving          
 | ||||
|         void add_parent_select(theory_var v_child, euf::enode* select); | ||||
|         void add_parent_default(theory_var v_child, euf::enode* def); | ||||
|         void add_lambda(theory_var v, euf::enode* lambda); | ||||
|         void add_lambda(theory_var v, euf::enode* lambda);         | ||||
|         void add_parent_lambda(theory_var v_child, euf::enode* lambda); | ||||
| 
 | ||||
|         void propagate_select_axioms(var_data const& d, euf::enode* a); | ||||
|  | @ -206,7 +204,7 @@ namespace array { | |||
| 
 | ||||
|         void set_prop_upward(theory_var v); | ||||
|         void set_prop_upward(var_data& d); | ||||
|         void set_prop_upward(euf::enode* n); | ||||
|         void set_prop_upward_store(euf::enode* n); | ||||
|         unsigned get_lambda_equiv_size(var_data const& d) const; | ||||
|         bool should_set_prop_upward(var_data const& d) const; | ||||
|         bool should_prop_upward(var_data const& d) const; | ||||
|  | @ -256,6 +254,7 @@ namespace array { | |||
|         void new_diseq_eh(euf::th_eq const& eq) override; | ||||
|         bool unit_propagate() override; | ||||
|         void init_model() override; | ||||
|         bool include_func_interp(func_decl* f) const override { return a.is_ext(f); } | ||||
|         void add_value(euf::enode* n, model& mdl, expr_ref_vector& values) override; | ||||
|         bool add_dep(euf::enode* n, top_sort<euf::enode>& dep) override; | ||||
|         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; | ||||
|  | @ -264,7 +263,9 @@ namespace array { | |||
|         void apply_sort_cnstr(euf::enode* n, sort* s) override; | ||||
|         bool is_shared(theory_var v) const override; | ||||
|         bool enable_self_propagate() const override { return true; } | ||||
| 
 | ||||
|         void relevant_eh(euf::enode* n) override; | ||||
|         bool enable_ackerman_axioms(euf::enode* n) const override { return !a.is_array(n->get_sort()); } | ||||
|   | ||||
|         void merge_eh(theory_var, theory_var, theory_var v1, theory_var v2); | ||||
|         void after_merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) {} | ||||
|         void unmerge_eh(theory_var v1, theory_var v2) {} | ||||
|  |  | |||
|  | @ -21,7 +21,10 @@ Author: | |||
| namespace bv { | ||||
| 
 | ||||
|     bool solver::check_delay_internalized(expr* e) { | ||||
|         if (!ctx.is_relevant(e)) | ||||
|         euf::enode* n = expr2enode(e); | ||||
|         if (!n) | ||||
|             return true; | ||||
|         if (!ctx.is_relevant(n)) | ||||
|             return true; | ||||
|         if (get_internalize_mode(e) != internalize_mode::delay_i) | ||||
|             return true; | ||||
|  | @ -247,7 +250,7 @@ namespace bv { | |||
|             return; | ||||
|         expr_ref tmp = literal2expr(bits.back()); | ||||
|         for (unsigned i = bits.size() - 1; i-- > 0; ) { | ||||
|             auto b = bits[i]; | ||||
|             sat::literal b = bits[i]; | ||||
|             tmp = m.mk_or(literal2expr(b), tmp); | ||||
|             xs.push_back(tmp); | ||||
|         } | ||||
|  | @ -356,10 +359,28 @@ namespace bv { | |||
|     solver::internalize_mode solver::get_internalize_mode(expr* e) { | ||||
|         if (!bv.is_bv(e)) | ||||
|             return internalize_mode::no_delay_i; | ||||
|         if (!get_config().m_bv_delay) | ||||
|             return internalize_mode::no_delay_i; | ||||
|         if (!reflect()) | ||||
|             return internalize_mode::no_delay_i; | ||||
|         if (get_config().m_bv_polysat) { | ||||
|             switch (to_app(e)->get_decl_kind()) { | ||||
|             case OP_BMUL:  | ||||
|             case OP_BUMUL_NO_OVFL: | ||||
|             case OP_BSMOD_I: | ||||
|             case OP_BUREM_I: | ||||
|             case OP_BUDIV_I: | ||||
|             case OP_BADD: | ||||
|                 return internalize_mode::polysat_i; | ||||
|             case OP_BSMUL_NO_OVFL: | ||||
|             case OP_BSMUL_NO_UDFL: | ||||
|             case OP_BSDIV_I:  | ||||
|             case OP_BSREM_I: | ||||
|             default: | ||||
|                 return internalize_mode::no_delay_i; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         if (!get_config().m_bv_delay) | ||||
|             return internalize_mode::no_delay_i; | ||||
|         internalize_mode mode; | ||||
|         switch (to_app(e)->get_decl_kind()) { | ||||
|         case OP_BMUL:  | ||||
|  |  | |||
|  | @ -144,10 +144,16 @@ namespace bv { | |||
|         SASSERT(!n->is_attached_to(get_id())); | ||||
|         mk_var(n); | ||||
|         SASSERT(n->is_attached_to(get_id())); | ||||
|         if (internalize_mode::no_delay_i != get_internalize_mode(a))  | ||||
|             mk_bits(n->get_th_var(get_id())); | ||||
|         else | ||||
|         switch (get_internalize_mode(a)) { | ||||
|         case internalize_mode::no_delay_i: | ||||
|             internalize_circuit(a); | ||||
|             break; | ||||
|         case internalize_mode::polysat_i: | ||||
|             NOT_IMPLEMENTED_YET(); | ||||
|             break; | ||||
|         default: | ||||
|             mk_bits(n->get_th_var(get_id())); | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -166,6 +172,7 @@ namespace bv { | |||
| #define internalize_pun(F) pun = [&](unsigned sz, expr* const* xs, unsigned p, expr_ref_vector& bits) { m_bb.F(sz, xs, p, bits);}; internalize_par_unary(a, pun); | ||||
| #define internalize_nfl(F) ebin = [&](unsigned sz, expr* const* xs, expr* const* ys, expr_ref& out) { m_bb.F(sz, xs, ys, out);}; internalize_novfl(a, ebin); | ||||
| #define internalize_int(B, U) ibin = [&](expr* x, expr* y) { return B(x, y); }; iun = [&](expr* x) { return U(x); }; internalize_interp(a, ibin, iun); | ||||
| #define if_unary(F) if (a->get_num_args() == 1) { internalize_un(F); break; } | ||||
| 
 | ||||
|         switch (a->get_decl_kind()) { | ||||
|         case OP_BV_NUM:           internalize_num(a); break; | ||||
|  | @ -190,7 +197,7 @@ namespace bv { | |||
|         case OP_BXOR:             internalize_ac(mk_xor); break; | ||||
|         case OP_BNAND:            internalize_bin(mk_nand); break; | ||||
|         case OP_BNOR:             internalize_bin(mk_nor); break; | ||||
|         case OP_BXNOR:            internalize_bin(mk_xnor); break; | ||||
|         case OP_BXNOR:            if_unary(mk_not); internalize_bin(mk_xnor); break; | ||||
|         case OP_BCOMP:            internalize_bin(mk_comp); break;         | ||||
|         case OP_SIGN_EXT:         internalize_pun(mk_sign_extend); break; | ||||
|         case OP_ZERO_EXT:         internalize_pun(mk_zero_extend); break; | ||||
|  | @ -370,6 +377,7 @@ namespace bv { | |||
|         if (m_true == sat::null_literal) { | ||||
|             ctx.push(value_trail<sat::literal>(m_true)); | ||||
|             m_true = ctx.internalize(m.mk_true(), false, true, false); | ||||
|             s().assign_unit(m_true); | ||||
|         } | ||||
|         return m_true; | ||||
|     } | ||||
|  | @ -426,7 +434,6 @@ namespace bv { | |||
|         expr_ref sum(m_autil.mk_add(sz, args.data()), m); | ||||
|         sat::literal lit = eq_internalize(n, sum); | ||||
| 	add_unit(lit); | ||||
| 	ctx.add_root(lit); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_int2bv(app* n) { | ||||
|  | @ -456,7 +463,6 @@ namespace bv { | |||
|         rhs = m_autil.mk_mod(e, m_autil.mk_int(mod)); | ||||
| 	sat::literal eq_lit = eq_internalize(lhs, rhs); | ||||
| 	add_unit(eq_lit); | ||||
| 	ctx.add_root(eq_lit); | ||||
|         | ||||
|         expr_ref_vector n_bits(m); | ||||
|         get_bits(n_enode, n_bits); | ||||
|  | @ -469,7 +475,6 @@ namespace bv { | |||
|             lhs = n_bits.get(i); | ||||
| 	    eq_lit = eq_internalize(lhs, rhs); | ||||
| 	    add_unit(eq_lit); | ||||
| 	    ctx.add_root(eq_lit); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -535,7 +540,6 @@ namespace bv { | |||
|         if (p.hi_div0()) { | ||||
|             eq_lit = eq_internalize(n, ibin(arg1, arg2)); | ||||
| 	    add_unit(eq_lit); | ||||
| 	    ctx.add_root(eq_lit); | ||||
| 	} | ||||
| 	else { | ||||
| 	    unsigned sz = bv.get_bv_size(n); | ||||
|  | @ -653,7 +657,6 @@ namespace bv { | |||
|         mk_bits(get_th_var(e)); | ||||
| 	sat::literal eq_lit = eq_internalize(e, r); | ||||
|         add_unit(eq_lit); | ||||
| 	ctx.add_root(eq_lit); | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize_bit2bool(app* n) { | ||||
|  |  | |||
|  | @ -58,6 +58,15 @@ namespace bv { | |||
|         m_bb.set_flat(false); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::is_fixed(euf::theory_var v, expr_ref& val, sat::literal_vector& lits) { | ||||
|         numeral n; | ||||
|         if (!get_fixed_value(v, n)) | ||||
|             return false; | ||||
|         val = bv.mk_numeral(n, m_bits[v].size()); | ||||
|         lits.append(m_bits[v]); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void solver::fixed_var_eh(theory_var v1) { | ||||
|         numeral val1, val2; | ||||
|         VERIFY(get_fixed_value(v1, val1)); | ||||
|  | @ -158,7 +167,7 @@ namespace bv { | |||
|         SASSERT(m_bits[v1][idx] == ~m_bits[v2][idx]); | ||||
|         TRACE("bv", tout << "found new diseq axiom\n" << pp(v1) << pp(v2);); | ||||
|         m_stats.m_num_diseq_static++; | ||||
|         expr_ref eq = mk_var_eq(v1, v2); | ||||
|         expr_ref eq(m.mk_eq(var2expr(v1), var2expr(v2)), m); | ||||
|         add_unit(~ctx.internalize(eq, false, false, m_is_redundant)); | ||||
|     } | ||||
| 
 | ||||
|  | @ -740,6 +749,7 @@ namespace bv { | |||
|     void solver::merge_eh(theory_var r1, theory_var r2, theory_var v1, theory_var v2) { | ||||
| 
 | ||||
|         TRACE("bv", tout << "merging: v" << v1 << " #" << var2enode(v1)->get_expr_id() << " v" << v2 << " #" << var2enode(v2)->get_expr_id() << "\n";); | ||||
| 
 | ||||
|         if (!merge_zero_one_bits(r1, r2)) { | ||||
|             TRACE("bv", tout << "conflict detected\n";); | ||||
|             return; // conflict was detected
 | ||||
|  |  | |||
|  | @ -226,6 +226,7 @@ namespace bv { | |||
|         void get_bits(euf::enode* n, expr_ref_vector& r); | ||||
|         void get_arg_bits(app* n, unsigned idx, expr_ref_vector& r); | ||||
|         void fixed_var_eh(theory_var v); | ||||
|         bool is_fixed(euf::theory_var v, expr_ref& val, sat::literal_vector& lits) override; | ||||
|         bool is_bv(theory_var v) const { return bv.is_bv(var2expr(v)); } | ||||
|         void register_true_false_bit(theory_var v, unsigned i); | ||||
|         void add_bit(theory_var v, sat::literal lit); | ||||
|  | @ -266,6 +267,7 @@ namespace bv { | |||
|         enum class internalize_mode { | ||||
|             delay_i, | ||||
|             no_delay_i, | ||||
|             polysat_i, | ||||
|             init_bits_only_i | ||||
|         }; | ||||
| 
 | ||||
|  |  | |||
|  | @ -99,14 +99,38 @@ namespace euf { | |||
|         m_tmp_inference->init(m_tmp_inference); | ||||
|     } | ||||
| 
 | ||||
|     bool ackerman::enable_cc(app* a, app* b) { | ||||
|         if (!s.enable_ackerman_axioms(a)) | ||||
|             return false; | ||||
|         if (!s.enable_ackerman_axioms(b)) | ||||
|             return false; | ||||
|         for (expr* arg : *a) | ||||
|             if (!s.enable_ackerman_axioms(arg)) | ||||
|                 return false; | ||||
|         for (expr* arg : *b) | ||||
|             if (!s.enable_ackerman_axioms(arg)) | ||||
|                 return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool ackerman::enable_eq(expr* a, expr* b, expr* c) { | ||||
|         return s.enable_ackerman_axioms(a) &&  | ||||
|             s.enable_ackerman_axioms(b) &&  | ||||
|             s.enable_ackerman_axioms(c);            | ||||
|     } | ||||
| 
 | ||||
|     void ackerman::cg_conflict_eh(expr * n1, expr * n2) { | ||||
|         if (!is_app(n1) || !is_app(n2)) | ||||
|             return; | ||||
|         if (!s.enable_ackerman_axioms(n1)) | ||||
|             return; | ||||
|         SASSERT(!s.m_drating); | ||||
|         app* a = to_app(n1); | ||||
|         app* b = to_app(n2); | ||||
|         if (a->get_decl() != b->get_decl() || a->get_num_args() != b->get_num_args()) | ||||
|             return; | ||||
|         if (!enable_cc(a, b)) | ||||
|             return; | ||||
|         TRACE("ack", tout << "conflict eh: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";); | ||||
|         insert(a, b); | ||||
|         gc(); | ||||
|  | @ -117,6 +141,8 @@ namespace euf { | |||
|             return; | ||||
|         if (s.m_drating) | ||||
|             return; | ||||
|         if (!enable_eq(a, b, c)) | ||||
|             return; | ||||
|         TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";); | ||||
|         insert(a, b, c); | ||||
|         gc(); | ||||
|  | @ -128,6 +154,8 @@ namespace euf { | |||
|         TRACE("ack", tout << "used cc: " << mk_pp(a, m) << " == " << mk_pp(b, m) << "\n";); | ||||
|         SASSERT(a->get_decl() == b->get_decl()); | ||||
|         SASSERT(a->get_num_args() == b->get_num_args()); | ||||
|         if (!enable_cc(a, b)) | ||||
|             return; | ||||
|         insert(a, b); | ||||
|         gc(); | ||||
|     } | ||||
|  | @ -173,13 +201,13 @@ namespace euf { | |||
|         app* b = to_app(_b); | ||||
|         TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << "\n";); | ||||
|         sat::literal_vector lits; | ||||
|         unsigned sz = a->get_num_args(); | ||||
|         unsigned sz = a->get_num_args();         | ||||
|          | ||||
|         for (unsigned i = 0; i < sz; ++i) { | ||||
|             expr_ref eq(m.mk_eq(a->get_arg(i), b->get_arg(i)), m); | ||||
|             expr_ref eq = s.mk_eq(a->get_arg(i), b->get_arg(i)); | ||||
|             lits.push_back(~s.mk_literal(eq)); | ||||
|         } | ||||
|         expr_ref eq(m.mk_eq(a, b), m); | ||||
|         expr_ref eq = s.mk_eq(a, b); | ||||
|         lits.push_back(s.mk_literal(eq)); | ||||
|         s.s().mk_clause(lits, sat::status::th(true, m.get_basic_family_id())); | ||||
|     } | ||||
|  | @ -187,9 +215,9 @@ namespace euf { | |||
|     void ackerman::add_eq(expr* a, expr* b, expr* c) { | ||||
|         flet<bool> _is_redundant(s.m_is_redundant, true); | ||||
|         sat::literal lits[3]; | ||||
|         expr_ref eq1(m.mk_eq(a, c), m); | ||||
|         expr_ref eq2(m.mk_eq(b, c), m); | ||||
|         expr_ref eq3(m.mk_eq(a, b), m); | ||||
|         expr_ref eq1(s.mk_eq(a, c), m); | ||||
|         expr_ref eq2(s.mk_eq(b, c), m); | ||||
|         expr_ref eq3(s.mk_eq(a, b), m); | ||||
|         TRACE("ack", tout << mk_pp(a, m) << " " << mk_pp(b, m) << " " << mk_pp(c, m) << "\n";); | ||||
|         lits[0] = ~s.mk_literal(eq1); | ||||
|         lits[1] = ~s.mk_literal(eq2); | ||||
|  |  | |||
|  | @ -70,6 +70,9 @@ namespace euf { | |||
|         void add_cc(expr* a, expr* b); | ||||
|         void add_eq(expr* a, expr* b, expr* c);         | ||||
|         void gc(); | ||||
|         bool enable_cc(app* a, app* b); | ||||
|         bool enable_eq(expr* a, expr* b, expr* c); | ||||
| 
 | ||||
| 
 | ||||
|     public: | ||||
|         ackerman(solver& s, ast_manager& m); | ||||
|  |  | |||
|  | @ -162,31 +162,34 @@ namespace euf { | |||
|             sat::literal lit2 = literal(v, false); | ||||
|             s().mk_clause(~lit, lit2, sat::status::th(m_is_redundant, m.get_basic_family_id())); | ||||
|             s().mk_clause(lit, ~lit2, sat::status::th(m_is_redundant, m.get_basic_family_id())); | ||||
|             if (relevancy_enabled()) { | ||||
|                 add_aux(~lit, lit2); | ||||
|                 add_aux(lit, ~lit2); | ||||
|             } | ||||
|             add_aux(~lit, lit2); | ||||
|             add_aux(lit, ~lit2); | ||||
|             lit = lit2; | ||||
|         } | ||||
| 
 | ||||
|         TRACE("euf", tout << "attach " << v << " " << mk_bounded_pp(e, m) << "\n";); | ||||
|         m_bool_var2expr.reserve(v + 1, nullptr); | ||||
|         if (m_bool_var2expr[v] && m_egraph.find(e)) { | ||||
|             if (m_egraph.find(e)->bool_var() != v) { | ||||
|                 IF_VERBOSE(0, verbose_stream() | ||||
|                  << "var " << v << "\n" | ||||
|                  << "found var " << m_egraph.find(e)->bool_var() << "\n" | ||||
|                  << mk_pp(m_bool_var2expr[v], m) << "\n" | ||||
|                  << mk_pp(e, m) << "\n"); | ||||
|             } | ||||
|             SASSERT(m_egraph.find(e)->bool_var() == v); | ||||
|             return lit; | ||||
|         } | ||||
|         TRACE("euf", tout << "attach " << v << " " << mk_bounded_pp(e, m) << "\n";); | ||||
| 
 | ||||
|         m_bool_var2expr[v] = e; | ||||
|         m_var_trail.push_back(v); | ||||
|         m_var_trail.push_back(v);         | ||||
|         enode* n = m_egraph.find(e); | ||||
|         if (!n) { | ||||
|         if (!n)  | ||||
|             n = mk_enode(e, 0, nullptr); | ||||
|         } | ||||
|         SASSERT(n->bool_var() == sat::null_bool_var || n->bool_var() == v); | ||||
|         m_egraph.set_bool_var(n, v); | ||||
|         if (m.is_eq(e) || m.is_or(e) || m.is_and(e) || m.is_not(e)) | ||||
|             m_egraph.set_merge_enabled(n, false); | ||||
|         if (!si.is_bool_op(e)) | ||||
|             track_relevancy(lit.var()); | ||||
|         if (s().value(lit) != l_undef)  | ||||
|             m_egraph.set_value(n, s().value(lit)); | ||||
|         return lit; | ||||
|  | @ -224,9 +227,8 @@ namespace euf { | |||
|                     lits.push_back(lit); | ||||
|                 } | ||||
|             } | ||||
|             add_root(lits); | ||||
|             s().mk_clause(lits, st); | ||||
|             if (relevancy_enabled()) | ||||
|                 add_root(lits); | ||||
|         } | ||||
|         else { | ||||
|             // g(f(x_i)) = x_i
 | ||||
|  | @ -244,15 +246,13 @@ namespace euf { | |||
|                 expr_ref gapp(m.mk_app(g, fapp.get()), m); | ||||
|                 expr_ref eq = mk_eq(gapp, arg); | ||||
|                 sat::literal lit = mk_literal(eq); | ||||
|                 s().add_clause(1, &lit, st); | ||||
|                 s().add_clause(lit, st); | ||||
|                 eqs.push_back(mk_eq(fapp, a)); | ||||
|             } | ||||
|             pb_util pb(m); | ||||
|             expr_ref at_least2(pb.mk_at_least_k(eqs.size(), eqs.data(), 2), m); | ||||
|             sat::literal lit = si.internalize(at_least2, m_is_redundant); | ||||
|             s().mk_clause(1, &lit, st); | ||||
|             if (relevancy_enabled()) | ||||
|                 add_root(1, &lit); | ||||
|             s().add_clause(lit, st); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -268,9 +268,7 @@ namespace euf { | |||
|                 for (unsigned j = i + 1; j < sz; ++j) { | ||||
|                     expr_ref eq = mk_eq(args[i]->get_expr(), args[j]->get_expr()); | ||||
|                     sat::literal lit = ~mk_literal(eq); | ||||
|                     s().add_clause(1, &lit, st); | ||||
|                     if (relevancy_enabled()) | ||||
|                         add_root(1, &lit); | ||||
|                     s().add_clause(lit, st); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|  | @ -287,9 +285,7 @@ namespace euf { | |||
|                 n->mark_interpreted(); | ||||
|                 expr_ref eq = mk_eq(fapp, fresh); | ||||
|                 sat::literal lit = mk_literal(eq); | ||||
|                 s().add_clause(1, &lit, st); | ||||
|                 if (relevancy_enabled()) | ||||
|                     add_root(1, &lit); | ||||
|                 s().add_clause(lit, st); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -302,16 +298,16 @@ namespace euf { | |||
|             expr_ref eq_th = mk_eq(e, th); | ||||
|             sat::literal lit_th = mk_literal(eq_th); | ||||
|             if (th == el) { | ||||
|                 s().add_clause(1, &lit_th, st); | ||||
|                 s().add_clause(lit_th, st); | ||||
|             } | ||||
|             else { | ||||
|                 sat::literal lit_c = mk_literal(c); | ||||
|                 expr_ref eq_el = mk_eq(e, el); | ||||
|                 sat::literal lit_el = mk_literal(eq_el); | ||||
|                 literal lits1[2] = { ~lit_c,  lit_th }; | ||||
|                 literal lits2[2] = { lit_c, lit_el }; | ||||
|                 s().add_clause(2, lits1, st); | ||||
|                 s().add_clause(2, lits2, st); | ||||
|                 add_root(~lit_c, lit_th); | ||||
|                 add_root(lit_c, lit_el); | ||||
|                 s().add_clause(~lit_c, lit_th, st); | ||||
|                 s().add_clause(lit_c, lit_el, st); | ||||
|             } | ||||
|         } | ||||
|         else if (m.is_distinct(e)) { | ||||
|  | @ -326,10 +322,10 @@ namespace euf { | |||
|             expr_ref fml(m.mk_or(eqs), m); | ||||
|             sat::literal dist(si.to_bool_var(e), false); | ||||
|             sat::literal some_eq = si.internalize(fml, m_is_redundant); | ||||
|             sat::literal lits1[2] = { ~dist, ~some_eq }; | ||||
|             sat::literal lits2[2] = { dist, some_eq }; | ||||
|             s().add_clause(2, lits1, st); | ||||
|             s().add_clause(2, lits2, st); | ||||
|             add_root(~dist, ~some_eq); | ||||
|             add_root(dist, some_eq); | ||||
|             s().add_clause(~dist, ~some_eq, st); | ||||
|             s().add_clause(dist, some_eq, st); | ||||
|         } | ||||
|         else if (m.is_eq(e, th, el) && !m.is_iff(e)) { | ||||
|             sat::literal lit1 = expr2literal(e); | ||||
|  | @ -338,10 +334,10 @@ namespace euf { | |||
|             enode* n2 = m_egraph.find(e2); | ||||
|             if (n2) { | ||||
|                 sat::literal lit2 = expr2literal(e2); | ||||
|                 sat::literal lits1[2] = { ~lit1, lit2 }; | ||||
|                 sat::literal lits2[2] = { lit1, ~lit2 }; | ||||
|                 s().add_clause(2, lits1, st); | ||||
|                 s().add_clause(2, lits2, st); | ||||
|                 add_root(~lit1, lit2); | ||||
|                 add_root(lit1, ~lit2); | ||||
|                 s().add_clause(~lit1, lit2, st); | ||||
|                 s().add_clause(lit1, ~lit2, st); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | @ -357,7 +353,7 @@ namespace euf { | |||
|         // contains a parent application.
 | ||||
|          | ||||
|         family_id th_id = m.get_basic_family_id(); | ||||
|         for (auto p : euf::enode_th_vars(n)) { | ||||
|         for (auto const& p : euf::enode_th_vars(n)) { | ||||
|             family_id id = p.get_id(); | ||||
|             if (m.get_basic_family_id() != id) { | ||||
|                 if (th_id != m.get_basic_family_id()) | ||||
|  | @ -402,7 +398,7 @@ namespace euf { | |||
|         // Remark: The inconsistency is not going to be detected if they are
 | ||||
|         // not marked as shared.
 | ||||
| 
 | ||||
|         for (auto p : euf::enode_th_vars(n))  | ||||
|         for (auto const& p : euf::enode_th_vars(n))  | ||||
|             if (fid2solver(p.get_id())->is_shared(p.get_var())) | ||||
|                 return true; | ||||
| 
 | ||||
|  |  | |||
|  | @ -35,7 +35,7 @@ namespace euf { | |||
|             s(s), m(s.m), mdl(mdl), values(values), factory(m) {} | ||||
| 
 | ||||
|         ~user_sort() { | ||||
|             for (auto kv : sort2values) | ||||
|             for (auto const& kv : sort2values) | ||||
|                 mdl->register_usort(kv.m_key, kv.m_value->size(), kv.m_value->data()); | ||||
|         } | ||||
| 
 | ||||
|  | @ -161,10 +161,9 @@ namespace euf { | |||
|                 default: | ||||
|                     break; | ||||
|                 } | ||||
|                 if (is_app(e) && to_app(e)->get_family_id() == m.get_basic_family_id()) | ||||
|                     continue; | ||||
|                 sat::bool_var v = get_enode(e)->bool_var(); | ||||
|                 SASSERT(v != sat::null_bool_var); | ||||
|                 if (v == sat::null_bool_var) | ||||
|                     continue; | ||||
|                 switch (s().value(v)) { | ||||
|                 case l_true: | ||||
|                     m_values.set(id, m.mk_true()); | ||||
|  | @ -200,6 +199,8 @@ namespace euf { | |||
|             expr* e = n->get_expr(); | ||||
|             if (!is_app(e)) | ||||
|                 continue; | ||||
|             if (!is_relevant(n)) | ||||
|                 continue; | ||||
|             app* a = to_app(e); | ||||
|             func_decl* f = a->get_decl();        | ||||
|             if (!include_func_interp(f)) | ||||
|  | @ -224,7 +225,7 @@ namespace euf { | |||
|                     enode* earg = get_enode(arg);  | ||||
|                     expr* val = m_values.get(earg->get_root_id()); | ||||
|                     args.push_back(val);                 | ||||
|                     CTRACE("euf", !val, tout << "no value for " << bpp(earg) << "\n";); | ||||
|                     CTRACE("euf", !val, tout << "no value for " << bpp(earg) << "\n" << bpp(n) << "\n"; display(tout);); | ||||
|                     SASSERT(val); | ||||
|                 } | ||||
|                 SASSERT(args.size() == arity); | ||||
|  | @ -259,7 +260,7 @@ namespace euf { | |||
|             if (n->is_root() && m_values.get(n->get_expr_id())) | ||||
|                 m_values2root.insert(m_values.get(n->get_expr_id()), n); | ||||
|         TRACE("model",  | ||||
|               for (auto kv : m_values2root)  | ||||
|               for (auto const& kv : m_values2root)  | ||||
|                   tout << mk_bounded_pp(kv.m_key, m) << "\n    -> " << bpp(kv.m_value) << "\n";); | ||||
|          | ||||
|         return m_values2root; | ||||
|  | @ -271,6 +272,7 @@ namespace euf { | |||
| 
 | ||||
|     void solver::display_validation_failure(std::ostream& out, model& mdl, enode* n) { | ||||
|         out << "Failed to validate " << n->bool_var() << " " << bpp(n) << " " << mdl(n->get_expr()) << "\n"; | ||||
|         s().display(out); | ||||
|         euf::enode_vector nodes; | ||||
|         nodes.push_back(n); | ||||
|         for (unsigned i = 0; i < nodes.size(); ++i) { | ||||
|  | @ -289,14 +291,20 @@ namespace euf { | |||
|         for (euf::enode* r : nodes) | ||||
|             r->unmark1(); | ||||
|         out << mdl << "\n"; | ||||
|         s().display(out); | ||||
|     } | ||||
| 
 | ||||
|     void solver::validate_model(model& mdl) { | ||||
|         if (!m_unhandled_functions.empty()) | ||||
|             return; | ||||
|         for (auto* s : m_solvers) | ||||
|             if (s && s->has_unhandled()) | ||||
|                 return; | ||||
|         model_evaluator ev(mdl); | ||||
|         ev.set_model_completion(true); | ||||
|         TRACE("model", | ||||
|             for (enode* n : m_egraph.nodes()) { | ||||
|                 if (!is_relevant(n)) | ||||
|                     continue; | ||||
|                 unsigned id = n->get_root_id(); | ||||
|                 expr* val = m_values.get(id, nullptr); | ||||
|                 if (!val) | ||||
|  | @ -322,8 +330,8 @@ namespace euf { | |||
|             IF_VERBOSE(0, display_validation_failure(verbose_stream(), mdl, n);); | ||||
|             CTRACE("euf", first, display_validation_failure(tout, mdl, n);); | ||||
|             (void)first; | ||||
|             exit(1); | ||||
|             first = false; | ||||
|             exit(1); | ||||
|         }         | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -3,157 +3,326 @@ Copyright (c) 2020 Microsoft Corporation | |||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     euf_relevancy.cpp | ||||
|     smt_relevant.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Features for relevancy tracking. | ||||
|     A reduced (minimal) implicant is extracted by running a dual solver. | ||||
|     Then the literals in the implicant are used as a basis for marking | ||||
|     subterms relevant. | ||||
|     Relevancy propagation | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-09-22 | ||||
|     Nikolaj Bjorner (nbjorner) 2021-12-27 | ||||
| 
 | ||||
| --*/ | ||||
| 
 | ||||
| #include "sat/sat_solver.h" | ||||
| #include "sat/smt/euf_solver.h" | ||||
| #include "sat/smt/euf_relevancy.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     void solver::add_auto_relevant(expr* e) { | ||||
|         if (!relevancy_enabled()) | ||||
|             return; | ||||
|         for (; m_auto_relevant_scopes > 0; --m_auto_relevant_scopes)  | ||||
|             m_auto_relevant_lim.push_back(m_auto_relevant.size()); | ||||
|         // std::cout << "add-auto " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n";
 | ||||
|         m_auto_relevant.push_back(e); | ||||
|     } | ||||
|      | ||||
|     void solver::pop_relevant(unsigned n) { | ||||
|         if (m_auto_relevant_scopes >= n) { | ||||
|             m_auto_relevant_scopes -= n; | ||||
|     void relevancy::pop(unsigned n) { | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         if (n <= m_num_scopes) { | ||||
|             m_num_scopes -= n; | ||||
|             return; | ||||
|         } | ||||
|         n -= m_auto_relevant_scopes; | ||||
|         m_auto_relevant_scopes = 0; | ||||
|         unsigned top = m_auto_relevant_lim.size() - n; | ||||
|         unsigned lim = m_auto_relevant_lim[top]; | ||||
|         m_auto_relevant_lim.shrink(top); | ||||
|         m_auto_relevant.shrink(lim);         | ||||
|         else if (m_num_scopes > 0) { | ||||
|             n -= m_num_scopes; | ||||
|             m_num_scopes = 0; | ||||
|         } | ||||
|         SASSERT(n > 0); | ||||
|         unsigned sz = m_lim[m_lim.size() - n]; | ||||
|         for (unsigned i = m_trail.size(); i-- > sz; ) { | ||||
|             auto const& [u, idx] = m_trail[i]; | ||||
|             switch (u) { | ||||
|             case update::relevant_var: | ||||
|                 m_relevant_var_ids[idx] = false; | ||||
|                 break; | ||||
|             case update::add_queue: | ||||
|                 m_queue.pop_back(); | ||||
|                 break; | ||||
|             case update::add_clause: { | ||||
|                 sat::clause* c = m_clauses.back(); | ||||
|                 for (sat::literal lit : *c) { | ||||
|                     SASSERT(m_occurs[lit.index()].back() == m_clauses.size() - 1); | ||||
|                     m_occurs[lit.index()].pop_back(); | ||||
|                 } | ||||
|                 m_clauses.pop_back(); | ||||
|                 m_roots.pop_back(); | ||||
|                 m_alloc.del_clause(c); | ||||
|                 break; | ||||
|             } | ||||
|             case update::set_root: | ||||
|                 m_roots[idx] = false; | ||||
|                 break; | ||||
|             case update::set_qhead: | ||||
|                 m_qhead = idx; | ||||
|                 break; | ||||
|             default: | ||||
|                 UNREACHABLE(); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         m_trail.shrink(sz); | ||||
|         m_lim.shrink(m_lim.size() - n); | ||||
|     } | ||||
|      | ||||
|     void solver::push_relevant() { | ||||
|         ++m_auto_relevant_scopes; | ||||
|     } | ||||
| 
 | ||||
|     bool solver::is_relevant(expr* e) const {  | ||||
|         return m_relevant_expr_ids.get(e->get_id(), true);  | ||||
|     } | ||||
| 
 | ||||
|     bool solver::is_relevant(enode* n) const {  | ||||
|         return m_relevant_expr_ids.get(n->get_expr_id(), true);  | ||||
|     } | ||||
| 
 | ||||
|     void solver::ensure_dual_solver() { | ||||
|         if (m_dual_solver) | ||||
|     void relevancy::add_root(unsigned n, sat::literal const* lits) { | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         m_dual_solver = alloc(sat::dual_solver, s().rlimit()); | ||||
|         for (unsigned i = s().num_user_scopes(); i-- > 0; ) | ||||
|             m_dual_solver->push();         | ||||
|         flush(); | ||||
|         TRACE("relevancy", tout << "root " << sat::literal_vector(n, lits) << "\n"); | ||||
|         sat::literal true_lit = sat::null_literal; | ||||
|         for (unsigned i = 0; i < n; ++i) { | ||||
|             if (ctx.s().value(lits[i]) == l_true) { | ||||
|                 if (is_relevant(lits[i])) | ||||
|                     return; | ||||
|                 true_lit = lits[i];                 | ||||
|             } | ||||
|         } | ||||
|         if (true_lit != sat::null_literal) { | ||||
|             mark_relevant(true_lit); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         sat::clause* cl = m_alloc.mk_clause(n, lits, false); | ||||
|         unsigned sz = m_clauses.size(); | ||||
|         m_clauses.push_back(cl); | ||||
|         m_roots.push_back(true); | ||||
|         m_trail.push_back(std::make_pair(update::add_clause, 0)); | ||||
|         for (sat::literal lit : *cl) { | ||||
|             ctx.s().set_external(lit.var()); | ||||
|             occurs(lit).push_back(sz); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     void relevancy::add_def(unsigned n, sat::literal const* lits) {         | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         flush(); | ||||
|         TRACE("relevancy", tout << "def " << sat::literal_vector(n, lits) << "\n"); | ||||
|         for (unsigned i = 0; i < n; ++i) { | ||||
|             if (ctx.s().value(lits[i]) == l_false && is_relevant(lits[i])) { | ||||
|                 add_root(n, lits); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|         sat::clause* cl = m_alloc.mk_clause(n, lits, false); | ||||
|         unsigned sz = m_clauses.size(); | ||||
|         m_clauses.push_back(cl); | ||||
|         m_roots.push_back(false); | ||||
|         m_trail.push_back(std::make_pair(update::add_clause, 0)); | ||||
|         for (sat::literal lit : *cl) { | ||||
|             ctx.s().set_external(lit.var()); | ||||
|             occurs(lit).push_back(sz); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::add_to_propagation_queue(sat::literal lit) { | ||||
|         m_trail.push_back(std::make_pair(update::add_queue, lit.var())); | ||||
|         m_queue.push_back(std::make_pair(lit, nullptr)); | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::set_relevant(sat::literal lit) { | ||||
|         euf::enode* n = ctx.bool_var2enode(lit.var()); | ||||
|         if (n) | ||||
|             mark_relevant(n);         | ||||
|         m_relevant_var_ids.setx(lit.var(), true, false); | ||||
|         m_trail.push_back(std::make_pair(update::relevant_var, lit.var())); | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::set_asserted(sat::literal lit) { | ||||
|         SASSERT(!is_relevant(lit)); | ||||
|         SASSERT(ctx.s().value(lit) == l_true); | ||||
|         set_relevant(lit); | ||||
|         add_to_propagation_queue(lit); | ||||
|         ctx.asserted(lit); | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Add a root clause. Root clauses must all be satisfied by the  | ||||
|      * final assignment. If a clause is added above search level it | ||||
|      * is subject to removal on backtracking. These clauses are therefore | ||||
|      * not tracked. | ||||
|      */ | ||||
|     void solver::add_root(unsigned n, sat::literal const* lits) { | ||||
|         if (!relevancy_enabled()) | ||||
|     * Boolean variable is set relevant because an E-node is relevant. | ||||
|     *  | ||||
|     */ | ||||
|     void relevancy::relevant_eh(sat::bool_var v) { | ||||
|         if (is_relevant(v)) | ||||
|             return; | ||||
|         ensure_dual_solver(); | ||||
|         m_dual_solver->add_root(n, lits); | ||||
|     } | ||||
| 
 | ||||
|     void solver::add_aux(unsigned n, sat::literal const* lits) { | ||||
|         if (!relevancy_enabled()) | ||||
|             return; | ||||
|         ensure_dual_solver(); | ||||
|         m_dual_solver->add_aux(n, lits); | ||||
|     } | ||||
| 
 | ||||
|     void solver::track_relevancy(sat::bool_var v) { | ||||
|         ensure_dual_solver(); | ||||
|         m_dual_solver->track_relevancy(v); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::init_relevancy() { | ||||
|         m_relevant_expr_ids.reset(); | ||||
|         if (!relevancy_enabled()) | ||||
|             return true; | ||||
|         if (!m_dual_solver) | ||||
|             return true; | ||||
|         if (!(*m_dual_solver)(s())) | ||||
|             return false; | ||||
|         unsigned max_id = 0; | ||||
|         for (enode* n : m_egraph.nodes()) | ||||
|             max_id = std::max(max_id, n->get_expr_id()); | ||||
|         m_relevant_expr_ids.resize(max_id + 1, false); | ||||
|         ptr_vector<expr>& todo = m_relevant_todo; | ||||
|         bool_vector& visited = m_relevant_visited; | ||||
|         auto const& core = m_dual_solver->core(); | ||||
|         todo.reset(); | ||||
|         for (auto lit : core) { | ||||
|             expr* e = m_bool_var2expr.get(lit.var(), nullptr); | ||||
|             if (e) | ||||
|                 todo.push_back(e); | ||||
|         sat::literal lit(v); | ||||
|         switch (ctx.s().value(lit)) { | ||||
|         case l_undef: | ||||
|             set_relevant(lit); | ||||
|             break; | ||||
|         case l_true: | ||||
|             set_asserted(lit); | ||||
|             break; | ||||
|         case l_false: | ||||
|             set_asserted(~lit); | ||||
|             break; | ||||
|         } | ||||
| #if 0 | ||||
|         std::cout << "init-relevant\n"; | ||||
|         for (expr* e : m_auto_relevant) | ||||
|             std::cout << "auto-relevant " << e->get_id() << " " << mk_bounded_pp(e, m) << "\n"; | ||||
| #endif | ||||
|         todo.append(m_auto_relevant); | ||||
|         for (unsigned i = 0; i < todo.size(); ++i) { | ||||
|             expr* e = todo[i]; | ||||
|             if (visited.get(e->get_id(), false)) | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::asserted(sat::literal lit) { | ||||
|         TRACE("relevancy", tout << "asserted " << lit << " relevant " << is_relevant(lit) << "\n"); | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         flush(); | ||||
|         if (is_relevant(lit)) { | ||||
|             add_to_propagation_queue(lit); | ||||
|             return; | ||||
|         } | ||||
|         if (ctx.s().lvl(lit) <= ctx.s().search_lvl()) { | ||||
|             set_relevant(lit); | ||||
|             add_to_propagation_queue(lit); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         for (auto idx : occurs(lit)) { | ||||
|             if (!m_roots[idx]) | ||||
|                 continue; | ||||
|             visited.setx(e->get_id(), true, false); | ||||
|             if (!si.is_bool_op(e))  | ||||
|                 m_relevant_expr_ids.setx(e->get_id(), true, false); | ||||
|             if (!is_app(e)) | ||||
|             for (sat::literal lit2 : *m_clauses[idx])  | ||||
|                 if (lit2 != lit && ctx.s().value(lit2) == l_true && is_relevant(lit2)) | ||||
|                     goto next;   | ||||
|             set_relevant(lit); | ||||
|             add_to_propagation_queue(lit); | ||||
|             return; | ||||
|         next: | ||||
|             ; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::propagate() { | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         flush(); | ||||
|         if (m_qhead == m_queue.size()) | ||||
|             return; | ||||
|         m_trail.push_back(std::make_pair(update::set_qhead, m_qhead)); | ||||
|         while (m_qhead < m_queue.size() && !ctx.s().inconsistent() && ctx.get_manager().inc()) { | ||||
|             auto const& [lit, n] = m_queue[m_qhead++]; | ||||
|             SASSERT(n || lit != sat::null_literal); | ||||
|             SASSERT(!n || lit == sat::null_literal); | ||||
|             if (n) | ||||
|                 propagate_relevant(n); | ||||
|             else  | ||||
|                 propagate_relevant(lit);             | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::merge(euf::enode* root, euf::enode* other) { | ||||
|         TRACE("relevancy", tout << "merge #" << ctx.bpp(root) << " " << is_relevant(root) << " #" << ctx.bpp(other) << " " << is_relevant(other) << "\n"); | ||||
|         if (is_relevant(root)) | ||||
|             mark_relevant(other); | ||||
|         else if (is_relevant(other)) | ||||
|             mark_relevant(root); | ||||
|     } | ||||
|      | ||||
|     void relevancy::mark_relevant(euf::enode* n) { | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         flush(); | ||||
|         if (is_relevant(n)) | ||||
|             return; | ||||
|         TRACE("relevancy", tout << "mark #" << ctx.bpp(n) << "\n"); | ||||
|         m_trail.push_back(std::make_pair(update::add_queue, 0)); | ||||
|         m_queue.push_back(std::make_pair(sat::null_literal, n)); | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::mark_relevant(sat::literal lit) { | ||||
|         TRACE("relevancy", tout << "mark " << lit << " " << is_relevant(lit) << " " << ctx.s().value(lit) << " lim: " << m_lim.size() << "\n"); | ||||
|         if (!m_enabled) | ||||
|             return; | ||||
|         flush(); | ||||
|         if (is_relevant(lit)) | ||||
|             return;         | ||||
|         set_relevant(lit); | ||||
|         switch (ctx.s().value(lit)) { | ||||
|         case l_true: | ||||
|             break; | ||||
|         case l_false: | ||||
|             lit.neg(); | ||||
|             break; | ||||
|         default: | ||||
|             return;                | ||||
|         } | ||||
|         add_to_propagation_queue(lit); | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::propagate_relevant(sat::literal lit) { | ||||
|         SASSERT(m_num_scopes == 0); | ||||
|         TRACE("relevancy", tout << "propagate " << lit << " lim: " << m_lim.size() << "\n"); | ||||
|         SASSERT(ctx.s().value(lit) == l_true); | ||||
|         SASSERT(is_relevant(lit)); | ||||
|         euf::enode* n = ctx.bool_var2enode(lit.var()); | ||||
|         if (n && !ctx.get_si().is_bool_op(n->get_expr())) | ||||
|             return; | ||||
|         for (auto idx : occurs(~lit)) { | ||||
|             if (m_roots[idx]) | ||||
|                 continue; | ||||
|             expr* c = nullptr, *th = nullptr, *el = nullptr; | ||||
|             if (m.is_ite(e, c, th, el) && get_enode(c)) { | ||||
|                 sat::literal lit = expr2literal(c); | ||||
|                 todo.push_back(c); | ||||
|                 switch (s().value(lit)) { | ||||
|                 case l_true: | ||||
|                     todo.push_back(th); | ||||
|                     break; | ||||
|                 case l_false: | ||||
|                     todo.push_back(el); | ||||
|                     break; | ||||
|                 default: | ||||
|                     todo.push_back(th); | ||||
|                     todo.push_back(el); | ||||
|                     break; | ||||
|             sat::clause* cl = m_clauses[idx]; | ||||
|             sat::literal true_lit = sat::null_literal; | ||||
|             for (sat::literal lit2 : *cl) { | ||||
|                 if (ctx.s().value(lit2) == l_true) { | ||||
|                     if (is_relevant(lit2)) | ||||
|                         goto next; | ||||
|                     true_lit = lit2; | ||||
|                 } | ||||
|                 continue; | ||||
|             } | ||||
|             for (expr* arg : *to_app(e)) | ||||
|                 todo.push_back(arg); | ||||
| 
 | ||||
|             if (true_lit != sat::null_literal)  | ||||
|                 set_asserted(true_lit);             | ||||
|             else { | ||||
|                 m_trail.push_back(std::make_pair(update::set_root, idx)); | ||||
|                 m_roots[idx] = true; | ||||
|             } | ||||
|         next: | ||||
|             TRACE("relevancy", tout << "propagate " << lit << " " << true_lit << " " << m_roots[idx] << "\n"); | ||||
|             ; | ||||
|         } | ||||
| 
 | ||||
|         for (auto * e : todo) | ||||
|             visited[e->get_id()] = false; | ||||
| 
 | ||||
|         TRACE("euf", | ||||
|             for (enode* n : m_egraph.nodes()) | ||||
|                 if (is_relevant(n))  | ||||
|                     tout << "relevant " << n->get_expr_id() << " [r" << n->get_root_id() << "]: " << mk_bounded_pp(n->get_expr(), m) << "\n";); | ||||
|               return true; | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::propagate_relevant(euf::enode* n) { | ||||
|         m_todo.push_back(n); | ||||
|         while (!m_todo.empty()) { | ||||
|             n = m_todo.back(); | ||||
|             m_todo.pop_back(); | ||||
|             TRACE("relevancy", tout << "propagate #" << ctx.bpp(n) << " lim: " << m_lim.size() << "\n"); | ||||
|             if (n->is_relevant()) | ||||
|                 continue; | ||||
|             m_stack.push_back(n); | ||||
|             while (!m_stack.empty()) { | ||||
|                 n = m_stack.back(); | ||||
|                 unsigned sz = m_stack.size(); | ||||
|                 bool is_bool_op = ctx.get_si().is_bool_op(n->get_expr()); | ||||
|                 if (!is_bool_op) | ||||
|                     for (euf::enode* arg : euf::enode_args(n)) | ||||
|                         if (!arg->is_relevant()) | ||||
|                             m_stack.push_back(arg); | ||||
|                 if (sz != m_stack.size()) | ||||
|                     continue; | ||||
|                 if (!n->is_relevant()) { | ||||
|                     ctx.get_egraph().set_relevant(n); | ||||
|                     ctx.relevant_eh(n); | ||||
|                     sat::bool_var v = n->bool_var();                     | ||||
|                     if (v != sat::null_bool_var) | ||||
|                         relevant_eh(v); | ||||
|                     for (euf::enode* sib : euf::enode_class(n)) | ||||
|                         if (!sib->is_relevant()) | ||||
|                             m_todo.push_back(sib); | ||||
|                 } | ||||
|                 if (!ctx.get_manager().inc()) { | ||||
|                     m_todo.reset(); | ||||
|                     m_stack.reset(); | ||||
|                     return; | ||||
|                 } | ||||
|                 m_stack.pop_back(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void relevancy::set_enabled(bool e) { | ||||
|         m_enabled = e; | ||||
|         ctx.get_egraph().set_default_relevant(!e); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
|  |  | |||
							
								
								
									
										173
									
								
								src/sat/smt/euf_relevancy.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/sat/smt/euf_relevancy.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,173 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     smt_relevant.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Relevancy propagation | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2021-12-27 | ||||
| 
 | ||||
| 
 | ||||
| Clauses are split into two parts: | ||||
| 
 | ||||
| - Roots | ||||
| - Defs | ||||
| 
 | ||||
| The goal is to establish a labeling of literals as "relevant" such that | ||||
| - the set of relevant literals satisfies Roots | ||||
| - there is a set of blocked literals that can be used to satisfy the clauses in Defs | ||||
|   independent of their real assignment. | ||||
| 
 | ||||
| The idea is that the Defs clauses are obtained from Tseitin transformation so they can be | ||||
| grouped by the blocked literal that was used to introduce them. | ||||
| For example, when clausifying (and a b) we have the clauses | ||||
| (=> (and a b) a) | ||||
| (=> (and a b) b) | ||||
| (or (not a) (not b) (and a b)) | ||||
| then the literal for "(and a b)" is blocked. | ||||
| And recursively for clauses introduced for a, b if they use a Boolean connectives | ||||
| at top level. | ||||
| 
 | ||||
| The state transitions are: | ||||
| 
 | ||||
| - A literal lit is assigned: | ||||
|   lit appears positively in a Root clause R and no other literal in R are relevant. | ||||
|   -> | ||||
|   lit is set relevant | ||||
| 
 | ||||
|   lit is justified at search level | ||||
|   ->  | ||||
|   lit is set relevant | ||||
| 
 | ||||
| - An equality n1 = n2 is assigned: | ||||
|   n1 is relevant | ||||
|   ->  | ||||
|   n2 is marked as relevant | ||||
| 
 | ||||
| - A lit is set relevant: | ||||
|   ->  | ||||
|   all clauses D in Defs where lit appears negatively are added to Roots | ||||
| 
 | ||||
| - When a clause R is added to Roots: | ||||
|   R contains a positive literal lit that is relevant | ||||
|   -> | ||||
|   skip adding R to Roots | ||||
| 
 | ||||
| - When a clause R is added to Roots: | ||||
|   R contains a positive literal lit, no positive literal in R are relevant | ||||
|   -> | ||||
|   lit is set relevant  | ||||
| 
 | ||||
| - When a clause D is added to Defs: | ||||
|   D contains a negative literal that is relevant | ||||
|   ->  | ||||
|   Add D to Roots | ||||
| 
 | ||||
| - When an expression is set relevant: | ||||
|   All non-relevant children above Boolean connectives are set relevant | ||||
|   If nodes are treated as Boolean connectives because they are clausified | ||||
|   to (=> cond (= n then)) and (=> (not cond) (= n else)) | ||||
| 
 | ||||
| Replay: | ||||
|   - literals that are replayed in clauses that are marked relevant are  | ||||
|     marked relevant again. | ||||
| 
 | ||||
|   - expressions corresponding to auxiliary clauses are added as auxiliary clauses. | ||||
|    | ||||
|   - TBD: Are root clauses added under a scope discarded? | ||||
|     The SAT solver re-initializes clauses on its own, should we just use this mechanism? | ||||
| 
 | ||||
| Can a literal that is not in a root be set relevant? | ||||
|  - yes, if we propagate over expressions | ||||
| 
 | ||||
| Do we need full watch lists instead of 2-watch lists? | ||||
|  - probably, but unclear. The dual SAT solver only uses 2-watch lists, but uses a large clause for tracking  | ||||
|    roots. | ||||
| 
 | ||||
| 
 | ||||
|    State machine for literals: relevant(lit), assigned(lit) | ||||
| 
 | ||||
| relevant(lit) transitions false -> true | ||||
|    if assigned(lit):     add to propagation queue | ||||
|    if not assigned(lit): no-op (or mark enodes as relevant) | ||||
| 
 | ||||
| assigned(lit) transitions false -> true | ||||
|    if relevant(lit):      add to propagation queue | ||||
|    if not relevant(lit):  set relevant if member of root, add to propagation queue | ||||
| 
 | ||||
| 
 | ||||
| --*/ | ||||
| #pragma once | ||||
| #include "sat/sat_solver.h" | ||||
| #include "sat/smt/sat_th.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace euf { | ||||
| 
 | ||||
|     class solver; | ||||
| 
 | ||||
|     class relevancy { | ||||
|         euf::solver&         ctx; | ||||
| 
 | ||||
|         enum class update { relevant_var, add_queue, add_clause, set_root, set_qhead }; | ||||
|         | ||||
|         bool                                 m_enabled = false; | ||||
|         svector<std::pair<update, unsigned>> m_trail; | ||||
|         unsigned_vector                      m_lim; | ||||
|         unsigned                             m_num_scopes = 0; | ||||
|         bool_vector                          m_relevant_var_ids;  // identifiers of relevant Boolean variables
 | ||||
|         sat::clause_allocator                m_alloc; | ||||
|         sat::clause_vector                   m_clauses;           // clauses
 | ||||
|         bool_vector                          m_roots;             // indicate if clause is a root
 | ||||
|         vector<unsigned_vector>              m_occurs;            // where do literals occur
 | ||||
|         unsigned                             m_qhead = 0;         // queue head for relevancy
 | ||||
|         svector<std::pair<sat::literal, euf::enode*>> m_queue;    // propagation queue for relevancy
 | ||||
|         euf::enode_vector                    m_stack, m_todo; | ||||
| 
 | ||||
|         void push_core() { m_lim.push_back(m_trail.size()); } | ||||
|         void flush() { for (; m_num_scopes > 0; --m_num_scopes) push_core(); } | ||||
| 
 | ||||
|         unsigned_vector& occurs(sat::literal lit) { m_occurs.reserve(lit.index() + 1); return m_occurs[lit.index()]; } | ||||
| 
 | ||||
|         void propagate_relevant(sat::literal lit); | ||||
| 
 | ||||
|         void add_to_propagation_queue(sat::literal lit);         | ||||
| 
 | ||||
|         void set_relevant(sat::literal lit); | ||||
| 
 | ||||
|         void set_asserted(sat::literal lit); | ||||
| 
 | ||||
|         void relevant_eh(sat::bool_var v); | ||||
| 
 | ||||
|         void propagate_relevant(euf::enode* n); | ||||
| 
 | ||||
|     public: | ||||
|         relevancy(euf::solver& ctx): ctx(ctx) {} | ||||
| 
 | ||||
|         void push() { if (m_enabled) ++m_num_scopes; } | ||||
|         void pop(unsigned n); | ||||
| 
 | ||||
|         void add_root(unsigned n, sat::literal const* lits); | ||||
|         void add_def(unsigned n, sat::literal const* lits); | ||||
|         void asserted(sat::literal lit); | ||||
|         void propagate(); | ||||
|         bool can_propagate() const { return m_qhead < m_queue.size(); } | ||||
| 
 | ||||
|         void mark_relevant(euf::enode* n); | ||||
|         void mark_relevant(sat::literal lit); | ||||
|         void merge(euf::enode* n1, euf::enode* n2); | ||||
| 
 | ||||
|         bool is_relevant(sat::bool_var v) const { return !m_enabled || m_relevant_var_ids.get(v, false); } | ||||
|         bool is_relevant(sat::literal lit) const { return is_relevant(lit.var()); } | ||||
|         bool is_relevant(euf::enode* n) const { return !m_enabled || n->is_relevant(); } | ||||
|          | ||||
|         bool enabled() const { return m_enabled; } | ||||
|         void set_enabled(bool e); | ||||
|     }; | ||||
| } | ||||
|  | @ -41,6 +41,7 @@ namespace euf { | |||
|         extension(symbol("euf"), m.mk_family_id("euf")), | ||||
|         m(m), | ||||
|         si(si), | ||||
|         m_relevancy(*this), | ||||
|         m_egraph(m), | ||||
|         m_trail(), | ||||
|         m_rewriter(m), | ||||
|  | @ -51,12 +52,21 @@ namespace euf { | |||
|         m_values(m) | ||||
|     { | ||||
|         updt_params(p); | ||||
|         m_relevancy.set_enabled(get_config().m_relevancy_lvl > 2); | ||||
| 
 | ||||
|         std::function<void(std::ostream&, void*)> disp = | ||||
|             [&](std::ostream& out, void* j) {  | ||||
|             display_justification_ptr(out, reinterpret_cast<size_t*>(j));  | ||||
|         }; | ||||
|         m_egraph.set_display_justification(disp); | ||||
| 
 | ||||
|         if (m_relevancy.enabled()) { | ||||
|             std::function<void(euf::enode* root, euf::enode* other)> on_merge = | ||||
|                 [&](enode* root, enode* other) { | ||||
|                 m_relevancy.merge(root, other); | ||||
|             }; | ||||
|             m_egraph.set_on_merge(on_merge); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void solver::updt_params(params_ref const& p) { | ||||
|  | @ -183,7 +193,7 @@ namespace euf { | |||
|     } | ||||
| 
 | ||||
|     void solver::propagate(literal lit, ext_justification_idx idx) { | ||||
|         add_auto_relevant(bool_var2expr(lit.var())); | ||||
|         mark_relevant(lit); | ||||
|         s().assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), idx)); | ||||
|     } | ||||
| 
 | ||||
|  | @ -233,11 +243,11 @@ namespace euf { | |||
|         m_egraph.explain_eq<size_t>(m_explain, a, b); | ||||
|     } | ||||
| 
 | ||||
|     void solver::add_diseq_antecedent(enode* a, enode* b) { | ||||
|         sat::bool_var v = get_egraph().explain_diseq(m_explain, a, b); | ||||
|     void solver::add_diseq_antecedent(ptr_vector<size_t>& ex, enode* a, enode* b) { | ||||
|         sat::bool_var v = get_egraph().explain_diseq(ex, a, b); | ||||
|         SASSERT(v == sat::null_bool_var || s().value(v) == l_false); | ||||
|         if (v != sat::null_bool_var)  | ||||
|             m_explain.push_back(to_ptr(sat::literal(v, true))); | ||||
|             ex.push_back(to_ptr(sat::literal(v, true))); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::propagate(enode* a, enode* b, ext_justification_idx idx) { | ||||
|  | @ -286,6 +296,11 @@ namespace euf { | |||
|     } | ||||
| 
 | ||||
|     void solver::asserted(literal l) { | ||||
| 
 | ||||
|         m_relevancy.asserted(l); | ||||
|         if (!m_relevancy.is_relevant(l)) | ||||
|             return;         | ||||
| 
 | ||||
|         expr* e = m_bool_var2expr.get(l.var(), nullptr); | ||||
|         TRACE("euf", tout << "asserted: " << l << "@" << s().scope_lvl() << " := " << mk_bounded_pp(e, m) << "\n";); | ||||
|         if (!e)  | ||||
|  | @ -329,6 +344,8 @@ namespace euf { | |||
|     bool solver::unit_propagate() { | ||||
|         bool propagated = false; | ||||
|         while (!s().inconsistent()) { | ||||
|             if (m_relevancy.enabled()) | ||||
|                 m_relevancy.propagate(); | ||||
|             if (m_egraph.inconsistent()) {   | ||||
|                 unsigned lvl = s().scope_lvl(); | ||||
|                 s().set_conflict(sat::justification::mk_ext_justification(lvl, conflict_constraint().to_index())); | ||||
|  | @ -345,9 +362,13 @@ namespace euf { | |||
|                 if (m_solvers[i]->unit_propagate()) | ||||
|                     propagated1 = true; | ||||
|              | ||||
|             if (!propagated1) | ||||
|                 break; | ||||
|             propagated = true;              | ||||
|             if (propagated1) { | ||||
|                 propagated = true; | ||||
|                 continue; | ||||
|             } | ||||
|             if (m_relevancy.enabled() && m_relevancy.can_propagate()) | ||||
|                 continue; | ||||
|             break;                   | ||||
|         } | ||||
|         DEBUG_CODE(if (!propagated && !s().inconsistent()) check_missing_eq_propagation();); | ||||
|         return propagated; | ||||
|  | @ -422,12 +443,10 @@ namespace euf { | |||
|     void solver::propagate_th_eqs() { | ||||
|         for (; m_egraph.has_th_eq() && !s().inconsistent() && !m_egraph.inconsistent(); m_egraph.next_th_eq()) { | ||||
|             th_eq eq = m_egraph.get_th_eq(); | ||||
|             if (eq.is_eq()) { | ||||
|                 if (!is_self_propagated(eq)) | ||||
|                     m_id2solver[eq.id()]->new_eq_eh(eq);     | ||||
|             } | ||||
|             else | ||||
|             if (!eq.is_eq()) | ||||
|                 m_id2solver[eq.id()]->new_diseq_eh(eq); | ||||
|             else if (!is_self_propagated(eq)) | ||||
|                 m_id2solver[eq.id()]->new_eq_eh(eq);                                 | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -458,9 +477,6 @@ namespace euf { | |||
| 
 | ||||
|         if (unit_propagate()) | ||||
|             return sat::check_result::CR_CONTINUE; | ||||
| 
 | ||||
|         if (!init_relevancy()) | ||||
|             give_up = true; | ||||
|          | ||||
|         unsigned num_nodes = m_egraph.num_nodes(); | ||||
|         auto apply_solver = [&](th_solver* e) { | ||||
|  | @ -525,9 +541,7 @@ namespace euf { | |||
|         for (auto* e : m_solvers) | ||||
|             e->push(); | ||||
|         m_egraph.push(); | ||||
|         if (m_dual_solver) | ||||
|             m_dual_solver->push(); | ||||
|         push_relevant(); | ||||
|         m_relevancy.push(); | ||||
|     } | ||||
| 
 | ||||
|     void solver::pop(unsigned n) { | ||||
|  | @ -537,7 +551,7 @@ namespace euf { | |||
|             e->pop(n); | ||||
|         si.pop(n); | ||||
|         m_egraph.pop(n); | ||||
|         pop_relevant(n); | ||||
|         m_relevancy.pop(n); | ||||
|         scope const & sc = m_scopes[m_scopes.size() - n]; | ||||
|         for (unsigned i = m_var_trail.size(); i-- > sc.m_var_lim; ) { | ||||
|             bool_var v = m_var_trail[i]; | ||||
|  | @ -546,8 +560,6 @@ namespace euf { | |||
|         } | ||||
|         m_var_trail.shrink(sc.m_var_lim);         | ||||
|         m_scopes.shrink(m_scopes.size() - n); | ||||
|         if (m_dual_solver) | ||||
|             m_dual_solver->pop(n); | ||||
|         SASSERT(m_egraph.num_scopes() == m_scopes.size()); | ||||
|         TRACE("euf_verbose", display(tout << "pop to: " << m_scopes.size() << "\n");); | ||||
|     } | ||||
|  | @ -720,6 +732,58 @@ namespace euf { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool solver::is_relevant(bool_var v) const { | ||||
|         if (m_relevancy.enabled()) | ||||
|             return m_relevancy.is_relevant(v); | ||||
|         auto* e = bool_var2enode(v); | ||||
|         return !e || is_relevant(e); | ||||
|     } | ||||
| 
 | ||||
|     void solver::relevant_eh(euf::enode* n) { | ||||
|         if (m_qsolver) | ||||
|             m_qsolver->relevant_eh(n); | ||||
|         for (auto const& thv : enode_th_vars(n)) { | ||||
|             auto* th = m_id2solver.get(thv.get_id(), nullptr); | ||||
|             if (th && th != m_qsolver) | ||||
|                 th->relevant_eh(n); | ||||
|         }        | ||||
|     } | ||||
| 
 | ||||
|     bool solver::enable_ackerman_axioms(expr* e) const { | ||||
|         euf::enode* n = get_enode(e); | ||||
|         if (!n) | ||||
|             return false; | ||||
|         for (auto const& thv : enode_th_vars(n)) { | ||||
|             auto* th = m_id2solver.get(thv.get_id(), nullptr); | ||||
|             if (th && !th->enable_ackerman_axioms(n)) | ||||
|                 return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool solver::is_fixed(euf::enode* n, expr_ref& val, sat::literal_vector& explain) { | ||||
|         if (n->bool_var() != sat::null_bool_var) { | ||||
|             switch (s().value(n->bool_var())) { | ||||
|             case l_true: | ||||
|                 val = m.mk_true(); | ||||
|                 explain.push_back(sat::literal(n->bool_var())); | ||||
|                 return true;                 | ||||
|             case l_false: | ||||
|                 val = m.mk_false(); | ||||
|                 explain.push_back(~sat::literal(n->bool_var())); | ||||
|                 return true; | ||||
|             default: | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         for (auto const& thv : enode_th_vars(n)) { | ||||
|             auto* th = m_id2solver.get(thv.get_id(), nullptr); | ||||
|             if (th && !th->is_fixed(thv.get_var(), val, explain)) | ||||
|                 return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void solver::pre_simplify() { | ||||
|         for (auto* e : m_solvers) | ||||
|             e->pre_simplify(); | ||||
|  | @ -765,13 +829,18 @@ namespace euf { | |||
|     } | ||||
| 
 | ||||
|     bool solver::set_root(literal l, literal r) { | ||||
|         expr* e = bool_var2expr(l.var()); | ||||
|         if (!e) | ||||
|             return true; | ||||
|         if (m_relevancy.enabled()) | ||||
|             return false; | ||||
|         bool ok = true; | ||||
|         for (auto* s : m_solvers) | ||||
|             if (!s->set_root(l, r)) | ||||
|                 ok = false; | ||||
| 
 | ||||
|         if (!ok) | ||||
|             return false; | ||||
|         expr* e = bool_var2expr(l.var()); | ||||
|         if (!e) | ||||
|             return true; | ||||
|         if (m.is_eq(e) && !m.is_iff(e)) | ||||
|             ok = false; | ||||
|         euf::enode* n = get_enode(e); | ||||
|  | @ -794,7 +863,7 @@ namespace euf { | |||
|         out << "bool-vars\n"; | ||||
|         for (unsigned v : m_var_trail) { | ||||
|             expr* e = m_bool_var2expr[v]; | ||||
|             out << v << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";         | ||||
|             out << v << (is_relevant(v)?"":"n") << ": " << e->get_id() << " " << m_solver->value(v) << " " << mk_bounded_pp(e, m, 1) << "\n";         | ||||
|         } | ||||
|         for (auto* e : m_solvers) | ||||
|             e->display(out); | ||||
|  |  | |||
|  | @ -25,11 +25,12 @@ Author: | |||
| #include "sat/sat_extension.h" | ||||
| #include "sat/smt/atom2bool_var.h" | ||||
| #include "sat/smt/sat_th.h" | ||||
| #include "sat/smt/sat_dual_solver.h" | ||||
| #include "sat/smt/euf_ackerman.h" | ||||
| #include "sat/smt/user_solver.h" | ||||
| #include "sat/smt/euf_relevancy.h" | ||||
| #include "smt/params/smt_params.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace euf { | ||||
|     typedef sat::literal literal; | ||||
|     typedef sat::ext_constraint_idx ext_constraint_idx; | ||||
|  | @ -91,6 +92,7 @@ namespace euf { | |||
|         std::function<::solver*(void)>   m_mk_solver; | ||||
|         ast_manager&                     m; | ||||
|         sat::sat_internalizer& si; | ||||
|         relevancy              m_relevancy; | ||||
|         smt_params             m_config; | ||||
|         euf::egraph            m_egraph; | ||||
|         trail_stack            m_trail; | ||||
|  | @ -181,17 +183,17 @@ namespace euf { | |||
|         void init_drat(); | ||||
| 
 | ||||
|         // relevancy
 | ||||
|         bool_vector m_relevant_expr_ids; | ||||
|         bool_vector m_relevant_visited; | ||||
|         ptr_vector<expr> m_relevant_todo; | ||||
|         void ensure_dual_solver(); | ||||
|         bool init_relevancy(); | ||||
| 
 | ||||
| 
 | ||||
|         //bool_vector m_relevant_expr_ids;
 | ||||
|         //bool_vector m_relevant_visited;
 | ||||
|         //ptr_vector<expr> m_relevant_todo;
 | ||||
|         //void init_relevant_expr_ids();
 | ||||
|         //void push_relevant(sat::bool_var v);
 | ||||
|         bool is_propagated(sat::literal lit); | ||||
|         // invariant
 | ||||
|         void check_eqc_bool_assignment() const; | ||||
|         void check_missing_bool_enode_propagation() const; | ||||
|         void check_missing_eq_propagation() const; | ||||
|          | ||||
| 
 | ||||
|         // diagnosis
 | ||||
|         std::ostream& display_justification_ptr(std::ostream& out, size_t* j) const; | ||||
|  | @ -250,6 +252,10 @@ namespace euf { | |||
|         sat::sat_internalizer& get_si() { return si; } | ||||
|         ast_manager& get_manager() { return m; } | ||||
|         enode* get_enode(expr* e) const { return m_egraph.find(e); } | ||||
|         enode* bool_var2enode(sat::bool_var b) const { | ||||
|             expr* e = m_bool_var2expr.get(b, nullptr); | ||||
|             return e ? get_enode(e) : nullptr; | ||||
|         } | ||||
|         sat::literal expr2literal(expr* e) const { return enode2literal(get_enode(e)); } | ||||
|         sat::literal enode2literal(enode* n) const { return sat::literal(n->bool_var(), false); } | ||||
|         lbool value(enode* n) const { return s().value(enode2literal(n)); } | ||||
|  | @ -299,7 +305,9 @@ namespace euf { | |||
|         void get_antecedents(literal l, ext_justification_idx idx, literal_vector& r, bool probing) override; | ||||
|         void get_antecedents(literal l, th_explain& jst, literal_vector& r, bool probing); | ||||
|         void add_antecedent(enode* a, enode* b); | ||||
|         void add_diseq_antecedent(enode* a, enode* b); | ||||
|         void add_diseq_antecedent(ptr_vector<size_t>& ex, enode* a, enode* b); | ||||
|         void add_explain(size_t* p) { m_explain.push_back(p); } | ||||
|         void reset_explain() { m_explain.reset(); } | ||||
|         void set_eliminated(bool_var v) override; | ||||
|         void asserted(literal l) override; | ||||
|         sat::check_result check() override; | ||||
|  | @ -362,32 +370,29 @@ namespace euf { | |||
|         th_rewriter& get_rewriter() { return m_rewriter; } | ||||
|         void rewrite(expr_ref& e) { m_rewriter(e); } | ||||
|         bool is_shared(euf::enode* n) const; | ||||
|         bool enable_ackerman_axioms(expr* n) const; | ||||
|         bool is_fixed(euf::enode* n, expr_ref& val, sat::literal_vector& explain); | ||||
| 
 | ||||
|         // relevancy
 | ||||
|         bool m_relevancy = true; | ||||
|         scoped_ptr<sat::dual_solver>  m_dual_solver; | ||||
|         ptr_vector<expr>              m_auto_relevant; | ||||
|         unsigned_vector               m_auto_relevant_lim; | ||||
|         unsigned                      m_auto_relevant_scopes = 0; | ||||
| 
 | ||||
|         bool relevancy_enabled() const { return m_relevancy && get_config().m_relevancy_lvl > 0; } | ||||
|         void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy = false;  } | ||||
|         void add_root(unsigned n, sat::literal const* lits); | ||||
|         bool relevancy_enabled() const { return m_relevancy.enabled(); } | ||||
|         void disable_relevancy(expr* e) { IF_VERBOSE(0, verbose_stream() << "disabling relevancy " << mk_pp(e, m) << "\n"); m_relevancy.set_enabled(false); } | ||||
|         void add_root(unsigned n, sat::literal const* lits) { m_relevancy.add_root(n, lits); } | ||||
|         void add_root(sat::literal_vector const& lits) { add_root(lits.size(), lits.data()); } | ||||
|         void add_root(sat::literal lit) { add_root(1, &lit); } | ||||
|         void add_root(sat::literal a, sat::literal b) { sat::literal lits[2] = {a, b}; add_root(2, lits); } | ||||
|         void add_root(sat::literal lit1, sat::literal lit2) { sat::literal lits[2] = { lit1, lit2, }; add_root(2, lits); } | ||||
|         void add_aux(sat::literal_vector const& lits) { add_aux(lits.size(), lits.data()); } | ||||
|         void add_aux(unsigned n, sat::literal const* lits); | ||||
|         void add_aux(unsigned n, sat::literal const* lits) { m_relevancy.add_def(n, lits); } | ||||
|         void add_aux(sat::literal a) { sat::literal lits[1] = { a }; add_aux(1, lits); } | ||||
|         void add_aux(sat::literal a, sat::literal b) { sat::literal lits[2] = {a, b}; add_aux(2, lits); }  | ||||
|         void add_aux(sat::literal a, sat::literal b, sat::literal c) { sat::literal lits[3] = { a, b, c }; add_aux(3, lits); } | ||||
|         void track_relevancy(sat::bool_var v); | ||||
|         bool is_relevant(expr* e) const; | ||||
|         bool is_relevant(enode* n) const; | ||||
|         void add_auto_relevant(expr* e); | ||||
|         void pop_relevant(unsigned n); | ||||
|         void push_relevant(); | ||||
|         void mark_relevant(sat::literal lit) { m_relevancy.mark_relevant(lit); } | ||||
|         bool is_relevant(enode* n) const { return m_relevancy.is_relevant(n); } | ||||
|         bool is_relevant(bool_var v) const; | ||||
|         bool is_relevant(sat::literal lit) const { return is_relevant(lit.var()); } | ||||
|         void relevant_eh(euf::enode* n); | ||||
| 
 | ||||
|         relevancy& get_relevancy() { return m_relevancy; } | ||||
| 
 | ||||
|         // model construction
 | ||||
|         void update_model(model_ref& mdl); | ||||
|  | @ -425,7 +430,11 @@ namespace euf { | |||
|             check_for_user_propagator(); | ||||
|             m_user_propagator->register_diseq(diseq_eh); | ||||
|         } | ||||
|         unsigned user_propagate_register(expr* e) { | ||||
|         void user_propagate_register_created(user_propagator::created_eh_t& ceh) { | ||||
|             check_for_user_propagator(); | ||||
|             m_user_propagator->register_created(ceh); | ||||
|         } | ||||
|         unsigned user_propagate_register_expr(expr* e) { | ||||
|             check_for_user_propagator(); | ||||
|             return m_user_propagator->add_expr(e); | ||||
|         } | ||||
|  |  | |||
|  | @ -269,7 +269,7 @@ namespace fpa { | |||
|         expr* xe = e_x->get_expr(); | ||||
|         expr* ye = e_y->get_expr(); | ||||
| 
 | ||||
|         if (m_fpa_util.is_bvwrap(xe) || m_fpa_util.is_bvwrap(ye)) | ||||
|         if (fu.is_bvwrap(xe) || fu.is_bvwrap(ye)) | ||||
|             return; | ||||
| 
 | ||||
|         expr_ref xc = convert(xe); | ||||
|  |  | |||
|  | @ -18,6 +18,7 @@ Author: | |||
| 
 | ||||
| #include "sat/smt/pb_solver.h" | ||||
| #include "ast/pb_decl_plugin.h" | ||||
| #include "sat/smt/euf_solver.h" | ||||
| 
 | ||||
| namespace pb { | ||||
| 
 | ||||
|  | @ -27,8 +28,12 @@ namespace pb { | |||
| 
 | ||||
|     literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { | ||||
|         flet<bool> _redundant(m_is_redundant, redundant); | ||||
|         if (m_pb.is_pb(e))  | ||||
|             return internalize_pb(e, sign, root); | ||||
|         if (m_pb.is_pb(e)) { | ||||
|             sat::literal lit = internalize_pb(e, sign, root); | ||||
|             if (m_ctx && !root && lit != sat::null_literal) | ||||
|                 m_ctx->attach_lit(lit, e);             | ||||
|             return lit; | ||||
|         } | ||||
|         UNREACHABLE(); | ||||
|         return sat::null_literal; | ||||
|     } | ||||
|  |  | |||
|  | @ -1338,7 +1338,9 @@ namespace pb { | |||
|     } | ||||
| 
 | ||||
|     solver::solver(euf::solver& ctx, euf::theory_id id) : | ||||
|         solver(ctx.get_manager(), ctx.get_si(), id) {} | ||||
|         solver(ctx.get_manager(), ctx.get_si(), id) { | ||||
|         m_ctx = &ctx; | ||||
|     } | ||||
| 
 | ||||
|     solver::solver(ast_manager& m, sat::sat_internalizer& si, euf::theory_id id) | ||||
|         : euf::th_solver(m, symbol("ba"), id), | ||||
|  | @ -1370,6 +1372,21 @@ namespace pb { | |||
|             s().mk_clause(_lits.size(), _lits.data(), sat::status::th(learned, get_id())); | ||||
|             return nullptr; | ||||
|         } | ||||
| 
 | ||||
|         if (k == 0) { | ||||
|             if (lit != sat::null_literal) | ||||
|                 s().add_clause(lit, sat::status::th(false, get_id())); | ||||
|             return nullptr; | ||||
|         } | ||||
| 
 | ||||
|         if (k > lits.size()) { | ||||
|             if (lit == sat::null_literal) | ||||
|                 s().add_clause(0, nullptr, sat::status::th(false, get_id())); | ||||
|             else | ||||
|                 s().add_clause(~lit, sat::status::th(false, get_id())); | ||||
|             return nullptr; | ||||
|         } | ||||
| 
 | ||||
|         if (!learned && clausify(lit, lits.size(), lits.data(), k)) { | ||||
|             return nullptr; | ||||
|         } | ||||
|  | @ -1403,8 +1420,10 @@ namespace pb { | |||
|             if (m_solver) m_solver->set_external(lit.var()); | ||||
|             c->watch_literal(*this, lit); | ||||
|             c->watch_literal(*this, ~lit); | ||||
|         }         | ||||
|         SASSERT(c->well_formed()); | ||||
|         }      | ||||
|         if (!c->well_formed())  | ||||
|             std::cout << *c << "\n"; | ||||
|         VERIFY(c->well_formed()); | ||||
|         if (m_solver && m_solver->get_config().m_drat) { | ||||
|             std::function<void(std::ostream& out)> fn = [&](std::ostream& out) { | ||||
|                 out << "c ba constraint " << *c << " 0\n"; | ||||
|  | @ -1429,12 +1448,27 @@ namespace pb { | |||
| 
 | ||||
|     constraint* solver::add_pb_ge(literal lit, svector<wliteral> const& wlits, unsigned k, bool learned) { | ||||
|         bool units = true; | ||||
|         for (wliteral wl : wlits) units &= wl.first == 1; | ||||
|         if (k == 0 && lit == sat::null_literal) { | ||||
|         for (wliteral wl : wlits)  | ||||
|             units &= wl.first == 1; | ||||
| 
 | ||||
|         if (k == 0) { | ||||
|             if (lit != sat::null_literal) | ||||
|                 s().add_clause(lit, sat::status::th(false, get_id())); | ||||
|             return nullptr; | ||||
|         } | ||||
|         rational weight(0); | ||||
|         for (auto const [w, l] : wlits) | ||||
|             weight += w; | ||||
|         if (weight < k) { | ||||
|             if (lit == sat::null_literal) | ||||
|                 s().add_clause(0, nullptr, sat::status::th(false, get_id())); | ||||
|             else | ||||
|                 s().add_clause(~lit, sat::status::th(false, get_id())); | ||||
|             return nullptr; | ||||
|         } | ||||
|         if (!learned) { | ||||
|             for (wliteral wl : wlits) s().set_external(wl.second.var());  | ||||
|             for (wliteral wl : wlits)  | ||||
|                 s().set_external(wl.second.var());  | ||||
|         } | ||||
|         if (units || k == 1) { | ||||
|             literal_vector lits; | ||||
|  | @ -2006,7 +2040,7 @@ namespace pb { | |||
|             s().pop_to_base_level(); | ||||
|         if (s().inconsistent()) | ||||
|             return; | ||||
|         unsigned trail_sz, count = 0; | ||||
|         unsigned trail_sz = 0, count = 0; | ||||
|         do { | ||||
|             trail_sz = s().init_trail_size(); | ||||
|             m_simplify_change = false; | ||||
|  | @ -2159,12 +2193,13 @@ namespace pb { | |||
|     } | ||||
| 
 | ||||
|     bool solver::set_root(literal l, literal r) {  | ||||
|         if (s().is_assumption(l.var())) { | ||||
|         if (s().is_assumption(l.var()))  | ||||
|             return false; | ||||
|         } | ||||
|         reserve_roots(); | ||||
|         m_roots[l.index()] = r; | ||||
|         m_roots[(~l).index()] = ~r; | ||||
|         m_roots[r.index()] = r; | ||||
|         m_roots[(~r).index()] = ~r; | ||||
|         m_root_vars[l.var()] = true; | ||||
|         return true; | ||||
|     } | ||||
|  | @ -2180,7 +2215,6 @@ namespace pb { | |||
|             flush_roots(*m_learned[i]); | ||||
|         cleanup_constraints(); | ||||
|         // validate();
 | ||||
| 
 | ||||
|         // validate_eliminated();
 | ||||
|     } | ||||
| 
 | ||||
|  | @ -2191,7 +2225,8 @@ namespace pb { | |||
| 
 | ||||
|     void solver::validate_eliminated(ptr_vector<constraint> const& cs) { | ||||
|         for (constraint const* c : cs) { | ||||
|             if (c->learned()) continue; | ||||
|             if (c->learned()) | ||||
|                 continue; | ||||
|             for (auto l : constraint::literal_iterator(*c)) | ||||
|                 VERIFY(!s().was_eliminated(l.var())); | ||||
|         } | ||||
|  | @ -2415,9 +2450,10 @@ namespace pb { | |||
|         for (unsigned i = 0; !found && i < c.size(); ++i) { | ||||
|             found = m_root_vars[c.get_lit(i).var()]; | ||||
|         } | ||||
|         if (!found) return; | ||||
|         if (!found) | ||||
|             return; | ||||
|         clear_watch(c); | ||||
|          | ||||
| 
 | ||||
|         // this could create duplicate literals
 | ||||
|         for (unsigned i = 0; i < c.size(); ++i) { | ||||
|             literal lit = m_roots[c.get_lit(i).index()]; | ||||
|  |  | |||
|  | @ -80,7 +80,8 @@ namespace pb { | |||
|         sat::sat_internalizer&      si; | ||||
|         pb_util                m_pb; | ||||
| 
 | ||||
|         sat::lookahead*        m_lookahead{ nullptr }; | ||||
|         sat::lookahead*        m_lookahead = nullptr; | ||||
|         euf::solver*           m_ctx = nullptr; | ||||
|         stats                  m_stats;  | ||||
|         small_object_allocator m_allocator; | ||||
|         | ||||
|  |  | |||
|  | @ -122,18 +122,17 @@ namespace q { | |||
|         return out << "]"; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     struct justification { | ||||
|         expr*     m_lhs, *m_rhs; | ||||
|         expr* m_lhs, * m_rhs; | ||||
|         bool      m_sign; | ||||
|         unsigned  m_num_ev; | ||||
|         euf::enode_pair* m_evidence; | ||||
|         clause&   m_clause; | ||||
|         unsigned  m_num_ex; | ||||
|         size_t** m_explain; | ||||
|         clause& m_clause; | ||||
|         euf::enode* const* m_binding; | ||||
|         justification(lit const& l, clause& c, euf::enode* const* b, unsigned n, euf::enode_pair* ev): | ||||
|             m_lhs(l.lhs), m_rhs(l.rhs), m_sign(l.sign), m_num_ev(n), m_evidence(ev), m_clause(c), m_binding(b) {} | ||||
|         sat::ext_constraint_idx to_index() const {  | ||||
|             return sat::constraint_base::mem2base(this);  | ||||
|         justification(lit const& l, clause& c, euf::enode* const* b, unsigned n, size_t** ev) : | ||||
|             m_lhs(l.lhs), m_rhs(l.rhs), m_sign(l.sign), m_num_ex(n), m_explain(ev), m_clause(c), m_binding(b) {} | ||||
|         sat::ext_constraint_idx to_index() const { | ||||
|             return sat::constraint_base::mem2base(this); | ||||
|         } | ||||
|         static justification& from_index(size_t idx) { | ||||
|             return *reinterpret_cast<justification*>(sat::constraint_base::from_index(idx)->mem()); | ||||
|  |  | |||
|  | @ -31,6 +31,7 @@ Done: | |||
| #include "ast/ast_util.h" | ||||
| #include "ast/rewriter/var_subst.h" | ||||
| #include "ast/rewriter/rewriter_def.h" | ||||
| #include "ast/normal_forms/pull_quant.h" | ||||
| #include "solver/solver.h" | ||||
| #include "sat/smt/sat_th.h" | ||||
| #include "sat/smt/euf_solver.h" | ||||
|  | @ -69,10 +70,16 @@ namespace q { | |||
|             m_mam->add_node(n, false); | ||||
|         }; | ||||
|         ctx.get_egraph().set_on_merge(_on_merge); | ||||
|         ctx.get_egraph().set_on_make(_on_make); | ||||
|         if (!ctx.relevancy_enabled()) | ||||
|             ctx.get_egraph().set_on_make(_on_make); | ||||
|         m_mam = mam::mk(ctx, *this); | ||||
|     } | ||||
| 
 | ||||
|     void ematch::relevant_eh(euf::enode* n) { | ||||
|         if (ctx.relevancy_enabled()) | ||||
|             m_mam->add_node(n, false); | ||||
|     } | ||||
| 
 | ||||
|     void ematch::ensure_ground_enodes(expr* e) { | ||||
|         mam::ground_subterms(e, m_ground); | ||||
|         for (expr* g : m_ground)  | ||||
|  | @ -90,32 +97,58 @@ namespace q { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|     * Create a justification for binding b. | ||||
|     * The justification involves equalities in the E-graph that have | ||||
|     * explanations. Retrieve the explanations while the justification | ||||
|     * is created to ensure the justification trail is well-founded | ||||
|     * during conflict resolution. | ||||
|     */ | ||||
|     sat::ext_justification_idx ematch::mk_justification(unsigned idx, clause& c, euf::enode* const* b) { | ||||
|         void* mem = ctx.get_region().allocate(justification::get_obj_size()); | ||||
|         sat::constraint_base::initialize(mem, &m_qs); | ||||
|         bool sign = false; | ||||
|         expr* l = nullptr, *r = nullptr;             | ||||
|         lit lit(expr_ref(l, m), expr_ref(r, m), sign);  | ||||
|         expr* l = nullptr, * r = nullptr; | ||||
|         lit lit(expr_ref(l, m), expr_ref(r, m), sign); | ||||
|         if (idx != UINT_MAX) | ||||
|             lit = c[idx]; | ||||
|         auto* ev = static_cast<euf::enode_pair*>(ctx.get_region().allocate(sizeof(euf::enode_pair) * m_evidence.size())); | ||||
|         for (unsigned i = m_evidence.size(); i-- > 0; ) | ||||
|             ev[i] = m_evidence[i]; | ||||
|         auto* constraint = new (sat::constraint_base::ptr2mem(mem)) justification(lit, c, b, m_evidence.size(), ev); | ||||
|         m_explain.reset(); | ||||
|         ctx.get_egraph().begin_explain(); | ||||
|         ctx.reset_explain(); | ||||
|         for (auto const& [a, b] : m_evidence) { | ||||
|             SASSERT(a->get_root() == b->get_root() || ctx.get_egraph().are_diseq(a, b)); | ||||
|             if (a->get_root() == b->get_root()) | ||||
|                 ctx.get_egraph().explain_eq<size_t>(m_explain, a, b); | ||||
|             else | ||||
|                 ctx.add_diseq_antecedent(m_explain, a, b); | ||||
|         } | ||||
|         ctx.get_egraph().end_explain(); | ||||
| 
 | ||||
|         size_t** ev = static_cast<size_t**>(ctx.get_region().allocate(sizeof(size_t*) * m_explain.size())); | ||||
|         for (unsigned i = m_explain.size(); i-- > 0; ) | ||||
|             ev[i] = m_explain[i]; | ||||
|         auto* constraint = new (sat::constraint_base::ptr2mem(mem)) justification(lit, c, b, m_explain.size(), ev); | ||||
|         return constraint->to_index(); | ||||
|     } | ||||
| 
 | ||||
|     void ematch::get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector& r, bool probing) { | ||||
|         m_eval.explain(l, justification::from_index(idx), r, probing); | ||||
|         justification& j = justification::from_index(idx); | ||||
|         for (unsigned i = 0; i < j.m_num_ex; ++i) | ||||
|             ctx.add_explain(j.m_explain[i]); | ||||
|         r.push_back(j.m_clause.m_literal); | ||||
|     } | ||||
| 
 | ||||
|     quantifier_ref ematch::nnf_skolem(quantifier* q) { | ||||
|         SASSERT(is_forall(q)); | ||||
|         expr_ref r(m); | ||||
|         proof_ref p(m); | ||||
|         m_new_defs.reset(); | ||||
|         m_new_proofs.reset(); | ||||
|         m_nnf(q, m_new_defs, m_new_proofs, r, p); | ||||
|         SASSERT(is_quantifier(r)); | ||||
|         SASSERT(is_forall(r)); | ||||
|         pull_quant pull(m); | ||||
|         pull(r, r, p); | ||||
|         SASSERT(is_forall(r)); | ||||
|         for (expr* d : m_new_defs) | ||||
|             m_qs.add_unit(m_qs.mk_literal(d)); | ||||
|         CTRACE("q", r != q, tout << mk_pp(q, m) << " -->\n" << r << "\n" << m_new_defs << "\n";); | ||||
|  | @ -295,7 +328,7 @@ namespace q { | |||
|         TRACE("q", b->display(ctx, tout << "on-binding " << mk_pp(q, m) << "\n") << "\n";); | ||||
| 
 | ||||
| 
 | ||||
|         if (false && propagate(false, _binding, max_generation, c, new_propagation)) | ||||
|         if (propagate(false, _binding, max_generation, c, new_propagation)) | ||||
|             return; | ||||
| 
 | ||||
|         binding::push_to_front(c.m_bindings, b); | ||||
|  | @ -304,6 +337,10 @@ namespace q { | |||
|     } | ||||
| 
 | ||||
|     bool ematch::propagate(bool is_owned, euf::enode* const* binding, unsigned max_generation, clause& c, bool& propagated) { | ||||
|         if (!m_enable_propagate) | ||||
|             return false; | ||||
|         if (ctx.s().inconsistent()) | ||||
|             return true; | ||||
|         TRACE("q", c.display(ctx, tout) << "\n";); | ||||
|         unsigned idx = UINT_MAX; | ||||
|         m_evidence.reset(); | ||||
|  | @ -331,7 +368,7 @@ namespace q { | |||
|             propagate(ev == l_false, idx, j_idx); | ||||
|         else | ||||
|             m_prop_queue.push_back(prop(ev == l_false, idx, j_idx)); | ||||
|         propagated = true; | ||||
|         propagated = true;         | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -352,7 +389,7 @@ namespace q { | |||
|         if (m_prop_queue.empty()) | ||||
|             return false; | ||||
|         for (unsigned i = 0; i < m_prop_queue.size(); ++i) { | ||||
|             auto [is_conflict, idx, j_idx] = m_prop_queue[i]; | ||||
|             auto const& [is_conflict, idx, j_idx] = m_prop_queue[i]; | ||||
|             propagate(is_conflict, idx, j_idx); | ||||
|         } | ||||
|         m_prop_queue.reset(); | ||||
|  | @ -527,7 +564,7 @@ namespace q { | |||
|     }; | ||||
| 
 | ||||
|     void ematch::add(quantifier* _q) { | ||||
|         TRACE("q", tout << "add " << mk_pp(_q, m) << "\n";); | ||||
|         TRACE("q", tout << "add " << mk_pp(_q, m) << "\n"); | ||||
|         clause* c = clausify(_q); | ||||
|         quantifier* q = c->q(); | ||||
|         if (m_q2clauses.contains(q)) { | ||||
|  | @ -551,7 +588,7 @@ namespace q { | |||
|             app * mp = to_app(q->get_pattern(i)); | ||||
|             SASSERT(m.is_pattern(mp)); | ||||
|             bool unary = (mp->get_num_args() == 1); | ||||
|             TRACE("q", tout << "adding:\n" << expr_ref(mp, m) << "\n";); | ||||
|             TRACE("q", tout << "adding:\n" << expr_ref(mp, m) << "\n"); | ||||
|             if (!unary && j >= num_eager_multi_patterns) { | ||||
|                 TRACE("q", tout << "delaying (too many multipatterns):\n" << mk_ismt2_pp(mp, m) << "\n";); | ||||
|                 if (!m_lazy_mam) | ||||
|  | @ -570,7 +607,7 @@ namespace q { | |||
| 
 | ||||
| 
 | ||||
|     bool ematch::unit_propagate() { | ||||
|         return false; | ||||
|         // return false;
 | ||||
|         return ctx.get_config().m_ematching && propagate(false); | ||||
|     } | ||||
| 
 | ||||
|  | @ -581,7 +618,7 @@ namespace q { | |||
|             return m_inst_queue.propagate() || propagated; | ||||
|         ctx.push(value_trail<unsigned>(m_qhead)); | ||||
|         ptr_buffer<binding> to_remove; | ||||
|         for (; m_qhead < m_clause_queue.size(); ++m_qhead) { | ||||
|         for (; m_qhead < m_clause_queue.size() && m.inc(); ++m_qhead) { | ||||
|             unsigned idx = m_clause_queue[m_qhead]; | ||||
|             clause& c = *m_clauses[idx]; | ||||
|             binding* b = c.m_bindings; | ||||
|  | @ -589,7 +626,7 @@ namespace q { | |||
|                 continue; | ||||
| 
 | ||||
|             do {                 | ||||
|                 if (false && propagate(true, b->m_nodes, b->m_max_generation, c, propagated))  | ||||
|                 if (propagate(true, b->m_nodes, b->m_max_generation, c, propagated))  | ||||
|                     to_remove.push_back(b); | ||||
|                 else if (flush) { | ||||
|                     instantiate(*b); | ||||
|  | @ -642,7 +679,7 @@ namespace q { | |||
|     void ematch::collect_statistics(statistics& st) const { | ||||
|         m_inst_queue.collect_statistics(st); | ||||
|         st.update("q redundant", m_stats.m_num_redundant); | ||||
|         st.update("q units",     m_stats.m_num_propagations); | ||||
|         st.update("q unit propagations",     m_stats.m_num_propagations); | ||||
|         st.update("q conflicts", m_stats.m_num_conflicts); | ||||
|         st.update("q delayed bindings", m_stats.m_num_delayed_bindings); | ||||
|     } | ||||
|  |  | |||
|  | @ -89,11 +89,13 @@ namespace q { | |||
|         unsigned                      m_qhead = 0; | ||||
|         unsigned_vector               m_clause_queue; | ||||
|         euf::enode_pair_vector        m_evidence; | ||||
|         bool                          m_enable_propagate = true; | ||||
| 
 | ||||
|         euf::enode* const* copy_nodes(clause& c, euf::enode* const* _binding); | ||||
|         binding* tmp_binding(clause& c, app* pat, euf::enode* const* _binding); | ||||
|         binding* alloc_binding(clause& c, app* pat, euf::enode* const* _binding, unsigned max_generation, unsigned min_top, unsigned max_top); | ||||
|          | ||||
|         | ||||
|         ptr_vector<size_t> m_explain; | ||||
|         sat::ext_justification_idx mk_justification(unsigned idx, clause& c, euf::enode* const* b); | ||||
| 
 | ||||
|         void ensure_ground_enodes(expr* e); | ||||
|  | @ -135,9 +137,10 @@ namespace q { | |||
| 
 | ||||
|         bool unit_propagate();         | ||||
| 
 | ||||
| 
 | ||||
|         void add(quantifier* q); | ||||
| 
 | ||||
|         void relevant_eh(euf::enode* n); | ||||
| 
 | ||||
|         void collect_statistics(statistics& st) const; | ||||
| 
 | ||||
|         void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector& r, bool probing); | ||||
|  |  | |||
|  | @ -230,13 +230,17 @@ namespace q { | |||
|                 if (!n) | ||||
|                     return nullptr; | ||||
|                 for (unsigned i = args.size(); i-- > 0; ) { | ||||
|                     if (args[i] != n->get_arg(i)) { | ||||
|                         // roots could be different when using commutativity
 | ||||
|                         // instead of compensating for this, we just bail out
 | ||||
|                         if (args[i]->get_root() != n->get_arg(i)->get_root()) | ||||
|                             return nullptr; | ||||
|                         evidence.push_back(euf::enode_pair(args[i], n->get_arg(i))); | ||||
|                     } | ||||
|                     euf::enode* a = args[i], *b = n->get_arg(i); | ||||
|                     if (a == b) | ||||
|                         continue; | ||||
| 
 | ||||
|                     // roots could be different when using commutativity
 | ||||
|                     // instead of compensating for this, we just bail out
 | ||||
|                     if (a->get_root() != b->get_root()) | ||||
|                         return nullptr; | ||||
| 
 | ||||
|                     TRACE("q", tout << "evidence " << ctx.bpp(a) << " " << ctx.bpp(b) << "\n"); | ||||
|                     evidence.push_back(euf::enode_pair(a, b)); | ||||
|                 } | ||||
|                 m_indirect_nodes.push_back(n); | ||||
|                 m_eval.setx(t->get_id(), n, nullptr); | ||||
|  | @ -246,20 +250,5 @@ namespace q { | |||
|         } | ||||
|         return m_eval[e->get_id()]; | ||||
|     } | ||||
| 
 | ||||
|     void eval::explain(sat::literal l, justification& j, sat::literal_vector& r, bool probing) { | ||||
|         clause& c = j.m_clause; | ||||
|         for (unsigned i = 0; i < j.m_num_ev; ++i) { | ||||
|             auto [a, b] = j.m_evidence[i]; | ||||
|             SASSERT(a->get_root() == b->get_root() || ctx.get_egraph().are_diseq(a, b)); | ||||
|             if (a->get_root() == b->get_root()) | ||||
|                 ctx.add_antecedent(a, b); | ||||
|             else | ||||
|                 ctx.add_diseq_antecedent(a, b);                             | ||||
|         } | ||||
|         r.push_back(c.m_literal); | ||||
|         (void)probing; // ignored        
 | ||||
|     } | ||||
| 
 | ||||
|      | ||||
| } | ||||
|  |  | |||
|  | @ -43,7 +43,6 @@ namespace q { | |||
|          | ||||
|     public: | ||||
|         eval(euf::solver& ctx); | ||||
|         void explain(sat::literal l, justification& j, sat::literal_vector& r, bool probing); | ||||
| 
 | ||||
|         lbool operator()(euf::enode* const* binding, clause& c, euf::enode_pair_vector& evidence); | ||||
|         lbool operator()(euf::enode* const* binding, clause& c, unsigned& idx, euf::enode_pair_vector& evidence); | ||||
|  |  | |||
|  | @ -424,15 +424,19 @@ namespace q { | |||
|         friend class compiler; | ||||
|         friend class code_tree_manager; | ||||
| 
 | ||||
|         void display_seq(std::ostream & out, instruction * head, unsigned indent) const { | ||||
|             for (unsigned i = 0; i < indent; i++) { | ||||
|         void spaces(std::ostream& out, unsigned indent) const { | ||||
|             for (unsigned i = 0; i < indent; i++)  | ||||
|                 out << "    "; | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         void display_seq(std::ostream & out, instruction * head, unsigned indent) const { | ||||
|             spaces(out, indent); | ||||
|             instruction * curr = head; | ||||
|             out << *curr; | ||||
|             curr = curr->m_next; | ||||
|             while (curr != nullptr && curr->m_opcode != CHOOSE && curr->m_opcode != NOOP) { | ||||
|                 out << "\n"; | ||||
|                 spaces(out, indent); | ||||
|                 out << *curr; | ||||
|                 curr = curr->m_next; | ||||
|             } | ||||
|  | @ -549,8 +553,8 @@ namespace q { | |||
|         void unmark(unsigned head) { | ||||
|             for (unsigned i = m_candidates.size(); i-- > head; ) { | ||||
|                 enode* app = m_candidates[i]; | ||||
|                 if (app->is_marked1()) | ||||
|                     app->unmark1(); | ||||
|                 if (app->is_marked3()) | ||||
|                     app->unmark3(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|  | @ -598,7 +602,7 @@ namespace q { | |||
|                 ast_manager & m = m_egraph->get_manager(); | ||||
|                 out << "patterns:\n"; | ||||
|                 for (auto [q, a] : m_patterns)  | ||||
|                     out << mk_pp(q, m) << " " << mk_pp(a, m) << "\n"; | ||||
|                     out << q->get_id() << ": " << mk_pp(q, m) << " " << mk_pp(a, m) << "\n"; | ||||
|             } | ||||
| #endif | ||||
|             out << "function: " << m_root_lbl->get_name(); | ||||
|  | @ -942,9 +946,9 @@ namespace q { | |||
|         void linearise_core() { | ||||
|             m_aux.reset(); | ||||
|             app *         first_app = nullptr; | ||||
|             unsigned      first_app_reg; | ||||
|             unsigned      first_app_sz; | ||||
|             unsigned      first_app_num_unbound_vars; | ||||
|             unsigned      first_app_reg = 0; | ||||
|             unsigned      first_app_sz = 0; | ||||
|             unsigned      first_app_num_unbound_vars = 0; | ||||
|             // generate first the non-BIND operations
 | ||||
|             for (unsigned reg : m_todo) { | ||||
|                 expr *  p    = m_registers[reg]; | ||||
|  | @ -2018,9 +2022,9 @@ namespace q { | |||
|                 code_tree::scoped_unmark _unmark(t); | ||||
|                 while ((app = t->next_candidate()) && !ctx.resource_limits_exceeded()) { | ||||
|                     TRACE("trigger_bug", tout << "candidate\n" << ctx.bpp(app) << "\n";); | ||||
|                     if (!app->is_marked1() && app->is_cgr()) { | ||||
|                     if (!app->is_marked3() && app->is_cgr()) { | ||||
|                         execute_core(t, app);                        | ||||
|                         app->mark1(); | ||||
|                         app->mark3(); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|  | @ -2878,8 +2882,10 @@ namespace q { | |||
|                 if (tree->expected_num_args() == p->get_num_args())  | ||||
|                     m_compiler.insert(tree, qa, mp, first_idx, false); | ||||
|             } | ||||
|             DEBUG_CODE(m_trees[lbl_id]->get_patterns().push_back(std::make_pair(qa, mp)); | ||||
|                        ctx.push(push_back_trail<std::pair<quantifier*,app*>, false>(m_trees[lbl_id]->get_patterns()));); | ||||
|             DEBUG_CODE(if (first_idx == 0) { | ||||
|                 m_trees[lbl_id]->get_patterns().push_back(std::make_pair(qa, mp)); | ||||
|                 ctx.push(push_back_trail<std::pair<quantifier*, app*>, false>(m_trees[lbl_id]->get_patterns())); | ||||
|             }); | ||||
|             TRACE("trigger_bug", tout << "after add_pattern, first_idx: " << first_idx << "\n"; m_trees[lbl_id]->display(tout);); | ||||
|         } | ||||
| 
 | ||||
|  | @ -3089,7 +3095,7 @@ namespace q { | |||
|         void add_candidate(code_tree * t, enode * app) { | ||||
|             if (!t) | ||||
|                 return; | ||||
|             TRACE("q", tout << "candidate " << t << " " << ctx.bpp(app) << "\n";); | ||||
|             TRACE("q", tout << "candidate " << ctx.bpp(app) << "\n";); | ||||
|             if (!t->has_candidates()) { | ||||
|                 ctx.push(push_back_trail<code_tree*, false>(m_to_match)); | ||||
|                 m_to_match.push_back(t); | ||||
|  |  | |||
|  | @ -51,7 +51,7 @@ namespace q { | |||
|         m_instantiations.reset(); | ||||
|         for (sat::literal lit : m_qs.m_universal) { | ||||
|             quantifier* q = to_quantifier(ctx.bool_var2expr(lit.var())); | ||||
|             if (!ctx.is_relevant(q)) | ||||
|             if (!ctx.is_relevant(lit.var())) | ||||
|                 continue; | ||||
|             init_model(); | ||||
|             switch (check_forall(q)) { | ||||
|  | @ -67,11 +67,10 @@ namespace q { | |||
|             } | ||||
|         } | ||||
|         m_max_cex += ctx.get_config().m_mbqi_max_cexs; | ||||
|         for (auto [qlit, fml, generation] : m_instantiations) { | ||||
|         for (auto const& [qlit, fml, generation] : m_instantiations) { | ||||
|             euf::solver::scoped_generation sg(ctx, generation + 1); | ||||
|             sat::literal lit = ctx.mk_literal(fml); | ||||
|             m_qs.add_clause(~qlit, ~lit); | ||||
|             ctx.add_root(~qlit, ~lit); | ||||
|         } | ||||
|         m_instantiations.reset(); | ||||
|         return result; | ||||
|  | @ -130,7 +129,7 @@ namespace q { | |||
|                 r = n; | ||||
|             } | ||||
|             else if (n->generation() == gen) { | ||||
|                 if ((++count) % m_qs.random() == 0) | ||||
|                 if ((m_qs.random() % ++count) == 0) | ||||
|                     r = n; | ||||
|             } | ||||
|             if (count > m_max_choose_candidates) | ||||
|  | @ -156,7 +155,7 @@ namespace q { | |||
|         unsigned inc = 1; | ||||
|         while (true) { | ||||
|             ::solver::scoped_push _sp(*m_solver); | ||||
|             add_universe_restriction(q, *qb); | ||||
|             add_universe_restriction(*qb); | ||||
|             m_solver->assert_expr(qb->mbody); | ||||
|             ++m_stats.m_num_checks; | ||||
|             lbool r = m_solver->check_sat(0, nullptr); | ||||
|  | @ -218,17 +217,17 @@ namespace q { | |||
|             qlit.neg(); | ||||
|         ctx.rewrite(proj); | ||||
|         TRACE("q", tout << "project: " << proj << "\n";); | ||||
|         IF_VERBOSE(11, verbose_stream() << "mbi:\n" << mk_pp(q, m) << "\n" << proj << "\n"); | ||||
|         ++m_stats.m_num_instantiations;         | ||||
|         unsigned generation = ctx.get_max_generation(proj);     | ||||
|         m_instantiations.push_back(instantiation_t(qlit, proj, generation)); | ||||
|     } | ||||
| 
 | ||||
|     void mbqi::add_universe_restriction(quantifier* q, q_body& qb) { | ||||
|         unsigned sz = q->get_num_decls(); | ||||
|         for (unsigned i = 0; i < sz; ++i) { | ||||
|             sort* s = q->get_decl_sort(i); | ||||
|     void mbqi::add_universe_restriction(q_body& qb) { | ||||
|         for (app* v : qb.vars) { | ||||
|             sort* s = v->get_sort(); | ||||
|             if (m_model->has_uninterpreted_sort(s)) | ||||
|                 restrict_to_universe(qb.vars.get(i), m_model->get_universe(s)); | ||||
|                 restrict_to_universe(v, m_model->get_universe(s)); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|  | @ -309,8 +308,10 @@ namespace q { | |||
|                 proj.extract_literals(*m_model, vars, fmls); | ||||
|                 fmls_extracted = true; | ||||
|             } | ||||
|             if (p) | ||||
|                 (*p)(*m_model, vars, fmls); | ||||
|             if (!p) | ||||
|                 continue; | ||||
|             if (!(*p)(*m_model, vars, fmls)) | ||||
|                 return expr_ref(nullptr, m); | ||||
|         } | ||||
|         for (app* v : vars) { | ||||
|             expr_ref term(m); | ||||
|  |  | |||
|  | @ -83,7 +83,7 @@ namespace q { | |||
|         q_body* specialize(quantifier* q); | ||||
|         q_body* q2body(quantifier* q); | ||||
|         expr_ref solver_project(model& mdl, q_body& qb, expr_ref_vector& eqs, bool use_inst); | ||||
|         void add_universe_restriction(quantifier* q, q_body& qb); | ||||
|         void add_universe_restriction(q_body& qb); | ||||
|         void add_domain_eqs(model& mdl, q_body& qb); | ||||
|         void add_domain_bounds(model& mdl, q_body& qb); | ||||
|         void eliminate_nested_vars(expr_ref_vector& fmls, q_body& qb); | ||||
|  |  | |||
|  | @ -66,7 +66,7 @@ namespace q { | |||
|         ptr_vector<quantifier> univ; | ||||
|         for (sat::literal lit : m_qs.universal()) { | ||||
|             quantifier* q = to_quantifier(ctx.bool_var2expr(lit.var())); | ||||
|             if (ctx.is_relevant(q)) | ||||
|             if (ctx.is_relevant(lit.var())) | ||||
|                 univ.push_back(q); | ||||
|         } | ||||
|         if (univ.empty()) | ||||
|  | @ -256,7 +256,7 @@ namespace q { | |||
|               tout << "invert-app " << mk_pp(t, m) << " = " << mk_pp(value, m) << "\n"; | ||||
|               if (v2r.find(value, r))  | ||||
|                   tout << "inverse " << mk_pp(r->get_expr(), m) << "\n"; | ||||
|               ctx.display(tout);               | ||||
|               /*ctx.display(tout); */ | ||||
|               ); | ||||
|         if (v2r.find(value, r))  | ||||
|             return r->get_expr(); | ||||
|  |  | |||
|  | @ -146,15 +146,10 @@ namespace q { | |||
|                  | ||||
|         unsigned gen = get_new_gen(f, ent.m_cost); | ||||
|         bool new_propagation = false; | ||||
|         if (false && em.propagate(true, f.nodes(), gen, *f.c, new_propagation)) | ||||
|         if (em.propagate(true, f.nodes(), gen, *f.c, new_propagation)) | ||||
|             return; | ||||
| 
 | ||||
| #if 0 | ||||
|         std::cout << mk_pp(q, m) << "\n"; | ||||
|         std::cout << num_bindings << "\n"; | ||||
|         for (unsigned i = 0; i < num_bindings; ++i) | ||||
|             std::cout << mk_pp(f[i]->get_expr(), m) << " " << mk_pp(f[i]->get_sort(), m) << "\n"; | ||||
| #endif | ||||
| 
 | ||||
|         auto* ebindings = m_subst(q, num_bindings); | ||||
|         for (unsigned i = 0; i < num_bindings; ++i) | ||||
|             ebindings[i] = f[i]->get_expr(); | ||||
|  | @ -168,7 +163,15 @@ namespace q { | |||
| 
 | ||||
|         m_stats.m_num_instances++; | ||||
| 
 | ||||
|         // f.display(ctx, std::cout << mk_pp(f.q(), m) << "\n" << instance << "\n") <<  "\n";
 | ||||
| #if 0 | ||||
|         std::cout << "instantiate\n"; | ||||
|         for (unsigned i = 0; i < num_bindings; ++i) | ||||
|             std::cout << ctx.bpp(f[i]) << " "; | ||||
|         std::cout << "\n"; | ||||
|         std::cout << mk_pp(q, m) << "\n"; | ||||
| #endif | ||||
| 
 | ||||
| //        f.display(ctx, std::cout << mk_pp(f.q(), m) << "\n" << instance << "\n") <<  "\n";
 | ||||
| 
 | ||||
|          | ||||
|         euf::solver::scoped_generation _sg(ctx, gen); | ||||
|  |  | |||
|  | @ -19,9 +19,11 @@ Author: | |||
| #include "ast/well_sorted.h" | ||||
| #include "ast/rewriter/var_subst.h" | ||||
| #include "ast/normal_forms/pull_quant.h" | ||||
| #include "ast/rewriter/inj_axiom.h" | ||||
| #include "sat/smt/q_solver.h" | ||||
| #include "sat/smt/euf_solver.h" | ||||
| #include "sat/smt/sat_th.h" | ||||
| #include "qe/lite/qe_lite.h" | ||||
| 
 | ||||
| 
 | ||||
| namespace q { | ||||
|  | @ -44,19 +46,23 @@ namespace q { | |||
|         if (l.sign() == is_forall(e)) { | ||||
|             sat::literal lit = skolemize(q); | ||||
|             add_clause(~l, lit); | ||||
|             ctx.add_root(~l, lit); | ||||
|             return; | ||||
|         } | ||||
|         else if (expand(q)) { | ||||
|             for (expr* e : m_expanded) { | ||||
|                 sat::literal lit = ctx.internalize(e, l.sign(), false, false); | ||||
|                 add_clause(~l, lit); | ||||
|                 ctx.add_root(~l, lit); | ||||
|         quantifier* q_flat = nullptr; | ||||
|         if (!m_flat.find(q, q_flat)) { | ||||
|             if (expand(q)) { | ||||
|                 for (expr* e : m_expanded) { | ||||
|                     sat::literal lit = ctx.internalize(e, l.sign(), false, false); | ||||
|                     add_clause(~l, lit); | ||||
|                 } | ||||
|                 return; | ||||
|             } | ||||
|             q_flat = flatten(q); | ||||
|         } | ||||
|         else if (is_ground(q->get_expr())) { | ||||
|             auto lit = ctx.internalize(q->get_expr(), l.sign(), false, false); | ||||
|              | ||||
|         if (is_ground(q_flat->get_expr())) { | ||||
|             auto lit = ctx.internalize(q_flat->get_expr(), l.sign(), false, false); | ||||
|             add_clause(~l, lit); | ||||
|             ctx.add_root(~l, lit); | ||||
|         } | ||||
|         else { | ||||
|             ctx.push_vec(m_universal, l); | ||||
|  | @ -72,9 +78,12 @@ namespace q { | |||
| 
 | ||||
|         if (ctx.get_config().m_mbqi) { | ||||
|             switch (m_mbqi()) { | ||||
|             case l_true:  return sat::check_result::CR_DONE; | ||||
|             case l_false: return sat::check_result::CR_CONTINUE; | ||||
|             case l_undef: break; | ||||
|             case l_true:   | ||||
|                 return sat::check_result::CR_DONE; | ||||
|             case l_false:  | ||||
|                 return sat::check_result::CR_CONTINUE; | ||||
|             case l_undef:  | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|         return sat::check_result::CR_GIVEUP; | ||||
|  | @ -169,13 +178,18 @@ namespace q { | |||
| 
 | ||||
|     quantifier* solver::flatten(quantifier* q) { | ||||
|         quantifier* q_flat = nullptr; | ||||
|         if (!has_quantifiers(q->get_expr()))  | ||||
|             return q; | ||||
|         if (m_flat.find(q, q_flat)) | ||||
|             return q_flat; | ||||
| 
 | ||||
|         expr_ref new_q(q, m), r(m); | ||||
|         proof_ref pr(m); | ||||
|         expr_ref  new_q(m); | ||||
|         if (is_forall(q)) { | ||||
|         if (!has_quantifiers(q->get_expr())) { | ||||
|             if (!ctx.get_config().m_refine_inj_axiom) | ||||
|                 return q; | ||||
|             if (!simplify_inj_axiom(m, q, new_q)) | ||||
|                 return q; | ||||
|         } | ||||
|         else if (is_forall(q)) { | ||||
|             pull_quant pull(m); | ||||
|             pull(q, new_q, pr); | ||||
|             SASSERT(is_well_sorted(m, new_q)); | ||||
|  | @ -221,15 +235,34 @@ namespace q { | |||
|         return val; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Expand returns true if it was able to rewrite the formula. | ||||
|      * If the rewrite results in a quantifier, the rewritten quantifier | ||||
|      * is stored in m_flat to avoid repeated expansions. | ||||
|      *  | ||||
|      */ | ||||
|     bool solver::expand(quantifier* q) { | ||||
|         expr_ref r(m); | ||||
|         expr_ref r(q, m); | ||||
|         proof_ref pr(m); | ||||
|         m_der(q, r, pr); | ||||
|         ctx.rewrite(r); | ||||
|         m_der(r, r, pr); | ||||
|         if (ctx.get_config().m_qe_lite) { | ||||
|             qe_lite qe(m, ctx.s().params()); | ||||
|             qe(r); | ||||
|         } | ||||
|         m_expanded.reset(); | ||||
|         if (r != q) { | ||||
|             ctx.get_rewriter()(r); | ||||
|             m_expanded.push_back(r); | ||||
|             return true; | ||||
|         bool updated = q != r; | ||||
|         if (updated) { | ||||
|             ctx.rewrite(r); | ||||
|             if (!is_quantifier(r)) { | ||||
|                 m_expanded.push_back(r); | ||||
|                 return true; | ||||
|             }             | ||||
|             if (is_forall(q)  != is_forall(r)) { | ||||
|                 m_expanded.push_back(r); | ||||
|                 return true; | ||||
|             } | ||||
|             q = to_quantifier(r); | ||||
|         } | ||||
|         if (is_forall(q))  | ||||
|             flatten_and(q->get_expr(), m_expanded); | ||||
|  | @ -253,6 +286,11 @@ namespace q { | |||
|                     idx = i; | ||||
|                 } | ||||
|             } | ||||
|             if (!e1 && updated) { | ||||
|                 m_expanded.reset(); | ||||
|                 m_expanded.push_back(r); | ||||
|                 return true; | ||||
|             } | ||||
|             if (!e1) | ||||
|                 return false; | ||||
| 
 | ||||
|  | @ -267,12 +305,19 @@ namespace q { | |||
|         if (m_expanded.size() > 1) { | ||||
|             for (unsigned i = m_expanded.size(); i-- > 0; ) { | ||||
|                 expr_ref tmp(m.update_quantifier(q, m_expanded.get(i)), m); | ||||
|                 ctx.get_rewriter()(tmp); | ||||
|                 ctx.rewrite(tmp); | ||||
|                 m_expanded[i] = tmp; | ||||
|             } | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|         else if (m_expanded.size() == 1 && updated) { | ||||
|             m_expanded[0] = r; | ||||
|             flatten(to_quantifier(r)); | ||||
|             return true; | ||||
|         } | ||||
|         else { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool solver::split(expr* arg, expr_ref& e1, expr_ref& e2) { | ||||
|  |  | |||
|  | @ -83,6 +83,7 @@ namespace q { | |||
|         void init_search() override; | ||||
|         void finalize_model(model& mdl) override; | ||||
|         bool is_shared(euf::theory_var v) const override { return true; } | ||||
|         void relevant_eh(euf::enode* n) override { m_ematch.relevant_eh(n); } | ||||
| 
 | ||||
|         ast_manager& get_manager() { return m; } | ||||
|         sat::literal_vector const& universal() const { return m_universal; } | ||||
|  |  | |||
|  | @ -68,8 +68,8 @@ namespace recfun { | |||
|         TRACEFN("case expansion " << e); | ||||
|         SASSERT(e.m_def->is_fun_macro()); | ||||
|         auto & vars = e.m_def->get_vars(); | ||||
|         auto lhs = e.m_lhs; | ||||
|         auto rhs = apply_args(vars, e.m_args, e.m_def->get_rhs()); | ||||
|         app_ref lhs = e.m_lhs; | ||||
|         expr_ref rhs = apply_args(vars, e.m_args, e.m_def->get_rhs()); | ||||
|         unsigned generation = std::max(ctx.get_max_generation(lhs), ctx.get_max_generation(rhs)); | ||||
|         euf::solver::scoped_generation _sgen(ctx, generation + 1); | ||||
|         auto eq = eq_internalize(lhs, rhs); | ||||
|  |  | |||
|  | @ -1,185 +0,0 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     sat_dual_solver.cpp | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Solver for obtaining implicant. | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-25 | ||||
| 
 | ||||
| --*/ | ||||
| #include "sat/smt/sat_dual_solver.h" | ||||
| 
 | ||||
| namespace sat { | ||||
| 
 | ||||
|     dual_solver::dual_solver(reslimit& l): | ||||
|         m_solver(m_params, l) | ||||
|     { | ||||
|         SASSERT(!m_solver.get_config().m_drat); | ||||
|         m_solver.set_incremental(true); | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::flush() { | ||||
|         while (m_num_scopes > 0) { | ||||
|             m_solver.user_push(); | ||||
|             m_roots.push_scope(); | ||||
|             m_tracked_vars.push_scope(); | ||||
|             m_units.push_scope(); | ||||
|             m_vars.push_scope(); | ||||
|             --m_num_scopes; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::push() { | ||||
|         ++m_num_scopes; | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::pop(unsigned num_scopes) {         | ||||
|         if (m_num_scopes >= num_scopes) { | ||||
|             m_num_scopes -= num_scopes; | ||||
|             return; | ||||
|         } | ||||
|         num_scopes -= m_num_scopes; | ||||
|         m_num_scopes = 0; | ||||
|         m_solver.user_pop(num_scopes); | ||||
|         unsigned old_sz = m_tracked_vars.old_size(num_scopes); | ||||
|         for (unsigned i = m_tracked_vars.size(); i-- > old_sz; ) | ||||
|             m_is_tracked[m_tracked_vars[i]] = false;  | ||||
|         old_sz = m_vars.old_size(num_scopes); | ||||
|         for (unsigned i = m_vars.size(); i-- > old_sz; ) { | ||||
|             unsigned v = m_vars[i]; | ||||
|             unsigned w = m_ext2var[v]; | ||||
|             m_ext2var[v] = null_bool_var; | ||||
|             m_var2ext[w] = null_bool_var; | ||||
|         } | ||||
|         m_vars.pop_scope(num_scopes); | ||||
|         m_units.pop_scope(num_scopes); | ||||
|         m_roots.pop_scope(num_scopes); | ||||
|         m_tracked_vars.pop_scope(num_scopes); | ||||
|     } | ||||
| 
 | ||||
|     bool_var dual_solver::ext2var(bool_var v) { | ||||
|         bool_var w = m_ext2var.get(v, null_bool_var); | ||||
|         if (null_bool_var == w) { | ||||
|             w = m_solver.mk_var(); | ||||
|             m_solver.set_external(w); | ||||
|             m_ext2var.setx(v, w, null_bool_var); | ||||
|             m_var2ext.setx(w, v, null_bool_var); | ||||
|             m_vars.push_back(v); | ||||
|         } | ||||
|         return w; | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::track_relevancy(bool_var w) { | ||||
|         flush(); | ||||
|         bool_var v = ext2var(w); | ||||
|         if (!m_is_tracked.get(v, false)) { | ||||
|             m_is_tracked.setx(v, true, false); | ||||
|             m_tracked_vars.push_back(v); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     literal dual_solver::ext2lit(literal lit) { | ||||
|         return literal(ext2var(lit.var()), lit.sign()); | ||||
|     } | ||||
| 
 | ||||
|     literal dual_solver::lit2ext(literal lit) { | ||||
|         return literal(m_var2ext[lit.var()], lit.sign()); | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::add_root(unsigned sz, literal const* clause) {       | ||||
|         flush(); | ||||
|         if (false && sz == 1) { | ||||
|             TRACE("dual", tout << "unit: " << clause[0] << "\n";); | ||||
|             m_units.push_back(clause[0]); | ||||
|             return; | ||||
|         } | ||||
|         literal root; | ||||
|         if (sz == 1)  | ||||
|             root = ext2lit(clause[0]); | ||||
|         else { | ||||
|             root = literal(m_solver.mk_var(), false); | ||||
|             for (unsigned i = 0; i < sz; ++i) | ||||
|                 m_solver.mk_clause(root, ~ext2lit(clause[i]), status::input()); | ||||
|         }         | ||||
|         m_solver.set_external(root.var()); | ||||
|         m_roots.push_back(~root); | ||||
|         TRACE("dual", tout << "root: " << ~root << " => " << literal_vector(sz, clause) << "\n";); | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::add_aux(unsigned sz, literal const* clause) { | ||||
|         flush();         | ||||
|         m_lits.reset(); | ||||
|         for (unsigned i = 0; i < sz; ++i)  | ||||
|             m_lits.push_back(ext2lit(clause[i])); | ||||
|         TRACE("dual", tout << "aux: " << literal_vector(sz, clause) << " -> " << m_lits << "\n";); | ||||
|         m_solver.mk_clause(sz, m_lits.data(), status::input()); | ||||
|     } | ||||
| 
 | ||||
|     void dual_solver::add_assumptions(solver const& s) { | ||||
|         flush(); | ||||
|         m_lits.reset(); | ||||
|         for (bool_var v : m_tracked_vars) | ||||
|             m_lits.push_back(literal(v, l_false == s.value(m_var2ext[v]))); | ||||
|         for (auto lit : m_units) { | ||||
|             bool_var w = m_ext2var.get(lit.var(), null_bool_var); | ||||
|             if (w != null_bool_var) | ||||
|                 m_lits.push_back(ext2lit(lit));             | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool dual_solver::operator()(solver const& s) {         | ||||
|         m_core.reset(); | ||||
|         m_core.append(m_units); | ||||
|         if (m_roots.empty()) | ||||
|             return true; | ||||
|         m_solver.user_push(); | ||||
|         m_solver.add_clause(m_roots.size(), m_roots.data(), status::input()); | ||||
|         add_assumptions(s); | ||||
|         lbool is_sat = m_solver.check(m_lits.size(), m_lits.data()); | ||||
|         if (is_sat == l_false)  | ||||
|             for (literal lit : m_solver.get_core()) | ||||
|                 m_core.push_back(lit2ext(lit));         | ||||
|         if (is_sat == l_true) { | ||||
|             TRACE("dual", display(s, tout); s.display(tout);); | ||||
|             IF_VERBOSE(0, verbose_stream() << "unexpected SAT\n"); | ||||
|             UNREACHABLE(); | ||||
|             return false; | ||||
|         } | ||||
|         TRACE("dual", m_solver.display(tout << "is-sat: " << is_sat << " core: " << m_core << "\n");); | ||||
|         m_solver.user_pop(1); | ||||
|         return is_sat == l_false; | ||||
|     } | ||||
| 
 | ||||
|     std::ostream& dual_solver::display(solver const& s, std::ostream& out) const {         | ||||
|         for (unsigned v = 0; v < m_solver.num_vars(); ++v) { | ||||
|             bool_var w = m_var2ext.get(v, null_bool_var); | ||||
|             if (w == null_bool_var) | ||||
|                 continue; | ||||
|             lbool v1 = m_solver.value(v); | ||||
|             lbool v2 = s.value(w); | ||||
|             if (v1 == v2 || v2 == l_undef) | ||||
|                 continue; | ||||
|             out << "ext: " << w << " " << v2 << "\n"; | ||||
|             out << "int: " << v << " " << v1 << "\n"; | ||||
|         } | ||||
|         literal_vector lits; | ||||
|         for (bool_var v : m_tracked_vars)  | ||||
|             lits.push_back(literal(m_var2ext[v], l_false == s.value(m_var2ext[v]))); | ||||
|         out << "tracked: " << lits << "\n";   | ||||
|         lits.reset(); | ||||
|         for (literal r : m_roots)  | ||||
|             if (m_solver.value(r) == l_true) | ||||
|                 lits.push_back(r); | ||||
|         out << "roots: " << lits << "\n"; | ||||
|         m_solver.display(out); | ||||
| 
 | ||||
|         return out; | ||||
|     } | ||||
| } | ||||
|  | @ -1,83 +0,0 @@ | |||
| /*++
 | ||||
| Copyright (c) 2020 Microsoft Corporation | ||||
| 
 | ||||
| Module Name: | ||||
| 
 | ||||
|     sat_dual_solver.h | ||||
| 
 | ||||
| Abstract: | ||||
| 
 | ||||
|     Solver for obtaining implicant. | ||||
|     Based on an idea by Armin Biere to use dual propagation  | ||||
|     for representation of negated goal. | ||||
| 
 | ||||
| Author: | ||||
| 
 | ||||
|     Nikolaj Bjorner (nbjorner) 2020-08-25 | ||||
| 
 | ||||
| --*/ | ||||
| #pragma once | ||||
| #include "util/lim_vector.h" | ||||
| #include "sat/sat_solver.h" | ||||
| 
 | ||||
| namespace sat { | ||||
| 
 | ||||
|     class dual_solver { | ||||
|         class dual_params : public no_drat_params { | ||||
|         public: | ||||
|             dual_params() { | ||||
|                 set_bool("core.minimize", false); | ||||
|             } | ||||
|         }; | ||||
|         dual_params     m_params; | ||||
|         solver          m_solver; | ||||
|         lim_svector<literal> m_units, m_roots; | ||||
|         lim_svector<bool_var> m_tracked_vars; | ||||
|         literal_vector  m_lits, m_core; | ||||
|         bool_var_vector m_is_tracked; | ||||
|         unsigned_vector m_ext2var; | ||||
|         unsigned_vector m_var2ext; | ||||
|         lim_svector<unsigned> m_vars; | ||||
|         unsigned        m_num_scopes = 0; | ||||
|         void add_literal(literal lit); | ||||
| 
 | ||||
|         bool_var ext2var(bool_var v); | ||||
|         bool_var var2ext(bool_var v); | ||||
|         literal  ext2lit(literal lit); | ||||
|         literal  lit2ext(literal lit); | ||||
| 
 | ||||
|         void add_assumptions(solver const& s); | ||||
| 
 | ||||
|         std::ostream& display(solver const& s, std::ostream& out) const; | ||||
| 
 | ||||
|         void flush(); | ||||
| 
 | ||||
|     public: | ||||
|         dual_solver(reslimit& l); | ||||
|         void push(); | ||||
|         void pop(unsigned num_scopes); | ||||
| 
 | ||||
|         /*
 | ||||
|         * track model relevancy for variable  | ||||
|         */ | ||||
|         void track_relevancy(bool_var v); | ||||
| 
 | ||||
|         /*
 | ||||
|         * Add a root clause from the input problem. | ||||
|         * At least one literal has to be satisfied in every root. | ||||
|         */ | ||||
|         void add_root(unsigned sz, literal const* clause); | ||||
| 
 | ||||
|         /*
 | ||||
|         * Add auxiliary clauses that originate from compiling definitions. | ||||
|         */ | ||||
|         void add_aux(unsigned sz, literal const* clause); | ||||
| 
 | ||||
|         /*
 | ||||
|         * Extract a minimized subset of relevant literals from a model for s. | ||||
|         */ | ||||
|         bool operator()(solver const& s); | ||||
| 
 | ||||
|         literal_vector const& core() const { return m_core; } | ||||
|     }; | ||||
| } | ||||
|  | @ -132,6 +132,7 @@ namespace euf { | |||
|     bool th_euf_solver::add_unit(sat::literal lit) { | ||||
|         bool was_true = is_true(lit); | ||||
|         ctx.s().add_clause(1, &lit, mk_status()); | ||||
|         ctx.add_root(lit); | ||||
|         return !was_true; | ||||
|     } | ||||
| 
 | ||||
|  | @ -143,32 +144,27 @@ namespace euf { | |||
|         return is_new; | ||||
|     } | ||||
| 
 | ||||
|     bool th_euf_solver::add_clause(sat::literal a, sat::literal b) { | ||||
|         bool was_true = is_true(a, b); | ||||
|     bool th_euf_solver::add_clause(sat::literal a, sat::literal b) {       | ||||
|         sat::literal lits[2] = { a, b }; | ||||
|         ctx.s().add_clause(2, lits, mk_status()); | ||||
|         return !was_true; | ||||
|         return add_clause(2, lits); | ||||
|     } | ||||
| 
 | ||||
|     bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) { | ||||
|         bool was_true = is_true(a, b, c); | ||||
|     bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c) {       | ||||
|         sat::literal lits[3] = { a, b, c }; | ||||
|         ctx.s().add_clause(3, lits, mk_status()); | ||||
|         return !was_true; | ||||
|         return add_clause(3, lits); | ||||
|     } | ||||
| 
 | ||||
|     bool th_euf_solver::add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d) { | ||||
|         bool was_true = is_true(a, b, c, d); | ||||
|         sat::literal lits[4] = { a, b, c, d }; | ||||
|         ctx.s().add_clause(4, lits, mk_status()); | ||||
|         return !was_true; | ||||
|         return add_clause(4, lits); | ||||
|     } | ||||
| 
 | ||||
|     bool th_euf_solver::add_clause(sat::literal_vector const& lits) { | ||||
|     bool th_euf_solver::add_clause(unsigned n, sat::literal* lits) { | ||||
|         bool was_true = false; | ||||
|         for (auto lit : lits) | ||||
|             was_true |= is_true(lit); | ||||
|         s().add_clause(lits.size(), lits.data(), mk_status()); | ||||
|         for (unsigned i = 0; i < n; ++i)        | ||||
|             was_true |= is_true(lits[i]); | ||||
|         ctx.add_root(n, lits); | ||||
|         s().add_clause(n, lits, mk_status()); | ||||
|         return !was_true; | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -95,6 +95,11 @@ namespace euf { | |||
|           \brief conclude model building | ||||
|         */ | ||||
|         virtual void finalize_model(model& mdl) {} | ||||
| 
 | ||||
|         /**
 | ||||
|         * \brief does solver have an unhandled function. | ||||
|         */ | ||||
|         virtual bool has_unhandled() const { return false; } | ||||
|     }; | ||||
| 
 | ||||
|     class th_solver : public sat::extension, public th_model_builder, public th_decompile, public th_internalizer { | ||||
|  | @ -111,6 +116,12 @@ namespace euf { | |||
| 
 | ||||
|         virtual void new_diseq_eh(euf::th_eq const& eq) {} | ||||
| 
 | ||||
|         virtual bool enable_ackerman_axioms(euf::enode* n) const { return true; } | ||||
| 
 | ||||
|         virtual bool is_fixed(euf::theory_var v, expr_ref& val, sat::literal_vector& lits) { return false; } | ||||
| 
 | ||||
|         virtual void relevant_eh(euf::enode* n) {} | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief Parametric theories (e.g. Arrays) should implement this method. | ||||
|         */ | ||||
|  | @ -139,7 +150,8 @@ namespace euf { | |||
|         bool add_clause(sat::literal a, sat::literal b); | ||||
|         bool add_clause(sat::literal a, sat::literal b, sat::literal c); | ||||
|         bool add_clause(sat::literal a, sat::literal b, sat::literal c, sat::literal d); | ||||
|         bool add_clause(sat::literal_vector const& lits); | ||||
|         bool add_clause(sat::literal_vector const& lits) { return add_clause(lits.size(), lits.data()); } | ||||
|         bool add_clause(unsigned n, sat::literal* lits); | ||||
|         void add_equiv(sat::literal a, sat::literal b); | ||||
|         void add_equiv_and(sat::literal a, sat::literal_vector const& bs); | ||||
| 
 | ||||
|  |  | |||
|  | @ -36,6 +36,10 @@ namespace user_solver { | |||
|             return n->get_th_var(get_id()); | ||||
|         euf::theory_var v = mk_var(n); | ||||
|         ctx.attach_th_var(n, this, v); | ||||
|         expr_ref r(m); | ||||
|         sat::literal_vector explain; | ||||
|         if (ctx.is_fixed(n, r, explain)) | ||||
|             m_prop.push_back(prop_info(explain, v, r)); | ||||
|         return v; | ||||
|     } | ||||
| 
 | ||||
|  | @ -93,6 +97,18 @@ namespace user_solver { | |||
|         m_pop_eh(m_user_context, num_scopes); | ||||
|     } | ||||
| 
 | ||||
|     void solver::propagate_consequence(prop_info const& prop) { | ||||
|         sat::literal lit = ctx.internalize(prop.m_conseq, false, false, true); | ||||
|         if (s().value(lit) != l_true) { | ||||
|             s().assign(lit, mk_justification(m_qhead)); | ||||
|             ++m_stats.m_num_propagations; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void solver::propagate_new_fixed(prop_info const& prop) { | ||||
|         new_fixed_eh(prop.m_var, prop.m_conseq, prop.m_lits.size(), prop.m_lits.data()); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::unit_propagate() { | ||||
|         if (m_qhead == m_prop.size()) | ||||
|             return false; | ||||
|  | @ -100,12 +116,11 @@ namespace user_solver { | |||
|         ctx.push(value_trail<unsigned>(m_qhead)); | ||||
|         unsigned np = m_stats.m_num_propagations; | ||||
|         for (; m_qhead < m_prop.size() && !s().inconsistent(); ++m_qhead) { | ||||
|             auto const& prop = m_prop[m_qhead];             | ||||
|             sat::literal lit = ctx.internalize(prop.m_conseq, false, false, true); | ||||
|             if (s().value(lit) != l_true) { | ||||
|                 s().assign(lit, mk_justification(m_qhead)); | ||||
|                 ++m_stats.m_num_propagations; | ||||
|             } | ||||
|             auto const& prop = m_prop[m_qhead]; | ||||
|             if (prop.m_var == euf::null_theory_var) | ||||
|                 propagate_consequence(prop); | ||||
|             else | ||||
|                 propagate_new_fixed(prop); | ||||
|         }        | ||||
|         return np < m_stats.m_num_propagations; | ||||
|     } | ||||
|  | @ -171,5 +186,51 @@ namespace user_solver { | |||
|         return result; | ||||
|     } | ||||
| 
 | ||||
|     sat::literal solver::internalize(expr* e, bool sign, bool root, bool redundant) { | ||||
|         if (!visit_rec(m, e, sign, root, redundant)) { | ||||
|             TRACE("array", tout << mk_pp(e, m) << "\n";); | ||||
|             return sat::null_literal; | ||||
|         } | ||||
|         sat::literal lit = ctx.expr2literal(e); | ||||
|         if (sign) | ||||
|             lit.neg(); | ||||
|         if (root) | ||||
|             add_unit(lit); | ||||
|         return lit; | ||||
|     } | ||||
| 
 | ||||
|     void solver::internalize(expr* e, bool redundant) { | ||||
|         visit_rec(m, e, false, false, redundant); | ||||
|     } | ||||
| 
 | ||||
|     bool solver::visit(expr* e) { | ||||
|         if (visited(e)) | ||||
|             return true; | ||||
|         if (!is_app(e) || to_app(e)->get_family_id() != get_id()) { | ||||
|             ctx.internalize(e, m_is_redundant); | ||||
|             return true; | ||||
|         } | ||||
|         m_stack.push_back(sat::eframe(e)); | ||||
|         return false;         | ||||
|     } | ||||
|      | ||||
|     bool solver::visited(expr* e) { | ||||
|         euf::enode* n = expr2enode(e); | ||||
|         return n && n->is_attached_to(get_id());         | ||||
|     } | ||||
|      | ||||
|     bool solver::post_visit(expr* e, bool sign, bool root) { | ||||
|         euf::enode* n = expr2enode(e); | ||||
|         SASSERT(!n || !n->is_attached_to(get_id())); | ||||
|         if (!n)  | ||||
|             n = mk_enode(e, false);         | ||||
|         auto v = add_expr(e); | ||||
|         if (m_created_eh) | ||||
|             m_created_eh(m_user_context, this, e, v); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -32,6 +32,9 @@ namespace user_solver { | |||
|             unsigned_vector m_ids; | ||||
|             expr_ref        m_conseq; | ||||
|             svector<std::pair<unsigned, unsigned>> m_eqs; | ||||
|             sat::literal_vector                    m_lits; | ||||
|             euf::theory_var                        m_var = euf::null_theory_var; | ||||
| 
 | ||||
|             prop_info(unsigned num_fixed, unsigned const* fixed_ids, unsigned num_eqs, unsigned const* eq_lhs, unsigned const* eq_rhs, expr_ref const& c): | ||||
|                 m_ids(num_fixed, fixed_ids), | ||||
|                 m_conseq(c) | ||||
|  | @ -39,6 +42,12 @@ namespace user_solver { | |||
|                 for (unsigned i = 0; i < num_eqs; ++i) | ||||
|                     m_eqs.push_back(std::make_pair(eq_lhs[i], eq_rhs[i])); | ||||
|             } | ||||
| 
 | ||||
|             prop_info(sat::literal_vector const& lits, euf::theory_var v, expr_ref const& val): | ||||
|                 m_conseq(val), | ||||
|                 m_lits(lits), | ||||
|                 m_var(v) {} | ||||
| 
 | ||||
|         }; | ||||
| 
 | ||||
|         struct stats { | ||||
|  | @ -55,6 +64,7 @@ namespace user_solver { | |||
|         user_propagator::fixed_eh_t     m_fixed_eh; | ||||
|         user_propagator::eq_eh_t        m_eq_eh; | ||||
|         user_propagator::eq_eh_t        m_diseq_eh; | ||||
|         user_propagator::created_eh_t   m_created_eh; | ||||
|         user_propagator::context_obj*   m_api_context = nullptr; | ||||
|         unsigned               m_qhead = 0; | ||||
|         vector<prop_info>      m_prop; | ||||
|  | @ -80,8 +90,15 @@ namespace user_solver { | |||
| 
 | ||||
|         sat::justification mk_justification(unsigned propagation_index); | ||||
| 
 | ||||
|         void propagate_consequence(prop_info const& prop); | ||||
|         void propagate_new_fixed(prop_info const& prop); | ||||
| 
 | ||||
| 	void validate_propagation(); | ||||
| 
 | ||||
|         bool visit(expr* e) override; | ||||
|         bool visited(expr* e) override; | ||||
|         bool post_visit(expr* e, bool sign, bool root) override; | ||||
| 
 | ||||
|     public: | ||||
|         solver(euf::solver& ctx); | ||||
|          | ||||
|  | @ -107,6 +124,7 @@ namespace user_solver { | |||
|         void register_fixed(user_propagator::fixed_eh_t& fixed_eh) { m_fixed_eh = fixed_eh; } | ||||
|         void register_eq(user_propagator::eq_eh_t& eq_eh) { m_eq_eh = eq_eh; } | ||||
|         void register_diseq(user_propagator::eq_eh_t& diseq_eh) { m_diseq_eh = diseq_eh; } | ||||
|         void register_created(user_propagator::created_eh_t& created_eh) { m_created_eh = created_eh; } | ||||
| 
 | ||||
|         bool has_fixed() const { return (bool)m_fixed_eh; } | ||||
| 
 | ||||
|  | @ -122,8 +140,8 @@ namespace user_solver { | |||
|         bool unit_propagate() override; | ||||
|         void get_antecedents(sat::literal l, sat::ext_justification_idx idx, sat::literal_vector & r, bool probing) override; | ||||
|         void collect_statistics(statistics& st) const override; | ||||
|         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override { UNREACHABLE(); return sat::null_literal; } | ||||
|         void internalize(expr* e, bool redundant) override { UNREACHABLE(); } | ||||
|         sat::literal internalize(expr* e, bool sign, bool root, bool learned) override; | ||||
|         void internalize(expr* e, bool redundant) override; | ||||
|         std::ostream& display(std::ostream& out) const override; | ||||
|         std::ostream& display_justification(std::ostream& out, sat::ext_justification_idx idx) const override; | ||||
|         std::ostream& display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const override; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue