Základní práce s maticemi v Sage

Tomáš Kalvoda, KAM FIT ČVUT, 2019

Tento notebook ukazuje jak v Sage pracovat s maticemi s prvky z různých těles (resp. nejen těles).

Základní pojmy a konvence

Připomeňme si nejprve základní pojmy. Mějme TT těleso a přirozená čísla m,nNm,n \in \mathbb{N}. Potom ATm,n\mathbb{A} \in T^{m,n}, tedy zobrazení, které dvojici indexů (k,)m^×n^(k,\ell) \in \hat m \times \hat n přiřadí prvek z TT (ozn. Ak,\mathbb{A}_{k,\ell}), nazýváme m×nm \times n maticí s prvky z tělesa TT.

Matice si můžeme přirozeně představovat jako tabulku čísel indexovanou pomocí dvou indexů kk a \ell. Konvečně se prvek Ak,\mathbb{A}_{k,\ell} klade do kktého řádku a \elltého sloupce. Prvek A11\mathbb{A}_{11} je v levém horním rohu, Am,1\mathbb{A}_{m,1} v levém dolním rohu, Am,n\mathbb{A}_{m,n} v pravém dolním rohu a A1,n\mathbb{A}_{1,n} v pravém horním rohu, konkrétně A=(A1,1A1,2A1,nA2,1A2,2A1,nAm,1Am,2Am,n)\mathbb{A} = \begin{pmatrix} \mathbb{A}_{1,1} & \mathbb{A}_{1,2} & \cdots & \mathbb{A}_{1,n} \\ \mathbb{A}_{2,1} & \mathbb{A}_{2,2} & \cdots & \mathbb{A}_{1,n} \\ \vdots & & \ddots & \vdots \\ \mathbb{A}_{m,1} & \mathbb{A}_{m,2} & \cdots & \mathbb{A}_{m,n} \end{pmatrix} S přimhouřenýma očima bychom o maticích z programátorského pohledu mohli mluvit jako o dvourozměrných polích. Občas se také samotné prvky matice značí malým latinským písmenkem, tj. mohli bychom psát A=(ak,)\mathbb{A} = (a_{k,\ell}).

Indexu kk se říká řádkový index, indexu \ell sloupcový index. Pokud platí m=nm=n, pak mluvíme o čtvercových maticích, jinak o maticích obdélníkových.

Často je potřeba mluvit o a hlavně pracovat s jednotlivými řádky a sloupci matic. K tomu zavádíme jednoduché značení kompatibilní s Pythonem. Pokud budeme mluvit o matici ATm,n\mathbb{A} \in T^{m,n} zmíněné výše, tak pod symbolem Ak:T1,n\mathbb{A}_{k:} \in T^{1,n} máme na mysli její kktý řádek, tj. Ak:=(Ak,1,Ak,2,,Ak,n),\mathbb{A}_{k:} = (\mathbb{A}_{k,1}, \mathbb{A}_{k,2}, \ldots, \mathbb{A_{k,n}}), a pod symbolem A:Tm,1\mathbb{A}_{:\ell} \in T^{m,1} pak její \elltý sloupec, tj. A:=(A1A2Am,)\mathbb{A}_{:\ell} = \begin{pmatrix} \mathbb{A}_{1\ell} \\ \mathbb{A}_{2\ell} \\ \vdots \\ \mathbb{A}_{m,\ell} \end{pmatrix}

Matice v Sage

K vytváření matic v Sage vystačíme s Pythonovským polem, tzv. listem, a funkcí matrix.

List (pole) v pythonu se vytváří jednoduše pomocí hranatých závorek a může jako své prvky obsahovat všechno možné:

a = [1, 2, 'a']
a
[1, 2, 'a']

V proměnné a je uložen Pythonovský list, na který se můžete v tomto případě dívat jako na pole, indexované od 00 a mající tři prvky. K prvkům se přistupuje opět pomocí hranatých závorek:

print(a[0])
print(a[1])
print(a[2])
1
2
a

Konstrukce matice

Matice v Sage nejjednodušeji zkonstruujeme pomocí funkce matrix, které předáme list listů představující požadovanou matici zadanou po řádcích (vnitřní listy tedy odpovídají řádkům naší matice).

A = matrix([
    [1, 2, 3],
    [4, 5, 6],
    [7, 8, 9]
])
show(A)
(123456789)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 1 & 2 & 3 \\ 4 & 5 & 6 \\ 7 & 8 & 9 \end{array}\right)

S obyčejným listem bychom si pro práci s maticemi nevystačili. Podívejte se, jaké metody jsou k dispozici pro a a jaké pro A. S maticí v proměnné A teď budeme moci provádět spoustu užitečených operací.

V úvodní části této sekce jsme nespecifikovali odkud máme prvky matice brát. Sage opět se v tomto případě snaží uhodnout, nad jakým tělesem se pohybujeme.

A.parent()
Full MatrixSpace of 3 by 3 dense matrices over Integer Ring

Tj. bez dalšího vysvětlování náš vstup chápe jako 3×33 \times 3 matici s prvky z množiny celých čísel Z\mathbb{Z}, tedy jako prvek Z3,3\mathbb{Z}^{3,3}.

Ve většině případů není potřeba toto chování měnit, například v následujících případech je výsledek takový, jaký jsme zřejmě chtěli:

A = matrix([
    [1/2, 2/3],
    [4/5, 5/6]
])
show(A)
A.parent()
(12234556)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} \frac{1}{2} & \frac{2}{3} \\ \frac{4}{5} & \frac{5}{6} \end{array}\right)
Full MatrixSpace of 2 by 2 dense matrices over Rational Field
A = matrix([
    [0.5, 2.2],
    [0.3, 1.1]
])
show(A)
A.parent()
(0.5000000000000002.200000000000000.3000000000000001.10000000000000)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 0.500000000000000 & 2.20000000000000 \\ 0.300000000000000 & 1.10000000000000 \end{array}\right)
Full MatrixSpace of 2 by 2 dense matrices over Real Field with 53 bits of precision

Občas chceme ale vynutit typ prvků matice, například když pracujeme nad konečným tělesem. V tom případě je nejsnazší předat toto těleso jako první argument funkce matrix. Například:

A = matrix(GF(5), [
    [2, 4],
    [1, 3]
])
show(A)
A.parent()
(2413)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 2 & 4 \\ 1 & 3 \end{array}\right)
Full MatrixSpace of 2 by 2 dense matrices over Finite Field of size 5

Na první pohled se nezdá, že se od prvního příkladu moc nezměnilo. Ale jak si ukážeme níže, výsledky různých maticových operací budu podstatně jiné, než kdybychom ji chápali jako prvek Z2,2\mathbb{Z}^{2,2}. Nyní jde o matici z GF(5)2,2GF(5)^{2,2}.

Práce s prvky matice

Ukážeme si různé jednoduché způsoby jak přistupovat k prvkům matice A definované hned nad tímto dostavcem. Jednotlivé prvky dostaneme tak jak jsme si zavedli indexování (pozor, v Sage i Pythonu od 00)!

print('Prvky v prvním řádku:')
print(A[0, 0])
print(A[0, 1])
print('Prvky v druhém řádku:')
print(A[1, 0])
print(A[1, 1])
Prvky v prvním řádku:
2
4
Prvky v druhém řádku:
1
3

Nebo bychom mohli získat jednotlivé sloupce a řádky (to se nám bude hodit později).

print('První řádek:')
show(A[0,:])
print('Druhý sloupec:')
show(A[:,1])
První řádek:
(24)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 2 & 4 \end{array}\right)
Druhý sloupec:
(43)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{r} 4 \\ 3 \end{array}\right)

Maticové operace: násobení číslem

Matice z ATm,n\mathbb{A} \in T^{m,n} můžeme přirozeně násobit číslem z α\alpha tělesa TT. Pod maticí αA\alpha\mathbb{A} rozumíme matici, jejíž prvky jsou α\alpha-násobky prvků matice A\mathbb{A}, přesněji (αA)k,:=αAk,,(\alpha\mathbb{A})_{k,\ell} := \alpha \cdot \mathbb{A}_{k,\ell}, pro každé km^k\in\hat m a n^\ell\in\hat n.

V Sage této operaci odpovídá * pokud nalevo máme číslo a napravo matici.

A = matrix(QQ, [
    [1, 1/2],
    [3, 3/5]
])
show(7 * A)
(77221215)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 7 & \frac{7}{2} \\ 21 & \frac{21}{5} \end{array}\right)

Důležité je uvědomit si, že operace násobení mezi prvky matice se provádí v tělese TT!

A = matrix(GF(7), [
    [1, 3],
    [2, 5]
])
show(2 * A)
(2643)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 2 & 6 \\ 4 & 3 \end{array}\right)

Maticové operace: sčítání matic

