Hlavní navigace

Framework Torch: pokročilejší operace nad vektory a maticemi

Pavel Tišnovský

Nejdříve dokončíme popis tvorby grafů s využitím knihovny Gnuplot a poté si popíšeme pokročilejší operace, které lze provádět s vektory i maticemi. Kromě operace gather se jedná o skalární, vektorový a vnější součin atd.

Obsah

1. Dokončení problematiky tvorby jednoduchých grafů ve frameworku Torch

2. Zobrazení kontur 2D funkce

3. Zobrazení 3D grafu funkce typu [x,y,z]=f(t)

4. Lorenzův atraktor

5. Operace typu gather provedená nad vektorem

6. Operace typu gather provedená nad maticí

7. Operace nad vektory a maticemi, násobení vektorů, lineární interpolace a další operace z BLAS

8. Vynásobení komponent tenzoru (vektoru, matice) skalární hodnotou

9. Vynásobení odpovídajících si komponent tenzoru

10. Skalární součin vektorů

11. Vektorový součin

12. Vnější součin

13. Součin matice a vektoru

14. Součin dvou matic

15. Lineární transformace

16. Příklad aplikace lineární transformace: otáčení bodu či vektoru v ploše

17. Lineární interpolace mezi dvěma tenzory

18. Další operace dostupné v knihovně BLAS

19. Repositář s demonstračními příklady

20. Odkazy na Internetu

1. Dokončení problematiky tvorby jednoduchých grafů ve frameworku Torch

V předchozí části seriálu o frameworku Torch jsme si mj. ukázali, jakým způsobem je možné vykreslit graf funkce dvou nezávislých proměnných. Připomeňme si, že se pro tyto účely používá metoda nazvaná gnuplot.splot (surface plot), které se předá (dvourozměrná) matice s hodnotami funkce. Graf je automaticky upraven takovým způsobem, aby se vykreslila lokální maxima i minima funkce; navíc se implicitně pod funkcí zobrazuje i kontura („vrstevnice“) s důležitými hodnotami. Celý příklad, který vykreslí průběh jednoduché funkce dvou nezávislých proměnných, je možné zapsat následovně:

require("gnuplot")
 
GRID=40
 
v = torch.range(0, GRID*GRID)
v = v - 12
m = v:resize(GRID, GRID)
m = torch.sin(m*4.0*math.pi/90.0/GRID)
 
gnuplot.pngfigure("plot7.png")
gnuplot.title("splot")
gnuplot.splot(m)
gnuplot.plotflush()
gnuplot.close()

Obrázek 1: Graf vykreslený předchozím demonstračním příkladem.

2. Zobrazení kontur 2D funkce

V některých případech však nemusí být zobrazení plochy představující funkci dostatečně názorné; navíc se v 3D grafu mohou některé důležité hodnoty ztratit (představme si funkci s mnoha lokálními minimy a maximy). V takovém případě lze namísto metody gnuplot.splot použít metodu gnuplot.imagesc. Tato metoda zobrazí funkci dvou nezávislých proměnných odlišně – formou běžného rastrového obrázku, v němž barva jednotlivých oblastí odpovídá hodnotě funkce v daném místě. Nejjednodušší je situace ve chvíli, kdy do druhého parametru metody gnuplot.imagesc předáme řetězec „gray“. Potom je rastrový obrázek zobrazen ve stupních šedi, přičemž nejmenší hodnotě odpovídá černá barva a nejvyšší hodnotě barva bílá:

Obrázek 2: Funkce dvou nezávislých proměnných zobrazená formou rastrového obrázku ve stupních šedi.

Obrázek číslo 2 byl vykreslen tímto kódem (funkce ger bude popsána níže):

require("gnuplot")
 
x = torch.linspace(-1,1,120)
xy = torch.ger(x,x)
z = torch.sin(xy*4.0*math.pi)
 
gnuplot.pngfigure("plot9.png")
gnuplot.imagesc(z, 'gray')
gnuplot.plotflush()
gnuplot.close()

Alternativně je možné obrázek obarvit barvami z palety s gradientními přechody:

Obrázek 3: Funkce dvou nezávislých proměnných zobrazená formou rastrového obrázku ve falešných barvách.

Třetí obrázek byl vykreslen s využitím tohoto kódu:

require("gnuplot")
 
x = torch.linspace(-1,1,120)
xy = torch.ger(x,x)
z = torch.sin(xy*4.0*math.pi)
 
gnuplot.pngfigure("plot10.png")
gnuplot.imagesc(z, 'color')
gnuplot.plotflush()
gnuplot.close()

3. Zobrazení 3D grafu funkce typu [x,y,z]=f(t)

Poslední typ grafu, s nímž se dnes seznámíme, je trojrozměrný graf, v němž se typicky zobrazuje funkce typu [x,y,z]=f(t) popř. složitější funkce [xn, yn, zn]=f(xn-1, yn-1, zn-1). O zobrazení průběhů těchto funkcí se stará metoda gnuplot.scatter3. Po zavolání této metody, které se předá trojice vektorů představujících souřadnice vykreslovaných bodů, se automaticky zjistí potřebné rozsahy na všech třech osách, což je dobře patrné ze čtvrtého screenshotu. Podívejme se tedy, jakým způsobem je možné zobrazit trojrozměrnou spirálu (helix). Je to snadné, protože se použije funkce [x,y,z]=[cos(t), sin(y), t]:

require("gnuplot")
 
t = torch.linspace(-3 * math.pi, 3 * math.pi, 250)
x = t:clone():cos()
y = t:clone():sin()
 
gnuplot.pngfigure("plot11.png")
gnuplot.scatter3(x, y, t)
gnuplot.plotflush()
gnuplot.close()

Obrázek 4: Šroubovice (helix) vykreslená demonstračním příkladem.

4. Lorenzův atraktor

Poměrně vděčným příkladem funkce zobrazené v 3D prostoru je dynamický systém s takzvaným podivným atraktorem, který je nazvaný Lorenzův atraktor podle svého objevitele. Tento systém sestávající ze tří dynamických rovnic použil Edward Lorenz v roce 1963 při simulaci vývoje počasí (resp. ve velmi zjednodušeném modelu počasí). Na tomto systému byla také numericky a analyticky ověřena velká citlivost na počáteční podmínky (někdy také nazývaná „motýlí efekt“). Pro upřesnění je však nutné říci, že při simulaci na počítači vlastně získáme atraktor, jenž je periodický. Je to z toho důvodu, že pro zobrazení číselných hodnot je použito konečného počtu bitů, z toho nutně vyplývá, že se po určitém počtu kroků (který je však obrovský, takže tento jev mnohdy nezaregistrujeme) začne dráha Lorenzova atraktoru překrývat. V matematicky přesném modelu však tato situace nenastane, každá smyčka funkce bude mít unikátní tvar a dráhy se nebudou překrývat, pouze protínat. Diferenciální rovnice Lorenzova atraktoru mají po převodu na diferenční tvar následující formát:

