{
 "cells": [
  {
   "cell_type": "markdown",
   "id": "intro_header",
   "metadata": {},
   "source": [
    "# Variational Autoencoder (VAE) on MNIST\n",
    "\n",
    "This tutorial demonstrates how to build and train a **Variational Autoencoder (VAE)** using BrainState. VAEs are powerful generative models that learn to encode data into a latent space and decode it back, enabling both reconstruction and generation of new samples.\n",
    "\n",
    "## Learning Objectives\n",
    "\n",
    "By the end of this tutorial, you will:\n",
    "- Understand the theory behind Variational Autoencoders\n",
    "- Build encoder and decoder networks in BrainState\n",
    "- Implement the VAE loss function (reconstruction + KL divergence)\n",
    "- Train a VAE on MNIST dataset\n",
    "- Generate new handwritten digits\n",
    "- Visualize the latent space\n",
    "\n",
    "## What is a Variational Autoencoder?\n",
    "\n",
    "A VAE consists of two main components:\n",
    "\n",
    "1. **Encoder**: Maps input data `x` to latent representation `z`\n",
    "   - Outputs mean `μ` and standard deviation `σ` of latent distribution\n",
    "   - Uses **reparameterization trick**: `z = μ + σ * ε`, where `ε ~ N(0,1)`\n",
    "\n",
    "2. **Decoder**: Reconstructs input from latent code `z`\n",
    "   - Maps `z` back to data space\n",
    "   - Outputs reconstruction `x̂`\n",
    "\n",
    "**Loss Function**:\n",
    "```\n",
    "Loss = Reconstruction Loss + β * KL Divergence\n",
    "```\n",
    "- **Reconstruction Loss**: How well we reconstruct the input\n",
    "- **KL Divergence**: Regularizes latent space to be close to N(0,1)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "setup",
   "metadata": {},
   "source": [
    "## Setup and Imports"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "id": "imports",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:10:33.792797Z",
     "start_time": "2025-10-11T10:10:29.081832Z"
    }
   },
   "outputs": [],
   "source": [
    "import typing as tp\n",
    "\n",
    "import jax\n",
    "import jax.numpy as jnp\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "import optax  # For loss functions\n",
    "import os\n",
    "os.environ['HF_ENDPOINT'] = 'https://mirrors.tuna.tsinghua.edu.cn/huggingface'\n",
    "from datasets import load_dataset\n",
    "\n",
    "import brainstate\n",
    "import braintools\n",
    "\n",
    "# Set random seed for reproducibility\n",
    "np.random.seed(42)\n",
    "brainstate.random.seed(42)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "config",
   "metadata": {},
   "source": [
    "## Configuration and Dataset\n",
    "\n",
    "### Hyperparameters"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "id": "hyperparams",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:10:33.802443Z",
     "start_time": "2025-10-11T10:10:33.796709Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Configuration:\n",
      "  Latent dimension: 32\n",
      "  Hidden size: 256\n",
      "  Batch size: 64\n",
      "  Epochs: 20\n"
     ]
    }
   ],
   "source": [
    "# Model configuration\n",
    "latent_size = 32          # Dimension of latent space\n",
    "hidden_size = 256         # Hidden layer size\n",
    "image_shape = (28, 28)    # MNIST image shape\n",
    "\n",
    "# Training configuration\n",
    "batch_size = 64\n",
    "steps_per_epoch = 200\n",
    "epochs = 20\n",
    "learning_rate = 1e-3\n",
    "kl_weight = 0.1           # β parameter for KL divergence\n",
    "\n",
    "print(f\"Configuration:\")\n",
    "print(f\"  Latent dimension: {latent_size}\")\n",
    "print(f\"  Hidden size: {hidden_size}\")\n",
    "print(f\"  Batch size: {batch_size}\")\n",
    "print(f\"  Epochs: {epochs}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "data_loading",
   "metadata": {},
   "source": [
    "### Load and Preprocess MNIST Dataset"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "id": "load_data",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:10:59.022119Z",
     "start_time": "2025-10-11T10:10:33.803955Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Loading MNIST dataset...\n"
     ]
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "ba0c69d3f3a04285b68082bc271f2a51",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "README.md: 0.00B [00:00, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "04c6e85366824a4da17e42709804ca2b",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "mnist/train-00000-of-00001.parquet:   0%|          | 0.00/15.6M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "cc2d3c7c6ff2445fb5c48d6f70c85fbf",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "mnist/test-00000-of-00001.parquet:   0%|          | 0.00/2.60M [00:00<?, ?B/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "c0598ba4da7443f78a6493788e07ff97",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Generating train split:   0%|          | 0/60000 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "application/vnd.jupyter.widget-view+json": {
       "model_id": "459280a7cfb941c38d10c5884f656a5f",
       "version_major": 2,
       "version_minor": 0
      },
      "text/plain": [
       "Generating test split:   0%|          | 0/10000 [00:00<?, ? examples/s]"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Dataset loaded:\n",
      "  Training samples: 60000\n",
      "  Test samples: 10000\n",
      "  Image shape: (28, 28)\n"
     ]
    }
   ],
   "source": [
    "# Load MNIST dataset\n",
    "print(\"Loading MNIST dataset...\")\n",
    "dataset = load_dataset('mnist')\n",
    "\n",
    "# Convert to numpy arrays\n",
    "X_train = np.array(np.stack(dataset['train']['image']), dtype=np.uint8)\n",
    "X_test = np.array(np.stack(dataset['test']['image']), dtype=np.uint8)\n",
    "\n",
    "# Binarize data (threshold at 0)\n",
    "X_train = (X_train > 0).astype(jnp.float32)\n",
    "X_test = (X_test > 0).astype(jnp.float32)\n",
    "\n",
    "print(f\"\\nDataset loaded:\")\n",
    "print(f\"  Training samples: {X_train.shape[0]}\")\n",
    "print(f\"  Test samples: {X_test.shape[0]}\")\n",
    "print(f\"  Image shape: {X_train.shape[1:]}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 36,
   "id": "visualize_data",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:01.923580Z",
     "start_time": "2025-10-11T10:11:01.817163Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABKEAAAHvCAYAAACMrcycAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAJ19JREFUeJzt3Qm0fWP9P/C9OSJTRaRSKKGJjCmZSqXJkBKJQlIpIo1CREoDkdWckhZJJSqkFEmDUohVNA8q0UCzeP7rs9dv3/+553vn7/18zzn3vF5rneV+77nn7GcPzz5nv32eZ9ellFIBAAAAQKKlMt8cAAAAAIIQCgAAAIB0QigAAAAA0gmhAAAAAEgnhAIAAAAgnRAKAAAAgHRCKAAAAADSCaEAAAAASCeEAgAAACCdEAoAZuCXv/xlVdf12OPrX/96tdB97GMfG7fO82Httdcee783v/nN8/KeDJfrr7++WnrppZtj4BnPeMZI9s9B7QdT9fmDDjpo7PcXXXRR39oIwHATQgEwZ2effXb11Kc+tbrf/e5XLbPMMtW97nWvap111qm222676pBDDqkuvvjifjdx6HRfnM70MegX3MPmRS960byHb/x/b3zjG6u77767+fm1r33tuOfi3DHRMb7ccstVa665ZrXjjjtWH/nIR6q77rqrT60fXYcddlgTHrb7sJTS7yYBMIQ6/W4AAMNpn332qT7xiU+M+93tt9/ePKIq4bLLLqt+9atfNSEVw2nzzTev3vGOd8zrex5xxBHV3/72t+bnxz/+8fP63gy+73//+9X555/f/LzRRhtV22677Yxe95///Kf63e9+1zwi3I7HOeecM/b8KqusMu5YfehDH1oNsmHsB7FNo3It9t8PfvCD6nOf+1z17Gc/u9/NAmDICKEAmLUYitEdQG266aZN2LTiiitWf/rTn6qrr766+ta3vtXXNg6r7ovT8Je//KV661vfOvbvJz/5ydVTnvKUca+Z6oI7QsGVV155Tm155CMf2Tzm0wEHHDCv78dw+cAHPjD28x577DHl397nPvdpKm7Cn//852ao2O9///vm35/+9KerH/7wh9VjHvOY5t9xjB9++OHVoLvjjjuqlVZaaWj7QeyzNkSMfSmEAmDWCgDM0qGHHhrjMJrHuuuuW/73v/8t8jd/+9vfyhVXXDHud7fddlt5zWteU574xCeWtdZaq6y44oplmWWWKauvvnrZYYcdyhlnnFHuvvvuca/52te+NrasePz4xz8uRx11VHnwgx9c7nnPe5bNN9+8XHjhhc3f3nLLLWW//fYr973vfctyyy1Xttpqq3L55Zcv0rbu9zv99NPLF7/4xeZvV1hhhXLve9+77LbbbuWmm24a95pf/OIX414X7ep1/vnnl5122qmsscYazXrFe22//fblzDPPXGS9Zqp3uUcfffS07frwhz9cNt5442YbbLTRRs3f/fznPy+HHHJIecITnlDWXHPNsvzyy5d73OMe5QEPeEB55jOf2bS9V2yb7vfutu222479/oUvfGG58cYbyx577FFWXXXVsuyyyzbLP++88xZ5z9jvE61L737+2c9+Vk477bTy6Ec/unm/1VZbrey///7lz3/+8yLv+Y9//KO8/vWvLw960IOav33EIx5R3ve+9zXrPN0+m0isz2Tr3f1cbIOf/OQnZZdddikrr7xyuc997lP23HPP8oc//KH526985SvN9o7jNI7JODZ72z+XPhHuvPPO8va3v73pf7EfH/KQh5Tjjz++/Pe//13k+O4VfeJ5z3tes73itSuttFLZcssty3vf+97m9b2uvfbastdeezXti7+P4ypeG8d2bPff/va3M9qu//znP5tltW2LY6ZX93EVy+t27rnnjlu3s846a0b9M46z7vf861//Wg4//PDmHBLbep111mm2Xe92/sEPflBe9rKXlS222KLpJ7HecXzF63bffffyjW98Y5H29y7r1ltvLS9/+cvLAx/4wLLUUkuVk046acp+0L3+kz16zwFxvL3hDW9o+nocP9HGhz70oc1yf/WrX024L375y182/TWO2TgXbL311uWSSy6Zss+HO+64ozkG4rlYn1//+tcTvj8ATEYIBcCsvfKVrxy7SImL65/+9Kczet1111037QXWvvvuO+41veHEpptuushr4mLo7LPPbi4me5+LC7Ibbrhh3Ht2Px8X0hO1I8KUCBhmcpF71113lb333nvK9Xruc587YVg33yFUXEx2/7sNoS644IJpt/0xxxwzpxBqww03HBcutI+6rpsgZi4hVIQ3E7Vxm222Gfd+EZr0rnP7eNaznpUaQsXxFhfxvctdf/31m/Aojsvp2j+XPhEiQJjJOveGUG984xunXFZsy7///e9jf3/99dc3IcVUr2lD4OlceumlY6+JUHEik4VQEd5FCDnZ/pxpCBX9+uEPf/iE63HkkUeOa8upp5465XrH8d27fbuXFefGDTbYYNxr5juEuvLKK5vlTPa397rXvRYJ4mNbRVA+0fo8/elPn/TYb3WfgycKOQFgKobjATBrm2yyydjPt956a7Xeeus1w2JiDqEYmrf99ttX66677iKvW2qppaqHP/zh1RZbbFGtscYa1b3vfe/q3//+dzO/yAUXXNBMdHv66adXL33pS5u/mWxOmec973nVQx7ykOq9731vM7wlJjluh/bsvffe1X3ve9/q1FNPrf73v/81c8m85z3vqd7//vdP+H5f+9rXmjY//elPr370ox8185yE2267rWnHpZdeOu32OPHEE8eGJ8Ykyrvttlsz380vfvGL5vd33nlnM3wotlE7vCjLN77xjWqttdZq2rD88stXt9xyS/P7TqfTLH+zzTarVltttWb40j/+8Y/qm9/8ZrMNwlve8pZq//33rx74wAfOapnXXnttM3Tq0EMPrf71r39VH/rQh5qJo2N/xjw9T3rSk2a9HldccUXzupgv57zzzquuu+665veXX3559e1vf7vacsstm3/Hvo11bm244YbVzjvvXF1zzTVjw4ayxP5dddVVm8m1f/7zn1fnnntu8/uf/OQnzZxpcYzHJOdXXXVV9dWvfnXC9s+lT8Ry4qYAregLcfz/+te/rj75yU9O2t54TffQzhhCu9VWW1V//OMfq49//OPV3//+92Zbxn784Ac/2PxN/P6f//xn83NMDP6CF7ygWmGFFarf/va3TX+JdZmp7v0UfW46MafcZBPDP+5xj6u22WabaraiX8cQ19g/D3jAA6oPf/jDzTmsPZbe9KY3Vfe4xz2afy+77LLNfop+E/s5hhvHUNnYl7FPY9+8+tWvbs5H97znPRdZVrxvPHbYYYdmO8dQ5biJw1Re9rKXVc985jPH/S72aQw9bLVDZGOo7S677DLW/uj3bVviGIm7EEZ741xw0003NTeOCK94xSuqP/zhD2Pv96xnPavaeOONqwsvvLD60pe+NO02jPN8nIfbfRrHOADM2JQRFQBMIIYCbbbZZlP+3/qoZPnhD3844etjiEgMrYnhP+985zvLO97xjma4SvvaY489dtIKmRe/+MVjz8UQlO7nDjrooAkrRTbZZJNxy+9+zSMf+cjyn//8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",
      "text/plain": [
       "<Figure size 1200x500 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Visualize some samples\n",
    "fig, axes = plt.subplots(2, 5, figsize=(12, 5))\n",
    "for i, ax in enumerate(axes.flat):\n",
    "    ax.imshow(X_train[i], cmap='gray')\n",
    "    ax.axis('off')\n",
    "plt.suptitle('Sample Training Images (Binarized)', fontsize=14, fontweight='bold')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "model_definition",
   "metadata": {},
   "source": [
    "## Building the VAE\n",
    "\n",
    "### Custom State for Loss Tracking\n",
    "\n",
    "We'll create a custom state type to track the KL divergence loss within the encoder:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "id": "loss_state",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:01.930248Z",
     "start_time": "2025-10-11T10:11:01.927588Z"
    }
   },
   "outputs": [],
   "source": [
    "class Loss(brainstate.State):\n",
    "    \"\"\"Custom state for tracking loss components.\"\"\"\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "encoder_section",
   "metadata": {},
   "source": [
    "### Encoder Network\n",
    "\n",
    "The encoder maps images to latent distributions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 38,
   "id": "encoder",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:01.937752Z",
     "start_time": "2025-10-11T10:11:01.933261Z"
    }
   },
   "outputs": [],
   "source": [
    "class Encoder(brainstate.nn.Module):\n",
    "    \"\"\"Encoder network for VAE.\n",
    "    \n",
    "    Maps input images to latent distribution parameters (mean and std).\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, din: int, dmid: int, dout: int):\n",
    "        super().__init__()\n",
    "        \n",
    "        # Shared hidden layer\n",
    "        self.linear1 = brainstate.nn.Linear(din, dmid)\n",
    "        \n",
    "        # Separate heads for mean and log-variance\n",
    "        self.linear_mean = brainstate.nn.Linear(dmid, dout)\n",
    "        self.linear_std = brainstate.nn.Linear(dmid, dout)\n",
    "    \n",
    "    def __call__(self, x: jax.Array) -> jax.Array:\n",
    "        \"\"\"Encode input to latent representation.\n",
    "        \n",
    "        Args:\n",
    "            x: Input images [batch, height, width]\n",
    "            \n",
    "        Returns:\n",
    "            z: Latent codes [batch, latent_dim]\n",
    "        \"\"\"\n",
    "        # Flatten input\n",
    "        x = x.reshape((x.shape[0], -1))\n",
    "        \n",
    "        # Shared hidden layer with ReLU\n",
    "        x = self.linear1(x)\n",
    "        x = jax.nn.relu(x)\n",
    "        \n",
    "        # Compute mean and std of latent distribution\n",
    "        mean = self.linear_mean(x)\n",
    "        log_std = self.linear_std(x)\n",
    "        std = jnp.exp(log_std)  # Ensure std > 0\n",
    "        \n",
    "        # Compute KL divergence: KL(N(μ,σ²) || N(0,1))\n",
    "        # KL = 0.5 * (μ² + σ² - log(σ²) - 1)\n",
    "        kl_loss = 0.5 * jnp.mean(\n",
    "            -jnp.log(std ** 2) - 1.0 + std ** 2 + mean ** 2, \n",
    "            axis=-1\n",
    "        )\n",
    "        \n",
    "        # Store KL loss in state for later retrieval\n",
    "        self.kl_loss = Loss(jnp.mean(kl_loss))\n",
    "        \n",
    "        # Reparameterization trick: z = μ + σ * ε, where ε ~ N(0,1)\n",
    "        epsilon = brainstate.random.normal(size=mean.shape)\n",
    "        z = mean + std * epsilon\n",
    "        \n",
    "        return z"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "decoder_section",
   "metadata": {},
   "source": [
    "### Decoder Network\n",
    "\n",
    "The decoder reconstructs images from latent codes:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 39,
   "id": "decoder",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:01.944330Z",
     "start_time": "2025-10-11T10:11:01.940759Z"
    }
   },
   "outputs": [],
   "source": [
    "class Decoder(brainstate.nn.Module):\n",
    "    \"\"\"Decoder network for VAE.\n",
    "    \n",
    "    Maps latent codes back to image space.\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(self, din: int, dmid: int, dout: int):\n",
    "        super().__init__()\n",
    "        \n",
    "        self.linear1 = brainstate.nn.Linear(din, dmid)\n",
    "        self.linear2 = brainstate.nn.Linear(dmid, dout)\n",
    "    \n",
    "    def __call__(self, z: jax.Array) -> jax.Array:\n",
    "        \"\"\"Decode latent code to reconstruction.\n",
    "        \n",
    "        Args:\n",
    "            z: Latent codes [batch, latent_dim]\n",
    "            \n",
    "        Returns:\n",
    "            logits: Reconstruction logits [batch, height * width]\n",
    "        \"\"\"\n",
    "        # Hidden layer with ReLU\n",
    "        z = self.linear1(z)\n",
    "        z = jax.nn.relu(z)\n",
    "        \n",
    "        # Output layer (logits for binary cross-entropy)\n",
    "        logits = self.linear2(z)\n",
    "        \n",
    "        return logits"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "vae_section",
   "metadata": {},
   "source": [
    "### Complete VAE Model\n",
    "\n",
    "Combine encoder and decoder:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 40,
   "id": "vae",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:01.951854Z",
     "start_time": "2025-10-11T10:11:01.947444Z"
    }
   },
   "outputs": [],
   "source": [
    "class VAE(brainstate.nn.Module):\n",
    "    \"\"\"Variational Autoencoder.\n",
    "    \n",
    "    Combines encoder and decoder for generative modeling.\n",
    "    \"\"\"\n",
    "    \n",
    "    def __init__(\n",
    "        self,\n",
    "        din: int,\n",
    "        hidden_size: int,\n",
    "        latent_size: int,\n",
    "        output_shape: tp.Sequence[int],\n",
    "    ):\n",
    "        super().__init__()\n",
    "        \n",
    "        self.output_shape = output_shape\n",
    "        \n",
    "        # Create encoder and decoder\n",
    "        self.encoder = Encoder(din, hidden_size, latent_size)\n",
    "        self.decoder = Decoder(\n",
    "            latent_size, \n",
    "            hidden_size, \n",
    "            int(np.prod(output_shape))\n",
    "        )\n",
    "    \n",
    "    def __call__(self, x: jax.Array) -> jax.Array:\n",
    "        \"\"\"Forward pass: encode then decode.\n",
    "        \n",
    "        Args:\n",
    "            x: Input images [batch, height, width]\n",
    "            \n",
    "        Returns:\n",
    "            logits: Reconstruction logits [batch, height, width]\n",
    "        \"\"\"\n",
    "        # Encode to latent space\n",
    "        z = self.encoder(x)\n",
    "        \n",
    "        # Decode to reconstruction\n",
    "        logits = self.decoder(z)\n",
    "        \n",
    "        # Reshape to image dimensions\n",
    "        logits = jnp.reshape(logits, (-1, *self.output_shape))\n",
    "        \n",
    "        return logits\n",
    "    \n",
    "    def generate(self, z: jax.Array) -> jax.Array:\n",
    "        \"\"\"Generate images from latent codes.\n",
    "        \n",
    "        Args:\n",
    "            z: Latent codes [batch, latent_dim]\n",
    "            \n",
    "        Returns:\n",
    "            images: Generated images [batch, height, width]\n",
    "        \"\"\"\n",
    "        logits = self.decoder(z)\n",
    "        logits = jnp.reshape(logits, (-1, *self.output_shape))\n",
    "        \n",
    "        # Apply sigmoid to get probabilities\n",
    "        return jax.nn.sigmoid(logits)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "instantiate",
   "metadata": {},
   "source": [
    "### Create Model and Optimizer"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 41,
   "id": "create_model",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:04.281852Z",
     "start_time": "2025-10-11T10:11:01.954862Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Model created successfully!\n",
      "\n",
      "Model architecture:\n",
      "  Input: 784 -> Encoder -> Latent: 32\n",
      "  Latent: 32 -> Decoder -> Output: 784\n",
      "\n",
      "Total parameters: 427,344\n"
     ]
    }
   ],
   "source": [
    "# Create VAE model\n",
    "model = VAE(\n",
    "    din=int(np.prod(image_shape)),\n",
    "    hidden_size=hidden_size,\n",
    "    latent_size=latent_size,\n",
    "    output_shape=image_shape,\n",
    ")\n",
    "\n",
    "# Create Adam optimizer\n",
    "optimizer = braintools.optim.Adam(lr=learning_rate)\n",
    "optimizer.register_trainable_weights(model.states(brainstate.ParamState))\n",
    "\n",
    "print(\"Model created successfully!\")\n",
    "print(f\"\\nModel architecture:\")\n",
    "print(f\"  Input: {np.prod(image_shape)} -> Encoder -> Latent: {latent_size}\")\n",
    "print(f\"  Latent: {latent_size} -> Decoder -> Output: {np.prod(image_shape)}\")\n",
    "print(f\"\\nTotal parameters: {sum(x.size for x in jax.tree.leaves(model.states(brainstate.ParamState).to_dict_values())):,}\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "training_section",
   "metadata": {},
   "source": [
    "## Training the VAE\n",
    "\n",
    "### Define Training Step\n",
    "\n",
    "The loss function combines reconstruction loss and KL divergence:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 42,
   "id": "train_step",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:04.313283Z",
     "start_time": "2025-10-11T10:11:04.308916Z"
    }
   },
   "outputs": [],
   "source": [
    "@brainstate.transform.jit\n",
    "def train_step(x: jax.Array):\n",
    "    \"\"\"Perform one training step.\n",
    "    \n",
    "    Args:\n",
    "        x: Batch of images [batch, height, width]\n",
    "        \n",
    "    Returns:\n",
    "        loss: Total loss value\n",
    "    \"\"\"\n",
    "    def loss_fn():\n",
    "        # Forward pass\n",
    "        logits = model(x)\n",
    "        \n",
    "        # Collect KL losses from encoder\n",
    "        losses = brainstate.graph.treefy_states(model, Loss)\n",
    "        kl_loss = sum(jax.tree_util.tree_leaves(losses), 0.0)\n",
    "        \n",
    "        # Reconstruction loss (binary cross-entropy)\n",
    "        reconstruction_loss = jnp.mean(\n",
    "            optax.sigmoid_binary_cross_entropy(logits, x)\n",
    "        )\n",
    "        \n",
    "        # Total loss = reconstruction + β * KL\n",
    "        total_loss = reconstruction_loss + kl_weight * kl_loss\n",
    "        \n",
    "        return total_loss\n",
    "    \n",
    "    # Compute gradients and update\n",
    "    grads, loss = brainstate.transform.grad(\n",
    "        loss_fn, \n",
    "        optimizer.param_states.to_pytree(), \n",
    "        return_value=True\n",
    "    )()\n",
    "    \n",
    "    optimizer.update(grads)\n",
    "    \n",
    "    return loss"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "helper_functions",
   "metadata": {},
   "source": [
    "### Helper Functions for Evaluation"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "eval_functions",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:04.320756Z",
     "start_time": "2025-10-11T10:11:04.315707Z"
    }
   },
   "outputs": [],
   "source": [
    "@brainstate.transform.jit\n",
    "def forward(x: jax.Array) -> jax.Array:\n",
    "    \"\"\"Forward pass with sigmoid activation.\"\"\"\n",
    "    return jax.nn.sigmoid(model(x))\n",
    "\n",
    "\n",
    "@brainstate.transform.jit\n",
    "def sample(z: jax.Array) -> jax.Array:\n",
    "    \"\"\"Generate samples from latent codes.\"\"\"\n",
    "    return model.generate(z)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "run_training",
   "metadata": {},
   "source": [
    "### Run Training Loop"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 44,
   "id": "training_loop",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:36.841029Z",
     "start_time": "2025-10-11T10:11:04.320756Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Starting training...\n",
      "\n",
      "Epoch  1/20: Loss = 0.3331\n",
      "Epoch  2/20: Loss = 0.2323\n",
      "Epoch  3/20: Loss = 0.2125\n",
      "Epoch  4/20: Loss = 0.2023\n",
      "Epoch  5/20: Loss = 0.1945\n",
      "Epoch  6/20: Loss = 0.1883\n",
      "Epoch  7/20: Loss = 0.1838\n",
      "Epoch  8/20: Loss = 0.1802\n",
      "Epoch  9/20: Loss = 0.1770\n",
      "Epoch 10/20: Loss = 0.1750\n",
      "Epoch 11/20: Loss = 0.1728\n",
      "Epoch 12/20: Loss = 0.1710\n",
      "Epoch 13/20: Loss = 0.1699\n",
      "Epoch 14/20: Loss = 0.1687\n",
      "Epoch 15/20: Loss = 0.1675\n",
      "Epoch 16/20: Loss = 0.1672\n",
      "Epoch 17/20: Loss = 0.1658\n",
      "Epoch 18/20: Loss = 0.1651\n",
      "Epoch 19/20: Loss = 0.1647\n",
      "Epoch 20/20: Loss = 0.1635\n",
      "\n",
      "Training complete!\n"
     ]
    }
   ],
   "source": [
    "print(\"Starting training...\\n\")\n",
    "\n",
    "loss_history = []\n",
    "\n",
    "for epoch in range(epochs):\n",
    "    epoch_losses = []\n",
    "    \n",
    "    for step in range(steps_per_epoch):\n",
    "        # Sample random batch\n",
    "        idxs = np.random.randint(0, len(X_train), size=(batch_size,))\n",
    "        x_batch = X_train[idxs]\n",
    "        \n",
    "        # Train step\n",
    "        loss = train_step(x_batch)\n",
    "        epoch_losses.append(np.asarray(loss))\n",
    "    \n",
    "    # Log progress\n",
    "    avg_loss = np.mean(epoch_losses)\n",
    "    loss_history.append(avg_loss)\n",
    "    print(f'Epoch {epoch + 1:2d}/{epochs}: Loss = {avg_loss:.4f}')\n",
    "\n",
    "print(\"\\nTraining complete!\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "plot_loss",
   "metadata": {},
   "source": [
    "### Visualize Training Progress"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 46,
   "id": "loss_plot",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:36.967619Z",
     "start_time": "2025-10-11T10:11:36.875971Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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qbDZ5eFLhpgecgAAAADwZYRu+BQN0mN7tnfbd9Gwjmb/bW+vNWt1v7d6n+ntnvPboQRvAAAAAD6N4eWwhF8PSZOnrjhBQo+G7Pd/3C93vrNWKipt3r40AAAAAKgToRuWce7gDvLMlcMlLKQqeH+4Nl1un0/wBgAAAOC7CN2wlLMHpsqzV42Q8JCqX92Pfz4gt779o5QTvAEAAAD4IEI3LOfM/iny79+PkPDQql/fT9dlyM1vrpGyCoI3AAAAAN/ik6H76aeflm7duklkZKSMGTNGVqxYUeexzz33nJxyyinStm1bs40fP97t+PLycrnnnntk8ODBEhMTI2lpaTJ58mRJT093O49+vaCgILftoYce8uj3iaY7vV+yPDd5pDN4f74hU258Y7WUVlR6+9IAAAAAwHdD9/z58+XOO++UmTNnypo1a2To0KFyzjnnSFZWVq3HL1myRK644gpZvHixLF26VDp37ixnn3227N+/3zxfXFxszjN9+nRz+/7778uWLVvkwgsvrHGuBx54QA4cOODcbrnlFo9/v2i60/okyYtTRknE0eC9cFOW/PG11VJSTvAGAAAA4Bt8LnQ/+uijMnXqVLnmmmtkwIAB8uyzz0p0dLS8+OKLtR7/xhtvyI033ijDhg2Tfv36yfPPPy82m02++uor83xCQoJ8+eWXctlll0nfvn3lxBNPlKeeekpWr14te/bscTtXXFycpKamOjftGYdvO7l3orx09SiJCgsxjxdvyZbrCd4AAAAAfIRPhe6ysjIThnWIuENwcLB5rL3YDaE92zqkvF27dnUek5eXZ4aPt2nTxm2/Didv3769nHDCCTJnzhypqKhoxneD1nJSr0R56ZpREh1eFby//iVbpr66So6UEbwBAAAAeFeo+JCcnByprKyUlJQUt/36ePPmzQ06h87f1nnbrsHdVUlJiTlGh6THx8c79996660yfPhwE9Z/+OEHue+++8wQc+15r01paanZHPLz882t9rLrhtY1ultbeenqkXLty6ukqKxSvt2aI9e+vFKemzxcosOrfs21Xex2O+1jAbSVtdBe1kFbWQdtZS20l3XQVtZhs0BbNfTafCp0N5f2VL/99ttmnrcWYatOe8B1mLk23rx589ye03nkDkOGDJHw8HC5/vrrZfbs2RIREVHjXLp/1qxZNfZnZ2ebYI/W1zVa5PGJveS2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3te8++675hhtn8GDB8unn37aatcb6Lp161ajvXS76aabaj2e91Xr+eabb+SCCy6QtLQ083P+4IMP3J7Xki0zZsyQDh06SFRUlIwfP162bt163PM+/fTTpt213caMGSMrVqzw4HcRGOprq/LycrnnnnvM37aYmBhzzOTJkyU9Pb3F/5aiZd5bV199dY2f/YQJE457Xt5brd9Wtf3/S7c5c+bUeU7eW977t3pJSYn590X79u0lNjZWLrnkEsnMzKz3vE39f11rI3TDL3z99dfmTbps2TL58ssvzT9izj77bCkqKqr3dfHx8XLgwAHntnv37la75kA2cOBAt5/7d999V+exP/zwg1xxxRVy3XXXyY8//mj+SOu2fv36Vr3mQLVy5Uq3ttL3l/rtb39b52t4X7UO/fs2dOhQ8w/52jzyyCPyxBNPyLPPPivLly83ge6cc84x/6ipy/z58+XOO+801WLXrFljzq+vycrK8uB3EthtVVxcbH7W06dPN7fvv/+++YfohRde2KJ/S9Fy7y2lIdv1Z//WW2/Ve07eW95pK9c20u3FF180IVrDXH14b3nn3+p33HGHfPTRR6azRY/XDx9/85vf1Hvepvy/ziu0ejngb7KysrQqv/3rr7+u85iXXnrJnpCQ0KrXBbt95syZ9qFDhzb4+Msuu8x+/vnnu+0bM2aM/frrr/fA1eF4brvtNnvPnj3tNput1ud5X3mH/r37z3/+43ys7ZOammqfM2eOc19ubq49IiLC/tZbb9V5ntGjR9tvuukm5+PKykp7Wlqaffbs2R68+sBuq9qsWLHCHLd79+4W+1uKlmuvKVOm2C+66KJGnYf3lm+8t7TdzjjjjHqP4b3lnX+r5+bm2sPCwuzvvvuu85hNmzaZY5YuXVrrOZr6/zpvoKcbfikvL8/ctmvXrt7jCgsLpWvXrtK5c2e56KKLZMOGDa10hYFNh/3oULAePXrIlVdeKXv27Knz2KVLl5qhQq70E0zdj9ZVVlYmr7/+ulx77bWmp6AuvK+8b+fOnZKRkeH23klISDBDWut672j7rl692u01wcHB5jHvt9b/f5i+x9q0adNif0vRspYsWWKGyPbt21duuOEGOXjwYJ3H8t7yDTpM+ZNPPjEj546H91br/1t99erVpvfb9X2iw/q7dOlS5/ukKf+v8xZCN/yOzWaT22+/XX71q1/J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",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.figure(figsize=(10, 5))\n",
    "plt.plot(range(1, epochs + 1), loss_history, marker='o', linewidth=2, markersize=6)\n",
    "plt.xlabel('Epoch', fontsize=12)\n",
    "plt.ylabel('Loss', fontsize=12)\n",
    "plt.title('VAE Training Loss', fontsize=14, fontweight='bold')\n",
    "plt.grid(True, alpha=0.3)\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "evaluation_section",
   "metadata": {},
   "source": [
    "## Evaluation and Visualization\n",
    "\n",
    "### Reconstruction Quality\n",
    "\n",
    "Let's see how well the VAE reconstructs test images:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 47,
   "id": "reconstruction",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:37.300149Z",
     "start_time": "2025-10-11T10:11:36.972632Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABJIAAAHvCAYAAAAPafWnAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAARUVJREFUeJzt3QmUXVWZKP5TSSWpzDMJY4DIPAhCGKSZbEYVlXlQRFsFpFGcmFRaW9Fu7dfqs0Weti6gHZhsJhUeKHOjAso8QwIBwhASQuakMtz/2ue/br1bIXvXzkkqNf1+a9WiUt85Z5977hnu/dh7f021Wq1WAAAAAEAH+nW0AAAAAAAEEkkAAAAAZJFIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCwSSQAA3cwBBxxQNDU1lT8f+9jHunp3+ow77rij7biHnxdeeKGrdwkAuh2JJAB6rMMOO6ztC9/o0aOLpUuXrna5Wq1WTJ48uW3ZXXfdtV38tddeKwYMGNDuC+QxxxwTbffSSy9tt2zs5+tf/3rW64htr7m5uRg7dmyx1157FRdeeGExd+7cNTxCvV9P/OLfW5JEm2+++WrP2yFDhhRbbrllccIJJxS333570Vv0xHMNADqDRBIAPVbjl/C33nqr+N3vfrfa5e65555i2rRpq10v+MUvflEsX7683d9++9vfFm+++WbRlVasWFHuw7333ltccMEFZUJp/vz5XbpPrB+f/vSni3/7t38rf0JCpidZvHhx8fzzzxdXXnll8Z73vKf46U9/2tW7BACsQ83rcmMAsD596EMfKkaNGlUmkYL/+q//Ko4++ui3LRf+Xhd6Hn34wx9uF7/sssvetk5ra2vx61//ujjzzDM73I/TTz+97PG0qne/+93Zr2V125s9e3ZxxRVXtPV8eOqpp4pLLrmk+OxnP1tpu6zevHnzihEjRhTdyfHHH1/0JKEHUkh+hevmkUceKa666qqyJ2Dw5S9/ufjkJz9Z9Ovn/18CQK9QA4Ae7PTTTw/fVsufAQMG1GbNmtUuvmTJktqoUaPaljnyyCPbxe+77762WPjZeuut237fbbfdVtvmJZdc0m6d22+/fa1eQ2p7Tz75ZLvYaaedttpt3HDDDbUPfOADtYkTJ5bHIbzmAw88sPbLX/6ytnLlytWu89JLL9XOOeec2i677FIbPnx4bdCgQbVNN9209sEPfrB2yy23vG353/zmN7X3vve9tQkTJrS1sffee9f+1//6X7WFCxe+bfnG/Q6vMWzzgAMOqA0dOrQ2bNiw2mGHHVZ77LHH3rbeXXfdVfvQhz5U22ijjcp2wvKTJk0ql//a175We+utt962/dX9nHLKKeVy4Xg2/v3ZZ5+t/du//Vtt2223rQ0cOLB8vcH+++//tnVj79Gqli1bVvv5z39eO/jgg2sbbLBBud/jxo2r7bnnnrWvf/3r5TJh3zva5+eff77DfQmefvrp8twP5+vgwYPLn6222qp26qmnlufMqsI26tsL237llVdqn/rUp8rzJRyDcCx++tOf1tZEeE8at9no+OOPb/e6Xn311bet/9prr9XOP//82jvf+c7yfAjn3+TJk2tnnHFGbfr06W9bfsGCBbV//ud/ru26667l8s3NzbXx48eX63/yk5+s3XTTTW3Lrvqe14/r6vY9vC8drZd7rgXXX3997dBDDy3Pg7CP4dracssty/Ps29/+dm3FihVrdJwBoLuRSAKgR/vLX/7S7gvdj370o3bxq6++ul08fMlr9OlPf7ottskmm9Suu+66dss/8sgjXZpImjdvXrvYV77ylXbrhi+lJ598cvJL7rHHHltbvnx5u/V+//vfl19wY+ucddZZbcuGdY877rhkG9ttt12ZnGjUGN9nn31qTU1Nb1tv7NixtZkzZ7at88c//rHWv3//ZFv1REnVRNK+++7b7t9rm0iaPXt2bcqUKdH9GDly5DpNJF111VW1lpaW6DZCQubyyy+PJpJCUmPDDTdc7bohGbYuEklf+MIX2mL9+vUrE7qN/vSnP5WJttQxCwnFRiEJmTp2IXnV1YmkVc+T1f0sXrw4+xgDQHdkaBsAPdqee+5ZbLfddsWTTz7ZNoztH//xH1c7rG2DDTYo3vve97b9O0zOHYaO1R133HHF4Ycf3m64XJgI+9///d+T+xDmgvnrX//6tr+feuqpazVkKsyP9J3vfKft32GC32OPPbbdMt/97nfLOZ7q8TC0753vfGc5R034+7Jly4qrr7662GWXXcohRsH06dPL7SxatKhtvQ984APlMm+88UZx2223tWvj29/+djlUqS7M1XTIIYeUxzxsOwi/hyGDq67bOE/VtttuWxx11FHFQw89VNx4443l38PwvZ///OfFeeedV/47zKcT5oYKwvJhP8Ok4y+++GK53gMPPNC2zTB/0NSpU4v/83/+T9vfwmsME68HO+6442r35e677y522GGH4ogjjiiHX/Xv379YGyeffHJx//33t/07nI/hPBs0aFDx4IMPlnNcBeGYDRs2rLj44ovb5uzafffd2w1jGzNmTLKt5557rmyvPrF8mIz9lFNOKd/DMERz1qxZZSz8bbfddiu22mqrt20jtN3S0lIORRs8eHC5P2Feo/r59A//8A+Vj0U43+pD2+o++MEPlseicShhGJYa9jWYNGlSeQzCvvzmN78pHn/88XJi+XAuP/vss8XIkSPL8ytMdh2EIXIf/ehHi6233rrcRjjX67HOknuuhWNZN2XKlOL9739/Of/aSy+9VJ4H9fsUAPRoXZ3JAoC19Z3vfKfd//EPw36CN954oxxiVP/75z//+XbrXXnlle3Wu//++8u//8M//EPb38IwrjBsqVFOr4PV9YKIydne6NGjy2Fqq/ZGauzV8U//9E/t4t/97nfb9fypD6lp7C0Sfn71q1+9bbv1fQ+/jxkzpm3ZMJStsXdTGBrXuK0HH3ywLdb49zBkLvSuqgvDk+qxo446qu3vYXhe/e+r9qoJwhCpxmF0HfU8Wd0ye+2112p7hVTpkRR6rDX+PQz9a21tbbfu1KlTs9vpaJnQU6yxp8+jjz7aFgu/h7+trldZY4+k8BN63tX94Ac/aBdrfJ9SGnv1xH7CcMTQY6vR//7f/7vded0YD8PXwnC1ejwsGzzwwAPter+tOlwznJMvvPBCp/VIytlmsPPOO7fF//znP78tHtYxtA2Ans6shwD0eKGHRmOvknoPndDbKPSQqPv4xz/ebr3Q26juHe94R9k7JGiskvX666+39Z7pSqGXSOgx1ejpp59u69URfOMb32hXnvycc85pi4WeP88880z5+//8z/+06z1z0kkntdtu6PERSrvX22isXveRj3yk3bEOPV8a/fnPf46+R8OHD2/7d+hNUjdnzpy23/fdd9921fUOPPDA4rTTTiu+973vlT06JkyYUJaXXxtf+tKXyh4560LjsQy+9rWvlRO6rzoR9brSeHxDj6PGXlfh9/C31S3baKONNip7CdVts8027eKN78faCNdUOCdX7WUVeqc1thV6VdXP2dBjK/SKq/vTn/7Udp6G5YLQqyds+5hjjil7BYXrPGwn9Gzqao3n78EHH1z2Qgs9JC+66KLi0UcfLa8rk44D0NN5kgHQ42244YblF7a6X/7yl+WQpcZhbe9617uKnXbaqe3fr7zySnHLLbe0/btxeFEoWR6Gwa0u4bQ6t99+e9neqj/1ZEyVqm3f/OY3230pDcPrwlC5Ro0Jnhz1L+iN622xxRbJdVZtIyRyUv+OJSFWPRaNQ51WrlzZ9vvnPve5tsRgGKIVhiyF4W5f/OIXyyF1O++8c/Hqq68WayMMmetIveJYXX0oWUfHp6PjubYa21v12K/6tyrvxarvR66QLAvDv0JFwfpwzjAMLyQCn3jiiehryD1nQ+IvDJfbbLPN2obn/fd//3fxL//yL8WJJ55YbLzxxmWycW3fz7UVhoGG4bHBggULij/84Q/Fj3/847L6Yzh3DzjggGLhwoWd0jYArC/mSAKgVwi9V2666aby9xdeeKH4z//8z3bz1oR4o9BrqT4XT/Ctb32r/Fmd3//+92WPnnqPiM4WklrhC2fobRHmWKm/rpDQCj2T6gmmVXt6hN5BsXmBGhMIjeuF+WVSVm0j9NBK/bs+Z8yqVu2lE3qfrE6YDykkAEPiLPRGCT2iws+1115bJkYee+yxcj6lMB9QVUOHDl3t3xt7itTnDKoLc/XkHJ9wPMePH190lsb2Vj32q/5tbd+LNbHpppuWPb2C0HNuv/32KxNSIWnymc98prj11ltX+xpCEvgLX/hCcruNCd5wfMM8WWG+rJCoCudImPOqtbW1OPvss8u5vkJvpVV7/TS+n2GOptUdu3UhJNFCD8aXX365+Mtf/lL2AgyJtHD+hjnJ7rzzznIeqn/+53/ulPYBYH2QSAKgVwhDdcIX53ovjM9//vNtsYEDB75t+FZHvYwahS+pv/rVr8reFutT+DL8wx/+sOxBU096/dM//VPZA6o+JCkkt0KSq/5luf5lvtHMmTPL4UT1L+V/93d/V9x3331tw4TC0KDG4Xyh90aYHDj0/ghthC/+9V4kobdXGGpWH962akLn3e9+91q95pA0CvsZkjGNw69CgqyecGiccHvVpEh9AvEqwiTrdWGS7PC+h3NnxowZ0cRVOJaNQk+ykDQICbG6MLl547Crxn1e0/0Nx7f+3v3tb38rJ6YOE4cHIckW/ta4bFfYZ599yl5l9WMWJmAPCZT999+/bb/qk3GHHkehN2HordMonIMh+TR58uTy30uWLCmTSGGIWxiCWh+GGpYL132YnDskrh5++OEykdT4XgYhqbP99tuXv4deTKv2UMqRc66F9yBcM5tsskk59K7urLPOKq/lVc9fAOiJJJIA6BXC8JwwxCUMI1n1S16oztXYmyh8qXzqqafaVX5b3TC08EW2PgfRJZdcEk0kxaq2hYRI45C5KsKX4rCNX//61+W/w1Cv0AsjfBkPiaaQXPnKV75SxsKX8zDkJ8zNEuYjeu2118r9CnMLhYTHkUceWS4XXkdjpa6QZAuvIVRtC4m40EboEfWDH/ygbCMk5S644IK2eXfCtsKX/3AMG6tzhWFMoWLc2vj+979f9hb7+7//+3KYWBiqFZJYjcMUG5MEYUhTozAfzaGHHlomckLvlMa5mDoSqmyFJFAQeruE4ZAhcRESd/Vk3arCcMlQoa0+j9bvfve78hiEv4XhWCHRc9ddd7Wby6pxn0Nvt9DDaty4ceXPqj3nVhVeX3jvwtCskDgJyZnGqm31YWkhAdZYvXB9O//888v3sb4/obdfPZEUXuOFF15YHpNQ0SwknkJ1vnCuh9cVkonhHAy9hsKxD+dBqKIYEkEhabbHHnuU8zyFKm9hjqqQRFr13AjJ13ANzJ8/v/z3GWecUb434ZqIzR3VkZxzLSRyQ6IvnL/1hGgYRhvuH6vuIwD0WF092zcArCv33XffaitH/fa3v2233Gmnndau8tX06dNXu70LLrig3XYefvjhNaraFipv5Vh1e6E6VKNQjaupqaktfvjhh7fFQgWok08+eY335fe//31t+PDh0eUbK36FiljHHntscvuhktaMGTPatdEYD6+xUWMVscZ9a3xvVvcT3q9rr7223bYaK8A1/lx99dXZ1baC119/vaxut7o2Dz300NVWbQtmzZpVmzJlSnSfR44c2W7566+/frXL7bDDDlmV3a666qpaS0tLtL1Bgwa9reJd7HivyfFZVWPls9Wd68ccc0y77d57771tsXvuuaddxcHYT/1aCNX6Olp2jz32aFdh8atf/epql9t9991rG2ywwRpXbcs511Y9T1b9Ce9buE8BQE9msm0Aeo3Qo6Q+zKdu4sSJxWGHHdb27zBEJvS+qTvooIPaJvBdVeg50Th/TGOvgvUpDOsKvarqwpxJ9eExocdQ6K0TerYcffTR5ZCa0Bsl9NAKw6nCeqFn0eWXX95um6HHTOgtE+aVCcOKQrWsMHQn9PR43/veV8brwjC20PPo6quvLv8eJiIPvTBGjhxZ9uYKkyyH+ajCumvrE5/4RHHuueeWc+yEHh2hV094PeH30GslDJH60Ic+1G6da665puxtFYbgrc18P+F1he2HyZLD8QhzKYV5eULvmMahf6sKvd3C0MGf/exn5fkUeqGE4xOGXIUqamEC8Uah98qPfvSjsrdTeG1rKhyHMEdQmJQ99OIJxyj8hGFgn/rUp8phean9XV/CHF+NQi+kutCjLpx/oadbOEZhbqFwnoXeOuHfYXLqMFF1OA+CcCzDMQu9DkPPpPBeh+XDemGYWxhSGHoQNg4pDBXjwuTXoUdTOLfD9RB6SoX3OPRmqqKjcy1cT2EYW5gYPvRgql+LYTLy0HMs9FYK9ykA6MmaQjapq3cCAAAAgO5PjyQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGSRSAIAAAAgi0QSAAAAAFkkkgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFomkbuSFF14ompqayp877rhjjdb92Mc+Vq53wAEHdMq+dfb2AQAAgO5PImkdevPNN4vzzjuv2G677YrBgwcXw4YNK3bZZZfiW9/6VrFo0aIO1x80aFCx5557lj8jRoxYo7YnT55crrf99tuvxSsAAAAAiGuq1Wq1RJxML7/8crHPPvsUL774YvnvzTffvGhtbS1eeeWV8t8hoXTXXXcVw4cPX+36YdmBAwcW3VXokXTZZZcV+++//xr3lgIAAAB6Bz2S1pEzzjijLYl0+eWXF88//3wxY8aM4l/+5V/Kvz300EPFV77ylfL3+vC17373u8VRRx1V9lw69dRTo0PbfvSjHxUbb7xxMXTo0OLDH/5w8YMf/KBtubBObOhZfZl///d/Lz7ykY+USaywnQsvvLDdvp9yyinFVlttVcZDMmvSpEnFZz/72WLevHnr5dgBAAAAPYNE0jowZ86c4ve//335e0jknHDCCW2xc845p9hiiy3K33/1q18VjR3ALrjgguLWW28t47HeSL/97W+Lz3zmM2XPpiFDhhR333138dWvfnWN9u/8888vbrvttqKlpaXcTmj3D3/4Q1v8+uuvL19DGB636aablgmx//iP/yg+8YlPrPGxAAAAAHoviaR14Nlnny1WrlzZNoStUb9+/Yqdd965bQ6lN954oy225ZZblj2KHn300eLiiy9e7bZDr6UgJJumTZtW/uy+++5rtH9h+dDOk08+WQwYMKD8W0hg1d15553FrFmzyl5TU6dObes5dd111xVLlixZo7YAAACA3ksiaR0LQ8lWFZJJqxOGlI0ePbr8vX///qtd5vHHHy//e/jhh5dDz5qbm4ujjz56jfbpuOOOK3s8jRs3rthggw3Kv73++utt8T/+8Y/FjjvuWE4QHvY/TA4eLF++vF3iCwAAAOjbmrt6B3qDd7zjHWWyKPRKevDBB9vFwt8efvjh8vcxY8YU48ePb4tNmDBhvezfqFGj2n4PiaigPsQuDLf70pe+VP6+4YYblkPbQu+k0PMpWLFixXrZRwAAAKD70yNpHQgJove9733l72GS7CuuuKLd0LR6UiZMlN3YY2l1vZdWFXoKBWFOo4ULF5aJnWuuuWad7ftf/vKX8r+ht1OYIPzee+8tDjnkkHW2fQAAAKD30CNpHbnooovKnkdhouoTTzyxnOC6tbW1nNy6PnfSqtXScpx99tnlBNthHqYwp9KgQYOK2bNnr7P9rs/fNH/+/Lbtz507d51tHwAAAOg99EhaR8KQsAceeKCs0rbNNtsUr776alkJLSRqQgLpnnvuKUaMGLHG2z3iiCPKCmph2NmCBQuKvffeu0xS1YV5jdZGqMz2hS98oZw/KSSTQtW5b3zjG2u1TQAAAKB3aqo11qOn21m2bFkxY8aMYvPNNy//HYa2hWF0N998c5lcCrGcIXIAAAAAa8vQtm4uzIsUJvPefffdi4kTJxaPPvpo25xL3/zmNyWRAAAAgPVGj6RubsmSJcVxxx1X3H///eXcSEOGDCl23XXX4vOf/3zxgQ98oKt3DwAAAOhDJJIAAAAAyGKybQAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZGnOW6xQZh4ydZf5612zkMc1Cz2LaxZ6Ftcs9L5rVo8kAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGSRSAIAAAAgi0QSAAAAAFkkkgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJClOW8xAHLUarX13mZTU9N6bxPoPfefju4hnbVdAKBn0iMJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAECW5rzF6G3lwjuLUr90J73p2uqK1+l6pq9zD4G+w7MUOkdvesa4nv8fPZIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGSRSAIAAAAgS3PeYgS1Wq2rd6FHH6Ompqb1ui/0Hq49oLO4v0DXXCM+F0LP4nnpu24jPZIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGRpzlus7+hJZQ3XpsRgT3qd9H7Ox46vaccIgM7iGQO9h+u5a6SO+9p8b++u9EgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJClOW8xOlNXlAPsijLjfa0kIn1TZ53LVberBCwAALAu6ZEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACBLc9EHdUU57J5U3j61r0qJ01f0pGu2L7wOWBt95dnleqc76Y7nY2fcC7rj64Teour11Vee+11JjyQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGSRSAIAAAAgi0QSAAAAAFkkkgAAAADI0tzVO9BbNDU1dfUuAECfVavViu7E5wIA+grPvL5HjyQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGSRSAIAAAAgi0QSAAAAAFmau3oHADoqGdoVZb1TbSpxCutfV9wH1oZ7CH3d2lyzrhGozvXD+qBHEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALM1dvQO9RWeVJe6s8o09rYwydCddcf0o5Qq9h7LokOZzKtAR94mupUcSAAAAAFkkkgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAszUUflCqdq4xg51GymPV97vSm67nqa3Hd0ZP0pmsWAOg7z/2mPvaZW48kAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyNKctxhdpVardfUuQI/V1NRU9PVrq6PXmTpGQM+6pl3PVNUV505feQ4DvYfn7P+jRxIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCzNeYv1HcqFrx0lEekL52pvuhcoJQ5AV+iMZ0xvej5Db9HTrkuff/PokQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIEtz3mJ0VAqwp5U1BNZ/WdCedp+our/KplJVbzp3etr1DgB94ZnXmz5rdCU9kgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZGnOW4zOLCPYk8olAtW5TwAA0BP1pM+ia/OZmzx6JAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAECW5rzF6ExNTU2V1qvVakVP2Veg79wn6B0669xxLgNA99PTnrO+l3YtPZIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALBJJAAAAAGRpzlsMgO6qp5VrpW9zvgJA5+lNz9mmpqau3gUi9EgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJClOW8xADpbbyrXCnQOpZAB6C2fGT3Tei49kgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZGnOWwyg6/SWEqe9iXKt0DlcWwD0ps++nmu9kx5JAAAAAGSRSAIAAAAgi0QSAAAAAFkkkgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkKU5bzGArtPU1BSN1Wq19bovfUnquNP7ue7WjusHgL7CM6/v0SMJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAECW5rzF6EuUb6Qn6azztbeUN3c90xmcV0BPvDf1lmc7dAXPfhrpkQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIEtz3mJ0R0qcQudR4hQAgL7yPc9nX9aEHkkAAAAAZJFIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALI05y1GT6SEIwAA/P98Nqa3cC7T1fRIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJClqVar1fIWBQAAAKAv0yOJdeLSSy8tmpqayh8AAACgd+qViaQDDjigLakRfpqbm4uJEycWxx57bPH8888XPVlXJ2zqx/ZjH/tYu7+PHz++2HPPPcsfAAAAoHdqLnqxgQMHFrvuumvx5ptvFs8++2zxm9/8pnjyySeLxx57rOgrWltby+PQ2d73vveVPwAAAEDv1St7JNVtuOGGxV/+8pfimWeeKU4++eTyb48//ngxe/bs8ve5c+cWZ511VjFp0qQy2bLJJpsUX/jCF4pFixa1284f/vCH4qCDDipGjhxZtLS0FNtuu23xy1/+si0eElNHHXVUMXbs2HI7W265ZXH++ecXixcvfltPno9+9KPF1772tXLfRo8eXXzkIx8p5s+f37bcjTfeWOy9997FqFGjiiFDhhTveMc7iuOPP76YM2dO2Qvo4x//eNuy9Z5JX//619v9+7vf/W65P8OGDStOPfXU4oUXXmiL3XHHHW3rb7755u3WD1577bVynU033bR8LRMmTChOOumktu3feeed5e+XXXZZ2zbD9mM9pS655JJit912KwYPHlwMHTq02GeffYrrr7++Ld64b2Eb73//+8vXvcUWWxQ///nP1+LdBwAAANa1Xt0jaXVCMmjEiBFlT52Q3HnooYfK5NB2221XJpy+//3vFw8//HDxxz/+sUxuXH311WUiJ8xJHpIhW221VfHiiy8Wf/3rX8skUOjhFBI/CxYsKBM3IfHz1FNPFf/6r/9a/O1vfytuueWWdu1fccUVZXvjxo0rkza/+tWvykTWt771reKNN94ojjzyyHLfNttsszKZFNq66qqriu985zvF5MmTyyTVtGnTym3Vh5GFBFijCy64oGwjJGPWpDdSSLDttddexfTp08t/h9e6dOnS4qabbmpr74knnigTX2H/w/4EgwYNWu32LrzwwnJf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ldbrqOxhqmxfqlzro48+mmzzlVdeicZ22GGHSmWJU+WVg5133jka22+//aKxa665JhpTPrj3S5UMDX7yk59EY4cddljlcscpqevyqaeeisbuv//+aGzOnDnJNnfddddobNNNN60US5Xt7qgc6UUXXRSNPfDAA9HY8uXLk23S+1UtR7s2JYInTJgQjR177LHR2EknnRSNbbvttpX3N1V+ORXrrFK+K1eurHxvuueee6Kxv/71r9GYe0HPKPndUdnvD37wg9HYoEGDorG77ror2WYqPnDgwEqvc+ONN658n6h6/9lggw2S8SFDhlTaLqSkztfx48dHY5dcckk0duCBBybbbGlpKXqK1LO0o++WqXvMiBEjorH9998/Gnv55ZeTbW644YbR2PPPP98tvyfrkQQAAABAFokkAAAAALJIJAEAAACQRSIJAAAAgCwSSQAAAABkkUgCAAAAIEv12tg9WP/+/aOxE088MRrbcccdO6Vk8bx586KxSy+9NBr7/ve/X7lEY6qk8fe+971obKONNkq2mYrvu+++0dj111/fLcsasu6kSmm++93vTq57xBFHRGPNzdVuYx2V2L7uuuuisfPOOy8amzlzZlHV5ptvHo0dffTR0dgHPvCBaOytt95Ktvnf//3f0dgTTzwRjSnr3bd1dP2krveqsY5KaO+9997R2CmnnBKNTZ48ufKzPbW/KStXrqx8bJcuXRqNLVq0KBp78cUXo7Evf/nLyTb/9Kc/RWOLFy+u/FpYt1Ln45QpU5Lr7rPPPpVKiadsv/32yfjw4cOjsQEDBkRjra2tlWJr85khZeHChcn466+/vs7bpPfr6PkybNiwaOwb3/hGpWfloEGDis6QehZ09JxYsmRJNPbyyy9X+jw+dOjQZJvbbLNNpWM0adKkSp/jg4ceeigamz59erf8nqxHEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALOu+BmYPsOmmm0Zjp556aqVSpGtTuvDII4+Mxu69995KJXc7Mnv27GhsxowZ0dhGG22U3G6qJOJWW21VqRQyvUPq3Dj55JOT67a0tFRqM3VdvvDCC8l1zzjjjEolRdemlOvzzz8fjd13333R2MCBAyuV/A6uueaaaGz+/PnJdendUudrZ5V1T53LHZUSTz2/N9xww2isX79+lV9n6tmVKkM+a9asaOytt95KtnnnnXdGY7fddls0dscdd0RjCxYsSLa5fPnyZJzuIXW+jh8/Prluqmx16hpJlZ6+5ZZbkm2mnqULFy6Mxvr37x+NTZw4sVjf7r///mR82bJl621f6D1S110wYsSIaGzp0qWVnrMdPfNS8dR33dT3ztQzLfjP//zPSp/l99tvv2jsP/7jPyp/76h6P3z66aeTbaa+86e225X0SAIAAAAgi0QSAAAAAFkkkgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAszUUv1NTUlIxvscUW0di4ceOisRUrVkRjy5YtS7Z58cUXR2N33313NLZ8+fKiM7S0tERjm2yySaX1gpUrV0Zjw4YNy9w7eqP+/ftHY0OHDk2uW6vVKp1zqevyuuuuS7a5dOnSSveY1P7069ev8ut85ZVXorHrr78+Gnv66aeTbb755puV9ofeb23e/9Q1kroOtt5662jsy1/+crLNnXbaqdI9Zm2es4sXL47GZs2aVemavfLKK5NtLlq0KBp77rnnorHW1tZK9y16jtR1N2fOnOS6CxcurPTZb8GCBdHYI488UvlcTt0nBg4cGI1tu+22a/Udocp94mc/+1lyXdcXVc7Hjp7BqfNqyZIl0djjjz8ejY0YMSLZ5ssvvxyNXXbZZdHYbbfdFo298cYblY9R6jv9d77znWhs1KhRRWdYmXhPnnrqqeS6s2fPrrTdrqRHEgAAAABZJJIAAAAAyCKRBAAAAEAWiSQAAAAAskgkAQAAAJBFIgkAAACALM1FL9RRac+NNtqoUrnwVOnP6dOnJ9u85JJLorEVK1YU67vc+n777ReNbbbZZuu8bGrwzDPPRGPKjPcOqfMjVT74ySefTG537733jsYGDx5cqdzx+PHjk23usssu0diLL75Y6R7S0bU+aNCgaGzu3LmVyi+/9dZbyTZde1TR0bMgVbp77Nix0dgpp5wSje21117JNocMGVJpf1P72tH1kSqxPGvWrGhs6dKlle8T06ZNq7Rd13rvlzrPn3vuueS6M2bMiMaGDx8ejb3yyiuVPocGEyZMqPSZe9iwYdHYSSedlGyz6vWeegbfeuutyTahM+7LqWfMz372s2jsuuuui8aam9OpgVQZ+meffbbSs7KjzxOp6/3aa6+t9H2/s96zRx55JBq78MILk9tNfX/orvRIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALJIJAEAAACQJV3jr5eWUhwxYkSlEuWp7XbUZqo8d6oUaap0akdlVVOlki+++OLKpR9TUsfh+eefr7QePUfqfUxdA6nyncEGG2wQjR188MGVrvWDDjoo2ebhhx9eqTx36npOlUYNBg0aFI099dRT0dh//dd/RWN33HFHss2OSo1DFalyvpMmTap0XY4ePTrZZurZlbo3pdZLlToOxo4dW+lekCqF/NBDDyXbfPrpp5Nx+q7U/fzVV19Nrnv33XdHY+PGjav0nD3xxBOTbW677baVSnfvscce0diGG26YbDN1XaZMnTo1GhswYEClbUJKR9+NUuXin3vuuUrnckdS3z1T10HqM0FH1+xPfvKTSveQzjJz5sxo7P3vf380tnTp0qK30SMJAAAAgCwSSQAAAABkkUgCAAAAIItEEgAAAABZJJIAAAAAyCKRBAAAAECW6nXee7BU2cPly5dXKnmYKtsdnH766ZXKek+fPj0aO+KII5JtHnPMMZXKta6NVCnK22+/vXKJS3q+VNnLhx9+OLnuj3/842hs/vz50dhee+0Vje24447JNltaWtZ5+eBUCeWOvPOd74zGDjnkkGjsYx/7WHK7N910U+XS51Dl+pk0aVI0NmrUqE7Zn1Tp4dTzJ7VeR58LxowZE43ttttu0dhZZ52VbLO1tTUau/HGGyutR++3aNGiZPz73/9+NDZ48OBo7OCDD47GNt9888rPtSFDhkRjzc3NlT9PVv28mdqfkSNHJtedO3dupTah6rncWd+rUp8LU7HRo0dHY9/85jeTbR500EHF+rZgwYJobMqUKdHYG2+8UfQleiQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAluaiF6rVasn4M888E43Nnj07Gttkk02isY033jjZ5ic+8YlorKWlJRpbuHBhNDZ06NBkmwMGDIjGmpqais7wxBNPRGMPP/xw5feM3m3JkiXJ+EMPPRSNzZgxIxp73/veF42dd955yTY33XTTbnVt9e/fPxobO3ZsNHbFFVckt7v77rtXule6Zvu2js7zgQMHRmMTJkyo9Mzr6D4xePDgSufrihUriqr69etX6Ril9nXPPfdMtnnuuedGY88991w09vjjj0djrufer6P3+PXXX4/Gvve970VjQ4YMicbe9a53JdscNWpUpWsr9VpWrlyZbDN1XabaTD33m5t75dcpWKNrL3X9HHfccdHYiSeemGwztd2q5s6dm4yn7l0vvfTSOt+fnkqPJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACCLRBIAAAAAWZpqmTVfO6ukdVdoaWmJxs4///xo7HOf+1ylUr5rc2xT5Y6XLVuW3O6gQYMqlWtNWb58eTL+0Y9+tFIZ8t5Ueri7vJbedM2m9O/fPxobNmxYNPbe9743ud2TTjopGtt7770rXVsdlTBNlRBOvc6Ujkohp0o+77HHHtHYjBkzuv01kKsvX7OpNlPnY+r5EkyePDka22STTaKxAw88MBrbf//9k22OHj06Gps6dWo09tRTT1Xa12CzzTaLxrbaaqtobMSIEZXPg9bW1mjsqKOOisZuuummyveJ7qYvX7NdIXW9T5gwIRrbeeedk9s95JBDorG/+7u/q/RsHzNmTLLN1DM69TqnT58eje2yyy7JNufNm1f0da7Z3iH1OTZ1zd56662VPmusjcWLF0djH/7wh5PrXnvttUVfl3PN6pEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACBLUy2zHmNfKZc4bty4aOzss8+uVA68o/KoqbLef/7zn6OxRYsWJds84YQTKpUeTpk9e3YyvsUWW0Rj8+fPL/oCJU67z+tMxQYMGJDcbqoM8Pjx46OxlpaWSvsTfPrTn650PY8aNaqoKnUf+dKXvhSNXXrppdHY0qVLi56kt1+zqXK9AwcOrFTavqNnSKqc/IoVKypdd6nX0dG5/Nprr1UqEdzR69xuu+2isV/84hfR2MSJE4vOOF8/85nPRGM/+clPorHly5cXPUlvv2Z7krU5BoMHD650b0qtd8YZZyTbPP3006OxMWPGRGMzZsyIxiZPnpxsc9myZUVf55rtGTo6Pu9617uisbvuuisaGzJkSNEZUp81rrzyymjs5JNPTm439Tmlr8i5ZvVIAgAAACCLRBIAAAAAWSSSAAAAAMgikQQAAABAFokkAAAAALJIJAEAAACQpTlvsb4jVd7+wgsvrFz6c+utt47Gpk2bVqlk8ZQpU5JtnnTSScW6LveXKu3YUfll6E4lZTsqd50q/fniiy9WKkXaUfny8847Lxq76aaborEf/vCH0dj48eOTbTY3xx8DJ5xwQjR2zz33RGNPPvlksk1lVbuPSZMmRWNnn312NPbmm28mtzt16tRK50dqux21OWfOnGhswIABle4FCxcuTLa5yy67RGOjR48uOkPq+rn//vs7pU3ojLLuqc+Mixcvjsbmz58fjd12223JNk8//fRiXZdFV1Ke7ib1ebOlpSUa23XXXZPb/d3vfheNDRkypFjf95h58+ZFY5/73OeiMZ9D1w09kgAAAADIIpEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACBLc1fvQHdTq9Wisfnz50djDz/8cHK7jzzySDQ2YMCAaGzQoEHR2Pjx45NttrS0FFUsX748GrvllluS665YsaJSm1BVU1NTNNa/f/9K13pH203FUttNXVvBwoULo7G77747GrvsssuisS9+8YvJNlP7u8UWW0Rj++67bzQ2derUZJuLFy9OxlkzqfOxo+fIAQccEI1ts8020diGG26YbPOBBx6Ixvbaa69o7KqrrorGNt1002SbL730UqVzeeXKldHY1ltvnWzznHPOicYGDhxYdIZZs2ZFY0899VQ05vlMT5J6NqViqftAR+um9OsX/3/vQ4cOTa7b2tpaqU2oek6OHTs2GjvttNOisXPPPTfZ5rBhw4p1raNr8oUXXojGPvrRj1Z6VrJu6JEEAAAAQBaJJAAAAACySCQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACBLc1fvQG/RUenCVDxVFjRVPrij8supspApixYtisauvfbaStuEtdG/f/9orLk5fhsbMWJE5dLcqety3rx5lfa1o/LbqWt2q622isZ22223yqXhU/s7ZsyYaGzixImV7z2pfapamrkv6+g9HjlyZDS2xx57RGObb7555X2aMmVKNDZ79uxobNKkSdHYgAEDkm0OGjQoGhs3blw0NmrUqGhsyJAhyTZT95GO3peYxYsXJ+Of/OQno7ElS5ZEY64teovUtdXRsz31mSG13dSzsqN7E1Q5rzo6l1PPrk984hPR2Nlnnx2NDR06tOgMqefPgw8+mFw39Voee+yxaGzlypWZe0dVeiQBAAAAkEUiCQAAAIAsEkkAAAAAZJFIAgAAACCLRBIAAAAAWSSSAAAAAMgSr4HJepMqN5oq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",
      "text/plain": [
       "<Figure size 1200x500 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Get random test samples\n",
    "num_samples = 5\n",
    "idxs = np.random.randint(0, len(X_test), size=(num_samples,))\n",
    "x_sample = X_test[idxs]\n",
    "\n",
    "# Get reconstructions\n",
    "y_pred = forward(x_sample)\n",
    "\n",
    "# Plot original vs reconstruction\n",
    "fig, axes = plt.subplots(2, num_samples, figsize=(12, 5))\n",
    "\n",
    "for i in range(num_samples):\n",
    "    # Original\n",
    "    axes[0, i].imshow(x_sample[i], cmap='gray')\n",
    "    axes[0, i].axis('off')\n",
    "    if i == 0:\n",
    "        axes[0, i].set_title('Original', fontsize=10, fontweight='bold')\n",
    "    \n",
    "    # Reconstruction\n",
    "    axes[1, i].imshow(y_pred[i], cmap='gray')\n",
    "    axes[1, i].axis('off')\n",
    "    if i == 0:\n",
    "        axes[1, i].set_title('Reconstruction', fontsize=10, fontweight='bold')\n",
    "\n",
    "plt.suptitle('VAE Reconstruction Results', fontsize=14, fontweight='bold')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "generation_section",
   "metadata": {},
   "source": [
    "### Generate New Samples\n",
    "\n",
    "Sample from the latent space to generate new digits:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 48,
   "id": "generation",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:37.704695Z",
     "start_time": "2025-10-11T10:11:37.310389Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAABJMAAAGNCAYAAACllzDIAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjYsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvq6yFwwAAAAlwSFlzAAAPYQAAD2EBqD+naQAAVBRJREFUeJzt/QecX1WdP/4PJb2SkAApJJDQew9IL8pSXARcQESKDRe7i7rrgq6iX1gEUWDXdRFcREX9KSUi0kF6LyGhJSQhIT0kpBdg/o/zefwnj0nmvId7yCQzmTyfj8c8DO+58/ncz73n3Hs/x3vPa4P6+vr6OgAAAACoYMMqCwEAAABAYjAJAAAAgMoMJgEAAABQmcEkAAAAACozmAQAAABAZQaTAAAAAKjMYBIAAAAAlRlMAgAAAKAyg0kAAAAAVGYwCQBoUUOHDq3bYIMNaj/f+973Wnt12qz7779/xXZKPxMmTKhrS5544om6f/iHf6jr27dv3YYbbrhiPefOndvaqwYAtDKDSQCsE2bPnl33n//5n3Uf/vCH6wYMGFDXuXPnuk6dOtVtscUWdQcffHDd+eefX/fggw/W1dfXt/aqthlpIKdhACAN8LR177zzTt3Pf/7zukMOOaQ2gNGhQ4e6TTbZpG748OF1Rx11VG0fP/roo629muuFadOm1QaS/va3v9W99dZb7bJfHX300Sv6R2pnS5cuzS6XPvuwYcNWLLvHHns02VaprTYeGDz55JPD9/3Vr3610rLRj4FYANqyjVt7BQDg/fziF7+o+/rXv163cOHCJr9LX+TSTxpI+vGPf1w3derUus0337xV1pMPbvny5bUv9/fee+9K9XQXTPoZN25c3d13311bbv/992+19Vxf3HHHHbVBpCQNbJx33nl1Q4YMqf13ly5d6tqDs846q/Y5k9TG/vKXv9SddNJJTZZ7+OGH615//fWV/q6xX//617WB0MZGjhxZ2359+vRZY+sPAK3JYBIAbdqll15a981vfnPFf6cvtocddljdiBEj6rp37177wvbcc8/VPfTQQ3VLliypa0/efffd2t0SXbt2rWvvfvnLX640kHTooYfWHXTQQbU70NIA4ZNPPln7Ye2YOHHiin8PHDiw7sorryz6+/nz59f16NGjri074YQT6nr37r3isb3rr78+O5iU6g3SHUinn376Sr//v//7vyZ/s2zZsrrf/va3dV/84hffdz3OPffc2p1PqzrggAMqfxYAWOvqAaCNGjNmTP1GG22Unq+p/fTt27f+4Ycfzi47f/78+v/6r/+qnzt3bpPfPffcc/Vnn312/dZbb13fuXPn+m7dutXvvvvu9T/84Q/rFyxY0GT5IUOGrHjP7373u/VPPfVU/bHHHlvfq1ev+i5dutQfeOCB9Q8++GB2PaZNm1b/r//6r/W77bZbfffu3es7depUP2zYsPp//ud/rp84cWKT5c8888wV73XIIYfUlvnkJz9Z379///oNNtig/qabbqot98tf/rL+4x//eP32229f2w4bb7xxfY8ePWrv881vfrN+5syZK17zvvvuW/Ga0c9111230nrceuut9R/96EfrN9988/oOHTrU9+7du/6www6rv+GGG+rfe++97Gf9xS9+Ub/zzjvXPuPAgQPrv/71r9fPmzevyfar4mMf+9iKvzn00EOzy0yfPr3+ySefXKn2+uuv13/lK1+p7ZNBgwbVd+3atb5jx471AwYMqD/uuONqn2tV6bM33hapzXzpS1+qffb09+n9H3/88dqy48aNqz/ppJNq2yPtz4985CP1o0aNWun1xo8fv9Lrpe1//fXX1++555619tavX79a+0tto7FV91N6ncbefffd2uscddRRtddI+2XTTTetP+aYY+pvu+227Da65ZZbauuY2k9DG0nt/h//8R/rf/SjH9Vesznv13ZSG03Sfm2opf09a9asWhtP7WDDDTes/8lPfrLiNd966636//iP/6jfa6+96nv27Fn7HGn/pH1+5513rvH905xzzz13xfuk9Uqfo7ElS5bUXrthmbTOjT3xxBMrreu222674t/p8+as+vnSNgeAdY3BJADarMZf9NLPH//4x+LXSANM6Ut19OV4xx13rJ86depKf9N4MGTfffetfclc9e/SAEoa7GrskUceqX3Zj94rDUb9/e9/DweTttlmm9oX5sZ/0zCYlL6YNvclP32Jf/PNN4sHk9LgwhlnnNHssmkQ65133llpvb/97W9nl917773rN9tss+LBpOOPP37F32y33Xa1gaMqRo4c+b6fNQ1kNPdlPrdt0yBQGpjp06dPk9+lwbwZM2aEg0mHH354dj3SoE7jv2tuMGnRokX1Rx55ZLOfKw3eNfe5cj+LFy9u8cGk1ObTIGfj5RoG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bSt6jahe2kaj5Tt16lT5faP2HE2WF02UGi3ftWvXogkAly9fnq336tUrW+/QoUPliQRnzJixRifgpvVFx+1u3bpVnlgymsg1astDhgzJ1rfbbruiye133333ovYZrU+0DXJ97rbbbssu+9xzzxX1f+e/tSfavyeeeGK2/u///u/Z+g477FD5vBK1zauvvrqo/UTtJJootvQcWnqubI8TfLd30b6M+sXWW2+drR977LFFAQa54210/VF6Lbf99ttn6//6r/+arX/2s58tmvie9iFq49HE7tF3gWji+C222CJbHz58eOUAhui7QHQNf9hhh2XrTz31VLY+atSoypOBrw/XJu5MAgAAAKAyg0kAAAAAVGYwCQAAAIDKDCYBAAAAUJnBJAAAAAAqk+ZWkIAQJSb06NEjW99rr72a1E4++eTssj179szWp0+fnq3/+c9/LkpRmDlzZlFCT5QuEs1In3ud9j57/QexJrdJ9NpRwkKUyFCaOBO9TrR8LqUqSmd64403svXLLrssWz/iiCOy9eOPP75y2lpzqVtRKkWUvJVLjtAv1j2liYVRu4qSq3IJZ1H/mTVrVuVEuOaO8dE65pJRkl122SVb7927d7b+zjvvZOv/9E//VHndp0yZUlSP3pMPLjq+565vkgsvvDBb33bbbYuuq5YtW9akNnny5Oyyv//974uue0rPldE6Rsf9KNGnhMTeti06PkfnhGHDhhVda7TE/o9eIzqfRe0/SlGMEucmTpyYrTs+tw/R8XDzzTfP1k866aRsff/99y/qE1HKcu6aKGr70XsOGDCgKOHz0UcfzdbPOeecbH3q1Knt+njuziQAAAAAKjOYBAAAAEBlBpMAAAAAqMxgEgAAAACVGUwCAAAAYP1Mc4tSR6J6NPP8xz72sWz9lFNOyda7du2arW+yySaVE3SiFIXFixdn60OGDMnW//a3v2XrL7/8clEqzuzZs4vSGHLrGSV0sWZE7bxjx44t8vqlyX9RW8ml9MybN68ozTBKIxk9enS2fttttxWlvJ111llFn3Xu3LnZepQ+ROuK2k9U79u3b7a+8847F6Xc9OnTJ1sfN25ck9qcOXOyy7711ltFyVXRueWaa64p+kybbbZZUZpbScLhHnvsUXTsij6TtKCWF23rY445JlvfeuutixKAooSexx9/vPI1WGlabSRK1onaYdTPFyxY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ttlmm2x9xx13rCsRJVdF/W7zzTfP1rfccstKaQysnq985SvZet++fVskxTJKy4hS2+69995s/eWXX66cltOlS5fsslF9woQJRf08SpaL+lyU/hglA+VS26K+Hi1bup94f9ExbP/998/W991332w92mdRO5w9e3abT7GMUth22WWXbP3JJ58sukaN0otofdFx8qKLLmozqW3RdXXu+jt58803i85D/fr1KzqPRueK6Hpu1113zda32mqrbH38+PGVv59RTe54G7Wf6Do7uu658MIL23xiZ/SZonYVXa9H10PvBOfF6PWjZLzoeJTrW9H+W5vb151JAAAAAFRmMAkAAACAygwmAQAAAFCZwSQAAAAAKjOYBAAAAED7SHOL0mbuuOOOorSpKKEjmgH9wQcfzNa/8Y1vFKVf5WZdj1JBzj333DWa2haZNGlStn7ppZdm63PmzGnziSzromjm/lxyU//+/bPLDh48OFvfZJNNitpttHy0jt27d8/We/bsWTmhacmSJUWvHa1LlOY2c+bMosTIqN9Fy0cJDrnUlCgZjGp69epVuc2WHj+j9IvouPfnP/85Wx89enRRkkiPHj0qp19FbTlK1onOo9OnTy9KJozSS3Lr3txnzZ13x44dW9Sv+OCixKaPf/zjRa8TpXVGqbdt6bgXHS+OPfbYbH2HHXbI1h9//PGitKMonVDybeuLUpWOPvroutYQXVOPGzeuSe2nP/1pdtnouBods88777yi662oHunWrVtRv7vnnnuKXp/3lzsGRcfy6BqkNPG5NVKvo7YZJQdG1yuDBg3K1p9++ulsfdttty1KONxjjz2y9QceeCBbnzZtWuVj15reH425MwkAAACAygwmAQAAAFCZwSQAAAAAKjOYBAAAAED7mIA7Mn78+Gz9Jz/5SbZ+yCGHZOtvvPFG0eTTM2bMKJooLzd58MUXX1w0SXhLyU3alZx++unZ+nPPPZetm2h77dpiiy0qT7bWsWPHogl7owl4o30cTcwbTaoYTZaXm/wxmpwumvA6mlwvmsg0mjx4/vz5RZ8pmsw46i+5iQ1Ntrp6ctsvmqg9aj/RZNXRJMFjxozJ1v/0pz8VtZNhw4ZVboeLFi0q6lelk8NH/TyaOD+aiDY3IXpzr5/7rNG5WF9pedG1xo477lh0rI0m973vvvvq1rZoHaPPeu2112brI0aMyNYnTpyYrUcT10YT8NN25cJOmjtXtJTo+HzFFVdk6xdddFHlMKFI7969s/V99903Wz/wwAPrWkIUiBGdc0on+Ob95a4fli1bVjQp9fbbb5+tDx8+vOg6OwreidpJFDKU66Ol14RHHXVU0YTdUZBF76Bvvfbaa9n6hAkTiq7zcp8rOg+tzcAHPRUAAACAygwmAQAAAFCZwSQAAAAAKjOYBAAAAEBlBpMAAAAAaN9pblFKzDXXXJOt33///UUzzM+bN68odSGaYf7KK69sUttzzz3rWkI0G/ubb76ZrZ922mnZ+iOPPJKtS21bu0pm7o+SO6I2EaW8RUkNUYrUNttsk61HSQJRIsNuu+1WOTHhhRdeyNajpIbnn38+W99yyy2z9dmzZxelekXb5m9/+1vltAoJVasnd2yKjtnRto4SPaLkw1dffbVyW27u3HLMMcdk62+99VaT2rhx44qSojbddNOiFNJtt902W4/OUdHrd+7cOVufO3dutn7zzTdXTgaLjosRfev9k5Ci65VoW0dJP9FxP0qzifpoJHfeipK1oiSe73//+9n6dtttV7QNos8aJYBF26y0PbP2RMlS0bmi9FgTXbcddNBB2fqzzz672u8bHQNy55vm3jO6HoquISOlabvR+9KyhgwZUnR8y6VMJ5///Oez9auvvrrou8lmm21WdJzPHVejBM4oDT663or60E477VR0ft0+SMCLzmnR6+TOLVGatDQ3AAAAANokg0kAAAAAVGYwCQAAAIDKDCYBAAAAUJnBJAAAAADad5pbNBN5lLgxffr0bP2AAw4oSnXo1KlTtv7JT36y8szzpekHkeizXnzxxdn6E088ka1LbWvbXnvttSa1vfbaK7vspEmTsvVBgwYVJU4NGzYsW58yZUpRslqU+PDhD3+4bnVNnTo1Wz/66KOz9RdffDFbP/TQQ4u2TbSNoxSwKMGFlj32vfTSS0VtOUpbWrp0abY+dOjQbP2ss84qSjKMEhFzSaH33HNPUX8r3Qa77rprtn7qqacWpbZFaSfR8eKGG25oUpszZ052WeenDy5KconS1qL9GCXuRGk2hx12WLZ+6623FvXF3OtEx+uoH/bs2bNo20T9P6pPmDChaPnouJBr55IJ166oTUyePDlb79OnT1H626c//ek1ltpW+pmi7zLRtWJ0bCgVHc+jhNIoQZuWbRNRml50DR+l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qVUlaVal6YzS39ZNUULVsGHDsvWzzz67Se2LX/xiUdJ09B0qakPbbbddUdIj65boOjVK5oxMnz696LtAlPKW++7c3DkkSmIrabPROYRq3JkEAAAAQGUGkwAAAACozGASAAAAAJUZTAIAAACgMoNJAAAAALSPNLcocSBKp4lmjD/zzDOz9ZNOOilb32yzzYqSEZYsWZKtz5o1q0mta9eudS2RRJdLb2luRvrodUaMGFG0za655prKs/hLslo3+1H37t2LEj2+/vWvZ+tDhw6tnCbUUqltkahfvPvuu0X9K0q3KEk8Kk1SYd0TnSui1J3cuStKuZoxY0a2fttttxUlrBxxxBFF6ULRea6lkmD44HJtJXf90VwSbGlqW7R/o7TB5557Llt/4oknKp8/onNKlCgaHcf33XffbP2ZZ57J1qMU3kcffbSor0TbOJfyFq17lB5M2xCd33v16pWtn3766dn65z73uWx9q622qnx9E/XRaB2jc87bb7+drY8fP77ouoq1p+Q6M1o22o9Re4u+O0epybvttluLfO/NXW9F67j99ttn64899ljRsdz1+srcmQQAAABAZQaTAAAAAKjMYBIAAAAAlRlMAgAAAKAyg0kAAAAAtI80tyj5om/fvtn6F77whWz905/+dLbeu3fvogSEpUuXZusLFiyonAzyxhtvFCXRDRkyJFvfe++9WyTlbfbs2dl6//79s/WBAwdm6zNnzqy8/6T8rBlRIkNpmtsmm2xSlJjTs2fPbH2LLbYoaotr0uLFi7P1a6+9NlsfM2ZMUbqFNr1+ivrWfvvtl6336dOncmLItGnTsss++eST2frEiROz9Y4dOxb1iWj5qO1HCUCsPbl98+abbxalgXXq1KmojUcJT9H1VpSWU3Kdd8YZZ2SXPfvss4sStBYuXFg5aTT5+9//XnTcz6WzNXcNmXudqH/SNkT9YsCAAdn6lVdema0fe+yxRcfbXBuaMGFCUapolEQ1adKkbP3888/P1qdOnZqtux5qfdE+zh1vS5PJov371ltvFb1O9B08SoWbMmVK5XNX1PaPOeaYbP3VV1/N1m+++eZsXWLhytyZBAAAAEBlBpMAAAAAqMxgEgAAAACVGUwCAAAAoDKDSQAAAACse2luuRnme/TokV32a1/7WrZ+5plnFqVTRTPSRyklTz/9dLZ+yy23VE70ePzxx+tK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rh7VoxSRqB7N9l6SRhIlzkWf6fjjjy9Krdpqq62y9ddee62uRLTNIrltIOlk3RSllLzzzjvZ+iuvvJKtb7rpptn6brvtVjktK0pSiJKEotdZ06JjQC7BQcpVy2/r3LE2+dSnPpWt/8u//Eu23qdPn9Vel+ZEx8SoPa9JUbrIZpttlq1/6EMfKjoXSSdsm6KEs8suuyxb32OPPYpS/zp16lS0PlESzdtvv105xfPZZ5/N1g866KBsfejQodl6dBy58847s3VtvP2L2ufIkSOz9SiFM/cdJ+nbt2/RtX/uu0J03tp8882z9SjpKjqfRenOazoRm3VL1ManTJmSrc+bN6+oDy1YsKDytUyUwBmlrY0ZM6YohXHw4MFFKYwbB9db0baJzkWl15xrizuTAAAAAKjMYBIAAAAAlRlMAgAAAKAyg0kAAAAAVGYwCQAAAIB1L80tl8IUzX4eJXpECQL7779/tv76668XpVNFaVa9e/eunKSw7777Vl426dy5c7a+zTbbZOujRo0qWvfShLooiUpC1bqnNHEjSjWJ0oHmzp2brR9++OGVE6Sidhil6EyYMKFuTSpNUshtsyhJpWPHjtn6+poYFCXIlKRYnnjiiUVJg6XHsdLkz6iee9+Sz9/ca5e22ShJJdpmUbuNzjnOFW3TuHHjsvXPfOYz2fqIESOy9S233LJov8+ePTtbnzVrVpPagQceWNRmhw0bVrTuUULdq6++WnQNGZ0rpVyte6LjWLTvp0+fnq3vtdde2fr555+frUcJbbm2HqUvR2lrUept1D5nzpyZrY8dOzZbZ/0UHeOja9joO8L48eOLjue57yBRv40S5B555JFs/YYbbijqKzsG55ADDjggW3/jjTey9SiNrq1eP7kzCQAAAIDKDCYBAAAAUJnBJAAAAAAqM5gEAAAAQGUGkwAAAABou2lu0QzoufSL1157LbvswQcfnK0vWLAgW99nn32y9YULFxYlFEyePDlb79evX7beqVOnJrXly5dnl+3evXu2vvPOOxcl9Dz33HPZeq9evYpmti9NI5FS0v6VJFE1l3K4yy67VE44i94zardjxowpWsdSpe08975R343SJ9q70kS0XMLldtttV5SIEx3fonYSHbejdY9SSqLjfC65NEpJi9Y9Sj+N+lZpOxw0aFBRklaU8kjbFO33F154IVsfPXp0Ub+N0glL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",
      "text/plain": [
       "<Figure size 1200x400 with 12 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Sample latent codes from standard normal distribution\n",
    "z_samples = np.random.normal(scale=1.0, size=(12, latent_size))\n",
    "samples = sample(z_samples)\n",
    "\n",
    "# Plot generated samples\n",
    "fig, axes = plt.subplots(2, 6, figsize=(12, 4))\n",
    "\n",
    "for i, ax in enumerate(axes.flat):\n",
    "    if i < len(samples):\n",
    "        ax.imshow(samples[i], cmap='gray')\n",
    "    ax.axis('off')\n",
    "\n",
    "plt.suptitle('Generated Samples from VAE', fontsize=14, fontweight='bold')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "interpolation_section",
   "metadata": {},
   "source": [
    "### Latent Space Interpolation\n",
    "\n",
    "Interpolate between two digits in latent space:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 49,
   "id": "interpolation",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-10-11T10:11:38.724271Z",
     "start_time": "2025-10-11T10:11:37.713539Z"
    }
   },
   "outputs": [
    {
     "data": {
      "image/png": "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e6bTTz/dabv55ptjx55Ubfbp08dpe/rpp5NeU0/xfZTqx+17n2XLlnkdkzlz5jhtzz77rNO2bt26pNc7duzwuoZWrVrVaWvfvr3Tdu+99ya9btiwobNMrVq1vOrTJ/g+HRXPrQYAAAAAAAAA4ChgEB0AAAAAAAAAgAgMogMAAAAAAAAAUBLmRFdzdKo5qP70pz/FzlWt5uBRc3SuX7/eaXv33Xe95gq15zlT856pOa1r1KjhtF1//fUFzp+l5mgryfN7Fha1/7t27eo1z5yaz9ieX03Ng6Xmh3377bedNjW/9llnnZX0un///l61bs9TrOaHXbFihbOMqrOSOL9nquwaUPNXn3HGGV7zY6r+x57DTM3NOHXqVKftk08+cdoqV67stF1wwQWxc/9XqFDBaVNzn9lz7q1evTp2jrZMryf12e2cBnXc1Dx5ipqf155XXNWPmpvPnrc2au68iy++OOn1tdde69VHtWzZ0mm7+uqrY+dqV/P/ZyqfudSPP/54p03Ni69qx56rWs3FaM8/bOzZsyfwoXIhRo4cmfS6adOmXnPbtm3b1mkbPnx40usHHnjAa19kMjXfqd2m5pD9/PPPnbYyZco4bR999FHsnLFqznU1D7Jav+or7feqZdS94SmnnOK0nX322Umv//Wvf3nNO1rS+F7H1fllf3/68ssvnWVmz57ttG3dutVpU9k+9vVMzdevviup71Sqf7Pv++rWressU758ea96Ou2002L7XLX9mVJTqk1dX+z7giVLljjLqH2r7n1mzJgRez/k+137q6++ctpU/2bPd16vXj2ve/N27do5bfYc/SqbIhPGCnxzidT++eKLL2L7sZkzZ3rNia6ujXa2jC9VO+raZW+H6qNUPam5sO37NLUvMuV7nW9NqewXu09SfY+6t1Jzom/evLnQMshUHp/6PrBw4cLYcSZ13VO5InbtqTHVdMST6AAAAAAAAAAARGAQHQAAAAAAAACACAyiAwAAAAAAAAAQgUF0AAAAAAAAAABKQrCoCo/q16+f06YCE1QoqR32M336dGeZe+65x2uCfRXKUbt27aTXDz30kFdowwknnOC0NW/evMCwLePOO+902gjJKjhMVoX2qUCyKlWqeIVH2KExjz/+uLPMSy+95BVkotZvB0vagTFRAX0qpLRFixZJrwcOHBgbphIVgJmp7HrKzs52lqlV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",
      "text/plain": [
       "<Figure size 1500x200 with 10 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Get two random samples\n",
    "idx1, idx2 = np.random.randint(0, len(X_test), size=2)\n",
    "x1, x2 = X_test[idx1:idx1+1], X_test[idx2:idx2+1]\n",
    "\n",
    "# Encode to latent space (using the mean, no sampling)\n",
    "# We'll do this by calling encoder directly\n",
    "z1 = model.encoder(x1)\n",
    "z2 = model.encoder(x2)\n",
    "\n",
    "# Interpolate in latent space\n",
    "n_steps = 10\n",
    "alphas = np.linspace(0, 1, n_steps)\n",
    "z_interp = jnp.array([alpha * z2 + (1 - alpha) * z1 for alpha in alphas]).squeeze()\n",
    "\n",
    "# Decode interpolated latent codes\n",
    "samples_interp = sample(z_interp)\n",
    "\n",
    "# Plot interpolation\n",
    "fig, axes = plt.subplots(1, n_steps, figsize=(15, 2))\n",
    "\n",
    "for i, ax in enumerate(axes):\n",
    "    ax.imshow(samples_interp[i], cmap='gray')\n",
    "    ax.axis('off')\n",
    "    if i == 0:\n",
    "        ax.set_title('Start', fontsize=9)\n",
    "    elif i == n_steps - 1:\n",
    "        ax.set_title('End', fontsize=9)\n",
    "\n",
    "plt.suptitle('Latent Space Interpolation', fontsize=14, fontweight='bold')\n",
    "plt.tight_layout()\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "summary",
   "metadata": {},
   "source": [
    "## Key Concepts Summary\n",
    "\n",
    "### VAE Architecture\n",
    "\n",
    "1. **Encoder**:\n",
    "   - Maps input to latent distribution parameters (μ, σ)\n",
    "   - Uses reparameterization trick for backpropagation\n",
    "   - Computes KL divergence as regularization\n",
    "\n",
    "2. **Decoder**:\n",
    "   - Maps latent codes to reconstructions\n",
    "   - Outputs logits (before sigmoid)\n",
    "   - Enables generation from random samples\n",
    "\n",
    "3. **Loss Function**:\n",
    "   - **Reconstruction**: Binary cross-entropy between input and output\n",
    "   - **KL Divergence**: Regularizes latent space to N(0,1)\n",
    "   - **β-VAE**: Weight parameter controls trade-off\n",
    "\n",
    "### BrainState Features Used\n",
    "\n",
    "1. **Custom State Types**: `Loss` state for tracking KL divergence\n",
    "2. **State Management**: Automatic handling via lifted transforms\n",
    "3. **JIT Compilation**: Fast training with `brainstate.transform.jit`\n",
    "4. **Automatic Differentiation**: Gradients with `brainstate.transform.grad`\n",
    "5. **Optimizers**: Adam from BrainTools\n",
    "\n",
    "### Applications of VAEs\n",
    "\n",
    "- **Image Generation**: Creating new, realistic images\n",
    "- **Data Compression**: Compact latent representations\n",
    "- **Anomaly Detection**: Identifying outliers via reconstruction error\n",
    "- **Semi-Supervised Learning**: Using latent structure\n",
    "- **Drug Discovery**: Molecular generation and optimization"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "exercises",
   "metadata": {},
   "source": [
    "## Exercises\n",
    "\n",
    "Try these to deepen your understanding:\n",
    "\n",
    "1. **Experiment with β**:\n",
    "   - Try different values of `kl_weight` (0.01, 0.1, 1.0)\n",
    "   - Observe effect on reconstruction vs. generation quality\n",
    "\n",
    "2. **Increase Latent Dimension**:\n",
    "   - Change `latent_size` to 64 or 128\n",
    "   - Compare reconstruction quality and training time\n",
    "\n",
    "3. **Convolutional VAE**:\n",
    "   - Replace linear layers with convolutional layers\n",
    "   - Use `brainstate.nn.Conv2d` and `brainstate.nn.ConvTranspose2d`\n",
    "\n",
    "4. **Conditional VAE**:\n",
    "   - Add digit labels as input to encoder and decoder\n",
    "   - Generate specific digits on demand\n",
    "\n",
    "5. **Disentangled Representations**:\n",
    "   - Implement β-VAE with high β (β > 1)\n",
    "   - Visualize individual latent dimensions"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "next_steps",
   "metadata": {},
   "source": [
    "## Next Steps\n",
    "\n",
    "Now that you've built a VAE, explore:\n",
    "\n",
    "1. **Advanced Generative Models**: GANs, diffusion models\n",
    "2. **Sequence Modeling**: VAEs for time-series data\n",
    "3. **Brain-Inspired Models**: Spiking VAEs\n",
    "4. **Model Deployment**: Save and serve trained VAEs\n",
    "\n",
    "## References\n",
    "\n",
    "- [Auto-Encoding Variational Bayes (Original Paper)](https://arxiv.org/abs/1312.6114)\n",
    "- [Tutorial on VAEs](https://arxiv.org/abs/1606.05908)\n",
    "- [β-VAE Paper](https://openreview.net/forum?id=Sy2fzU9gl)\n",
    "- [BrainState Documentation](https://brainstate.readthedocs.io/)"
   ]
  }
 ],
 "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.11.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
