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!'))
\(\displaystyle 16\)
\(\displaystyle 4 \, \pi\)
\(\displaystyle \verb|Ahoj!Ahoj!Ahoj!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:
\(\displaystyle 3^{2}\)

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.