{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "b0473d7b",
   "metadata": {},
   "source": [
    "# Fitting with Gradients\n",
    "\n",
    "This is what makes brainmass different. Because every model is a differentiable JAX program,\n",
    "you can **backpropagate through the entire simulation** -- the ODE solve, the coupling, the\n",
    "forward model -- and use the gradient of a loss to update model parameters. No finite\n",
    "differences, no black-box search: the optimiser knows exactly which way to move every\n",
    "parameter.\n",
    "\n",
    "In this tutorial you will fit a model parameter to a target observation by gradient descent\n",
    "with {class}`brainmass.Fitter` (`backend='grad'`). You will see:\n",
    "\n",
    "1. a {class}`brainstate.nn.Param` marked `fit=True` to select what is trainable,\n",
    "2. a `predict` that runs a {class}`~brainmass.Simulator` and reduces it to a **scalar\n",
    "   summary**,\n",
    "3. the gradient flowing from that scalar back through the solve to the parameter,\n",
    "4. a loss curve that drops to near-zero in a few dozen evaluations.\n",
    "\n",
    ":::{important}\n",
    "For an **oscillatory** model, fit a *scalar summary* of the trajectory (amplitude, a spectral\n",
    "feature, or functional connectivity) -- **not** the raw time series with a point-by-point\n",
    "RMSE. Two limit cycles with the same shape but a different phase have a large pointwise RMSE,\n",
    "so that loss is phase-degenerate and its gradient is uninformative. We fit the settled\n",
    "**amplitude** here.\n",
    ":::"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "1f311144",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:12.056817Z",
     "iopub.status.busy": "2026-06-19T07:10:12.056624Z",
     "iopub.status.idle": "2026-06-19T07:10:17.603745Z",
     "shell.execute_reply": "2026-06-19T07:10:17.602849Z"
    }
   },
   "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 brainmass\n",
    "import brainstate\n",
    "import braintools\n",
    "import brainunit as u\n",
    "import jax.numpy as jnp\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "from brainstate.nn import Param, SoftplusT\n",
    "\n",
    "brainstate.environ.set(dt=0.1 * u.ms)\n",
    "brainstate.random.seed(0)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "53e1ac62",
   "metadata": {},
   "source": [
    "## The model and the scalar summary\n",
    "\n",
    "We use a supercritical {class}`~brainmass.HopfStep` oscillator. Above its bifurcation it\n",
    "settles onto a limit cycle whose radius grows with the bifurcation parameter `a` (for the\n",
    "normal form, amplitude scales as $\\sqrt{a}$). We give it a small non-zero initial condition so\n",
    "the trajectory spirals *out* to that limit cycle, then measure the **settled RMS amplitude**\n",
    "after discarding the transient.\n",
    "\n",
    "The summary -- a single number per simulation -- is what we will match to data."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "4441eca7",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:17.606059Z",
     "iopub.status.busy": "2026-06-19T07:10:17.605617Z",
     "iopub.status.idle": "2026-06-19T07:10:18.084582Z",
     "shell.execute_reply": "2026-06-19T07:10:18.083697Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "a = 0.25  ->  amplitude = 0.502\n",
      "a = 1.0   ->  amplitude = 1.001\n",
      "a = 2.0   ->  amplitude = 1.430\n"
     ]
    }
   ],
   "source": [
    "def make_hopf(a, trainable=False):\n",
    "    # When trainable, `a` is a bounded Param (fit=True selects it for optimisation).\n",
    "    a_param = Param(a, t=SoftplusT(0.0), fit=True) if trainable else a\n",
    "    return brainmass.HopfStep(\n",
    "        in_size=1, a=a_param, w=0.3,\n",
    "        init_x=braintools.init.Constant(0.5),   # kick off the spiral toward the limit cycle\n",
    "        init_y=braintools.init.Constant(0.0),\n",
    "    )\n",
    "\n",
    "def settled_amplitude(node):\n",
    "    '''Run the Simulator and reduce the trajectory to one scalar: the limit-cycle amplitude.'''\n",
    "    res = brainmass.Simulator(node, dt=0.1 * u.ms).run(\n",
    "        300.0 * u.ms, monitors=['x'], transient=150.0 * u.ms,\n",
    "    )\n",
    "    x = u.get_magnitude(res['x'])                # strip the mV-style unit before reducing\n",
    "    return jnp.sqrt(jnp.mean(x ** 2)) * jnp.sqrt(2.0)   # RMS -> sinusoid amplitude\n",
    "\n",
    "# The amplitude is a smooth, monotone function of `a` -- a well-conditioned target.\n",
    "for a in [0.25, 1.0, 2.0]:\n",
    "    print(f\"a = {a:<4}  ->  amplitude = {float(settled_amplitude(make_hopf(a))):.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1866190e",
   "metadata": {},
   "source": [
    "## The target\n",
    "\n",
    "In a real study the target comes from data. Here we synthesise it by running the model at a\n",
    "known \"true\" parameter `a* = 1.5` and recording its amplitude -- so we can check that the fit\n",
    "recovers it."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "d01f3969",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:18.086732Z",
     "iopub.status.busy": "2026-06-19T07:10:18.086494Z",
     "iopub.status.idle": "2026-06-19T07:10:18.188625Z",
     "shell.execute_reply": "2026-06-19T07:10:18.187931Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "target amplitude (true a* = 1.5): 1.2296\n"
     ]
    }
   ],
   "source": [
    "target_amplitude = float(settled_amplitude(make_hopf(1.5)))\n",
    "print(f\"target amplitude (true a* = 1.5): {target_amplitude:.4f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "8df871a9",
   "metadata": {},
   "source": [
    "## Fit by backprop through the solve\n",
    "\n",
    "Now the centrepiece. We start far from the truth (`a = 0.1`) and let {class}`~brainmass.Fitter`\n",
    "minimise the squared error between the simulated amplitude and the target.\n",
    "\n",
    "The `grad` backend does exactly the canonical loop: it collects the model's trainable\n",
    "`ParamState` weights, evaluates the loss, and calls\n",
    "`brainstate.transform.grad(...)` -- which differentiates **through the `Simulator.run`\n",
    "`for_loop`** -- then steps the optax optimiser. We pass a `loss_fn(model) -> (loss, aux)` so we\n",
    "own the scalar reduction; the `aux` (the amplitude) is carried along for inspection."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "55f511e1",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:18.191273Z",
     "iopub.status.busy": "2026-06-19T07:10:18.190847Z",
     "iopub.status.idle": "2026-06-19T07:10:18.837770Z",
     "shell.execute_reply": "2026-06-19T07:10:18.836952Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "trainable parameters: [('a', 'val')]\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fitted a   : 1.5471   (true a* = 1.5)\n",
      "best loss  : 3.37e-05\n",
      "loss drop  : 0.8262  ->  3.37e-05\n",
      "evaluations: 60\n"
     ]
    }
   ],
   "source": [
    "node = make_hopf(0.1, trainable=True)\n",
    "print(\"trainable parameters:\", list(node.states(brainstate.ParamState).keys()))\n",
    "\n",
    "def loss_fn(model):\n",
    "    amp = settled_amplitude(model)\n",
    "    loss = (amp - target_amplitude) ** 2\n",
    "    return loss, amp                      # aux = current amplitude\n",
    "\n",
    "fitter = brainmass.Fitter(\n",
    "    node,\n",
    "    braintools.optim.Adam(lr=0.1),        # grad backend takes a braintools.optim optimiser\n",
    "    loss_fn=loss_fn,\n",
    "    backend='grad',\n",
    ")\n",
    "result = fitter.fit(n_steps=60, verbose=False)\n",
    "\n",
    "print(f\"fitted a   : {float(result.best_params['a']):.4f}   (true a* = 1.5)\")\n",
    "print(f\"best loss  : {result.best_loss:.2e}\")\n",
    "print(f\"loss drop  : {result.history[0]:.4f}  ->  {result.best_loss:.2e}\")\n",
    "print(f\"evaluations: {len(result.history)}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0d384587",
   "metadata": {},
   "source": [
    "The fit recovers `a* ~ 1.5` and the loss collapses by several orders of magnitude. Plotting the\n",
    "full loss history shows the characteristic smooth descent of a gradient method -- each step\n",
    "moves *downhill*, because the gradient told it which way that is."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "927dc334",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:18.840045Z",
     "iopub.status.busy": "2026-06-19T07:10:18.839807Z",
     "iopub.status.idle": "2026-06-19T07:10:19.055838Z",
     "shell.execute_reply": "2026-06-19T07:10:19.054917Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 700x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, ax = plt.subplots(figsize=(7, 4))\n",
    "ax.plot(result.history, marker='.', lw=1.2)\n",
    "ax.set_yscale('log')\n",
    "ax.set_xlabel('optimisation step')\n",
    "ax.set_ylabel('loss  (amplitude squared error)')\n",
    "ax.set_title(f\"Gradient descent through the solve  (a: 0.1 -> {float(result.best_params['a']):.2f})\")\n",
    "ax.grid(alpha=0.3)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "654989d2",
   "metadata": {},
   "source": [
    "## Confirming the gradient is real\n",
    "\n",
    "To make the differentiability concrete, take the gradient of the loss with respect to `a`\n",
    "directly with {func}`brainstate.transform.grad` and compare it to a finite-difference estimate.\n",
    "We use a *plain* (untransformed) `Param` here so autodiff and finite differences live in the\n",
    "**same** space: with a transform like `SoftplusT`, autodiff returns the gradient in the\n",
    "optimiser's unconstrained space, which differs from a physical-space finite difference by the\n",
    "transform's Jacobian (correct, but not directly comparable). With no transform they agree to\n",
    "several digits -- the gradient that drove the fit above genuinely flows back through the entire\n",
    "{class}`~brainmass.Simulator` rollout."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "fa3491c5",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:19.058445Z",
     "iopub.status.busy": "2026-06-19T07:10:19.058110Z",
     "iopub.status.idle": "2026-06-19T07:10:20.327612Z",
     "shell.execute_reply": "2026-06-19T07:10:20.326782Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "autodiff d(loss)/da  = -0.36418\n",
      "finite-difference    = -0.36414\n"
     ]
    }
   ],
   "source": [
    "from brainstate.nn import Param as _Param\n",
    "\n",
    "def loss_at(a_value):\n",
    "    # Plain trainable param (no transform): unconstrained space == physical space.\n",
    "    n = brainmass.HopfStep(in_size=1, a=_Param(a_value, fit=True), w=0.3,\n",
    "                           init_x=braintools.init.Constant(0.5),\n",
    "                           init_y=braintools.init.Constant(0.0))\n",
    "    weights = n.states(brainstate.ParamState)\n",
    "    (key,) = weights.keys()                       # the single trainable weight ('a', 'val')\n",
    "    grad_fn = brainstate.transform.grad(\n",
    "        lambda: (settled_amplitude(n) - target_amplitude) ** 2,\n",
    "        weights, return_value=True,\n",
    "    )\n",
    "    grads, loss = grad_fn()\n",
    "    return float(loss), float(grads[key])\n",
    "\n",
    "a0 = 0.8\n",
    "loss0, autodiff_grad = loss_at(a0)\n",
    "eps = 1e-3\n",
    "fd_grad = (loss_at(a0 + eps)[0] - loss_at(a0 - eps)[0]) / (2 * eps)\n",
    "print(f\"autodiff d(loss)/da  = {autodiff_grad:+.5f}\")\n",
    "print(f\"finite-difference    = {fd_grad:+.5f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "aab59d13",
   "metadata": {},
   "source": [
    "## Selecting trainables and composing objectives\n",
    "\n",
    "Two pieces generalise this to richer problems.\n",
    "\n",
    "**`Param(fit=True)` selects what is trainable.** Only parameters wrapped in a\n",
    "{class}`~brainstate.nn.Param` with `fit=True` become `ParamState` weights; everything else is\n",
    "held fixed. A transform such as {class}`~brainstate.nn.SoftplusT` keeps the parameter in a\n",
    "valid range (here `a > 0`) while the optimiser works in unconstrained space.\n",
    "\n",
    "**{mod}`brainmass.objectives` composes losses.** When you fit to a *signal* rather than a\n",
    "hand-written scalar, build the loss from the objective library -- e.g. match functional\n",
    "connectivity with {func}`~brainmass.objectives.fc_corr`, or combine several terms with\n",
    "{func}`~brainmass.objectives.combine`. The example below fits a coupling gain so a 3-region\n",
    "Hopf network reproduces a target FC, using `fc_corr(as_loss=True)` (which returns `1 - corr`,\n",
    "so minimising it *maximises* the correlation)."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "6546744e",
   "metadata": {
    "execution": {
     "iopub.execute_input": "2026-06-19T07:10:20.329656Z",
     "iopub.status.busy": "2026-06-19T07:10:20.329498Z",
     "iopub.status.idle": "2026-06-19T07:10:31.759781Z",
     "shell.execute_reply": "2026-06-19T07:10:31.759006Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "fitted k  : 1.183   (true k* = 1.0)\n",
      "FC loss   : 0.158  ->  0.000\n"
     ]
    }
   ],
   "source": [
    "# A 3-region driven network whose FC depends on a trainable coupling gain k.\n",
    "W = jnp.asarray([[0.0, 0.8, 0.2],\n",
    "                 [0.8, 0.0, 0.5],\n",
    "                 [0.2, 0.5, 0.0]])\n",
    "\n",
    "def make_net(k_value, trainable=False):\n",
    "    k = Param(k_value, t=SoftplusT(0.0), fit=True) if trainable else k_value\n",
    "    n = brainmass.HopfStep(in_size=3, a=0.5, w=0.3,\n",
    "                           noise_x=brainmass.OUProcess(3, sigma=0.3, tau=10.0 * u.ms))\n",
    "    return brainmass.Network(n, conn=W, coupling='diffusive', coupled_var='x', k=k)\n",
    "\n",
    "def predict_fc(net):\n",
    "    res = brainmass.Simulator(net, dt=0.1 * u.ms).run(\n",
    "        400.0 * u.ms, monitors=lambda m: m.node.x.value, transient=100.0 * u.ms,\n",
    "    )\n",
    "    return res['output']                          # (T, 3) time series -> FC computed by objective\n",
    "\n",
    "# Target FC from a known coupling k* = 1.0.\n",
    "brainstate.random.seed(1)\n",
    "target_fc = predict_fc(make_net(1.0))\n",
    "\n",
    "fc_loss = brainmass.objectives.fc_corr(as_loss=True)   # 1 - corr(FC_pred, FC_target)\n",
    "\n",
    "brainstate.random.seed(1)\n",
    "net = make_net(0.1, trainable=True)\n",
    "fc_fitter = brainmass.Fitter(\n",
    "    net, braintools.optim.Adam(lr=0.2),\n",
    "    predict=predict_fc, objective=fc_loss, backend='grad',\n",
    ")\n",
    "fc_result = fc_fitter.fit(target=target_fc, n_steps=60)\n",
    "print(f\"fitted k  : {float(fc_result.best_params['coupling.k']):.3f}   (true k* = 1.0)\")\n",
    "print(f\"FC loss   : {fc_result.history[0]:.3f}  ->  {fc_result.best_loss:.3f}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "59bd7b0a",
   "metadata": {},
   "source": [
    "## Why gradients\n",
    "\n",
    "The fit above converged in a few dozen forward simulations, and every one of those steps was\n",
    "informed by the gradient -- the optimiser never had to guess a direction. That efficiency is\n",
    "the whole point of a *differentiable* simulator: for problems with more than a handful of\n",
    "parameters (whole-brain coupling matrices, lead-field heads, task-trained networks),\n",
    "gradient-based fitting is the only thing that scales.\n",
    "\n",
    "But gradients are not always available -- a non-differentiable objective, a discrete choice, a\n",
    "black-box metric. The next tutorial fits the *same* amplitude target with **gradient-free**\n",
    "search and contrasts the cost.\n",
    "\n",
    "## Summary\n",
    "\n",
    "- brainmass models are differentiable, so {class}`~brainmass.Fitter` with `backend='grad'`\n",
    "  **backpropagates through the `Simulator` solve**.\n",
    "- Mark trainables with `Param(..., fit=True)`; reduce the trajectory to a **scalar summary**\n",
    "  (amplitude / FC / spectrum), never a phase-sensitive pointwise RMSE.\n",
    "- The loss curve drops smoothly; autodiff and finite-difference gradients agree.\n",
    "- Compose richer losses from {mod}`brainmass.objectives`.\n",
    "\n",
    "## Next steps\n",
    "\n",
    "- {doc}`/tutorials/07_gradient_free_fitting` -- the same fit without gradients, and when to\n",
    "  prefer it.\n",
    "- {doc}`/tutorials/08_training_on_tasks` -- train a network on a cognitive task over epochs.\n",
    "- {doc}`/reference/orchestration` -- {class}`~brainmass.Fitter` / {class}`~brainmass.FitResult`\n",
    "  API."
   ]
  }
 ],
 "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
}
