mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 05:19:11 +00:00 
			
		
		
		
	trying trim
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									d22c86f9fe
								
							
						
					
					
						commit
						c1c659dc93
					
				
					 4 changed files with 457 additions and 196 deletions
				
			
		| 
						 | 
				
			
			@ -45,6 +45,7 @@ Proof checker for clauses created during search.
 | 
			
		|||
#include "smt/smt_solver.h"
 | 
			
		||||
#include "sat/sat_solver.h"
 | 
			
		||||
#include "sat/sat_drat.h"
 | 
			
		||||
#include "sat/sat_proof_trim.h"
 | 
			
		||||
#include "sat/smt/euf_proof_checker.h"
 | 
			
		||||
#include "cmd_context/cmd_context.h"
 | 
			
		||||
#include "params/solver_params.hpp"
 | 
			
		||||
| 
						 | 
				
			
			@ -181,203 +182,57 @@ public:
 | 
			
		|||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
namespace sat {
 | 
			
		||||
    /**
 | 
			
		||||
     * Replay proof entierly, then walk backwards extracting reduced proof.
 | 
			
		||||
     */
 | 
			
		||||
    class proof_trim {
 | 
			
		||||
        cmd_context& ctx;
 | 
			
		||||
        ast_manager& m;
 | 
			
		||||
        solver s;
 | 
			
		||||
        literal_vector m_clause;
 | 
			
		||||
 | 
			
		||||
        vector<std::tuple<literal_vector, clause*, bool, bool>> m_trail;
 | 
			
		||||
/**
 | 
			
		||||
 * Replay proof entierly, then walk backwards extracting reduced proof.
 | 
			
		||||
 */
 | 
			
		||||
class proof_trim {
 | 
			
		||||
    cmd_context& ctx;
 | 
			
		||||
    ast_manager& m;
 | 
			
		||||
    sat::proof_trim trim;
 | 
			
		||||
        
 | 
			
		||||
        struct hash {
 | 
			
