Obsah
1. Práce s vektory, maticemi a n-dimenzionálními poli v knihovně SymPy
2. Co si představit pod termínem „pole“?
3. Čtyři varianty reprezentace matic v knihovně SymPy
4. Konstrukce neměnného pole – matice
5. Tisk obsahu matice funkcí sympy.pprint
6. Konstrukce vektoru a matice s využitím třídy range
7. Počet dimenzí a tvar n-dimenzionálního pole
8. Změna tvaru n-dimenzionálního pole
10. Nemodifikovatelná vs. modifikovatelná pole
11. Vynásobení matice skalárem a součet matic (přetížené operátory)
13. Symbolická reprezentace matice
14. Ukázka symbolické reprezentace matice
15. Maticový součin v symbolické podobě
16. Transpozice matic v symbolické podobě
18. Výpočet inverze maticového součinu
19. Repositář s demonstračními příklady
1. Práce s vektory, maticemi a n-dimenzionálními poli v knihovně SymPy
Ve čtvrtém článku o knihovně SymPy (předchozí části jsou dostupné v [1], [2] a [3]), která je primárně určena pro provádění symbolických výpočtů v Pythonu, se budeme zabývat především zpracováním vektorů a matic. Některé dále popsané operace jsou podobné operacím dostupným v knihovně NumPy, další podobné vlastnosti můžeme najít například v knihovně SciPy (řídké matice atd.). Ovšem pro SymPy unikátní je podpora pro symbolické výpočty s maticemi.
Dnes se tedy začneme zabývat jednou poměrně rozsáhlou a současně i poněkud specifickou oblastí v informatice. Tou je zpracování vektorů, matic a taktéž vícerozměrných polí – obecně se v tomto kontextu mluví o n-rozměrných polích. S těmito velmi užitečnými datovými strukturami se můžeme setkat v různých (mnohdy zdánlivě i velmi vzdálených) disciplínách, například ve finančnictví, pojišťovnictví, statistice, zpracování numerických dat, simulacích, zpracování 1D a 2D signálů atd. Zapomenout ovšem nesmíme ani na strojové učení (machine learning) a umělou inteligencí (artifical intelligence), protože například datové struktury určené pro uložení neuronových sítí (zejména konvolučních sítí) jsou realizovány n-rozměrnými poli. Současně se jedná i o velmi zajímavou oblast, neboť právě kvůli nutnosti co nejrychlejší práce s velkými maticemi byly vytvořeny speciální výpočetní bloky v některých superpočítačích (příkladem mohou být superpočítače Cray) a došlo tak k důležitému podnětu pro další rozvoj výpočetní techniky (ten nepřímo vedl k vývoji moderních GPU). A pokud zůstaneme u 1D a 2D polí – zde došlo k rozšíření digitálních signálových procesorů orientovaných a optimalizovaných právě na tuto oblast.
Operace s poli jsou buď součástí syntaxe a sémantiky programovacích jazyků nebo jsou realizovány formou knihovny. V ekosystému programovacího jazyka Python se v první řadě jedná o již výše zmíněnou knihovnu NumPy, ovšem operace s vektory a maticemi (obecně s poli) jsou ve skutečnosti podporovány i v knihovně SymPy. A právě tímto tématem se budeme zabývat v dnešním článku.
2. Co si představit pod termínem „pole“?
V programovacích jazycích se termín „pole“ resp. array používá velmi často, ovšem ani zdaleka ne konzistentně. V případě, že se v dokumentaci jazyka bez dalších podrobností termín array použije, je vhodné hledat odpovědi na následující otázky:
- Kolik dimenzí může pole mít? Typicky 1 a 2, někdy i více.
- Začínají indexy prvků od 0, 1 či je první index volitelný?
- Jsou podporována obdélníková pole nebo nepravidelná pole?
- Jsou jednotlivé osy na sobě nezávislé? (což vylučuje nepravidelná pole)
- Je možné indexy na jednotlivých osách pojmenovat? (a vytvořit tak vlastně datový rámec)
- Jedná se o homogenní nebo o heterogenní datovou strukturu? Homogenní struktura může uchovávat prvky jediného (typicky předem definovaného) typu zatímco v heterogenní struktuře mohou být umístěny prvky různých typů.
- Je nějakým způsobem omezen datový typ prvků pole? (například jen na celá čísla a čísla reálná).
- Lze prvky pole měnit (mutable) nebo je pole neměnitelné (immutable).
- Pokud jsou pole heterogenní a měnitelná, může prvek pole obsahovat to samé pole?
- Obsahuje pole přímo hodnoty prvků nebo jen reference na prvky?
- Jsou prvky v poli uloženy v operační paměti za sebou nebo se jedná o strukturu s ukazateli?
- Jsou prvky v 2D poli uloženy po řádcích nebo po sloupcích? (C versus Fortran).
- Lze měnit tvar (shape) pole?
- Podporuje jazyk operace nad celými poli?
- Podporuje jazyk takzvaný broadcasting (aplikaci skaláru na všechny prvky pole atd.)?
- Jsou pole plnohodnotným datovým typem nebo speciální strukturou?
- Je podporován „literál typu pole“?
3. Čtyři varianty reprezentace matic v knihovně SymPy
V knihovně SymPy je možné hodnoty ukládat do čtyř typů polí, jejichž názvy i stručný popis jsou uvedeny v následující tabulce:
Třída | Stručný popis | Zdrojový kód |
---|---|---|
sympy.tensor.array.ImmutableDenseNDimArray | nemodifikovatelné pole | link |
sympy.tensor.array.ImmutableSparseNDimArray | nemodifikovatelné řídké pole | link |
sympy.tensor.array.MutableDenseNDimArray | modifikovatelné pole | link |
sympy.tensor.array.MutableSparseNDimArray | modifikovatelné řídké pole | link |
Vidíme tedy, že ony čtyři varianty N-dimenzionálních polí můžeme rozdělit podle dvou kritérií:
- Modifikovatelné vs. nemodifikovatelné, tedy podle toho, zda je možné obsah pole měnit či nikoli (tedy zda lze zapisovat nové hodnoty prvků).
- „Husté“ vs. „řídké“ pole, přičemž řídké pole je uloženo takovým způsobem, aby nebylo nutné explicitně ukládat prvky s nejčastější hodnotou (typicky se jedná o nulovou hodnotu).
4. Konstrukce neměnného pole – matice
Pro konstrukci neměnného (immutable) pole, ať již se má jednat o vektor, matici, či o vícedimenzionální pole, se používá konstruktor nazvaný jednoduše Array. Tomuto konstruktoru postačuje předat seznam, n-tici či seznam seznamů s prvky vloženými do budoucího pole.
V dnešním prvním demonstračním příkladu vytvoříme dvojrozměrné pole z prvků předaných v seznamu seznamů:
import sympy as sp a = sp.Array([ [1, 2, 3], [4, 5, 6], [7, 8, 9]]) print(a) print(type(a))
Pole se při použití standardní funkce print vypíše tímto způsobem:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
Přičemž jeho typ je následující:
<class 'sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray'>
Naprosto stejným způsobem lze pole zkonstruovat z prvků předaných v n-tici (což je neměnitelná obdoba seznamů):
import sympy as sp a = sp.Array(( (1, 2, 3), (4, 5, 6), (7, 8, 9))) print(a) print(type(a))
Výsledkem bude pole (matice) zcela totožné s polem předchozím:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]] <class 'sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray'>
5. Tisk obsahu matice funkcí sympy.pprint
Tisk matice či dokonce vícerozměrného pole ve tvaru:
[[1, 2, 3], [4, 5, 6], [7, 8, 9]]
je poměrně nečitelný. Ovšem jak již víme z předchozích článků, podporuje knihovna SymPy i „pěkný“ tisk realizovaný funkcí sympy.pprint. Její chování si můžeme otestovat velmi snadno:
import sympy as sp a = sp.Array([ [1, 2, 3], [4, 5, 6], [7, 8, 9]]) sp.pprint(a)
Po spuštění tohoto skriptu by se na terminál měl vypsat následující výsledek:
⎡1 2 3⎤ ⎢ ⎥ ⎢4 5 6⎥ ⎢ ⎥ ⎣7 8 9⎦
6. Konstrukce vektoru a matice s využitím třídy range
Konstruktor sympy.Array akceptuje jako svůj první parametr i instanci standardní třídy range. Můžeme tak velmi snadno zkonstruovat například jednorozměrný vektor, a to konkrétně následující konstrukcí:
import sympy as sp a = sp.Array(range(9)) sp.pprint(a)
S výsledkem:
[0 1 2 3 4 5 6 7 8]
Pro konstrukci (dvourozměrné) matice je navíc nutné konstruktoru Array předat i tvar (shape) matice; v našem případě konkrétně půjde o matici se čtyřmi řádky a třemi sloupci. Tvar matice se předává n-ticí nebo seznamem:
import sympy as sp a = sp.Array(range(12), (4,3)) sp.pprint(a)
Nyní bude výsledek získaný po spuštění skriptu vypadat takto:
⎡0 1 2 ⎤ ⎢ ⎥ ⎢3 4 5 ⎥ ⎢ ⎥ ⎢6 7 8 ⎥ ⎢ ⎥ ⎣9 10 11⎦
7. Počet dimenzí a tvar n-dimenzionálního pole
Počet dimenzí pole je možné zjistit metodou nazvanou rank zatímco takzvaný tvar pole (počet prvků ve směru jednotlivých os) se kupodivu nezjišťuje metodou, ale je uložen v atributu shape. Nejdříve se podívejme na zjištění těchto dvou velmi důležitých vlastností n-dimenzionálních polí pro jednodimenzionální vektor:
import sympy as sp a = sp.Array(range(9)) sp.pprint(a) print(a.rank()) print(a.shape)
Výsledek:
[0 1 2 3 4 5 6 7 8] 1 (9,)
Podobný příklad, ovšem pro dvourozměrnou matici:
import sympy as sp a = sp.Array(range(12), (4,3)) sp.pprint(a) print(a.rank()) print(a.shape)
S výsledky:
⎡0 1 2 ⎤ ⎢ ⎥ ⎢3 4 5 ⎥ ⎢ ⎥ ⎢6 7 8 ⎥ ⎢ ⎥ ⎣9 10 11⎦ 2 (4, 3)
8. Změna tvaru n-dimenzionálního pole
Další velmi důležitou operací, s níž se v praxi často setkáme, je operace nazvaná reshape(), která dokáže změnit velikost matice a vhodným způsobem přeorganizovat prvky v původní matici (operace je převzata z jazyka APL – jak jinak). Tato operace je představována metodou nazvanou reshape se předává tvar nového n-dimenzionálního pole. Původní pole přitom zůstane nezměněno.
V dalším demonstračním příkladu nejdříve vytvoříme jednodimenzionální vektor se šestnácti prvky a následně z tohoto vektoru vytvoříme dvoudimenzionální matice i pole se třemi dimenzemi. Povšimněte si, že ve všech případech je zachován počet prvků vytvořeného n-dimenzionálního pole (16):
import sympy as sp a1 = sp.Array(range(16)) sp.pprint(a1) print() a2 = a1.reshape(2,8) sp.pprint(a2) print() a3 = a1.reshape(4,4) sp.pprint(a3) print() a4 = a1.reshape(4,2,2) sp.pprint(a4) print() a5 = a1.reshape(2,4,2) sp.pprint(a5) print() a6 = a1.reshape(2,2,4) sp.pprint(a6) print()
Výsledná pole získaná tímto skriptem:
Původní vektor se šestnácti prvky:
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]
Dvourozměrné matice:
⎡0 1 2 3 4 5 6 7 ⎤ ⎢ ⎥ ⎣8 9 10 11 12 13 14 15⎦ ⎡0 1 2 3 ⎤ ⎢ ⎥ ⎢4 5 6 7 ⎥ ⎢ ⎥ ⎢8 9 10 11⎥ ⎢ ⎥ ⎣12 13 14 15⎦
Trojrozměrná pole:
⎡⎡0 1⎤ ⎡4 5⎤ ⎡8 9 ⎤ ⎡12 13⎤⎤ ⎢⎢ ⎥ ⎢ ⎥ ⎢ ⎥ ⎢ ⎥⎥ ⎣⎣2 3⎦ ⎣6 7⎦ ⎣10 11⎦ ⎣14 15⎦⎦ ⎡⎡0 1⎤ ⎡8 9 ⎤⎤ ⎢⎢ ⎥ ⎢ ⎥⎥ ⎢⎢2 3⎥ ⎢10 11⎥⎥ ⎢⎢ ⎥ ⎢ ⎥⎥ ⎢⎢4 5⎥ ⎢12 13⎥⎥ ⎢⎢ ⎥ ⎢ ⎥⎥ ⎣⎣6 7⎦ ⎣14 15⎦⎦ ⎡⎡0 1 2 3⎤ ⎡8 9 10 11⎤⎤ ⎢⎢ ⎥ ⎢ ⎥⎥ ⎣⎣4 5 6 7⎦ ⎣12 13 14 15⎦⎦
Vytvořit lze pochopitelně i pole s větším počtem dimenzí:
import sympy as sp a1 = sp.Array(range(16)) sp.pprint(a1) print(a1.rank()) print(a1.shape) print() a2 = a1.reshape(2,2,2,2) sp.pprint(a2) print(a2.rank()) print(a2.shape)
Výsledkem činnosti tohoto skriptu bude pole 2×2×2×2 prvky:
[0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15] 1 (16,) ⎡ ⎡0 1⎤ ⎡4 5⎤ ⎤ ⎢ ⎢ ⎥ ⎢ ⎥ ⎥ ⎢ ⎣2 3⎦ ⎣6 7⎦ ⎥ ⎢ ⎥ ⎢⎡8 9 ⎤ ⎡12 13⎤⎥ ⎢⎢ ⎥ ⎢ ⎥⎥ ⎣⎣10 11⎦ ⎣14 15⎦⎦ 4 (2, 2, 2, 2)
9. Řídká pole
V mnoha oblastech se poměrně často setkáme se situací, kdy je nutné pracovat s obrovskými n-rozměrnými poli (typicky s maticemi), které ovšem převážně obsahují prvky se shodnou hodnotou. Příkladem mohou být takzvané matice incidence používané pro reprezentaci grafů. V případě, že má graf relativně velké množství uzlů, ovšem málo hran, bude matice incidence obsahovat mnoho nulových prvků. A právě v těchto případech je možné využít takzvané řídké matice resp. obecně řídká n-rozměrná pole (sparse array). Podívejme se nyní na rozdílný způsob konstrukce „hustých“ a „řídkých“ polí. Nejdříve zkonstruujeme „hustou“ neměnitelnou matici o rozměrech 5×5 prvků, která bude obsahovat dvacet pět nulových prvků:
import sympy as sp a1 = sp.Array([0]*25) a2 = a1.reshape(5,5) print(type(a2)) print() sp.pprint(a2)
Tento skript nejdříve vypíše typ pole a posléze i jeho obsah:
<class 'sympy.tensor.array.dense_ndim_array.ImmutableDenseNDimArray'> ⎡0 0 0 0 0⎤ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎣0 0 0 0 0⎦
Naproti tomu řídkou matici musíme vytvořit konstruktorem ImmutableSparseNDimArray či MutableSparseNDimArray, a to následujícím způsobem:
import sympy as sp a1 = sp.ImmutableSparseNDimArray([0]*25) a2 = a1.reshape(5,5) print(type(a2)) print() sp.pprint(a2)
I tento skript nejdříve vypíše typ pole a posléze i jeho obsah:
<class 'sympy.tensor.array.sparse_ndim_array.ImmutableSparseNDimArray'> ⎡0 0 0 0 0⎤ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎣0 0 0 0 0⎦
10. Nemodifikovatelná vs. modifikovatelná pole
N-rozměrné pole vytvořené konstruktory Array, ImmutableDenseNDimArray či ImmutableSparseNDimArray je neměnitelné (nemodifikovatelné), což znamená, že ani není možné měnit hodnotu jeho prvků. Opět si to pochopitelně můžeme otestovat na jednoduchém příkladu, v němž se pokusíme změnit hodnotu prostředního prvku v matici (povšimněte si způsobu zápisu „souřadnic“ tohoto prvku s využitím n-tice):
import sympy as sp a1 = sp.ImmutableSparseNDimArray([0]*25) a2 = a1.reshape(5,5) print(type(a2)) print() sp.pprint(a2) a2[(2,2)] = 42 print() sp.pprint(a2)
Tento příklad nejprve vypíše typ pole:
<class 'sympy.tensor.array.sparse_ndim_array.ImmutableSparseNDimArray'>
Následně jeho původní obsah:
⎡0 0 0 0 0⎤ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎣0 0 0 0 0⎦
A po pokusu o modifikaci prvku pole tento skript zhavaruje:
Traceback (most recent call last): File "/home/user/sympy69.py", line 11, in <module> a2[(2,2)] = 42 File "/home/user/.local/lib/python3.10/site-packages/sympy/tensor/array/sparse_ndim_array.py", line 132, in __setitem__ raise TypeError("immutable N-dim array") TypeError: immutable N-dim array
Situaci napravíme jednoduše, a to použitím konstruktoru MutableDenseNDimArray nebo MutableSparseNDimArray:
import sympy as sp a1 = sp.MutableSparseNDimArray([0]*25) a2 = a1.reshape(5,5) print(type(a2)) print() sp.pprint(a2) a2[(2,2)] = 42 print() sp.pprint(a2)
Chování skriptu – nejdříve vypíše typ pole:
<class 'sympy.tensor.array.sparse_ndim_array.MutableSparseNDimArray'>
Dále se zobrazí původní pole před jeho modifikací:
⎡0 0 0 0 0⎤ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎣0 0 0 0 0⎦
Pole po modifikaci – povšimněte si prvku uprostřed pole (matice):
⎡0 0 0 0 0⎤ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎢0 0 42 0 0⎥ ⎢ ⎥ ⎢0 0 0 0 0⎥ ⎢ ⎥ ⎣0 0 0 0 0⎦
11. Vynásobení matice skalárem a součet matic (přetížené operátory)
Knihovna SymPy podporuje provádění základních operací s maticemi s využitím přetížených (původně většinou aritmetických) operátorů. V následujícím příkladu je ukázáno, jak lze využít přetížené operátory * a + pro vynásobení všech prvků matice skalární hodnotou a následně pro součet matic (které by měly mít stejný počet dimenzí i shodný tvar):
import sympy as sp a1 = sp.ImmutableSparseNDimArray(range(12)) a2 = a1.reshape(3,4) sp.pprint(a2) print() a3 = 2*a2 sp.pprint(a3) print() a4 = a2 + a3 sp.pprint(a4)
Původní matice vypsaná skriptem:
⎡0 1 2 3 ⎤ ⎢ ⎥ ⎢4 5 6 7 ⎥ ⎢ ⎥ ⎣8 9 10 11⎦
Nová matice vytvořená vynásobení všech prvků původní matice skalární hodnotou:
⎡0 2 4 6 ⎤ ⎢ ⎥ ⎢8 10 12 14⎥ ⎢ ⎥ ⎣16 18 20 22⎦
Další matice, která vznikla součtem obou předchozích matic:
⎡0 3 6 9 ⎤ ⎢ ⎥ ⎢12 15 18 21⎥ ⎢ ⎥ ⎣24 27 30 33⎦
12. Transpozice matic
Knihovna SymPy podporuje i transpozici matic. Pro tento účel se používá metoda nazvaná transpose(), která vrátí novou matici, jež je odvozena z matice zdrojové. Opět se pochopitelně podíváme na příklad:
import sympy as sp a1 = sp.ImmutableSparseNDimArray(range(12)) a2 = a1.reshape(3,4) sp.pprint(a2) print() a3 = a2.transpose() sp.pprint(a3)
Tento skript nejdříve zobrazí původní matici a posléze matici transponovanou:
⎡0 1 2 3 ⎤ ⎢ ⎥ ⎢4 5 6 7 ⎥ ⎢ ⎥ ⎣8 9 10 11⎦ ⎡0 4 8 ⎤ ⎢ ⎥ ⎢1 5 9 ⎥ ⎢ ⎥ ⎢2 6 10⎥ ⎢ ⎥ ⎣3 7 11⎦
13. Symbolická reprezentace matice
Jméno knihovny SymPy je odvozeno od slova „symbolic“, což znamená, že manipulace s matematickými vztahy (typicky s rovnicemi, ale například i se zápisem transformací typu derivace a integrace) je prováděn v symbolické formě, nikoli numericky. To do jisté míry platí i pro matice. Prozatím jsme s maticemi (a obecně n-dimenzionálními poli) pracovali jako s kontejnery na numerické hodnoty, s nimiž je možné provádět numerické operace (maticový součin atd.), ovšem SymPy umožňuje na matice nahlížet taktéž jako na kontejnery, v nichž má každý prvek určité symbolické jméno, například A₁₂. Takové matice se konstruují s využitím konstruktoru MatrixSymbol, který použijeme v navazujících kapitolách. A operace s takovými maticemi jsou opět prováděny na symbolické úrovni, tj. výsledkem typicky bývají opět „symbolické“ matice.
14. Ukázka symbolické reprezentace matice
V této kapitole si ukážeme, jak se pracuje se symbolickou reprezentací matice. Takovou matici je zapotřebí deklarovat, přičemž namísto nám již známého konstruktoru Symbols použijeme konstruktor MatrixSymbol, jemuž se kromě symbolického jména matice předávají i její rozměry. Matice se třemi řádky a čtyřmi sloupci se zkonstruuje takto:
a = sp.MatrixSymbol("A", 3, 4)
Taková matice se vypíše v symbolické podobě příkazem:
sp.pprint(sp.Matrix(a))
Podívejme se na skript, který tyto operace provádí:
import sympy as sp a = sp.MatrixSymbol("A", 3, 4) print(a) print() print(sp.Matrix(a)) print() sp.pprint(sp.Matrix(a))
Tento skript postupně zobrazí jméno matice (jako symbolu), obsah matice v „řádkové“ formě a obsah matice v „čitelné“ formě:
A Matrix([[A[0, 0], A[0, 1], A[0, 2], A[0, 3]], [A[1, 0], A[1, 1], A[1, 2], A[1, 3]], [A[2, 0], A[2, 1], A[2, 2], A[2, 3]]]) ⎡A₀₀ A₀₁ A₀₂ A₀₃⎤ ⎢ ⎥ ⎢A₁₀ A₁₁ A₁₂ A₁₃⎥ ⎢ ⎥ ⎣A₂₀ A₂₁ A₂₂ A₂₃⎦
15. Maticový součin v symbolické podobě
Vyzkoušejme si nyní, jak vlastně bude vypadat maticový součin pro matice zapsané v symbolické podobě. Výsledkem by přitom opět měla být matice v symbolické podobě. Nejdříve obě matice vytvoříme:
a = sp.MatrixSymbol("A", 3, 4) b = sp.MatrixSymbol("B", 4, 3)
Nyní vypočteme součin a necháme si zobrazit výslednou matici:
c = a*b sp.pprint(sp.Matrix(c))
Úplný skript provádějící tuto operaci vypadá následovně:
import sympy as sp a = sp.MatrixSymbol("A", 3, 4) b = sp.MatrixSymbol("B", 4, 3) sp.pprint(sp.Matrix(a)) print() sp.pprint(sp.Matrix(b)) print() c = a*b sp.pprint(sp.Matrix(c))
První matice vstupující do součinu:
⎡A₀₀ A₀₁ A₀₂ A₀₃⎤ ⎢ ⎥ ⎢A₁₀ A₁₁ A₁₂ A₁₃⎥ ⎢ ⎥ ⎣A₂₀ A₂₁ A₂₂ A₂₃⎦
Druhá matice vstupující do součinu:
⎡B₀₀ B₀₁ B₀₂⎤ ⎢ ⎥ ⎢B₁₀ B₁₁ B₁₂⎥ ⎢ ⎥ ⎢B₂₀ B₂₁ B₂₂⎥ ⎢ ⎥ ⎣B₃₀ B₃₁ B₃₂⎦
Symbolicky zapsaný výsledek maticového součinu
⎡A₀₀⋅B₀₀ + A₀₁⋅B₁₀ + A₀₂⋅B₂₀ + A₀₃⋅B₃₀ A₀₀⋅B₀₁ + A₀₁⋅B₁₁ + A₀₂⋅B₂₁ + A₀₃⋅B₃₁ A₀₀⋅B₀₂ + A₀₁⋅B₁₂ + A₀₂⋅B₂₂ + A₀₃⋅B₃₂⎤ ⎢ ⎥ ⎢A₁₀⋅B₀₀ + A₁₁⋅B₁₀ + A₁₂⋅B₂₀ + A₁₃⋅B₃₀ A₁₀⋅B₀₁ + A₁₁⋅B₁₁ + A₁₂⋅B₂₁ + A₁₃⋅B₃₁ A₁₀⋅B₀₂ + A₁₁⋅B₁₂ + A₁₂⋅B₂₂ + A₁₃⋅B₃₂⎥ ⎢ ⎥ ⎣A₂₀⋅B₀₀ + A₂₁⋅B₁₀ + A₂₂⋅B₂₀ + A₂₃⋅B₃₀ A₂₀⋅B₀₁ + A₂₁⋅B₁₁ + A₂₂⋅B₂₁ + A₂₃⋅B₃₁ A₂₀⋅B₀₂ + A₂₁⋅B₁₂ + A₂₂⋅B₂₂ + A₂₃⋅B₃₂⎦
16. Transpozice matic v symbolické podobě
Pro získání transponované matice v symbolické podobě postačuje (což je zvláštní) přistoupit k atributu T. Je tomu tak z toho důvodu, že se zápis atributu do určité míry přibližuje zápisu MT, který nelze takto přímo v Pythonu použít:
import sympy as sp a = sp.MatrixSymbol("A", 3, 4) sp.pprint(sp.Matrix(a)) print() sp.pprint(sp.Matrix(a.T))
Tento skript nejdříve vypíše původní matici a posléze matici transponovanou:
⎡A₀₀ A₀₁ A₀₂ A₀₃⎤ ⎢ ⎥ ⎢A₁₀ A₁₁ A₁₂ A₁₃⎥ ⎢ ⎥ ⎣A₂₀ A₂₁ A₂₂ A₂₃⎦ ⎡A₀₀ A₁₀ A₂₀⎤ ⎢ ⎥ ⎢A₀₁ A₁₁ A₂₁⎥ ⎢ ⎥ ⎢A₀₂ A₁₂ A₂₂⎥ ⎢ ⎥ ⎣A₀₃ A₁₃ A₂₃⎦
17. Výpočet inverzní matice
Zápisem matice.I můžeme získat inverzní matici, a to samozřejmě opět v symbolické podobě (přístupem k atributu I, což evokuje zápis M-1). Podívejme se nyní na způsob výpočtu inverzní matice k relativně malé matici o rozměrech 2×2 prvky:
import sympy as sp a = sp.MatrixSymbol("A", 2, 2) sp.pprint(sp.Matrix(a)) print() sp.pprint(sp.Matrix(a.I))
Nejprve se vypíše původní matice:
⎡A₀₀ A₀₁⎤ ⎢ ⎥ ⎣A₁₀ A₁₁⎦
A následně matice inverzní:
⎡ A₁₁ -A₀₁ ⎤ ⎢───────────────── ─────────────────⎥ ⎢A₀₀⋅A₁₁ - A₀₁⋅A₁₀ A₀₀⋅A₁₁ - A₀₁⋅A₁₀⎥ ⎢ ⎥ ⎢ -A₁₀ A₀₀ ⎥ ⎢───────────────── ─────────────────⎥ ⎣A₀₀⋅A₁₁ - A₀₁⋅A₁₀ A₀₀⋅A₁₁ - A₀₁⋅A₁₀⎦
Kontrola výpočtu:
import sympy as sp a = sp.MatrixSymbol("A", 2, 2) i = a.I r = a*i sp.pprint(sp.Matrix(r))
S výsledkem:
⎡1 0⎤ ⎢ ⎥ ⎣0 1⎦
Což je (podle očekávání) skutečně jednotková matice.
18. Výpočet inverze maticového součinu
V dnešním posledním demonstračním příkladu je ukázán výpočet maticového součinu (ten již ostatně velmi dobře známe), přičemž z výsledku této operace je vypočtena inverzní matice. Obě vstupní matice, které budeme násobit, mají pro jednoduchost rozměry 2×2 prvky. Pro větší matice totiž trvá výpočet inverzní matice neúměrně dlouhou dobu:
import sympy as sp a = sp.MatrixSymbol("A", 2, 2) b = sp.MatrixSymbol("B", 2, 2) c = a*b sp.pprint(sp.Matrix(c)) print() sp.pprint(sp.Matrix(c.I))
Skript po svém spuštění nejdříve vypíše výsledek maticového součinu a posléze i inverzní matici k tomuto výsledku:
⎡A₀₀⋅B₀₀ + A₀₁⋅B₁₀ A₀₀⋅B₀₁ + A₀₁⋅B₁₁⎤ ⎢ ⎥ ⎣A₁₀⋅B₀₀ + A₁₁⋅B₁₀ A₁₀⋅B₀₁ + A₁₁⋅B₁₁⎦ ⎡ A₁₀⋅B₀₁ A₁₁⋅B₁₁ ⎢ ─────────────────────────────────────── + ────────────────────────────────── ⎢ (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁₁ - B₀₁⋅B₁₀) (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁₁ - B₀₁ ⎢ ⎢ A₁₀⋅B₀₀ A₁₁⋅B₁₀ ⎢- ─────────────────────────────────────── - ───────────────────────────────── ⎣ (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁₁ - B₀₁⋅B₁₀) (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁₁ - B₀ A₀₀⋅B₀₁ A₀₁⋅B₁₁ ───── - ─────────────────────────────────────── - ────────────────────────── ⋅B₁₀) (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁₁ - B₀₁⋅B₁₀) (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B A₀₀⋅B₀₀ A₀₁⋅B₁₀ ────── ─────────────────────────────────────── + ─────────────────────────── ₁⋅B₁₀) (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁₁ - B₀₁⋅B₁₀) (A₀₀⋅A₁₁ - A₀₁⋅A₁₀)⋅(B₀₀⋅B₁ ⎤ ─────────────⎥ ₁₁ - B₀₁⋅B₁₀)⎥ ⎥ ⎥ ──────────── ⎥ ₁ - B₀₁⋅B₁₀) ⎦
19. Repositář s demonstračními příklady
Zdrojové kódy všech prozatím popsaných demonstračních příkladů určených pro programovací jazyk Python 3 byly uloženy do Git repositáře dostupného na adrese https://github.com/tisnik/most-popular-python-libs. V případě, že nebudete chtít klonovat celý repositář (ten je ovšem stále velmi malý, dnes má velikost zhruba několik desítek kilobajtů), můžete namísto toho použít odkazy na jednotlivé příklady, které naleznete v následující tabulce:
20. Odkazy na Internetu
- SymPy
https://www.sympy.org/en/index.html - SymPy na PyPi
https://pypi.org/project/sympy/ - mpmath
https://mpmath.org/ - mpmath na PyPi
https://pypi.org/project/mpmath/ - Symbolic Maths in Python
https://alexandrugris.github.io/maths/2017/04/30/symbolic-maths-python.html - SymPy shell
https://live.sympy.org/ - Symbolic programming
https://en.wikipedia.org/wiki/Symbolic_programming - Symbolic language (programming)
https://en.wikipedia.org/wiki/Symbolic_language_(programming) - Computer algebra
https://en.wikipedia.org/wiki/Computer_algebra - Common Lisp: A Gentle Introduction to Symbolic Computation
https://www.cs.cmu.edu/~dst/LispBook/ - List of computer algebra systems
https://en.wikipedia.org/wiki/List_of_computer_algebra_systems - Polynom
https://cs.wikipedia.org/wiki/Polynom - What is SimPy? How to run python simulations?
https://divyas090909.medium.com/what-is-simpy-how-to-run-python-simulations-348736b50615 - SimPy: Simulating Real-World Processes With Python
https://realpython.com/simpy-simulating-with-python/ - Jazyky umožňující operace s poli aneb rozsáhlý svět „array programmingu“
https://www.root.cz/clanky/jazyky-umoznujici-operace-s-poli-aneb-rozsahly-svet-bdquo-array-programmingu-ldquo/ - What is an Array?
https://vector.org.uk/what-is-an-array/ - Vector (Wolfram MathWorld)
https://mathworld.wolfram.com/Vector.html - n-Tuple (Wolfram MathWorld)
https://mathworld.wolfram.com/n-Tuple.html - n-Vector (Wolfram MathWorld)
https://mathworld.wolfram.com/n-Vector.html - Matrix (Wolfram MathWorld)
https://mathworld.wolfram.com/Matrix.html - Array (Wolfram MathWorld)
https://mathworld.wolfram.com/Array.html - ND Arrays (Tensors) in different languages
https://www.youtube.com/watch?v=WbpbEilgQBc - Extending APL to Infinity\
https://www.jsoftware.com/papers/eem/infinity.htm - Sparse matrix
https://en.wikipedia.org/wiki/Sparse_matrix - Matice incidence
https://cs.wikipedia.org/wiki/Matice_incidence