Mpizos Dimitris
Mpizos Dimitris

Reputation: 5001

dot product between numpy and sparse matrix

Assuming we have the following data:

import numpy as np
from scipy.sparse import csr_matrix 
A = csr_matrix([[1, 2, 0], [0, 0, 3], [4, 0, 5]]) # (3,3)
B = np.array([[1,2],[2,3],[3,3]]) # (2,3)

Then:

A.dot(B)
array([[ 5,  8],
       [ 9,  9],
       [19, 23]])

In the above we have sparce_matrix.dot(numpy_matrix) and output is a (3,2)`. But if I change:

B = np.array([[1,2,3],[2,3,3],[3,3,3]])

and:

B.dot(A)
array([[ <3x3 sparse matrix of type '<type 'numpy.int64'>'
     with 5 stored elements in Compressed Sparse Row format>,
    <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>,
    <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>],
   [ <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>,
    <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>,
    <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>],
   [ <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>,
    <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>,
    <3x3 sparse matrix of type '<type 'numpy.int64'>'
    with 5 stored elements in Compressed Sparse Row format>]], dtype=object)

So in the above we have a numpy_matrix.dot(sparce_matrix) and the expected output would be a (3,3) but its not.

It can be calculated by:

A.T.dot(B.T).T

but why is this behaviour of the output of B.dot(A) in the second case? Is there a way to do it without transpose it?

Upvotes: 2

Views: 1901

Answers (1)

hpaulj
hpaulj

Reputation: 231738

B.dot(A) is evaluated as B.dot(np.array(A)). It doesn't know A is sparse with it's own dot. A.dot(B) uses sparse method of A.

np.array(A) is different from A.toarray(). Try it.

Also experiment with np.dot(B, A). It may give A control, but I'm not sure. In any case don't assume numpy functions handle sparse matrices right. If they delegate to sparse methods it's ok. But test.


In [744]: np.array(A)
Out[744]: 
array(<3x3 sparse matrix of type '<class 'numpy.int32'>'
    with 5 stored elements in Compressed Sparse Row format>, dtype=object)
In [745]: _.shape
Out[745]: ()

So B.dot(A) is B dotted with a 'scalar' object. I'm almost surprised that dot allows that; I wonder if that's a relatively recent change.


For this small example, the transpose solution and the toarray one time about the same.

In [750]: timeit B.dot(A.A)
119 µs ± 319 ns per loop (mean ± std. dev. of 7 runs, 10000 loops each)
In [751]: timeit A.T.dot(B.T).T
102 µs ± 2.35 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)

Another way to force a sparse calculation - make B sparse

In [758]: Bs = sparse.csr_matrix(B)
In [759]: timeit (Bs*A)
211 µs ± 9.23 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)

Notice that the sparse * dense produces a dense result. It the sparse matrix is sparse enough it is still faster than the pure dense operation.

The relative times will vary with the sparsity of the sparse matrix, and also the size of the inputs and outputs.

If A is too large to turn into a dense array (either time or memory constraint), won't the either B or the result be too large as well? If A has many rows and few columns then B (in B.dot(A)) will have to have many columns. But if A has many columns instead, the result will have as many columns. So one way other you will have a large dense array, comparable to A.toarray() in size.

Upvotes: 1

Related Questions