		||||
            unsigned operator()(literal_vector const& v) const {
 | 
			
		||||
                return string_hash((char const*)v.begin(), v.size()*sizeof(literal), 3);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        struct eq {
 | 
			
		||||
            bool operator()(literal_vector const& a, literal_vector const& b) const {
 | 
			
		||||
                return a == b;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        map<literal_vector, clause_vector, hash, eq> m_clauses;
 | 
			
		||||
    void mk_clause(expr_ref_vector const& clause) {
 | 
			
		||||
        trim.init_clause();
 | 
			
		||||
        for (expr* arg: clause)
 | 
			
		||||
            add_literal(arg);
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
        void mk_clause(expr_ref_vector const& clause) {
 | 
			
		||||
            m_clause.reset();
 | 
			
		||||
            for (expr* arg: clause)
 | 
			
		||||
                add_literal(arg);
 | 
			
		||||
            std::sort(m_clause.begin(), m_clause.end());
 | 
			
		||||
        }
 | 
			
		||||
    sat::bool_var mk_var(expr* arg) {
 | 
			
		||||
        while (arg->get_id() >= trim.num_vars())
 | 
			
		||||
            trim.mk_var();
 | 
			
		||||
        return arg->get_id();
 | 
			
		||||
    }
 | 
			
		||||
        
 | 
			
		||||
        bool_var mk_var(expr* arg) {
 | 
			
		||||
            while (arg->get_id() >= s.num_vars())
 | 
			
		||||
                s.mk_var(true, true);
 | 
			
		||||
            return arg->get_id();
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        void add_literal(expr* arg) {
 | 
			
		||||
            bool sign = m.is_not(arg, arg);
 | 
			
		||||
            m_clause.push_back(literal(mk_var(arg), sign));
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
           Pseudo-code from Gurfinkel, Vizel, FMCAD 2014
 | 
			
		||||
           Input: trail (a0,d0), ..., (an,dn) = ({},bot)
 | 
			
		||||
           Output: reduced trail - result 
 | 
			
		||||
           result = []
 | 
			
		||||
           C = { an }
 | 
			
		||||
           for i = n to 0 do
 | 
			
		||||
               if s.is_deleted(ai) then s.revive(ai)
 | 
			
		||||
               else 
 | 
			
		||||
                  if s.isontrail(ai) then
 | 
			
		||||
                     s.undotrailcore(ai,C)
 | 
			
		||||
                  s.delete(ai)
 | 
			
		||||
                  if ai in C then 
 | 
			
		||||
                      if ai is not initial then
 | 
			
		||||
                         s.savetrail()
 | 
			
		||||
                         s.enqueue(not ai)
 | 
			
		||||
                         c = s.propagate()
 | 
			
		||||
                         s.conflictanalysiscore(c, C)
 | 
			
		||||
                         s.restoretrail()
 | 
			
		||||
                       result += [ai]
 | 
			
		||||
            reverse(result)
 | 
			
		||||
            
 | 
			
		||||
            is_deleted(ai):
 | 
			
		||||
               clause was detached
 | 
			
		||||
            revive(ai):
 | 
			
		||||
               attach clause ai
 | 
			
		||||
            isontrail(ai):
 | 
			
		||||
                some literal on the current trail in s is justified by ai
 | 
			
		||||
            undotrailcore(ai, C):
 | 
			
		||||
                pop the trail until dependencies on ai are gone
 | 
			
		||||
            savetrail:
 | 
			
		||||
                store current trail so it can be restored
 | 
			
		||||
            enqueue(not ai):
 | 
			
		||||
                assert negations of ai at a new decision level
 | 
			
		||||
            conflictanalysiscore(c, C):
 | 
			
		||||
                ?
 | 
			
		||||
            restoretrail:
 | 
			
		||||
                restore the trail to the position before enqueue
 | 
			
		||||
                
 | 
			
		||||
                                                           
 | 
			
		||||
        */        
 | 
			
		||||
        void trim() {            
 | 
			
		||||
            vector<literal_vector> result, clauses;
 | 
			
		||||
            clauses.push_back(literal_vector());
 | 
			
		||||
            for (unsigned i = m_trail.size(); i-- > 0; ) {
 | 
			
		||||
                auto const& [cl, clp, is_add, is_initial] = m_trail[i];
 | 
			
		||||
                if (!is_add) {
 | 
			
		||||
                    revive(cl, clp);
 | 
			
		||||
                    continue;
 | 
			
		||||
                }
 | 
			
		||||
                prune_trail(cl, clp);
 | 
			
		||||
                del(cl, clp);
 | 
			
		||||
                if (!clauses.contains(cl))
 | 
			
		||||
                    continue;
 | 
			
		||||
                if (!is_initial) {
 | 
			
		||||
                    s.push();
 | 
			
		||||
                    unsigned lvl = s.scope_lvl();
 | 
			
		||||
                    for (auto lit : cl)
 | 
			
		||||
                        s.assign(~lit, justification(lvl));
 | 
			
		||||
                    s.propagate(false);
 | 
			
		||||
                    SASSERT(s.inconsistent());
 | 
			
		||||
                    conflict_analysis(clauses);
 | 
			
		||||
                    s.pop(1);
 | 
			
		||||
                }
 | 
			
		||||
                result.push_back(cl);
 | 
			
		||||
            }
 | 
			
		||||
            result.reverse();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void del(literal_vector const& cl, clause* cp) {
 | 
			
		||||
            if (cp) 
 | 
			
		||||
                s.detach_clause(*cp);
 | 
			
		||||
            else 
 | 
			
		||||
                del(cl);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void prune_trail(literal_vector const& cl, clause* cp) {
 | 
			
		||||
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void conflict_analysis(vector<literal_vector> const& clauses) {
 | 
			
		||||
            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        void revive(literal_vector const& cl, clause* cp) {
 | 
			
		||||
            if (cp) 
 | 
			
		||||
                s.attach_clause(*cp);
 | 
			
		||||
            else 
 | 
			
		||||
                s.mk_clause(cl, status::redundant());            
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        clause* del(literal_vector const& cl) {
 | 
			
		||||
            clause* cp = nullptr;
 | 
			
		||||
            IF_VERBOSE(3, verbose_stream() << "del: " << cl << "\n");
 | 
			
		||||
            if (m_clause.size() == 2) {
 | 
			
		||||
                s.detach_bin_clause(cl[0], cl[1], true);
 | 
			
		||||
                return cp;
 | 
			
		||||
            }
 | 
			
		||||
            auto* e = m_clauses.find_core(cl);            
 | 
			
		||||
            if (!e)
 | 
			
		||||
                return cp;
 | 
			
		||||
            auto& v = e->get_data().m_value;
 | 
			
		||||
            if (!v.empty()) {
 | 
			
		||||
                cp = v.back();
 | 
			
		||||
                IF_VERBOSE(3, verbose_stream() << "del: " << *cp << "\n");
 | 
			
		||||
                s.detach_clause(*cp);
 | 
			
		||||
                v.pop_back();
 | 
			
		||||
            }
 | 
			
		||||
            return cp;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void save(literal_vector const& lits, clause* cl) {
 | 
			
		||||
            if (!cl)                
 | 
			
		||||
                return;
 | 
			
		||||
            IF_VERBOSE(3, verbose_stream() << "add: " << *cl << "\n");
 | 
			
		||||
            auto& v = m_clauses.insert_if_not_there(lits, clause_vector());            
 | 
			
		||||
            v.push_back(cl);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
    public:
 | 
			
		||||
        proof_trim(cmd_context& ctx):
 | 
			
		||||
            ctx(ctx),
 | 
			
		||||
            m(ctx.m()),
 | 
			
		||||
            s(gparams::get_module("sat"), m.limit()) {            
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        void assume(expr_ref_vector const& _clause, bool is_initial = true) {        
 | 
			
		||||
            mk_clause(_clause);
 | 
			
		||||
            IF_VERBOSE(3, verbose_stream() << "add: " << m_clause << "\n");
 | 
			
		||||
            auto* cl = s.mk_clause(m_clause, status::redundant());
 | 
			
		||||
            m_trail.push_back({ m_clause, cl, true, is_initial });
 | 
			
		||||
            s.propagate(false);
 | 
			
		||||
            save(m_clause, cl);
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        void del(expr_ref_vector const& _clause) {
 | 
			
		||||
            mk_clause(_clause);
 | 
			
		||||
            clause* cp = del(m_clause);
 | 
			
		||||
            m_trail.push_back({ m_clause, cp, false, true });
 | 
			
		||||
        }
 | 
			
		||||
        
 | 
			
		||||
        void infer(expr_ref_vector const& _clause, app*) {
 | 
			
		||||
            assume(_clause, false);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        void updt_params(params_ref const& p) {
 | 
			
		||||
            s.updt_params(p);
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
    void add_literal(expr* arg) {
 | 
			
		||||
        bool sign = m.is_not(arg, arg);
 | 
			
		||||
        trim.add_literal(mk_var(arg), sign);
 | 
			
		||||
    }
 | 
			
		||||
      
 | 
			
		||||
public:
 | 
			
		||||
    proof_trim(cmd_context& ctx):
 | 
			
		||||
        ctx(ctx),
 | 
			
		||||
        m(ctx.m()),
 | 
			
		||||
        trim(gparams::get_module("sat"), m.limit()) {            
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void assume(expr_ref_vector const& _clause, bool is_initial = true) {        
 | 
			
		||||
        mk_clause(_clause);
 | 
			
		||||
        trim.assume(true);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void del(expr_ref_vector const& _clause) {
 | 
			
		||||
        mk_clause(_clause);
 | 
			
		||||
        trim.del();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void infer(expr_ref_vector const& _clause, app*) {
 | 
			
		||||
        mk_clause(_clause);
 | 
			
		||||
        trim.infer();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void updt_params(params_ref const& p) {
 | 
			
		||||
        trim.updt_params(p);
 | 
			
		||||
    }    
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class proof_saver {
 | 
			
		||||
    cmd_context& ctx;
 | 
			
		||||
| 
						 | 
				
			
			@ -415,11 +270,11 @@ class proof_cmds_imp : public proof_cmds {
 | 
			
		|||
    bool            m_trim   = false;
 | 
			
		||||
    scoped_ptr<smt_checker>     m_checker;
 | 
			
		||||
    scoped_ptr<proof_saver>     m_saver;
 | 
			
		||||
    scoped_ptr<sat::proof_trim>      m_trimmer;
 | 
			
		||||
    scoped_ptr<proof_trim>      m_trimmer;
 | 
			
		||||
    
 | 
			
		||||
    smt_checker& checker() { if (!m_checker) m_checker = alloc(smt_checker, m); return *m_checker; }
 | 
			
		||||
    proof_saver& saver() { if (!m_saver) m_saver = alloc(proof_saver, ctx); return *m_saver; }
 | 
			
		||||
    sat::proof_trim& trim() { if (!m_trimmer) m_trimmer = alloc(sat::proof_trim, ctx); return *m_trimmer; }
 | 
			
		||||
    proof_trim& trim() { if (!m_trimmer) m_trimmer = alloc(proof_trim, ctx); return *m_trimmer; }
 | 
			
		||||
    
 | 
			
		||||
public:
 | 
			
		||||
    proof_cmds_imp(cmd_context& ctx): ctx(ctx), m(ctx.m()), m_lits(m), m_proof_hint(m) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -30,6 +30,7 @@ z3_add_component(sat
 | 
			
		|||
    sat_parallel.cpp
 | 
			
		||||
    sat_prob.cpp
 | 
			
		||||
    sat_probing.cpp
 | 
			
		||||
    sat_proof_trim.cpp
 | 
			
		||||
    sat_scc.cpp
 | 
			
		||||
    sat_simplifier.cpp
 | 
			
		||||
    sat_solver.cpp
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										326
									
								
								src/sat/sat_proof_trim.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										326
									
								
								src/sat/sat_proof_trim.cpp
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,326 @@
 | 
			
		|||
/*++
 | 
			
		||||
  Copyright (c) 2020 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
  Module Name:
 | 
			
		||||
 | 
			
		||||
   sat_proof_trim.cpp
 | 
			
		||||
 | 
			
		||||
  Abstract:
 | 
			
		||||
   
 | 
			
		||||
    proof replay and trim
 | 
			
		||||
 | 
			
		||||
  Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner 2023-10-04
 | 
			
		||||
 | 
			
		||||
  Notes:
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
#include "sat/sat_proof_trim.h"
 | 
			
		||||
 | 
			
		||||
namespace sat {
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       Pseudo-code from Gurfinkel, Vizel, FMCAD 2014
 | 
			
		||||
       Input: trail (a0,d0), ..., (an,dn) = ({},bot)
 | 
			
		||||
       Output: reduced trail - result 
 | 
			
		||||
       result = []
 | 
			
		||||
       C = { an }
 | 
			
		||||
       for i = n to 0 do
 | 
			
		||||
           if s.is_deleted(ai) then s.revive(ai)
 | 
			
		||||
           else 
 | 
			
		||||
              if s.isontrail(ai) then
 | 
			
		||||
                  s.undotrailcore(ai,C)
 | 
			
		||||
              s.delete(ai)
 | 
			
		||||
              if ai in C then 
 | 
			
		||||
                  if ai is not initial then
 | 
			
		||||
                      s.savetrail()
 | 
			
		||||
                      s.enqueue(not ai)
 | 
			
		||||
                      c = s.propagate()
 | 
			
		||||
                      s.conflictanalysiscore(c, C)
 | 
			
		||||
                      s.restoretrail()
 | 
			
		||||
                   result += [ai]
 | 
			
		||||
       reverse(result)
 | 
			
		||||
            
 | 
			
		||||
       is_deleted(ai):
 | 
			
		||||
          clause was detached
 | 
			
		||||
       revive(ai):
 | 
			
		||||
          attach clause ai
 | 
			
		||||
       isontrail(ai):
 | 
			
		||||
          some literal on the current trail in s is justified by ai
 | 
			
		||||
       undotrailcore(ai, C):
 | 
			
		||||
          pop the trail until dependencies on ai are gone
 | 
			
		||||
       savetrail:
 | 
			
		||||
          store current trail so it can be restored
 | 
			
		||||
       enqueue(not ai):
 | 
			
		||||
          assert negations of ai at a new decision level
 | 
			
		||||
       conflictanalysiscore(c, C):
 | 
			
		||||
          ?
 | 
			
		||||
       restoretrail:
 | 
			
		||||
          restore the trail to the position before enqueue
 | 
			
		||||
                                                                          
 | 
			
		||||
    */        
 | 
			
		||||
 | 
			
		||||
    void proof_trim::trim() {
 | 
			
		||||
        vector<literal_vector> result, clauses;
 | 
			
		||||
        clauses.push_back(literal_vector());
 | 
			
		||||
        for (unsigned i = m_trail.size(); i-- > 0; ) {
 | 
			
		||||
            auto const& [cl, clp, is_add, is_initial] = m_trail[i];
 | 
			
		||||
            if (!is_add) {
 | 
			
		||||
                revive(cl, clp);
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            prune_trail(cl, clp);
 | 
			
		||||
            del(cl, clp);
 | 
			
		||||
            if (!clauses.contains(cl))
 | 
			
		||||
                continue;
 | 
			
		||||
            result.push_back(cl);
 | 
			
		||||
            if (is_initial)
 | 
			
		||||
                continue;
 | 
			
		||||
            s.push();
 | 
			
		||||
            unsigned init_sz = s.m_trail.size();
 | 
			
		||||
            unsigned lvl = s.scope_lvl();
 | 
			
		||||
            for (auto lit : cl)
 | 
			
		||||
                s.assign(~lit, justification(lvl));
 | 
			
		||||
            s.propagate(false);
 | 
			
		||||
            SASSERT(s.inconsistent());
 | 
			
		||||
            conflict_analysis_core(init_sz, clauses);
 | 
			
		||||
            s.pop(1);                
 | 
			
		||||
        }
 | 
			
		||||
        result.reverse();
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void proof_trim::del(literal_vector const& cl, clause* cp) {
 | 
			
		||||
        if (cp) 
 | 
			
		||||
            s.detach_clause(*cp);
 | 
			
		||||
        else 
 | 
			
		||||
            del(cl);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool proof_trim::match_clause(literal_vector const& cl, literal l1, literal l2) const {
 | 
			
		||||
        return cl.size() == 2 && ((l1 == cl[0] && l2 == cl[1]) || (l1 == cl[1] && l2 == cl[0]));
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    bool proof_trim::match_clause(literal_vector const& cl, literal l1, literal l2, literal l3) const {
 | 
			
		||||
        return cl.size() == 3 &&
 | 
			
		||||
            ((l1 == cl[0] && l2 == cl[1] && l3 == cl[2]) ||
 | 
			
		||||
             (l1 == cl[0] && l2 == cl[2] && l3 == cl[1]) ||
 | 
			
		||||
             (l1 == cl[1] && l2 == cl[0] && l3 == cl[2]) ||
 | 
			
		||||
             (l1 == cl[1] && l2 == cl[2] && l3 == cl[0]) ||
 | 
			
		||||
             (l1 == cl[2] && l2 == cl[1] && l3 == cl[0]) ||
 | 
			
		||||
             (l1 == cl[2] && l2 == cl[0] && l3 == cl[1]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
         * cl is on the trail if there is some literal l that is implied by cl
 | 
			
		||||
         * Remove all clauses after cl that are in the cone of influence of cl.
 | 
			
		||||
         * The coi is defined inductively: C is in coi of cl if it contains ~l
 | 
			
		||||
         * or it contains ~l' where l' is implied by a clause in the coi of cl.
 | 
			
		||||
         * Possible optimization: 
 | 
			
		||||
         * - check if clause contains a literal that is on implied on the trail
 | 
			
		||||
         *   if it doesn't contain any such literal, bypass the trail adjustment.
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
    void proof_trim::prune_trail(literal_vector const& cl, clause* cp) {
 | 
			
		||||
        m_in_clause.reset();
 | 
			
		||||
        m_in_coi.reset();
 | 
			
		||||
        
 | 
			
		||||
        for (literal lit : cl) 
 | 
			
		||||
            m_in_clause.insert(lit.index());
 | 
			
		||||
        
 | 
			
		||||
        bool on_trail = false;
 | 
			
		||||
        unsigned j = 0;
 | 
			
		||||
        for (unsigned i = 0; i < s.trail_size(); ++i) {
 | 
			
		||||
            literal l = s.trail_literal(i);
 | 
			
		||||
            if (m_in_clause.contains(l.index())) {
 | 
			
		||||
                SASSERT(!on_trail);
 | 
			
		||||
                on_trail = true;
 | 
			
		||||
                m_in_coi.insert((~l).index());
 | 
			
		||||
                s.m_assignment[l.index()] = l_undef;
 | 
			
		||||
                s.m_assignment[(~l).index()] = l_undef;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            if (!on_trail) {
 | 
			
		||||
                s.m_trail[j++] = s.m_trail[i];
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
            
 | 
			
		||||
            auto js = s.get_justification(l);
 | 
			
		||||
            bool in_coi = false;
 | 
			
		||||
            if (js.is_clause())
 | 
			
		||||
                for (literal lit : s.get_clause(j))
 | 
			
		||||
                    in_coi |= m_in_coi.contains(lit.index());                
 | 
			
		||||
            else if (js.is_binary_clause())
 | 
			
		||||
                in_coi = m_in_coi.contains(js.get_literal().index());
 | 
			
		||||
            else if (js.is_ternary_clause())
 | 
			
		||||
                in_coi = m_in_coi.contains(js.get_literal1().index()) || m_in_coi.contains(js.get_literal2().index());
 | 
			
		||||
            else
 | 
			
		||||
                UNREACHABLE(); // approach does not work for external justifications
 | 
			
		||||
            
 | 
			
		||||
            if (in_coi) {
 | 
			
		||||
                m_in_coi.insert((~l).index());
 | 
			
		||||
                s.m_assignment[l.index()] = l_undef;
 | 
			
		||||
                s.m_assignment[(~l).index()] = l_undef;
 | 
			
		||||
            }
 | 
			
		||||
            else
 | 
			
		||||
                s.m_trail[j++] = s.m_trail[i];
 | 
			
		||||
        }            
 | 
			
		||||
        s.m_trail.shrink(j);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
       The current state is in conflict.
 | 
			
		||||
       Chase justifications for conflict to extract clauses that are in coi of conflict.
 | 
			
		||||
 | 
			
		||||
       Assume:
 | 
			
		||||
       F | G, ~C |- []
 | 
			
		||||
       Let T (trail) be the extension of G, ~C that derives the empty clause.
 | 
			
		||||
       T := G, ~C, l1:j1, l2:j2, ..., lk:jk
 | 
			
		||||
       The goal is to extract clauses in T that are used to derive C.
 | 
			
		||||
       - some of the literals in ~C that are not set to true already (they must be unassigned relative)
 | 
			
		||||
       are used to derive the empty clause.
 | 
			
		||||
       - some literals in ~C that are assigned to true may also be used to derive the empty clause.
 | 
			
		||||
             
 | 
			
		||||
       Example:
 | 
			
		||||
       C = c or d or e
 | 
			
		||||
       G = a
 | 
			
		||||
       F = { ~a or ~b, c or d or b, ... }
 | 
			
		||||
       T = ~b : ~a or ~b, ~c: D ~d : D , ~e : D, b : c or d or b 
 | 
			
		||||
       where D is a decision marker (justification::NONE)
 | 
			
		||||
       The conflict depends on the first two clauses in F.
 | 
			
		||||
             
 | 
			
		||||
       All literals that are are used in clauses leading to the conflict are
 | 
			
		||||
       queried for their explanation. Their explanation is added to the clauses.
 | 
			
		||||
       
 | 
			
		||||
    */
 | 
			
		||||
    void proof_trim::conflict_analysis_core(unsigned init_sz, vector<literal_vector>& clauses) {
 | 
			
		||||
        justification j = s.m_conflict;
 | 
			
		||||
        literal consequent = null_literal;
 | 
			
		||||
        unsigned idx = s.m_trail.size() - 1;
 | 
			
		||||
        unsigned old_sz = s.m_unmark.size();
 | 
			
		||||
        bool_var c_var;
 | 
			
		||||
        auto add_dependency = [&](bool_var v) {
 | 
			
		||||
            auto j = s.m_justification[v];
 | 
			
		||||
            literal lit = literal(v, s.value(v) == l_false);
 | 
			
		||||
            add_core(lit, j, clauses);            
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        if (s.m_not_l != null_literal) {
 | 
			
		||||
            s.process_antecedent_for_unsat_core(s.m_not_l);
 | 
			
		||||
            add_core(s.m_not_l, s.m_justification[s.m_not_l.var()], clauses);
 | 
			
		||||
            add_core(~s.m_not_l, j, clauses);
 | 
			
		||||
            consequent = ~s.m_not_l;                
 | 
			
		||||
        }
 | 
			
		||||
        while (true) {
 | 
			
		||||
            s.process_consequent_for_unsat_core(consequent, j);
 | 
			
		||||
            while (idx >= init_sz) {
 | 
			
		||||
                consequent = s.m_trail[idx];
 | 
			
		||||
                c_var = consequent.var();
 | 
			
		||||
                if (s.is_marked(c_var))
 | 
			
		||||
                    break;
 | 
			
		||||
                --idx;
 | 
			
		||||
            }
 | 
			
		||||
            if (idx < init_sz)
 | 
			
		||||
                break;
 | 
			
		||||
            j = s.m_justification[c_var];
 | 
			
		||||
            --idx;
 | 
			
		||||
        }
 | 
			
		||||
        for (unsigned i = s.m_mark.size(); i-- > old_sz; ) 
 | 
			
		||||
            add_dependency(s.m_unmark[i]);
 | 
			
		||||
        s.reset_unmark(old_sz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void proof_trim::add_core(literal l, justification j, vector<literal_vector>& clauses) {
 | 
			
		||||
        m_clause.reset();
 | 
			
		||||
        switch (j.get_kind()) {
 | 
			
		||||
        case justification::NONE:
 | 
			
		||||
            return;                
 | 
			
		||||
        case justification::BINARY:
 | 
			
		||||
            m_clause.push_back(l);
 | 
			
		||||
            m_clause.push_back(j.get_literal());
 | 
			
		||||
            break;
 | 
			
		||||
        case justification::TERNARY:
 | 
			
		||||
            m_clause.push_back(l);
 | 
			
		||||
            m_clause.push_back(j.get_literal1());
 | 
			
		||||
            m_clause.push_back(j.get_literal2());
 | 
			
		||||
            break;
 | 
			
		||||
        case justification::CLAUSE: 
 | 
			
		||||
            for (auto lit : s.get_clause(j))
 | 
			
		||||
                m_clause.push_back(lit);
 | 
			
		||||
            break;
 | 
			
		||||
        default:
 | 
			
		||||
            UNREACHABLE();
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
        std::sort(m_clause.begin(), m_clause.end());
 | 
			
		||||
        clauses.insert(m_clause);            
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    void proof_trim::revive(literal_vector const& cl, clause* cp) {
 | 
			
		||||
        if (cp) 
 | 
			
		||||
            s.attach_clause(*cp);
 | 
			
		||||
        else 
 | 
			
		||||
            s.mk_clause(cl, status::redundant());            
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    clause* proof_trim::del(literal_vector const& cl) {
 | 
			
		||||
        clause* cp = nullptr;
 | 
			
		||||
        IF_VERBOSE(3, verbose_stream() << "del: " << cl << "\n");
 | 
			
		||||
        if (m_clause.size() == 2) {
 | 
			
		||||
            s.detach_bin_clause(cl[0], cl[1], true);
 | 
			
		||||
            return cp;
 | 
			
		||||
        }
 | 
			
		||||
        auto* e = m_clauses.find_core(cl);            
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return cp;
 | 
			
		||||
        auto& v = e->get_data().m_value;
 | 
			
		||||
        if (!v.empty()) {
 | 
			
		||||
            cp = v.back();
 | 
			
		||||
            IF_VERBOSE(3, verbose_stream() << "del: " << *cp << "\n");
 | 
			
		||||
            s.detach_clause(*cp);
 | 
			
		||||
            v.pop_back();
 | 
			
		||||
        }
 | 
			
		||||
        return cp;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void proof_trim::save(literal_vector const& lits, clause* cl) {
 | 
			
		||||
        if (!cl)                
 | 
			
		||||
            return;
 | 
			
		||||
        IF_VERBOSE(3, verbose_stream() << "add: " << *cl << "\n");
 | 
			
		||||
        auto& v = m_clauses.insert_if_not_there(lits, clause_vector());            
 | 
			
		||||
        v.push_back(cl);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
 | 
			
		||||
    proof_trim::proof_trim(params_ref const& p, reslimit& lim):
 | 
			
		||||
        s(p, lim)
 | 
			
		||||
    {}
 | 
			
		||||
 | 
			
		||||
    void proof_trim::assume(bool is_initial) {
 | 
			
		||||
        std::sort(m_clause.begin(), m_clause.end());
 | 
			
		||||
        IF_VERBOSE(3, verbose_stream() << "add: " << m_clause << "\n");
 | 
			
		||||
        auto* cl = s.mk_clause(m_clause, status::redundant());
 | 
			
		||||
        m_trail.push_back({ m_clause, cl, true, is_initial });
 | 
			
		||||
        s.propagate(false);
 | 
			
		||||
        save(m_clause, cl);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void proof_trim::del() {
 | 
			
		||||
        std::sort(m_clause.begin(), m_clause.end());
 | 
			
		||||
        clause* cp = del(m_clause);
 | 
			
		||||
        m_trail.push_back({ m_clause, cp, false, true });
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void proof_trim::infer() {
 | 
			
		||||
        assume(false);        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										79
									
								
								src/sat/sat_proof_trim.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/sat/sat_proof_trim.h
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,79 @@
 | 
			
		|||
/*++
 | 
			
		||||
  Copyright (c) 2020 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
  Module Name:
 | 
			
		||||
 | 
			
		||||
   sat_trim.h
 | 
			
		||||
 | 
			
		||||
  Abstract:
 | 
			
		||||
   
 | 
			
		||||
    proof replay and trim
 | 
			
		||||
 | 
			
		||||
  Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner 2023-10-04
 | 
			
		||||
 | 
			
		||||
  Notes:
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include "util/params.h"
 | 
			
		||||
#include "util/statistics.h"
 | 
			
		||||
#include "sat/sat_clause.h"
 | 
			
		||||
#include "sat/sat_types.h"
 | 
			
		||||
#include "sat/sat_solver.h"
 | 
			
		||||
 | 
			
		||||
namespace sat {
 | 
			
		||||
 | 
			
		||||
    class proof_trim {
 | 
			
		||||
        solver         s;
 | 
			
		||||
        literal_vector m_clause;
 | 
			
		||||
        uint_set       m_in_clause;
 | 
			
		||||
        uint_set       m_in_coi;
 | 
			
		||||
        vector<std::tuple<literal_vector, clause*, bool, bool>> m_trail;
 | 
			
		||||
        
 | 
			
		||||
        struct hash {
 | 
			
		||||
            unsigned operator()(literal_vector const& v) const {
 | 
			
		||||
                return string_hash((char const*)v.begin(), v.size()*sizeof(literal), 3);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        struct eq {
 | 
			
		||||
            bool operator()(literal_vector const& a, literal_vector const& b) const {
 | 
			
		||||
                return a == b;
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
        map<literal_vector, clause_vector, hash, eq> m_clauses;
 | 
			
		||||
 | 
			
		||||
        void del(literal_vector const& cl, clause* cp);
 | 
			
		||||
 | 
			
		||||
        bool match_clause(literal_vector const& cl, literal l1, literal l2) const;
 | 
			
		||||
        bool match_clause(literal_vector const& cl, literal l1, literal l2, literal l3) const;
 | 
			
		||||
 | 
			
		||||
        void prune_trail(literal_vector const& cl, clause* cp);
 | 
			
		||||
        void conflict_analysis_core(unsigned init_sz, vector<literal_vector>& clauses);
 | 
			
		||||
        void add_core(literal l, justification j, vector<literal_vector>& clauses);
 | 
			
		||||
        void revive(literal_vector const& cl, clause* cp);        
 | 
			
		||||
        clause* del(literal_vector const& cl);
 | 
			
		||||
        void save(literal_vector const& lits, clause* cl);
 | 
			
		||||
        
 | 
			
		||||
    public:
 | 
			
		||||
 | 
			
		||||
        proof_trim(params_ref const& p, reslimit& lim);
 | 
			
		||||
 | 
			
		||||
        bool_var mk_var() { return s.mk_var(true, true); }
 | 
			
		||||
        void init_clause() { m_clause.reset(); }
 | 
			
		||||
        void add_literal(bool_var v, bool sign) { m_clause.push_back(literal(v, sign)); }
 | 
			
		||||
        unsigned num_vars() { return s.num_vars(); }
 | 
			
		||||
 | 
			
		||||
        void assume(bool is_initial = true);
 | 
			
		||||
        void del();
 | 
			
		||||
        void infer();
 | 
			
		||||
        void updt_params(params_ref const& p) { s.updt_params(p); }
 | 
			
		||||
 | 
			
		||||
        void trim();
 | 
			
		||||
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue