{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "field-notebook-cover",
     "field-notebook-style"
    ]
   },
   "source": [
    "<style>\n",
    "/*\n",
    " * The Evidence Path\n",
    " * A restrained, dependency-free reading layer for Jupyter and the web.\n",
    " */\n",
    "\n",
    ":root {\n",
    "  --fn-paper: #ffffff;\n",
    "  --fn-ink: #111111;\n",
    "  --fn-ink-soft: #666666;\n",
    "  --fn-intro: #555555;\n",
    "  --fn-rule: #dddddd;\n",
    "  --fn-rule-strong: #111111;\n",
    "  --fn-code-paper: #f7f7f7;\n",
    "  --fn-code-ink: #191919;\n",
    "  --fn-code-leading: 1.5;\n",
    "  --fn-line-number: #888888;\n",
    "  --fn-line-number-width: 2.5rem;\n",
    "  --fn-code-gutter: 3.5rem;\n",
    "  --fn-syntax-name: #191919;\n",
    "  --fn-syntax-keyword: #5a3b73;\n",
    "  --fn-syntax-builtin: #315f75;\n",
    "  --fn-syntax-string: #2f6b43;\n",
    "  --fn-syntax-number: #8a4d20;\n",
    "  --fn-syntax-comment: #777777;\n",
    "  --fn-syntax-operator: #555555;\n",
    "  --fn-syntax-decorator: #7a4e20;\n",
    "  --fn-page-width: 42rem;\n",
    "  --fn-wide-width: 64rem;\n",
    "  --fn-measure: 38rem;\n",
    "  --fn-cell-top: 0;\n",
    "  --fn-cell-bottom: 0;\n",
    "  --fn-ui: Arial, Helvetica, sans-serif;\n",
    "  --fn-prose: Georgia, \"Times New Roman\", serif;\n",
    "  --fn-mono: ui-monospace, \"SFMono-Regular\", Consolas, \"Liberation Mono\", Menlo,\n",
    "    monospace;\n",
    "}\n",
    "\n",
    "html {\n",
    "  color: var(--fn-ink);\n",
    "  background: var(--fn-paper);\n",
    "}\n",
    "\n",
    ".jp-Notebook,\n",
    ".jp-NotebookPanel-notebook,\n",
    ".notebook_app .container,\n",
    ".notebook-container {\n",
    "  color: var(--fn-ink) !important;\n",
    "  background: var(--fn-paper) !important;\n",
    "}\n",
    "\n",
    ".jp-Notebook .jp-Cell,\n",
    ".cell {\n",
    "  box-sizing: border-box;\n",
    "  position: relative;\n",
    "  max-width: var(--fn-page-width);\n",
    "  margin: 0 auto !important;\n",
    "  border: 0 !important;\n",
    "  box-shadow: none !important;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon,\n",
    ".text_cell_render {\n",
    "  box-sizing: border-box;\n",
    "  color: var(--fn-ink) !important;\n",
    "  background: var(--fn-paper) !important;\n",
    "  border: 0 !important;\n",
    "  font-family: var(--fn-prose) !important;\n",
    "  font-size: 1.08rem;\n",
    "  line-height: 1.64;\n",
    "  padding: var(--fn-cell-top) 0 var(--fn-cell-bottom) !important;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon p,\n",
    ".text_cell_render p {\n",
    "  max-width: var(--fn-measure);\n",
    "  margin: 0 0 1.35rem;\n",
    "  text-wrap: pretty;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon h1,\n",
    ".jp-RenderedHTMLCommon h2,\n",
    ".jp-RenderedHTMLCommon h3,\n",
    ".jp-RenderedHTMLCommon h4,\n",
    ".text_cell_render h1,\n",
    ".text_cell_render h2,\n",
    ".text_cell_render h3,\n",
    ".text_cell_render h4 {\n",
    "  color: var(--fn-ink) !important;\n",
    "  border: 0 !important;\n",
    "  font-family: var(--fn-ui) !important;\n",
    "  font-weight: 700;\n",
    "  text-wrap: balance;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon h1,\n",
    ".text_cell_render h1 {\n",
    "  font-size: clamp(2rem, 7vw, 3.5rem);\n",
    "  letter-spacing: -0.04em;\n",
    "  line-height: 1.08;\n",
    "  margin: 0 0 1rem;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon h2,\n",
    ".text_cell_render h2 {\n",
    "  font-size: 1.65rem;\n",
    "  letter-spacing: -0.025em;\n",
    "  line-height: 1.2;\n",
    "  margin: 3.25rem 0 1rem;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon h3,\n",
    ".text_cell_render h3 {\n",
    "  font-size: 1.2rem;\n",
    "  letter-spacing: -0.015em;\n",
    "  line-height: 1.3;\n",
    "  margin: 2.25rem 0 0.75rem;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon h4,\n",
    ".text_cell_render h4 {\n",
    "  font-size: 1rem;\n",
    "  letter-spacing: 0;\n",
    "  line-height: 1.35;\n",
    "  margin: 1.75rem 0 0.65rem;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon > :first-child,\n",
    ".text_cell_render > :first-child {\n",
    "  margin-top: 0;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon a,\n",
    ".text_cell_render a {\n",
    "  color: inherit !important;\n",
    "  text-decoration: underline;\n",
    "  text-decoration-thickness: 1px;\n",
    "  text-underline-offset: 0.18em;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon a:hover,\n",
    ".text_cell_render a:hover {\n",
    "  text-decoration-thickness: 2px;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon a:focus-visible,\n",
    ".text_cell_render a:focus-visible {\n",
    "  outline: 2px solid currentColor;\n",
    "  outline-offset: 3px;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon strong,\n",
    ".text_cell_render strong {\n",
    "  color: var(--fn-ink);\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon blockquote,\n",
    ".text_cell_render blockquote {\n",
    "  max-width: 36rem;\n",
    "  margin: 1.75rem 0;\n",
    "  padding: 0 0 0 1.25rem;\n",
    "  border-left: 2px solid var(--fn-rule-strong) !important;\n",
    "  color: #444444 !important;\n",
    "  font-style: italic;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon blockquote p:last-child,\n",
    ".text_cell_render blockquote p:last-child {\n",
    "  margin-bottom: 0;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon ul,\n",
    ".jp-RenderedHTMLCommon ol,\n",
    ".text_cell_render ul,\n",
    ".text_cell_render ol {\n",
    "  max-width: var(--fn-measure);\n",
    "  margin: 0 0 1.5rem;\n",
    "  padding-left: 1.5rem;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon li,\n",
    ".text_cell_render li {\n",
    "  margin: 0.25rem 0;\n",
    "  padding-left: 0.15rem;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon table,\n",
    ".text_cell_render table {\n",
    "  display: table;\n",
    "  width: 100%;\n",
    "  margin: 1.75rem 0 2rem;\n",
    "  border-collapse: collapse;\n",
    "  border-spacing: 0;\n",
    "  color: var(--fn-ink);\n",
    "  background: transparent;\n",
    "  font-family: var(--fn-ui);\n",
    "  font-size: 0.88rem;\n",
    "  line-height: 1.45;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon th,\n",
    ".jp-RenderedHTMLCommon td,\n",
    ".text_cell_render th,\n",
    ".text_cell_render td {\n",
    "  padding: 0.65rem 0.5rem !important;\n",
    "  border: 0 !important;\n",
    "  border-bottom: 1px solid var(--fn-rule) !important;\n",
    "  text-align: left !important;\n",
    "  vertical-align: top !important;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon th,\n",
    ".text_cell_render th {\n",
    "  border-bottom-color: var(--fn-rule-strong) !important;\n",
    "  font-weight: 700 !important;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon code,\n",
    ".text_cell_render code {\n",
    "  color: var(--fn-code-ink) !important;\n",
    "  background: transparent !important;\n",
    "  border: 0;\n",
    "  font-family: var(--fn-mono) !important;\n",
    "  font-size: 0.85em;\n",
    "  padding: 0;\n",
    "}\n",
    "\n",
    ".jp-RenderedHTMLCommon pre,\n",
    ".text_cell_render pre {\n",
    "  box-sizing: border-box;\n",
    "  max-width: 100%;\n",
    "  margin: 1.5rem 0 2rem;\n",
    "  overflow-x: auto;\n",
    "  color: var(--fn-code-ink);\n",
    "  background: var(--fn-code-paper);\n",
    "  border: 1px solid var(--fn-rule);\n",
    "  font-family: var(--fn-mono);\n",
    "  font-size: 0.8rem;\n",
    "  line-height: var(--fn-code-leading);\n",
    "  padding: 1rem;\n",
    "  tab-size: 4;\n",
    "  white-space: pre;\n",
    "}\n",
    "\n",
    ".jp-CodeCell,\n",
    ".code_cell,\n",
    ".workbench-note,\n",
    ".bench-insert {\n",
    "  box-sizing: border-box;\n",
    "  width: 100%;\n",
    "  max-width: var(--fn-page-width) !important;\n",
    "  margin: 1.75rem auto 2rem !important;\n",
    "  padding: 0 !important;\n",
    "  transform: none;\n",
    "  background: var(--fn-paper) !important;\n",
    "  border: 0 !important;\n",
    "}\n",
    "\n",
    ".jp-CodeCell .jp-InputArea,\n",
    ".code_cell .input_area {\n",
    "  box-sizing: border-box;\n",
    "  max-width: 100%;\n",
    "  overflow-x: auto;\n",
    "  background: var(--fn-code-paper) !important;\n",
    "  border: 1px solid var(--fn-rule) !important;\n",
    "}\n",
    "\n",
    ".jp-CodeCell .jp-InputArea pre,\n",
    ".code_cell .input_area pre,\n",
    ".jp-CodeCell .jp-InputArea code,\n",
    ".code_cell .input_area code {\n",
    "  margin: 0 !important;\n",
    "  color: var(--fn-code-ink) !important;\n",
    "  background: transparent !important;\n",
    "  border: 0 !important;\n",
    "  font-family: var(--fn-mono) !important;\n",
    "  font-size: 0.8rem !important;\n",
    "  line-height: var(--fn-code-leading) !important;\n",
    "  padding: 1rem !important;\n",
    "  white-space: pre !important;\n",
    "}\n",
    "\n",
    ".jp-OutputArea,\n",
    ".output_area,\n",
    ".output-slip {\n",
    "  box-sizing: border-box;\n",
    "  max-width: 100%;\n",
    "  margin: 0.85rem 0 0 !important;\n",
    "  padding: 0.15rem 0 0.15rem 1rem !important;\n",
    "  overflow-x: auto;\n",
    "  color: var(--fn-code-ink) !important;\n",
    "  background: transparent !important;\n",
    "  border: 0 !important;\n",
    "  border-left: 2px solid var(--fn-rule) !important;\n",
    "}\n",
    "\n",
    ".jp-OutputArea pre,\n",
    ".output_area pre,\n",
    ".output-slip pre {\n",
    "  margin: 0.45rem 0 0 !important;\n",
    "  overflow-x: auto;\n",
    "  color: var(--fn-code-ink) !important;\n",
    "  background: transparent !important;\n",
    "  border: 0 !important;\n",
    "  font-family: var(--fn-mono) !important;\n",
    "  font-size: 0.8rem !important;\n",
    "  line-height: 1.5 !important;\n",
    "  padding: 0 !important;\n",
    "  white-space: pre-wrap;\n",
    "}\n",
    "\n",
    ".field-cover {\n",
    "  margin: 0 0 2rem;\n",
    "  padding: 2rem 0 1.75rem;\n",
    "  border-bottom: 1px solid var(--fn-rule-strong);\n",
    "}\n",
    "\n",
    ".cover-kicker,\n",
    ".folio-kicker {\n",
    "  color: var(--fn-ink-soft);\n",
    "  font-family: var(--fn-ui);\n",
    "  font-size: 0.82rem;\n",
    "  letter-spacing: 0;\n",
    "  line-height: 1.4;\n",
    "}\n",
    "\n",
    ".cover-kicker {\n",
    "  display: none;\n",
    "}\n",
    "\n",
    ".cover-subtitle {\n",
    "  max-width: 36rem;\n",
    "  margin: 0 0 1rem;\n",
    "  color: var(--fn-intro);\n",
    "  font-family: var(--fn-prose);\n",
    "  font-size: 1.25rem;\n",
    "  line-height: 1.5;\n",
    "}\n",
    "\n",
    ".cover-scribble {\n",
    "  display: none;\n",
    "}\n",
    "\n",
    ".folio-opener,\n",
    ".experiment-opener {\n",
    "  margin: 3.75rem 0 1.75rem;\n",
    "  padding: 1.5rem 0 0;\n",
    "  border-top: 2px solid var(--fn-rule-strong);\n",
    "}\n",
    "\n",
    ".folio-opener .folio-kicker,\n",
    ".experiment-opener .folio-kicker {\n",
    "  display: none;\n",
    "}\n",
    "\n",
    ".folio-opener h1,\n",
    ".experiment-opener h1 {\n",
    "  font-size: clamp(2rem, 6vw, 3rem) !important;\n",
    "  margin-bottom: 0.65rem !important;\n",
    "}\n",
    "\n",
    ".folio-opener > p,\n",
    ".experiment-opener > p {\n",
    "  color: var(--fn-intro);\n",
    "  font-size: 1.12rem;\n",
    "  margin-bottom: 1rem;\n",
    "}\n",
    "\n",
    ".opening-question,\n",
    ".field-question {\n",
    "  max-width: 36rem;\n",
    "  margin: 1.25rem 0 1.75rem;\n",
    "  color: #444444;\n",
    "  font-family: var(--fn-prose);\n",
    "  font-size: 1rem;\n",
    "  font-style: italic;\n",
    "  line-height: 1.6;\n",
    "}\n",
    "\n",
    ".leaf-download,\n",
    ".source-links {\n",
    "  font-family: var(--fn-ui);\n",
    "  font-size: 0.85rem;\n",
    "}\n",
    "\n",
    ".reading-ribbon,\n",
    ".insert-legend {\n",
    "  display: none;\n",
    "}\n",
    "\n",
    ".two-page-spread,\n",
    ".notebook-grid {\n",
    "  display: grid;\n",
    "  grid-template-columns: repeat(2, minmax(0, 1fr));\n",
    "  gap: 2rem;\n",
    "  margin: 2rem 0 2.5rem;\n",
    "  padding-top: 1.25rem;\n",
    "  border-top: 1px solid var(--fn-rule);\n",
    "}\n",
    "\n",
    ".two-page-spread h3,\n",
    ".notebook-grid h3 {\n",
    "  margin-top: 0 !important;\n",
    "}\n",
    "\n",
    ".margin-note,\n",
    ".research-note,\n",
    ".warning-note,\n",
    ".provenance-note {\n",
    "  max-width: 36rem;\n",
    "  margin: 1.75rem 0;\n",
    "  padding-left: 1.25rem;\n",
    "  border-left: 2px solid var(--fn-rule);\n",
    "  color: #444444;\n",
    "  font-size: 0.95rem;\n",
    "}\n",
    "\n",
    ".margin-note p:last-child,\n",
    ".research-note p:last-child,\n",
    ".warning-note p:last-child,\n",
    ".provenance-note p:last-child {\n",
    "  margin-bottom: 0;\n",
    "}\n",
    "\n",
    ".observation,\n",
    ".experiment,\n",
    ".definition-card,\n",
    ".execution-note {\n",
    "  margin: 2rem 0;\n",
    "  padding: 1.25rem 0;\n",
    "  border-top: 1px solid var(--fn-rule);\n",
    "  border-bottom: 1px solid var(--fn-rule);\n",
    "}\n",
    "\n",
    ".observation > :last-child,\n",
    ".experiment > :last-child,\n",
    ".definition-card > :last-child,\n",
    ".execution-note > :last-child {\n",
    "  margin-bottom: 0;\n",
    "}\n",
    "\n",
    ".sketch,\n",
    ".two-page-spread,\n",
    ".evidence-strip {\n",
    "  max-width: 100%;\n",
    "}\n",
    "\n",
    ".sketch {\n",
    "  margin: 2rem 0;\n",
    "}\n",
    "\n",
    ".sketch svg,\n",
    ".sketch img {\n",
    "  display: block;\n",
    "  width: 100%;\n",
    "  height: auto;\n",
    "}\n",
    "\n",
    ".sketch svg text {\n",
    "  fill: var(--fn-ink) !important;\n",
    "  font-family: var(--fn-ui) !important;\n",
    "}\n",
    "\n",
    ".sketch svg [stroke] {\n",
    "  stroke: var(--fn-ink) !important;\n",
    "}\n",
    "\n",
    ".sketch svg [filter] {\n",
    "  filter: none !important;\n",
    "}\n",
    "\n",
    ".sketch-caption {\n",
    "  margin-top: 0.65rem;\n",
    "  color: var(--fn-ink-soft);\n",
    "  font-family: var(--fn-ui);\n",
    "  font-size: 0.8rem;\n",
    "}\n",
    "\n",
    ".chapter-bridge {\n",
    "  margin: 2.5rem 0;\n",
    "  padding-top: 1.25rem;\n",
    "  border-top: 1px solid var(--fn-rule);\n",
    "  color: #444444;\n",
    "  font-style: italic;\n",
    "}\n",
    "\n",
    ".atlas-source,\n",
    ".evidence-leaf {\n",
    "  display: block;\n",
    "  margin: 3rem 0 1rem;\n",
    "  padding-top: 1rem;\n",
    "  border-top: 1px solid var(--fn-rule-strong);\n",
    "  color: var(--fn-ink-soft);\n",
    "  font-family: var(--fn-ui);\n",
    "  font-size: 0.78rem;\n",
    "  line-height: 1.45;\n",
    "}\n",
    "\n",
    ".atlas-source + h2,\n",
    ".evidence-leaf + h2 {\n",
    "  margin-top: 1rem !important;\n",
    "}\n",
    "\n",
    ".binding-placeholder,\n",
    ".binding-note {\n",
    "  display: block;\n",
    "  margin: 2.5rem 0;\n",
    "  padding: 1.25rem 0;\n",
    "  border-top: 1px solid var(--fn-rule-strong);\n",
    "  border-bottom: 1px solid var(--fn-rule);\n",
    "}\n",
    "\n",
    ".binding-placeholder strong,\n",
    ".binding-note h3 {\n",
    "  font-family: var(--fn-ui);\n",
    "}\n",
    "\n",
    ".binding-placeholder p,\n",
    ".binding-note p {\n",
    "  margin: 0.5rem 0 0.75rem;\n",
    "}\n",
    "\n",
    ".source-stamp,\n",
    ".tape-label,\n",
    ".binding-path,\n",
    ".source-leaf-path,\n",
    ".page-number {\n",
    "  color: var(--fn-ink-soft);\n",
    "  background: transparent;\n",
    "  font-family: var(--fn-mono);\n",
    "  font-size: 0.75rem;\n",
    "  font-weight: 400;\n",
    "}\n",
    "\n",
    ".run-note,\n",
    ".run-command {\n",
    "  margin-top: 1rem;\n",
    "  font-family: var(--fn-ui);\n",
    "  font-size: 0.82rem;\n",
    "}\n",
    "\n",
    ".equation-note,\n",
    ".math-display {\n",
    "  box-sizing: border-box;\n",
    "  width: 100%;\n",
    "  margin: 1.5rem 0 1.75rem;\n",
    "  overflow-x: auto;\n",
    "  text-align: center;\n",
    "}\n",
    "\n",
    ".math-inline {\n",
    "  white-space: normal;\n",
    "}\n",
    "\n",
    ".handwritten,\n",
    ".underlined-red {\n",
    "  color: inherit;\n",
    "  font-family: inherit;\n",
    "  text-decoration: none;\n",
    "}\n",
    "\n",
    ".ink-sheet,\n",
    ".source-sheet,\n",
    ".source-leaf-body,\n",
    ".binding-note-body {\n",
    "  background: transparent;\n",
    "  border: 0;\n",
    "}\n",
    "\n",
    ".anchor-alias {\n",
    "  position: absolute;\n",
    "  width: 1px;\n",
    "  height: 1px;\n",
    "  overflow: hidden;\n",
    "}\n",
    "\n",
    "@media (max-width: 32rem) {\n",
    "  .jp-RenderedHTMLCommon,\n",
    "  .text_cell_render {\n",
    "    font-size: 1rem;\n",
    "  }\n",
    "\n",
    "  .field-cover {\n",
    "    padding-top: 2rem;\n",
    "  }\n",
    "\n",
    "  .folio-opener,\n",
    "  .experiment-opener {\n",
    "    margin-top: 3rem;\n",
    "  }\n",
    "\n",
    "  .two-page-spread,\n",
    "  .notebook-grid {\n",
    "    grid-template-columns: 1fr;\n",
    "    gap: 0;\n",
    "  }\n",
    "\n",
    "  .jp-RenderedHTMLCommon table,\n",
    "  .text_cell_render table {\n",
    "    display: block;\n",
    "    overflow-x: auto;\n",
    "    white-space: nowrap;\n",
    "  }\n",
    "\n",
    "}\n",
    "\n",
    "@media print {\n",
    "  :root {\n",
    "    --fn-page-width: 100%;\n",
    "    --fn-wide-width: 100%;\n",
    "  }\n",
    "\n",
    "  .jp-CodeCell,\n",
    "  .code_cell,\n",
    "  .workbench-note,\n",
    "  .bench-insert {\n",
    "    width: 100%;\n",
    "  }\n",
    "\n",
    "  .leaf-download,\n",
    "  .source-links {\n",
    "    display: none;\n",
    "  }\n",
    "\n",
    "  a {\n",
    "    color: inherit !important;\n",
    "  }\n",
    "}\n",
    "\n",
    "</style>\n",
    "\n",
    "<div class=\"field-cover\">\n",
    "  <div class=\"cover-kicker\">LAB 06 · MANY FORMS</div>\n",
    "  <h1>Evidence Beyond the Paragraph</h1>\n",
    "  <div class=\"cover-subtitle\">Graphs, hierarchy, tables, page images, and domain-shaped retrieval</div>\n",
    "  <div class=\"cover-scribble\">not every answer lives in a rectangular chunk of text</div>\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The working page\n",
    "\n",
    "“GraphRAG” and “multimodal RAG” name families, not single algorithms. This\n",
    "lab decomposes representations, construction, retrieval, generation, and\n",
    "evaluation so the extra structure is justified by the task.\n",
    "\n",
    "**Learning outcomes**\n",
    "\n",
    "- distinguish curated KGs, extracted entity graphs, passage graphs, query-time graphs, and community-report systems;\n",
    "- run Personalized PageRank and blend graph propagation with retrieval seeds;\n",
    "- select hierarchical evidence without double-counting descendant leaves;\n",
    "- retrieve table rows with schema/numeric signals and row provenance;\n",
    "- compute ColBERT/ColPali-style MaxSim and quantify vector pooling;\n",
    "- choose text, structure, or pixels based on the evidence—not fashion.\n",
    "\n",
    "Companion chapter: [Structured and multimodal RAG](../research/structured_and_multimodal_rag.md).\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Repository root: resolved from the notebook location\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "import sys\n",
    "\n",
    "ROOT = Path.cwd()\n",
    "if not (ROOT / \"src\").exists():\n",
    "    ROOT = ROOT.parent\n",
    "sys.path.insert(0, str(ROOT / \"src\"))\n",
    "print(\"Repository root: resolved from the notebook location\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 1. Graph representations answer different questions\n",
    "\n",
    "A curated knowledge graph has typed canonical entities and relations. An\n",
    "OpenIE graph extracts noisy triples from text. A passage graph links chunks\n",
    "by entity overlap, citations, hyperlinks, or learned edges. A hierarchical\n",
    "graph organizes documents/sections/summaries. Microsoft GraphRAG builds an\n",
    "entity/relation graph, clusters communities, generates community reports,\n",
    "and map-reduces global questions. Query-specific systems build a small graph\n",
    "during search. These have different build cost, freshness, and failure modes.\n",
    "\n",
    "Use graph structure when relation chains, neighborhoods, corpus-wide themes,\n",
    "hierarchy, or path explanations matter. Plain hybrid retrieval often wins on\n",
    "local fact lookup. Graph construction cannot recover relations omitted or\n",
    "hallucinated by extraction.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Personalized PageRank:\n",
      "entity:DPR 0.3242\n",
      "entity:RAG 0.2068\n",
      "passage:dpr 0.1837\n",
      "query:DPR 0.15\n",
      "passage:rag 0.1352\n",
      "Mass: 1.0\n"
     ]
    }
   ],
   "source": [
    "from rag_evolution.structured import personalized_pagerank\n",
    "\n",
    "graph = {\n",
    "    \"query:DPR\": {\"entity:DPR\": 1.0},\n",
    "    \"entity:DPR\": {\"passage:dpr\": 1.0, \"entity:RAG\": 0.5},\n",
    "    \"entity:RAG\": {\"passage:rag\": 1.0, \"entity:DPR\": 0.3},\n",
    "    \"passage:dpr\": {\"entity:DPR\": 1.0},\n",
    "    \"passage:rag\": {\"entity:RAG\": 1.0},\n",
    "}\n",
    "ranks = personalized_pagerank(graph, {\"query:DPR\": 1.0}, damping=0.85)\n",
    "print(\"Personalized PageRank:\")\n",
    "for node, score in sorted(ranks.items(), key=lambda item: -item[1]):\n",
    "    print(node, round(score, 4))\n",
    "print(\"Mass:\", round(sum(ranks.values()), 8))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 2. Graph retrieval is seed, propagate, filter, and ground\n",
    "\n",
    "Entity linking maps query mentions to graph seeds. Personalized PageRank,\n",
    "path search, beam search, subgraph matching, GNN scoring, or LLM-guided\n",
    "traversal propagates relevance. The system then maps nodes/edges back to\n",
    "source passages; without that last step a graph answer may be structurally\n",
    "plausible but ungrounded.\n",
    "\n",
    "Evaluate entity-link accuracy, edge/triple precision/recall, supporting-path\n",
    "recall, passage recall, answer/citation quality, build/update cost, graph\n",
    "storage, and performance when the graph is incomplete or conflicting.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Blended retrieval + graph propagation:\n",
      "entity:DPR 0.5584\n",
      "entity:RAG 0.3356\n",
      "passage:rag 0.1279\n",
      "passage:dpr 0.1181\n",
      "query:DPR 0.0\n"
     ]
    }
   ],
   "source": [
    "from rag_evolution.structured import graph_expand\n",
    "\n",
    "seeds = {\"entity:DPR\": 1.0, \"entity:RAG\": 0.4}\n",
    "expanded = graph_expand(seeds, graph, k=6, propagation_weight=0.65)\n",
    "print(\"Blended retrieval + graph propagation:\")\n",
    "for node, score in expanded:\n",
    "    print(node, round(score, 4))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 3. Corpus-wide GraphRAG is not neighborhood expansion\n",
    "\n",
    "Global sensemaking systems extract entities/relations, run community\n",
    "detection (often Leiden), precompute hierarchical reports, select relevant\n",
    "communities, and aggregate partial answers. This can improve broad questions\n",
    "such as “What themes and actors shape this corpus?” but shifts cost to\n",
    "ingestion and update propagation. Dynamic community selection and DRIFT-like\n",
    "global-to-local refinement reduce wasted report reads.\n",
    "\n",
    "Test global synthesis and local fact questions separately. Use human factual\n",
    "audits in addition to LLM-judged comprehensiveness/diversity. Measure omitted\n",
    "facts, report hallucinations, extraction errors, update latency, and token\n",
    "cost. Never generalize a win on global summaries to all QA.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dynamic community selection proxy: [('control', 0.3333333333333333), ('generation', 0.3333333333333333), ('retrieval', 0.3333333333333333)]\n",
      "Selected reports: ['control', 'generation', 'retrieval']\n"
     ]
    }
   ],
   "source": [
    "communities = {\n",
    "    \"retrieval\": {\"DPR\", \"ColBERT\", \"SPLADE\", \"BM25\"},\n",
    "    \"generation\": {\"RAG\", \"FiD\", \"RETRO\", \"Atlas\"},\n",
    "    \"control\": {\"Self-RAG\", \"Adaptive-RAG\", \"Search-R1\", \"GRIP\"},\n",
    "}\n",
    "query_entities = {\"DPR\", \"RAG\", \"GRIP\"}\n",
    "scored = sorted(\n",
    "    ((name, len(members & query_entities) / len(query_entities)) for name, members in communities.items()),\n",
    "    key=lambda item: (-item[1], item[0]),\n",
    ")\n",
    "print(\"Dynamic community selection proxy:\", scored)\n",
    "print(\"Selected reports:\", [name for name, score in scored if score > 0])\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 4. Hierarchical retrieval changes granularity during search\n",
    "\n",
    "RAPTOR recursively clusters and summarizes chunks into a tree. Parent–child\n",
    "indexes retrieve small units and expand context. Document/section trees can\n",
    "first route coarsely, then search leaves. Hierarchies help holistic long-\n",
    "document questions but summaries are lossy, can hallucinate, and must be\n",
    "rebuilt upward after edits. Keep leaf provenance and evaluate evidence lost\n",
    "in every summary level.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Selected nodes: ['dpr-leaf', 'rag-leaf', 'retrieval']\n",
      "Leaf evidence coverage: ('dpr', 'rag') tokens 32\n"
     ]
    }
   ],
   "source": [
    "from rag_evolution.structured import HierarchyNode, select_hierarchy\n",
    "\n",
    "nodes = (\n",
    "    HierarchyNode(\"root-summary\", \"retrieval and generation overview\", children=(\"retrieval\", \"generation\"), evidence_ids=(\"dpr\", \"rag\", \"fid\"), token_cost=40),\n",
    "    HierarchyNode(\"retrieval\", \"dense and sparse retrieval\", evidence_ids=(\"dpr\",), token_cost=16),\n",
    "    HierarchyNode(\"generation\", \"latent and fusion generation\", evidence_ids=(\"rag\", \"fid\"), token_cost=20),\n",
    "    HierarchyNode(\"dpr-leaf\", \"DPR evidence\", evidence_ids=(\"dpr\",), token_cost=8),\n",
    "    HierarchyNode(\"rag-leaf\", \"RAG evidence\", evidence_ids=(\"rag\",), token_cost=8),\n",
    ")\n",
    "selection = select_hierarchy(\n",
    "    nodes,\n",
    "    {\"root-summary\": 0.75, \"retrieval\": 0.8, \"generation\": 0.7, \"dpr-leaf\": 0.95, \"rag-leaf\": 0.9},\n",
    "    token_budget=32,\n",
    ")\n",
    "print(\"Selected nodes:\", [node.identifier for node in selection.nodes])\n",
    "print(\"Leaf evidence coverage:\", selection.evidence_ids, \"tokens\", selection.spent)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 5. Tables require structural and numeric semantics\n",
    "\n",
    "Flattening a table may detach headers, units, footnotes, merged cells, and row\n",
    "relationships. Alternatives include row/column serialization, table-aware\n",
    "encoders, SQL generation over governed schemas, hybrid text+cell indexes,\n",
    "region/image retrieval, or cell graphs. Preserve table ID, page, bounding\n",
    "box, row/column headers, units, and source version through citations.\n",
    "\n",
    "Test exact numeric questions, aggregations, comparisons, joins, temporal\n",
    "versions, missing values, unit conversion, and adversarially similar rows.\n",
    "Exact match on an answer is insufficient if the cited row is wrong.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "self-rag 0.825 lexical 0.833 numeric 1.0 page 4\n",
      "baseline 0.442 lexical 0.5 numeric 0.3333333333333333 page 4\n",
      "grip 0.125 lexical 0.167 numeric 0.0 page 8\n"
     ]
    }
   ],
   "source": [
    "from rag_evolution.structured import TableRow, retrieve_table_rows\n",
    "\n",
    "rows = (\n",
    "    TableRow(\"rag-results\", \"self-rag\", {\"method\": \"Self-RAG 7B\", \"PopQA\": \"54.9\", \"year\": \"2024\"}, page=4, source=\"paper-a.pdf\"),\n",
    "    TableRow(\"rag-results\", \"baseline\", {\"method\": \"RAG baseline\", \"PopQA\": \"43.5\", \"year\": \"2024\"}, page=4, source=\"paper-a.pdf\"),\n",
    "    TableRow(\"rag-results\", \"grip\", {\"method\": \"GRIP 8B\", \"average\": \"41.0\", \"year\": \"2026\"}, page=8, source=\"paper-b.pdf\"),\n",
    ")\n",
    "for hit in retrieve_table_rows(\"Which 2024 method reports PopQA 54.9?\", rows):\n",
    "    print(hit.row.row_id, round(hit.score, 3), \"lexical\", round(hit.lexical_overlap, 3), \"numeric\", hit.numeric_overlap, \"page\", hit.row.page)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 6. Visual-document RAG can bypass destructive parsing\n",
    "\n",
    "ColPali encodes rendered page patches and uses late interaction between query\n",
    "tokens and patch vectors. VisRAG retrieves page images and answers with a\n",
    "vision-language model. Visual approaches preserve layout, charts, equations,\n",
    "typography, and spatial relationships that text parsing can lose, but store\n",
    "many vectors per page and still need page/region attribution. Dynamic visual\n",
    "token compression reduces generation cost.\n",
    "\n",
    "Parsed text may win on clean prose and exact string search; pixels may win on\n",
    "tables/forms/figures. Hybrid systems can index both. Evaluate clean and\n",
    "degraded scans, multilingual pages, paraphrases, page retrieval, region\n",
    "localization, answer support, storage, latency, and token cost.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MaxSim complete page: 1.9\n",
      "MaxSim missing concept: 0.8\n",
      "pool 1 vectors 4 score 1.9\n",
      "pool 2 vectors 2 score 1.0\n",
      "pool 4 vectors 1 score 0.4\n"
     ]
    }
   ],
   "source": [
    "from rag_evolution.structured import late_interaction_score, pool_vectors\n",
    "\n",
    "query_patches = ((1.0, 0.0, 0.0), (0.0, 1.0, 0.0))  # two query-token vectors\n",
    "page_with_table_and_title = ((0.9, 0.1, 0.0), (0.0, 1.0, 0.1), (0.1, 0.0, 0.9), (-0.5, 0.0, 0.0))\n",
    "text_only_page = ((0.8, 0.0, 0.1), (0.7, 0.0, 0.2))\n",
    "print(\"MaxSim complete page:\", round(late_interaction_score(query_patches, page_with_table_and_title), 3))\n",
    "print(\"MaxSim missing concept:\", round(late_interaction_score(query_patches, text_only_page), 3))\n",
    "for group in (1, 2, 4):\n",
    "    pooled = pool_vectors(page_with_table_and_title, group)\n",
    "    print(\"pool\", group, \"vectors\", len(pooled), \"score\", round(late_interaction_score(query_patches, pooled), 3))\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 7. Other modalities need modality-native evidence contracts\n",
    "\n",
    "Image RAG may retrieve global images, objects, regions, captions, OCR, or\n",
    "generated descriptions. Chart RAG needs axes, legends, series, marks, and\n",
    "visual comparison. Audio/video RAG needs transcripts, speakers, timecodes,\n",
    "shots, keyframes, acoustic/visual embeddings, and temporal alignment. A\n",
    "citation should open the exact region or time span, not merely the file.\n",
    "\n",
    "Multimodal fusion can happen at retrieval (separate indexes + fusion), in a\n",
    "shared embedding space, through a graph, or in the generator. Evaluate each\n",
    "modality alone, oracle evidence, fused evidence, missing/corrupt modalities,\n",
    "and cross-modal contradictions.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "text   retrieval-unit=passage          citation=character span\n",
      "table  retrieval-unit=row/cell         citation=page + bounding box + headers\n",
      "image  retrieval-unit=region           citation=image + bounding box\n",
      "audio  retrieval-unit=speaker segment  citation=start/end time\n",
      "video  retrieval-unit=shot/keyframe    citation=time range + region\n",
      "code   retrieval-unit=symbol           citation=repository + commit + path + lines\n"
     ]
    }
   ],
   "source": [
    "modality_units = {\n",
    "    \"text\": (\"passage\", \"character span\"),\n",
    "    \"table\": (\"row/cell\", \"page + bounding box + headers\"),\n",
    "    \"image\": (\"region\", \"image + bounding box\"),\n",
    "    \"audio\": (\"speaker segment\", \"start/end time\"),\n",
    "    \"video\": (\"shot/keyframe\", \"time range + region\"),\n",
    "    \"code\": (\"symbol\", \"repository + commit + path + lines\"),\n",
    "}\n",
    "for modality, (unit, citation) in modality_units.items():\n",
    "    print(f\"{modality:6} retrieval-unit={unit:16} citation={citation}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 8. Code, web/API, multilingual, and regulated domains\n",
    "\n",
    "Code RAG indexes symbols, definitions, references, call/import graphs,\n",
    "repository paths, commits, tests, and generated artifacts; retrieval should\n",
    "respect repository revision. Web/API RAG needs live search provenance,\n",
    "robots/licenses, time snapshots, structured tool schemas, and defenses\n",
    "against untrusted pages. Multilingual RAG must test query/document language\n",
    "pairs, scripts, tokenization, transliteration, translation loss, and citation\n",
    "language—not just English averages.\n",
    "\n",
    "Biomedical, legal, financial, scientific, and enterprise RAG add ontology,\n",
    "authority, jurisdiction, valid-time, version, evidence hierarchy, access\n",
    "control, privacy, and calibrated abstention requirements. Domain adaptation\n",
    "cannot substitute for source governance or expert review.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "TASK: exact error code in repository \n",
      "  USE: lexical + symbol/call graph \n",
      "  PROOF: commit/path/lines\n",
      "TASK: portfolio value on a historical date \n",
      "  USE: table/SQL + bitemporal filter \n",
      "  PROOF: row + snapshot\n",
      "TASK: themes across 50k reports \n",
      "  USE: community reports + local verification \n",
      "  PROOF: report claims -> leaves\n",
      "TASK: answer from scanned forms \n",
      "  USE: visual page/region retrieval \n",
      "  PROOF: page + bounding boxes\n",
      "TASK: simple policy definition \n",
      "  USE: hybrid text + reranker \n",
      "  PROOF: immutable paragraph span\n"
     ]
    }
   ],
   "source": [
    "decision_examples = (\n",
    "    (\"exact error code in repository\", \"lexical + symbol/call graph\", \"commit/path/lines\"),\n",
    "    (\"portfolio value on a historical date\", \"table/SQL + bitemporal filter\", \"row + snapshot\"),\n",
    "    (\"themes across 50k reports\", \"community reports + local verification\", \"report claims -> leaves\"),\n",
    "    (\"answer from scanned forms\", \"visual page/region retrieval\", \"page + bounding boxes\"),\n",
    "    (\"simple policy definition\", \"hybrid text + reranker\", \"immutable paragraph span\"),\n",
    ")\n",
    "for task, architecture, proof in decision_examples:\n",
    "    print(\"TASK:\", task, \"\\n  USE:\", architecture, \"\\n  PROOF:\", proof)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 9. Evaluation and selection rules\n",
    "\n",
    "Compare against strong lexical, dense, hybrid, and long-context baselines.\n",
    "For graphs report construction and update cost; for hierarchies report\n",
    "summary loss; for tables report cell/row fidelity; for visual systems report\n",
    "storage and region attribution; for every system report end-to-end answer and\n",
    "citation support. Use task slices rather than a single average.\n",
    "\n",
    "**This lab does not claim graphs or pixels are universally superior.** It\n",
    "provides the algorithms and measurements that expose when relational,\n",
    "hierarchical, spatial, numeric, temporal, or multimodal signal earns its\n",
    "additional complexity.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "name": "python",
   "version": "3.9"
  },
  "rag_evolution": {
   "presentation": "plain-field-notebook",
   "visual_version": 2
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
