5.9. Matrix-reduktion, ligningsløsning og inverse matricer#

En af de store fordele ved lineær algebra er, at vi nemt kan løse lineære ligningssystemer. Til dette kan vi gøre brug af lidt forskellige værktøjer i SymPy, som vil blive gennemgået i denne sektion.

import sympy as sp          # Importer SymPy
from sympy import Matrix    # Vi kommer til at definerer mange matricer i denne Notebook

5.9.1. Matrix-reduktion#

Python/SymPy indeholder en række indbyggede metoder til at reducere matricer. Særligt brugbart i LinALys er funktionen, der bringer en matrix på reduceret række-echelonform. For en matrix \(A\) kan vi finde den ækvivalente matrix på reduceret række-echelonform ved .rref():

A = Matrix([[1, 1, -2, 1], [3, 2, 1, -5], [2, 1, 3, -6]])
display(A)
A.rref()
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 1 & -2 & 1\\3 & 2 & 1 & -5\\2 & 1 & 3 & -6\end{matrix}\right]\end{split}\]
(Matrix([
 [1, 0,  5, -7],
 [0, 1, -7,  8],
 [0, 0,  0,  0]]),
 (0, 1))

Resultatet er den reducerede række-echelonform og en liste over nummeret på de kolonner, der har en ledende indgang. Hvis man vil udelade denne liste, kan man i stedet skrive .rref(pivots = False). Man kan få printet tingene fint, hvis man i starten af Notebooken kalder sp.init_printing(), men det giver lidt bøvl med vores noteformat, og vi har derfor udeladt dette. Alternativt kan man blot indeksere løsningen:

display(A.rref()[0],
        A.rref()[1])
\[\begin{split}\displaystyle \left[\begin{matrix}1 & 0 & 5 & -7\\0 & 1 & -7 & 8\\0 & 0 & 0 & 0\end{matrix}\right]\end{split}\]
(0, 1)

5.9.2. Ligningssystemer med een løsning#

Som det nok er gået op for de fleste, anviser lineær algebra en effektiv måde til at bestemme løsningerne til lineære ligningssystemer ved hjælp af matrixregning. Vi kan f.eks. omskrive:

\(\begin{aligned} x_1 + 3 x_3 &= 20 \\ 4x_2 + 6 x_3 &= 74 \\ 3 x_1 + 6x_2 + 11 x_3 &= 136 \end{aligned}\)

til:

\(\displaystyle \left[\begin{matrix}1 & 0 & 3\\0 & 4 & 6\\3 & 6 & 11\end{matrix}\right] \displaystyle \left[\begin{matrix}x_{1}\\x_{2}\\x_{3}\end{matrix}\right] = \displaystyle \left[\begin{matrix}20\\74\\136\end{matrix}\right]\)

Når vi har et ligningssystem på denne form, og der findes en entydig løsning, kan vi uden videre beregne løsningen med SymPy. Vi benytter blot LUsolve-metoden på en matrix, og giver den højre side af vores ligningssystem som inputparameter:

#Definerer her matrix A og vektor b
A = Matrix([[1, 0, 3], [0, 4, 6], [3, 6, 11]])
b = Matrix([20, 74, 136])

#Løsningen findes nu blot ved:
sol = A.LUsolve(b)
display(sol)
\[\begin{split}\displaystyle \left[\begin{matrix}5\\11\\5\end{matrix}\right]\end{split}\]

Skulle vi være mistroiske overfor moderne datamater, kan vi nu verificere at

\(\displaystyle \left[\begin{matrix}x_{1}\\x_{2}\\x_{3}\end{matrix}\right] = \displaystyle \left[\begin{matrix}5 \\11\\5\end{matrix}\right]\)

virkelig er en løsning ved at multiplicere matricen \(A\) med løsningsvektoren:

A * sol
\[\begin{split}\displaystyle \left[\begin{matrix}20\\74\\136\end{matrix}\right]\end{split}\]

Vi kan lave tilsvarende operationer med symbolske variable. Lad \(A\) være matricen angivet ovenfor, men i stedet ønsker vi nu at finde en generel løsning til

\(\displaystyle \left[\begin{matrix}1 & 0 & 3\\0 & 4 & 6\\3 & 6 & 11\end{matrix}\right] \displaystyle \left[\begin{matrix}x_{1}\\x_{2}\\x_{3}\end{matrix}\right] = \displaystyle \left[\begin{matrix}d\\e\\f\end{matrix}\right]\)

from sympy.abc import d, e, f
b = Matrix([d, e, f])

A.LUsolve(b)
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2 d}{7} - \frac{9 e}{14} + \frac{3 f}{7}\\- \frac{9 d}{14} - \frac{e}{14} + \frac{3 f}{14}\\\frac{3 d}{7} + \frac{3 e}{14} - \frac{f}{7}\end{matrix}\right]\end{split}\]

