6.13. Lineární algebra¶
Lineární algebra na kameře je práce s malými maticemi: rotace 3x3, která fúzuje vzorek z IMU do souřadnic světa, kalibrační matice, která rektifikuje objektiv, aktualizace stavové kovariance Kalmanova filtru, polynomiální proložení, jehož normální rovnice vyjdou jako drobné lineární řešení. Submoduly numpy.linalg a scipy.linalg pokrývají přesně tento rozsah.
Funkce sídlí ve dvou modulech. Operace maticového součinu jsou na nejvyšší úrovni numpy; dekompozice a inverze matice jsou pod numpy.linalg; vyhrazené řešiče lineárních soustav jsou pod scipy.linalg. Například násobení matic 2x2:
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. Co je k dispozici¶
dot()– maticový nebo vektorový součin.cross()– vektorový součin 3D vektorů.trace()– součet diagonály.inv()– inverze matice.det()– determinant.cholesky()– Choleského rozklad (symetrický pozitivně definitní vstup).eig()– vlastní čísla a vlastní vektory reálné symetrické matice.norm()– 2-norma vektoru nebo matice.qr()– QR rozklad smode='reduced'(výchozí) nebomode='complete'.solve_triangular()– řešíA @ x = b, když jeAtrojúhelníková.cho_solve()– řešíA @ x = bpři zadaném Choleského faktoru maticeA.
6.13.2. dot, cross, trace¶
dot() je způsob, jakým se na kameře zapisuje násobení matic. Operátor @, který desktopový numpy poskytuje pro stejnou úlohu, není na ndarray implementován, takže každý maticově-vektorový, maticově-maticový a vektorový skalární součin prochází voláním 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
Výsledek dot() je vždy typu float. cross() je vektorový součin dvou 3-vektorů, trace() součet hlavní diagonály čtvercové matice.
6.13.3. inv a 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))
Inverze se počítá Gaussovou-Jordanovou eliminací, takže inv() vyvolá ValueError, když je matice singulární (diagonální prvek se během eliminace stane nulovým). Náklady na RAM jsou zhruba dvojnásobkem velikosti vstupu.
Determinant znovu využívá tutéž eliminaci – doba běhu je v podstatě stejná jako u inverze.
Když je cílem aplikace vyřešit lineární soustavu, neinvertujte a nenásobte – dejte přednost vyhrazeným řešičům níže. Oba jsou rychlejší a numericky se chovají lépe.
6.13.4. cholesky¶
Pro symetrickou pozitivně definitní matici A vrací cholesky() dolní trojúhelníkovou matici L takovou, že A = L @ L.T
a = np.array([[25, 15, -5],
[15, 18, 0],
[-5, 0, 11]])
L = np.linalg.cholesky(a)
Pokud vstup není pozitivně definitní nebo není symetrický, vyvolá se ValueError.
Choleského faktor představuje polovinu práce LU faktorizace a je správným výchozím bodem pro každý problém, jehož matice je známa jako symetrická pozitivně definitní (aktualizace kovariance, normální rovnice z proložení metodou nejmenších čtverců).
6.13.5. eig¶
eig() funguje pouze na reálných symetrických maticích. Nesymetrické matice vyvolají ValueError. Vrací dvojici (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)
Poznámky:
Vlastní čísla se vracejí bez konkrétního pořadí. Když na seřazeném pořadí záleží, použijte
sort()(a stejnou permutaci aplikujte na vlastní vektory pomocíargsort()).Vlastní vektor je jednoznačný pouze až na nenulový skalár, takže znaménko jednotlivých vlastních vektorů není jednoznačně určeno. Dva správné běhy mohou vyprodukovat vektory opačného znaménka; to je neškodné.
6.13.6. norm¶
Eukleidovská (Frobeniova) norma vektoru nebo matice:
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...
Volitelné klíčové slovo axis= počítá normu podél jediné osy namísto přes celé pole.
6.13.7. qr¶
qr() rozkládá obdélníkovou matici A (tvar (M, N)) na ortonormální Q a horní trojúhelníkovou R takovou, že 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)
Rozklad je implementován pomocí postupných Givensových rotací. Správná volba pro problémy nejmenších čtverců, kde matice není symetrická.
6.13.8. Řešení soustav¶
Dva vyhrazené řešiče pod ulab.scipy.linalg jsou rychlejší i přesnější než np.dot(np.linalg.inv(A), b):
solve_triangular(a, b, lower=False)()– řešía @ x = bza předpokladu, žeaje trojúhelníková: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)()– při zadaném Choleského faktoruLřešíA @ x = b, kdeA = L @ L.TL = np.linalg.cholesky(A) x = sp.linalg.cho_solve(L, b)
Sáhněte po nich namísto invertování, kdykoli to struktura A dovolí – ušetří práci s eliminací i explicitní inverzi.
6.13.9. Řešení malé lineární soustavy¶
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
Tentýž problém lze vyjádřit faktorizací A a voláním vhodného řešiče – rychlejší a přesnější, když má A správnou strukturu.
Úplnou referenci na úrovni argumentů najdete v numpy.linalg — Rutiny lineární algebry a scipy.linalg — Rutiny lineární algebry.