{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "e7218e6e",
   "metadata": {},
   "source": [
    "# Synthetic Pairing Design\n",
    "\n",
    "## Objective\n",
    "This notebook builds the artificial pairing setup that links the corrected BreaKHis image branch to the unchanged Wisconsin tabular branch. My first draft of the pairing logic was simply label aligned, and the numbers looked clean enough to make me suspicious, so this notebook is also where I make the control logic explicit.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## Notebook Purpose\n",
    "\n",
    "This notebook constructs the synthetic pairing design used for exploratory multimodal experiments. It pairs Wisconsin tabular records with BreaKHis patient-level image embeddings under controlled strategies.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## Why This Matters\n",
    "\n",
    "Because true matched multimodal patient data is not available, pairing must be treated as a methodological simulation. This notebook makes the pairing choices transparent and introduces random-pairing controls to avoid overclaiming same-label results.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## Build Pairing Inputs\n",
    "\n",
    "This setup cell loads the published Wisconsin dataframe and creates patient-level BreaKHis image embeddings. Aggregating image embeddings by patient keeps the image side aligned with the corrected patient-level protocol.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "b1309631",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-19T22:41:28.653735Z",
     "iopub.status.busy": "2026-04-19T22:41:28.653556Z",
     "iopub.status.idle": "2026-04-19T22:42:23.316597Z",
     "shell.execute_reply": "2026-04-19T22:42:23.315811Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Project root: /Users/sergeysotskiy/Documents/UNI/year 3/Dissertation/dissertation_project\n",
      "Outputs: /Users/sergeysotskiy/Documents/UNI/year 3/Dissertation/dissertation_project/outputs\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Wisconsin rows: 569\n",
      "Image embedding rows: 82\n",
      "Unique image patients: 82\n"
     ]
    }
   ],
   "source": [
    "from __future__ import annotations\n",
    "\n",
    "import json\n",
    "import random\n",
    "import sys\n",
    "from pathlib import Path\n",
    "\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import pandas as pd\n",
    "\n",
    "SEED = 42\n",
    "random.seed(SEED)\n",
    "np.random.seed(SEED)\n",
    "plt.style.use('seaborn-v0_8-whitegrid')\n",
    "\n",
    "CWD = Path.cwd().resolve()\n",
    "if (CWD / 'src').exists() and (CWD / 'data').exists():\n",
    "    PROJECT_ROOT = CWD\n",
    "elif (CWD.parent / 'src').exists() and (CWD.parent / 'data').exists():\n",
    "    PROJECT_ROOT = CWD.parent\n",
    "elif (CWD.parent.parent / 'src').exists() and (CWD.parent.parent / 'data').exists():\n",
    "    PROJECT_ROOT = CWD.parent.parent\n",
    "else:\n",
    "    raise RuntimeError(f'Could not resolve dissertation_project root from {CWD}')\n",
    "\n",
    "REPO_ROOT = PROJECT_ROOT.parent\n",
    "OUTPUTS = PROJECT_ROOT / 'outputs'\n",
    "FIGURES = OUTPUTS / 'figures'\n",
    "METRICS = OUTPUTS / 'metrics'\n",
    "REPORTS = OUTPUTS / 'reports'\n",
    "MODELS = PROJECT_ROOT / 'models'\n",
    "DATA_ROOT = PROJECT_ROOT / 'data' / 'dataset_cancer_v1' / 'dataset_cancer_v1'\n",
    "WISCONSIN_ROOT = PROJECT_ROOT / 'notebook_Wisconsin'\n",
    "\n",
    "for path in [FIGURES, METRICS, REPORTS]:\n",
    "    path.mkdir(parents=True, exist_ok=True)\n",
    "\n",
    "if str(PROJECT_ROOT) not in sys.path:\n",
    "    sys.path.append(str(PROJECT_ROOT))\n",
    "\n",
    "print('Project root:', PROJECT_ROOT)\n",
    "print('Outputs:', OUTPUTS)\n",
    "\n",
    "from IPython.display import display\n",
    "\n",
    "from src.breakhis import build_binary_records, patient_level_split\n",
    "from src.fusion import aggregate_image_embeddings, extract_image_embeddings, load_wisconsin_dataframe\n",
    "from src.pairing import add_consistency_flag, build_random_pairs, build_same_label_pairs\n",
    "\n",
    "wisconsin = load_wisconsin_dataframe(WISCONSIN_ROOT / 'brca.csv')\n",
    "df = build_binary_records(DATA_ROOT)\n",
    "split = patient_level_split(df, random_state=SEED)\n",
    "image_source = pd.concat([split.train, split.val, split.test], ignore_index=True)\n",
    "image_embeddings = aggregate_image_embeddings(\n",
    "    extract_image_embeddings(image_source, MODELS / 'breakhis_resnet18_patient_level_clean.pth', batch_size=32)\n",
    ")\n",
    "image_embeddings.to_csv(REPORTS / 'breakhis_patient_level_embeddings.csv', index=False)\n",
    "print('Wisconsin rows:', len(wisconsin))\n",
    "print('Image embedding rows:', len(image_embeddings))\n",
    "print('Unique image patients:', image_embeddings['patient_id'].nunique())\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## Compare Same-Label And Random Pairing\n",
    "\n",
    "This cell creates one same-label pairing and one random pairing. The `pair_label_match` flag quantifies how much artificial label agreement each strategy introduces.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "31a70c54",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-19T22:42:23.328384Z",
     "iopub.status.busy": "2026-04-19T22:42:23.328210Z",
     "iopub.status.idle": "2026-04-19T22:42:23.351465Z",
     "shell.execute_reply": "2026-04-19T22:42:23.351008Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>pairing_type</th>\n",
       "      <th>match_rate</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>same_label</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>random</td>\n",
       "      <td>0.428822</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "  pairing_type  match_rate\n",
       "0   same_label    1.000000\n",
       "1       random    0.428822"
      ]
     },
     "execution_count": 2,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "same_label_pairs = add_consistency_flag(build_same_label_pairs(wisconsin, image_embeddings, random_state=11).paired)\n",
    "random_pairs = add_consistency_flag(build_random_pairs(wisconsin, image_embeddings, random_state=11).paired)\n",
    "consistency_summary = pd.DataFrame(\n",
    "    [\n",
    "        {'pairing_type': 'same_label', 'match_rate': same_label_pairs['pair_label_match'].mean()},\n",
    "        {'pairing_type': 'random', 'match_rate': random_pairs['pair_label_match'].mean()},\n",
    "    ]\n",
    ")\n",
    "consistency_summary.to_csv(REPORTS / 'synthetic_pairing_consistency_summary.csv', index=False)\n",
    "consistency_summary\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## Visualize Pairing Design\n",
    "\n",
    "This cell plots label agreement and image-label distribution after pairing. The figure helps explain why random controls are necessary when multimodal pairs are manufactured.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "e8d74dc8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-19T22:42:23.352956Z",
     "iopub.status.busy": "2026-04-19T22:42:23.352857Z",
     "iopub.status.idle": "2026-04-19T22:42:23.602807Z",
     "shell.execute_reply": "2026-04-19T22:42:23.602335Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAA90AAAGGCAYAAABmGOKbAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAX19JREFUeJzt3Qu8TPX+//EPWzZyvx6XomRHcouudCSXIh1sSXVKKskJdbqcOkhECF0lkTgppRK6OKXSRTeVQ0juciu5Jbewse3/4/39/dc0s29ma9aePXtez8djHnvPrJk1a9bMWt/1+X4/3++3QFpaWpoBAAAAAICIKxj5VQIAAAAAAIJuAAAAAAB8REs3AAAAAAA+IegGAAAAAMAnBN0AAAAAAPiEoBsAAAAAAJ8QdAMAAAAA4BOCbgAAAAAAfELQDQBhSktLi+n1AwDytmiXA9F+/3jbbsQPgm7kSzfccIOdeeaZIbezzz7bLrnkEnvooYdsz549OVrf008/7daRU//+97/t0ksv9f01mfnmm2/cNusv/ryFCxdaz549fduVa9assWuvvda39QPI3060nMqLTqT8imSZpzJYZXFO6f31PchPP/3k7s+cOTPs148bN84mTZqUo+07kffJSTkU/Jnygq1bt9rf//53q1evnl144YW2b98+Gz58uL3zzjuW14Vzfce1W/5VKNobAPjlrLPOskGDBgXuHzlyxH744Qd7/PHHbcWKFTZt2jQrUKBAWOvq0qWLXXzxxXxZcWz69Om2bt0639Y/Z84c++6773xbPwAg91SsWNFee+01O/XUU8N+zVNPPWV9+vQ57vPGjh1rxYsXt9woh/QZ/vKXv1heMWXKFFu8eLGNHj3aKlWq5BpR9NiIESMsr7v99tutW7du2T6nbt26bp+fccYZubZdyB0E3ci3VCA1bNgw5LFzzz3Xfv/9dxszZowtWbIkw/KsqMDJS4UOAADIuwoXLhz2NcaJNCrkFr8+w4navXu3q9Bo165doKU/VoRTAZPZtSvyB9LLEXeUZi5btmxxf1NTU+25556z9u3bW/369d3J7pprrrGvv/46y7Q9pa/fe++9dscdd7jn33TTTWG996FDh+yxxx6zNm3auO0455xz3GvV8p6eajqVDq9tuvHGG2358uUhy7X9d999t5133nnWoEGDTJ8Tqe2ZNWuWK+CUzvW3v/3N5s+f7wp9L51Nf3VfrcFNmzZ127R27Vq3bO7cuZacnOxeq2UPP/ywHThwIGT9q1evtttuu829v269e/e2zZs3Z0i30vtq32ufaN/o/bZv3+5aBho1amTNmze3F154IUMB/eCDD9pFF13ktuHqq6926wmmdb/88ss2YMAAt+1a15133mk7d+4MpIRpH/z888/ZpvHpd9K6dWvXCqH1NGvWzNXCH28/63V6TfpUvmPHjrnfptap11122WX20ksv5eg7BhCfdJ7SOe9///ufde7c2f2vc8jHH39sP/74oyszVHbo/PLf//435LULFiywW265xVVU69yjlFidl3RO8ujce9ddd7lznZ6n8+wTTzyRIX1W5+krrrgi0MVL61G5m1MqS6677jp3fta6Lr/8cnfeTk9lj56nz6vPlv6cGanz6rfffmtdu3Z1+1Dr+Oqrr0KWp0/71vt6+8fbpyoXlIUn3jWGygLv/6zKlMzS37dt2+bKUZWPKgvVuBC8nzNLEw++tsmqHEr/On3v/fr1c++h97rqqqvso48+ylGZmhXts/vuu899TrX4Kn1c93/77Te3XJ9b+1PXP3oP7YOWLVu6Zdqm4N+efvfXX3+9+360Dffff7/t2rUrsDy765bMvkcdI7169XLr0+/4mWeeCTkewrmeSp9erv+VGq9jUftS+yt9ern3G/j000/tyiuvDPxm33zzzZDtVCberbfe6t5X1zv6rWmf6JoJeQMt3Yg769evd39POeUU9/fRRx91qeb33HOPO9Gp4NLJVAWETnJFixbNdD3vvfeeC0CfffbZkBNvdlR4qCBQsKwaz40bN7p0Mr23Tuheurv6LKnw0+Oq9dT/OnGqz1KVKlVcwaGKAW3bwIED3V+lV6mf0xtvvGE1a9aM2PboxK6CQin2OoEvXbrUpUilv2jS/cmTJ9uwYcNcAalt0PaqckIFxT//+U8XtKogUMH2n//8x61f34c+y+mnn24jR460o0ePun2qfmVvvfWWlStXLvAe2k71q9b766JJ3Qe03W3btnWf/ZVXXnEpZip0VIClpKS4wkwFvS4OVTs+Y8YM69Gjhz3//POuQPdou1SwqfuBAn6tJyEhwd3X+2mfq1JD30V2tdW6GJg3b55bnwL+UqVKucqZ7Paz9q2+c313wal8gwcPdhcGupDSRYsuhFVA792711VMAEB2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JMujcNdF0A0AQJx56aWX3HzMnkcffdT91RRVCxYssIkTJ7oWF1306e+iRYusV69e7jmam3vhwoWBaT1/+eUXd9PjAAAgI4JuAADiTNWqVUPua0owqV69uhsU7bHHHnPTYl1zzTX26quvun7emh5LNN2UNz2W0o/1vEsuucROOeWUqHwWAADyOubpBgAAAcWLF7cJEyYEWrOXLFni5mX2+mQrJV1Thz3zzDMuAC9VqpSNGDGCPQgAQBZo6QYAIM5pfu5g9evXt1mzZmX5fAXjXno5AADIHi3dAAAAAAD4hKAbAAAgD5k5c6Zdeuml0d4MAECEkF4OAADylle65u77Xfda7r4fACCu0NINAAAAAIBPaOkGAADIgZ9++slatmxpd9xxh73wwgvWvn17q1Spkr3++uu2fft2K126tJturU+fPu75mmLtoosusv/9739uHvTKlSvbAw88YBdffLFbvm3bNhswYIBbftppp1nz5s1D3m/dunU2fPhw++6779z0bl27drXbb7/dChYsaE8//bRt3rzZSpQo4dLSy5Qp40aX37Bhg40bN86OHTvmntutWze+YwCIElq6AQAATsCiRYtsxowZbm7zKVOmuDnL58yZY71793bB8A8//BB47vjx4+2KK66w2bNnW+3atW3gwIEuIJY777zT/T99+nS79dZb3bo8u3btsuuuu84qVqzolg8aNMimTp1qL774YuA57777rgu633rrLTfy/D//+U/74osv7KWXXnIB/8iRI916AADRQdANAABwAm688UY79dRTrUmTJm6u8gsvvNCqVavm5i+vUKGCrVmzJvBctV5rmjU9/x//+If98ssvtmPHDvcctWA//PDDVqtWLWvXrp17vUdBetGiRW3o0KFWs2ZNa9WqlQvSn3/++cBz1Lqtx7TuTp062b59+1zLuZ5/yy232NGjR23jxo18xwAQJQTdAAAAJ6Bq1aru7wUXXOAC38cee8ylcrdo0cIF1F5LttSoUSPwf/Hixd1fBcNr16516ehVqlQJLK9Xr15IanndunWtUKE/egQ2atTIrX/v3r3uvgL9AgUKuP+LFCkSsm3e/cOHD/MdA0CUEHQDAACcgMTERPdXad/du3e3lJQUa9Omjevn/Ze//CXkuSeddFKG16elpYX8zey53nsE84L51NRU9zc4IPeovzcAIG9gIDUAAIA/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yENGjx5ty5cvd320X3jhBStcuHDIct0/fPiw+1/9wLNbnpnU1FR3ixT6j/vL27/sZ39F8phAPuGNjaG/BQpEe2vyrdQIHnvhriusoFsjm3pBd1Yn4Vq1atm9996bk20EAAB5IOCeMmWKG0wtKSnJVaoroy2YAuoiRYq4/7U8fYCt+2o9z8rq1asjus179u6N6PqQub3MSuMrr8tGLEk7eiTamxAXjqYejfYm5GuLo3DsFQq3H7cGSpGqVau6wVOC+3oBAIDYM3ToUJs2bZoLvC+77DL3WKVKlWzt2rUhz9u5c2cgpVzLdT/98jp16mT5PgrmI3ndUGrpwoitCxmpcUUBd8kSJawArW2+0ZR8seb7uSdFexPyt7Q0F3AXSihES7eP6kXw2Dtw4EBYFcs5HkhN04bs2rXLVqxY4fqAeSdn1XIrNa1nz54ntsUAACDXjB071g2M+vjjj7sxWjyae/u5556zQ4cOBVq3Fy5c6AZT85brvkfp5ir/dX2QFXU/i2QXNALB3KH9zL72D90ykclBF/oXef7YC3ddOQ66X3/9dden++jRo+5E7KWa6//69esTdAMAkMetW7fOxo0b58psBdMaHC24S1nlypWtX79+bopQzd+9dOnSQMZb586dbdKkSS4wb9GihT3zzDNWrVo1Ri4HACBSo5ePHz/eevXq5QpgjVaqwnj27Nkurax169Y5XR0AAMhlH330kRv85dlnn7VmzZqF3FRrr4BcgXhycrK9/fbbLrCuUqWKe60C7KefftrN233VVVe5/t9aTosoAAARaunevn27dezY0Y1U6s3L2bZtW+vfv78NGDDAevTokdNVAgCAXKQW7uy6g1WvXt1NIZaV5s2buxsAAPChpVuDqqlPt5x++umub7c3sIrm6gYAAAAAACcYdKtV+/7777dFixbZxRdfbDNnzrT333/fpZapZhwAAAAAAJxgernm4i5RooT99ttv1rJlSzegyqBBg6x06dI2fPjwnK4OAAAAAIB8K8dB90knnRQyLchdd93lbgAAAAAA4ASCbs3lGa7s5ukEAAAAACCehB10FyxY0E0LdvLJJwfm5k6P6UIAAAAAAMhh0K0+23PnznXTg5177rmuL7duGskcAAAAAAD8iaD72muvdbf9+/fbvHnz7MMPP7TRo0dbUlKStWrVylq3bm1Vq1YNZ1UAAAAAAMSN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x46FPFePaVmBAgXcff0955xzbPHixbm+3QAAxLOclN8AACCKQfeOHTusTJkyVrhw4cBj5cuXd/3Edu/eneG5FStWDHmsXLlytnXr1lzbXgAAkLPyGwAAmBWK1k44ePBgSIEt3v3Dhw+H9dz0z/N4Ne2///67paamRmR7Ty1SKSLrAaJp3759MfcFHK1eI9qbAOSpY+/QoUPub7RalXNSfvtRHktaiSoRWxcy28FpZsdSLa1ggtIL2UU+icUymWPP7x3MsZdfy+SoBd3qo52+cPbuFylSJKznpn+eR7XtsmnTpoht78DTborYuoBoWb16dezt/MHDo70FwJ+214djT2Vd8eLFLS+X336Ux875fSK7PmSgUJvOAv6KyTKZY893HHuxeewdr0yOWtBdqVIl++2331y/sEKFCgVS1lRglyxZMsNzd+7cGfKY7qdPOfeUKlXKatSo4S4MChZkKnIAQP6h2nQV7irr8nr5TXkMAMjPwi2ToxZ016lTxxXWGgxNU4HJwoULrV69ehkCZc3NPXHiREtLS3ODqOnvokWLrFevXpmuW+tVn28AAPKjaLRwn0j5TXkMAMjvwimTo9YMXLRoUevYsaMNHjzYli5danPnzrXJkydbt27dArXmXo785Zdfbnv37rVhw4bZ2rVr3V/1KWvbtm20Nh8AgLh0vPIbAACEKpCmZuMoUeCsQvuDDz5wNQS33HKLde/e3S0788wzbcSIEZacnOzuq2AfNGiQrVu3zi176KGH7KyzzorWpgMAELeyK78BAEAeCrqBcDz99NP27bff2ksvvXTc5/773/92fx955JEc79yffvrJWrZsaR999JFVq1aNLwdxZebMmTZ27Fj7+OOPo70pAE6An2XYpZdean369Ak0hAA48WPohhtusPPOO8/69u0b1d2oATDffPNNu/rqq6O6HfEian26AQAAkPe98cYbVqxYsWhvBpBvGpNOOumkaG+G/fe//7Xx48cTdOcSgm4AAABkqWzZsuwdIEJKly6dJ/Ylyc65i/m0YC+++KK1aNHCjTyrtJf//e9/bq8oRU2D5ehxjVB799132++//x6opbvvvvts6NCh1qhRI5c288UXX9jUqVPtoosusgsuuMCt16OB8P71r3/ZOeecY82aNXOv8wbKy6np06e7wfXOPvtsO//8813//tTU1MDy/fv32+233+62+8orr7Svv/46JJXm4Ycfdq/T7d5777Xdu3fzK0BMpI5qPItnnnnGzj33XPe7Vw21jj0dCzqulB7uUfras88+6/ra1q9f3y677DL7/PPPA8u3bdtmPXr0sIYNG1qnTp0yzKOs8TP0Wh2zF198sVu3psU4keMfQO6ZM2eO/fWvf3XH7oMPPhiYQ11lu8p4nQ9UNr7//vshXbM0js4///lPN2NM8+bNXdqpR8e4uqCIzgOPPvpooBwdN26ctW7d2r755hu3XOept956y9q3b+/OTdddd51t3ryZnwBittz99NNP3TGg8k7XkJrjWceSys/bbrvNXXfqONMxpPKybt267vmvvfZaputV+axy1PPCCy+41+mY1fq13DvetJ6XX37ZtUbrurZDhw62bNmywGs1c8S1117rjlttz6233mrbt293y7QOrWvMmDHuWNW1vLZRwbaO1379+tnPP//sPqM+K/xF0B3nli9fbqNGjXKD1L333nvugFShqwvwO++80xWWevzJJ5+0r776yl5//fXAa999910rUaKEK1xViOt1uvBW32sd5CNHjrRdu3a55w4YMMD27dtn06ZNcwX0999/b0OGDMnx9qpvt05IqgDQhYUCD6W9qYLA8+GHH1pSUpK7YGjatKnrQ6P3lscff9ydrDQFnYICnSj1OYFYoekSZ8yY4aZFnDJlipvNQcdC7969XSH+ww8/BJ6roPyKK66w2bNnW+3atW3gwIGBwFm/e/2vSiwV0lqXR8etjv2KFSu65To/KKAODqRzcvwDyD0qp5944gl3/H/22Wc2YcIENyOMggMFCu+8846rcFOg7VWyiy7sFSzofNGmTRt33HtlZzCtT+XrY489Zv/5z39cQJI+qNa5SOW+Lvo1p7uuIYBY9dxzz7lrV1U0q4zTdeU999xjkyZNclMn6jpUz9GxoN++ymQ1Wun5O3fuzHbdb7/9tguK+/fv74J0Bb8LFiwIeY7W2bNnT/dclbu6DhYdnzquda2r41bbo+t3bYvnu+++s/Xr17vrb10DqBzX9bwqEPSef/nLX1zZXblyZZ/2HgI0kBri1wcffJB29tlnp61atcrd//3339O++uqrtPXr16dNmzYt5Ll33XVXWr9+/dz/Y8aMSWvWrFnasWPH3P1PP/00LSkpKW3Tpk3u/sGDB939RYsWpW3cuDGtdu3aaXv37g2s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",
      "text/plain": [
       "<Figure size 1000x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, axes = plt.subplots(1, 2, figsize=(10, 4))\n",
    "axes[0].bar(consistency_summary['pairing_type'], consistency_summary['match_rate'], color=['#31a354', '#de2d26'])\n",
    "axes[0].set_ylim(0, 1.05)\n",
    "axes[0].set_title('Pair label agreement rate')\n",
    "axes[0].set_ylabel('Match rate')\n",
    "\n",
    "same_counts = same_label_pairs['img_label'].value_counts().rename_axis('label').reset_index(name='count')\n",
    "random_counts = random_pairs['img_label'].value_counts().rename_axis('label').reset_index(name='count')\n",
    "axes[1].bar(same_counts['label'], same_counts['count'], alpha=0.7, label='same_label')\n",
    "axes[1].bar(random_counts['label'], random_counts['count'], alpha=0.7, label='random')\n",
    "axes[1].set_title('Image-label distribution after pairing')\n",
    "axes[1].legend()\n",
    "fig.tight_layout()\n",
    "fig.savefig(FIGURES / 'synthetic_pairing_design_summary.png', dpi=300)\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## Repeated-Seed Pairing Preview\n",
    "\n",
    "This cell repeats the pairing process across seeds. The goal is to show whether match rates are stable or seed-dependent before running full fusion experiments.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "043fe759",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-04-19T22:42:23.604277Z",
     "iopub.status.busy": "2026-04-19T22:42:23.604179Z",
     "iopub.status.idle": "2026-04-19T22:42:23.793170Z",
     "shell.execute_reply": "2026-04-19T22:42:23.792681Z"
    }
   },
   "outputs": [
    {
     "data": {
      "text/html": [
       "<div>\n",
       "<style scoped>\n",
       "    .dataframe tbody tr th:only-of-type {\n",
       "        vertical-align: middle;\n",
       "    }\n",
       "\n",
       "    .dataframe tbody tr th {\n",
       "        vertical-align: top;\n",
       "    }\n",
       "\n",
       "    .dataframe thead th {\n",
       "        text-align: right;\n",
       "    }\n",
       "</style>\n",
       "<table border=\"1\" class=\"dataframe\">\n",
       "  <thead>\n",
       "    <tr style=\"text-align: right;\">\n",
       "      <th></th>\n",
       "      <th>seed</th>\n",
       "      <th>pairing_type</th>\n",
       "      <th>match_rate</th>\n",
       "    </tr>\n",
       "  </thead>\n",
       "  <tbody>\n",
       "    <tr>\n",
       "      <th>0</th>\n",
       "      <td>11</td>\n",
       "      <td>same_label</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>1</th>\n",
       "      <td>11</td>\n",
       "      <td>random</td>\n",
       "      <td>0.428822</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>2</th>\n",
       "      <td>21</td>\n",
       "      <td>same_label</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>3</th>\n",
       "      <td>21</td>\n",
       "      <td>random</td>\n",
       "      <td>0.439367</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>4</th>\n",
       "      <td>31</td>\n",
       "      <td>same_label</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>5</th>\n",
       "      <td>31</td>\n",
       "      <td>random</td>\n",
       "      <td>0.483304</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>6</th>\n",
       "      <td>41</td>\n",
       "      <td>same_label</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>7</th>\n",
       "      <td>41</td>\n",
       "      <td>random</td>\n",
       "      <td>0.441125</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>8</th>\n",
       "      <td>51</td>\n",
       "      <td>same_label</td>\n",
       "      <td>1.000000</td>\n",
       "    </tr>\n",
       "    <tr>\n",
       "      <th>9</th>\n",
       "      <td>51</td>\n",
       "      <td>random</td>\n",
       "      <td>0.414763</td>\n",
       "    </tr>\n",
       "  </tbody>\n",
       "</table>\n",
       "</div>"
      ],
      "text/plain": [
       "   seed pairing_type  match_rate\n",
       "0    11   same_label    1.000000\n",
       "1    11       random    0.428822\n",
       "2    21   same_label    1.000000\n",
       "3    21       random    0.439367\n",
       "4    31   same_label    1.000000\n",
       "5    31       random    0.483304\n",
       "6    41   same_label    1.000000\n",
       "7    41       random    0.441125\n",
       "8    51   same_label    1.000000\n",
       "9    51       random    0.414763"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "seed_preview_rows = []\n",
    "for seed in [11, 21, 31, 41, 51]:\n",
    "    same = add_consistency_flag(build_same_label_pairs(wisconsin, image_embeddings, random_state=seed).paired)\n",
    "    rand = add_consistency_flag(build_random_pairs(wisconsin, image_embeddings, random_state=seed).paired)\n",
    "    seed_preview_rows.append({'seed': seed, 'pairing_type': 'same_label', 'match_rate': same['pair_label_match'].mean()})\n",
    "    seed_preview_rows.append({'seed': seed, 'pairing_type': 'random', 'match_rate': rand['pair_label_match'].mean()})\n",
    "seed_preview_df = pd.DataFrame(seed_preview_rows)\n",
    "seed_preview_df.to_csv(REPORTS / 'synthetic_pairing_seed_preview.csv', index=False)\n",
    "seed_preview_df\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb2a40dd",
   "metadata": {},
   "source": [
    "## Interpretation\n",
    "\n",
    "Same-label pairing keeps the class labels aligned on purpose, while the random control breaks that alignment. That distinction is what makes the fusion study interpretable. If the same-label version looks much stronger than the control, that is still not evidence of real multimodal complementarity on matched patients. It is evidence that an artificial pairing rule can manufacture a very easy problem if I am not careful.\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "codex_research_commentary": true
   },
   "source": [
    "## How This Notebook Supports The Dissertation\n",
    "\n",
    "This notebook is the methodological bridge into fusion. It documents that synthetic pairings are artificial and that controls are built into the design before any fusion metrics are reported.\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python (dissertation_dl)",
   "language": "python",
   "name": "dissertation_dl"
  },
  "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.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