5.9.3. Omvendte/inverse matricer#

Som beskrevet i Messer afsnit 5.2 kan matricer inverteres. Dette gøres meget simpelt for en matrix \(A\) med syntaksen A.inv():

A.inv()
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2}{7} & - \frac{9}{14} & \frac{3}{7}\\- \frac{9}{14} & - \frac{1}{14} & \frac{3}{14}\\\frac{3}{7} & \frac{3}{14} & - \frac{1}{7}\end{matrix}\right]\end{split}\]

Såfremt den omvendte/inverse matrix for \(A\) eksisterer, kan vi derfor også løse det ovenstående ligningssystem som

\(\displaystyle \left[\begin{matrix}x_{1}\\x_{2}\\x_{3}\end{matrix}\right] = \displaystyle \left[\begin{matrix}1 & 0 & 3\\0 & 4 & 6\\3 & 6 & 11\end{matrix}\right]^{-1} \displaystyle \left[\begin{matrix}d\\e\\f\end{matrix}\right]\)

hvilket i Python-syntax gøres ved

A.inv() * b
\[\begin{split}\displaystyle \left[\begin{matrix}- \frac{2 d}{7} - \frac{9 e}{14} + \frac{3 f}{7}\\- \frac{9 d}{14} - \frac{e}{14} + \frac{3 f}{14}\\\frac{3 d}{7} + \frac{3 e}{14} - \frac{f}{7}\end{matrix}\right]\end{split}\]

5.9.4. Matrixrang#

Bemærk at man hverken kan finde løsninger med LUsolve eller ved at beregne den inverse matrix hvis ikke ligningssystemet har netop én løsning (dvs. hvis ligningerne i ligningssystemet har indbygget en modstrid eller løsningen har en eller flere frie variable). I disse tilfælde vil begge metoder give fejlmeddelelser.

Årsagen hertil er at koefficientmatricen ikke har fuld rang. Rangen for en matrix \(A\) kan findes ved A.rank(), og angiver antallet af ledende et-taller i den matrix, der fremkommer ved at føre \(A\) på reduceret række-echelonform.

Af denne grund, og i overensstemmelse med Messer sætning 5.12, er det derfor ingen overraskelse at se at \(A\) fra overfor, der repræsenterer 3 lineært uafgængige ligninger med 3 ubekendte, har rang 3:

A.rank()
3

5.9.5. Ligningssystemer med flere løsninger: frie parametre#

Vi kommer dog ofte ud for ligningssystemer med flere ubekendte end der er lineært uafhængige ligninger, svarende til at løsningen kan parametriseres med et antal frie variable/parametre. Dette svarer til at rangen af den tilsvarende koefficientmatrix \(A\) er mindre end antallet af variable i ligningssystemet. I sådanne tilfælde er A ikke invertibel og LU-solve vil derfor ikke virke.

Eksempel: Vi vil gerne finde den fuldstændige løsning til:

\(\displaystyle \left[\begin{matrix}1 & 0 & 3 & 1\\0 & 4 & 6 & 1\\-1 & 4 & 3 & 0\end{matrix}\right] \displaystyle \left[\begin{matrix}x_{1}\\x_{2}\\x_{3}\end{matrix}\right] = \displaystyle \left[\begin{matrix}0\\0\\0\end{matrix}\right]\)

En mulighed er at føre \(A\) på reduceret række-echelonform:

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

Vi sætter \(x_3 = r\) og \(x_4 = s\) da der ikke er ledende indgange i 3. og 4. kolonne. Herefter opskriver vi løsningen som

\( \displaystyle \left[\begin{matrix}x_{1}\\x_{2}\\x_{3}\\x_4\end{matrix}\right] = \displaystyle \left[\begin{matrix}-3\\-\frac32\\1\\0\end{matrix}\right]\cdot r + \displaystyle \left[\begin{matrix}-1\\-\frac14\\0\\1\end{matrix}\right]\cdot s, \hspace{1cm}r,s\in {\rm I\!R}\)

Men vi kan også benytte metoden .gauss_jordan_solve() som efterligner denne metode, blot med en lidt anden typografi:

b = Matrix([0, 0, 0])
display(A.gauss_jordan_solve(b)[0],
        A.gauss_jordan_solve(b)[1])
\[\begin{split}\displaystyle \left[\begin{matrix}- 3 \tau_{0} - \tau_{1}\\- \frac{3 \tau_{0}}{2} - \frac{\tau_{1}}{4}\\\tau_{0}\\\tau_{1}\end{matrix}\right]\end{split}\]
\[\begin{split}\displaystyle \left[\begin{matrix}\tau_{0}\\\tau_{1}\end{matrix}\right]\end{split}\]

Her får vi altså først en løsning parametriseret ved \(\tau_0 ... \tau_n\), og dernæst en vektor over de frie parametre.