¿Cómo puedo convertir de manera eficiente una matriz dispersa scipy en una matriz dispersa sympy?

Aug 19 2020

Tengo una matriz A con las siguientes propiedades.

<1047x1047 sparse matrix of type '<class 'numpy.float64'>'
    with 888344 stored elements in Compressed Sparse Column format>

A tiene este contenido.

array([[ 1.00000000e+00, -5.85786642e-17, -3.97082034e-17, ...,
         0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
       [ 6.82195979e-17,  1.00000000e+00, -4.11166786e-17, ...,
         0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
       [-4.98202332e-17,  1.13957868e-17,  1.00000000e+00, ...,
         0.00000000e+00,  0.00000000e+00,  0.00000000e+00],
       ...,
       [ 4.56847824e-15,  1.32261454e-14, -7.22890998e-15, ...,
         1.00000000e+00,  0.00000000e+00,  0.00000000e+00],
       [-9.11597396e-15, -2.28796167e-14,  1.26624823e-14, ...,
         0.00000000e+00,  1.00000000e+00,  0.00000000e+00],
       [ 1.80765584e-14,  1.93779820e-14, -1.36520100e-14, ...,
         0.00000000e+00,  0.00000000e+00,  1.00000000e+00]])

Ahora estoy tratando de crear una matriz dispersa y simple a partir de esta matriz dispersa.

from sympy.matrices import SparseMatrix
A = SparseMatrix(A)

Pero recibo este mensaje de error.

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().

Estoy confundido porque esta matriz no tiene entradas lógicas.

¡Gracias por cualquier ayuda!

Respuestas

3 hpaulj Aug 19 2020 at 02:26

El error

Cuando reciba un error que no comprenda, tómese un poco de tiempo para ver el rastreo. ¡O al menos muéstranoslo!

In [288]: M = sparse.random(5,5,.2, 'csr')                                                           

In [289]: M                                                                                          
Out[289]: 
<5x5 sparse matrix of type '<class 'numpy.float64'>'
    with 5 stored elements in Compressed Sparse Row format>

In [290]: print(M)                                                                                   
  (1, 1)    0.17737340878962138
  (2, 2)    0.12362174819457106
  (2, 3)    0.24324155883057885
  (3, 0)    0.7666429046432961
  (3, 4)    0.21848551209470246

In [291]: SparseMatrix(M)                                                                            
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-291-cca56ea35868> in <module>
----> 1 SparseMatrix(M)

/usr/local/lib/python3.6/dist-packages/sympy/matrices/sparse.py in __new__(cls, *args, **kwargs)
    206             else:
    207                 # handle full matrix forms with _handle_creation_inputs
--> 208                 r, c, _list = Matrix._handle_creation_inputs(*args)
    209                 self.rows = r
    210                 self.cols = c

/usr/local/lib/python3.6/dist-packages/sympy/matrices/matrices.py in _handle_creation_inputs(cls, *args, **kwargs)
   1070                             if 0 in row.shape:
   1071                                 continue
-> 1072                         elif not row:
   1073                             continue
   1074 

/usr/local/lib/python3.6/dist-packages/scipy/sparse/base.py in __bool__(self)
    281             return self.nnz != 0
    282         else:
--> 283             raise ValueError("The truth value of an array with more than one "
    284                              "element is ambiguous. Use a.any() or a.all().")
    285     __nonzero__ = __bool__

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().

Una comprensión completa requiere leer el sympycódigo, pero una mirada superficial indica que está tratando de manejar su entrada como "matriz completa" y mira las filas. El error no es el resultado de que realice operaciones lógicas en las entradas, sino que sympyestá haciendo una prueba lógica en su matriz dispersa. Está tratando de verificar si la fila está vacía (para que pueda omitirla).

SparseMatrixEs posible que los documentos no sean los más claros, pero la mayoría de los ejemplos muestran un dictado de puntos o una matriz plana de TODOS los valores más la forma, o una lista irregular de listas. Sospecho que está tratando de tratar su matriz de esa manera, mirándola fila por fila.

Pero la fila de Mes en sí misma una matriz dispersa:

In [295]: [row for row in M]                                                                         
Out[295]: 
[<1x5 sparse matrix of type '<class 'numpy.float64'>'
    with 0 stored elements in Compressed Sparse Row format>,
 <1x5 sparse matrix of type '<class 'numpy.float64'>'
    with 1 stored elements in Compressed Sparse Row format>,
...]

Y tratar de verificar si esa fila está vacía not rowproduce este error:

In [296]: not [row for row in M][0]                                                                  
...
ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all().

Claramente SparseMatrix, no se puede manejar una scipy.sparsematriz tal como está (al menos no en el formato csro csc, y probablemente no en los demás. ¡Además, scipy.sparseno se menciona en ninguna parte de los SparseMatrixdocumentos!

de una matriz densa

La conversión de la matriz dispersa a su equivalente denso funciona:

In [297]: M.A                                                                                        
Out[297]: 
array([[0.        , 0.        , 0.        , 0.        , 0.        ],
       [0.        , 0.17737341, 0.        , 0.        , 0.        ],
       [0.        , 0.        , 0.12362175, 0.24324156, 0.        ],
       [0.7666429 , 0.        , 0.        , 0.        , 0.21848551],
       [0.        , 0.        , 0.        , 0.        , 0.        ]])

In [298]: SparseMatrix(M.A)                                                                          
Out[298]: 
⎡        0                  0                  0                  0                  0        ⎤
...⎦

O una lista de listas:

 SparseMatrix(M.A.tolist()) 

de dict

El dokformato almacena una matriz dispersa como a dict, que luego puede ser

In [305]: dict(M.todok())                                                                            
Out[305]: 
{(3, 0): 0.7666429046432961,
 (1, 1): 0.17737340878962138,
 (2, 2): 0.12362174819457106,
 (2, 3): 0.24324155883057885,
 (3, 4): 0.21848551209470246}

Que funciona bien como entrada:

SparseMatrix(5,5,dict(M.todok()))

No sé qué es lo más eficiente. Generalmente, cuando trabajamos con sympynosotros (o al menos yo) no nos preocupamos por la eficiencia. Solo haz que funcione es suficiente. La eficiencia es más relevante numpy/scipycuando las matrices pueden ser grandes, y el uso de métodos numéricos compilados rápidamente hace una gran diferencia en la velocidad.

Finalmente - numpyy sympyno están integrados. Eso se aplica también a las versiones dispersas. sympyestá construido en Python, no numpy. Así que las entradas en forma de listas y dictados tienen más sentido.

1 CJR Aug 19 2020 at 01:24
from sympy.matrices import SparseMatrix
import scipy.sparse as sps

A = sps.random(100, 10, format="dok")
B = SparseMatrix(100, 10, dict(A.items()))

Desde la perspectiva de alguien a quien le gustan las estructuras de memoria eficientes, esto es como mirar al abismo. Pero funcionará.

1 mathfux Aug 19 2020 at 02:12

Esta es una versión simplificada de su error.

from scipy import sparse
row = np.array([0, 0, 1, 2, 2, 2])
col = np.array([0, 2, 2, 0, 1, 2])
data = np.array([1, 2, 3, 4, 5, 6])
A = sparse.csc_matrix((data, (row, col)), shape=(3, 3))

También lo Aes una matriz dispersa con 6 elementos:

<3x3 sparse matrix of type '<class 'numpy.intc'>'
    with 6 stored elements in Compressed Sparse Column format>

Invocarlo SparseMatrix()devuelve el mismo tipo de error que tiene. Es posible Aque desee convertir primero a una matriz numpy:

>>> SparseMatrix(A.todense())
Matrix([
[1, 0, 2],
[0, 0, 3],
[4, 5, 6]])