numpy.linalg.invを使用せずに行列を反転させたい。
理由は、コードを高速化するためにNumbaを使用しているが、numpy.linalg.invはサポートされていないため、「classic」Python code。
numpy.linalg.invを使用すると、コード例は次のようになります。
import numpy as np
M = np.array([[1,0,0],[0,1,0],[0,0,1]])
Minv = np.linalg.inv(M)
これが、よりエレガントでスケーラブルなソリューション、imoです。任意のnxn行列で機能し、他の方法にも使用できます。 getMatrixInverse(m)は、配列の配列を入力として受け取ることに注意してください。ご質問はお気軽にどうぞ。
def transposeMatrix(m):
return map(list,Zip(*m))
def getMatrixMinor(m,i,j):
return [row[:j] + row[j+1:] for row in (m[:i]+m[i+1:])]
def getMatrixDeternminant(m):
#base case for 2x2 matrix
if len(m) == 2:
return m[0][0]*m[1][1]-m[0][1]*m[1][0]
determinant = 0
for c in range(len(m)):
determinant += ((-1)**c)*m[0][c]*getMatrixDeternminant(getMatrixMinor(m,0,c))
return determinant
def getMatrixInverse(m):
determinant = getMatrixDeternminant(m)
#special case for 2x2 matrix:
if len(m) == 2:
return [[m[1][1]/determinant, -1*m[0][1]/determinant],
[-1*m[1][0]/determinant, m[0][0]/determinant]]
#find matrix of cofactors
cofactors = []
for r in range(len(m)):
cofactorRow = []
for c in range(len(m)):
minor = getMatrixMinor(m,r,c)
cofactorRow.append(((-1)**(r+c)) * getMatrixDeternminant(minor))
cofactors.append(cofactorRow)
cofactors = transposeMatrix(cofactors)
for r in range(len(cofactors)):
for c in range(len(cofactors)):
cofactors[r][c] = cofactors[r][c]/determinant
return cofactors
少なくとも2018年7月16日の時点で、Numbaには高速行列逆行列があります。 (標準のNumPy逆行列およびその他の演算をオーバーロードする方法を見ることができます ここ 。)
ベンチマークの結果は次のとおりです。
import numpy as np
from scipy import linalg as sla
from scipy import linalg as nla
import numba
def gen_ex(d0):
x = np.random.randn(d0,d0)
return x.T + x
@numba.jit
def inv_nla_jit(A):
return np.linalg.inv(A)
@numba.jit
def inv_sla_jit(A):
return sla.inv(A)
小さな行列の場合、特に高速です。
ex1 = gen_ex(4)
%timeit inv_nla_jit(ex1) # NumPy + Numba
%timeit inv_sla_jit(ex1) # SciPy + Numba
%timeit nla.inv(ex1) # NumPy
%timeit sla.inv(ex1) # SciPy
[でる]
2.54 µs ± 467 ns per loop (mean ± std. dev. of 7 runs, 100000 loops each)
67.3 µs ± 9.18 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
63.5 µs ± 7.65 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
56.6 µs ± 5.03 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
スピードアップはNumPy逆関数に対してのみ機能し、SciPyには機能しないことに注意してください(予想どおり)。
少し大きいマトリックス:
ex2 = gen_ex(40)
%timeit inv_nla_jit(ex2) # NumPy + Numba
%timeit inv_sla_jit(ex2) # SciPy + Numba
%timeit nla.inv(ex2) # NumPy
%timeit sla.inv(ex2) # SciPy
[でる]
131 µs ± 12.9 µs per loop (mean ± std. dev. of 7 runs, 10000 loops each)
278 µs ± 26.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
231 µs ± 24.5 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
189 µs ± 11.2 µs per loop (mean ± std. dev. of 7 runs, 1000 loops each)
そのため、ここでも速度は向上していますが、SciPyは追いついています。
4 x 4マトリックスの場合は、おそらくグーグルの「4 x 4マトリックス逆行列の式」を使用して見つけることができる数式を使用しても大丈夫でしょう。たとえば、ここに(正確さを保証することはできません):
http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche23.html
一般に、一般的な行列を反転させることは、気の弱い人向けではありません。数学的に困難なすべてのケースを認識し、それらが使用に適用されない理由を理解し、数学的に病理学的な入力が与えられたときにそれらをキャッチする必要があります実際にゼロで割ったり、MAXFLOATをオーバーフローさせたりしない限り、使用例では問題になりません...例外ハンドラでキャッチし、「エラー:行列が特異または非常に近い」と表示される場合があります)。
プログラマーとしては、数値数学の専門家によって書かれたライブラリコードを使用する方が一般的には優れています。ただし、対処している特定の問題の物理的および数学的な性質を理解し、自分の専門分野の数学の専門家にならない限りです。