mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	Update use of insert_eval and lemma scores to support propagation
This commit is contained in:
		
							parent
							
								
									fca4f18194
								
							
						
					
					
						commit
						85715eb164
					
				
					 6 changed files with 76 additions and 59 deletions
				
			
		| 
						 | 
				
			
			@ -75,13 +75,14 @@ namespace polysat {
 | 
			
		|||
        m_literals.push_back(c.blit());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clause_builder::insert_eval(sat::literal lit) {
 | 
			
		||||
        insert_eval(m_solver->lit2cnstr(lit));
 | 
			
		||||
    void clause_builder::insert_eval(sat::literal lit, bool status) {
 | 
			
		||||
        insert_eval(m_solver->lit2cnstr(lit), status);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void clause_builder::insert_eval(signed_constraint c) {
 | 
			
		||||
    void clause_builder::insert_eval(signed_constraint c, bool status) {
 | 
			
		||||
        if (c.bvalue(*m_solver) == l_undef) {
 | 
			
		||||
            m_solver->assign_eval(~c.blit());
 | 
			
		||||
            sat::literal lit = c.blit();
 | 
			
		||||
            m_solver->assign_eval(status ? lit : ~lit);
 | 
			
		||||
        }
 | 
			
		||||
        insert(c);
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,11 +51,17 @@ namespace polysat {
 | 
			
		|||
        void insert(signed_constraint c);
 | 
			
		||||
        void insert(inequality const& i) { insert(i.as_signed_constraint()); }
 | 
			
		||||
 | 
			
		||||
        /// Inserting constraints into the clause.
 | 
			
		||||
        /// If they are not yet on the search stack, add them as evaluated to false.
 | 
			
		||||
        /// \pre Constraint must be false in the current assignment.
 | 
			
		||||
        void insert_eval(sat::literal lit);
 | 
			
		||||
        void insert_eval(signed_constraint c);
 | 
			
		||||
        /// Insert constraints into the clause.
 | 
			
		||||
        /// If they are not yet on the search stack, add them as evaluated to the given status.
 | 
			
		||||
        /// \pre Constraint must evaluate to true or false according to the given status in the current assignment.
 | 
			
		||||
        void insert_eval(sat::literal lit, bool status);
 | 
			
		||||
        void insert_eval(signed_constraint c, bool status);
 | 
			
		||||
 | 
			
		||||
        /// Insert constraints into the clause.
 | 
			
		||||
        /// If they are not yet on the search stack, add them as evaluated to \b false.
 | 
			
		||||
        /// \pre Constraint must be \b false in the current assignment.
 | 
			
		||||
        void insert_eval(sat::literal lit) { insert_eval(lit, false); }
 | 
			
		||||
        void insert_eval(signed_constraint c) { insert_eval(c, false); }
 | 
			
		||||
        void insert_eval(inequality const& i) { insert_eval(i.as_signed_constraint()); }
 | 
			
		||||
 | 
			
		||||
        using const_iterator = decltype(m_literals)::const_iterator;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -96,8 +96,7 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
        SASSERT(all_of(m_lemma, [this](sat::literal lit) { return is_forced_false(s.lit2cnstr(lit)); }));
 | 
			
		||||
 | 
			
		||||
        // NSB review question: insert_eval: Is this right?
 | 
			
		||||
        m_lemma.insert_eval(c);
 | 
			
		||||
        m_lemma.insert(c);
 | 
			
		||||
        core.add_lemma(m_rule, m_lemma.build());
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -514,20 +513,20 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
        auto prop1 = [&](signed_constraint c) {
 | 
			
		||||
            m_lemma.reset();
 | 
			
		||||
            m_lemma.insert(~s.eq(b));
 | 
			
		||||
            m_lemma.insert(~s.eq(y));
 | 
			
		||||
            m_lemma.insert(x_eq_0);
 | 
			
		||||
            m_lemma.insert(a_eq_0);
 | 
			
		||||
            m_lemma.insert_eval(~s.eq(b));
 | 
			
		||||
            m_lemma.insert_eval(~s.eq(y));
 | 
			
		||||
            m_lemma.insert_eval(x_eq_0);
 | 
			
		||||
            m_lemma.insert_eval(a_eq_0);
 | 
			
		||||
            return propagate(core, axb_l_y, c);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        auto prop2 = [&](signed_constraint ante, signed_constraint c) {
 | 
			
		||||
            m_lemma.reset();
 | 
			
		||||
            m_lemma.insert(~s.eq(b));
 | 
			
		||||
            m_lemma.insert(~s.eq(y));
 | 
			
		||||
            m_lemma.insert(x_eq_0);
 | 
			
		||||
            m_lemma.insert(a_eq_0);
 | 
			
		||||
            m_lemma.insert(~ante);
 | 
			
		||||
            m_lemma.insert_eval(~s.eq(b));
 | 
			
		||||
            m_lemma.insert_eval(~s.eq(y));
 | 
			
		||||
            m_lemma.insert_eval(x_eq_0);
 | 
			
		||||
            m_lemma.insert_eval(a_eq_0);
 | 
			
		||||
            m_lemma.insert_eval(~ante);
 | 
			
		||||
            return propagate(core, axb_l_y, c);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -648,27 +647,34 @@ namespace polysat {
 | 
			
		|||
        signed_constraint b_is_odd = s.odd(b);
 | 
			
		||||
        signed_constraint a_is_odd = s.odd(a);
 | 
			
		||||
        signed_constraint x_is_odd = s.odd(X);
 | 
			
		||||
#if 0
 | 
			
		||||
        LOG_H1("try_parity: " << X << " on: " << lit_pp(s, axb_l_y.as_signed_constraint()));
 | 
			
		||||
        LOG("y: " << y << "   a: " << a << "   b: " << b);
 | 
			
		||||
        LOG("b_is_odd: " << lit_pp(s, b_is_odd));
 | 
			
		||||
        LOG("a_is_odd: " << lit_pp(s, a_is_odd));
 | 
			
		||||
        LOG("x_is_odd: " << lit_pp(s, x_is_odd));
 | 
			
		||||
#endif
 | 
			
		||||
        if (!b_is_odd.is_currently_true(s)) {
 | 
			
		||||
            if (!a_is_odd.is_currently_true(s))
 | 
			
		||||
                return false;
 | 
			
		||||
            if (!x_is_odd.is_currently_true(s))
 | 
			
		||||
                return false;
 | 
			
		||||
            m_lemma.reset();
 | 
			
		||||
            m_lemma.insert(~s.eq(y));
 | 
			
		||||
            m_lemma.insert(~a_is_odd);
 | 
			
		||||
            m_lemma.insert(~x_is_odd);
 | 
			
		||||
            m_lemma.insert_eval(~s.eq(y));
 | 
			
		||||
            m_lemma.insert_eval(~a_is_odd);
 | 
			
		||||
            m_lemma.insert_eval(~x_is_odd);
 | 
			
		||||
            if (propagate(core, axb_l_y, b_is_odd))
 | 
			
		||||
                return true;
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        m_lemma.reset();
 | 
			
		||||
        m_lemma.insert(~s.eq(y));
 | 
			
		||||
        m_lemma.insert(~b_is_odd);
 | 
			
		||||
        m_lemma.insert_eval(~s.eq(y));
 | 
			
		||||
        m_lemma.insert_eval(~b_is_odd);
 | 
			
		||||
        if (propagate(core, axb_l_y, a_is_odd))
 | 
			
		||||
            return true;
 | 
			
		||||
        m_lemma.reset();
 | 
			
		||||
        m_lemma.insert(~s.eq(y));
 | 
			
		||||
        m_lemma.insert(~b_is_odd);
 | 
			
		||||
        m_lemma.insert_eval(~s.eq(y));
 | 
			
		||||
        m_lemma.insert_eval(~b_is_odd);
 | 
			
		||||
        if (propagate(core, axb_l_y, x_is_odd))
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;        
 | 
			
		||||
| 
						 | 
				
			
			@ -693,14 +699,14 @@ namespace polysat {
 | 
			
		|||
        if (!is_forced_diseq(a, 0, a_eq_0))
 | 
			
		||||
            return false;
 | 
			
		||||
        m_lemma.reset();
 | 
			
		||||
        m_lemma.insert(s.eq(y));
 | 
			
		||||
        m_lemma.insert(~s.eq(b));
 | 
			
		||||
        m_lemma.insert(a_eq_0);
 | 
			
		||||
        m_lemma.insert_eval(s.eq(y));
 | 
			
		||||
        m_lemma.insert_eval(~s.eq(b));
 | 
			
		||||
        m_lemma.insert_eval(a_eq_0);
 | 
			
		||||
        if (propagate(core, axb_l_y, s.even(X)))
 | 
			
		||||
            return true;
 | 
			
		||||
        if (!is_forced_diseq(X, 0, x_eq_0))
 | 
			
		||||
            return false;
 | 
			
		||||
        m_lemma.insert(x_eq_0);
 | 
			
		||||
        m_lemma.insert_eval(x_eq_0);
 | 
			
		||||
        if (propagate(core, axb_l_y, s.even(a)))
 | 
			
		||||
            return true;
 | 
			
		||||
        return false;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -824,17 +824,12 @@ namespace polysat {
 | 
			
		|||
        backjump_and_learn(max_jump_level);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    //
 | 
			
		||||
    // NSB review: this code assumes that these lemmas are false.
 | 
			
		||||
    // It does not allow saturation to add unit propagation into freshly created literals.
 | 
			
		||||
    // 
 | 
			
		||||
    std::optional<lemma_score> solver::compute_lemma_score(clause const& lemma) {
 | 
			
		||||
        unsigned max_level = 0;     // highest level in lemma
 | 
			
		||||
        unsigned lits_at_max_level = 0;  // how many literals at the highest level in lemma
 | 
			
		||||
        unsigned snd_level = 0;     // second-highest level in lemma
 | 
			
		||||
        bool is_propagation = false;
 | 
			
		||||
        unsigned max_level = 0;             // highest level in lemma
 | 
			
		||||
        unsigned lits_at_max_level = 0;     // how many literals at the highest level in lemma
 | 
			
		||||
        unsigned snd_level = 0;             // second-highest level in lemma
 | 
			
		||||
        bool is_propagation = false;        // whether there is an unassigned literal (at most one)
 | 
			
		||||
        for (sat::literal lit : lemma) {
 | 
			
		||||
            SASSERT(m_bvars.is_assigned(lit));  // any new constraints should have been assign_eval'd
 | 
			
		||||
            if (m_bvars.is_true(lit))  // may happen if we only use the clause to justify a new constraint; it is not a real lemma
 | 
			
		||||
                return std::nullopt;
 | 
			
		||||
            if (!m_bvars.is_assigned(lit)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -861,15 +856,19 @@ namespace polysat {
 | 
			
		|||
        //               Do the standard backjumping known from SAT solving (back to second-highest level in the lemma, propagate it there).
 | 
			
		||||
        // - Semantic split clause: multiple literals on the highest level in the lemma.
 | 
			
		||||
        //                          Backtrack to "highest level - 1" and split on the lemma there.
 | 
			
		||||
        // For now, we follow the same convention for computing the jump levels.
 | 
			
		||||
        // For now, we follow the same convention for computing the jump levels,
 | 
			
		||||
        // but we support an additional type of clause:
 | 
			
		||||
        // - Propagation clause: a single literal is unassigned and should be propagated after backjumping.
 | 
			
		||||
        //                       backjump to max_level so we can propagate
 | 
			
		||||
        unsigned jump_level;
 | 
			
		||||
        unsigned branching_factor = lits_at_max_level;
 | 
			
		||||
        if (is_propagation)
 | 
			
		||||
            jump_level = max_level;
 | 
			
		||||
        if (lits_at_max_level <= 1)
 | 
			
		||||
            jump_level = max_level, branching_factor = 1;
 | 
			
		||||
        else if (lits_at_max_level <= 1)
 | 
			
		||||
            jump_level = snd_level;
 | 
			
		||||
        else
 | 
			
		||||
            jump_level = (max_level == 0) ? 0 : (max_level - 1);
 | 
			
		||||
        return {{jump_level, lits_at_max_level}};
 | 
			
		||||
        return {{jump_level, branching_factor}};
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void solver::backjump_and_learn(unsigned max_jump_level) {
 | 
			
		||||
| 
						 | 
				
			
			@ -886,6 +885,10 @@ namespace polysat {
 | 
			
		|||
        auto appraise_lemma = [&](clause* lemma) {
 | 
			
		||||
            m_simplify_clause.apply(*lemma);
 | 
			
		||||
            auto score = compute_lemma_score(*lemma);
 | 
			
		||||
            if (score)
 | 
			
		||||
                LOG("    score: "  << *score);
 | 
			
		||||
            else
 | 
			
		||||
                LOG("    score: <none>");
 | 
			
		||||
            if (score && *score < best_score) {
 | 
			
		||||
                best_score = *score;
 | 
			
		||||
                best_lemma = lemma;
 | 
			
		||||
| 
						 | 
				
			
			@ -905,6 +908,9 @@ namespace polysat {
 | 
			
		|||
        unsigned const jump_level = std::max(best_score.jump_level(), base_level());
 | 
			
		||||
        SASSERT(jump_level <= max_jump_level);
 | 
			
		||||
 | 
			
		||||
        LOG("best_score: " << best_score);
 | 
			
		||||
        LOG("best_lemma: " << *best_lemma);
 | 
			
		||||
 | 
			
		||||
        m_conflict.reset();
 | 
			
		||||
        backjump(jump_level);
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -940,10 +946,7 @@ namespace polysat {
 | 
			
		|||
            SASSERT(!is_conflict());
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        LOG("best_score: " << best_score);
 | 
			
		||||
        LOG("best_lemma: " << *best_lemma);
 | 
			
		||||
 | 
			
		||||
        if (best_score.literals_at_max_level() > 1) {
 | 
			
		||||
        if (best_score.branching_factor() > 1) {
 | 
			
		||||
            // NOTE: at this point it is possible that the best_lemma is non-asserting.
 | 
			
		||||
            //       We need to double-check, because the backjump level may not be exact (see comment on checking is_conflict above).
 | 
			
		||||
            bool const is_asserting = all_of(*best_lemma, [this](sat::literal lit) { return m_bvars.is_assigned(lit); });
 | 
			
		||||
| 
						 | 
				
			
			@ -1269,7 +1272,8 @@ namespace polysat {
 | 
			
		|||
        out << lpad(4, lit) << ": " << rpad(30, c);
 | 
			
		||||
        if (!c)
 | 
			
		||||
            return out;
 | 
			
		||||
        out << "  [ " << s.m_bvars.value(lit);
 | 
			
		||||
        out << "  [ b:" << rpad(7, s.m_bvars.value(lit));
 | 
			
		||||
        out << " p:" << rpad(7, c.eval(s));
 | 
			
		||||
        if (s.m_bvars.is_assigned(lit)) {
 | 
			
		||||
            out << ' ';
 | 
			
		||||
            if (s.m_bvars.is_assumption(lit))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,20 +52,19 @@ namespace polysat {
 | 
			
		|||
     *
 | 
			
		||||
     * Comparison criterion:
 | 
			
		||||
     * - Lowest jump level has priority, because otherwise, some of the accumulated lemmas may still be false after backjumping.
 | 
			
		||||
     * - To break ties on jump level, choose clause with the fewest literals at its highest decision level;
 | 
			
		||||
     *   to limit case splits.
 | 
			
		||||
     * - To break ties on jump level, choose clause with the lowest branching factor.
 | 
			
		||||
     */
 | 
			
		||||
    class lemma_score {
 | 
			
		||||
        unsigned m_jump_level;
 | 
			
		||||
        unsigned m_literals_at_max_level;
 | 
			
		||||
        unsigned m_branching_factor;    // how many literals will be unassigned after backjumping to jump_level
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        lemma_score(unsigned jump_level, unsigned at_max_level)
 | 
			
		||||
            : m_jump_level(jump_level), m_literals_at_max_level(at_max_level)
 | 
			
		||||
        lemma_score(unsigned jump_level, unsigned bf)
 | 
			
		||||
            : m_jump_level(jump_level), m_branching_factor(bf)
 | 
			
		||||
        { }
 | 
			
		||||
 | 
			
		||||
        unsigned jump_level() const { return m_jump_level; }
 | 
			
		||||
        unsigned literals_at_max_level() const { return m_literals_at_max_level; }
 | 
			
		||||
        unsigned branching_factor() const { return m_branching_factor; }
 | 
			
		||||
 | 
			
		||||
        static lemma_score max() {
 | 
			
		||||
            return {UINT_MAX, UINT_MAX};
 | 
			
		||||
| 
						 | 
				
			
			@ -73,20 +72,20 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
        bool operator==(lemma_score const& other) const {
 | 
			
		||||
            return m_jump_level == other.m_jump_level
 | 
			
		||||
                && m_literals_at_max_level == other.m_literals_at_max_level;
 | 
			
		||||
                && m_branching_factor == other.m_branching_factor;
 | 
			
		||||
        }
 | 
			
		||||
        bool operator!=(lemma_score const& other) const { return !operator==(other); }
 | 
			
		||||
 | 
			
		||||
        bool operator<(lemma_score const& other) const {
 | 
			
		||||
            return m_jump_level < other.m_jump_level
 | 
			
		||||
                || (m_jump_level == other.m_jump_level && m_literals_at_max_level < other.m_literals_at_max_level);
 | 
			
		||||
                || (m_jump_level == other.m_jump_level && m_branching_factor < other.m_branching_factor);
 | 
			
		||||
        }
 | 
			
		||||
        bool operator>(lemma_score const& other) const { return other.operator<(*this); }
 | 
			
		||||
        bool operator<=(lemma_score const& other) const { return operator==(other) || operator<(other); }
 | 
			
		||||
        bool operator>=(lemma_score const& other) const { return operator==(other) || operator>(other); }
 | 
			
		||||
 | 
			
		||||
        std::ostream& display(std::ostream& out) const {
 | 
			
		||||
            return out << "jump_level=" << m_jump_level << " at_max_level=" << m_literals_at_max_level;
 | 
			
		||||
            return out << "jump_level=" << m_jump_level << " branching_factor=" << m_branching_factor;
 | 
			
		||||
        }
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -413,6 +412,7 @@ namespace polysat {
 | 
			
		|||
        signed_constraint eq(pdd const& p, unsigned q) { return eq(p - q); }
 | 
			
		||||
        signed_constraint odd(pdd const& p) { return ~even(p); }
 | 
			
		||||
        signed_constraint even(pdd const& p) { return parity(p, 1); }
 | 
			
		||||
        /** parity(p) >= k   (<=> p * 2^(K-k) == 0) */
 | 
			
		||||
        signed_constraint parity(pdd const& p, unsigned k) { return eq(p*rational::power_of_two(p.manager().power_of_2() - k)); }
 | 
			
		||||
        signed_constraint diseq(pdd const& p, rational const& q) { return diseq(p - q); }
 | 
			
		||||
        signed_constraint diseq(pdd const& p, unsigned q) { return diseq(p - q); }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -725,7 +725,7 @@ namespace polysat {
 | 
			
		|||
        }
 | 
			
		||||
        while (e != first);
 | 
			
		||||
 | 
			
		||||
        SASSERT(all_of(lemma, [this](sat::literal lit) { return s.m_bvars.value(lit) == l_false; }));
 | 
			
		||||
        SASSERT(all_of(lemma, [this](sat::literal lit) { return s.m_bvars.value(lit) == l_false || s.lit2cnstr(lit).is_currently_false(s); }));
 | 
			
		||||
        core.add_lemma("viable", lemma.build());
 | 
			
		||||
        core.logger().log(inf_fi(*this, v));
 | 
			
		||||
        return true;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue