mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	Add more PDD utilities (div, pow) (#5180)
* Expose 'inv' on rationals to get reciprocal value * Align parameter names with implementation * Add cached operation that divides PDD by a constant * Fix display for constant PDDs * operator^ should probably call ^ instead of + (mk_xor instead of add) * Add helper function 'pow' on PDDs
This commit is contained in:
		
							parent
							
								
									2f7069a8b7
								
							
						
					
					
						commit
						324d9ed461
					
				
					 5 changed files with 146 additions and 20 deletions
				
			
		|  | @ -202,7 +202,7 @@ namespace dd { | |||
|     public: | ||||
|         struct mem_out {}; | ||||
| 
 | ||||
|         bdd_manager(unsigned nodes); | ||||
|         bdd_manager(unsigned num_vars); | ||||
|         ~bdd_manager(); | ||||
| 
 | ||||
|         void set_max_num_nodes(unsigned n) { m_max_num_bdd_nodes = n; } | ||||
|  |  | |||
|  | @ -443,6 +443,97 @@ namespace dd { | |||
|         return r; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Divide PDD by a constant value. | ||||
|      * | ||||
|      * IMPORTANT: Performs regular numerical division. | ||||
|      * For semantics 'mod2N_e', this means that 'c' must be an integer | ||||
|      * and all coefficients of 'a' must be divisible by 'c'. | ||||
|      * | ||||
|      * NOTE: Why do we not just use 'mul(a, inv(c))' instead? | ||||
|      * In case of semantics 'mod2N_e', an invariant is that all PDDs have integer coefficients. | ||||
|      * But such a multiplication would create nodes with non-integral coefficients. | ||||
|      */ | ||||
|     pdd pdd_manager::div(pdd const& a, rational const& c) { | ||||
|         if (m_semantics == free_e) { | ||||
|             // Don't cache separately for the free semantics;
 | ||||
|             // use 'mul' so we can share results for a/c and a*(1/c).
 | ||||
|             return mul(inv(c), a); | ||||
|         } | ||||
|         SASSERT(c.is_int()); | ||||
|         bool first = true; | ||||
|         SASSERT(well_formed()); | ||||
|         scoped_push _sp(*this); | ||||
|         while (true) { | ||||
|             try { | ||||
|                 return pdd(div_rec(a.root, c, null_pdd), this); | ||||
|             } | ||||
|             catch (const mem_out &) { | ||||
|                 try_gc(); | ||||
|                 if (!first) throw; | ||||
|                 first = false; | ||||
|             } | ||||
|         } | ||||
|         SASSERT(well_formed()); | ||||
|         return pdd(zero_pdd, this); | ||||
|     } | ||||
| 
 | ||||
|     pdd_manager::PDD pdd_manager::div_rec(PDD a, rational const& c, PDD c_pdd) { | ||||
|         SASSERT(m_semantics != free_e); | ||||
|         SASSERT(c.is_int()); | ||||
|         if (is_zero(a)) | ||||
|             return zero_pdd; | ||||
|         if (is_val(a)) { | ||||
|             rational r = val(a) / c; | ||||
|             SASSERT(r.is_int()); | ||||
|             return imk_val(r); | ||||
|         } | ||||
|         if (c_pdd == null_pdd) | ||||
|             c_pdd = imk_val(c); | ||||
|         op_entry* e1 = pop_entry(a, c_pdd, pdd_div_const_op); | ||||
|         op_entry const* e2 = m_op_cache.insert_if_not_there(e1); | ||||
|         if (check_result(e1, e2, a, c_pdd, pdd_div_const_op)) | ||||
|             return e2->m_result; | ||||
|         push(div_rec(lo(a), c, c_pdd)); | ||||
|         push(div_rec(hi(a), c, c_pdd)); | ||||
|         PDD r = make_node(level(a), read(2), read(1)); | ||||
|         pop(2); | ||||
|         e1->m_result = r; | ||||
|         return r; | ||||
|     } | ||||
| 
 | ||||
|     pdd pdd_manager::pow(pdd const &p, unsigned j) { | ||||
|         return pdd(pow(p.root, j), this); | ||||
|     } | ||||
| 
 | ||||
|     pdd_manager::PDD pdd_manager::pow(PDD p, unsigned j) { | ||||
|         if (j == 0) | ||||
|             return one_pdd; | ||||
|         else if (j == 1) | ||||
|             return p; | ||||
|         else if (is_zero(p)) | ||||
|             return zero_pdd; | ||||
|         else if (is_one(p)) | ||||
|             return one_pdd; | ||||
|         else if (is_val(p)) | ||||
|             return imk_val(power(val(p), j)); | ||||
|         else | ||||
|             return pow_rec(p, j); | ||||
|     } | ||||
| 
 | ||||
|     pdd_manager::PDD pdd_manager::pow_rec(PDD p, unsigned j) { | ||||
|         SASSERT(j > 0); | ||||
|         if (j == 1) | ||||
|             return p; | ||||
|         // j even: pow(p,2*j')   =   pow(p*p,j')
 | ||||
|         // j odd:  pow(p,2*j'+1) = p*pow(p*p,j')
 | ||||
|         PDD q = pow_rec(apply(p, p, pdd_mul_op), j / 2); | ||||
|         if (j & 1) { | ||||
|             q = apply(q, p, pdd_mul_op); | ||||
|         } | ||||
|         return q; | ||||
|     } | ||||
| 
 | ||||
|     //
 | ||||
|     // produce polynomial where a is reduced by b.
 | ||||
|     // all monomials in a that are divisible by lm(b)
 | ||||
|  | @ -754,6 +845,15 @@ namespace dd { | |||
|         e->m_rest = rest.root; | ||||
|     } | ||||
| 
 | ||||
|     /**
 | ||||
|      * Apply function f to all coefficients of the polynomial. | ||||
|      * The function should be of type | ||||
|      *      rational const& -> rational | ||||
|      *      rational const& -> unsigned | ||||
|      * and should always return integers. | ||||
|      * | ||||
|      * NOTE: the operation is not cached. | ||||
|      */ | ||||
|     template <class Fn> | ||||
|     pdd pdd_manager::map_coefficients(pdd const& p, Fn f) { | ||||
|         if (p.is_val()) { | ||||
|  | @ -803,17 +903,9 @@ namespace dd { | |||
|         unsigned const j = std::min(max_pow2_divisor(a), max_pow2_divisor(c)); | ||||
|         SASSERT(j != UINT_MAX);  // should only be possible when both l and m are 0
 | ||||
|         rational const pow2j = rational::power_of_two(j); | ||||
|         auto div_pow2j = [&pow2j](rational const& r) -> rational { | ||||
|             rational result = r / pow2j; | ||||
|             SASSERT(result.is_int()); | ||||
|             return result; | ||||
|         }; | ||||
|         pdd aa = map_coefficients(a, div_pow2j); | ||||
|         pdd cc = map_coefficients(c, div_pow2j); | ||||
|         pdd vv = one(); | ||||
|         for (unsigned deg = l - m; deg-- > 0; ) { | ||||
|             vv *= mk_var(v); | ||||
|         } | ||||
|         pdd const aa = div(a, pow2j); | ||||
|         pdd const cc = div(c, pow2j); | ||||
|         pdd vv = pow(mk_var(v), l - m); | ||||
|         r = b * cc - aa * d * vv; | ||||
|         return true; | ||||
|     } | ||||
|  | @ -1366,9 +1458,11 @@ namespace dd { | |||
|                 pow = 1; | ||||
|                 v_prev = v; | ||||
|             } | ||||
|             out << "v" << v_prev; | ||||
|             if (pow > 1) | ||||
|                 out << "^" << pow; | ||||
|             if (v_prev != UINT_MAX) { | ||||
|                 out << "v" << v_prev; | ||||
|                 if (pow > 1) | ||||
|                     out << "^" << pow; | ||||
|             } | ||||
|         } | ||||
|         if (first) out << "0"; | ||||
|         return out; | ||||
|  |  | |||
|  | @ -63,7 +63,8 @@ namespace dd { | |||
|             pdd_mul_op = 5, | ||||
|             pdd_reduce_op = 6, | ||||
|             pdd_subst_val_op = 7, | ||||
|             pdd_no_op = 8 | ||||
|             pdd_div_const_op = 8, | ||||
|             pdd_no_op = 9 | ||||
|         }; | ||||
| 
 | ||||
|         struct node { | ||||
|  | @ -213,6 +214,9 @@ namespace dd { | |||
|         PDD apply(PDD arg1, PDD arg2, pdd_op op); | ||||
|         PDD apply_rec(PDD arg1, PDD arg2, pdd_op op); | ||||
|         PDD minus_rec(PDD p); | ||||
|         PDD div_rec(PDD p, rational const& c, PDD c_pdd); | ||||
|         PDD pow(PDD p, unsigned j); | ||||
|         PDD pow_rec(PDD p, unsigned j); | ||||
| 
 | ||||
|         PDD reduce_on_match(PDD a, PDD b); | ||||
|         bool lm_occurs(PDD p, PDD q) const; | ||||
|  | @ -297,7 +301,7 @@ namespace dd { | |||
| 
 | ||||
|         struct mem_out {}; | ||||
| 
 | ||||
|         pdd_manager(unsigned nodes, semantics s = free_e, unsigned power_of_2 = 0); | ||||
|         pdd_manager(unsigned num_vars, semantics s = free_e, unsigned power_of_2 = 0); | ||||
|         ~pdd_manager(); | ||||
| 
 | ||||
|         semantics get_semantics() const { return m_semantics; } | ||||
|  | @ -317,6 +321,7 @@ namespace dd { | |||
|         pdd sub(pdd const& a, pdd const& b); | ||||
|         pdd mul(pdd const& a, pdd const& b); | ||||
|         pdd mul(rational const& c, pdd const& b); | ||||
|         pdd div(pdd const& a, rational const& c); | ||||
|         pdd mk_or(pdd const& p, pdd const& q); | ||||
|         pdd mk_xor(pdd const& p, pdd const& q); | ||||
|         pdd mk_xor(pdd const& p, unsigned q); | ||||
|  | @ -325,6 +330,7 @@ namespace dd { | |||
|         pdd subst_val(pdd const& a, vector<std::pair<unsigned, rational>> const& s); | ||||
|         pdd subst_val(pdd const& a, unsigned v, rational const& val); | ||||
|         bool resolve(unsigned v, pdd const& p, pdd const& q, pdd& r); | ||||
|         pdd pow(pdd const& p, unsigned j); | ||||
| 
 | ||||
|         bool is_linear(PDD p) { return degree(p) == 1; } | ||||
|         bool is_linear(pdd const& p); | ||||
|  | @ -399,6 +405,8 @@ namespace dd { | |||
|         pdd operator+(rational const& other) const { return m.add(other, *this); } | ||||
|         pdd operator~() const { return m.mk_not(*this); } | ||||
|         pdd rev_sub(rational const& r) const { return m.sub(m.mk_val(r), *this); } | ||||
|         pdd div(rational const& other) const { return m.div(*this, other); } | ||||
|         pdd pow(unsigned j) const { return m.pow(*this, j); } | ||||
|         pdd reduce(pdd const& other) const { return m.reduce(*this, other); } | ||||
|         bool different_leading_term(pdd const& other) const { return m.different_leading_term(*this, other); } | ||||
|         void factor(unsigned v, unsigned degree, pdd& lc, pdd& rest) const { m.factor(*this, v, degree, lc, rest); } | ||||
|  | @ -433,8 +441,8 @@ namespace dd { | |||
|     inline pdd operator+(int x, pdd const& b) { return b + rational(x); } | ||||
|     inline pdd operator+(pdd const& b, int x) { return b + rational(x); } | ||||
| 
 | ||||
|     inline pdd operator^(unsigned x, pdd const& b) { return b + x; } | ||||
|     inline pdd operator^(bool x, pdd const& b) { return b + x; } | ||||
|     inline pdd operator^(unsigned x, pdd const& b) { return b ^ x; } | ||||
|     inline pdd operator^(bool x, pdd const& b) { return b ^ x; } | ||||
| 
 | ||||
|     inline pdd operator-(rational const& r, pdd const& b) { return b.rev_sub(r); } | ||||
|     inline pdd operator-(int x, pdd const& b) { return rational(x) - b; } | ||||
|  |  | |||
|  | @ -438,9 +438,10 @@ public : | |||
| 
 | ||||
|         SASSERT(m.zero().max_pow2_divisor() == UINT_MAX); | ||||
|         SASSERT(m.one().max_pow2_divisor() == 0); | ||||
|         pdd p = (1 << 20) * a * b + 1024 * b * b * b; | ||||
|         pdd p = (1 << 20)*a*b + 1024*b*b*b; | ||||
|         std::cout << p << " divided by 2^" << p.max_pow2_divisor() << "\n"; | ||||
|         SASSERT(p.max_pow2_divisor() == 10); | ||||
|         SASSERT(p.div(rational::power_of_two(10)) == 1024*a*b + b*b*b); | ||||
|         SASSERT((p + p).max_pow2_divisor() == 11); | ||||
|         SASSERT((p * p).max_pow2_divisor() == 20); | ||||
|         SASSERT((p + 2*b).max_pow2_divisor() == 1); | ||||
|  | @ -492,6 +493,22 @@ public : | |||
|         SASSERT(r == -(2*a*a*b*b - 2*a*a*b - 3*a*b*b + a*b*b*b + 4*b)); | ||||
|     } | ||||
| 
 | ||||
|     static void pow() { | ||||
|         std::cout << "pow\n"; | ||||
|         pdd_manager m(4, pdd_manager::mod2N_e, 5); | ||||
| 
 | ||||
|         unsigned const va = 0; | ||||
|         unsigned const vb = 1; | ||||
|         pdd const a = m.mk_var(va); | ||||
|         pdd const b = m.mk_var(vb); | ||||
| 
 | ||||
|         SASSERT(a.pow(0) == m.one()); | ||||
|         SASSERT(a.pow(1) == a); | ||||
|         SASSERT(a.pow(2) == a*a); | ||||
|         SASSERT(a.pow(7) == a*a*a*a*a*a*a); | ||||
|         SASSERT((3*a*b).pow(3) == 27*a*a*a*b*b*b); | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| } | ||||
|  | @ -510,4 +527,5 @@ void tst_pdd() { | |||
|     dd::test::factor(); | ||||
|     dd::test::max_pow2_divisor(); | ||||
|     dd::test::binary_resolve(); | ||||
|     dd::test::pow(); | ||||
| } | ||||
|  |  | |||
|  | @ -157,6 +157,12 @@ public: | |||
|     friend inline rational numerator(rational const & r) { rational result; m().get_numerator(r.m_val, result.m_val); return result; } | ||||
|      | ||||
|     friend inline rational denominator(rational const & r) { rational result; m().get_denominator(r.m_val, result.m_val); return result; } | ||||
| 
 | ||||
|     friend inline rational inv(rational const & r) { | ||||
|         rational result; | ||||
|         m().inv(r.m_val, result.m_val); | ||||
|         return result; | ||||
|     } | ||||
|      | ||||
|     rational & operator+=(rational const & r) {  | ||||
|         m().add(m_val, r.m_val, m_val); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue