Come posso convertire in modo efficiente una matrice sparsa scipy in una matrice sparsa sympy?

Aug 19 2020

Ho una matrice A con le seguenti proprietà.

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

A ha questo contenuto.

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

Ora sto cercando di creare una matrice sparsa sympy da questa matrice sparsa scipy.

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

Ma ricevo questo messaggio di errore.

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

Sono confuso perché questa matrice non ha voci logiche.

Grazie per qualsiasi aiuto!

Risposte

3 hpaulj Aug 19 2020 at 02:26

L'errore

Quando ricevi un errore che non capisci, dedica un po 'di tempo a guardare il traceback. O almeno mostracelo!

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 piena comprensione richiede la lettura del sympycodice, ma uno sguardo superficiale indica che sta cercando di gestire l'input come "matrice completa" e guarda le righe. L'errore non è il risultato dell'esecuzione di operazioni logiche sulle voci, ma di sympyun test logico sulla matrice sparsa. Sta cercando di controllare se la riga è vuota (quindi può saltarla).

SparseMatrixdocs potrebbe non essere il più chiaro, ma la maggior parte degli esempi mostra un dettato di punti o un array piatto di TUTTI i valori più la forma o un elenco irregolare di elenchi. Sospetto che stia cercando di trattare la tua matrice in quel modo, guardandola riga per riga.

Ma la riga di Mè essa stessa una matrice sparsa:

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

E provare a verificare se quella riga è vuota not rowproduce questo errore:

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

Quindi chiaramente SparseMatrixnon può gestire una scipy.sparsematrice così com'è (almeno non nel formato csro csc, e probabilmente non negli altri. Inoltre scipy.sparsenon è menzionato da nessuna parte nei SparseMatrixdocumenti!

dalla matrice densa

La conversione della matrice sparsa nel suo equivalente denso funziona:

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 un elenco di elenchi:

 SparseMatrix(M.A.tolist()) 

da dict

Il dokformato memorizza una matrice sparsa come un dict, che quindi può essere

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}

Che funziona bene come input:

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

Non so cosa sia più efficiente. Generalmente quando si lavora con sympynoi (o almeno io) non ci si preoccupa dell'efficienza. Basta farlo funzionare è sufficiente. L'efficienza è più rilevante numpy/scipyquando gli array possono essere grandi e l'uso dei metodi numpy compilati velocemente fa una grande differenza in termini di velocità.

Infine - numpye sympynon sono integrati. Ciò vale anche per le versioni sparse. sympyè costruito su Python, non numpy. Quindi gli input sotto forma di elenchi e dettami hanno più senso.

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

Dal punto di vista di qualcuno a cui piacciono le strutture di memoria efficienti, è come guardare nell'abisso. Ma funzionerà.

1 mathfux Aug 19 2020 at 02:12

Questa è una versione semplificata del tuo errore.

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

Quindi Aè una matrice sparsa con 6 elementi:

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

Richiamarlo SparseMatrix()restituisce lo stesso tipo di errore che hai. Potresti prima convertire Ain array numpy:

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