mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	throttle lemmas in nla_solver untested
This commit is contained in:
		
							parent
							
								
									46319156b8
								
							
						
					
					
						commit
						5caa7f1a29
					
				
					 9 changed files with 156 additions and 105 deletions
				
			
		|  | @ -2542,7 +2542,7 @@ namespace lp { | |||
|     public: | ||||
| 
 | ||||
|         void explain(explanation& ex) { | ||||
|             SASSERT(ex.empty()); | ||||
|             ex.clear(); | ||||
|             if (has_conflict_index()) { | ||||
|                 TRACE(dio, print_entry(m_normalize_conflict_index, tout << "conflict:", true) << std::endl;); | ||||
|                 for (auto ci : lra.flatten(explain_fixed_in_meta_term(m_l_matrix.m_rows[m_normalize_conflict_index], m_normalize_conflict_gcd))) | ||||
|  |  | |||
|  | @ -70,7 +70,7 @@ public: | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool empty() const {  return m_vector.empty() || m_set.empty();  } | ||||
|     bool empty() const {  return m_vector.empty() && m_set.empty();  } | ||||
|     size_t size() const { return std::max(m_vector.size(), m_set.size()); } | ||||
| 
 | ||||
|     class cimpq { | ||||
|  |  | |||
|  | @ -1040,7 +1040,7 @@ rational core::val(const factorization& f) const { | |||
|     return r; | ||||
| } | ||||
| 
 | ||||
| lemma_builder::lemma_builder(core& c, char const* name):name(name), c(c) { | ||||
| lemma_builder::lemma_builder(core& c, const char* name):name(name), c(c) { | ||||
|     c.m_lemmas.push_back(lemma()); | ||||
| } | ||||
| 
 | ||||
|  |  | |||
|  | @ -7,10 +7,30 @@ | |||
|   --*/ | ||||
| #include "math/lp/nla_basics_lemmas.h" | ||||
| #include "math/lp/nla_core.h" | ||||
| #include "util/trail.h" | ||||
| namespace nla { | ||||
| 
 | ||||
| monotone::monotone(core * c) : common(c) {} | ||||
| 
 | ||||
| bool monotone::throttle_monotone(const monic& m, bool is_lt, const std::string& debug_location) { | ||||
|     // Check if throttling is enabled
 | ||||
|     if (!c().params().arith_nl_thrl()) | ||||
|         return false; | ||||
|      | ||||
|     // Create the key for this specific monotonicity_lemma invocation
 | ||||
|     monotone_key key(m.var(), is_lt); | ||||
|      | ||||
|     // Check if this combination has already been processed
 | ||||
|     if (m_processed_monotone.contains(key)) { | ||||
|         TRACE(nla_solver, tout << "throttled monotonicity_lemma at " << debug_location << "\n";); | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     // Mark this combination as processed and add to trail for backtracking
 | ||||
|     m_processed_monotone.insert(key); | ||||
|     c().trail().push(insert_map(m_processed_monotone, key)); | ||||
|     return false; | ||||
| } | ||||
|      | ||||
| void monotone::monotonicity_lemma() { | ||||
|     unsigned shift = random(); | ||||
|  | @ -29,7 +49,13 @@ void monotone::monotonicity_lemma(monic const& m) { | |||
|        return; | ||||
|     const rational prod_val = abs(c().product_value(m)); | ||||
|     const rational m_val = abs(var_val(m)); | ||||
|     if (m_val < prod_val) | ||||
|     bool is_lt = m_val < prod_val; | ||||
|      | ||||
|     // Check if this specific combination should be throttled
 | ||||
|     if (throttle_monotone(m, is_lt, __FUNCTION__)) | ||||
|         return; | ||||
|      | ||||
|     if (is_lt) | ||||
|         monotonicity_lemma_lt(m); | ||||
|     else if (m_val > prod_val) | ||||
|         monotonicity_lemma_gt(m); | ||||
|  |  | |||
|  | @ -6,13 +6,43 @@ | |||
|    Nikolaj Bjorner (nbjorner) | ||||
|   --*/ | ||||
| #pragma once | ||||
| #include "util/hashtable.h" | ||||
| #include "util/hash.h" | ||||
| 
 | ||||
| namespace nla { | ||||
|     class core; | ||||
|     class monotone : common {     | ||||
|     public: | ||||
|         monotone(core *core); | ||||
|         void monotonicity_lemma(); | ||||
|          | ||||
|         // Structure to represent the key parameters for throttling monotonicity_lemma
 | ||||
|         struct monotone_key { | ||||
|             short mvar; | ||||
|             bool is_lt; | ||||
|              | ||||
|             // Default constructor for hashtable
 | ||||
|             monotone_key() : mvar(0), is_lt(false) {} | ||||
|              | ||||
|             monotone_key(lpvar mvar, bool is_lt) | ||||
|                 : mvar(static_cast<short>(mvar)), is_lt(is_lt) {} | ||||
|                  | ||||
|             bool operator==(const monotone_key& other) const { | ||||
|                 return mvar == other.mvar && is_lt == other.is_lt; | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         struct monotone_key_hash { | ||||
|             unsigned operator()(const monotone_key& k) const { | ||||
|                 return combine_hash(static_cast<unsigned>(k.mvar),  | ||||
|                                   static_cast<unsigned>(k.is_lt)); | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|     private: | ||||
|         hashtable<monotone_key, monotone_key_hash, default_eq<monotone_key>> m_processed_monotone; | ||||
|         bool throttle_monotone(const monic& m, bool is_lt, const std::string& debug_location); | ||||
|          | ||||
|         void monotonicity_lemma(monic const& m); | ||||
|         void monotonicity_lemma_gt(const monic& m);     | ||||
|         void monotonicity_lemma_lt(const monic& m); | ||||
|  |  | |||
|  | @ -84,33 +84,20 @@ void order::order_lemma_on_binomial(const monic& ac) { | |||
| void order::order_lemma_on_binomial_sign(const monic& xy, lpvar x, lpvar y, int sign) { | ||||
|     if (!c().var_is_int(x) && val(x).is_big()) | ||||
|         return; | ||||
|     // throttle!!!
 | ||||
|      | ||||
| 
 | ||||
|     SASSERT(!_().mon_has_zero(xy.vars())); | ||||
|     int sy = rat_sign(val(y)); | ||||
|     // throttle here 
 | ||||
|     if (throttle_binomial_sign(xy, x, y, sign, sy, __FUNCTION__))  | ||||
|         return; | ||||
|      | ||||
|     lemma_builder lemma(c(), __FUNCTION__); | ||||
|     lemma |= ineq(y,                                   sy == 1      ? llc::LE : llc::GE, 0); // negate sy
 | ||||
|     lemma |= ineq(x,                                   sy*sign == 1 ? llc::GT : llc::LT , val(x));  | ||||
|     lemma |= ineq(term(xy.var(), - val(x), y), sign == 1    ? llc::LE : llc::GE, 0); | ||||
| } | ||||
| 
 | ||||
| bool order::throttle_monic(const monic& ac, std::string const & debug_location ) { // todo - remove debug location!
 | ||||
|     // Check if throttling is enabled
 | ||||
|     if (!c().params().arith_nl_thrl()) | ||||
|         return false; | ||||
|      | ||||
|     // Check if this monic has already been processed using its variable ID
 | ||||
|     if (m_processed_monics.contains(ac.var())) { | ||||
|         TRACE(nla_solver, tout << "throttled at " << debug_location << "\n";); | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     // Mark this monic as processed and add to trail for backtracking
 | ||||
|     m_processed_monics.insert(ac.var()); | ||||
|     c().trail().push(insert_map(m_processed_monics, ac.var())); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool order::throttle_mon_ol(const monic& ac, lpvar a, const rational& c_sign, lpvar c_var, | ||||
|                            const monic& bd, const factor& b, const rational& d_sign,  | ||||
|                            lpvar d, llc ab_cmp, const std::string& debug_location) { | ||||
|  | @ -133,6 +120,26 @@ bool order::throttle_mon_ol(const monic& ac, lpvar a, const rational& c_sign, lp | |||
|     return false; | ||||
| } | ||||
| 
 | ||||
| bool order::throttle_binomial_sign(const monic& xy, lpvar x, lpvar y, int sign, int sy, const std::string& debug_location) { | ||||
|     // Check if throttling is enabled
 | ||||
|     if (!c().params().arith_nl_thrl()) | ||||
|         return false; | ||||
|      | ||||
|     // Create the key for this specific order_lemma_on_binomial_sign invocation
 | ||||
|     binomial_sign_key key(xy.var(), x, y, sign, sy); | ||||
|      | ||||
|     // Check if this combination has already been processed
 | ||||
|     if (m_processed_binomial_sign.contains(key)) { | ||||
|         TRACE(nla_solver, tout << "throttled order_lemma_on_binomial_sign at " << debug_location << "\n";); | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     // Mark this combination as processed and add to trail for backtracking
 | ||||
|     m_processed_binomial_sign.insert(key); | ||||
|     c().trail().push(insert_map(m_processed_binomial_sign, key)); | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // We look for monics e = m.rvars()[k]*d and see if we can create an order lemma for m and  e
 | ||||
| void order::order_lemma_on_factor_binomial_explore(const monic& ac, bool k) { | ||||
|     TRACE(nla_solver, tout << "ac = " <<  pp_mon_with_vars(c(), ac);); | ||||
|  |  | |||
|  | @ -21,24 +21,29 @@ public: | |||
|     void order_lemma(); | ||||
|      | ||||
|     // Structure to represent the key parameters for throttling generate_mon_ol
 | ||||
|     // Optimized for memory efficiency with packed fields
 | ||||
|     struct mon_ol_key { | ||||
|         lpvar ac_var; | ||||
|         lpvar a; | ||||
|         rational c_sign; | ||||
|         lpvar c; | ||||
|         lpvar bd_var; | ||||
|         lpvar b_var; | ||||
|         rational d_sign; | ||||
|         lpvar d; | ||||
|         llc ab_cmp; | ||||
|         short ac_var; | ||||
|         short a; | ||||
|         signed char c_sign;  // -1, 0, 1 fits in signed char
 | ||||
|         short c; | ||||
|         short bd_var; | ||||
|         short b_var; | ||||
|         signed char d_sign;  // -1, 0, 1 fits in signed char  
 | ||||
|         short d; | ||||
|         unsigned char ab_cmp;  // llc enum fits in unsigned char
 | ||||
|          | ||||
|         // Default constructor for hashtable
 | ||||
|         mon_ol_key() : ac_var(0), a(0), c_sign(0), c(0), bd_var(0), b_var(0), d_sign(0), d(0), ab_cmp(llc::EQ) {} | ||||
|         mon_ol_key() : ac_var(0), a(0), c_sign(0), c(0), bd_var(0), b_var(0), d_sign(0), d(0), ab_cmp(static_cast<unsigned char>(llc::EQ)) {} | ||||
|          | ||||
|         mon_ol_key(lpvar ac_var, lpvar a, const rational& c_sign, lpvar c, | ||||
|                    lpvar bd_var, lpvar b_var, const rational& d_sign, lpvar d, llc ab_cmp) | ||||
|             : ac_var(ac_var), a(a), c_sign(c_sign), c(c), bd_var(bd_var),  | ||||
|               b_var(b_var), d_sign(d_sign), d(d), ab_cmp(ab_cmp) {} | ||||
|         mon_ol_key(lpvar ac_var, lpvar a, const rational& c_sign_rat, lpvar c, | ||||
|                    lpvar bd_var, lpvar b_var, const rational& d_sign_rat, lpvar d, llc ab_cmp) | ||||
|             : ac_var(static_cast<short>(ac_var)), a(static_cast<short>(a)),  | ||||
|               c_sign(c_sign_rat.is_pos() ? 1 : (c_sign_rat.is_neg() ? -1 : 0)),  | ||||
|               c(static_cast<short>(c)), bd_var(static_cast<short>(bd_var)),  | ||||
|               b_var(static_cast<short>(b_var)),  | ||||
|               d_sign(d_sign_rat.is_pos() ? 1 : (d_sign_rat.is_neg() ? -1 : 0)),  | ||||
|               d(static_cast<short>(d)), ab_cmp(static_cast<unsigned char>(ab_cmp)) {} | ||||
|                | ||||
|         bool operator==(const mon_ol_key& other) const { | ||||
|             return ac_var == other.ac_var && a == other.a && c_sign == other.c_sign && | ||||
|  | @ -49,13 +54,16 @@ public: | |||
|      | ||||
|     struct mon_ol_key_hash { | ||||
|         unsigned operator()(const mon_ol_key& k) const { | ||||
|             return combine_hash(combine_hash(combine_hash(combine_hash( | ||||
|                    combine_hash(combine_hash(combine_hash(combine_hash( | ||||
|                    static_cast<unsigned>(k.ac_var), static_cast<unsigned>(k.a)), | ||||
|                    k.c_sign.hash()), static_cast<unsigned>(k.c)), | ||||
|                    static_cast<unsigned>(k.bd_var)), static_cast<unsigned>(k.b_var)), | ||||
|                    k.d_sign.hash()), static_cast<unsigned>(k.d)), | ||||
|                    static_cast<unsigned>(k.ab_cmp)); | ||||
|             // Optimized hash with better distribution using bit shifts
 | ||||
|             unsigned h1 = (static_cast<unsigned>(k.ac_var) << 16) | static_cast<unsigned>(k.a); | ||||
|             unsigned h2 = (static_cast<unsigned>(k.c_sign + 1) << 24) |  | ||||
|                          (static_cast<unsigned>(k.c) << 8) | static_cast<unsigned>(k.bd_var); | ||||
|             unsigned h3 = (static_cast<unsigned>(k.b_var) << 16) |  | ||||
|                          ((static_cast<unsigned>(k.d_sign + 1) << 8)) |  | ||||
|                          static_cast<unsigned>(k.d); | ||||
|             unsigned h4 = static_cast<unsigned>(k.ab_cmp); | ||||
|              | ||||
|             return combine_hash(combine_hash(h1, h2), combine_hash(h3, h4)); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|  | @ -64,8 +72,44 @@ public: | |||
|                          const monic& bd, const factor& b, const rational& d_sign,  | ||||
|                          lpvar d, llc ab_cmp, const std::string& debug_location); | ||||
|      | ||||
|     int_hashtable<int_hash, default_eq<int>> m_processed_monics; | ||||
|     bool throttle_monic(const monic&, const std::string & debug_location); | ||||
|     // Structure to represent the key parameters for throttling order_lemma_on_binomial_sign
 | ||||
|     // Optimized for memory efficiency with packed fields
 | ||||
|     struct binomial_sign_key { | ||||
|         short xy_var; | ||||
|         short x; | ||||
|         short y; | ||||
|         signed char sign; | ||||
|         signed char sy; | ||||
|          | ||||
|         // Default constructor for hashtable
 | ||||
|         binomial_sign_key() : xy_var(0), x(0), y(0), sign(0), sy(0) {} | ||||
|          | ||||
|         binomial_sign_key(lpvar xy_var, lpvar x, lpvar y, int sign, int sy) | ||||
|             : xy_var(static_cast<short>(xy_var)), x(static_cast<short>(x)),  | ||||
|               y(static_cast<short>(y)), sign(static_cast<signed char>(sign)),  | ||||
|               sy(static_cast<signed char>(sy)) {} | ||||
|                | ||||
|         bool operator==(const binomial_sign_key& other) const { | ||||
|             return xy_var == other.xy_var && x == other.x && y == other.y && | ||||
|                    sign == other.sign && sy == other.sy; | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     struct binomial_sign_key_hash { | ||||
|         unsigned operator()(const binomial_sign_key& k) const { | ||||
|             // Optimized hash with better distribution 
 | ||||
|             unsigned h1 = (static_cast<unsigned>(k.xy_var) << 16) | static_cast<unsigned>(k.x); | ||||
|             unsigned h2 = (static_cast<unsigned>(k.y) << 16) |  | ||||
|                          ((static_cast<unsigned>(k.sign + 127) << 8)) |  // shift sign to positive range
 | ||||
|                          static_cast<unsigned>(k.sy + 127);               // shift sy to positive range
 | ||||
|              | ||||
|             return combine_hash(h1, h2); | ||||
|         } | ||||
|     }; | ||||
|      | ||||
|     hashtable<binomial_sign_key, binomial_sign_key_hash, default_eq<binomial_sign_key>> m_processed_binomial_sign; | ||||
|     bool throttle_binomial_sign(const monic& xy, lpvar x, lpvar y, int sign, int sy, const std::string& debug_location); | ||||
|      | ||||
|    private: | ||||
| 
 | ||||
|     bool order_lemma_on_ac_and_bc_and_factors(const monic& ac, | ||||
|  |  | |||
|  | @ -107,9 +107,9 @@ namespace nla { | |||
|     void solver::check_bounded_divisions() { | ||||
|         m_core->check_bounded_divisions(); | ||||
|     } | ||||
|     //return only the non-empty lemmas
 | ||||
|     solver::non_empty_lemmas_range solver::lemmas() const { | ||||
|         return non_empty_lemmas_range(m_core->lemmas()); | ||||
| 
 | ||||
|     const vector<lemma>&  solver::lemmas() const { | ||||
|         return m_core->lemmas(); | ||||
|     } | ||||
|      | ||||
|     vector<nla::ineq> const& solver::literals() const { | ||||
|  |  | |||
|  | @ -54,63 +54,7 @@ namespace nla { | |||
|         vector<lp::equality> const& equalities() const; | ||||
|         bool should_check_feasible() const { return m_core->should_check_feasible(); } | ||||
|          | ||||
|         // Iterator class for filtering out empty lemmas
 | ||||
|         class non_empty_lemma_iterator { | ||||
|             vector<nla::lemma>::const_iterator current_; | ||||
|             vector<nla::lemma>::const_iterator end_; | ||||
|              | ||||
|             void advance_to_non_empty() { | ||||
|                 while (current_ != end_ && current_->is_empty()) { | ||||
|                     std::cout << "skip\n"; | ||||
|                     ++current_; | ||||
|                 } | ||||
|             } | ||||
|              | ||||
|         public: | ||||
|             non_empty_lemma_iterator(vector<nla::lemma>::const_iterator start,  | ||||
|                                    vector<nla::lemma>::const_iterator end)  | ||||
|                 : current_(start), end_(end) { | ||||
|                 advance_to_non_empty(); | ||||
|             } | ||||
|              | ||||
|             const nla::lemma& operator*() const { return *current_; } | ||||
|             const nla::lemma* operator->() const { return &*current_; } | ||||
|              | ||||
|             non_empty_lemma_iterator& operator++() { | ||||
|                 ++current_; | ||||
|                 advance_to_non_empty(); | ||||
|                 return *this; | ||||
|             } | ||||
|              | ||||
|             bool operator!=(const non_empty_lemma_iterator& other) const { | ||||
|                 return current_ != other.current_; | ||||
|             } | ||||
|              | ||||
|             bool operator==(const non_empty_lemma_iterator& other) const { | ||||
|                 return current_ == other.current_; | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         // Helper class to provide range-based iteration over non-empty lemmas
 | ||||
|         class non_empty_lemmas_range { | ||||
|             const vector<nla::lemma>& lemmas_; | ||||
|         public: | ||||
|             non_empty_lemmas_range(const vector<nla::lemma>& lemmas) : lemmas_(lemmas) {} | ||||
|              | ||||
|             non_empty_lemma_iterator begin() const { | ||||
|                 return non_empty_lemma_iterator(lemmas_.begin(), lemmas_.end()); | ||||
|             } | ||||
|              | ||||
|             non_empty_lemma_iterator end() const { | ||||
|                 return non_empty_lemma_iterator(lemmas_.end(), lemmas_.end()); | ||||
|             } | ||||
|              | ||||
|             bool empty() const { | ||||
|                 return begin() == end(); | ||||
|             } | ||||
|         }; | ||||
|          | ||||
|         non_empty_lemmas_range lemmas() const; | ||||
|         const vector<nla::lemma>&  lemmas() const; | ||||
|          | ||||
|     }; | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue