mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			3811 lines
		
	
	
	
		
			127 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			3811 lines
		
	
	
	
		
			127 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*++
 | |
| Copyright (c) 2017 Microsoft Corporation
 | |
| 
 | |
| Module Name:
 | |
| 
 | |
|     pb_solver.cpp
 | |
| 
 | |
| Abstract:
 | |
| 
 | |
|     Extension for cardinality reasoning.
 | |
| 
 | |
| Author:
 | |
| 
 | |
|     Nikolaj Bjorner (nbjorner) 2017-01-30
 | |
| 
 | |
| --*/
 | |
| 
 | |
| #include <cmath>
 | |
| #include "util/mpz.h"
 | |
| #include "sat/sat_types.h"
 | |
| #include "sat/smt/pb_solver.h"
 | |
| #include "sat/smt/euf_solver.h"
 | |
| #include "sat/sat_simplifier_params.hpp"
 | |
| #include "sat/sat_scc.h"
 | |
| 
 | |
| namespace pb {
 | |
| 
 | |
|     static unsigned _bad_id = 11111111; // 2759; //
 | |
| #define BADLOG(_cmd_) if (p.id() == _bad_id) { _cmd_; }
 | |
| 
 | |
| 
 | |
|     // -----------------------
 | |
|     // constraint
 | |
| 
 | |
|     void solver::set_conflict(constraint& c, literal lit) {
 | |
|         m_stats.m_num_conflicts++;
 | |
|         TRACE("pb", display(tout, c, true); );
 | |
|         if (!validate_conflict(c)) {
 | |
|             IF_VERBOSE(0, display(verbose_stream(), c, true));
 | |
|             UNREACHABLE();
 | |
|         }
 | |
|         SASSERT(validate_conflict(c));
 | |
|         SASSERT(value(lit) == l_false);
 | |
|         set_conflict(sat::justification::mk_ext_justification(s().scope_lvl(), c.cindex()), ~lit);
 | |
|         SASSERT(inconsistent());
 | |
|     }
 | |
| 
 | |
|     void solver::assign(constraint& c, literal lit) {
 | |
|         if (inconsistent()) return;
 | |
|         switch (value(lit)) {
 | |
|         case l_true: 
 | |
|             break;
 | |
|         case l_false: 
 | |
|             set_conflict(c, lit); 
 | |
|             break;
 | |
|         default:
 | |
|             m_stats.m_num_propagations++;
 | |
|             m_num_propagations_since_pop++;
 | |
|             //TRACE("pb", tout << "#prop: " << m_stats.m_num_propagations << " - " << c.lit() << " => " << lit << "\n";);
 | |
|             SASSERT(validate_unit_propagation(c, lit));
 | |
|             assign(lit, sat::justification::mk_ext_justification(s().scope_lvl(), c.cindex()));
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // -------------------
 | |
|     // pb_base
 | |
| 
 | |
|     void solver::simplify(constraint& p) {
 | |
|         SASSERT(s().at_base_lvl());
 | |
|         if (p.lit() != sat::null_literal && value(p.lit()) == l_false) {
 | |
|             TRACE("pb", tout << "pb: flip sign " << p << "\n";);
 | |
|             IF_VERBOSE(2, verbose_stream() << "sign is flipped " << p << "\n";);
 | |
|             return;
 | |
|         }
 | |
|         bool nullify = p.lit() != sat::null_literal && value(p.lit()) == l_true;
 | |
|         if (nullify) {
 | |
|             IF_VERBOSE(100, display(verbose_stream() << "nullify tracking literal\n", p, true););
 | |
|             SASSERT(lvl(p.lit()) == 0);
 | |
|             p.nullify_tracking_literal(*this);
 | |
|             init_watch(p);
 | |
|         }
 | |
| 
 | |
|         SASSERT(p.lit() == sat::null_literal || value(p.lit()) != l_false);
 | |
| 
 | |
|         unsigned true_val = 0, slack = 0, num_false = 0;
 | |
|         for (unsigned i = 0; i < p.size(); ++i) {
 | |
|             literal l = p.get_lit(i);
 | |
|             bool_var v = l.var();
 | |
|             if (s().was_eliminated(v)) {
 | |
|                 VERIFY(p.learned());
 | |
|                 remove_constraint(p, "contains eliminated");
 | |
|                 return;
 | |
|             }
 | |
|             switch (value(l)) {
 | |
|             case l_true: true_val += p.get_coeff(i); break;
 | |
|             case l_false: ++num_false; break;
 | |
|             default: slack += p.get_coeff(i); break;
 | |
|             }
 | |
|         }
 | |
|         if (p.k() == 1 && p.lit() == sat::null_literal) {
 | |
|             literal_vector lits(p.literals());
 | |
|             s().mk_clause(lits.size(), lits.data(), sat::status::th(p.learned(), get_id()));
 | |
|             IF_VERBOSE(100, display(verbose_stream() << "add clause: " << lits << "\n", p, true););
 | |
|             remove_constraint(p, "implies clause");
 | |
|         }
 | |
|         else if (true_val == 0 && num_false == 0) {
 | |
|             if (p.lit() == sat::null_literal || value(p.lit()) == l_true) {
 | |
|                 init_watch(p);
 | |
|             }
 | |
|         }
 | |
|         else if (true_val >= p.k()) {
 | |
|             IF_VERBOSE(100, display(verbose_stream() << "assign true literal ", p, true););
 | |
|             if (p.lit() != sat::null_literal) 
 | |
|                 s().assign_scoped(p.lit());
 | |
|             else 
 | |
|                 remove_constraint(p, "is true");
 | |
|         }
 | |
|         else if (slack + true_val < p.k()) {
 | |
|             if (p.lit() != sat::null_literal) {
 | |
|                 IF_VERBOSE(3, display(verbose_stream() << "assign false literal ", p, true););
 | |
|                 s().assign_scoped(~p.lit());
 | |
|             }
 | |
|             else {
 | |
|                 IF_VERBOSE(1, verbose_stream() << "unsat during simplification\n");
 | |
|                 s().set_conflict(sat::justification(0));
 | |
|             }
 | |
|         }
 | |
|         else if (slack + true_val == p.k()) {
 | |
|             literal_vector lits(p.literals());    
 | |
|             assert_unconstrained(p.lit(), lits);
 | |
|             remove_constraint(p, "is tight");
 | |
|         }
 | |
|         else {
 | |
| 
 | |
|             unsigned sz = p.size();
 | |
|             clear_watch(p);
 | |
|             unsigned j = 0;
 | |
|             for (unsigned i = 0; i < sz; ++i) {
 | |
|                 literal l = p.get_lit(i);
 | |
|                 if (value(l) == l_undef) {
 | |
|                     if (i != j)
 | |
|                         p.swap(i, j);
 | |
|                     ++j;                    
 | |
|                 }
 | |
|             }
 | |
|             sz = j;
 | |
|             // _bad_id = p.id();
 | |
|             BADLOG(display(verbose_stream() << "simplify ", p, true));
 | |
| 
 | |
|             unsigned k = p.k() - true_val;
 | |
| 
 | |
|             if (k == 1 && p.lit() == sat::null_literal) {
 | |
|                 literal_vector lits(sz, p.literals().data());
 | |
|                 s().mk_clause(sz, lits.data(), sat::status::th(p.learned(), get_id()));
 | |
|                 remove_constraint(p, "is clause");
 | |
|                 return;
 | |
|             }
 | |
|             p.set_size(sz);
 | |
|             p.set_k(k);
 | |
|             if (p.lit() == sat::null_literal || value(p.lit()) == l_true) {
 | |
|                 init_watch(p);
 | |
|             }
 | |
|             else {
 | |
|                 SASSERT(value(p.lit()) == l_undef);
 | |
|             }
 | |
|             BADLOG(display(verbose_stream() << "simplified ", p, true); verbose_stream() << "\n");
 | |
|             // display(verbose_stream(), c, true);
 | |
|             _bad_id = 11111111;
 | |
|             SASSERT(p.well_formed());
 | |
|             m_simplify_change = true;
 | |
| 
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief split PB constraint into two because root is reused in arguments.
 | |
|     
 | |
|       x <=> a*x + B*y >= k
 | |
|      
 | |
|       x => a*x + By >= k
 | |
|       ~x => a*x + By < k
 | |
|      
 | |
|       k*~x + a*x + By >= k
 | |
|       (B+a-k + 1)*x + a*~x + B*~y >= B + a - k + 1
 | |
| 
 | |
|       (k - a) * ~x + By >= k - a
 | |
|       k' * x + B'y >= k'
 | |
|        
 | |
|     */
 | |
|     
 | |
|     void solver::split_root(constraint& p) {
 | |
|         SASSERT(p.lit() != sat::null_literal);
 | |
|         SASSERT(!p.learned());
 | |
|         m_weights.resize(2*s().num_vars(), 0);
 | |
|         unsigned k = p.k();
 | |
|         unsigned w, w1, w2;
 | |
|         literal root = p.lit();
 | |
|         m_weights[(~root).index()] = k;
 | |
|         for (unsigned i = 0; i < p.size(); ++i) {
 | |
|             m_weights[p.get_lit(i).index()] += p.get_coeff(i);
 | |
|         }
 | |
|         literal_vector lits(p.literals());
 | |
|         lits.push_back(~root);
 | |
| 
 | |
|         for (literal l : lits) {
 | |
|             w1 = m_weights[l.index()];
 | |
|             w2 = m_weights[(~l).index()];
 | |
|             if (w1 >= w2) {
 | |
|                 if (w2 >= k) {
 | |
|                     for (literal l2 : lits) m_weights[l2.index()] = 0;
 | |
|                     // constraint is true
 | |
|                     return;
 | |
|                 }
 | |
|                 k -= w2;
 | |
|                 m_weights[(~l).index()] = 0;
 | |
|                 m_weights[l.index()] = w1 - w2;
 | |
|             }
 | |
|         }
 | |
|         SASSERT(k > 0);
 | |
| 
 | |
|         // ~root * (k - a) + p >= k - a
 | |
| 
 | |
|         m_wlits.reset();
 | |
|         for (literal l : lits) {
 | |
|             w = m_weights[l.index()];
 | |
|             if (w != 0) {
 | |
|                 m_wlits.push_back(wliteral(w, l));
 | |
|             } 
 | |
|             m_weights[l.index()] = 0;
 | |
|         }
 | |
|         
 | |
|         add_pb_ge(sat::null_literal, m_wlits, k, false);        
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // -------------------
 | |
|     // pb
 | |
| 
 | |
| 
 | |
|     /*
 | |
|       Chai Kuhlmann:
 | |
|       Lw - set of watched literals
 | |
|       Lu - set of unwatched literals that are not false
 | |
|       
 | |
|       Lw = Lw \ { alit }
 | |
|       Sw -= value
 | |
|       a_max = max { a | l in Lw u Lu, l = undef }
 | |
|       while (Sw < k + a_max & Lu != 0) {
 | |
|         a_s = max { a | l in Lu }
 | |
|         Sw += a_s
 | |
|         Lw = Lw u {l_s}
 | |
|         Lu = Lu \ {l_s}
 | |
|       }
 | |
|       if (Sw < k) return conflict
 | |
|       for (li in Lw | Sw < k + ai) 
 | |
|          assign li
 | |
|       return no-conflict
 | |
| 
 | |
|      a_max index: index of non-false literal with maximal weight.
 | |
|     */
 | |
| 
 | |
|     void solver::add_index(pbc& p, unsigned index, literal lit) {
 | |
|         if (value(lit) == l_undef) {
 | |
|             m_pb_undef.push_back(index);
 | |
|             if (p[index].first > m_a_max) {
 | |
|                 m_a_max = p[index].first;
 | |
|             }
 | |
|         }
 | |
|     }    
 | |
|     
 | |
|     /*
 | |
|       \brief propagate assignment to alit in constraint p.
 | |
|       
 | |
|       TBD: 
 | |
|       - consider reordering literals in watch list so that the search for watched literal takes average shorter time.
 | |
|       - combine with caching literals that are assigned to 'true' to a cold store where they are not being revisited.
 | |
|         Since 'true' literals may be unassigned (unless they are assigned at level 0) the cache has to be backtrack
 | |
|         friendly (and the overhead of backtracking has to be taken into account).
 | |
|      */
 | |
|     lbool solver::add_assign(pbc& p, literal alit) {
 | |
|         BADLOG(display(verbose_stream() << "assign: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true));
 | |
|         TRACE("pb", display(tout << "assign: " << alit << "\n", p, true););
 | |
|         SASSERT(!inconsistent());
 | |
|         unsigned sz = p.size();
 | |
|         unsigned bound = p.k();
 | |
|         unsigned num_watch = p.num_watch();
 | |
|         unsigned slack = p.slack();
 | |
|         SASSERT(value(alit) == l_false);
 | |
|         SASSERT(p.lit() == sat::null_literal || value(p.lit()) == l_true);
 | |
|         SASSERT(num_watch <= sz);
 | |
|         SASSERT(num_watch > 0);
 | |
|         SASSERT(validate_watch(p, sat::null_literal));
 | |
|         unsigned index = 0;
 | |
|         m_a_max = 0;
 | |
|         m_pb_undef.reset();
 | |
|         for (; index < num_watch; ++index) {
 | |
|             literal lit = p[index].second;
 | |
|             if (lit == alit) {
 | |
|                 break;
 | |
|             }
 | |
|             add_index(p, index, lit);
 | |
|         }
 | |
|         if (index == num_watch || num_watch == 0) {
 | |
|             _bad_id = p.id();
 | |
|             BADLOG(
 | |
|                 verbose_stream() << "BAD: " << p.id() << "\n";
 | |
|                 display(verbose_stream(), p, true);
 | |
|                 verbose_stream() << "alit: " << alit << "\n";
 | |
|                 verbose_stream() << "num watch " << num_watch << "\n");
 | |
|             UNREACHABLE();
 | |
|             return l_undef;
 | |
|         }
 | |
|         
 | |
|         
 | |
|         SASSERT(index < num_watch);
 | |
|         unsigned index1 = index + 1;
 | |
|         for (; m_a_max == 0 && index1 < num_watch; ++index1) {
 | |
|             add_index(p, index1, p[index1].second);
 | |
|         }
 | |
|         
 | |
|         unsigned val = p[index].first;
 | |
|         SASSERT(value(p[index].second) == l_false);
 | |
|         SASSERT(val <= slack);
 | |
|         slack -= val;
 | |
| 
 | |
|         // find literals to swap with:            
 | |
|         for (unsigned j = num_watch; j < sz && slack < bound + m_a_max; ++j) {
 | |
|             literal lit = p[j].second;
 | |
|             if (value(lit) != l_false) {
 | |
|                 slack += p[j].first;
 | |
|                 SASSERT(!p.is_watched(*this, p[j].second));
 | |
|                 p.watch_literal(*this, p[j].second);
 | |
|                 p.swap(num_watch, j);
 | |
|                 add_index(p, num_watch, lit);             
 | |
|                 ++num_watch;
 | |
|             }
 | |
|         }
 | |
|         
 | |
|         SASSERT(!inconsistent());
 | |
|         DEBUG_CODE(for (auto idx : m_pb_undef) { SASSERT(value(p[idx].second) == l_undef); });
 | |
|         
 | |
|         if (slack < bound) {
 | |
|             // maintain watching the literal
 | |
|             slack += val;
 | |
|             p.set_slack(slack);
 | |
|             p.set_num_watch(num_watch);
 | |
|             SASSERT(validate_watch(p, sat::null_literal));
 | |
|             BADLOG(display(verbose_stream() << "conflict: " << alit << " watch: " << p.num_watch() << " size: " << p.size(), p, true));            
 | |
|             SASSERT(bound <= slack);
 | |
|             TRACE("pb", tout << "conflict " << alit << "\n";);
 | |
|             set_conflict(p, alit);
 | |
|             return l_false;
 | |
|         }
 | |
| 
 | |
|         if (num_watch == 1) { _bad_id = p.id(); }
 | |
| 
 | |
|         BADLOG(verbose_stream() << "size: " << p.size() << " index: " << index << " num watch: " << num_watch << "\n");
 | |
| 
 | |
|         // swap out the watched literal.
 | |
|         --num_watch;
 | |
|         SASSERT(num_watch > 0);
 | |
|         p.set_slack(slack);
 | |
|         p.set_num_watch(num_watch);
 | |
|         p.swap(num_watch, index);
 | |
| 
 | |
| 
 | |
| 
 | |
|         // 
 | |
|         // slack >= bound, but slack - w(l) < bound 
 | |
|         // l must be true.
 | |
|         // 
 | |
|         if (slack < bound + m_a_max) {            
 | |
|             BADLOG(verbose_stream() << "slack " << slack << " " << bound << " " << m_a_max << "\n";);
 | |
|             TRACE("pb", tout << p << "\n"; for(auto j : m_pb_undef) tout << j << " "; tout << "\n";);
 | |
|             for (unsigned index1 : m_pb_undef) {
 | |
|                 if (index1 == num_watch) {
 | |
|                     index1 = index;
 | |
|                 }
 | |
|                 wliteral wl = p[index1];
 | |
|                 literal lit = wl.second;
 | |
|                 SASSERT(value(lit) == l_undef);
 | |
|                 if (slack < bound + wl.first) {
 | |
|                     BADLOG(verbose_stream() << "Assign " << lit << " " << wl.first << "\n");                
 | |
|                     assign(p, lit);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         SASSERT(validate_watch(p, alit)); // except that alit is still watched.
 | |
| 
 | |
|         TRACE("pb", display(tout << "assign: " << alit << "\n", p, true););
 | |
| 
 | |
|         BADLOG(verbose_stream() << "unwatch " << alit << " watch: " << p.num_watch() << " size: " << p.size() << " slack: " << p.slack() << " " << inconsistent() << "\n");
 | |
| 
 | |
|         return l_undef;
 | |
|     }
 | |
| 
 | |
|     void solver::recompile(pbc& p) {
 | |
|         // IF_VERBOSE(2, verbose_stream() << "re: " << p << "\n";);
 | |
|         SASSERT(p.num_watch() == 0);
 | |
|         m_weights.resize(2*s().num_vars(), 0);
 | |
|         for (wliteral wl : p) {
 | |
|             m_weights[wl.second.index()] += wl.first;
 | |
|         }
 | |
|         unsigned k = p.k();
 | |
|         unsigned sz = p.size();
 | |
|         bool all_units = true;
 | |
|         unsigned j = 0;
 | |
|         for (unsigned i = 0; i < sz && 0 < k; ++i) {
 | |
|             literal l = p[i].second;
 | |
|             unsigned w1 = m_weights[l.index()];
 | |
|             unsigned w2 = m_weights[(~l).index()];
 | |
|             if (w1 == 0 || w1 < w2) {
 | |
|                 continue;
 | |
|             }
 | |
|             else if (k <= w2) {
 | |
|                 k = 0;
 | |
|                 break;
 | |
|             }
 | |
|             else {
 | |
|                 SASSERT(w2 <= w1 && w2 < k);
 | |
|                 k -= w2;
 | |
|                 w1 -= w2;
 | |
|                 m_weights[l.index()] = 0;
 | |
|                 m_weights[(~l).index()] = 0;        
 | |
|                 if (w1 == 0) {
 | |
|                     continue;
 | |
|                 }    
 | |
|                 else {
 | |
|                     p[j] = wliteral(w1, l);            
 | |
|                     all_units &= w1 == 1;
 | |
|                     ++j;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         sz = j;
 | |
|         // clear weights
 | |
|         for (wliteral wl : p) {
 | |
|             m_weights[wl.second.index()] = 0;
 | |
|             m_weights[(~wl.second).index()] = 0;
 | |
|         }
 | |
| 
 | |
|         if (k == 0) {
 | |
|             if (p.lit() != sat::null_literal) {
 | |
|                 s().assign_scoped(p.lit());
 | |
|             }
 | |
|             remove_constraint(p, "recompiled to true");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         else if (k == 1 && p.lit() == sat::null_literal) {
 | |
|             literal_vector lits(sz, p.literals().data());
 | |
|             s().mk_clause(sz, lits.data(), sat::status::th(p.learned(), get_id()));
 | |
|             remove_constraint(p, "recompiled to clause");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         else if (all_units) {
 | |
|             literal_vector lits(sz, p.literals().data());
 | |
|             add_at_least(p.lit(), lits, k, p.learned());
 | |
|             remove_constraint(p, "recompiled to cardinality");
 | |
|             return;
 | |
|         }
 | |
|         else {
 | |
|             p.set_size(sz);
 | |
|             p.update_max_sum();
 | |
|             if (p.max_sum() < k) {
 | |
|                 if (p.lit() == sat::null_literal) {
 | |
|                     s().set_conflict(sat::justification(0));
 | |
|                 }
 | |
|                 else {
 | |
|                     s().assign_scoped(~p.lit());
 | |
|                 }
 | |
|                 remove_constraint(p, "recompiled to false");
 | |
|                 return;
 | |
|             }
 | |
|             p.set_k(k);
 | |
|             SASSERT(p.well_formed());
 | |
|             
 | |
|             if (clausify(p)) {
 | |
|                 return;
 | |
|             }
 | |
|             if (p.lit() == sat::null_literal || value(p.lit()) == l_true) {
 | |
|                 init_watch(p);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // ---------------------------
 | |
|     // conflict resolution
 | |
|     
 | |
| 
 | |
|     void solver::inc_coeff(literal l, unsigned offset) {
 | |
|         SASSERT(offset > 0);
 | |
|         bool_var v = l.var();
 | |
|         SASSERT(v != sat::null_bool_var);
 | |
|         m_coeffs.reserve(v + 1, 0);
 | |
|         TRACE("ba_verbose", tout << l << " " << offset << "\n";);
 | |
| 
 | |
|         int64_t coeff0 = m_coeffs[v];
 | |
|         if (coeff0 == 0) {
 | |
|             m_active_vars.push_back(v);
 | |
|         }
 | |
|         
 | |
|         int64_t loffset = static_cast<int64_t>(offset);
 | |
|         int64_t inc = l.sign() ? -loffset : loffset;
 | |
|         int64_t coeff1 = inc + coeff0;
 | |
|         m_coeffs[v] = coeff1;
 | |
|         if (coeff1 > INT_MAX || coeff1 < INT_MIN) {
 | |
|             m_overflow = true;
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (coeff0 > 0 && inc < 0) {
 | |
|             inc_bound(std::max((int64_t)0, coeff1) - coeff0);
 | |
|         }
 | |
|         else if (coeff0 < 0 && inc > 0) {
 | |
|             inc_bound(coeff0 - std::min((int64_t)0, coeff1));
 | |
|         }
 | |
|         int64_t lbound = static_cast<int64_t>(m_bound);
 | |
| 
 | |
|         // reduce coefficient to be no larger than bound.
 | |
|         if (coeff1 > lbound) {
 | |
|             m_coeffs[v] = lbound;
 | |
|         }
 | |
|         else if (coeff1 < 0 && -coeff1 > lbound) {
 | |
|             m_coeffs[v] = -lbound;
 | |
|         }        
 | |
|     }
 | |
| 
 | |
|     int64_t solver::get_coeff(bool_var v) const {
 | |
|         return m_coeffs.get(v, 0);
 | |
|     }
 | |
| 
 | |
|     uint64_t solver::get_coeff(literal lit) const {
 | |
|         int64_t c1 = get_coeff(lit.var());
 | |
|         SASSERT((c1 < 0) == lit.sign());
 | |
|         int64_t c = std::abs(c1);
 | |
|         m_overflow |= (c != c1);
 | |
|         return static_cast<uint64_t>(c);
 | |
|     }
 | |
| 
 | |
|     wliteral solver::get_wliteral(bool_var v) {
 | |
|         int64_t c1 = get_coeff(v);
 | |
|         literal l = literal(v, c1 < 0);
 | |
|         c1 = std::abs(c1);
 | |
|         unsigned c = static_cast<unsigned>(c1);
 | |
|         // TRACE("pb", tout << l << " " << c << "\n";);
 | |
|         m_overflow |= c != c1;
 | |
|         return wliteral(c, l);
 | |
|     }
 | |
| 
 | |
|     unsigned solver::get_abs_coeff(bool_var v) const {
 | |
|         int64_t c1 = std::abs(get_coeff(v));
 | |
|         unsigned c = static_cast<unsigned>(c1);
 | |
|         m_overflow |= c != c1;
 | |
|         return c;
 | |
|     }
 | |
| 
 | |
|     int solver::get_int_coeff(bool_var v) const {
 | |
|         int64_t c1 = m_coeffs.get(v, 0);
 | |
|         int c = static_cast<int>(c1);
 | |
|         m_overflow |= c != c1;
 | |
|         return c;
 | |
|     }
 | |
| 
 | |
|     void solver::inc_bound(int64_t i) {
 | |
|         int64_t new_bound = m_bound;
 | |
|         new_bound += i;
 | |
|         unsigned nb = static_cast<unsigned>(new_bound);
 | |
|         m_overflow |= new_bound < 0 || nb != new_bound;
 | |
|         m_bound = nb;        
 | |
|     }
 | |
| 
 | |
|     void solver::reset_coeffs() {
 | |
|         for (unsigned i = m_active_vars.size(); i-- > 0; ) {
 | |
|             m_coeffs[m_active_vars[i]] = 0;
 | |
|         }
 | |
|         m_active_vars.reset();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     static bool _debug_conflict = false;
 | |
|     static literal _debug_consequent = sat::null_literal;
 | |
|     static unsigned_vector _debug_var2position;
 | |
| 
 | |
| // #define DEBUG_CODE(_x_) _x_
 | |
| 
 | |
|     void solver::bail_resolve_conflict(unsigned idx) {
 | |
|         literal_vector const& lits = s().m_trail;
 | |
|         while (m_num_marks > 0) {
 | |
|             bool_var v = lits[idx].var();
 | |
|             if (s().is_marked(v)) {
 | |
|                 s().reset_mark(v);
 | |
|                 --m_num_marks;
 | |
|             }
 | |
|             if (idx == 0 && !_debug_conflict && m_num_marks > 0) {
 | |
|                 _debug_conflict = true;
 | |
|                 _debug_var2position.reserve(s().num_vars());
 | |
|                 for (unsigned i = 0; i < lits.size(); ++i) {
 | |
|                     _debug_var2position[lits[i].var()] = i;
 | |
|                 }
 | |
|                 IF_VERBOSE(0, verbose_stream() << "num marks: " << m_num_marks << "\n");
 | |
|                 IF_VERBOSE(0, 
 | |
|                            active2pb(m_A);
 | |
|                            uint64_t c = 0;
 | |
|                            for (wliteral l : m_A.m_wlits) c += l.first;
 | |
|                            verbose_stream() << "sum of coefficients: " << c << "\n";
 | |
|                            display(verbose_stream(), m_A, true);
 | |
|                            verbose_stream() << "conflicting literal: " << s().m_not_l << "\n";);
 | |
| 
 | |
|                 for (literal l : lits) {
 | |
|                     if (s().is_marked(l.var())) {
 | |
|                         IF_VERBOSE(0, verbose_stream() << "missing mark: " << l << "\n";);
 | |
|                         s().reset_mark(l.var());
 | |
|                     }
 | |
|                 }
 | |
|                 m_num_marks = 0;
 | |
|                 resolve_conflict();
 | |
|                 exit(0);
 | |
|             }
 | |
|             --idx;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     lbool solver::resolve_conflict() { 
 | |
|         if (0 == m_num_propagations_since_pop) 
 | |
|             return l_undef;
 | |
| 
 | |
|         if (s().m_config.m_pb_resolve == sat::PB_ROUNDING) 
 | |
|             return resolve_conflict_rs();
 | |
|        
 | |
|         m_overflow = false;
 | |
|         reset_coeffs();
 | |
|         m_num_marks = 0;
 | |
|         m_bound = 0;
 | |
|         literal consequent = s().m_not_l;
 | |
|         sat::justification js = s().m_conflict;
 | |
|         TRACE("pb", tout << consequent << " " << js << "\n";);
 | |
|         bool unique_max;
 | |
|         m_conflict_lvl = s().get_max_lvl(consequent, js, unique_max);
 | |
|         if (m_conflict_lvl == 0) {
 | |
|             return l_undef;
 | |
|         }
 | |
|         if (consequent != sat::null_literal) {
 | |
|             consequent.neg();
 | |
|             process_antecedent(consequent, 1);
 | |
|         }
 | |
|         literal_vector const& lits = s().m_trail;
 | |
|         unsigned idx = lits.size() - 1;
 | |
|         unsigned offset = 1;
 | |
|         DEBUG_CODE(active2pb(m_A););
 | |
| 
 | |
|         do {
 | |
| 
 | |
| 
 | |
|             if (m_overflow || offset > (1 << 12)) {
 | |
|                 IF_VERBOSE(20, verbose_stream() << "offset: " << offset << "\n";
 | |
|                            DEBUG_CODE(active2pb(m_A); display(verbose_stream(), m_A);););
 | |
|                 goto bail_out;
 | |
|             }
 | |
| 
 | |
|             if (offset == 0) {
 | |
|                 goto process_next_resolvent;            
 | |
|             }
 | |
| 
 | |
|             DEBUG_CODE(TRACE("sat_verbose", display(tout, m_A);););
 | |
|             TRACE("pb", tout << "process consequent: " << consequent << " : "; s().display_justification(tout, js) << "\n";);
 | |
|             SASSERT(offset > 0);
 | |
| 
 | |
|             DEBUG_CODE(justification2pb(js, consequent, offset, m_B););
 | |
|             
 | |
|             if (_debug_conflict) {
 | |
|                 IF_VERBOSE(0, 
 | |
|                            verbose_stream() << consequent << "\n";
 | |
|                            s().display_justification(verbose_stream(), js);
 | |
|                            verbose_stream() << "\n";);
 | |
|                 _debug_consequent = consequent;
 | |
|             }
 | |
|             switch(js.get_kind()) {
 | |
|             case sat::justification::NONE:
 | |
|                 SASSERT (consequent != sat::null_literal);
 | |
|                 inc_bound(offset);
 | |
|                 break;
 | |
|             case sat::justification::BINARY:
 | |
|                 inc_bound(offset);
 | |
|                 SASSERT (consequent != sat::null_literal);
 | |
|                 inc_coeff(consequent, offset);
 | |
|                 process_antecedent(js.get_literal(), offset);
 | |
|                 break;
 | |
|             case sat::justification::CLAUSE: {
 | |
|                 inc_bound(offset); 
 | |
|                 sat::clause & c = s().get_clause(js);
 | |
|                 unsigned i = 0;
 | |
|                 if (consequent != sat::null_literal) {
 | |
|                     inc_coeff(consequent, offset);
 | |
|                     if (c[0] == consequent) {
 | |
|                         i = 1;
 | |
|                     }
 | |
|                     else {
 | |
|                         SASSERT(c[1] == consequent);
 | |
|                         process_antecedent(c[0], offset);
 | |
|                         i = 2;
 | |
|                     }
 | |
|                 }
 | |
|                 unsigned sz = c.size();
 | |
|                 for (; i < sz; i++)
 | |
|                     process_antecedent(c[i], offset);
 | |
|                 break;
 | |
|             }
 | |
|             case sat::justification::EXT_JUSTIFICATION: {
 | |
|                 auto cindex = js.get_ext_justification_idx();
 | |
|                 auto* ext = sat::constraint_base::to_extension(cindex);
 | |
|                 if (ext != this) {
 | |
|                     m_lemma.reset();
 | |
|                     ext->get_antecedents(consequent, idx, m_lemma, false);
 | |
|                     for (literal l : m_lemma) process_antecedent(~l, offset);
 | |
|                     break;
 | |
|                 }
 | |
|                 constraint& cnstr = index2constraint(cindex);
 | |
|                 ++m_stats.m_num_resolves;
 | |
|                 switch (cnstr.tag()) {
 | |
|                 case pb::tag_t::card_t: {
 | |
|                     card& c = cnstr.to_card();
 | |
|                     inc_bound(static_cast<int64_t>(offset) * c.k());
 | |
|                     process_card(c, offset);
 | |
|                     break;
 | |
|                 }
 | |
|                 case pb::tag_t::pb_t: {
 | |
|                     pbc& p = cnstr.to_pb();
 | |
|                     m_lemma.reset();
 | |
|                     inc_bound(offset);
 | |
|                     inc_coeff(consequent, offset);
 | |
|                     get_antecedents(consequent, p, m_lemma);
 | |
|                     TRACE("pb", display(tout, p, true); tout << m_lemma << "\n";);
 | |
|                     if (_debug_conflict) {
 | |
|                         verbose_stream() << consequent << " ";
 | |
|                         verbose_stream() << "antecedents: " << m_lemma << "\n";
 | |
|                     }
 | |
|                     for (literal l : m_lemma) process_antecedent(~l, offset);
 | |
|                     break;
 | |
|                 }
 | |
|                 default:
 | |
|                     UNREACHABLE();
 | |
|                     break;
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             default:
 | |
|                 UNREACHABLE();
 | |
|                 break;
 | |
|             }
 | |
|             
 | |
|             SASSERT(validate_lemma());            
 | |
| 
 | |
|             DEBUG_CODE(
 | |
|                 active2pb(m_C);
 | |
|                 VERIFY(validate_resolvent());
 | |
|                 m_A = m_C;
 | |
|                 TRACE("pb", display(tout << "conflict: ", m_A);););
 | |
| 
 | |
|             cut();
 | |
| 
 | |
|         process_next_resolvent:
 | |
|             
 | |
|             // find the next marked variable in the assignment stack
 | |
|             bool_var v;
 | |
|             while (true) {
 | |
|                 consequent = lits[idx];
 | |
|                 v = consequent.var();
 | |
|                 if (s().is_marked(v)) {
 | |
|                     if (s().lvl(v) == m_conflict_lvl) {
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 if (idx == 0) {
 | |
|                     IF_VERBOSE(2, verbose_stream() << "did not find marked literal\n";);
 | |
|                     goto bail_out;
 | |
|                 }
 | |
|                 SASSERT(idx > 0);
 | |
|                 --idx;
 | |
|             }
 | |
|             
 | |
|             SASSERT(lvl(v) == m_conflict_lvl);
 | |
|             s().reset_mark(v);
 | |
|             --idx;
 | |
|             TRACE("sat_verbose", tout << "Unmark: v" << v << "\n";);
 | |
|             --m_num_marks;
 | |
|             js = s().m_justification[v];
 | |
|             offset = get_abs_coeff(v);
 | |
|             if (offset > m_bound) {
 | |
|                 int64_t bound64 = static_cast<int64_t>(m_bound);
 | |
|                 m_coeffs[v] = (get_coeff(v) < 0) ? -bound64 : bound64;
 | |
|                 offset = m_bound;
 | |
|                 DEBUG_CODE(active2pb(m_A););
 | |
|             }
 | |
|             SASSERT(value(consequent) == l_true);
 | |
|         }        
 | |
|         while (m_num_marks > 0);
 | |
|         
 | |
|         DEBUG_CODE(for (bool_var i = 0; i < static_cast<bool_var>(s().num_vars()); ++i) SASSERT(!s().is_marked(i)););
 | |
|         SASSERT(validate_lemma());
 | |
| 
 | |
|         if (!create_asserting_lemma()) {
 | |
|             goto bail_out;
 | |
|         }
 | |
|         active2lemma();
 | |
| 
 | |
| 
 | |
|         DEBUG_CODE(VERIFY(validate_conflict(m_lemma, m_A)););
 | |
|         
 | |
|         return l_true;
 | |
| 
 | |
|     bail_out:
 | |
|         if (m_overflow) {
 | |
|             ++m_stats.m_num_overflow;
 | |
|             m_overflow = false;
 | |
|         }
 | |
|         bail_resolve_conflict(idx);
 | |
|         return l_undef;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     unsigned solver::ineq::bv_coeff(bool_var v) const {
 | |
|         for (unsigned i = size(); i-- > 0; ) {
 | |
|             if (lit(i).var() == v) return coeff(i);
 | |
|         }
 | |
|         UNREACHABLE();
 | |
|         return 0;
 | |
|     }
 | |
| 
 | |
|     void solver::ineq::divide(unsigned c) {
 | |
|         if (c == 1) return;
 | |
|         for (unsigned i = size(); i-- > 0; ) {
 | |
|             m_wlits[i].first = (coeff(i) + c - 1) / c;
 | |
|         }
 | |
|         m_k = (m_k + c - 1) / c;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Remove literal at position i, subtract coefficient from bound.
 | |
|      */
 | |
|     void solver::ineq::weaken(unsigned i) {
 | |
|         unsigned ci = coeff(i);
 | |
|         SASSERT(m_k >= ci);
 | |
|         m_k -= ci;
 | |
|         m_wlits[i] = m_wlits.back();
 | |
|         m_wlits.pop_back();
 | |
|     }
 | |
| 
 | |
|     /** 
 | |
|      * Round coefficient of inequality to 1.
 | |
|      */
 | |
|     void solver::round_to_one(ineq& ineq, bool_var v) {
 | |
|         unsigned c = ineq.bv_coeff(v);
 | |
|         if (c == 1) return;
 | |
|         unsigned sz = ineq.size();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             unsigned ci = ineq.coeff(i); 
 | |
|             unsigned q = ci % c;
 | |
|             if (q != 0 && !is_false(ineq.lit(i))) {
 | |
| #if 1
 | |
|                 // code review by Elffers:
 | |
|                 ineq.weaken(i);
 | |
|                 --i;
 | |
|                 --sz;                
 | |
| #else
 | |
|                 if (q == ci) {
 | |
|                     ineq.weaken(i);
 | |
|                     --i;
 | |
|                     --sz;
 | |
|                 }
 | |
|                 else {
 | |
|                     ineq.m_wlits[i].first -= q;
 | |
|                     ineq.m_k -= q;
 | |
|                 }
 | |
| #endif
 | |
|             }
 | |
|         }
 | |
|         ineq.divide(c);
 | |
|         TRACE("pb", display(tout << "var: " << v << " " << c << ": ", ineq, true););
 | |
|     }
 | |
| 
 | |
|     void solver::round_to_one(bool_var w) {
 | |
|         unsigned c = get_abs_coeff(w);
 | |
|         if (c == 1 || c == 0) return;
 | |
|         for (bool_var v : m_active_vars) {
 | |
|             wliteral wl = get_wliteral(v);
 | |
|             unsigned q = wl.first % c;
 | |
|             if (q != 0 && !is_false(wl.second)) {
 | |
|                 m_coeffs[v] = wl.first - q;
 | |
|                 m_bound -= q;
 | |
|                 SASSERT(m_bound > 0);
 | |
|             }
 | |
|         }        
 | |
|         SASSERT(validate_lemma());
 | |
|         divide(c);
 | |
|         SASSERT(validate_lemma());
 | |
|         TRACE("pb", active2pb(m_B); display(tout, m_B, true););
 | |
|     }
 | |
| 
 | |
|     void solver::divide(unsigned c) {
 | |
|         SASSERT(c != 0);
 | |
|         if (c == 1) return;
 | |
|         reset_active_var_set();
 | |
|         unsigned j = 0, sz = m_active_vars.size();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             bool_var v = m_active_vars[i];
 | |
|             int ci = get_int_coeff(v);
 | |
|             if (!test_and_set_active(v) || ci == 0) continue;
 | |
|             if (ci > 0) {
 | |
|                 m_coeffs[v] = (ci + c - 1) / c;
 | |
|             }
 | |
|             else {
 | |
|                 m_coeffs[v] = -static_cast<int64_t>((-ci + c - 1) / c);
 | |
|             }
 | |
|             m_active_vars[j++] = v;
 | |
|         }
 | |
|         m_active_vars.shrink(j);
 | |
|         m_bound = static_cast<unsigned>((m_bound + c - 1) / c);                    
 | |
|     }
 | |
| 
 | |
|     void solver::resolve_on(literal consequent) {
 | |
|         round_to_one(consequent.var());
 | |
|         m_coeffs[consequent.var()] = 0;
 | |
|     }
 | |
| 
 | |
|     void solver::resolve_with(ineq const& ineq) {
 | |
|         TRACE("pb", display(tout, ineq, true););
 | |
|         inc_bound(ineq.m_k);        
 | |
|         TRACE("pb", tout << "bound: " << m_bound << "\n";);
 | |
| 
 | |
|         for (unsigned i = ineq.size(); i-- > 0; ) {
 | |
|             literal l = ineq.lit(i);
 | |
|             inc_coeff(l, static_cast<unsigned>(ineq.coeff(i)));
 | |
|             TRACE("pb", tout << "bound: " << m_bound << " lit: " << l << " coeff: " << ineq.coeff(i) << "\n";);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::reset_marks(unsigned idx) {
 | |
|         while (m_num_marks > 0) {
 | |
|             SASSERT(idx > 0);
 | |
|             bool_var v = s().m_trail[idx].var();
 | |
|             if (s().is_marked(v)) {
 | |
|                 s().reset_mark(v);
 | |
|                 --m_num_marks;
 | |
|             }
 | |
|             --idx;
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     /**
 | |
|      * \brief mark variables that are on the assignment stack but 
 | |
|      * below the current processing level.
 | |
|      */
 | |
|     void solver::mark_variables(ineq const& ineq) {
 | |
|         for (wliteral wl : ineq.m_wlits) {
 | |
|             literal l = wl.second;
 | |
|             if (!is_false(l)) continue;
 | |
|             bool_var v = l.var();
 | |
|             unsigned level = lvl(v);
 | |
|             if (!s().is_marked(v) && !is_visited(v) && level == m_conflict_lvl) {
 | |
|                 s().mark(v);
 | |
|                 ++m_num_marks;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     lbool solver::resolve_conflict_rs() {
 | |
|         if (0 == m_num_propagations_since_pop) {
 | |
|             return l_undef;
 | |
|         }
 | |
|         m_overflow = false;
 | |
|         reset_coeffs();
 | |
|         init_visited();
 | |
|         m_num_marks = 0;
 | |
|         m_bound = 0;
 | |
|         literal consequent = s().m_not_l;
 | |
|         sat::justification js = s().m_conflict;
 | |
|         bool unique_max;
 | |
|         m_conflict_lvl = s().get_max_lvl(consequent, js, unique_max);
 | |
|         if (m_conflict_lvl == 0) {
 | |
|             return l_undef;
 | |
|         }
 | |
|         if (consequent != sat::null_literal) {
 | |
|             consequent.neg();
 | |
|             process_antecedent(consequent, 1);
 | |
|         }
 | |
|         TRACE("pb", tout << consequent << " " << js << "\n";);
 | |
|         unsigned idx = s().m_trail.size() - 1;
 | |
|         
 | |
|         do {
 | |
|             TRACE("pb", s().display_justification(tout << "process consequent: " << consequent << " : ", js) << "\n";
 | |
|                   if (consequent != sat::null_literal) { active2pb(m_A); display(tout, m_A, true); }
 | |
|                   );
 | |
| 
 | |
|             switch (js.get_kind()) {
 | |
|             case sat::justification::NONE:
 | |
|                 SASSERT(consequent != sat::null_literal);
 | |
|                 round_to_one(consequent.var());
 | |
|                 inc_bound(1);
 | |
|                 inc_coeff(consequent, 1);
 | |
|                 break;
 | |
|             case sat::justification::BINARY:
 | |
|                 SASSERT(consequent != sat::null_literal);
 | |
|                 round_to_one(consequent.var());
 | |
|                 inc_bound(1);
 | |
|                 inc_coeff(consequent, 1);
 | |
|                 process_antecedent(js.get_literal());
 | |
|                 break;
 | |
|             case sat::justification::CLAUSE: {
 | |
|                 sat::clause & c = s().get_clause(js);
 | |
|                 unsigned i = 0;
 | |
|                 if (consequent != sat::null_literal) {
 | |
|                     round_to_one(consequent.var());
 | |
|                     inc_coeff(consequent, 1);
 | |
|                     if (c[0] == consequent) {
 | |
|                         i = 1;
 | |
|                     }
 | |
|                     else {
 | |
|                         SASSERT(c[1] == consequent);
 | |
|                         process_antecedent(c[0]);
 | |
|                         i = 2;
 | |
|                     }
 | |
|                 }
 | |
|                 inc_bound(1);
 | |
|                 unsigned sz = c.size();
 | |
|                 for (; i < sz; i++)
 | |
|                     process_antecedent(c[i]);
 | |
|                 break;
 | |
|             }
 | |
|             case sat::justification::EXT_JUSTIFICATION: {
 | |
|                 ++m_stats.m_num_resolves;
 | |
|                 sat::ext_justification_idx index = js.get_ext_justification_idx();
 | |
|                 auto* ext = sat::constraint_base::to_extension(index);
 | |
|                 if (ext != this) {
 | |
|                     m_lemma.reset();
 | |
|                     ext->get_antecedents(consequent, index, m_lemma, false);
 | |
|                     for (literal l : m_lemma) 
 | |
|                         process_antecedent(~l, 1);
 | |
|                     break;
 | |
|                 }
 | |
|                 constraint& p = index2constraint(index);
 | |
|                 SASSERT(!p.was_removed());
 | |
|                 unsigned k = p.k(), sz = p.size();
 | |
|                 m_A.reset(0);
 | |
|                 for (unsigned i = 0; i < sz; ++i) {
 | |
|                     literal l = p.get_lit(i);
 | |
|                     unsigned c = p.get_coeff(i);
 | |
|                     if (l == consequent || !is_visited(l.var())) {
 | |
|                         m_A.push(l, c);
 | |
|                     }
 | |
|                     else {
 | |
|                         SASSERT(k > c);
 | |
|                         TRACE("pb", tout << "visited: " << l << "\n";);
 | |
|                         k -= c;
 | |
|                     }
 | |
|                 }
 | |
|                 SASSERT(k > 0);
 | |
|                 if (p.lit() != sat::null_literal) m_A.push(~p.lit(), k);
 | |
|                 m_A.m_k = k;
 | |
| 
 | |
|                 mark_variables(m_A);
 | |
|                 if (consequent == sat::null_literal) {
 | |
|                     SASSERT(validate_ineq(m_A)); 
 | |
|                     m_bound = static_cast<unsigned>(m_A.m_k);
 | |
|                     for (wliteral wl : m_A.m_wlits) {
 | |
|                         process_antecedent(wl.second, wl.first);
 | |
|                     } 
 | |
|                 }
 | |
|                 else {
 | |
|                     round_to_one(consequent.var());
 | |
|                     if (p.is_pb()) round_to_one(m_A, consequent.var()); 
 | |
|                     SASSERT(validate_ineq(m_A)); 
 | |
|                     resolve_with(m_A);
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             default:
 | |
|                 UNREACHABLE();
 | |
|                 break;
 | |
|             }            
 | |
| 
 | |
|             SASSERT(validate_lemma());
 | |
|             cut();
 | |
| 
 | |
|             // find the next marked variable in the assignment stack
 | |
|             bool_var v;
 | |
|             while (true) {
 | |
|                 consequent = s().m_trail[idx];
 | |
|                 v = consequent.var();
 | |
|                 mark_visited(v);
 | |
|                 if (s().is_marked(v)) {
 | |
|                     int64_t c = get_coeff(v);
 | |
|                     if (c == 0 || ((c < 0) == consequent.sign())) {
 | |
|                         s().reset_mark(v);
 | |
|                         --m_num_marks;
 | |
|                     }
 | |
|                     else {
 | |
|                         break;
 | |
|                     }
 | |
|                 }
 | |
|                 if (idx == 0) {
 | |
|                     TRACE("pb", tout << "there is no consequent\n";);
 | |
|                     goto bail_out;
 | |
|                 }
 | |
|                 --idx;
 | |
|             }
 | |
|             
 | |
|             SASSERT(lvl(v) == m_conflict_lvl);
 | |
|             s().reset_mark(v);
 | |
|             --idx;
 | |
|             --m_num_marks;
 | |
|             js = s().m_justification[v];
 | |
|         }        
 | |
|         while (m_num_marks > 0 && !m_overflow);
 | |
|         TRACE("pb", active2pb(m_A); display(tout, m_A, true););
 | |
| 
 | |
|         // TBD: check if this is useful
 | |
|         if (!m_overflow && consequent != sat::null_literal) {
 | |
|             round_to_one(consequent.var());
 | |
|         }
 | |
|         if (!m_overflow && create_asserting_lemma()) {
 | |
|             active2lemma();
 | |
|             return l_true;
 | |
|         }
 | |
|         
 | |
|     bail_out:
 | |
|         TRACE("pb", tout << "bail " << m_overflow << "\n";);
 | |
|         if (m_overflow) {
 | |
|             ++m_stats.m_num_overflow;
 | |
|             m_overflow = false;
 | |
|         }
 | |
|         return l_undef;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     bool solver::create_asserting_lemma() {
 | |
|         int64_t bound64 = m_bound;
 | |
|         int64_t slack = -bound64;
 | |
|         reset_active_var_set();
 | |
|         unsigned j = 0, sz = m_active_vars.size();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             bool_var v = m_active_vars[i];
 | |
|             unsigned c = get_abs_coeff(v);
 | |
|             if (!test_and_set_active(v) || c == 0) continue;
 | |
|             slack += c;
 | |
|             m_active_vars[j++] = v;
 | |
|         }
 | |
|         m_active_vars.shrink(j);
 | |
|         m_lemma.reset();        
 | |
|         m_lemma.push_back(sat::null_literal);
 | |
|         unsigned num_skipped = 0;
 | |
|         int64_t asserting_coeff = 0;
 | |
|         for (unsigned i = 0; 0 <= slack && i < m_active_vars.size(); ++i) { 
 | |
|             bool_var v = m_active_vars[i];
 | |
|             int64_t coeff = get_coeff(v);
 | |
|             lbool val = value(v);
 | |
|             bool is_true = val == l_true;
 | |
|             bool append = coeff != 0 && val != l_undef && ((coeff < 0) == is_true);
 | |
|             if (append) {
 | |
|                 literal lit(v, !is_true);
 | |
|                 if (lvl(lit) == m_conflict_lvl) {
 | |
|                     if (m_lemma[0] == sat::null_literal) {
 | |
|                         asserting_coeff = std::abs(coeff);
 | |
|                         slack -= asserting_coeff;
 | |
|                         m_lemma[0] = ~lit;
 | |
|                     }
 | |
|                     else {
 | |
|                         ++num_skipped;
 | |
|                         if (asserting_coeff < std::abs(coeff)) {
 | |
|                             m_lemma[0] = ~lit;
 | |
|                             slack -= (std::abs(coeff) - asserting_coeff);
 | |
|                             asserting_coeff = std::abs(coeff);
 | |
|                         }
 | |
|                     }
 | |
|                 }
 | |
|                 else if (lvl(lit) < m_conflict_lvl) {
 | |
|                     slack -= std::abs(coeff);
 | |
|                     m_lemma.push_back(~lit);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         if (slack >= 0) {
 | |
|             TRACE("pb", tout << "slack is non-negative\n";);
 | |
|             IF_VERBOSE(20, verbose_stream() << "(sat.card slack: " << slack << " skipped: " << num_skipped << ")\n";);
 | |
|             return false;
 | |
|         }
 | |
|         if (m_overflow) {
 | |
|             TRACE("pb", tout << "overflow\n";);
 | |
|             return false;
 | |
|         }        
 | |
|         if (m_lemma[0] == sat::null_literal) {
 | |
|             if (m_lemma.size() == 1) {
 | |
|                 s().set_conflict(sat::justification(0));
 | |
|             }
 | |
|             TRACE("pb", tout << "no asserting literal\n";);
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         TRACE("pb", tout << m_lemma << "\n";);
 | |
| 
 | |
|         if (get_config().m_drat && m_solver) {
 | |
|             s().m_drat.add(m_lemma, sat::status::th(true, get_id()));
 | |
|         }
 | |
| 
 | |
|         s().m_lemma.reset();
 | |
|         s().m_lemma.append(m_lemma);
 | |
|         for (unsigned i = 1; i < m_lemma.size(); ++i) {
 | |
|             CTRACE("pb", s().is_marked(m_lemma[i].var()), tout << "marked: " << m_lemma[i] << "\n";);
 | |
|             s().mark(m_lemma[i].var());
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief compute a cut for current resolvent.
 | |
|      */
 | |
| 
 | |
|     void solver::cut() {
 | |
| 
 | |
|         // bypass cut if there is a unit coefficient
 | |
|         for (bool_var v : m_active_vars) {
 | |
|             if (1 == get_abs_coeff(v)) return;
 | |
|         }
 | |
| 
 | |
|         unsigned g = 0;
 | |
| 
 | |
|         for (unsigned i = 0; g != 1 && i < m_active_vars.size(); ++i) {
 | |
|             bool_var v = m_active_vars[i];
 | |
|             unsigned coeff = get_abs_coeff(v);
 | |
|             if (coeff == 0) {
 | |
|                 continue;
 | |
|             }
 | |
|             if (m_bound < coeff) {
 | |
|                 int64_t bound64 = m_bound;
 | |
|                 if (get_coeff(v) > 0) {
 | |
|                     m_coeffs[v] = bound64;
 | |
|                 }
 | |
|                 else {
 | |
|                     m_coeffs[v] = -bound64;
 | |
|                 }
 | |
|                 coeff = m_bound;
 | |
|             }
 | |
|             SASSERT(0 < coeff && coeff <= m_bound);
 | |
|             if (g == 0) {
 | |
|                 g = coeff;
 | |
|             }
 | |
|             else {
 | |
|                 g = u_gcd(g, coeff);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         if (g >= 2) {
 | |
|             reset_active_var_set();
 | |
|             unsigned j = 0, sz = m_active_vars.size();
 | |
|             for (unsigned i = 0; i < sz; ++i) {
 | |
|                 bool_var v = m_active_vars[i];
 | |
|                 int64_t c = m_coeffs[v];
 | |
|                 if (!test_and_set_active(v) || c == 0) continue;
 | |
|                 m_coeffs[v] /= static_cast<int>(g);
 | |
|                 m_active_vars[j++] = v;
 | |
|             }
 | |
|             m_active_vars.shrink(j);
 | |
|             m_bound = (m_bound + g - 1) / g;
 | |
|             ++m_stats.m_num_cut;
 | |
|         }        
 | |
|     }
 | |
| 
 | |
|     void solver::process_card(card& c, unsigned offset) {
 | |
|         literal lit = c.lit();
 | |
|         SASSERT(c.k() <= c.size());       
 | |
|         SASSERT(lit == sat::null_literal || value(lit) != l_undef);
 | |
|         SASSERT(0 < offset);
 | |
|         for (unsigned i = c.k(); i < c.size(); ++i) {
 | |
|             process_antecedent(c[i], offset);
 | |
|         }
 | |
|         for (unsigned i = 0; i < c.k(); ++i) {
 | |
|             inc_coeff(c[i], offset);                        
 | |
|         }
 | |
|         if (lit != sat::null_literal) {
 | |
|             uint64_t offset1 = static_cast<uint64_t>(offset) * c.k();
 | |
|             if (offset1 > UINT_MAX) {
 | |
|                 m_overflow = true;
 | |
|             }
 | |
|             if (value(lit) == l_true) {
 | |
|                 process_antecedent(~lit, static_cast<unsigned>(offset1));
 | |
|             }
 | |
|             else {
 | |
|                 process_antecedent(lit, static_cast<unsigned>(offset1));
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::process_antecedent(literal l, unsigned offset) {
 | |
|         SASSERT(value(l) == l_false);
 | |
|         bool_var v = l.var();
 | |
|         unsigned level = lvl(v);
 | |
| 
 | |
|         if (!s().is_marked(v) && level == m_conflict_lvl) {
 | |
|             s().mark(v);
 | |
|             ++m_num_marks;
 | |
|             if (_debug_conflict && _debug_consequent != sat::null_literal && _debug_var2position[_debug_consequent.var()] < _debug_var2position[l.var()]) {
 | |
|                 IF_VERBOSE(0, verbose_stream() << "antecedent " << l << " is above consequent in stack\n";);
 | |
|             }
 | |
|         }
 | |
|         inc_coeff(l, offset);                
 | |
|     }   
 | |
| 
 | |
|     literal solver::get_asserting_literal(literal p) {
 | |
|         if (get_abs_coeff(p.var()) != 0) {
 | |
|             return p;
 | |
|         }
 | |
|         unsigned level = 0;        
 | |
|         for (unsigned i = 0; i < m_active_vars.size(); ++i) { 
 | |
|             bool_var v = m_active_vars[i];
 | |
|             literal lit(v, get_coeff(v) < 0);
 | |
|             if (value(lit) == l_false && lvl(lit) > level) {
 | |
|                 p = lit;
 | |
|                 level = lvl(lit);
 | |
|             }
 | |
|         }
 | |
|         return p;        
 | |
|     }
 | |
| 
 | |
|     solver::solver(euf::solver& ctx, euf::theory_id id) :
 | |
|         solver(ctx.get_manager(), ctx.get_si(), id) {
 | |
|         m_ctx = &ctx;
 | |
|     }
 | |
| 
 | |
|     solver::solver(ast_manager& m, sat::sat_internalizer& si, euf::theory_id id)
 | |
|         : euf::th_solver(m, symbol("pb"), id),
 | |
|           si(si), m_pb(m),
 | |
|           m_lookahead(nullptr), 
 | |
|           m_constraint_id(0), m_ba(*this), m_sort(m_ba) {
 | |
|         m_num_propagations_since_pop = 0;
 | |
|     }
 | |
| 
 | |
|     solver::~solver() {
 | |
|         m_stats.reset();
 | |
|         for (constraint* c : m_constraints) {
 | |
|             c->deallocate(m_allocator);
 | |
|         }
 | |
|         for (constraint* c : m_learned) {
 | |
|             c->deallocate(m_allocator);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::add_at_least(bool_var v, literal_vector const& lits, unsigned k) {
 | |
|         literal lit = v == sat::null_bool_var ? sat::null_literal : literal(v, false);
 | |
|         add_at_least(lit, lits, k, m_is_redundant);
 | |
|     }
 | |
| 
 | |
|     constraint* solver::add_at_least(literal lit, literal_vector const& lits, unsigned k, bool learned) {
 | |
|         if (k == 1 && lit == sat::null_literal) {
 | |
|             literal_vector _lits(lits);
 | |
|             s().mk_clause(_lits.size(), _lits.data(), sat::status::th(learned, get_id()));
 | |
|             return nullptr;
 | |
|         }
 | |
| 
 | |
|         if (k == 0) {
 | |
|             if (lit != sat::null_literal)
 | |
|                 s().add_clause(lit, sat::status::th(false, get_id()));
 | |
|             return nullptr;
 | |
|         }
 | |
| 
 | |
|         if (k > lits.size()) {
 | |
|             if (lit == sat::null_literal)
 | |
|                 s().add_clause(0, nullptr, sat::status::th(false, get_id()));
 | |
|             else
 | |
|                 s().add_clause(~lit, sat::status::th(false, get_id()));
 | |
|             return nullptr;
 | |
|         }
 | |
| 
 | |
|         if (!learned && clausify(lit, lits.size(), lits.data(), k)) {
 | |
|             return nullptr;
 | |
|         }
 | |
|         void * mem = m_allocator.allocate(card::get_obj_size(lits.size()));
 | |
|         sat::constraint_base::initialize(mem, this);
 | |
|         card* c = new (sat::constraint_base::ptr2mem(mem)) card(next_id(), lit, lits, k);
 | |
|         c->set_learned(learned);
 | |
|         add_constraint(c);
 | |
|         return c;
 | |
|     }
 | |
| 
 | |
|     void solver::add_constraint(constraint* c) {
 | |
|         literal_vector lits(c->literals());
 | |
|         if (c->learned()) {
 | |
|             m_learned.push_back(c);
 | |
|         }
 | |
|         else {
 | |
|             SASSERT(!m_solver || s().at_base_lvl());
 | |
|             m_constraints.push_back(c);
 | |
|         }
 | |
|         literal lit = c->lit();
 | |
|         if (c->learned() && m_solver && !s().at_base_lvl()) {
 | |
|             SASSERT(lit == sat::null_literal);
 | |
|             // gets initialized after backjump.
 | |
|             m_constraint_to_reinit.push_back(c);
 | |
|         }
 | |
|         else if (lit == sat::null_literal) {
 | |
|             init_watch(*c);
 | |
|             if (c->is_pb()) 
 | |
|                 validate_watch(c->to_pb(), sat::null_literal);
 | |
|         }
 | |
|         else {
 | |
|             if (m_solver) m_solver->set_external(lit.var());
 | |
|             c->watch_literal(*this, lit);
 | |
|             c->watch_literal(*this, ~lit);
 | |
|         }     
 | |
|         if (!c->well_formed()) 
 | |
|             IF_VERBOSE(0, verbose_stream() << *c << "\n");
 | |
|         SASSERT(c->well_formed());
 | |
|         VERIFY(c->well_formed());
 | |
|         if (m_solver && m_solver->get_config().m_drat) {
 | |
|             auto * out = s().get_drat().out();
 | |
|             if (out)
 | |
|                 *out << "c ba constraint " << *c << " 0\n";
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     bool solver::init_watch(constraint& c) {
 | |
|         return !inconsistent() && c.init_watch(*this);
 | |
|     }
 | |
| 
 | |
|     lbool solver::add_assign(constraint& c, literal l) {
 | |
|         switch (c.tag()) {
 | |
|         case pb::tag_t::card_t: return add_assign(c.to_card(), l);
 | |
|         case pb::tag_t::pb_t: return add_assign(c.to_pb(), l);
 | |
|         }
 | |
|         UNREACHABLE();
 | |
|         return l_undef;
 | |
|     }
 | |
| 
 | |
|     constraint* solver::add_pb_ge(literal lit, svector<wliteral> const& wlits, unsigned k, bool learned) {
 | |
|         bool units = true;
 | |
|         for (wliteral wl : wlits) 
 | |
|             units &= wl.first == 1;
 | |
| 
 | |
|         if (k == 0) {
 | |
|             if (lit != sat::null_literal)
 | |
|                 s().add_clause(lit, sat::status::th(false, get_id()));
 | |
|             return nullptr;
 | |
|         }
 | |
|         rational weight(0);
 | |
|         for (auto const &[w, l] : wlits)
 | |
|             weight += w;
 | |
|         if (weight < k) {
 | |
|             if (lit == sat::null_literal)
 | |
|                 s().add_clause(0, nullptr, sat::status::th(false, get_id()));
 | |
|             else
 | |
|                 s().add_clause(~lit, sat::status::th(false, get_id()));
 | |
|             return nullptr;
 | |
|         }
 | |
|         if (!learned) {
 | |
|             for (wliteral wl : wlits) 
 | |
|                 s().set_external(wl.second.var()); 
 | |
|         }
 | |
|         if (units || k == 1) {
 | |
|             literal_vector lits;
 | |
|             for (wliteral wl : wlits) lits.push_back(wl.second);
 | |
|             return add_at_least(lit, lits, k, learned);
 | |
|         }
 | |
|         void * mem = m_allocator.allocate(pbc::get_obj_size(wlits.size()));
 | |
|         sat::constraint_base::initialize(mem, this);
 | |
|         pbc* p = new (sat::constraint_base::ptr2mem(mem)) pbc(next_id(), lit, wlits, k);
 | |
|         p->set_learned(learned);
 | |
|         add_constraint(p);
 | |
|         return p;
 | |
|     }
 | |
| 
 | |
|     void solver::add_pb_ge(bool_var v, bool sign, svector<wliteral> const& wlits, unsigned k) {
 | |
|         literal lit = v == sat::null_bool_var ? sat::null_literal : literal(v, sign);
 | |
|         add_pb_ge(lit, wlits, k, m_is_redundant);
 | |
|     }
 | |
| 
 | |
|     bool solver::is_external(bool_var v) {
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief return true to keep watching literal.
 | |
|     */
 | |
|     bool solver::propagated(literal l, sat::ext_constraint_idx idx) {
 | |
|         SASSERT(value(l) == l_true);
 | |
|         constraint& c = index2constraint(idx);
 | |
|         if (c.lit() != sat::null_literal && l.var() == c.lit().var()) {
 | |
|             init_watch(c);
 | |
|             return true;
 | |
|         }
 | |
|         else if (c.lit() != sat::null_literal && value(c.lit()) != l_true) {
 | |
|             return true;
 | |
|         }
 | |
|         else {
 | |
|             return l_undef != add_assign(c, ~l);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     double solver::get_reward(literal l, sat::ext_justification_idx idx, sat::literal_occs_fun& occs) const {
 | |
|         constraint const& c = index2constraint(idx);
 | |
|         return c.get_reward(*this, occs);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void solver::ensure_parity_size(bool_var v) {
 | |
|         if (m_parity_marks.size() <= static_cast<unsigned>(v)) {
 | |
|             m_parity_marks.resize(static_cast<unsigned>(v) + 1, 0);
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     unsigned solver::get_parity(bool_var v) {
 | |
|         return m_parity_marks.get(v, 0);
 | |
|     }
 | |
| 
 | |
|     void solver::inc_parity(bool_var v) {
 | |
|         ensure_parity_size(v);
 | |
|         m_parity_marks[v]++;
 | |
|     }
 | |
| 
 | |
|     void solver::reset_parity(bool_var v) {
 | |
|         ensure_parity_size(v);
 | |
|         m_parity_marks[v] = 0;
 | |
|     }
 | |
|     
 | |
| 
 | |
|     /**
 | |
|        \brief retrieve a sufficient set of literals from p that imply l.
 | |
|        
 | |
|        Find partition: 
 | |
| 
 | |
|          - Ax + coeff*l + B*y >= k
 | |
|          - all literals in x are false.
 | |
|          - B < k
 | |
| 
 | |
|        Then x is an explanation for l
 | |
| 
 | |
|      */
 | |
| 
 | |
|     bool solver::assigned_above(literal above, literal below) {
 | |
|         unsigned l = lvl(above);
 | |
|         SASSERT(l == lvl(below));
 | |
|         if (l == 0) return false;
 | |
|         unsigned start = s().m_scopes[l-1].m_trail_lim;
 | |
|         literal_vector const& lits = s().m_trail;
 | |
|         for (unsigned sz = lits.size(); sz-- > start; ) {
 | |
|             if (lits[sz] == above) return true;
 | |
|             if (lits[sz] == below) return false;
 | |
|         }
 | |
|         UNREACHABLE();
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     void solver::get_antecedents(literal l, pbc const& p, literal_vector& r) {
 | |
|         TRACE("pb", display(tout << l << " level: " << s().scope_lvl() << " ", p, true););
 | |
|         SASSERT(p.lit() == sat::null_literal || value(p.lit()) == l_true);
 | |
| 
 | |
|         if (p.lit() != sat::null_literal) {
 | |
|             r.push_back(p.lit());
 | |
|         }
 | |
| 
 | |
|         unsigned k = p.k();
 | |
| 
 | |
|         if (_debug_conflict) {
 | |
|             IF_VERBOSE(0, display(verbose_stream(), p, true);
 | |
|                        verbose_stream() << "literal: " << l << " value: " << value(l) << " num-watch: " << p.num_watch() << " slack: " << p.slack() << "\n";);
 | |
|         }
 | |
| 
 | |
|         if (value(l) == l_false) {
 | |
|             // The literal comes from a conflict.
 | |
|             // it is forced true, but assigned to false.
 | |
|             unsigned slack = 0;
 | |
|             for (wliteral wl : p) {
 | |
|                 if (value(wl.second) != l_false) {
 | |
|                     slack += wl.first;
 | |
|                 }
 | |
|             }
 | |
|             SASSERT(slack < k);
 | |
|             for (wliteral wl : p) {
 | |
|                 literal lit = wl.second;
 | |
|                 if (lit != l && value(lit) == l_false) {
 | |
|                     unsigned w = wl.first;
 | |
|                     if (slack + w < k) {
 | |
|                         slack += w;
 | |
|                     }
 | |
|                     else {
 | |
|                         r.push_back(~lit);
 | |
|                     }
 | |
|                 } 
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             // comes from a unit propagation
 | |
|             SASSERT(value(l) == l_true);
 | |
|             unsigned coeff = 0, j = 0;
 | |
|             for (; j < p.size(); ++j) {
 | |
|                 if (p[j].second == l) {
 | |
|                     coeff = p[j].first;
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|             
 | |
|             ++j;
 | |
|             if (j < p.num_watch()) {
 | |
|                 j = p.num_watch();
 | |
|             }
 | |
|             CTRACE("pb", coeff == 0, display(tout << l << " coeff: " << coeff << "\n", p, true);); 
 | |
|             
 | |
|             if (_debug_conflict) {
 | |
|                 IF_VERBOSE(0, verbose_stream() << "coeff " << coeff << "\n";);
 | |
|             }
 | |
| 
 | |
|             SASSERT(coeff > 0);
 | |
|             unsigned slack = p.max_sum() - coeff;
 | |
|             
 | |
|             // we need antecedents to be deeper than alit.
 | |
|             for (; j < p.size(); ++j) {
 | |
|                 literal lit = p[j].second;
 | |
|                 unsigned w = p[j].first;
 | |
|                 if (l_false != value(lit)) {
 | |
|                     // skip
 | |
|                 }
 | |
|                 else if (lvl(lit) > lvl(l)) {
 | |
|                     // skip
 | |
|                 }
 | |
|                 else if (lvl(lit) == lvl(l) && assigned_above(~lit, l)) {
 | |
|                     // skip
 | |
|                 }
 | |
|                 else if (slack + w < k) {
 | |
|                     slack += w;
 | |
|                 }
 | |
|                 else {
 | |
|                     r.push_back(~lit); 
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         SASSERT(validate_unit_propagation(p, r, l));
 | |
|     }
 | |
| 
 | |
|     bool solver::is_extended_binary(sat::ext_justification_idx idx, literal_vector & r) {
 | |
|         return index2constraint(idx).is_extended_binary(r);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void solver::get_antecedents(literal l, card const& c, literal_vector& r) {
 | |
|         if (l == ~c.lit()) {
 | |
|             for (unsigned i = c.k() - 1; i < c.size(); ++i) {
 | |
|                 VERIFY(value(c[i]) == l_false);
 | |
|                 r.push_back(~c[i]);
 | |
|             }   
 | |
|             return;
 | |
|         }
 | |
|         DEBUG_CODE(
 | |
|             bool found = false;
 | |
|             for (unsigned i = 0; !found && i < c.k(); ++i) {
 | |
|                 found = c[i] == l;
 | |
|             }
 | |
|             CTRACE("pb",!found, s().display(tout << l << ":" << c << "\n"););
 | |
|             SASSERT(found););
 | |
|         
 | |
|         VERIFY(c.lit() == sat::null_literal || value(c.lit()) != l_false);
 | |
|         if (c.lit() != sat::null_literal) r.push_back(value(c.lit()) == l_true ? c.lit() : ~c.lit());
 | |
|         for (unsigned i = c.k(); i < c.size(); ++i) {
 | |
|             SASSERT(value(c[i]) == l_false);
 | |
|             r.push_back(~c[i]);
 | |
|         }
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // ----------------------------
 | |
|     // constraint generic methods
 | |
| 
 | |
|     void solver::get_antecedents(literal l, sat::ext_justification_idx idx, literal_vector & r, bool probing) {
 | |
|         get_antecedents(l, index2constraint(idx), r, probing);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void solver::get_antecedents(literal l, constraint const& c, literal_vector& r, bool probing) {
 | |
|         switch (c.tag()) {
 | |
|         case pb::tag_t::card_t: get_antecedents(l, c.to_card(), r); break;
 | |
|         case pb::tag_t::pb_t: get_antecedents(l, c.to_pb(), r); break;
 | |
|         default: UNREACHABLE(); break;            
 | |
|         }
 | |
|         if (get_config().m_drat && m_solver && !probing) {
 | |
|             literal_vector lits;
 | |
|             for (literal lit : r) 
 | |
|                 lits.push_back(~lit);
 | |
|             lits.push_back(l);
 | |
|             s().m_drat.add(lits, sat::status::th(true, get_id()));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::clear_watch(constraint& c) {
 | |
|         c.clear_watch(*this);
 | |
|     }
 | |
| 
 | |
|     void solver::remove_constraint(constraint& c, char const* reason) {
 | |
|         TRACE("pb", display(tout << "remove ", c, true) << " " << reason << "\n";);
 | |
|         IF_VERBOSE(21, display(verbose_stream() << "remove " << reason << " ", c, true););
 | |
|         c.nullify_tracking_literal(*this);
 | |
|         clear_watch(c);
 | |
|         c.set_removed();
 | |
|         m_constraint_removed = true;
 | |
|     }
 | |
| 
 | |
|     // --------------------------------
 | |
|     // validation
 | |
| 
 | |
|     bool solver::validate_unit_propagation(constraint const& c, literal l) const {
 | |
|         return true;
 | |
|         return c.validate_unit_propagation(*this, l);
 | |
|     }
 | |
| 
 | |
|     bool solver::validate_conflict(constraint const& c) const {
 | |
|         return eval(c) == l_false;
 | |
|     }
 | |
| 
 | |
|     lbool solver::eval(constraint const& c) const {
 | |
|         lbool v1 = c.lit() == sat::null_literal ? l_true : value(c.lit());
 | |
|         return eval(v1, c.eval(*this));
 | |
|     }
 | |
| 
 | |
|     lbool solver::eval(sat::model const& m, constraint const& c) const {
 | |
|         lbool v1 = c.lit() == sat::null_literal ? l_true : pb::value(m, c.lit());
 | |
|         return eval(v1, c.eval(m));
 | |
|     }
 | |
| 
 | |
|     lbool solver::eval(lbool a, lbool b) const {
 | |
|         if (a == l_undef || b == l_undef) return l_undef;
 | |
|         return (a == b) ? l_true : l_false;
 | |
|     }
 | |
| 
 | |
|     bool solver::validate() {
 | |
|         if (!validate_watch_literals()) {
 | |
|             return false;
 | |
|         }
 | |
|         for (constraint* c : m_constraints) {
 | |
|             if (!validate_watched_constraint(*c)) 
 | |
|                 return false;
 | |
|         }
 | |
|         for (constraint* c : m_learned) {
 | |
|             if (!validate_watched_constraint(*c)) 
 | |
|                 return false;
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool solver::validate_watch_literals() const {
 | |
|         for (unsigned v = 0; v < s().num_vars(); ++v) {
 | |
|             literal lit(v, false);
 | |
|             if (lvl(lit) == 0) continue;
 | |
|             if (!validate_watch_literal(lit)) return false;
 | |
|             if (!validate_watch_literal(~lit)) return false;
 | |
|         }        
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool solver::validate_watch_literal(literal lit) const {
 | |
|         if (lvl(lit) == 0) return true;
 | |
|         for (auto const & w : get_wlist(lit)) {
 | |
|             if (w.get_kind() == sat::watched::EXT_CONSTRAINT) {
 | |
|                 constraint const& c = index2constraint(w.get_ext_constraint_idx());
 | |
|                 if (!c.is_watching(~lit) && lit.var() != c.lit().var()) {
 | |
|                     IF_VERBOSE(0, display(verbose_stream() << lit << " " << lvl(lit) << " is not watched in " << c << "\n", c, true););
 | |
|                     UNREACHABLE();
 | |
|                     return false;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool solver::validate_watched_constraint(constraint const& c) const {
 | |
|         if (c.is_pb() && !validate_watch(c.to_pb(), sat::null_literal)) {
 | |
|             return false;
 | |
|         }
 | |
|         if (c.lit() != sat::null_literal && value(c.lit()) != l_true) return true;
 | |
|         SASSERT(c.lit() == sat::null_literal || lvl(c.lit()) == 0 || (c.is_watched(*this, c.lit()) && c.is_watched(*this, ~c.lit())));
 | |
|         if (eval(c) == l_true) 
 | |
|             return true;
 | |
|         
 | |
|         literal_vector lits(c.literals());
 | |
|         for (literal l : lits) {
 | |
|             if (lvl(l) == 0) continue;
 | |
|             bool found = c.is_watched(*this, l);
 | |
|             if (found != c.is_watching(l)) {
 | |
| 
 | |
|                 IF_VERBOSE(0, 
 | |
|                            verbose_stream() << "Discrepancy of watched literal: " << l << " id: " << c.id() 
 | |
|                            << " clause: " << c << (found?" is watched, but shouldn't be":" not watched, but should be") << "\n";
 | |
|                            s().display_watch_list(verbose_stream() <<  l << ": ", get_wlist(l)) << "\n";
 | |
|                            s().display_watch_list(verbose_stream() << ~l << ": ", get_wlist(~l)) << "\n";
 | |
|                            verbose_stream() << "value: " << value(l) << " level: " << lvl(l) << "\n";
 | |
|                            display(verbose_stream(), c, true);
 | |
|                            if (c.lit() != sat::null_literal) verbose_stream() << value(c.lit()) << "\n";);
 | |
| 
 | |
|                 IF_VERBOSE(0, s().display_watches(verbose_stream()));
 | |
| 
 | |
|                 UNREACHABLE();
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     bool solver::validate_watch(pbc const& p, literal alit) const {
 | |
|         if (p.lit() == sat::null_literal || value(p.lit()) != l_true)
 | |
|             return true;
 | |
|         for (unsigned i = 0; i < p.size(); ++i) {
 | |
|             literal l = p[i].second;
 | |
|             if (l != alit && lvl(l) != 0 && p.is_watched(*this, l) != (i < p.num_watch())) {
 | |
|                 IF_VERBOSE(0, display(verbose_stream(), p, true);
 | |
|                            verbose_stream() << "literal " << l << " at position " << i << " " << p.is_watched(*this, l) << "\n";);
 | |
|                 UNREACHABLE();
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         unsigned slack = 0;
 | |
|         for (unsigned i = 0; i < p.num_watch(); ++i) 
 | |
|             slack += p[i].first;        
 | |
|         if (slack != p.slack()) {
 | |
|             IF_VERBOSE(0, display(verbose_stream(), p, true););
 | |
|             UNREACHABLE();
 | |
|             return false;
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     
 | |
|     /**
 | |
|        \brief Lex on (glue, size)
 | |
|     */
 | |
|     struct constraint_glue_psm_lt {
 | |
|         bool operator()(constraint const * c1, constraint const * c2) const {
 | |
|             return 
 | |
|                 (c1->glue()  < c2->glue()) ||
 | |
|                 (c1->glue() == c2->glue() && 
 | |
|                  (c1->psm() < c2->psm() || 
 | |
|                   (c1->psm() == c2->psm() && c1->size() < c2->size())));
 | |
|         }
 | |
|     };
 | |
| 
 | |
|     void solver::update_psm(constraint& c) const {
 | |
|         unsigned r = 0;
 | |
|         switch (c.tag()) {            
 | |
|         case pb::tag_t::card_t:
 | |
|             for (literal l : c.to_card()) {                
 | |
|                 if (s().m_phase[l.var()] == !l.sign()) ++r;
 | |
|             }
 | |
|             break;
 | |
|         case pb::tag_t::pb_t:
 | |
|             for (wliteral l : c.to_pb()) {                
 | |
|                 if (s().m_phase[l.second.var()] == !l.second.sign()) ++r;
 | |
|             }
 | |
|             break;
 | |
|         default:
 | |
|             break;
 | |
|         }
 | |
|         c.set_psm(r);
 | |
|     }
 | |
|     
 | |
|     void solver::gc() {
 | |
|         if (m_learned.size() >= 2 * m_constraints.size() && 
 | |
|             (s().at_search_lvl() || s().at_base_lvl())) {
 | |
|             for (auto & c : m_learned) update_psm(*c);
 | |
|             std::stable_sort(m_learned.begin(), m_learned.end(), constraint_glue_psm_lt());
 | |
|             gc_half("glue-psm");
 | |
|             cleanup_constraints(m_learned, true);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::gc_half(char const* st_name) {
 | |
|         TRACE("pb", tout << "gc\n";);
 | |
|         unsigned sz     = m_learned.size();
 | |
|         unsigned new_sz = sz/2;
 | |
|         unsigned removed = 0;
 | |
|         for (unsigned i = new_sz; i < sz; i++) {
 | |
|             constraint* c = m_learned[i];
 | |
|             if (!m_constraint_to_reinit.contains(c)) {
 | |
|                 remove_constraint(*c, "gc");
 | |
|                 c->deallocate(m_allocator);
 | |
|                 ++removed;
 | |
|             }
 | |
|             else {
 | |
|                 m_learned[new_sz++] = c;
 | |
|             }
 | |
|         }
 | |
|         m_stats.m_num_gc += removed;
 | |
|         m_learned.shrink(new_sz);
 | |
|         IF_VERBOSE(2, verbose_stream() << "(sat-gc :strategy " << st_name << " :deleted " << removed << ")\n";);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void solver::gc_vars(unsigned num_vars) {
 | |
|         gc_vars(num_vars, m_constraints);
 | |
|         gc_vars(num_vars, m_learned);
 | |
|     }
 | |
| 
 | |
|     void solver::gc_vars(unsigned num_vars, ptr_vector<constraint>& cs) {
 | |
|         unsigned j = 0;
 | |
|         for (unsigned i = 0; i < cs.size(); ++i) {
 | |
|             auto* c = cs[i];
 | |
|             unsigned m = c->fold_max_var(0);
 | |
|             if (m >= num_vars) {
 | |
|                 clear_watch(*c);
 | |
|                 c->nullify_tracking_literal(*this);
 | |
|                 c->deallocate(m_allocator);
 | |
|             }
 | |
|             else 
 | |
|                 cs[j++] = c;
 | |
|         }
 | |
|         cs.shrink(j);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     lbool solver::add_assign(card& c, literal alit) {
 | |
|         // literal is assigned to false.        
 | |
|         unsigned sz = c.size();
 | |
|         unsigned bound = c.k();
 | |
|         TRACE("pb", tout << "assign: " << c.lit() << ": " << ~alit << "@" << lvl(~alit) << " " << c << "\n";);
 | |
| 
 | |
|         SASSERT(0 < bound && bound <= sz);
 | |
|         if (bound == sz) {
 | |
|             if (c.lit() != sat::null_literal && value(c.lit()) == l_undef) {
 | |
|                 assign(c, ~c.lit());
 | |
|                 return inconsistent() ? l_false : l_true;
 | |
|             }
 | |
|             set_conflict(c, alit);
 | |
|             return l_false;
 | |
|         }
 | |
|         SASSERT(value(alit) == l_false);
 | |
|         VERIFY(c.lit() == sat::null_literal || value(c.lit()) != l_false);
 | |
|         unsigned index = 0;
 | |
|         for (index = 0; index <= bound; ++index) {
 | |
|             if (c[index] == alit) {
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         if (index == bound + 1) {
 | |
|             // literal is no longer watched.
 | |
|             return l_undef;
 | |
|         }
 | |
|         VERIFY(index <= bound);
 | |
|         VERIFY(c[index] == alit);
 | |
|         
 | |
|         // find a literal to swap with:
 | |
|         for (unsigned i = bound + 1; i < sz; ++i) {
 | |
|             literal lit2 = c[i];
 | |
|             if (value(lit2) != l_false) {
 | |
|                 c.swap(index, i);
 | |
|                 c.watch_literal(*this, lit2);
 | |
|                 return l_undef;
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // conflict
 | |
|         if (bound != index && value(c[bound]) == l_false) {
 | |
|             TRACE("pb", tout << "conflict " << c[bound] << " " << alit << "\n";);
 | |
|             if (c.lit() != sat::null_literal && value(c.lit()) == l_undef) {
 | |
|                 if (index + 1 < bound) c.swap(index, bound - 1);
 | |
|                 assign(c, ~c.lit());
 | |
|                 return inconsistent() ? l_false : l_true;
 | |
|             }
 | |
|             set_conflict(c, alit);
 | |
|             return l_false;
 | |
|         }
 | |
| 
 | |
|         if (index != bound) {
 | |
|             c.swap(index, bound);
 | |
|         }        
 | |
| 
 | |
|         // TRACE("pb", tout << "no swap " << index << " " << alit << "\n";);
 | |
|         // there are no literals to swap with,
 | |
|         // prepare for unit propagation by swapping the false literal into 
 | |
|         // position bound. Then literals in positions 0..bound-1 have to be
 | |
|         // assigned l_true.
 | |
| 
 | |
|         if (c.lit() != sat::null_literal && value(c.lit()) == l_undef) {
 | |
|             return l_true;
 | |
|         }
 | |
| 
 | |
|         for (unsigned i = 0; i < bound; ++i) {
 | |
|             assign(c, c[i]);
 | |
|         }
 | |
| 
 | |
|         if (c.learned() && c.glue() > 2) {
 | |
|             unsigned glue;
 | |
|             if (s().num_diff_false_levels_below(c.size(), c.begin(), c.glue()-1, glue)) {
 | |
|                 c.set_glue(glue);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         return inconsistent() ? l_false : l_true;
 | |
|     }
 | |
| 
 | |
|     void solver::asserted(literal l) {
 | |
|     }
 | |
| 
 | |
| 
 | |
|     sat::check_result solver::check() { return sat::check_result::CR_DONE; }
 | |
| 
 | |
|     void solver::push() {
 | |
|         m_constraint_to_reinit_lim.push_back(m_constraint_to_reinit.size());
 | |
|     }
 | |
| 
 | |
|     void solver::pop(unsigned n) {        
 | |
|         TRACE("sat_verbose", tout << "pop:" << n << "\n";);
 | |
|         unsigned new_lim = m_constraint_to_reinit_lim.size() - n;
 | |
|         m_constraint_to_reinit_last_sz = m_constraint_to_reinit_lim[new_lim];
 | |
|         m_constraint_to_reinit_lim.shrink(new_lim);
 | |
|         m_num_propagations_since_pop = 0;
 | |
|     }
 | |
| 
 | |
|     void solver::pop_reinit() {
 | |
|         unsigned sz = m_constraint_to_reinit_last_sz;
 | |
|         for (unsigned i = sz; i < m_constraint_to_reinit.size(); ++i) {
 | |
|             constraint* c = m_constraint_to_reinit[i];
 | |
|             if (!init_watch(*c) && !s().at_base_lvl()) {
 | |
|                 m_constraint_to_reinit[sz++] = c;
 | |
|             }
 | |
|         }
 | |
|         m_constraint_to_reinit.shrink(sz);        
 | |
|     }
 | |
| 
 | |
|     void solver::simplify() {
 | |
|         if (!s().at_base_lvl()) 
 | |
|             s().pop_to_base_level();
 | |
|         if (s().inconsistent())
 | |
|             return;
 | |
|         unsigned trail_sz = 0, count = 0;
 | |
|         do {
 | |
|             trail_sz = s().init_trail_size();
 | |
|             m_simplify_change = false;
 | |
|             m_clause_removed = false;
 | |
|             m_constraint_removed = false;
 | |
|             for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) simplify(*m_constraints[i]);
 | |
|             for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) simplify(*m_learned[i]);
 | |
|             init_use_lists();
 | |
|             // remove_unused_defs();
 | |
|             set_non_external();
 | |
|             elim_pure();
 | |
|             for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) subsumption(*m_constraints[i]);
 | |
|             for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) subsumption(*m_learned[i]);    
 | |
|             unit_strengthen();
 | |
|             cleanup_clauses();
 | |
|             cleanup_constraints();
 | |
| 
 | |
|             count++;
 | |
|         }        
 | |
|         while (count < 10 && (m_simplify_change || trail_sz < s().init_trail_size()));
 | |
| 
 | |
|         gc();
 | |
| 
 | |
|         // validate_eliminated();
 | |
| 
 | |
|         IF_VERBOSE(1, 
 | |
|                    unsigned subs = m_stats.m_num_bin_subsumes + m_stats.m_num_clause_subsumes + m_stats.m_num_pb_subsumes;
 | |
|                    verbose_stream() << "(ba.simplify" << " :constraints " << m_constraints.size();
 | |
|                    if (!m_learned.empty()) verbose_stream() << " :lemmas " << m_learned.size();
 | |
|                    if (subs > 0) verbose_stream() << " :subsumes " << subs;
 | |
|                    if (m_stats.m_num_gc > 0) verbose_stream() << " :gc " << m_stats.m_num_gc;
 | |
|                    verbose_stream() << ")\n";);
 | |
| 
 | |
|         // IF_VERBOSE(0, s().display(verbose_stream()));
 | |
|         // mutex_reduction();
 | |
|         // if (s().m_clauses.size() < 80000) lp_lookahead_reduction();
 | |
|     }
 | |
| 
 | |
|     void solver::mutex_reduction() {
 | |
|         literal_vector lits;
 | |
|         for (unsigned v = 0; v < s().num_vars(); ++v) {
 | |
|             lits.push_back(literal(v, false));
 | |
|             lits.push_back(literal(v, true));
 | |
|         }
 | |
|         vector<literal_vector> mutexes;
 | |
|         s().find_mutexes(lits, mutexes);
 | |
|         for (literal_vector& mux : mutexes) {
 | |
|             if (mux.size() > 2) {
 | |
|                 IF_VERBOSE(1, verbose_stream() << "mux: " << mux << "\n";);
 | |
|                 for (unsigned i = 0; i < mux.size(); ++i) mux[i].neg(); 
 | |
|                 add_at_least(sat::null_literal, mux, mux.size() - 1, false);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // -------------------------
 | |
|     // sorting networks
 | |
|     literal solver::ba_sort::mk_false() {
 | |
|         return ~mk_true();
 | |
|     }
 | |
| 
 | |
|     literal solver::ba_sort::mk_true() {
 | |
|         if (m_true == sat::null_literal) {
 | |
|             bool_var v = s.s().mk_var(false, false);
 | |
|             m_true = literal(v, false);
 | |
|             s.s().mk_clause(1,&m_true);
 | |
|         }
 | |
|         VERIFY(m_true != sat::null_literal);
 | |
|         return m_true;
 | |
|     }
 | |
| 
 | |
|     literal solver::ba_sort::mk_not(literal l) { 
 | |
|         return ~l; 
 | |
|     }
 | |
| 
 | |
|     literal solver::ba_sort::fresh(char const*) {
 | |
|         bool_var v = s.s().mk_var(false, true);
 | |
|         return literal(v, false);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     literal solver::ba_sort::mk_max(unsigned n, literal const* lits) {
 | |
|         m_lits.reset();        
 | |
|         for (unsigned i = 0; i < n; ++i) {
 | |
|             if (lits[i] == m_true) return m_true;
 | |
|             if (lits[i] == ~m_true) continue;
 | |
|             m_lits.push_back(lits[i]);
 | |
|         }
 | |
|         switch (m_lits.size()) {
 | |
|         case 0: 
 | |
|             return ~m_true;
 | |
|         case 1: 
 | |
|             return m_lits[0];
 | |
|         default: {
 | |
|             literal max = fresh("max");
 | |
|             for (unsigned i = 0; i < n; ++i) {
 | |
|                 s.s().mk_clause(~m_lits[i], max);
 | |
|             }
 | |
|             m_lits.push_back(~max);
 | |
|             s.s().mk_clause(m_lits.size(), m_lits.data());
 | |
|             return max;
 | |
|         }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     literal solver::ba_sort::mk_min(unsigned n, literal const* lits) {
 | |
|         m_lits.reset();        
 | |
|         for (unsigned i = 0; i < n; ++i) {
 | |
|             if (lits[i] == ~m_true) return ~m_true;
 | |
|             if (lits[i] == m_true) continue;
 | |
|             m_lits.push_back(lits[i]);
 | |
|         }
 | |
|         switch (m_lits.size()) {
 | |
|         case 0: 
 | |
|             return m_true;
 | |
|         case 1: 
 | |
|             return m_lits[0];
 | |
|         default: {
 | |
|             literal min = fresh("min");
 | |
|             for (unsigned i = 0; i < n; ++i) {
 | |
|                 s.s().mk_clause(~min, m_lits[i]);
 | |
|                 m_lits[i] = ~m_lits[i];
 | |
|             }
 | |
|             m_lits.push_back(min);
 | |
|             s.s().mk_clause(m_lits.size(), m_lits.data());
 | |
|             return min;
 | |
|         }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::ba_sort::mk_clause(unsigned n, literal const* lits) {
 | |
|         m_lits.reset();
 | |
|         m_lits.append(n, lits);
 | |
|         s.s().mk_clause(n, m_lits.data());
 | |
|     }
 | |
| 
 | |
|     std::ostream& solver::ba_sort::pp(std::ostream& out, literal l) const {
 | |
|         return out << l;
 | |
|     }
 | |
| 
 | |
|     
 | |
|     // -------------------------------
 | |
|     // set literals equivalent
 | |
| 
 | |
|     void solver::reserve_roots() {
 | |
|         m_root_vars.reserve(s().num_vars(), false);
 | |
|         for (unsigned i = m_roots.size(); i < 2 * s().num_vars(); ++i) {
 | |
|             m_roots.push_back(sat::to_literal(i));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool solver::set_root(literal l, literal r) {
 | |
|         if (s().is_assumption(l.var())) 
 | |
|             return false;
 | |
|         reserve_roots();
 | |
|         m_roots[l.index()] = r;
 | |
|         m_roots[(~l).index()] = ~r;
 | |
|         m_roots[r.index()] = r;
 | |
|         m_roots[(~r).index()] = ~r;
 | |
|         m_root_vars[l.var()] = true;
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void solver::flush_roots() {
 | |
|         if (m_roots.empty())
 | |
|             return;
 | |
|         reserve_roots();
 | |
|         DEBUG_CODE(validate(););
 | |
|         m_constraint_removed = false;
 | |
|         for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) 
 | |
|             flush_roots(*m_constraints[i]);
 | |
|         for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) 
 | |
|             flush_roots(*m_learned[i]);
 | |
|         cleanup_constraints();
 | |
|         DEBUG_CODE(validate(););
 | |
|         DEBUG_CODE(validate_eliminated(););
 | |
|     }
 | |
| 
 | |
|     void solver::validate_eliminated() {
 | |
|         validate_eliminated(m_constraints);
 | |
|         validate_eliminated(m_learned);
 | |
|     }
 | |
| 
 | |
|     void solver::validate_eliminated(ptr_vector<constraint> const& cs) {
 | |
|         for (constraint const* c : cs) {
 | |
|             if (c->learned())
 | |
|                 continue;
 | |
|             for (auto l : constraint::literal_iterator(*c))
 | |
|                 VERIFY(!s().was_eliminated(l.var()));
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::recompile(constraint& c) {
 | |
|         if (c.id() == _bad_id) {
 | |
|             IF_VERBOSE(0, display(verbose_stream() << "recompile\n", c, true););
 | |
|         }
 | |
|         switch (c.tag()) {
 | |
|         case pb::tag_t::card_t:
 | |
|             recompile(c.to_card());
 | |
|             break;
 | |
|         case pb::tag_t::pb_t:
 | |
|             recompile(c.to_pb());
 | |
|             break;
 | |
|         default:
 | |
|             UNREACHABLE();
 | |
|         }                
 | |
|     }
 | |
| 
 | |
|     void solver::recompile(card& c) {
 | |
|         SASSERT(c.lit() == sat::null_literal || c.is_watched(*this, c.lit()));
 | |
| 
 | |
|         // pre-condition is that the literals, except c.lit(), in c are unwatched.
 | |
|         //if (c.id() == _bad_id) std::cout << "recompile: " << c << "\n";
 | |
|         m_weights.resize(2*s().num_vars(), 0);
 | |
|         for (literal l : c) {
 | |
|             ++m_weights[l.index()];
 | |
|         }
 | |
|         unsigned k = c.k();
 | |
|         bool all_units = true;
 | |
|         unsigned sz = c.size();
 | |
|         unsigned_vector coeffs;
 | |
|         literal_vector lits;
 | |
|         unsigned j = 0;
 | |
|         for (unsigned i = 0; i < sz && 0 < k; ++i) {
 | |
|             literal l = c[i];
 | |
|             unsigned w = m_weights[l.index()];
 | |
|             unsigned w2 = m_weights[(~l).index()];
 | |
|             if (w == 0 || w < w2) {
 | |
|                 continue;
 | |
|             }
 | |
|             else if (k <= w2) {
 | |
|                 k = 0;
 | |
|                 break;
 | |
|             }
 | |
|             else {
 | |
|                 SASSERT(w2 <= w && w2 < k);
 | |
|                 k -= w2;
 | |
|                 w -= w2;
 | |
|                 m_weights[(~l).index()] = 0;        
 | |
|                 m_weights[l.index()] = 0;        
 | |
|                 if (w == 0) {
 | |
|                     continue;
 | |
|                 }    
 | |
|                 else {
 | |
|                     all_units &= (w == 1);
 | |
|                     coeffs.push_back(w);
 | |
|                     c[j++] = l;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         sz = j;
 | |
| 
 | |
|         // clear weights
 | |
|         for (literal l : c) {
 | |
|             m_weights[l.index()] = 0;
 | |
|             m_weights[(~l).index()] = 0;
 | |
|         }       
 | |
| 
 | |
|         if (k == 0 && c.lit() == sat::null_literal) {
 | |
|             remove_constraint(c, "recompiled to true");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (k == 1 && c.lit() == sat::null_literal) {
 | |
|             literal_vector lits(sz, c.literals().data());
 | |
|             s().mk_clause(sz, lits.data(), sat::status::th(c.learned(), get_id()));
 | |
|             remove_constraint(c, "recompiled to clause");
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (sz == 0) {
 | |
|             if (c.lit() == sat::null_literal) {
 | |
|                 if (k > 0) {
 | |
|                     s().mk_clause(0, nullptr, sat::status::th(false, get_id()));
 | |
|                 }
 | |
|             }
 | |
|             else if (k > 0) {
 | |
|                 literal lit = ~c.lit();
 | |
|                 s().mk_clause(1, &lit, sat::status::th(c.learned(), get_id()));
 | |
|             }
 | |
|             else {
 | |
|                 literal lit = c.lit();
 | |
|                 s().mk_clause(1, &lit, sat::status::th(c.learned(), get_id()));
 | |
|             }
 | |
|             remove_constraint(c, "recompiled to clause");
 | |
|             return;
 | |
|         }
 | |
|         if (all_units && sz < k) {
 | |
|             if (c.lit() == sat::null_literal) {
 | |
|                 s().mk_clause(0, nullptr, sat::status::th(c.learned(), get_id()));
 | |
|             }
 | |
|             else {
 | |
|                 literal lit = ~c.lit();
 | |
|                 s().mk_clause(1, &lit, sat::status::th(c.learned(), get_id()));
 | |
|             }
 | |
|             remove_constraint(c, "recompiled to clause");
 | |
|             return;            
 | |
|         }
 | |
|         VERIFY(!all_units || c.size() - c.k() >= sz - k);
 | |
|         c.set_size(sz);
 | |
|         c.set_k(k);    
 | |
| 
 | |
|         if (all_units && clausify(c)) {
 | |
|             return;
 | |
|         }
 | |
| 
 | |
|         if (!all_units) {            
 | |
|             TRACE("pb", tout << "replacing by pb: " << c << "\n";);
 | |
|             m_wlits.reset();
 | |
|             for (unsigned i = 0; i < sz; ++i) {
 | |
|                 m_wlits.push_back(wliteral(coeffs[i], c[i]));
 | |
|             }
 | |
|             literal root = c.lit();
 | |
|             remove_constraint(c, "recompiled to pb");
 | |
|             add_pb_ge(root, m_wlits, k, c.learned());
 | |
|         }
 | |
|         else {
 | |
|             if (c.lit() == sat::null_literal || value(c.lit()) == l_true) {
 | |
|                 init_watch(c);
 | |
|             }
 | |
|             SASSERT(c.lit() == sat::null_literal || c.is_watched(*this, c.lit()));
 | |
|             SASSERT(c.well_formed());
 | |
|         }        
 | |
|     }
 | |
| 
 | |
|     bool solver::clausify(literal lit, unsigned n, literal const* lits, unsigned k) {
 | |
|         return false;
 | |
|         bool is_def = lit != sat::null_literal;
 | |
|         if ((!is_def || !s().was_eliminated(lit)) && 
 | |
|             !std::any_of(lits, lits + n, [&](literal l) { return s().was_eliminated(l); })) {
 | |
|             literal def_lit = m_sort.ge(is_def, k, n, lits);
 | |
|             if (is_def) {
 | |
|                 s().mk_clause(~lit,  def_lit);
 | |
|                 s().mk_clause( lit, ~def_lit);
 | |
|             }
 | |
|             return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     bool solver::clausify(card& c) {
 | |
|         return false;
 | |
|         if (get_config().m_card_solver)
 | |
|             return false;
 | |
| 
 | |
|         //
 | |
|         // TBD: conditions for when to clausify are TBD and
 | |
|         // handling of conditional cardinality as well.
 | |
|         // 
 | |
|         if (!c.learned() && clausify(c.lit(), c.size(), c.begin(), c.k())) {
 | |
|             IF_VERBOSE(1, verbose_stream() << "clausify " << c << "\n";);                       
 | |
|             // compiled
 | |
|         }        
 | |
|         remove_constraint(c, "recompiled to clauses");
 | |
|         return true;        
 | |
|     }
 | |
| 
 | |
|     bool solver::clausify(pbc& p) {
 | |
|         return false;
 | |
|         if (get_config().m_card_solver) 
 | |
|             return false;
 | |
|         
 | |
|         bool ok = !p.learned();
 | |
|         bool is_def = p.lit() != sat::null_literal;
 | |
|         for (wliteral wl : p) {
 | |
|             ok &= !s().was_eliminated(wl.second);
 | |
|         }
 | |
|         ok &= !is_def || !s().was_eliminated(p.lit());
 | |
|         if (!ok) {
 | |
|             remove_constraint(p, "recompiled to clauses");
 | |
|             return true;
 | |
|         }
 | |
| 
 | |
|         if (is_cardinality(p, m_lemma)) {
 | |
|             literal lit = m_sort.ge(is_def, p.k(), m_lemma.size(), m_lemma.data());
 | |
|             if (is_def) {
 | |
|                 s().mk_clause(p.lit(), ~lit);
 | |
|                 s().mk_clause(~p.lit(), lit);
 | |
|             }
 | |
|             remove_constraint(p, "recompiled to clauses");
 | |
|             return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     bool solver::is_cardinality(pbc const& p, literal_vector& lits) {
 | |
|         lits.reset();
 | |
|         p.size();
 | |
|         for (wliteral wl : p) {
 | |
|             if (lits.size() > 2*p.size() + wl.first) {
 | |
|                 return false;
 | |
|             }
 | |
|             for (unsigned i = 0; i < wl.first; ++i) {
 | |
|                 lits.push_back(wl.second);
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
| 
 | |
|     void solver::flush_roots(constraint& c) {
 | |
|         if (c.lit() != sat::null_literal && !c.is_watched(*this, c.lit())) {
 | |
|             c.watch_literal(*this, c.lit());
 | |
|             c.watch_literal(*this, ~c.lit());
 | |
|         }
 | |
|         SASSERT(c.lit() == sat::null_literal || c.is_watched(*this, c.lit()));
 | |
|         bool found = c.lit() != sat::null_literal && m_root_vars[c.lit().var()];
 | |
|         for (unsigned i = 0; !found && i < c.size(); ++i) {
 | |
|             found = m_root_vars[c.get_lit(i).var()];
 | |
|         }
 | |
|         if (!found)
 | |
|             return;
 | |
|         clear_watch(c);
 | |
| 
 | |
|         // this could create duplicate literals
 | |
|         for (unsigned i = 0; i < c.size(); ++i) {
 | |
|             literal lit = m_roots[c.get_lit(i).index()];
 | |
|             c.set_lit(i, lit);
 | |
|         }
 | |
| 
 | |
|         literal root = c.lit();
 | |
|         if (root != sat::null_literal && m_roots[root.index()] != root) {
 | |
|             root = m_roots[root.index()];
 | |
|             c.nullify_tracking_literal(*this);
 | |
|             c.update_literal(root);
 | |
|             c.watch_literal(*this, root);
 | |
|             c.watch_literal(*this, ~root);
 | |
|         }
 | |
| 
 | |
|         bool found_dup = false;
 | |
|         bool found_root = false;
 | |
|         init_visited();
 | |
|         for (unsigned i = 0; i < c.size(); ++i) {
 | |
|             literal l = c.get_lit(i);
 | |
|             if (is_visited(l)) {
 | |
|                 found_dup = true;
 | |
|                 break;
 | |
|             }
 | |
|             else {
 | |
|                 mark_visited(l);
 | |
|                 mark_visited(~l);
 | |
|             }
 | |
|         }
 | |
|         for (unsigned i = 0; i < c.size(); ++i) {
 | |
|             found_root |= c.get_lit(i).var() == root.var();
 | |
|         }
 | |
| 
 | |
|         if (found_root) {
 | |
|             split_root(c);
 | |
|             c.negate();
 | |
|             split_root(c);
 | |
|             remove_constraint(c, "flush roots");
 | |
|         }
 | |
|         else if (found_dup) {
 | |
|             recompile(c);
 | |
|         }
 | |
|         else {
 | |
|             if (c.lit() == sat::null_literal || value(c.lit()) != l_undef) init_watch(c);            
 | |
|             SASSERT(c.well_formed());
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     unsigned solver::get_num_unblocked_bin(literal l) {
 | |
|         return s().m_simplifier.num_nonlearned_bin(l);
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief garbage collection.
 | |
|       This entails 
 | |
|       - finding pure literals, 
 | |
|       - setting literals that are not used in the extension to non-external.
 | |
|       - subsumption
 | |
|       - resolution 
 | |
|       - blocked literals
 | |
|      */
 | |
|     void solver::init_use_lists() {
 | |
|         m_clause_use_list.init(s().num_vars());
 | |
|         m_cnstr_use_list.reset();
 | |
|         m_cnstr_use_list.resize(2*s().num_vars());
 | |
|         for (sat::clause* c : s().m_clauses) {
 | |
|             if (!c->frozen()) 
 | |
|                 m_clause_use_list.insert(*c);
 | |
|         }
 | |
|         for (constraint* cp : m_constraints) {
 | |
|             literal lit = cp->lit();
 | |
|             if (lit != sat::null_literal) {
 | |
|                 m_cnstr_use_list[lit.index()].push_back(cp);
 | |
|                 m_cnstr_use_list[(~lit).index()].push_back(cp);
 | |
|             }
 | |
|             for (literal l : constraint::literal_iterator(*cp)) {
 | |
|                 m_cnstr_use_list[l.index()].push_back(cp);
 | |
|                 if (lit != sat::null_literal) m_cnstr_use_list[(~l).index()].push_back(cp);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::remove_unused_defs() {
 | |
|         if (incremental_mode()) 
 | |
|             return;
 | |
|         // remove constraints where indicator literal isn't used.
 | |
|         NOT_IMPLEMENTED_YET();
 | |
|         // TODO: #6675
 | |
|         // need to add this inequality to the model reconstruction
 | |
|         // stack in order to produce correct models.
 | |
|         for (constraint* cp : m_constraints) {
 | |
|             constraint& c = *cp;
 | |
|             literal lit = c.lit();
 | |
|             if (lit != sat::null_literal &&
 | |
|                 value(lit) == l_undef && 
 | |
|                 use_count(lit) == 1 &&
 | |
|                 use_count(~lit) == 1 &&
 | |
|                 get_num_unblocked_bin(lit) == 0 && 
 | |
|                 get_num_unblocked_bin(~lit) == 0) {
 | |
|                 remove_constraint(c, "unused def");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool solver::incremental_mode() const {
 | |
|         sat_simplifier_params p(s().m_params);
 | |
|         bool incremental_mode = s().get_config().m_incremental && !p.override_incremental();
 | |
|         incremental_mode |=  s().tracking_assumptions();
 | |
|         return incremental_mode;
 | |
|     }
 | |
|     
 | |
|     unsigned solver::set_non_external() {
 | |
|         // set variables to be non-external if they are not used in theory constraints.
 | |
|         unsigned ext = 0;
 | |
|         if (!incremental_mode() && s().get_extension() == this) {
 | |
|             for (unsigned v = 0; v < s().num_vars(); ++v) {
 | |
|                 literal lit(v, false);
 | |
|                 if (s().is_external(v) && 
 | |
|                     m_cnstr_use_list[lit.index()].empty() && 
 | |
|                     m_cnstr_use_list[(~lit).index()].empty()) {
 | |
|                     s().set_non_external(v);
 | |
|                     ++ext;
 | |
|                 }            
 | |
|             }
 | |
|         }
 | |
|         // ensure that lemmas use only non-eliminated variables
 | |
|         for (constraint* cp : m_learned) {
 | |
|             constraint& c = *cp;
 | |
|             if (c.was_removed()) continue;
 | |
|             SASSERT(c.lit() == sat::null_literal);
 | |
|             for (unsigned i = 0; i < c.size(); ++i) {
 | |
|                 bool_var v = c.get_lit(i).var();
 | |
|                 if (s().was_eliminated(v)) {
 | |
|                     remove_constraint(c, "contains eliminated var");                    
 | |
|                     break;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         return ext;
 | |
|     }
 | |
| 
 | |
|     bool solver::elim_pure(literal lit) {
 | |
|         if (value(lit) == l_undef && !m_cnstr_use_list[lit.index()].empty() && 
 | |
|             use_count(~lit) == 0 && get_num_unblocked_bin(~lit) == 0) {
 | |
|             IF_VERBOSE(100, verbose_stream() << "pure literal: " << lit << "\n";);
 | |
|             s().assign_scoped(lit);
 | |
|             return true;
 | |
|         }
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     unsigned solver::elim_pure() {
 | |
|         if (!get_config().m_elim_vars || incremental_mode()) {
 | |
|             return 0;
 | |
|         }
 | |
|         // eliminate pure literals
 | |
|         unsigned pure_literals = 0;
 | |
|         for (unsigned v = 0; v < s().num_vars(); ++v) {
 | |
|             literal lit(v, false);
 | |
|             if (value(v) != l_undef) continue;
 | |
|             if (m_cnstr_use_list[lit.index()].empty() &&
 | |
|                 m_cnstr_use_list[(~lit).index()].empty()) continue;
 | |
| 
 | |
|             if (elim_pure(lit) || elim_pure(~lit)) {
 | |
|                 ++pure_literals;
 | |
|             }
 | |
|         }
 | |
|         return pure_literals;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * Strengthen inequalities using binary implication information.
 | |
|      *
 | |
|      * x -> ~y, x -> ~z,   y + z + u >= 2
 | |
|      * ----------------------------------
 | |
|      *       y + z + u + ~x >= 3
 | |
|      * 
 | |
|      * for c : constraints
 | |
|      *   for l : c:
 | |
|      *    slack <- of c under root(~l)
 | |
|      *    if slack < 0:
 | |
|      *       add ~root(~l) to c, k <- k + 1
 | |
|      */ 
 | |
|     void solver::unit_strengthen() {
 | |
|         return;
 | |
| 
 | |
|         // TODO - this is unsound exposed by 4_21_21_-1.txt
 | |
| 
 | |
|         sat::big big(s().m_rand);
 | |
|         big.init(s(), true);
 | |
|         for (unsigned sz = m_constraints.size(), i = 0; i < sz; ++i) 
 | |
|             unit_strengthen(big, *m_constraints[i]);
 | |
|         for (unsigned sz = m_learned.size(), i = 0; i < sz; ++i) 
 | |
|             unit_strengthen(big, *m_learned[i]);          
 | |
|     }
 | |
| 
 | |
|     void solver::unit_strengthen(sat::big& big, constraint& p) {
 | |
|         if (p.lit() != sat::null_literal) 
 | |
|             return;
 | |
|         unsigned sz = p.size();
 | |
|         for (unsigned i = 0; i < sz; ++i) {
 | |
|             literal u = p.get_lit(i);
 | |
|             literal r = big.get_root(u);
 | |
|             if (r == u) continue;
 | |
|             unsigned k = p.k(), b = 0;
 | |
|             for (unsigned j = 0; j < sz; ++j) {
 | |
|                 literal v = p.get_lit(j);
 | |
|                 if (r == big.get_root(v)) {
 | |
|                     b += p.get_coeff(j);
 | |
|                 }
 | |
|             }            
 | |
|             if (b > k) {
 | |
|                 r.neg();
 | |
|                 unsigned coeff = b - k;
 | |
|                 
 | |
|                 svector<wliteral> wlits;
 | |
|                 // add coeff * r to p
 | |
|                 wlits.push_back(wliteral(coeff, r));
 | |
|                 for (unsigned j = 0; j < sz; ++j) {
 | |
|                     u = p.get_lit(j);
 | |
|                     unsigned c = p.get_coeff(j);
 | |
|                     if (r == u) {
 | |
|                         wlits[0].first += c;
 | |
|                     }
 | |
|                     else if (~r == u) {
 | |
|                         if (coeff == c) {
 | |
|                             wlits[0] = wlits.back();
 | |
|                             wlits.pop_back();
 | |
|                             b -= c;
 | |
|                         }
 | |
|                         else if (coeff < c) {
 | |
|                             wlits[0].first = c - coeff;
 | |
|                             wlits[0].second.neg();
 | |
|                             b -= coeff;
 | |
|                         }
 | |
|                         else {
 | |
|                             // coeff > c
 | |
|                             wlits[0].first = coeff - c;
 | |
|                             b -= c;
 | |
|                         }
 | |
|                     }
 | |
|                     else {
 | |
|                         wlits.push_back(wliteral(c, u));
 | |
|                     }
 | |
|                 }
 | |
|                 ++m_stats.m_num_big_strengthenings;
 | |
|                 add_pb_ge(sat::null_literal, wlits, b, p.learned());
 | |
|                 p.set_removed();
 | |
|                 return;
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::subsumption(constraint& cnstr) {
 | |
|         if (cnstr.was_removed())
 | |
|             return;
 | |
|         if (cnstr.k() <= 1)
 | |
|             return;
 | |
|         switch (cnstr.tag()) {
 | |
|         case pb::tag_t::card_t:
 | |
|             subsumption(cnstr.to_card());
 | |
|             break;
 | |
|         case pb::tag_t::pb_t:
 | |
|             subsumption(cnstr.to_pb());
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::init_visited() { s().init_visited(); }
 | |
|     void solver::mark_visited(literal l) { s().mark_visited(l); }
 | |
|     void solver::mark_visited(bool_var v) { s().mark_visited(v); }
 | |
|     bool solver::is_visited(bool_var v) const { return s().is_visited(v); }    
 | |
|     bool solver::is_visited(literal l) const { return s().is_visited(l); }
 | |
| 
 | |
|     void solver::cleanup_clauses() {
 | |
|         if (m_clause_removed) {
 | |
|             cleanup_clauses(s().m_clauses);
 | |
|             cleanup_clauses(s().m_learned);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     typedef sat::clause_vector clause_vector;
 | |
|     typedef sat::clause clause;
 | |
| 
 | |
|     void solver::cleanup_clauses(clause_vector& clauses) {
 | |
|         // version in simplify first clears 
 | |
|         // all watch literals, then reinserts them.
 | |
|         // this ensures linear time cleanup.
 | |
|         clause_vector::iterator it = clauses.begin();
 | |
|         clause_vector::iterator end = clauses.end();
 | |
|         clause_vector::iterator it2 = it;
 | |
|         for (; it != end; ++it) {
 | |
|             clause* c = *it;
 | |
|             if (c->was_removed() && s().can_delete(*c)) {
 | |
|                 s().detach_clause(*c);
 | |
|                 s().del_clause(*c);
 | |
|             }
 | |
|             else {
 | |
|                 if (it2 != it) {
 | |
|                     *it2 = *it;
 | |
|                 }
 | |
|                 ++it2;
 | |
|             }
 | |
|         }
 | |
|         clauses.set_end(it2);        
 | |
|     }
 | |
|     
 | |
|     void solver::cleanup_constraints() {
 | |
|         if (m_constraint_removed) {
 | |
|             cleanup_constraints(m_constraints, false);
 | |
|             cleanup_constraints(m_learned, true);
 | |
|             m_constraint_removed = false;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::cleanup_constraints(ptr_vector<constraint>& cs, bool learned) {
 | |
|         ptr_vector<constraint>::iterator it = cs.begin();
 | |
|         ptr_vector<constraint>::iterator it2 = it;
 | |
|         ptr_vector<constraint>::iterator end = cs.end();
 | |
|         for (; it != end; ++it) {
 | |
|             constraint& c = *(*it);            
 | |
|             if (c.was_removed()) {
 | |
|                 clear_watch(c);
 | |
|                 c.nullify_tracking_literal(*this);
 | |
|                 c.deallocate(m_allocator);
 | |
|             }
 | |
|             else if (learned && !c.learned()) {
 | |
|                 m_constraints.push_back(&c);
 | |
|             }
 | |
|             else {
 | |
|                 if (it != it2) {
 | |
|                     *it2 = *it;
 | |
|                 }
 | |
|                 ++it2;
 | |
|             }
 | |
|         }
 | |
|         cs.set_end(it2);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     /*
 | |
|       \brief subsumption between two cardinality constraints
 | |
|       - A >= k       subsumes A + B >= k' for k' <= k
 | |
|       - A + A' >= k  subsumes A + B >= k' for k' + |A'| <= k
 | |
|       - A + lit >= k self subsumes A + ~lit + B >= k' into A + B >= k' for k' <= k
 | |
|       - version that generalizes self-subsumption to more than one literal
 | |
|         A + ~L + B >= k'   =>    A + B >= k'    if A + A' + L >= k and k' + |L| + |A'| <= k
 | |
|      */
 | |
|     bool solver::subsumes(card& c1, card& c2, literal_vector & comp) {
 | |
|         if (c2.lit() != sat::null_literal) return false; 
 | |
| 
 | |
|         unsigned common = 0;
 | |
|         comp.reset();
 | |
|         for (literal l : c2) {
 | |
|             if (is_visited(l)) {
 | |
|                 ++common;
 | |
|             }
 | |
|             else if (is_visited(~l)) {
 | |
|                 comp.push_back(l);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         unsigned c1_exclusive = c1.size() - common - comp.size();
 | |
|         return c1_exclusive + c2.k() + comp.size() <= c1.k();
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief L + A >= k subsumes L + C if |A| < k
 | |
|              A + L + B >= k self-subsumes A + ~L + C >= 1
 | |
|              if k + 1 - |B| - |C| - |L| > 0
 | |
|      */
 | |
|     bool solver::subsumes(card& c1, sat::clause& c2, bool& self) {
 | |
|         unsigned common = 0, complement = 0, c2_exclusive = 0;
 | |
|         self = false;
 | |
|         
 | |
|         for (literal l : c2) {
 | |
|             if (is_visited(l)) {
 | |
|                 ++common;
 | |
|             }
 | |
|             else if (is_visited(~l)) {
 | |
|                 ++complement;
 | |
|             }
 | |
|             else {
 | |
|                 ++c2_exclusive;
 | |
|             }
 | |
|         }
 | |
|         unsigned c1_exclusive = c1.size() - common - complement;
 | |
|         if (complement > 0 && c1.k() + 1 > c1_exclusive + c2_exclusive + common) {
 | |
|             self = true;
 | |
|             return true; 
 | |
|         }
 | |
|         return c1.size() - common < c1.k();
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief Ax >= k subsumes By >= k' if
 | |
|       all coefficients in A are <= B and k >= k'
 | |
|      */
 | |
|     bool solver::subsumes(pbc const& p1, constraint const& p2) {
 | |
|         if (p1.k() < p2.k() || p1.size() > p2.size()) return false;
 | |
|         unsigned num_sub = 0;
 | |
|         for (unsigned i = 0; i < p2.size(); ++i) {
 | |
|             literal l = p2.get_lit(i);
 | |
|             if (is_visited(l) && m_weights[l.index()] <= p2.get_coeff(i)) {
 | |
|                 ++num_sub;
 | |
|             }
 | |
|             if (p1.size() + i > p2.size() + num_sub) return false;
 | |
|         }
 | |
|         return num_sub == p1.size();
 | |
|     }
 | |
| 
 | |
|     void solver::subsumes(pbc& p1, literal lit) {
 | |
|         for (constraint* c : m_cnstr_use_list[lit.index()]) {
 | |
|             if (c == &p1 || c->was_removed() || c->lit() != sat::null_literal)
 | |
|                 continue;
 | |
|             bool sub = false;
 | |
|             switch (c->tag()) {
 | |
|             case pb::tag_t::card_t:
 | |
|                 sub = subsumes(p1, c->to_card()); 
 | |
|                 break;
 | |
|             case pb::tag_t::pb_t:
 | |
|                 sub = subsumes(p1, c->to_pb()); 
 | |
|                 break;
 | |
|             default: 
 | |
|                 break;
 | |
|             }
 | |
|             if (sub) {
 | |
|                 ++m_stats.m_num_pb_subsumes;                
 | |
|                 set_non_learned(p1);
 | |
|                 remove_constraint(*c, "subsumed");
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     literal solver::get_min_occurrence_literal(card const& c) {
 | |
|         unsigned occ_count = UINT_MAX;
 | |
|         literal lit = sat::null_literal;
 | |
|         for (literal l : c) {
 | |
|             unsigned occ_count1 = m_cnstr_use_list[l.index()].size();
 | |
|             if (occ_count1 < occ_count) {
 | |
|                 lit = l;
 | |
|                 occ_count = occ_count1;
 | |
|             }
 | |
|         }
 | |
|         return lit;
 | |
|     }
 | |
| 
 | |
|     void solver::card_subsumption(card& c1, literal lit) {
 | |
|         literal_vector slit;
 | |
|         for (constraint* c : m_cnstr_use_list[lit.index()]) {
 | |
|             if (!c->is_card() || c == &c1 || c->was_removed()) {
 | |
|                 continue;
 | |
|             }
 | |
|             card& c2 = c->to_card();
 | |
| 
 | |
|             SASSERT(&c1 != &c2);
 | |
|             if (subsumes(c1, c2, slit)) {
 | |
|                 if (slit.empty()) {
 | |
|                     TRACE("pb", tout << "subsume cardinality\n" << c1 << "\n" << c2 << "\n";);
 | |
|                     remove_constraint(c2, "subsumed");
 | |
|                     ++m_stats.m_num_pb_subsumes;
 | |
|                     set_non_learned(c1);
 | |
|                 }
 | |
|                 else {
 | |
|                     TRACE("pb", tout << "self subsume cardinality\n";);
 | |
|                     IF_VERBOSE(11, 
 | |
|                                verbose_stream() << "self-subsume cardinality\n"; 
 | |
|                                verbose_stream() << c1 << "\n";
 | |
|                                verbose_stream() << c2 << "\n";);
 | |
|                     clear_watch(c2);                    
 | |
|                     unsigned j = 0;
 | |
|                     for (unsigned i = 0; i < c2.size(); ++i) {
 | |
|                         if (!is_visited(~c2[i])) {
 | |
|                             c2[j++] = c2[i];
 | |
|                         }
 | |
|                     }
 | |
|                     c2.set_size(j);
 | |
|                     init_watch(c2);
 | |
|                     m_simplify_change = true;
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::clause_subsumption(card& c1, literal lit, sat::clause_vector& removed_clauses) {
 | |
|         SASSERT(!c1.was_removed());
 | |
|         sat::clause_use_list& occurs = m_clause_use_list.get(lit);
 | |
|         sat::clause_use_list::iterator it = occurs.mk_iterator();
 | |
|         while (!it.at_end()) {
 | |
|             sat::clause& c2 = it.curr();
 | |
|             bool self;
 | |
|             if (!c2.was_removed() && subsumes(c1, c2, self)) {
 | |
|                 if (self) {
 | |
|                     // self-subsumption is TBD
 | |
|                 }
 | |
|                 else {
 | |
|                     TRACE("pb", tout << "remove\n" << c1 << "\n" << c2 << "\n";);
 | |
|                     removed_clauses.push_back(&c2);
 | |
|                     ++m_stats.m_num_clause_subsumes;
 | |
|                     set_non_learned(c1);
 | |
|                 }            
 | |
|             }
 | |
|             it.next();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::set_non_learned(constraint& c) {
 | |
|         literal lit = c.lit();
 | |
|         if (lit != sat::null_literal) {
 | |
|             s().set_external(lit.var());
 | |
|         }
 | |
|         for (literal lit : constraint::literal_iterator(c)) {
 | |
|             s().set_external(lit.var());             
 | |
|             SASSERT(!s().was_eliminated(lit.var()));
 | |
|         }
 | |
|         c.set_learned(false);
 | |
|     }
 | |
| 
 | |
|     typedef sat::watch_list watch_list;
 | |
| 
 | |
|     void solver::binary_subsumption(card& c1, literal lit) {
 | |
|         if (c1.k() + 1 != c1.size()) return;
 | |
|         SASSERT(is_visited(lit));
 | |
|         SASSERT(!c1.was_removed());
 | |
|         watch_list & wlist = get_wlist(~lit);
 | |
|         watch_list::iterator it = wlist.begin();
 | |
|         watch_list::iterator it2 = it;
 | |
|         watch_list::iterator end = wlist.end();
 | |
|         for (; it != end; ++it) {
 | |
|             sat::watched w = *it;
 | |
|             if (w.is_binary_clause() && is_visited(w.get_literal())) {
 | |
|                 ++m_stats.m_num_bin_subsumes;
 | |
|                 IF_VERBOSE(20, verbose_stream() << c1 << " subsumes (" << lit << " " << w.get_literal() << ")\n";);
 | |
|                 if (!w.is_learned()) {
 | |
|                     set_non_learned(c1);
 | |
|                 }
 | |
|             }
 | |
|             else {
 | |
|                 if (it != it2) {
 | |
|                     *it2 = *it;
 | |
|                 }
 | |
|                 ++it2;
 | |
|             }
 | |
|         }
 | |
|         wlist.set_end(it2);        
 | |
|     }
 | |
| 
 | |
|     void solver::subsumption(card& c1) {
 | |
|         if (c1.was_removed() || c1.lit() != sat::null_literal) {
 | |
|             return;
 | |
|         }
 | |
|         sat::clause_vector removed_clauses;
 | |
|         init_visited();
 | |
|         for (literal l : c1) mark_visited(l);  
 | |
|         for (unsigned i = 0; i < std::min(c1.size(), c1.k() + 1); ++i) {
 | |
|             literal lit = c1[i];            
 | |
|             card_subsumption(c1, lit);
 | |
|             clause_subsumption(c1, lit, removed_clauses);
 | |
|             binary_subsumption(c1, lit);                   
 | |
|         }
 | |
|         m_clause_removed |= !removed_clauses.empty();
 | |
|         for (clause *c : removed_clauses) {
 | |
|             c->set_removed(true);
 | |
|             m_clause_use_list.erase(*c);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::subsumption(pbc& p1) {
 | |
|         if (p1.was_removed() || p1.lit() != sat::null_literal) {
 | |
|             return;
 | |
|         }
 | |
|         init_visited();
 | |
|         for (wliteral l : p1) {
 | |
|             SASSERT(m_weights.size() <= l.second.index() || m_weights[l.second.index()] == 0);
 | |
|             m_weights.setx(l.second.index(), l.first, 0);
 | |
|             mark_visited(l.second);  
 | |
|         }
 | |
|         for (unsigned i = 0; i < std::min(10u, p1.num_watch()); ++i) {
 | |
|             unsigned j = s().m_rand() % p1.num_watch();
 | |
|             subsumes(p1, p1[j].second);
 | |
|         }
 | |
|         for (wliteral l : p1) {
 | |
|             m_weights[l.second.index()] = 0;
 | |
|         }        
 | |
|     }
 | |
| 
 | |
|     void solver::clauses_modifed() {}
 | |
| 
 | |
|     lbool solver::get_phase(bool_var v) { return l_undef; }
 | |
| 
 | |
|     /*
 | |
|       \brief lit <=> conjunction of unconstrained lits
 | |
|      */
 | |
|     void solver::assert_unconstrained(literal lit, literal_vector const& lits) {
 | |
|         if (lit == sat::null_literal) {
 | |
|             for (literal l : lits) {
 | |
|                 if (value(l) == l_undef) {
 | |
|                     s().assign_scoped(l);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|         else {
 | |
|             // add clauses for: lit <=> conjunction of undef literals
 | |
|             SASSERT(value(lit) == l_undef);
 | |
|             literal_vector cl;
 | |
|             cl.push_back(lit);
 | |
|             for (literal l : lits) {
 | |
|                 if (value(l) == l_undef) {
 | |
|                     s().mk_clause(~lit, l);
 | |
|                     cl.push_back(~l);
 | |
|                 }
 | |
|             }    
 | |
|             s().mk_clause(cl);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     sat::extension* solver::copy(sat::solver* s) {
 | |
|         return clone_aux(m, *s, si, m_id);
 | |
|     }
 | |
| 
 | |
|     euf::th_solver* solver::clone(euf::solver& new_ctx) {
 | |
|         return clone_aux(new_ctx.get_manager(), new_ctx.s(), new_ctx.get_si(), get_id());
 | |
|     }
 | |
| 
 | |
|     euf::th_solver* solver::clone_aux(ast_manager& m, sat::solver& s, sat::sat_internalizer& si, euf::theory_id id) {
 | |
|         solver* result = alloc(solver, m, si, id);
 | |
|         result->set_solver(&s);
 | |
|         copy_constraints(result, m_constraints);
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     void solver::copy_constraints(solver* result, ptr_vector<constraint> const& constraints) {
 | |
|         literal_vector lits;
 | |
|         svector<wliteral> wlits;
 | |
|         for (constraint* cp : constraints) {
 | |
|             switch (cp->tag()) {
 | |
|             case pb::tag_t::card_t: {
 | |
|                 card const& c = cp->to_card();
 | |
|                 lits.reset();
 | |
|                 for (literal l : c) lits.push_back(l);
 | |
|                 result->add_at_least(c.lit(), lits, c.k(), c.learned());        
 | |
|                 break;
 | |
|             }
 | |
|             case pb::tag_t::pb_t: {
 | |
|                 pbc const& p = cp->to_pb();
 | |
|                 wlits.reset();
 | |
|                 for (wliteral w : p) {
 | |
|                     wlits.push_back(w);
 | |
|                 }
 | |
|                 result->add_pb_ge(p.lit(), wlits, p.k(), p.learned());
 | |
|                 break;
 | |
|             }
 | |
|             default:
 | |
|                 UNREACHABLE();
 | |
|             }                
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::init_use_list(sat::ext_use_list& ul) {
 | |
|         ul.init(s().num_vars());
 | |
|         for (constraint const* cp : m_constraints) {
 | |
|             sat::ext_constraint_idx idx = cp->cindex();
 | |
|             if (cp->lit() != sat::null_literal) {
 | |
|                 ul.insert(cp->lit(), idx);
 | |
|                 ul.insert(~cp->lit(), idx);                
 | |
|             }
 | |
|             cp->init_use_list(ul);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     //
 | |
|     // literal is used in a clause (C or l), it
 | |
|     // it occurs negatively in constraint c.
 | |
|     // all literals in C are marked
 | |
|     // 
 | |
|     bool solver::is_blocked(literal l, sat::ext_constraint_idx idx) {
 | |
|         constraint const& c = index2constraint(idx);
 | |
|         sat::simplifier& sim = s().m_simplifier;
 | |
|         if (c.lit() != sat::null_literal) return false;
 | |
|         return c.is_blocked(sim, l);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void solver::find_mutexes(literal_vector& lits, vector<literal_vector> & mutexes) {
 | |
|         sat::literal_set slits(lits);
 | |
|         bool change = false;
 | |
|         
 | |
|         for (constraint* cp : m_constraints) {
 | |
|             if (!cp->is_card()) 
 | |
|                 continue;
 | |
|             if (cp->lit() != sat::null_literal)
 | |
|                 continue;
 | |
|             card const& c = cp->to_card();
 | |
|             if (c.size() != c.k() + 1) 
 | |
|                 continue;
 | |
| 
 | |
|             literal_vector mux;
 | |
|             for (literal lit : c) 
 | |
|                 if (slits.contains(~lit)) 
 | |
|                     mux.push_back(~lit);
 | |
| 
 | |
|             if (mux.size() <= 1) 
 | |
|                 continue;
 | |
|             
 | |
|             for (literal m : mux) 
 | |
|                 slits.remove(m);
 | |
| 
 | |
|             change = true;
 | |
|             mutexes.push_back(mux);            
 | |
|         }        
 | |
|         if (!change) 
 | |
|             return;
 | |
|         lits.reset();
 | |
|         for (literal l : slits) 
 | |
|             lits.push_back(l);
 | |
|     }
 | |
| 
 | |
|     void solver::display(std::ostream& out, ineq const& ineq, bool values) const {
 | |
|         for (unsigned i = 0; i < ineq.size(); ++i) {
 | |
|             if (ineq.coeff(i) != 1) out << ineq.coeff(i) << "*";
 | |
|             out << ineq.lit(i) << " ";
 | |
|             if (values) out << value(ineq.lit(i)) << " ";
 | |
|         }
 | |
|         out << ">= " << ineq.m_k << "\n";
 | |
|     }
 | |
| 
 | |
| 
 | |
|     std::ostream& solver::display(std::ostream& out) const {
 | |
|         for (constraint const* c : m_constraints) {
 | |
|             out << (*c) << "\n";
 | |
|         }
 | |
|         if (!m_learned.empty()) {
 | |
|             out << "learned:\n";
 | |
|         }
 | |
|         for (constraint const* c : m_learned) {
 | |
|             out << (*c) << "\n";
 | |
|         }
 | |
|         return out;
 | |
|     }
 | |
| 
 | |
|     std::ostream& solver::display_justification(std::ostream& out, sat::ext_justification_idx idx) const {
 | |
|         return out << index2constraint(idx);
 | |
|     }
 | |
| 
 | |
|     std::ostream& solver::display_constraint(std::ostream& out, sat::ext_constraint_idx idx) const {
 | |
|         return out << index2constraint(idx);
 | |
|     }
 | |
| 
 | |
|     std::ostream& solver::display(std::ostream& out, constraint const& c, bool values) const {
 | |
|         return c.display(out, *this, values);
 | |
|     }
 | |
| 
 | |
|     void solver::collect_statistics(statistics& st) const {
 | |
|         st.update("pb propagations", m_stats.m_num_propagations);
 | |
|         st.update("pb conflicts", m_stats.m_num_conflicts);
 | |
|         st.update("pb resolves", m_stats.m_num_resolves);
 | |
|         st.update("pb cuts", m_stats.m_num_cut);
 | |
|         st.update("pb gc", m_stats.m_num_gc);
 | |
|         st.update("pb overflow", m_stats.m_num_overflow);
 | |
|         st.update("pb big strengthenings", m_stats.m_num_big_strengthenings);
 | |
|         st.update("pb lemmas", m_stats.m_num_lemmas);
 | |
|         st.update("pb subsumes", m_stats.m_num_bin_subsumes + m_stats.m_num_clause_subsumes + m_stats.m_num_pb_subsumes);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     bool solver::validate_unit_propagation(pbc const& p, literal_vector const& r, literal alit) const {
 | |
|         // all elements of r are true, 
 | |
|         for (literal l : r) {
 | |
|             if (value(l) != l_true) {
 | |
|                 IF_VERBOSE(0, verbose_stream() << "value of " << l << " is " << value(l) << "\n";
 | |
|                            display(verbose_stream(), p, true););
 | |
|                 return false;
 | |
|             }
 | |
|             if (value(alit) == l_true && lvl(l) > lvl(alit)) {
 | |
|                 IF_VERBOSE(0, 
 | |
|                            verbose_stream() << "level of premise " << l << " is " << lvl(l) << "\n";
 | |
|                            verbose_stream() << "level of asserting literal " << alit << " is " << lvl(alit) << "\n";
 | |
|                            display(verbose_stream(), p, true););
 | |
|                 return false;                
 | |
|             }
 | |
|         }
 | |
|         // the sum of elements not in r or alit add up to less than k.
 | |
|         unsigned sum = 0;
 | |
|         // 
 | |
|         // a*x + b*alit + c*r >= k
 | |
|         // sum a < k
 | |
|         // val(r) = false
 | |
|         // hence alit has to be true.
 | |
|         for (wliteral wl : p) {
 | |
|             literal lit = wl.second;
 | |
|             if (lit != alit && !r.contains(~lit)) {
 | |
|                 sum += wl.first;
 | |
|             }
 | |
|         }
 | |
|         if (sum >= p.k()) {
 | |
|             IF_VERBOSE(0,             
 | |
|                        verbose_stream() << "sum is " << sum << " >= " << p.k() << "\n";
 | |
|                        display(verbose_stream(), p, true);
 | |
|                        verbose_stream() << "id: " << p.id() << "\n";
 | |
|                        sum = 0;
 | |
|                        for (wliteral wl : p) sum += wl.first;
 | |
|                        verbose_stream() << "overall sum " << sum << "\n";
 | |
|                        verbose_stream() << "asserting literal: " << alit << "\n";
 | |
|                        verbose_stream() << "reason: " << r << "\n";);
 | |
|             return false;
 | |
|         }
 | |
|         for (wliteral wl : p) {
 | |
|             if (alit == wl.second) {
 | |
|                 return true;
 | |
|             }
 | |
|         }
 | |
|         IF_VERBOSE(0, verbose_stream() << alit << " not found among literals\n";);
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     bool solver::validate_lemma() { 
 | |
|         int64_t bound64 = m_bound;
 | |
|         int64_t val = -bound64;
 | |
|         reset_active_var_set();
 | |
|         for (bool_var v : m_active_vars) {
 | |
|             if (!test_and_set_active(v)) continue;
 | |
|             wliteral wl = get_wliteral(v);
 | |
|             if (wl.first == 0) continue;
 | |
|             if (!is_false(wl.second)) {
 | |
|                 val += wl.first;
 | |
|             }
 | |
|         }
 | |
|         CTRACE("pb", val >= 0, active2pb(m_A); display(tout, m_A, true););
 | |
|         return val < 0;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * the slack of inequalities on the stack should be non-positive.
 | |
|      */
 | |
|     bool solver::validate_ineq(ineq const& ineq) const {
 | |
|         int64_t k = -static_cast<int64_t>(ineq.m_k);
 | |
|         for (wliteral wl : ineq.m_wlits) {
 | |
|             if (!is_false(wl.second))
 | |
|                 k += wl.first;
 | |
|         }
 | |
|         CTRACE("pb", k > 0, display(tout, ineq, true););
 | |
|         return k <= 0;
 | |
|     }
 | |
| 
 | |
|     void solver::reset_active_var_set() {
 | |
|         while (!m_active_var_set.empty()) m_active_var_set.erase();
 | |
|     }
 | |
| 
 | |
|     bool solver::test_and_set_active(bool_var v) {
 | |
|         if (m_active_var_set.contains(v)) {
 | |
|             return false;
 | |
|         }
 | |
|         else {
 | |
|             m_active_var_set.insert(v);
 | |
|             return true;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::active2pb(ineq& p) {
 | |
|         p.reset(m_bound);
 | |
|         active2wlits(p.m_wlits);
 | |
|     }
 | |
| 
 | |
|     void solver::active2wlits() {
 | |
|         m_wlits.reset();
 | |
|         active2wlits(m_wlits);
 | |
|     }
 | |
| 
 | |
|     void solver::active2wlits(svector<wliteral>& wlits) {
 | |
|         uint64_t sum = 0;        
 | |
|         reset_active_var_set();
 | |
|         for (bool_var v : m_active_vars) {
 | |
|             if (!test_and_set_active(v)) continue;
 | |
|             wliteral wl = get_wliteral(v);
 | |
|             if (wl.first == 0) continue;
 | |
|             wlits.push_back(wl);
 | |
|             sum += wl.first;
 | |
|         }
 | |
|         m_overflow |= sum >= UINT_MAX/2;
 | |
|     }
 | |
| 
 | |
|     constraint* solver::active2lemma() {
 | |
|         switch (get_config().m_pb_lemma_format) {
 | |
|         case sat::PB_LEMMA_CARDINALITY:
 | |
|             return active2card();
 | |
|         case sat::PB_LEMMA_PB:
 | |
|             return active2constraint();
 | |
|         default:
 | |
|             UNREACHABLE();
 | |
|             return nullptr;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     constraint* solver::active2constraint() {
 | |
|         active2wlits();
 | |
|         if (m_overflow) {
 | |
|             return nullptr;
 | |
|         }
 | |
|         constraint* c = add_pb_ge(sat::null_literal, m_wlits, m_bound, true);                
 | |
|         TRACE("pb", if (c) display(tout, *c, true););
 | |
|         ++m_stats.m_num_lemmas;
 | |
|         return c;
 | |
|     }
 | |
|     
 | |
|     /*
 | |
|       Chai Kuhlmann:
 | |
|       
 | |
|       a1*l1 + ... + a_n*l_n >= k
 | |
|       s.t.
 | |
|       a1 >= a2 >= .. >= a_n
 | |
| 
 | |
|       let m be such that
 | |
| 
 | |
|          sum_{i = 1}^{m-1} a_i < k <= sum_{i = 1}^{m}
 | |
| 
 | |
|       then 
 | |
| 
 | |
|       l1 + ... + l_n >= m
 | |
| 
 | |
|       furthermore, for the largest n' <= n, such that
 | |
| 
 | |
|          sum_{i = n'+1}^n a_i + sum_{i = 1}^{m-1} a_i < k
 | |
| 
 | |
|       then 
 | |
| 
 | |
|          l1 + ... + l_n' >= m
 | |
|       
 | |
|      */
 | |
|     struct compare_wlit {
 | |
|         bool operator()(wliteral l1, wliteral l2) const {
 | |
|             return l1.first > l2.first;
 | |
|         }
 | |
|     };
 | |
| 
 | |
| 
 | |
|     constraint* solver::active2card() {
 | |
|         active2wlits();
 | |
|         if (m_overflow) {
 | |
|             return nullptr;
 | |
|         }
 | |
|         std::sort(m_wlits.begin(), m_wlits.end(), compare_wlit());
 | |
|         unsigned k = 0;
 | |
|         uint64_t sum = 0, sum0 = 0;
 | |
|         for (wliteral wl : m_wlits) {
 | |
|             if (sum >= m_bound) break;
 | |
|             sum0 = sum;
 | |
|             sum += wl.first;
 | |
|             ++k;
 | |
|         }
 | |
|         if (k == 1) {
 | |
|             return nullptr;
 | |
|         }
 | |
|         while (!m_wlits.empty()) {
 | |
|             wliteral wl = m_wlits.back();
 | |
|             if (wl.first + sum0 >= m_bound) break;
 | |
|             m_wlits.pop_back();
 | |
|             sum0 += wl.first;
 | |
|         }
 | |
| 
 | |
|         unsigned slack = 0;
 | |
|         unsigned max_level = 0;
 | |
|         for (wliteral wl : m_wlits) {
 | |
|             if (value(wl.second) != l_false) ++slack;
 | |
|             unsigned level = lvl(wl.second);
 | |
|             if (level > max_level) {
 | |
|                 max_level = level;
 | |
|             }
 | |
|             else if (max_level == level) {
 | |
|             }
 | |
|         }
 | |
|         if (m_overflow) 
 | |
|             return nullptr;
 | |
| 
 | |
|         if (slack >= k) 
 | |
|             return nullptr;
 | |
| 
 | |
|         // produce asserting cardinality constraint
 | |
|         literal_vector lits;
 | |
|         for (wliteral wl : m_wlits) { 
 | |
|             lits.push_back(wl.second);
 | |
|         }       
 | |
|         constraint* c = add_at_least(sat::null_literal, lits, k, true);      
 | |
| 
 | |
|         ++m_stats.m_num_lemmas;
 | |
| 
 | |
|         if (c) {
 | |
|             lits.reset();
 | |
|             for (wliteral wl : m_wlits) {
 | |
|                 if (value(wl.second) == l_false) lits.push_back(wl.second);        
 | |
|             }
 | |
|             unsigned glue = s().num_diff_levels(lits.size(), lits.data());
 | |
|             c->set_glue(glue);
 | |
|         }
 | |
|         return c;
 | |
|     }
 | |
| 
 | |
| 
 | |
|     void solver::justification2pb(sat::justification const& js, literal lit, unsigned offset, ineq& ineq) {
 | |
|         switch (js.get_kind()) {
 | |
|         case sat::justification::NONE:
 | |
|             SASSERT(lit != sat::null_literal);
 | |
|             ineq.reset(offset);
 | |
|             ineq.push(lit, offset);
 | |
|             break;
 | |
|         case sat::justification::BINARY:
 | |
|             SASSERT(lit != sat::null_literal);
 | |
|             ineq.reset(offset);
 | |
|             ineq.push(lit, offset);
 | |
|             ineq.push(js.get_literal(), offset);
 | |
|             break;
 | |
|         case sat::justification::CLAUSE: {
 | |
|             ineq.reset(offset);
 | |
|             sat::clause & c = s().get_clause(js);
 | |
|             for (literal l : c) ineq.push(l, offset);
 | |
|             break;
 | |
|         }
 | |
|         case sat::justification::EXT_JUSTIFICATION: {
 | |
|             sat::ext_justification_idx index = js.get_ext_justification_idx();
 | |
|             VERIFY(this == sat::constraint_base::to_extension(index));
 | |
|             constraint& cnstr = index2constraint(index);
 | |
|             constraint2pb(cnstr, lit, offset, ineq);
 | |
|             break;
 | |
|         }
 | |
|         default:
 | |
|             UNREACHABLE();
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     void solver::constraint2pb(constraint& cnstr, literal lit, unsigned offset, ineq& ineq) {
 | |
|         switch (cnstr.tag()) {
 | |
|         case pb::tag_t::card_t: {
 | |
|             card& c = cnstr.to_card();
 | |
|             ineq.reset(static_cast<uint64_t>(offset)*c.k());
 | |
|             for (literal l : c) ineq.push(l, offset);
 | |
|             if (c.lit() != sat::null_literal) ineq.push(~c.lit(), offset*c.k());                
 | |
|             break;
 | |
|         }
 | |
|         case pb::tag_t::pb_t: {
 | |
|             pbc& p = cnstr.to_pb();
 | |
|             ineq.reset(static_cast<uint64_t>(offset) * p.k());
 | |
|             for (wliteral wl : p) ineq.push(wl.second, offset * wl.first);
 | |
|             if (p.lit() != sat::null_literal) ineq.push(~p.lit(), offset * p.k());
 | |
|             break;
 | |
|         }
 | |
|         default:
 | |
|             UNREACHABLE();
 | |
|             break;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // validate that m_A & m_B implies m_C
 | |
| 
 | |
|     bool solver::validate_resolvent() {
 | |
|         return true;
 | |
|         u_map<uint64_t> coeffs;
 | |
|         uint64_t k = m_A.m_k + m_B.m_k;
 | |
|         for (unsigned i = 0; i < m_A.size(); ++i) {
 | |
|             uint64_t coeff = m_A.coeff(i);
 | |
|             SASSERT(!coeffs.contains(m_A.lit(i).index()));
 | |
|             coeffs.insert(m_A.lit(i).index(), coeff);
 | |
|         }
 | |
|         for (unsigned i = 0; i < m_B.size(); ++i) {
 | |
|             uint64_t coeff1 = m_B.coeff(i), coeff2;
 | |
|             literal lit = m_B.lit(i);
 | |
|             if (coeffs.find((~lit).index(), coeff2)) {
 | |
|                 if (coeff1 == coeff2) {
 | |
|                     coeffs.remove((~lit).index());
 | |
|                     k += coeff1;
 | |
|                 }
 | |
|                 else if (coeff1 < coeff2) {
 | |
|                     coeffs.insert((~lit).index(), coeff2 - coeff1);
 | |
|                     k += coeff1;
 | |
|                 }
 | |
|                 else {
 | |
|                     SASSERT(coeff2 < coeff1);
 | |
|                     coeffs.remove((~lit).index());
 | |
|                     coeffs.insert(lit.index(), coeff1 - coeff2);
 | |
|                     k += coeff2;
 | |
|                 }
 | |
|             }
 | |
|             else if (coeffs.find(lit.index(), coeff2)) {
 | |
|                 coeffs.insert(lit.index(), coeff1 + coeff2);
 | |
|             }
 | |
|             else {
 | |
|                 coeffs.insert(lit.index(), coeff1);
 | |
|             }
 | |
|         }
 | |
|         // C is above the sum of A and B
 | |
|         for (unsigned i = 0; i < m_C.size(); ++i) {
 | |
|             literal lit = m_C.lit(i);
 | |
|             uint64_t coeff;
 | |
|             if (coeffs.find(lit.index(), coeff)) {
 | |
|                 if (coeff > m_C.coeff(i) && m_C.coeff(i) < m_C.m_k) {
 | |
|                     goto violated;
 | |
|                 }
 | |
|                 coeffs.remove(lit.index());
 | |
|             }
 | |
|         }
 | |
|         if (!coeffs.empty()) goto violated;
 | |
|         if (m_C.m_k > k) goto violated;
 | |
|         SASSERT(coeffs.empty());
 | |
|         SASSERT(m_C.m_k <= k);
 | |
|         return true;
 | |
| 
 | |
|     violated:
 | |
|         // last ditch effort by translating to SAT.
 | |
|         sat::solver s0(s().m_params, s().rlimit());
 | |
|         u_map<bool_var> translation;
 | |
|         literal l1 = translate_to_sat(s0, translation, m_A);
 | |
|         if (l1 == sat::null_literal) return true;
 | |
|         literal l2 = translate_to_sat(s0, translation, m_B);
 | |
|         if (l2 == sat::null_literal) return true;
 | |
|         ineq notC = negate(m_B);
 | |
|         literal l3 = translate_to_sat(s0, translation, notC);
 | |
|         if (l3 == sat::null_literal) return true;
 | |
|         s0.assign_scoped(l1);
 | |
|         s0.assign_scoped(l2);
 | |
|         s0.assign_scoped(l3);
 | |
|         lbool is_sat = s0.check();
 | |
|         TRACE("pb", s0.display(tout << "trying sat encoding"););
 | |
|         if (is_sat == l_false) return true;
 | |
| 
 | |
|         IF_VERBOSE(0, 
 | |
|                    display(verbose_stream(), m_A);
 | |
|                    display(verbose_stream(), m_B);
 | |
|                    display(verbose_stream(), m_C);
 | |
|                    for (auto& e : coeffs) {
 | |
|                        verbose_stream() << sat::to_literal(e.m_key) << ": " << e.m_value << "\n";
 | |
|                    });
 | |
|         
 | |
|         UNREACHABLE();
 | |
|         return false;
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|        \brief translate PB inequality to SAT formula.
 | |
|      */
 | |
|     literal solver::translate_to_sat(sat::solver& s, u_map<bool_var>& translation, ineq const& pb) {
 | |
|         SASSERT(pb.m_k > 0);
 | |
|         if (pb.size() > 1) {
 | |
|             ineq a, b;
 | |
|             a.reset(pb.m_k);
 | |
|             b.reset(pb.m_k);
 | |
|             for (unsigned i = 0; i < pb.size()/2; ++i) {
 | |
|                 a.push(pb.lit(i), pb.coeff(i));
 | |
|             }
 | |
|             for (unsigned i = pb.size()/2; i < pb.size(); ++i) {
 | |
|                 b.push(pb.lit(i), pb.coeff(i));
 | |
|             }
 | |
|             bool_var v = s.mk_var();
 | |
|             literal lit(v, false);
 | |
|             literal_vector lits;
 | |
|             lits.push_back(~lit);            
 | |
|             push_lit(lits, translate_to_sat(s, translation, a));
 | |
|             push_lit(lits, translate_to_sat(s, translation, b));
 | |
|             push_lit(lits, translate_to_sat(s, translation, a, b));
 | |
|             s.mk_clause(lits);
 | |
|             return lit;
 | |
|         }
 | |
|         if (pb.coeff(0) >= pb.m_k) {
 | |
|             return translate_to_sat(s, translation, pb.lit(0));
 | |
|         }
 | |
|         else {
 | |
|             return sat::null_literal;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /*
 | |
|       \brief encode the case where Sum(a) >= k-1 & Sum(b) >= 1 \/ ... \/ Sum(a) >= 1 & Sum(b) >= k-1
 | |
|      */
 | |
|     literal solver::translate_to_sat(sat::solver& s, u_map<bool_var>& translation, ineq& a, ineq& b) {
 | |
|         uint64_t k0 = a.m_k;
 | |
|         literal_vector lits;
 | |
|         for (unsigned k = 1; k < a.m_k - 1; ++k) {
 | |
|             a.m_k = k; b.m_k = k0 - k;
 | |
|             literal lit1 = translate_to_sat(s, translation, a);
 | |
|             literal lit2 = translate_to_sat(s, translation, b);
 | |
|             if (lit1 != sat::null_literal && lit2 != sat::null_literal) {
 | |
|                 bool_var v = s.mk_var();
 | |
|                 literal lit(v, false);
 | |
|                 s.mk_clause(~lit, lit1);
 | |
|                 s.mk_clause(~lit, lit2);
 | |
|                 lits.push_back(lit);
 | |
|             }
 | |
|         }
 | |
|         a.m_k = k0;
 | |
|         b.m_k = k0;
 | |
|         switch (lits.size()) {
 | |
|         case 0: return sat::null_literal;
 | |
|         case 1: return lits[0];
 | |
|         default: {
 | |
|             bool_var v = s.mk_var();
 | |
|             literal lit(v, false);
 | |
|             lits.push_back(~lit);
 | |
|             s.mk_clause(lits);
 | |
|             return lit;
 | |
|         }
 | |
|         }
 | |
|     }
 | |
|     
 | |
|     literal solver::translate_to_sat(sat::solver& s, u_map<bool_var>& translation, literal lit) {
 | |
|         bool_var v;
 | |
|         if (!translation.find(lit.var(), v)) {            
 | |
|             v = s.mk_var();
 | |
|             translation.insert(lit.var(), v);
 | |
|         }
 | |
|         return literal(v, lit.sign());
 | |
|     }
 | |
| 
 | |
|     solver::ineq solver::negate(ineq const& a) const {
 | |
|         ineq result;
 | |
|         uint64_t sum = 0;
 | |
|         for (unsigned i = 0; i < a.size(); ++i) { 
 | |
|             result.push(~a.lit(i), a.coeff(i));
 | |
|             sum += a.coeff(i);
 | |
|         }
 | |
|         SASSERT(sum >= a.m_k + 1);
 | |
|         result.m_k = sum + 1 - a.m_k;
 | |
|         return result;
 | |
|     }
 | |
| 
 | |
|     void solver::push_lit(literal_vector& lits, literal lit) {
 | |
|         if (lit != sat::null_literal) {
 | |
|             lits.push_back(lit);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     bool solver::validate_conflict(literal_vector const& lits, ineq& p) { 
 | |
|         for (literal l : lits) {
 | |
|             if (value(l) != l_false) {
 | |
|                 TRACE("pb", tout << "literal " << l << " is not false\n";);
 | |
|                 return false;
 | |
|             }
 | |
|             if (!p.contains(l)) {
 | |
|                 TRACE("pb", tout << "lemma contains literal " << l << " not in inequality\n";);
 | |
|                 return false;
 | |
|             }
 | |
|         }
 | |
|         uint64_t value = 0;        
 | |
|         for (unsigned i = 0; i < p.size(); ++i) {
 | |
|             uint64_t coeff = p.coeff(i);
 | |
|             if (!lits.contains(p.lit(i))) {
 | |
|                 value += coeff;
 | |
|             }
 | |
|         }
 | |
|         CTRACE("pb", value >= p.m_k, tout << "slack: " << value << " bound " << p.m_k << "\n";
 | |
|                display(tout, p);
 | |
|                tout << lits << "\n";);
 | |
|         return value < p.m_k;
 | |
|     }
 | |
| 
 | |
|     bool solver::check_model(sat::model const& m) const {
 | |
|         bool ok = true;
 | |
|         for (constraint const* c : m_constraints) {
 | |
|             if (c->was_removed()) {
 | |
|                 continue;
 | |
|             }
 | |
|             if (c->is_pure() && c->lit() != sat::null_literal && m[c->lit().var()] == (c->lit().sign() ? l_true : l_false)) {
 | |
|                 continue;
 | |
|             }
 | |
|             switch (eval(m, *c)) {
 | |
|             case l_false: 
 | |
|                 IF_VERBOSE(0, verbose_stream() << "failed checking " << c->id() << ": " << *c << "\n";);
 | |
|                 ok = false;
 | |
|                 break;
 | |
|             case l_true:
 | |
|                 break;
 | |
|             case l_undef:
 | |
|                 IF_VERBOSE(0, verbose_stream() << "undef " << c->id() << ": " << *c << "\n";);
 | |
|                 break;
 | |
|             }
 | |
|         }
 | |
|         return ok;
 | |
|     }
 | |
| 
 | |
|     bool solver::extract_pb(std::function<void(unsigned sz, literal const* c, unsigned k)>& add_cardinality,
 | |
|                                std::function<void(unsigned sz, literal const* c, unsigned const* coeffs, unsigned k)>& add_pb) {
 | |
|         
 | |
|         unsigned_vector coeffs;
 | |
|         literal_vector lits;
 | |
|         for (constraint* cp : m_constraints) {
 | |
|             switch (cp->tag()) {
 | |
|             case pb::tag_t::card_t: {
 | |
|                 card const& c = cp->to_card();
 | |
|                 unsigned n = c.size();
 | |
|                 unsigned k = c.k();
 | |
| 
 | |
|                 if (c.lit() == sat::null_literal) {
 | |
|                     //    c.lits() >= k 
 | |
|                     // <=> 
 | |
|                     //    ~c.lits() <= n - k
 | |
|                     lits.reset();
 | |
|                     for (unsigned j = 0; j < n; ++j) lits.push_back(c[j]);
 | |
|                     add_cardinality(lits.size(), lits.data(), n - k);
 | |
|                 }
 | |
|                 else {
 | |
|                     //
 | |
|                     // c.lit() <=> c.lits() >= k
 | |
|                     // 
 | |
|                     //    (c.lits() < k) or c.lit()
 | |
|                     // =  (c.lits() + (n - k + 1)*~c.lit()) <= n    
 | |
|                     //
 | |
|                     //    ~c.lit() or (c.lits() >= k)
 | |
|                     // =  ~c.lit() or (~c.lits() <= n - k)
 | |
|                     // =  k*c.lit() + ~c.lits() <= n 
 | |
|                     // 
 | |
|                     lits.reset();
 | |
|                     coeffs.reset();
 | |
|                     for (literal l : c) lits.push_back(l), coeffs.push_back(1);
 | |
|                     lits.push_back(~c.lit()); coeffs.push_back(n - k + 1);
 | |
|                     add_pb(lits.size(), lits.data(), coeffs.data(), n);
 | |
| 
 | |
|                     lits.reset();
 | |
|                     coeffs.reset();
 | |
|                     for (literal l : c) lits.push_back(~l), coeffs.push_back(1);
 | |
|                     lits.push_back(c.lit()); coeffs.push_back(k);
 | |
|                     add_pb(lits.size(), lits.data(), coeffs.data(), n);
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             case pb::tag_t::pb_t: {
 | |
|                 pbc const& p = cp->to_pb();
 | |
|                 lits.reset();
 | |
|                 coeffs.reset();
 | |
|                 unsigned sum = 0;
 | |
|                 for (wliteral wl : p) sum += wl.first;
 | |
| 
 | |
|                 if (p.lit() == sat::null_literal) {
 | |
|                     //   w1 + .. + w_n >= k
 | |
|                     // <=> 
 | |
|                     //  ~wl + ... + ~w_n <= sum_of_weights - k
 | |
|                     for (wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first);
 | |
|                     add_pb(lits.size(), lits.data(), coeffs.data(), sum - p.k());
 | |
|                 }
 | |
|                 else {
 | |
|                     //    lit <=> w1 + .. + w_n >= k
 | |
|                     // <=>
 | |
|                     //     lit or w1 + .. + w_n <= k - 1
 | |
|                     //    ~lit or w1 + .. + w_n >= k
 | |
|                     // <=> 
 | |
|                     //     (sum - k + 1)*~lit + w1 + .. + w_n <= sum
 | |
|                     //     k*lit + ~wl + ... + ~w_n <= sum
 | |
|                     lits.push_back(p.lit()), coeffs.push_back(p.k());
 | |
|                     for (wliteral wl : p) lits.push_back(~(wl.second)), coeffs.push_back(wl.first);
 | |
|                     add_pb(lits.size(), lits.data(), coeffs.data(), sum);
 | |
| 
 | |
|                     lits.reset();
 | |
|                     coeffs.reset();
 | |
|                     lits.push_back(~p.lit()), coeffs.push_back(sum + 1 - p.k());
 | |
|                     for (wliteral wl : p) lits.push_back(wl.second), coeffs.push_back(wl.first);
 | |
|                     add_pb(lits.size(), lits.data(), coeffs.data(), sum);
 | |
|                 }
 | |
|                 break;
 | |
|             }
 | |
|             }
 | |
|         }
 | |
|         return true;
 | |
|     }
 | |
|     
 | |
| };
 | |
| 
 | |
| 
 |