{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "0b675efe",
   "metadata": {},
   "source": [
    "# Tutorial 4 · Train a spiking network\n",
    "\n",
    "**What you'll learn.** How to turn a spiking network into a trainable model:\n",
    "plug a surrogate gradient into the spiking nonlinearity, run the network over\n",
    "time with `for_loop`, and take gradient-descent steps with an optimizer.\n",
    "\n",
    "**Who it's for.** Readers who have built a network (tutorials 1–3) and now want\n",
    "to *train* it. (Audience: training / brain-inspired computing.)\n",
    "\n",
    "The spike threshold is a step function, so its derivative is zero almost\n",
    "everywhere — vanilla backprop would see no gradient. The fix is a **surrogate\n",
    "gradient**: the forward pass still emits hard spikes, but the backward pass uses\n",
    "a smooth surrogate derivative. In `brainpy.state` you simply pass a\n",
    "`braintools.surrogate` function as `spk_fun=`. The rest is ordinary deep-learning\n",
    "machinery, with one twist: the per-step `for_loop` is differentiable, so\n",
    "gradients flow backward through time (BPTT) automatically.\n",
    "\n",
    "To keep this notebook self-contained we train on a small synthetic\n",
    "classification task rather than downloading a dataset. The same recipe scales to\n",
    "MNIST / Fashion-MNIST — see the gallery scripts."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1318cfb8",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:10:13.584594Z",
     "iopub.status.busy": "2026-06-17T09:10:13.584370Z",
     "iopub.status.idle": "2026-06-17T09:10:17.699765Z",
     "shell.execute_reply": "2026-06-17T09:10:17.698850Z"
    }
   },
   "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": "markdown",
   "id": "c67df2b4",
   "metadata": {},
   "source": [
    "## A feed-forward spiking classifier\n",
    "\n",
    "Three stages: an input→recurrent synapse (a linear layer feeding an exponential\n",
    "synapse), a recurrent LIF layer whose `spk_fun` is the surrogate, and a\n",
    "readout→output synapse. Calling the network once advances every stage by one\n",
    "time step."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "1e96203f",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:10:17.702099Z",
     "iopub.status.busy": "2026-06-17T09:10:17.701695Z",
     "iopub.status.idle": "2026-06-17T09:10:17.706374Z",
     "shell.execute_reply": "2026-06-17T09:10:17.705667Z"
    }
   },
   "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",
    "                n_rec, tau=5. * u.ms,\n",
    "                g_initializer=braintools.init.Constant(0. * u.mA)),\n",
    "        )\n",
    "        # the surrogate gradient lives here, on the spiking layer\n",
    "        self.r = brainpy.state.LIF(\n",
    "            n_rec, tau=20. * u.ms,\n",
    "            V_rest=0. * u.mV, V_reset=0. * u.mV, V_th=1. * u.mV,\n",
    "            spk_fun=braintools.surrogate.ReluGrad(),\n",
    "        )\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,\n",
    "            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": "90a0b42c",
   "metadata": {},
   "source": [
    "## A synthetic dataset\n",
    "\n",
    "Each sample is a `[T, B, n_in]` tensor of Poisson input spikes; the label is a\n",
    "random binary class. The network's job is to map the spike pattern to its class."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "00d9d407",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:10:17.708237Z",
     "iopub.status.busy": "2026-06-17T09:10:17.708028Z",
     "iopub.status.idle": "2026-06-17T09:10:21.094617Z",
     "shell.execute_reply": "2026-06-17T09:10:21.093696Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "inputs: (200, 256, 100)   labels: (256,)\n"
     ]
    }
   ],
   "source": [
    "with brainstate.environ.context(dt=1.0 * u.ms):\n",
    "    n_in, n_rec, n_out = 100, 4, 2\n",
    "    num_step, num_sample = 200, 256\n",
    "    freq = 5. * u.Hz\n",
    "\n",
    "    x_data = brainstate.random.rand(num_step, num_sample, n_in) < freq * brainstate.environ.get_dt()\n",
    "    x_data = x_data.astype(float)\n",
    "    y_data = u.math.asarray(brainstate.random.rand(num_sample) < 0.5, dtype=int)\n",
    "\n",
    "print('inputs:', x_data.shape, '  labels:', y_data.shape)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ad1ab58c",
   "metadata": {},
   "source": [
    "## Loss, optimizer, and a compiled train step\n",
    "\n",
    "The loss runs the network over all time steps with `for_loop`, averages the\n",
    "output over time, and applies a cross-entropy. The train step is JIT-compiled and\n",
    "takes one gradient step; the **outer epoch loop is an ordinary Python loop** —\n",
    "that is fine, because each iteration calls the compiled step. (The rule is never\n",
    "to time-step a *model* with a bare Python loop, which we don't: time-stepping\n",
    "goes through `for_loop`.)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "fd153d19",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:10:21.096905Z",
     "iopub.status.busy": "2026-06-17T09:10:21.096410Z",
     "iopub.status.idle": "2026-06-17T09:10:24.729127Z",
     "shell.execute_reply": "2026-06-17T09:10:24.728135Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "first loss: 0.6931   last loss: 0.5738\n"
     ]
    }
   ],
   "source": [
    "with brainstate.environ.context(dt=1.0 * u.ms):\n",
    "    net = SNN(n_in, n_rec, n_out)\n",
    "    optimizer = braintools.optim.Adam(lr=3e-3)\n",
    "    optimizer.register_trainable_weights(net.states(brainstate.ParamState))\n",
    "\n",
    "    def loss_fn():\n",
    "        preds = brainstate.transform.for_loop(net.update, x_data)   # [T, B, C]\n",
    "        preds = u.math.mean(preds, axis=0)                          # [B, C]\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, loss = brainstate.transform.grad(\n",
    "            loss_fn, net.states(brainstate.ParamState), return_value=True)()\n",
    "        optimizer.update(grads)\n",
    "        return loss\n",
    "\n",
    "    losses = []\n",
    "    for epoch in range(1, 201):           # outer optimization loop — OK\n",
    "        losses.append(float(train_step()))\n",
    "\n",
    "print('first loss: %.4f   last loss: %.4f' % (losses[0], losses[-1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "eaf2c15b",
   "metadata": {},
   "source": [
    "## The training curve"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "4c85b98e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-17T09:10:24.731133Z",
     "iopub.status.busy": "2026-06-17T09:10:24.730884Z",
     "iopub.status.idle": "2026-06-17T09:10:24.810775Z",
     "shell.execute_reply": "2026-06-17T09:10:24.809851Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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s2/fPgD8/f3LOLrSkZmZycmTJ/H39yc8PBxbW1urbRYfH09CQkKl2GZz587Fx8eHzp0737ZeZdtG1apVw8/Pz2q7pKens337dst2iYyMJDU1ld27d1vqrF+/HrPZbEm6KpqbCcjx48dZt24dnp6ed5xn3759aLXaApc0Kqrz589z9epVy75WGbfTTV9//TXh4eE0atTojnXV3k53+u4uynddZGQkBw4csEoabybK9erVK5UgRRn74YcfFIPBoMybN085fPiw8tJLLylubm5WvY0rskGDBimurq7KH3/8oSQmJlpe2dnZiqIoyokTJ5R3331X2bVrl3L69Gll+fLlSvXq1ZU2bdqoHPmtvfrqq8off/yhnD59WtmyZYsSFRWleHl5KZcuXVIURVFefvllpWrVqsr69euVXbt2KZGRkUpkZKTKUd+ZyWRSqlatqowaNcqqvLJso4yMDGXv3r3K3r17FUCZOnWqsnfvXsudIh988IHi5uamLF++XPnrr7+UJ554QqlWrZpy/fp1yzI6duyoNGnSRNm+fbvy559/KjVr1lSeeeYZtZp02zYZjUalS5cuSmBgoLJv3z6r4+vmnQdbt25VPvnkE2Xfvn3KyZMnlQULFije3t5Knz59KmSbMjIylNdee02Ji4tTTp8+raxbt05p2rSpUrNmTSUnJ8eyjMq0nW5KS0tTHBwclJkzZxaYvyJupzt9dyvKnb/r8vPzlbCwMOWRRx5R9u3bp6xZs0bx9vZWRo8eXSoxShJSTj7//HOlatWqil6vV1q0aKFs27ZN7ZCKDCj0NXfuXEVRFCUhIUFp06aN4uHhoRgMBqVGjRrK66+/rqSlpakb+G306tVL8ff3V/R6vRIQEKD06tVLOXHihGX69evXlVdeeUVxd3dXHBwclG7duimJiYkqRlw0a9euVQAlPj7eqryybKMNGzYUuq/17dtXUZQbt+mOGTNG8fX1VQwGg9K+ffsCbb169aryzDPPKE5OToqLi4vSv39/JSMjQ4XW3HC7Np0+ffqWx9eGDRsURVGU3bt3KxEREYqrq6tiZ2en1K1bV5k4caLVD3pFalN2drbyyCOPKN7e3oqtra0SHBysDBgwoMAfXZVpO9305ZdfKvb29kpqamqB+SvidrrTd7eiFO277syZM0qnTp0Ue3t7xcvLS3n11VeVvLy8UolR879AhRBCCCHKlfQJEUIIIYQqJAkRQgghhCokCRFCCCGEKiQJEUIIIYQqJAkRQgghhCokCRFCCCGEKiQJEUIIIYQqJAkRQgghhCokCRFC3Dc0Gg3Lli1TOwwhxP9IEiKEKBf9+vVDo9EUeHXs2FHt0IQQKrFROwAhxP2jY8eOzJ0716rMYDCoFI0QQm1yJkQIUW4MBgN+fn5WL3d3d+DGpZKZM2fSqVMn7O3tqV69OkuWLLGa/8CBAzz88MPY29vj6enJSy+9RGZmplWdb775hvr162MwGPD392fw4MFW069cuUK3bt1wcHCgZs2arFixomwbLYS4JUlChBAVxpgxY+jevTv79++nd+/ePP300xw5cgSArKwsoqOjcXd3Z+fOnSxevJh169ZZJRkzZ84kJiaGl156iQMHDrBixQpq1Khh9RnvvPMOPXv25K+//uLRRx+ld+/epKSklGs7hRD/UyrP4hVCiDvo27evotPpFEdHR6vX+++/ryjKjceOv/zyy1bzREREKIMGDVIURVFmz56tuLu7K5mZmZbpq1atUrRareUx8VWqVFHefvvtW8YAKP/3f/9neZ+ZmakAyq+//lpq7RRCFJ30CRFClJuHHnqImTNnWpV5eHhY/h0ZGWk1LTIykn379gFw5MgRGjVqhKOjo2V6q1atMJvNxMfHo9FouHjxIu3bt79tDA0bNrT829HRERcXFy5dunS3TRJClIAkIUKIcuPo6Fjg8khpsbe3L1I9W1tbq/cajQaz2VwWIQkh7kD6hAghKoxt27YVeF+3bl0A6taty/79+8nKyrJM37JlC1qtltq1a+Ps7ExISAixsbHlGrMQ4u7JmRAhRLnJzc0lKSnJqszGxgYvLy8AFi9eTLNmzXjwwQdZuHAhO3bs4Ouvvwagd+/ejBs3jr59+zJ+/HguX77MkCFDeO655/D19QVg/PjxvPzyy/j4+NCpUycyMjLYsmULQ4YMKd+GCiGKRJIQIUS5WbNmDf7+/lZltWvX5ujRo8CNO1d++OEHXnnlFfz9/fn++++pV68eAA4ODqxdu5Zhw4bRvHlzHBwc6N69O1OnTrUsq2/fvuTk5PDJJ5/w2muv4eXlxVNPPVV+DRRCFItGURRF7SCEEEKj0bB06VK6du2qdihCiHIifUKEEEIIoQpJQoQQQgihCukTIoSoEOTKsBD3HzkTIoQQQghVSBIihBBCCFVIEiKEEEIIVUgSIoQQQghVSBIihBBCCFVIEiKEEEIIVUgSIoQQQghVSBIihBBCCFX8P1Kgng6lfg9kAAAAAElFTkSuQmCC",
      "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')\n",
    "plt.ylabel('Training loss')\n",
    "plt.title('Surrogate-gradient training of a spiking classifier')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a17e14f",
   "metadata": {},
   "source": [
    "## See also\n",
    "\n",
    "- {doc}`/concepts/differentiability` — surrogate gradients and BPTT through the\n",
    "  transform loops, in depth.\n",
    "- {doc}`../howto/train-surrogate-gradients` — compare different surrogate\n",
    "  functions.\n",
    "- {doc}`../howto/train-readouts` — use a `LeakyRateReadout` head instead of\n",
    "  averaging.\n",
    "- {doc}`../howto/train-long-rollouts-checkpoint` — bound memory for long\n",
    "  rollouts with `checkpointed_for_loop`."
   ]
  }
 ],
 "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
}
