mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-03 21:09:11 +00:00 
			
		
		
		
	introduce notion of beta redex to deal with lambdas in non-extensional positions
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									b9b5377c69
								
							
						
					
					
						commit
						8efa3c8ade
					
				
					 10 changed files with 65 additions and 51 deletions
				
			
		| 
						 | 
				
			
			@ -1677,6 +1677,7 @@ quantifier* ast_manager::is_lambda_def(func_decl* f) {
 | 
			
		|||
    return nullptr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
void ast_manager::register_plugin(family_id id, decl_plugin * plugin) {
 | 
			
		||||
    SASSERT(m_plugins.get(id, 0) == 0);
 | 
			
		||||
    m_plugins.setx(id, plugin, 0);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1618,7 +1618,7 @@ public:
 | 
			
		|||
    bool is_lambda_def(quantifier* q) const { return q->get_qid() == m_lambda_def; }
 | 
			
		||||
    void add_lambda_def(func_decl* f, quantifier* q);
 | 
			
		||||
    quantifier* is_lambda_def(func_decl* f);
 | 
			
		||||
    
 | 
			
		||||
    quantifier* is_lambda_def(app* e) { return is_lambda_def(e->get_decl()); }
 | 
			
		||||
 | 
			
		||||
    symbol const& lambda_def_qid() const { return m_lambda_def; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3737,15 +3737,12 @@ namespace smt {
 | 
			
		|||
 | 
			
		||||
        reset_model();
 | 
			
		||||
 | 
			
		||||
        if (m_last_search_failure != OK) {
 | 
			
		||||
        if (m_last_search_failure != OK) 
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (status == l_false) {
 | 
			
		||||
        if (status == l_false) 
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (status == l_true && !m_qmanager->has_quantifiers() && m_lambdas.empty()) {
 | 
			
		||||
        if (status == l_true && !m_qmanager->has_quantifiers() && !has_lambda()) 
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (status == l_true && m_qmanager->has_quantifiers()) {
 | 
			
		||||
            // possible outcomes   DONE l_true, DONE l_undef, CONTINUE
 | 
			
		||||
            mk_proto_model();
 | 
			
		||||
| 
						 | 
				
			
			@ -3766,7 +3763,7 @@ namespace smt {
 | 
			
		|||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        if (status == l_true && !m_lambdas.empty()) {
 | 
			
		||||
        if (status == l_true && has_lambda()) {
 | 
			
		||||
            m_last_search_failure = LAMBDAS;
 | 
			
		||||
            status = l_undef;
 | 
			
		||||
            return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -4010,7 +4007,7 @@ namespace smt {
 | 
			
		|||
        TRACE("final_check_step", tout << "RESULT final_check: " << result << "\n";);
 | 
			
		||||
        if (result == FC_GIVEUP && f != OK)
 | 
			
		||||
            m_last_search_failure = f;
 | 
			
		||||
        if (result == FC_DONE && !m_lambdas.empty()) {
 | 
			
		||||
        if (result == FC_DONE && has_lambda()) {
 | 
			
		||||
            m_last_search_failure = LAMBDAS;
 | 
			
		||||
            result = FC_GIVEUP;
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			@ -4468,9 +4465,8 @@ namespace smt {
 | 
			
		|||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        case 1: {
 | 
			
		||||
            if (m_qmanager->is_shared(n)) {
 | 
			
		||||
            if (m_qmanager->is_shared(n) && !m.is_lambda_def(n->get_expr()) && !m_lambdas.contains(n)) 
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            // the variable is shared if the equivalence class of n
 | 
			
		||||
            // contains a parent application.
 | 
			
		||||
| 
						 | 
				
			
			@ -4482,6 +4478,8 @@ namespace smt {
 | 
			
		|||
                app* p = parent->get_expr();
 | 
			
		||||
                family_id fid = p->get_family_id();
 | 
			
		||||
                if (fid != th_id && fid != m.get_basic_family_id()) {
 | 
			
		||||
                    if (is_beta_redex(parent, n))
 | 
			
		||||
                        continue;
 | 
			
		||||
                    TRACE("is_shared", tout << enode_pp(n, *this) 
 | 
			
		||||
                          << "\nis shared because of:\n" 
 | 
			
		||||
                          << enode_pp(parent, *this) << "\n";);
 | 
			
		||||
| 
						 | 
				
			
			@ -4522,6 +4520,12 @@ namespace smt {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool context::is_beta_redex(enode* p, enode* n) const {
 | 
			
		||||
        family_id th_id = p->get_expr()->get_family_id();
 | 
			
		||||
        theory * th = get_theory(th_id);
 | 
			
		||||
        return th && th->is_beta_redex(p, n);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool context::get_value(enode * n, expr_ref & value) {
 | 
			
		||||
        sort * s      = n->get_sort();
 | 
			
		||||
        family_id fid = s->get_family_id();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -773,7 +773,12 @@ namespace smt {
 | 
			
		|||
 | 
			
		||||
        void internalize_quantifier(quantifier * q, bool gate_ctx);
 | 
			
		||||
 | 
			
		||||
        obj_hashtable<quantifier> m_lambdas, m_non_lambdas;
 | 
			
		||||
        obj_map<enode, quantifier*> m_lambdas;
 | 
			
		||||
 | 
			
		||||
        bool has_lambda();
 | 
			
		||||
 | 
			
		||||
        bool is_beta_redex(enode* p, enode* n) const;
 | 
			
		||||
 | 
			
		||||
        void internalize_lambda(quantifier * q);
 | 
			
		||||
 | 
			
		||||
        void internalize_formula_core(app * n, bool gate_ctx);
 | 
			
		||||
| 
						 | 
				
			
			@ -783,8 +788,6 @@ namespace smt {
 | 
			
		|||
        friend class set_enode_flag_trail;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
 | 
			
		||||
        void add_non_lambda(quantifier* q);
 | 
			
		||||
        
 | 
			
		||||
        void set_enode_flag(bool_var v, bool is_new_var);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -578,20 +578,19 @@ namespace smt {
 | 
			
		|||
        m_qmanager->add(q, generation);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void context::internalize_lambda(quantifier * q) {
 | 
			
		||||
        TRACE("internalize_quantifier", tout << mk_pp(q, m) << "\n";);
 | 
			
		||||
        SASSERT(is_lambda(q));
 | 
			
		||||
        if (e_internalized(q)) {
 | 
			
		||||
        if (e_internalized(q)) 
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        app_ref lam_name(m.mk_fresh_const("lambda", q->get_sort()), m);
 | 
			
		||||
        app_ref eq(m), lam_app(m);
 | 
			
		||||
        expr_ref_vector vars(m);
 | 
			
		||||
        vars.push_back(lam_name);
 | 
			
		||||
        unsigned sz = q->get_num_decls();
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) {
 | 
			
		||||
        for (unsigned i = 0; i < sz; ++i) 
 | 
			
		||||
            vars.push_back(m.mk_var(sz - i - 1, q->get_decl_sort(i)));
 | 
			
		||||
        }
 | 
			
		||||
        array_util autil(m);
 | 
			
		||||
        lam_app = autil.mk_select(vars.size(), vars.data());
 | 
			
		||||
        eq = m.mk_eq(lam_app, q->get_expr());
 | 
			
		||||
| 
						 | 
				
			
			@ -599,28 +598,28 @@ namespace smt {
 | 
			
		|||
        expr * patterns[1] = { m.mk_pattern(lam_app) };
 | 
			
		||||
        fa = m.mk_forall(sz, q->get_decl_sorts(), q->get_decl_names(), eq, 0, m.lambda_def_qid(), symbol::null, 1, patterns);
 | 
			
		||||
        internalize_quantifier(fa, true);
 | 
			
		||||
        if (!e_internalized(lam_name)) internalize_uninterpreted(lam_name);
 | 
			
		||||
        m_app2enode.setx(q->get_id(), get_enode(lam_name), nullptr);
 | 
			
		||||
        if (!e_internalized(lam_name)) 
 | 
			
		||||
            internalize_uninterpreted(lam_name);
 | 
			
		||||
        enode* lam_node = get_enode(lam_name);
 | 
			
		||||
        push_trail(insert_obj_map<enode, quantifier*>(m_lambdas, lam_node));
 | 
			
		||||
        m_lambdas.insert(lam_node, q);
 | 
			
		||||
        m_app2enode.setx(q->get_id(), lam_node, nullptr);
 | 
			
		||||
        m_l_internalized_stack.push_back(q);
 | 
			
		||||
        m_trail_stack.push_back(&m_mk_lambda_trail);
 | 
			
		||||
        bool_var bv = get_bool_var(fa);
 | 
			
		||||
        assign(literal(bv, false), nullptr);
 | 
			
		||||
        mark_as_relevant(bv);
 | 
			
		||||
        if (m_non_lambdas.contains(q))
 | 
			
		||||
            return;
 | 
			
		||||
        push_trail(insert_obj_trail<quantifier>(m_lambdas, q));
 | 
			
		||||
        m_lambdas.insert(q);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void context::add_non_lambda(quantifier* q) {
 | 
			
		||||
        if (m_non_lambdas.contains(q))
 | 
			
		||||
            return;
 | 
			
		||||
        m_non_lambdas.insert(q);
 | 
			
		||||
        push_trail(insert_obj_trail<quantifier>(m_non_lambdas, q));
 | 
			
		||||
        if (m_lambdas.contains(q)) {
 | 
			
		||||
            m_lambdas.remove(q);
 | 
			
		||||
            push_trail(remove_obj_trail<quantifier>(m_lambdas, q));
 | 
			
		||||
    bool context::has_lambda() {
 | 
			
		||||
        for (auto const & [n, q] : m_lambdas) {
 | 
			
		||||
            if (n->get_class_size() != 1) 
 | 
			
		||||
                return true;
 | 
			
		||||
            for (enode* p : enode::parents(n)) 
 | 
			
		||||
                if (!is_beta_redex(p, n)) 
 | 
			
		||||
                    return true;
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -321,6 +321,13 @@ namespace smt {
 | 
			
		|||
        virtual bool is_shared(theory_var v) const {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Determine if node \c n under parent \c p is in a beta redex position.
 | 
			
		||||
        */
 | 
			
		||||
        virtual bool is_beta_redex(enode* p, enode* n) const {
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
    
 | 
			
		||||
        /**
 | 
			
		||||
           \brief Return true if the theory has something to propagate
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -473,6 +473,15 @@ namespace smt {
 | 
			
		|||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool theory_array_base::is_beta_redex(enode* p, enode* n) const {
 | 
			
		||||
        if (is_select(p))
 | 
			
		||||
            return p->get_arg(0)->get_root() == n->get_root();
 | 
			
		||||
        if (is_map(p))
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    bool theory_array_base::is_select_arg(enode* r) {
 | 
			
		||||
        for (enode* n : r->get_parents()) 
 | 
			
		||||
            if (is_select(n)) 
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -142,6 +142,7 @@ namespace smt {
 | 
			
		|||
        // 
 | 
			
		||||
        // --------------------------------------------------
 | 
			
		||||
        bool is_shared(theory_var v) const override;
 | 
			
		||||
        bool is_beta_redex(enode* p, enode* n) const override;
 | 
			
		||||
        void collect_shared_vars(sbuffer<theory_var> & result);
 | 
			
		||||
        unsigned mk_interface_eqs();
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1457,8 +1457,6 @@ bool theory_seq::internalize_term(app* term) {
 | 
			
		|||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    suppress_lambda(term);
 | 
			
		||||
 | 
			
		||||
    if (m.is_bool(term) && 
 | 
			
		||||
        (m_util.str.is_in_re(term) || m_sk.is_skolem(term))) {
 | 
			
		||||
        bool_var bv = ctx.mk_bool_var(term);
 | 
			
		||||
| 
						 | 
				
			
			@ -1486,26 +1484,18 @@ bool theory_seq::internalize_term(app* term) {
 | 
			
		|||
    mk_var(e);
 | 
			
		||||
    if (!ctx.relevancy()) {
 | 
			
		||||
        relevant_eh(term);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    }    
 | 
			
		||||
    return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void theory_seq::suppress_lambda(app* term) {
 | 
			
		||||
bool theory_seq::is_beta_redex(enode* p, enode* n) const {
 | 
			
		||||
    expr* term = p->get_expr();
 | 
			
		||||
    if (!m_util.str.is_map(term) && !m_util.str.is_mapi(term) &&
 | 
			
		||||
        !m_util.str.is_foldl(term) && !m_util.str.is_foldli(term))
 | 
			
		||||
        return;
 | 
			
		||||
    
 | 
			
		||||
    expr* fn = to_app(term)->get_arg(0);
 | 
			
		||||
    quantifier* q = nullptr;
 | 
			
		||||
    if (is_lambda(fn)) 
 | 
			
		||||
        q = to_quantifier(fn);
 | 
			
		||||
    else if (is_app(fn))
 | 
			
		||||
        q = m.is_lambda_def(to_app(fn)->get_decl());
 | 
			
		||||
 | 
			
		||||
    if (q) 
 | 
			
		||||
        ctx.add_non_lambda(q);
 | 
			
		||||
        return false;
 | 
			
		||||
    if (p->get_arg(0)->get_root() == n->get_root())
 | 
			
		||||
        return true;
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -3292,7 +3282,7 @@ bool theory_seq::should_research(expr_ref_vector & unsat_core) {
 | 
			
		|||
        k_min *= 2;
 | 
			
		||||
        if (m_util.is_seq(s_min))
 | 
			
		||||
            k_min = std::max(m_util.str.min_length(s_min), k_min);
 | 
			
		||||
        IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-length " << mk_pp(s_min, m) << " " << k_min << ")\n");
 | 
			
		||||
        IF_VERBOSE(1, verbose_stream() << "(smt.seq :increase-length " << mk_bounded_pp(s_min, m, 3) << " " << k_min << ")\n");
 | 
			
		||||
        add_length_limit(s_min, k_min, false);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -388,7 +388,6 @@ namespace smt {
 | 
			
		|||
        bool internalize_atom(app* atom, bool) override;
 | 
			
		||||
        bool internalize_term(app*) override;
 | 
			
		||||
        void internalize_eq_eh(app * atom, bool_var v) override;
 | 
			
		||||
        void suppress_lambda(app* term);
 | 
			
		||||
        void new_eq_eh(theory_var, theory_var) override;
 | 
			
		||||
        void new_diseq_eh(theory_var, theory_var) override;
 | 
			
		||||
        void assign_eh(bool_var v, bool is_true) override;
 | 
			
		||||
| 
						 | 
				
			
			@ -413,6 +412,7 @@ namespace smt {
 | 
			
		|||
        void finalize_model(model_generator & mg) override;
 | 
			
		||||
        void init_search_eh() override;
 | 
			
		||||
        void validate_model(model& mdl) override;
 | 
			
		||||
        bool is_beta_redex(enode* p, enode* n) const override;
 | 
			
		||||
 | 
			
		||||
        void init_model(expr_ref_vector const& es);
 | 
			
		||||
        app* get_ite_value(expr* a);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue