Úvod

Počítače pro vědecké výpočty

Potřeba delších numerických výpočtů provází fyziku od jejího vzniku. Rozvoj techniky ve druhé polovině 20. století vedl k exponenciálnímu růstu možností počítačů a schopnosti i těch nejobyčejnějších dnes velmi překračují schopnosti člověka počítačům "rozumět". Je ale žádoucí, aby právě student fyziky dokázal počítač užívat ku svému prospěchu. Zdá se, že nejnovější výdobytky oboru počítačů shrnuté pod štítkem umělá inteligence (AI) tomu budou značně nápomocny. Ve své současné podobě představuje AI nástroj extrapolující dostupné znalosti a používá tedy jazyka v němž jsou tyto znalosti formulovány. V problému vědeckotechnických výpočtů tedy dnes používá ve svých odpovědích metody toho, čemu říkáme Programování. Proto bude i cílem tohoto kurzu seznámit studenty se základy programování elementárních výpočtů na počítačích tak, aby pak ve své kariéře dokázali využít počítače i za pomoci přicházejících revolučních prostředků práce s nimi.

Nejprve shrňme základní pojmy, se kterými se budeme potkávat a které jsou vám jistě známy.

Počítač je dnes zařízení vykonávající požadované úkoly na základě povelů. Automatické vykonávání zajišťuje procesor (řídící jednotka počítače, CPU), který řídí operace s jednotlivými částmi počítače. Povely jsou uložených v elektronické formě v podobě tzv. instrukcí procesoru. Tyto instrukce jsou typicky elementární operace, které dokáže CPU vykonat v co nejkratším čase. Jde o číselné kódy přináležející různým operacím, konkrétní podoba závisí na druhu CPU. (Vývoj procesorů je velmi náročný a tak jsou architektury CPU většinou pojmenovány po komerčních entitách jako je Intel/AMD, ARM, ...).

Pro uložení povelů pro práci počítače stejně tak jako dat, se kterými počítač pracuje, slouží počítačová paměť. Vybrané části paměti mohou být určeny je ke čtení počítače (read-only memory - ROM), univerzálnost použití počítačů ale vyžaduje aby významná část paměti mohla sloužit jak k zapisování tak čtení (RAM). Vstup a výstup dat obstarávají vám známá zařízení, jako jsou klávesnice, displeje, tiskárny...

Z ekonomických důvodů, kvůli možnosti zachování dat při výpadku napájení počítače ale i např. transportu dat je část paměti realizována odlišnou technologií (disky magneto-mechanické a polovodičové, DVD-ROM, ...). Organizaci takto uložených dat složí souborové systémy, které daný umožňují danému souboru dat přiřadit jméno a uspořádat takové soubory v rámci hierarchické struktury (adresar/podadresar/podpodadresar/.../jmeno.souboru.pripona). V OS Windows je na začátku uveden ještě logický disk označený jedním písmenem a dvojtečkou, tedy máme cestu např. C:\Users\student, navíc zde u jména souboru nezáleží na volbě velké/malé písmeno.

Operace se vykonávají s údaji, které mohou mít rozličnou podobu a většinou budeme považovat za interní záležitost, jak jsou data v počítači uložena. Přesto musíme tušit, že základem informací s nimiž dnešní počítače zacházejí je bit - hodnota 0 nebo 1. Z bitů jsou složeny větší celky určené k ukládání celých čísel, buňka paměti o velikosti \(n\) bitů může nabývat \(2^n\) různých stavů. Praktickou jednotkou informace se stala osmice bitů, tzv byte, protože 256 možnými hodnotami lze pohodlně pokrýt základní sadu znaků anglické abecedy a nejdůležitějších symbolů (+*,:@ atd.). Aby počítače pracovaly rychleji, pracují najednou se slovem nikoli bytem, přičemž šířka slova u PC bývala 16, 32, dnes je to 64 bitů. Čísla uvnitř počítačů jsou kvůli rychlosti uložena v binárním tvaru, nikoli pomocí cifer dekadického zápisu.