dx/dt = σ (y-x)
dy/dt = x(ρ – z) – y
dz/dt = xy – Βz

Takže pro iterativní (samozřejmě že nepřesný) výpočet můžeme pracovat s následujícími vztahy, které pro dostatečně malé dt vedou k výpočtu bodů tvořících Lorenzův atraktor:

xn+1=xn+(σ (y-x)) dt
yn+1=yn+(x(ρ – z) – y) dt
zn+1=zn+(xy – Βz) dt

Podívejme se nyní na způsob implementace této funkce:

-- funkce pro výpočet dalšího bodu Lorenzova atraktoru
function lorenz(x, y, z, s, r, b)
    x_dot = s*(y - x)
    y_dot = r*x - y - x*z
    z_dot = x*y - b*z
    return x_dot, y_dot, z_dot
end

A výpočtu sekvence bodů ležících na atraktoru:

-- krok (změna času)
dt = 0.01
 
-- celkový počet vypočtených bodů na Lorenzově atraktoru
n = 10000
 
-- prozatím prázdné vektory připravená pro výpočet
x = torch.zeros(n)
y = torch.zeros(n)
z = torch.zeros(n)
 
-- počáteční hodnoty
x[1], y[1], z[1] = 0., 1., 1.05
 
-- vlastní výpočet atraktoru
for i=1, n-1 do
    x_dot, y_dot, z_dot = lorenz(x[i], y[i], z[i], 10, 28, 2.667)
    x[i+1] = x[i] + x_dot * dt
    y[i+1] = y[i] + y_dot * dt
    z[i+1] = z[i] + z_dot * dt
end

Obrázek 5: Lorenzův atraktor vykreslený přes framework Torch.

Celý příklad i se zobrazením atraktoru lze naprogramovat například takto:

require("gnuplot")
 
-- funkce pro výpočet dalšího bodu Lorenzova atraktoru
function lorenz(x, y, z, s, r, b)
    x_dot = s*(y - x)
    y_dot = r*x - y - x*z
    z_dot = x*y - b*z
    return x_dot, y_dot, z_dot
end
 
-- krok (změna času)
dt = 0.01
 
-- celkový počet vypočtených bodů na Lorenzově atraktoru
n = 10000
 
-- prozatím prázdné vektory připravená pro výpočet
x = torch.zeros(n)
y = torch.zeros(n)
z = torch.zeros(n)
 
-- počáteční hodnoty
x[1], y[1], z[1] = 0., 1., 1.05
 
-- vlastní výpočet atraktoru
for i=1, n-1 do
    x_dot, y_dot, z_dot = lorenz(x[i], y[i], z[i], 10, 28, 2.667)
    x[i+1] = x[i] + x_dot * dt
    y[i+1] = y[i] + y_dot * dt
    z[i+1] = z[i] + z_dot * dt
end
 
-- vykreslení grafu
gnuplot.pngfigure("plot12.png")
gnuplot.raw("set pointsize 0.1")
gnuplot.scatter3("lorenz attractor", x, y, z)
gnuplot.plotflush()
gnuplot.close()

Poznámka: za povšimnutí též stojí řádek:

gnuplot.raw("set pointsize 0.1")

který přímo (bez dalšího zpracování) volá příkaz gnuplotu – samotná knihovna Torch totiž zdaleka nereflektuje všechny možnosti gnuplotu.

5. Operace typu gather provedená nad vektorem

Velmi užitečná, i když na první pohled možná poněkud komplikovaná, je operace typu gather představovaná metodou tensor:gather. Tato operace může být aplikována například na jednorozměrný vektor; argumentem metody tensor:gather bude další vektor, který musí obsahovat celá čísla (typu int, long atd.). V takovém případě metoda tensor:gather vrátí ty prvky původního vektoru, které leží na zvolených indexech:

v1 = torch.range(10, 100, 10)
print(v1)
 
indexes = torch.LongTensor({2, 4, 6, 8, 10})
print(indexes)
 
print(v1:gather(1, indexes))
 
print(v1:gather(1, indexes):isContiguous())
 
print("-----------------------")
 
indexes = torch.range(10, 1, -1):long()
print(indexes)
 
print(v1:gather(1, indexes))
 
print(v1:gather(1, indexes):isContiguous())

Podívejme se na zprávy vypisované příkladem. Nejprve se vypíše obsah původního vektoru:

  10
  20
  30
  40
  50
  60
  70
  80
  90
 100
[torch.DoubleTensor of size 10]

Dále se vypíše vektor s indexy vybíraných prvků:

  2
  4
  6
  8
 10
[torch.LongTensor of size 5]

A na konec výsledný vektor s vybranými prvky:

  20
  40
  60
  80
 100
[torch.DoubleTensor of size 5]

Nový vektor s indexy tentokrát obsahuje hodnoty od 10 do 1:

 10
  9
  8
  7
  6
  5
  4
  3
  2
  1
[torch.LongTensor of size 10]

Výsledkem operace gather jsou prvky původního vektoru, ovšem v opačném pořadí:

 100
  90
  80
  70
  60
  50
  40
  30
  20
  10
[torch.DoubleTensor of size 10]

6. Operace typu gather provedená nad maticí

Operaci gather lze aplikovat i na matice, její použití je však v tomto případě složitější, protože indexy nyní nemusí tvořit pouhý jednorozměrný vektor, ale mohou mít podobu 2D matice s různým počtem řádků a sloupců. Příklad použití nyní bude komplikovanější, protože si ukážeme různé možnosti, které nám tato operace nabízí. Nejprve vytvoříme zdrojovou matici:

v = torch.range(1, 24)
m1 = v:resize(6, 4)
print(m1)
  1   2   3   4
  5   6   7   8
  9  10  11  12
 13  14  15  16
 17  18  19  20
 21  22  23  24
[torch.DoubleTensor of size 6x4]

Postupně budeme maticí po sloupcích procházet a vybereme vždy první prvek sloupce:

indexes = torch.LongTensor({{1,1,1,1}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(1, indexes))

Výsledky:

Indexes:
 1  1  1  1
[torch.LongTensor of size 1x4]
 
Result:
 1  2  3  4
[torch.DoubleTensor of size 1x4]

Opačný případ – výběr prvních prvků z každého řádku matice:

indexes = torch.LongTensor({{1},{1},{1},{1},{1},{1}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(2, indexes))

Výsledky:

Indexes:
 1
 1
 1
 1
 1
 1
[torch.LongTensor of size 6x1]
 
Result:
  1
  5
  9
 13
 17
 21
[torch.DoubleTensor of size 6x1]

Výběr prvního, druhého a třetího řádku tabulky:

indexes = torch.LongTensor({{1,1,1,1}, {2,2,2,2}, {3,3,3,3}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(1, indexes))

Výsledky:

Indexes:
 1  1  1  1
 2  2  2  2
 3  3  3  3
[torch.LongTensor of size 3x4]
 
Result:
  1   2   3   4
  5   6   7   8
  9  10  11  12
[torch.DoubleTensor of size 3x4]

Výběr z prvních dvou řádků zdrojové matice, ovšem napřeskáčku:

indexes = torch.LongTensor({{1,2,1,2}, {2,1,2,1}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(1, indexes))

Výsledky:

Indexes:
 1  2  1  2
 2  1  2  1
[torch.LongTensor of size 2x4]
 
Result:
 1  6  3  8
 5  2  7  4
[torch.DoubleTensor of size 2x4]

Prohození všech řádků matice:

indexes = torch.LongTensor({{6,6,6,6}, {5,5,5,5}, {4,4,4,4}, {3,3,3,3}, {2,2,2,2}, {1,1,1,1}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(1, indexes))

Výsledky:

Indexes:
 6  6  6  6
 5  5  5  5
 4  4  4  4
 3  3  3  3
 2  2  2  2
 1  1  1  1
[torch.LongTensor of size 6x4]
 
Result:
 21  22  23  24
 17  18  19  20
 13  14  15  16
  9  10  11  12
  5   6   7   8
  1   2   3   4
[torch.DoubleTensor of size 6x4]

Získání matice se dvěma sloupci, prvky jsou získány postupně z prvního, druhého, třetího… sloupce původní matice:

indexes = torch.LongTensor({{1,1}, {2,2}, {3,3}, {4,4}, {1,1}, {1,1}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(2, indexes))

Výsledky:

Indexes:
 1  1
 2  2
 3  3
 4  4
 1  1
 1  1
[torch.LongTensor of size 6x2]
 
Result:
  1   1
  6   6
 11  11
 16  16
 17  17
 21  21
[torch.DoubleTensor of size 6x2]

Výběr prvního a čtvrtého sloupce, opět na přeskáčku:

indexes = torch.LongTensor({{1,4}, {1,4}, {1,4}, {4,1}, {4,1}, {4,1}})
print("Indexes:")
print(indexes)
print("Result:")
print(m1:gather(2, indexes))

Výsledky:

Indexes:
 1  4
 1  4
 1  4
 4  1
 4  1
 4  1
[torch.LongTensor of size 6x2]
 
Result:
  1   4
  5   8
  9  12
 16  13
 20  17
 24  21
[torch.DoubleTensor of size 6x2]

Vidíme, že tato operace je skutečně velmi flexibilní.

7. Operace nad vektory a maticemi, násobení vektorů, lineární interpolace a další operace z BLAS

V knihovně Torch nalezneme mnoho funkcí, které mohou provádět různé operace nad vektory, maticemi, popř. nad obecnými tenzory. Některé funkce lze použít pro všechny typy tenzorů (vynásobení komponent tenzoru se skalární hodnotou), jiné je však možné aplikovat jen na tenzory s jednou dimenzí (vektorové operace), se dvěma dimenzemi (maticové násobení) či dokonce jen na tenzory s omezeným počtem komponent (vektorový součin). V dalších kapitolách si popíšeme následující operace:

Operace Zápis Popis v kapitole
Součin tenzoru a skaláru torch.mul(tenzor, skalár), tenzor:mul(skalár) 8
Součin odpovídajících si komponent torch.cmul(tenzor1, tenzor2), tenzor2:cmul(tenzor2) 9
Skalární součin torch.dot(vektor1, vektor2), vektor1:dot(vektor2) 10
Vektorový součin torch.cross(vektor1, vektor2), vektor1:cross(vektor2) 11
Vnější součin gtorch.ger(vektor1, vektor2), vektor1:ger(vektor2) 12
Součin matice a vektoru torch.mv(matice, vektor), * 13
Součin dvou matic torch.mm(matice1, matice2), * 14

Ze zápisů ve druhém sloupci je patrné, že některé operace jsou dostupné jako běžné funkce i jako metody. Součin matice a vektoru popř. maticový součin je možné zapsat i pomocí přetíženého operátoru *.

8. Vynásobení komponent tenzoru (vektoru, matice) skalární hodnotou

Jednou z nejjednodušších operací, v nichž se vyskytuje součin, je operace sloužící k vynásobení všech komponent vybraného tenzoru (typicky vektoru nebo matice) skalární hodnotou. Tato operace je představována metodou mul, kterou lze volat dvěma způsoby:

v3 = torch.mul(v1, 10)
v4 = v2:mul(10)

V prvním případě se vektor (matice, tenzor) v1 nezmění a výsledkem operace je nový tenzor, ovšem v případě druhém dojde k modifikaci komponent původního tenzoru v2. Záleží jen na rozhodnutí uživatele, zda potřebuje zachovat původní tenzor či zda mu nevadí provedení součinu s modifikací původního tenzoru (což je samozřejmě časově i paměťově méně náročné).

Podívejme se na příklad, v němž budeme násobit prvky vektoru [1, 2, 3] konstantou 10:

v1 = torch.Tensor({1,2,3})
v2 = torch.Tensor({1,2,3})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.mul(v1, 10)
v4 = v2:mul(10)
 
print("Results of vector*scalar")
print(v3)
print(v4)
 
print("New value of vector 1")
print(v1)
 
print("New value of vector 2")
print(v2)

Obsah původních vektorů:

Original vector 1
 1
 2
 3
[torch.DoubleTensor of size 3]
 
Original vector 2
 1
 2
 3
[torch.DoubleTensor of size 3]

Výsledek součinu všech prvků vektorů skalární hodnotou:

Results of vector*scalar
 10
 20
 30
[torch.DoubleTensor of size 3]
 
 10
 20
 30
[torch.DoubleTensor of size 3]

Nový obsah původních vektorů (vidíme, že druhý vektor byl skutečně modifikován):

New value of vector 1
 1
 2
 3
[torch.DoubleTensor of size 3]
 
New value of vector 2
 10
 20
 30
[torch.DoubleTensor of size 3]

9. Vynásobení odpovídajících si komponent tenzoru

Další operace, v níž se provádí součin, spočívá ve vynásobení odpovídajících si komponent tenzoru (tj. v praxi opět vektorů či matic). Tato operace se jmenuje cmul a chová se podobně jako operace mul z předchozí kapitoly. První způsob volání nemění ani jeden ze vstupních vektorů, druhý způsob volání (metody) modifikuje prvky původního vektoru:

v3 = torch.cmul(v1, v2)
v4 = v1:cmul(v2)

Opět si vše ukažme na jednoduchém příkladu:

v1 = torch.Tensor({1,2,3})
v2 = torch.Tensor({1,2,3})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.cmul(v1, v2)
v4 = v1:cmul(v2)
 
print("Results of vector*vector element-wise")
print(v3)
print(v4)
 
print("New value of vector 1")
print(v1)
 
print("New value of vector 2")
print(v2)

Obsah původních vektorů:

Original vector 1
 1
 2
 3
[torch.DoubleTensor of size 3]
 
Original vector 2
 1
 2
 3
[torch.DoubleTensor of size 3]

Výsledek součinu prvek po prvku:

Results of vector*vector element-wise
 1
 4
 9
[torch.DoubleTensor of size 3]
 
 1
 4
 9
[torch.DoubleTensor of size 3]

Nový obsah původních vektorů (vidíme, že druhý vektor byl skutečně modifikován):

New value of vector 1
 1
 4
 9
[torch.DoubleTensor of size 3]
 
New value of vector 2
 1
 2
 3
[torch.DoubleTensor of size 3]

10. Skalární součin vektorů

Další podporovanou operací je klasický skalární součin dvou vektorů neboli anglicky dot product. Tuto operaci je možné zavolat buď pomocí metody dot, nebo alternativně přetíženým operátorem *. Tato operace NEmodifikuje obsah původních vektorů, takže následující tři příkazy provádí prakticky stejný výpočet:

v3 = torch.dot(v1, v2)
v4 = v1 * v2
v5 = v1:dot(v2)

Opět si vše ukážeme na příkladu:

v1 = torch.range(1,5)
v2 = torch.range(1,5)
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.dot(v1, v2)
v4 = v1 * v2
v5 = v1:dot(v2)
 
print("Dot products")
print(v3)
print(v4)
print(v5)
 
print("New value of vector 1")
print(v1)
 
print("New value of vector 2")
print(v2)

Původní vektory mají pět prvků:

Original vector 1
 1
 2
 3
 4
 5
[torch.DoubleTensor of size 5]
 
Original vector 2
 1
 2
 3
 4
 5
[torch.DoubleTensor of size 5]

Výsledky skalárního součinu jsou ve všech třech případech shodné:

Dot products
55
55
55

Ani jeden vektor se přitom nezměnil:

New value of vector 1
 1
 2
 3
 4
 5
[torch.DoubleTensor of size 5]
 
New value of vector 2
 1
 2
 3
 4
 5
[torch.DoubleTensor of size 5]

11. Vektorový součin

V knihovně Torch je samozřejmě podporována i operace vektorového součinu (cross product). Tato operace předpokládá (a taktéž kontroluje), jestli mají oba vstupní vektory tři prvky, protože vektorový součin je definován pro vektory v 3D prostoru (výsledkem je vektor kolmý na oba vstupní vektory). Ukažme si jednoduchý příklad, nejdříve s vektory [1, 0, 1] a [0, 1, 0] pro ověření, zda je výsledkem opravdu kolmý vektor a taktéž pro vektory [1, 2, 3] a [3, 2, 1]. Výsledky si můžete ověřit například zde:

v1 = torch.Tensor({1,0,0})
v2 = torch.Tensor({0,1,0})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.cross(v1, v2)
v4 = v1:cross(v2)
 
print("Cross products")
print(v3)
print(v4)
 
print("New value of vector 1")
print(v1)
 
print("New value of vector 2")
print(v2)
 
print("----------------------------")
 
v1 = torch.Tensor({1,2,3})
v2 = torch.Tensor({3,2,1})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.cross(v1, v2)
v4 = v1:cross(v2)
 
print("Cross products")
print(v3)
print(v4)
 
print("New value of vector 1")
print(v1)
 
print("New value of vector 2")
print(v2)

Ověření vektorového součinu [1, 0, 0] × [0, 1, 0]:

Original vector 1
 1
 0
 0
[torch.DoubleTensor of size 3]
 
Original vector 2
 0
 1
 0
[torch.DoubleTensor of size 3]
 
Cross products
 0
 0
 1
[torch.DoubleTensor of size 3]
 
 0
 0
 1
[torch.DoubleTensor of size 3]
 
New value of vector 1
 1
 0
 0
[torch.DoubleTensor of size 3]
 
New value of vector 2
 0
 1
 0
[torch.DoubleTensor of size 3]

Ověření vektorového součinu [1, 2, 3] × [3, 2, 1]:

Original vector 1
 1
 2
 3
[torch.DoubleTensor of size 3]
 
Original vector 2
 3
 2
 1
[torch.DoubleTensor of size 3]
 
Cross products
-4
 8
-4
[torch.DoubleTensor of size 3]
 
-4
 8
-4
[torch.DoubleTensor of size 3]
 
New value of vector 1
 1
 2
 3
[torch.DoubleTensor of size 3]
 
New value of vector 2
 3
 2
 1
[torch.DoubleTensor of size 3]

12. Vnější součin

Další operace, kterou lze provádět s vektory, je vnější součin (outer product). Vstupními operandy je dvojice vektorů, které mohou mít libovolnou a rozdílnou délku, výsledkem je dvourozměrná matice. Ve skutečnosti se totiž provádí maticové násobení prvního vstupního vektoru s transponovaným druhým vektorem:

m = v1v2T

Poznámka: pokud naopak budeme transponovat první vstupní vektor, bude výsledkem maticového násobení jediné číslo odpovídající skalárnímu součinu:

s = v1Tv2

Poznámka: vektory v1 a v2 jsou považovány za sloupcové vektory, proto je zdánlivě transponován vždy opačný vektor, než nám napovídá intuice.

Vyzkoušejme si nyní provést vnější součin nad dvojicí vektorů [1, 0, 0], [0, 1, 0], dále pak nad dvojicí [1, 2, 4], [1, 2, 3] a konečně nad vektory [1, … 10] a [1, 2, 3]:

v1 = torch.Tensor({1,0,0})
v2 = torch.Tensor({0,1,0})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.ger(v1, v2)
 
print("Outer products")
print(v3)
 
print("---------------")
 
v1 = torch.Tensor({1,2,3})
v2 = torch.Tensor({1,2,3})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.ger(v1, v2)
 
print("Outer products")
print(v3)
 
print("---------------")
 
v1 = torch.range(1,10)
v2 = torch.Tensor({1,2,3})
 
print("Original vector 1")
print(v1)
 
print("Original vector 2")
print(v2)
 
v3 = torch.ger(v1, v2)
 
print("Outer products")
print(v3)

Ověření vnějšího součinu [1, 0, 0] ⊗ [0, 1, 0]:

Original vector 1
 1
 0
 0
[torch.DoubleTensor of size 3]
 
Original vector 2
 0
 1
 0
[torch.DoubleTensor of size 3]
 
Outer products
 0  1  0
 0  0  0
 0  0  0
[torch.DoubleTensor of size 3x3]

Ověření vnějšího součinu [1, 2, 3] ⊗ [1, 2, 3]:

Original vector 1
 1
 2
 3
[torch.DoubleTensor of size 3]
 
Original vector 2
 1
 2
 3
[torch.DoubleTensor of size 3]
 
Outer products
 1  2  3
 2  4  6
 3  6  9
[torch.DoubleTensor of size 3x3]

První sloupec odpovídá prvkům původního vektoru, druhý sloupec nese hodnoty vynásobené dvěma, třetí pak vynásobené třemi.

Ověření vnějšího součinu [1, 2, 3 … 10] ⊗ [1, 2, 3]:

Original vector 1
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
[torch.DoubleTensor of size 10]
 
Original vector 2
 1
 2
 3
[torch.DoubleTensor of size 3]
 
Outer products
  1   2   3
  2   4   6
  3   6   9
  4   8  12
  5  10  15
  6  12  18
  7  14  21
  8  16  24
  9  18  27
 10  20  30
[torch.DoubleTensor of size 10x3]

Na posledním příkladu je jasně patrné, jak je maticové násobení prováděno (jen se nenechte zmást tím, že jednodimenzionální tenzory jsou zobrazeny jako sloupce čísel a nikoli jako řádky).

13. Součin matice a vektoru

Další podporovanou operací je součin matice a vektoru. Ten je možné zapsat buď pomocí funkce torch.mv nebo s využitím přetíženého operátoru *. Počet sloupců matice musí odpovídat počtu prvků vektoru. Výsledkem je vektor s počtem prvků odpovídajícím počtu řádků matice:

m = torch.Tensor({{1, 2},
                  {3, 4},
                  {5, 6},
                  {7, 8}})
 
v = torch.Tensor({1, 2})
 
print("Original matrix")
print(m)
 
print("Original vector")
print(v)
 
m2 = torch.mv(m, v)
m3 = m * v
 
print("New matrix")
print(m2)
print(m3)

Původní matice a vektor vstupující do operace:

Original matrix
 1  2
 3  4
 5  6
 7  8
[torch.DoubleTensor of size 4x2]
 
Original vector
 1
 2
[torch.DoubleTensor of size 2]

Výsledek operace je shodný při použití torch.mv i operátoru *. Poslední prvek vznikl výpočtem 7*1 + 8*2:

New matrix
  5
 11
 17
 23
[torch.DoubleTensor of size 4]
 
  5
 11
 17
 23
[torch.DoubleTensor of size 4]

14. Součin dvou matic

Pochopitelně následuje operace pro součin dvou matic, která se zapisuje buď funkcí torch.mm nebo s využitím přetíženého operátoru *. U této operace je nutné zajistit, aby počet sloupců první matice odpovídal počtu řádků matice druhé. Pokud má první matice rozměry m×n (m=počet řádků, n=počet sloupců) a druhá rozměry n×p, bude mít výsledná matice rozměry m×p:

m1 = torch.Tensor({{1, 2, 3},
                   {4, 5, 6},
                   {7, 8, 9}})
 
m2 = torch.eye(3,3)
 
m3 = torch.eye(3,3)
m3[{2 ,2}] = 10
 
print("Original matrix 1")
print(m1)
 
print("Original matrix 2")
print(m2)
 
print("Original matrix 3")
print(m3)
 
m12 = m1 * m2
 
print("m1 x m2:")
print(m12)
 
m13 = m1 * m3
 
print("m1 x m3:")
print(m13)

Původní trojice matic:

Original matrix 1
 1  2  3
 4  5  6
 7  8  9
[torch.DoubleTensor of size 3x3]
 
Original matrix 2
 1  0  0
 0  1  0
 0  0  1
[torch.DoubleTensor of size 3x3]
 
Original matrix 3
  1   0   0
  0  10   0
  0   0   1
[torch.DoubleTensor of size 3x3]

Výsledek operace m1·m2:

m1 · m2:
 1  2  3
 4  5  6
 7  8  9
[torch.DoubleTensor of size 3x3]

Výsledek operace m1·m3:

m1 · m3:
  1  20   3
  4  50   6
  7  80   9
[torch.DoubleTensor of size 3x3]

15. Lineární transformace

Díky možnosti provést vynásobení vektoru s maticí je možné použít přetížený operátor * i pro aplikaci lineárních transformací, a to jak v ploše, tak i v trojrozměrném prostoru. Mezi lineární transformace patří posun, otáčení, změna měřítka a zkosení. Jestliže máme tři body, které leží na jedné přímce, tak po aplikaci libovolné lineární transformace získáme nové tři body, které také leží na přímce (proto jsou transformace lineární). Všechny lineární transformace lze vyjádřit maticí o rozměrech n × n, kde n je dimenze vektoru či bodu, který transformujeme.

Pokud provádíme lineární transformace v ploše, vyjadřujeme souřadnici dvěma složkami x a y. Lineární transformaci lze zapsat jako výpočet nových souřadnic x' a y':

x' = xa11 + ya12,
y' = xa21 + ya22.

Členy a11, a12, a21 a a22 představují parametry lineární transformace.

Přehlednější je však vektorově/maticový zápis, kdy dvojici [x, y] označíme jako v, dvojici [x', y'] jako v' a hodnoty a11, a12, a21 a a22 zapíšeme jako prvky do matice M o rozměrech 2×2. Potom lze celou lineární transformaci zapsat jedním vztahem:

v' = M * v – v tomto případě jsou v a v' sloupcové vektory.

Pro zadání lineárních transformací v trojrozměrném prostoru by nám měla stačit transformační matice o rozměrech 3×3 prvky. To je skutečně pravda, ale v počítačové grafice často potřebujeme kromě lineárních transformací vyjádřit i perspektivní projekci. Proto se ke třem souřadnicím [x, y, z] přidává ještě čtvrtá souřadnice označovaná w ze slova weight – váha. Bod je potom vyjádřen čtveřicí [x, y, z, 1] a vektor [x, y, z, 0]. Transformační matice je poté zvětšena na rozměry 4×4 prvky. Stejně tak pro 2D plochu rozšíříme vektor na [x, y, z] a transformační matice bude mít 3×3 prvky.

16. Příklad aplikace lineární transformace: otáčení bodu či vektoru v ploše

Ukažme si nyní aplikaci lineární transformace pro otáčení bodu či vektoru v ploše. Matice otáčení bude mít rozměry 3×3 prvky, přičemž poslední sloupec musí obsahovat hodnoty 0, 0, 1 (neprovádí se nelineární perspektivní projekce) a spodní řádek bude mít taktéž hodnoty 0, 0, 1 (neprovádí se posun). Zbývá nám určit prvky podmatice 2×2 prvky. Pro matici otáčení tyto prvky mají následující obsah:

| cos α  -sin α |
| sin α   cos α |

Postupné otáčení bodu [1, 0] o 30° (π/6) lze naprogramovat následovně:

trans_identity = torch.eye(3, 3)
 
angle = math.pi/6
 
trans_rotation = torch.Tensor({{math.cos(angle), -math.sin(angle), 0},
                               {math.sin(angle),  math.cos(angle), 0},
                               {0,                0,               1}})
 
v = torch.Tensor({1, 0, 1})
 
print("Original point")
print(v)
 
v = trans_identity * v
 
print("After transformation #1")
print(v)
 
v = trans_rotation * v
 
print("First rotation by 30")
print(v)
 
v = trans_rotation * v
 
print("Second rotation by 30°")
print(v)
 
print("Third rotation by 30°")
v = trans_rotation * v
 
print(v)

Výsledky (včetně původního bodu):

Original point
 1
 0
 1
[torch.DoubleTensor of size 3]
 
After transformation #1
 1
 0
 1
[torch.DoubleTensor of size 3]
 
First rotation by 30
 0.8660
 0.5000
 1.0000
[torch.DoubleTensor of size 3]
 
Second rotation by 30°
 0.5000
 0.8660
 1.0000
[torch.DoubleTensor of size 3]
 
Third rotation by 30°
 2.7756e-16
 1.0000e+00
 1.0000e+00
[torch.DoubleTensor of size 3]

Povšimněte si, že bod se skutečně otočil až na souřadnice [0, 1] (ona nula je kvůli kumulativní chybě vyjádřena jako 2,7×10-16).

17. Lineární interpolace mezi dvěma tenzory

Poslední operací, kterou si dnes podrobněji popíšeme, je operace pro provedení lineární interpolace mezi komponentami dvou tenzorů. Typicky se jedná o jednorozměrné vektory či o dvourozměrné matice, ovšem teoreticky není počet dimenzí nijak omezený. Operace pro výpočet nového tenzoru pomocí lineární interpolace je představována metodou nazvanou lerp, což je jméno používané například v počítačové grafice (mj. i proto, že se jedná o jednu ze základních operací prováděných na grafických akcelerátorech). Oba tenzory předávané metodě lerp by měly mít stejný tvar, tj. shodný počet dimenzí i shodnou velikost. Třetím parametrem této metody je váha, která je typicky představována reálným číslem ležícím mezi 0,0 až 1,0. Ve skutečnosti však může být váha jakákoli – záporné číslo i kladné číslo větší než nula (musíte však umět interpretovat výsledky).

Podívejme se nyní na způsob vytvoření nového vektoru s pěti prvky s využitím lineární interpolace mezi vektorem [100, 0, 0, 0, 0] a vektorem [0, 1, 2, 3, 4]. Váha se postupně mění od nuly do jedničky s krokem 0,2:

v1 = torch.Tensor({100, 0, 0, 0, 0})
v2 = torch.range(0, 4)
 
print("Source vector 1")
print(v1)
 
print("Source vector 2")
print(v2)
 
for weight = 0.0, 1.0, 0.2 do
    print("Linear interpolation with weight=" .. weight)
    vt = torch.lerp(v1, v2, weight)
    print(vt)
end

Zdrojové vektory:

Source vector 1
 100
   0
   0
   0
   0
[torch.DoubleTensor of size 5]
 
Source vector 2
 0
 1
 2
 3
 4
[torch.DoubleTensor of size 5]

Pokud je váha nastavená na nulu, vrátí se pochopitelně vektor s prvky odpovídajícími prvnímu vektoru:

Linear interpolation with weight=0
 100
   0
   0
   0
   0
[torch.DoubleTensor of size 5]

Jak se váha postupně zvyšuje, mění se i hodnota prvků výsledného vektoru:

Linear interpolation with weight=0.2
 80.0000
  0.2000
  0.4000
  0.6000
  0.8000
[torch.DoubleTensor of size 5]
 
Linear interpolation with weight=0.4
 60.0000
  0.4000
  0.8000
  1.2000
  1.6000
[torch.DoubleTensor of size 5]
 
Linear interpolation with weight=0.6
 40.0000
  0.6000
  1.2000
  1.8000
  2.4000
[torch.DoubleTensor of size 5]
 
Linear interpolation with weight=0.8
 20.0000
  0.8000
  1.6000
  2.4000
  3.2000
[torch.DoubleTensor of size 5]

Na konci smyčky, kdy váha dosáhne hodnoty 1, získáme prvky odpovídající druhému vektoru:

Linear interpolation with weight=1
 0
 1
 2
 3
 4
[torch.DoubleTensor of size 5]

Stejnou operaci můžeme provést i pro 2D matice, pokud je jejich rozměr a tvar shodný:

m1 = torch.zeros(5, 5)
m2 = torch.range(0, 25):resize(5,5)
 
print("Source matrix 1")
print(m1)
 
print("Source matrix 2")
print(m2)
 
for weight = 0.0, 1.0, 0.2 do
    print("Linear interpolation with weight=" .. weight)
    mt = torch.lerp(m1, m2, weight)
    print(mt)
end

Obě zdrojové matice:

Source matrix 1
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
[torch.DoubleTensor of size 5x5]
 
Source matrix 2
  0   1   2   3   4
  5   6   7   8   9
 10  11  12  13  14
 15  16  17  18  19
 20  21  22  23  24
[torch.DoubleTensor of size 5x5]

Výsledky pro váhu mezi 0 až 1:

Linear interpolation with weight=0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
 0  0  0  0  0
[torch.DoubleTensor of size 5x5]
 
Linear interpolation with weight=0.2
 0.0000  0.2000  0.4000  0.6000  0.8000
 1.0000  1.2000  1.4000  1.6000  1.8000
 2.0000  2.2000  2.4000  2.6000  2.8000
 3.0000  3.2000  3.4000  3.6000  3.8000
 4.0000  4.2000  4.4000  4.6000  4.8000
[torch.DoubleTensor of size 5x5]
 
Linear interpolation with weight=0.4
 0.0000  0.4000  0.8000  1.2000  1.6000
 2.0000  2.4000  2.8000  3.2000  3.6000
 4.0000  4.4000  4.8000  5.2000  5.6000
 6.0000  6.4000  6.8000  7.2000  7.6000
 8.0000  8.4000  8.8000  9.2000  9.6000
[torch.DoubleTensor of size 5x5]
 
Linear interpolation with weight=0.6
  0.0000   0.6000   1.2000   1.8000   2.4000
  3.0000   3.6000   4.2000   4.8000   5.4000
  6.0000   6.6000   7.2000   7.8000   8.4000
  9.0000   9.6000  10.2000  10.8000  11.4000
 12.0000  12.6000  13.2000  13.8000  14.4000
[torch.DoubleTensor of size 5x5]
 
Linear interpolation with weight=0.8
  0.0000   0.8000   1.6000   2.4000   3.2000
  4.0000   4.8000   5.6000   6.4000   7.2000
  8.0000   8.8000   9.6000  10.4000  11.2000
 12.0000  12.8000  13.6000  14.4000  15.2000
 16.0000  16.8000  17.6000  18.4000  19.2000
[torch.DoubleTensor of size 5x5]
 
Linear interpolation with weight=1
  0   1   2   3   4
  5   6   7   8   9
 10  11  12  13  14
 15  16  17  18  19
 20  21  22  23  24
[torch.DoubleTensor of size 5x5]

18. Další operace dostupné v knihovně BLAS

Ve frameworku Torch najdeme i mnoho dalších užitečných operací, o nichž se dnes (alespoň prozatím) zmíníme pouze ve stručnosti:

Metoda Stručný popis
torch.clamp ořezání všech komponent tak, aby byly v rozsahu min_value, max_value
   
torch.addcmul sekvence operací * a + aplikovaná postupně na všechny prvky tenzorů
torch.addcdiv sekvence operací / a + aplikovaná postupně na všechny prvky tenzorů
   
torch.addmv kombinace operací add a mv (viz předchozí kapitoly)
torch.addmm kombinace operací add a mm (viz předchozí kapitoly)
   
torch.min vrátí komponentu s nejmenší hodnotou
torch.max vrátí komponentu s největší hodnotou
torch.mean vrátí průměr vypočtený ze všech komponent
torch.median vrátí medián vypočtený ze všech komponent
torch.sum vypočte sumu prvků
torch.std vypočte standardní odchylku
torch.var vypočte rozptyl
torch.mode vrátí prvek, který se v tenzoru vyskytuje nejčastěji
torch.sort setřídění prvků (lze určit osu atd.)
   
torch.bitand bitová operace AND všech komponent tenzoru se zadanou hodnotou (skalárem)
torch.cbitand bitový operace AND všech komponent tenzoru s hodnotami přečtenými z dalšího tenzoru
torch.bitor bitová operace OR všech komponent tenzoru se zadanou hodnotou (skalárem)
torch.cbitor bitový operace OR všech komponent tenzoru s hodnotami přečtenými z dalšího tenzoru
torch.bitxor bitová operace XOR všech komponent tenzoru se zadanou hodnotou (skalárem)
torch.cbitxor bitový operace XOR všech komponent tenzoru s hodnotami přečtenými z dalšího tenzoru
   
torch.lshift bitový posun komponent tenzoru o zadanou hodnotu (skalár)
torch.clshift bitový posun komponent tenzoru o hodnoty přečtené z dalšího tenzoru
torch.rshift bitový posun komponent tenzoru o zadanou hodnotu (skalár)
torch.crshift bitový posun komponent tenzoru o hodnoty přečtené z dalšího tenzoru

19. Repositář s demonstračními příklady

Všechny demonstrační příklady, které jsme si popsali v předchozích kapitolách, najdete v GIT repositáři dostupném na adrese https://github.com/tisnik/torch-examples.git. Následují odkazy na zdrojové kódy jednotlivých příkladů:

Příklad Odkaz
   
graphs/09_contour.lua https://github.com/tisnik/torch-examples/blob/master/grap­hs/09_contour.lua
graphs/10_contour.lua https://github.com/tisnik/torch-examples/blob/master/grap­hs/10_contour.lua
graphs/11_plot_3d.lua https://github.com/tisnik/torch-examples/blob/master/grap­hs/11_plot_3d.lua
graphs/12_lorenz_attractor.lua https://github.com/tisnik/torch-examples/blob/master/grap­hs/12_lorenz_attractor.lua
   
blas/01_mul_by_scalar.lua https://github.com/tisnik/torch-examples/blob/master/blas/01_mul_by_sca­lar.lua
blas/02_mul_element_wise.lua https://github.com/tisnik/torch-examples/blob/master/blas/02_mul_e­lement_wise.lua
blas/03_dot_product.lua https://github.com/tisnik/torch-examples/blob/master/blas/03_dot_pro­duct.lua
blas/04_cross_product.lua https://github.com/tisnik/torch-examples/blob/master/blas/04_cros­s_product.lua
blas/05_outer_product.lua https://github.com/tisnik/torch-examples/blob/master/blas/05_ou­ter_product.lua
blas/06_matrix_mul_vector.lua https://github.com/tisnik/torch-examples/blob/master/blas/06_ma­trix_mul_vector.lua
blas/07_matrix_mul.lua https://github.com/tisnik/torch-examples/blob/master/blas/07_ma­trix_mul.lua
blas/08_linear_transformation.lua https://github.com/tisnik/torch-examples/blob/master/blas/08_li­near_transformation.lua
blas/09_lerp.lua https://github.com/tisnik/torch-examples/blob/master/blas/09_lerp.lua
blas/10_matrix_lerp.lua https://github.com/tisnik/torch-examples/blob/master/blas/10_ma­trix_lerp.lua
   
24_gather_from_vector.lua https://github.com/tisnik/torch-examples/blob/master/basic­s/24_gather_from_vector.lua
25_gather_from_matrix.lua https://github.com/tisnik/torch-examples/blob/master/basic­s/25_gather_from_matrix.lua

20. Odkazy na Internetu

  1. Stránka projektu Torch
    http://torch.ch/
  2. Torch: Serialization
    https://github.com/torch/tor­ch7/blob/master/doc/seria­lization.md
  3. Torch na GitHubu (několik repositářů)
    https://github.com/torch
  4. Torch (machine learning), Wikipedia
    https://en.wikipedia.org/wi­ki/Torch_%28machine_learnin­g%29
  5. Torch Package Reference Manual
    https://github.com/torch/tor­ch7/blob/master/README.md
  6. Torch Cheatsheet
    https://github.com/torch/tor­ch7/wiki/Cheatsheet
  7. Lorenzův atraktor
    http://www.root.cz/clanky/fraktaly-v-pocitacove-grafice-iii/#k03
  8. Dot product (Wikipedia)
    https://en.wikipedia.org/wi­ki/Dot_product
  9. Cross product (Wikipedia)
    https://en.wikipedia.org/wi­ki/Cross_product
  10. Outer product
    https://en.wikipedia.org/wi­ki/Outer_product
  11. Linear interpolation
    https://en.wikipedia.org/wi­ki/Linear_interpolation
  12. Matrix multiplication
    https://en.wikipedia.org/wi­ki/Matrix_multiplication
  13. Demos for gnuplot version 5.2
    http://gnuplot.info/demo/
  14. Plotting with Torch7
    http://www.lighting-torch.com/2015/08/24/plotting-with-torch7/
  15. Plotting Package Manual with Gnuplot
    https://github.com/torch/gnu­plot/blob/master/README.md
  16. An Introduction to Tensors
    https://math.stackexchange­.com/questions/10282/an-introduction-to-tensors
  17. Differences between a matrix and a tensor
    https://math.stackexchange­.com/questions/412423/dif­ferences-between-a-matrix-and-a-tensor
  18. Qualitatively, what is the difference between a matrix and a tensor?
    https://math.stackexchange­.com/questions/1444412/qu­alitatively-what-is-the-difference-between-a-matrix-and-a-tensor?
  19. BLAS (Basic Linear Algebra Subprograms)
    http://www.netlib.org/blas/
  20. Basic Linear Algebra Subprograms (Wikipedia)
    https://en.wikipedia.org/wi­ki/Basic_Linear_Algebra_Sub­programs
  21. Helix
    https://en.wikipedia.org/wiki/Helix
  22. Comparison of deep learning software
    https://en.wikipedia.org/wi­ki/Comparison_of_deep_lear­ning_software
  23. TensorFlow
    https://www.tensorflow.org/
  24. Caffe2 (A New Lightweight, Modular, and Scalable Deep Learning Framework)
    https://caffe2.ai/
  25. PyTorch
    http://pytorch.org/
  26. Seriál o programovacím jazyku Lua
    http://www.root.cz/serialy/pro­gramovaci-jazyk-lua/
  27. LuaJIT – Just in Time překladač pro programovací jazyk Lua
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua/
  28. LuaJIT – Just in Time překladač pro programovací jazyk Lua (2)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-2/
  29. LuaJIT – Just in Time překladač pro programovací jazyk Lua (3)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-3/
  30. LuaJIT – Just in Time překladač pro programovací jazyk Lua (4)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-4/
  31. LuaJIT – Just in Time překladač pro programovací jazyk Lua (5 – tabulky a pole)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-5-tabulky-a-pole/
  32. LuaJIT – Just in Time překladač pro programovací jazyk Lua (6 – překlad programových smyček do mezijazyka LuaJITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-6-preklad-programovych-smycek-do-mezijazyka-luajitu/
  33. LuaJIT – Just in Time překladač pro programovací jazyk Lua (7 – dokončení popisu mezijazyka LuaJITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-7-dokonceni-popisu-mezijazyka-luajitu/
  34. LuaJIT – Just in Time překladač pro programovací jazyk Lua (8 – základní vlastnosti trasovacího JITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-8-zakladni-vlastnosti-trasovaciho-jitu/
  35. LuaJIT – Just in Time překladač pro programovací jazyk Lua (9 – další vlastnosti trasovacího JITu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-9-dalsi-vlastnosti-trasovaciho-jitu/
  36. LuaJIT – Just in Time překladač pro programovací jazyk Lua (10 – JIT překlad do nativního kódu)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-10-jit-preklad-do-nativniho-kodu/
  37. LuaJIT – Just in Time překladač pro programovací jazyk Lua (11 – JIT překlad do nativního kódu procesorů s architekturami x86 a ARM)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-11-jit-preklad-do-nativniho-kodu-procesoru-s-architekturami-x86-a-arm/
  38. LuaJIT – Just in Time překladač pro programovací jazyk Lua (12 – překlad operací s reálnými čísly)
    http://www.root.cz/clanky/luajit-just-in-time-prekladac-pro-programovaci-jazyk-lua-12-preklad-operaci-s-realnymi-cisly/
  39. Lua Profiler (GitHub)
    https://github.com/luafor­ge/luaprofiler
  40. Lua Profiler (LuaForge)
    http://luaforge.net/projec­ts/luaprofiler/
  41. ctrace
    http://webserver2.tecgraf.puc-rio.br/~lhf/ftp/lua/
  42. The Lua VM, on the Web
    https://kripken.github.io/lu­a.vm.js/lua.vm.js.html
  43. Lua.vm.js REPL
    https://kripken.github.io/lu­a.vm.js/repl.html
  44. lua2js
    https://www.npmjs.com/package/lua2js
  45. lua2js na GitHubu
    https://github.com/basicer/lua2js-dist
  46. Lua (programming language)
    http://en.wikipedia.org/wi­ki/Lua_(programming_langu­age)
  47. LuaJIT 2.0 SSA IRhttp://wiki.luajit.org/SSA-IR-2.0
  48. The LuaJIT Project
    http://luajit.org/index.html
  49. LuaJIT FAQ
    http://luajit.org/faq.html
  50. LuaJIT Performance Comparison
    http://luajit.org/performance.html
  51. LuaJIT 2.0 intellectual property disclosure and research opportunities
    http://article.gmane.org/gma­ne.comp.lang.lua.general/58908
  52. LuaJIT Wiki
    http://wiki.luajit.org/Home
  53. LuaJIT 2.0 Bytecode Instructions
    http://wiki.luajit.org/Bytecode-2.0
  54. Programming in Lua (first edition)
    http://www.lua.org/pil/contents.html
  55. Lua 5.2 sources
    http://www.lua.org/source/5.2/
  56. REPL
    https://en.wikipedia.org/wi­ki/Read%E2%80%93eval%E2%80%93prin­t_loop
  57. The LLVM Compiler Infrastructure
    http://llvm.org/ProjectsWithLLVM/
  58. clang: a C language family frontend for LLVM
    http://clang.llvm.org/
  59. LLVM Backend („Fastcomp“)
    http://kripken.github.io/emscripten-site/docs/building_from_source/LLVM-Backend.html#llvm-backend
  60. Lambda the Ultimate: Coroutines in Lua,
    http://lambda-the-ultimate.org/node/438
  61. Coroutines Tutorial,
    http://lua-users.org/wiki/CoroutinesTutorial
  62. Lua Coroutines Versus Python Generators,
    http://lua-users.org/wiki/LuaCorouti­nesVersusPythonGenerators
Našli jste v článku chybu?