#1956: implement multivariate power series arithmetic
-------------------------------------------+--------------------------------
   Reporter:  was                          |       Owner:  malb                 
    
       Type:  enhancement                  |      Status:  needs_review         
    
   Priority:  major                        |   Milestone:  sage-4.6             
    
  Component:  commutative algebra          |    Keywords:  multivariate power 
series
     Author:  Niles Johnson                |    Upstream:  N/A                  
    
   Reviewer:  Martin Albrecht, Simon King  |      Merged:                       
    
Work_issues:                               |  
-------------------------------------------+--------------------------------

Comment(by niles):

 Replying to [comment:47 niles]:

 Mike, would you be able to take a look at this some time?

 Here are some further comparisons with polynomial arithmetic, computing
 ``f**k`` for various values of ``k``.  Obviously the power series
 computation has a greater and greater advantage over the polynomial
 computation as ``k`` grows, since the polynomial arithmetic computes many
 more (irrelevant) terms.  So the time tests below aren't exactly fair --
 one could compute the necessary terms by computing small polynomial
 powers, truncating, and repeating -- but part of the point of the power
 series code is to automate this for the user.  One side effect is that
 arithmetic for small powers is slower, but for large powers the power
 series arithmetic is fairly good: below, I have a comparison in a simple
 special case: ``k = 2**n`` and non-zero constant coefficient.

 In any case, the point of this ticket is to implement the basic
 arithmetic.  The code should be fast enough for basic use, and speed
 improvement can be part of a separate ticket.

 == Timings for ``f**k`` v.s. ``fp**k`` ==

 {{{
 sage: BaseRingList = [ZZ,QQ,GF(11),GF(65537)]

 sage: for B in BaseRingList:
     R = PowerSeriesRing(B,4,'t'); R
     f = R.random_element(prec=10,bound=20)
     fp = f.polynomial()
     for k in [2,5,8,11]:
         print 'f**'+str(k)+': (power series)'
         timeit('f**k',number=5)
         print 'fp**'+str(k)+': (polynomial)'
         timeit('fp**k',number=5)
         print '--'
 ....:
 Multivariate Power Series Ring in t0, t1, t2, t3 over Integer Ring
 f**2: (power series)
 5 loops, best of 3: 879 µs per loop
 fp**2: (polynomial)
 5 loops, best of 3: 33.2 µs per loop
 --
 f**5: (power series)
 5 loops, best of 3: 4.12 ms per loop
 fp**5: (polynomial)
 5 loops, best of 3: 2.52 ms per loop
 --
 f**8: (power series)
 5 loops, best of 3: 6.47 ms per loop
 fp**8: (polynomial)
 5 loops, best of 3: 51.5 ms per loop
 --
 f**11: (power series)
 5 loops, best of 3: 10.8 ms per loop
 fp**11: (polynomial)
 5 loops, best of 3: 652 ms per loop
 --

 Multivariate Power Series Ring in t0, t1, t2, t3 over Rational Field
 f**2: (power series)
 5 loops, best of 3: 1 ms per loop
 fp**2: (polynomial)
 5 loops, best of 3: 44.8 µs per loop
 --
 f**5: (power series)
 5 loops, best of 3: 8.68 ms per loop
 fp**5: (polynomial)
 5 loops, best of 3: 4.12 ms per loop
 --
 f**8: (power series)
 5 loops, best of 3: 26.4 ms per loop
 fp**8: (polynomial)
 5 loops, best of 3: 119 ms per loop
 --
 f**11: (power series)
 5 loops, best of 3: 58.4 ms per loop
 fp**11: (polynomial)
 5 loops, best of 3: 2.28 s per loop
 --

 Multivariate Power Series Ring in t0, t1, t2, t3 over Finite Field of size
 11
 f**2: (power series)
 5 loops, best of 3: 480 µs per loop
 fp**2: (polynomial)
 5 loops, best of 3: 10.4 µs per loop
 --
 f**5: (power series)
 5 loops, best of 3: 2.49 ms per loop
 fp**5: (polynomial)
 5 loops, best of 3: 547 µs per loop
 --
 f**8: (power series)
 5 loops, best of 3: 4.23 ms per loop
 fp**8: (polynomial)
 5 loops, best of 3: 11 ms per loop
 --
 f**11: (power series)
 5 loops, best of 3: 8.6 ms per loop
 fp**11: (polynomial)
 5 loops, best of 3: 190 ms per loop
 --

 Multivariate Power Series Ring in t0, t1, t2, t3 over Finite Field of size
 65537
 f**2: (power series)
 5 loops, best of 3: 614 µs per loop
 fp**2: (polynomial)
 5 loops, best of 3: 18.8 µs per loop
 --
 f**5: (power series)
 5 loops, best of 3: 4.37 ms per loop
 fp**5: (polynomial)
 5 loops, best of 3: 3.13 ms per loop
 --
 f**8: (power series)
 5 loops, best of 3: 16.5 ms per loop
 fp**8: (polynomial)
 5 loops, best of 3: 231 ms per loop
 --
 f**11: (power series)
 5 loops, best of 3: 29.7 ms per loop
 fp**11: (polynomial)
 5 loops, best of 3: 4.85 s per loop
 --
 }}}


 == Timings for ``f**(2**n)`` v.s. iterated square-and-truncate for
 ``fp**(2**n)`` ==

 Here is a simple special case:  for a power series `f` with non-zero
 constant coefficient, the total-degree precision of `f**k` is equal to the
 total-degree precision of `f` for all `k`.  Below is a function which uses
 polynomial arithmetic to compute coefficients of `fp**k` up to given total
 degree for `k = 2^n`, where `fp = f.polynomial()`.

 {{{
 sage: def tot_deg_truncate(g,d):
     return sum(g.monomial_coefficient(m)*m for m in [x for x in
 g.monomials() if x.degree() < d])
 ....:

 sage: def pow_recursive(g,k,d):
     if k == 2:
         return tot_deg_truncate(g**2,d)
     else:
         return pow_recursive(tot_deg_truncate(g**2,d),k/2,d)
 ....:

 sage: R = PowerSeriesRing(ZZ,4,'t')
 sage: f = 1 + R.random_element(prec=10,bound=20)
 sage: f.constant_coefficient()
 1
 sage: fp = f.polynomial()
 }}}

 Check that ``pow_recursive`` gets the right answer:
 {{{
 sage: f**16 - pow_recursive(fp,16,10)
 0 + O(t0, t1, t2, t3)^10
 sage: f**64 - pow_recursive(fp,64,10)
 0 + O(t0, t1, t2, t3)^10
 }}}

 Compare timings; although the polynomial arithmetic is faster, they are on
 roughly the same order of magnitude.
 {{{
 sage: %timeit f**16
 125 loops, best of 3: 4.8 ms per loop
 sage: %timeit pow_recursive(fp,16,10)
 625 loops, best of 3: 1.18 ms per loop

 sage: %timeit f**64
 125 loops, best of 3: 7.21 ms per loop
 sage: %timeit pow_recursive(fp,64,10)
 125 loops, best of 3: 1.76 ms per loop
 }}}

 Increasing the precision exaggerates the difference:

 {{{
 sage: f = 1 + R.random_element(prec=30,bound=20)
 sage: f.constant_coefficient()
 1
 sage: fp = f.polynomial()
 sage: f**16 - pow_recursive(fp,16,30)
 0 + O(t0, t1, t2, t3)^30
 sage: f**64 - pow_recursive(fp,64,30)
 0 + O(t0, t1, t2, t3)^30

 sage: %timeit f**16
 25 loops, best of 3: 15.9 ms per loop
 sage: %timeit pow_recursive(fp,16,30)
 125 loops, best of 3: 1.81 ms per loop

 sage: %timeit f**64
 25 loops, best of 3: 23.9 ms per loop
 sage: %timeit pow_recursive(fp,64,30)
 125 loops, best of 3: 2.65 ms per loop
 }}}

-- 
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/1956#comment:49>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica, 
and MATLAB

-- 
You received this message because you are subscribed to the Google Groups 
"sage-trac" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/sage-trac?hl=en.

Reply via email to