mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-03 21:09:11 +00:00 
			
		
		
		
	mk_extract
This commit is contained in:
		
							parent
							
								
									69e54b62c5
								
							
						
					
					
						commit
						73757e3fa4
					
				
					 3 changed files with 100 additions and 69 deletions
				
			
		| 
						 | 
				
			
			@ -157,6 +157,7 @@ namespace polysat {
 | 
			
		|||
            switch (m_trail.back()) {
 | 
			
		||||
            case trail_item::add_var:       undo_add_var();     break;
 | 
			
		||||
            case trail_item::split_core:    undo_split_core();  break;
 | 
			
		||||
            case trail_item::mk_extract:    undo_mk_extract();  break;
 | 
			
		||||
            default: UNREACHABLE();
 | 
			
		||||
            }
 | 
			
		||||
            m_trail.pop_back();
 | 
			
		||||
| 
						 | 
				
			
			@ -644,49 +645,56 @@ namespace polysat {
 | 
			
		|||
        get_base_core<true>(src, out_base);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pvar slicing::mk_slice_extract(enode* src, unsigned hi, unsigned lo) {
 | 
			
		||||
        enode_vector slices;
 | 
			
		||||
    pvar slicing::mk_extract(enode* src, unsigned hi, unsigned lo) {
 | 
			
		||||
        enode_vector& slices = m_tmp3;
 | 
			
		||||
        SASSERT(slices.empty());
 | 
			
		||||
        mk_slice(src, hi, lo, slices, false, true);
 | 
			
		||||
        pvar v = null_var;
 | 
			
		||||
        // try to re-use variable of an existing slice
 | 
			
		||||
        if (slices.size() == 1)
 | 
			
		||||
            v = slice2var(slices[0]);
 | 
			
		||||
        // allocate new variable if we cannot reuse it
 | 
			
		||||
        if (v == null_var)
 | 
			
		||||
            v = m_solver.add_var(hi - lo + 1);
 | 
			
		||||
#if 0
 | 
			
		||||
        // slice didn't have a variable yet; so we can re-use it for the new variable
 | 
			
		||||
        // (we end up with a "phantom" enode that was first created for the variable)
 | 
			
		||||
        if (slices.size() == 1) {
 | 
			
		||||
            enode* s = slices[0];
 | 
			
		||||
            if (slice2var(s) != null_var)
 | 
			
		||||
                return slice2var(s);
 | 
			
		||||
            // TODO: optimization: could save a slice-tree by directly assigning slice2var(s) = v for new var v.
 | 
			
		||||
            SASSERT_EQ(info(s).var, null_var);
 | 
			
		||||
            info(m_var2slice[v]).var = null_var;  // disconnect the "phantom" enode
 | 
			
		||||
            info(s).var = v;
 | 
			
		||||
            m_var2slice[v] = s;
 | 
			
		||||
        }
 | 
			
		||||
        pvar v = m_solver.add_var(hi - lo + 1);
 | 
			
		||||
        // TODO: can we use 'compressed' slice trees again if we store the source slice here as dependency?
 | 
			
		||||
#endif
 | 
			
		||||
        // connect new variable
 | 
			
		||||
        VERIFY(merge(slices, var2slice(v), dep_t()));
 | 
			
		||||
        slices.reset();
 | 
			
		||||
        return v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pvar slicing::mk_extract_var(pvar src, unsigned hi, unsigned lo) {
 | 
			
		||||
        return mk_slice_extract(var2slice(src), hi, lo);
 | 
			
		||||
    pvar slicing::mk_extract(pvar src, unsigned hi, unsigned lo) {
 | 
			
		||||
        extract_args args{src, hi, lo};
 | 
			
		||||
        auto it = m_extract_dedup.find_iterator(args);
 | 
			
		||||
        if (it != m_extract_dedup.end())
 | 
			
		||||
            return it->m_value;
 | 
			
		||||
        pvar const v = mk_extract(var2slice(src), hi, lo);
 | 
			
		||||
        m_extract_dedup.insert(args, v);
 | 
			
		||||
        m_extract_trail.push_back(args);
 | 
			
		||||
        m_trail.push_back(trail_item::mk_extract);
 | 
			
		||||
        return v;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pdd slicing::mk_extract(pvar src, unsigned hi, unsigned lo) {
 | 
			
		||||
        return m_solver.var(mk_extract_var(src, hi, lo));
 | 
			
		||||
    void slicing::undo_mk_extract() {
 | 
			
		||||
        extract_args args = m_extract_trail.back();
 | 
			
		||||
        m_extract_trail.pop_back();
 | 
			
		||||
        m_extract_dedup.remove(args);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pdd slicing::mk_extract(pdd const& p, unsigned hi, unsigned lo) {
 | 
			
		||||
        SASSERT(hi >= lo);
 | 
			
		||||
        SASSERT(p.power_of_2() > hi);
 | 
			
		||||
        if (p.is_val()) {
 | 
			
		||||
            // p[hi:lo] = (p >> lo) % 2^(hi - lo + 1)
 | 
			
		||||
            rational q = mod2k(machine_div2k(p.val(), lo), hi - lo + 1);
 | 
			
		||||
            return p.manager().mk_val(q);
 | 
			
		||||
        }
 | 
			
		||||
        if (!lo) {
 | 
			
		||||
            // TODO: we could push the extract down into variables of the term instead of introducing a name.
 | 
			
		||||
        }
 | 
			
		||||
        return m_solver.var(mk_slice_extract(pdd2slice(p), hi, lo));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    slicing::enode* slicing::pdd2slice(pdd const& p) {
 | 
			
		||||
        pvar const v = m_solver.m_names.mk_name(p);
 | 
			
		||||
        return var2slice(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pdd slicing::mk_concat(pdd const& p, pdd const& q) {
 | 
			
		||||
    pvar slicing::mk_concat(unsigned num_args, pvar const* args) {
 | 
			
		||||
        NOT_IMPLEMENTED_YET();
 | 
			
		||||
        return null_var;
 | 
			
		||||
#if 0
 | 
			
		||||
        // v := p ++ q      (new variable of size |p| + |q|)
 | 
			
		||||
        // v[:|q|] = p
 | 
			
		||||
        // v[|q|:] = q
 | 
			
		||||
| 
						 | 
				
			
			@ -707,6 +715,11 @@ namespace polysat {
 | 
			
		|||
        tmp.push_back(pdd2slice(q));
 | 
			
		||||
        VERIFY(merge(tmp, var2slice(v), dep_t()));
 | 
			
		||||
        return m_solver.var(v);
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    pvar slicing::mk_concat(std::initializer_list<pvar> args) {
 | 
			
		||||
        return mk_concat(args.size(), args.begin());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void slicing::add_constraint(signed_constraint c) {
 | 
			
		||||
| 
						 | 
				
			
			@ -838,9 +851,18 @@ namespace polysat {
 | 
			
		|||
            // properties below only matter for representatives
 | 
			
		||||
            if (!s->is_root())
 | 
			
		||||
                continue;
 | 
			
		||||
            enode_vector s_base;
 | 
			
		||||
            get_base(s, s_base);
 | 
			
		||||
            for (enode* n : euf::enode_class(s)) {
 | 
			
		||||
                // equivalence class only contains slices of equal length
 | 
			
		||||
                VERIFY_EQ(width(s), width(n));
 | 
			
		||||
                // bases of equivalent nodes are equivalent
 | 
			
		||||
                enode_vector n_base;
 | 
			
		||||
                get_base(n, n_base);
 | 
			
		||||
                VERIFY_EQ(s_base.size(), n_base.size());
 | 
			
		||||
                for (unsigned i = s_base.size(); i-- > 0; ) {
 | 
			
		||||
                    VERIFY_EQ(s_base[i]->get_root(), n_base[i]->get_root());
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -189,16 +189,32 @@ namespace polysat {
 | 
			
		|||
        // Check whether two slices are known to be equal
 | 
			
		||||
        bool is_equal(enode* x, enode* y);
 | 
			
		||||
 | 
			
		||||
        enum class trail_item {
 | 
			
		||||
        // deduplication of extract terms
 | 
			
		||||
        struct extract_args {
 | 
			
		||||
            pvar src;
 | 
			
		||||
            unsigned hi;
 | 
			
		||||
            unsigned lo;
 | 
			
		||||
            bool operator==(extract_args const& other) const { return src == other.src && hi == other.hi && lo == other.lo; }
 | 
			
		||||
            unsigned hash() const { return mk_mix(src, hi, lo); }
 | 
			
		||||
        };
 | 
			
		||||
        using extract_args_eq = default_eq<extract_args>;
 | 
			
		||||
        using extract_args_hash = obj_hash<extract_args>;
 | 
			
		||||
        using extract_map = map<extract_args, pvar, extract_args_hash, extract_args_eq>;
 | 
			
		||||
        extract_map m_extract_dedup;
 | 
			
		||||
 | 
			
		||||
        enum class trail_item : std::uint8_t {
 | 
			
		||||
            add_var,
 | 
			
		||||
            split_core,
 | 
			
		||||
            mk_extract,
 | 
			
		||||
        };
 | 
			
		||||
        svector<trail_item> m_trail;
 | 
			
		||||
        enode_vector        m_split_trail;
 | 
			
		||||
        svector<extract_args> m_extract_trail;
 | 
			
		||||
        unsigned_vector     m_scopes;
 | 
			
		||||
 | 
			
		||||
        void undo_add_var();
 | 
			
		||||
        void undo_split_core();
 | 
			
		||||
        void undo_mk_extract();
 | 
			
		||||
 | 
			
		||||
        mutable enode_vector m_tmp1;
 | 
			
		||||
        mutable enode_vector m_tmp2;
 | 
			
		||||
| 
						 | 
				
			
			@ -207,18 +223,12 @@ namespace polysat {
 | 
			
		|||
        sat::literal_set     m_marked_lits;
 | 
			
		||||
        uint_set             m_marked_vars;
 | 
			
		||||
 | 
			
		||||
        // get a slice that is equivalent to the given pdd (may introduce new variable)
 | 
			
		||||
        enode* pdd2slice(pdd const& p);
 | 
			
		||||
 | 
			
		||||
        /** Get variable representing src[hi:lo] */
 | 
			
		||||
        pvar mk_slice_extract(enode* src, unsigned hi, unsigned lo);
 | 
			
		||||
        pvar mk_extract(enode* src, unsigned hi, unsigned lo);
 | 
			
		||||
 | 
			
		||||
        bool invariant() const;
 | 
			
		||||
        bool invariant_needs_congruence() const;
 | 
			
		||||
 | 
			
		||||
        /** Get variable representing x[hi:lo] */
 | 
			
		||||
        pvar mk_extract_var(pvar x, unsigned hi, unsigned lo);
 | 
			
		||||
 | 
			
		||||
        std::ostream& display(std::ostream& out, enode* s) const;
 | 
			
		||||
        std::ostream& display_tree(std::ostream& out, enode* s, unsigned indent, unsigned hi, unsigned lo) const;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -230,14 +240,12 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
        void add_var(unsigned bit_width);
 | 
			
		||||
 | 
			
		||||
        /** Create expression for x[hi:lo] */
 | 
			
		||||
        pdd mk_extract(pvar x, unsigned hi, unsigned lo);
 | 
			
		||||
        /** Get or create variable representing x[hi:lo] */
 | 
			
		||||
        pvar mk_extract(pvar x, unsigned hi, unsigned lo);
 | 
			
		||||
 | 
			
		||||
        /** Create expression for p[hi:lo] */
 | 
			
		||||
        pdd mk_extract(pdd const& p, unsigned hi, unsigned lo);
 | 
			
		||||
 | 
			
		||||
        /** Create expression for p ++ q */
 | 
			
		||||
        pdd mk_concat(pdd const& p, pdd const& q);
 | 
			
		||||
        /** Get or create variable representing x1 ++ x2 ++ ... ++ xn */
 | 
			
		||||
        pvar mk_concat(unsigned num_args, pvar const* args);
 | 
			
		||||
        pvar mk_concat(std::initializer_list<pvar> args);
 | 
			
		||||
 | 
			
		||||
        // Track value assignments to variables (and propagate to subslices)
 | 
			
		||||
        // (could generalize to fixed bits, then we need a way to merge interpreted enodes)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -82,13 +82,13 @@ namespace polysat {
 | 
			
		|||
            pvar x = s.add_var(8);
 | 
			
		||||
            pvar y = s.add_var(8);
 | 
			
		||||
 | 
			
		||||
            pvar a = sl.mk_extract_var(x, 7, 3);
 | 
			
		||||
            pvar a = sl.mk_extract(x, 7, 3);
 | 
			
		||||
            std::cout << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(x), sl.var2slice(y), sat::literal(1)));
 | 
			
		||||
            std::cout << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            pvar b = sl.mk_extract_var(y, 5, 0);
 | 
			
		||||
            pvar b = sl.mk_extract(y, 5, 0);
 | 
			
		||||
            std::cout << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            sl.display_tree(std::cout);
 | 
			
		||||
| 
						 | 
				
			
			@ -113,17 +113,17 @@ namespace polysat {
 | 
			
		|||
            pvar y = s.add_var(8);
 | 
			
		||||
            std::cout << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            pvar a = sl.mk_extract_var(x, 7, 3);
 | 
			
		||||
            pvar a = sl.mk_extract(x, 7, 3);
 | 
			
		||||
            std::cout << "v" << a << " := v" << x << "[7:3]\n" << sl << "\n";
 | 
			
		||||
            pvar b = sl.mk_extract_var(y, 5, 0);
 | 
			
		||||
            pvar b = sl.mk_extract(y, 5, 0);
 | 
			
		||||
            std::cout << "v" << b << " := v" << y << "[5:0]\n" << sl << "\n";
 | 
			
		||||
            pvar c = sl.mk_extract_var(x, 5, 0);
 | 
			
		||||
            pvar c = sl.mk_extract(x, 5, 0);
 | 
			
		||||
            std::cout << "v" << c << " := v" << x << "[5:0]\n" << sl << "\n";
 | 
			
		||||
            pdd d = sl.mk_concat(sl.mk_extract(x, 5, 4), sl.mk_extract(y, 3, 0));
 | 
			
		||||
            pvar d = sl.mk_concat({sl.mk_extract(x, 5, 4), sl.mk_extract(y, 3, 0)});
 | 
			
		||||
            std::cout << d << " := v" << x << "[5:4] ++ v" << y << "[3:0]\n" << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            std::cout << "v" << b << " = v" << c << "? " << sl.is_equal(sl.var2slice(b), sl.var2slice(c)) << "\n\n";
 | 
			
		||||
            std::cout << "v" << b << " = "  << d << "? " << sl.is_equal(sl.var2slice(b), sl.pdd2slice(d)) << "\n\n";
 | 
			
		||||
            std::cout << "v" << b << " = v" << d << "? " << sl.is_equal(sl.var2slice(b), sl.var2slice(d)) << "\n\n";
 | 
			
		||||
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(x), sl.var2slice(y), sat::literal(123)));
 | 
			
		||||
            std::cout << "v" << x << " = v" << y << "\n" << sl << "\n";
 | 
			
		||||
| 
						 | 
				
			
			@ -137,13 +137,13 @@ namespace polysat {
 | 
			
		|||
            sl.explain_equal(sl.var2slice(b), sl.var2slice(c), reason_lits, reason_vars);
 | 
			
		||||
            std::cout << "    Reason: " << reason_lits << " vars " << reason_vars << "\n";
 | 
			
		||||
 | 
			
		||||
            std::cout << "v" << b << " = "  << d << "? " << sl.is_equal(sl.var2slice(b), sl.pdd2slice(d))
 | 
			
		||||
            std::cout << "v" << b << " = v" << d << "? " << sl.is_equal(sl.var2slice(b), sl.var2slice(d))
 | 
			
		||||
                      << "    root(v" << b << ") = " << sl.var2slice(b)->get_root_id()
 | 
			
		||||
                      << "    root("  << d << ") = " << sl.pdd2slice(d)->get_root_id()
 | 
			
		||||
                      << "    root(v" << d << ") = " << sl.var2slice(d)->get_root_id()
 | 
			
		||||
                      << "\n";
 | 
			
		||||
            reason_lits.reset();
 | 
			
		||||
            reason_vars.reset();
 | 
			
		||||
            sl.explain_equal(sl.var2slice(b), sl.pdd2slice(d), reason_lits, reason_vars);
 | 
			
		||||
            sl.explain_equal(sl.var2slice(b), sl.var2slice(d), reason_lits, reason_vars);
 | 
			
		||||
            std::cout << "    Reason: " << reason_lits << " vars " << reason_vars << "\n";
 | 
			
		||||
 | 
			
		||||
            sl.display_tree(std::cout);
 | 
			
		||||
| 
						 | 
				
			
			@ -171,9 +171,13 @@ namespace polysat {
 | 
			
		|||
            pvar e = s.add_var(2);
 | 
			
		||||
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(a), sl.var2slice(b), sat::literal(101)));
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(d), sl.var2slice(sl.mk_extract_var(c, 1, 0)), sat::literal(102)));
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(c), sl.var2slice(sl.mk_extract_var(b, 3, 0)), sat::literal(103)));
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(e), sl.var2slice(sl.mk_extract_var(a, 1, 0)), sat::literal(104)));
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(d), sl.var2slice(sl.mk_extract(c, 1, 0)), sat::literal(102)));
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(c), sl.var2slice(sl.mk_extract(b, 3, 0)), sat::literal(103)));
 | 
			
		||||
            VERIFY(sl.merge(sl.var2slice(e), sl.var2slice(sl.mk_extract(a, 1, 0)), sat::literal(104)));
 | 
			
		||||
 | 
			
		||||
            // sl.propagate();
 | 
			
		||||
            sl.display_tree(std::cout);
 | 
			
		||||
            VERIFY(sl.invariant());
 | 
			
		||||
 | 
			
		||||
            std::cout << "v" << d << " = v" << e << "? " << sl.is_equal(sl.var2slice(d), sl.var2slice(e))
 | 
			
		||||
                      << "    root(v" << d << ") = " << sl.var2slice(d)->get_root_id()
 | 
			
		||||
| 
						 | 
				
			
			@ -185,9 +189,6 @@ namespace polysat {
 | 
			
		|||
            unsigned_vector reason_vars;
 | 
			
		||||
            sl.explain_equal(sl.var2slice(d), sl.var2slice(e), reason_lits, reason_vars);
 | 
			
		||||
            std::cout << "    Reason: " << reason_lits << " vars " << reason_vars << "\n";
 | 
			
		||||
 | 
			
		||||
            sl.display_tree(std::cout);
 | 
			
		||||
            VERIFY(sl.invariant());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // x[5:2] = y
 | 
			
		||||
| 
						 | 
				
			
			@ -201,9 +202,9 @@ namespace polysat {
 | 
			
		|||
            pvar x = s.add_var(6);
 | 
			
		||||
            std::cout << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            pvar y = sl.mk_extract_var(x, 5, 2);
 | 
			
		||||
            pvar y = sl.mk_extract(x, 5, 2);
 | 
			
		||||
            std::cout << "v" << y << " := v" << x << "[5:2]\n" << sl << "\n";
 | 
			
		||||
            pvar z = sl.mk_extract_var(x, 3, 0);
 | 
			
		||||
            pvar z = sl.mk_extract(x, 3, 0);
 | 
			
		||||
            std::cout << "v" << z << " := v" << x << "[3:0]\n" << sl << "\n";
 | 
			
		||||
 | 
			
		||||
            slicing::enode* nine = sl.mk_value_slice(rational(9), 4);
 | 
			
		||||
| 
						 | 
				
			
			@ -280,12 +281,12 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
void tst_slicing() {
 | 
			
		||||
    using namespace polysat;
 | 
			
		||||
    // test_slicing::test1();
 | 
			
		||||
    // test_slicing::test2();
 | 
			
		||||
    test_slicing::test1();
 | 
			
		||||
    test_slicing::test2();
 | 
			
		||||
    // test_slicing::test3();
 | 
			
		||||
    // test_slicing::test4();
 | 
			
		||||
    // test_slicing::test5();
 | 
			
		||||
    // test_slicing::test6();
 | 
			
		||||
    test_slicing::test4();
 | 
			
		||||
    test_slicing::test5();
 | 
			
		||||
    test_slicing::test6();
 | 
			
		||||
    test_slicing::test7();
 | 
			
		||||
    std::cout << "ok\n";
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue