mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 03:32:28 +00:00 
			
		
		
		
	add option to propagation quotients
for equations x*y + z = 0, with x, y, z integer, enforce that x divides z It is (currently) enabled within Grobner completion and applied partially to x a variable, z linear, and only when |z| < |x|.
This commit is contained in:
		
							parent
							
								
									91b4873b79
								
							
						
					
					
						commit
						e91e432496
					
				
					 10 changed files with 516 additions and 258 deletions
				
			
		|  | @ -11,6 +11,7 @@ Author: | |||
| 
 | ||||
| --*/ | ||||
| #include "util/uint_set.h" | ||||
| #include "params/smt_params_helper.hpp" | ||||
| #include "math/lp/nla_core.h" | ||||
| #include "math/lp/factorization_factory_imp.h" | ||||
| #include "math/grobner/pdd_solver.h" | ||||
|  | @ -27,6 +28,11 @@ namespace nla { | |||
|         m_quota(m_core.params().arith_nl_gr_q()) | ||||
|     {} | ||||
| 
 | ||||
|     void grobner::updt_params(params_ref const& p) { | ||||
|         smt_params_helper ph(p); | ||||
|         m_config.m_propagate_quotients = ph.arith_nl_grobner_propagate_quotients(); | ||||
|     } | ||||
| 
 | ||||
|     lp::lp_settings& grobner::lp_settings() { | ||||
|         return c().lp_settings(); | ||||
|     } | ||||
|  | @ -72,6 +78,10 @@ namespace nla { | |||
|              | ||||
|             if (propagate_linear_equations()) | ||||
|                 return; | ||||
| 
 | ||||
|             if (propagate_quotients()) | ||||
|                 return; | ||||
|             IF_VERBOSE(0, m_solver.display(verbose_stream() << "grobner\n")); | ||||
|              | ||||
|         } | ||||
|         catch (...) { | ||||
|  | @ -181,23 +191,12 @@ namespace nla { | |||
| 
 | ||||
|         // IF_VERBOSE(0, verbose_stream() << "factored " << q << " : " << vars << "\n");
 | ||||
| 
 | ||||
|         term t; | ||||
|         rational lc(1); | ||||
|         auto ql = q; | ||||
|         while (!ql.is_val()) { | ||||
|             lc = lcm(lc, denominator(ql.hi().val())); | ||||
|             ql = ql.lo(); | ||||
|         } | ||||
|         lc = lcm(denominator(ql.val()), lc); | ||||
|         auto [t, offset] = linear_to_term(q); | ||||
| 
 | ||||
|         while (!q.is_val()) { | ||||
|             t.add_monomial(lc*q.hi().val(), q.var()); | ||||
|             q = q.lo(); | ||||
|         } | ||||
|         vector<ineq> ineqs; | ||||
|         for (auto v : vars) | ||||
|             ineqs.push_back(ineq(v, llc::EQ, rational::zero())); | ||||
|         ineqs.push_back(ineq(t, llc::EQ, -lc*q.val())); | ||||
|         ineqs.push_back(ineq(t, llc::EQ, -offset)); | ||||
|         for (auto const& i : ineqs) | ||||
|             if (c().ineq_holds(i)) | ||||
|                 return false; | ||||
|  | @ -210,6 +209,151 @@ namespace nla { | |||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     std::pair<lp::lar_term, rational> grobner::linear_to_term(dd::pdd q) { | ||||
|         SASSERT(q.is_linear()); | ||||
|         rational lc(1); | ||||
|         auto ql = q; | ||||
|         lp::lar_term t; | ||||
|         while (!ql.is_val()) { | ||||
|             lc = lcm(lc, denominator(ql.hi().val())); | ||||
|             ql = ql.lo(); | ||||
|         } | ||||
|         lc = lcm(denominator(ql.val()), lc); | ||||
| 
 | ||||
|         while (!q.is_val()) { | ||||
|             t.add_monomial(lc * q.hi().val(), q.var()); | ||||
|             q = q.lo(); | ||||
|         } | ||||
|         rational offset = lc * q.val(); | ||||
|         return {t, offset}; | ||||
|     } | ||||
| 
 | ||||
|     bool grobner::propagate_quotients() { | ||||
|         if (!m_config.m_propagate_quotients) | ||||
|             return false; | ||||
|         unsigned changed = 0; | ||||
|         for (auto eq : m_solver.equations()) | ||||
|             if (propagate_quotients(*eq) && ++changed >= m_solver.number_of_conflicts_to_report()) | ||||
|                 return true; | ||||
|         return changed > 0; | ||||
|     } | ||||
| 
 | ||||
|     // factor each nl var at a time.
 | ||||
|     // x*y + z = 0
 | ||||
|     // x = 0 => z = 0
 | ||||
|     // y = 0 => z = 0
 | ||||
|     // z = 0 => x = 0 or y = 0
 | ||||
|     // z > 0 & x > 0 => x <= z
 | ||||
|     // z < 0 & x > 0 => x <= -z
 | ||||
|     // z > 0 & x < 0 => -x <= z
 | ||||
|     // z < 0 & x < 0 => -x <= -z
 | ||||
|     bool grobner::propagate_quotients(dd::solver::equation const& eq) { | ||||
|         dd::pdd const& p = eq.poly(); | ||||
|         if (p.is_linear()) | ||||
|             return false; | ||||
|         if (p.is_val()) | ||||
|             return false; | ||||
|         auto v = p.var(); | ||||
|         if (!c().var_is_int(v)) | ||||
|             return false; | ||||
|         for (auto v : p.free_vars()) | ||||
|             if (!c().var_is_int(v)) | ||||
|                 return false; | ||||
|         tracked_uint_set nl_vars; | ||||
|         for (auto const& m : p) { | ||||
|             if (m.vars.size() == 1) | ||||
|                 continue; | ||||
|             for (auto j : m.vars) | ||||
|                 nl_vars.insert(j); | ||||
|         } | ||||
|         dd::pdd_eval eval; | ||||
|         eval.var2val() = [&](unsigned j) { return val(j); }; | ||||
| 
 | ||||
|         for (auto v : nl_vars) { | ||||
|             auto& m = p.manager(); | ||||
|             dd::pdd lc(m), r(m); | ||||
|             p.factor(v, 1, lc, r); | ||||
|             if (!r.is_linear()) | ||||
|                 continue; | ||||
|             auto v_value = val(v); | ||||
|             auto r_value = eval(r); | ||||
|             auto lc_value = eval(lc); | ||||
|             if (r_value == 0) { | ||||
|                 if (v_value == 0) | ||||
|                     continue; | ||||
|                 if (lc_value == 0) | ||||
|                     continue; | ||||
|                 if (!lc.is_linear()) | ||||
|                     continue; | ||||
|                 auto [t, offset] = linear_to_term(lc); | ||||
|                 auto [t2, offset2] = linear_to_term(r); | ||||
|                 lemma_builder lemma(c(), "pdd-quotient"); | ||||
|                 add_dependencies(lemma, eq); | ||||
|                 // v = 0 or lc = 0 or r != 0
 | ||||
|                 lemma |= ineq(v, llc::EQ, rational::zero()); | ||||
|                 lemma |= ineq(t, llc::EQ, -offset); | ||||
|                 lemma |= ineq(t2, llc::NE, -offset2); | ||||
|                 return true; | ||||
|             } | ||||
|             // r_value != 0
 | ||||
|             if (v_value == 0) { | ||||
|                 // v = 0 => r = 0
 | ||||
|                 lemma_builder lemma(c(), "pdd-quotient"); | ||||
|                 add_dependencies(lemma, eq); | ||||
|                 auto [t, offset] = linear_to_term(r); | ||||
|                 lemma |= ineq(v, llc::NE, rational::zero()); | ||||
|                 lemma |= ineq(t, llc::EQ, -offset); | ||||
|                 return true; | ||||
|             } | ||||
|             if (lc_value == 0) { | ||||
|                 if (!lc.is_linear()) | ||||
|                     continue; | ||||
|                 // lc = 0 => r = 0
 | ||||
|                 lemma_builder lemma(c(), "pdd-quotient"); | ||||
|                 add_dependencies(lemma, eq); | ||||
|                 auto [t, offset] = linear_to_term(lc); | ||||
|                 auto [t2, offset2] = linear_to_term(r); | ||||
|                 lemma |= ineq(t, llc::NE, -offset); | ||||
|                 lemma |= ineq(t2, llc::EQ, -offset2); | ||||
|                 return true;             | ||||
|             } | ||||
|             if (divides(v_value, r_value)) | ||||
|                 continue; | ||||
|                | ||||
|             if (abs(v_value) > abs(r_value)) { | ||||
|                 // v*c + r = 0 & v > 0 => r >= v or -r >= v or r = 0
 | ||||
|                 lemma_builder lemma(c(), "pdd-quotient"); | ||||
|                 auto [t, offset] = linear_to_term(r); | ||||
|                 add_dependencies(lemma, eq); | ||||
|                 if (v_value > 0) { | ||||
|                     lemma |= ineq(v, llc::LE, rational::zero()); | ||||
|                     lemma |= ineq(t, llc::EQ, -offset); | ||||
|                     t.add_monomial(rational(-1), v); | ||||
|                     lemma |= ineq(t, llc::GE, -offset); | ||||
|                     auto [t2, offset2] = linear_to_term(-r); | ||||
|                     t2.add_monomial(rational(-1), v); | ||||
|                     lemma |= ineq(t2, llc::GE, -offset2); | ||||
|                 }  | ||||
|                 else { | ||||
|                     // v*lc + r = 0 & v < 0 => r <= v or -r <= v or r = 0
 | ||||
|                     lemma |= ineq(v, llc::GE, rational::zero()); | ||||
|                     lemma |= ineq(t, llc::EQ, -offset); | ||||
|                     t.add_monomial(rational(-1), v); | ||||
|                     lemma |= ineq(t, llc::LE, -offset); | ||||
|                     auto [t2, offset2] = linear_to_term(-r); | ||||
|                     t2.add_monomial(rational(-1), v); | ||||
|                     lemma |= ineq(t2, llc::LE, -offset2); | ||||
|                 } | ||||
|                 return true; | ||||
|             }  | ||||
|             // other division lemmas are possible.  
 | ||||
|             // also extend to non-linear r, non-linear lc         
 | ||||
|         } | ||||
| 
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void grobner::explain(dd::solver::equation const& eq, lp::explanation& exp) { | ||||
|         u_dependency_manager dm; | ||||
|         vector<unsigned, false> lv; | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue