mirror of
				https://github.com/Z3Prover/z3
				synced 2025-11-04 13:29:11 +00:00 
			
		
		
		
	retire deprecated functionality
Signed-off-by: Nikolaj Bjorner <nbjorner@microsoft.com>
This commit is contained in:
		
							parent
							
								
									8ec5ccbb9a
								
							
						
					
					
						commit
						8db711bc3c
					
				
					 15 changed files with 395 additions and 1100 deletions
				
			
		| 
						 | 
				
			
			@ -15,7 +15,6 @@ z3_add_component(polysat
 | 
			
		|||
    solver.cpp
 | 
			
		||||
    ule_constraint.cpp
 | 
			
		||||
    viable.cpp
 | 
			
		||||
    viable2.cpp
 | 
			
		||||
    variable_elimination.cpp
 | 
			
		||||
  COMPONENT_DEPENDENCIES
 | 
			
		||||
    util
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -100,12 +100,6 @@ namespace polysat {
 | 
			
		|||
        LOG("Conflict: v" << v);
 | 
			
		||||
        SASSERT(empty());
 | 
			
		||||
        m_conflict_var = v;
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
        for (auto c : s.m_cjust[v]) {
 | 
			
		||||
            c->set_var_dependent(); // ??
 | 
			
		||||
            insert(c);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
        SASSERT(!empty());
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -300,23 +294,13 @@ namespace polysat {
 | 
			
		|||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if NEW_VIABLE
 | 
			
		||||
        if (conflict_var() == v && s.m_viable.resolve(v, *this))
 | 
			
		||||
            return true;
 | 
			
		||||
#else
 | 
			
		||||
        if (conflict_var() == v && s.m_forbidden_intervals.perform(v, cjust_v, *this))
 | 
			
		||||
            return true;        
 | 
			
		||||
#endif
 | 
			
		||||
        
 | 
			
		||||
        m_vars.remove(v);
 | 
			
		||||
 | 
			
		||||
#if NEW_VIABLE
 | 
			
		||||
        for (auto const& c : s.m_viable.get_constraints(v))
 | 
			
		||||
            insert(c);
 | 
			
		||||
#else
 | 
			
		||||
        for (auto c : cjust_v)
 | 
			
		||||
            insert(c);
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        for (auto* engine : ex_engines)
 | 
			
		||||
            if (engine->try_explain(v, *this))
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,147 +22,6 @@ Author:
 | 
			
		|||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Find a sequence of intervals that covers all of Z_modulus.
 | 
			
		||||
     *
 | 
			
		||||
     * \returns true iff such a covering exists
 | 
			
		||||
     * \param longest_i: the longest interval (as index into 'records')
 | 
			
		||||
     * \param out_seq: will contain the covering (as list of indices into 'records')
 | 
			
		||||
     */
 | 
			
		||||
    static bool find_covering_sequence(vector<fi_record> const& records, unsigned longest_i, rational modulus, unsigned_vector& out_seq) {
 | 
			
		||||
        rational baseline = records[longest_i].interval.hi_val();
 | 
			
		||||
        while (!records[longest_i].interval.currently_contains(baseline)) {
 | 
			
		||||
            rational best_extent = rational::zero();
 | 
			
		||||
            unsigned furthest_i = UINT_MAX;
 | 
			
		||||
            for (unsigned i = records.size(); i-- > 0; ) {
 | 
			
		||||
                auto const& interval = records[i].interval;
 | 
			
		||||
                if (interval.currently_contains(baseline)) {
 | 
			
		||||
                    rational extent = mod(interval.hi_val() - baseline, modulus);
 | 
			
		||||
                    if (extent > best_extent) {
 | 
			
		||||
                        best_extent = extent;
 | 
			
		||||
                        furthest_i = i;
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            if (furthest_i == UINT_MAX) {
 | 
			
		||||
                // There's a hole we can't cover.
 | 
			
		||||
                // This can happen if a constraint didn't produce an interval
 | 
			
		||||
                // (but not necessarily, values may be covered by multiple constraints)
 | 
			
		||||
                return false;
 | 
			
		||||
            }
 | 
			
		||||
            SASSERT(best_extent > 0);
 | 
			
		||||
            out_seq.push_back(furthest_i);
 | 
			
		||||
            baseline = records[furthest_i].interval.hi_val();
 | 
			
		||||
        }
 | 
			
		||||
        SASSERT(out_seq.size() > 0);
 | 
			
		||||
        if (!records[out_seq[0]].interval.currently_contains(baseline))
 | 
			
		||||
            out_seq.push_back(longest_i);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
    * A single constraint implies an empty interval.
 | 
			
		||||
    * We assume that neg_cond is a consequence of src that  
 | 
			
		||||
    * does not mention the variable v to be eliminated.
 | 
			
		||||
    */
 | 
			
		||||
    void forbidden_intervals::full_interval_conflict(signed_constraint src, vector<signed_constraint> const& side_cond, conflict& core) {
 | 
			
		||||
        core.reset();
 | 
			
		||||
        for (auto c : side_cond)
 | 
			
		||||
            core.insert(c);
 | 
			
		||||
        core.insert(src);
 | 
			
		||||
        core.set_bailout();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool forbidden_intervals::perform(pvar v, vector<signed_constraint> const& just, conflict& core) {
 | 
			
		||||
        
 | 
			
		||||
        // Extract forbidden intervals from conflicting constraints
 | 
			
		||||
        vector<fi_record> records;
 | 
			
		||||
        rational longest_len;
 | 
			
		||||
        unsigned longest_i = UINT_MAX;
 | 
			
		||||
        for (signed_constraint c : just) {
 | 
			
		||||
            LOG_H3("Computing forbidden interval for: " << c);
 | 
			
		||||
            eval_interval interval = eval_interval::full();
 | 
			
		||||
            vector<signed_constraint> side_cond;
 | 
			
		||||
            if (get_interval(c, v, interval, side_cond)) {
 | 
			
		||||
                LOG("interval: " << interval);
 | 
			
		||||
                LOG("neg_cond: " << side_cond);
 | 
			
		||||
                if (interval.is_currently_empty())
 | 
			
		||||
                    continue;
 | 
			
		||||
                if (interval.is_full()) {
 | 
			
		||||
                    // We have a single interval covering the whole domain
 | 
			
		||||
                    // => the side conditions of that interval are enough to produce a conflict
 | 
			
		||||
                    full_interval_conflict(c, side_cond, core);
 | 
			
		||||
                    revert_core(core);
 | 
			
		||||
                    return true;
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                    auto const len = interval.current_len();
 | 
			
		||||
                    if (len > longest_len) {
 | 
			
		||||
                        longest_len = len;
 | 
			
		||||
                        longest_i = records.size();
 | 
			
		||||
                    }
 | 
			
		||||
                }
 | 
			
		||||
                records.push_back({ std::move(interval), std::move(side_cond), c });
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        if (records.empty()) {
 | 
			
		||||
            LOG("aborted (no intervals)");
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        SASSERT(longest_i != UINT_MAX);
 | 
			
		||||
        LOG("longest: i=" << longest_i << "; " << records[longest_i].interval);
 | 
			
		||||
 | 
			
		||||
        rational const modulus = rational::power_of_two(s.size(v));
 | 
			
		||||
 | 
			
		||||
        // Select a sequence of covering intervals
 | 
			
		||||
        unsigned_vector seq;
 | 
			
		||||
        if (!find_covering_sequence(records, longest_i, modulus, seq)) {
 | 
			
		||||
            LOG("aborted (intervals do not cover domain)");
 | 
			
		||||
            return false;
 | 
			
		||||
        }
 | 
			
		||||
        LOG("seq: " << seq);
 | 
			
		||||
        SASSERT(seq.size() >= 2);  // otherwise has_full should have been true
 | 
			
		||||
 | 
			
		||||
        // Update the conflict state
 | 
			
		||||
        // Idea:
 | 
			
		||||
        // - If the src constraints hold, and
 | 
			
		||||
        // - if the side conditions hold, and
 | 
			
		||||
        // - the upper bound of each interval is contained in the next interval,
 | 
			
		||||
        // then the forbidden intervals cover the whole domain and we have a conflict.
 | 
			
		||||
        // 
 | 
			
		||||
 | 
			
		||||
        core.reset();
 | 
			
		||||
        // Add side conditions and interval constraints
 | 
			
		||||
        for (unsigned seq_i = seq.size(); seq_i-- > 0; ) {
 | 
			
		||||
            unsigned const i = seq[seq_i];
 | 
			
		||||
            unsigned const next_i = seq[(seq_i + 1) % seq.size()];
 | 
			
		||||
            // Build constraint: upper bound of each interval is not contained in the next interval,
 | 
			
		||||
            // using the equivalence:  t \in [l;h[  <=>  t-l < h-l
 | 
			
		||||
            auto const& hi = records[i].interval.hi();
 | 
			
		||||
            auto const& next_lo = records[next_i].interval.lo();
 | 
			
		||||
            auto const& next_hi = records[next_i].interval.hi();
 | 
			
		||||
            auto lhs = hi - next_lo;
 | 
			
		||||
            auto rhs = next_hi - next_lo;
 | 
			
		||||
            signed_constraint c = s.m_constraints.ult(lhs, rhs);
 | 
			
		||||
            LOG("constraint: " << c);
 | 
			
		||||
            core.insert(c);
 | 
			
		||||
            // Side conditions
 | 
			
		||||
            // TODO: check whether the condition is subsumed by c?  maybe at the end do a "lemma reduction" step, to try and reduce branching?
 | 
			
		||||
            for (auto sc : records[i].side_cond)
 | 
			
		||||
                core.insert(sc);
 | 
			
		||||
            
 | 
			
		||||
            core.insert(records[i].src);
 | 
			
		||||
        }
 | 
			
		||||
        core.set_bailout();
 | 
			
		||||
        revert_core(core);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    /** Precondition: all variables other than v are assigned.
 | 
			
		||||
     *
 | 
			
		||||
     * \param[out] out_interval     The forbidden interval for this constraint
 | 
			
		||||
| 
						 | 
				
			
			@ -464,15 +323,4 @@ namespace polysat {
 | 
			
		|||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void forbidden_intervals::revert_core(conflict& core) {
 | 
			
		||||
        for (auto c : core) {
 | 
			
		||||
            if (c.bvalue(s) == l_false) {
 | 
			
		||||
                core.reset();
 | 
			
		||||
                core.set(~c);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -22,8 +22,6 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
    class forbidden_intervals {
 | 
			
		||||
        solver& s;
 | 
			
		||||
        void revert_core(conflict& core);
 | 
			
		||||
        void full_interval_conflict(signed_constraint c, vector<signed_constraint> const & side_cond, conflict& core);
 | 
			
		||||
 | 
			
		||||
        void push_eq(bool is_trivial, pdd const& p, vector<signed_constraint>& side_cond);
 | 
			
		||||
        eval_interval to_interval(signed_constraint const& c, bool is_trivial, rational const& coeff,
 | 
			
		||||
| 
						 | 
				
			
			@ -60,7 +58,6 @@ namespace polysat {
 | 
			
		|||
        bool coefficient_is_01(dd::pdd_manager& m, rational const& r) { return r.is_zero() || r.is_one() || r == m.max_value(); };
 | 
			
		||||
    public:
 | 
			
		||||
        forbidden_intervals(solver& s) :s(s) {}
 | 
			
		||||
        bool perform(pvar v, vector<signed_constraint> const& just, conflict& core);
 | 
			
		||||
        bool get_interval(signed_constraint const& c, pvar v, eval_interval& out_interval, vector<signed_constraint>& side_cond);
 | 
			
		||||
    };
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -180,17 +180,10 @@ namespace polysat {
 | 
			
		|||
        if (x_max * y_max  > pddm.max_value())            
 | 
			
		||||
            push_omega_bisect(x, x_max, y, y_max);
 | 
			
		||||
        else {
 | 
			
		||||
#if NEW_VIABLE
 | 
			
		||||
            for (auto const& c : s.m_viable.get_constraints(y.var()))
 | 
			
		||||
                m_new_constraints.insert(c);
 | 
			
		||||
            for (auto const& c : s.m_viable.get_constraints(x.var()))
 | 
			
		||||
                m_new_constraints.insert(c);            
 | 
			
		||||
#else
 | 
			
		||||
            for (auto c : s.m_cjust[y.var()])
 | 
			
		||||
                m_new_constraints.insert(c);
 | 
			
		||||
            for (auto c : s.m_cjust[x.var()])
 | 
			
		||||
                m_new_constraints.insert(c);
 | 
			
		||||
#endif
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -98,9 +98,6 @@ namespace polysat {
 | 
			
		|||
        m_value.push_back(rational::zero());
 | 
			
		||||
        m_justification.push_back(justification::unassigned());
 | 
			
		||||
        m_viable.push(sz);
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
        m_cjust.push_back({});
 | 
			
		||||
#endif
 | 
			
		||||
        m_pwatch.push_back({});
 | 
			
		||||
        m_activity.push_back(0);
 | 
			
		||||
        m_vars.push_back(sz2pdd(sz).mk_var(v));
 | 
			
		||||
| 
						 | 
				
			
			@ -118,9 +115,6 @@ namespace polysat {
 | 
			
		|||
        // TODO also remove v from all learned constraints.
 | 
			
		||||
        pvar v = m_value.size() - 1;
 | 
			
		||||
        m_viable.pop();
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
        m_cjust.pop_back();
 | 
			
		||||
#endif
 | 
			
		||||
        m_value.pop_back();
 | 
			
		||||
        m_justification.pop_back();
 | 
			
		||||
        m_pwatch.pop_back();
 | 
			
		||||
| 
						 | 
				
			
			@ -345,15 +339,6 @@ namespace polysat {
 | 
			
		|||
                m_search.pop();
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            case trail_instr_t::just_i: {
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
                auto v = m_cjust_trail.back();
 | 
			
		||||
                LOG_V("Undo just_i");
 | 
			
		||||
                m_cjust[v].pop_back();
 | 
			
		||||
                m_cjust_trail.pop_back();
 | 
			
		||||
#endif
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
            default:
 | 
			
		||||
                UNREACHABLE();
 | 
			
		||||
            }
 | 
			
		||||
| 
						 | 
				
			
			@ -480,12 +465,8 @@ namespace polysat {
 | 
			
		|||
        LOG_H2("Resolve conflict");
 | 
			
		||||
        LOG("\n" << *this);
 | 
			
		||||
        LOG("search state: " << m_search);
 | 
			
		||||
#if NEW_VIABLE
 | 
			
		||||
        m_viable.log();
 | 
			
		||||
#else
 | 
			
		||||
        for (pvar v = 0; v < m_cjust.size(); ++v)
 | 
			
		||||
            LOG("cjust[v" << v << "]: " << m_cjust[v]);
 | 
			
		||||
#endif
 | 
			
		||||
        for (pvar v = 0; v < m_justification.size(); ++v)
 | 
			
		||||
            LOG("v" << v << " " << viable::var_pp(m_viable, v));
 | 
			
		||||
        ++m_stats.m_num_conflicts;
 | 
			
		||||
 | 
			
		||||
        SASSERT(is_conflict());
 | 
			
		||||
| 
						 | 
				
			
			@ -829,10 +810,6 @@ namespace polysat {
 | 
			
		|||
                pvar v = item.var();
 | 
			
		||||
                auto const& j = m_justification[v];
 | 
			
		||||
                out << "\t" << assignment_pp(*this, v, get_value(v)) << " @" << j.level();   
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
                if (j.is_propagation())
 | 
			
		||||
                    out << " " << m_cjust[v];
 | 
			
		||||
#endif
 | 
			
		||||
                out << "\n";
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -32,11 +32,9 @@ Author:
 | 
			
		|||
#include "math/polysat/forbidden_intervals.h"
 | 
			
		||||
#include "math/polysat/trail.h"
 | 
			
		||||
#include "math/polysat/viable.h"
 | 
			
		||||
#include "math/polysat/viable2.h"
 | 
			
		||||
#include "math/polysat/log.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#define NEW_VIABLE 1
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -77,11 +75,7 @@ namespace polysat {
 | 
			
		|||
        params_ref               m_params;
 | 
			
		||||
 | 
			
		||||
        scoped_ptr_vector<dd::pdd_manager> m_pdd;
 | 
			
		||||
#if NEW_VIABLE
 | 
			
		||||
        viable2                  m_viable;
 | 
			
		||||
#else
 | 
			
		||||
        viable                   m_viable;   // viable sets per variable
 | 
			
		||||
#endif
 | 
			
		||||
        linear_solver            m_linear_solver;
 | 
			
		||||
        conflict                 m_conflict;        
 | 
			
		||||
        forbidden_intervals      m_forbidden_intervals;
 | 
			
		||||
| 
						 | 
				
			
			@ -101,9 +95,6 @@ namespace polysat {
 | 
			
		|||
        // Per variable information
 | 
			
		||||
        vector<rational>         m_value;         // assigned value
 | 
			
		||||
        vector<justification>    m_justification; // justification for variable assignment
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
        vector<signed_constraints> m_cjust;       // constraints justifying variable range.
 | 
			
		||||
#endif
 | 
			
		||||
        vector<signed_constraints> m_pwatch;      // watch list datastructure into constraints.
 | 
			
		||||
 | 
			
		||||
        unsigned_vector          m_activity; 
 | 
			
		||||
| 
						 | 
				
			
			@ -132,17 +123,6 @@ namespace polysat {
 | 
			
		|||
            m_qhead_trail.pop_back();
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if !NEW_VIABLE
 | 
			
		||||
        void push_cjust(pvar v, signed_constraint c) {
 | 
			
		||||
            if (m_cjust[v].contains(c))  // TODO: better check (flag on constraint?)
 | 
			
		||||
                return;
 | 
			
		||||
            LOG_V("cjust[v" << v << "] += " << c);
 | 
			
		||||
            SASSERT(c);
 | 
			
		||||
            m_cjust[v].push_back(c);
 | 
			
		||||
            m_trail.push_back(trail_instr_t::just_i);
 | 
			
		||||
            m_cjust_trail.push_back(v);
 | 
			
		||||
        }
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
        unsigned size(pvar v) const { return m_size[v]; }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -129,9 +129,6 @@ namespace polysat {
 | 
			
		|||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
#if NEW_VIABLE
 | 
			
		||||
        // setup with viable2:
 | 
			
		||||
        // we no longer need cjust
 | 
			
		||||
        pvar v = null_var;
 | 
			
		||||
        if (p.is_unilinear())
 | 
			
		||||
            v = p.var();
 | 
			
		||||
| 
						 | 
				
			
			@ -152,64 +149,6 @@ namespace polysat {
 | 
			
		|||
                break;
 | 
			
		||||
            }                
 | 
			
		||||
        }
 | 
			
		||||
#else
 | 
			
		||||
 | 
			
		||||
        // p <= 0, e.g., p == 0
 | 
			
		||||
        if (q.is_zero() && p.is_unilinear()) {
 | 
			
		||||
            // a*x + b == 0
 | 
			
		||||
            pvar v = p.var();
 | 
			
		||||
            s.push_cjust(v, { this, is_positive });
 | 
			
		||||
 | 
			
		||||
            rational a = p.hi().val();
 | 
			
		||||
            rational b = p.lo().val();
 | 
			
		||||
            s.m_viable.intersect_eq(a, v, b, is_positive);
 | 
			
		||||
 | 
			
		||||
            rational val;
 | 
			
		||||
            if (s.m_viable.find_viable(v, val) == dd::find_t::singleton)
 | 
			
		||||
                s.propagate(v, val, { this, is_positive });
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        pvar v = null_var;
 | 
			
		||||
        rational a, b, c, d;
 | 
			
		||||
        if (p.is_unilinear() && q.is_unilinear() && p.var() == q.var()) {
 | 
			
		||||
            // a*x + b <=u c*x + d
 | 
			
		||||
            v = p.var();
 | 
			
		||||
            a = p.hi().val();
 | 
			
		||||
            b = p.lo().val();
 | 
			
		||||
            c = q.hi().val();
 | 
			
		||||
            d = q.lo().val();
 | 
			
		||||
        }
 | 
			
		||||
        else if (p.is_unilinear() && q.is_val()) {
 | 
			
		||||
            // a*x + b <=u d
 | 
			
		||||
            v = p.var();
 | 
			
		||||
            a = p.hi().val();
 | 
			
		||||
            b = p.lo().val();
 | 
			
		||||
            c = rational::zero();
 | 
			
		||||
            d = q.val();
 | 
			
		||||
        }
 | 
			
		||||
        else if (p.is_val() && q.is_unilinear()) {
 | 
			
		||||
            // b <=u c*x + d
 | 
			
		||||
            v = q.var();
 | 
			
		||||
            a = rational::zero();
 | 
			
		||||
            b = p.val();
 | 
			
		||||
            c = q.hi().val();
 | 
			
		||||
            d = q.lo().val();
 | 
			
		||||
        }
 | 
			
		||||
        if (v != null_var) {
 | 
			
		||||
            s.push_cjust(v, {this, is_positive});
 | 
			
		||||
 | 
			
		||||
            s.m_viable.intersect_ule(v, a, b, c, d, is_positive);
 | 
			
		||||
 | 
			
		||||
            rational val;
 | 
			
		||||
            if (s.m_viable.find_viable(v, val) == dd::find_t::singleton) 
 | 
			
		||||
                s.propagate(v, val, {this, is_positive});
 | 
			
		||||
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        // TODO: other cheap constraints possible?
 | 
			
		||||
#endif
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool ule_constraint::is_always_false(bool is_positive, pdd const& lhs, pdd const& rhs) const {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -10,133 +10,349 @@ Author:
 | 
			
		|||
    Nikolaj Bjorner (nbjorner) 2021-03-19
 | 
			
		||||
    Jakob Rath 2021-04-6
 | 
			
		||||
 | 
			
		||||
Notes:
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "util/debug.h"
 | 
			
		||||
#include "math/polysat/viable.h"
 | 
			
		||||
#include "math/polysat/solver.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
    viable::viable(solver& s):
 | 
			
		||||
        s(s),
 | 
			
		||||
        m_bdd(1000)
 | 
			
		||||
    {}
 | 
			
		||||
    viable::viable(solver& s) : s(s) {}
 | 
			
		||||
 | 
			
		||||
    viable::~viable() {
 | 
			
		||||
        for (entry* e : m_alloc) 
 | 
			
		||||
            dealloc(e);        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::push_viable(pvar v) {
 | 
			
		||||
        s.m_trail.push_back(trail_instr_t::viable_add_i);
 | 
			
		||||
        m_viable_trail.push_back(std::make_pair(v, m_viable[v]));
 | 
			
		||||
 | 
			
		||||
    viable::entry* viable::alloc_entry() {
 | 
			
		||||
        if (m_alloc.empty())
 | 
			
		||||
            return alloc(entry);
 | 
			
		||||
        auto* e = m_alloc.back();
 | 
			
		||||
        e->side_cond.reset();
 | 
			
		||||
        m_alloc.pop_back();
 | 
			
		||||
        return e;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::pop_viable() {
 | 
			
		||||
        auto const & p = m_viable_trail.back();
 | 
			
		||||
        m_viable.set(p.first, p.second);
 | 
			
		||||
        m_viable_trail.pop_back();
 | 
			
		||||
        auto& [v, e] = m_trail.back();
 | 
			
		||||
        e->remove_from(m_viable[v], e);
 | 
			
		||||
        m_alloc.push_back(e);       
 | 
			
		||||
        m_trail.pop_back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::push_viable() {
 | 
			
		||||
        auto& [v, e] = m_trail.back();
 | 
			
		||||
        SASSERT(e->prev() != e || !m_viable[v]);
 | 
			
		||||
        SASSERT(e->prev() != e || e->next() == e);
 | 
			
		||||
        if (e->prev() != e) {
 | 
			
		||||
            e->prev()->insert_after(e);
 | 
			
		||||
            if (e->interval.lo_val() < e->next()->interval.lo_val())
 | 
			
		||||
                m_viable[v] = e;
 | 
			
		||||
        }
 | 
			
		||||
        else 
 | 
			
		||||
            m_viable[v] = e;        
 | 
			
		||||
        m_trail.pop_back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // a*v + b == 0 or a*v + b != 0
 | 
			
		||||
    void viable::intersect_eq(rational const& a, pvar v, rational const& b, bool is_positive) {
 | 
			
		||||
        
 | 
			
		||||
        bddv const& x = var2bits(v).var();
 | 
			
		||||
        if (b == 0 && a.is_odd()) {
 | 
			
		||||
            // hacky test optimizing special case.
 | 
			
		||||
            // general case is compute inverse(a)*-b for equality 2^k*a*x + b == 0
 | 
			
		||||
            // then constrain x.
 | 
			
		||||
            // 
 | 
			
		||||
            intersect_viable(v, is_positive ? x.all0() : !x.all0());
 | 
			
		||||
        }
 | 
			
		||||
        else if (a.is_odd()) {
 | 
			
		||||
            rational a_inv;
 | 
			
		||||
            VERIFY(a.mult_inverse(x.size(), a_inv));
 | 
			
		||||
            bdd eq = x == mod(a_inv * -b, rational::power_of_two(x.size()));
 | 
			
		||||
            intersect_viable(v, is_positive ? eq : !eq);
 | 
			
		||||
        }
 | 
			
		||||
    void viable::intersect(pvar v, signed_constraint const& c) {
 | 
			
		||||
        auto& fi = s.m_forbidden_intervals;
 | 
			
		||||
        entry* ne = alloc_entry();
 | 
			
		||||
        if (!fi.get_interval(c, v, ne->interval, ne->side_cond) || ne->interval.is_currently_empty())
 | 
			
		||||
            m_alloc.push_back(ne);
 | 
			
		||||
        else {
 | 
			
		||||
            IF_VERBOSE(10, verbose_stream() << a << "*x + " << b << "\n");
 | 
			
		||||
            
 | 
			
		||||
            bddv lhs = a * x + b;
 | 
			
		||||
            bdd xs = is_positive ? lhs.all0() : !lhs.all0();
 | 
			
		||||
            intersect_viable(v, xs);
 | 
			
		||||
            ne->src = c;
 | 
			
		||||
            intersect(v, ne);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::intersect_ule(pvar v, rational const& a, rational const& b, rational const& c, rational const& d, bool is_positive) {
 | 
			
		||||
        bddv const& x = var2bits(v).var();
 | 
			
		||||
        // hacky special case
 | 
			
		||||
        if (a == 1 && b == 0 && c == 0 && d == 0) 
 | 
			
		||||
            // x <= 0
 | 
			
		||||
            intersect_viable(v, is_positive ? x.all0() : !x.all0());
 | 
			
		||||
    void viable::intersect(pvar v, entry* ne) {
 | 
			
		||||
        entry* e = m_viable[v];
 | 
			
		||||
        if (e && e->interval.is_full())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (ne->interval.is_currently_empty()) {
 | 
			
		||||
            m_alloc.push_back(ne);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto create_entry = [&]() {
 | 
			
		||||
            m_trail.push_back({ v, ne });
 | 
			
		||||
            s.m_trail.push_back(trail_instr_t::viable_add_i);
 | 
			
		||||
            ne->init(ne);            
 | 
			
		||||
            return ne;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        auto remove_entry = [&](entry* e) {
 | 
			
		||||
            m_trail.push_back({ v, e });
 | 
			
		||||
            s.m_trail.push_back(trail_instr_t::viable_rem_i);
 | 
			
		||||
            e->remove_from(m_viable[v], e);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        //LOG("intersect " << ne->interval);
 | 
			
		||||
 | 
			
		||||
        if (!e) 
 | 
			
		||||
            m_viable[v] = create_entry();
 | 
			
		||||
        else {
 | 
			
		||||
            IF_VERBOSE(10, verbose_stream() << a << "*x + " << b << (is_positive ? " <= " : " > ") << c << "*x + " << d << "\n");
 | 
			
		||||
            bddv l = a * x + b;
 | 
			
		||||
            bddv r = c * x + d;
 | 
			
		||||
            bdd xs = is_positive ? (l <= r) : (l > r);
 | 
			
		||||
            intersect_viable(v, xs);
 | 
			
		||||
            entry* first = e;
 | 
			
		||||
            do {
 | 
			
		||||
                if (e->interval.contains(ne->interval)) {
 | 
			
		||||
                    m_alloc.push_back(ne);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                while (ne->interval.contains(e->interval)) {
 | 
			
		||||
                    entry* n = e->next();
 | 
			
		||||
                    remove_entry(e);
 | 
			
		||||
                    if (!m_viable[v]) {
 | 
			
		||||
                        m_viable[v] = create_entry();
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (e == first) 
 | 
			
		||||
                        first = n;                    
 | 
			
		||||
                    e = n;
 | 
			
		||||
                }             
 | 
			
		||||
                SASSERT(e->interval.lo_val() != ne->interval.lo_val());
 | 
			
		||||
                if (e->interval.lo_val() > ne->interval.lo_val()) {
 | 
			
		||||
                    if (first->prev()->interval.contains(ne->interval)) {
 | 
			
		||||
                        m_alloc.push_back(ne);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    e->insert_before(create_entry());
 | 
			
		||||
                    if (e == first)
 | 
			
		||||
                        m_viable[v] = e->prev();
 | 
			
		||||
                    SASSERT(well_formed(m_viable[v]));
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                e = e->next();
 | 
			
		||||
            }             
 | 
			
		||||
            while (e != first);
 | 
			
		||||
            // otherwise, append to end of list
 | 
			
		||||
            first->insert_before(create_entry());
 | 
			
		||||
        }
 | 
			
		||||
        SASSERT(well_formed(m_viable[v]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable::has_viable(pvar v) {
 | 
			
		||||
        return !m_viable[v].is_false();
 | 
			
		||||
    bool viable::has_viable(pvar v) { 
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return true;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        auto const& max_value = s.var2pdd(v).max_value();
 | 
			
		||||
        do {
 | 
			
		||||
            if (e->interval.is_full())
 | 
			
		||||
                return false;
 | 
			
		||||
            entry* n = e->next();
 | 
			
		||||
            if (n == e)
 | 
			
		||||
                return true;
 | 
			
		||||
            if (e->interval.hi_val() < n->interval.lo_val())
 | 
			
		||||
                return true;
 | 
			
		||||
            if (n == first)
 | 
			
		||||
                return e->interval.lo_val() <= e->interval.hi_val();
 | 
			
		||||
            e = n;
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable::is_viable(pvar v, rational const& val) {
 | 
			
		||||
        return var2bits(v).contains(m_viable[v], val);
 | 
			
		||||
    bool viable::is_viable(pvar v, rational const& val) { 
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return true;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        entry* last = first->prev();
 | 
			
		||||
        if (last->interval.currently_contains(val))
 | 
			
		||||
            return false;
 | 
			
		||||
        for (; e != last; e = e->next()) {        
 | 
			
		||||
            if (e->interval.currently_contains(val))
 | 
			
		||||
                return false;
 | 
			
		||||
            if (val < e->interval.lo_val())
 | 
			
		||||
                return true;
 | 
			
		||||
        }         
 | 
			
		||||
        return true; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::intersect_viable(pvar v, bdd vals) {
 | 
			
		||||
        push_viable(v);
 | 
			
		||||
        m_viable[v] &= vals;
 | 
			
		||||
        if (m_viable[v].is_false())
 | 
			
		||||
            s.set_conflict(v);
 | 
			
		||||
    rational viable::min_viable(pvar v) { 
 | 
			
		||||
        rational lo(0);
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return lo;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        entry* last = first->prev();
 | 
			
		||||
        if (last->interval.currently_contains(lo))
 | 
			
		||||
            lo = last->interval.hi_val();
 | 
			
		||||
        do {        
 | 
			
		||||
            if (!e->interval.currently_contains(lo))
 | 
			
		||||
                break;
 | 
			
		||||
            lo = e->interval.hi_val();
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
        SASSERT(is_viable(v, lo));        
 | 
			
		||||
        return lo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dd::find_t viable::find_viable(pvar v, rational & val) {
 | 
			
		||||
        return var2bits(v).find_hint(m_viable[v], s.m_value[v], val);
 | 
			
		||||
    rational viable::max_viable(pvar v) { 
 | 
			
		||||
        rational hi = s.var2pdd(v).max_value();
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return hi;
 | 
			
		||||
        entry* last = e->prev();
 | 
			
		||||
        e = last;
 | 
			
		||||
        do {
 | 
			
		||||
            if (!e->interval.currently_contains(hi))
 | 
			
		||||
                break;
 | 
			
		||||
            hi = e->interval.lo_val() - 1;
 | 
			
		||||
            e = e->prev();
 | 
			
		||||
        }           
 | 
			
		||||
        while (e != last);
 | 
			
		||||
        SASSERT(is_viable(v, hi));
 | 
			
		||||
        return hi;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rational viable::min_viable(pvar v) {
 | 
			
		||||
        return var2bits(v).min(m_viable[v]);
 | 
			
		||||
    dd::find_t viable::find_viable(pvar v, rational& lo) { 
 | 
			
		||||
        lo = 0;
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return dd::find_t::multiple;
 | 
			
		||||
        if (e->interval.is_full())
 | 
			
		||||
            return dd::find_t::empty;
 | 
			
		||||
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        entry* last = first->prev();
 | 
			
		||||
        if (last->interval.currently_contains(lo))
 | 
			
		||||
            lo = last->interval.hi_val();
 | 
			
		||||
        do {
 | 
			
		||||
            if (!e->interval.currently_contains(lo))
 | 
			
		||||
                break;
 | 
			
		||||
            lo = e->interval.hi_val();
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
 | 
			
		||||
        if (e->interval.currently_contains(lo))
 | 
			
		||||
            return dd::find_t::empty;
 | 
			
		||||
 | 
			
		||||
        rational hi = s.var2pdd(v).max_value();
 | 
			
		||||
        e = last;
 | 
			
		||||
        do {
 | 
			
		||||
            if (!e->interval.currently_contains(hi))
 | 
			
		||||
                break;
 | 
			
		||||
            hi = e->interval.lo_val() - 1;
 | 
			
		||||
            e = e->prev();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != last);
 | 
			
		||||
        if (lo == hi)
 | 
			
		||||
            return dd::find_t::singleton;
 | 
			
		||||
        else
 | 
			
		||||
            return dd::find_t::multiple;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rational viable::max_viable(pvar v) {
 | 
			
		||||
        return var2bits(v).max(m_viable[v]);
 | 
			
		||||
    }
 | 
			
		||||
    bool viable::resolve(pvar v, conflict& core) {
 | 
			
		||||
        if (has_viable(v))
 | 
			
		||||
            return false;
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        SASSERT(e);
 | 
			
		||||
        core.reset();
 | 
			
		||||
        do {
 | 
			
		||||
            // Build constraint: upper bound of each interval is not contained in the next interval,
 | 
			
		||||
            // using the equivalence:  t \in [l;h[  <=>  t-l < h-l
 | 
			
		||||
            entry* n = e->next();
 | 
			
		||||
            if (!e->interval.is_full()) {
 | 
			
		||||
                auto const& hi = e->interval.hi();
 | 
			
		||||
                auto const& next_lo = n->interval.lo();
 | 
			
		||||
                auto const& next_hi = n->interval.hi();
 | 
			
		||||
                auto lhs = hi - next_lo;
 | 
			
		||||
                auto rhs = next_hi - next_lo;
 | 
			
		||||
                signed_constraint c = s.m_constraints.ult(lhs, rhs);
 | 
			
		||||
                core.insert(c);
 | 
			
		||||
            }
 | 
			
		||||
            for (auto sc : e->side_cond)
 | 
			
		||||
                core.insert(sc);
 | 
			
		||||
            e->src->set_var_dependent(); // ?
 | 
			
		||||
            core.insert(e->src);
 | 
			
		||||
            e = n;
 | 
			
		||||
        }             
 | 
			
		||||
        while (e != first);
 | 
			
		||||
 | 
			
		||||
    dd::fdd const& viable::sz2bits(unsigned sz) {
 | 
			
		||||
        m_bits.reserve(sz + 1);
 | 
			
		||||
        auto* bits = m_bits[sz];
 | 
			
		||||
        if (!bits) {
 | 
			
		||||
            m_bits.set(sz, alloc(dd::fdd, m_bdd, sz));
 | 
			
		||||
            bits = m_bits[sz];
 | 
			
		||||
        core.set_bailout();
 | 
			
		||||
        for (auto c : core) {
 | 
			
		||||
            if (c.bvalue(s) == l_false) {
 | 
			
		||||
                core.reset();
 | 
			
		||||
                core.set(~c);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return *bits;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::log() {
 | 
			
		||||
        // only for small problems
 | 
			
		||||
        for (pvar v = 0; v < std::min(10u, m_viable.size()); ++v) 
 | 
			
		||||
            log(v);            
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable::log(pvar v) {
 | 
			
		||||
        if (s.size(v) <= 5) {
 | 
			
		||||
            vector<rational> xs;
 | 
			
		||||
            for (rational x = rational::zero(); x < rational::power_of_two(s.size(v)); x += 1) 
 | 
			
		||||
                if (is_viable(v, x)) 
 | 
			
		||||
                    xs.push_back(x);
 | 
			
		||||
                            
 | 
			
		||||
            LOG("Viable for v" << v << ": " << xs);
 | 
			
		||||
        } 
 | 
			
		||||
        if (!well_formed(m_viable[v]))
 | 
			
		||||
            LOG("v" << v << " not well formed");
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        do {
 | 
			
		||||
            LOG("v" << v << ": " << e->interval << " " << e->side_cond << " " << e->src);
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dd::fdd const& viable::var2bits(pvar v) { return sz2bits(s.size(v)); }
 | 
			
		||||
    void viable::log() {
 | 
			
		||||
        for (pvar v = 0; v < std::min(10u, m_viable.size()); ++v)
 | 
			
		||||
            log(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& viable::display(std::ostream& out, pvar v) const {
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return out;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        do {
 | 
			
		||||
            out << "v" << v << ": " << e->interval << " " << e->side_cond << " " << e->src << "\n";
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& viable::display(std::ostream& out) const {
 | 
			
		||||
        for (pvar v = 0; v < m_viable.size(); ++v)
 | 
			
		||||
            display(out, v);
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    * Lower bounds are strictly ascending.
 | 
			
		||||
    * intervals don't contain each-other (since lower bounds are ascending, 
 | 
			
		||||
    * it suffices to check containment in one direction).
 | 
			
		||||
    */
 | 
			
		||||
    bool viable::well_formed(entry* e) {
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return true;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            if (e->interval.is_full())
 | 
			
		||||
                return e->next() == e;
 | 
			
		||||
            if (e->interval.is_currently_empty()) 
 | 
			
		||||
                return false;
 | 
			
		||||
            
 | 
			
		||||
            auto* n = e->next();
 | 
			
		||||
            if (n != e && e->interval.contains(n->interval)) 
 | 
			
		||||
                return false;
 | 
			
		||||
            
 | 
			
		||||
            if (n == first)
 | 
			
		||||
                break;            
 | 
			
		||||
            if (e->interval.lo_val() >= n->interval.lo_val())
 | 
			
		||||
                return false;
 | 
			
		||||
            e = n;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,77 +4,67 @@ Copyright (c) 2021 Microsoft Corporation
 | 
			
		|||
Module Name:
 | 
			
		||||
 | 
			
		||||
    maintain viable domains
 | 
			
		||||
 | 
			
		||||
    It uses the interval extraction functions from forbidden intervals.
 | 
			
		||||
    An empty viable set corresponds directly to a conflict that does not rely on 
 | 
			
		||||
    the non-viable variable.
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2021-03-19
 | 
			
		||||
    Jakob Rath 2021-04-6
 | 
			
		||||
 | 
			
		||||
Notes:
 | 
			
		||||
 | 
			
		||||
    NEW_VIABLE uses cheaper book-keeping, but is partial.
 | 
			
		||||
  
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include "math/dd/dd_bdd.h"
 | 
			
		||||
#include "math/polysat/types.h"
 | 
			
		||||
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include "util/dlist.h"
 | 
			
		||||
#include "util/small_object_allocator.h"
 | 
			
		||||
#include "math/polysat/types.h"
 | 
			
		||||
#include "math/polysat/conflict.h"
 | 
			
		||||
#include "math/polysat/constraint.h"
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
    class solver;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    class viable {
 | 
			
		||||
        typedef dd::bdd bdd;
 | 
			
		||||
        typedef dd::fdd fdd;
 | 
			
		||||
        solver&                      s;
 | 
			
		||||
        dd::bdd_manager              m_bdd;
 | 
			
		||||
        scoped_ptr_vector<dd::fdd>   m_bits;        
 | 
			
		||||
        vector<bdd>                  m_viable;   // set of viable values.
 | 
			
		||||
        vector<std::pair<pvar, bdd>> m_viable_trail;
 | 
			
		||||
        solver& s;
 | 
			
		||||
        
 | 
			
		||||
        struct entry : public dll_base<entry>, public fi_record { 
 | 
			
		||||
        public: 
 | 
			
		||||
            entry() : fi_record({ eval_interval::full(), {}, {} }) {}
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        ptr_vector<entry>                    m_alloc;
 | 
			
		||||
        ptr_vector<entry>                    m_viable;   // set of viable values.
 | 
			
		||||
        svector<std::pair<pvar, entry*>>     m_trail;    // undo stack
 | 
			
		||||
 | 
			
		||||
        bool well_formed(entry* e);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Register all values that are not contained in vals as non-viable.
 | 
			
		||||
         */
 | 
			
		||||
        void intersect_viable(pvar v, bdd vals);
 | 
			
		||||
        entry* alloc_entry();
 | 
			
		||||
 | 
			
		||||
        dd::bdd_manager& get_bdd() { return m_bdd; }
 | 
			
		||||
        dd::fdd const& sz2bits(unsigned sz);
 | 
			
		||||
        dd::fdd const& var2bits(pvar v);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        void push_viable(pvar v);
 | 
			
		||||
        void intersect(pvar v, entry* e);
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        viable(solver& s);
 | 
			
		||||
 | 
			
		||||
        ~viable();
 | 
			
		||||
 | 
			
		||||
        void push(unsigned num_bits) { 
 | 
			
		||||
            m_viable.push_back(m_bdd.mk_true()); 
 | 
			
		||||
        }
 | 
			
		||||
        // declare and remove var
 | 
			
		||||
        void push(unsigned) { m_viable.push_back(nullptr); }
 | 
			
		||||
 | 
			
		||||
        void pop() {
 | 
			
		||||
            m_viable.pop_back();
 | 
			
		||||
        }
 | 
			
		||||
        void pop() { m_viable.pop_back(); }
 | 
			
		||||
 | 
			
		||||
        void pop_viable();
 | 
			
		||||
 | 
			
		||||
        void push_viable() {}
 | 
			
		||||
        void push_viable();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * update state of viable for pvar v
 | 
			
		||||
         * based on affine constraints
 | 
			
		||||
         */
 | 
			
		||||
 | 
			
		||||
        void intersect_eq(rational const& a, pvar v, rational const& b, bool is_positive);
 | 
			
		||||
 | 
			
		||||
        void intersect_ule(pvar v, rational const& a, rational const& b, rational const& c, rational const& d, bool is_positive);
 | 
			
		||||
        void intersect(pvar v, signed_constraint const& c);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check whether variable v has any viable values left according to m_viable.
 | 
			
		||||
| 
						 | 
				
			
			@ -98,6 +88,12 @@ namespace polysat {
 | 
			
		|||
         */
 | 
			
		||||
        dd::find_t find_viable(pvar v, rational & val);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
        * Retrieve the unsat core for v.
 | 
			
		||||
        * \pre there are no viable values for v
 | 
			
		||||
        */
 | 
			
		||||
        bool resolve(pvar v, conflict& core);
 | 
			
		||||
 | 
			
		||||
        /** Log all viable values for the given variable.
 | 
			
		||||
         * (Inefficient, but useful for debugging small instances.)
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			@ -106,7 +102,64 @@ namespace polysat {
 | 
			
		|||
        /** Like log(v) but for all variables */
 | 
			
		||||
        void log();
 | 
			
		||||
 | 
			
		||||
        std::ostream& display(std::ostream& out) const;
 | 
			
		||||
 | 
			
		||||
        class iterator {
 | 
			
		||||
            entry* curr = nullptr;
 | 
			
		||||
            bool   visited = false;
 | 
			
		||||
        public:
 | 
			
		||||
            iterator(entry* curr, bool visited) : 
 | 
			
		||||
                curr(curr), visited(visited || !curr) {}
 | 
			
		||||
 | 
			
		||||
            iterator& operator++() {
 | 
			
		||||
                visited = true;
 | 
			
		||||
                curr = curr->next();
 | 
			
		||||
                return *this;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            signed_constraint& operator*() { 
 | 
			
		||||
                return curr->src; 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool operator==(iterator const& other) const {
 | 
			
		||||
                return visited == other.visited && curr == other.curr;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool operator!=(iterator const& other) const {
 | 
			
		||||
                return !(*this == other);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        class constraints {
 | 
			
		||||
            viable& v;
 | 
			
		||||
            pvar var;
 | 
			
		||||
        public:
 | 
			
		||||
            constraints(viable& v, pvar var) : v(v), var(var) {}
 | 
			
		||||
            iterator begin() const { return iterator(v.m_viable[var], false); }
 | 
			
		||||
            iterator end() const { return iterator(v.m_viable[var], true); }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        constraints get_constraints(pvar v) { 
 | 
			
		||||
            return constraints(*this, v); 
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        std::ostream& display(std::ostream& out, pvar v) const;
 | 
			
		||||
 | 
			
		||||
        struct var_pp {
 | 
			
		||||
            viable& v;
 | 
			
		||||
            pvar var;        
 | 
			
		||||
            var_pp(viable& v, pvar var) : v(v), var(var) {}
 | 
			
		||||
        };
 | 
			
		||||
       
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    inline std::ostream& operator<<(std::ostream& out, viable const& v) {
 | 
			
		||||
        return v.display(out);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    inline std::ostream& operator<<(std::ostream& out, viable::var_pp const& v) {
 | 
			
		||||
        return v.v.display(out, v.var);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,359 +0,0 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2021 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    maintain viable domains
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2021-03-19
 | 
			
		||||
    Jakob Rath 2021-04-6
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "util/debug.h"
 | 
			
		||||
#include "math/polysat/viable2.h"
 | 
			
		||||
#include "math/polysat/solver.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
    viable2::viable2(solver& s) : s(s) {}
 | 
			
		||||
 | 
			
		||||
    viable2::~viable2() {
 | 
			
		||||
        for (entry* e : m_alloc) 
 | 
			
		||||
            dealloc(e);        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    viable2::entry* viable2::alloc_entry() {
 | 
			
		||||
        if (m_alloc.empty())
 | 
			
		||||
            return alloc(entry);
 | 
			
		||||
        auto* e = m_alloc.back();
 | 
			
		||||
        e->side_cond.reset();
 | 
			
		||||
        m_alloc.pop_back();
 | 
			
		||||
        return e;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable2::pop_viable() {
 | 
			
		||||
        auto& [v, e] = m_trail.back();
 | 
			
		||||
        e->remove_from(m_viable[v], e);
 | 
			
		||||
        m_alloc.push_back(e);       
 | 
			
		||||
        m_trail.pop_back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable2::push_viable() {
 | 
			
		||||
        auto& [v, e] = m_trail.back();
 | 
			
		||||
        SASSERT(e->prev() != e || !m_viable[v]);
 | 
			
		||||
        SASSERT(e->prev() != e || e->next() == e);
 | 
			
		||||
        if (e->prev() != e) {
 | 
			
		||||
            e->prev()->insert_after(e);
 | 
			
		||||
            if (e->interval.lo_val() < e->next()->interval.lo_val())
 | 
			
		||||
                m_viable[v] = e;
 | 
			
		||||
        }
 | 
			
		||||
        else 
 | 
			
		||||
            m_viable[v] = e;        
 | 
			
		||||
        m_trail.pop_back();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable2::intersect(pvar v, signed_constraint const& c) {
 | 
			
		||||
        auto& fi = s.m_forbidden_intervals;
 | 
			
		||||
        entry* ne = alloc_entry();
 | 
			
		||||
        if (!fi.get_interval(c, v, ne->interval, ne->side_cond) || ne->interval.is_currently_empty())
 | 
			
		||||
            m_alloc.push_back(ne);
 | 
			
		||||
        else {
 | 
			
		||||
            ne->src = c;
 | 
			
		||||
            intersect(v, ne);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable2::intersect(pvar v, entry* ne) {
 | 
			
		||||
        entry* e = m_viable[v];
 | 
			
		||||
        if (e && e->interval.is_full())
 | 
			
		||||
            return;
 | 
			
		||||
 | 
			
		||||
        if (ne->interval.is_currently_empty()) {
 | 
			
		||||
            m_alloc.push_back(ne);
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        auto create_entry = [&]() {
 | 
			
		||||
            m_trail.push_back({ v, ne });
 | 
			
		||||
            s.m_trail.push_back(trail_instr_t::viable_add_i);
 | 
			
		||||
            ne->init(ne);            
 | 
			
		||||
            return ne;
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        auto remove_entry = [&](entry* e) {
 | 
			
		||||
            m_trail.push_back({ v, e });
 | 
			
		||||
            s.m_trail.push_back(trail_instr_t::viable_rem_i);
 | 
			
		||||
            e->remove_from(m_viable[v], e);
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        //LOG("intersect " << ne->interval);
 | 
			
		||||
 | 
			
		||||
        if (!e) 
 | 
			
		||||
            m_viable[v] = create_entry();
 | 
			
		||||
        else {
 | 
			
		||||
            entry* first = e;
 | 
			
		||||
            do {
 | 
			
		||||
                if (e->interval.contains(ne->interval)) {
 | 
			
		||||
                    m_alloc.push_back(ne);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                while (ne->interval.contains(e->interval)) {
 | 
			
		||||
                    entry* n = e->next();
 | 
			
		||||
                    remove_entry(e);
 | 
			
		||||
                    if (!m_viable[v]) {
 | 
			
		||||
                        m_viable[v] = create_entry();
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    if (e == first) 
 | 
			
		||||
                        first = n;                    
 | 
			
		||||
                    e = n;
 | 
			
		||||
                }             
 | 
			
		||||
                SASSERT(e->interval.lo_val() != ne->interval.lo_val());
 | 
			
		||||
                if (e->interval.lo_val() > ne->interval.lo_val()) {
 | 
			
		||||
                    if (first->prev()->interval.contains(ne->interval)) {
 | 
			
		||||
                        m_alloc.push_back(ne);
 | 
			
		||||
                        return;
 | 
			
		||||
                    }
 | 
			
		||||
                    e->insert_before(create_entry());
 | 
			
		||||
                    if (e == first)
 | 
			
		||||
                        m_viable[v] = e->prev();
 | 
			
		||||
                    SASSERT(well_formed(m_viable[v]));
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                e = e->next();
 | 
			
		||||
            }             
 | 
			
		||||
            while (e != first);
 | 
			
		||||
            // otherwise, append to end of list
 | 
			
		||||
            first->insert_before(create_entry());
 | 
			
		||||
        }
 | 
			
		||||
        SASSERT(well_formed(m_viable[v]));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable2::has_viable(pvar v) { 
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return true;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        auto const& max_value = s.var2pdd(v).max_value();
 | 
			
		||||
        do {
 | 
			
		||||
            if (e->interval.is_full())
 | 
			
		||||
                return false;
 | 
			
		||||
            entry* n = e->next();
 | 
			
		||||
            if (n == e)
 | 
			
		||||
                return true;
 | 
			
		||||
            if (e->interval.hi_val() < n->interval.lo_val())
 | 
			
		||||
                return true;
 | 
			
		||||
            if (n == first)
 | 
			
		||||
                return e->interval.lo_val() <= e->interval.hi_val();
 | 
			
		||||
            e = n;
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable2::is_viable(pvar v, rational const& val) { 
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return true;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        entry* last = first->prev();
 | 
			
		||||
        if (last->interval.currently_contains(val))
 | 
			
		||||
            return false;
 | 
			
		||||
        for (; e != last; e = e->next()) {        
 | 
			
		||||
            if (e->interval.currently_contains(val))
 | 
			
		||||
                return false;
 | 
			
		||||
            if (val < e->interval.lo_val())
 | 
			
		||||
                return true;
 | 
			
		||||
        }         
 | 
			
		||||
        return true; 
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rational viable2::min_viable(pvar v) { 
 | 
			
		||||
        rational lo(0);
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return lo;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        entry* last = first->prev();
 | 
			
		||||
        if (last->interval.currently_contains(lo))
 | 
			
		||||
            lo = last->interval.hi_val();
 | 
			
		||||
        do {        
 | 
			
		||||
            if (!e->interval.currently_contains(lo))
 | 
			
		||||
                break;
 | 
			
		||||
            lo = e->interval.hi_val();
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
        SASSERT(is_viable(v, lo));        
 | 
			
		||||
        return lo;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rational viable2::max_viable(pvar v) { 
 | 
			
		||||
        rational hi = s.var2pdd(v).max_value();
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return hi;
 | 
			
		||||
        entry* last = e->prev();
 | 
			
		||||
        e = last;
 | 
			
		||||
        do {
 | 
			
		||||
            if (!e->interval.currently_contains(hi))
 | 
			
		||||
                break;
 | 
			
		||||
            hi = e->interval.lo_val() - 1;
 | 
			
		||||
            e = e->prev();
 | 
			
		||||
        }           
 | 
			
		||||
        while (e != last);
 | 
			
		||||
        SASSERT(is_viable(v, hi));
 | 
			
		||||
        return hi;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    dd::find_t viable2::find_viable(pvar v, rational& lo) { 
 | 
			
		||||
        lo = 0;
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return dd::find_t::multiple;
 | 
			
		||||
        if (e->interval.is_full())
 | 
			
		||||
            return dd::find_t::empty;
 | 
			
		||||
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        entry* last = first->prev();
 | 
			
		||||
        if (last->interval.currently_contains(lo))
 | 
			
		||||
            lo = last->interval.hi_val();
 | 
			
		||||
        do {
 | 
			
		||||
            if (!e->interval.currently_contains(lo))
 | 
			
		||||
                break;
 | 
			
		||||
            lo = e->interval.hi_val();
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
 | 
			
		||||
        if (e->interval.currently_contains(lo))
 | 
			
		||||
            return dd::find_t::empty;
 | 
			
		||||
 | 
			
		||||
        rational hi = s.var2pdd(v).max_value();
 | 
			
		||||
        e = last;
 | 
			
		||||
        do {
 | 
			
		||||
            if (!e->interval.currently_contains(hi))
 | 
			
		||||
                break;
 | 
			
		||||
            hi = e->interval.lo_val() - 1;
 | 
			
		||||
            e = e->prev();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != last);
 | 
			
		||||
        if (lo == hi)
 | 
			
		||||
            return dd::find_t::singleton;
 | 
			
		||||
        else
 | 
			
		||||
            return dd::find_t::multiple;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable2::resolve(pvar v, conflict& core) {
 | 
			
		||||
        if (has_viable(v))
 | 
			
		||||
            return false;
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        SASSERT(e);
 | 
			
		||||
        core.reset();
 | 
			
		||||
        do {
 | 
			
		||||
            // Build constraint: upper bound of each interval is not contained in the next interval,
 | 
			
		||||
            // using the equivalence:  t \in [l;h[  <=>  t-l < h-l
 | 
			
		||||
            entry* n = e->next();
 | 
			
		||||
            if (!e->interval.is_full()) {
 | 
			
		||||
                auto const& hi = e->interval.hi();
 | 
			
		||||
                auto const& next_lo = n->interval.lo();
 | 
			
		||||
                auto const& next_hi = n->interval.hi();
 | 
			
		||||
                auto lhs = hi - next_lo;
 | 
			
		||||
                auto rhs = next_hi - next_lo;
 | 
			
		||||
                signed_constraint c = s.m_constraints.ult(lhs, rhs);
 | 
			
		||||
                core.insert(c);
 | 
			
		||||
            }
 | 
			
		||||
            for (auto sc : e->side_cond)
 | 
			
		||||
                core.insert(sc);
 | 
			
		||||
            e->src->set_var_dependent(); // ?
 | 
			
		||||
            core.insert(e->src);
 | 
			
		||||
            e = n;
 | 
			
		||||
        }             
 | 
			
		||||
        while (e != first);
 | 
			
		||||
 | 
			
		||||
        core.set_bailout();
 | 
			
		||||
        for (auto c : core) {
 | 
			
		||||
            if (c.bvalue(s) == l_false) {
 | 
			
		||||
                core.reset();
 | 
			
		||||
                core.set(~c);
 | 
			
		||||
                break;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable2::log(pvar v) {
 | 
			
		||||
        if (!well_formed(m_viable[v]))
 | 
			
		||||
            LOG("v" << v << " not well formed");
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        do {
 | 
			
		||||
            LOG("v" << v << ": " << e->interval << " " << e->side_cond << " " << e->src);
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    void viable2::log() {
 | 
			
		||||
        for (pvar v = 0; v < std::min(10u, m_viable.size()); ++v)
 | 
			
		||||
            log(v);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& viable2::display(std::ostream& out, pvar v) const {
 | 
			
		||||
        auto* e = m_viable[v];
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return out;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        do {
 | 
			
		||||
            out << "v" << v << ": " << e->interval << " " << e->side_cond << " " << e->src << "\n";
 | 
			
		||||
            e = e->next();
 | 
			
		||||
        }         
 | 
			
		||||
        while (e != first);
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    std::ostream& viable2::display(std::ostream& out) const {
 | 
			
		||||
        for (pvar v = 0; v < m_viable.size(); ++v)
 | 
			
		||||
            display(out, v);
 | 
			
		||||
        return out;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
    * Lower bounds are strictly ascending.
 | 
			
		||||
    * intervals don't contain each-other (since lower bounds are ascending, 
 | 
			
		||||
    * it suffices to check containment in one direction).
 | 
			
		||||
    */
 | 
			
		||||
    bool viable2::well_formed(entry* e) {
 | 
			
		||||
        if (!e)
 | 
			
		||||
            return true;
 | 
			
		||||
        entry* first = e;
 | 
			
		||||
        while (true) {
 | 
			
		||||
            if (e->interval.is_full())
 | 
			
		||||
                return e->next() == e;
 | 
			
		||||
            if (e->interval.is_currently_empty()) 
 | 
			
		||||
                return false;
 | 
			
		||||
            
 | 
			
		||||
            auto* n = e->next();
 | 
			
		||||
            if (n != e && e->interval.contains(n->interval)) 
 | 
			
		||||
                return false;
 | 
			
		||||
            
 | 
			
		||||
            if (n == first)
 | 
			
		||||
                break;            
 | 
			
		||||
            if (e->interval.lo_val() >= n->interval.lo_val())
 | 
			
		||||
                return false;
 | 
			
		||||
            e = n;
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,155 +0,0 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2021 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    maintain viable domains
 | 
			
		||||
    It uses the interval extraction functions from forbidden intervals.
 | 
			
		||||
    An empty viable set corresponds directly to a conflict that does not rely on 
 | 
			
		||||
    the non-viable variable.
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2021-03-19
 | 
			
		||||
    Jakob Rath 2021-04-6
 | 
			
		||||
 | 
			
		||||
--*/
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <limits>
 | 
			
		||||
#include "util/dlist.h"
 | 
			
		||||
#include "util/small_object_allocator.h"
 | 
			
		||||
#include "math/polysat/types.h"
 | 
			
		||||
#include "math/polysat/conflict.h"
 | 
			
		||||
#include "math/polysat/constraint.h"
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
    class solver;
 | 
			
		||||
 | 
			
		||||
    class viable2 {
 | 
			
		||||
        solver& s;
 | 
			
		||||
        
 | 
			
		||||
        struct entry : public dll_base<entry>, public fi_record { 
 | 
			
		||||
        public: 
 | 
			
		||||
            entry() : fi_record({ eval_interval::full(), {}, {} }) {}
 | 
			
		||||
        };
 | 
			
		||||
        
 | 
			
		||||
        ptr_vector<entry>                    m_alloc;
 | 
			
		||||
        ptr_vector<entry>                    m_viable;   // set of viable values.
 | 
			
		||||
        svector<std::pair<pvar, entry*>>     m_trail;    // undo stack
 | 
			
		||||
 | 
			
		||||
        bool well_formed(entry* e);
 | 
			
		||||
 | 
			
		||||
        entry* alloc_entry();
 | 
			
		||||
 | 
			
		||||
        void intersect(pvar v, entry* e);
 | 
			
		||||
 | 
			
		||||
        std::ostream& display(std::ostream& out, pvar v) const;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
        viable2(solver& s);
 | 
			
		||||
 | 
			
		||||
        ~viable2();
 | 
			
		||||
 | 
			
		||||
        // declare and remove var
 | 
			
		||||
        void push(unsigned) { m_viable.push_back(nullptr); }
 | 
			
		||||
 | 
			
		||||
        void pop() { m_viable.pop_back(); }
 | 
			
		||||
 | 
			
		||||
        void pop_viable();
 | 
			
		||||
 | 
			
		||||
        void push_viable();
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * update state of viable for pvar v
 | 
			
		||||
         * based on affine constraints
 | 
			
		||||
         */
 | 
			
		||||
        void intersect(pvar v, signed_constraint const& c);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Check whether variable v has any viable values left according to m_viable.
 | 
			
		||||
         */
 | 
			
		||||
        bool has_viable(pvar v);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * check if value is viable according to m_viable.
 | 
			
		||||
         */
 | 
			
		||||
        bool is_viable(pvar v, rational const& val);
 | 
			
		||||
 | 
			
		||||
        /*
 | 
			
		||||
        * Extract min and max viable values for v
 | 
			
		||||
        */
 | 
			
		||||
        rational min_viable(pvar v);
 | 
			
		||||
 | 
			
		||||
        rational max_viable(pvar v);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * Find a next viable value for variable.
 | 
			
		||||
         */
 | 
			
		||||
        dd::find_t find_viable(pvar v, rational & val);
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
        * Retrieve the unsat core for v.
 | 
			
		||||
        * \pre there are no viable values for v
 | 
			
		||||
        */
 | 
			
		||||
        bool resolve(pvar v, conflict& core);
 | 
			
		||||
 | 
			
		||||
        /** Log all viable values for the given variable.
 | 
			
		||||
         * (Inefficient, but useful for debugging small instances.)
 | 
			
		||||
         */
 | 
			
		||||
        void log(pvar v);
 | 
			
		||||
 | 
			
		||||
        /** Like log(v) but for all variables */
 | 
			
		||||
        void log();
 | 
			
		||||
 | 
			
		||||
        std::ostream& display(std::ostream& out) const;
 | 
			
		||||
 | 
			
		||||
        class iterator {
 | 
			
		||||
            entry* curr = nullptr;
 | 
			
		||||
            bool   visited = false;
 | 
			
		||||
        public:
 | 
			
		||||
            iterator(entry* curr, bool visited) : 
 | 
			
		||||
                curr(curr), visited(visited || !curr) {}
 | 
			
		||||
 | 
			
		||||
            iterator& operator++() {
 | 
			
		||||
                visited = true;
 | 
			
		||||
                curr = curr->next();
 | 
			
		||||
                return *this;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            signed_constraint& operator*() { 
 | 
			
		||||
                return curr->src; 
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool operator==(iterator const& other) const {
 | 
			
		||||
                return visited == other.visited && curr == other.curr;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            bool operator!=(iterator const& other) const {
 | 
			
		||||
                return !(*this == other);
 | 
			
		||||
            }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        class constraints {
 | 
			
		||||
            viable2& viable;
 | 
			
		||||
            pvar var;
 | 
			
		||||
        public:
 | 
			
		||||
            constraints(viable2& viable, pvar var) : viable(viable), var(var) {}
 | 
			
		||||
            iterator begin() const { return iterator(viable.m_viable[var], false); }
 | 
			
		||||
            iterator end() const { return iterator(viable.m_viable[var], true); }
 | 
			
		||||
        };
 | 
			
		||||
 | 
			
		||||
        constraints get_constraints(pvar v) { 
 | 
			
		||||
            return constraints(*this, v); 
 | 
			
		||||
        }
 | 
			
		||||
       
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    inline std::ostream& operator<<(std::ostream& out, viable2 const& v) {
 | 
			
		||||
        return v.display(out);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,56 +0,0 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2021 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    set of viable values as wrap-around interval
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2021-03-19
 | 
			
		||||
    Jakob Rath 2021-04-6
 | 
			
		||||
 | 
			
		||||
Notes:
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
  replace BDDs by viable sets that emulate affine relations.
 | 
			
		||||
  viable_set has an interval of feasible values.
 | 
			
		||||
  it also can use ternary bit-vectors.
 | 
			
		||||
  or we could also just use a vector of lbool instead of ternary bit-vectors
 | 
			
		||||
  updating them at individual positions is relatively cheap instead of copying the
 | 
			
		||||
  vectors every time a range is narrowed.
 | 
			
		||||
    
 | 
			
		||||
    
 | 
			
		||||
--*/
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <limits>
 | 
			
		||||
 | 
			
		||||
#include "math/dd/dd_bdd.h"
 | 
			
		||||
#include "math/polysat/types.h"
 | 
			
		||||
#include "math/interval/mod_interval.h"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
  
 | 
			
		||||
    class viable_set : public mod_interval<rational> {
 | 
			
		||||
        unsigned m_num_bits;
 | 
			
		||||
        rational p2() const { return rational::power_of_two(m_num_bits); }
 | 
			
		||||
        bool is_max(rational const& a) const override;
 | 
			
		||||
        void intersect_eq(rational const& a, bool is_positive);
 | 
			
		||||
        bool narrow(std::function<bool(rational const&)>& eval);
 | 
			
		||||
    public:
 | 
			
		||||
        viable_set(unsigned num_bits): m_num_bits(num_bits) {}
 | 
			
		||||
        ~viable_set() override {}
 | 
			
		||||
        dd::find_t find_hint(rational const& c, rational& val) const;
 | 
			
		||||
        bool intersect_eq(rational const& a, rational const& b, bool is_positive);
 | 
			
		||||
        bool intersect_le(rational const& a, rational const& b, rational const& c, rational const& d, bool is_positive);
 | 
			
		||||
        rational prev(rational const& p) const;
 | 
			
		||||
	    rational next(rational const& p) const;
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,121 +0,0 @@
 | 
			
		|||
/*++
 | 
			
		||||
Copyright (c) 2021 Microsoft Corporation
 | 
			
		||||
 | 
			
		||||
Module Name:
 | 
			
		||||
 | 
			
		||||
    set of viable values as wrap-around interval
 | 
			
		||||
 | 
			
		||||
Author:
 | 
			
		||||
 | 
			
		||||
    Nikolaj Bjorner (nbjorner) 2021-03-19
 | 
			
		||||
    Jakob Rath 2021-04-6
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
--*/
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include "math/polysat/viable_set.h"
 | 
			
		||||
#include "math/interval/mod_interval_def.h"
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
      dd::find_t viable_set::find_hint(rational const& d, rational& val) const {
 | 
			
		||||
        if (is_empty())
 | 
			
		||||
            return dd::find_t::empty;         
 | 
			
		||||
        if (contains(d))
 | 
			
		||||
            val = d;
 | 
			
		||||
        else 
 | 
			
		||||
            val = lo;
 | 
			
		||||
        if (lo + 1 == hi || hi == 0 && is_max(lo))
 | 
			
		||||
            return dd::find_t::singleton;            
 | 
			
		||||
        return dd::find_t::multiple;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable_set::is_max(rational const& a) const {
 | 
			
		||||
        return a + 1 == rational::power_of_two(m_num_bits);
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    void viable_set::intersect_eq(rational const& a, bool is_positive) {
 | 
			
		||||
        if (is_positive) 
 | 
			
		||||
            intersect_fixed(a);        
 | 
			
		||||
        else 
 | 
			
		||||
            intersect_diff(a);        
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable_set::intersect_eq(rational const& a, rational const& b, bool is_positive) {
 | 
			
		||||
        if (!a.is_odd()) {
 | 
			
		||||
            std::function<bool(rational const&)> eval = [&](rational const& x) {
 | 
			
		||||
                return is_positive == (mod(a * x + b, p2()) == 0);
 | 
			
		||||
            };       
 | 
			
		||||
            return narrow(eval);            
 | 
			
		||||
        }
 | 
			
		||||
        if (b == 0)
 | 
			
		||||
            intersect_eq(b, is_positive);
 | 
			
		||||
        else {
 | 
			
		||||
            rational a_inv;
 | 
			
		||||
            VERIFY(a.mult_inverse(m_num_bits, a_inv));
 | 
			
		||||
            intersect_eq(mod(a_inv * -b, p2()), is_positive);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable_set::intersect_le(rational const& a, rational const& b, rational const& c, rational const& d, bool is_positive) {
 | 
			
		||||
        // x <= 0
 | 
			
		||||
        if (a.is_odd() && b == 0 && c == 0 && d == 0)
 | 
			
		||||
            intersect_eq(b, is_positive);
 | 
			
		||||
        else if (a == 1 && b == 0 && c == 0) {
 | 
			
		||||
            // x <= d or x > d
 | 
			
		||||
            if (is_positive)
 | 
			
		||||
                intersect_ule(d);
 | 
			
		||||
            else 
 | 
			
		||||
                intersect_ugt(d);
 | 
			
		||||
        }
 | 
			
		||||
        else if (a == 0 && c == 1 && d == 0) {
 | 
			
		||||
            // x >= b or x < b
 | 
			
		||||
            if (is_positive)
 | 
			
		||||
                intersect_uge(b);
 | 
			
		||||
            else
 | 
			
		||||
                intersect_ult(b);
 | 
			
		||||
        }
 | 
			
		||||
        // TBD: can also handle wrap-around semantics (for signed comparison)
 | 
			
		||||
        else {
 | 
			
		||||
            std::function<bool(rational const&)> eval = [&](rational const& x) {
 | 
			
		||||
                return is_positive == mod(a * x + b, p2()) <= mod(c * x + d, p2());
 | 
			
		||||
            };       
 | 
			
		||||
            return narrow(eval);
 | 
			
		||||
        }
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rational viable_set::prev(rational const& p) const {
 | 
			
		||||
        if (p > 0)
 | 
			
		||||
            return p - 1;
 | 
			
		||||
        else
 | 
			
		||||
            return rational::power_of_two(m_num_bits) - 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    rational viable_set::next(rational const& p) const {
 | 
			
		||||
        if (is_max(p))
 | 
			
		||||
	    return rational(0);
 | 
			
		||||
        else
 | 
			
		||||
            return p + 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    bool viable_set::narrow(std::function<bool(rational const&)>& eval) {
 | 
			
		||||
        unsigned budget = 10;
 | 
			
		||||
        while (budget > 0 && !is_empty() && !eval(lo)) {
 | 
			
		||||
            --budget;
 | 
			
		||||
            intersect_diff(lo);
 | 
			
		||||
        }
 | 
			
		||||
        while (budget > 0 && !is_empty() && !eval(prev(hi))) {
 | 
			
		||||
            --budget;
 | 
			
		||||
            intersect_diff(prev(hi));
 | 
			
		||||
        }
 | 
			
		||||
        return 0 < budget;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
#include "math/polysat/log.h"
 | 
			
		||||
#include "math/polysat/solver.h"
 | 
			
		||||
#include "math/polysat/viable2.h"
 | 
			
		||||
#include "math/polysat/viable.h"
 | 
			
		||||
 | 
			
		||||
namespace polysat {
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +10,7 @@ namespace polysat {
 | 
			
		|||
 | 
			
		||||
    class scoped_solverv : public solver_scopev, public solver {
 | 
			
		||||
    public:
 | 
			
		||||
        viable2 v;
 | 
			
		||||
        viable v;
 | 
			
		||||
        scoped_solverv(): solver(lim), v(*this) {}
 | 
			
		||||
        ~scoped_solverv() {
 | 
			
		||||
            for (unsigned i = m_trail.size(); i-- > 0;) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue