{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b45190f6",
   "metadata": {},
   "source": [
    "In this document, we present most of our compression results.\n",
    "\n",
    "To compare against BPE and Unigram, covers and merges are interchangeable"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "c53a2912",
   "metadata": {},
   "outputs": [],
   "source": [
    "from pcatt import greedy_builder\n",
    "import pickle\n",
    "import matplotlib.pyplot as plt\n",
    "import re\n",
    "import numpy as np\n",
    "\n",
    "def read_cpp_res(domain):\n",
    "    tokens = [bytes.fromhex(t.strip()) for t in open(f'cpp_outputs/{domain}/tokens.txt','r').read().strip().split('\\n')]\n",
    "    merges_per_turn = [int(x) for x in open(f'cpp_outputs/{domain}/merges.txt','r').read().strip().split('\\n')]\n",
    "    total = 0\n",
    "    totals = []\n",
    "    for m in merges_per_turn:\n",
    "        total += m\n",
    "        totals.append(total)\n",
    "    return tokens, merges_per_turn, totals\n",
    "\n",
    "def read_hf_bl_bpe(domain):\n",
    "    return [re.sub('Äł', 'Ġ', re.sub(' ', '', t)) \n",
    "            for t in open(f\"python_outputs/hf_bl_bpe/{domain}_model/merges.txt\",'r')\n",
    "            .read().strip().split('\\n')][1:]\n",
    "\n",
    "def score_turns(rank, counts):\n",
    "    test = greedy_builder.build(rank)\n",
    "    turns = [0] * len(rank)\n",
    "    words = list(counts.keys())\n",
    "    turn_scores = test.batch_score_merges_per_turn(words)\n",
    "    for w, ts in zip(words, turn_scores):\n",
    "        for t,s in ts:\n",
    "            turns[t] += s*counts[w]\n",
    "    aggregate = [0]\n",
    "    for turn in turns:\n",
    "        aggregate.append(turn+aggregate[-1])\n",
    "    aggregate = np.array(aggregate[1:])\n",
    "    total = sum([len(v.encode('utf-8'))*c for v,c in counts.items()])\n",
    "    word_per_token = (total - aggregate) / sum(counts.values())\n",
    "    \n",
    "    return turns, aggregate, word_per_token"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 380,
   "id": "a529adee",
   "metadata": {},
   "outputs": [],
   "source": [
    "greed_merges = {}\n",
    "greed_aggr = {}\n",
    "greed_tpw = {}\n",
    "bpe_merges = {}\n",
    "bpe_aggr = {}\n",
    "bpe_tpw = {}\n",
    "hfbpe_merges = {}\n",
    "hfbpe_aggr = {}\n",
    "hfbpe_tpw = {}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 386,
   "id": "827e9cb0",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "un\n",
      "arxiv\n"
     ]
    }
   ],
   "source": [
    "for domain in ['un', 'arxiv']:\n",
    "    counts = pickle.load(open(f'python_inputs/{domain}_counts.pkl', 'rb'))\n",
    "    tokens, merges_per_turn, totals = read_cpp_res(domain)\n",
    "    greed_merges[domain], greed_aggr[domain], greed_tpw[domain] = score_turns(tokens[:5000],counts)\n",
    "    tokens = [b''.join(t) for t in pickle.load(open(f\"python_outputs/slow_bpe/{domain}_bpe.pkl\",'rb'))['rules']]\n",
    "    bpe_merges[domain], bpe_aggr[domain], bpe_tpw[domain] = score_turns(tokens[:5000],counts)\n",
    "    tokens = read_hf_bl_bpe(domain)\n",
    "    hfbpe_merges[domain], hfbpe_aggr[domain], hfbpe_tpw[domain] = score_turns(tokens[:5000],counts)\n",
    "    print(domain)\n",
    "    \n",
    "for domain in ['wiki', 'pubmed']:\n",
    "    counts = pickle.load(open(f'python_inputs/{domain}_counts.pkl', 'rb'))\n",
    "    tokens, merges_per_turn, totals = read_cpp_res(domain)\n",
    "    greed_merges[domain], greed_aggr[domain], greed_tpw[domain] = score_turns(tokens[:10000],counts)\n",
    "    tokens = [b''.join(t) for t in pickle.load(open(f\"python_outputs/slow_bpe/{domain}_bpe.pkl\",'rb'))['rules']]\n",
    "    bpe_merges[domain], bpe_aggr[domain], bpe_tpw[domain] = score_turns(tokens[:10000],counts)\n",
    "    print(domain)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 389,
   "id": "0be2514a",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "for domain in ['un','arxiv']:\n",
    "    os.makedirs(f'processed_results/{domain}',exist_ok=True)    \n",
    "    pickle.dump(greed_merges[domain], open(f'processed_results/{domain}/greed_merges.pkl','wb'))\n",
    "    pickle.dump(greed_aggr[domain], open(f'processed_results/{domain}/greed_aggr.pkl','wb'))\n",
    "    pickle.dump(greed_tpw[domain], open(f'processed_results/{domain}/greed_tpw.pkl','wb'))\n",
    "    pickle.dump(bpe_merges[domain], open(f'processed_results/{domain}/bpe_merges.pkl','wb'))\n",
    "    pickle.dump(bpe_aggr[domain], open(f'processed_results/{domain}/bpe_aggr.pkl','wb'))\n",
    "    pickle.dump(bpe_tpw[domain], open(f'processed_results/{domain}/bpe_tpw.pkl','wb'))\n",
    "    pickle.dump(hfbpe_merges[domain], open(f'processed_results/{domain}/hfbpe_merges.pkl','wb'))\n",
    "    pickle.dump(hfbpe_aggr[domain], open(f'processed_results/{domain}/hfbpe_agr.pkl','wb'))\n",
    "    pickle.dump(hfbpe_tpw[domain], open(f'processed_results/{domain}/hfbpe_tpw.pkl','wb'))\n",
    "\n",
    "\n",
    "for domain in ['wiki','pubmed']:\n",
    "    os.makedirs(f'processed_results/{domain}',exist_ok=True)    \n",
    "    pickle.dump(greed_merges[domain], open(f'processed_results/{domain}/greed_merges.pkl','wb'))\n",
    "    pickle.dump(greed_aggr[domain], open(f'processed_results/{domain}/greed_aggr.pkl','wb'))\n",
    "    pickle.dump(greed_tpw[domain], open(f'processed_results/{domain}/greed_tpw.pkl','wb'))\n",
    "    pickle.dump(bpe_merges[domain], open(f'processed_results/{domain}/bpe_merges.pkl','wb'))\n",
    "    pickle.dump(bpe_aggr[domain], open(f'processed_results/{domain}/bpe_aggr.pkl','wb'))\n",
    "    pickle.dump(bpe_tpw[domain], open(f'processed_results/{domain}/bpe_tpw.pkl','wb'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 390,
   "id": "91bd025e",
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "for domain in ['un','arxiv']:\n",
    "    greed_merges[domain]  = pickle.load(open(f'processed_results/{domain}/greed_merges.pkl','rb'))\n",
    "    greed_aggr[domain] = pickle.load(open(f'processed_results/{domain}/greed_aggr.pkl','rb'))\n",
    "    greed_tpw[domain] = pickle.load(open(f'processed_results/{domain}/greed_tpw.pkl','rb'))\n",
    "    bpe_merges[domain] = pickle.load(open(f'processed_results/{domain}/bpe_merges.pkl','rb'))\n",
    "    bpe_aggr[domain] = pickle.load(open(f'processed_results/{domain}/bpe_aggr.pkl','rb'))\n",
    "    bpe_tpw[domain] = pickle.load(open(f'processed_results/{domain}/bpe_tpw.pkl','rb'))\n",
    "    hfbpe_merges[domain] = pickle.load(open(f'processed_results/{domain}/hfbpe_merges.pkl','rb'))\n",
    "    hfbpe_aggr[domain] = pickle.load(open(f'processed_results/{domain}/hfbpe_agr.pkl','rb'))\n",
    "    hfbpe_tpw[domain] = pickle.load(open(f'processed_results/{domain}/hfbpe_tpw.pkl','rb'))\n",
    "    \n",
    "\n",
    "for domain in ['wiki','pubmed']:\n",
    "    greed_merges[domain]  = pickle.load(open(f'processed_results/{domain}/greed_merges.pkl','rb'))\n",
    "    greed_aggr[domain] = pickle.load(open(f'processed_results/{domain}/greed_aggr.pkl','rb'))\n",
    "    greed_tpw[domain] = pickle.load(open(f'processed_results/{domain}/greed_tpw.pkl','rb'))\n",
    "    bpe_merges[domain] = pickle.load(open(f'processed_results/{domain}/bpe_merges.pkl','rb'))\n",
    "    bpe_aggr[domain] = pickle.load(open(f'processed_results/{domain}/bpe_aggr.pkl','rb'))\n",
    "    bpe_tpw[domain] = pickle.load(open(f'processed_results/{domain}/bpe_tpw.pkl','rb'))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 391,
   "id": "f61451e7",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 312024 characters]",
      "text/plain": [
       "<Figure size 1200x1200 with 16 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(constrained_layout=True, figsize=(12,12))\n",
    "# fig.suptitle('Figure title')            # set global suptitle if desired\n",
    "suptitles = [r'UN corpus - $|\\mathbf{T}|=884,708$ - $|\\mathbf{W}|=105,505$',\n",
    "            r'arXiv corpus - $|\\mathbf{T}|=7,626,684$ - $|\\mathbf{W}|=881,233$',\n",
    "            r'wiki corpus - $|\\mathbf{T}|=93,571,695$ - $|\\mathbf{W}|=8,769,943$',\n",
    "            r'PubMed corpus - $|\\mathbf{T}|=121,163,347$ - $|\\mathbf{W}|=6,527,614$']\n",
    "subfigs = fig.subfigures(4, 1) \n",
    "for d, domain in enumerate(['un','arxiv', 'wiki', 'pubmed']):\n",
    "    \n",
    "    axs = subfigs[d].subplots(1, 4)  \n",
    "    for idx, scope in enumerate([(50,100), (450,500), (1450,1500), (4000,4050)]):\n",
    "        i,j = scope\n",
    "        axs[idx].scatter(range(len(greed_merges[domain]))[i:j], greed_merges[domain][i:j], color='blue', marker='.', alpha=.5, label='Greedy')\n",
    "        axs[idx].scatter(range(len(bpe_merges[domain]))[i:j], bpe_merges[domain][i:j], color='orange', marker='.', alpha=.5, label='BPE')\n",
    "        if domain in hfbpe_merges:\n",
    "            axs[idx].scatter(range(len(hfbpe_merges[domain]))[i:j], hfbpe_merges[domain][i:j], color='red', marker='.', alpha=.5, label='HFBPE')\n",
    "        axs[idx].set_xlabel(\"$k$\")\n",
    "    axs[-1].legend()\n",
    "    axs[0].set_ylabel(\"Increment of merges at $k$ step\")\n",
    "    subfigs[d].suptitle(suptitles[d])\n",
    "\n",
    "plt.show()\n",
    "# HF BPE have byte fallback"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "581f7d78",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 252468 characters]",
      "text/plain": [
       "<Figure size 1200x1300 with 12 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(constrained_layout=True, figsize=(12,13))\n",
    "\n",
    "suptitles = [f'UN corpus',\n",
    "            f'arXiv corpus',\n",
    "            f'wiki corpus',\n",
    "            f'PubMed corpus']\n",
    "subfigs = fig.subfigures(4, 1) \n",
    "for d, domain in enumerate(['un','arxiv','wiki','pubmed']):\n",
    "    axs = subfigs[d].subplots(1, 3, width_ratios=[2, 2, 1])\n",
    "    for idx, scope in enumerate([(400,1000), (4000,5000), (4900,5000)]):\n",
    "        i,j = scope\n",
    "        axs[idx].scatter(range(len(greed_aggr[domain]))[i:j], greed_aggr[domain][i:j], color='blue', marker='.', alpha=.5, label='Greedy')\n",
    "        axs[idx].scatter(range(len(bpe_aggr[domain]))[i:j], bpe_aggr[domain][i:j], color='orange', marker='.', alpha=.5, label='BPE')\n",
    "        if domain in hfbpe_aggr:\n",
    "            axs[idx].scatter(range(len(hfbpe_aggr[domain]))[i:j], hfbpe_aggr[domain][i:j], color='red', marker='.', alpha=.5, label='HFBPE')\n",
    "        axs[idx].set_xlabel(\"$k$\")\n",
    "    axs[-1].legend()\n",
    "    axs[0].set_ylabel(\"Total num. of merges (Objective)\")\n",
    "    subfigs[d].suptitle(suptitles[d])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 79,
   "id": "d88f65f0",
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 286100 characters]",
      "text/plain": [
       "<Figure size 1200x1300 with 12 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig = plt.figure(constrained_layout=True, figsize=(12,13))\n",
    "\n",
    "suptitles = [f'UN corpus',\n",
    "            f'arXiv corpus',\n",
    "             f'wiki corpus',\n",
    "            f'PubMed corpus']\n",
    "subfigs = fig.subfigures(4, 1) \n",
    "for d, domain in enumerate(['un','arxiv','wiki','pubmed']):\n",
    "    axs = subfigs[d].subplots(1, 3, width_ratios=[2, 2, 1])\n",
    "    for idx, scope in enumerate([(400,500), (800,1000), (2400,2500)]):\n",
    "        i,j = scope\n",
    "        if domain in hfbpe_tpw:\n",
    "            axs[idx].scatter(range(len(hfbpe_tpw[domain]))[i:j], hfbpe_tpw[domain][i:j], color='red', marker='.', alpha=.5, label='HF BPE')\n",
    "        axs[idx].scatter(range(len(bpe_tpw[domain]))[i:j], bpe_tpw[domain][i:j], color='orange', marker='.', alpha=.5, label='BPE')\n",
    "        axs[idx].scatter(range(len(greed_tpw[domain]))[i:j], greed_tpw[domain][i:j], color='blue', marker='.', alpha=.5, label='Greedy')\n",
    "        axs[idx].set_xlabel(\"$k$\")\n",
    "        inspect = int((i+j)/2)\n",
    "        if inspect < len(greed_tpw[domain]):\n",
    "            axs[idx].set_title(f\"Ratio greedy/BPE = \\\n",
    "{greed_tpw[domain][inspect]/bpe_tpw[domain][inspect]:.3f} \\\n",
    "@ $k={inspect}$\")\n",
    "    axs[-1].legend()\n",
    "    axs[0].set_ylabel(\"Token per word\")\n",
    "    subfigs[d].suptitle(suptitles[d])\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "f4149a0c",
   "metadata": {},
   "outputs": [],
   "source": [
    "def bold(s):\n",
    "    return '\\033[1m' + s + '\\033[0m'\n",
    "\n",
    "def ul(s):\n",
    "    return '\\033[4m' + s + '\\033[0m'\n",
    "\n",
    "def b_cond(s , target_ratio):\n",
    "    if target_ratio == 15 or target_ratio == 25:\n",
    "        return bold(s)\n",
    "    return s"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 84,
   "id": "0940a609",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[4m\u001b[1mun\u001b[0m\u001b[0m\n",
      "token/w tar\t \u001b[4m1.3\u001b[0m\t\u001b[4m\u001b[1m1.5\u001b[0m\u001b[0m\t\u001b[4m1.7\u001b[0m\t\u001b[4m1.9\u001b[0m\t\u001b[4m2.1\u001b[0m\t\u001b[4m2.3\u001b[0m\t\u001b[4m\u001b[1m2.5\u001b[0m\u001b[0m\t\u001b[4m2.7\u001b[0m\n",
      "Greedy $k$\t 2625\t\u001b[1m1340\u001b[0m\t793\t516\t350\t242\t\u001b[1m175\u001b[0m\t129\n",
      "BPE t/w   \t 1.348\t\u001b[1m1.572\u001b[0m\t1.789\t1.992\t2.189\t2.392\t\u001b[1m2.589\u001b[0m\t2.780\n",
      "Greedy/BPE\t 0.964\t\u001b[1m0.954\u001b[0m\t0.950\t0.954\t0.959\t0.962\t\u001b[1m0.966\u001b[0m\t0.971\n",
      "BPE tar@k \t 3131\t\u001b[1m1629\u001b[0m\t971\t625\t416\t285\t\u001b[1m202\u001b[0m\t147\n",
      "tar@k diff\t 506\t\u001b[1m289\u001b[0m\t178\t109\t66\t43\t\u001b[1m27\u001b[0m\t18\n",
      "% > greed \t 19.3%\t\u001b[1m21.6%\u001b[0m\t22.4%\t21.1%\t18.9%\t17.8%\t\u001b[1m15.4%\u001b[0m\t14.0%\n",
      "\n",
      "\u001b[4m\u001b[1marxiv\u001b[0m\u001b[0m\n",
      "token/w tar\t \u001b[4m1.3\u001b[0m\t\u001b[4m\u001b[1m1.5\u001b[0m\u001b[0m\t\u001b[4m1.7\u001b[0m\t\u001b[4m1.9\u001b[0m\t\u001b[4m2.1\u001b[0m\t\u001b[4m2.3\u001b[0m\t\u001b[4m\u001b[1m2.5\u001b[0m\u001b[0m\t\u001b[4m2.7\u001b[0m\n",
      "Greedy $k$\t 3609\t\u001b[1m1857\u001b[0m\t1104\t708\t478\t333\t\u001b[1m237\u001b[0m\t171\n",
      "BPE t/w   \t 1.350\t\u001b[1m1.580\u001b[0m\t1.792\t2.002\t2.208\t2.407\t\u001b[1m2.593\u001b[0m\t2.773\n",
      "Greedy/BPE\t 0.963\t\u001b[1m0.950\u001b[0m\t0.949\t0.949\t0.951\t0.956\t\u001b[1m0.964\u001b[0m\t0.974\n",
      "BPE tar@k \t 4309\t\u001b[1m2293\u001b[0m\t1373\t871\t585\t404\t\u001b[1m282\u001b[0m\t195\n",
      "tar@k diff\t 700\t\u001b[1m436\u001b[0m\t269\t163\t107\t71\t\u001b[1m45\u001b[0m\t24\n",
      "% > greed \t 19.4%\t\u001b[1m23.5%\u001b[0m\t24.4%\t23.0%\t22.4%\t21.3%\t\u001b[1m19.0%\u001b[0m\t14.0%\n",
      "\n",
      "\u001b[4m\u001b[1mwiki\u001b[0m\u001b[0m\n",
      "token/w tar\t \u001b[4m1.3\u001b[0m\t\u001b[4m\u001b[1m1.5\u001b[0m\u001b[0m\t\u001b[4m1.7\u001b[0m\t\u001b[4m1.9\u001b[0m\t\u001b[4m2.1\u001b[0m\t\u001b[4m2.3\u001b[0m\t\u001b[4m\u001b[1m2.5\u001b[0m\u001b[0m\t\u001b[4m2.7\u001b[0m\n",
      "Greedy $k$\t 9118\t\u001b[1m3836\u001b[0m\t1952\t1105\t665\t421\t\u001b[1m277\u001b[0m\t188\n",
      "BPE t/w   \t 1.319\t\u001b[1m1.531\u001b[0m\t1.739\t1.945\t2.146\t2.339\t\u001b[1m2.532\u001b[0m\t2.725\n",
      "Greedy/BPE\t 0.986\t\u001b[1m0.980\u001b[0m\t0.977\t0.977\t0.978\t0.983\t\u001b[1m0.988\u001b[0m\t0.991\n",
      "BPE tar@k \t 9999\t\u001b[1m4287\u001b[0m\t2197\t1246\t746\t460\t\u001b[1m296\u001b[0m\t197\n",
      "tar@k diff\t 881\t\u001b[1m451\u001b[0m\t245\t141\t81\t39\t\u001b[1m19\u001b[0m\t9\n",
      "% > greed \t 9.7%\t\u001b[1m11.8%\u001b[0m\t12.6%\t12.8%\t12.2%\t9.3%\t\u001b[1m6.9%\u001b[0m\t4.8%\n",
      "\n",
      "\u001b[4m\u001b[1mpubmed\u001b[0m\u001b[0m\n",
      "token/w tar\t \u001b[4m1.3\u001b[0m\t\u001b[4m\u001b[1m1.5\u001b[0m\u001b[0m\t\u001b[4m1.7\u001b[0m\t\u001b[4m1.9\u001b[0m\t\u001b[4m2.1\u001b[0m\t\u001b[4m2.3\u001b[0m\t\u001b[4m\u001b[1m2.5\u001b[0m\u001b[0m\t\u001b[4m2.7\u001b[0m\n",
      "Greedy $k$\t 6036\t\u001b[1m2772\u001b[0m\t1506\t893\t564\t373\t\u001b[1m254\u001b[0m\t177\n",
      "BPE t/w   \t 1.327\t\u001b[1m1.540\u001b[0m\t1.751\t1.958\t2.158\t2.351\t\u001b[1m2.544\u001b[0m\t2.740\n",
      "Greedy/BPE\t 0.980\t\u001b[1m0.974\u001b[0m\t0.971\t0.971\t0.973\t0.978\t\u001b[1m0.983\u001b[0m\t0.985\n",
      "BPE tar@k \t 6805\t\u001b[1m3155\u001b[0m\t1737\t1026\t642\t415\t\u001b[1m277\u001b[0m\t190\n",
      "tar@k diff\t 769\t\u001b[1m383\u001b[0m\t231\t133\t78\t42\t\u001b[1m23\u001b[0m\t13\n",
      "% > greed \t 12.7%\t\u001b[1m13.8%\u001b[0m\t15.3%\t14.9%\t13.8%\t11.3%\t\u001b[1m9.1%\u001b[0m\t7.3%\n",
      "\n"
     ]
    }
   ],
   "source": [
    "target_ratios = [13+(x*2) for x in range(8)]\n",
    "greed_ratio2k = {}\n",
    "bpe_ratio2k = {}\n",
    "greed_k2ratio = {}\n",
    "bpe_k2ratio = {}\n",
    "\n",
    "for domain in ['un', 'arxiv', 'wiki', 'pubmed']:\n",
    "    greed_ratio2k[domain] = {}\n",
    "    greed_k2ratio[domain] = {}\n",
    "    bpe_ratio2k[domain] = {}\n",
    "    bpe_k2ratio[domain] = {}\n",
    "    for target_ratio in target_ratios:\n",
    "        tr = target_ratio/10\n",
    "        best_diff = 9999\n",
    "        for k, token_per_word in enumerate(greed_tpw[domain]):\n",
    "            diff = abs(token_per_word-tr)\n",
    "            if diff < best_diff:\n",
    "                best_diff = diff\n",
    "                greed_ratio2k[domain][target_ratio] = k\n",
    "                greed_k2ratio[domain][k] = token_per_word\n",
    "                \n",
    "                bpe_k2ratio[domain][k] = bpe_tpw[domain][k]\n",
    "                bpe_ratio2k[domain][bpe_tpw[domain][k]] = k\n",
    "        \n",
    "        best_diff = 9999\n",
    "        for k, token_per_word in enumerate(bpe_tpw[domain]):\n",
    "            diff = abs(token_per_word-tr)\n",
    "            if diff < best_diff:\n",
    "                best_diff = diff\n",
    "                bpe_ratio2k[domain][target_ratio] = k\n",
    "                bpe_k2ratio[domain][k] = token_per_word\n",
    "                \n",
    "                greed_k2ratio[domain][k] = greed_tpw[domain][k]\n",
    "                greed_ratio2k[domain][greed_tpw[domain][k]] = k\n",
    "\n",
    "\n",
    "for domain in ['un', 'arxiv', 'wiki', 'pubmed']:\n",
    "    print(ul(bold(domain)))\n",
    "    print(\"token/w tar\\t\", \"\\t\".join([ul(b_cond(f\"{tr/10:.1f}\",tr)) for tr in target_ratios]))\n",
    "    print(\"Greedy $k$\\t\", \"\\t\".join([b_cond(f\"{greed_ratio2k[domain][tr]}\",tr) for tr in target_ratios]))\n",
    "    print(\"BPE t/w   \\t\", \"\\t\".join([b_cond(f\"{bpe_k2ratio[domain][greed_ratio2k[domain][tr]]:.3f}\",tr) for tr in target_ratios]))\n",
    "    \n",
    "    print(\"Greedy/BPE\\t\", \"\\t\".join([b_cond(f\"{tr/10/bpe_k2ratio[domain][greed_ratio2k[domain][tr]]:.3f}\",tr) for tr in target_ratios]))\n",
    "    print(\"BPE tar@k \\t\", \"\\t\".join([b_cond(f\"{bpe_ratio2k[domain][tr]}\",tr) for tr in target_ratios]))\n",
    "    print(\"tar@k diff\\t\", \"\\t\".join([b_cond(f\"{bpe_ratio2k[domain][tr]-greed_ratio2k[domain][tr]}\",tr) for tr in target_ratios]))\n",
    "    print(\"% > greed \\t\", \"\\t\".join([b_cond(f\"{(bpe_ratio2k[domain][tr]-greed_ratio2k[domain][tr])/greed_ratio2k[domain][tr]*100:.1f}%\",tr) for tr in target_ratios]))\n",
    "    \n",
    "    print()\n",
    "# this results compare at some target token per word, refer to #paper results below"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4a17c505",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\u001b[1mun\u001b[0m\n",
      "\u001b[1mk\u001b[0m\t& \u001b[4m1000\u001b[0m\t& \u001b[4m2000\u001b[0m\t& \u001b[4m3000\u001b[0m\t& \u001b[4m4000\u001b[0m\t& \u001b[4m5000\u001b[0m\n",
      "\u001b[1mGTK\u001b[0m\t& 1.607\t& 1.374\t& 1.268\t& 1.205\t& 1.163\n",
      "\u001b[1mBPE\u001b[0m\t& 1.688\t& 1.431\t& 1.311\t& 1.241\t& 1.194\n",
      "\u001b[1mimpr%\u001b[0m\t& 4.83\t& 3.99\t& 3.33\t& 2.92\t& 2.54\n",
      "\u001b[1mSPU\u001b[0m\t& 1.655\t& 1.385\t& 1.261\t& 1.193\t& 1.148\n",
      "\u001b[1mimpr%\u001b[0m\t& 2.90\t& 0.78\t& -0.51\t& -0.97\t& -1.30\n",
      "\n",
      "\u001b[1marxiv\u001b[0m\n",
      "\u001b[1mk\u001b[0m\t& \u001b[4m1000\u001b[0m\t& \u001b[4m2000\u001b[0m\t& \u001b[4m3000\u001b[0m\t& \u001b[4m4000\u001b[0m\t& \u001b[4m5000\u001b[0m\n",
      "\u001b[1mGTK\u001b[0m\t& 1.742\t& 1.475\t& 1.349\t& 1.275\t& 1.226\n",
      "\u001b[1mBPE\u001b[0m\t& 1.837\t& 1.551\t& 1.407\t& 1.320\t& 1.263\n",
      "\u001b[1mimpr%\u001b[0m\t& 5.12\t& 4.94\t& 4.15\t& 3.41\t& 2.89\n",
      "\u001b[1mSPU\u001b[0m\t& 1.868\t& 1.551\t& 1.409\t& 1.328\t& 1.276\n",
      "\u001b[1mimpr%\u001b[0m\t& 6.74\t& 4.92\t& 4.30\t& 3.96\t& 3.88\n",
      "\n",
      "\u001b[1mpubmed\u001b[0m\n",
      "\u001b[1mk\u001b[0m\t& \u001b[4m2000\u001b[0m\t& \u001b[4m4000\u001b[0m\t& \u001b[4m6000\u001b[0m\t& \u001b[4m8000\u001b[0m\t& \u001b[4m10000\u001b[0m\n",
      "\u001b[1mGTK\u001b[0m\t& 1.603\t& 1.397\t& 1.301\t& 1.244\t& 1.206\n",
      "\u001b[1mBPE\u001b[0m\t& 1.650\t& 1.431\t& 1.328\t& 1.266\t& 1.225\n",
      "\u001b[1mimpr%\u001b[0m\t& 2.85\t& 2.38\t& 2.02\t& 1.75\t& 1.52\n",
      "\u001b[1mSPU\u001b[0m\t& 1.699\t& 1.465\t& 1.359\t& 1.297\t& 1.257\n",
      "\u001b[1mimpr%\u001b[0m\t& 5.63\t& 4.63\t& 4.21\t& 4.05\t& 4.02\n",
      "\n",
      "\u001b[1mwiki\u001b[0m\n",
      "\u001b[1mk\u001b[0m\t& \u001b[4m2000\u001b[0m\t& \u001b[4m4000\u001b[0m\t& \u001b[4m6000\u001b[0m\t& \u001b[4m8000\u001b[0m\t& \u001b[4m10000\u001b[0m\n",
      "\u001b[1mGTK\u001b[0m\t& 1.692\t& 1.489\t& 1.389\t& 1.326\t& 1.283\n",
      "\u001b[1mBPE\u001b[0m\t& 1.731\t& 1.519\t& 1.413\t& 1.347\t& 1.301\n",
      "\u001b[1mimpr%\u001b[0m\t& 2.26\t& 1.98\t& 1.71\t& 1.53\t& 1.37\n",
      "\u001b[1mSPU\u001b[0m\t& 1.793\t& 1.558\t& 1.444\t& 1.378\t& 1.332\n",
      "\u001b[1mimpr%\u001b[0m\t& 5.62\t& 4.43\t& 3.84\t& 3.75\t& 3.70\n",
      "\n",
      "mean improvement on bpe: 2.875\n",
      "mean improvement on spm: 3.429\n"
     ]
    }
   ],
   "source": [
    "import sentencepiece as spm\n",
    "import pickle\n",
    "import os\n",
    "import json\n",
    "import numpy as np\n",
    "\n",
    "bpe_iprv = []\n",
    "spm_iprv = []\n",
    "for domain in ['un','arxiv','pubmed','wiki']:\n",
    "    print(bold(domain))\n",
    "    results = []\n",
    "    greed_tpw = pickle.load(open(f'/home/author97/project11/processed_results/{domain}/greed_tpw.pkl','rb'))\n",
    "    bpe_tpw = pickle.load(open(f'/home/author97/project11/processed_results/{domain}/bpe_tpw.pkl','rb'))\n",
    "    word_count = pickle.load(open(f\"/home/author97/project11/python_inputs/{domain}_counts.pkl\", \"rb\"))\n",
    "    \n",
    "    # spm unigram fits on a smaller subset\n",
    "    if domain == \"pubmed\":\n",
    "        word_count = {w: c // 30 for w, c in word_count.items()}\n",
    "        ranges = range(2000, 10001, 2000)\n",
    "    elif domain == \"wiki\":\n",
    "        word_count = {w: c // 10 for w, c in word_count.items()}\n",
    "        ranges = range(2000, 10001, 2000)\n",
    "    else:\n",
    "        ranges = range(1000, 5001, 1000)\n",
    "    word_count = {w: c for w, c in word_count.items() if c > 0}\n",
    "\n",
    "    for step in ranges:\n",
    "        total_tokens = 0\n",
    "        tok = spm.SentencePieceProcessor(model_file=f\"/home/author97/project11/python_outputs/spm_unigram/{domain}_model_{step}/model\")\n",
    "        for c, v in word_count.items():\n",
    "            total_tokens += len(tok.encode(c)) * v\n",
    "        total_words = sum(word_count.values())\n",
    "        spm_tpw = total_tokens/total_words\n",
    "        ipr_bpe = (bpe_tpw[step-1]-greed_tpw[step-1])/bpe_tpw[step-1]\n",
    "        ipr_spm = (spm_tpw-greed_tpw[step-1])/spm_tpw\n",
    "        results.append([step, greed_tpw[step-1], bpe_tpw[step-1], ipr_bpe*100, spm_tpw, ipr_spm*100])\n",
    "    results = sorted(results, key=lambda x:x[0])\n",
    "    for r in results:\n",
    "        r[0] = ul(str(r[0]))\n",
    "    header = bold(\"k\")+'\\t' + bold('GTK') +'\\t'+ bold('BPE')+'\\t'+bold('impr%')+'\\t' + bold('SPU')+'\\t'+bold('impr%')\n",
    "    table = [header.split('\\t')]\n",
    "    for r in results:\n",
    "        table.append(\"{0}\\t{1:.3f}\\t{2:.3f}\\t{3:.2f}\\t{4:.3f}\\t{5:.2f}\".format(*r).split('\\t'))\n",
    "    for i in np.array(table).T:\n",
    "        print('\\t& '.join(i))\n",
    "    print()\n",
    "    bpe_iprv.extend([r[-3] for r in results])\n",
    "    spm_iprv.extend([r[-1] for r in results])\n",
    "print(f\"mean improvement on bpe: {np.mean(bpe_iprv):.3f}\")\n",
    "print(f\"mean improvement on spm: {np.mean(spm_iprv):.3f}\")\n",
    "#paper results for main compression experiment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 337,
   "id": "4cf676b9",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "187147903 187147903 181111127\n",
      "1876232596 1876232596 1793499648\n",
      "14659210396 14659000682 13825591837\n"
     ]
    }
   ],
   "source": [
    "for domain in ['un', 'arxiv', 'wiki']:\n",
    "    counts = pickle.load(open(f'python_inputs/{domain}_counts.pkl', 'rb'))\n",
    "    total = 0\n",
    "    for k,c in counts.items():\n",
    "        total += (len(k.encode('utf-8'))-1) * c\n",
    "    mc = sum([int(x) for x in open(f\"cpp_outputs/{domain}/max_cover_count.txt\").read().strip().split('\\n')])\n",
    "    mp = sum([int(x) for x in open(f\"cpp_outputs/{domain}/merges.txt\").read().strip().split('\\n')])\n",
    "    print(total, mc, mp)\n",
    "# un and arxiv fully covered by 5k steps, while wiki is not fully covered at 10k steps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "051f3b92",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 43748 characters]",
      "text/plain": [
       "<Figure size 1000x300 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "def read_cpp_res(domain):\n",
    "    tokens = [bytes.fromhex(t.strip()) for t in open(f'cpp_outputs/{domain}/tokens.txt','r').read().strip().split('\\n')]\n",
    "    merges_per_turn = [int(x) for x in open(f'cpp_outputs/{domain}/merges.txt','r').read().strip().split('\\n')]\n",
    "    total = 0\n",
    "    totals = []\n",
    "    for m in merges_per_turn:\n",
    "        total += m\n",
    "        totals.append(total)\n",
    "    return tokens, merges_per_turn, totals\n",
    "\n",
    "def read_cpp_max_cover(domain):\n",
    "    ranks = [bytes.fromhex(t.strip()) for t in open(f'cpp_outputs/{domain}/tokens.txt','r').read().strip().split('\\n')]\n",
    "    merges_per_turn = [int(x) for x in open(f'cpp_outputs/{domain}/max_cover_count.txt','r').read().split()]\n",
    "    total = 0\n",
    "    totals = []\n",
    "    for m in merges_per_turn:\n",
    "        total += m\n",
    "        totals.append(total)\n",
    "    return ranks, merges_per_turn, totals\n",
    "\n",
    "\n",
    "suptitles = [f'UN corpus',\n",
    "            f'arXiv corpus',\n",
    "             f'wiki corpus',\n",
    "            f'PubMed corpus']\n",
    "fig, axs = plt.subplots(1,4, figsize=(10,3), layout='constrained')\n",
    "for idx, domain in enumerate(['un', 'arxiv', 'wiki', 'pubmed']):\n",
    "    _, __, mp_totals = read_cpp_res(domain)\n",
    "    _, __, mc_totals = read_cpp_max_cover(domain)\n",
    "    totals = np.array(mp_totals)/np.array(mc_totals)\n",
    "    axs[idx].scatter(range(len(totals)), totals, marker='.', s=1, color='k')\n",
    "    axs[idx].set_title(suptitles[idx])\n",
    "    if idx == 0:\n",
    "        axs[idx].set_ylabel('$d_{inst}$')\n",
    "    axs[idx].set_xlabel('$k$')\n",
    "# plt.savefig(\"images/bound_plots.png\", dpi=600)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "76faccd8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "44\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 22228 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import pickle\n",
    "import os\n",
    "\n",
    "ratios = []\n",
    "print(len(os.listdir(\"approx_test/results2/\")))\n",
    "for f in os.listdir(\"approx_test/results2/\"):\n",
    "    p = pickle.load(open(f\"approx_test/results2/{f}\",'rb'))\n",
    "    pco_agg = 0\n",
    "    mc_agg = 0\n",
    "    ratio = []\n",
    "    for pco, mc in zip(p['pco_score'], p['mc_score']):\n",
    "        pco_agg += pco\n",
    "        mc_agg += mc\n",
    "        ratio.append(pco_agg/mc_agg)\n",
    "    ratios.append(np.array(ratio))  \n",
    "ratios = np.stack(ratios)\n",
    "ratio_mean = np.mean(ratios, axis=0)\n",
    "ratio_std = np.std(ratios, axis=0)\n",
    "plt.scatter(range(len(ratio_mean)), ratio_mean, marker='.', s=1, color='k')\n",
    "plt.ylabel('$d_{inst}$')\n",
    "plt.xlabel('$k$')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "ca1409dc",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 49908 characters]",
      "text/plain": [
       "<Figure size 1000x300 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "unigram_tpw = {\n",
    "    'un':    [1.6547230689095729,1.384557981887297,1.261267932464068,1.1929431540263797,1.1482463263787883],\n",
    "    'arxiv': [1.8684303124751664,1.5508279304102872,1.4093128267563269,1.3277218451014559,1.275640565121876],\n",
    "    'pubmed':[1.698776915304708,1.464818912824655,1.3585441096816817,1.2967560243525538,1.2569127896048244],\n",
    "    'wiki':  [1.7931141945243383,1.5580684018498459,1.4442784905842898,1.3775943525017846,1.3319863170903983],\n",
    "}\n",
    "\n",
    "suptitles = [f'UN corpus',\n",
    "            f'arXiv corpus',\n",
    "            f'PubMed corpus',\n",
    "             f'wiki corpus',]\n",
    "fig, axs = plt.subplots(1,4, figsize=(10,3), layout='constrained')\n",
    "for idx, domain in enumerate(['un', 'arxiv', 'pubmed', 'wiki']):\n",
    "    x = list(range(1000,5001,1000)) if (domain == 'un' or domain =='arxiv') else list(range(2000,10001,2000))\n",
    "    word_count = pickle.load(open(f\"/home/author97/project11/python_inputs/{domain}_counts.pkl\", \"rb\"))\n",
    "    _, __, mp_totals = read_cpp_res(domain)\n",
    "    _, __, mc_totals = read_cpp_max_cover(domain)\n",
    "    mc_totals = [mc_totals[x2-1] for x2 in x]\n",
    "    mp_totals = [mp_totals[x2-1] for x2 in x]\n",
    "    \n",
    "    bpe_tpw = pickle.load(open(f'/home/author97/project11/processed_results/{domain}/bpe_tpw.pkl','rb'))\n",
    "    total = sum([len(w.encode('utf-8')) * c for w,c in word_count.items()])\n",
    "    bpe_totals = [total - (bpe_tpw[x2-1] * sum(word_count.values())) for x2 in x]\n",
    "    uni_totals = [total - tpw*sum(word_count.values()) for tpw in unigram_tpw[domain]]\n",
    "    \n",
    "    axs[idx].scatter(x, [u/x2 for u,x2 in zip(mp_totals, mc_totals)] , color=\"blue\", alpha=.75, label='GreedTok', marker='.')    \n",
    "    axs[idx].scatter(x, [u/x2 for u,x2 in zip(bpe_totals, mc_totals)] , color=\"red\", alpha=.5, label='BPE', marker='x')    \n",
    "    axs[idx].scatter(x, [u/x2 for u,x2 in zip(uni_totals, mc_totals)], color='k', marker = '^', alpha=.33, label='Unigram')\n",
    "\n",
    "    axs[idx].set_title(suptitles[idx])\n",
    "    axs[idx].set_yticks(ticks=[0.8+i*0.025 for i in range(8)])\n",
    "    axs[idx].set_xticks(ticks=x)\n",
    "    if idx == 0:\n",
    "        axs[idx].set_ylabel('$d_{inst}$')\n",
    "    axs[idx].set_xlabel('$k$')\n",
    "    if idx == 3:\n",
    "        legend = axs[idx].legend(loc = \"lower right\")\n",
    "        for leg_obj in legend.get_lines():\n",
    "            leg_obj.set_linewidth(3.0)\n",
    "#     plt.savefig(\"images/alpha.png\", dpi=900)\n",
    "plt.show()\n",
    "# Plots showing ratio to GreedWMC"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "989c6e80",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 54296 characters]",
      "text/plain": [
       "<Figure size 1000x300 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import pickle\n",
    "import os\n",
    "import matplotlib.pyplot as plt\n",
    "from pprint import pprint\n",
    "\n",
    "common_words = set([b'\\xc4\\xa0'+w.encode('utf-8') for w in pickle.load(open(\"/data/author97/wiki/vocab.pkl\",'rb')).keys()])    \n",
    "    \n",
    "suptitles = [f'UN corpus',\n",
    "            f'arXiv corpus',\n",
    "            f'PubMed corpus',\n",
    "             f'wiki corpus',]\n",
    "fig, axs = plt.subplots(1,4, figsize=(10,3), layout='constrained')\n",
    "for idx, domain in enumerate(['un', 'arxiv', 'pubmed', 'wiki']):\n",
    "    common_results = {'bpe':[],'gtk':[],'uni':[]}\n",
    "    \n",
    "    bpe_tokens = [b''.join(t) for t in pickle.load(open(f\"/home/author97/project11/python_outputs/slow_bpe/{domain}_bpe.pkl\",'rb'))['rules']][0:10000]\n",
    "\n",
    "    greed_tokens , _, __ = read_cpp_res(domain)\n",
    "    \n",
    "    start = 1000 if (domain == 'arxiv' or domain == 'un') else 2000\n",
    "    x = list(range(1000,5001,1000)) if (domain == 'un' or domain =='arxiv') else list(range(2000,10001,1000))\n",
    "    for k in x:\n",
    "        try:\n",
    "            sp = spm.SentencePieceProcessor(model_file=f'/home/author97/project11/python_outputs/spm_unigram/{domain}_model_{k}/model')\n",
    "            sp_set = []\n",
    "            for i in range(11000):\n",
    "                try:\n",
    "                    piece = sp.id_to_piece(i)\n",
    "                    if len(piece) > 1:\n",
    "                        sp_set.append(piece)\n",
    "                except:\n",
    "                    break\n",
    "            sp_set2 = set([w.strip(\"▁\").encode('utf-8') for w in sp_set if len(w)>1])\n",
    "            common_results['uni'].append(len(common_words.intersection(sp_set2))/k)\n",
    "            common_results['bpe'].append(len(common_words.intersection( bpe_tokens[:k]))/k)\n",
    "            common_results['gtk'].append(len(common_words.intersection( greed_tokens[:k]))/k)\n",
    "        except:\n",
    "            break \n",
    "            \n",
    "    x = list(range(1000,5001,1000)) if (domain == 'un' or domain =='arxiv') else list(range(2000,10001,1000))\n",
    "    \n",
    "    axs[idx].scatter(x, common_results['gtk'], color=\"blue\", alpha=.75, label='GreedTok', marker='.')    \n",
    "    axs[idx].scatter(x, common_results['bpe'], color=\"red\", alpha=.5, label='BPE', marker='x')    \n",
    "    axs[idx].scatter(x, common_results['uni'], color='k', marker = '^', alpha=.33, label='Unigram')\n",
    "    \n",
    "    x = list(range(1000,5001,1000)) if (domain == 'un' or domain =='arxiv') else list(range(2000,10001,2000))\n",
    "    axs[idx].set_title(suptitles[idx])\n",
    "    \n",
    "    axs[idx].set_yticks(ticks=[0.2+i*0.05 for i in range(8)])\n",
    "    axs[idx].set_xticks(ticks=x)\n",
    "    if idx == 0:\n",
    "        axs[idx].set_ylabel('Ratio of wiki words in token set')\n",
    "    axs[idx].set_xlabel('$k$')\n",
    "    if idx == 3:\n",
    "        legend = axs[idx].legend(loc = \"best\")\n",
    "        for leg_obj in legend.get_lines():\n",
    "            leg_obj.set_linewidth(3.0)\n",
    "#     plt.savefig(\"images/words_as_tokens.png\", dpi=900)\n",
    "plt.show()\n",
    "# Plots showing ratio of tokens that appear as \"whole\" words"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "adddf0e7",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0.754302978515625\n"
     ]
    }
   ],
   "source": [
    "import json\n",
    "bpe64 = set([w.replace('Ġ',' ').encode('utf-8') for w in json.load(open(\"/home/author97/project11/65k_bpe_vocab.json\")).keys()])\n",
    "gtk64 = set([bytes.fromhex(t.strip()) for t in open(f'/home/author97/project11/65k_pcatt_hex_vocab.txt','r').read().strip().split('\\n')])\n",
    "print(len(gtk64.intersection(bpe64))/len(gtk64))\n",
    "#common tokens for the tokens sets used in pretraining experiment"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "fe9b7a4e",
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python [conda env:tbb]",
   "language": "python",
   "name": "conda-env-tbb-py"
  },
  "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.13.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
