5.10. Flere metoder i Linær Algebra#

Målet for denne sektion er at give en oversigt over de funktioner, som man kan få brug for, når man benytter SymPy som redskab i lineær algebra.

Langt de fleste funktioner i denne notebook virker på samme måde som matrixinversion, som for en matrix \(A\) findes ved A.inv().

Først importerer vi de rette pakker:

import sympy as sp              # Importer sympy 
from sympy import Matrix        # Vi kommer til at lave mange matricer

Tip: Mange metoder i SymPy returnerer en liste over forskellige løsninger. Da disse ikke automatisk bliver vist som matematik, har vi mange steder sat en * foran metoden inde i display. I stedet for at skrive display(*liste) kan man i sin egen Notebook benytte sig af sp.init_printing() i starten af notebooken. Dette giver SymPy muligheden for at vælge, hvordan lister skal printes. Vi har desværre ikke kunne bruge denne mulighed på grund af formatteringen af vores notesbøger.

5.10.1. Transponering, adjungering og konjugering#

Hvis vi har givet en matrix, kan vi nemt beregne den transponerede matrix med at ombytte rækker og kolonner:

A = Matrix([[1, 2], [3, 4]]);
display(A)
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 2\\3 & 4\end{matrix}\right]\end{split}\]

Den transponerede beregnes med .T

A.T    # Bemærk, at der ikke skal parenteser bag T
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 3\\2 & 4\end{matrix}\right]\end{split}\]

Messer arbejder ikke med komplekse tal som indgange i matricer, men hvis man gør (og det er der mange gode grunde til, f.eks. i kvantemekanik), vil man også møde den såkaldte konjugerede matrix A.conjugate() (der fremkommer ved kompleks konjugering af indgangene af \(A\)) og den såkaldte adjungerede (matricen der er \(A\)s transponerede konjugerede matrix) A.adjoint() eller nemmere A.H . Notationen med H kommer af at den adjungerede matrix også kaldes den hermitisk konjugerede. Eksempelvis kan vi finde den konjugerede matrix her:

from sympy import I, pi # Imaginære tal og pi skal hentes

A = Matrix([[1, 3+I], [2-I, sp.exp(I*pi/3)]])
display(A)
A.conjugate()
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 3 + i\\2 - i & e^{\frac{i \pi}{3}}\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 3 - i\\2 + i & e^{- \frac{i \pi}{3}}\end{matrix}\right]\end{split}\]

hvor vi ser at alle imaginærdelene har skiftet fortegn. Som eksempel på den adjungerede (altså komplekst konjugerede og transponerede) matrix får vi

A.H     # Som med .T indeholder syntaksen ikke ()
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 2 + i\\3 - i & e^{- \frac{i \pi}{3}}\end{matrix}\right]\end{split}\]

5.10.2. Nulrum og søjlerum#

For en matrix \(M\) kan vi finde dennes nulrum og søjlerum ved at bruge hhv. M.nullspace() og M.columnspace().

M = Matrix([[1, 1, 2], [2, 1, 3], [3, 1, 4]])
display(M)
display(*M.nullspace())
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 1 & 2\\2 & 1 & 3\\3 & 1 & 4\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}-1\\-1\\1\end{matrix}\right]\end{split}\]

Hvilket giver en liste (her med eet element) af vektorer som er basis for matricens nulrum. Altså vektorer som opfylder ligningen \(M\boldsymbol{x} = \boldsymbol{0}\). Dette gælder derfor også for linearkombinationer af vektorerne i nulrummet.

Søjlerummet findes helt tilsvarende:

display(*M.columnspace())
\[\begin{split}\displaystyle \left[\begin{matrix}1\\2\\3\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}1\\1\\1\end{matrix}\right]\end{split}\]

Herved får vi en liste af vektorer, som udspænder søjlerummet, og som består af de søjler fra \(M\), der indeholder ledende indgange når \(M\) er bragt på række-echelonform. Dette verificerer vi ved at betragte \(M\)s ækvivalente matrix på reduceret række-echelonform:

display(*M.rref())
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 1\\0 & 1 & 1\\0 & 0 & 0\end{matrix}\right]\end{split}\]
(0, 1)

5.10.3. Gram-Schmidt-ortogonalisering#

Er vi givet flere vektorer i rummet som ikke er lineært afhængigt af hinanden, kan vi danne et ortogonalt (og evt. ortonomalt, hvis vi normaliserer vektorerne) sæt af vektorer ved at bruge Gram-Schmidt-ortogonalisering.

Dette kan være en ret omgangsrig procedure, som man i LinALys kun vil blive bedt om at udføre i relativt simple tilfælde. Men selv i disse simple tilfælde er der masser af muligheder for at lave regnefejl, og det kan derfor være rart hurtigt at kunne checke beregningerne, især for når der er tale om mere end 2-3 vektorer. Til dette importerer man GramSchmidt fra sympy.matrices og benytter den på en liste \(L\) af vektorer: GramScmidt(L). Som udgangspunkt normaliserer SymPy ikke de ortogonaliserede vektorer, men hvis vi giver funktionen et ekstra argument GramSchmidt(L, True), fortages normaliseringen som en del af beregningen.

Som eksempel genregner vi her eksemplet fra side 162 i Messer med SymPy:

from sympy.matrices import GramSchmidt

# Definer en liste med matrix indgange, som nu er vektorer
L = [Matrix([1, 2, 2]), Matrix([0, 1, 0]), Matrix([0, 0, 1])]
display(*L)
\[\begin{split}\displaystyle \left[\begin{matrix}1\\2\\2\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}0\\1\\0\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}0\\0\\1\end{matrix}\right]\end{split}\]
# Vi benytter nu GramScmidt
display(*GramSchmidt(L))
\[\begin{split}\displaystyle \left[\begin{matrix}1\\2\\2\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2}{9}\\\frac{5}{9}\\- \frac{4}{9}\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2}{5}\\0\\\frac{1}{5}\end{matrix}\right]\end{split}\]
# Eller hvis vi vil inkluderer en normalisering gør vi følgende:
display(*GramSchmidt(L, True))
\[\begin{split}\displaystyle \left[\begin{matrix}\frac{1}{3}\\\frac{2}{3}\\\frac{2}{3}\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2 \sqrt{5}}{15}\\\frac{\sqrt{5}}{3}\\- \frac{4 \sqrt{5}}{15}\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2 \sqrt{5}}{5}\\0\\\frac{\sqrt{5}}{5}\end{matrix}\right]\end{split}\]

hvilket kan ses at passe med Messers

\(\left\{ \left(\frac{1}{3}, \frac{2}{3}, \frac{2}{3}\right), \left(\frac{-2}{\sqrt{45}}, \frac{5}{\sqrt{45}}, \frac{-4}{\sqrt{45}}\right),\left(\frac{-2}{\sqrt{5}}, 0, \frac{1}{\sqrt{5}}\right)\right\}\)

ved anvendelse af kvadratrods- og brøkregneregler.

5.10.4. Determinant#

Determinanten beregnes ved at tilføje .det() til matricens navn:

A = Matrix([[1, 2], [3, 4]])
display(A)
A.det()
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 2\\3 & 4\end{matrix}\right]\end{split}\]
\[\displaystyle -2\]

Vi kan også beregne determinanten for en symbolsk matrix:

from sympy.abc import a, b, c, d

B = Matrix([[a, b], [c, d]])
display(B)

B.det()
\[\begin{split}\displaystyle \left[\begin{matrix}a & b\\c & d\end{matrix}\right]\end{split}\]
\[\displaystyle a d - b c\]

i overensstemmelse med, hvordan vi selv ville regne den.

5.10.5. Sporet / Trace#

Sporet udregnes ved at tilføje .trace() til den ønskede matrix, hvilket giver summen af diagonalindgangene:

A = Matrix([[pi, 1, 1],[0, pi, 1],[0, 0, pi**2]])
display(A)
A.trace()
\[\begin{split}\displaystyle \left[\begin{matrix}\pi & 1 & 1\\0 & \pi & 1\\0 & 0 & \pi^{2}\end{matrix}\right]\end{split}\]
\[\displaystyle 2 \pi + \pi^{2}\]

5.10.6. Krydsprodukt, vektorprodukt / Cross product#

For to vektorer i tre dimensioner kan vi benytte beregne krydsproduktet mellem v og w med v.cross(w) (i analogi med hvordan vi beregner det indre produkt):

v = Matrix([1, 0, 1])
w = Matrix([1, 2, 0])
display(v, w)

display(v.cross(w))
\[\begin{split}\displaystyle \left[\begin{matrix}1\\0\\1\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}1\\2\\0\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}-2\\1\\2\end{matrix}\right]\end{split}\]