Tutorial

Einführung

SymPy ist eine Python-Bibliothek für symbolische Mathematik. SymPy soll ein vollständiges Computer-Algebra-System (CAS) bereitstellen und dabei den Programmcode so einfach halten, dass er nachvollziehbar und einfach erweiterbar bleibt. SymPy ist vollständig in Python geschrieben und benötigt keine weiteren Bibliotheken.

Dieses Tutorial schafft einen Überblick und gibt eine Einführung in SymPy. Wenn du wissen möchtest, was SymPy tun kann (und wie), lies diese Seite, und wenn du mehr erfahren möchtest, lies den SymPy User’s Guide, die SymPy Modules Reference, oder den Quellcode selbst.

Erste Schritte mit SymPy

Am einfachsten kannst du SymPy mit der aktuellsten .tar-Datei aus den “Featured Downloads” von http://code.google.com/p/sympy/ installieren:

../_images/featured-downloads.png

Dann musst du die Datei auspacken:

$ tar xzf sympy-0.5.12.tar.gz

und kannst sie mit einem Python-Interpreter ausprobieren.

$ cd sympy-0.5.12
$ python
Python 2.4.4 (#2, Jan  3 2008, 13:36:28)
[GCC 4.2.3 20071123 (prerelease) (Debian 4.2.2-4)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from sympy import Symbol, cos
>>> x = Symbol("x")
>>> (1/cos(x)).series(x, 0, 10)
1 + x**2/2 + 5*x**4/24 + 61*x**6/720 + 277*x**8/8064 + O(x**10)

SymPy kann wie im Beispiel gezeigt benutzt werden, und dies ist auch der übliche Weg, es von einem anderen Programm aus zu verwenden. Du kannst es ebenfalls wie jedes andere Modul mithilfe von ./setup.py install installieren. Auch ist es natürlich möglich, einfach ein Paket deiner Linux-Distribution zu verwenden, zum Beispiel:

Installing SymPy in Debian

$ sudo apt-get install python-sympy
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following NEW packages will be installed:
  python-sympy
0 upgraded, 1 newly installed, 0 to remove and 18 not upgraded.
Need to get 991kB of archives.
After this operation, 5976kB of additional disk space will be used.
Get:1 http://ftp.cz.debian.org unstable/main python-sympy 0.5.12-1 [991kB]
Fetched 991kB in 2s (361kB/s)
Selecting previously deselected package python-sympy.
(Reading database ... 232619 files and directories currently installed.)
Unpacking python-sympy (from .../python-sympy_0.5.12-1_all.deb) ...
Setting up python-sympy (0.5.12-1) ...

Weitere Installationsmöglichkeiten findest du unter Download and Installation (auf Englisch) auf der SymPy-Wiki.

isympy-Konsole

Für Experimente mit neuen Funktionen und zum Ausprobieren kann die um IPython aufgebaute Umgebung namens isympy (im Quellverzeichnis unter bin/isympy) benutzt werden. Es handelt sich hierbei nur um eine Standard-Python-Konsole, die allerdings bereits die wichtigen sympy-Module importiert hat und unter anderem die Symbole x, y und z bereits definiert hat:

$ cd sympy
$ ./bin/isympy
IPython console for SymPy 0.7.2-git (Python 2.7.1) (ground types: gmpy)

These commands were executed:
>>> from __future__ import division
>>> from sympy import *
>>> x, y, z, t = symbols('x y z t')
>>> k, m, n = symbols('k m n', integer=True)
>>> f, g, h = symbols('f g h', cls=Function)

Documentation can be found at http://www.sympy.org

In [1]: (1/cos(x)).series(x, 0, 10)
Out[1]:
     2      4       6        8
    x    5*x    61*x    277*x     / 10\
1 + -- + ---- + ----- + ------ + O\x  /
    2     24     720     8064

Bemerkung

Benutzereingaben sind fett dargestellt. Was in einem normalen Python-Interpreter drei Zeilen gebraucht hätte, kann mit isympy in nur einer Zeile ausgedrückt werden.

Mit SymPy rechnen

SymPy hat drei eingebaute Zahlentypen: Float, Rational und Integer.

Die Klasse Rational stellt eine rationale Zahl als Paar von zwei Ganzzahlen dar: dem Zähler und dem Nenner. Rational(1, 2) repräsentiert also 1/2, Rational(5, 2) repräsentiert 5/2 und so weiter.

>>> from sympy import Rational
>>> a = Rational(1, 2)

>>> a
1/2

>>> a*2
1

>>> Rational(2)**50/Rational(10)**50
1/88817841970012523233890533447265625

Beim Arbeiten mit Pythons Ganzzahlen und Fließkommazahlen ist Vorsicht geboten, besonders bei Division, da eine Python-Zahl statt einer SymPy-Zahl das Ergebnis sein kann. Eine Division von zwei Python-Ganzzahlen hat eine Fließkommazahl zum Ergebnis – die “echte Divison” von Python 3 und das Standardverhalten von isympy, welches division aus __future__ importiert:

>>> 1/2 
0.5

In älteren Python-Versionen ohne den Import von division ist das Ergebnis hingegen eine abgerundete Ganzzahl:

>>> 1/2 
0

In beiden Fällen handelt es sich jedoch nicht um eine SymPy-Zahl, weil Python seine eigenen Zahl zurückgegeben hat. Meistens wirst du aber mit Zahlen des Typs Rational arbeiten wollen, also achte darauf, auch solche zu erhalten. Oft wird zur besseren Lesbarkeit R mit Rational gleichgesetzt:

>>> R = Rational
>>> R(1, 2)
1/2
>>> R(1)/2 # R(1) is a SymPy Integer and Integer/int gives a Rational
1/2

Es gibt auch einige spezielle Konstanten wie e oder pi, die als Symbole behandelt werden (1 + pi wird nicht als Zahl ausgerechnet, sondern bleibt 1 + pi) und in beliebiger Präzision verfügbar sind:

>>> from sympy import pi, E
>>> pi**2
pi**2

>>> pi.evalf()
3.14159265358979

>>> (pi + E).evalf()
5.85987448204884

Wie man sieht, berechnet evalf aus dem Ausdruck eine Fließkommazahl.

Das Symnol oo wird für eine Klasse benutzt, die mathematische Unendlichkeit darstellt:

>>> from sympy import oo
>>> oo > 99999
True
>>> oo + 1
oo

Symbole

Im Gegensatz zu anderen Computer-Algebra-Systemen müssen in SymPy symbolische Variablen ausdrücklich deklariert werden:

>>> from sympy import Symbol
>>> x = Symbol('x')
>>> y = Symbol('y')

Links steht eine normale Python-Variable, der ein SymPy-Symbol-Objekt zugewiesen wird. Vordefiniert Symbole (inklusiv Symbole mit griechischen Namen) sind von Import von abc verfügbar:

>>> from sympy.abc import x, theta

Symbole können auch mit den symbols oder var Funktionen erstellt werden. Die letztere fügt automatisch die Symbole in den Namespace ein, und beide Funktionen akzeptieren eine Reihennotation:

>>> from sympy import symbols, var
>>> a, b, c = symbols('a,b,c')
>>> d, e, f = symbols('d:f')
>>> var('g:h')
(g, h)
>>> var('g:2')
(g0, g1)

Aus Symbol-Objekten kann man bequem Ausdrücke zusammensetzen:

>>> x + y + x - y
2*x

>>> (x + y)**2
(x + y)**2

>>> ((x + y)**2).expand()
x**2 + 2*x*y + y**2

Sie können mit subs(old, new) durch Zahlen, andere Symbole oder Ausdrücke ersetzt werden:

>>> ((x + y)**2).subs(x, 1)
(y + 1)**2

>>> ((x + y)**2).subs(x, y)
4*y**2

>>> ((x + y)**2).subs(x, 1 - y)
1

Für den Rest des Tutorials gehen wir davon aus, dass folgende Zeile ausgeführt wurde:

>>> from sympy import init_printing
>>> init_printing(use_unicode=False, wrap_line=False, no_global=True)

Dies sorgt dafür, dass Ausdrücke bei der Ausgabe besser aussehen (siehe Ausgabe weiter unten). Wenn eine Unicode-Schrift installiert ist, erreichst du mit use_unicode=True eine noch hübschere Ausgabe.

Algebra

Für die Partialbruchzerlegung kannst du apart(expr, x) benutzen:

>>> from sympy import apart
>>> from sympy.abc import x, y, z

>>> 1/( (x + 2)*(x + 1) )
       1
---------------
(x + 1)*(x + 2)

>>> apart(1/( (x + 2)*(x + 1) ), x)
    1       1
- ----- + -----
  x + 2   x + 1

>>> (x + 1)/(x - 1)
x + 1
-----
x - 1

>>> apart((x + 1)/(x - 1), x)
      2
1 + -----
    x - 1

Zum Kombinieren gibt es die Funktion together(expr, x):

>>> from sympy import together
>>> together(1/x + 1/y + 1/z)
x*y + x*z + y*z
---------------
     x*y*z

>>> together(apart((x + 1)/(x - 1), x), x)
x + 1
-----
x - 1

>>> together(apart(1/( (x + 2)*(x + 1) ), x), x)
       1
---------------
(x + 1)*(x + 2)

Infinitesimalrechnung

Limes

Grenzwerte sind in SymPy einfach zu benutzen, sie folgen der Syntax limit(function, variable, point), um also den Grenzwert von f(x) bei x -> 0 zu berechnen, kann limit(f, x, 0) benutzt werden:

>>> from sympy import limit, Symbol, sin, oo
>>> x = Symbol("x")
>>> limit(sin(x)/x, x, 0)
1

Analog kann der Limes für x gegen unendlich berechnet werden:

>>> limit(x, x, oo)
oo

>>> limit(1/x, x, oo)
0

>>> limit(x**x, x, 0)
1

Einige nicht-triviale Beispiele zu Grenzwerten finden sich in der Datei test_demidovich.py

Differentialrechnung

Mithilfe von diff(func, var) kann jeder SymPy-Ausdruck differenziert werden. Beispiele:

>>> from sympy import diff, Symbol, sin, tan
>>> x = Symbol('x')
>>> diff(sin(x), x)
cos(x)
>>> diff(sin(2*x), x)
2*cos(2*x)

>>> diff(tan(x), x)
   2
tan (x) + 1

Folgendermaßen kann überprüft werden, ob dies korrekt ist:

>>> from sympy import limit
>>> from sympy.abc import delta
>>> limit((tan(x + delta) - tan(x))/delta, delta, 0)
   2
tan (x) + 1

Höhere Ableitungen können mithilfe von diff(func, var, n) berechnet werden:

>>> diff(sin(2*x), x, 1)
2*cos(2*x)

>>> diff(sin(2*x), x, 2)
-4*sin(2*x)

>>> diff(sin(2*x), x, 3)
-8*cos(2*x)

Reihenentwicklung

Benutze .series(var, point, order):

>>> from sympy import Symbol, cos
>>> x = Symbol('x')
>>> cos(x).series(x, 0, 10)
     2    4     6      8
    x    x     x      x      / 10\
1 - -- + -- - --- + ----- + O\x  /
    2    24   720   40320
>>> (1/cos(x)).series(x, 0, 10)
     2      4       6        8
    x    5*x    61*x    277*x     / 10\
1 + -- + ---- + ----- + ------ + O\x  /
    2     24     720     8064

Ein weiteres einfaches Beispiel:

>>> from sympy import Integral, pprint

>>> y = Symbol("y")
>>> e = 1/(x + y)
>>> s = e.series(x, 0, 5)

>>> print(s)
1/y - x/y**2 + x**2/y**3 - x**3/y**4 + x**4/y**5 + O(x**5)
>>> pprint(s)
          2    3    4
1   x    x    x    x     / 5\
- - -- + -- - -- + -- + O\x /
y    2    3    4    5
    y    y    y    y

Summation

Compute the summation of f with respect to the given summation variable over the given limits.

summation(f, (i, a, b)) computes the sum of f with respect to i from a to b, i.e.,

                            b
                          ____
                          \   `
summation(f, (i, a, b)) =  )    f
                          /___,
                          i = a

If it cannot compute the sum, it prints the corresponding summation formula. Repeated sums can be computed by introducing additional limits:

>>> from sympy import summation, oo, symbols, log
>>> i, n, m = symbols('i n m', integer=True)

>>> summation(2*i - 1, (i, 1, n))
 2
n
>>> summation(1/2**i, (i, 0, oo))
2
>>> summation(1/log(n)**n, (n, 2, oo))
  oo
 ___
 \  `
  \     -n
  /   log (n)
 /__,
n = 2
>>> summation(i, (i, 0, n), (n, 0, m))
      3    2
m    m    m
-- + -- + -
6    2    3
>>> from sympy.abc import x
>>> from sympy import factorial
>>> summation(x**n/factorial(n), (n, 0, oo))
 x
e

Integralrechnung

SymPy unterstützt unendliche und endliche Integration transzendenter elementarer und spezieller Funktionen durch integrate(), welches den starken Risch-Norman-Algorithmus nutzt, sowie einige Heuristiken und Mustererkennungen:

>>> from sympy import integrate, erf, exp, sin, log, oo, pi, sinh, symbols
>>> x, y = symbols('x,y')

Es können elementare Funktionen integriert werden:

>>> integrate(6*x**5, x)
 6
x
>>> integrate(sin(x), x)
-cos(x)
>>> integrate(log(x), x)
x*log(x) - x
>>> integrate(2*x + sinh(x), x)
 2
x  + cosh(x)

Aber auch mit speziellen Funktionen kann einfach umgegangen werden:

>>> integrate(exp(-x**2)*erf(x), x)
  ____    2
\/ pi *erf (x)
--------------
      4

Es ist möglich, ein endliches Integral zu berechnen:

>>> integrate(x**3, (x, -1, 1))
0
>>> integrate(sin(x), (x, 0, pi/2))
1
>>> integrate(cos(x), (x, -pi/2, pi/2))
2

Auch uneigentliche Integrale werden unterstützt:

>>> integrate(exp(-x), (x, 0, oo))
1
>>> integrate(log(x), (x, 0, 1))
-1

Komplexe Zahlen

Mit Ausnahme der imaginären Einheit, I, die rein imaginär ist, können Symbole mit Attributen (z.B. reale, positive, komplex, usw.) erstellt werden, und dies hat Auswirkungen darauf, wie sie sich verhalten:

>>> from sympy import Symbol, exp, I
>>> x = Symbol("x") # a plain x with no attributes
>>> exp(I*x).expand()
 I*x
e
>>> exp(I*x).expand(complex=True)
   -im(x)               -im(x)
I*e      *sin(re(x)) + e      *cos(re(x))
>>> x = Symbol("x", real=True)
>>> exp(I*x).expand(complex=True)
I*sin(x) + cos(x)

Funktionen

trigonometrische

>>> from sympy import asin, asinh, cos, sin, sinh, symbols, I
>>> x, y = symbols('x,y')

>>> sin(x + y).expand(trig=True)
sin(x)*cos(y) + sin(y)*cos(x)

>>> cos(x + y).expand(trig=True)
-sin(x)*sin(y) + cos(x)*cos(y)

>>> sin(I*x)
I*sinh(x)

>>> sinh(I*x)
I*sin(x)

>>> asinh(I)
I*pi
----
 2

>>> asinh(I*x)
I*asin(x)

>>> sin(x).series(x, 0, 10)
     3     5     7       9
    x     x     x       x       / 10\
x - -- + --- - ---- + ------ + O\x  /
    6    120   5040   362880

>>> sinh(x).series(x, 0, 10)
     3     5     7       9
    x     x     x       x       / 10\
x + -- + --- + ---- + ------ + O\x  /
    6    120   5040   362880

>>> asin(x).series(x, 0, 10)
     3      5      7       9
    x    3*x    5*x    35*x     / 10\
x + -- + ---- + ---- + ----- + O\x  /
    6     40    112     1152

>>> asinh(x).series(x, 0, 10)
     3      5      7       9
    x    3*x    5*x    35*x     / 10\
x - -- + ---- - ---- + ----- + O\x  /
    6     40    112     1152

Kugelflächen

>>> from sympy import Ylm
>>> from sympy.abc import theta, phi

>>> Ylm(1, 0, theta, phi)
  ___
\/ 3 *cos(theta)
----------------
        ____
    2*\/ pi

>>> Ylm(1, 1, theta, phi)
   ___  I*phi
-\/ 6 *e     *sin(theta)
------------------------
            ____
        4*\/ pi

>>> Ylm(2, 1, theta, phi)
   ____  I*phi
-\/ 30 *e     *sin(theta)*cos(theta)
------------------------------------
                  ____
              4*\/ pi

Fakultät und Gamma-Funktion

>>> from sympy import factorial, gamma, Symbol
>>> x = Symbol("x")
>>> n = Symbol("n", integer=True)

>>> factorial(x)
x!

>>> factorial(n)
n!

>>> gamma(x + 1).series(x, 0, 3) # i.e. factorial(x)
                      /          2     2\
                    2 |EulerGamma    pi |    / 3\
1 - EulerGamma*x + x *|----------- + ---| + O\x /
                      \     2         12/

Zeta-Funktion

>>> from sympy import zeta
>>> zeta(4, x)
zeta(4, x)

>>> zeta(4, 1)
  4
pi
---
 90

>>> zeta(4, 2)
       4
     pi
-1 + ---
      90

>>> zeta(4, 3)
         4
  17   pi
- -- + ---
  16    90

Polynome

>>> from sympy import assoc_legendre, chebyshevt, legendre, hermite
>>> chebyshevt(2, x)
   2
2*x  - 1

>>> chebyshevt(4, x)
   4      2
8*x  - 8*x  + 1

>>> legendre(2, x)
   2
3*x    1
---- - -
 2     2

>>> legendre(8, x)
      8         6         4        2
6435*x    3003*x    3465*x    315*x     35
------- - ------- + ------- - ------ + ---
  128        32        64       32     128

>>> assoc_legendre(2, 1, x)
        __________
       /    2
-3*x*\/  - x  + 1

>>> assoc_legendre(2, 2, x)
     2
- 3*x  + 3

>>> hermite(3, x)
   3
8*x  - 12*x

Differenzialgleichungen

In isympy:

>>> from sympy import Function, Symbol, dsolve
>>> f = Function('f')
>>> x = Symbol('x')
>>> f(x).diff(x, x) + f(x)
        2
       d
f(x) + ---(f(x))
         2
       dx

>>> dsolve(f(x).diff(x, x) + f(x), f(x))
f(x) = C1*sin(x) + C2*cos(x)

Algebraische Gleichungen

In isympy:

>>> from sympy import solve, symbols
>>> x, y = symbols('x,y')
>>> solve(x**4 - 1, x)
[-1, 1, -I, I]

>>> solve([x + 5*y - 2, -3*x + 6*y - 15], [x, y])
{x: -3, y: 1}

Lineare Algebra

Matrizen

Matrizen werden als Instanzen der Matrix-Klasse erzeugt:

>>> from sympy import Matrix, Symbol
>>> Matrix([[1, 0], [0, 1]])
[1  0]
[    ]
[0  1]

Sie können auch Symbole enthalten:

>>> x = Symbol('x')
>>> y = Symbol('y')
>>> A = Matrix([[1, x], [y, 1]])
>>> A
[1  x]
[    ]
[y  1]

>>> A**2
[x*y + 1    2*x  ]
[                ]
[  2*y    x*y + 1]

Mehr Informationen und Beispiele zu Matrizen finden sich im LinearAlgebraTutorial.

Musterabgleich

Die Methode .match() kann gemeinsam mit der Klasse Wild Ausdrücke auf Muster überprüfen. Die Methode gibt ein dictionary mit den nötigen Ersetzungen zurück, wie im folgenden Beispiel ersichtlich:

>>> from sympy import Symbol, Wild
>>> x = Symbol('x')
>>> p = Wild('p')
>>> (5*x**2).match(p*x**2)
{p: 5}

>>> q = Wild('q')
>>> (x**2).match(p*x**q)
{p: 1, q: 2}

Wenn der Musterabgleich fehlschlägt, wird None zurückgegeben:

>>> print (x + 1).match(p**x)
None

Über den Parameter exclude kann man manches aus dem Ergebnis ausschließen:

>>> p = Wild('p', exclude=[1, x])
>>> print (x + 1).match(x + p) # 1 is excluded
None
>>> print (x + 1).match(p + 1) # x is excluded
None
>>> print (x + 1).match(x + 2 + p) # -1 is not excluded
{p_: -1}

Ausgabe

Es existieren mehrere Wege, Ausdrücke auszugeben:

Standard

Dies ist die Ausgabe von str(expression) und sieht so aus:

>>> from sympy import Integral
>>> from sympy.abc import x
>>> print x**2
x**2
>>> print 1/x
1/x
>>> print Integral(x**2, x)
Integral(x**2, x)

ASCII-Art-Ausgabe

Eine pprint-Funktion erzeugt diese hübschere ASCII-Art-Ausgabe.

>>> from sympy import Integral, pprint
>>> from sympy.abc import x
>>> pprint(x**2)
 2
x
>>> pprint(1/x)
1
-
x
>>> pprint(Integral(x**2, x))
  /
 |
 |  2
 | x  dx
 |
/

Wenn eine Unicode-Schriftart installiert ist, sollte die ASCII-Art-Ausgabe standardmäßig die Unicode-Fassung verwenden. Dies kann mit dem Parameter use_unicode erzwungen oder abgeschaltet werden.

>>> pprint(Integral(x**2, x), use_unicode=True)

⎮  2
⎮ x  dx

Siehe auch die Wiki-Seite Pretty Printing für mehr Beispiele von hübschen Unicode-Ausgaben.

Tipp: Die ASCII-Art-Ausgabe kann auch als Standard-Methode gesetzt werden:

$ python
Python 2.5.2 (r252:60911, Jun 25 2008, 17:58:32)
[GCC 4.3.1] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> from sympy import init_printing, var, Integral
>>> init_printing(use_unicode=False, wrap_line=False, no_global=True)
>>> var("x")
x
>>> x**3/3
 3
x
--
3
>>> Integral(x**2, x) #doctest: +NORMALIZE_WHITESPACE
  /
 |
 |  2
 | x  dx
 |
/

Python-Ausgabe

>>> from sympy.printing.python import python
>>> from sympy import Integral
>>> from sympy.abc import x
>>> print python(x**2)
x = Symbol('x')
e = x**2
>>> print python(1/x)
x = Symbol('x')
e = 1/x
>>> print python(Integral(x**2, x))
x = Symbol('x')
e = Integral(x**2, x)

LaTeX-Ausgabe

>>> from sympy import Integral, latex
>>> from sympy.abc import x
>>> latex(x**2)
x^{2}
>>> latex(x**2, mode='inline')
$x^{2}$
>>> latex(x**2, mode='equation')
\begin{equation}x^{2}\end{equation}
>>> latex(x**2, mode='equation*')
\begin{equation*}x^{2}\end{equation*}
>>> latex(1/x)
\frac{1}{x}
>>> latex(Integral(x**2, x))
\int x^{2}\, dx

MathML

>>> from sympy.printing.mathml import mathml
>>> from sympy import Integral, latex
>>> from sympy.abc import x
>>> print mathml(x**2)
<apply><power/><ci>x</ci><cn>2</cn></apply>
>>> print mathml(1/x)
<apply><power/><ci>x</ci><cn>-1</cn></apply>

Pyglet

>>> from sympy import Integral, preview
>>> from sympy.abc import x
>>> preview(Integral(x**2, x)) 

Dies öffnet ein pyglet-Fenster mit dem in LaTeX gerenderten Ausdruck, wenn pyglet installiert ist:

../_images/pngview1.png

Hinweise

isympy ruft pprint automatisch auf – deswegen sind die Ausgaben standardmäßig hübsch.

Es ist gibt auch ein Ausgabemodul sympy.printing. Andere Ausgabemethoden, die durch dieses Modul erreichbar sind:

  • pretty(expr), pretty_print(expr), pprint(expr): Return or print, respectively, a pretty representation of expr. This is the same as the second level of representation described above.
  • latex(expr), print_latex(expr): Return or print, respectively, a LaTeX representation of expr
  • mathml(expr), print_mathml(expr): Return or print, respectively, a MathML representation of expr.
  • print_gtk(expr): Print expr to Gtkmathview, a GTK widget that displays MathML code. The Gtkmathview program is required.

Weitere Dokumentation

Nun ist Zeit, mehr über SymPy zu lernen. Lies den SymPy User’s Guide und die SymPy Modules Reference.

Unser öffentliches Wiki unter wiki.sympy.org, enthält einen Haufen nützlicher Beispiele und Anleitungen von uns und unseren Nutzern. (Fühle dich frei, dazu beizutragen und Dinge zu verändern!)

Translations

This tutorial is also available in other languages: