{
 "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 01 · FOUNDATIONS</div>\n",
    "  <h1>From Words to Vectors</h1>\n",
    "  <div class=\"cover-subtitle\">A working notebook on sparse, semantic, and hybrid retrieval</div>\n",
    "  <div class=\"cover-scribble\">first, learn what the catalogue remembers</div>\n",
    "</div>\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The working page\n",
    "\n",
    "This notebook makes the architectural history executable. It starts with BM25, adds an independently encoded semantic representation, fuses heterogeneous rankings, and connects those components to DPR, RAG, FiD, RETRO, and modern hybrid systems.\n",
    "\n",
    "The implementation is intentionally offline and inspectable. `BM25Retriever` implements real Okapi BM25. `HashingSemanticRetriever` is a fixed-width synonym and character-ngram proxy for a dual encoder; it demonstrates the interface but is **not** a pretrained neural retriever or a paper reproduction.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Historical map\n",
    "\n",
    "| Period | Core question | Representative answer |\n",
    "|---|---|---|\n",
    "| 1972–1995 | How should exact terms be weighted? | TF-IDF, relevance weighting, BM25 |\n",
    "| 2014–2017 | Can raw facts be external memory? | Memory Networks, DrQA |\n",
    "| 2019–2020 | Can retrieval be learned from QA/LM objectives? | ORQA, REALM, DPR |\n",
    "| 2020–2022 | How should a generator consume and learn from retrieval? | RAG, FiD, FiD-KD, EMDR², RETRO, Atlas |\n",
    "| 2022–2024 | Can queries, timing, and evidence quality be controlled? | HyDE, FLARE, Self-RAG, CRAG, Adaptive-RAG |\n",
    "| 2025–2026 | Can retrieval become a learned reasoning action? | Search-R1, GRIP, Q-RAG, HiPRAG |"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "tags": [
     "binding-instrument",
     "publication-instrument"
    ]
   },
   "source": [
    "### Instrument check — what is actually bound here?\n",
    "\n",
    "These two observations inspect the current repository and dated source\n",
    "ledger. Run them after changing the evidence registry; if the counts or\n",
    "topic surface move, reread the conclusions that depended on them.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "tags": [
     "binding-instrument",
     "publication-instrument"
    ]
   },
   "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": "code",
   "execution_count": 2,
   "metadata": {
    "tags": [
     "binding-instrument",
     "publication-instrument"
    ]
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Evidence cutoff: 2026-08-09\n",
      "Primary-source registry: 203 {'peer-reviewed': 181, 'preprint': 19, 'industry-report': 2, 'benchmark-program': 1}\n",
      "Most represented topic tags: [('benchmark', 26), ('multi-hop', 12), ('dense-retrieval', 12), ('generation', 9), ('efficiency', 9), ('reranking', 9), ('ann', 9), ('pretraining', 8), ('rag', 8), ('multimodal', 8), ('long-context', 8), ('graph', 8), ('evaluation', 8), ('memory', 7), ('reasoning', 7), ('reinforcement-learning', 7), ('citations', 7), ('embeddings', 7), ('retrieval', 6), ('distillation', 6)]\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "from collections import Counter\n",
    "\n",
    "registry = json.loads((ROOT / \"research\" / \"sources.json\").read_text(encoding=\"utf-8\"))\n",
    "statuses = Counter(source[\"status\"] for source in registry[\"sources\"])\n",
    "topics = Counter(topic for source in registry[\"sources\"] for topic in source[\"topics\"])\n",
    "print(\"Evidence cutoff:\", registry[\"evidence_cutoff\"])\n",
    "print(\"Primary-source registry:\", len(registry[\"sources\"]), dict(statuses))\n",
    "print(\"Most represented topic tags:\", topics.most_common(20))\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "14 source documents → 14 traceable chunks\n",
      "8 labeled questions; evidence dates 2020-04-10 to 2026-07-01\n"
     ]
    }
   ],
   "source": [
    "from pathlib import Path\n",
    "import sys\n",
    "\n",
    "ROOT = Path.cwd()\n",
    "if ROOT.name == 'notebooks':\n",
    "    ROOT = ROOT.parent\n",
    "sys.path.insert(0, str(ROOT / 'src'))\n",
    "\n",
    "from rag_evolution.demo_data import demo_documents, demo_questions\n",
    "from rag_evolution.evaluation import aggregate_metrics, evaluate_retriever\n",
    "from rag_evolution.retrievers import BM25Retriever, HashingSemanticRetriever, HybridRetriever\n",
    "from rag_evolution.text import chunk_documents\n",
    "\n",
    "documents = demo_documents()\n",
    "questions = demo_questions()\n",
    "chunks = chunk_documents(documents, chunk_size=90, overlap=18)\n",
    "print(f'{len(documents)} source documents → {len(chunks)} traceable chunks')\n",
    "print(f'{len(questions)} labeled questions; evidence dates {documents[0].date} to {documents[-1].date}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 1 — BM25: a sparse baseline that never became obsolete\n",
    "\n",
    "For document length $|d|$, average length $\\operatorname{avgdl}$, term frequency $f(t,d)$, and parameters $k_1,b$:\n",
    "\n",
    "$$\\operatorname{BM25}(q,d)=\\sum_{t\\in q}\\operatorname{IDF}(t)\\frac{(k_1+1)f(t,d)}{f(t,d)+k_1(1-b+b|d|/\\operatorname{avgdl})}.$$\n",
    "\n",
    "Sparse retrieval is strong for names, codes, dates, and exact terminology. Its failure surface is vocabulary mismatch."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "QUERY: Which system used hard BM25 negatives?\n",
      "  1. dpr-2020           score=9.111\n",
      "  2. search-r1-2025     score=1.900\n",
      "  3. crag-2024          score=1.809\n",
      "\n",
      "QUERY: What did RAG-Token marginalize?\n",
      "  1. rag-2020           score=6.571\n",
      "\n",
      "QUERY: lookup external documents by meaning\n",
      "  1. atlas-2022         score=2.390\n",
      "  2. graphrag-2024      score=2.263\n",
      "  3. rag-2020           score=1.704\n"
     ]
    }
   ],
   "source": [
    "bm25 = BM25Retriever(chunks)\n",
    "for query in [\n",
    "    'Which system used hard BM25 negatives?',\n",
    "    'What did RAG-Token marginalize?',\n",
    "    'lookup external documents by meaning',\n",
    "]:\n",
    "    print(f'\\nQUERY: {query}')\n",
    "    for result in bm25.search(query, 3):\n",
    "        print(f'  {result.rank}. {result.chunk.document_id:18s} score={result.score:.3f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 2 — independently encoded semantic retrieval\n",
    "\n",
    "DPR made the dual-encoder recipe standard: $s(q,p)=E_Q(q)^\\top E_P(p)$, trained by contrastive loss with in-batch and hard negatives. Query and passage vectors can be indexed independently, unlike a cross-encoder. The proxy below preserves that contract and bridges a small declared synonym vocabulary, so it can run without downloading a model."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Semantic-proxy results for vocabulary-mismatch query:\n",
      "  1. dpr-2020           cosine=0.177\n",
      "  2. atlas-2022         cosine=0.171\n",
      "  3. grip-2026          cosine=0.137\n",
      "  4. rag-2020           cosine=0.136\n",
      "  5. fid-2021           cosine=0.101\n"
     ]
    }
   ],
   "source": [
    "semantic = HashingSemanticRetriever(chunks, dimensions=512)\n",
    "query = 'lookup external documents by meaning'\n",
    "print('Semantic-proxy results for vocabulary-mismatch query:')\n",
    "for result in semantic.search(query, 5):\n",
    "    print(f'  {result.rank}. {result.chunk.document_id:18s} cosine={result.score:.3f}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 3 — hybrid candidate generation and reciprocal-rank fusion\n",
    "\n",
    "Sparse and semantic scores are not naturally calibrated. Reciprocal-rank fusion combines order rather than raw scale:\n",
    "\n",
    "$$\\operatorname{RRF}(d)=\\sum_r \\frac{w_r}{K+\\operatorname{rank}_r(d)}.$$\n",
    "\n",
    "This pattern reflects the empirical lesson from DPR, BEIR, SPLADE, and modern production retrieval: sparse and dense systems have complementary errors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Document-level retrieval on the teaching questions:\n",
      "BM25             recall@5=1.000  MRR=1.000  nDCG@5=0.985\n",
      "semantic proxy   recall@5=0.938  MRR=1.000  nDCG@5=0.952\n",
      "hybrid RRF       recall@5=0.938  MRR=1.000  nDCG@5=0.952\n"
     ]
    }
   ],
   "source": [
    "hybrid = HybridRetriever((('sparse', bm25, 1.0), ('semantic', semantic, 1.0)), rrf_constant=30)\n",
    "systems = {'BM25': bm25, 'semantic proxy': semantic, 'hybrid RRF': hybrid}\n",
    "\n",
    "def show_metrics(name, rows):\n",
    "    mean = aggregate_metrics(rows)\n",
    "    print(f\"{name:16s} recall@5={mean['recall@5']:.3f}  MRR={mean['mrr']:.3f}  nDCG@5={mean['ndcg@5']:.3f}\")\n",
    "\n",
    "print('Document-level retrieval on the teaching questions:')\n",
    "for name, system in systems.items():\n",
    "    show_metrics(name, evaluate_retriever(system, questions, k=5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Stage 4 — retrieval-conditioned generation\n",
    "\n",
    "The original RAG paper optimized a truncated latent-document likelihood. RAG-Sequence chose one document for the output; RAG-Token marginalized documents per token. FiD instead encoded many passages independently and let one decoder fuse their representations. RETRO injected retrieved chunks during autoregressive pretraining; Atlas combined Contriever, FiD, pretraining, and reader-to-retriever distillation.\n",
    "\n",
    "These are not interchangeable: they differ in retrieval supervision, when retrieval occurs, passage count, integration point, index refresh, generator scale, and evaluation corpus."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2020-04-10  Dense Passage Retrieval (DPR)           https://aclanthology.org/2020.emnlp-main.550/\n",
      "2020-05-22  Retrieval-Augmented Generation (RAG)    https://proceedings.neurips.cc/paper/2020/hash/6b493230205f780e1bc26945df7481e5-Abstract.html\n",
      "2020-07-02  Fusion-in-Decoder (FiD)                 https://aclanthology.org/2021.eacl-main.74/\n",
      "2021-12-08  RETRO                                   https://proceedings.mlr.press/v162/borgeaud22a.html\n",
      "2022-08-05  Atlas                                   https://jmlr.org/papers/v24/23-0037.html\n",
      "2023-10-17  Self-RAG                                https://openreview.net/forum?id=hSyW5go0v8\n",
      "2026-07-01  GRIP: Retrieval as Generation           https://aclanthology.org/2026.acl-long.196/\n"
     ]
    }
   ],
   "source": [
    "timeline_ids = ['dpr-2020', 'rag-2020', 'fid-2021', 'retro-2022', 'atlas-2022', 'self-rag-2023', 'grip-2026']\n",
    "by_id = {document.id: document for document in documents}\n",
    "for document_id in timeline_ids:\n",
    "    document = by_id[document_id]\n",
    "    print(f'{document.date}  {document.title:38s}  {document.source}')"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Interpretation\n",
    "\n",
    "1. Retrieval metrics diagnose candidate quality; they do not establish grounded answers.\n",
    "2. Dense retrieval is not a replacement for sparse retrieval. Hybrid retrieval is a strong default.\n",
    "3. More context is not monotonically better: FiD benefits from many passages, but Lost in the Middle and later evidence-utility work show distraction.\n",
    "4. Retrieval-conditioned generation does not guarantee causal attribution. The next notebook adds reranking, evidence budgeting, citations, abstention, and adaptive routing."
   ]
  }
 ],
 "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
}
