Reputation: 37
I'm having trouble with the matrix exponential calculation using scipy.linalg.expm.
The code is like this:
import numpy as np
import scipy.sparse as sp
import scipy.linalg as linA
from scipy.sparse.linalg import expm
linA.expm(sp.kron(np.arange(N), np.identity(2)))
where, N can be any integer, number is an NxN diagonal matrix with diagonal elements diag{0,1,2, ... N-1}, and sp.kron is a kronecker product in scipy.sparse. This works fine only for N<6. When I try to run the code with N=6
linA.expm(sp.kron(np.arange(6), np.identity(2)))
This should be a fairly simple code, but I don't know why it is giving the following error:
NotImplementedError Traceback (most recent call last)
<ipython-input-168-b0f10db2dd69> in <module>
----> 1 linA.expm(sp.kron(number(6), identity(2)) )
~\anaconda3\lib\site-packages\scipy\linalg\matfuncs.py in expm(A)
253 # Input checking and conversion is provided by sparse.linalg.expm().
254 import scipy.sparse.linalg
--> 255 return scipy.sparse.linalg.expm(A)
256
257
~\anaconda3\lib\site-packages\scipy\sparse\linalg\matfuncs.py in expm(A)
590 [ 0. , 0. , 20.08553692]])
591 """
--> 592 return _expm(A, use_exact_onenorm='auto')
593
594
~\anaconda3\lib\site-packages\scipy\sparse\linalg\matfuncs.py in _expm(A, use_exact_onenorm)
675 if structure == UPPER_TRIANGULAR:
676 # Invoke Code Fragment 2.1.
--> 677 X = _fragment_2_1(X, h.A, s)
678 else:
679 # X = r_13(A)^(2^s) by repeated squaring.
~\anaconda3\lib\site-packages\scipy\sparse\linalg\matfuncs.py in _fragment_2_1(X, T, s)
811 lam_1 = scale * diag_T[k]
812 lam_2 = scale * diag_T[k+1]
--> 813 t_12 = scale * T[k, k+1]
814 value = _eq_10_42(lam_1, lam_2, t_12)
815 X[k, k+1] = value
~\anaconda3\lib\site-packages\scipy\sparse\bsr.py in __getitem__(self, key)
313
314 def __getitem__(self,key):
--> 315 raise NotImplementedError
316
317 def __setitem__(self,key,val):
NotImplementedError:
Upvotes: 0
Views: 538
Reputation: 231385
According to the traceback T[k, k+1]
doesn't work because T
is a bsr
format sparse matrix, which does not implement indexing. (coo
is a more common format that doesn't have this either).
Looking at the sp.kron
code, https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.kron.html
if (format is None or format == "bsr") and 2*B.nnz >= B.shape[0] * B.shape[1]:
# B is fairly dense, use BSR
A = csr_matrix(A,copy=True)
...
return bsr_matrix((data,A.indices,A.indptr), shape=output_shape)
So in sp.kron(number(N), np.identity(2))
In [251]: B = sparse.coo_matrix(np.identity(2))
In [252]: B
Out[252]:
<2x2 sparse matrix of type '<class 'numpy.float64'>'
with 2 stored elements in COOrdinate format>
In [253]: 2*B.nnz >= B.shape[0]*B.shape[1]
Out[253]: True
In [254]: sparse.kron(np.arange(4).reshape(2,2), np.identity(2))
Out[254]:
<4x4 sparse matrix of type '<class 'numpy.float64'>'
with 12 stored elements (blocksize = 2x2) in Block Sparse Row format>
In [258]: lg.expm(sparse.kron(np.identity(6), np.identity(2)))
/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/dsolve/linsolve.py:144: SparseEfficiencyWarning: spsolve requires A be CSC or CSR matrix format
warn('spsolve requires A be CSC or CSR matrix format',
/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/dsolve/linsolve.py:215: SparseEfficiencyWarning: spsolve is more efficient when sparse b is in the CSC matrix format
warn('spsolve is more efficient when sparse b '
Out[258]:
<12x12 sparse matrix of type '<class 'numpy.float64'>'
with 12 stored elements in Compressed Sparse Column format>
changing to csc
to get avoid this warning:
In [265]: lg.expm(sparse.kron(np.identity(6), np.identity(2)).tocsc())
Out[265]:
<12x12 sparse matrix of type '<class 'numpy.float64'>'
with 12 stored elements in Compressed Sparse Column format>
So simply providing a bsr
to expm
doesn't cause your error. Looks like we'd have to examine expm
what else happening. I looked at this function (and MATLAB's) years ago. It uses a pade
approximation that includes a inv
(which is spsolve(I,A)
). It's a complex function that tries different things, including different Pade
orders.
So you'll have to tell us more about this number
and the nature of the kron()
result. None of my guesses reproduce your error.
Correction, the traceback tells us that it detect that your matrix is upper triangular
:
if structure == UPPER_TRIANGULAR:
# Invoke Code Fragment 2.1.
X = _fragment_2_1(X, h.A, s)
so there's more code to trace through.
In any case doing the tocsc
conversion before passing the matrix to expm
might solve the problem:
lg.expm(sp.kron(...).tocsc())
In [268]: A = np.array([[1,2,3],[0,4,5],[0,0,6]])
In [269]: M = sparse.bsr_matrix(A)
In [270]: M
Out[270]:
<3x3 sparse matrix of type '<class 'numpy.int64'>'
with 6 stored elements (blocksize = 1x1) in Block Sparse Row format>
Your error:
In [271]: lg.expm(M)
/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/dsolve/linsolve.py:144: SparseEfficiencyWarning: spsolve requires A be CSC or CSR matrix format
warn('spsolve requires A be CSC or CSR matrix format',
/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/dsolve/linsolve.py:215: SparseEfficiencyWarning: spsolve is more efficient when sparse b is in the CSC matrix format
warn('spsolve is more efficient when sparse b '
Traceback (most recent call last):
File "<ipython-input-271-d1b1437dc466>", line 1, in <module>
lg.expm(M)
File "/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/matfuncs.py", line 592, in expm
return _expm(A, use_exact_onenorm='auto')
File "/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/matfuncs.py", line 677, in _expm
X = _fragment_2_1(X, h.A, s)
File "/usr/local/lib/python3.8/dist-packages/scipy/sparse/linalg/matfuncs.py", line 813, in _fragment_2_1
t_12 = scale * T[k, k+1]
File "/usr/local/lib/python3.8/dist-packages/scipy/sparse/bsr.py", line 315, in __getitem__
raise NotImplementedError
NotImplementedError
with csc correction:
In [272]: lg.expm(M.tocsc())
Out[272]:
<3x3 sparse matrix of type '<class 'numpy.float64'>'
with 6 stored elements in Compressed Sparse Column format>
np.diag(np.arange(N))
In [303]: sparse.kron(np.diag(np.arange(3)), np.identity(2)).A
Out[303]:
array([[0., 0., 0., 0., 0., 0.],
[0., 0., 0., 0., 0., 0.],
[0., 0., 1., 0., 0., 0.],
[0., 0., 0., 1., 0., 0.],
[0., 0., 0., 0., 2., 0.],
[0., 0., 0., 0., 0., 2.]])
In [304]: sparse.kron(np.diag(np.arange(5)), np.identity(2))
Out[304]:
<10x10 sparse matrix of type '<class 'numpy.float64'>'
with 16 stored elements (blocksize = 2x2) in Block Sparse Row format>
In [305]: sparse.kron(np.diag(np.arange(6)), np.identity(2))
Out[305]:
<12x12 sparse matrix of type '<class 'numpy.float64'>'
with 20 stored elements (blocksize = 2x2) in Block Sparse Row format>
No significant difference in the kron
result except size as N
grows.
In [308]: lg.expm(sparse.kron(np.diag(np.arange(6)), np.identity(2)))
...
t_12 = scale * T[k, k+1]
File "/usr/local/lib/python3.8/dist-packages/scipy/sparse/bsr.py", line 315, in __getitem__
raise NotImplementedError
NotImplementedError
Specifying a csc
format in kron
avoids that error (we can ignore this efficiency warning):
In [309]: lg.expm(sparse.kron(np.diag(np.arange(6)), np.identity(2),'csc'))
/usr/local/lib/python3.8/dist-packages/scipy/sparse/_index.py:82: SparseEfficiencyWarning: Changing the sparsity structure of a csc_matrix is expensive. lil_matrix is more efficient.
self._set_intXint(row, col, x.flat[0])
Out[309]:
<12x12 sparse matrix of type '<class 'numpy.float64'>'
with 23 stored elements in Compressed Sparse Column format>
Why N=6
gives this warning and not small N
probably has to do with which Pade
order it had to try. Keep in mind that expm
is a complex calculation, and the best it can do (numerically) is approximate it. That approximation is easier with small matrices. There's a lot of math theory behind this code.
Upvotes: 3