diff --git a/notebooks/presentations/FMTea2022.ipynb b/notebooks/presentations/FMTea2022.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..d48cb6b598e2045921096ca0e17dc7302d347855
--- /dev/null
+++ b/notebooks/presentations/FMTea2022.ipynb
@@ -0,0 +1,867 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "slide"
+    }
+   },
+   "source": [
+    "# ProB for Teaching Theoretical Computer Science and Formal Methods\n",
+    "\n",
+    "### David Geleßus, Michael Leuschel\n",
+    "### FMTea 2022\n",
+    "\n",
+    "https://gitlab.cs.uni-duesseldorf.de/general/stups/prob2-jupyter-kernel\n",
+    "\n",
+    "![ProB](./img/prob_logo.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "slide"
+    }
+   },
+   "source": [
+    "# Intro: Notebooks, Jupyter"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## What is a Notebook?\n",
+    "\n",
+    "* Document containing text and executable code blocks\n",
+    "* Code can be executed interactively\n",
+    "* Results are shown in the notebook below the corresponding code\n",
+    "* Similar to a REPL (read-eval-print-loop), with some differences:\n",
+    "    * Code blocks can be edited and executed out-of-order\n",
+    "    * Results can contain rich text and graphics\n",
+    "    * Can be shared with other users"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## Jupyter Notebook\n",
+    "\n",
+    "* Browser-based notebook interface\n",
+    "* Open-source and cross-platform\n",
+    "* Originated in the Python community, implemented in Python\n",
+    "* Also supports languages other than \n",
+    "* Language integration provided by a separate *kernel*"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## ProB Jupyter Kernel (https://prob.hhu.de/w/)\n",
+    "\n",
+    "* Understands both classical B syntax and Rodin Event-B syntax\n",
+    "* Many features: Code completion, graph visualisation, ..."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "slide"
+    }
+   },
+   "source": [
+    "# Some Features"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## Evaluating Formulas\n",
+    "\n",
+    "Evaluating B expressions and solving predicates:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 6,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$3$"
+      ],
+      "text/plain": [
+       "3"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1+2"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 7,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\mathit{TRUE}$\n",
+       "\n",
+       "**Solution:**\n",
+       "* $\\mathit{x} = 2$"
+      ],
+      "text/plain": [
+       "TRUE\n",
+       "\n",
+       "Solution:\n",
+       "\tx = 2"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1 + x = 3"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    },
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\{-3,1,2,3,4,5,10,18\\}$"
+      ],
+      "text/plain": [
+       "{−3,1,2,3,4,5,10,18}"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "1..5 \\/ {-3, 4, 10, 18}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Example: Set of all prime numbers < 500"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\newcommand{\\qdot}{\\mathord{\\mkern1mu\\cdot\\mkern1mu}}/*@\\mathit{symbolic}*/ \\{\\mathit{x}\\mid\\mathit{x} > 1 \\land \\lnot(\\exists\\mathit{y}\\qdot(\\mathit{y} > 1 \\land \\mathit{y} < \\mathit{x} \\land \\mathit{x} \\mathit{mod} \\mathit{y} = 0))\\}$"
+      ],
+      "text/plain": [
+       "/*@symbolic*/ {x∣x > 1 ∧ ¬(∃y·(y > 1 ∧ y < x ∧ x mod y = 0))}"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{x | x > 1 & x < 500 & not(#y.(y > 1 & y < x & x mod y = 0))}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "Outputs are rendered as $\\LaTeX$ formulas:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/latex": [
+       "$\\mathit{x} > 1 \\wedge  \\mathit{x} < 500 \\wedge  \\neg (\\exists \\mathit{y}\\cdot (\\mathit{y} > 1 \\wedge  \\mathit{y} < \\mathit{x} \\wedge  \\mathit{x} \\mod  \\mathit{y} = 0))$"
+      ],
+      "text/plain": [
+       "x > 1 ∧ x < 500 ∧ ¬(∃y·(y > 1 ∧ y < x ∧ x mod y = 0))"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":prettyprint x > 1 & x < 500 & not(#y.(y > 1 & y < x & x mod y = 0))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Unicode symbols can also be used in inputs:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\{2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499\\}$"
+      ],
+      "text/plain": [
+       "{2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499}"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{x | x>1 ∧ x<500 ∧ ¬(∃y.(y>1 ∧ y<x ∧ x mod y=0))}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "Convenient multiline input, with syntax highlighting and code completion:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "|S|E|N|D|M|O|R|Y|\n",
+       "|---|---|---|---|---|---|---|---|\n",
+       "|$9$|$5$|$6$|$7$|$1$|$0$|$8$|$2$|\n"
+      ],
+      "text/plain": [
+       "S\tE\tN\tD\tM\tO\tR\tY\n",
+       "9\t5\t6\t7\t1\t0\t8\t2\n"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":table {S,E,N,D,M,O,R,Y |\n",
+    "\n",
+    "{S, E, N, D, M, O, R, Y} ⊆ 0..9\n",
+    "∧ S > 0 ∧ M > 0\n",
+    "∧ card({S, E, N, D, M, O, R, Y}) = 8\n",
+    "∧\n",
+    "            S*1000 + E*100 + N*10 + D\n",
+    "+           M*1000 + O*100 + R*10 + E\n",
+    "= M*10000 + O*1000 + N*100 + E*10 + Y\n",
+    "\n",
+    "}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Find distinct digits such that this multiplication becomes true:\n",
+    "\n",
+    "![ProB](./img/KissKissPassion.png)\n",
+    "\n",
+    "(This is a more difficult version of the Send+More=Money puzzle.)"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "|K|I|S|P|A|O|N|\n",
+       "|---|---|---|---|---|---|---|\n",
+       "|2|0|3|4|1|8|9|\n"
+      ],
+      "text/plain": [
+       "K\tI\tS\tP\tA\tO\tN\n",
+       "2\t0\t3\t4\t1\t8\t9\n"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":table {K,I,S,P,A,O,N | {K,P} ⊆ 1..9 ∧\n",
+    "    {I,S,A,O,N} ⊆ 0..9 ∧\n",
+    "    (1000*K+100*I+10*S+S) * (1000*K+100*I+10*S+S) \n",
+    "     =  1000000*P+100000*A+10000*S+1000*S+100*I+10*O+N ∧\n",
+    "    card({K, I, S, P, A, O, N}) = 7}"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## Visualisation\n",
+    "\n",
+    "In B, sequences are also functions, functions are relations, and relations are sets.\n",
+    "Relations can be displayed visually:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\{(2\\mapsto 1),(3\\mapsto 1),(3\\mapsto 2),(4\\mapsto 1),(4\\mapsto 2),(4\\mapsto 3),(5\\mapsto 1),(5\\mapsto 2),(5\\mapsto 3),(5\\mapsto 4)\\}$"
+      ],
+      "text/plain": [
+       "{(2↦1),(3↦1),(3↦2),(4↦1),(4↦2),(4↦3),(5↦1),(5↦2),(5↦3),(5↦4)}"
+      ]
+     },
+     "execution_count": 8,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "{x,y | x:1..5 & y:1..5 & x>y}"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    },
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Preference changed: DOT_ENGINE = circo\n"
+      ]
+     },
+     "execution_count": 9,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":pref DOT_ENGINE=circo"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "image/svg+xml": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<!-- Generated by graphviz version 2.40.1 (20161225.0304)\n -->\n<!-- Title: state Pages: 1 -->\n<svg width=\"243pt\" height=\"235pt\"\n viewBox=\"0.00 0.00 243.39 234.72\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 230.7203)\">\n<title>state</title>\n<polygon fill=\"#ffffff\" stroke=\"transparent\" points=\"-4,4 -4,-230.7203 239.3858,-230.7203 239.3858,4 -4,4\"/>\n<!-- 5 -->\n<g id=\"node1\" class=\"node\">\n<title>5</title>\n<polygon fill=\"#cdba96\" stroke=\"#cdba96\" points=\"166.1026,-36 112.1026,-36 112.1026,0 166.1026,0 166.1026,-36\"/>\n<text text-anchor=\"middle\" x=\"139.1026\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">5</text>\n</g>\n<!-- 4 -->\n<g id=\"node2\" class=\"node\">\n<title>4</title>\n<polygon fill=\"#cdba96\" stroke=\"#cdba96\" points=\"235.3858,-131.3602 181.3858,-131.3602 181.3858,-95.3602 235.3858,-95.3602 235.3858,-131.3602\"/>\n<text text-anchor=\"middle\" x=\"208.3858\" y=\"-109.6602\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">4</text>\n</g>\n<!-- 5&#45;&gt;4 -->\n<g id=\"edge1\" class=\"edge\">\n<title>5&#45;&gt;4</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M152.4611,-36.3864C162.879,-50.7254 177.4897,-70.8353 189.1646,-86.9044\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"186.5938,-89.3207 195.3033,-95.3536 192.257,-85.2062 186.5938,-89.3207\"/>\n<text text-anchor=\"middle\" x=\"161.8128\" y=\"-65.4454\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 3 -->\n<g id=\"node3\" class=\"node\">\n<title>3</title>\n<polygon fill=\"#cdba96\" stroke=\"#cdba96\" points=\"54,-190.296 0,-190.296 0,-154.296 54,-154.296 54,-190.296\"/>\n<text text-anchor=\"middle\" x=\"27\" y=\"-168.596\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">3</text>\n</g>\n<!-- 5&#45;&gt;3 -->\n<g id=\"edge2\" class=\"edge\">\n<title>5&#45;&gt;3</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M126.0236,-36.0017C106.3845,-63.0326 69.1556,-114.2737 46.3626,-145.6457\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"43.3895,-143.7832 40.3432,-153.9307 49.0526,-147.8978 43.3895,-143.7832\"/>\n<text text-anchor=\"middle\" x=\"77.1931\" y=\"-94.6237\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 2 -->\n<g id=\"node4\" class=\"node\">\n<title>2</title>\n<polygon fill=\"#cdba96\" stroke=\"#cdba96\" points=\"166.1026,-226.7203 112.1026,-226.7203 112.1026,-190.7203 166.1026,-190.7203 166.1026,-226.7203\"/>\n<text text-anchor=\"middle\" x=\"139.1026\" y=\"-205.0203\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">2</text>\n</g>\n<!-- 5&#45;&gt;2 -->\n<g id=\"edge3\" class=\"edge\">\n<title>5&#45;&gt;2</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M139.1026,-36.2871C139.1026,-69.6799 139.1026,-140.734 139.1026,-180.4178\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"135.6027,-180.4886 139.1026,-190.4886 142.6027,-180.4887 135.6027,-180.4886\"/>\n<text text-anchor=\"middle\" x=\"130.1026\" y=\"-112.1524\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 1 -->\n<g id=\"node5\" class=\"node\">\n<title>1</title>\n<polygon fill=\"#cdba96\" stroke=\"#cdba96\" points=\"54,-72.4243 0,-72.4243 0,-36.4243 54,-36.4243 54,-72.4243\"/>\n<text text-anchor=\"middle\" x=\"27\" y=\"-50.7243\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">1</text>\n</g>\n<!-- 5&#45;&gt;1 -->\n<g id=\"edge4\" class=\"edge\">\n<title>5&#45;&gt;1</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M111.9657,-26.8173C97.6137,-31.4806 79.779,-37.2754 64.1603,-42.3502\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"62.7045,-39.143 54.2756,-45.562 64.8677,-45.8004 62.7045,-39.143\"/>\n<text text-anchor=\"middle\" x=\"79.063\" y=\"-38.3838\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 4&#45;&gt;3 -->\n<g id=\"edge5\" class=\"edge\">\n<title>4&#45;&gt;3</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M181.2305,-122.1835C150.0179,-132.3251 98.6277,-149.0227 63.8291,-160.3295\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"62.4733,-157.0898 54.0443,-163.5088 64.6364,-163.7472 62.4733,-157.0898\"/>\n<text text-anchor=\"middle\" x=\"113.5298\" y=\"-145.0565\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 4&#45;&gt;2 -->\n<g id=\"edge6\" class=\"edge\">\n<title>4&#45;&gt;2</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M195.0273,-131.7466C184.6094,-146.0856 169.9987,-166.1955 158.3238,-182.2646\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"155.2315,-180.5663 152.1851,-190.7138 160.8946,-184.6809 155.2315,-180.5663\"/>\n<text text-anchor=\"middle\" x=\"167.6756\" y=\"-160.8056\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 4&#45;&gt;1 -->\n<g id=\"edge7\" class=\"edge\">\n<title>4&#45;&gt;1</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M181.2305,-104.5369C150.0179,-94.3953 98.6277,-77.6976 63.8291,-66.3908\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"64.6364,-62.9731 54.0443,-63.2116 62.4733,-69.6305 64.6364,-62.9731\"/>\n<text text-anchor=\"middle\" x=\"113.5298\" y=\"-89.2639\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 3&#45;&gt;2 -->\n<g id=\"edge8\" class=\"edge\">\n<title>3&#45;&gt;2</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M54.1369,-181.1133C68.4889,-185.7765 86.3236,-191.5714 101.9423,-196.6462\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"101.2349,-200.0964 111.827,-199.858 103.3981,-193.439 101.2349,-200.0964\"/>\n<text text-anchor=\"middle\" x=\"69.0396\" y=\"-192.6798\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 3&#45;&gt;1 -->\n<g id=\"edge9\" class=\"edge\">\n<title>3&#45;&gt;1</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M27,-154.1369C27,-135.157 27,-105.1104 27,-82.9567\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"30.5001,-82.7983 27,-72.7983 23.5001,-82.7983 30.5001,-82.7983\"/>\n<text text-anchor=\"middle\" x=\"18\" y=\"-122.3468\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n<!-- 2&#45;&gt;1 -->\n<g id=\"edge10\" class=\"edge\">\n<title>2&#45;&gt;1</title>\n<path fill=\"none\" stroke=\"#b22222\" d=\"M126.0236,-190.7187C106.3845,-163.6878 69.1556,-112.4466 46.3626,-81.0746\"/>\n<polygon fill=\"#b22222\" stroke=\"#b22222\" points=\"49.0526,-78.8226 40.3432,-72.7896 43.3895,-82.9371 49.0526,-78.8226\"/>\n<text text-anchor=\"middle\" x=\"77.1931\" y=\"-139.6966\" font-family=\"Times,serif\" font-size=\"14.00\" fill=\"#000000\">K5</text>\n</g>\n</g>\n</svg>\n",
+      "text/plain": [
+       "<Dot visualization: expr_as_graph [(\"K5\",{x,y|x:1..5 & y:1..5 & x>y})]>"
+      ]
+     },
+     "execution_count": 10,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":dot expr_as_graph (\"K5\", {x,y | x:1..5 & y:1..5 & x>y})"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "slide"
+    }
+   },
+   "source": [
+    "# Applications"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "* Interactive Development\n",
+    "* Documentation of Models\n",
+    "* Documentation of ProB's standard libraries"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## Use in Teaching\n",
+    "\n",
+    "* Course materials/lecture notes as notebooks\n",
+    "    * Students can execute examples themselves and experiment with the code\n",
+    "    * Visualisation of relations, graphs, etc.\n",
+    "    * `nbconvert` renders notebooks to HTML, PDF, etc.\n",
+    "* Exercise sheets as notebooks\n",
+    "    * An incomplete notebook with exercises is provided\n",
+    "    * Students solve the exercises and turn in the finished notebook\n",
+    "    * `nbgrader` assists with creating and grading exercises\n",
+    "    * Automatic grading sometimes possible"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "## Example: Course Notes for Theoretical CS (German)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "\n",
+    "### DFA\n",
+    "\n",
+    "Ein __deterministischer endlicher Automat__ (kurz DFA für\n",
+    "  deterministic finite automaton) ist ein Quintupel \n",
+    "  $M =(\\Sigma, Z, \\delta , z_0, F)$, wobei\n",
+    "* $\\Sigma$ ein Alphabet ist,\n",
+    "* $Z$ eine endliche Menge von Zuständen mit\n",
+    "  $\\Sigma \\cap Z = \\emptyset$,\n",
+    "* $\\delta : Z \\times \\Sigma \\rightarrow Z$ die Überführungsfunktion,\n",
+    "* $z_0 \\in Z$ der Startzustand und\n",
+    "* $F \\subseteq Z$ die Menge der Endzustände (Finalzustände).\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Loaded machine: DFA"
+      ]
+     },
+     "execution_count": 2,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "MACHINE DFA\n",
+    "SETS\n",
+    "   Z = {z0,z1,z2,z3}\n",
+    "CONSTANTS Σ, F, δ\n",
+    "PROPERTIES\n",
+    " F ⊆ Z ∧\n",
+    " δ ∈ (Z×Σ) → Z\n",
+    " ∧\n",
+    " /* Der Automat von Folie 10: */\n",
+    " Σ = {0,1} ∧\n",
+    " F = {z2} ∧\n",
+    " δ = {     (z0,0)↦z1, (z0,1)↦z3,\n",
+    "           (z1,0)↦z3, (z1,1)↦z2,\n",
+    "           (z2,0)↦z2, (z2,1)↦z2,\n",
+    "           (z3,0)↦z3, (z3,1)↦z3 }\n",
+    "DEFINITIONS // Für den Zustandsgraphen:\n",
+    "  CUSTOM_GRAPH_NODES1 == rec(shape:\"doublecircle\",nodes:F); // Endzustände\n",
+    "  CUSTOM_GRAPH_NODES2 == rec(shape:\"circle\",nodes:Z\\F); // andere Zustände\n",
+    "  CUSTOM_GRAPH_NODES3 == rec(shape:\"none\",color:\"white\",style:\"none\",nodes:{\"\"});\n",
+    "  CUSTOM_GRAPH_EDGES1 == rec(color:\"red\",label:\"0\",edges:{a,b|(a,0)|->b:δ}); \n",
+    "  CUSTOM_GRAPH_EDGES2 == rec(color:\"green\",label:\"1\",edges:{a,b|(a,1)|->b:δ});\n",
+    "  CUSTOM_GRAPH_EDGES3 == rec(color:\"black\",label:\"\",edges:{\"\" |-> z0}) // Kante für den Startknoten\n",
+    "END"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "Executed operation: SETUP_CONSTANTS()"
+      ]
+     },
+     "execution_count": 3,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":constants"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Ein Automat befindet sich jeweils in einem der Zustände aus Z. Am Anfang befindet er sich in $z_0$. \n",
+    "Der Automat kann jeweils in einem Zustand $z$ ein Symbol $x$ aus $\\Sigma$ verarbeiten und wechselt dann in den Zustand $\\delta(z,x)$.\n",
+    "Zum Beispiel, wenn der DFA im Startzustand z0 das Symbol $0$ erhält wechselt er nach:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\mathit{z1}$"
+      ],
+      "text/plain": [
+       "z1"
+      ]
+     },
+     "execution_count": 4,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "δ(z0,0)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Wenn der Automat dann das Symbol 1 erhält wechselt er von Zustand $z1$ nach:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\mathit{z2}$"
+      ],
+      "text/plain": [
+       "z2"
+      ]
+     },
+     "execution_count": 5,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "δ(z1,1)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Da $z2\\in F$ ein Endzustand ist, akzeptiert der DFA das Wort $01$ (oder $[0,1]$ in der Notation vom Notebook).\n",
+    "\n",
+    "\n",
+    "### Arbeitsweise eines DFAs\n",
+    "\n",
+    "Ein DFA $M= (\\Sigma, Z, \\delta , z_0, F)$ akzeptiert bzw. verwirft  eine\n",
+    "Eingabe $x$ wie folgt:\n",
+    "* $M$ beginnt beim Anfangszustand $z_0$ und führt insgesamt $|x|$ Schritte aus.\n",
+    "* Der Lesekopf wandert dabei v.l.n.r. über das Eingabewort $x$, Symbol\n",
+    "  für Symbol, und ändert dabei seinen Zustand jeweils gemäß der\n",
+    "  Überführungsfunktion $\\delta$:\n",
+    "  Ist $M$ im Zustand $z \\in Z$ und liest das\n",
+    "  Symbol $a \\in \\Sigma$ und gilt $\\delta(z,a) = z'$, so ändert $M$ seinen\n",
+    "  Zustand in $z'$.\n",
+    "* Ist der letzte erreichte Zustand (nachdem $x$ abgearbeitet ist)\n",
+    " * ein  Endzustand, so akzeptiert $M$ die Eingabe $x$;\n",
+    " * andernfalls lehnt $M$ sie ab.\n",
+    "\n",
+    "![Arbeitsweise](./img/endl_auto.png)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "Da in diesem Automaten z0 kein Endzustand ist, wird zum Beispiel das leere Wort abgelehnt:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/markdown": [
+       "$\\mathit{FALSE}$"
+      ],
+      "text/plain": [
+       "FALSE"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    "z0 ∈ F"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "#### Zustandgraph\n",
+    "\n",
+    "Man kann den DFA auch grafisch darstellen: Endzustände sind mit einem doppelten Kreis gekennzeichnet, der Anfangszustand wird durch eine besondere Startkante gekennzeichnet.\n",
+    "\n",
+    "Formal ist dies so definiert:\n",
+    "Ein DFA $M= (\\Sigma, Z, \\delta , z_0, F)$ lässt sich anschaulich durch \n",
+    "seinen __Zustandsgraphen__ darstellen,\n",
+    "\n",
+    "* dessen Knoten die Zustände von $M$ und\n",
+    "* dessen Kanten Zustandsübergänge gemäß der\n",
+    "  Überführungsfunktion $\\delta$ repräsentieren.\n",
+    "* Gilt $\\delta(z,a) = z'$ für ein Symbol $a \\in \\Sigma$ und für\n",
+    "  zwei Zustände $z, z' \\in Z$, so hat dieser Graph eine gerichtete\n",
+    "  Kante von $z$ nach $z'$, die mit $a$ beschriftet ist.\n",
+    "* Der Startzustand wird durch einen Pfeil auf $z_0$ dargestellt.\n",
+    "* Endzustände sind durch einen Doppelkreis markiert.\n",
+    "\n",
+    "Für den Automaten oben ergiebt dies folgenden Zustandsgraphen.\n",
+    "(Anmerkung: diese Darstellung erfordert eine neue Version des ProB-Jupyter-Kernels. Falls diese bei ihnen nicht funktioniert schauen Sie sich die Abbildung auf den Folien an)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {
+    "vscode": {
+     "languageId": "python"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "image/svg+xml": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n<!-- Generated by graphviz version 2.50.0 (20211204.2007)\n -->\n<!-- Title: prob_graph Pages: 1 -->\n<svg width=\"540pt\" height=\"717pt\"\n viewBox=\"0.00 0.00 540.00 717.37\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n<g id=\"graph0\" class=\"graph\" transform=\"scale(0.99 0.99) rotate(0) translate(4 724)\">\n<title>prob_graph</title>\n<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-724 544,-724 544,4 -4,4\"/>\n<!-- 0 -->\n<g id=\"node1\" class=\"node\">\n<title>0</title>\n<ellipse fill=\"lightgrey\" stroke=\"black\" cx=\"59.91\" cy=\"-55\" rx=\"18\" ry=\"18\"/>\n<ellipse fill=\"none\" stroke=\"black\" cx=\"59.91\" cy=\"-55\" rx=\"22\" ry=\"22\"/>\n<text text-anchor=\"middle\" x=\"59.91\" y=\"-51.9\" font-family=\"Times,serif\" font-size=\"12.00\">z2</text>\n</g>\n<!-- 0&#45;&gt;0 -->\n<g id=\"edge3\" class=\"edge\">\n<title>0&#45;&gt;0</title>\n<path fill=\"none\" stroke=\"green\" d=\"M79.67,-65.43C90.26,-67.2 99.91,-63.72 99.91,-55 99.91,-49.28 95.75,-45.81 89.9,-44.61\"/>\n<polygon fill=\"green\" stroke=\"green\" points=\"89.68,-41.11 79.67,-44.57 89.66,-48.11 89.68,-41.11\"/>\n<text text-anchor=\"middle\" x=\"103.41\" y=\"-51.9\" font-family=\"Times,serif\" font-size=\"12.00\">1</text>\n</g>\n<!-- 0&#45;&gt;0 -->\n<g id=\"edge7\" class=\"edge\">\n<title>0&#45;&gt;0</title>\n<path fill=\"none\" stroke=\"red\" d=\"M75.65,-70.43C95.01,-82.46 117.91,-77.32 117.91,-55 117.91,-36.17 101.61,-29.57 84.88,-35.19\"/>\n<polygon fill=\"red\" stroke=\"red\" points=\"83.18,-32.12 75.65,-39.57 86.18,-38.45 83.18,-32.12\"/>\n<text text-anchor=\"middle\" x=\"121.41\" y=\"-51.9\" font-family=\"Times,serif\" font-size=\"12.00\">0</text>\n</g>\n<!-- 1 -->\n<g id=\"node2\" class=\"node\">\n<title>1</title>\n<ellipse fill=\"lightgrey\" stroke=\"black\" cx=\"280.91\" cy=\"-492\" rx=\"18\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"280.91\" y=\"-488.9\" font-family=\"Times,serif\" font-size=\"12.00\">z0</text>\n</g>\n<!-- 2 -->\n<g id=\"node3\" class=\"node\">\n<title>2</title>\n<ellipse fill=\"lightgrey\" stroke=\"black\" cx=\"206.91\" cy=\"-278\" rx=\"18\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"206.91\" y=\"-274.9\" font-family=\"Times,serif\" font-size=\"12.00\">z1</text>\n</g>\n<!-- 1&#45;&gt;2 -->\n<g id=\"edge5\" class=\"edge\">\n<title>1&#45;&gt;2</title>\n<path fill=\"none\" stroke=\"red\" d=\"M275.23,-474.72C262.45,-438.11 231.36,-349.05 215.92,-304.81\"/>\n<polygon fill=\"red\" stroke=\"red\" points=\"219.21,-303.63 212.61,-295.34 212.6,-305.93 219.21,-303.63\"/>\n<text text-anchor=\"middle\" x=\"250.41\" y=\"-381.9\" font-family=\"Times,serif\" font-size=\"12.00\">0</text>\n</g>\n<!-- 3 -->\n<g id=\"node4\" class=\"node\">\n<title>3</title>\n<ellipse fill=\"lightgrey\" stroke=\"black\" cx=\"354.91\" cy=\"-55\" rx=\"18\" ry=\"18\"/>\n<text text-anchor=\"middle\" x=\"354.91\" y=\"-51.9\" font-family=\"Times,serif\" font-size=\"12.00\">z3</text>\n</g>\n<!-- 1&#45;&gt;3 -->\n<g id=\"edge1\" class=\"edge\">\n<title>1&#45;&gt;3</title>\n<path fill=\"none\" stroke=\"green\" d=\"M283.76,-474.21C295.03,-408.02 336.42,-164.69 350.32,-82.97\"/>\n<polygon fill=\"green\" stroke=\"green\" points=\"353.8,-83.42 352.02,-72.97 346.89,-82.24 353.8,-83.42\"/>\n<text text-anchor=\"middle\" x=\"322.41\" y=\"-274.9\" font-family=\"Times,serif\" font-size=\"12.00\">1</text>\n</g>\n<!-- 2&#45;&gt;0 -->\n<g id=\"edge2\" class=\"edge\">\n<title>2&#45;&gt;0</title>\n<path fill=\"none\" stroke=\"green\" d=\"M197.26,-262.5C172.9,-225.87 108.61,-129.22 77.4,-82.3\"/>\n<polygon fill=\"green\" stroke=\"green\" points=\"80.25,-80.26 71.8,-73.87 74.42,-84.14 80.25,-80.26\"/>\n<text text-anchor=\"middle\" x=\"141.41\" y=\"-168.9\" font-family=\"Times,serif\" font-size=\"12.00\">1</text>\n</g>\n<!-- 2&#45;&gt;3 -->\n<g id=\"edge6\" class=\"edge\">\n<title>2&#45;&gt;3</title>\n<path fill=\"none\" stroke=\"red\" d=\"M216.62,-262.5C241.77,-224.95 309.17,-124.29 339.62,-78.84\"/>\n<polygon fill=\"red\" stroke=\"red\" points=\"342.7,-80.52 345.36,-70.27 336.88,-76.63 342.7,-80.52\"/>\n<text text-anchor=\"middle\" x=\"284.41\" y=\"-168.9\" font-family=\"Times,serif\" font-size=\"12.00\">0</text>\n</g>\n<!-- 3&#45;&gt;3 -->\n<g id=\"edge4\" class=\"edge\">\n<title>3&#45;&gt;3</title>\n<path fill=\"none\" stroke=\"green\" d=\"M370.08,-64.81C380.57,-67.79 390.91,-64.52 390.91,-55 390.91,-48.76 386.46,-45.2 380.43,-44.33\"/>\n<polygon fill=\"green\" stroke=\"green\" points=\"379.75,-40.88 370.08,-45.19 380.33,-47.85 379.75,-40.88\"/>\n<text text-anchor=\"middle\" x=\"394.41\" y=\"-51.9\" font-family=\"Times,serif\" font-size=\"12.00\">1</text>\n</g>\n<!-- 3&#45;&gt;3 -->\n<g id=\"edge8\" class=\"edge\">\n<title>3&#45;&gt;3</title>\n<path fill=\"none\" stroke=\"red\" d=\"M367.11,-68.67C385.6,-82.89 408.91,-78.34 408.91,-55 408.91,-35.31 392.31,-28.99 376.02,-36.04\"/>\n<polygon fill=\"red\" stroke=\"red\" points=\"373.93,-33.21 367.11,-41.33 377.5,-39.23 373.93,-33.21\"/>\n<text text-anchor=\"middle\" x=\"412.41\" y=\"-51.9\" font-family=\"Times,serif\" font-size=\"12.00\">0</text>\n</g>\n<!-- 4 -->\n<g id=\"node5\" class=\"node\">\n<title>4</title>\n</g>\n<!-- 4&#45;&gt;1 -->\n<g id=\"edge9\" class=\"edge\">\n<title>4&#45;&gt;1</title>\n<path fill=\"none\" stroke=\"black\" d=\"M280.91,-656.97C280.91,-625.57 280.91,-558.31 280.91,-520.42\"/>\n<polygon fill=\"black\" stroke=\"black\" points=\"284.41,-520.11 280.91,-510.11 277.41,-520.11 284.41,-520.11\"/>\n</g>\n</g>\n</svg>\n",
+      "text/plain": [
+       "<Dot visualization: custom_graph []>"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
+   "source": [
+    ":dot custom_graph"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "slide"
+    }
+   },
+   "source": [
+    "# Conclusion\n",
+    "\n",
+    "* ProB Jupyter notebooks allow conveniently working interactively with B\n",
+    "* Usable standalone or with existing models\n",
+    "* Applications: development, documentation, teaching\n",
+    "* Jupyter Notebook makes it easy to integrate new languages/tools in notebooks\n",
+    "* The Jupyter ecosystem provides a standard file format and useful tools (`nbconvert`, `nbgrader`, ...)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {
+    "slideshow": {
+     "slide_type": "subslide"
+    }
+   },
+   "source": [
+    "### Links\n",
+    "\n",
+    "Load this notebook in your browser: https://mybinder.org/v2/git/https%3A%2F%2Fgitlab.cs.uni-duesseldorf.de%2Fgeneral%2Fstups%2Fprob2-jupyter-kernel.git/master?filepath=notebooks%2Fpresentations%2FABZ%202021.ipynb\n",
+    "\n",
+    "Download and install locally: https://gitlab.cs.uni-duesseldorf.de/general/stups/prob2-jupyter-kernel"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "ProB 2",
+   "language": "prob",
+   "name": "prob2"
+  },
+  "language_info": {
+   "codemirror_mode": "prob2_jupyter_repl",
+   "file_extension": ".prob",
+   "mimetype": "text/x-prob2-jupyter-repl",
+   "name": "prob"
+  },
+  "vscode": {
+   "interpreter": {
+    "hash": "301501caf4ced90c88b4867a75dc974b7f77d88824feb914d0cf4e75e12213b3"
+   }
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}