Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

constructing matrices is very slow #18231

Closed
videlec opened this issue Apr 16, 2015 · 32 comments
Closed

constructing matrices is very slow #18231

videlec opened this issue Apr 16, 2015 · 32 comments

Comments

@videlec
Copy link
Contributor

videlec commented Apr 16, 2015

The problem is in the code which looks like the following

def test1():
    K = QuadraticField(2)
    for _ in range(100):
        i = randint(2,3)
        j = randint(2,3)
        m = matrix(K, i, j, range(i*j))

It causes the various matrix spaces to rebuilt almost each time! If we compare with

def test2():
    K = QuadraticField(2)
    M = {(i,j): MatrixSpace(K,i,j) for i in range(2,4) for j in range(2,4)}
    for _ in range(100):
        i = randint(2,3)
        j = randint(2,3)
        m = M[i,j](range(i*j))

then we got

sage: %timeit test1()
10 loops, best of 3: 55 ms per loop
sage: %timeit test2()
100 loops, best of 3: 6.49 ms per loop

This is one of the reasons why the creation of Polyhedron is so slow (see #18213). (This has now improved since the creation of the ticket)

In the branch we:

  • replace some old Cython code in matrix_generic_dense and matrix0 and do some optimization
  • move the function _get_matrix_class in MatrixSpace out of the class (as it depends only on the base ring and the sparseness)
  • In the class MatrixSpace the argument __matrix_class is now _matrix_class. That way, it is possible in other part of Sage to optimize the creation of matrices by calling directly
sage: M = MatrixSpace(ZZ,3,2)
sage: M._matrix_class([1,5,3,-1,2,3], coerce=False, copy=False)
  • we simplify the main element constructor of matrix, namely MatrixSpace.matrix and gain a significant speedup.

follow up: #18258

CC: @nathanncohen

Component: linear algebra

Issue created by migration from https://trac.sagemath.org/ticket/18231

@videlec videlec added this to the sage-6.7 milestone Apr 16, 2015
@videlec
Copy link
Contributor Author

videlec commented Apr 18, 2015

Author: Vincent Delecroix

@videlec
Copy link
Contributor Author

videlec commented Apr 18, 2015

Commit: c1c7034

@videlec
Copy link
Contributor Author

videlec commented Apr 18, 2015

comment:1

I only did some cleanup in matrix_space.py and matrix_generic_dense.pyx. And there is a substantial gain in speed! The tiny modification in #18213 to NumberField.__cmp__ actually makes a much bigger difference.

I did not solve the problem of the matrix space being rebuilt every time.


New commits:

f6a87edTrac 18231: better code in matrices
d834354Trac 18231: move _get_matrix_class out of the class
d52a63dTrac 18231: constructor + __matrix_class -> _matrix_class
ecd6e27Trac 18231: removed unused import
a23d09cTrac 18231: get rid of a one year old deprecation
6e9aab1Trac 18231: remove a useless function
95c9dfeTrac 18231: set .__call__() as an alias of .matrix()
6dbf25eTrac 18231: rewrite .matrix()
c1c7034Trac 18231: fix dict_to_list

@videlec
Copy link
Contributor Author

videlec commented Apr 18, 2015

Branch: u/vdelecroix/18231

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 18, 2015

Branch pushed to git repo; I updated commit sha1. New commits:

da310dbTrac 18231: typo

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 18, 2015

Changed commit from c1c7034 to da310db

@nathanncohen
Copy link
Mannequin

nathanncohen mannequin commented Apr 19, 2015

comment:3

Hello Vincent,

In this branch you create functions, remove others, and change a lot of code. There is still a lot of guessing to do on the reviewer's part.

It would be really cool if you could be more verbose about what you do. Really.

I began to read the first commit, and I have several more specific questions:

  • You modify a function which takes a MonoidElement as input, which you immeditely cast with cdef Matrix right = _right. Either it is a matrix and it should not read MonoidElement in the first place, or it is not a Matrix and you cannot cast it?..

  • This is cool and everything to gain miliseconds, but that's not a good way to write public code

    -        matrix.Matrix.__init__(self, parent)
    +        # the four lines below avoid the following call
    +        # matrix.Matrix.__init__(self, parent)
    +        self._parent = parent
    +        res._ncols  = parent.ncols()
    +        res._nrows  = parent.nrows()
    +        R = res._base_ring = parent.base_ring()
    

    If the main constructor does not do what you want ty to change it. What if somebody adds a new parameter to matrix? The code has to be copy/pasted in every single function that did not want to call __init__?

  • Why?

    
         cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):
    -        Py_DECREF(<object>PyList_GET_ITEM(self._entries, i*self._ncols + j))
    -        Py_INCREF(value)
    -        PyList_SET_ITEM(self._entries, i*self._ncols + j, value)
    +        self._entries[i*self._ncols + j] = value
     
         cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):
    -        return <object>PyList_GET_ITEM(self._entries, i*self._ncols + j)
    +        return self._entries[i*self._ncols + j]
    

Nathann

@videlec

This comment has been minimized.

@videlec
Copy link
Contributor Author

videlec commented Apr 19, 2015

comment:4

Hello Nathann,

Thanks for having a look.

Replying to @nathanncohen:

In this branch you create functions, remove others, and change a lot of code. There is still a lot of guessing to do on the reviewer's part.

It would be really cool if you could be more verbose about what you do. Really.

I made a big effort to separate the commit at least! It is now in the description of the ticket.

