{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "remove-cell"
    ]
   },
   "source": [
    "<center><img src=\"images/ML_video_w_s.webp\" style=\"margin: 20 auto;\"></center>\n",
    "<p style=\"font-family: Protomolecule; font-size: 2.3em; line-height: 90%; margin: 0 auto; text-align: center; width: 100%;\"><span style=\"letter-spacing: .1rem;\">Machine</span><br><span style=\"letter-spacing: -.1rem;\">Learning</span></p>\n",
    "<p class=\"author\" style=\"font-family: Protomolecule; margin: 0px auto;  text-align: center; width: 100%; font-size: 1.2em;\">Joern Ploennigs</p>\n",
    "<p class=\"subtitle\" style=\"font-family: Protomolecule; font-size: larger; margin: 1em auto; text-align: center; width: 100%; font-size: 1.2em;\">Lineare Algebra</p>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "# Lineare Algebra"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "skip"
    },
    "tags": []
   },
   "source": [
    "![](images/03_Numpy/mj_title_band.png)\n",
    "\n",
    "> The Matrix is everywhere. It is all around us. Even now in this very room\n",
    "> \n",
    "> — Morpheus (The Matrix)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "skip"
    }
   },
   "source": [
    "## <a href=\"/lec_slides/03_Numpy.slides.html\">Folien</a>\n",
    "<iframe src=\"/lec_slides/03_Numpy.slides.html\" width=\"750\" height=\"500\"></iframe>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Erstellung von Arrays für Vektor und Matrixberechnungen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "NumPy ist die führende Bibliothek in Python für Vektor- und Matrixberechnungen. Daten und Berechnungen werden dabei nicht in Python, sondern mit schnellen internen Funktionen und Operationen ausgeführt. Dadurch ist es besonders platzsparend und schnell in der Verarbeitung großer Datensätze.\n",
    "\n",
    "Matrixberechnungen werden z.B. in der Finite-Elemente-Methode (FEM) genutzt, um Spannungen und Verschiebungen in Tragwerken zu berechnen."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Die Bibliothek NumPy wird normalerweise in Python mit der Abkürzung `np` importiert. Dadurch kann man einfach das kurze `np` im Code schreiben, wenn man die Bibliothek referenziert. Das auch Standard in den meisten Dokumentationen online."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "# Import von NumPy\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Die wichtigste Datenstruktur in **NumPy** sind $n$-dimensionale Arrays, die sowohl zur Darstellung von Vektoren ($n=1$) und Matrizen ($n=2$) verwendet werden. Die Arrays können nicht nur Vektoren sein (Temperaturverlauf entlang eines Stabträgers) sondern auch höhere Dimensionen haben, wie Matrizen (Verformungen eines 2D-Tragwerks) oder Tensoren (Spannungsverteilungen in einem 3D-Bauteil). Diese Tensoren sind insbesondere für komplexe ML-Modelle wie Tiefe Neuronale Netzwerke wichtig.\n",
    "\n",
    "Arrays in NumPy werden mit `np.array([1, 2, 3, 4, 5])` erstellt. Wir übergeben dabei eine Liste `[1, 2, 3, 4, 5]` der Zahlen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Messreihe:\n",
      "[88.2 86.5 85.1 83.9 82.4]\n"
     ]
    }
   ],
   "source": [
    "# Beispiel: Messwerte zur Luftfeuchtigkeit in einem Betonbauteil\n",
    "arr_1d = np.array([88.2, 86.5, 85.1, 83.9, 82.4])\n",
    "print(\"Messreihe:\")\n",
    "print(arr_1d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Dabei wird die Liste in ein NumPy `ndarray` umgewandelt. Dies sehen wir, wenn wir den Datentyp prüfen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Datentyp des Vektors in Python:  <class 'numpy.ndarray'>\n"
     ]
    }
   ],
   "source": [
    "print(\"Datentyp des Vektors in Python: \", type(arr_1d))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Intern nutzt NumPy keine Python-Datentypen (`int` in dem Beispiel), sondern C-Datentypen, die effizienter in der Speichernutzung sind und vor allem deutlich schnellere numerische Berechnungen erlauben. Für ML-Anwendungen sind `ndarray`-Objekte meist besser geeignet als Python-Listen, wenn Merkmale oder Matrizen dargestellt werden sollen. Der wichtigste Grund ist, dass `ndarray` homogene numerische Daten kompakt speichert und vektorisierte Operationen wie Addition, Skalierung oder Matrixmultiplikation direkt unterstützt. Python-Listen sind flexibler, aber für numerische Daten langsamer und für lineare Algebra deutlich unhandlicher. Den Datentyp kann man mit dem `dtype` Attribut einsehen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Datentyp der Werte im Vektor in NumPy:  float64\n"
     ]
    }
   ],
   "source": [
    "print(\"Datentyp der Werte im Vektor in NumPy: \", arr_1d.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Den Datentypen kann man beim Erstellen festlegen. Das ist insbesondere sinnvoll, wenn NumPy den falschen Datentyp erkennt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x1 Vektor:\n",
      "[ 6.  7.  8.  9. 10.]\n",
      "Datentyp der Werte im Vektor in NumPy:  float32\n"
     ]
    }
   ],
   "source": [
    "# Erstellen eines einfachen Vektors als 1D-Arrays mit Reellen Zahl\n",
    "arr_1d_f = np.array([6, 7, 8, 9, 10], dtype=np.float32)\n",
    "print(\"3x1 Vektor:\")\n",
    "print(arr_1d_f)\n",
    "print(\"Datentyp der Werte im Vektor in NumPy: \", arr_1d_f.dtype)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "NumPy unterstützt hierbei viele unterschiedliche Datentypen, die in der folgenden Tabelle dargestellt sind."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "| Klasse    | Datentyp        | Python | NumPy                               | Beispiel                                                       |\n",
    "|-----------|-----------------|--------|-----------------------------|---------------------------------------------------------------|\n",
    "| Numerisch | Ganze Zahl      | `int`   | `np.int8`, `np.int16`, `np.int32`, `np.int64`           | `np.array([1, 2, 3], dtype=np.int64)`                         |\n",
    "|           | Natürliche Zahl | `int`   | `np.uint8`, `np.uint16`, `np.uint32`, `np.uint64`         | `np.array([1, 2, 3], dtype=np.int64)`                         |\n",
    "|           | Reelle Zahl     | `float` | `np.float16`, `np.float32`, `np.float64`       | `np.array([1.0, 2.5, 3.7], dtype=np.float64)`                 |\n",
    "|           | Komplexe Zahl   | `complex.complex`$^*$     | `np.complex64`, `np.complex128`, `np.complex192`, `np.complex256`  | `np.array([1 \\| 2j, 3 \\| 4j], dtype=np.complex128)`           |\n",
    "| Logisch   | Boolean         | `bool`  | `np.bool_`                          | `np.array([True, False, True], dtype=np.bool_)`               |\n",
    "| Textuell  | Textuell        | `str`   | `np.str_`                           | `np.array(['hello', 'world'], dtype=np.str_)`                 |\n",
    "| Temporal  | Datum und Zeit  | `datetime.date`$^*$, `datetime.datetime`$^*$   | `np.datetime64`                     | `np.array(['2022-01-01', '2022-01-02'], dtype=np.datetime64)` |\n",
    "|           | Zeitdifferenz   | `datetime.timedelta`$^*$  | `np.timedelta64`                    | `np.datetime64('2011-06-15T00:00') + np.timedelta64(12, 'h')` |\n",
    "| Komplex   | Python Objekt   | `list`, `tuple`, `dict`, `set`, `object`   | `np.object_`                        | `np.array([1, 'two', 3.0], dtype=np.object_)`                 |\n",
    "\n",
    "$^*$ in separaten Packages verfügbar"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Die Gestalt des Arrays können wir mit dem `shape` Attribut abfragen:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Gestalt des Arrays: (5,)\n"
     ]
    }
   ],
   "source": [
    "print(\"Gestalt des Arrays:\", arr_1d.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Wenn wir eine Matrix (zweidimensionales Array) erstellen wollen, können wir an `np.array` eine Liste von Listen übergeben, mit je einer Unterliste für jede Zeile der Matrix. Wichtig ist, dass jede Unterliste gleich groß ist:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x2 Matrix:\n",
      "[[1 2 3]\n",
      " [4 5 6]]\n"
     ]
    }
   ],
   "source": [
    "# Erstellen einer Matrix als 2D-Arrays\n",
    "arr_2d = np.array([[1, 2, 3], [4, 5, 6]])\n",
    "print(\"3x2 Matrix:\")\n",
    "print(arr_2d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "Die Gestalt der Matrix hat sich entsprechend geändert. In unserem Beispiel ist die Shape gleich `(2, 3)`. Das bedeutet, das wir zwei Zeilen und drei Spalten haben."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Shape des Arrays: (2, 3)\n"
     ]
    }
   ],
   "source": [
    "print(\"Shape des Arrays:\", arr_2d.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Mit `reshape` kann man ein Array in eine andere Form bringen, solange die Anzahl der Elemente gleich bleibt. Das ist für ML sehr wichtig, weil Merkmalsdaten oft als Matrix der Form `(Anzahl Beobachtungen, Anzahl Merkmale)` vorliegen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {
    "editable": true,
    "tags": []
   },
   "outputs": [],
   "source": [
    "arr_flat = np.array([1, 2, 3, 4, 5, 6])\n",
    "arr_matrix = arr_flat.reshape((2, 3))\n",
    "print(\"Ursprüngliche Shape:\", arr_flat.shape)\n",
    "print(\"Neue Shape:\", arr_matrix.shape)\n",
    "print(arr_matrix)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Gerade der Unterschied zwischen einem Vektor mit Shape `(n,)` und einer Matrix mit Shape `(n, 1)` oder `(1, n)` ist wichtig, weil sich daraus bei linearen Algebra-Operationen unterschiedliche Ergebnisse ergeben können."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Zur Erzeugung von Standart-Vektoren oder -Matrizen gibt es spezielle Funktionen in NumPy. Um eine Einsmatrix zu erzeugen, nutzt man:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x3 Einsmatrix:\n",
      "[[1. 1. 1.]\n",
      " [1. 1. 1.]\n",
      " [1. 1. 1.]]\n"
     ]
    }
   ],
   "source": [
    "# Erstellen eines 3x3 Einsmatrix\n",
    "ones_arr = np.ones((3,3))\n",
    "print(\"3x3 Einsmatrix:\")\n",
    "print(ones_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Nicht zu verwechseln mit der Einheitsmatrix, die zu erstellen ist, mit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x3 Einheitsmatrix:\n",
      "[[1. 0. 0.]\n",
      " [0. 1. 0.]\n",
      " [0. 0. 1.]]\n"
     ]
    }
   ],
   "source": [
    "# Erstellen eines 3x3 Einheitsmatrix \n",
    "identiy_arr = np.identity(3)\n",
    "print(\"3x3 Einheitsmatrix:\")\n",
    "print(identiy_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Genauso lassen sich 0-Matrizen erzeugen, die man oft zur Initialisierung benötigt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x3 Nullmatrix:\n",
      "[[0. 0. 0.]\n",
      " [0. 0. 0.]\n",
      " [0. 0. 0.]]\n"
     ]
    }
   ],
   "source": [
    "# Erstellen eines 3x3 0-Matrix\n",
    "zeros_arr = np.zeros((3, 3))\n",
    "print(\"3x3 Nullmatrix:\")\n",
    "print(zeros_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Zufallszahlen lassen sich mit der Unterbibliothek `random` erzeugen. Um eine Zufallsvektor mit gleichverteilten Werten zu erzeugen kann man z.B. die `np.random.random()` Funktion nutzen. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x3 Zufallsmatrix:\n",
      "[[0.86140385 0.22796493 0.41925648]\n",
      " [0.6714088  0.67004514 0.36611217]\n",
      " [0.68567549 0.74678371 0.66436267]]\n"
     ]
    }
   ],
   "source": [
    "# Erstellen eines 3 Zufallsarrays mit gleichverteilten Werten zwischen 0 und 1\n",
    "rand_arr = np.random.random((3,3))\n",
    "print(\"3x3 Zufallsmatrix:\")\n",
    "print(rand_arr)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Für die Normalverteilung gibt es die Funktion:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3x3 Zufallsmatrix:\n",
      "[[3.51339763 2.01666659 1.07832075]\n",
      " [3.82598396 3.58756852 4.27521662]\n",
      " [4.1077942  4.56044517 3.73872824]]\n"
     ]
    }
   ],
   "source": [
    "# Erstellen eines 3 Zufallsarrays mit normalverteilten Werten mit dem Mittelwert 3 und der Standardabweichung 2\n",
    "rand_arr_norm = np.random.normal(3, 2, size=(3,3))\n",
    "print(\"3x3 Zufallsmatrix:\")\n",
    "print(rand_arr_norm)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Zugriff und Slicing"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Auf einzelne Elemente im Array kann über den numerischen Index zuzugreifen. Zu beachten ist das der ***Index bei 0 anfängt zu zählen***."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Element in der zweiten Zeile und dritten Spalte des 2D-Arrays: 6\n"
     ]
    }
   ],
   "source": [
    "# Zugriff auf Elemente in einem ndarray\n",
    "print(\"Element in der zweiten Zeile und dritten Spalte des 2D-Arrays:\", arr_2d[1, 2])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Damit lassen sich auch Werte über den Index direkt zuweisen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Matrix nach Änderung:\n",
      "[[ 1  2  3]\n",
      " [ 4  5 99]]\n"
     ]
    }
   ],
   "source": [
    "# Ändern von Elementen in einem ndarray\n",
    "arr_2d[1, 2] = 99\n",
    "print(\"Matrix nach Änderung:\")\n",
    "print(arr_2d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Bei großen Matrizen möchte man diese oft zerlegen. NumPy hat einige komfortable Operationen dafür. Möchte man zum Beispiel auf die zweite Zeile zugreifen, nutzt man in der Spalte den `:`-Operator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4,  5, 99])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr_2d[1, :]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Hierbei werden auch die aus Python bekannten negative Indizes unterstützt, mit denen man auf die letzten Elemente zugreifen kann. Um auf die letzte Zeile zuzugreifen, kann man schreiben:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 4,  5, 99])"
      ]
     },
     "execution_count": 17,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "arr_2d[-1, :]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Den Slices kann man auch Werte zuweisen. Um die letzte Zeile auf `99` zu setzen, schreiben wir:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Matrix nach Änderung:\n",
      "[[ 1  2  3]\n",
      " [99 99 99]]\n"
     ]
    }
   ],
   "source": [
    "arr_2d[-1, :] = 99\n",
    "print(\"Matrix nach Änderung:\")\n",
    "print(arr_2d)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Bei der Analyse von Daten ist es oft wichtig, Daten zu filtern. Wollen wir zum Beispiel alle Werte, die nicht `99` sind erhalten, können wir eine Logische Bedingung auf die gleiche Variable als Index verwenden"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Werte kleiner als 99:\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "array([1, 2, 3])"
      ]
     },
     "execution_count": 19,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "print(\"Werte kleiner als 99:\")\n",
    "arr_2d[arr_2d<99]"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Vektorisierte Funktionen und Lineare Algebra"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Es ist möglich, Schleifen zu verwenden, um Berechnungen mit Numpy-Objekten durchzuführen wie bei der Arbeit mit Listen in Python. Allerdings sollte man stattdessen immer vektorisierte Operationen von Numpy verwenden, wenn möglich. Diese sind deutlich performanter als Schleifen und meist auch zu programmieren und einfacher zu lesen.\n",
    "\n",
    "Numpy bietet eine Vielzahl von vektorisierten Funktionen und Operatoren, die als universelle Funktionen bezeichnet werden. Zum Beispiel mathematische Operationen für die effiziente Rechnung mit Vektoren und Matrizen, was unteranderem für die lineare Algebra sehr sinnvoll ist. \n",
    "\n",
    "So kann man die Vektoraddition und skalare Multiplikation einfach mit den vektoriellen Operatoren `+`, `-` und `*` durchführen. Nehmen wir als Beispiel die Vektoren $a, b, c$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "a = np.array([1, 2, 3, 4])\n",
    "b = np.array([5, 6, 7, 8])\n",
    "c = np.array([9, 10, 11, 12])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Wir können zum Beispiel $ a$ und $b$ addieren durch:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 6,  8, 10, 12])"
      ]
     },
     "execution_count": 21,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a + b"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "oder eine skalare Multiplikation ausführen mit"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([2, 4, 6, 8])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "2 * a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "fragment"
    },
    "tags": []
   },
   "source": [
    "und das Quadrat aller Elemente berechnen durch"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 1,  4,  9, 16])"
      ]
     },
     "execution_count": 23,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a**2"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Wir können auch zeigen, dass $a, b, c$ nicht linear unabhängig sind, indem wir zeigen, dass: $2 b - c - a = 0$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([0, 0, 0, 0])"
      ]
     },
     "execution_count": 24,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "2*b - c - a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Ähnlich wie wir 1-D-Arrays als Vektoren verwendet haben, können wir 2-D-Arrays als Matrizen verwenden. Die Operationen `+`, `-` und `*` funktionieren wie erwartet für zwei Arten von Operationen - elementweise Addition, Subtraktion und Multiplikation - sowie für die Addition, Subtraktion von Konstanten und Multiplikation mit einem Skalar. Hier sind ein paar Beispiele:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[11, 12],\n",
       "       [13, 14]])"
      ]
     },
     "execution_count": 25,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A = np.array([[1, 2], [3, 4]])\n",
    "A + 10  # Addition einer Konstanten"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[10, 20],\n",
       "       [30, 40]])"
      ]
     },
     "execution_count": 26,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A * 10  # Skalare Multiplikation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Es gibt noch ein paar andere praktische Möglichkeiten. `np.diag` erzeugt entweder eine diagonale Matrix eines gegebenen Vektors oder gibt, falls eine Matrix gegeben ist, deren Diagonale zurück:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 0, 0],\n",
       "       [0, 2, 0],\n",
       "       [0, 0, 3]])"
      ]
     },
     "execution_count": 27,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.diag([1, 2, 3])  # Erzeugt eine Diagonal Matrix mit der angegebenen Diagonalen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([1, 4])"
      ]
     },
     "execution_count": 28,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.diag(A)  # Gibt die Diagonale der Matrix zurück"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Auch das Transponieren von Vektoren und Matrizen ist einfach mit `.T` erreichbar"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[1, 3],\n",
       "       [2, 4]])"
      ]
     },
     "execution_count": 29,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.T  # Transponierte Matrix A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Skalarprodukt und Matrizenmultiplikation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Das Skalarprodukt (Punktprodukt) zweier Vektoren kann mit dem `@`-Symbol als Multiplikationszeichen durchgeführt werden, es gibt auch die Funktion `np.dot()` und die Methode `.dot()`.\n",
    "\n",
    "Wir erstellen zum Beispiel Vektoren $a=(1,2)$ und $b=(11,12)^T$:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [],
   "source": [
    "a = np.array([1, 2])  # Zeilenvektor\n",
    "b = np.array([[11], [12]])  # Spaltenvektor"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "und berechnen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([35])"
      ]
     },
     "execution_count": 31,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a @ b  # Inneres (Matrix) Produkt"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-block alert-warning\">\n",
    "<b>Achtung: Elementweise Multiplikation vs. Matrizenmultiplikation</b>\n",
    "\n",
    "Es ist wichtig zu wissen, dass das gewöhnliche Multiplikationszeichen `*` kein Matrizenmultiplikation ist, sondern eine elementweise Multiplikation mit bestimmten Regeln zur Behandlung von Dimensionsunterschieden.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Da $b$ transponiert ist ergibt sich zum Beispiel"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[11, 22],\n",
       "       [12, 24]])"
      ]
     },
     "execution_count": 32,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a * b  # elementweises Produkt (Übertragung, da die Abmessungen nicht übereinstimmen)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Dies ist nicht das Matrixprodukt! Beachten Sie auch, dass wir hier keinen Fehler erhalten haben (obwohl das manchmal vorkommen kann), da die Berechnung immer noch gültig ist, nur nicht das, was wir hier erhalten wollen."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Genau wie bei Vektoren kann das Matrizenmultiplikation mit `@` und nicht mit `*` bei Matrizen durchgeführt werden."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 7, 10])"
      ]
     },
     "execution_count": 33,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "a @ A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Auch ist zu beachten, dass die Matrizenmultiplikation nicht kommutativ ist, also die Reihenfolge der Matrizen bei der Produktbildung nicht vertauscht werden darf und zu anderen Ergebnissen führt."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 5, 11])"
      ]
     },
     "execution_count": 34,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A @ a"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Gleichungssysteme mit linearer Algebra lösen"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Gleichungssysteme lassen sich numerisch einfach mit Matrizen und Vektoren lösen. Nehmen wir das folgende Beispiel eines Gleichungssystems mit drei Unbekannte und drei Gleichungen.\n",
    "\n",
    "\\begin{align}\n",
    " 5 x_1 &+&  3x_2 & & \\, &= 1 \\\\\n",
    "   x_1 &+&  2x_2 &+& 3x_3  &= 2 \\\\\n",
    "   x_1 &+&   x_2 &+&  x_3  &= 3 \n",
    "\\end{align}"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Wir können dieses Gleichungssystem auch als Matrizenprodukt darzustellen, in dem wir die bekannten Koeffizienten in der Matrix $\\mathbf{A}$ von den unbekannten Variablen im Vektor $\\mathbf{x}$ trennen und den bekannten Ergebnisvektor $\\mathbf{b}$ zuweisen.\n",
    "\n",
    "$$ \n",
    "\\mathbf{Ax}=\\mathbf{b}\n",
    "$$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Angewandt auf die obigen Gleichungen (1)-(3) erhalten wir die Matrixform:\n",
    "\n",
    "$$\n",
    "\\left[ \\begin{array}{ccc}\n",
    "5 & 3 & 0 \\\\\n",
    "1 & 2 & 3 \\\\\n",
    "1 & 1 & 1 \\end{array} \\right]\n",
    "\\left[\\begin{array}{c} \n",
    "x_{1} \\\\ \n",
    "x_{2} \\\\\n",
    "x_{3}\\end{array}\\right]=\\left[\\begin{array}{c} \n",
    "1 \\\\\n",
    "2 \\\\\n",
    "3\\end{array}\\right]\n",
    "$$\n",
    "\n",
    "Die Koeffizientenmatrix $\\mathbf{A}$ enthält alle Konstanten, die mit Ihren unbekannten Variablen $x_1, x_2$ und $x_3$ multipliziert werden. Der Ergebnisvektor $\\mathbf{y}$ enthält alle bekannten Konstanten, die nicht mit Ihren unbekannten Variablen $x_1, x_2$ und $x_3$ multipliziert werden. Schließlich enthält der Vektor $\\mathbf{x}=[x_1, x_2, x_3]$ die unbekannten Werte. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Dies können wir mit linearer Algebra lösen indem wir die obige Gleichung (4) mit der inversen Matrix $\\mathbf{A}^{-1}$ von links multiplizieren, um die Gleichung nach $\\mathbf{x}$ umzustellen:\n",
    "\n",
    "\\begin{align}\n",
    "\\mathbf{Ax}&=\\mathbf{b} \\\\\n",
    "\\mathbf{A}^{-1}\\mathbf{A}\\mathbf{x}&=\\mathbf{A}^{-1}\\mathbf{b} \\\\\n",
    "\\mathbf{x}&=\\mathbf{A}^{-1}\\mathbf{b} \n",
    "\\end{align}\n",
    "\n",
    "Dieses Gleichungssystem ist immer dann lösbar, wenn eine Lösung für die inverse Matrix $\\mathbf{A}^{-1}$ existiert, was der Fall ist wenn deren Determinante $\\det \\mathbf{A} \\neq 0$ nicht null ist."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Wenn wir das gelernte auf Numpy ergibt sich folgender Lösungsweg. Zuerst definieren wir unsere Koeffizientenmatrix $\\mathbf{A}$ und den Ergebnisvektor $\\mathbf{b}$."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "A = np.array([[5, 3, 0], [1, 2, 3], [1, 1, 1]])\n",
    "b = np.array([1, 2, 3])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Wir prüfen die Determinante der Matrix. Hierfür nutzen wir die Funktion `det()` aus der Bibliothek `np.linalg` für Lineare Algebra in _Numpy_:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "1.0000000000000002"
      ]
     },
     "execution_count": 36,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "np.linalg.det(A)  # Gibt die Determinante der Matrix zurück"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Die Determinante ist nicht 0, also können wir die Inverse der Matrix berechnen."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Dies geschieht mit der Funktion `inv()` wie folgt:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "A_inv = np.linalg.inv(A)  # Berechnet die Inverse von A"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "<div class=\"alert alert-block alert-info\">\n",
    "<b>Tip</b>\n",
    "\n",
    "Nicht alle Matrizen sind invertierbar, und wenn Sie versuchen, die Inverse einer Matrix mit Determinante 0 zu berechnen, wird die Funktion `inv()` eine `LinAlgError`-Ausnahme auslösen.\n",
    "</div>"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Letztendlich können wir den gesuchten $\\mathbf{x}$-Vektor nach der obigen Gleichung (5) bestimmen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "metadata": {
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 20., -33.,  16.])"
      ]
     },
     "execution_count": 38,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = A_inv @ b\n",
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": []
   },
   "source": [
    "## Gleichungssysteme formal lösen mit _SymPy_"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "Bisher haben wir betrachtet, wie wir Gleichungssysteme numerisch lösen können. In manchen Situationen ist es jedoch notwendig eine formale Lösung für eine Berechnung algebraisch zu finden. Hier hilft das Python Paket _SymPy_.\n",
    "\n",
    "Definieren wir formal unser Gleichungssystem aus Gleichung (3) als"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": [
     "remove-cell"
    ]
   },
   "outputs": [],
   "source": [
    "from sympy import init_printing\n",
    "init_printing(use_latex='mathjax')"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [],
   "source": [
    "from sympy import *\n",
    "\n",
    "A = MatrixSymbol('A', 3, 3).as_explicit()\n",
    "x = MatrixSymbol('x', 3, 1).as_explicit()\n",
    "b = MatrixSymbol('b', 3, 1).as_explicit()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "So erhalten wir keine numerischen, sondern formale Matrizen und Vektoren."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[\\begin{matrix}{A}_{0,0} & {A}_{0,1} & {A}_{0,2}\\\\{A}_{1,0} & {A}_{1,1} & {A}_{1,2}\\\\{A}_{2,0} & {A}_{2,1} & {A}_{2,2}\\end{matrix}\\right]$"
      ],
      "text/plain": [
       "⎡A₀₀  A₀₁  A₀₂⎤\n",
       "⎢             ⎥\n",
       "⎢A₁₀  A₁₁  A₁₂⎥\n",
       "⎢             ⎥\n",
       "⎣A₂₀  A₂₁  A₂₂⎦"
      ]
     },
     "execution_count": 41,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[\\begin{matrix}{x}_{0,0}\\\\{x}_{1,0}\\\\{x}_{2,0}\\end{matrix}\\right]$"
      ],
      "text/plain": [
       "⎡x₀₀⎤\n",
       "⎢   ⎥\n",
       "⎢x₁₀⎥\n",
       "⎢   ⎥\n",
       "⎣x₂₀⎦"
      ]
     },
     "execution_count": 42,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Hierfür können wir jetzt zum Beispiel algebraisch die Lösung der Determinante bestimmen"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle {A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}$"
      ],
      "text/plain": [
       "A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁⋅ ↪\n",
       "\n",
       "↪ A₂₀"
      ]
     },
     "execution_count": 43,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.det()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "source": [
    "oder die Inverse der Matrix"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[\\begin{matrix}\\frac{{A}_{1,1} {A}_{2,2} - {A}_{1,2} {A}_{2,1}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}} & \\frac{- {A}_{0,1} {A}_{2,2} + {A}_{0,2} {A}_{2,1}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}} & \\frac{{A}_{0,1} {A}_{1,2} - {A}_{0,2} {A}_{1,1}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}}\\\\\\frac{- {A}_{1,0} {A}_{2,2} + {A}_{1,2} {A}_{2,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}} & \\frac{{A}_{0,0} {A}_{2,2} - {A}_{0,2} {A}_{2,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}} & \\frac{- {A}_{0,0} {A}_{1,2} + {A}_{0,2} {A}_{1,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}}\\\\\\frac{{A}_{1,0} {A}_{2,1} - {A}_{1,1} {A}_{2,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}} & \\frac{- {A}_{0,0} {A}_{2,1} + {A}_{0,1} {A}_{2,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}} & \\frac{{A}_{0,0} {A}_{1,1} - {A}_{0,1} {A}_{1,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}}\\end{matrix}\\right]$"
      ],
      "text/plain": [
       "⎡                                A₁₁⋅A₂₂ - A₁₂⋅A₂₁                             ↪\n",
       "⎢───────────────────────────────────────────────────────────────────────────── ↪\n",
       "⎢A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁ ↪\n",
       "⎢                                                                              ↪\n",
       "⎢                               -A₁₀⋅A₂₂ + A₁₂⋅A₂₀                             ↪\n",
       "⎢───────────────────────────────────────────────────────────────────────────── ↪\n",
       "⎢A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁ ↪\n",
       "⎢                                                                              ↪\n",
       "⎢                                A₁₀⋅A₂₁ - A₁₁⋅A₂₀                             ↪\n",
       "⎢───────────────────────────────────────────────────────────────────────────── ↪\n",
       "⎣A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁ ↪\n",
       "\n",
       "↪                                      -A₀₁⋅A₂₂ + A₀₂⋅A₂₁                      ↪\n",
       "↪ ────  ────────────────────────────────────────────────────────────────────── ↪\n",
       "↪ ⋅A₂₀  A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ -  ↪\n",
       "↪                                                                              ↪\n",
       "↪                                       A₀₀⋅A₂₂ - A₀₂⋅A₂₀                      ↪\n",
       "↪ ────  ────────────────────────────────────────────────────────────────────── ↪\n",
       "↪ ⋅A₂₀  A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ -  ↪\n",
       "↪                                                                              ↪\n",
       "↪                                      -A₀₀⋅A₂₁ + A₀₁⋅A₂₀                      ↪\n",
       "↪ ────  ────────────────────────────────────────────────────────────────────── ↪\n",
       "↪ ⋅A₂₀  A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ -  ↪\n",
       "\n",
       "↪                                              A₀₁⋅A₁₂ - A₀₂⋅A₁₁               ↪\n",
       "↪ ───────────  ─────────────────────────────────────────────────────────────── ↪\n",
       "↪ A₀₂⋅A₁₁⋅A₂₀  A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀ ↪\n",
       "↪                                                                              ↪\n",
       "↪                                             -A₀₀⋅A₁₂ + A₀₂⋅A₁₀               ↪\n",
       "↪ ───────────  ─────────────────────────────────────────────────────────────── ↪\n",
       "↪ A₀₂⋅A₁₁⋅A₂₀  A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀ ↪\n",
       "↪                                                                              ↪\n",
       "↪                                              A₀₀⋅A₁₁ - A₀₁⋅A₁₀               ↪\n",
       "↪ ───────────  ─────────────────────────────────────────────────────────────── ↪\n",
       "↪ A₀₂⋅A₁₁⋅A₂₀  A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀ ↪\n",
       "\n",
       "↪                   ⎤\n",
       "↪ ──────────────────⎥\n",
       "↪ ⋅A₂₁ - A₀₂⋅A₁₁⋅A₂₀⎥\n",
       "↪                   ⎥\n",
       "↪                   ⎥\n",
       "↪ ──────────────────⎥\n",
       "↪ ⋅A₂₁ - A₀₂⋅A₁₁⋅A₂₀⎥\n",
       "↪                   ⎥\n",
       "↪                   ⎥\n",
       "↪ ──────────────────⎥\n",
       "↪ ⋅A₂₁ - A₀₂⋅A₁₁⋅A₂₀⎦"
      ]
     },
     "execution_count": 44,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.inv()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Auch können wir das Gleichungssystem komplett algebraisch lösen lassen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 45,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[\\begin{matrix}\\frac{{A}_{0,1} {A}_{1,2} {b}_{2,0} - {A}_{0,1} {A}_{2,2} {b}_{1,0} - {A}_{0,2} {A}_{1,1} {b}_{2,0} + {A}_{0,2} {A}_{2,1} {b}_{1,0} + {A}_{1,1} {A}_{2,2} {b}_{0,0} - {A}_{1,2} {A}_{2,1} {b}_{0,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}}\\\\\\frac{- {A}_{0,0} {A}_{1,2} {b}_{2,0} + {A}_{0,0} {A}_{2,2} {b}_{1,0} + {A}_{0,2} {A}_{1,0} {b}_{2,0} - {A}_{0,2} {A}_{2,0} {b}_{1,0} - {A}_{1,0} {A}_{2,2} {b}_{0,0} + {A}_{1,2} {A}_{2,0} {b}_{0,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}}\\\\\\frac{{A}_{0,0} {A}_{1,1} {b}_{2,0} - {A}_{0,0} {A}_{2,1} {b}_{1,0} - {A}_{0,1} {A}_{1,0} {b}_{2,0} + {A}_{0,1} {A}_{2,0} {b}_{1,0} + {A}_{1,0} {A}_{2,1} {b}_{0,0} - {A}_{1,1} {A}_{2,0} {b}_{0,0}}{{A}_{0,0} {A}_{1,1} {A}_{2,2} - {A}_{0,0} {A}_{1,2} {A}_{2,1} - {A}_{0,1} {A}_{1,0} {A}_{2,2} + {A}_{0,1} {A}_{1,2} {A}_{2,0} + {A}_{0,2} {A}_{1,0} {A}_{2,1} - {A}_{0,2} {A}_{1,1} {A}_{2,0}}\\end{matrix}\\right]$"
      ],
      "text/plain": [
       "⎡A₀₁⋅A₁₂⋅b₂₀ - A₀₁⋅A₂₂⋅b₁₀ - A₀₂⋅A₁₁⋅b₂₀ + A₀₂⋅A₂₁⋅b₁₀ + A₁₁⋅A₂₂⋅b₀₀ - A₁₂⋅A₂₁ ↪\n",
       "⎢───────────────────────────────────────────────────────────────────────────── ↪\n",
       "⎢A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁ ↪\n",
       "⎢                                                                              ↪\n",
       "⎢-A₀₀⋅A₁₂⋅b₂₀ + A₀₀⋅A₂₂⋅b₁₀ + A₀₂⋅A₁₀⋅b₂₀ - A₀₂⋅A₂₀⋅b₁₀ - A₁₀⋅A₂₂⋅b₀₀ + A₁₂⋅A₂ ↪\n",
       "⎢───────────────────────────────────────────────────────────────────────────── ↪\n",
       "⎢A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁ ↪\n",
       "⎢                                                                              ↪\n",
       "⎢A₀₀⋅A₁₁⋅b₂₀ - A₀₀⋅A₂₁⋅b₁₀ - A₀₁⋅A₁₀⋅b₂₀ + A₀₁⋅A₂₀⋅b₁₀ + A₁₀⋅A₂₁⋅b₀₀ - A₁₁⋅A₂₀ ↪\n",
       "⎢───────────────────────────────────────────────────────────────────────────── ↪\n",
       "⎣A₀₀⋅A₁₁⋅A₂₂ - A₀₀⋅A₁₂⋅A₂₁ - A₀₁⋅A₁₀⋅A₂₂ + A₀₁⋅A₁₂⋅A₂₀ + A₀₂⋅A₁₀⋅A₂₁ - A₀₂⋅A₁₁ ↪\n",
       "\n",
       "↪ ⋅b₀₀ ⎤\n",
       "↪ ──── ⎥\n",
       "↪ ⋅A₂₀ ⎥\n",
       "↪      ⎥\n",
       "↪ ₀⋅b₀₀⎥\n",
       "↪ ─────⎥\n",
       "↪ ⋅A₂₀ ⎥\n",
       "↪      ⎥\n",
       "↪ ⋅b₀₀ ⎥\n",
       "↪ ──── ⎥\n",
       "↪ ⋅A₂₀ ⎦"
      ]
     },
     "execution_count": 45,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "A.solve(b)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Prinzipiell können wir die Gleichungen (1)-(3) auch direkt algebraisch lösen, ohne sie vorher in eine Matrizendarstellung zu bringen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[ \\left\\{ x_{1} : 20, \\  x_{2} : -33, \\  x_{3} : 16\\right\\}\\right]$"
      ],
      "text/plain": [
       "[{x₁: 20, x₂: -33, x₃: 16}]"
      ]
     },
     "execution_count": 46,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x1, x2, x3 = symbols(\"x1, x2, x3\")\n",
    "solve([\n",
    "    5 * x1 + 3 * x2 - 1, \n",
    "        x1 + 2 * x2 + 3 * x3 - 2,  \n",
    "        x1 + x2 + x3 - 3\n",
    "    ], [x1, x2, x3], dict=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Weitere Funktionen in _SimPy_ erlauben das Vereinfachen von Formeln. Insbesondere wenn es um komplexe Brüche oder trigonometrische Funktionen geht, hat man nicht alle Ersetzungsmuster im Kopf und kann das von _SimPy_ lösen lassen."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle 1$"
      ],
      "text/plain": [
       "1"
      ]
     },
     "execution_count": 47,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "x = symbols(\"x\")\n",
    "\n",
    "simplify(sin(x)**2 + cos(x)**2)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle x - 1$"
      ],
      "text/plain": [
       "x - 1"
      ]
     },
     "execution_count": 48,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "simplify((x**3 + x**2 - x - 1)/(x**2 + 2*x + 1))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "In gleicher Weise lassen sich vereinfachte Formen erweitern"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle x^{2} + 2 x + 1$"
      ],
      "text/plain": [
       " 2          \n",
       "x  + 2⋅x + 1"
      ]
     },
     "execution_count": 49,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "expand((x + 1)**2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Das funktioniert auch für Differentialgleichungen. Von einfachen Lösungen wie"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 50,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle - \\sin{\\left(x \\right)}$"
      ],
      "text/plain": [
       "-sin(x)"
      ]
     },
     "execution_count": 50,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "diff(cos(x), x)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    }
   },
   "source": [
    "bis zu komplexen Ausdrücken, wie diese Differentialgleichung zweiter Ordnung"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 51,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle f{\\left(x \\right)} - 2 \\frac{d}{d x} f{\\left(x \\right)} + \\frac{d^{2}}{d x^{2}} f{\\left(x \\right)} = \\sin{\\left(x \\right)}$"
      ],
      "text/plain": [
       "                     2                \n",
       "         d          d                 \n",
       "f(x) - 2⋅──(f(x)) + ───(f(x)) = sin(x)\n",
       "         dx           2               \n",
       "                    dx                "
      ]
     },
     "execution_count": 51,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f = symbols(\"f\", cls=Function)\n",
    "\n",
    "diffeq = Eq(f(x).diff(x, x) - 2*f(x).diff(x) + f(x), sin(x))\n",
    "diffeq"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "die wir mit der Funktion `dsolve` formal lösen können"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 52,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle f{\\left(x \\right)} = \\left(C_{1} + C_{2} x\\right) e^{x} + \\frac{\\cos{\\left(x \\right)}}{2}$"
      ],
      "text/plain": [
       "                    x   cos(x)\n",
       "f(x) = (C₁ + C₂⋅x)⋅ℯ  + ──────\n",
       "                          2   "
      ]
     },
     "execution_count": 52,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dsolve(diffeq)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "als auch für bestimmte Randbedingungen für $f(x)$ wie $f(0) = 1$ und $f(2) = 3$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 53,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle f{\\left(x \\right)} = \\left(\\frac{x \\left(- e^{2} - \\cos{\\left(2 \\right)} + 6\\right)}{4 e^{2}} + \\frac{1}{2}\\right) e^{x} + \\frac{\\cos{\\left(x \\right)}}{2}$"
      ],
      "text/plain": [
       "       ⎛  ⎛   2             ⎞  -2    ⎞            \n",
       "       ⎜x⋅⎝- ℯ  - cos(2) + 6⎠⋅ℯ     1⎟  x   cos(x)\n",
       "f(x) = ⎜───────────────────────── + ─⎟⋅ℯ  + ──────\n",
       "       ⎝            4               2⎠        2   "
      ]
     },
     "execution_count": 53,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dsolve(diffeq, ics={f(0): 1, f(2): 3})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "Das funktioniert auch für Differentialgleichungssysteme"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 54,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[ \\frac{d}{d x} f{\\left(x \\right)} = g{\\left(x \\right)}, \\  \\frac{d}{d x} g{\\left(x \\right)} = f{\\left(x \\right)}\\right]$"
      ],
      "text/plain": [
       "⎡d                d              ⎤\n",
       "⎢──(f(x)) = g(x), ──(g(x)) = f(x)⎥\n",
       "⎣dx               dx             ⎦"
      ]
     },
     "execution_count": 54,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "f, g = symbols(\"f g\", cls=Function)\n",
    "\n",
    "x = symbols(\"x\")\n",
    "\n",
    "eqs = [Eq(f(x).diff(x), g(x)), Eq(g(x).diff(x), f(x))]\n",
    "eqs"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "mit der formalen Lösung"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 55,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[ f{\\left(x \\right)} = - C_{1} e^{- x} + C_{2} e^{x}, \\  g{\\left(x \\right)} = C_{1} e^{- x} + C_{2} e^{x}\\right]$"
      ],
      "text/plain": [
       "⎡             -x       x             -x       x⎤\n",
       "⎣f(x) = - C₁⋅ℯ   + C₂⋅ℯ , g(x) = C₁⋅ℯ   + C₂⋅ℯ ⎦"
      ]
     },
     "execution_count": 55,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dsolve(eqs, [f(x), g(x)])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "slideshow": {
     "slide_type": "subslide"
    },
    "tags": []
   },
   "source": [
    "und einer spezifischen Lösung mit gegebenen Randbedingungen $f(0) = 1$ und $g(2) = 3$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 56,
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": ""
    },
    "tags": []
   },
   "outputs": [
    {
     "data": {
      "text/latex": [
       "$\\displaystyle \\left[ f{\\left(x \\right)} = \\frac{\\left(1 + 3 e^{2}\\right) e^{x}}{1 + e^{4}} - \\frac{\\left(- e^{4} + 3 e^{2}\\right) e^{- x}}{1 + e^{4}}, \\  g{\\left(x \\right)} = \\frac{\\left(1 + 3 e^{2}\\right) e^{x}}{1 + e^{4}} + \\frac{\\left(- e^{4} + 3 e^{2}\\right) e^{- x}}{1 + e^{4}}\\right]$"
      ],
      "text/plain": [
       "⎡       ⎛       2⎞  x   ⎛   4      2⎞  -x         ⎛       2⎞  x   ⎛   4      2 ↪\n",
       "⎢       ⎝1 + 3⋅ℯ ⎠⋅ℯ    ⎝- ℯ  + 3⋅ℯ ⎠⋅ℯ           ⎝1 + 3⋅ℯ ⎠⋅ℯ    ⎝- ℯ  + 3⋅ℯ  ↪\n",
       "⎢f(x) = ───────────── - ─────────────────, g(x) = ───────────── + ──────────── ↪\n",
       "⎢               4                 4                       4                 4  ↪\n",
       "⎣          1 + ℯ             1 + ℯ                   1 + ℯ             1 + ℯ   ↪\n",
       "\n",
       "↪ ⎞  -x⎤\n",
       "↪ ⎠⋅ℯ  ⎥\n",
       "↪ ─────⎥\n",
       "↪      ⎥\n",
       "↪      ⎦"
      ]
     },
     "execution_count": 56,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "dsolve(eqs, [f(x), g(x)], ics={f(0): 1, g(2): 3})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "editable": true,
    "slideshow": {
     "slide_type": "slide"
    },
    "tags": [
     "remove-cell"
    ]
   },
   "source": [
    "<div id=\"tsparticles_question\" style=\"width: 100%; height:5em; background-color: white;\">\n",
    "    <div class=\"questions\" style=\"letter-spacing: 0.03em; font-family: Protomolecule; font-size: 2.3em; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); color: black; z-index: 5;\">f&nbsp;&nbsp;r&nbsp;&nbsp;a&nbsp;&nbsp;g&nbsp;&nbsp;e&nbsp;&nbsp;n&nbsp;&nbsp;?</div>\n",
    "</div>"
   ]
  }
 ],
 "metadata": {
  "celltoolbar": "Slideshow",
  "kernelspec": {
   "display_name": "lehre4",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.12.8"
  },
  "rise": {
   "auto_select": "none",
   "center": false,
   "enable_chalkboard": true,
   "header": "<object data=\"images/IntroML_c.svg\" type=\"image/svg+xml\" class=\"header_title_logo\"></object><object data=\"images/ai4sc_logo2.svg\" type=\"image/svg+xml\" class=\"header_ai4sc_logo\"></object><object data=\"images/uni_logo2.svg\" type=\"image/svg+xml\" class=\"header_uni_logo\"></object>",
   "scroll": true,
   "show_buttons_on_startup": false,
   "slideNumber": true,
   "theme": "white"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
