6.13. Álgebra linear¶
Álgebra linear em uma câmera é trabalho com matrizes pequenas: uma rotação 3x3 que funde uma amostra da IMU ao referencial do mundo, uma matriz de calibração que retifica uma lente, a atualização de estado-covariância de um filtro de Kalman, um ajuste polinomial cujas equações normais resultam em uma pequena resolução linear. Os submódulos numpy.linalg e scipy.linalg cobrem exatamente essa escala.
As funções residem em dois módulos. As operações de produto de matrizes estão no nível superior de numpy; as decomposições e a inversa de matriz estão em numpy.linalg; os solucionadores dedicados de sistemas lineares estão em scipy.linalg. Uma multiplicação de matrizes 2 por 2, por exemplo:
from ulab import numpy as np
A = np.array([[1, 2], [3, 4]], dtype=np.float)
B = np.array([[5, 6], [7, 8]], dtype=np.float)
np.dot(A, B)
# array([[19.0, 22.0],
# [43.0, 50.0]])
6.13.1. O que está disponível¶
dot()– produto de matrizes ou vetores.cross()– produto vetorial de vetores 3-D.trace()– soma da diagonal.inv()– inversa de matriz.det()– determinante.cholesky()– decomposição de Cholesky (entrada simétrica positiva definida).eig()– autovalores e autovetores de uma matriz real simétrica.norm()– norma 2 de um vetor ou matriz.qr()– decomposição QR commode='reduced'(padrão) oumode='complete'.solve_triangular()– resolveA @ x = bquandoAé triangular.cho_solve()– resolveA @ x = bdado um fator de Cholesky deA.
6.13.2. dot, cross, trace¶
dot() é como a multiplicação de matrizes é escrita na câmera. O operador @ que o numpy de desktop fornece para a mesma tarefa não é implementado em ndarray, então todo produto matriz-vetor, matriz-matriz e produto escalar de vetores passa pela chamada dot()
a = np.array([1, 2, 3])
b = np.array([4, 5, 6])
np.dot(a, b) # 32.0 (scalar product)
np.cross(a, b) # array([-3.0, 6.0, -3.0])
m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
np.dot(m, a) # matrix-vector product
np.dot(m, m) # matrix-matrix product
np.trace(m) # 1 + 5 + 9 = 15.0
O resultado de dot() é sempre do dtype float. cross() é o produto vetorial de dois vetores de 3 elementos, e trace() é a soma da diagonal principal de uma matriz quadrada.
6.13.3. inv e det¶
m = np.array([[1, 2, 3, 4],
[4, 5, 6, 4],
[7, 9, 9, 4],
[3, 4, 5, 6]])
print(np.linalg.inv(m))
print(np.linalg.det(m))
A inversa é calculada por eliminação de Gauss-Jordan, então inv() lança ValueError quando a matriz é singular (uma entrada da diagonal se torna zero durante a eliminação). O custo de RAM é aproximadamente o dobro do tamanho da entrada.
O determinante reutiliza a mesma eliminação – o tempo de execução é essencialmente o mesmo da inversa.
Quando o objetivo da aplicação é resolver um sistema linear, não inverta e multiplique – prefira os solucionadores dedicados abaixo. Ambos são mais rápidos e numericamente mais bem-comportados.
6.13.4. cholesky¶
Para uma matriz simétrica positiva definida A, cholesky() retorna uma matriz triangular inferior L tal que A = L @ L.T
a = np.array([[25, 15, -5],
[15, 18, 0],
[-5, 0, 11]])
L = np.linalg.cholesky(a)
Se a entrada não for positiva definida ou não for simétrica, ValueError é lançada.
O fator de Cholesky tem metade do trabalho de uma fatoração LU e é o ponto de partida certo para qualquer problema cuja matriz seja conhecidamente simétrica positiva definida (atualizações de covariância, equações normais de um ajuste por mínimos quadrados).
6.13.5. eig¶
eig() funciona apenas em matrizes reais simétricas. Matrizes não simétricas lançam ValueError. Ela retorna uma 2-tupla (eigenvalues, eigenvectors)
a = np.array([[1, 2, 1, 4],
[2, 5, 3, 5],
[1, 3, 6, 1],
[4, 5, 1, 7]], dtype=np.uint8)
x, y = np.linalg.eig(a)
Notas:
Os autovalores retornam sem ordem específica. Aplique
sort()(e a mesma permutação aos autovetores viaargsort()) quando uma ordem classificada for importante.Um autovetor é único apenas a menos de um escalar não nulo, então o sinal de autovetores individuais não é definido de forma única. Duas execuções corretas podem produzir vetores de sinal oposto; isso é inofensivo.
6.13.6. norm¶
A norma euclidiana (de Frobenius) de um vetor ou matriz:
v = np.array([1, 2, 3, 4, 5])
m = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
np.linalg.norm(v) # 7.416...
np.linalg.norm(m) # 16.881...
A palavra-chave opcional axis= calcula a norma ao longo de um único eixo, em vez de sobre o array inteiro.
6.13.7. qr¶
qr() fatora uma matriz retangular A (formato (M, N)) em uma Q ortonormal e uma R triangular superior tais que A == Q @ R
A = np.arange(6).reshape((3, 2))
q, r = np.linalg.qr(A)
# mode='reduced' (default): q is (3, 2), r is (2, 2)
q, r = np.linalg.qr(A, mode='complete')
# q is (3, 3), r is (3, 2)
A decomposição é implementada por meio de sucessivas rotações de Givens. É a escolha certa para problemas de mínimos quadrados em que a matriz não é simétrica.
6.13.8. Resolvendo sistemas¶
Os dois solucionadores dedicados em ulab.scipy.linalg são ao mesmo tempo mais rápidos e mais precisos do que np.dot(np.linalg.inv(A), b):
solve_triangular(a, b, lower=False)()– resolvea @ x = bsupondo queaseja triangular:A = np.array([[3, 0, 0, 0], [2, 1, 0, 0], [1, 0, 1, 0], [1, 2, 1, 8]]) b = np.array([4, 2, 4, 2]) x = sp.linalg.solve_triangular(A, b, lower=True)
cho_solve(L, b)()– dado um fator de CholeskyL, resolveA @ x = bondeA = L @ L.TL = np.linalg.cholesky(A) x = sp.linalg.cho_solve(L, b)
Recorra a estes em vez de inverter sempre que a estrutura de A permitir – eles economizam tanto o trabalho de eliminação quanto a inversa explícita.
6.13.9. Resolvendo um pequeno sistema linear¶
A = np.array([[3, 0, 1, 1],
[0, 1, 0, 2],
[1, 0, 1, 1],
[1, 2, 1, 8]])
b = np.array([4, 2, 4, 2])
x = np.dot(np.linalg.inv(A), b)
print(x)
print(np.dot(A, x)) # should equal b
O mesmo problema pode ser expresso fatorando A e chamando o solucionador apropriado – mais rápido e mais preciso quando A tem a estrutura adequada.
Para a referência completa em nível de argumentos, consulte numpy.linalg — Rotinas de álgebra linear e scipy.linalg — Rotinas de álgebra linear.