web-dev-qa-db-fra.com

Inversion de matrice sans Numpy

Je veux inverser une matrice sans utiliser numpy.linalg.inv.

La raison en est que j'utilise Numba pour accélérer le code, mais numpy.linalg.inv n'est pas pris en charge, donc je me demande si je peux inverser une matrice avec le code 'classique' Python code.

Avec numpy.linalg.inv un exemple de code ressemblerait à ça:

import numpy as np
M = np.array([[1,0,0],[0,1,0],[0,0,1]])
Minv = np.linalg.inv(M)
20
Alessandro Vianello

Voici une solution plus élégante et évolutive, imo. Cela fonctionnera pour n'importe quelle matrice nxn et vous pouvez trouver une utilisation pour les autres méthodes. Notez que getMatrixInverse (m) accepte un tableau de tableaux en entrée. N'hésitez pas à poser des questions.

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
39
stackPusher

Au moins le 16 juillet 2018, Numba a une matrice inverse rapide. (Vous pouvez voir comment ils surchargent l'inverse de NumPy standard et d'autres opérations ici .)

Voici les résultats de mon benchmarking:

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)

Pour les petites matrices, c'est particulièrement rapide:

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

[En dehors]

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)

Notez que l'accélération ne fonctionne que pour NumPy inverse, pas SciPy (comme prévu).

Matrice légèrement plus grande:

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

[En dehors]

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)

Il y a donc encore une accélération ici, mais SciPy rattrape son retard.

5
webelo

Pour une matrice 4 x 4, il est probablement à peu près correct d'utiliser la formule mathématique, que vous pouvez trouver en utilisant Google "formule pour matrice 4 x 4 inverse". Par exemple ici (je ne peux pas garantir sa précision):

http://www.cg.info.hiroshima-cu.ac.jp/~miyazaki/knowledge/teche23.html

En général, inverser une matrice générale n'est pas pour les timides. Vous devez être au courant de tous les cas mathématiquement difficiles et savoir pourquoi ils ne s'appliquent pas à votre utilisation, et les attraper lorsque vous recevez des entrées mathématiquement pathologiques (cela, ou renvoyer des résultats de faible précision ou des ordures numériques en sachant que cela n'aura pas d'importance dans votre cas d'utilisation à condition que vous ne finissiez pas réellement par diviser par zéro ou par débordement de MAXFLOAT ... que vous pourriez attraper avec un gestionnaire d'exceptions et présenter comme "Erreur: la matrice est singulière ou très proche").

En tant que programmeur, il est généralement préférable d'utiliser le code de bibliothèque écrit par des experts en mathématiques numériques, sauf si vous êtes prêt à passer du temps à comprendre la nature physique et mathématique du problème particulier que vous abordez et à devenir votre propre expert en mathématiques dans votre propre domaine de spécialité.

3
nigel222