Obsah
1. Programovací jazyk Julia: pole, vektory, matice a lineární algebra
2. Jednorozměrná pole a dvourozměrná pole s jedním řádkem
3. Vícerozměrná pole, sloupcové a řádkové vektory
4. Vytvoření pole konstruktorem Array
7. Funkce range, linspace a collect
9. Operace slicedim pro řezy maticemi, převod matice na vektor, přístup k prvkům na diagonále
10. Lineární algebra: základní operace
1. Programovací jazyk Julia: pole, vektory, matice a lineární algebra
Již v předchozích článcích věnovaných programovacím jazykům APL a J jsme se zmínili o tom, že jednou poměrně rozsáhlou oblastí v informatice je zpracování vektorů a matic, protože s těmito důležitými datovými strukturami se můžeme setkat v různých disciplínách, například ve finančnictví, pojišťovnictví, statistice, zpracování rozsáhlých numerických dat, simulacích, zpracování 1D a 2D signálů atd. Současně se jedná i o velmi zajímavou oblast, neboť právě kvůli co nejrychlejší práci 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). Současné knihovny dokážou v případě potřeby využít jak některé rozšíření instrukčních sad (SIMD instrukce typu SSE, původně též MMX či 3DNow!), tak i programovatelné grafické akcelerátory (GPU). Práce s vektory a maticemi byla (a samozřejmě doposud je) podporována především v překladačích programovacího jazyka FORTRAN.
Překladače FORTRANu začaly být po vzniku superpočítačů vybavovány algoritmy, které dokázaly převést některé typy programových smyček na „vektorové operace“. Paralelně vznikly i specializované jazyky určené téměř výhradně pro práci s vektory i maticemi – příkladem je již zmíněná dvojice APL a J. Dnes se seznámíme s podporou práce s vektory a maticemi v programovacím jazyce Julia. Kromě toho si řekneme, jak tento jazyk dokáže využívat optimalizované a odladěné funkce dostupné ve známé knihovně LAPACK (Linear Algebra Package). Díky použití této knihovny tak může jazyk Julia a jeho uživatelé využít jednu z nejznámějších sad algoritmů pro zpracování vektorů a především matic (samotná knihovna LAPACK je překládána Fortranem 90, původní verze pak FORTRANem 77).
2. Jednorozměrná pole a dvourozměrná pole s jedním řádkem
Základní homogenní datovou strukturou, kterou programovací jazyk Julia svým uživatelům nabízí, jsou jednorozměrná pole. Všechny prvky pole mají stejný typ (ostatně právě proto je to homogenní datová struktura) a ke každému prvku je možné přistupovat přes jeho index, přičemž indexování prvků má konstantní složitost (nezáleží tedy na délce pole). Prvky v běžných jednorozměrných polích je možné měnit, takže pole jsou měnitelné datové struktury (mutable). Podívejme se nyní na způsob vytvoření jednorozměrných polí:
julia> a=[1, 2, 3, 4, 5]
5-element Array{Int64,1}:
1
2
3
4
5
Při konstrukci pole se automaticky může zjistit datový typ prvků (resp. typ, který všem prvkům odpovídá po případné konverzi). Povšimněte si, jak se jazyk rozhoduje, který typ použít:
julia> a=[1, 2.1, 1//3]
3-element Array{Float64,1}:
1.0
2.1
0.333333
julia> a=[1, 2, 1//3]
3-element Array{Rational{Int64},1}:
1//1
2//1
1//3
julia> a=[1/0, -1/0, 0/0]
3-element Array{Float64,1}:
Inf
-Inf
NaN
julia> a=[pi, pi]
2-element Array{Irrational{:π},1}:
π = 3.1415926535897...
π = 3.1415926535897...
Typ je možné specifikovat explicitně:
julia> Int8[1, 2, 3, 4, 5]
5-element Array{Int8,1}:
1
2
3
4
5
julia> Float16[1, 2, 3, 4, 5]
5-element Array{Float16,1}:
1.0
2.0
3.0
4.0
5.0
Pokud vynecháte čárky, vytvoří se ve skutečnosti dvourozměrné pole s jedním řádkem:
julia> a=[1 2 3 4 5]
1x5 Array{Int64,2}:
1 2 3 4 5
julia> Float16[1 2 3 4 5]
1x5 Array{Float16,2}:
1.0 2.0 3.0 4.0 5.0
julia> a=[1 2 3 4]
1x4 Array{Int64,2}:
1 2 3 4
3. Vícerozměrná pole, sloupcové a řádkové vektory
Jak se však vytváří dvourozměrná pole? První pokus, který může vycházet ze zkušeností z jiných programovacích jazyků, není příliš úspěšný:
julia> [[1,2,3], [4,5,6]]
WARNING: [a,b] concatenation is deprecated; use [a;b] instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 0
6-element Array{Int64,1}:
1
2
3
4
5
6
Problém představuje čárka vložená mezi oba vektory. Jedno z možných řešení může vypadat takto – vytvoříme vlastně pole složené ze dvou sloupců (povšimněte si chybějící čárky mezi vektory):
julia> [[1,2,3] [4,5,6]]
3x2 Array{Int64,2}:
1 4
2 5
3 6
Pokud preferujete zápis po řádcích, lze použít tento alternativní způsob se středníkem. Je to sice poněkud neobvyklé, ale středník zde nahrazuje volání funkce hvcat() zmíněné níže:
julia> a=[1 2; 3 4]
2x2 Array{Int64,2}:
1 2
3 4
Pole se dvěma řádky a třemi sloupci:
julia> [1 2 3 ; 3 4 5]
2x3 Array{Int64,2}:
1 2 3
3 4 5
Kromě zápisu prvků pole do hranatých závorek lze pro konstrukci použít i funkce hcat („horizontal concatenate“), vcat („vertical concatenate“) a hvcat (kombinace obou možností se specifikací počtu sloupců):
julia> hcat(1,2,3,4)
1x4 Array{Int64,2}:
1 2 3 4
julia> vcat(1,2,3,4)
4-element Array{Int64,1}:
1
2
3
4
U funkce hvcat() si povšimněte, že první parametr specifikuje počet sloupců a až po něm následují jednotlivé prvky:
julia> hvcat(2,1,2,3,4)
2x2 Array{Int64,2}:
1 2
3 4
julia> hvcat(1,1,2,3,4)
4x1 Array{Int64,2}:
1
2
3
4
julia> hvcat(4,1,2,3,4)
1x4 Array{Int64,2}:
1 2 3 4
4. Vytvoření pole konstruktorem Array
Pro vytvoření pole s udáním typu prvků (ovšem bez inicializace jednotlivých prvků) slouží konstruktor nazvaný jednoduše Array. Při volání tohoto konstruktoru se nejprve ve složených závorkách specifikuje typ prvků a již běžně v kulatých závorkách pak rozměry pole v jednotlivých dimenzích:
help?> Array
search: Array SubArray BitArray DenseArray StridedArray mmap_array
Array(dims)
Array{T}(dims) constructs an uninitialized dense array with element type T.
dims may be a tuple or a series of integer arguments. The syntax Array(T,
dims) is also available, but deprecated.
Konstrukce pole o rozměrech 2×2 prvky typu Int8 (osmibitové celé číslo se znaménkem):
julia> a=Array{Int8}(2,2)
2x2 Array{Int8,2}:
112 -26
82 -34
Povšimněte si toho, že prvky skutečně nejsou inicializovány, ale obsahují obecně náhodné hodnoty:
julia> a=Array{Int8}(2,2)
2x2 Array{Int8,2}:
80 126
-123 -36
julia> a=Array{Int8}(2,2)
2x2 Array{Int8,2}:
112 -121
14 -33
Zde se nám čistě náhodou podařilo vytvořit pole s prvky s plovoucí řádovou čárkou s poloviční přesností, které mají hodnotu 0, ale samozřejmě se na to nelze spoléhat:
julia> a=Array{Float16}(10,10)
10x10 Array{Float16,2}:
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.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 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.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
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.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 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.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
Alternativní způsob zápisu s inicializací prvků:
julia> a = Array[[1,2], [3,4]]
2-element Array{Array{T,N},1}:
[1,2]
[3,4]
Při konstrukci pole se neprovádí automatické konverze, což souvisí se silným typovým systémem:
julia> a=Array{Int8}(1//2, 1//4)
ERROR: MethodError: `convert` has no method matching convert(::Type{Array{Int8,N}}, ::Rational{Int64}, ::Rational{Int64})
This may have arisen from a call to the constructor Array{Int8,N}(...),
since type constructors fall back to convert methods.
Closest candidates are:
call{T}(::Type{T}, ::Any)
convert{T,n}(::Type{Array{T,N}}, ::Array{T,n})
convert{T,n,S}(::Type{Array{T,N}}, ::Array{S,n})
...
in call at essentials.jl:57
julia> a=Array{Int8}(1.2, 3.4)
ERROR: MethodError: `convert` has no method matching convert(::Type{Array{Int8,N}}, ::Float64, ::Float64)
This may have arisen from a call to the constructor Array{Int8,N}(...),
since type constructors fall back to convert methods.
Closest candidates are:
call{T}(::Type{T}, ::Any)
convert{T,n}(::Type{Array{T,N}}, ::Array{T,n})
convert{T,n,S}(::Type{Array{T,N}}, ::Array{S,n})
...
in call at essentials.jl:57
5. Další konstruktory polí
Programovací jazyk Julia nabízí svým uživatelům i několik dalších funkcí určených pro konstrukci pole a současně i pro inicializaci jeho prvků. Pole o libovolných rozměrech s nulovými prvky se vytvoří funkcí zeros():
julia> b=zeros(5)
5-element Array{Float64,1}:
0.0
0.0
0.0
0.0
0.0
julia> b=zeros(5,5)
5x5 Array{Float64,2}:
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.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
Alternativně je možné při konstrukci pole specifikovat i typ prvků:
julia> c=zeros(Int8, 5, 5)
5x5 Array{Int8,2}:
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
Podobná funkce, ovšem pro inicializaci všech prvků na jedničku, se jmenuje pochopitelně ones():
julia> o=ones(10,10)
10x10 Array{Float64,2}:
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0 1.0
Alternativně je možné specifikovat i typ prvků:
julia> o=ones(Int8, 3, 3, 3)
3x3x3 Array{Int8,3}:
[:, :, 1] =
1 1 1
1 1 1
1 1 1
[:, :, 2] =
1 1 1
1 1 1
1 1 1
[:, :, 3] =
1 1 1
1 1 1
1 1 1
Prvky pole lze vyplnit libovolnou hodnotou funkcí fill:
julia> fill(42, 3, 4)
3x4 Array{Int64,2}:
42 42 42 42
42 42 42 42
42 42 42 42
Další užitečná funkce se jmenuje eye() a slouží k vytvoření jednotkové matice o zadaných rozměrech:
julia> eye(5,5)
5x5 Array{Float64,2}:
1.0 0.0 0.0 0.0 0.0
0.0 1.0 0.0 0.0 0.0
0.0 0.0 1.0 0.0 0.0
0.0 0.0 0.0 1.0 0.0
0.0 0.0 0.0 0.0 1.0
Alternativně je možné i zde specifikovat typ prvků:
julia> eye(Int16, 5,5)
5x5 Array{Int16,2}:
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
Funkce diagm() vytvoří matici, kde prvky na diagonále mají hodnoty specifikované objektem typu „range“:
julia> diagm(1:5)
5x5 Array{Int64,2}:
1 0 0 0 0
0 2 0 0 0
0 0 3 0 0
0 0 0 4 0
0 0 0 0 5
julia> diagm(6:10)
5x5 Array{Int64,2}:
6 0 0 0 0
0 7 0 0 0
0 0 8 0 0
0 0 0 9 0
0 0 0 0 10
V některých případech může být výhodné vytvořit pole naplněné náhodnými hodnotami. I zde nabízí jazyk Julia řešení:
julia> rand(4,5)
4x5 Array{Float64,2}:
0.2724 0.119104 0.670161 0.835602 0.785017
0.485103 0.207542 0.0146372 0.379119 0.329223
0.972542 0.0750364 0.931973 0.744018 0.114805
0.0806015 0.773603 0.162323 0.0592224 0.0124386
6. Přístup k prvkům polí
V této kapitole si ukážeme, jakým způsobem je možné přistupovat k prvkům polí. Nejprve si vytvoříme pole o velikosti 10×10 prvků, přičemž všechny prvky budou reprezentovány hodnotami s plovoucí řádovou čárkou s poloviční přesností (u rozsáhlejších polí může být použití tohoto možná poněkud neobvyklého datového typu výhodné, neboť se tím ušetří velké množství operační paměti):
julia> a=Array{Float16}(10,10)
10x10 Array{Float16,2}:
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.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 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.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
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.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 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.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
Pokusme se změnit hodnotu prvního prvku v poli:
julia> a[0,0]=42
ERROR: BoundsError: attempt to access 10x10 Array{Float16,2}:
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.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 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.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
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.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 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.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
at index [0,0]
in setindex! at array.jl:314
Vidíme, že se tato operace nepodařila, a to z toho důvodu, že se prvky indexují od jedničky a nikoli od nuly. To se sice může zdát poněkud neobvyklé, ovšem ve skutečnosti mnoho jazyků (dovolím si říci, že většina jazyků NEodvozených od céčka) zvolilo stejný přístup: Fortran, Mathematica, R, MATLAB, Lua atd. Správně tedy má příkaz vypadat takto:
julia> a[1,1]=42 42
Prvek pole se skutečně změnil:
julia> a
10x10 Array{Float16,2}:
42.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 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.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 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.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 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.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 0.0 0.0
0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0 0.0
Podívejme se nyní na složitější indexování, tentokrát vektoru:
julia> v=[1 2 3 4 10 -1]
1x6 Array{Int64,2}:
1 2 3 4 10 -1
Přístup k prvnímu prvku:
julia> v[1] 1
Přístup k prvkům 2 až 4:
julia> v[2:4]
3-element Array{Int64,1}:
2
3
4
Přístup ke všem prvkům:
julia> v[:]
6-element Array{Int64,1}:
1
2
3
4
10
-1
Pokud potřebujeme přistoupit k poslednímu prvku, není možné použít index –1 (to lze v jiných jazycích), ale používá se zde slovo end. To opět není nijak unikátní, podobně se toto slovo používá i v MATLABu:
julia> v[end] -1
Kombinace předchozích způsobů – od čtvrtého prvku do konce vektoru:
julia> v[4:end]
3-element Array{Int64,1}:
4
10
-1
Zajímavý je výběr sekvence libovolných prvků vektoru (pole), a to s využitím jiného vektoru obsahujícího indexy prvků. Povšimněte si nutnosti použití dvojitých hranatých závorek – vnější závorky představují operaci výběru prvků, vnitřní závorky vektor indexů:
julia> v[[1,5,6,2,5,5]]
6-element Array{Int64,1}:
1
10
-1
2
10
10
7. Funkce range, linspace a collect
Při konstrukci polí je možné využít (i když jen nepřímo) funkci pojmenovanou range(), u níž je zapotřebí si dát pozor na to, že její parametry se mohou odlišovat od stejnojmenných funkcí známých z jiných programovacích jazyků (někdy se uvádí pořadí start, stop, step atd.):
help?> range search: range Range RangeIndex nzrange linrange histrange UnitRange StepRange range(start, [step], length) Construct a range by length, given a starting value and optional step (defaults to 1).
Funkce range() vytváří hodnotu typu „range“, tj. negeneruje přímo jednotlivé prvky:
julia> range(1,10) 1:10 julia> range(1,2,10) 1:2:19 julia> range(10,-1,10) 10:-1:1
Jak je tedy možné z hodnot typu „range“ vytvořit skutečné pole (či spíše vektor)? Použijeme další funkci pojmenovanou collect():
help?> collect
search: collect Collections
collect(collection)
Return an array of all items in a collection. For associative collections,
returns Pair{KeyType, ValType}.
collect(element_type, collection)
Return an array of type Array{element_type,1} of all items in a collection.
Nenechme se zmýlit tím, že tato funkce jako svůj parametr vyžaduje kolekci, protože i objekt typu „range“ je v tomto pojetí kolekce. Pokud tedy budeme chtít vytvořit vektor hodnot od 1 do 10, použijeme tento (uznávám, že zbytečně dlouhý) zápis:
julia> collect(range(1,10))
10-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10
Alternativně lze explicitně specifikovat typ prvků vytvářeného pole:
julia> collect(Float16, range(1,10))
10-element Array{Float16,1}:
1.0
2.0
3.0
4.0
5.0
6.0
7.0
8.0
9.0
10.0
Vzhledem k tomu, že objekt typu „range“ má i svůj literál, můžeme použít zápis:
julia> collect(3.2 : 8.6)
6-element Array{Float64,1}:
3.2
4.2
5.2
6.2
7.2
8.2
Popř. specifikovat jak počáteční hodnotu, tak i krok a celkový počet prvků (což je asi nejčastější způsob):
julia> collect(3.2 : 0.4 : 5)
5-element Array{Float64,1}:
3.2
3.6
4.0
4.4
4.8
V některých případech nemusí být použití výše popsané funkce range() tím nejlepším řešením při vytváření vektoru obsahujícího sekvenci číselných hodnot. Typickým příkladem je sekvence generovaná s krokem 0.1, protože hodnotu 0.1 není možné formáty IEEE 754 single ani double přesně reprezentovat. Pokud je nutné vytvořit vektor s přesným počtem prvků, může se namísto range hodit funkce linspace(), které se předá počáteční hodnota, koncová hodnota a popř. i počet prvků vektoru. Použití funkce linspace() je tak ve skutečnosti velmi jednoduché:
julia> collect(linspace(1.0,100.0,12))
12-element Array{Float64,1}:
1.0
10.0
19.0
28.0
37.0
46.0
55.0
64.0
73.0
82.0
91.0
100.0
8. Funkce reshape
Další velmi důležitou funkcí, s níž se v praxi často setkáme, je funkce nazvaná reshape(), která dokáže změnit velikost matice a vhodným způsobem přeorganizovat prvky v původní matici. Této funkci se předávají dva parametry – prvním parametrem je vstupní pole (vektor, matice, …), druhým parametrem (popř. více parametry) pak specifikace tvaru výsledného pole. Podívejme se nejdříve na oficiální popis této funkce:
help?> reshape search: reshape promote_shape reshape(A, dims) Create an array with the same data as the given array, but with different dimensions. An implementation for a particular type of array may choose whether the data is copied or shared.
Vytvořme si testovací vektor s dvanácti prvky (což je číslo dělitelné 2, 3, 4 i 6):
julia> a=[1 2 3 4 5 6 7 8 9 10 11 12]
1x12 Array{Int64,2}:
1 2 3 4 5 6 7 8 9 10 11 12
Z tohoto vektoru pak snadno získáme matice o rozměrech 4×3, 3×4, 2×6, 6×2 či 1×12:
julia> reshape(a, 4, 3)
4x3 Array{Int64,2}:
1 5 9
2 6 10
3 7 11
4 8 12
julia> reshape(a, 3, 4)
3x4 Array{Int64,2}:
1 4 7 10
2 5 8 11
3 6 9 12
julia> reshape(a, 2, 6)
2x6 Array{Int64,2}:
1 3 5 7 9 11
2 4 6 8 10 12
julia> reshape(a, 6, 2)
6x2 Array{Int64,2}:
1 7
2 8
3 9
4 10
5 11
6 12
Pozor ovšem na to, že počet prvků výsledné matice musí přesně odpovídat počtu prvků vstupní matice/vektoru. V tomto je Julia nekompromisní:
julia> reshape(a, 5, 2)
ERROR: DimensionMismatch("new dimensions (5,2) must be consistent with array size 12")
in reshape at array.jl:135
in reshape at abstractarray.jl:215
Vytvoření trojrozměrných polí je stejně snadné (a opět je nutné zachovat počet prvků):
julia> reshape(a, 2, 3, 2)
2x3x2 Array{Int64,3}:
[:, :, 1] =
1 3 5
2 4 6
[:, :, 2] =
7 9 11
8 10 12
julia> reshape(a, 2, 2, 3)
2x2x3 Array{Int64,3}:
[:, :, 1] =
1 3
2 4
[:, :, 2] =
5 7
6 8
[:, :, 3] =
9 11
10 12
julia> reshape(a, 3, 2, 2)
3x2x2 Array{Int64,3}:
[:, :, 1] =
1 4
2 5
3 6
[:, :, 2] =
7 10
8 11
9 12
Mimochodem – velikost matice lze získat funkcí size() vracející n-tici:
julia> size(reshape(a, 6,2)) (6,2)
9. Operace slicedim pro řezy maticemi, převod matice na vektor, přístup k prvkům na diagonále
Kromě funkce reshape() nabízí základní knihovna programovacího jazyka Julia i trojici dalších funkcí, které lze použít pro výběr prvků z matic předem definovaným způsobem. Jedná se o funkce pojmenované slicedim(), vec() a diag(). Podívejme se na jejich oficiální popis:
help?> slicedim search: slicedim slicedim(A, d, i) Return all the data of A where the index for dimension d equals i. Equivalent to A[:,:,...,i,:,:,...] where i is in position d.
help?> vec search: vec vecdot vecnorm Vector VecOrMat @vectorize_2arg @vectorize_1arg vec(Array) -> Vector Vectorize an array using column-major convention.
help?> diag search: diag diagm diagind Diagonal isdiag spdiagm Bidiagonal blkdiag diag(M[, k]) The kth diagonal of a matrix, as a vector. Use diagm to construct a diagonal matrix.
Před ukázkou možností těchto funkcí si vytvořme testovací matici o velikosti 3×4 prvky. Můžeme použít nám již známý objekt typu „range“ a funkce collect() společně s reshape():
julia> a=reshape(collect(1:12), 3, 4)
3x4 Array{Int64,2}:
1 4 7 10
2 5 8 11
3 6 9 12
Získání jednotlivých řádků pole je snadné (první dimenze + index řádku):
julia> slicedim(a, 1, 1)
1x4 Array{Int64,2}:
1 4 7 10
julia> slicedim(a, 1, 2)
1x4 Array{Int64,2}:
2 5 8 11
julia> slicedim(a, 1, 3)
1x4 Array{Int64,2}:
3 6 9 12
Podobně lze získat jednotlivé sloupce (druhá dimenze + index sloupce):
julia> slicedim(a, 2, 1)
3x1 Array{Int64,2}:
1
2
3
julia> slicedim(a, 2, 2)
3x1 Array{Int64,2}:
4
5
6
julia> slicedim(a, 2, 4)
3x1 Array{Int64,2}:
10
11
12
Vidíme, že funkce slicedim() je až překvapivě flexibilní.
Funkce vec() je naproti tomu velmi jednoduchá až triviální:
julia> vec(a)
12-element Array{Int64,1}:
1
2
3
4
5
6
7
8
9
10
11
12
Podobně funkce diag(), kterou lze přečíst prvky na diagonále (tato funkce si dobře poradí i s maticí, která není čtvercová):
julia> diag(a)
3-element Array{Int64,1}:
1
5
9
10. Lineární algebra: základní operace
V následující části tohoto článku si popíšeme některé funkce, které jsou nabízeny v knihovně LAPACK, ovšem již dnes se můžeme seznámit se základními operátory, které lze použít při práci s vektory a maticemi. Vytvořme si nejprve dva pomocné vektory:
julia> v1=[1,2,3]
3-element Array{Int64,1}:
1
2
3
julia> v2=[2,2,2]
3-element Array{Int64,1}:
2
2
2
Součet a rozdíl vektorů se provádí prvek po prvku:
julia> v1+v1
3-element Array{Int64,1}:
2
4
6
julia> v1+v2
3-element Array{Int64,1}:
3
4
5
julia> v1-v2
3-element Array{Int64,1}:
-1
0
1
Vynásobení vektoru skalárem:
julia> v1*2
3-element Array{Int64,1}:
2
4
6
Součin stylem prvek po prvku (první prvek s prvním prvkem atd.):
julia> v1 .* v2
3-element Array{Int64,1}:
2
4
6
Podíl stylem prvek po prvku (první prvek s prvním prvkem atd.):
julia> v1 ./ v2
3-element Array{Float64,1}:
0.5
1.0
1.5
Tečku lze spojit i s dalšími operátory – vždy to značí aplikaci operátoru na dvojici příslušných prvků vektoru či matice:
julia> v1 .^ v2
3-element Array{Int64,1}:
1
4
9
Skalární součin vektorů:
julia> dot(v1,v2) 12
Vektorový součin vektorů:
julia> cross(v1, v2)
3-element Array{Int64,1}:
-2
4
-2
Vektorový součin si lze nejlépe ukázat na jednotkových vektorech, které jsou na sebe kolmé (výsledkem je třetí vektor kolmý na oba vstupní vektory):
julia> cross([1,0,0], [0,1,0])
3-element Array{Int64,1}:
0
0
1
Lineární operace odpovídající násobení skalárem:
julia> scale(v1, 10)
3-element Array{Int64,1}:
10
20
30
julia> scale(v1, 1//10)
3-element Array{Rational{Int64},1}:
1//10
1//5
3//10
Součet hodnot prvků vektorů:
julia> sum(v1) 6 julia> sum(v2) 6
Kombinaci tečka-operátor lze použít i pro relační operátory. Výsledkem je vektor či matice hodnot true/false:
julia> v1.<=v2
3-element BitArray{1}:
true
true
false
julia> v1 .!= 2
3-element BitArray{1}:
true
false
true
Aplikace funkce na prvky vektoru či matice nevyžaduje žádné speciální metody zápisu, minimálně pro funkce ze základní knihovny:
julia> sin(v1)
3-element Array{Float64,1}:
0.841471
0.909297
0.14112
Mnohem větší množství funkcí najdeme v rozhraní knihovny LAPACK pro matice. Tyto funkce (a mnohé další) si představíme příště.
11. Odkazy na Internetu
- Array Programming
https://en.wikipedia.org/wiki/Array_programming - Discovering Array Languages
http://archive.vector.org.uk/art10008110 - no stinking loops – Kalothi
http://www.nsl.com/ - Vector (obsahuje odkazy na články, knihy a blogy o programovacích jazycích APL, J a K)
http://www.vector.org.uk/ - APL Interpreters
http://www.vector.org.uk/?area=interpreters - APL_(programming_language
http://en.wikipedia.org/wiki/APL_(programming_language - APL FAQ
http://www.faqs.org/faqs/apl-faq/ - APL FAQ (nejnovější verze)
http://home.earthlink.net/~swsirlin/apl.faq.html - A+
http://www.aplusdev.org/ - APLX
http://www.microapl.co.uk/ - FreeAPL
http://www.pyr.fi/apl/index.htm - J: a modern, high-level, general-purpose, high-performance programming language
http://www.jsoftware.com/ - K, Kdb: an APL derivative for Solaris, Linux, Windows
http://www.kx.com - openAPL (GPL)
http://sourceforge.net/projects/openapl - Parrot APL (GPL)
http://www.parrotcode.org/ - Learning J (Roger Stokes)
http://www.jsoftware.com/help/learning/contents.htm - Rosetta Code
http://rosettacode.org/wiki/Main_Page - Why APL
http://www.acm.org/sigapl/whyapl.htm - Introducing Julia/Functions
https://en.wikibooks.org/wiki/Introducing_Julia/Functions - Functions (Julia documentation)
http://docs.julialang.org/en/release-0.4/manual/functions/ - Evaluate binomial coefficients
http://rosettacode.org/wiki/Evaluate_binomial_coefficients - Ackermann function
http://rosettacode.org/wiki/Ackermann_function - Julia (front page)
http://julialang.org/ - Julia – dokumentace
http://docs.julialang.org/en/release-0.4/ - Julia – repositář na GitHubu
https://github.com/JuliaLang/julia - Julia (programming language)
https://en.wikipedia.org/wiki/Julia_%28programming_language%29 - IJulia
https://github.com/JuliaLang/IJulia.jl - Introducing Julia
https://en.wikibooks.org/wiki/Introducing_Julia - Julia: the REPL
https://en.wikibooks.org/wiki/Introducing_Julia/The_REPL - Introducing Julia/Metaprogramming
https://en.wikibooks.org/wiki/Introducing_Julia/Metaprogramming - Month of Julia
https://github.com/DataWookie/MonthOfJulia - Learn X in Y minutes (where X=Julia)
https://learnxinyminutes.com/docs/julia/ - New Julia language seeks to be the C for scientists
http://www.infoworld.com/article/2616709/application-development/new-julia-language-seeks-to-be-the-c-for-scientists.html - Julia: A Fast Dynamic Language for Technical Computing
http://karpinski.org/publications/2012/julia-a-fast-dynamic-language - The LLVM Compiler Infrastructure
http://llvm.org/ - Julia: benchmarks
http://julialang.org/benchmarks/ - Type system
https://en.wikipedia.org/wiki/Type_system - Half-precision floating-point format
https://en.wikipedia.org/wiki/Half-precision_floating-point_format