Comment puis-je convertir efficacement une matrice scipy sparse en une matrice sympy sparse?

Aug 19 2020

J'ai une matrice A avec les propriétés suivantes.

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

A a ce contenu.

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]])

J'essaye maintenant de créer une matrice clairsemée sympy à partir de cette matrice clairsemée scipy.

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

Mais j'obtiens ce message d'erreur.

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

Je suis confus car cette matrice n'a pas d'entrées logiques.

Merci pour toute aide!

Réponses

3 hpaulj Aug 19 2020 at 02:26

L'erreur

Lorsque vous obtenez une erreur que vous ne comprenez pas, prenez un peu de temps pour examiner la trace. Ou du moins nous le montrer!

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().

Une compréhension complète nécessite la lecture du sympycode, mais un coup d'œil rapide indique qu'il essaie de gérer votre entrée comme une «matrice complète» et examine les lignes. L'erreur n'est pas le résultat de l'exécution d'opérations logiques sur les entrées, mais d' sympyun test logique sur votre matrice creuse. Il essaie de vérifier si la ligne est vide (afin de pouvoir l'ignorer).

SparseMatrixLes documents ne sont peut-être pas les plus clairs, mais la plupart des exemples montrent soit un dict de points, soit un tableau plat de TOUTES les valeurs plus la forme, soit une liste irrégulière de listes. Je soupçonne qu'il essaie de traiter votre matrice de cette façon, en la regardant ligne par ligne.

Mais la rangée de Mest elle-même une matrice clairsemée:

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>,
...]

Et essayer de vérifier si cette ligne est vide not rowproduit cette erreur:

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().

Il est donc clair que SparseMatrixje ne peux pas gérer une scipy.sparsematrice telle quelle (du moins pas au format csrou csc, et probablement pas les autres. Plus scipy.sparsen'est mentionné nulle part dans la SparseMatrixdocumentation!

à partir d'un tableau dense

La conversion de la matrice clairsemée en son équivalent dense fonctionne:

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        ⎤
...⎦

Ou une liste de listes:

 SparseMatrix(M.A.tolist()) 

de dict

Le dokformat stocke une matrice creuse sous forme de a dict, qui peut alors être

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}

Ce qui fonctionne très bien comme entrée:

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

Je ne sais pas ce qui est le plus efficace. En général, lorsque vous travaillez avec sympynous (ou du moins moi), ne vous inquiétez pas de l'efficacité. Le faire fonctionner suffit. L'efficacité est plus pertinente numpy/scipylà où les tableaux peuvent être volumineux, et l'utilisation des méthodes numpy compilées rapidement fait une grande différence en termes de vitesse.

Enfin - numpyet sympyne sont pas intégrés. Cela vaut également pour les versions clairsemées. sympyest construit sur Python, pas numpy. Ainsi, les entrées sous forme de listes et de dictionnaires ont plus de sens.

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()))

Du point de vue de quelqu'un qui aime les structures de mémoire efficaces, c'est comme regarder dans l'abîme. Mais cela fonctionnera.

1 mathfux Aug 19 2020 at 02:12

Ceci est une version simplifiée de votre erreur.

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))

Ainsi Aest une matrice clairsemée avec 6 éléments:

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

L'appeler SparseMatrix()renvoie le même type d'erreur que vous. Vous voudrez peut-être d'abord convertir Aen tableau numpy:

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