#11589: faster zero matrix creation
------------------------------+---------------------------------------------
   Reporter:  malb            |          Owner:  jason, was                 
       Type:  enhancement     |         Status:  needs_review               
   Priority:  major           |      Milestone:  sage-4.7.2                 
  Component:  linear algebra  |       Keywords:                             
Work_issues:                  |       Upstream:  N/A                        
   Reviewer:                  |         Author:  Martin Albrecht, Simon King
     Merged:                  |   Dependencies:                             
------------------------------+---------------------------------------------
Changes (by SimonKing):

  * status:  new => needs_review


Comment:

 I made all matrices accept "None" as an argument.

 I tried to determine the threshold for copying versus creating a zero
 matrix, using the following test function:
 {{{
 sage: def tests(R):
 ....:     BetterCopy = []
 ....:     for d in srange(10,511,20):
 ....:         MS = MatrixSpace(R,d)
 ....:         MS._copy_zero = True
 ....:         globals()['d'] = d
 ....:         globals()['R'] = R
 ....:         TCopy = timeit.eval("M = matrix(R,d,d)",number=1000)
 ....:         MS._copy_zero = False
 ....:         TNoCopy = timeit.eval("M = matrix(R,d,d)",number=1000)
 ....:         if TCopy.stats[3] < TNoCopy.stats[3]:
 ....:             BetterCopy.append((d,TCopy,TNoCopy))
 ....:     return BetterCopy
 ....:
 }}}

 It turns out that with both patches applied, copying is not a good idea
 over GF(2) and over the rationals:
 {{{
 sage: tests(GF(2))
 []
 sage: tests(QQ)
 []
 }}}
 Note that I observed that the memory consumption for the test over the
 rationals constantly increased: Perhaps there is a memory leak.

 However, in all other cases I studied, copying was better, for matrices of
 up to 30 rows and columns:
 {{{
 sage: tests(GF(3))
 [(10, 1000 loops, best of 3: 48.2 µs per loop, 1000 loops, best of 3: 49
 µs per loop), (30, 1000 loops, best of 3: 50 µs per loop, 1000 loops, best
 of 3: 50.9 µs per loop)]
 sage: tests(GF(1019))
 [(10, 1000 loops, best of 3: 48.3 µs per loop, 1000 loops, best of 3: 49.2
 µs per loop)]
 sage: tests(GF(625,'a'))
 [(10, 1000 loops, best of 3: 45.1 µs per loop, 1000 loops, best of 3: 63.1
 µs per loop), (30, 1000 loops, best of 3: 62.9 µs per loop, 1000 loops,
 best of 3: 68 µs per loop), (230, 1000 loops, best of 3: 1.15 ms per loop,
 1000 loops, best of 3: 367 µs per loop), (250, 1000 loops, best of 3: 1.35
 ms per loop, 1000 loops, best of 3: 423 µs per loop), (270, 1000 loops,
 best of 3: 1.71 ms per loop, 1000 loops, best of 3: 618 µs per loop),
 (290, 1000 loops, best of 3: 1.95 ms per loop, 1000 loops, best of 3: 701
 µs per loop), (310, 1000 loops, best of 3: 2.23 ms per loop, 1000 loops,
 best of 3: 791 µs per loop), (330, 1000 loops, best of 3: 2.52 ms per
 loop, 1000 loops, best of 3: 883 µs per loop), (350, 1000 loops, best of
 3: 2.82 ms per loop, 1000 loops, best of 3: 988 µs per loop)]
 sage: tests(ZZ)
 [(10, 625 loops, best of 3: 48.6 µs per loop, 625 loops, best of 3: 56.5
 µs per loop), (30, 625 loops, best of 3: 111 µs per loop, 625 loops, best
 of 3: 118 µs per loop), (50, 625 loops, best of 3: 230 µs per loop, 625
 loops, best of 3: 235 µs per loop)]
 }}}

 Therefore, I modified the threshold in `_copy_zero` a bit. There is no
 special case for `Matrix_modn_dense`, but for `Matrix_rational_dense`.

 Here are timings similar to the ones you did above (adding another test
 case).

 With both patches:
 {{{
 sage: for n in (50,500,1000): timeit('A=matrix(GF(2),%d,%d)'%(n,n))
 ....:
 625 loops, best of 3: 105 µs per loop
 625 loops, best of 3: 108 µs per loop
 625 loops, best of 3: 116 µs per loop
 sage: for n in (50,500,1000): timeit('A=matrix(GF(3),%d,%d)'%(n,n))
 ....:
 625 loops, best of 3: 103 µs per loop
 625 loops, best of 3: 403 µs per loop
 625 loops, best of 3: 1.3 ms per loop
 sage: for n in (50,500,1000): timeit('A=matrix(GF(625,"a"),%d,%d)'%(n,n))
 ....:
 625 loops, best of 3: 198 µs per loop
 125 loops, best of 3: 1.97 ms per loop
 125 loops, best of 3: 7.29 ms per loop
 }}}

 Without patches:
 {{{
 sage: for n in (50,500,1000): timeit('A=matrix(GF(2),%d,%d)'%(n,n))
 ....:
 625 loops, best of 3: 108 µs per loop
 625 loops, best of 3: 116 µs per loop
 625 loops, best of 3: 137 µs per loop
 sage: for n in (50,500,1000): timeit('A=matrix(GF(3),%d,%d)'%(n,n))
 ....:
 625 loops, best of 3: 103 µs per loop
 625 loops, best of 3: 566 µs per loop
 125 loops, best of 3: 2.46 ms per loop
 sage: for n in (50,500,1000): timeit('A=matrix(GF(625,"a"),%d,%d)'%(n,n))
 ....:
 625 loops, best of 3: 225 µs per loop
 125 loops, best of 3: 5.95 ms per loop
 25 loops, best of 3: 22.9 ms per loop
 }}}

 So, with both patches applied, the timings consistently improve.

 '''__Reviewing__'''

 I am not sure how the reviewing should be done, as we are both authors. Do
 you think it is ok if I review the first patch and you review the second
 patch? I need to run all long doctests, though; so far, I only did the
 tests in sage/matrix.

 '''__TODO__'''

 On a different ticket, the potential memory leak for rational dense
 matrices should be studied.

 I recall that in some arithmetic routines of matrices, zero matrices are
 created at the beginning. I am not sure whether that is done "the official
 way" or by ''always'' copying a zero matrix, but I think it was the
 latter. That might be worth while to fix as well.

-- 
Ticket URL: <http://trac.sagemath.org/sage_trac/ticket/11589#comment:9>
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