Vysoký výkon elektronických komunikací umožňuje mít výkonný počítač (server) a jeho uzivatele velmi daleko. To poskytuje různé výhody. Je k tomu nutné projit oba prostřednictvím počítačové sítě. Uživatelův (obvykle méně výkonný) počítač je vybaven SW (client) zprostředkujícím komunikaci v požadované formě. Příkladem klienta je webový prohlížeč, jde mimochodem o velmi komplikovaný program. Jednoduchým klientem je např. terminál, který se tváří, jako bychom seděli u elektrického psacího stroje napojeného na vzdálený počítač. V oblasti vědeckých výpočtů jde o častý způsob práce, přičemž přes terminál jsou spouštěny i ty nejrozsáhlejší (a nejdražší) výpočty.

Servery mohou být doplněny o velkokapacitní úložiště data, provozovány ve velkém počtu a přidělovány v různém počtu podle aktuálních požadavků (služby typu cloud).

Naivní měřítko výkonu počítačů je počet aritmetických operací za sekundu a množství paměti. Aktuálně jde u jednoho počítače typicky o miliardy číselných operací za sekundu (giga-FLOPS) a desítky miliard bytů. Jsou-li potřeba vyšší hodnoty, řeší se to dnes tak, že

  • v součástce stále označované jako CPU je obsaženo více (jednotky až stovky) jader (což je označení pro jednotlivé CPU),

  • jádro dokáže najednou provádět 4-8 stejných aritmetických operací,

  • součástí počítače může být přídavný HW obsahující i tisíce specializovanějších výpočetních jader (typicky tzv. grafická karta, GPU),

  • více počítačů (\(10-10^4\)) řeší problém souběžně, data komunikují přes vysoce propustnou datovou síť.

Sekvence instrukcí CPU uložených v pořadí, v jakém se mají vykonávat, tvoří hlavní součást tzv. spustitelného souboru. Takové soubory jsou pak načteny do paměti a procesoru je přikázáno začít vykonávat instrukce od určeného místa. Toto zařizuje rozsáhlý balík spustitelných programů známý pod jménem operační systém (OS). Jinou částí operačního systému jsou programy převádějící přání uživatele počítače zadaná prostřednictvím klávesnice a myši do podoby akcí (spuštění programu, předání kódu klávesy běžícímu programu např. textovému editoru, ukončení běžícího programu,...). Důležitou prací operačního systému je sdílení CPU a dalších omezených prostředků počítače mezi jednotlivými programy a uživateli. Z bezpečnostních důvodů musí OS uživateli některé činnosti zakázat a také izolovat navzájem programy spuštěné různými uživateli. Součástí OS jsou dnes grafická uživatelská rozhraní (GUI) poskytující efektivní představu programů jako okének na pracovní ploše ovládaných myší a klávesnicí.

Kromě spustitelných souborů pracujeme i s dalšími soubory. Zejména jde o data (záznamy měření, fotografie, audiozáznam), texty (kancelářské, vědecké, ...) a textový zápis kódů programů.

Počítačové jazyky, kompilátor a interpret

Řídit běh počítač přímo prostřednictvím instrukcí CPU je pracné a nepraktické, spustitelné programy tak dnes vytváří kompilátor překladem zdrojového kódu programu. Ten vyjadřuje návod, jakým má činnost počítače postupovat prostřednictvím člověku i stroji srozumitelného počítačového jazyka. Podoba jazyka bývá uzpůsobena řešenému problému. Příklady jazyků tohoto druhu jsou:

  • FORTRAN vznikl v samých počátcích počítačů jako prostředek pro zápis vědecko-technických výpočtů.

  • C vznikl jako jazyk pro vývoj operačního systému UNIX, stal se ale i jazykem pro vědecké výpočty.

  • Pascal měl podporovat "správné" postupy při psaní programů a ve výuce programování.

  • C++ rozšířil jazyk C o mnoho moderních prvků podporujících jak efektivní tak i bezpečnější psaní kódu.

Dosáhnutí výsledku v této variantě vyžaduje tři kroky

  1. Napsání zdrojového kódu programu ve vybraném počítačovém jazyce.

  2. Přeložení prostřednictvím kompilátoru (vytvoří spustitelný soubor)

  3. Spuštění tohoto souboru

Například kód zapsaný v daném programovacím jazyce C pojmenovaný hello.c

#include <stdio.h>

int main(void)
{
    printf("hello, world\n");
}

Ten je kompilací převeden do podoby spustitelného programu, tedy např. hello.exe. Tento program je pak potřeba spustit. Povšimněte si, že často používáme totéž slovo program pro kód i spustitelný soubor.

V terminálu linuxového počítače vypadají tyto operace takto

tomas@dronte:~/tmp/prog$ gcc -o hello.exe hello.c
tomas@dronte:~/tmp/prog$ ls -l hello*
-rw-rw-r-- 1 tomas tomas    69 may  1 12:18 hello.c
-rwxrwxr-x 1 tomas tomas 15960 may  1 12:19 hello.exe
tomas@dronte:~/tmp/prog$ ./hello.exe
hello, world
tomas@dronte:~/tmp/prog$

Příkaz gcc je kompilátor jazyka C, příkaz ls vypíše všechny soubory začínající na "hello" a ukáže, že zatímco zdrojový kód má pouhých 69 bytů (znaků), spustitelný soubor je mnohem větší. Spuštění programu má podobu napsání jeho jména z bezpečnostních důvodů musí být na začátku tečka a lomítko. Následně se výstup "hello, world" se také objeví na terminálu.

Místo kompilátoru můžeme použít program, tzv. interpret, který zdrojový kód přeloží i provede. V principu by mohlo jít jen o zkratku dvou kroků (kompilace+spuštění) do jednoho, ale nabízí se i možnost, že ono provádění probíhá postupně, jak jsou interpretu příkazy zadávány uživatelem. Interpret tedy umožňuje interaktivní vykonávání a i psaní kódu programu. Příklady jazyků tohoto druhu jsou mj. Python jako jazyk pro všeobecné použití a Wolfram Mathematica specializovaný na matematicko-fyzikální výpočty.

Jazyk Python

Jazyk Python verze 3, který budeme v kurzu používat pro psaní programů, spadá do této kategorie interpretovaných jazyků.

Je to jazyk, který je dnes na čele seznamu počítačových jazyků, které profesionální fyzikové používají jako každodenní nástroje k řešení problémů. Jeho použití ve vědeckých výpočtech usnadňuje to, že vzniklo mnoho knihoven, které dnes umožňují řešit jednoduché ale praktické problémy za pomoci několika málo řádek kódu.

Současné rychlé počítače umožňují pak tolerovat, že výpočty v Pythonu nemusejí probíhat nejrychleji. Jsou také připraveny cesty, jak toto omezení obejít.

Pro začátečníky však přinášejí Python ztíženou možnost automatické identifikace chyb, kterých se při psaní kódu dopustí. Většinou se o chybě vyskytující se na nějakém místě dozvíme až při provádění tohoto příkazu.

Představíme si tzv. procedurální přístup k řešení jednoduchých úloh inspirovaných matematikou a fyzikou prvních ročníků studia fyziky na MFF UK. Průběžně se seznámíme s potřebnými prvky jazyka Python, přičemž se nevyhýbáme ani "modernějším" vlastnostem jazyka, pokud vedou ke kódu, který lépe reflektuje řešený problém. je třeba vědět, že použitý jazyk nejen přímo určuje podobu tohoto zápisu nějakého výpočtu ale nepřímo ovlivňuje i použitý postup tím, jak daný jazyk některé způsoby řešení usnadňuje a jiné ztěžuje. Protože jde o úvodní kurz budeme při výkladu volit postupy, které v daném jazyce nevypadají nejlépe, ale je potřeba se s nimi seznámit.

Text předpokládá, že v případě nejasností použijete nějakou učebnici jazyka nebo jeho dokumentaci.

Euklidův algoritmus

Sekvence elementárních kroků, které zaručeně vedou k požadovanému výsledku se nazývá algoritmus. Tento pojem má v rámci matematiky svoji formální definici, která pak umožňuje formulovat matematické věty, mj. o tom, co počítač může a co nemůže "spočíst". Pro nás bude algoritmus ale jen slovní zkratkou nahrazující spojení správný postup vedoucí k vyřešení nějakého problému v konečném čase. Jako první si přestavíme Euklidův algoritmus pro výpočet největšího společného dělitele (NSD) dvou kladných celých čísel.

Euklidův návod říká, že NSD dvou čísel kladných celých čísel spočteme tak, že opakovaně od většího odečítáme to menší, dokud nedostaneme dvojici stejných čísel. Operace odečítání nemění NSD a protože takových odečítání může pro danou dvojici čísel nastat jen konečný počet, musí Euklidův návod vést v konečném počtu kroků k požadovanému výsledku.

Jak vypadá program v Pythonu

Ambicí programátora je takový výpočet nechat provádět počítač. Jak již víme, aby nám byla jeho síla zpřístupněna máme spolu s počítači k dispozici programovací jazyky, kterými svá přání počítačům sdělujeme právě v podobě návodů, co mají počítat. Nacházíme se na začátku časů, kdy počítače dokáží porozumět zadání algoritmu tak, jak je uveden výše, a kdy jej pak můžeme požádat, aby napsal program, který Euklidův algoritmus aplikuje na dvojici čísel 4477 a 1073. Protože jde o známý algoritmus nemusíme jej ani popisovat:

Human: I need a program that computes greatest common divisor of two numbers using the Euklidean algorithm. As an example use numbers 4477 and 1073.

AI: Sure! Here's a Python program that computes the greatest common divisor (GCD) of two numbers using the Euclidean algorithm:

def gcd(a, b):
    while b != 0:
        a, b = b, a % b
    return a

# Example usage
num1 = 4477
num2 = 1073

result = gcd(num1, num2)
print("The GCD of", num1, "and", num2, "is:", result)

K tomu AI dodá

When you run this program, it will output:
The GCD of 4477 and 1073 is: 97

In this example, the GCD of 4477 and 1073 is 97, which is the greatest common divisor of the two numbers computed using the Euclidean algorithm.

Zde jsme použili službu https://chat.openai.com/ ve verzi z jara 2023 zjednodušeně označovanou termínem umělá inteligence. Ta automaticky zvolila jako jazyk právě Python. Zatímco kód programu je správně, strojem odhadnutý výsledek výpočtu se správnému výsledku, tedy číslu 37 jen podobá. Takový výpočet jsme ale po našem pomocníkovi nechtěli - spustit takto jednoduchý kód je oproti jeho vymyšlení snadné.

Na složitějších příkladech lze ukázat, že se umělá inteligence může splést i v kódu programu a lze tak předpokládat, že v nadcházející době bude uživatel počítače jako nástroje pro výpočty muset stále používat za tímto účelem programovací jazyk, ovšem kód v tomto programovacím jazyce bude vznikat za stále intenzivnějšího použití nových, doposud nevídaných nástrojů. V oblasti fyziky lze předpokládat, že pro řešení nových problémů v nejbližší době budeme muset stále psát programy.

Je tedy na čase si podrobněji popsat, co takový zápis programu vyjadřuje. Pro naše potřeby má výše uvedený program nevýhodu, že jednak používá rozdělení kód programu na funkci a hlavní program, a také představuje vylepšenou verzi Euklidova algoritmu založenou na operaci zbytku po dělení celých čísel. Proto pro náš další výklad použijeme následující podobu kódu pro výpočet největšího společného dělitele:

# program pro Vypocet NSD

a = 4477                                       # vezmi dvě čísla: 4477
b = 1073                                       # a 1073

print( 'NSD(',a,',',b,')=', sep='', end='' )   # vypíše:   NSD(4477,1073)=

while not b==a:                                # dokud jsou obě čísla různá
    if a>b:                                    # zjisti, které je větší
       a = a-b                                 # a odečti menší od většího
    else:
       b = b-a

print( a )

Úvodní rozjímání nad kódem v jazyce Python

  • Čísla si na začátku asociujeme s písmeny a a b. Jde o tzv. identifikátory a vidíme, že v programu zastupují konkrétní hodnoty. Operace probíhají, jak jsme z algebry zvyklí, s těmito identifikátory, nikoli přímo s čísly.

  • Identifikátory a a b označují tzv. proměnné.

  • Zavedení proměnné má povahu přiřazovacího příkazu (např. `a = 4477``).

  • Na rozdíl od mnoha jiných jazyků v tomto kroku nevyjadřujeme přání, že proměnná je určena k ukládání celých čísel. To proto, že zatím je v Pythonu zvykem se neobtěžovat s uváděním typu u proměnných. Navíc, v tomto jazyce může proměnná za dobu běhu programu nabývat různých typů podle toho, co do ní "přiřadíme". Uvidíme, že podle situace může jít při psaní programu o výhodu i komplikaci. (Porovnejte alternativu v C++, kde i když při deklaraci neuvedeme explicitně typ proměnné, např. auto x=1.2, nemůžeme později přiřadit x="text".)

  • Kromě přiřazovacího příkazu promenna = nova_hodnota jsou v programu i další příkazy. Komunikaci směrem k uživateli programu představuje print(neco, neco_dalsiho, ... ). Opakování zařizuje příkaz while podminka: toto_opakuj. Rozhodnutí co učinit představuje příkaz if podminka: co_pak else: co_jinak.

  • Python nemá "programové závorky" jako jsou např. složené závorky {...} v C.

  • Rozdělení kódu programu na řádky má v kódu klíčový význam protože konce řádků ukončují příkazy (většinou).

  • Mezery (tabelátor) na začátku řádků jsou velmi důležité. Určují totiž například, které příkazy jsou součástí těla cyklu while a které následují až po jeho ukončení.

  • Zatímco přiřazovací příkaz používá jednoduché =, porovnání na rovnost používá zdvojené rovnítko ==.

  • Příkaz print automaticky vypisuje mezi jednotlivými argumenty mezery, abychom je potlačili použijeme nepovinný pojmenovaný argument sep='', kterým oznámíme, že mezi argumenty chceme vkládat nic (podobně jako v jiných jazycích '',' ','  ' představuje zápis žádné, jedné a dvou mezer).

  • Příkaz print zakončí výpis automatickým přechodem na nový řádek, to potlačíme uvedením end='' jako nepovinného argumentu funkce print.

  • Komentáře vysvětlující záměr programátora následují po znaku #.

Spouštíme kód v Pythonu

Další velkou výhodou je velký výběr prostředí umožňujících programování v Pythonu. Pro nás to ale znamená, že se musíme seznámit hned s několika metodami, jakými budeme programy v tomto jazyce psát a spouštět.

Python na příkazové řádce

Prvním příkladem bude situace, kdy spouštíme program v prostředí OS Windows 10. Následující obrázek zachycuje pracovní plochu s dvěma spuštěnými programy. Textovým editorem Notepad++ nahoře a tzv. příkazovou řádkou emulující tzv. terminál počítače. Textový editor zobrazuje uvažovaný zdrojový kód zmíněný výše, na příkazové řádce jsou pak vidět dva příkazy. První,

cd Documents\programovani

říká systému, že chceme změnit běžný adresář (cd=change directory). To proto abychom v dalším řádku nemuseli psát podrobně cestu k souboru C:\Users\student\Documents\programovani\nsdtest.py se zdrojovým kódem našeho programu. Místo toho napíšeme příkaz, kde uvedeme jen název programu

python nsdtest.py

Tento příkaz říká - spusť interpret jazyka python a tomu řekni, že má provést příkazy v souboru nsdtest.py.

image0

Zde jsme použili samostatné programy na psaní kódu a pro terminál s příkazovou řádkou.

Python v IDE Visual Studio Code

Pro pohodlí programátora je časté tyto spojit do jediného integrovaného vývojového prostředí (IDE). Jeho příklad ukazuje následující obrázek. Je na něm v editoru IDE otevřen soubor nsdtest.py a tento je pak spuštěn znakem > vpravo nahoře. Výsledek je vidět v panelu vpravo dole.

image1

Interpretr Python v režimu REPL

V příkazové řádce můžeme spustit interpret Python i bez argumentu určujícího, který kód se má spustit. V tom případě se dostaneme do prostředí označovaného zkratkou REPL (reade-valuate-print loop). Příklad použití ukazuje následující záznam komunikace, kde interpret reaguje na příkazy zadané z klávesnice (ty začínají ">>>")

Python 3.10.12 (main, Jun 11 2023, 05:26:28) [GCC 11.4.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> a = 2
>>> x = 1.5
>>> x=(x+a/x)/2
>>> x
1.4166666666666665
>>> x=(x+a/x)/2
>>> x
1.4142156862745097
>>> x=(x+a/x)/2
>>> x
1.4142135623746899
>>> x=(x+a/x)/2
>>> x
1.414213562373095
>>>

Jistě jste poznali opakované použití vztahu pro výpočet \(\sqrt{a}\)

\[x_{n+1} =\frac12 \left(x_n+\frac{a}{x}\right).\]

Za pozornost stojí chování REPL, který, pokud má zadání podobu výrazu (např. x), vytiskne výslednou hodnotu výrazu, zatímco pokud je zadání příkaz (např. přiřazovací), nic se netiskne.

Prostředí Jupyter

Další, velmi pohodlnou možností bude prostředí pracovního sešitu Jupyter. Toto prostředí umožňuje kombinovat strukturovaný text s kusy kódu programu následovaných výstupem tohoto kódu. Oproti konzoli, může ten mít i podobu obrázku, animace nbo i zvuku. Důležité ale snadné je pochopit, že pořadí provádění kódu je určeno tím, jak uživatel spouští a přeskakuje jednotlivé buňky, nikoli přímo pořadím buněk v sešitu.

Prostředí Jupyter je m.j. dostupné v rámci Visual Studio Code a také (září 2023) jako volná služba Google Colab

Následující buňka obsahuje zdrojový kód a výsledek jeho běhu.

[1]:
# program pro Vypocet NSD

a = 4477                                       # vezmi dvě čísla: 4477
b = 1073                                       # a 1073

print( 'NSD(',a,',',b,')=', sep='', end='' )   # vypíše:   NSD(4477,1073)=

while not b==a:                                # dokud jsou obě čísla různá
    if a>b:                                    # zjisti, které je větší
       a = a-b                                 # a odečti menší od většího
    else:
       b = b-a

print( a )
NSD(4477,1073)=37

Velkou výhodou takového postupu je, že ačkoli kód již proběhl a výsledky běhu tohoto kusu kódu jsou již zobrazeny pod ním, data se kterými se počítá (přesněji tzv. globální proměnné) jsou i nadále k dispozici. Můžeme toho využít a např. zjistit, kolik je 4477/a takto

[2]:
4477/a
[2]:
121.0

Uveďme však, že oblíbenost jazyka Python spočívá též v tom, že pro kdejaký myslitelný problém je k dispozici potřebný nástroj. Například pro nalezení NSD (anglicky GCD) stačí napsat

[3]:
import math
print( math.gcd(4477, 1073) )
37

Zde je math název knihovny, kde jsou shromážděny mnohé užitečné funkce. Ty co tam nenajdete, jsou v dalších snadno dostupných knihovnách.

V našem kurzu budeme používat obojí přístup. Nějaké funkce si napíšeme, abychom se to naučili, jiné budeme používat hotové a připravené v dostupných knihovnách a budeme s jejich pomocí řešit složitější úlohy.

Kontrolní úlohy

  1. Vyzkoušejte všechny tři metody spuštění programu pro NSD pro dvojici čísel 11322 a 6035.

  2. Spočtěte v REPL hodnotu \(\sqrt2\) na základě příkladu výše. Nemáte-li zatím nainstalovaný Python, zkuste si to na https://trinket.io/console Nezapomeňte, že k jednou již zadanému výrazu nebo příkazu se můžete vrátit klávesou <up>.

  3. Totéž si zkuste v prostředí Jupyter lite. Pro otevření nového sešitu zvolte v menu File -> New -> Notebook nebo klikněte na ikonu Python (Pyodide) v odstavci Notebook v záložce Launcher.