Как я могу эффективно преобразовать scipy разреженную матрицу в sympy sparse matrix?

Aug 19 2020

У меня есть матрица A со следующими свойствами.

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

A имеет это содержание.

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

Теперь я пытаюсь создать симпатичную разреженную матрицу из этой scipy разреженной матрицы.

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

Но я получаю это сообщение об ошибке.

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

Я смущен, потому что в этой матрице нет логических записей.

Спасибо за любую помощь!

Ответы

3 hpaulj Aug 19 2020 at 02:26

Ошибка

Когда вы получаете сообщение об ошибке, которое не понимаете, потратьте немного времени на то, чтобы просмотреть трассировку. Или хотя бы покажите нам!

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

Для полного понимания требуется прочитать sympyкод, но беглый взгляд показывает, что он пытается обрабатывать ваш ввод как «полную матрицу» и просматривает строки. Ошибка не является результатом выполнения вами логических операций с записями, а sympyявляется логической проверкой вашей разреженной матрицы. Он пытается проверить, пуста ли строка (чтобы ее можно было пропустить).

SparseMatrixдокументы могут быть не самыми ясными, но в большинстве примеров либо показан список точек, либо плоский массив ВСЕХ значений плюс форма, либо рваный список списков. Я подозреваю, что он пытается так относиться к вашей матрице, глядя на нее строка за строкой.

Но строка Mсама по себе является разреженной матрицей:

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

И попытка проверить, пуста ли эта строка, not rowвызывает эту ошибку:

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

Так что явно SparseMatrixне может обрабатывать scipy.sparseматрицу как есть (по крайней мере, не в формате csrили csc, и, вероятно, не в других. Плюс scipy.sparseнигде не упоминается в SparseMatrixдокументации!

из плотного массива

Преобразование разреженной матрицы в ее плотный эквивалент действительно работает:

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

Или список списков:

 SparseMatrix(M.A.tolist()) 

из диктата

dokФормат хранит разреженную матрицу в виде dict, который затем может быть

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}

Что отлично работает в качестве ввода:

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

Я не знаю, что наиболее эффективно. Обычно при работе с sympyнами (или, по крайней мере, я) не беспокоимся об эффективности. Достаточно просто заставить его работать. Эффективность более важна numpy/scipyтам, где массивы могут быть большими, и использование методов быстрой компиляции numpy имеет большое значение в скорости.

Наконец - так numpyи sympyне интегрированы. Это касается и разреженных версий. sympyпостроен на Python, а не numpy. Так что ввод в виде списков и dicts имеет наибольший смысл.

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

С точки зрения человека, которому нравятся эффективные структуры памяти, это все равно что смотреть в бездну. Но это сработает.

1 mathfux Aug 19 2020 at 02:12

Это упрощенная версия вашей ошибки.

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

Итак, Aразреженная матрица с 6 элементами:

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

Его вызов SparseMatrix()возвращает ошибку того же типа, что и у вас. Вы можете Aсначала преобразовать в массив numpy:

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