{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "ee3d0b64",
   "metadata": {},
   "source": [
    "# How to train through long rollouts without exhausting memory\n",
    "\n",
    "**Task.** Backpropagate through many time steps (BPTT) without the activation\n",
    "memory growing linearly with the sequence length, using gradient checkpointing.\n",
    "\n",
    "**Audience.** Training. Assumes {doc}`../tutorials/04-train-an-snn`.\n",
    "\n",
    "Backprop through time stores every step's activations for the backward pass, so\n",
    "memory grows with the rollout length `T`. For long sequences this becomes the\n",
    "binding constraint. `brainstate.transform.checkpointed_for_loop` (and\n",
    "`checkpointed_scan`) rematerialize activations on the backward pass instead of\n",
    "storing them all, trading extra recomputation for bounded memory. The `base`\n",
    "argument tunes the checkpoint granularity (roughly, memory scales with\n",
    "`base + T/base`).\n",
    "\n",
    "Crucially, it is a **drop-in replacement** for `for_loop` inside the loss: same\n",
    "signature, same result — only the memory/compute profile differs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "dffda3b5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:08:37.689322Z",
     "iopub.status.busy": "2026-06-17T09:08:37.689099Z",
     "iopub.status.idle": "2026-06-17T09:08:41.752348Z",
     "shell.execute_reply": "2026-06-17T09:08:41.751304Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "An NVIDIA GPU may be present on this machine, but a CUDA-enabled jaxlib is not installed. Falling back to cpu.\n"
     ]
    }
   ],
   "source": [
    "import brainpy\n",
    "import brainstate\n",
    "import braintools\n",
    "import brainunit as u\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "04c6e889",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:08:41.755949Z",
     "iopub.status.busy": "2026-06-17T09:08:41.755334Z",
     "iopub.status.idle": "2026-06-17T09:08:41.761109Z",
     "shell.execute_reply": "2026-06-17T09:08:41.760252Z"
    }
   },
   "outputs": [],
   "source": [
    "class SNN(brainstate.nn.Module):\n",
    "    def __init__(self, n_in, n_rec, n_out):\n",
    "        super().__init__()\n",
    "        self.i2r = brainstate.nn.Sequential(\n",
    "            brainstate.nn.Linear(\n",
    "                n_in, n_rec,\n",
    "                w_init=braintools.init.KaimingNormal(unit=u.mA),\n",
    "                b_init=braintools.init.ZeroInit(unit=u.mA)),\n",
    "            brainpy.state.Expon(n_rec, tau=5. * u.ms,\n",
    "                                g_initializer=braintools.init.Constant(0. * u.mA)))\n",
    "        self.r = brainpy.state.LIF(\n",
    "            n_rec, tau=20. * u.ms, V_rest=0. * u.mV, V_reset=0. * u.mV,\n",
    "            V_th=1. * u.mV, spk_fun=braintools.surrogate.ReluGrad())\n",
    "        self.r2o = brainstate.nn.Linear(\n",
    "            n_rec, n_out, w_init=braintools.init.KaimingNormal())\n",
    "        self.o = brainpy.state.Expon(\n",
    "            n_out, tau=10. * u.ms, g_initializer=braintools.init.Constant(0.))\n",
    "\n",
    "    def update(self, spike):\n",
    "        return self.o(self.r2o(self.r(self.i2r(spike))))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "af77e7a1",
   "metadata": {},
   "source": [
    "## Swap `for_loop` for `checkpointed_for_loop`\n",
    "\n",
    "The only change versus an ordinary BPTT loss is the loop function. We expose a\n",
    "`use_checkpoint` flag to make the difference explicit, and a `base` to control\n",
    "the checkpoint spacing."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "9df871a4",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:08:41.763041Z",
     "iopub.status.busy": "2026-06-17T09:08:41.762746Z",
     "iopub.status.idle": "2026-06-17T09:08:41.767194Z",
     "shell.execute_reply": "2026-06-17T09:08:41.766374Z"
    }
   },
   "outputs": [],
   "source": [
    "def make_train_step(net, x_data, y_data, num_sample, use_checkpoint, base=16):\n",
    "    optimizer = braintools.optim.Adam(lr=2e-3)\n",
    "    optimizer.register_trainable_weights(net.states(brainstate.ParamState))\n",
    "\n",
    "    def loss_fn():\n",
    "        if use_checkpoint:\n",
    "            preds = brainstate.transform.checkpointed_for_loop(\n",
    "                net.update, x_data, base=base)\n",
    "        else:\n",
    "            preds = brainstate.transform.for_loop(net.update, x_data)\n",
    "        preds = u.math.mean(preds, axis=0)\n",
    "        return braintools.metric.softmax_cross_entropy_with_integer_labels(\n",
    "            preds, y_data).mean()\n",
    "\n",
    "    @brainstate.transform.jit\n",
    "    def train_step():\n",
    "        brainstate.nn.init_all_states(net, batch_size=num_sample)\n",
    "        grads, l = brainstate.transform.grad(\n",
    "            loss_fn, net.states(brainstate.ParamState), return_value=True)()\n",
    "        optimizer.update(grads)\n",
    "        return l\n",
    "\n",
    "    return train_step"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "cc0b3f31",
   "metadata": {},
   "source": [
    "## Train over a long sequence\n",
    "\n",
    "We use a long rollout (`T = 400`) so checkpointing matters. The training curve\n",
    "is the same whether or not we checkpoint — only the peak memory differs."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "b027d755",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:08:41.769106Z",
     "iopub.status.busy": "2026-06-17T09:08:41.768880Z",
     "iopub.status.idle": "2026-06-17T09:08:51.345649Z",
     "shell.execute_reply": "2026-06-17T09:08:51.344975Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "checkpointed training: first 0.6931  last 0.6510\n"
     ]
    }
   ],
   "source": [
    "with brainstate.environ.context(dt=1.0 * u.ms):\n",
    "    n_in, n_rec, n_out = 80, 8, 2\n",
    "    num_step, num_sample = 400, 64\n",
    "    x_data = (brainstate.random.rand(num_step, num_sample, n_in)\n",
    "              < 5. * u.Hz * brainstate.environ.get_dt()).astype(float)\n",
    "    y_data = u.math.asarray(brainstate.random.rand(num_sample) < 0.5, dtype=int)\n",
    "\n",
    "    net = SNN(n_in, n_rec, n_out)\n",
    "    step = make_train_step(net, x_data, y_data, num_sample,\n",
    "                           use_checkpoint=True, base=16)\n",
    "    losses = [float(step()) for _ in range(120)]\n",
    "\n",
    "print('checkpointed training: first %.4f  last %.4f' % (losses[0], losses[-1]))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "6396bb69",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:08:51.347606Z",
     "iopub.status.busy": "2026-06-17T09:08:51.347237Z",
     "iopub.status.idle": "2026-06-17T09:08:51.682116Z",
     "shell.execute_reply": "2026-06-17T09:08:51.681388Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAiUAAAE8CAYAAAAfXMElAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAXRVJREFUeJzt3XdcU9f7B/BPEpKwt0wRcFDcWlCKo2oduEddVVtxtFrFXVu1/qzaOtpaRx11fevosNpibdVaravWVRfOCoqKigMQkCU7Ob8/kKuRoKBAInzer+ZVc++59z73hCRPzj3nXJkQQoCIiIjIwOSGDoCIiIgIYFJCRERERoJJCRERERkFJiVERERkFJiUEBERkVFgUkJERERGgUkJERERGQUmJURERGQUmJQQERGRUWBSQkRFsm7dOshkMly/fl1a1rJlS7Rs2dJgMZWU69evQyaTYd26dUUu+9VXX5V+YKXIy8sLgwYNkp7//fffkMlk+Pvvvw0W0/Mw1OuRX1+hoaFlelygdN53xXkPlCYmJY/J/9B9/OHk5IRWrVrhzz//LFD+8XJyuRxubm5o166d9KaeMWNGgf3pe7Rs2bLI5UhX27ZtIZPJMGrUKL3rv/32W9SsWROmpqaoUaMGlixZorfc7du30adPH9ja2sLa2hrdunXDtWvXSizOHTt2YMaMGSW2v4rEUHXH16x0zJkzB7/99puhw6iwNmzYgEWLFhk6jEKZGDoAY/Tpp5/C29sbQgjExsZi3bp16NixI7Zt24bOnTvrlG3bti0GDhwIIQSioqLwzTff4I033sAff/yBN998E9WrV5fKpqWlYcSIEejRowfefPNNaXlCQgLefffdZ5ZzdnYuxbN++fz66684evRooetXrlyJ999/Hz179sSECRNw8OBBjBkzBunp6Zg0aZJULi0tDa1atUJycjI+/vhjKJVKLFy4EC1atMCZM2fg4ODwwrHu2LEDy5Yt45fccyiLuvP09ERGRgaUSmWZHteYvP7668jIyIBKpSrV48yZMwe9evVC9+7dS/U45dlff/313Ntu2LABFy5cwLhx43SW63sPGAKTEj06dOgAf39/6fnQoUPh7OyMn376qUBS4uPjg7ffflt63qNHD9SrVw+LFi3Crl27UK9ePWldfHw8RowYgXr16uls86SiljNWmZmZUKlUkMtLryEuMzMTH3zwASZNmoRPPvmkwPqMjAxMnToVnTp1kppX33vvPWi1Wnz22WcYNmwY7OzsAADffPMNIiMjcfz4cTRq1AhA3t9AnTp1MH/+fMyZM6fUzsOQcnNzodVqS/1L6GUgk8lgampq6DCeSQiBzMxMmJmZlfi+5XL5S1EHhFJ5zxrLe4CXb4rA1tYWZmZmMDF5dg5Xt25dODo6Iioqqgwi03X69Gl06NAB1tbWsLS0ROvWrfHvv/9K60+ePAmZTIb169cX2HbXrl2QyWTYvn27tOz27dsYMmQInJ2doVarUbt2baxZs0Znu/zrqhs3bsT//d//wd3dHebm5khJSSk0zq+++gpNmjSBg4MDzMzM4OfnV+zrsl9++SW0Wi0mTpyod/3+/fuRkJCAkSNH6iwPCQnBgwcP8Mcff0jLQkND0ahRIykhAQBfX1+0bt0aP//88zNjycnJwcyZM1GjRg2YmprCwcEBzZo1w+7duwEAgwYNwrJlywDoXvLLp9VqsWjRItSuXRumpqZwdnbG8OHDcf/+fZ3jeHl5oXPnzvjrr7/QoEEDmJqaolatWvj111+fGePj190XLVqEatWqQa1W4+LFiwCAffv2oXnz5rCwsICtrS26deuG8PDwZ+5Xn7i4OCmRNzU1Rf369Qv8zRXWf+HJ69rPqrsnTZgwAQ4ODnj85uejR4+GTCbD4sWLpWWxsbGQyWRYvnz5Cx131apVUl02atQIJ06cKFIdnTt3Di1atICZmRkqV66MWbNmYe3atQX67OS/5rt27YK/vz/MzMywcuVKAMDatWvxxhtvwMnJCWq1GrVq1ZLO53FCCMyaNQuVK1eGubk5WrVqhf/++69AucJek2PHjqF9+/awsbGBubk5WrRogcOHD+uUyb9UfeXKFQwaNAi2trawsbHB4MGDkZ6eLpWTyWR48OAB1q9fL9Xp4/1a9MnMzMSMGTPg4+MDU1NTuLq64s0338TVq1cLlC3K6xEREYFevXrB3t4epqam8Pf3x9atWwuUS0pKwvjx4+Hl5QW1Wo3KlStj4MCBiI+PLzTWrKwsdO7cGTY2Njhy5IhO3URERKBPnz6wtraGg4MDxo4di8zMTJ3tc3Nz8dlnn0nn4OXlhY8//hhZWVk65Z7sU5L/2v3888+YPXs2KleuDFNTU7Ru3RpXrlzR2e6PP/7AjRs3pPr38vICoL9PyaBBg2BpaYnbt2+je/fusLS0RKVKlTBx4kRoNBqdmBISEvDOO+/A2toatra2CA4OxtmzZ4vdT4UtJXokJycjPj4eQgjExcVhyZIlSEtLK1Krxf3793H//n2dyzZl4b///kPz5s1hbW2Njz76CEqlEitXrkTLli1x4MABBAQEwN/fH1WrVsXPP/+M4OBgne03bdoEOzs7BAUFAcj70H7ttdek/hqVKlXCn3/+iaFDhyIlJaVA099nn30GlUqFiRMnIisr66mZ/Ndff42uXbtiwIAByM7OxsaNG9G7d29s374dnTp1eua53rx5E59//jnWrFlT6C/G06dPA4BOixcA+Pn5QS6X4/Tp03j77beh1Wpx7tw5DBkypMA+GjdujL/++gupqamwsrIqNJ4ZM2Zg7ty5ePfdd9G4cWOkpKTg5MmTCAsLQ9u2bTF8+HDcuXMHu3fvxvfff19g++HDh2PdunUYPHgwxowZg6ioKCxduhSnT5/G4cOHdZpTIyMj0bdvX7z//vsIDg7G2rVr0bt3b+zcuRNt27Z9Zt2tXbsWmZmZGDZsGNRqNezt7bFnzx506NABVatWxYwZM5CRkYElS5agadOmCAsLkz60iiIjIwMtW7bElStXMGrUKHh7e+OXX37BoEGDkJSUhLFjxxZ5X/l187S6e1Lz5s2xcOFC/Pfff6hTpw4A4ODBg5DL5dLlu/xlQN4li+c97oYNG5Camorhw4dDJpPhyy+/xJtvvolr1649tQn89u3baNWqFWQyGaZMmQILCwv873//g1qt1lv+0qVL6NevH4YPH4733nsPr7zyCgBg+fLlqF27Nrp27QoTExNs27YNI0eOhFarRUhIiLT9J598glmzZqFjx47o2LEjwsLC0K5dO2RnZz+jNvOS1Q4dOsDPzw/Tp0+HXC6XkqGDBw+icePGOuX79OkDb29vzJ07F2FhYfjf//4HJycnfPHFFwCA77//XnqfDBs2DABQrVq1Qo+v0WjQuXNn7N27F2+99RbGjh2L1NRU7N69GxcuXNDZtiivx3///YemTZvC3d0dkydPhoWFBX7++Wd0794dmzdvRo8ePQDkXdJt3rw5wsPDMWTIELz66quIj4/H1q1bcevWLTg6OhaINSMjA926dcPJkyexZ88enR85+XXj5eWFuXPn4t9//8XixYtx//59fPfdd1KZd999F+vXr0ev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",
      "text/plain": [
       "<Figure size 600x300 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(6, 3))\n",
    "plt.plot(np.asarray(losses))\n",
    "plt.xlabel('Epoch'); plt.ylabel('Training loss')\n",
    "plt.title('BPTT over a 400-step rollout with gradient checkpointing')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "02a3ffd2",
   "metadata": {},
   "source": [
    "## When to reach for this\n",
    "\n",
    "- Use plain `for_loop`/`scan` by default.\n",
    "- Switch to `checkpointed_for_loop`/`checkpointed_scan` only when reverse-mode\n",
    "  gradients through a long simulation would otherwise exhaust memory.\n",
    "- Tune `base` to trade recomputation against peak memory.\n",
    "\n",
    "## See also\n",
    "\n",
    "- {doc}`/concepts/state-paradigm` — the transform primitives and when to use\n",
    "  each.\n",
    "- {doc}`/concepts/differentiability` — BPTT through the transform loops.\n",
    "- {doc}`train-surrogate-gradients` — the surrogate on the hidden layer."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.13.11"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
