diff --git a/logic_programming/1_IntroProlog.ipynb b/logic_programming/1_IntroProlog.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..328498427d5ac5b645a90e4077c46f4cd5aea459
--- /dev/null
+++ b/logic_programming/1_IntroProlog.ipynb
@@ -0,0 +1,825 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "id": "7359298e-63ac-4080-9139-aa90c8043b2a",
+   "metadata": {},
+   "source": [
+    "# Introduction to Prolog"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "49fd5211",
+   "metadata": {},
+   "source": [
+    "Prolog programs consist of <b>clauses</b>.\n",
+    "A clause is always terminated by a dot (```.```).\n",
+    "The simplest clauses are facts. Here we define two propositions to be true:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 1,
+   "id": "93e05f7b-a91c-4262-861e-a399e094b710",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:rains/0\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:no_hat/0\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "rains.\n",
+    "no_hat."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "b05ae74b",
+   "metadata": {},
+   "source": [
+    "We can now ask the Prolog system whether it rains:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 2,
+   "id": "20b05b2c",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mtrue"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-rains."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "50c188fa",
+   "metadata": {},
+   "source": [
+    "More complicated clauses make use of the implication operator ```:-```. They are also called rules. Logically they stipulate that the left-hand side of the clause must be true if the right-hand side is true. The right-hand side can contain multiple propositions separated by commas. The comma can be read as a logical conjunction (and)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 3,
+   "id": "2b8b84a0",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:carry_umbrella/0\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "carry_umbrella :- rains, no_hat."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 4,
+   "id": "4e6314e8",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mtrue"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- carry_umbrella."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "161b0f75",
+   "metadata": {},
+   "source": []
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d7254b1b",
+   "metadata": {},
+   "source": [
+    "Instead of propositions we can also use predicates with arguments within our clauses. The arguments to predicates denote objects for which the predicate is true. Arguments which start with an upper-case letter are logical variables. Below ```X``` is such a variable and it can stand for any object."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 5,
+   "id": "1d6eed4f",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:human/1\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:tiger/1\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:mortal/1\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "% Asserting clauses for user:animal/1\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "human(sokrates).\n",
+    "human(schopenhauer).\n",
+    "human(locke).\n",
+    "\n",
+    "tiger(hobbes).\n",
+    "\n",
+    "mortal(X) :- human(X).\n",
+    "mortal(X) :- animal(X).\n",
+    "\n",
+    "animal(X) :- tiger(X)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "e645f31e",
+   "metadata": {},
+   "source": [
+    "You can now ask questions about logical consequences of your logic program. In simple queries you provide all arguments:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 8,
+   "id": "a2ab9e95",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mtrue"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-human(locke)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 9,
+   "id": "838bc91a",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1;31mfalse"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- human(hobbes)."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 10,
+   "id": "4d3217da",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mtrue"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- animal(hobbes)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "c8d0600e",
+   "metadata": {},
+   "source": [
+    "You can also use variables in queries, and Prolog will find values for the variables so that the result is a logical consequence of you program:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 11,
+   "id": "93e47505",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = sokrates"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?- mortal(X)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d82e2815",
+   "metadata": {},
+   "source": [
+    "In the standard Prolog console you can type a semicolong (```;```) to get more answers. Here in Jupyter we need to use ```jupyter:retry```."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 13,
+   "id": "0c4d96e2",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: mortal(X)\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = schopenhauer"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 14,
+   "id": "47e57f93",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: mortal(X)\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = locke"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 15,
+   "id": "7fd70461",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: mortal(X)\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = hobbes"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 16,
+   "id": "81724623",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: mortal(X)\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1;31mfalse"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "d90546b0",
+   "metadata": {},
+   "source": [
+    "Prolog also has a built-in predicate called ```findall``` which can be used to find all solutions in one go:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 17,
+   "id": "a7478245",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mResults = [sokrates,schopenhauer,locke,hobbes]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-findall(X,mortal(X),Results)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "74c96ce2",
+   "metadata": {},
+   "source": [
+    "The result is a Prolog list. Lists play an important role in Prolog and they can be written using square brackets. ```[]``` denotes the empty list. The built-in predicate ```append``` can be used to concatenate two lists:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 18,
+   "id": "0352f53e",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mR = [sokrates,locke,hobbes]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-append([sokrates,locke],[hobbes],R)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "fd8a78b7",
+   "metadata": {},
+   "source": [
+    "Lists can contain any kind of object, e.g., numbers but also other lists:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 19,
+   "id": "9e3be61b",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mOut = [1,2,sokrates,3,4,[sokrates],4]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-append([1,2,sokrates,3],[4,[sokrates],4],Out)."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "db2baf14",
+   "metadata": {},
+   "source": [
+    "One nice feature of logic programming is that the input/output relation is not pre-determined. One can run predicates backwards, meaning one can use ```append``` to deconstruct a list:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 20,
+   "id": "186fe078",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = [],\n",
+       "Y = [1,2,3]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-append(X,Y,[1,2,3])."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 21,
+   "id": "de721f76",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: append(X,Y,[1,2,3])\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = [1],\n",
+       "Y = [2,3]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 22,
+   "id": "ea9e3961",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: append(X,Y,[1,2,3])\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = [1,2],\n",
+       "Y = [3]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 23,
+   "id": "e09505d2",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: append(X,Y,[1,2,3])\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = [1,2,3],\n",
+       "Y = []"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 24,
+   "id": "52857ccd",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "% Retrying goal: append(X,Y,[1,2,3])\n"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    },
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1;31mfalse"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "jupyter:retry."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "aacfbf9d",
+   "metadata": {},
+   "source": [
+    "Variables can also appear multiple times in clauses or queries. Here we check if we can split a list in half:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 26,
+   "id": "12f78859",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = [a,b]"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-append(X,X,[a,b,a,b])."
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "id": "16ffb236",
+   "metadata": {},
+   "source": [
+    "With the underscore we indicate that we are not interested in an argument; it is an anonymous logical variable. Here we use this to find the last element of a list:"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": 27,
+   "id": "99bfae95",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [
+    {
+     "data": {
+      "text/plain": [
+       "\u001b[1mX = d"
+      ]
+     },
+     "metadata": {},
+     "output_type": "display_data"
+    }
+   ],
+   "source": [
+    "?-append(_,[X],[a,b,c,d])."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "5627c07e",
+   "metadata": {
+    "vscode": {
+     "languageId": "prolog"
+    }
+   },
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Prolog",
+   "language": "prolog",
+   "name": "prolog_kernel"
+  },
+  "language_info": {
+   "codemirror_mode": "prolog",
+   "file_extension": ".pl",
+   "mimetype": "text/x-prolog",
+   "name": "Prolog"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}