mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	* Introduce X-macro-based trace tag definition - Created trace_tags.def to centralize TRACE tag definitions - Each tag includes a symbolic name and description - Set up enum class TraceTag for type-safe usage in TRACE macros * Add script to generate Markdown documentation from trace_tags.def - Python script parses trace_tags.def and outputs trace_tags.md * Refactor TRACE_NEW to prepend TraceTag and pass enum to is_trace_enabled * trace: improve trace tag handling system with hierarchical tagging - Introduce hierarchical tag-class structure: enabling a tag class activates all child tags - Unify TRACE, STRACE, SCTRACE, and CTRACE under enum TraceTag - Implement initial version of trace_tag.def using X(tag, tag_class, description) (class names and descriptions to be refined in a future update) * trace: replace all string-based TRACE tags with enum TraceTag - Migrated all TRACE, STRACE, SCTRACE, and CTRACE macros to use enum TraceTag values instead of raw string literals * trace : add cstring header * trace : Add Markdown documentation generation from trace_tags.def via mk_api_doc.py * trace : rename macro parameter 'class' to 'tag_class' and remove Unicode comment in trace_tags.h. * trace : Add TODO comment for future implementation of tag_class activation * trace : Disable code related to tag_class until implementation is ready (#7663).
		
			
				
	
	
		
			796 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			796 lines
		
	
	
	
		
			27 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*++
 | |
|   Copyright (c) 2017 Microsoft Corporation
 | |
| 
 | |
| Module Name:
 | |
| 
 | |
|   sat_local_search.cpp
 | |
| 
 | |
| Abstract:
 | |
|    
 | |
|   Local search module for cardinality clauses.
 | |
| 
 | |
| Author:
 | |
| 
 | |
|   Sixue Liu 2017-2-21
 | |
| 
 | |
| Notes:
 | |
| 
 | |
| --*/
 | |
| 
 | |
| #include "sat/sat_local_search.h"
 | |
| #include "sat/sat_solver.h"
 | |
| #include "params/sat_params.hpp"
 | |
| #include "util/timer.h"
 | |
| 
 | |
| namespace sat {
 | |
| 
 | |
|     void local_search::init() {
 | |
|         flet<bool> _init(m_initializing, true);
 | |
|         m_unsat_stack.reset();
 | |
|         for (unsigned i = 0; i < m_assumptions.size(); ++i) {
 | |
|             add_clause(1, m_assumptions.data() + i);
 | |
|         }
 | |
|         if (m_is_unsat)
 | |
|             return;
 | |
|         
 | |
|         // add sentinel variable.
 | |
|         m_vars.push_back(var_info());
 | |
| 
 | |
|         if (m_config.phase_sticky()) {
 | |
|             for (var_info& vi : m_vars)
 | |
|                 if (!vi.m_unit) 
 | |
|                     vi.m_value = vi.m_bias > 50;
 | |
|         }
 | |
|         else {
 | |
|             for (var_info& vi : m_vars) 
 | |
|                 if (!vi.m_unit)
 | |
|                     vi.m_value = (0 == (m_rand() % 2));
 | |
|         }
 | |
| 
 | |
|         m_index_in_unsat_stack.resize(num_constraints(), 0);
 | |
|         set_parameters();
 | |
|     }
 | |
| 
 | |
|     void local_search::init_cur_solution() {
 | |
|         for (var_info& vi : m_vars) {
 | |
|             if (!vi.m_unit) {
 | |
|                 if (m_config.phase_sticky()) {
 | |
|                     vi.m_value = ((unsigned)(m_rand() % 100) < vi.m_bias);
 | |
|                 }
 | |
|                 else {
 | |
|                     vi.m_value = (m_rand() % 2) == 0;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // figure out slack, and init unsat stack
 | |
|     void local_search::init_slack() {
 | |
|         for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|             bool is_true = cur_solution(v);
 | |
|             coeff_vector& truep = m_vars[v].m_watch[is_true];
 | |
|             for (auto const& coeff : truep) {
 | |
|                 unsigned c = coeff.m_constraint_id;
 | |
|                 constraint& cn = m_constraints[c];
 | |
|                 cn.m_slack -= coeff.m_coeff;
 | |
|             }            
 | |
|         }
 | |
|         for (unsigned c = 0; c < num_constraints(); ++c) {
 | |
|             // violate the at-most-k constraint
 | |
|             if (m_constraints[c].m_slack < 0)
 | |
|                 unsat(c);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // figure out variables scores and slack_scores
 | |
|     void local_search::init_scores() {
 | |
|         for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|             bool is_true = cur_solution(v);
 | |
|             coeff_vector& truep = m_vars[v].m_watch[is_true];
 | |
|             coeff_vector& falsep = m_vars[v].m_watch[!is_true];
 | |
|             for (auto const& coeff : falsep) {
 | |
|                 constraint& c = m_constraints[coeff.m_constraint_id];
 | |
|                 // will --slack
 | |
|                 if (c.m_slack <= 0) {
 | |
|                     dec_slack_score(v);
 | |
|                     if (c.m_slack == 0)
 | |
|                         dec_score(v);
 | |
|                 }
 | |
|             }
 | |
|             for (auto const& coeff : truep) {
 | |
|                 constraint& c = m_constraints[coeff.m_constraint_id];
 | |
|                 // will --true_terms_count[c]
 | |
|                 // will ++slack
 | |
|                 if (c.m_slack <= -1) {
 | |
|                     inc_slack_score(v);
 | |
|                     if (c.m_slack == -1)
 | |
|                         inc_score(v);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // init goodvars 
 | |
|     void local_search::init_goodvars() {
 | |
|         m_goodvar_stack.reset();
 | |
|         for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|             if (score(v) > 0) { // && conf_change[v] == true
 | |
|                 m_vars[v].m_in_goodvar_stack = true;
 | |
|                 m_goodvar_stack.push_back(v);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::reinit() {
 | |
| 
 | |
|         //
 | |
|         // the following methods does NOT converge for pseudo-boolean
 | |
|         // can try other way to define "worse" and "better"
 | |
|         // the current best noise is below 1000
 | |
|         //
 | |
|         if (m_best_unsat_rate > m_last_best_unsat_rate) {
 | |
|             // worse
 | |
|             m_noise -= m_noise * 2 * m_noise_delta;
 | |
|             m_best_unsat_rate *= 1000.0;
 | |
|         }
 | |
|         else {
 | |
|             // better
 | |
|             m_noise += (10000 - m_noise) * m_noise_delta;
 | |
|         }
 | |
| 
 | |
|         for (constraint & c : m_constraints) {
 | |
|             c.m_slack = c.m_k;
 | |
|         }
 | |
|         
 | |
|         // init unsat stack
 | |
|         m_is_unsat = false;
 | |
|         m_unsat_stack.reset();
 | |
|         
 | |
|         // init solution using the bias
 | |
|         init_cur_solution();
 | |
|         
 | |
|         // init variable information
 | |
|         // the last variable is the virtual variable
 | |
| 
 | |
|         m_vars.back().m_score = INT_MIN;
 | |
|         m_vars.back().m_conf_change = false;       
 | |
|         m_vars.back().m_slack_score = INT_MIN;
 | |
|         m_vars.back().m_time_stamp = m_max_steps + 1;
 | |
|         for (unsigned i = 0; i < num_vars(); ++i) {
 | |
|             m_vars[i].m_time_stamp = 0;
 | |
|             m_vars[i].m_conf_change = true;
 | |
|             m_vars[i].m_in_goodvar_stack = false;
 | |
|             m_vars[i].m_score = 0;
 | |
|             m_vars[i].m_slack_score = 0;
 | |
|         }         
 | |
|         init_slack();
 | |
|         init_scores();
 | |
|         init_goodvars();
 | |
|         set_best_unsat();
 | |
| 
 | |
|         for (unsigned i = 0; !m_is_unsat && i < m_units.size(); ++i) {
 | |
|             bool_var v = m_units[i];
 | |
|             propagate(literal(v, !cur_solution(v)));
 | |
|         }
 | |
|         if (m_is_unsat) {
 | |
|             IF_VERBOSE(0, verbose_stream() << "unsat during reinit\n");
 | |
|         }
 | |
|         DEBUG_CODE(verify_slack(););
 | |
|     }
 | |
| 
 | |
|     bool local_search::propagate(literal lit) {
 | |
|         bool unit = is_unit(lit);
 | |
|         VERIFY(is_true(lit));
 | |
|         m_prop_queue.reset();
 | |
|         add_propagation(lit);
 | |
|         for (unsigned i = 0; i < m_prop_queue.size() && i < m_vars.size(); ++i) {
 | |
|             literal lit2 = m_prop_queue[i];
 | |
|             if (!is_true(lit2)) {
 | |
|                 if (is_unit(lit2)) {
 | |
|                     return false;
 | |
|                 }
 | |
|                 flip_walksat(lit2.var());
 | |
|                 add_propagation(lit2);
 | |
|             }
 | |
|         }
 | |
|         if (m_prop_queue.size() >= m_vars.size()) {
 | |
|             IF_VERBOSE(0, verbose_stream() << "propagation loop\n");
 | |
|             return false;
 | |
|         }
 | |
|         if (unit) {
 | |
|             for (literal lit2 : m_prop_queue) {
 | |
|                 VERIFY(is_true(lit2));
 | |
|                 add_unit(lit2, lit);
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void local_search::add_propagation(literal l) {
 | |
|         VERIFY(is_true(l));
 | |
|         for (literal lit : m_vars[l.var()].m_bin[l.sign()]) {
 | |
|             if (!is_true(lit)) {
 | |
|                 m_prop_queue.push_back(lit);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::set_best_unsat() {
 | |
|         m_best_unsat = m_unsat_stack.size();
 | |
|         m_best_phase.reserve(m_vars.size());
 | |
|         for (unsigned i = m_vars.size(); i-- > 0; ) {
 | |
|             m_best_phase[i] = m_vars[i].m_value;
 | |
|         }        
 | |
|     }    
 | |
|     
 | |
|     void local_search::verify_solution() const {
 | |
|         IF_VERBOSE(10, verbose_stream() << "verifying solution\n");
 | |
|         for (constraint const& c : m_constraints) 
 | |
|             verify_constraint(c);
 | |
|     }
 | |
| 
 | |
|     void local_search::verify_unsat_stack() const {        
 | |
|         for (unsigned i : m_unsat_stack) {
 | |
|             constraint const& c = m_constraints[i];
 | |
|             if (c.m_k >= constraint_value(c)) {
 | |
|                 IF_VERBOSE(0, display(verbose_stream() << i << " ", c) << "\n");
 | |
|                 IF_VERBOSE(0, verbose_stream() << "units " << m_units << "\n");
 | |
|             }
 | |
|             VERIFY(c.m_k < constraint_value(c));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool local_search::verify_goodvar() const {
 | |
|         unsigned g = 0;
 | |
|         for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|             if (conf_change(v) && score(v) > 0) {
 | |
|                 ++g;
 | |
|             }
 | |
|         }
 | |
|         return g == m_goodvar_stack.size();
 | |
|     }
 | |
| 
 | |
|     unsigned local_search::constraint_coeff(constraint const& c, literal l) const {
 | |
|         for (auto const& pb : m_vars[l.var()].m_watch[is_pos(l)]) {
 | |
|             if (pb.m_constraint_id == c.m_id) return pb.m_coeff;
 | |
|         }
 | |
|         UNREACHABLE();
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     void local_search::verify_constraint(constraint const& c) const {
 | |
|         uint64_t value = constraint_value(c);
 | |
|         IF_VERBOSE(11, display(verbose_stream() << "verify ", c););
 | |
|         TRACE(sat, display(verbose_stream() << "verify ", c););
 | |
|         if (c.m_k < value) {
 | |
|             IF_VERBOSE(0, display(verbose_stream() << "violated constraint: ", c) << "value: " << value << "\n";);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::verify_slack(constraint const& c) const {
 | |
|         VERIFY(constraint_value(c) + c.m_slack == c.m_k);
 | |
|     }
 | |
| 
 | |
|     void local_search::verify_slack() const {
 | |
|         for (constraint const& c : m_constraints) {
 | |
|             verify_slack(c);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     uint64_t local_search::constraint_value(constraint const& c) const {
 | |
|         uint64_t value = 0;
 | |
|         for (literal t : c) {
 | |
|             if (is_true(t)) {
 | |
|                 value += constraint_coeff(c, t);
 | |
|             }
 | |
|         }
 | |
|         return value;
 | |
|     }
 | |
|     
 | |
|     void local_search::add_clause(unsigned sz, literal const* c) {
 | |
|         add_cardinality(sz, c, sz - 1);
 | |
|     }
 | |
| 
 | |
|     // ~c <= k
 | |
|     void local_search::add_cardinality(unsigned sz, literal const* c, unsigned k) {
 | |
|         if (sz == 1 && k == 0) {
 | |
|             add_unit(c[0], null_literal);
 | |
|             return;
 | |
|         }
 | |
|         if (k == 1 && sz == 2) {
 | |
|             // IF_VERBOSE(0, verbose_stream() << "bin: " << ~c[0] << " + " << ~c[1] << " <= 1\n");
 | |
|             for (unsigned i = 0; i < 2; ++i) {
 | |
|                 literal t(c[i]), s(c[1-i]);
 | |
|                 m_vars.reserve(t.var() + 1);
 | |
|                 m_vars[t.var()].m_bin[is_pos(t)].push_back(s);
 | |
|             }
 | |
|         }
 | |
|         unsigned id = m_constraints.size();
 | |
|         m_constraints.push_back(constraint(k, id));
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             m_vars.reserve(c[i].var() + 1);
 | |
|             literal t(~c[i]);            
 | |
|             m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, 1));
 | |
|             m_constraints.back().push(t);
 | |
|         }
 | |
|         
 | |
|     }
 | |
| 
 | |
|     // c * coeffs <= k
 | |
|     void local_search::add_pb(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k) {
 | |
|         if (sz == 1 && k == 0) {     
 | |
|             add_unit(~c[0], null_literal);
 | |
|             return;
 | |
|         }
 | |
|         m_is_pb = true;
 | |
|         unsigned id = m_constraints.size();
 | |
|         m_constraints.push_back(constraint(k, id));
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             m_vars.reserve(c[i].var() + 1);            
 | |
|             literal t(c[i]);            
 | |
|             m_vars[t.var()].m_watch[is_pos(t)].push_back(pbcoeff(id, coeffs[i]));
 | |
|             m_constraints.back().push(t);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::add_unit(literal lit, literal exp) {
 | |
|         bool_var v = lit.var();
 | |
|         if (is_unit(lit)) {
 | |
|             if (m_vars[v].m_value == lit.sign()) {
 | |
|                 m_is_unsat = true;                
 | |
|             }
 | |
|             return;
 | |
|         }
 | |
|         SASSERT(!m_units.contains(v));
 | |
|         if (m_vars[v].m_value == lit.sign() && !m_initializing) {
 | |
|             flip_walksat(v);
 | |
|         }
 | |
|         m_vars[v].m_value = !lit.sign();
 | |
|         m_vars[v].m_bias  = lit.sign() ? 0 : 100;
 | |
|         m_vars[v].m_unit  = true;
 | |
|         m_vars[v].m_explain = exp;
 | |
|         m_units.push_back(v);
 | |
|         DEBUG_CODE(verify_unsat_stack(););
 | |
|     }
 | |
| 
 | |
|     void local_search::reinit(solver& s, bool_vector const& phase) {
 | |
|         import(s, true);
 | |
|         for (unsigned i = phase.size(); i-- > 0; )
 | |
|             set_phase(i, phase[i]);
 | |
|     }
 | |
| 
 | |
|     void local_search::import(solver const& s, bool _init) {
 | |
|         flet<bool> linit(m_initializing, true);
 | |
|         m_is_pb = false;
 | |
|         m_vars.reset();
 | |
|         m_constraints.reset();
 | |
|         m_units.reset();
 | |
|         m_unsat_stack.reset();
 | |
|         m_vars.reserve(s.num_vars());
 | |
|         m_config.set_config(s.get_config());
 | |
| 
 | |
|         unsigned v = 0;
 | |
|         if (m_config.phase_sticky()) 
 | |
|             for (var_info& vi : m_vars) 
 | |
|                 vi.m_bias = s.m_phase[v++] ? 98 : 2;        
 | |
| 
 | |
|         // copy units
 | |
|         unsigned trail_sz = s.init_trail_size();
 | |
|         for (unsigned i = 0; i < trail_sz; ++i) 
 | |
|             add_clause(1, s.m_trail.data() + i);
 | |
| 
 | |
|         // copy binary clauses
 | |
|         {
 | |
|             unsigned sz = s.m_watches.size();
 | |
|             for (unsigned l_idx = 0; l_idx < sz; ++l_idx) {
 | |
|                 literal l1 = ~to_literal(l_idx);
 | |
|                 watch_list const & wlist = s.m_watches[l_idx];
 | |
|                 for (watched const& w : wlist) {
 | |
|                     if (!w.is_binary_non_learned_clause())
 | |
|                         continue;
 | |
|                     literal l2 = w.get_literal();
 | |
|                     if (l1.index() > l2.index()) 
 | |
|                         continue;
 | |
|                     literal ls[2] = { l1, l2 };
 | |
|                     add_clause(2, ls);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // copy clauses
 | |
|         for (clause* c : s.m_clauses) 
 | |
|             add_clause(c->size(), c->begin());
 | |
|         m_num_non_binary_clauses = s.m_clauses.size();
 | |
| 
 | |
| 
 | |
|         // copy cardinality clauses
 | |
|         extension* ext = s.get_extension();
 | |
|         std::function<void(unsigned, literal const*, unsigned)> card = 
 | |
|             [&](unsigned sz, literal const* c, unsigned k) { add_cardinality(sz, c, k); };
 | |
|         std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)> pb = 
 | |
|             [&](unsigned sz, literal const* c, unsigned const* coeffs, unsigned k) { add_pb(sz, c, coeffs, k); };
 | |
|         if (ext && (!ext->is_pb() || !ext->extract_pb(card, pb))) {
 | |
|             IF_VERBOSE(0, verbose_stream() << (ext) << " is-pb " << (!ext && ext->is_pb()) << "\n");
 | |
|             throw default_exception("local search is incomplete with extensions beyond PB");
 | |
|         }
 | |
|         if (_init) 
 | |
|             init();        
 | |
|     }
 | |
| 
 | |
|     lbool local_search::check() {
 | |
|         return check(0, nullptr, nullptr);
 | |
|     }
 | |
| 
 | |
| #define PROGRESS(tries, flips)                                          \
 | |
|     if (tries % 10 == 0 || m_unsat_stack.empty()) {                     \
 | |
|         IF_VERBOSE(1, verbose_stream() << "(sat.local-search"           \
 | |
|                    << " :flips " << flips                               \
 | |
|                    << " :noise " << m_noise                             \
 | |
|                    << " :unsat " << /*m_unsat_stack.size()*/ m_best_unsat               \
 | |
|                    << " :constraints " << m_constraints.size()          \
 | |
|                    << " :time " << (timer.get_seconds() < 0.001 ? 0.0 : timer.get_seconds()) << ")\n";); \
 | |
|     }
 | |
| 
 | |
|     void local_search::walksat() {
 | |
|         m_best_unsat_rate = 1;
 | |
|         m_last_best_unsat_rate = 1;
 | |
| 
 | |
|         reinit();
 | |
|         DEBUG_CODE(verify_slack(););
 | |
|         timer timer;
 | |
|         unsigned step = 0, total_flips = 0, tries = 0;
 | |
|         
 | |
|         for (tries = 1; !m_unsat_stack.empty() && m_limit.inc(); ++tries) {
 | |
|             ++m_stats.m_num_restarts;
 | |
|             for (step = 0; step < m_max_steps && !m_unsat_stack.empty(); ++step) {
 | |
|                 pick_flip_walksat();
 | |
|                 if (m_unsat_stack.size() < m_best_unsat) {
 | |
|                     set_best_unsat();
 | |
|                     m_last_best_unsat_rate = m_best_unsat_rate;
 | |
|                     m_best_unsat_rate = (double)m_unsat_stack.size() / num_constraints();                                        
 | |
|                 }
 | |
|                 if (m_is_unsat) return;                    
 | |
|             }
 | |
|             total_flips += step;
 | |
|             PROGRESS(tries, total_flips);
 | |
|             if (m_par) {
 | |
|                 double max_avg = 0;
 | |
|                 for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|                     max_avg = std::max(max_avg, (double)m_vars[v].m_slow_break);
 | |
|                 }
 | |
|                 double sum = 0;
 | |
|                 for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|                     sum += exp(m_config.itau() * (m_vars[v].m_slow_break - max_avg));
 | |
|                 }
 | |
|                 if (sum == 0) {
 | |
|                     sum = 0.01;
 | |
|                 }
 | |
|                 for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|                     m_vars[v].m_break_prob = exp(m_config.itau() * (m_vars[v].m_slow_break - max_avg)) / sum;
 | |
|                 }
 | |
|                 
 | |
|                 m_par->to_solver(*this);
 | |
|             }
 | |
|             if (m_par && m_par->from_solver(*this)) {
 | |
|                 reinit();
 | |
|             }
 | |
|             if (tries % 10 == 0 && !m_unsat_stack.empty()) {
 | |
|                 reinit();
 | |
|             }            
 | |
|         }
 | |
|         PROGRESS(0, total_flips);
 | |
|     }
 | |
|     
 | |
|     lbool local_search::check(unsigned sz, literal const* assumptions, parallel* p) {
 | |
|         flet<parallel*> _p(m_par, p);
 | |
|         m_model.reset();
 | |
|         m_assumptions.reset();
 | |
|         m_assumptions.append(sz, assumptions);
 | |
|         unsigned num_units = m_units.size();
 | |
|         init();
 | |
|         if (m_is_unsat)
 | |
|             return l_false;
 | |
|         walksat();
 | |
| 
 | |
|         TRACE(sat, tout << m_units << "\n";);
 | |
|         // remove unit clauses
 | |
|         for (unsigned i = m_units.size(); i-- > num_units; ) {
 | |
|             m_vars[m_units[i]].m_unit = false;
 | |
|         }
 | |
|         m_units.shrink(num_units); 
 | |
| 
 | |
|         TRACE(sat, display(tout););
 | |
| 
 | |
|         lbool result;
 | |
|         if (m_is_unsat) {
 | |
|             result = l_false;
 | |
|         }
 | |
|         else if (m_unsat_stack.empty()) {
 | |
|             verify_solution();
 | |
|             extract_model();
 | |
|             result = l_true;
 | |
|         }
 | |
|         else {
 | |
|             result = l_undef;
 | |
|         }
 | |
|         m_vars.pop_back();  // remove sentinel variable
 | |
|         IF_VERBOSE(1, verbose_stream() << "(sat.local-search " << result << ")\n";);
 | |
|         IF_VERBOSE(20, display(verbose_stream()););
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void local_search::sat(unsigned c) {
 | |
|         unsigned last_unsat_constraint = m_unsat_stack.back();
 | |
|         int index = m_index_in_unsat_stack[c];
 | |
|         m_unsat_stack[index] = last_unsat_constraint;
 | |
|         m_index_in_unsat_stack[last_unsat_constraint] = index;
 | |
|         m_unsat_stack.pop_back();
 | |
|     }
 | |
| 
 | |
|     // swap the deleted one with the last one and pop
 | |
|     void local_search::unsat(unsigned c) {
 | |
|         m_index_in_unsat_stack[c] = m_unsat_stack.size();
 | |
|         m_unsat_stack.push_back(c);
 | |
|     }
 | |
| 
 | |
|     void local_search::pick_flip_lookahead() {
 | |
|         unsigned num_unsat = m_unsat_stack.size();
 | |
|         constraint const& c = m_constraints[m_unsat_stack[m_rand() % num_unsat]];
 | |
|         literal best = null_literal;
 | |
|         unsigned best_make = UINT_MAX;
 | |
|         for (literal lit : c.m_literals) {
 | |
|             if (!is_unit(lit) && is_true(lit)) {
 | |
|                 flip_walksat(lit.var());
 | |
|                 if (propagate(~lit) && best_make > m_unsat_stack.size()) {
 | |
|                     best = lit;
 | |
|                     best_make = m_unsat_stack.size();
 | |
|                 }
 | |
|                 flip_walksat(lit.var());
 | |
|                 propagate(lit);
 | |
|             }
 | |
|         }
 | |
|         if (best != null_literal) {
 | |
|             flip_walksat(best.var());
 | |
|             propagate(~best);
 | |
|         }        
 | |
|         else {
 | |
|             IF_VERBOSE(1, verbose_stream() << "(sat.local-search no best)\n");
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::pick_flip_walksat() {
 | |
|     reflip:
 | |
|         bool_var best_var = null_bool_var;
 | |
|         unsigned n = 1;
 | |
|         bool_var v = null_bool_var;
 | |
|         unsigned num_unsat = m_unsat_stack.size();
 | |
|         constraint const& c = m_constraints[m_unsat_stack[m_rand() % num_unsat]];
 | |
|         bool is_core = m_unsat_stack.size() <= 10;
 | |
|         if (m_rand() % 10000 <= m_noise) {
 | |
|             // take this branch with 98% probability.
 | |
|             // find the first one, to fast break the rest    
 | |
|             unsigned best_bsb = 0;
 | |
|             literal_vector::const_iterator cit = c.m_literals.begin(), cend = c.m_literals.end();
 | |
|             literal l;
 | |
|             for (; (cit != cend) && (!is_true(*cit) || is_unit(*cit)); ++cit) { }
 | |
|             if (cit == cend) {
 | |
|                 if (c.m_k < constraint_value(c)) {
 | |
|                     IF_VERBOSE(0, display(verbose_stream() << "unsat clause\n", c)); 
 | |
|                     m_is_unsat = true;
 | |
|                     return;
 | |
|                 }
 | |
|                 goto reflip;
 | |
|             }
 | |
|             l = *cit;
 | |
|             best_var = v = l.var();
 | |
|             bool tt = cur_solution(v);
 | |
|             coeff_vector const& falsep = m_vars[v].m_watch[!tt];
 | |
|             for (pbcoeff const& pbc : falsep) {
 | |
|                 int64_t slack = constraint_slack(pbc.m_constraint_id);
 | |
|                 if (slack < 0)
 | |
|                     ++best_bsb;
 | |
|                 else if (slack < static_cast<int64_t>(pbc.m_coeff))
 | |
|                     best_bsb += num_unsat;
 | |
|             }
 | |
|             ++cit;
 | |
|             for (; cit != cend; ++cit) {
 | |
|                 l = *cit;
 | |
|                 if (is_true(l) && !is_unit(l)) {
 | |
|                     v = l.var();                    
 | |
|                     unsigned bsb = 0;
 | |
|                     coeff_vector const& falsep = m_vars[v].m_watch[!cur_solution(v)];
 | |
|                     auto it = falsep.begin(), end = falsep.end();
 | |
|                     for (; it != end; ++it) {
 | |
|                         int64_t slack = constraint_slack(it->m_constraint_id);
 | |
|                         if (slack < 0) {
 | |
|                             if (bsb == best_bsb) {
 | |
|                                 break;
 | |
|                             }
 | |
|                             else {
 | |
|                                 ++bsb;
 | |
|                             }
 | |
|                         }
 | |
|                         else if (slack < static_cast<int64_t>(it->m_coeff)) {
 | |
|                             bsb += num_unsat;
 | |
|                             if (bsb > best_bsb) {
 | |
|                                 break;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                     if (it == end) {
 | |
|                         if (bsb < best_bsb) {
 | |
|                             best_bsb = bsb;
 | |
|                             best_var = v;
 | |
|                             n = 1;
 | |
|                         }
 | |
|                         else {// if (bsb == best_bb)
 | |
|                             ++n;
 | |
|                             if (m_rand() % n == 0) {
 | |
|                                 best_var = v;
 | |
|                             }
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             for (literal l : c) {
 | |
|                 if (is_true(l) && !is_unit(l)) {
 | |
|                     if (m_rand() % n == 0) {
 | |
|                         best_var = l.var();
 | |
|                     }
 | |
|                     ++n;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (best_var == null_bool_var) {
 | |
|             IF_VERBOSE(1, verbose_stream() << "(sat.local_search :unsat)\n");
 | |
|             return;
 | |
|         }
 | |
|         if (is_unit(best_var)) {
 | |
|             goto reflip;
 | |
|         }
 | |
|         flip_walksat(best_var);
 | |
|         literal lit(best_var, !cur_solution(best_var));
 | |
|         if (!propagate(lit)) {
 | |
|             if (is_true(lit)) { 
 | |
|                 flip_walksat(best_var);
 | |
|             }
 | |
|             add_unit(~lit, null_literal);
 | |
|             if (!propagate(~lit)) {
 | |
|                 IF_VERBOSE(2, verbose_stream() << "unsat\n");
 | |
|                 m_is_unsat = true;
 | |
|                 return;
 | |
|             }
 | |
|             if (m_unsat_stack.empty())
 | |
|                 return;
 | |
|             goto reflip;
 | |
|         }
 | |
| 
 | |
|         if (false && is_core && c.m_k < constraint_value(c)) {
 | |
|             goto reflip;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::flip_walksat(bool_var flipvar) {
 | |
| 
 | |
|         ++m_stats.m_num_flips;
 | |
|         VERIFY(!is_unit(flipvar));
 | |
|         m_vars[flipvar].m_value = !cur_solution(flipvar);
 | |
|         m_vars[flipvar].m_flips++;
 | |
|         m_vars[flipvar].m_slow_break.update(abs(m_vars[flipvar].m_slack_score));
 | |
| 
 | |
|         bool flip_is_true = cur_solution(flipvar);
 | |
|         coeff_vector const& truep = m_vars[flipvar].m_watch[flip_is_true];
 | |
|         coeff_vector const& falsep = m_vars[flipvar].m_watch[!flip_is_true];
 | |
| 
 | |
|         for (auto const& pbc : truep) {
 | |
|             unsigned ci = pbc.m_constraint_id;
 | |
|             constraint& c = m_constraints[ci];
 | |
|             auto old_slack = c.m_slack;
 | |
|             c.m_slack -= pbc.m_coeff;
 | |
|             DEBUG_CODE(verify_slack(c););
 | |
|             if (c.m_slack < 0 && old_slack >= 0) { // from non-negative to negative: sat -> unsat
 | |
|                 unsat(ci);
 | |
|             }
 | |
|         }
 | |
|         for (auto const& pbc : falsep) {
 | |
|             unsigned ci = pbc.m_constraint_id;
 | |
|             constraint& c = m_constraints[ci];
 | |
|             auto old_slack = c.m_slack;
 | |
|             c.m_slack += pbc.m_coeff;
 | |
|             DEBUG_CODE(verify_slack(c););
 | |
|             if (c.m_slack >= 0 && old_slack < 0) { // from negative to non-negative: unsat -> sat
 | |
|                 sat(ci);
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         DEBUG_CODE(verify_unsat_stack(););
 | |
|     }
 | |
| 
 | |
|     void local_search::set_parameters()  {
 | |
|         m_rand.set_seed(m_config.random_seed());
 | |
|         m_best_known_value = m_config.best_known_value();
 | |
| 
 | |
|         m_max_steps = std::min(static_cast<unsigned>(20 * num_vars()), static_cast<unsigned>(1 << 17)); // cut steps off at 100K
 | |
|         
 | |
|         TRACE(sat, 
 | |
|               tout << "seed:\t" << m_config.random_seed() << '\n';
 | |
|               tout << "best_known_value:\t" << m_config.best_known_value() << '\n';
 | |
|               tout << "max_steps:\t" << m_max_steps << '\n';
 | |
|               );
 | |
|     }
 | |
| 
 | |
|     void local_search::print_info(std::ostream& out) {
 | |
|         for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|             out << "v" << v << "\t" 
 | |
|                 << m_vars[v].m_neighbors.size() << '\t' 
 | |
|                 << cur_solution(v) << '\t' 
 | |
|                 << conf_change(v)  << '\t' 
 | |
|                 << score(v)        << '\t' 
 | |
|                 << slack_score(v)  << '\n';
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void local_search::extract_model() {
 | |
|         m_model.reset();
 | |
|         for (unsigned v = 0; v < num_vars(); ++v) {
 | |
|             m_model.push_back(cur_solution(v) ? l_true : l_false);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     std::ostream& local_search::display(std::ostream& out) const {
 | |
|         for (constraint const& c : m_constraints) {
 | |
|             display(out, c);
 | |
|         }        
 | |
|         for (bool_var v = 0; v < num_vars(); ++v) {
 | |
|             display(out, v, m_vars[v]);
 | |
|         }
 | |
|         return out;
 | |
|     }
 | |
|     
 | |
|     std::ostream& local_search::display(std::ostream& out, constraint const& c) const {
 | |
|         for (literal l : c) {
 | |
|             unsigned coeff = constraint_coeff(c, l);
 | |
|             if (coeff > 1) out << coeff << " * ";
 | |
|             out << l << " ";
 | |
|         }
 | |
|         return out << " <= " << c.m_k << " lhs value: " << constraint_value(c) << "\n";
 | |
|     }
 | |
|     
 | |
|     std::ostream& local_search::display(std::ostream& out, unsigned v, var_info const& vi) const {
 | |
|         out << "v" << v << " := " << (vi.m_value?"true":"false") << " bias: " << vi.m_bias;
 | |
|         if (vi.m_unit) out << " u " << vi.m_explain;
 | |
|         return out << "\n";
 | |
|     }
 | |
| 
 | |
|     void local_search::collect_statistics(statistics& st) const {
 | |
|         if (m_config.dbg_flips()) {
 | |
|             unsigned i = 0;
 | |
|             for (var_info const& vi : m_vars) {
 | |
|                 IF_VERBOSE(0, verbose_stream() << "flips: " << i << " " << vi.m_flips << " " << vi.m_slow_break  << "\n");               
 | |
|                 ++i;
 | |
|             }
 | |
|         }
 | |
|         st.update("local-search-flips", m_stats.m_num_flips);
 | |
|         st.update("local-search-restarts", m_stats.m_num_restarts);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void local_search::set_phase(bool_var v, bool f) {
 | |
|         unsigned& bias = m_vars[v].m_bias;
 | |
|         if (f  && bias < 100) bias++;
 | |
|         if (!f && bias > 0) bias--;
 | |
|     }
 | |
| 
 | |
|     void local_search::set_bias(bool_var v, lbool f) {
 | |
|         switch (f) {
 | |
|         case l_true: m_vars[v].m_bias = 99; break;
 | |
|         case l_false: m_vars[v].m_bias = 1; break;
 | |
|         default: break;
 | |
|         }
 | |
| 
 | |
|     }
 | |
| 
 | |
| }
 |