mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	array solver fixes
This commit is contained in:
		
							parent
							
								
									78f4513441
								
							
						
					
					
						commit
						9a975a4523
					
				
					 10 changed files with 227 additions and 98 deletions
				
			
		| 
						 | 
				
			
			@ -182,30 +182,31 @@ public:
 | 
			
		|||
 | 
			
		||||
    bool is_as_array_tree(expr * n);
 | 
			
		||||
 | 
			
		||||
    app * mk_store(unsigned num_args, expr * const * args) {
 | 
			
		||||
    app * mk_store(unsigned num_args, expr * const * args) const {
 | 
			
		||||
        return m_manager.mk_app(m_fid, OP_STORE, 0, nullptr, num_args, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app * mk_store(expr_ref_vector const& args) {
 | 
			
		||||
        return mk_store(args.size(), args.c_ptr());
 | 
			
		||||
    }
 | 
			
		||||
    app * mk_store(ptr_vector<expr> const& args) {
 | 
			
		||||
    app * mk_store(expr_ref_vector const& args) const {
 | 
			
		||||
        return mk_store(args.size(), args.c_ptr());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app * mk_select(unsigned num_args, expr * const * args) {
 | 
			
		||||
    app * mk_store(ptr_vector<expr> const& args) const {
 | 
			
		||||
        return mk_store(args.size(), args.c_ptr());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app * mk_select(unsigned num_args, expr * const * args) const {
 | 
			
		||||
        return m_manager.mk_app(m_fid, OP_SELECT, 0, nullptr, num_args, args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app * mk_select(ptr_vector<expr> const& args) {
 | 
			
		||||
    app * mk_select(ptr_vector<expr> const& args) const {
 | 
			
		||||
        return mk_select(args.size(), args.c_ptr());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app * mk_select(ptr_buffer<expr> const& args) {
 | 
			
		||||
    app * mk_select(ptr_buffer<expr> const& args) const {
 | 
			
		||||
        return mk_select(args.size(), args.c_ptr());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    app * mk_select(expr_ref_vector const& args) {
 | 
			
		||||
    app * mk_select(expr_ref_vector const& args) const {
 | 
			
		||||
        return mk_select(args.size(), args.c_ptr());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,7 @@ z3_add_component(sat_smt
 | 
			
		|||
    arith_internalize.cpp
 | 
			
		||||
    arith_solver.cpp
 | 
			
		||||
    array_axioms.cpp
 | 
			
		||||
    array_diagnostics.cpp
 | 
			
		||||
    array_internalize.cpp
 | 
			
		||||
    array_model.cpp
 | 
			
		||||
    array_solver.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -78,7 +78,7 @@ namespace arith {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        // clone rows into m_solver, m_nla, m_lia
 | 
			
		||||
        NOT_IMPLEMENTED_YET();
 | 
			
		||||
        // NOT_IMPLEMENTED_YET();
 | 
			
		||||
 | 
			
		||||
        return result;        
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,22 +25,27 @@ namespace array {
 | 
			
		|||
    void solver::push_axiom(axiom_record const& r) { 
 | 
			
		||||
        unsigned idx = m_axiom_trail.size();
 | 
			
		||||
        m_axiom_trail.push_back(r); 
 | 
			
		||||
        TRACE("array", display(tout, r) << " " << m_axioms.contains(idx) << "\n";);
 | 
			
		||||
        if (m_axioms.contains(idx))
 | 
			
		||||
            m_axiom_trail.pop_back();
 | 
			
		||||
        else
 | 
			
		||||
            ctx.push(push_back_vector<svector<axiom_record>>(m_axiom_trail));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool solver::propagate_axiom(unsigned idx) {
 | 
			
		||||
        if (m_axioms.contains(idx))
 | 
			
		||||
    bool solver::propagate_axiom(unsigned idx) {        
 | 
			
		||||
        if (!m_axioms.contains(idx)) {
 | 
			
		||||
            m_axioms.insert(idx);
 | 
			
		||||
            ctx.push(insert_map<axiom_table_t, unsigned>(m_axioms, idx));
 | 
			
		||||
        }
 | 
			
		||||
        else if (!m_axiom_trail[idx].is_delayed())
 | 
			
		||||
            return false;
 | 
			
		||||
        m_axioms.insert(idx);
 | 
			
		||||
        ctx.push(insert_map<axiom_table_t, unsigned>(m_axioms, idx));
 | 
			
		||||
        return assert_axiom(idx);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    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()));
 | 
			
		||||
| 
						 | 
				
			
			@ -62,8 +67,7 @@ namespace array {
 | 
			
		|||
    bool solver::assert_default(axiom_record& r) {
 | 
			
		||||
        expr* child = r.n->get_expr();
 | 
			
		||||
        SASSERT(can_beta_reduce(r.n));
 | 
			
		||||
        if (!ctx.is_relevant(child))
 | 
			
		||||
            return false;
 | 
			
		||||
            
 | 
			
		||||
        TRACE("array", tout << "default-axiom: " << mk_bounded_pp(child, m, 2) << "\n";);
 | 
			
		||||
        if (a.is_const(child))
 | 
			
		||||
            return assert_default_const_axiom(to_app(child));
 | 
			
		||||
| 
						 | 
				
			
			@ -79,30 +83,47 @@ namespace array {
 | 
			
		|||
        solver& s;
 | 
			
		||||
        unsigned m_idx;
 | 
			
		||||
        set_delay_bit(solver& s, unsigned idx) : s(s), m_idx(idx) {}
 | 
			
		||||
        void undo(/*euf::solver& euf*/) override {
 | 
			
		||||
        void undo() override {
 | 
			
		||||
            s.m_axiom_trail[m_idx].m_delayed = 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();
 | 
			
		||||
        SASSERT(a.is_select(select));
 | 
			
		||||
        SASSERT(can_beta_reduce(r.n));
 | 
			
		||||
        if (!ctx.is_relevant(child))
 | 
			
		||||
            return false;
 | 
			
		||||
        for (unsigned i = 1; i < select->get_num_args(); ++i)
 | 
			
		||||
            if (!ctx.is_relevant(select->get_arg(i)))
 | 
			
		||||
                return false;
 | 
			
		||||
        TRACE("array", tout << "select-axiom: " << mk_bounded_pp(select, m, 2) << " " << mk_bounded_pp(child, m, 2) << "\n";);
 | 
			
		||||
        if (get_config().m_array_delay_exp_axiom && r.select->get_arg(0)->get_root() != r.n->get_root() && !r.m_delayed) {
 | 
			
		||||
        TRACE("array", display(tout << "select-axiom: ", r) << "\n";);
 | 
			
		||||
 | 
			
		||||
        if (get_config().m_array_delay_exp_axiom && r.select->get_arg(0)->get_root() != r.n->get_root() && !r.m_delayed && m_enable_delay) {
 | 
			
		||||
            IF_VERBOSE(11, verbose_stream() << "delay: " << mk_bounded_pp(child, m) << " " << mk_bounded_pp(select, m) << "\n");
 | 
			
		||||
            ctx.push(set_delay_bit(*this, idx));
 | 
			
		||||
            r.m_delayed = true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        if (r.select->get_arg(0)->get_root() != r.n->get_root() && r.m_delayed)
 | 
			
		||||
            return false;
 | 
			
		||||
        r.m_delayed = false;
 | 
			
		||||
        if (a.is_const(child))
 | 
			
		||||
            return assert_select_const_axiom(select, to_app(child));
 | 
			
		||||
        else if (a.is_as_array(child))
 | 
			
		||||
| 
						 | 
				
			
			@ -163,8 +184,13 @@ namespace array {
 | 
			
		|||
        expr_ref sel_eq_e(m.mk_eq(sel1, sel2), m);
 | 
			
		||||
        euf::enode* s1 = e_internalize(sel1);
 | 
			
		||||
        euf::enode* s2 = e_internalize(sel2);
 | 
			
		||||
        TRACE("array", 
 | 
			
		||||
              tout << "select-store " << ctx.bpp(s1) << " " << ctx.bpp(s1->get_root()) << "\n";
 | 
			
		||||
              tout << "select-store " << ctx.bpp(s2) << " " << ctx.bpp(s2->get_root()) << "\n";);
 | 
			
		||||
 | 
			
		||||
        if (s1->get_root() == s2->get_root())
 | 
			
		||||
            return false;
 | 
			
		||||
 | 
			
		||||
        sat::literal sel_eq = mk_literal(sel_eq_e);
 | 
			
		||||
        if (s().value(sel_eq) == l_true)
 | 
			
		||||
            return false;
 | 
			
		||||
| 
						 | 
				
			
			@ -173,8 +199,8 @@ namespace array {
 | 
			
		|||
        for (unsigned i = 1; i < num_args; i++) {
 | 
			
		||||
            expr* idx1 = store->get_arg(i);
 | 
			
		||||
            expr* idx2 = select->get_arg(i);
 | 
			
		||||
            euf::enode* r1 = expr2enode(idx1)->get_root();
 | 
			
		||||
            euf::enode* r2 = expr2enode(idx2)->get_root();
 | 
			
		||||
            euf::enode* r1 = expr2enode(idx1);
 | 
			
		||||
            euf::enode* r2 = expr2enode(idx2);
 | 
			
		||||
            if (r1 == r2)
 | 
			
		||||
                continue;
 | 
			
		||||
            if (m.are_distinct(r1->get_expr(), r2->get_expr())) {
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +212,7 @@ namespace array {
 | 
			
		|||
            if (add_clause(idx_eq, sel_eq))
 | 
			
		||||
                new_prop = true;
 | 
			
		||||
        }
 | 
			
		||||
        TRACE("array", tout << "select-stored " << new_prop << "\n";);
 | 
			
		||||
        return new_prop;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -483,9 +510,11 @@ namespace array {
 | 
			
		|||
        bool change = false;
 | 
			
		||||
        unsigned sz = m_axiom_trail.size();
 | 
			
		||||
        m_delay_qhead = 0;
 | 
			
		||||
        
 | 
			
		||||
        for (; m_delay_qhead < sz; ++m_delay_qhead) 
 | 
			
		||||
            if (m_axiom_trail[m_delay_qhead].m_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;
 | 
			
		||||
        return change;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										138
									
								
								src/sat/smt/array_diagnostics.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/sat/smt/array_diagnostics.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,138 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2020 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    array_diagnostics.cpp
 | 
			
		||||
 | 
			
		||||
Abstract:
 | 
			
		||||
 | 
			
		||||
    Theory plugin for arrays, diagnostics functions
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2020-09-08
 | 
			
		||||
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
#include "sat/smt/array_solver.h"
 | 
			
		||||
#include "sat/smt/euf_solver.h"
 | 
			
		||||
 | 
			
		||||
namespace array {
 | 
			
		||||
    std::ostream& solver::display(std::ostream& out) const {
 | 
			
		||||
        if (get_num_vars() > 0)
 | 
			
		||||
            out << "array\n";
 | 
			
		||||
        for (unsigned i = 0; i < get_num_vars(); ++i) {
 | 
			
		||||
            auto& d = get_var_data(i);
 | 
			
		||||
            out << var2enode(i)->get_expr_id() << " " << (d.m_prop_upward?"up":"fx") << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
 | 
			
		||||
            display_info(out, "parent lambdas", d.m_parent_lambdas);
 | 
			
		||||
            display_info(out, "parent select", d.m_parent_selects);
 | 
			
		||||
            display_info(out, "lambdas", d.m_lambdas);
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& solver::display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const {
 | 
			
		||||
        if (v.empty())
 | 
			
		||||
            return out;
 | 
			
		||||
        out << id << ":\n";
 | 
			
		||||
        for (euf::enode* p : v)
 | 
			
		||||
            out << "   " << ctx.bpp(p) << "\n";
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& solver::display(std::ostream& out, axiom_record const& r) const {
 | 
			
		||||
        if (r.is_delayed())
 | 
			
		||||
            out << "delay ";
 | 
			
		||||
        switch (r.m_kind) {
 | 
			
		||||
        case axiom_record::kind_t::is_store:
 | 
			
		||||
            return out << "store " << ctx.bpp(r.n);            
 | 
			
		||||
        case axiom_record::kind_t::is_select:
 | 
			
		||||
            return out << "select " << ctx.bpp(r.n) << " " << ctx.bpp(r.select);
 | 
			
		||||
        case axiom_record::kind_t::is_default:
 | 
			
		||||
            return out << "default " << ctx.bpp(r.n);
 | 
			
		||||
        case axiom_record::kind_t::is_extensionality:
 | 
			
		||||
            return out << "extensionality " << ctx.bpp(r.n) << " " << ctx.bpp(r.select);
 | 
			
		||||
        case axiom_record::kind_t::is_congruence:
 | 
			
		||||
            return out << "congruence " << ctx.bpp(r.n) << " " << ctx.bpp(r.select);
 | 
			
		||||
        default:
 | 
			
		||||
            UNREACHABLE();
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; }
 | 
			
		||||
    std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; }
 | 
			
		||||
 | 
			
		||||
    void solver::collect_statistics(statistics& st) const {
 | 
			
		||||
        st.update("array store",        m_stats.m_num_store_axiom);
 | 
			
		||||
        st.update("array sel/store",    m_stats.m_num_select_store_axiom);
 | 
			
		||||
        st.update("array sel/const",    m_stats.m_num_select_const_axiom);
 | 
			
		||||
        st.update("array sel/map",      m_stats.m_num_select_map_axiom);
 | 
			
		||||
        st.update("array sel/as array", m_stats.m_num_select_as_array_axiom);
 | 
			
		||||
        st.update("array sel/lambda",   m_stats.m_num_select_lambda_axiom);
 | 
			
		||||
        st.update("array def/map",      m_stats.m_num_default_map_axiom);
 | 
			
		||||
        st.update("array def/const",    m_stats.m_num_default_const_axiom);
 | 
			
		||||
        st.update("array def/store",    m_stats.m_num_default_store_axiom);
 | 
			
		||||
        st.update("array ext ax",       m_stats.m_num_extensionality_axiom);
 | 
			
		||||
        st.update("array cong ax",      m_stats.m_num_congruence_axiom);        
 | 
			
		||||
        st.update("array exp ax2",      m_stats.m_num_select_store_axiom_delayed);
 | 
			
		||||
        st.update("array splits",       m_stats.m_num_eq_splits);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::validate_check() const {
 | 
			
		||||
        for (euf::enode* n : ctx.get_egraph().nodes()) {
 | 
			
		||||
            if (!ctx.is_relevant(n)) 
 | 
			
		||||
                continue;
 | 
			
		||||
            if (a.is_select(n->get_expr()) && a.is_store(n->get_arg(0)->get_expr()))
 | 
			
		||||
                validate_select_store(n);
 | 
			
		||||
            if (is_array(n) && n->is_root() && ctx.is_shared(n)) {
 | 
			
		||||
                for (euf::enode* k : ctx.get_egraph().nodes())
 | 
			
		||||
                    if (k->get_expr_id() > n->get_expr_id() && k->is_root() && is_array(k) && ctx.is_shared(k))
 | 
			
		||||
                        validate_extensionality(n, k);
 | 
			
		||||
            }
 | 
			
		||||
            expr* x = nullptr, *y = nullptr;
 | 
			
		||||
            if (m.is_eq(n->get_expr(), x, y) && a.is_array(x))
 | 
			
		||||
                std::cout << ctx.bpp(n) << " " << s().value(n->bool_var()) << "\n";
 | 
			
		||||
            if (m.is_eq(n->get_expr(), x, y) && a.is_array(x) && s().value(n->bool_var()) == l_false)
 | 
			
		||||
                validate_extensionality(expr2enode(x), expr2enode(y));
 | 
			
		||||
        }        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void solver::validate_select_store(euf::enode* n) const {
 | 
			
		||||
        SASSERT(a.is_select(n->get_expr()));
 | 
			
		||||
        SASSERT(a.is_store(n->get_arg(0)->get_expr()));
 | 
			
		||||
        bool same_args = true;
 | 
			
		||||
        for (unsigned i = 1; same_args && i < n->num_args(); ++i) 
 | 
			
		||||
            same_args = n->get_arg(i)->get_root() == n->get_arg(0)->get_arg(i)->get_root(); 
 | 
			
		||||
        if (same_args) {
 | 
			
		||||
            VERIFY(n->get_arg(0)->get_arg(n->num_args())->get_root() == n->get_root());
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
        euf::enode_vector args;
 | 
			
		||||
        ptr_vector<expr> eargs;
 | 
			
		||||
        args.push_back(n->get_arg(0)->get_arg(0));
 | 
			
		||||
        for (unsigned i = 1; i < n->num_args(); ++i)
 | 
			
		||||
            args.push_back(n->get_arg(i));
 | 
			
		||||
        for (euf::enode* n : args)
 | 
			
		||||
            eargs.push_back(n->get_expr());
 | 
			
		||||
        expr_ref sel(a.mk_select(eargs.size(), eargs.c_ptr()), m);
 | 
			
		||||
        euf::enode* n1 = ctx.get_egraph().find(sel, args.size(), args.c_ptr());
 | 
			
		||||
        if (n1 && n1->get_root() == n->get_root())
 | 
			
		||||
            return;
 | 
			
		||||
        IF_VERBOSE(0, 
 | 
			
		||||
                   verbose_stream() << ctx.bpp(n) << "\n";
 | 
			
		||||
                   verbose_stream() << sel << "\n";
 | 
			
		||||
                   verbose_stream() << n1 << " " << n->get_root() << "\n";);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::validate_extensionality(euf::enode* s, euf::enode* t) const {
 | 
			
		||||
        if (s->get_sort() != t->get_sort())
 | 
			
		||||
            return;
 | 
			
		||||
        IF_VERBOSE(0, 
 | 
			
		||||
                   verbose_stream() << "extensionality " << ctx.bpp(s) << " " << ctx.bpp(t) << "\n";);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -3,7 +3,7 @@ Copyright (c) 2020 Microsoft Corporation
 | 
			
		|||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    array_solver.h
 | 
			
		||||
    array_solver.cpp
 | 
			
		||||
 | 
			
		||||
Abstract:
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -101,6 +101,10 @@ namespace array {
 | 
			
		|||
            else if (!turn[idx] && add_interface_equalities())
 | 
			
		||||
                return sat::check_result::CR_CONTINUE;
 | 
			
		||||
        }
 | 
			
		||||
        if (m_delay_qhead < m_axiom_trail.size()) 
 | 
			
		||||
            return sat::check_result::CR_CONTINUE;
 | 
			
		||||
            
 | 
			
		||||
        // validate_check();
 | 
			
		||||
        return sat::check_result::CR_DONE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -109,48 +113,6 @@ namespace array {
 | 
			
		|||
        m_var_data.resize(get_num_vars());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& solver::display(std::ostream& out) const {
 | 
			
		||||
        if (get_num_vars() > 0)
 | 
			
		||||
            out << "array\n";
 | 
			
		||||
        for (unsigned i = 0; i < get_num_vars(); ++i) {
 | 
			
		||||
            auto& d = get_var_data(i);
 | 
			
		||||
            out << var2enode(i)->get_expr_id() << " " << mk_bounded_pp(var2expr(i), m, 2) << "\n";
 | 
			
		||||
            display_info(out, "parent lambdas", d.m_parent_lambdas);
 | 
			
		||||
            display_info(out, "parent select",  d.m_parent_selects);
 | 
			
		||||
            display_info(out, "lambdas",        d.m_lambdas);
 | 
			
		||||
        }
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& solver::display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const {
 | 
			
		||||
        if (v.empty())
 | 
			
		||||
            return out;
 | 
			
		||||
        out << id << ": ";
 | 
			
		||||
        for (euf::enode* p : v)
 | 
			
		||||
            out << mk_bounded_pp(p->get_expr(), m, 2) << " ";
 | 
			
		||||
        out << "\n";
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const { return out; }
 | 
			
		||||
    std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const { return out; }
 | 
			
		||||
 | 
			
		||||
    void solver::collect_statistics(statistics& st) const {
 | 
			
		||||
        st.update("array store",        m_stats.m_num_store_axiom);
 | 
			
		||||
        st.update("array sel/store",    m_stats.m_num_select_store_axiom);
 | 
			
		||||
        st.update("array sel/const",    m_stats.m_num_select_const_axiom);
 | 
			
		||||
        st.update("array sel/map",      m_stats.m_num_select_map_axiom);
 | 
			
		||||
        st.update("array sel/as array", m_stats.m_num_select_as_array_axiom);
 | 
			
		||||
        st.update("array sel/lambda",   m_stats.m_num_select_lambda_axiom);
 | 
			
		||||
        st.update("array def/map",      m_stats.m_num_default_map_axiom);
 | 
			
		||||
        st.update("array def/const",    m_stats.m_num_default_const_axiom);
 | 
			
		||||
        st.update("array def/store",    m_stats.m_num_default_store_axiom);
 | 
			
		||||
        st.update("array ext ax",       m_stats.m_num_extensionality_axiom);
 | 
			
		||||
        st.update("array cong ax",      m_stats.m_num_congruence_axiom);        
 | 
			
		||||
        st.update("array exp ax2",      m_stats.m_num_select_store_axiom_delayed);
 | 
			
		||||
        st.update("array splits",       m_stats.m_num_eq_splits);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    euf::th_solver* solver::clone(euf::solver& dst_ctx) {
 | 
			
		||||
        auto* result = alloc(solver, dst_ctx, get_id());
 | 
			
		||||
        for (unsigned i = 0; i < get_num_vars(); ++i)          
 | 
			
		||||
| 
						 | 
				
			
			@ -186,6 +148,7 @@ namespace array {
 | 
			
		|||
    void solver::merge_eh(theory_var v1, theory_var v2, theory_var, theory_var) {
 | 
			
		||||
        euf::enode* n1 = var2enode(v1);
 | 
			
		||||
        euf::enode* n2 = var2enode(v2);
 | 
			
		||||
        TRACE("array", tout << "merge: " << ctx.bpp(n1) << " == " << ctx.bpp(n2) << "\n";);
 | 
			
		||||
        SASSERT(n1->get_root() == n2->get_root());
 | 
			
		||||
        SASSERT(v1 == find(v1));
 | 
			
		||||
        expr* e1 = n1->get_expr();
 | 
			
		||||
| 
						 | 
				
			
			@ -207,11 +170,11 @@ namespace array {
 | 
			
		|||
    void solver::add_parent_select(theory_var v_child, euf::enode* select) {
 | 
			
		||||
        SASSERT(a.is_select(select->get_expr()));
 | 
			
		||||
        SASSERT(select->get_arg(0)->get_sort() == var2expr(v_child)->get_sort());
 | 
			
		||||
 | 
			
		||||
        v_child = find(v_child);
 | 
			
		||||
        ctx.push_vec(get_var_data(v_child).m_parent_selects, select);
 | 
			
		||||
        euf::enode* child = var2enode(v_child);
 | 
			
		||||
        if (can_beta_reduce(child) && child != select->get_arg(0))
 | 
			
		||||
        TRACE("array", tout << "v" << v_child << " - " << ctx.bpp(select) << " " << ctx.bpp(child) << " prop: " << should_prop_upward(get_var_data(v_child)) << "\n";);
 | 
			
		||||
        if (can_beta_reduce(child)) 
 | 
			
		||||
            push_axiom(select_axiom(select, child));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -260,7 +223,9 @@ namespace array {
 | 
			
		|||
        expr* e = var2expr(v);
 | 
			
		||||
        if (!a.is_array(e))
 | 
			
		||||
            return;
 | 
			
		||||
        
 | 
			
		||||
        auto& d = get_var_data(v);
 | 
			
		||||
 | 
			
		||||
        for (euf::enode* lambda : d.m_parent_lambdas)
 | 
			
		||||
            propagate_select_axioms(d, lambda);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,6 +96,8 @@ namespace array {
 | 
			
		|||
            bool        m_delayed { false };
 | 
			
		||||
            axiom_record(kind_t k, euf::enode* n, euf::enode* select = nullptr) : m_kind(k), n(n), select(select) {}
 | 
			
		||||
 | 
			
		||||
            bool is_delayed() const { return m_delayed; }
 | 
			
		||||
 | 
			
		||||
            struct hash {
 | 
			
		||||
                solver& s;
 | 
			
		||||
                hash(solver& s) :s(s) {}
 | 
			
		||||
| 
						 | 
				
			
			@ -122,12 +124,14 @@ namespace array {
 | 
			
		|||
        svector<axiom_record> m_axiom_trail;
 | 
			
		||||
        unsigned              m_qhead { 0 };
 | 
			
		||||
        unsigned              m_delay_qhead { 0 };
 | 
			
		||||
        bool                  m_enable_delay { true };
 | 
			
		||||
        struct set_delay_bit;
 | 
			
		||||
        void push_axiom(axiom_record const& r);
 | 
			
		||||
        bool propagate_axiom(unsigned idx);
 | 
			
		||||
        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;
 | 
			
		||||
 | 
			
		||||
        axiom_record select_axiom(euf::enode* s, euf::enode* n) { return axiom_record(axiom_record::kind_t::is_select, n, s); }
 | 
			
		||||
        axiom_record default_axiom(euf::enode* n) { return axiom_record(axiom_record::kind_t::is_default, n); }
 | 
			
		||||
| 
						 | 
				
			
			@ -188,7 +192,11 @@ namespace array {
 | 
			
		|||
        bool have_different_model_values(theory_var v1, theory_var v2);
 | 
			
		||||
 | 
			
		||||
        // diagnostics
 | 
			
		||||
        std::ostream& display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const;
 | 
			
		||||
        std::ostream& display_info(std::ostream& out, char const* id, euf::enode_vector const& v) const; 
 | 
			
		||||
        std::ostream& display(std::ostream& out, axiom_record const& r) const;
 | 
			
		||||
        void validate_check() const;
 | 
			
		||||
        void validate_select_store(euf::enode* n) const;
 | 
			
		||||
        void validate_extensionality(euf::enode* s, euf::enode* t) const;
 | 
			
		||||
    public:
 | 
			
		||||
        solver(euf::solver& ctx, theory_id id);
 | 
			
		||||
        ~solver() override;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -236,13 +236,16 @@ namespace euf {
 | 
			
		|||
            expr* e = n->get_expr();
 | 
			
		||||
            if (!m.is_bool(e))
 | 
			
		||||
                continue;
 | 
			
		||||
            unsigned id = n->get_root_id();
 | 
			
		||||
            if (!m_values.get(id))
 | 
			
		||||
            if (!is_relevant(n))
 | 
			
		||||
                continue;
 | 
			
		||||
            bool tt = m.is_true(m_values.get(id));
 | 
			
		||||
            if (mdl.is_true(e) != tt) {
 | 
			
		||||
                IF_VERBOSE(0, verbose_stream() << "Failed to evaluate " << id << " " << mk_bounded_pp(e, m) << " " << mdl(e) << " " << mk_bounded_pp(m_values.get(id), m) << "\n");
 | 
			
		||||
            bool tt = l_true == s().value(n->bool_var());
 | 
			
		||||
            if (tt && mdl.is_false(e)) {
 | 
			
		||||
                IF_VERBOSE(0, verbose_stream() << "Failed to validate " << bpp(n) << " " << mdl(e) << "\n");
 | 
			
		||||
                for (auto* arg : euf::enode_args(n))
 | 
			
		||||
                    IF_VERBOSE(0, verbose_stream() << bpp(arg) << "\n" << mdl(arg->get_expr()) << "\n");
 | 
			
		||||
            }
 | 
			
		||||
            if (!tt && mdl.is_true(e))
 | 
			
		||||
                IF_VERBOSE(0, verbose_stream() << "Failed to validate " << bpp(n) << " " << mdl(e) << "\n");
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -359,6 +359,7 @@ namespace euf {
 | 
			
		|||
        bool relevancy_enabled() const { return get_config().m_relevancy_lvl > 0; }
 | 
			
		||||
        void add_root(unsigned n, sat::literal const* lits);
 | 
			
		||||
        void add_aux(unsigned n, sat::literal const* lits);
 | 
			
		||||
        void add_aux(sat::literal a, sat::literal b) { sat::literal lits[2] = {a, b}; add_aux(2, lits); }
 | 
			
		||||
        void track_relevancy(sat::bool_var v);
 | 
			
		||||
        bool is_relevant(expr* e) const;
 | 
			
		||||
        bool is_relevant(enode* n) const;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -59,23 +59,6 @@ struct theory_array_params {
 | 
			
		|||
 | 
			
		||||
    void updt_params(params_ref const & _p);
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
    void register_params(ini_params & p) {
 | 
			
		||||
        p.register_int_param("array_solver", 0, 3, reinterpret_cast<int&>(m_array_mode), "0 - no array, 1 - simple, 2 - model based, 3 - full");
 | 
			
		||||
        p.register_bool_param("array_weak", m_array_weak);
 | 
			
		||||
        p.register_bool_param("array_extensional", m_array_extensional);
 | 
			
		||||
        p.register_unsigned_param("array_laziness", m_array_laziness);
 | 
			
		||||
        p.register_bool_param("array_delay_exp_axiom", m_array_delay_exp_axiom);
 | 
			
		||||
        p.register_bool_param("array_cg", m_array_cg);
 | 
			
		||||
        p.register_bool_param("array_always_prop_upward", m_array_always_prop_upward, 
 | 
			
		||||
                              "Disable the built-in filter upwards propagation");
 | 
			
		||||
        p.register_bool_param("array_lazy_ieq", m_array_lazy_ieq);
 | 
			
		||||
        p.register_unsigned_param("array_lazy_ieq_delay", m_array_lazy_ieq_delay);
 | 
			
		||||
        p.register_bool_param("array_canonize", m_array_canonize_simplify, 
 | 
			
		||||
                              "Normalize arrays into normal form during simplification");
 | 
			
		||||
    }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
    void display(std::ostream & out) const;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue