execute_notebooks :cache# Whether to execute notebooks at build time. Must be one of ("auto", "force", "cache", "off")
cache :"_build/.jupyter_cache/"# A path to the jupyter cache that will be used to store execution artifacs. Defaults to `_build/.jupyter_cache/`
exclude_patterns :["README.md","*.ipynb","visualisierung_lecture.md","parallelisierung_lecture.md","parallelisierung_exercise.md"]# A list of patterns to *skip* in execution (e.g. a notebook that takes a really long time)
exclude_patterns :["README.md","*.ipynb","parallelisierung_lecture.md","parallelisierung_exercise.md"]# A list of patterns to *skip* in execution (e.g. a notebook that takes a really long time)
for idx, rootf_numpy in enumerate(newton_numpy(f, 0)):
if idx ==4:
break
...
...
@@ -119,6 +112,7 @@ print(rootf_numpy)
```
```{code-cell}
:tags: ['hide-cell']
for idx, rootg_numpy in enumerate(newton_numpy(g, 0)):
if idx ==4:
break
...
...
@@ -126,6 +120,7 @@ print(rootg_numpy)
```
```{code-cell}
:tags: ['hide-cell']
# Hier sind auch problemlos wesentlich mehr Iterationen möglich
for idx, rootf_numpy in enumerate(newton_numpy(f, 0)):
if idx == 100:
...
...
@@ -136,65 +131,13 @@ for idx, rootf_numpy in enumerate(newton_numpy(f, 0)):
# und der numerischen Welt zu bauen.
```
## Aufgabe 3\*
Gegeben seien die Listen $L1 = [x, x^3, x^5, x^7, x^9,x^{11}x^{13},x^{15},x^{17},]$ und $L2 = [x, x^2, x^6, x^{24}, x^{120}]$.
* Erstellen Sie diese beiden Listen mit Hilfe von list-comprehensions.
* Bilden Sie $L3 = L1+L2$.
* Bilden Sie von jedem Eintrag von $L3$ die Ableitung und die Stammfunktion.
* Bestimmen Sie von jedem Element von $L3$ den Funktionswert an der Stelle $x=3$ und entfernen Sie den größten Eintrag aus der resultierenden Liste.
* Speichern Sie die Funktionen aus obiger Liste jeweils in ein Dictionary ab, welches den Funktionsausdruck, die Ableitung und Stammfunktion enthält.
* Geben Sie mit Hilfe dieses Dictionaries die Funktionen mit erklärendem Text aus.
```{code-cell}
x = sy.symbols('x')
L1 = [x**(2*n-1) for n in range(1,10)]
L2 = [x**(sy.factorial(n)) for n in range(1,6)]
L3 = L1 + L2
```
```{code-cell}
[f.diff() for f in L3]
```
```{code-cell}
[f.integrate(x) for f in L3]
```
```{code-cell}
L = [f.subs(x,3) for f in L3]; L
```
```{code-cell}
m = max(L)
L.remove(m); L
```
```{code-cell}
L4=[{"Funktion":f,"diff":f.diff(x), "int":f.integrate(x)} for f in L3]; L4
```
```{code-cell}
for D in L4:
print(' Für die Funktion {} ist die Ableitung {} und die Stammfunktion {}'.format(D['Funktion'],D['diff'],D['int']) )
```
```{code-cell}
for D in L4:
print(' Für die Funktion {} ist die Ableitung {} und die Stammfunktion {}'.format(D['Funktion'],D['diff'],D['int']) )
```
```{code-cell}
for D in L4:
print ('Für die Funktion f(x)='+str(D['Funktion']) +' ist die Ableitung f`(x)='+str(D['diff'])+' und die Stammfunktion F(x)='+str(D['int']))
```
## Aufgabe 4\*
## Aufgabe 2\*
Schreiben Sie eine Funktion inklusive Hilfetext, die für eine gegebene mathematische Funktion (als Sympy-Ausdruck mit Symbol `x` als Variablen) die lokalen Extrema samt Funktionswerten und Maxima/Minima-Bestimmung ausrechnet und eine Liste von Dictionaries zurückgibt, die jeweils den Extremwert und seine Eigenschaft enthalten. Den Fall, dass die 2. Ableitung verschwindet (also *möglicherweise* ein Sattelpunkt), können Sie dabei ignorieren. Achten Sie darauf, dass wir Variablen und Lösungen in den reellen Zahlen haben.
```{code-cell}
:tags: ['hide-cell']
import sympy as sy
def extrema(f):
...
...
@@ -233,12 +176,14 @@ def extrema(f):
```
```{code-cell}
:tags: ['hide-cell']
x = sy.symbols('x', real=True) # this is to ensure we are only in real numbers
Eine Funktion $f \colon D \to \mathbb{R}$ heißt *differenzierbar* an der Stelle $x \in D$, wenn
$$
\lim_{z \to x} \frac{f(z) - f(x)}{z - x}
$$
existiert. In diesem Fall ist $f$ in $x$ auch stetig.
Der Grenzwert wird *Ableitung* oder *Differentialquotient* von $f$ an $x$ genannt und mit $f'(x) = f^{(1)}(x)$ bezeichnet. $f$ heißt differenzierbar auf $D$, wenn $f$ an allen $x \in D$ differenzierbar ist, und *stetig differenzierbar*, wenn die Ableitung selbst wieder stetig ist.
## Differentiation in SymPy
```{code-cell}
import sympy as sy
sy.init_printing()
```
In SymPy wird ein Ausdruck `expr` mit
```{code-block}
sympy.diff(expr)
```
oder
```{code-block}
expr.diff()
```
abgeleitet.
```{code-cell}
x = sy.symbols('x')
sy.diff(sy.sin(x))
```
```{code-cell}
(x**x).diff()
```
Wenn `expr` mehrere Symbole enthält, dann muss das Symbol, nach dem abgeleitet werden soll, als zusätzliches Argument an `diff()` übergeben werden.
```{code-cell}
c = sy.symbols('c')
sy.diff(c * x**2, x)
```
Ohne `x` tritt eine Exception auf.
```{code-cell}
:tags: ["raises-exception"]
sy.diff(c * x**2)
```
## Höhere Ableitungen und Ableitungen von Funktionen mehrerer Veränderlicher
Höhere Ableitungen werden induktiv definiert: Für $n > 1$ ist $f^{(n)}(x)$ die Ableitung von $f^{(n-1)}$ bei $x \in D$.
Sei $f \colon D_1 \times D_2 \to \mathbb{R}$ eine Funktion in zwei Variablen. Dann ist die partielle Ableitung von $f$ bezüglich $x$ definiert als
wenn der Grenzwert existiert. Höhere und gemischte Ableitungen wie $f_{xy}$ werden wieder induktiv definiert; hier wird zunächst bezüglich $x$, dann bezüglich $y$ abgeleitet. Der Fall mit mehr als zwei Variablen ist analog.
Der *Satz von Schwarz* besagt, dass Stetigkeit von $f_{xy}$ und $f_{yx}$ impliziert, dass $f_{xy} = f_{yx}$ gilt.
Höhere und gemischte Ableitungen werden in SymPy durch
```{code-block}
sympy.diff(expr, x1, x2, x3, ...)
```
(oder durch `expr.diff(x1, x2, x3, ...)`) berechnet. Der Fall mehrerer identischer Variablen, also höherer Ableitungen, kann auch als
```{code-block}
sy.diff(expr, x, n)
```
berechnet werden, wobei `n` ein Integer ist.
```{code-cell}
y = sy.symbols('y')
sy.diff(c * x**2 * y**2, x, y, 2)
```
```{code-cell}
sy.diff(1/x, x, 10)
```
## Höherdimensionale Funktionen
Höherdimensionale Funktionen können mit `sympy.Matrix` repräsentiert werden.
F = sy.Matrix([rho*sy.cos(phi), rho*sy.sin(phi), rho**2])
F
```
Hier funktionieren jetzt z.B. Substituieren, Differenzieren usw. wie bei einfachen Ausdrücken:
```{code-cell}
F.subs(rho, 4), F.diff(rho)
```
### Jacobi-Matrix
Die Jacobi-Matrix ist die Matrix der partiellen Ableitungen bzgl. aller Variablen, hier $\rho$ und $\phi$.
```{code-cell}
X = (rho, phi)
F.jacobian(X)
```
Schnelle numerische Berechnung als Beispiel, wieder mit `lambdify`:
```{code-cell}
f = sy.lambdify(X, F.jacobian(X))
```
```{code-cell}
f(4, 4)
```
### Symbolisch oder numerisch?
Hier haben wir nun Matrizen sowohl im numerischen Sinne (`NumPy`) als auch symbolischen Sinne kennengelernt (`SymPy`). Im Prinzip ist hier Funktionalität ein bisschen "doppelt".
Sehr kurz gefasst könnte man sagen: Numerisch ist schneller, während symbolisch genauer ist (rationale Zahlen, reelle Zahlen-Objekte). Unter anderem zeigt das Beispiel mit der Jacobi-Matrix eine gute Verbindung und Nutzung beider Welten: symbolische Ableitung und dann numerische Auswertung.
Im Übergang dieser beiden Welten helfen Funktionen wie `sy.lambdify``sy.matrices.dense.matrix2numpy` und `sy.matrices.dense.list2numpy`.
<!-- TODO: hessian? -->
## Gewöhnliche Differentialgleichungen
Gewöhnliche Differentialgleichungen sind Bestimmungsgleichungen einer Funktion in einer Variablen, die neben der unbekannten Funktion selbst auch ihre Ableitungen enthalten. Beispiel: Die lineare Gleichung zweiter Ordnung
$$
f''(x) + c^2 f(x) = 0, \quad c > 0,
$$
hat zwei linear unabhängige Lösungen $x \mapsto \sin(cx)$ und $x \mapsto \cos(cx)$.
Gewöhnliche Differentialgleichungen können mit `sympy.dsolve` gelöst werden. Die unbekannte Funktion muss dabei als `sympy.Function` erzeugt werden.
```{code-cell}
f = sy.Function('f')
c = sy.symbols('c', real=True, positive=True)
sol = sy.dsolve(sy.diff(f(x), x, x) + c**2 * f(x), f(x))
sol
```
Die unbekannten Konstanten können durch Wahl einer Anfangsbedingung festgelegt werden. `dsolve` berücksichtigt selbst allerdings keine Anfangsbedinungen; diese müssen "von Hand" zum Bestimmen der Konstanten genutzt werden. Beispiel:
SymPy kann auch verschiedene Typen nichtlinearer Differentialgleichungen lösen, z.B.
```{code-cell}
sy.dsolve(sy.diff(f(x), x) - 1/f(x), f(x))
```
Für einige Gleichungen gibt SymPy approximative Lösungen durch Potenzreihen, z.B. für $f''(x) = xf(x)$ (die exakten Lösungen sind hier Airy-Funktionen).
```{code-cell}
sy.dsolve(sy.diff(f(x), x, x) - x*f(x), f(x))
```
Die Ordnung und der Basispunkt der Potenzreihe lassen sich über die optionalen Parameter `x0` und `n` steuern. Standardwerte sind `x0=0` und `n=6`.
In diesem Code gibt es diverse mögliche Fehlerquellen. Benutzen Sie Exception Handling, um hilfreichere Fehlermeldungen auszugeben und die Verarbeitung ggf. in sinnvoller Weise fortzusetzen.
```{code-cell}
:tags: ['hide-cell']
# Datei generieren:
with open('sample.csv', 'w') as f:
...
...
@@ -43,6 +44,7 @@ with open('sample.csv', 'w') as f:
Loggen Sie sich auf [https://gitlab.gwdg.de]() mit ihrem Studierenden-Account ein (der Nutzername ist Ihre volle Email-Adresse) und erzeugen Sie ein neues Projekt. Clonen Sie dieses Projekt in zwei separate Verzeichnisse.
Loggen Sie sich auf [https://gitlab.gwdg.de](https://gitlab.gwdg.de) mit ihrem Studierenden-Account ein (der Nutzername ist Ihre volle Email-Adresse) und erzeugen Sie ein neues Projekt. Clonen Sie dieses Projekt in zwei separate Verzeichnisse.
(a) Ziehen Sie mit diesen beiden Verzeichnissen die Schritte aus der Vorlesung nach: