{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "knowledge-0",
   "metadata": {},
   "source": [
    "# Background knowledge\n",
    "\n",
    "[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/Abel-ai-lab/andrey/blob/main/examples/knowledge.ipynb)\n",
    "\n",
    "You often know part of the answer before you look at the data: a cause comes before its effect in\n",
    "time, a treatment was given before the outcome was measured, or an earlier study settled one\n",
    "link. `andrey.Knowledge` passes what you know to a search, which then returns only graphs that\n",
    "agree with it. Here PC finds every pair of a simulated graph but cannot orient a single edge;\n",
    "ordering the variables in time orients nearly all of them."
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-1",
   "metadata": {},
   "source": [
    "## Start\n",
    "\n",
    "Choose a CPU runtime and run this cell first. In Colab it installs `andrey-core==0.13.0` from PyPI;\n",
    "local kernels use the installed environment."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "knowledge-2",
   "metadata": {
    "tags": [
     "colab-setup"
    ]
   },
   "outputs": [],
   "source": [
    "import os\n",
    "import sys\n",
    "\n",
    "IN_COLAB = \"google.colab\" in sys.modules\n",
    "\n",
    "if IN_COLAB:\n",
    "    os.environ[\"ANDREY_DEVICE\"] = \"cpu\"\n",
    "    !pip install --quiet \"andrey-core[viz]==0.13.0\" pandas"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "knowledge-3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import pandas as pd\n",
    "\n",
    "import andrey\n",
    "from andrey.data import sample_scm\n",
    "from andrey.metrics import shd"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-4",
   "metadata": {},
   "source": [
    "A linear-Gaussian model with 10 variables, whose true graph we know. `sample_scm` lists the\n",
    "columns in an order drawn from the seed, so their order says nothing about the causal order."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "knowledge-5",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "true edges: x1 -> x0, x0 -> x8, x9 -> x0, x9 -> x1, x7 -> x2, x5 -> x3, x4 -> x6, x7 -> x4, x8 -> x4, x8 -> x7\n"
     ]
    }
   ],
   "source": [
    "sim = sample_scm(d=10, n=2000, density=2, seed=5)\n",
    "names = [f\"x{i}\" for i in range(10)]\n",
    "data = pd.DataFrame(sim.data, columns=names)\n",
    "truth = sim.graph\n",
    "print(\"true edges:\", \", \".join(f\"{names[a]} -> {names[b]}\" for a, b, _ in truth.oriented_edges()))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-6",
   "metadata": {},
   "source": [
    "## Without knowledge\n",
    "\n",
    "PC finds all ten pairs, but the data alone cannot say which way any of them points: every edge\n",
    "is undirected, and each counts as an error against the true graph."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "knowledge-7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pc  cpdag  |  10 nodes  |  10 edges (10 undirected)\n",
      "\n",
      "  x0 -- x1\n",
      "  x0 -- x8\n",
      "  x0 -- x9\n",
      "  x1 -- x9\n",
      "  x2 -- x7\n",
      "  x3 -- x5\n",
      "  x4 -- x6\n",
      "  x4 -- x7\n",
      "  x4 -- x8\n",
      "  x7 -- x8\n",
      "\n",
      "  indep_test: fisherz\n",
      "  tie_break: rank\n",
      "errors: 10\n"
     ]
    }
   ],
   "source": [
    "out = andrey.pc(data)\n",
    "print(out)\n",
    "print(\"errors:\", shd(out.structure, truth))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-8",
   "metadata": {},
   "source": [
    "## Tiers\n",
    "\n",
    "Tiers group the variables in time: no edge may point from a later tier into an earlier one. Say\n",
    "the first three variables were measured at the start, the next four in the middle, and the last\n",
    "three at the end. In this simulation the tiers follow the true causal order, as a domain\n",
    "expert's would."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "knowledge-9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pc  cpdag  |  10 nodes  |  10 edges (9 directed, 1 undirected)\n",
      "\n",
      "  x1 -> x0\n",
      "  x0 -> x8\n",
      "  x9 -> x0\n",
      "  x1 -- x9\n",
      "  x7 -> x2\n",
      "  x5 -> x3\n",
      "  x4 -> x6\n",
      "  x7 -> x4\n",
      "  x8 -> x4\n",
      "  x8 -> x7\n",
      "\n",
      "  indep_test: fisherz\n",
      "  tie_break: rank\n",
      "errors: 1\n"
     ]
    }
   ],
   "source": [
    "knowledge = andrey.Knowledge(\n",
    "    tiers=[[\"x9\", \"x5\", \"x1\"], [\"x3\", \"x0\", \"x8\", \"x7\"], [\"x2\", \"x4\", \"x6\"]]\n",
    ")\n",
    "guided = andrey.pc(data, knowledge=knowledge)\n",
    "print(guided)\n",
    "print(\"errors:\", shd(guided.structure, truth))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "knowledge-10",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 664 240\" width=\"100%\" font-family=\"Inter, system-ui, sans-serif\"><rect width=\"664\" height=\"240\" fill=\"#fafbfc\"/><line x1=\"209.00\" y1=\"151.64\" x2=\"45.00\" y2=\"152.96\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"219.00,151.56 208.96,146.64 209.04,156.64\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"245.00\" y1=\"151.45\" x2=\"309.00\" y2=\"151.45\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"319.00,151.45 309.00,146.45 309.00,156.45\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"210.11\" y1=\"158.51\" x2=\"144.37\" y2=\"179.72\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"219.63,155.44 208.58,153.75 211.65,163.27\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"44.43\" y1=\"156.87\" x2=\"119.57\" y2=\"179.90\" stroke=\"#e0982b\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><line x1=\"509.03\" y1=\"187.44\" x2=\"444.98\" y2=\"184.34\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"519.02,187.92 509.27,182.44 508.79,192.43\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"109.00\" y1=\"51.45\" x2=\"45.00\" y2=\"51.45\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"119.00,51.45 109.00,46.45 109.00,56.45\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"545.00\" y1=\"117.58\" x2=\"609.00\" y2=\"117.58\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"619.00,117.58 609.00,112.58 609.00,122.58\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"512.82\" y1=\"130.27\" x2=\"442.84\" y2=\"176.54\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"521.16,124.75 510.06,126.10 515.57,134.44\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"509.32\" y1=\"121.42\" x2=\"344.82\" y2=\"149.28\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"519.18,119.75 508.49,116.49 510.16,126.35\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"410.11\" y1=\"176.65\" x2=\"344.37\" y2=\"155.44\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"419.63,179.72 411.65,171.89 408.58,181.41\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><circle cx=\"232.00\" cy=\"151.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"232.00\" y=\"151.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x0</text><circle cx=\"32.00\" cy=\"153.06\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"32.00\" y=\"153.06\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x1</text><circle cx=\"532.00\" cy=\"188.55\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"532.00\" y=\"188.55\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x2</text><circle cx=\"132.00\" cy=\"51.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"132.00\" y=\"51.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x3</text><circle cx=\"532.00\" cy=\"117.58\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"532.00\" y=\"117.58\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x4</text><circle cx=\"32.00\" cy=\"51.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"32.00\" y=\"51.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x5</text><circle cx=\"632.00\" cy=\"117.58\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"632.00\" y=\"117.58\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x6</text><circle cx=\"432.00\" cy=\"183.71\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"432.00\" y=\"183.71\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x7</text><circle cx=\"332.00\" cy=\"151.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"332.00\" y=\"151.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x8</text><circle cx=\"132.00\" cy=\"183.71\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"132.00\" y=\"183.71\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x9</text></svg>"
      ],
      "text/plain": [
       "'<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 664 240\" width=\"100%\" font-family=\"Inter, system-ui, sans-serif\"><rect width=\"664\" height=\"240\" fill=\"#fafbfc\"/><line x1=\"209.00\" y1=\"151.64\" x2=\"45.00\" y2=\"152.96\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"219.00,151.56 208.96,146.64 209.04,156.64\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"245.00\" y1=\"151.45\" x2=\"309.00\" y2=\"151.45\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"319.00,151.45 309.00,146.45 309.00,156.45\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"210.11\" y1=\"158.51\" x2=\"144.37\" y2=\"179.72\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"219.63,155.44 208.58,153.75 211.65,163.27\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"44.43\" y1=\"156.87\" x2=\"119.57\" y2=\"179.90\" stroke=\"#e0982b\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><line x1=\"509.03\" y1=\"187.44\" x2=\"444.98\" y2=\"184.34\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"519.02,187.92 509.27,182.44 508.79,192.43\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"109.00\" y1=\"51.45\" x2=\"45.00\" y2=\"51.45\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"119.00,51.45 109.00,46.45 109.00,56.45\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"545.00\" y1=\"117.58\" x2=\"609.00\" y2=\"117.58\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"619.00,117.58 609.00,112.58 609.00,122.58\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"512.82\" y1=\"130.27\" x2=\"442.84\" y2=\"176.54\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"521.16,124.75 510.06,126.10 515.57,134.44\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"509.32\" y1=\"121.42\" x2=\"344.82\" y2=\"149.28\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"519.18,119.75 508.49,116.49 510.16,126.35\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><line x1=\"410.11\" y1=\"176.65\" x2=\"344.37\" y2=\"155.44\" stroke=\"#3a4658\" stroke-width=\"2.0\" stroke-linecap=\"round\"/><polygon points=\"419.63,179.72 411.65,171.89 408.58,181.41\" fill=\"#3a4658\" stroke-linejoin=\"round\"/><circle cx=\"232.00\" cy=\"151.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"232.00\" y=\"151.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x0</text><circle cx=\"32.00\" cy=\"153.06\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"32.00\" y=\"153.06\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x1</text><circle cx=\"532.00\" cy=\"188.55\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"532.00\" y=\"188.55\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x2</text><circle cx=\"132.00\" cy=\"51.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"132.00\" y=\"51.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x3</text><circle cx=\"532.00\" cy=\"117.58\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"532.00\" y=\"117.58\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x4</text><circle cx=\"32.00\" cy=\"51.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"32.00\" y=\"51.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x5</text><circle cx=\"632.00\" cy=\"117.58\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"632.00\" y=\"117.58\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x6</text><circle cx=\"432.00\" cy=\"183.71\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"432.00\" y=\"183.71\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x7</text><circle cx=\"332.00\" cy=\"151.45\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"332.00\" y=\"151.45\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x8</text><circle cx=\"132.00\" cy=\"183.71\" r=\"13.00\" fill=\"#ffffff\" stroke=\"#182233\" stroke-width=\"1.8\"/><text x=\"132.00\" y=\"183.71\" text-anchor=\"middle\" font-size=\"11\" font-weight=\"600\" fill=\"#182233\" dominant-baseline=\"central\">x9</text></svg>'"
      ]
     },
     "execution_count": 6,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import andrey.viz\n",
    "\n",
    "andrey.viz.draw(guided.structure)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-11",
   "metadata": {},
   "source": [
    "## Required and forbidden edges\n",
    "\n",
    "When you know one edge, require it; when you know an edge cannot exist, forbid it. Forbidding\n",
    "`a -> b` forbids that direction only, so forbid both to keep two variables apart. PC then orients\n",
    "the edges these imply."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "knowledge-12",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "pc  cpdag  |  10 nodes  |  10 edges (7 undirected, 3 directed)\n",
      "\n",
      "  x0 -- x1\n",
      "  x0 -- x8\n",
      "  x0 -- x9\n",
      "  x1 -- x9\n",
      "  x7 -> x2\n",
      "  x3 -- x5\n",
      "  x4 -> x6\n",
      "  x4 -- x7\n",
      "  x4 -- x8\n",
      "  x8 -> x7\n",
      "\n",
      "  indep_test: fisherz\n",
      "  tie_break: rank\n",
      "errors: 7\n"
     ]
    }
   ],
   "source": [
    "knowledge = andrey.Knowledge(required=[(\"x8\", \"x7\")], forbidden=[(\"x6\", \"x4\")])\n",
    "out = andrey.pc(data, knowledge=knowledge)\n",
    "print(out)\n",
    "print(\"errors:\", shd(out.structure, truth))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-13",
   "metadata": {},
   "source": [
    "## Every family\n",
    "\n",
    "The same `Knowledge` works across families: the constraint-based, score-based, and\n",
    "permutation-based searches, the LiNGAM methods, and the time-series methods, where a link can\n",
    "also name its lag. Each method's reference page says which parts it honors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "knowledge-14",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "   pc: 10 errors without knowledge, 1 with tiers\n",
      "  ges: 10 errors without knowledge, 1 with tiers\n",
      " boss: 10 errors without knowledge, 1 with tiers\n",
      "grasp: 10 errors without knowledge, 1 with tiers\n"
     ]
    }
   ],
   "source": [
    "knowledge = andrey.Knowledge(\n",
    "    tiers=[[\"x9\", \"x5\", \"x1\"], [\"x3\", \"x0\", \"x8\", \"x7\"], [\"x2\", \"x4\", \"x6\"]]\n",
    ")\n",
    "for method, settings in [(\"pc\", {}), (\"ges\", {}), (\"boss\", {\"seed\": 0}), (\"grasp\", {\"seed\": 0})]:\n",
    "    fit = getattr(andrey, method)\n",
    "    before = shd(fit(data, **settings).structure, truth)\n",
    "    after = shd(fit(data, knowledge=knowledge, **settings).structure, truth)\n",
    "    print(f\"{method:>5}: {before} errors without knowledge, {after} with tiers\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-15",
   "metadata": {},
   "source": [
    "## Wrong knowledge\n",
    "\n",
    "A search treats knowledge as fact. Require an edge that is not in the true graph, and the result\n",
    "has it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "knowledge-16",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "errors: 11\n"
     ]
    }
   ],
   "source": [
    "wrong = andrey.pc(data, knowledge=andrey.Knowledge(required=[(\"x9\", \"x3\")]))\n",
    "print(\"errors:\", shd(wrong.structure, truth))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "knowledge-17",
   "metadata": {},
   "source": [
    "So pass only what you are sure of. [Background knowledge](../guides/getting-started.md#background-knowledge)\n",
    "in the getting-started guide covers every part of `Knowledge`."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "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.12"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