Matice stejných rozměrů A,BTm,n\mathbb{A},\mathbb{B} \in T^{m,n} s prvky ze stejného tělesa TT můžeme přirozeně sčítat prvek po prvku, (A+B)k,:=Ak,+Bk,,(\mathbb{A} + \mathbb{B})_{k,\ell} := \mathbb{A}_{k,\ell} + \mathbb{B}_{k,\ell}, pro každé km^k\in\hat m a n^\ell\in\hat n.

V Sage této operaci odpovídá + mezi maticemi stejných rozměrů. Podobně bude přirozeně fungovat operátor -.

A = matrix(QQ, [
    [1, 1/2, 1/3],
    [3, 2,   1]
])
B = matrix(QQ, [
    [-1, -1, -1],
    [1/2, 0, 1/2]
])
show(A + B)
(0122372232)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 0 & -\frac{1}{2} & -\frac{2}{3} \\ \frac{7}{2} & 2 & \frac{3}{2} \end{array}\right)

Důležité je uvědomit si, že operace sčítání mezi prvky matice se provádí v tělese TT!

A = matrix(GF(7), [
    [1, 2, 3],
    [5, 5, 2]
])
B = matrix(GF(7), [
    [6, 1, 1],
    [2, 4, 3]
])
show(A + B)
(034025)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 0 & 3 & 4 \\ 0 & 2 & 5 \end{array}\right)

Maticové operace: násobení matic

Tato operace mezi maticemi může při prvním setkání vypadat poněkud komplikovaně.

Mějme matice ATm,n\mathbb{A} \in T^{m,n} a BTn,o\mathbb{B} \in T^{n,o}. Tedy A\mathbb{A} má stejně sloupců jako B\mathbb{B} řádků. Součinem těchto dvou matic je matice ABTm,o\mathbb{A}\mathbb{B} \in T^{m,o} jejíž prvky jsou dány následovně (AB)k,:=j=1nAk,jBj,,(\mathbb{A} \mathbb{B})_{k,\ell} := \sum_{j=1}^n \mathbb{A}_{k,j} \mathbb{B}_{j,\ell}, pro každé km^k\in\hat m a o^\ell\in\hat o.

Pokud se nad uvedenou sumou zamyslíme, tak vidíme, že prvek součinu AB\mathbb{A}\mathbb{B} na kktém řádku a \elltém sloupci vznikl tak, že jsme vzali kktý řádek matice A\mathbb{A} a \elltý řádek matice B\mathbb{B} (tyto mají stejný počet prvků), vynásobili jsme je člen po členu (vnitřek sumy) a součiny sečetli (suma).

V tento okamžik se tato operace může zdát velmi "libovolná". Později během semestru si ukážeme, jak se k ní lze velmi přirozeně dostat při studiu lineárních zobrazení a jejich skládání.

V Sage této operaci odpovídá operátor * mezi maticemi správných rozměrů. Vezměmě konkrétní příklad.

A = matrix(QQ, [
    [1, 1/2, 1/3],
    [3, 2,   1]
])
B = matrix(QQ, [
    [-1, -1, -1],
    [1/2, 0, 1/2],
    [3,   4, 5]
])
show(A)
show(B)
print('Součin:')
show(A * B)
(11213321)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 1 & \frac{1}{2} & \frac{1}{3} \\ 3 & 2 & 1 \end{array}\right)
(11112012345)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} -1 & -1 & -1 \\ \frac{1}{2} & 0 & \frac{1}{2} \\ 3 & 4 & 5 \end{array}\right)
Součin:
(14131112113)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} \frac{1}{4} & \frac{1}{3} & \frac{11}{12} \\ 1 & 1 & 3 \end{array}\right)

Pojďme si tento výsledek podrobněji rozebrat a spočtěme prvek v prvním řádku a druhém sloupci. Ten by měl vzniknout tak, že vezme první řádek matice A a druhý sloupec matice B,

show(A[0, :])
show(B[:, 1])
(11213)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 1 & \frac{1}{2} & \frac{1}{3} \end{array}\right)
(104)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{r} -1 \\ 0 \\ 4 \end{array}\right)

vynásobíme je prvek po prvku a výsledky sečteme, dostaneme tak očekávaný výsledek

sum(A[0, j] * B[j, 1] for j in range(3)) # range(3) = {0, 1, 2}, více méně
1/3

Poznámka: Speciálně platí, že čtvercové matice stejného rozměru můžeme násobit dle libosti. Pro opakované násobení se přirozeně používá zkrácené značení Ak=AA\mathbb{A}^k = \mathbb{A} \cdots \mathbb{A} (kk matic A\mathbb{A} vynásobených samo se sebou).

A = matrix([
    [1, 2],
    [3, 4]
])
show(A^5)
(1069155823373406)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 1069 & 1558 \\ 2337 & 3406 \end{array}\right)

Poznámka: Další operací, kterou s maticemi lze provádět je inverze. Té se budeme věnovat později během semestru.

Poznámka: Operace násobení i sčítání matic jsou asociativní. Distributivita maticového násobení vůči sčítání také platí. Násobení matic ovšem není komutativní!!!

Následující výpočet není důkaz těchto tvrzení, pouze demonstrace o čem mluvíme.

A = matrix(QQ, [
    [1, 3],
    [1, 2]
])
B = matrix(QQ, [
    [1, -3],
    [3, 0]
])
C = matrix(QQ, [
    [0, -1],
    [1, 0]
])
print('A + (B + C) == (A + B) + C:')
print(A + (B + C) == (A + B) + C)
print('A * (B * C) == (A * B) * C:')
print(A * (B * C) == (A * B) * C)
print('A * (B + C) == (A * B) + (A * C):')
print(A * (B + C) == (A * B) + (A * C))
print('A * B != B * A:')
print(A * B == B * A)
A + (B + C) == (A + B) + C:
True
A * (B * C) == (A * B) * C:
True
A * (B + C) == (A * B) + (A * C):
True
A * B != B * A:
False

Opravdu, součiny těchto dvou matic jsou:

show(A * B)
show(B * A)
(10373)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} 10 & -3 \\ 7 & -3 \end{array}\right)
(2339)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rr} -2 & -3 \\ 3 & 9 \end{array}\right)

Dodatek: Další užitečné funkce

V tomto dodatku probereme pár užitečných způsobů jak konstruovat různé matice.

Matice s dvěma řádky a čtyřmi sloupci s náhodně zvolenými prvky z tělesa GF(3)GF(3).

show(random_matrix(GF(3), 2, 4))
(00121000)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrrr} 0 & 0 & 1 & 2 \\ 1 & 0 & 0 & 0 \end{array}\right)

Náhodná matice stejného rozměru, pouze s prvky zvolenými z tělesa racionálních čísel Q\mathbb{Q}.

show(random_matrix(QQ, 2, 4))
(122201001)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrrr} -\frac{1}{2} & 2 & -2 & 0 \\ -1 & 0 & 0 & -1 \end{array}\right)

Nulová matice (matice se všemi prvky nulovými) s čtyřmi řádky a třemi sloupci.

show(zero_matrix(4, 3))
(000000000000)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrr} 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \\ 0 & 0 & 0 \end{array}\right)

Matice rozměru 4×44 \times 4 se všemi prvky rovnými 22.

show(matrix(4, 4, 2))
matrix?
(2000020000200002)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrrr} 2 & 0 & 0 & 0 \\ 0 & 2 & 0 & 0 \\ 0 & 0 & 2 & 0 \\ 0 & 0 & 0 & 2 \end{array}\right)

Jednotková matice (matice s jedničkami na diagonále a nulami všude jinde) rozměru 5×55 \times 5.

show(identity_matrix(5, 5))
(1000001000001000001000001)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrrrr} 1 & 0 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 & 0 \\ 0 & 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 0 & 1 \end{array}\right)

Uvedené funkce umožňují i další možnosti jak matice konstruovat. Zvídavý čtenář se může inspirovat v dokumentaci. Alespoň jeden zajímavý netriviální příklad si uveďme.

f = lambda i, j: 1/(1 + i + j) # funkce dvou proměnných, f(i, j) = 1/(1 + i + j)
m = matrix(QQ, 5, 5, f)        # matice mající prvky zkonstruované pomocí uvedené funkce
show(m)
(1121314151213141516131415161714151617181516171819)\newcommand{\Bold}[1]{\mathbf{#1}}\left(\begin{array}{rrrrr} 1 & \frac{1}{2} & \frac{1}{3} & \frac{1}{4} & \frac{1}{5} \\ \frac{1}{2} & \frac{1}{3} & \frac{1}{4} & \frac{1}{5} & \frac{1}{6} \\ \frac{1}{3} & \frac{1}{4} & \frac{1}{5} & \frac{1}{6} & \frac{1}{7} \\ \frac{1}{4} & \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} \\ \frac{1}{5} & \frac{1}{6} & \frac{1}{7} & \frac{1}{8} & \frac{1}{9} \end{array}\right)