I began to read the first commit, and I have several more specific questions:

  • You modify a function which takes a MonoidElement as input, which you immeditely cast with cdef Matrix right = _right. Either it is a matrix and it should not read MonoidElement in the first place, or it is not a Matrix and you cannot cast it?..

No choice. This is a cdef function defined in structure.element.Element and you can not change the signature in children classes. I thought it was more readable this way.

  • This is cool and everything to gain miliseconds, but that's not a good way to write public code
    If the main constructor does not do what you want ty to change it. What if somebody adds a new parameter to matrix? The code has to be copy/pasted in every single function that did not want to call __init__?

All right. But then there is something wrong in the docstring of matrix0.Matrix

    def __init__(...):
        ...
        Subclasses of ``Matrix`` can safely skip calling
        ``Matrix.__init__`` provided they take care of initializing these
        attributes themselves.
        ...
  • Why?

All this was just old Cython code at the time Cython was not able to understand l[i] when l is a cdef list.

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 19, 2015

Branch pushed to git repo; I updated commit sha1. New commits:

88525b8Trac 18231: fix documentation

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 19, 2015

Changed commit from da310db to 88525b8

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 19, 2015

Branch pushed to git repo; I updated commit sha1. New commits:

715f49eTrac 18231: fix doctests

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Apr 19, 2015

Changed commit from 88525b8 to 715f49e

@nathanncohen
Copy link
Mannequin

nathanncohen mannequin commented Apr 19, 2015

comment:7

Yoooooo !

I made a big effort to separate the commit at least! It is now in the description of the ticket.

Thanks

No choice. This is a cdef function defined in structure.element.Element and you can not change the signature in children classes. I thought it was more readable this way.

Oh, okay!

All right. But then there is something wrong in the docstring of matrix0.Matrix

Hmmmm.... Can you at least observe the difference? All that this __init__ function does is precisely what you do. Can you measure this function call when your function copies lists around? O_o

All this was just old Cython code at the time Cython was not able to understand l[i] when l is a cdef list.

Okay!

Nathann

@nathanncohen
Copy link
Mannequin

nathanncohen mannequin commented Apr 19, 2015

comment:8

Okay, I give up. Sorry, I just don't know these classes enough, I barely ever used them, I would do a terrible job as a reviewer.

Nathann

@videlec

This comment has been minimized.

@fchapoton
Copy link
Contributor

comment:10

needs rebase, does not apply

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Aug 13, 2015

Changed commit from 715f49e to d4fc6cd

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Aug 13, 2015

Branch pushed to git repo; I updated commit sha1. This was a forced push. Last 10 new commits:

2d55497Trac 18231: constructor + __matrix_class -> _matrix_class
5ac117fTrac 18231: removed unused import
fcd06faTrac 18231: get rid of a one year old deprecation
b7bf1aaTrac 18231: remove a useless function
12c9d0eTrac 18231: set .__call__() as an alias of .matrix()
d59a9dbTrac 18231: rewrite .matrix()
46d8b6bTrac 18231: fix dict_to_list
aa7ddb2Trac 18231: typo
03fe3b1Trac 18231: fix documentation
d4fc6cdTrac 18231: fix doctests

@videlec
Copy link
Contributor Author

videlec commented Aug 13, 2015

Dependencies: #18231

@videlec
Copy link
Contributor Author

videlec commented Aug 13, 2015

Changed dependencies from #18231 to #19026

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Aug 13, 2015

Branch pushed to git repo; I updated commit sha1. This was a forced push. Last 10 new commits:

268cfc3Trac 18231: move _get_matrix_class out of the class
66e5068Trac 18231: constructor + __matrix_class -> _matrix_class
c9a82f2Trac 18231: removed unused import
321d073Trac 18231: get rid of a one year old deprecation
6111c37Trac 18231: remove a useless function
cb6b82dTrac 18231: set .__call__() as an alias of .matrix()
2dd0d09Trac 18231: rewrite .matrix()
00dd4acTrac 18231: fix dict_to_list
07ba681Trac 18231: fix documentation
742de47Trac 18231: fix doctests

@sagetrac-git
Copy link
Mannequin

sagetrac-git mannequin commented Aug 13, 2015

Changed commit from d4fc6cd to 742de47

@videlec
Copy link
Contributor Author

videlec commented Aug 13, 2015

comment:16

I moved the first commit to another ticket (#19026).

@fchapoton
Copy link
Contributor

comment:17

needs rebase

@jplab

This comment has been minimized.

@mkoeppe
Copy link
Member

mkoeppe commented Feb 19, 2022

Changed author from Vincent Delecroix to none

@mkoeppe
Copy link
Member

mkoeppe commented Feb 19, 2022

comment:19

Outdated, this was fixed in #24742.

sage: %timeit test1()
3.91 ms ± 47.3 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)
sage: %timeit test2()
3.03 ms ± 19.4 µs per loop (mean ± std. dev. of 7 runs, 100 loops each)

@mkoeppe mkoeppe removed this from the sage-6.7 milestone Feb 19, 2022
@videlec
Copy link
Contributor Author

videlec commented Feb 19, 2022

Changed dependencies from #19026 to none

@videlec
Copy link
Contributor Author

videlec commented Feb 19, 2022

Changed branch from u/vdelecroix/18231 to none

@videlec
Copy link
Contributor Author

videlec commented Feb 19, 2022

Changed commit from 742de47 to none

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants