1. SageMath & Python#
Počítačový algebraický systém SageMath (zkráceně též "Sage") je založen na rozšířeném programovacím jazyku Python. Díky tomu SageMath obsahuje celou řadu zajímavých matematických balíčků (knihoven) dostupných v Python prostředí (např. NumPy, SymPy a další). Řada funkcionalit v SageMath je implementována formou bezešvého rozhraní k jinému open-source matematickému programu či knihovně. Například některé symbolické operace (úpravy algebraických výrazů, symbolická integrace) jsou prováděny pomocí programu Maxima. Kompletní seznam takovýchto programů a knihoven lze nalézt v dokumentaci.
Python, jakožto interpretovaný jazyk, je pomalý. To nám ale nevadí. SageMath ho využívá primárně (pokud to lze) pouze jako pojivo. Kritické výpočty využívají Cython (rozšíření Python vytvořené pro vývoji SageMath, ale s širším využitím), nebo specializovanou knihovnu napsanou v nějakém - pro tyto účely vhodnějším - jazyce (C, FORTRAN, atp.). I výše zmíněný a veleznámý Python balíček NumPy, umožňující provádět lineárně-algebraické numerické operace, je ve skutečnosti pouze Python rozhranní k C a FORTRAN knihovnám.
Orientovat se v Pythonu, alespoň na základní úrovni, je proto důležité. V této kapitole shrneme to nejnutnější minimum týkající se jazyka Python, aby případní čtenáři neměli problém chápat další výklad. Čtenáři prahnoucí po hlubším proniknutí do tohoto programovacího jazyka nebudou mít problém nalézt zajímavé studijní zdroje na internetu. Na FIT ČVUT navíc existují dva předměty dedikované Pythonu: bakalářský BI-PYT a magisterský NI-PYT.
1.1 Instalace SageMath#
Stačí postupovat podle instalačních pokynů. V dnešní době by neměl být problém zprovoznit SageMath na vašem oblíbeném operačním systému. Alternativně mohou uživatelé využít on-line službu CoCalc.
Tyto notebooky pak můžete zobrazit experimentovat s nimi v Jupyter Notebooku, který jsou součásti SageMath. Z příkazové řádky server spustíte příkazem
$ sage --notebook
Automaticky by se pak měla otevřít stránka ve vašem výchozím prohlížeči.
1.2 Proměnné#
V Pythonu není potřeba deklarovat typ proměnných, k inicializaci se používá standardní symbol přiřazení =
.
Kód v následující buňce vyhodnotíte stiskem SHIFT+ENTER.
Jupyter Notebook vypíše hodnotu posledního výrazu, k výpisu více hodnot lze použít funkci print
nebo show
(viz níže).
a = 4 # integer
print(a) # výpis na stadardní výstup pomocí `print`
a = 'Hello world!' # řetězec, lze použít " i '
print(a)
4
Hello world!
Přiřazení čísel a řetězců předává hodnotu:
b = a # <- a nese hodnotu 'Hello world!'
b = 12 # <- původní hodnota a nebude změněna
print(a)
print(b)
Hello world!
12
1.3 List (pole) a tuple (ntice)#
Často používanou datovou strukturou je tzv. list
, či pole.
Pokud chceme list
zadat explicitně používáme k tomu hranaté závorky a prvky oddělujeme čárkami.
K prvkům pole pak přistupujeme pomocí indexu běžícího od nuly.
Počet prvků listu získáme pomocí funkce len
.
a = [1, 'B', pi ]
print(a[1])
print(len(a))
B
3
Pozor, přiřazení nyní předává jen referenci na objekt. Tj.:
b = a # <- b i a ukazují na stejný list
a[1] = 100
b[2] = 'Ahoj!'
print(a) # oba výpisy jsou shodné, výše jsme změnili jeden
print(b) # a ten samý list!
[1, 100, 'Ahoj!']
[1, 100, 'Ahoj!']
Prvky pole jak vidno můžeme měnit (jde o mutable objekt). Oproti tomu prvky tuple (ntice) měnit nemůžeme (jde o immutable objekt). Tuple vytvoříme pomocí kulatých závorek a k jeho prvkům přistupujeme také pomocí hranatých závorek:
t = (4, "dva", sin)
t[2]
sin
Pozor, případnou 1tici musíme zadat jako (42,)
, výraz (42)
by byl vyhodnocen jakou pouhé celé číslo.
(42,) == 42
False
1.4 Dictionary (slovník)#
Pro práci s zobrazeními klíč-hodnota slouží slovníky.
d = dict() # vytvoří prázdný slovník
d['a'] = 42 # pod klíčem 'a' uloží hodnotu 42
d['b'] = 'Ahoj!' # pod klíčem 'b' uloží hodnotu 'Ahoj!'
print(d)
{'a': 42, 'b': 'Ahoj!'}
Alternativně můžeme použít rovnou složených závorek:
dd = { 1: 'Hello', 2: 'World', 'a': 'b' }
print(dd)
{1: 'Hello', 2: 'World', 'a': 'b'}
1.5 Řídící struktury#
Nyní se podívejme na jednu specifickou, a pro nováčka potenciálně matoucí vlastnost Pythonu. K oddělení bloku kódu se nepoužívá klíčových slov, ani závorek, ale odsazení. Demonstrujme tento jev na ukázce if-then-else řídící struktury.
if pi > 3:
print('Ano!')
else:
print('Ne!')
Ano!
V případě více možností můžeme přidat i podmínky elif
.
Podobně se chová známý for
cyklus (pod range(4)
je dobré představovat si množinu indexů od \(0\) do \(3\), tedy délky \(4\)):
for k in range(4):
y = k^2
print(y)
0
1
4
9
Často se hodí i jednoduchý while
cyklus.
j = 50
while j > 0:
print(j)
j = j - 12
50
38
26
14
2
1.6 Funkce a metody#
Pokud chceme definovat vlastní funkci, použijeme pro to klíčové slovo def
.
Všimněte si opět odsazení.
def f(x):
"""
Báječná funkce umožňující vypočítat čtyřnásobek svého argumentu.
"""
y = 4 * x
return y
Dále je dobré zdůraznit, že u argumentů funkcí není potřeba udávat jejich typ. Naše funkce bude fungovat na všech objektech, pro které lze provést operace uvedené v těle funkce. Například:
show(f(4))
show(f(pi))
show(f('Ahoj!'))
Python je objektový jazyk. Prostředí SageMath Notebook nabízí kontextovou nápovědu snadno vyvolatelnou pomocí klávesy TAB. Zkuste nastavit kurzor za tečku a stisknout ji, získáte tak výpis vlastností a metod, které daný objekt poskytuje.
a = 9 # malé celé číslo
print('Je prvočíslo?')
print(a.is_prime())
print('Faktorizace:')
show(a.factor())
Je prvočíslo?
False
Faktorizace:
Ve výpisech používáme dvě metody: print
a show
. První uvedená vytváří textovou reprezentaci objektu. Druhá se snaží objekt zobrazit skutečně hezky (vzorce pomocí LaTeXu, poradí si s grafickými objekty, atp.).
1.7 Dokumentace a dál...#
Pokud si nevíme rady s jistou funkcí, můžeme vyvolat nápovědu po stisknoutí TAB klávesy za otevírací závorkou. Případně lze nápovědu vypsat pomocí otazníku za názvem funkce.
# vyhodnoťte tuto buňku stiskem SHIFT+ENTER
a.is_prime?
Docstring:
Test whether "self" is prime.
INPUT:
* "proof" -- Boolean or "None" (default). If False, use a strong
pseudo-primality test (see "is_pseudoprime()"). If True, use a
provable primality test. If unset, use the "default arithmetic
proof flag".
Note:
Integer primes are by definition *positive*! This is different
than Magma, but the same as in PARI. See also the
"is_irreducible()" method.
EXAMPLES:
sage: z = 2^31 - 1
sage: z.is_prime()
True
sage: z = 2^31
sage: z.is_prime()
False
sage: z = 7
sage: z.is_prime()
True
sage: z = -7
sage: z.is_prime()
False
sage: z.is_irreducible()
True
sage: z = 10^80 + 129
sage: z.is_prime(proof=False)
True
sage: z.is_prime(proof=True)
True
When starting Sage the arithmetic proof flag is True. We can change
it to False as follows:
sage: proof.arithmetic()
True
sage: n = 10^100 + 267
sage: timeit("n.is_prime()") # not tested
5 loops, best of 3: 163 ms per loop
sage: proof.arithmetic(False)
sage: proof.arithmetic()
False
sage: timeit("n.is_prime()") # not tested
1000 loops, best of 3: 573 us per loop
ALGORITHM:
Calls the PARI "isprime" function.
Init docstring: Initialize self. See help(type(self)) for accurate signature.
File: ~/src/sage/src/sage/rings/integer.pyx
Type: builtin_function_or_method
f?
Signature: f(x)
Docstring: Báječná funkce umožňující vypočítat čtyřnásobek svého argumentu.
Init docstring: Initialize self. See help(type(self)) for accurate signature.
File: /tmp/ipykernel_394624/757238393.py
Type: function
Dva otazníky zobrazí zdrojový kód.
a.is_prime??
Docstring:
Test whether "self" is prime.
INPUT:
* "proof" -- Boolean or "None" (default). If False, use a strong
pseudo-primality test (see "is_pseudoprime()"). If True, use a
provable primality test. If unset, use the "default arithmetic
proof flag".
Note:
Integer primes are by definition *positive*! This is different
than Magma, but the same as in PARI. See also the
"is_irreducible()" method.
EXAMPLES:
sage: z = 2^31 - 1
sage: z.is_prime()
True
sage: z = 2^31
sage: z.is_prime()
False
sage: z = 7
sage: z.is_prime()
True
sage: z = -7
sage: z.is_prime()
False
sage: z.is_irreducible()
True
sage: z = 10^80 + 129
sage: z.is_prime(proof=False)
True
sage: z.is_prime(proof=True)
True
When starting Sage the arithmetic proof flag is True. We can change
it to False as follows:
sage: proof.arithmetic()
True
sage: n = 10^100 + 267
sage: timeit("n.is_prime()") # not tested
5 loops, best of 3: 163 ms per loop
sage: proof.arithmetic(False)
sage: proof.arithmetic()
False
sage: timeit("n.is_prime()") # not tested
1000 loops, best of 3: 573 us per loop
ALGORITHM:
Calls the PARI "isprime" function.
Source:
def is_prime(self, proof=None):
r"""
Test whether ``self`` is prime.
INPUT:
- ``proof`` -- Boolean or ``None`` (default). If False, use a
strong pseudo-primality test (see :meth:`is_pseudoprime`).
If True, use a provable primality test. If unset, use the
:mod:`default arithmetic proof flag <sage.structure.proof.proof>`.
.. NOTE::
Integer primes are by definition *positive*! This is
different than Magma, but the same as in PARI. See also the
:meth:`is_irreducible()` method.
EXAMPLES::
sage: z = 2^31 - 1
sage: z.is_prime()
True
sage: z = 2^31
sage: z.is_prime()
False
sage: z = 7
sage: z.is_prime()
True
sage: z = -7
sage: z.is_prime()
False
sage: z.is_irreducible()
True
::
sage: z = 10^80 + 129
sage: z.is_prime(proof=False)
True
sage: z.is_prime(proof=True)
True
When starting Sage the arithmetic proof flag is True. We can change
it to False as follows::
sage: proof.arithmetic()
True
sage: n = 10^100 + 267
sage: timeit("n.is_prime()") # not tested
5 loops, best of 3: 163 ms per loop
sage: proof.arithmetic(False)
sage: proof.arithmetic()
False
sage: timeit("n.is_prime()") # not tested
1000 loops, best of 3: 573 us per loop
ALGORITHM:
Calls the PARI ``isprime`` function.
TESTS:
We compare the output of this method to a straightforward sieve::
sage: size = 10000
sage: tab = [0,0] + [1] * (size-2)
sage: for i in range(size):
....: if tab[i]:
....: for j in range(2*i, size, i):
....: tab[j] = 0
sage: all(ZZ(i).is_prime() == b for i,b in enumerate(tab))
True
"""
if mpz_sgn(self.value) <= 0:
return False
cdef unsigned long u
if mpz_fits_ulong_p(self.value):
u = mpz_get_ui(self.value)
if not (u & 1):
return u == 2
if u < 1000:
return _small_primes_table[u >> 1]
global pari_is_prime
if pari_is_prime is None:
try:
from sage.libs.pari.convert_sage import pari_is_prime
except ImportError:
pass
if pari_is_prime is not None:
return pari_is_prime(self)
if proof is None:
from sage.structure.proof.proof import get_flag
proof = get_flag(proof, "arithmetic")
if proof:
return self.__pari__().isprime()
else:
return self.__pari__().ispseudoprime()
File: ~/src/sage/src/sage/rings/integer.pyx
Type: builtin_function_or_method
f??
Signature: f(x)
Docstring: Báječná funkce umožňující vypočítat čtyřnásobek svého argumentu.
Source:
def f(x):
"""
Báječná funkce umožňující vypočítat čtyřnásobek svého argumentu.
"""
y = Integer(4) * x
return y
File: /tmp/ipykernel_394624/757238393.py
Type: function
Informace uvedené zde nejsou v žádném případě vyčerpávající. Smyslem je zde ukázat základní vlastnosti a specifika jazyka, aby pak čtenářky a čtenáři neměli problém s pochopením dalších partií tohoto dílka. Zájemce odkazuji na výše zmíněné předměty BI-PYT a případně pak později NI-PYT. Dobrým zdrojem informací je i oficiální dokumentace SageMath.