{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "intro",
   "metadata": {},
   "source": [
    "# Basic Neural Network Layers\n",
    "\n",
    "BrainState provides a comprehensive set of pre-built layers for building neural networks. This tutorial covers the essential building blocks.\n",
    "\n",
    "You will learn about:\n",
    "\n",
    "- 📏 **Linear layers** - Fully connected transformations\n",
    "- 🔲 **Convolutional layers** - Spatial feature extraction (1D, 2D, 3D)\n",
    "- 🏊 **Pooling layers** - Downsampling operations\n",
    "- 💧 **Dropout layers** - Regularization techniques\n",
    "- 🔧 **Utility layers** - Flatten, reshape, and more\n",
    "\n",
    "These layers are optimized, well-tested, and ready to use in your models!"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "imports",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:45.678298Z",
     "start_time": "2025-10-10T15:36:43.413355Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:43.252191Z",
     "iopub.status.busy": "2026-05-30T16:19:43.251850Z",
     "iopub.status.idle": "2026-05-30T16:19:45.468923Z",
     "shell.execute_reply": "2026-05-30T16:19:45.467845Z"
    }
   },
   "outputs": [],
   "source": [
    "import brainstate\n",
    "from brainstate import environ\n",
    "import brainunit as u\n",
    "import jax.numpy as jnp\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "linear_layers",
   "metadata": {},
   "source": [
    "\n",
    "## 1. Linear (Fully Connected) Layers\n",
    "\n",
    "Linear layers perform the transformation: **y = Wx + b**.\n",
    "\n",
    "BrainState linear modules expect feature shapes as tuples so they can validate tensor shapes. For example, use `Linear(in_size=(10,), out_size=(5,))`.\n",
    "\n",
    "### Basic Usage\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "linear_basic",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:46.738416Z",
     "start_time": "2025-10-10T15:36:45.693664Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:45.471171Z",
     "iopub.status.busy": "2026-05-30T16:19:45.470717Z",
     "iopub.status.idle": "2026-05-30T16:19:48.896395Z",
     "shell.execute_reply": "2026-05-30T16:19:48.895491Z"
    }
   },
   "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"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Linear Layer:\n",
      "Linear(\n",
      "  in_size=(10,),\n",
      "  out_size=(5,),\n",
      "  weight=ParamState(\n",
      "    value={\n",
      "      'bias': ShapedArray(float32[5]),\n",
      "      'weight': ShapedArray(float32[10,5])\n",
      "    }\n",
      "  )\n",
      ")\n",
      "Weight shape: (10, 5)\n",
      "Bias shape: (5,)\n",
      "Input shape: (10,)\n",
      "Output shape: (5,)\n",
      "Output: [ 0.24681929  1.2860886  -1.6367221   0.29457197 -0.9486235 ]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Create a linear layer\n",
    "brainstate.random.seed(42)\n",
    "linear = brainstate.nn.Linear(in_size=(10,), out_size=(5,))\n",
    "\n",
    "print(\"Linear Layer:\")\n",
    "print(linear)\n",
    "print(f\"Weight shape: {linear.weight.value['weight'].shape}\")\n",
    "print(f\"Bias shape: {linear.weight.value['bias'].shape}\")\n",
    "\n",
    "# Forward pass\n",
    "x = brainstate.random.randn(10)\n",
    "y = linear(x)\n",
    "\n",
    "print(f\"Input shape: {x.shape}\")\n",
    "print(f\"Output shape: {y.shape}\")\n",
    "print(f\"Output: {y}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "linear_batch",
   "metadata": {},
   "source": [
    "### Batch Processing\n",
    "\n",
    "Linear layers automatically handle batched inputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "linear_batched",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:49.447570Z",
     "start_time": "2025-10-10T15:36:49.071992Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:48.898425Z",
     "iopub.status.busy": "2026-05-30T16:19:48.898022Z",
     "iopub.status.idle": "2026-05-30T16:19:50.294580Z",
     "shell.execute_reply": "2026-05-30T16:19:50.293763Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Batched input shape: (32, 10)\n",
      "Batched output shape: (32, 5)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Multi-batch input: (8, 4, 10)\n",
      "Multi-batch output: (8, 4, 5)\n"
     ]
    }
   ],
   "source": [
    "# Batched input: (batch_size, features)\n",
    "x_batch = brainstate.random.randn(32, 10)  # 32 samples, 10 features each\n",
    "y_batch = linear(x_batch)\n",
    "\n",
    "print(f\"Batched input shape: {x_batch.shape}\")\n",
    "print(f\"Batched output shape: {y_batch.shape}\")\n",
    "\n",
    "# Works with arbitrary batch dimensions\n",
    "x_multi = brainstate.random.randn(8, 4, 10)  # (batch1, batch2, features)\n",
    "y_multi = linear(x_multi)\n",
    "\n",
    "print(f\"\\nMulti-batch input: {x_multi.shape}\")\n",
    "print(f\"Multi-batch output: {y_multi.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "linear_variants",
   "metadata": {},
   "source": [
    "\n",
    "### Linear Layer Variants\n",
    "\n",
    "BrainState provides specialized linear layers:\n",
    "\n",
    "- `ScaledWSLinear` applies weight standardization for stable training.\n",
    "- `LoRA` adds low-rank adapters to an existing projection.\n",
    "- `SparseLinear` consumes a `brainunit.sparse` matrix so you can encode custom connectivity patterns.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "linear_sparse",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:49.995627Z",
     "start_time": "2025-10-10T15:36:49.454578Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:50.297220Z",
     "iopub.status.busy": "2026-05-30T16:19:50.296921Z",
     "iopub.status.idle": "2026-05-30T16:19:51.116282Z",
     "shell.execute_reply": "2026-05-30T16:19:51.115416Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Sparse Linear Layer:\n",
      "SparseLinear(\n",
      "  in_size=(100,),\n",
      "  out_size=(50,),\n",
      "  spar_mat=CSR(float32[100, 50], nse=485),\n",
      "  weight=ParamState(\n",
      "    value={\n",
      "      'weight': ShapedArray(float32[485])\n",
      "    }\n",
      "  )\n",
      ")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input: (100,) → Output: (50,)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# SparseLinear: For sparse connectivity\n",
    "brainstate.random.seed(0)\n",
    "dense_weight = brainstate.random.rand(100, 50)\n",
    "sparsity_mask = brainstate.random.rand(100, 50) < 0.1\n",
    "sparse_matrix = u.sparse.CSR.fromdense(dense_weight * sparsity_mask)\n",
    "sparse_linear = brainstate.nn.SparseLinear(sparse_matrix, in_size=(100,))\n",
    "\n",
    "print(\"Sparse Linear Layer:\")\n",
    "print(sparse_linear)\n",
    "\n",
    "x = brainstate.random.rand(100)\n",
    "y = sparse_linear(x)\n",
    "print(f\"Input: {x.shape} → Output: {y.shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv_layers",
   "metadata": {},
   "source": [
    "\n",
    "## 2. Convolutional Layers\n",
    "\n",
    "Convolutional layers extract spatial features using learnable filters. Supply the expected input shape (without the batch dimension) via `in_size` so each layer can initialize weights and validate its inputs.\n",
    "\n",
    "### Conv1d - 1D Convolution\n",
    "\n",
    "Used for sequential data (time series, audio, text):\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "conv1d",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:50.397808Z",
     "start_time": "2025-10-10T15:36:50.002634Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:51.118407Z",
     "iopub.status.busy": "2026-05-30T16:19:51.118158Z",
     "iopub.status.idle": "2026-05-30T16:19:51.996645Z",
     "shell.execute_reply": "2026-05-30T16:19:51.995857Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Conv1d Layer:\n",
      "Conv1d(\n",
      "  in_size=(100, 3),\n",
      "  out_size=(100, 16),\n",
      "  channel_first=False,\n",
      "  channels_last=True,\n",
      "  in_channels=3,\n",
      "  out_channels=16,\n",
      "  stride=(1,),\n",
      "  kernel_size=(3,),\n",
      "  lhs_dilation=(1,),\n",
      "  rhs_dilation=(1,),\n",
      "  groups=1,\n",
      "  dimension_numbers=ConvDimensionNumbers(lhs_spec=(0, 2, 1), rhs_spec=(2, 1, 0), out_spec=(0, 2, 1)),\n",
      "  padding=SAME,\n",
      "  kernel_shape=(3, 3, 16),\n",
      "  w_initializer=XavierNormal(\n",
      "    scale=1.0,\n",
      "    mode='fan_avg',\n",
      "    in_axis=-2,\n",
      "    out_axis=-1,\n",
      "    distribution='truncated_normal',\n",
      "    rng=RandomState(Array((), dtype=key<fry>) overlaying:\n",
      "    [1797259609 2579123966]),\n",
      "    unit=Unit(\"1\")\n",
      "  ),\n",
      "  weight=ParamState(\n",
      "    value={\n",
      "      'weight': ShapedArray(float32[3,3,16])\n",
      "    }\n",
      "  )\n",
      ")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input shape: (4, 100, 3)\n",
      "Output shape: (4, 100, 16)\n",
      "Kernel shape: (3, 3, 16)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Conv1d: (batch, length, in_channels) → (batch, length, out_channels)\n",
    "brainstate.random.seed(0)\n",
    "conv1d = brainstate.nn.Conv1d(\n",
    "    in_size=(100, 3),\n",
    "    out_channels=16,\n",
    "    kernel_size=3,\n",
    "    padding='SAME'\n",
    ")\n",
    "\n",
    "print(\"Conv1d Layer:\")\n",
    "print(conv1d)\n",
    "\n",
    "# Input: (batch=4, length=100, channels=3)\n",
    "x = brainstate.random.randn(4, 100, 3)\n",
    "y = conv1d(x)\n",
    "\n",
    "print(f\"Input shape: {x.shape}\")\n",
    "print(f\"Output shape: {y.shape}\")\n",
    "print(f\"Kernel shape: {conv1d.weight.value['weight'].shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv2d",
   "metadata": {},
   "source": [
    "### Conv2d - 2D Convolution\n",
    "\n",
    "The workhorse for image processing:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "conv2d_basic",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:50.831519Z",
     "start_time": "2025-10-10T15:36:50.404817Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:51.998948Z",
     "iopub.status.busy": "2026-05-30T16:19:51.998756Z",
     "iopub.status.idle": "2026-05-30T16:19:53.016203Z",
     "shell.execute_reply": "2026-05-30T16:19:53.015250Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Conv2d Layer:\n",
      "Conv2d(\n",
      "  in_size=(28, 28, 3),\n",
      "  out_size=(28, 28, 32),\n",
      "  channel_first=False,\n",
      "  channels_last=True,\n",
      "  in_channels=3,\n",
      "  out_channels=32,\n",
      "  stride=(1, 1),\n",
      "  kernel_size=(3, 3),\n",
      "  lhs_dilation=(1, 1),\n",
      "  rhs_dilation=(1, 1),\n",
      "  groups=1,\n",
      "  dimension_numbers=ConvDimensionNumbers(lhs_spec=(0, 3, 1, 2), rhs_spec=(3, 2, 0, 1), out_spec=(0, 3, 1, 2)),\n",
      "  padding=SAME,\n",
      "  kernel_shape=(3, 3, 3, 32),\n",
      "  w_initializer=XavierNormal(\n",
      "    scale=1.0,\n",
      "    mode='fan_avg',\n",
      "    in_axis=-2,\n",
      "    out_axis=-1,\n",
      "    distribution='truncated_normal',\n",
      "    rng=RandomState(Array((), dtype=key<fry>) overlaying:\n",
      "    [ 683029726 1624662641]),\n",
      "    unit=Unit(\"1\")\n",
      "  ),\n",
      "  weight=ParamState(\n",
      "    value={\n",
      "      'weight': ShapedArray(float32[3,3,3,32])\n",
      "    }\n",
      "  )\n",
      ")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input shape: (8, 28, 28, 3)\n",
      "Output shape: (8, 28, 28, 32)\n",
      "Kernel shape: (3, 3, 3, 32)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Conv2d: (batch, height, width, in_channels) → (batch, height, width, out_channels)\n",
    "conv2d = brainstate.nn.Conv2d(\n",
    "    in_size=(28, 28, 3),      # (height, width, channels)\n",
    "    out_channels=32,          # 32 feature maps\n",
    "    kernel_size=(3, 3),       # 3x3 kernel\n",
    "    stride=(1, 1),            # Stride of 1\n",
    "    padding='SAME'\n",
    ")\n",
    "\n",
    "print(\"Conv2d Layer:\")\n",
    "print(conv2d)\n",
    "\n",
    "# Input: (batch=8, height=28, width=28, channels=3)\n",
    "x_image = brainstate.random.randn(8, 28, 28, 3)\n",
    "y_image = conv2d(x_image)\n",
    "\n",
    "print(f\"Input shape: {x_image.shape}\")\n",
    "print(f\"Output shape: {y_image.shape}\")\n",
    "print(f\"Kernel shape: {conv2d.weight.value['weight'].shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv_features",
   "metadata": {},
   "source": [
    "### Visualizing Convolutional Features"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "id": "visualize_conv",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:51.500349Z",
     "start_time": "2025-10-10T15:36:50.841525Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:53.018177Z",
     "iopub.status.busy": "2026-05-30T16:19:53.017920Z",
     "iopub.status.idle": "2026-05-30T16:19:54.042294Z",
     "shell.execute_reply": "2026-05-30T16:19:54.041637Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABb4AAAExCAYAAACzsrRmAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAKDNJREFUeJzt3XuYVXW9P/DPMDPMDAMMF0EME0FQVEoUDl4SL5ShogYmHvTJIBWzQ6dTJ+3Y8aeCF46mqHkJ61R0EtGOdw/mMSu8FGpSqamZWqBgEndEhvus3x8dJrczuDY2zOXL6/U88zzMms9e38/aPPsze7/3mrVLsizLAgAAAAAAEtGupRsAAAAAAICmJPgGAAAAACApgm8AAAAAAJIi+AYAAAAAICmCbwAAAAAAkiL4BgAAAAAgKYJvAAAAAACSIvgGAAAAACApgm8AAAAAAJIi+AYAAAAAICmC72b2gx/8IEpKSmLevHkt3UpERNTW1sbkyZPj0UcfLar+0UcfjZKSkrjrrrt2bGNAk9s6fxr7uuCCC3bImnPnzo3JkyfHqlWrdsj+/x7vvj9+8YtfNPh5lmXx4Q9/OEpKSuKEE05ogQ7/6nvf+17su+++UVlZGQMGDIgbb7yxxXqBHcmMKtQWZtT06dNj7Nixsccee0RJSUlMmDChRfqA5mBGFWrtM2rhwoUxZcqUGDZsWHTt2jV22WWXOOqoo+KnP/1ps/cCzcGMKtTaZ9S6devirLPOikGDBkVNTU107NgxDjjggPjmN78ZmzZtavZ+UlbW0g3Qsmpra2PKlCkREXHUUUe1bDNAs7j00kujb9++BdsGDRq0Q9aaO3duTJkyJSZMmBBdunTZIWv8vSorK2PWrFlx+OGHF2x/7LHHYtGiRVFRUdFCnUV8+9vfjnPPPTc+/elPx7/+67/GE088EV/60peitrY2/u3f/q3F+oIdyYwq1Jpn1FVXXRVr1qyJYcOGxVtvvdVifUBzMqMKtdYZdf/998dVV10Vo0ePjvHjx8fmzZvjhz/8YRxzzDHx/e9/Pz73uc+1SF+wo5lRhVrrjFq3bl28+OKLcfzxx8eee+4Z7dq1i7lz58ZXvvKVePrpp2PWrFkt0leKBN8AO5njjjsuhg4d2tJt/F3Wrl0b1dXVTbKv448/Pu6888644YYboqzsb78WZ82aFUOGDIlly5Y1yTrba926dXHhhRfGqFGj6v/KZuLEiVFXVxeXXXZZnHPOOdG1a9cW6Q12JDOqUGudURF/fdG49Wzvjh07tlgf0JzMqEKtdUYdffTR8cYbb8Quu+xSv+3cc8+NwYMHx8UXXyz4JllmVKHWOqO6desWTz31VMG2c889N2pqauKmm26Ka6+9Nnr16tUivaXGpU5agQkTJkTHjh3jzTffjNGjR0fHjh2jR48ecd5558WWLVvq6xYsWBAlJSVxzTXXxHXXXRd9+vSJqqqqOPLII+OFF14o2OdRRx3V6BncEyZMiD333LN+fz169IiIiClTptT/GcjkyZO3q//JkydHSUlJvPLKK/GZz3wmampqokePHnHRRRdFlmWxcOHC+NSnPhWdO3eOXr16xbRp0wpuv3Hjxrj44otjyJAhUVNTE9XV1TF8+PCYM2dOg7WWL18eZ5xxRnTu3Dm6dOkS48ePj+eeey5KSkriBz/4QUHtyy+/HKecckp069YtKisrY+jQofHAAw9s17HBzuihhx6K4cOHR3V1dXTq1ClGjRoVL774YkHN888/HxMmTIh+/fpFZWVl9OrVK84888xYvnx5fc3kyZPj/PPPj4iIvn371s+YBQsW1M+z9z5uI6LBHNo6Y1566aU4/fTTo2vXrgXv2M+cOTOGDBkSVVVV0a1btxg3blwsXLiw6OM97bTTYvny5fHII4/Ub9u4cWPcddddcfrppzd6m2uuuSYOO+yw6N69e1RVVcWQIUMavQRUSUlJfPGLX4zbbrst9tlnn6isrIwhQ4bE448/ntvXnDlzYvny5fFP//RPBdsnTZoUa9eujQcffLDoY4SUmFGtY0ZFRPTp0ydKSkqKPhbYGZhRrWNG7b///gWhd0RERUVFHH/88bFo0aJYs2ZN0ccIKTGjWseM2pateV1rvHxMWyX4biW2bNkSI0eOjO7du8c111wTRx55ZEybNi2+853vNKj94Q9/GDfccENMmjQpvv71r8cLL7wQI0aMiL/85S/btWaPHj1i+vTpERExZsyYuPXWW+PWW2+Nk08++QMdwz/+4z9GXV1dXHnllXHwwQfH5ZdfHtdff30cc8wx0bt377jqqquif//+cd555xUMgrfffju++93vxlFHHRVXXXVVTJ48OZYuXRojR46MZ599tr6urq4uTjzxxLj99ttj/PjxccUVV8Rbb70V48ePb9DLiy++GIccckj8/ve/jwsuuCCmTZsW1dXVMXr06Lj33ns/0PFBKlavXh3Lli0r+Nrq1ltvjVGjRkXHjh3jqquuiosuuiheeumlOPzww2PBggX1dY888kj86U9/is997nNx4403xrhx4+KOO+6I448/PrIsi4iIk08+OU477bSIiLjuuuvqZ8zWN9y219ixY6O2tjamTp0aEydOjIiIK664Ij772c/GgAED4tprr40vf/nL8bOf/SyOOOKIop8s7LnnnnHooYfG7bffXr/toYceitWrV8e4ceMavc03v/nNOPDAA+PSSy+NqVOnRllZWYwdO7bRMPqxxx6LL3/5y/GZz3wmLr300li+fHkce+yxDd6wfK/f/va3ERENztgYMmRItGvXrv7nkBozqlBrnVGwszKjCrW1GbV48eLo0KFDdOjQ4QPdHlo7M6pQa59RGzdujGXLlsXChQvj3nvvjWuuuSb69OkT/fv3L+r2FCGjWc2YMSOLiOyZZ56p3zZ+/PgsIrJLL720oPbAAw/MhgwZUv/9/Pnzs4jIqqqqskWLFtVvf/rpp7OIyL7yla/UbzvyyCOzI488ssH648ePz/r06VP//dKlS7OIyC655JKi+p8zZ04WEdmdd95Zv+2SSy7JIiI755xz6rdt3rw523333bOSkpLsyiuvrN++cuXKrKqqKhs/fnxB7YYNGwrWWblyZbbrrrtmZ555Zv22u+++O4uI7Prrr6/ftmXLlmzEiBFZRGQzZsyo3/7xj388+8hHPpKtX7++fltdXV122GGHZQMGDCjqWCE1W+dPY19ZlmVr1qzJunTpkk2cOLHgdosXL85qamoKttfW1jbY/+23355FRPb444/Xb7v66quziMjmz59fULt1nr37cbvVe2fS1hlz2mmnFdQtWLAgKy0tza644oqC7b/73e+ysrKyBtu3dX8888wz2U033ZR16tSp/rjGjh2bHX300VmWZVmfPn2yUaNGFdz2vce/cePGbNCgQdmIESMaHEtEZPPmzavf9vrrr2eVlZXZmDFj3re/SZMmZaWlpY3+rEePHtm4cePe9/bQ1phRjd8frXVGvVd1dXXB8ztIjRnV+P3RVmZUlmXZq6++mlVWVmZnnHHGdt8WWjszqvH7o7XPqK3369avoUOHZs8//3xRt6U4zvhuRc4999yC74cPHx5/+tOfGtSNHj06evfuXf/9sGHD4uCDD44f//jHO7zH93P22WfX/7u0tDSGDh0aWZbFWWedVb+9S5cusc8++xQcV2lpabRv3z4i/npW94oVK2Lz5s0xdOjQ+M1vflNf97//+79RXl5e/+5fRES7du1i0qRJBX2sWLEifv7zn8epp54aa9asqX+Xc/ny5TFy5Mh49dVX480332zy44e24uabb45HHnmk4Cvir+/sr1q1Kk477bSCMwRKS0vj4IMPLrj8UFVVVf2/169fH8uWLYtDDjkkIqLgcduU3jsj77nnnqirq4tTTz21oN9evXrFgAEDGr1c0raceuqpsW7dupg9e3asWbMmZs+evc0/fYsoPP6VK1fG6tWrY/jw4Y0e+6GHHhpDhgyp/36PPfaIT33qU/Hwww8XXM7qvdatW1c/G9+rsrIy1q1bV8yhQZtjRjXUGmcU7KzMqIbawoyqra2NsWPHRlVVVVx55ZVF3w7aGjOqodY8o44++uh45JFH4s4774xzzz03ysvLY+3atUUfG/l8uGUrUVlZ2eBPQrp27RorV65sUDtgwIAG2/bee+/47//+7x3WXzH22GOPgu9ramqisrKywbXVampqCq4NFRHxX//1XzFt2rR4+eWXY9OmTfXb3/1pxK+//nrstttuDf4s7b1/AvLaa69FlmVx0UUXxUUXXdRor0uWLCl48wB2JsOGDWv0A09effXViIgYMWJEo7fr3Llz/b9XrFgRU6ZMiTvuuCOWLFlSULd69eom7PZv3vvp5K+++mpkWdboTIyIKC8vL3rfPXr0iE984hMxa9asqK2tjS1btsQpp5yyzfrZs2fH5ZdfHs8++2xs2LChfntj17rd1syura2NpUuXbvNDS6qqqmLjxo2N/mz9+vUFT8ggJWZUQ61xRsHOyoxqqLXPqC1btsS4cePipZdeioceeig+9KEPFXlk0PaYUQ215hm16667xq677hoREaecckpMnTo1jjnmmHj11Vc9B2sigu9WorS0tEn3V1JSUn/tpXfbkWfuNHYM2zqud/c2c+bMmDBhQowePTrOP//86NmzZ5SWlsZ//Md/xB//+Mft7qOuri4iIs4777wYOXJkozWulwQNbX3s3HrrrY3+kn33p2CfeuqpMXfu3Dj//PNj8ODB0bFjx6irq4tjjz22fj/vZ1sfhvZ+M+q9QW9dXV2UlJTEQw891Ois6dixY24f73b66afHxIkTY/HixXHcccdFly5dGq174okn4qSTToojjjgivvWtb8Vuu+0W5eXlMWPGjJg1a9Z2rfl+dtttt9iyZUssWbIkevbsWb9948aNsXz5ci/a2OmYUa1rRgGFzKjWO6MmTpwYs2fPjttuu22boR+kzoxqvTPq3U455ZS48MIL4/7774/Pf/7zO3y9nYHguw3a+k7du73yyiv1n/4a8dezxRu7TMrrr79e8P22BlJzuuuuu6Jfv35xzz33FPRzySWXFNT16dMn5syZE7W1tQVnfb/22msFdf369YuIv74D+IlPfGIHdg5p2WuvvSIiomfPnu/72Fm5cmX87Gc/iylTpsTFF19cv72x2bStGdO1a9eIaPhp1e+dUXn9ZlkWffv2jb333rvo223LmDFj4vOf/3w89dRT8aMf/WibdXfffXdUVlbGww8/HBUVFfXbZ8yY0Wj9tmZ2hw4d3vfDXwYPHhwREfPmzYvjjz++fvu8efOirq6u/uewszCjWteMAgqZUa1zRp1//vkxY8aMuP766+s/iA92RmZU65xR77X1cpY76sz6nZFrfLdB9913X8E1qn/1q1/F008/Hccdd1z9tr322itefvnlWLp0af225557Ln75y18W7GtrgFzsJ+LuCFvfvXv3WeBPP/10PPnkkwV1I0eOjE2bNsV//ud/1m+rq6uLm2++uaCuZ8+ecdRRR8W3v/3teOuttxqs9+77BPibkSNHRufOnWPq1KkFlxzaautjp7HHbETE9ddf3+A21dXVEdFwxnTu3Dl22WWXePzxxwu2f+tb3yq635NPPjlKS0tjypQpDXrJsqzBJZXydOzYMaZPnx6TJ0+OE088cZt1paWlUVJSUnDGwoIFC+K+++5rtP7JJ58suB7cwoUL4/77749PfvKT7/vXPiNGjIhu3brF9OnTC7ZPnz49OnToEKNGjSryyCANZlTrmlFAITOq9c2oq6++Oq655pr493//9/iXf/mX7ToeSI0Z1bpm1LJlyxq9SsN3v/vdiIhGL1fDB+OM7zaof//+cfjhh8cXvvCF2LBhQ1x//fXRvXv3+NrXvlZfc+aZZ8a1114bI0eOjLPOOiuWLFkSt9xyS+y///7x9ttv19dVVVXFfvvtFz/60Y9i7733jm7dusWgQYNi0KBBzXY8J5xwQtxzzz0xZsyY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",
      "text/plain": [
       "<Figure size 1500x300 with 5 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "# Create a simple image with a pattern\n",
    "def create_test_image():\n",
    "    img = jnp.zeros((28, 28, 1))\n",
    "    # Add vertical edge\n",
    "    img = img.at[5:23, 10:13, 0].set(1.0)\n",
    "    # Add horizontal edge\n",
    "    img = img.at[10:13, 5:23, 0].set(1.0)\n",
    "    return img\n",
    "\n",
    "# Create and apply conv\n",
    "brainstate.random.seed(42)\n",
    "edge_conv = brainstate.nn.Conv2d(\n",
    "    in_size=(28, 28, 1),\n",
    "    out_channels=4,\n",
    "    kernel_size=(3, 3),\n",
    "    padding='SAME'\n",
    ")\n",
    "\n",
    "test_img = create_test_image()\n",
    "features = edge_conv(test_img[None, ...])[0]  # Add/remove batch dim\n",
    "\n",
    "# Visualize\n",
    "fig, axes = plt.subplots(1, 5, figsize=(15, 3))\n",
    "\n",
    "# Original image\n",
    "axes[0].imshow(test_img[:, :, 0], cmap='gray')\n",
    "axes[0].set_title('Input Image')\n",
    "axes[0].axis('off')\n",
    "\n",
    "# Feature maps\n",
    "for i in range(4):\n",
    "    axes[i+1].imshow(np.array(features[:, :, i]), cmap='viridis')\n",
    "    axes[i+1].set_title(f'Feature Map {i}')\n",
    "    axes[i+1].axis('off')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv_params",
   "metadata": {},
   "source": [
    "\n",
    "### Convolution Parameters\n",
    "\n",
    "Understanding stride and padding (`stride=...`, `padding=...`):\n",
    "\n",
    "- `stride` controls the step size; pass it with the `stride` keyword (the API no longer accepts `strides`).\n",
    "- `'SAME'` padding preserves spatial dimensions when the stride is 1; `'VALID'` performs no padding.\n",
    "- Explicit tuples let you set different values per spatial dimension.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "conv_stride",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:51.984196Z",
     "start_time": "2025-10-10T15:36:51.510362Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:54.047058Z",
     "iopub.status.busy": "2026-05-30T16:19:54.046699Z",
     "iopub.status.idle": "2026-05-30T16:19:55.190289Z",
     "shell.execute_reply": "2026-05-30T16:19:55.189000Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input shape: (1, 28, 28, 3)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Stride 1, SAME      : (1, 28, 28, 16)\n",
      "Stride 2, SAME      : (1, 14, 14, 16)\n",
      "Stride 1, VALID     : (1, 26, 26, 16)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Different stride values\n",
    "x = brainstate.random.randn(1, 28, 28, 3)\n",
    "\n",
    "configs = [\n",
    "    {'stride': (1, 1), 'padding': 'SAME', 'name': 'Stride 1, SAME'},\n",
    "    {'stride': (2, 2), 'padding': 'SAME', 'name': 'Stride 2, SAME'},\n",
    "    {'stride': (1, 1), 'padding': 'VALID', 'name': 'Stride 1, VALID'},\n",
    "]\n",
    "\n",
    "print(f\"Input shape: {x.shape}\")\n",
    "\n",
    "for config in configs:\n",
    "    brainstate.random.seed(0)\n",
    "    conv = brainstate.nn.Conv2d(\n",
    "        in_size=(28, 28, 3),\n",
    "        out_channels=16,\n",
    "        kernel_size=(3, 3),\n",
    "        stride=config['stride'],\n",
    "        padding=config['padding']\n",
    "    )\n",
    "    y = conv(x)\n",
    "    print(f\"{config['name']:20s}: {y.shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "conv3d",
   "metadata": {},
   "source": [
    "### Conv3d - 3D Convolution\n",
    "\n",
    "For video or volumetric data:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "id": "conv3d_example",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:52.413200Z",
     "start_time": "2025-10-10T15:36:51.992203Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:55.194584Z",
     "iopub.status.busy": "2026-05-30T16:19:55.194198Z",
     "iopub.status.idle": "2026-05-30T16:19:57.038362Z",
     "shell.execute_reply": "2026-05-30T16:19:57.037530Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Video input shape: (2, 16, 64, 64, 3)\n",
      "Video output shape: (2, 16, 64, 64, 16)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Conv3d: (batch, depth, height, width, channels)\n",
    "conv3d = brainstate.nn.Conv3d(\n",
    "    in_size=(16, 64, 64, 3),\n",
    "    out_channels=16,\n",
    "    kernel_size=(3, 3, 3),\n",
    "    padding='SAME'\n",
    ")\n",
    "\n",
    "# Video input: (batch=2, frames=16, height=64, width=64, channels=3)\n",
    "x_video = brainstate.random.randn(2, 16, 64, 64, 3)\n",
    "y_video = conv3d(x_video)\n",
    "\n",
    "print(f\"Video input shape: {x_video.shape}\")\n",
    "print(f\"Video output shape: {y_video.shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pooling",
   "metadata": {},
   "source": [
    "## 3. Pooling Layers\n",
    "\n",
    "Pooling layers downsample feature maps, reducing spatial dimensions.\n",
    "\n",
    "### Max Pooling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "maxpool",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:52.588486Z",
     "start_time": "2025-10-10T15:36:52.443720Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:57.042936Z",
     "iopub.status.busy": "2026-05-30T16:19:57.042707Z",
     "iopub.status.idle": "2026-05-30T16:19:57.601157Z",
     "shell.execute_reply": "2026-05-30T16:19:57.600423Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MaxPool2d Layer:\n",
      "MaxPool2d(\n",
      "  init_value=-inf,\n",
      "  computation=<function max at 0x720d0f2e3f60>,\n",
      "  pool_dim=2,\n",
      "  return_indices=False,\n",
      "  kernel_size=(2, 2),\n",
      "  stride=(2, 2),\n",
      "  padding=VALID,\n",
      "  channel_axis=-1\n",
      ")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input shape: (4, 28, 28, 16)\n",
      "Output shape: (4, 14, 14, 16)\n",
      "Spatial reduction: 28x28 → 14x14\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# MaxPool2d: Takes maximum value in each window\n",
    "maxpool = brainstate.nn.MaxPool2d(\n",
    "    kernel_size=(2, 2),\n",
    "    stride=(2, 2)\n",
    ")\n",
    "\n",
    "print(\"MaxPool2d Layer:\")\n",
    "print(maxpool)\n",
    "\n",
    "# Input: (batch=4, height=28, width=28, channels=16)\n",
    "x = brainstate.random.randn(4, 28, 28, 16)\n",
    "y = maxpool(x)\n",
    "\n",
    "print(f\"Input shape: {x.shape}\")\n",
    "print(f\"Output shape: {y.shape}\")\n",
    "print(f\"Spatial reduction: {x.shape[1]}x{x.shape[2]} → {y.shape[1]}x{y.shape[2]}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "avgpool",
   "metadata": {},
   "source": [
    "### Average Pooling"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "avgpool_example",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:52.670528Z",
     "start_time": "2025-10-10T15:36:52.617499Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:57.603151Z",
     "iopub.status.busy": "2026-05-30T16:19:57.602929Z",
     "iopub.status.idle": "2026-05-30T16:19:57.656852Z",
     "shell.execute_reply": "2026-05-30T16:19:57.656104Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AvgPool2d Layer:\n",
      "AvgPool2d(\n",
      "  init_value=0.0,\n",
      "  computation=<function add at 0x720d0f2e3c40>,\n",
      "  pool_dim=2,\n",
      "  return_indices=False,\n",
      "  kernel_size=(2, 2),\n",
      "  stride=(2, 2),\n",
      "  padding=VALID,\n",
      "  channel_axis=-1\n",
      ")\n",
      "Output shape: (4, 14, 14, 16)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# AvgPool2d: Takes average value in each window\n",
    "avgpool = brainstate.nn.AvgPool2d(\n",
    "    kernel_size=(2, 2),\n",
    "    stride=(2, 2)\n",
    ")\n",
    "\n",
    "y_avg = avgpool(x)\n",
    "\n",
    "print(\"AvgPool2d Layer:\")\n",
    "print(avgpool)\n",
    "print(f\"Output shape: {y_avg.shape}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "adaptive_pool",
   "metadata": {},
   "source": [
    "### Adaptive Pooling\n",
    "\n",
    "Pools to a fixed output size regardless of input size:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "adaptive_pool_example",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:54.510435Z",
     "start_time": "2025-10-10T15:36:52.677534Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:19:57.659046Z",
     "iopub.status.busy": "2026-05-30T16:19:57.658760Z",
     "iopub.status.idle": "2026-05-30T16:20:00.770489Z",
     "shell.execute_reply": "2026-05-30T16:20:00.769865Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "AdaptiveAvgPool2d (output: 7x7)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input (28, 28) → Output (7, 7)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input (56, 56) → Output (7, 7)\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input (224, 224) → Output (7, 7)\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# AdaptiveAvgPool2d: Always outputs specified size\n",
    "adaptive_pool = brainstate.nn.AdaptiveAvgPool2d(target_size=(7, 7))\n",
    "\n",
    "# Works with any input size\n",
    "inputs = [\n",
    "    brainstate.random.randn(1, 28, 28, 16),\n",
    "    brainstate.random.randn(1, 56, 56, 16),\n",
    "    brainstate.random.randn(1, 224, 224, 16),\n",
    "]\n",
    "\n",
    "print(\"AdaptiveAvgPool2d (output: 7x7)\")\n",
    "for i, x in enumerate(inputs):\n",
    "    y = adaptive_pool(x)\n",
    "    print(f\"Input {x.shape[1:3]} → Output {y.shape[1:3]}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "pool_comparison",
   "metadata": {},
   "source": [
    "### Comparing Pooling Operations"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "id": "pool_visual",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:55.287654Z",
     "start_time": "2025-10-10T15:36:54.781122Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:20:00.772741Z",
     "iopub.status.busy": "2026-05-30T16:20:00.772513Z",
     "iopub.status.idle": "2026-05-30T16:20:01.533699Z",
     "shell.execute_reply": "2026-05-30T16:20:01.532789Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1200x400 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MaxPool takes the maximum value from each 2x2 window\n",
      "AvgPool takes the average value from each 2x2 window\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Create a simple test pattern\n",
    "x_test = jnp.array([\n",
    "    [1, 2, 3, 4],\n",
    "    [5, 6, 7, 8],\n",
    "    [9, 10, 11, 12],\n",
    "    [13, 14, 15, 16]\n",
    "], dtype=jnp.float32)\n",
    "\n",
    "x_test = x_test[None, :, :, None]  # Add batch and channel dims\n",
    "\n",
    "# Apply different pooling\n",
    "maxpool_2x2 = brainstate.nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))\n",
    "avgpool_2x2 = brainstate.nn.AvgPool2d(kernel_size=(2, 2), stride=(2, 2))\n",
    "\n",
    "y_max = maxpool_2x2(x_test)\n",
    "y_avg = avgpool_2x2(x_test)\n",
    "\n",
    "fig, axes = plt.subplots(1, 3, figsize=(12, 4))\n",
    "\n",
    "# Original\n",
    "im1 = axes[0].imshow(x_test[0, :, :, 0], cmap='viridis', interpolation='nearest')\n",
    "axes[0].set_title('Original (4x4)', fontsize=12, fontweight='bold')\n",
    "for i in range(4):\n",
    "    for j in range(4):\n",
    "        axes[0].text(j, i, f'{x_test[0,i,j,0]:.0f}', \n",
    "                    ha='center', va='center', color='white', fontsize=10)\n",
    "plt.colorbar(im1, ax=axes[0])\n",
    "\n",
    "# Max pooled\n",
    "im2 = axes[1].imshow(y_max[0, :, :, 0], cmap='viridis', interpolation='nearest')\n",
    "axes[1].set_title('Max Pooled (2x2)', fontsize=12, fontweight='bold')\n",
    "for i in range(2):\n",
    "    for j in range(2):\n",
    "        axes[1].text(j, i, f'{y_max[0,i,j,0]:.0f}', \n",
    "                    ha='center', va='center', color='white', fontsize=10)\n",
    "plt.colorbar(im2, ax=axes[1])\n",
    "\n",
    "# Avg pooled\n",
    "im3 = axes[2].imshow(y_avg[0, :, :, 0], cmap='viridis', interpolation='nearest')\n",
    "axes[2].set_title('Avg Pooled (2x2)', fontsize=12, fontweight='bold')\n",
    "for i in range(2):\n",
    "    for j in range(2):\n",
    "        axes[2].text(j, i, f'{y_avg[0,i,j,0]:.1f}', \n",
    "                    ha='center', va='center', color='white', fontsize=10)\n",
    "plt.colorbar(im3, ax=axes[2])\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"MaxPool takes the maximum value from each 2x2 window\")\n",
    "print(\"AvgPool takes the average value from each 2x2 window\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dropout",
   "metadata": {},
   "source": [
    "\n",
    "## 4. Dropout and Regularization\n",
    "\n",
    "Dropout randomly sets activations to zero during training for regularization. Pass `prob` to control the keep probability and enable stochastic behaviour with `environ.context(fit=True)` during training.\n",
    "\n",
    "### Standard Dropout\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "id": "dropout_basic",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:55.490420Z",
     "start_time": "2025-10-10T15:36:55.299660Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:20:01.536154Z",
     "iopub.status.busy": "2026-05-30T16:20:01.535769Z",
     "iopub.status.idle": "2026-05-30T16:20:01.792875Z",
     "shell.execute_reply": "2026-05-30T16:20:01.792245Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Dropout Layer:\n",
      "Dropout(\n",
      "  prob=0.5,\n",
      "  broadcast_dims=()\n",
      ")\n",
      "Original: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n",
      "Dropout outputs (training mode):\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "  1: [0. 0. 2. 2. 2. 2. 2. 2. 2. 2.]\n",
      "  2: [2. 2. 2. 2. 0. 2. 0. 0. 0. 2.]\n",
      "  3: [2. 2. 2. 2. 0. 0. 2. 0. 0. 0.]\n",
      "Evaluation mode: [1. 1. 1. 1. 1. 1. 1. 1. 1. 1.]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "# Dropout: Randomly zero out elements\n",
    "dropout = brainstate.nn.Dropout(prob=0.5)  # Keep 50% of activations during training\n",
    "\n",
    "print(\"Dropout Layer:\")\n",
    "print(dropout)\n",
    "\n",
    "# Create test input\n",
    "brainstate.random.seed(42)\n",
    "x = jnp.ones(10)\n",
    "\n",
    "print(\"Original:\", x)\n",
    "print(\"Dropout outputs (training mode):\")\n",
    "with environ.context(fit=True):\n",
    "    for i in range(3):\n",
    "        y = dropout(x)\n",
    "        print(f\"  {i + 1}: {y}\")\n",
    "\n",
    "with environ.context(fit=False):\n",
    "    stable = dropout(x)\n",
    "\n",
    "print(\"Evaluation mode:\", stable)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "dropout_modes",
   "metadata": {},
   "source": [
    "\n",
    "### Training vs Evaluation Mode\n",
    "\n",
    "Dropout behaves differently during training and evaluation:\n",
    "\n",
    "- Wrap forward passes in `with environ.context(fit=True):` to enable dropout.\n",
    "- Outside that context (or with `fit=False`) dropout becomes a no-op for deterministic evaluation.\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "id": "dropout_modes_example",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:56.301641Z",
     "start_time": "2025-10-10T15:36:55.500426Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:20:01.794445Z",
     "iopub.status.busy": "2026-05-30T16:20:01.794296Z",
     "iopub.status.idle": "2026-05-30T16:20:02.864894Z",
     "shell.execute_reply": "2026-05-30T16:20:02.864144Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "With dropout (training):\n",
      "  Output 1: [ 1.7709473  -1.7486901  -0.11733629  0.30774632  1.7717932 ]\n",
      "  Output 2: [-2.4300232   0.03660802  1.8875287  -1.3981102  -0.3061459 ]\n",
      "  Outputs differ: True\n",
      "Evaluation mode (dropout off):\n",
      "  Output: [-0.44592363 -0.25877538  0.6993926  -1.0398884   0.14154091]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "class NetworkWithDropout(brainstate.nn.Module):\n",
    "    def __init__(self, input_dim, hidden_dim, output_dim):\n",
    "        super().__init__()\n",
    "        self.linear1 = brainstate.nn.Linear((input_dim,), (hidden_dim,))\n",
    "        self.dropout = brainstate.nn.Dropout(prob=0.5)\n",
    "        self.linear2 = brainstate.nn.Linear((hidden_dim,), (output_dim,))\n",
    "\n",
    "    def update(self, x):\n",
    "        x = self.linear1(x)\n",
    "        x = jnp.maximum(0, x)  # ReLU\n",
    "        x = self.dropout(x)\n",
    "        x = self.linear2(x)\n",
    "        return x\n",
    "\n",
    "# Create network\n",
    "brainstate.random.seed(0)\n",
    "net = NetworkWithDropout(10, 20, 5)\n",
    "\n",
    "# Test input\n",
    "x = brainstate.random.randn(10)\n",
    "\n",
    "# Compare outputs in training mode\n",
    "with environ.context(fit=True):\n",
    "    y1 = net(x)\n",
    "    y2 = net(x)\n",
    "\n",
    "with environ.context(fit=False):\n",
    "    y_eval = net(x)\n",
    "\n",
    "print(\"With dropout (training):\")\n",
    "print(f\"  Output 1: {y1}\")\n",
    "print(f\"  Output 2: {y2}\")\n",
    "print(f\"  Outputs differ: {not jnp.allclose(y1, y2)}\")\n",
    "\n",
    "print(\"Evaluation mode (dropout off):\")\n",
    "print(f\"  Output: {y_eval}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "utility_layers",
   "metadata": {},
   "source": [
    "## 5. Utility Layers\n",
    "\n",
    "### Flatten Layer\n",
    "\n",
    "Flattens multi-dimensional inputs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "id": "flatten",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:56.619822Z",
     "start_time": "2025-10-10T15:36:56.403019Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:20:02.867458Z",
     "iopub.status.busy": "2026-05-30T16:20:02.867196Z",
     "iopub.status.idle": "2026-05-30T16:20:03.243475Z",
     "shell.execute_reply": "2026-05-30T16:20:03.242803Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Before flatten: (4, 7, 7, 64)\n",
      "After flatten: (4, 3136)\n",
      "Flattened: 3136 = 3136 features per sample\n"
     ]
    }
   ],
   "source": [
    "# Flatten: Reshape to 1D\n",
    "flatten = brainstate.nn.Flatten(start_axis=1)\n",
    "\n",
    "# Example: After convolution, flatten before fully connected\n",
    "x_conv = brainstate.random.randn(4, 7, 7, 64)  # (batch, H, W, C)\n",
    "x_flat = flatten(x_conv)\n",
    "\n",
    "print(f\"Before flatten: {x_conv.shape}\")\n",
    "print(f\"After flatten: {x_flat.shape}\")\n",
    "print(f\"Flattened: {7 * 7 * 64} = {x_flat.shape[1]} features per sample\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "complete_cnn",
   "metadata": {},
   "source": [
    "## 6. Building a Complete CNN\n",
    "\n",
    "Let's combine everything into a complete convolutional neural network:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "id": "complete_cnn_example",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:37:39.668439Z",
     "start_time": "2025-10-10T15:37:39.560344Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:20:03.245675Z",
     "iopub.status.busy": "2026-05-30T16:20:03.245441Z",
     "iopub.status.idle": "2026-05-30T16:20:06.339360Z",
     "shell.execute_reply": "2026-05-30T16:20:06.338561Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Simple CNN Architecture:\n",
      "SimpleCNN(\n",
      "  conv1=Conv2d(\n",
      "    in_size=(32, 32, 3),\n",
      "    out_size=(32, 32, 32),\n",
      "    channel_first=False,\n",
      "    channels_last=True,\n",
      "    in_channels=3,\n",
      "    out_channels=32,\n",
      "    stride=(1, 1),\n",
      "    kernel_size=(3, 3),\n",
      "    lhs_dilation=(1, 1),\n",
      "    rhs_dilation=(1, 1),\n",
      "    groups=1,\n",
      "    dimension_numbers=ConvDimensionNumbers(lhs_spec=(0, 3, 1, 2), rhs_spec=(3, 2, 0, 1), out_spec=(0, 3, 1, 2)),\n",
      "    padding=SAME,\n",
      "    kernel_shape=(3, 3, 3, 32),\n",
      "    w_initializer=XavierNormal(\n",
      "      scale=1.0,\n",
      "      mode='fan_avg',\n",
      "      in_axis=-2,\n",
      "      out_axis=-1,\n",
      "      distribution='truncated_normal',\n",
      "      rng=RandomState(Array((), dtype=key<fry>) overlaying:\n",
      "      [ 827075891 2691092366]),\n",
      "      unit=Unit(\"1\")\n",
      "    ),\n",
      "    weight=ParamState(\n",
      "      value={\n",
      "        'weight': ShapedArray(float32[3,3,3,32])\n",
      "      }\n",
      "    )\n",
      "  ),\n",
      "  pool1=MaxPool2d(\n",
      "    init_value=-inf,\n",
      "    computation=<function max at 0x720d0f2e3f60>,\n",
      "    pool_dim=2,\n",
      "    return_indices=False,\n",
      "    kernel_size=(2, 2),\n",
      "    stride=(2, 2),\n",
      "    padding=VALID,\n",
      "    channel_axis=-1\n",
      "  ),\n",
      "  conv2=Conv2d(\n",
      "    in_size=(16, 16, 32),\n",
      "    out_size=(16, 16, 64),\n",
      "    channel_first=False,\n",
      "    channels_last=True,\n",
      "    in_channels=32,\n",
      "    out_channels=64,\n",
      "    stride=(1, 1),\n",
      "    kernel_size=(3, 3),\n",
      "    lhs_dilation=(1, 1),\n",
      "    rhs_dilation=(1, 1),\n",
      "    groups=1,\n",
      "    dimension_numbers=ConvDimensionNumbers(lhs_spec=(0, 3, 1, 2), rhs_spec=(3, 2, 0, 1), out_spec=(0, 3, 1, 2)),\n",
      "    padding=SAME,\n",
      "    kernel_shape=(3, 3, 32, 64),\n",
      "    w_initializer=XavierNormal(\n",
      "      scale=1.0,\n",
      "      mode='fan_avg',\n",
      "      in_axis=-2,\n",
      "      out_axis=-1,\n",
      "      distribution='truncated_normal',\n",
      "      rng=RandomState(Array((), dtype=key<fry>) overlaying:\n",
      "      [ 827075891 2691092366]),\n",
      "      unit=Unit(\"1\")\n",
      "    ),\n",
      "    weight=ParamState(\n",
      "      value={\n",
      "        'weight': ShapedArray(float32[3,3,32,64])\n",
      "      }\n",
      "    )\n",
      "  ),\n",
      "  pool2=MaxPool2d(\n",
      "    init_value=-inf,\n",
      "    computation=<function max at 0x720d0f2e3f60>,\n",
      "    pool_dim=2,\n",
      "    return_indices=False,\n",
      "    kernel_size=(2, 2),\n",
      "    stride=(2, 2),\n",
      "    padding=VALID,\n",
      "    channel_axis=-1\n",
      "  ),\n",
      "  conv3=Conv2d(\n",
      "    in_size=(8, 8, 64),\n",
      "    out_size=(8, 8, 128),\n",
      "    channel_first=False,\n",
      "    channels_last=True,\n",
      "    in_channels=64,\n",
      "    out_channels=128,\n",
      "    stride=(1, 1),\n",
      "    kernel_size=(3, 3),\n",
      "    lhs_dilation=(1, 1),\n",
      "    rhs_dilation=(1, 1),\n",
      "    groups=1,\n",
      "    dimension_numbers=ConvDimensionNumbers(lhs_spec=(0, 3, 1, 2), rhs_spec=(3, 2, 0, 1), out_spec=(0, 3, 1, 2)),\n",
      "    padding=SAME,\n",
      "    kernel_shape=(3, 3, 64, 128),\n",
      "    w_initializer=XavierNormal(\n",
      "      scale=1.0,\n",
      "      mode='fan_avg',\n",
      "      in_axis=-2,\n",
      "      out_axis=-1,\n",
      "      distribution='truncated_normal',\n",
      "      rng=RandomState(Array((), dtype=key<fry>) overlaying:\n",
      "      [ 827075891 2691092366]),\n",
      "      unit=Unit(\"1\")\n",
      "    ),\n",
      "    weight=ParamState(\n",
      "      value={\n",
      "        'weight': ShapedArray(float32[3,3,64,128])\n",
      "      }\n",
      "    )\n",
      "  ),\n",
      "  pool3=MaxPool2d(\n",
      "    in_size=(8, 8, 128),\n",
      "    out_size=(4, 4, 128),\n",
      "    init_value=-inf,\n",
      "    computation=<function max at 0x720d0f2e3f60>,\n",
      "    pool_dim=2,\n",
      "    return_indices=False,\n",
      "    kernel_size=(2, 2),\n",
      "    stride=(2, 2),\n",
      "    padding=VALID,\n",
      "    channel_axis=-1\n",
      "  ),\n",
      "  flatten=Flatten(\n",
      "    in_size=(4, 4, 128),\n",
      "    out_size=(2048,),\n",
      "    start_axis=0,\n",
      "    end_axis=-1\n",
      "  ),\n",
      "  fc1=Linear(\n",
      "    in_size=(2048,),\n",
      "    out_size=(256,),\n",
      "    weight=ParamState(\n",
      "      value={\n",
      "        'bias': ShapedArray(float32[256]),\n",
      "        'weight': ShapedArray(float32[2048,256])\n",
      "      }\n",
      "    )\n",
      "  ),\n",
      "  dropout=Dropout(\n",
      "    prob=0.5,\n",
      "    broadcast_dims=()\n",
      "  ),\n",
      "  fc2=Linear(\n",
      "    in_size=(256,),\n",
      "    out_size=(10,),\n",
      "    weight=ParamState(\n",
      "      value={\n",
      "        'bias': ShapedArray(float32[10]),\n",
      "        'weight': ShapedArray(float32[256,10])\n",
      "      }\n",
      "    )\n",
      "  )\n",
      ")\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Input shape: (8, 32, 32, 3)\n",
      "Output shape: (8, 10)\n",
      "Logits for first image: [ 0.5667763   0.50765955  0.52835906 -0.2162368   0.5647521   0.07235157\n",
      "  0.7832138  -0.7866171  -0.6562585   0.10693425]\n"
     ]
    }
   ],
   "source": [
    "\n",
    "class SimpleCNN(brainstate.nn.Module):\n",
    "    'Simple CNN for image classification.'\n",
    "\n",
    "    def __init__(self, num_classes=10):\n",
    "        super().__init__()\n",
    "\n",
    "        # Conv block 1\n",
    "        self.conv1 = brainstate.nn.Conv2d((32, 32, 3), out_channels=32, kernel_size=(3, 3), padding='SAME')\n",
    "        self.pool1 = brainstate.nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))\n",
    "\n",
    "        # Conv block 2\n",
    "        self.conv2 = brainstate.nn.Conv2d((16, 16, 32), out_channels=64, kernel_size=(3, 3), padding='SAME')\n",
    "        self.pool2 = brainstate.nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2))\n",
    "\n",
    "        # Conv block 3\n",
    "        self.conv3 = brainstate.nn.Conv2d((8, 8, 64), out_channels=128, kernel_size=(3, 3), padding='SAME')\n",
    "        self.pool3 = brainstate.nn.MaxPool2d(kernel_size=(2, 2), stride=(2, 2), in_size=self.conv3.out_size)\n",
    "\n",
    "        # Flatten and classify\n",
    "        self.flatten = brainstate.nn.Flatten(in_size=self.pool3.out_size)\n",
    "        self.fc1 = brainstate.nn.Linear(4 * 4 * 128, (256,))  # Assuming 32x32 input\n",
    "        self.dropout = brainstate.nn.Dropout(prob=0.5)\n",
    "        self.fc2 = brainstate.nn.Linear((256,), (num_classes,))\n",
    "\n",
    "    def update(self, x):\n",
    "        # Conv block 1\n",
    "        x = self.conv1(x)\n",
    "        x = jnp.maximum(0, x)  # ReLU\n",
    "        x = self.pool1(x)\n",
    "\n",
    "        # Conv block 2\n",
    "        x = self.conv2(x)\n",
    "        x = jnp.maximum(0, x)\n",
    "        x = self.pool2(x)\n",
    "\n",
    "        # Conv block 3\n",
    "        x = self.conv3(x)\n",
    "        x = jnp.maximum(0, x)\n",
    "        x = self.pool3(x)\n",
    "\n",
    "        # Classifier\n",
    "        x = self.flatten(x)\n",
    "        x = self.fc1(x)\n",
    "        x = jnp.maximum(0, x)\n",
    "        x = self.dropout(x)\n",
    "        x = self.fc2(x)\n",
    "\n",
    "        return x\n",
    "\n",
    "# Create CNN\n",
    "brainstate.random.seed(42)\n",
    "cnn = SimpleCNN(num_classes=10)\n",
    "\n",
    "print(\"Simple CNN Architecture:\")\n",
    "print(cnn)\n",
    "\n",
    "# Test with batch of images\n",
    "batch_size = 8\n",
    "images = brainstate.random.randn(batch_size, 32, 32, 3)  # CIFAR-10 size\n",
    "with brainstate.environ.context(fit=True):\n",
    "    logits = cnn(images)\n",
    "\n",
    "print(f\"Input shape: {images.shape}\")\n",
    "print(f\"Output shape: {logits.shape}\")\n",
    "print(f\"Logits for first image: {logits[0]}\")\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "feature_viz",
   "metadata": {},
   "source": [
    "### Visualizing CNN Feature Maps"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "id": "feature_visualization",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-10T15:36:59.428533Z",
     "start_time": "2025-10-10T15:36:58.928483Z"
    },
    "execution": {
     "iopub.execute_input": "2026-05-30T16:20:06.341449Z",
     "iopub.status.busy": "2026-05-30T16:20:06.341164Z",
     "iopub.status.idle": "2026-05-30T16:20:06.851697Z",
     "shell.execute_reply": "2026-05-30T16:20:06.850978Z"
    }
   },
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers). Got range [-1.3665657..2.0698853].\n"
     ]
    },
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABhoAAAGZCAYAAAB/rOtPAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAV45JREFUeJzt3Xmc3ePZ+PHrZCb7LotEkEgkgpAIkYi9pbSopaqKorTVRZWWUqW0RbW1PLS0RdFaqna11lpUYos9lpBEECS27JKYme/vj/4ybR601/k2xySe9/v1el5PMz5zn3vOnPneZ3LNmVSKoigCAAAAAACghFYtvQEAAAAAAGDFZdAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtAAAAAAAACUZtBA1bbaaquoVCoxYMCAlt5KDBgwICqVShx//PH/sT3++OOjUqlEpVKJF198seZ7A6D2Fi5cGKeddlqMHj06unTpEh06dIghQ4bEQQcdFFOmTGmRPf3ud7+LzTbbLDp27Nh87jz77LMtshcA/mF5Oy/mzp0bhx56aGy44YbRs2fPaN++fQwZMiSOPfbYmDt37ke+HwD+YXk7LyIiDjjggBg8eHB06tQpOnbsGIMGDYpDDjkk3n777RbZD3yY+pbeAABAGe+880588pOfjEcffTQiIjp37hyDBg2Kl156Kc4555zYZJNNYuDAgR/5vm6++eZ49NFHo1evXjFt2rSP/PYBWNryeF689dZbccYZZ0Tbtm1j6NChMX369Hj++efjhBNOiAkTJsRNN930ke4HgOXzvIiIuO6666Jr164xdOjQeOONN2LKlCnxq1/9KiZNmhS33HLLR74f+DBe0cAyseRVDvvuu28cd9xx0bdv3+jevXvss88+S/1EzpJXIBx11FFx8MEHx0orrRRdu3aNb37zm7Fo0aLmbslPgF544YXvu439998/XnzxxahUKs1/gfPjH/+4+X2q8a+vcrj55ptjyJAh0bFjx9h7771j/vz5ccIJJ0SvXr2ib9++cdxxxy31vvvtt18MHjw4OnfuHG3atIn+/fvHIYccEnPmzGluiqKIY489Nnr27BndunWLgw8+OI4++ugP3OvFF18co0aNig4dOkTnzp1j++23j8cee6yqjwfg/5KDDz64+ZuAI444It5+++148sknY/bs2XH33XfHWmutFRERf/nLX2KzzTaLTp06Rbt27WKDDTaI3//+90utteS6fOqpp8Y+++wTnTt3jn79+sUJJ5zQ3AwdOjQqlUocdthhzW+bN29edOjQISqVSvz2t7+NiIizzz475syZk3q1HQC1tzyeF+3atYtf/vKX8cYbb8Rjjz0WL7/8cowZMyYi/jGwfuedd2p9twDwvyyP50VExPTp02PKlCnx8MMPx7Rp02KzzTaLiIj77ruvpvcHVK2AKm255ZZFRBT9+/d/39tat25ddO7cuVhjjTWKiCgiojj66KObu/79+xcRUbRt27bo0aPHUt1hhx3W3C152wUXXPC+29hvv/2KV199tRg9enTRpk2bIiKKfv36FaNHjy5Gjx79ofs+7rjjmtedOnXq+97WqVOnYq211mr+89prr120b9++GDhwYPPbbrnllub1unbtWvTo0aMYPnz4Us3uu+/e3Jx55pnNb+/Xr1/Ru3fvomPHjs1vW+LnP/9589uGDBlSrLLKKkVEFB07diyefvrpMp8mgI+1WbNmFfX19UVEFMOHDy+ampo+sLvooouar68rr7xy8zkUEcUJJ5zQ3C15W+vWrYu+ffsWPXv2bH7brbfeWhRFUZx00knN1/PGxsaiKIrikksuaT7X3nnnnaVu+4ILLmhe45lnnqnNHQHAv7UinBdLfPvb3y4iomjVqlUxZ86cZXtHAPBvLe/nxTHHHFNsvPHGS93e9ttvX7s7BErwigaWqXbt2sUzzzwTL7zwQmy44YYREXHHHXe8r1t99dVj6tSpMWXKlPjiF78YERFnnXVWzJ49O3U7ffv2jfvvvz/69u0bERFf+cpX4v7774/777+/9N5/+9vfxrPPPhubbrppREQ888wzcdttt8Xzzz8f/fv3j4iIu+66q7m/++67480334zHHnssJk+eHD/84Q8jIuLaa6+NhQsXRkTEL37xi4iI2GSTTZo/3lVXXXWp212wYEH8+Mc/joh/vDLjueeei2nTpsVGG20U8+fPj5NOOqn0xwTwcTVp0qRoaGiIiIjNN9/8Q1/RtuTaPHr06Jg2bVpMnTo1dt1114iIOPHEE2PBggVL9RtttFG8+OKL8cwzz0Tr1q0j4p/n2Je+9KVo1apVTJ8+Pf7+979HRMSf//zniIjYeeedo1u3bsv2gwTgv7ainBczZ86Mq666KiIi9txzz+jcufN/82EDUKXl/bx4/vnn48EHH2z+zR7bbLNNXH755cviQ4dlxqCBZeoTn/hE9OvXL1q1ahVDhw6NiIgZM2a8r9txxx2bnzzvueeeERGxePHimDRp0ke32f9lp512ioho/keuu3fvHptuumm0atWqedDwrx/L7bffHsOGDYv27dtHpVKJE088MSIiGhoa4o033og5c+bEK6+8EhERu+66a7Ru3To6duwYO+yww1K3O3HixOaD6LjjjotKpRKtW7eOhx9+OCLivxqeAHxcFUXR/L8/7JuAmTNnxksvvRQREbvttlu0bds2KpVK87nz7rvvxsSJE5d6nz322CPatGkTPXv2jN69e0fEP6/9q666anziE5+IiIjLLrssZs+eHX/9618jImL//fdfdh8cAMvMinBeTJ48OTbbbLN49dVXY9NNN23+VRkAfHSW9/Pisssui8WLF8ejjz4aw4YNi9tvvz2+9a1v/ZcfNSxbBg0sU/86ba2v/8e/Nf6vF+tqNTY2Nv/v7KsdyurSpUtE/HPfS/4c8c9DZsnHcskll8Thhx8eEydOjO7du8fGG2+81D8I9K/7rsbaa68do0ePXur/hgwZUmotgI+ztdZaq/l6/fe///2/Omv+1X86x/bbb7+IiLjqqqviqquuikWLFkXfvn3jU5/61DK5fQCWreX9vBg/fnyMGTMmnn/++dhpp53i1ltv9WoGgBawvJ8XERGtW7eOESNGxFe/+tWIiLjooota9Ad24X8zaKBF3HjjjTFv3ryIiOaXerVp06b5L9WXTHmXXDCfffbZePLJJ9+3TocOHSIiYv78+TXf879a8iqDzp07x9SpU+OBBx543yHQpUuXWG211SIi4oYbboiGhoZYsGBB3HjjjUt16667brRv3z4iIrbffvsYP35886+B+s1vftP8sjwA/qlr166xxx57RETEo48+GkcffXTzS50j/vGqsxdeeCFWX331iIi4+uqrY9GiRVEURVx22WUREdG+fftYd911q7rd3XbbLTp37hwzZ85svj7vs88+UVdXtyw+LACWseX5vLjyyivjE5/4RLz55pvx7W9/O6699trm728A+Ggtr+fFQw89FH/729+a+8WLF8ftt9/e/OeP+u/D4N8xaKBFTJ8+PdZYY40YNGhQXHLJJRER8Y1vfCO6du0aERGf/OQnIyLi1FNPja233jo22WSTD5wmL/n1TGeeeWaMGjUqvvzlL38k+19//fUjImLu3LkxcODAGDhw4Af+brwjjjgiIiLuueeeGDhwYKyxxhrx8ssvL9V06NAhjj322IiIOP3002PVVVeNESNGRI8ePWLkyJFx66231vijAVgx/epXv4oRI0ZERMTJJ58cPXr0iOHDh8dKK60U2267bUyaNKn519o98MAD0b9//1hjjTXimmuuiYh//H7Vav9Cp0OHDrH77rtHRMTrr78eEf/8KaQljjzyyFhzzTXjyCOPbH7bdtttF2uuuWaceeaZpT5WAMpbHs+LV199NfbYY49YuHBhtGnTJh588MEYO3ZsjBkzJsaMGROPPPLIf/thA1Cl5fG8mDhxYmy99dax0korxYgRI6Jv375x/fXXR0TEiBEjYvjw4f/VxwzLkkEDLeI73/lO7LPPPvHOO+9E586d46CDDoqTTz65+b+fdtppscMOO0T79u1j8uTJcfTRR8dmm232vnVOOOGEGDNmTLRq1SoefvjhD3zVQy0ceOCB8d3vfjd69uwZc+fOja222ip+8pOfvK87+OCD45hjjomVVlopZs+eHbvuumsceOCBEfGPfzh7iR/84Afxhz/8IUaNGhXvvPNOvPDCC9G7d+/4+te/HrvttttH8jEBrGhWWmmlGD9+fJxyyikxatSoaGpqiueeey66d+8eX/nKV2KLLbaIffbZJ6677rrYdNNNY+7cufH666/HiBEj4rzzziv9irF/feK/0UYbve+nlmbMmBGTJ0+OmTNnNr/tpZdeismTJ8fbb79d7oMFoLTl8bxYvHhx8w9SLV68OB544IGl/m/OnDn/3QcNQNWWx/Ni2LBhsf3220e7du3i6aefjgULFsTaa68dhx9+eNx5553RqpW/2mX5USmW1S8dg4QBAwbEtGnT4rjjjovjjz++pbdTc7Nnz45FixY1/yqohQsXxqhRo+Kpp56KMWPGxPjx41t4hwAAAAAA/536lt4AfJxNnTo1xowZExtvvHF069YtJkyYEK+++mrU1dXFj3/845beHgAAAADAf83ra6CGevXqFVtssUU888wzcfPNN8eiRYviM5/5TNx1113v+8ejAQAAAABWRH51EgAAAAAAUJpXNAAAAAAAAKUZNLDMvPXWW9GlS5fo0qVLzJ49u6W3s9zbb7/9olKpxDnnnNPSWwGoGWfDstfY2BgDBw6M+vr6ePbZZ1t6OwDLjDNj2XNmAP8XOD+qc9ddd0WlUolNN920pbfCx4xBA8vML3/5y5g7d24ceOCB0bVr13j88cdjm222iT59+kSbNm2iR48eMXr06Dj//POXer9TTz01ttpqq+jbt2+0bds2+vfvH/vtt19MmTKl9F5WhNv+3ve+FxERJ5xwQrz33nulbw9geeZsWPa3XVdXF4cddlg0NjbG8ccfX3pPAMub/31mLFy4MPbdd98YOnRotGrVKiqVSowZM+YD33f+/PlxzDHHxJAhQ6Jt27bRvXv3GDt2bDz44IOl9tKSt73EeeedF6NGjYqOHTtGp06dYtiwYXHBBRd8YLvHHntEpVKJSqUSe+65Z/PbnRnA/wX/+/yIiLjiiiti0003jZ49e0a7du1itdVWi/333z9eeuml0rczefLk2GeffWK11VaLtm3bRs+ePWPLLbeM6667rvSaEydOjF133TX69evXfB0/6qij3tcdddRRsckmm0Tv3r2jXbt2MXDgwPj2t78dM2fOXKp75JFHYpdddolVVlkl2rZtGyuvvHJ8+tOfjnvvvbe52XrrrWPkyJExbty4uOWWW0rvHd6ngGVg0aJFRY8ePYqIKB5++OGiKIrimmuuKTp16lSsvfbaxciRI4vOnTsXEVFERPGnP/2p+X379+9fVCqVYujQocUaa6zR3PTp06eYPXv2Urfzu9/9rliwYMFSb2toaCjOOuusoqGhofltK8JtF0VRDBs2rIiI4uqrr67m7gZYITgbanfbM2bMKOrq6or6+vpixowZ6c8JwPLqg86Md955p4iIol+/fkXXrl2LiChGjx79vvd99913i1GjRhURUbRq1apYa621ivXWW6/o3LlzcdFFFy3VZq/bLXnbRVEUBx98cPMZsPrqqxcjRowoevfuXRx44IHv28P555/f3EZE8YUvfGGp/+7MAD7OPuj8uPPOO4tKpdL8PHr99dcvWrVqVUREMXLkyKXef/z48cX999//vnVvuOGG4vnnn2/+c1NTU/Pz87Zt2xYbbLBB0a1btyIiikqlUjz22GPN7fz584tzzjnnfWvOnDmzuOSSS5Z62zXXXNP8/H/JdfzII4983/tGRFFXV1cMGzasWGWVVZrbYcOGFY2NjUVR/OPsWrKnTp06FRtssEHRoUOH5j3PnDmzeb1TTjmliIjis5/9bOZuhhSDBpaJG264oYiIom/fvs1vW7RoUdHU1NT85xdeeKH5QnjwwQc3v/2EE04opk2b1vznQw89tLn717+A/+tf/1pERLHtttsWCxcuLIriHxf6fffdt4iIpS7iK8JtF0VRHHXUUUVEFJ///Of/7f0LsCJyNtTutouiKMaMGVNERHHWWWcVACu6DzozGhoaildffbUoiqLYcsstP/Qv+3/2s581v++zzz671PvPnz+/+c/VXLdb8rbHjRvXPLj439f+OXPmLPXnF154oejUqVOxySabFKuuuuoHDhqKwpkBfHx90Pnx05/+tPk59Ouvv14URdF8ve3Ro0dzt3jx4qJ///5Ft27digkTJjS//aabbiratGlTjB07tvltL7/8cvOaJ598clEU/xhoLHnb9ddf39z+4Ac/KCKiOPbYY5vf9tZbbxXDhw8vWrVqVTzzzDPNb581a1bzDxT9u0HDD3/4w+ZBQUNDQ/G5z32uuX/kkUeKoiiKe++9t/ltl112WVEUSw+jn3zyyeb1nn322SIiivr6+mLWrFnp+xv+HYMGloklf2H+vyehixYtKkaPHl2MHDmy6NKly/sueB/kqquuau5uvPHGpf7b9773vSIiih133LFYtGhR8bWvfa35yfT//imgFeG2r7766uYJO8DHjbOhtrd9yCGHFBFR7Lnnnh/6/gArig87M5b4d3/ZP2LEiCIiik033bQYNWpU0aFDh2Lo0KHFmWee2fxTnktUc91uqdteMmRebbXVih133LHo0qVLsdpqqxUHH3zwUq9ue++994rRo0cXXbp0KaZMmVL079//QwcNzgzg4+qDzo+77rrrA1/R0Lt37+Kqq65a6v3HjRtXdOrUqejRo0fxxBNPFLfffnvRrl27om/fvku9oqGhoaFYc8013/eKhvr6+uKAAw5Y6jo+f/78YosttigiojjppJOKWbNmFRtttFEREcWvf/3rD/1Y/t2g4X879dRTm/uJEycWRVEUb7/9dtG9e/fmVzSMHDmy6NChQ9G+ffvi6KOPXur9m5qaml+xd8stt/zH24MMgwaWiSWT1EMOOWSpt7/77rvNF74lk9IzzjjjQ9dpaGgotttuuyIiioEDBzb/tM+/WvIy4gEDBhQRUey6667Fe++9975uRbjtCRMmNDfz5s370L0BrIicDbW97SXfXGy00UYfugbAiuLDzowl/t1f9rdv37752tqzZ8+lfvXcL3/5y/f12et2S932pz/96eY12rdvX6y77rrNf2G24447NnfHHHNMERHFxRdfXBRF8W8HDc4M4OPqw86PK6+8sujYseNSz7/HjBmz1K84WuLuu+8uOnToUPTq1avo0KFD0bt37+Lpp59+X/fyyy8XG2644VJr9urV6wOHB3Pnzm1+NdmSa/4pp5zybz+W7KBh3rx5xfDhw5sH3f/qqaeeKgYOHLjUHvv3719cfvnl71tnvfXW+4/DD6iGQQPLxDbbbFNERPHDH/7wA//7nDlzigsvvLCoq6sr2rZt+76fyiyKf1wod9ppp+aJ85KJ7P/W1NRUrL/++kVEFCuvvPL7fs/pinTbzz//fPOFf/r06f92LYAVjbOhtrd97rnnFhFRDB48+N/eHsCK4D+dGf/uL/tbt25dRESx0korFbNmzSqampqa1+vfv//7+mqv2x/1bW+77bbN3yPce++9RVEUxU9+8pPmt02dOrV46KGHirq6umKfffZpfr9/N2hwZgAfVx90fjzxxBNF7969i9atWxf33HNPMXv27GL33Xdvfm79Qdfef32FwAf9O5qNjY3FDjvsUERE8Z3vfKeYN29eccUVVzS/zzXXXPO+95k+fXrRtm3b973i4sNkBg0zZ84sRo8eXUREMXTo0OZf81cU//geYskrJ0455ZRi3rx5zR9XpVJp/hVLS2y66aZFRBQnnnjif9wbZLQKWAa6dOkSERHz5s37wP/euXPn2G+//WL99dePRYsWxQknnLDUf3/99ddjyy23jOuvvz6GDBkS9913X6yzzjofuNYxxxwTTzzxRPTu3TtmzJgRBx10UDQ1NX3o3pbn254zZ07z/15yHwJ8XDgbanvbS84Q5wfwcfCfzox/p1+/fhERMWTIkOjatWtUKpXYaKONIiLipZdeet81udrr9kd920vWjIgYNWpURERsvPHGzW978cUX46mnnorGxsa48soro1OnTtGpU6d46aWXIiLiqquuik6dOsXs2bOb38eZAXxcfdD5cfLJJ8fMmTNj7bXXjs033zy6dOkSe+21V0T847n2xIkTl1rj4Ycfjp/85CfRtWvXaNOmTRxyyCExderUpZo77rgjbrzxxoiI2G+//aJjx46x++67N9/+7bffvlS/cOHC2HfffWPRokXRu3fvuP766+Pcc8/9rz7W5557LsaMGRMPPPBAjBkzJu69997o27dv83+/9NJL4+GHH46IiAMOOCA6duwYX/7ylyMioiiKuOOOO5Zaz9nAsmbQwDIxePDgiIiYNm1a89suueSSmD59evOfJ02aFC+88EJERMyfP7/57RMnTowxY8bEhAkTYvPNN4/x48fHwIEDP/B2fvzjH8dJJ50UW2+9dUyePDm++MUvxkUXXRRf+9rXoiiKFeq2//X+6tOnT3Tq1OkDbxdgReVsqO1tL7lfl9zPACuyDzozsrbZZpuI+Me1dc6cOVEURUyYMCEiIgYNGhStWv3z297sdbslb3vJmhHR/BdGS/5/pVKJNddcs/m/L1y4MObPnx/z589vXqOhoWGpP0c4M4CPrw86P5YMWqdNmxZvvvlmRPzzOhoR0bFjx+b//dhjj8WnPvWpKIoibr311rjiiitixowZsfXWWzcPcP91zX9da9KkSTF37tz3rblo0aLYZZdd4o477ogjjzwyJk6cGOuss04cdNBBceGFF5b6OO+5554YO3ZsTJkyJXbfffe46667omfPnks1H7THD/u4i6Jo/vicDSwzLfI6Cj52brjhhiLiH/9g2RJbbrllUalUiv79+xfDhg0r6uvrm18GduqppzZ3Q4YMaX77iBEjitGjRzf/37nnntvc3Xrrrc2/f27Jv2fw3nvvNf8+vn9tV4TbLop//qNFn//85//bTwHAcsfZULvbLoqi+Xe+nnXWWaU/RwDLiw86M4qiKAYNGlQMGjSoaNeuXRHxj3+Ac8nbXnnllaIoimLKlClFt27dmn9X9r/+buo//vGPzWtVc91uydtevHhx86++6NChQzFs2LDmf6PhgAMO+ND78N/96iRnBvBx9UHnx/nnn998Le7WrVuxzjrrNP954403bu4WL15c9O/fv+jYsWPx97//vfntV1xxRVFXV1eMHTu2+W1vvvlm8z+03KpVq2LYsGHN/wZE69ati0cffbS5Pfroo4uIKA499NDmt7322mvFWmutVdTV1RXPPPNM89vvv//+5rPlX/c8aNCgYsstt2zu2rRp0/wrkDbeeOOlvk+44YYbiqIoimeeeaa5a9OmTbHeeus1/+qmrl27LvVrlp599tki4h//btysWbP+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",
      "text/plain": [
       "<Figure size 1600x400 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Feature map shapes:\n",
      "  Layer 1: (1, 32, 32, 32)\n",
      "  Layer 2: (1, 16, 16, 64)\n",
      "  Layer 3: (1, 8, 8, 128)\n"
     ]
    }
   ],
   "source": [
    "# Get intermediate features\n",
    "def get_conv_features(model, x):\n",
    "    \"\"\"Extract features from each conv layer.\"\"\"\n",
    "    features = []\n",
    "    \n",
    "    # Conv 1\n",
    "    x = model.conv1(x)\n",
    "    x = jnp.maximum(0, x)\n",
    "    features.append(x)\n",
    "    x = model.pool1(x)\n",
    "    \n",
    "    # Conv 2\n",
    "    x = model.conv2(x)\n",
    "    x = jnp.maximum(0, x)\n",
    "    features.append(x)\n",
    "    x = model.pool2(x)\n",
    "    \n",
    "    # Conv 3\n",
    "    x = model.conv3(x)\n",
    "    x = jnp.maximum(0, x)\n",
    "    features.append(x)\n",
    "    \n",
    "    return features\n",
    "\n",
    "# Extract features\n",
    "single_image = images[0:1]  # Take first image\n",
    "features = get_conv_features(cnn, single_image)\n",
    "\n",
    "# Visualize\n",
    "fig, axes = plt.subplots(1, 4, figsize=(16, 4))\n",
    "\n",
    "# Original image\n",
    "axes[0].imshow((np.array(single_image[0]) + 1) / 2)  # Normalize to [0,1]\n",
    "axes[0].set_title('Input Image\\n(32×32×3)', fontsize=10, fontweight='bold')\n",
    "axes[0].axis('off')\n",
    "\n",
    "# Feature maps\n",
    "layer_names = ['Conv1\\n(32×32×32)', 'Conv2\\n(16×16×64)', 'Conv3\\n(8×8×128)']\n",
    "for i, (feat, name) in enumerate(zip(features, layer_names)):\n",
    "    # Show first feature map from each layer\n",
    "    feat_map = np.array(feat[0, :, :, 0])\n",
    "    axes[i+1].imshow(feat_map, cmap='viridis')\n",
    "    axes[i+1].set_title(name, fontsize=10, fontweight='bold')\n",
    "    axes[i+1].axis('off')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "print(\"Feature map shapes:\")\n",
    "for i, feat in enumerate(features):\n",
    "    print(f\"  Layer {i+1}: {feat.shape}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "summary",
   "metadata": {},
   "source": [
    "\n",
    "## Summary\n",
    "\n",
    "In this tutorial, you learned about:\n",
    "\n",
    "✅ **Linear layers** - Fully connected transformations  \n",
    "✅ **Convolutional layers** - 1D, 2D, 3D spatial feature extraction  \n",
    "✅ **Pooling layers** - Max, average, and adaptive pooling  \n",
    "✅ **Dropout** - Regularization through random masking  \n",
    "✅ **Utility layers** - Flatten and reshape operations  \n",
    "✅ **Complete CNN** - Building end-to-end architectures  \n",
    "\n",
    "### Key Takeaways\n",
    "\n",
    "| Layer Type | Use Case | Key Parameters |\n",
    "|------------|----------|----------------|\n",
    "| **Linear** | Fully connected | `in_size`, `out_size` |\n",
    "| **Conv2d** | Image features | `in_size`, `out_channels`, `kernel_size`, `stride`, `padding` |\n",
    "| **MaxPool2d** | Downsampling | `kernel_size`, `stride` |\n",
    "| **Dropout** | Regularization | `prob`, `broadcast_dims` |\n",
    "| **Flatten** | Shape transformation | `start_axis`, `end_axis` |\n",
    "\n",
    "### Best Practices\n",
    "\n",
    "1. 🎯 **Use SAME padding** to preserve spatial dimensions\n",
    "2. 🧮 **Provide the correct `in_size`** so layers can validate incoming tensors\n",
    "3. 💧 **Add dropout** after dense layers for regularization\n",
    "4. 🏊 **Pool after activation** - standard practice in CNNs\n",
    "5. 🔍 **Visualize features** - helps debug and understand the network\n",
    "\n",
    "### Next Steps\n",
    "\n",
    "Continue with:\n",
    "- **Activations & Normalization** - Improve training stability\n",
    "- **Recurrent Networks** - Handle sequential data\n",
    "- **Training** - Put it all together with optimization\n"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Ecosystem-py",
   "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
}
