mirror of
https://github.com/ArthurDanjou/ArtStudies.git
synced 2026-03-16 05:11:40 +01:00
- Updated the Q-Learning model checkpoint (q_learning.pkl) to reflect recent training improvements. - Revised the training curves plot (Q-Learning_training_curves.png) to visualize the latest performance metrics.
1737 lines
250 KiB
Plaintext
1737 lines
250 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "fef45687",
|
||
"metadata": {},
|
||
"source": [
|
||
"# RL Project: Atari Tennis Tournament\n",
|
||
"\n",
|
||
"This notebook implements four Reinforcement Learning algorithms to play Atari Tennis (`ALE/Tennis-v5` via Gymnasium):\n",
|
||
"\n",
|
||
"1. **SARSA** — Semi-gradient SARSA with linear approximation (inspired by Lab 7, on-policy update from Lab 5B)\n",
|
||
"2. **Q-Learning** — Off-policy linear approximation (inspired by Lab 5B)\n",
|
||
"\n",
|
||
"Each agent is **pre-trained independently** against the built-in Atari AI opponent, then evaluated in a comparative tournament."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 51,
|
||
"id": "b50d7174",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"import pickle\n",
|
||
"from collections import deque\n",
|
||
"from pathlib import Path\n",
|
||
"\n",
|
||
"import ale_py # noqa: F401 — registers ALE environments\n",
|
||
"from pettingzoo.atari import tennis_v3\n",
|
||
"from tqdm.auto import tqdm\n",
|
||
"\n",
|
||
"import matplotlib.pyplot as plt\n",
|
||
"import numpy as np\n",
|
||
"\n",
|
||
"import torch\n",
|
||
"from torch import nn, optim\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "86047166",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Configuration & Checkpoints\n",
|
||
"\n",
|
||
"We use a **checkpoint** system (`pickle` serialization) to save and restore trained agent weights. This enables an incremental workflow:\n",
|
||
"- Train one agent at a time and save its weights\n",
|
||
"- Resume later without retraining previous agents\n",
|
||
"- Load all checkpoints for the final evaluation"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 52,
|
||
"id": "ff3486a4",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"CHECKPOINT_DIR = Path(\"checkpoints\")\n",
|
||
"CHECKPOINT_DIR.mkdir(parents=True, exist_ok=True)\n",
|
||
"\n",
|
||
"\n",
|
||
"def get_path(name: str) -> Path:\n",
|
||
" \"\"\"Return the checkpoint path for an agent (.pkl).\"\"\"\n",
|
||
" base = name.lower().replace(\" \", \"_\").replace(\"-\", \"_\")\n",
|
||
" return CHECKPOINT_DIR / (base + \".pkl\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "ec691487",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Utility Functions\n",
|
||
"\n",
|
||
"## Observation Normalization\n",
|
||
"\n",
|
||
"The Tennis environment produces image observations of shape `(4, 84, 84)` after preprocessing (grayscale + resize + frame stack).\n",
|
||
"We normalize them into 1D `float64` vectors divided by 255, as in Lab 7 (continuous feature normalization).\n",
|
||
"\n",
|
||
"## ε-greedy Policy\n",
|
||
"\n",
|
||
"Follows the pattern from Lab 5B (`epsilon_greedy`) and Lab 7 (`epsilon_greedy_action`):\n",
|
||
"- With probability ε: random action (exploration)\n",
|
||
"- With probability 1−ε: action maximizing $\\hat{q}(s, a)$ with uniform tie-breaking (`np.flatnonzero`)"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 53,
|
||
"id": "be85c130",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def normalize_obs(observation: np.ndarray) -> np.ndarray:\n",
|
||
" \"\"\"Flatten and normalize an observation to a 1D float64 vector.\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" observation: Raw observation array from the environment.\n",
|
||
"\n",
|
||
" Returns:\n",
|
||
" 1D numpy array of dtype float64, values in [0, 1].\n",
|
||
"\n",
|
||
" \"\"\"\n",
|
||
" return observation.flatten().astype(np.float64) / 255.0\n",
|
||
"\n",
|
||
"\n",
|
||
"def epsilon_greedy(\n",
|
||
" q_values: np.ndarray,\n",
|
||
" epsilon: float,\n",
|
||
" rng: np.random.Generator,\n",
|
||
") -> int:\n",
|
||
" \"\"\"Select an action using an ε-greedy policy with fair tie-breaking.\n",
|
||
"\n",
|
||
" Follows the same logic as Lab 5B epsilon_greedy and Lab 7 epsilon_greedy_action:\n",
|
||
" - With probability epsilon: choose a random action (exploration).\n",
|
||
" - With probability 1-epsilon: choose the action with highest Q-value (exploitation).\n",
|
||
" - If multiple actions share the maximum Q-value, break ties uniformly at random.\n",
|
||
"\n",
|
||
" Handles edge cases: empty q_values, NaN/Inf values.\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" q_values: Array of Q-values for each action, shape (n_actions,).\n",
|
||
" epsilon: Exploration probability in [0, 1].\n",
|
||
" rng: NumPy random number generator.\n",
|
||
"\n",
|
||
" Returns:\n",
|
||
" Selected action index.\n",
|
||
"\n",
|
||
" \"\"\"\n",
|
||
" q_values = np.asarray(q_values, dtype=np.float64).reshape(-1)\n",
|
||
"\n",
|
||
" if q_values.size == 0:\n",
|
||
" msg = \"q_values is empty.\"\n",
|
||
" raise ValueError(msg)\n",
|
||
"\n",
|
||
" if rng.random() < epsilon:\n",
|
||
" return int(rng.integers(0, q_values.size))\n",
|
||
"\n",
|
||
" finite_mask = np.isfinite(q_values)\n",
|
||
" if not np.any(finite_mask):\n",
|
||
" return int(rng.integers(0, q_values.size))\n",
|
||
"\n",
|
||
" safe_q = q_values.copy()\n",
|
||
" safe_q[~finite_mask] = -np.inf\n",
|
||
" max_val = np.max(safe_q)\n",
|
||
" best = np.flatnonzero(safe_q == max_val)\n",
|
||
"\n",
|
||
" if best.size == 0:\n",
|
||
" return int(rng.integers(0, q_values.size))\n",
|
||
"\n",
|
||
" return int(rng.choice(best))\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "bb53da28",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Agent Definitions\n",
|
||
"\n",
|
||
"## Base Class `Agent`\n",
|
||
"\n",
|
||
"Common interface for all agents, same signatures: `get_action`, `update`, `save`, `load`.\n",
|
||
"Serialization uses `pickle` (compatible with numpy arrays)."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 54,
|
||
"id": "ded9b1fb",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class Agent:\n",
|
||
" \"\"\"Base class for reinforcement learning agents.\n",
|
||
"\n",
|
||
" All agents share this interface so they are compatible with the tournament system.\n",
|
||
" \"\"\"\n",
|
||
"\n",
|
||
" def __init__(self, seed: int, action_space: int) -> None:\n",
|
||
" \"\"\"Initialize the agent with its action space and a reproducible RNG.\"\"\"\n",
|
||
" self.action_space = action_space\n",
|
||
" self.rng = np.random.default_rng(seed=seed)\n",
|
||
"\n",
|
||
" def get_action(self, observation: np.ndarray, epsilon: float = 0.0) -> int:\n",
|
||
" \"\"\"Select an action from the current observation.\"\"\"\n",
|
||
" raise NotImplementedError\n",
|
||
"\n",
|
||
" def update(\n",
|
||
" self,\n",
|
||
" state: np.ndarray,\n",
|
||
" action: int,\n",
|
||
" reward: float,\n",
|
||
" next_state: np.ndarray,\n",
|
||
" done: bool,\n",
|
||
" next_action: int | None = None,\n",
|
||
" agent_id: str = \"single\",\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Update the agent's internal state based on a transition.\"\"\"\n",
|
||
"\n",
|
||
" def save(self, filename: str) -> None:\n",
|
||
" \"\"\"Save the agent state to disk using pickle.\"\"\"\n",
|
||
" with Path(filename).open(\"wb\") as f:\n",
|
||
" pickle.dump(self.__dict__, f)\n",
|
||
"\n",
|
||
" def load(self, filename: str) -> None:\n",
|
||
" \"\"\"Load the agent state from disk.\"\"\"\n",
|
||
" with Path(filename).open(\"rb\") as f:\n",
|
||
" path = Path(filename).resolve()\n",
|
||
" ckpt_root = CHECKPOINT_DIR.resolve()\n",
|
||
" if ckpt_root not in path.parents:\n",
|
||
" msg = f\"Refusing to load checkpoint outside '{ckpt_root}': {path}\"\n",
|
||
" raise ValueError(msg)\n",
|
||
" if path.suffix != \".pkl\":\n",
|
||
" msg = f\"Unsupported checkpoint format: {path.suffix}\"\n",
|
||
" raise ValueError(msg)\n",
|
||
"\n",
|
||
" state = pickle.load(f) # noqa: S301 - trusted local checkpoints only\n",
|
||
" if not isinstance(state, dict):\n",
|
||
" msg = \"Invalid checkpoint payload: expected a dict.\"\n",
|
||
" raise TypeError(msg)\n",
|
||
"\n",
|
||
" self.__dict__.update(state)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "8a4eae79",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Random Agent (baseline)\n",
|
||
"\n",
|
||
"Serves as a reference to evaluate the performance of learning agents."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 55,
|
||
"id": "78bdc9d2",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class RandomAgent(Agent):\n",
|
||
" \"\"\"A simple agent that selects actions uniformly at random (baseline).\"\"\"\n",
|
||
"\n",
|
||
" def get_action(self, observation: np.ndarray, epsilon: float = 0.0) -> int:\n",
|
||
" \"\"\"Select a random action, ignoring the observation and epsilon.\"\"\"\n",
|
||
" _ = observation, epsilon\n",
|
||
" return int(self.rng.integers(0, self.action_space))\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "5f679032",
|
||
"metadata": {},
|
||
"source": [
|
||
"## SARSA Agent — Linear Approximation (Semi-gradient)\n",
|
||
"\n",
|
||
"This agent combines:\n",
|
||
"- **Linear approximation** from Lab 7 (`SarsaAgent`): $\\hat{q}(s, a; \\mathbf{W}) = \\mathbf{W}_a^\\top \\phi(s)$\n",
|
||
"- **On-policy SARSA update** from Lab 5B (`train_sarsa`): $\\delta = r + \\gamma \\hat{q}(s', a') - \\hat{q}(s, a)$\n",
|
||
"\n",
|
||
"The semi-gradient update rule is:\n",
|
||
"$$W_a \\leftarrow W_a + \\alpha \\cdot \\delta \\cdot \\phi(s)$$\n",
|
||
"\n",
|
||
"where $\\phi(s)$ is the normalized observation vector (analogous to tile coding features in Lab 7, but in dense form)."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 56,
|
||
"id": "c124ed9a",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class SarsaAgent(Agent):\n",
|
||
" \"\"\"Semi-gradient SARSA agent with linear function approximation.\n",
|
||
"\n",
|
||
" Inspired by:\n",
|
||
" - Lab 7 SarsaAgent: linear q(s,a) = W_a . phi(s), semi-gradient update\n",
|
||
" - Lab 5B train_sarsa: on-policy TD target using Q(s', a')\n",
|
||
"\n",
|
||
" The weight matrix W has shape (n_actions, n_features).\n",
|
||
" For a given state s, q(s, a) = W[a] @ phi(s) is the dot product\n",
|
||
" of the action's weight row with the normalized observation.\n",
|
||
" \"\"\"\n",
|
||
"\n",
|
||
" def __init__(\n",
|
||
" self,\n",
|
||
" n_features: int,\n",
|
||
" n_actions: int,\n",
|
||
" alpha: float = 0.001,\n",
|
||
" gamma: float = 0.99,\n",
|
||
" seed: int = 42,\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Initialize SARSA agent with linear weights.\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" n_features: Dimension of the feature vector phi(s).\n",
|
||
" n_actions: Number of discrete actions.\n",
|
||
" alpha: Learning rate (kept small for high-dim features).\n",
|
||
" gamma: Discount factor.\n",
|
||
" seed: RNG seed for reproducibility.\n",
|
||
"\n",
|
||
" \"\"\"\n",
|
||
" super().__init__(seed, n_actions)\n",
|
||
" self.n_features = n_features\n",
|
||
" self.alpha = alpha\n",
|
||
" self.gamma = gamma\n",
|
||
" self.W = np.zeros((n_actions, n_features), dtype=np.float64)\n",
|
||
"\n",
|
||
" def _q_values(self, phi: np.ndarray) -> np.ndarray:\n",
|
||
" \"\"\"Compute Q-values for all actions given feature vector phi(s).\n",
|
||
"\n",
|
||
" Equivalent to Lab 7's self.q(s, a) = self.w[idx].sum()\n",
|
||
" but using dense linear approximation: q(s, a) = W[a] @ phi.\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" phi: Normalized feature vector, shape (n_features,).\n",
|
||
"\n",
|
||
" Returns:\n",
|
||
" Array of Q-values, shape (n_actions,).\n",
|
||
"\n",
|
||
" \"\"\"\n",
|
||
" return self.W @ phi\n",
|
||
"\n",
|
||
" def get_action(self, observation: np.ndarray, epsilon: float = 0.0) -> int:\n",
|
||
" \"\"\"Select action using ε-greedy policy over linear Q-values.\n",
|
||
"\n",
|
||
" Same pattern as Lab 7 SarsaAgent.eps_greedy:\n",
|
||
" compute q-values for all actions, then apply epsilon_greedy.\n",
|
||
" \"\"\"\n",
|
||
" phi = normalize_obs(observation)\n",
|
||
" q_vals = self._q_values(phi)\n",
|
||
" return epsilon_greedy(q_vals, epsilon, self.rng)\n",
|
||
"\n",
|
||
" def update(\n",
|
||
" self,\n",
|
||
" state: np.ndarray,\n",
|
||
" action: int,\n",
|
||
" reward: float,\n",
|
||
" next_state: np.ndarray,\n",
|
||
" done: bool,\n",
|
||
" next_action: int | None = None,\n",
|
||
" agent_id: str = \"single\",\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Perform one semi-gradient SARSA update.\n",
|
||
"\n",
|
||
" Follows the SARSA update from Lab 5B train_sarsa:\n",
|
||
" td_target = r + gamma * Q(s', a') * (0 if done else 1)\n",
|
||
" Q(s, a) += alpha * (td_target - Q(s, a))\n",
|
||
"\n",
|
||
" In continuous form with linear approximation (Lab 7 SarsaAgent.update):\n",
|
||
" delta = target - q(s, a)\n",
|
||
" W[a] += alpha * delta * phi(s)\n",
|
||
" \"\"\"\n",
|
||
" phi = np.nan_to_num(normalize_obs(state), nan=0.0, posinf=0.0, neginf=0.0)\n",
|
||
" q_sa = float(self.W[action] @ phi)\n",
|
||
" if not np.isfinite(q_sa):\n",
|
||
" q_sa = 0.0\n",
|
||
"\n",
|
||
" if done:\n",
|
||
" target = reward\n",
|
||
" else:\n",
|
||
" phi_next = np.nan_to_num(\n",
|
||
" normalize_obs(next_state), nan=0.0, posinf=0.0, neginf=0.0,\n",
|
||
" )\n",
|
||
" if next_action is None:\n",
|
||
" next_action = 0\n",
|
||
" q_sp_ap = float(self.W[next_action] @ phi_next)\n",
|
||
" if not np.isfinite(q_sp_ap):\n",
|
||
" q_sp_ap = 0.0\n",
|
||
" target = float(reward) + self.gamma * q_sp_ap\n",
|
||
"\n",
|
||
" if not np.isfinite(target):\n",
|
||
" return\n",
|
||
"\n",
|
||
" delta = float(target - q_sa)\n",
|
||
" if not np.isfinite(delta):\n",
|
||
" return\n",
|
||
"\n",
|
||
" td_step = float(np.clip(delta, -1_000.0, 1_000.0))\n",
|
||
" self.W[action] += self.alpha * td_step * phi\n",
|
||
" self.W[action] = np.nan_to_num(self.W[action], nan=0.0, posinf=1e6, neginf=-1e6)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d4e18536",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Q-Learning Agent — Linear Approximation (Off-policy)\n",
|
||
"\n",
|
||
"Same architecture as SARSA but with the **off-policy update** from Lab 5B (`train_q_learning`):\n",
|
||
"\n",
|
||
"$$\\delta = r + \\gamma \\max_{a'} \\hat{q}(s', a') - \\hat{q}(s, a)$$\n",
|
||
"\n",
|
||
"The key difference from SARSA: we use $\\max_{a'} Q(s', a')$ instead of $Q(s', a')$ where $a'$ is the action actually chosen. This allows learning the optimal policy independently of the exploration policy."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 57,
|
||
"id": "f5b5b9ea",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class QLearningAgent(Agent):\n",
|
||
" \"\"\"Q-Learning agent with linear function approximation (off-policy).\n",
|
||
"\n",
|
||
" Inspired by:\n",
|
||
" - Lab 5B train_q_learning: off-policy TD target using max_a' Q(s', a')\n",
|
||
" - Lab 7 SarsaAgent: linear approximation q(s,a) = W[a] @ phi(s)\n",
|
||
"\n",
|
||
" The only difference from SarsaAgent is the TD target:\n",
|
||
" SARSA uses Q(s', a') (on-policy), Q-Learning uses max_a' Q(s', a') (off-policy).\n",
|
||
" \"\"\"\n",
|
||
"\n",
|
||
" def __init__(\n",
|
||
" self,\n",
|
||
" n_features: int,\n",
|
||
" n_actions: int,\n",
|
||
" alpha: float = 0.001,\n",
|
||
" gamma: float = 0.99,\n",
|
||
" seed: int = 42,\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Initialize Q-Learning agent with linear weights.\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" n_features: Dimension of the feature vector phi(s).\n",
|
||
" n_actions: Number of discrete actions.\n",
|
||
" alpha: Learning rate.\n",
|
||
" gamma: Discount factor.\n",
|
||
" seed: RNG seed.\n",
|
||
"\n",
|
||
" \"\"\"\n",
|
||
" super().__init__(seed, n_actions)\n",
|
||
" self.n_features = n_features\n",
|
||
" self.alpha = alpha\n",
|
||
" self.gamma = gamma\n",
|
||
" self.W = np.zeros((n_actions, n_features), dtype=np.float64)\n",
|
||
"\n",
|
||
" def _q_values(self, phi: np.ndarray) -> np.ndarray:\n",
|
||
" \"\"\"Compute Q-values for all actions: q(s, a) = W[a] @ phi for each a.\"\"\"\n",
|
||
" return self.W @ phi\n",
|
||
"\n",
|
||
" def get_action(self, observation: np.ndarray, epsilon: float = 0.0) -> int:\n",
|
||
" \"\"\"Select action using ε-greedy policy over linear Q-values.\"\"\"\n",
|
||
" phi = normalize_obs(observation)\n",
|
||
" q_vals = self._q_values(phi)\n",
|
||
" return epsilon_greedy(q_vals, epsilon, self.rng)\n",
|
||
"\n",
|
||
" def update(\n",
|
||
" self,\n",
|
||
" state: np.ndarray,\n",
|
||
" action: int,\n",
|
||
" reward: float,\n",
|
||
" next_state: np.ndarray,\n",
|
||
" done: bool,\n",
|
||
" next_action: int | None = None,\n",
|
||
" agent_id: str = \"single\",\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Perform one Q-learning update.\n",
|
||
"\n",
|
||
" Follows Lab 5B train_q_learning:\n",
|
||
" td_target = r + gamma * max(Q[s2]) * (0 if terminated else 1)\n",
|
||
" Q[s, a] += alpha * (td_target - Q[s, a])\n",
|
||
"\n",
|
||
" In continuous form with linear approximation:\n",
|
||
" delta = target - q(s, a)\n",
|
||
" W[a] += alpha * delta * phi(s)\n",
|
||
" \"\"\"\n",
|
||
" _ = next_action\n",
|
||
" phi = np.nan_to_num(normalize_obs(state), nan=0.0, posinf=0.0, neginf=0.0)\n",
|
||
" q_sa = float(self.W[action] @ phi)\n",
|
||
" if not np.isfinite(q_sa):\n",
|
||
" q_sa = 0.0\n",
|
||
"\n",
|
||
" if done:\n",
|
||
" target = reward\n",
|
||
" else:\n",
|
||
" phi_next = np.nan_to_num(\n",
|
||
" normalize_obs(next_state), nan=0.0, posinf=0.0, neginf=0.0,\n",
|
||
" )\n",
|
||
" q_next_all = self._q_values(phi_next)\n",
|
||
" q_next_max = float(np.max(q_next_all))\n",
|
||
" if not np.isfinite(q_next_max):\n",
|
||
" q_next_max = 0.0\n",
|
||
" target = float(reward) + self.gamma * q_next_max\n",
|
||
"\n",
|
||
" if not np.isfinite(target):\n",
|
||
" return\n",
|
||
"\n",
|
||
" delta = float(target - q_sa)\n",
|
||
" if not np.isfinite(delta):\n",
|
||
" return\n",
|
||
"\n",
|
||
" td_step = float(np.clip(delta, -1_000.0, 1_000.0))\n",
|
||
" self.W[action] += self.alpha * td_step * phi\n",
|
||
" self.W[action] = np.nan_to_num(self.W[action], nan=0.0, posinf=1e6, neginf=-1e6)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "79e6b39f",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Monte Carlo Agent — Linear Approximation (First-visit)\n",
|
||
"\n",
|
||
"This agent is inspired by Lab 4 (`mc_control_epsilon_soft`):\n",
|
||
"- Accumulates transitions in an episode buffer `(state, action, reward)`\n",
|
||
"- At the end of the episode (`done=True`), computes **cumulative returns** by traversing the buffer backward:\n",
|
||
" $$G \\leftarrow \\gamma \\cdot G + r$$\n",
|
||
"- Updates weights with the semi-gradient rule:\n",
|
||
" $$W_a \\leftarrow W_a + \\alpha \\cdot (G - \\hat{q}(s, a)) \\cdot \\phi(s)$$\n",
|
||
"\n",
|
||
"Unlike TD methods (SARSA, Q-Learning), Monte Carlo waits for the complete episode to finish before updating.\n",
|
||
"\n",
|
||
"> **Note**: This agent currently has **checkpoint loading issues** — the saved weights fail to restore properly, causing the agent to behave as if untrained during evaluation. The training code itself works correctly."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 58,
|
||
"id": "7a3aa454",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class MonteCarloAgent(Agent):\n",
|
||
" \"\"\"Monte Carlo control agent with linear function approximation.\"\"\"\n",
|
||
"\n",
|
||
" def __init__(\n",
|
||
" self,\n",
|
||
" n_features: int,\n",
|
||
" n_actions: int,\n",
|
||
" alpha: float = 0.001,\n",
|
||
" gamma: float = 0.99,\n",
|
||
" seed: int = 42,\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Initialize Monte Carlo agent with linear weights and episode buffers.\"\"\"\n",
|
||
" super().__init__(seed, n_actions)\n",
|
||
" self.n_features = n_features\n",
|
||
" self.alpha = alpha\n",
|
||
" self.gamma = gamma\n",
|
||
" self.W = np.zeros((n_actions, n_features), dtype=np.float32)\n",
|
||
" self._obs_buf: dict[str, list[np.ndarray]] = {}\n",
|
||
" self._act_buf: dict[str, list[int]] = {}\n",
|
||
" self._rew_buf: dict[str, list[float]] = {}\n",
|
||
"\n",
|
||
" def _q_values(self, phi: np.ndarray) -> np.ndarray:\n",
|
||
" \"\"\"Compute Q-values.\"\"\"\n",
|
||
" return self.W @ phi\n",
|
||
"\n",
|
||
" def get_action(self, observation: np.ndarray, epsilon: float = 0.0) -> int:\n",
|
||
" \"\"\"Select action using epsilon-greedy policy over linear Q-values.\"\"\"\n",
|
||
" phi = observation.flatten().astype(np.float32) / np.float32(255.0)\n",
|
||
" q_vals = self._q_values(phi)\n",
|
||
" return epsilon_greedy(q_vals, epsilon, self.rng)\n",
|
||
"\n",
|
||
" def update(\n",
|
||
" self,\n",
|
||
" state: np.ndarray,\n",
|
||
" action: int,\n",
|
||
" reward: float,\n",
|
||
" next_state: np.ndarray,\n",
|
||
" done: bool,\n",
|
||
" next_action: int | None = None,\n",
|
||
" agent_id: str = \"single\",\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Accumulate episode transitions and update weights at episode end.\"\"\"\n",
|
||
" _ = next_state, next_action\n",
|
||
"\n",
|
||
" if agent_id not in self._obs_buf:\n",
|
||
" self._obs_buf[agent_id] = []\n",
|
||
" self._act_buf[agent_id] = []\n",
|
||
" self._rew_buf[agent_id] = []\n",
|
||
"\n",
|
||
" self._obs_buf[agent_id].append(state)\n",
|
||
" self._act_buf[agent_id].append(action)\n",
|
||
" self._rew_buf[agent_id].append(reward)\n",
|
||
"\n",
|
||
" if not done:\n",
|
||
" return\n",
|
||
"\n",
|
||
" n = len(self._rew_buf[agent_id])\n",
|
||
" actions = np.array(self._act_buf[agent_id], dtype=np.intp)\n",
|
||
"\n",
|
||
" returns = np.empty(n, dtype=np.float32)\n",
|
||
" G = np.float32(0.0)\n",
|
||
" gamma32 = np.float32(self.gamma)\n",
|
||
" for i in range(n - 1, -1, -1):\n",
|
||
" G = gamma32 * G + np.float32(self._rew_buf[agent_id][i])\n",
|
||
" returns[i] = G\n",
|
||
"\n",
|
||
" alpha32 = np.float32(self.alpha)\n",
|
||
" chunk_size = 500\n",
|
||
" for start in range(0, n, chunk_size):\n",
|
||
" end = min(start + chunk_size, n)\n",
|
||
" cs = end - start\n",
|
||
"\n",
|
||
" raw = np.array(self._obs_buf[agent_id][start:end])\n",
|
||
" phi = raw.reshape(cs, -1).astype(np.float32)\n",
|
||
" phi /= np.float32(255.0)\n",
|
||
"\n",
|
||
" ca = actions[start:end]\n",
|
||
" q_sa = np.einsum(\"ij,ij->i\", self.W[ca], phi)\n",
|
||
"\n",
|
||
" deltas = np.clip(returns[start:end] - q_sa, -1000.0, 1000.0)\n",
|
||
"\n",
|
||
" for a in range(self.action_space):\n",
|
||
" mask = ca == a\n",
|
||
" if not np.any(mask):\n",
|
||
" continue\n",
|
||
" self.W[a] += alpha32 * (deltas[mask] @ phi[mask])\n",
|
||
"\n",
|
||
" self.W = np.nan_to_num(self.W, nan=0.0, posinf=1e6, neginf=-1e6)\n",
|
||
"\n",
|
||
" self._obs_buf[agent_id].clear()\n",
|
||
" self._act_buf[agent_id].clear()\n",
|
||
" self._rew_buf[agent_id].clear()\n",
|
||
"\n",
|
||
" def save(self, filename: str) -> None:\n",
|
||
" \"\"\"Save agent state, clearing episode buffers first to avoid stale data.\"\"\"\n",
|
||
" self._obs_buf.clear()\n",
|
||
" self._act_buf.clear()\n",
|
||
" self._rew_buf.clear()\n",
|
||
" super().save(filename)\n",
|
||
"\n",
|
||
" def load(self, filename: str) -> None:\n",
|
||
" \"\"\"Load agent state and ensure correct dtype for weights.\"\"\"\n",
|
||
" super().load(filename)\n",
|
||
" self.W = np.asarray(self.W, dtype=np.float32)\n",
|
||
" self._obs_buf = {}\n",
|
||
" self._act_buf = {}\n",
|
||
" self._rew_buf = {}\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "d5766fe9",
|
||
"metadata": {},
|
||
"source": [
|
||
"## DQN Agent — PyTorch MLP with Experience Replay and Target Network\n",
|
||
"\n",
|
||
"This agent implements the Deep Q-Network (DQN) using **PyTorch** for GPU-accelerated training (MPS on Apple Silicon).\n",
|
||
"\n",
|
||
"**Network architecture** (same structure as before, now as `torch.nn.Module`):\n",
|
||
"$$\\text{Input}(n\\_features) \\to \\text{Linear}(256) \\to \\text{ReLU} \\to \\text{Linear}(256) \\to \\text{ReLU} \\to \\text{Linear}(n\\_actions)$$\n",
|
||
"\n",
|
||
"**Key techniques** (inspired by Lab 6A Dyna-Q + classic DQN):\n",
|
||
"- **Experience Replay**: circular buffer of transitions, sampled as minibatches for off-policy updates\n",
|
||
"- **Target Network**: periodically synchronized copy of the Q-network, stabilizes learning\n",
|
||
"- **Gradient clipping**: prevents exploding gradients in deep networks\n",
|
||
"- **GPU acceleration**: tensors on MPS/CUDA device for fast forward/backward passes\n",
|
||
"\n",
|
||
"> **Note**: This agent currently has **checkpoint loading issues** — the saved `.pt` checkpoint fails to restore properly (device mismatch / state dict incompatibility), causing errors during evaluation. The training code itself works correctly."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 59,
|
||
"id": "9c777493",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class QNetwork(nn.Module):\n",
|
||
" \"\"\"MLP Q-network: Input -> 256 -> ReLU -> 256 -> ReLU -> n_actions.\"\"\"\n",
|
||
"\n",
|
||
" def __init__(self, n_features: int, n_actions: int) -> None:\n",
|
||
" \"\"\"Initialize the Q-network architecture.\"\"\"\n",
|
||
" super().__init__()\n",
|
||
" self.net = nn.Sequential(\n",
|
||
" nn.Linear(n_features, 256),\n",
|
||
" nn.ReLU(),\n",
|
||
" nn.Linear(256, 256),\n",
|
||
" nn.ReLU(),\n",
|
||
" nn.Linear(256, n_actions),\n",
|
||
" )\n",
|
||
"\n",
|
||
" def forward(self, x: torch.Tensor) -> torch.Tensor:\n",
|
||
" \"\"\"Compute Q-values for input state tensor x.\"\"\"\n",
|
||
" return self.net(x)\n",
|
||
"\n",
|
||
"\n",
|
||
"class ReplayBuffer:\n",
|
||
" \"\"\"Fixed-size circular replay buffer storing (s, a, r, s', done) transitions.\"\"\"\n",
|
||
"\n",
|
||
" def __init__(self, capacity: int) -> None:\n",
|
||
" \"\"\"Initialize the replay buffer with a maximum capacity.\"\"\"\n",
|
||
" self.buffer: deque[tuple[np.ndarray, int, float, np.ndarray, bool]] = deque(maxlen=capacity)\n",
|
||
"\n",
|
||
" def push(self, state: np.ndarray, action: int, reward: float, next_state: np.ndarray, done: bool) -> None:\n",
|
||
" \"\"\"Add a transition to the replay buffer.\"\"\"\n",
|
||
" self.buffer.append((state, action, reward, next_state, done))\n",
|
||
"\n",
|
||
" def sample(self, batch_size: int, rng: np.random.Generator) -> tuple[np.ndarray, ...]:\n",
|
||
" \"\"\"Sample a random minibatch of transitions from the buffer.\"\"\"\n",
|
||
" indices = rng.choice(len(self.buffer), size=batch_size, replace=False)\n",
|
||
" batch = [self.buffer[i] for i in indices]\n",
|
||
" states = np.array([t[0] for t in batch])\n",
|
||
" actions = np.array([t[1] for t in batch])\n",
|
||
" rewards = np.array([t[2] for t in batch])\n",
|
||
" next_states = np.array([t[3] for t in batch])\n",
|
||
" dones = np.array([t[4] for t in batch], dtype=np.float32)\n",
|
||
" return states, actions, rewards, next_states, dones\n",
|
||
"\n",
|
||
" def __len__(self) -> int:\n",
|
||
" \"\"\"Return the current number of transitions stored in the buffer.\"\"\"\n",
|
||
" return len(self.buffer)\n",
|
||
"\n",
|
||
"\n",
|
||
"class DQNAgent(Agent):\n",
|
||
" \"\"\"Deep Q-Network agent using PyTorch with GPU acceleration (MPS/CUDA).\n",
|
||
"\n",
|
||
" Inspired by:\n",
|
||
" - Lab 6A Dyna-Q: experience replay (store transitions, sample for updates)\n",
|
||
" - Classic DQN (Mnih et al., 2015): target network, minibatch SGD\n",
|
||
"\n",
|
||
" Uses Adam optimizer and Huber loss (smooth L1) for stable training.\n",
|
||
" \"\"\"\n",
|
||
"\n",
|
||
" def __init__(\n",
|
||
" self,\n",
|
||
" n_features: int,\n",
|
||
" n_actions: int,\n",
|
||
" lr: float = 1e-4,\n",
|
||
" gamma: float = 0.99,\n",
|
||
" buffer_size: int = 50_000,\n",
|
||
" batch_size: int = 64,\n",
|
||
" target_update_freq: int = 1000,\n",
|
||
" train_freq: int = 4,\n",
|
||
" seed: int = 42,\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Initialize DQN agent.\n",
|
||
"\n",
|
||
" Args:\n",
|
||
" n_features: Input feature dimension.\n",
|
||
" n_actions: Number of discrete actions.\n",
|
||
" lr: Learning rate for Adam optimizer.\n",
|
||
" gamma: Discount factor.\n",
|
||
" buffer_size: Maximum replay buffer capacity.\n",
|
||
" batch_size: Minibatch size for updates.\n",
|
||
" target_update_freq: Steps between target network syncs.\n",
|
||
" train_freq: Number of steps between each training update.\n",
|
||
" seed: RNG seed.\n",
|
||
"\n",
|
||
" \"\"\"\n",
|
||
" super().__init__(seed, n_actions)\n",
|
||
" self.n_features = n_features\n",
|
||
" self.lr = lr\n",
|
||
" self.gamma = gamma\n",
|
||
" self.batch_size = batch_size\n",
|
||
" self.target_update_freq = target_update_freq\n",
|
||
" self.train_freq = train_freq\n",
|
||
" self.update_step = 0\n",
|
||
" self.train_step = 0\n",
|
||
"\n",
|
||
" torch.manual_seed(seed)\n",
|
||
" self.device = torch.device(\"cpu\")\n",
|
||
" self.q_net = QNetwork(n_features, n_actions).to(self.device)\n",
|
||
" self.target_net = QNetwork(n_features, n_actions).to(self.device)\n",
|
||
" self.target_net.load_state_dict(self.q_net.state_dict())\n",
|
||
" self.target_net.eval()\n",
|
||
"\n",
|
||
" self.optimizer = optim.Adam(self.q_net.parameters(), lr=lr)\n",
|
||
" self.loss_fn = nn.SmoothL1Loss()\n",
|
||
"\n",
|
||
" self.replay_buffer = ReplayBuffer(buffer_size)\n",
|
||
"\n",
|
||
" def get_action(self, observation: np.ndarray, epsilon: float = 0.0) -> int:\n",
|
||
" \"\"\"Select action using epsilon-greedy policy over Q-network outputs.\"\"\"\n",
|
||
" if self.rng.random() < epsilon:\n",
|
||
" return int(self.rng.integers(0, self.action_space))\n",
|
||
"\n",
|
||
" phi = normalize_obs(observation)\n",
|
||
" with torch.no_grad():\n",
|
||
" state_t = torch.from_numpy(phi).float().unsqueeze(0).to(self.device)\n",
|
||
" q_vals = self.q_net(state_t).numpy().squeeze(0)\n",
|
||
" return epsilon_greedy(q_vals, 0.0, self.rng)\n",
|
||
"\n",
|
||
" def update(\n",
|
||
" self,\n",
|
||
" state: np.ndarray,\n",
|
||
" action: int,\n",
|
||
" reward: float,\n",
|
||
" next_state: np.ndarray,\n",
|
||
" done: bool,\n",
|
||
" next_action: int | None = None,\n",
|
||
" agent_id: str = \"single\",\n",
|
||
" ) -> None:\n",
|
||
" \"\"\"Store transition and perform a minibatch DQN update.\"\"\"\n",
|
||
" _ = next_action\n",
|
||
"\n",
|
||
" phi_s = normalize_obs(state)\n",
|
||
" phi_sp = normalize_obs(next_state)\n",
|
||
" self.replay_buffer.push(phi_s, action, reward, phi_sp, done)\n",
|
||
"\n",
|
||
" self.update_step += 1\n",
|
||
" # Optimize: only train every `train_freq` steps to save computation\n",
|
||
" if (\n",
|
||
" len(self.replay_buffer) < self.batch_size\n",
|
||
" or self.update_step % self.train_freq != 0\n",
|
||
" ):\n",
|
||
" return\n",
|
||
"\n",
|
||
" states_b, actions_b, rewards_b, next_states_b, dones_b = (\n",
|
||
" self.replay_buffer.sample(\n",
|
||
" self.batch_size,\n",
|
||
" self.rng,\n",
|
||
" )\n",
|
||
" )\n",
|
||
"\n",
|
||
" states_t = torch.from_numpy(states_b).float().to(self.device)\n",
|
||
" actions_t = torch.from_numpy(actions_b).long().to(self.device)\n",
|
||
" rewards_t = torch.from_numpy(rewards_b).float().to(self.device)\n",
|
||
" next_states_t = torch.from_numpy(next_states_b).float().to(self.device)\n",
|
||
" dones_t = torch.from_numpy(dones_b).float().to(self.device)\n",
|
||
"\n",
|
||
" q_values = self.q_net(states_t)\n",
|
||
" q_curr = q_values.gather(1, actions_t.unsqueeze(1)).squeeze(1)\n",
|
||
"\n",
|
||
" with torch.no_grad():\n",
|
||
" q_next = self.target_net(next_states_t).max(dim=1).values\n",
|
||
" targets = rewards_t + (1.0 - dones_t) * self.gamma * q_next\n",
|
||
"\n",
|
||
" loss = self.loss_fn(q_curr, targets)\n",
|
||
" self.optimizer.zero_grad()\n",
|
||
" loss.backward()\n",
|
||
" nn.utils.clip_grad_norm_(self.q_net.parameters(), max_norm=10.0)\n",
|
||
" self.optimizer.step()\n",
|
||
"\n",
|
||
" self.train_step += 1\n",
|
||
" if self.train_step % self.target_update_freq == 0:\n",
|
||
" self.target_net.load_state_dict(self.q_net.state_dict())\n",
|
||
"\n",
|
||
" def save(self, filename: str) -> None:\n",
|
||
" \"\"\"Save agent state using torch.save (networks + optimizer + metadata).\"\"\"\n",
|
||
" torch.save(\n",
|
||
" {\n",
|
||
" \"q_net\": self.q_net.state_dict(),\n",
|
||
" \"target_net\": self.target_net.state_dict(),\n",
|
||
" \"optimizer\": self.optimizer.state_dict(),\n",
|
||
" \"train_step\": self.train_step,\n",
|
||
" \"n_features\": self.n_features,\n",
|
||
" \"action_space\": self.action_space,\n",
|
||
" \"lr\": self.lr,\n",
|
||
" \"gamma\": self.gamma,\n",
|
||
" \"batch_size\": self.batch_size,\n",
|
||
" \"target_update_freq\": self.target_update_freq,\n",
|
||
" },\n",
|
||
" filename,\n",
|
||
" )\n",
|
||
"\n",
|
||
" def load(self, filename: str) -> None:\n",
|
||
" \"\"\"Load agent state from a torch checkpoint.\"\"\"\n",
|
||
" checkpoint = torch.load(filename, map_location=self.device, weights_only=False)\n",
|
||
" self.q_net.load_state_dict(checkpoint[\"q_net\"])\n",
|
||
" self.target_net.load_state_dict(checkpoint[\"target_net\"])\n",
|
||
" self.train_step = checkpoint.get(\"train_step\", 0)\n",
|
||
" self.optimizer = optim.Adam(self.q_net.parameters(), lr=self.lr)\n",
|
||
" self.optimizer.load_state_dict(checkpoint[\"optimizer\"])\n",
|
||
"\n",
|
||
" buffer_capacity = self.replay_buffer.buffer.maxlen\n",
|
||
" if buffer_capacity is None:\n",
|
||
" buffer_capacity = 50_000\n",
|
||
" self.replay_buffer = ReplayBuffer(buffer_capacity)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "91e51dc8",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Tennis Environment\n",
|
||
"\n",
|
||
"Creation of the Atari Tennis environment via Gymnasium (`ALE/Tennis-v5`) with standard wrappers:\n",
|
||
"- **Grayscale**: `obs_type=\"grayscale\"` — single-channel observations\n",
|
||
"- **Resize**: `ResizeObservation(84, 84)` — downscale to 84×84\n",
|
||
"- **Frame stack**: `FrameStackObservation(4)` — stack 4 consecutive frames\n",
|
||
"\n",
|
||
"The final observation is an array of shape `(4, 84, 84)`, which flattens to 28,224 features.\n",
|
||
"\n",
|
||
"The agent plays against the **built-in Atari AI opponent**."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 60,
|
||
"id": "13b99a28",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def create_env() -> tennis_v3.env:\n",
|
||
" \"\"\"Create a PettingZoo Tennis environment with grayscale, resized, frame-stacked observations.\"\"\"\n",
|
||
" return tennis_v3.env(obs_type=\"ram\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "18cb28d8",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Training & Evaluation Infrastructure\n",
|
||
"\n",
|
||
"Functions for training and evaluating agents in the single-agent Gymnasium environment:\n",
|
||
"\n",
|
||
"1. **`train_agent`** — Pre-trains an agent against the built-in AI for a given number of episodes with ε-greedy exploration\n",
|
||
"2. **`plot_training_curves`** — Plots the training reward history (moving average) for all agents"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 61,
|
||
"id": "06b91580",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def train_agent(\n",
|
||
" env: tennis_v3.env,\n",
|
||
" agent: Agent,\n",
|
||
" name: str,\n",
|
||
" episodes: int = 5000,\n",
|
||
" epsilon_start: float = 1.0,\n",
|
||
" epsilon_end: float = 0.05,\n",
|
||
" total_decay_steps: int = 500_000,\n",
|
||
" max_steps: int = 5000,\n",
|
||
") -> dict[str, list[float]]:\n",
|
||
" \"\"\"Train the given agent in the environment and return training history.\"\"\"\n",
|
||
" history = {\"avg_rewards\": [], \"abs_rewards\": [], \"lengths\": []}\n",
|
||
" global_step = 0\n",
|
||
" pbar = tqdm(range(episodes), desc=f\"Training {name}\", leave=True)\n",
|
||
"\n",
|
||
" for _ep in pbar:\n",
|
||
" env.reset()\n",
|
||
" last_obs = {}\n",
|
||
" last_action = {}\n",
|
||
" total_reward = {\"first_0\": 0.0, \"second_0\": 0.0}\n",
|
||
" steps_in_episode = 0\n",
|
||
"\n",
|
||
" for step_idx, agent_id in enumerate(env.agent_iter()):\n",
|
||
" if step_idx >= max_steps:\n",
|
||
" break\n",
|
||
"\n",
|
||
" steps_in_episode = step_idx\n",
|
||
" obs, reward, termination, truncation, _info = env.last()\n",
|
||
" done = termination or truncation\n",
|
||
" obs_arr = np.asarray(obs)\n",
|
||
"\n",
|
||
" total_reward[agent_id] += float(reward)\n",
|
||
"\n",
|
||
" epsilon = max(\n",
|
||
" epsilon_end,\n",
|
||
" epsilon_start\n",
|
||
" - global_step / total_decay_steps * (epsilon_start - epsilon_end),\n",
|
||
" )\n",
|
||
"\n",
|
||
" action = None if done else agent.get_action(obs_arr, epsilon=epsilon)\n",
|
||
"\n",
|
||
" if agent_id in last_obs:\n",
|
||
" agent.update(\n",
|
||
" state=last_obs[agent_id],\n",
|
||
" action=last_action[agent_id],\n",
|
||
" reward=float(reward),\n",
|
||
" next_state=obs_arr,\n",
|
||
" done=done,\n",
|
||
" next_action=action,\n",
|
||
" agent_id=agent_id,\n",
|
||
" )\n",
|
||
"\n",
|
||
" if action is not None:\n",
|
||
" last_obs[agent_id] = obs_arr\n",
|
||
" last_action[agent_id] = action\n",
|
||
" global_step += 1\n",
|
||
"\n",
|
||
" env.step(action)\n",
|
||
"\n",
|
||
" history[\"avg_rewards\"].append(\n",
|
||
" (total_reward[\"first_0\"] + total_reward[\"second_0\"]) / 2.0,\n",
|
||
" )\n",
|
||
" history[\"abs_rewards\"].append(abs(total_reward[\"first_0\"]))\n",
|
||
" history[\"lengths\"].append(float(steps_in_episode))\n",
|
||
"\n",
|
||
" recent_window = 50\n",
|
||
" if len(history[\"avg_rewards\"]) >= recent_window:\n",
|
||
" recent_avg = np.mean(history[\"avg_rewards\"][-recent_window:])\n",
|
||
" pbar.set_postfix(\n",
|
||
" avg50=f\"{recent_avg:.1f}\",\n",
|
||
" eps=f\"{epsilon:.3f}\",\n",
|
||
" len=f\"{steps_in_episode}\",\n",
|
||
" steps=f\"{global_step}\",\n",
|
||
" )\n",
|
||
"\n",
|
||
" return history\n",
|
||
"\n",
|
||
"def plot_training_curves(\n",
|
||
" training_histories: dict[str, dict[str, list[float]]],\n",
|
||
" path: str,\n",
|
||
" window: int = 100,\n",
|
||
") -> None:\n",
|
||
" \"\"\"Plot training curves for multiple agents, showing average rewards, reward intensity, and episode lengths.\"\"\"\n",
|
||
" fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(13, 10), sharex=True)\n",
|
||
"\n",
|
||
" for name, data in training_histories.items():\n",
|
||
" color = AGENT_COLORS.get(name, \"#7f8c8d\")\n",
|
||
"\n",
|
||
" rewards = data[\"avg_rewards\"]\n",
|
||
" abs_rewards = data[\"abs_rewards\"]\n",
|
||
" if len(rewards) >= window:\n",
|
||
" ma_rew = np.convolve(rewards, np.ones(window) / window, mode=\"valid\")\n",
|
||
" ma_abs = np.convolve(abs_rewards, np.ones(window) / window, mode=\"valid\")\n",
|
||
" x = np.arange(window - 1, len(rewards))\n",
|
||
"\n",
|
||
" ax1.plot(x, ma_rew, label=f\"{name} (Average)\", color=color, linewidth=2)\n",
|
||
" ax1.plot(\n",
|
||
" x,\n",
|
||
" ma_abs,\n",
|
||
" label=f\"{name} (Intensity)\",\n",
|
||
" color=color,\n",
|
||
" linewidth=1.5,\n",
|
||
" linestyle=\"--\",\n",
|
||
" alpha=0.6,\n",
|
||
" )\n",
|
||
" ax1.fill_between(\n",
|
||
" x,\n",
|
||
" ma_rew - np.std(rewards) * 0.2,\n",
|
||
" ma_rew + np.std(rewards) * 0.2,\n",
|
||
" color=color,\n",
|
||
" alpha=0.1,\n",
|
||
" )\n",
|
||
"\n",
|
||
" lengths = data[\"lengths\"]\n",
|
||
" if len(lengths) >= window:\n",
|
||
" ma_len = np.convolve(lengths, np.ones(window) / window, mode=\"valid\")\n",
|
||
" ax2.plot(x, ma_len, label=name, color=color, linewidth=2)\n",
|
||
" ax2.fill_between(\n",
|
||
" x,\n",
|
||
" ma_len - np.std(lengths) * 0.2,\n",
|
||
" ma_len + np.std(lengths) * 0.2,\n",
|
||
" color=color,\n",
|
||
" alpha=0.1,\n",
|
||
" )\n",
|
||
"\n",
|
||
" ax1.set_ylabel(f\"Reward (MA {window})\", fontsize=12)\n",
|
||
" ax1.set_title(\n",
|
||
" \"Training Rewards & Intensity (Self-Play)\", fontsize=14, fontweight=\"bold\",\n",
|
||
" )\n",
|
||
" ax1.legend(fontsize=9, loc=\"upper left\", ncol=2)\n",
|
||
" ax1.grid(alpha=0.3, linestyle=\"--\")\n",
|
||
" ax1.axhline(y=0, color=\"black\", linewidth=0.8, alpha=0.4)\n",
|
||
"\n",
|
||
" ax2.set_xlabel(\"Episodes\", fontsize=12)\n",
|
||
" ax2.set_ylabel(f\"Steps per Episode (MA {window})\", fontsize=12)\n",
|
||
" ax2.set_title(\n",
|
||
" \"Episode Duration (Stability/Survival)\", fontsize=14, fontweight=\"bold\",\n",
|
||
" )\n",
|
||
" ax2.grid(alpha=0.3, linestyle=\"--\")\n",
|
||
"\n",
|
||
" for ax in [ax1, ax2]:\n",
|
||
" ax.spines[\"top\"].set_visible(False)\n",
|
||
" ax.spines[\"right\"].set_visible(False)\n",
|
||
"\n",
|
||
" fig.tight_layout()\n",
|
||
" plt.savefig(path, dpi=150, bbox_inches=\"tight\")\n",
|
||
" plt.show()\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "9605e9c4",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Agent Instantiation & Incremental Training (One Agent at a Time)\n",
|
||
"\n",
|
||
"**Environment**: `ALE/Tennis-v5` (grayscale, 84×84×4 frames → 28,224 features, 18 actions).\n",
|
||
"\n",
|
||
"**Agents**:\n",
|
||
"- **Random** — random baseline (no training needed)\n",
|
||
"- **SARSA** — linear approximation, semi-gradient TD(0)\n",
|
||
"- **Q-Learning** — linear approximation, off-policy\n",
|
||
"- **Monte Carlo** — first-visit MC with linear weights (⚠️ checkpoint loading issues)\n",
|
||
"- **DQN** — deep Q-network with experience replay and target network (⚠️ `.pt` checkpoint loading issues)\n",
|
||
"\n",
|
||
"**Workflow**:\n",
|
||
"1. Train **one** selected agent (`AGENT_TO_TRAIN`)\n",
|
||
"2. Save its weights to `checkpoints/` (`.pkl` for linear agents, `.pt` for DQN)\n",
|
||
"3. Repeat later for another agent without retraining previous ones\n",
|
||
"4. Load all saved checkpoints before the final evaluation"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 72,
|
||
"id": "6f6ba8df",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"env = create_env()\n",
|
||
"env.reset()\n",
|
||
"obs, *_ = env.last()\n",
|
||
"\n",
|
||
"n_actions = env.action_space(\"first_0\").n\n",
|
||
"n_features = 128\n",
|
||
"\n",
|
||
"agent_random = RandomAgent(seed=42, action_space=int(n_actions))\n",
|
||
"agent_sarsa = SarsaAgent(n_features=n_features, n_actions=n_actions, alpha=1e-4)\n",
|
||
"agent_q = QLearningAgent(n_features=n_features, n_actions=n_actions, alpha=1e-4)\n",
|
||
"agent_mc = MonteCarloAgent(n_features=n_features, n_actions=n_actions, alpha=1e-4)\n",
|
||
"agent_dqn = DQNAgent(n_features=n_features, n_actions=n_actions)\n",
|
||
"\n",
|
||
"agents = {\n",
|
||
" \"Random\": agent_random,\n",
|
||
" \"SARSA\": agent_sarsa,\n",
|
||
" \"Q-Learning\": agent_q,\n",
|
||
" \"MonteCarlo\": agent_mc,\n",
|
||
" \"DQN\": agent_dqn,\n",
|
||
"}\n",
|
||
"\n",
|
||
"AGENT_COLORS = {\n",
|
||
" \"Random\": \"#95a5a6\",\n",
|
||
" \"SARSA\": \"#3498db\",\n",
|
||
" \"Q-Learning\": \"#e74c3c\",\n",
|
||
" \"MonteCarlo\": \"#2ecc71\",\n",
|
||
" \"DQN\": \"#9b59b6\",\n",
|
||
"}\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 75,
|
||
"id": "4d449701",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Starting training for Q-Learning...\n"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"application/vnd.jupyter.widget-view+json": {
|
||
"model_id": "0f0c108ba22041e9a6077cbfcbbd96ed",
|
||
"version_major": 2,
|
||
"version_minor": 0
|
||
},
|
||
"text/plain": [
|
||
"Training Q-Learning: 0%| | 0/10000 [00:00<?, ?it/s]"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Final average reward for Q-Learning: 0.00\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"AGENT_TO_TRAIN = \"Q-Learning\"\n",
|
||
"EPISODES = 10_000\n",
|
||
"FORCE_RETRAIN = True\n",
|
||
"\n",
|
||
"\n",
|
||
"training_histories = {}\n",
|
||
"agent = agents[AGENT_TO_TRAIN]\n",
|
||
"checkpoint_path = get_path(AGENT_TO_TRAIN)\n",
|
||
"\n",
|
||
"if checkpoint_path.exists() and not FORCE_RETRAIN:\n",
|
||
" agent.load(str(checkpoint_path))\n",
|
||
" training_histories[AGENT_TO_TRAIN] = {\n",
|
||
" \"avg_rewards\": [],\n",
|
||
" \"abs_rewards\": [],\n",
|
||
" \"lengths\": [],\n",
|
||
" }\n",
|
||
" print(f\"Loaded {AGENT_TO_TRAIN} from checkpoint.\")\n",
|
||
"else:\n",
|
||
" print(f\"Starting training for {AGENT_TO_TRAIN}...\")\n",
|
||
"\n",
|
||
" d_steps = 500_000 if AGENT_TO_TRAIN in [\"DQN\", \"MonteCarlo\"] else 1_000_000\n",
|
||
" e_end = 0.05 if AGENT_TO_TRAIN in [\"DQN\", \"MonteCarlo\"] else 0.01\n",
|
||
"\n",
|
||
" training_histories[AGENT_TO_TRAIN] = train_agent(\n",
|
||
" env=env,\n",
|
||
" agent=agent,\n",
|
||
" name=AGENT_TO_TRAIN,\n",
|
||
" episodes=EPISODES,\n",
|
||
" epsilon_start=1.0,\n",
|
||
" epsilon_end=e_end,\n",
|
||
" total_decay_steps=d_steps,\n",
|
||
" max_steps=5000,\n",
|
||
" )\n",
|
||
"\n",
|
||
" agent.save(str(checkpoint_path))\n",
|
||
" rewards = training_histories[AGENT_TO_TRAIN][\"avg_rewards\"]\n",
|
||
" if rewards:\n",
|
||
" avg_val = float(np.mean(rewards[-100:]))\n",
|
||
" print(f\"Final average reward for {AGENT_TO_TRAIN}: {avg_val:.2f}\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 76,
|
||
"id": "a13a65df",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"data": {
|
||
"image/png": "iVBORw0KGgoAAAANSUhEUgAABQkAAAPdCAYAAAApge72AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsnQW4XNXV/tf49bgS94QIAYJLcAleKFocWqAu/371r+1X+b56S0uheIu7u0NwAkQIkpBA3HN9/P+8a2afu+eMnTMu6/c8k9w7d+TYPnvvd79rLUc0Go2SIAiCIAiCIAiCIAiCIAh1i7PcGyAIgiAIgiAIgiAIgiAIQnkRkVAQBEEQBEEQBEEQBEEQ6hwRCQVBEARBEARBEARBEAShzhGRUBAEQRAEQRAEQRAEQRDqHBEJBUEQBEEQBEEQBEEQBKHOEZFQEARBEARBEARBEARBEOocEQkFQRAEQRAEQRAEQRAEoc4RkVAQBEEQBEEQBEEQBEEQ6hwRCQVBEARBEARBEARBEAShzhGRUBAEQRCqjBtvvJEcDofxKATjxo0zPu+///u/C/KZQu1dJ0L5Of/8841zOn/+fKoU/uu//svYrrvuuqts25HtXnbLLbfQXnvtRS0tLcbrdtttt6Juk94O0S5LSU9PDw0ZMoS/G8fG7/eX9PsFQRCE6kJEQkEQBEGwOfG0+nj++efl2BZJHNEfPp+PRo4cSUcddRTdcMMNFIlE5LiXgeeee45OPvlkGjNmDJ+ToUOH0h577EGXX345rVixoiDnvBDUsnhaLgFx3bp19Ne//pV/njhxIn3hC19Ies0TTzzB18cuu+xCXq+XWltbaezYsbTffvvRZZddRnfccUfRtxPbcM4559Cbb75JXV1dtt+f7l7f0NDA+3LGGWfQiy++SJVEY2MjXXHFFfzz6tWr6aqrrir3JgmCIAgVjLvcGyAIgiAIgj3mzZtHv/vd7wp62H70ox/Rzp07+WdM2quJQCBA69ev58eTTz5JTz/9NLuFhNJx3XXX0cUXX5zw3ObNm/nxzjvv0JFHHsnikZA/EKJmzpzJP48ePboiDulvf/tbdqyBr371q+R0JvoQfvrTn9Ivf/nLhOeCwSB1dnbSZ599Rq+++io/Tj/99KJu5+233278PHDgQN5WiJUQtPMB7jzsBx4QO//nf/6H76mVAkTCX/3qVxQKhejXv/41i7IQ8gVBEATBjIiEgiAIgmBTRAPbt2/nyZbiiCOOYCFEJ5Mo0t7eTm1tbTkd+1133ZUfheSSSy6hagNCKVyDcMf8+9//po6ODn7+1ltvpe9///s0e/ZsqlXyuX6Kwd/+9jfj50GDBvH1BHfV559/Ti+88ELNOfbKydFHH82PSgHi4M0338w/Qxz84he/mPD3ZcuWsWimmDp1Kp144ok0YMAA2rZtG7333nv08ssvl2Rbca9QHHvssfTzn/8858/ac889WdTEPejjjz/me5AK5f3JT37Cnz937lyqBBBufOihh/IiCoT7e++9l84888xyb5YgCIJQiUQFQRAEQbDNp59+GkU3qh4/+9nPMv79ueeei1577bXRuXPnRhsaGqJz5szh161cuTL6jW98I3rAAQdER40aFW1qaop6vd7oyJEjo8cdd1z0wQcfTPruG264IeGzdQ4++GDj+fPOOy/60UcfRc8444zooEGDoj6fj7///vvvT/rMsWPHptwXbLf+XStWrIj+/e9/j86aNYs/b8iQIdGLLrooum3btqTP7Orqiv7Xf/1XdPTo0fzaGTNmRK+66ireZ/OxsQL2J91+43P1v912221J7+/t7Y3+7W9/ix544IHRAQMGRD0eT3T48OHRU089Nbpw4cKE17777rsJn7d69Wrjbz/4wQ+M57/1rW8Zz2/YsCHhPa+99ho/v3Xr1uj3vve96KGHHsrHuaWlhb976NCh0cMPPzx68803RyORSML3m4/7xx9/HP3d734XnTZtGl8fJ554ovHaVatW8TnGPuH6wf499dRTGa+TzZs3R7/zne/wOcF7sD3Dhg2Lzps3L3rFFVdEX3311agdDjroION7LrvssqS/h8PhaC5kOuf633Ddr1u3LnrJJZfwOcUxwrG65ppr0rbJVA9zO37xxRejp59+Ol/D+MzW1tboPvvsE73yyiujgUAgaXv1z8Lxf/LJJ6Pz58+PNjc383k/+uijo0uWLEl6H77npJNO4naPc4HX41rB67FNO3bsSLvfwHyuUz1wTenn6cwzz0zaDuyX+juup56enqzn6D//+Y/xnv322y/p73/5y1+Mv2O/Ojs7k17T3d0dffbZZ1N+PtriBRdcEJ0wYQLfO/EZu+22W/RXv/pVys9KdS/D/3bOezr09+A86PzrX/9K+PtPfvKTlO/DuVLY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8+cXfdkAAKWJJCEAAEWWedGf66EXkBgY3uSMPoLBoJSXl8vYsWNljTXWkD322EOuvfZaaW5uHjGbfCQlAAtx+umnSzwet/+fcsopPf7+zTffyAknnCDTp0+X6upq23+TJk2StdZaS/bbbz+58MILpaGhwdfbKFMxEohPPPGEfbbGidcdd9whO+64o0ycOFEikYjU19fLtGnTLNl1/PHHy6OPPjrgy1IqdHv/4he/sP/rOen8888f6kUCAJSI8FAvAAAAGDwrrbSSXHTRRanfx4wZw+ZOttTp6uqSRYsW2eODDz6Q+++/X371q1/JjTfeaElDP9hhhx2kpqbG/q9JnOHqtddekwceeMD+v84668hWW22V9vfXX39dtt12W2lsbEx7fu7cufb43//+J3fddZfstNNOMnr0aPHT/hjqGL733nvtpzcmDjnkEPnb3/6W9rqmpiZ7aKLy3//+t3z55ZeWRByudDt6t6tu5+HkwAMPlF/+8peyYMECueaaa+TUU0+VyZMnD/ViAQB8jiQhAABDTFtBbbDBBj2e1+5mS2vZZZdNtUhBeqs1TdJoNz5NaLzyyiv2vCYM99prL7ntttvkRz/60aBuslgsJp2dnVJVVTVo37HpppvaY7i77rrrUv/Ptt1/9rOfpRKE2opQY2bFFVeU7u5u+fjjj+W5556Tr7/+Woa7/uyPoYxhTaa7yVs3SfjII4+kJQjXX399SwZq8lPjSRO6L774YtGWUVva1dbW9vl92o13OJ8btXWmnouuv/56u6GhXdxPO+20oV4sAIDfJQAAQFE9/fTTCS2C3cdNN93U63s+//zztPfoZ/z1r39NrLfeeomKiorE+PHjE4cffnhizpw5eb9LP8fV0tKSOOeccxLrrrtuoqamJhEOh+1z1llnncRPfvKTxL/+9a8ey/Hhhx8mjj766MSqq66aqKystMcqq6ySOOqooxLvv/9+1mX/4osvEj/60Y8So0ePTlRVVSW22GKLxOOPP27r7V22TB0dHYkrr7zSXq/vjUQiiUmTJiX23nvvxAsvvJDoi7POOivndlD33Xdfory8PPV33R7z5s1L/f3QQw9N/W2rrbYqeBtnvu/LL79MHHTQQYkJEyYkAoFA4t5777XX3XjjjYl99tknsfrqqyfGjh1r+6K2ttb2xSmnnJKYP39+zu/L9nCPKe96L7/88j22y6JFi+wYWH/99RN1dXW2jadMmZLYc889E4899liP12fuM91Hv/nNb+wYKCsrS0ydOjVx8skn2/OFamtrs3V1P/Ojjz5K+3tjY2Pad958881ZP+fll19Obae+bKPPPvsscfzxxyc233zzxDLLLGPHqK6Lboddd9018cADD/S6Hbq6uhLnnXdeYqWVVrLjaNq0abZdOzs7096Xb3/o7+7f9HW9HV+9rZ8ee3/+859Tv2usLl68OO07GxoabJ+7r7njjjt6bFN9XrdFPB6350488cTU61deeeVENBrtsX10nz3//PMFr3u281uu9y1YsCDxs5/9zI61YDBo21n3Wb7z6b777pv6+/bbb5/3OzU+c8W6evjhh1N/1+//6quvUvvpiCOOsPOpnqf0GNJtrsfEYYcdlnj77bd7fFa+84rSGPRuawAABhtJQgAARmCScNttt82aGFhxxRXTklv5Elhbb7113iTDfvvtl7YMd911lyUkc71ekyO33357j+XWC+bM12qCbOedd86ZJNR1mDlzZs7v0ovzyy+/fMCShOqiiy5Ke80FF1wwoElCTaRlbgs3SahJunz7QhMi3377bZ8TYPkSM++9954lxfJ9jibP8iXHNLGW7X0HH3xwwfvmqaeeSr1Pk9SZFi5cmPbZv/jFL7ImpvLtk3zb6MEHH+z1tZqIyrcddtlll6zv23333VPJtaFIEra3t1vS2X3u6quvTvtObxJRE/GZyd3TTz/d/nbMMceknjv22GNT7xk3blzik08+ybsvBjJJqN+niXTvay+77DI73tzfd9hhh7TPbm5utmSd+/fbbrst73c++eSTaeeZb775Ju3zcn2XJsfz7Q9NGurNkb4kCZuamuxcme+8BQDAQKK7MQAAQ0y77+m4U5m0S6V2Nczmqaeekm222Ua22GIL+c9//iNPPvmkPf/ZZ5/Z2FV//vOf837n+++/n5rNUyfx0DHGVl11VVuOzz//vMdMn5988okcfPDB1j1W6YQfhx56qA2w/5e//MXep3/T57T74SqrrGKv+/nPfy5z5sxJfc5uu+0m6667rvzrX/+Shx9+OOfy6Xe9+eab9n/tSnjAAQfIMsssY+uq20snuDjxxBOtm/Zmm20mA+GII46wCTOc/IvI008/PaDd+7RbrNIuhDruno7Z5o5LN2HCBNs2Oi6ajpWms/x+++23cuedd8rChQvt/7/5zW/kj3/8Y2qMuscee0wef/xxe7+Ow6ddqF0bbrhh3mWJRqOy55572mQgSr9Pt7lu4/vuu8/G+FNXXHGFrLfeenZ8ZPP888/b56y55ppy6623pibO0P//9re/lSlTpvS6XbSrsEuPnUy6PZZffnnbXuriiy+Wm266yfa7HkuzZs2yyTJ0IhNXX7ZROByWmTNn2rE0fvx464ba2tpqx5oeA+q8886TH//4xzJ16tSs66DHsm6/5ZZbTv7+97/bGJdKu+pq19xc229p6Pp9+umnNuGOS9fPHZNRhyuoqKiQI4880vaFuuGGG6zrtuvuu+9O/V9jzLsNc41HqMeDS+Nezxu6/XR76v7T89LKK6884Ovrfp8+tt9+e9v/2r1ZJ045/PDDU12g9Vw4b948iymlx3N7e7v9f9SoUXa85qPLr5Pc6LGs5xmdoOXkk0+2v+nn6Oe59Htd2g1ex9LUiXT0mK2srLTY/ec//2nnW+0yfNxxx8l7771X8PrquW+11VZLHU8aK36cgAcAMIwMaMoRAAD0qpBWTpmtaTJbvWgLFreFkv7U370tVlpbW7N+l9sS5fXXX089t8Yaa6S1dlLaUku7Cbu0RZm3dc0777yT+pv+X5/LbH02e/bstFYw2o3Ppd0zp0+fnrZsrrfeeivteW1p5uVtgajdYgeqJaHSbsDua9Zcc80BbUmoj3ytH3WfPfHEE4nrr78+cemll1rLxh/84AdprURzrVO2rsT5XqMtGL3L9cc//jGt+6+3VZt2ec7Vgu6EE05I/e3NN99M+1u2brrZHHLIIan3HHnkkVlf849//CPtWMp81NfXW2u/zBaGhWwjb1d67W6rXdwvvvhi2/7ebqzavT/Xdjj//PPTutpqizf3b5ttttmgtCTs7W8u7eIeCoVSr3nttddSXc29XY3d513a7dvdthqvru7u7sQGG2yQ99ylLUz1eBjoloSZx5xLz1/axdt9je7DbOcLb4vIfN959tlnp57XVr7e1tT5Wl7GYrHESy+9ZF3iNdb1GDrppJPSvsftnlxIS0Kl3aOzHRcAAAyG4FAnKQEAQN8ddNBB1opP6U+dCdOlLVbeeeedvO9fY401rDWg0lYu2vJn7733tpZI2nKmoaHBWm+5vBMRaGsh76Qq+n9vCzD3tTpjrdsqT3mXUQfl33fffbMum7bg8tJZbXUd3Ye3BeILL7wgA8m7vANNW3j93//9X9a/XXrppdYiSltIHXXUUXLSSSfZzKY647LLbfU3EDInlvC2dNMWUN598/bbb0tbW1vWz/G2StMWT156DBVCW4P1NnOvtv7S1rN6LGjL10w6qclZZ51lLf76SluMaas0XX6dNOXYY4+1CS10+3vXO9/211aELm2JqK1CXTqRx1DS1o0/+MEPUr//6U9/sp/aIk4nflFrr712WgtBbyvCnXfe2eLVpS0vdV9oK1s9ZnO1MP3e976Xtm8HyhlnnNHjOT0vHHbYYanfb7/9dvupLfnclqSZLf/y0c9yz696HnNbAbufq/bff/+0lpf6PdOmTZONN97Y3n/CCSfYMaSx7dXXOHbP02owticAAF4kCQEAGGLadTI5TnDaQ7tQ5uJ2pXNlXqwvXrw473dqN8S77rrLEghuN2XtJnnhhRfaxa92q/Re3Oqsv7m+K/M5NzmUuQy9LXO27+rNQF406/d6u33n6lqamUh0u2D3RrvAaoIlkyZrtDtjS0tL3vdr8negeLexzkqrXSVz7Rtd31zHk7frY2ZXVe2qOZA0HrQrqS67dlc/++yze8wKftlll/X5c7UrbSHJ5nz7Od+xrV1UCz1GBot2c3VpokuTnxr/3q72mdxutdm652o32AsuuEC+++4765p+44032lAD3lmGNTa9syAPRAyNGzcuLWnmpYk5N4GsSXBN/mp3ajcRqjczeuuG79IbJJqQduls55qI9t6g8G6z2bNn23H01Vdf9frZfT0WBvPGBQAAmRiTEACAEUjH3PKaO3du2u869lZv9CJYxx/Ulk46/p+OO6jJEh33ShNS2gpm9913t1aG3hZemd+V+Zw7JlrmMvS2zLlak5177rnWuq1YyVqXN0ngbb3mjm/mclsZ9SYzEefScQe9Cbt//OMfNtakJnJ1DMJcrQ+Xhncba3JSx+DzLp9332iLqlzHk7eFmdvyqq808dOX1oc6juP3v/99e2jrQR0r0B2Ds6mpyZY9VwI604cffihvvfVW2rh8v//9720sRV0fTf4VkojWY9s7fqh3++l+zEygFps7Vp62MNZk13XXXZcax7SsrCytla/ScUT/+9//2nLvtNNOOT9Xt9H06dPtoUkzTdxqMtxNEHtjYzBjSOkND43ZJ554wuJYW0RrMrmvrQi9r3e3kSZW9fPdBJ+2vPS2nn7wwQfTWp1ecskldlzqsapjEOr2GYiEvo6ZCQDAYKIlIQAAI9Att9ySSmjpT50owqUX/ZoQyKejo8O6GeuFu7bG+slPfmKTG/z73/9OTaahF/puAmXTTTdNvVe737377rup37UlkT7ncl+r3Re9iSPvMmrrHm9LJi/vd7lJJO3+mfnQ5MUmm2wiA+Ghhx5K68aoLaJ0m7i8STJNLLkt6zThcvXVVy/Vd2uXSNeKK65o3TQ1saTb/5577sn5Pm+CLld34Fwyt/Ff//rXtASOd9/oJCtVVVUyWHSdXV9//XXW12grNe8x5qWJVZcez97WbL1tI++2V9rlXluQ6nGrk/cU2lLV22JOE5WaNMo3GctA8a5fb8eBdqN26bACbgs77RrtTdQq7eau55XtttsubfsqnahIk4y6ntmSeN5koDduvP/X7aqTrihNvOlkNAPB27pPJ3RxJ8XR7eTtEl4InWDIPRdqzHu7smcmHDOPI/27+95c57lCeWPCGysAAAwGWhICADBMZzfWi0ydmTQbnbVVL+C33HJLG//LbfHitobqLamjSS6dkVZbuGy00UbWckpb6+lnaeIr88JeW7Ndc801dkGvySttmeSd3dhtOaQJSrflm36mJvLcLnqa2NTEgs6Eqi18vIlGL01KaaLMHUtMZ0jW12uyRRMQOsuttnjUJKe2JNt8882lr3RcNt2+ut2fffZZeemll1J/03XS7pPexIm3m6Kug86qq9tNx0/UmYeXho6F566rjv+n3b11zEhdZ23NlYu3O7QmXTQxoftUl1/3Qb7Wl7vssot9ryY/3ATSK6+8Yp+p3UzdmYSVziI9mLyzU+cav0+TmPrQVmq6vzVZouupSWxteenSePAe+71tI20lq8eUe/wef/zx1qpWkz7asrRQmmDWGWi1m6omdr3xnCuGB0Jml3hdpx133NG6tWsrYJ152KWtBXXmc22tqTcJ8rWwc7sae2c1dmnr43POOcfG3NN9ofGsLVN1m+m668zZLm3t6crs6qv7Xc8jus+1FfNA0K7Res7S85v3GNbjva+t8DR+dIxKTYi66+0mHDNbXmaOx6nfp+c+jed8if7eNDc3y0cffZT6XVsYAwAwqAZlOhQAALDUsxt7ZwDNnIlzl112yfqeFVZYITF37txeZz/97rvvev3+jTbayGYy9c7sWVFRkfP15eXlidtvvz1tXT/77LO0GYO9D53N0/u7l67DzJkze13GQmf7zJwhNddj7NixWWflbW9vT6yyyipZ3+OdPTVzhtlCZi/9+OOPE7W1tT0+NxwOJw488MCc20j3oXf2Xe9j/vz5vc4o+9577yWWWWaZvNvjuOOOS3tP5qy+mbx/09cWoqWlJW099JjJ97m5HmPGjEmbdbvQbXT00Udn/ft2222XmDp1atZjLXM7bL311lk/Q+PUO3P4QM9urNZdd92s33333Xf32I6/+MUv0l4zefLkHjNC6+zMOkO6zljuPZdkW4d8j2wzVW+xxRYFxVCu2Y17m6Fa6QzGmZ+fLabzzW7s0pmKMz9rr7326vE6nf15rbXWyrpumTOce7+nt/PDY489lvr7Siut1Ou6AwCwtOhuDADACKTdbXWcLG1dp11TdTB/bdmnLewyJ1HIRscNvOqqq6zVmras0pZAoVDIZmbV7sfatU5bJ3on2thnn32sldXRRx9tLbD0e/Whrbu0tdQbb7xhLW+8dLZPbQ2ns+VqCx9tnTNr1izrjumdjTSTroO27tPWizrOmLbq0+XT7oyrr766ze6s3Zd13MT+0nXT7aCfp7O/aoshbX3knZnWpeup28NdD/1dZzHVGWCXZhmUbkttzbjDDjtYKzjt3qktrPT7dLbjXCZNmmTbUVtk5RurLRdtragt8XQcOe0art+r22Ty5MnWIuvRRx+VK664QgabLvt+++2X+j1byyttbXbRRRdZCy13Zm49HrRrsbbqPOWUU6xlqnfW7UK30ZVXXmnjXmorQG0lpmPP6T7V92WbaCYbbfX561//2o53bU2rE7poK1edDKi/YzUWSltS6v7SGO7tu7Slobc7sM5qrdvRS1v+6pikGqfZziXaglD3kc5sra1pdXtpXOt6a8tGbcGo63399df3eO8DDzxg3fi1VZ+Od6hj+91www12LhoomS0jdXzKfOMq5qPrlzmeYLaWl3rc6IzPek7TY1PXTY9F3QYaX/3ljYVsk8sAADDQApopHPBPBQAAA0pn6tQEhOvpp5/OO/sxMJJoV2dNyChNWOYafxBLR7sZa+LUHVJAu0hndpXVRL9OpqNJWb0ZgaGhY0bqkA3adV0TsFoGaAIfAIDBREtCAAAADCkdr27XXXdNtRrUsTExcLQ1r459qq3R3AShtlLNTBBqC0J3RuBs4xGieLSltDu2pbbeJkEIACgGWhICADAC0JIQfqezZOukNTqJiHYr1hmnMTC0+7N3Ig9tmaaJQ+2qjeFHO3rpDPXahV671OtM0H2deAUAgP5gdmMAAAAMOR3DLRaLDfVi+Jo7huNvfvMbEoTDmI4tqUlzAACKjZaEAAAAAAAAQIljTEIAAAAAAACgxJEkBAAAAAAAAEocScJ+Dibc1NRkPwEAAAAAAICRjiRhPzQ3N0t9fb39HAk0manLSlITIOYAv6GMA4g5wM8o5wDirZhIEpZIwdLa2kqSECDmAN+hjAOIOcDPKOcA4q2YSBICAAAAAAAAJY4kIQAAAAAAAFDiSBKWgEAgINXV1fYTADEH+AllHEDMAX5GOQcQb8UUSDCbRZ/pzMY6cUljY6PU1dUNxn4BAAAAAAAAioaWhCVA88CLFi1i4hKAmAN8hzIOIOYAP6OcA4i3YiJJWCIFS1dXF0lCgJgDfIcyDiDmAD+jnAOIt2IiSQgAAAAAAACUOJKEAAAAAAAAQIkjSVgiM2LpBCvMbgwQc4DfUMYBxBzgZ5RzAPFWTMxu3A/MbgwAAAAAAAA/oSVhCYjH47JgwQL7CYCYA/yEMg4g5gA/o5wDiLdiIklYIqLR6FAvAlBSiDmAeAP8ijIOIOYAv4qWeO6EJCEAAAAAAABQ4kgSAgAAAAAAACWOJGGJzIg1evRoZjcGiDnAdyjjAGIO8DPKOYB4K6ZwUb8NQ1awlJeXs/UBYg7wHco4gJgD/IxyDiDeiomWhCUyI9bcuXOZ3Rgg5gDfoYwDiDnAzyjnAOKtmEgSlohEIjHUiwCUFGIOIN4Av6KMA4g5wK8SJZ47IUkIAAAAAAAAlDiShAAAAAAAAECJCyRKvS1lPzQ1NUl9fb00NjZKXV2dDHe6i6PRqITDYWY4Bog5wFco4wBiDvAzyjmAeCsmWhKWyIxYoVCIBCFAzAG+QxkHEHOAn1HOAcRbMZEkLJEZsebNm8fsxgAxB/gOZRxAzAF+RjkHEG/FRJIQAAAAAAAAKHEkCQEAAAAAAIASR5IQAAAAAAAAKHHMblwCsxu7Y1kEg+SEAWIO8B/KOICYA/yMcg4g3oqFrFEJSCQSEovF7CcAYg7wE8o4gJgD/IxyDiDeiokkYYkULAsXLiRJCBBzgO9QxgHEHOBnlHMA8VZMJAkBAAAAAACAEkeSEAAAAAAAAChxJAlLRCAQGOpFAEoKMQcQb4BfUcYBxBzgV4ESz50wu3GJzG4MAAAAAAAA5EJLwhIZ7Lazs5OJSwBiDvAdyjiAmAP8jHIOIN6KiSRhiRQsDQ0NJAkBYg7wHco4gJgD/IxyDiDeiokkIQAAAAAAAFDiSBICAAAAAAAAJY4kYYkIh8NDvQhASSHmAOIN8CvKOICYA/wqXOK5E2Y37gdmNwYAAAAAAICf0JKwRAa7bWtrY+ISgJgDfIcyDiDmAD+jnAOIt2IiSVgiBYu2ftSfAIg5wE8o4wBiDvAzyjmAeCsmkoQAAAAAAABAiSNJCAAAAAAAAJQ4koQlIBAISFlZmf0EQMwBfkIZBxBzgJ9RzgHEWzExu3E/MLsxAAAAAAAA/ISWhCUy2G1zczMTlwDEHOA7lHEAMQf4GeUcQLwVE0nCEilYWltbSRICxBzgO5RxADEH+BnlHEC8FRNJQgAAAAAAAKDEkSQEAAAAAAAAShxJwhKZEauyspLZjQFiDvAdyjiAmAP8jHIOIN6KidmN+4HZjQEAAAAAAOAntCQskcFuGxsbmbgEIOYA36GMA4g5wM8o5wDirWSThGeffbY1p/Y+Vl99dfvbokWL5Nhjj5XVVlvNus4ut9xyctxxx1nyy+urr76SXXbZRaqqqmTChAnyy1/+UqLRaNprnnnmGVlvvfWkvLxcVl55Zbn55pvF7wVLe3s7SUKAmAN8hzIOIOYAP6OcA4i3YgrLMDN9+nR54oknUr+Hw84izp492x4XX3yxrLnmmvLll1/K0Ucfbc/dc8899ppYLGYJwkmTJskLL7wg3333nRxyyCESiUTkggsusNd8/vnn9hp976233ipPPvmk/OQnP5HJkyfLjjvuOERrDQAAAAAAAAydYZck1KSgJvkyzZgxQ/7+97+nfl9ppZXk/PPPl4MOOshaCur7HnvsMXnvvfcsyThx4kSZOXOmnHfeeXLqqadaK8WysjK59tprZdq0aXLJJZfY56yxxhry/PPPy2WXXZYzSdjZ2WkP75iEKh6P20O5LR/1To8+XL09776/v88Hg8Een535vL7H/Znt9f1d9qFcp96eZ53YT0N57Ck35ognzhGc9wb3XO4t4yifKHOpRwx+3cj9P/U96rDUy4tz/ZStnOP6iWtCrt0HJx8RT/4/81puMOqwQ5Fjca9VR1SS8OOPP5YpU6ZIRUWFzJo1Sy688ELrWpyNdjWuq6tLtTZ88cUXZa211rIEoUsTf8ccc4y8++67su6669prtt9++7TP0deccMIJOZdJl+Gcc87p8fz8+fOlo6PD/q9doOvr6y2BqF17XdXV1VJbWysNDQ3S1dWVel6XW7tEazdqb3fo0aNHWzdo/WzvwTF27FgJhUIyb968tGXQLtXagnLhwoWp53TH6zbQ79Pv1c/R5dT/jx8/3pbPTXQqTZ6OGTNGWlpapLW1NfX8cF4nl+77cePGsU7sp2F17On36vv0e90TMfHEOYLz3uCcy9va2qyM03irqamhfKLMpR4xyHUjfU7/ps9rmTnUZS51WOrlfj/29GLfLed0WfywTn7cT6yTP/ZTIpGwz9C48+Oxp71sR9Tsxv/6179sI+q4g9pVWBNz3377rfzvf/+zDea1YMECWX/99a0lobYoVEcddZR1Q3700UdTr9OLB93gDz/8sOy0006y6qqryuGHHy6nnXZa6jX6N+2CrK/VnVZIS8Jll13WdqDutOGQEabV3cjI3LOf2E8ce8QT54iRfxeWczn7iWOPeOIcwbmc8okyl3oEdaORVocdcS0JNYnnWnvttWXjjTeW5ZdfXu666y758Y9/nJak06Sejk2o3YgHm2Zo9ZFJN7Q+su2UTLmez3x/f57v7Tv1YNCEpmab+7OMw3Gdiv0868R+6ssx4425zL9x7BFPuc4pnPf6tw1UZrxxLqfMJZ4G75yiZZy2aMhWxuU6v3Heow47kOVcqdXLs9UrR/o6Ffo868R+Kvaxl/CUccPpmByoeBpxsxtnGjVqlLX8++STT1LPNTc3y/e//31rWXjvvfemNZfUsQznzp2b9hnu7+44h7leoy0Cs7Ui9AM90LXJ6jBqNAr4GjEHEG+AX1HGAcQc4FcJcifDO0moXY8//fRTm3nYbUG4ww47WP/uBx54wMYt9NIxDN955520/tePP/64JQC11aH7Gp3R2Etfo88DAAAAAAAApWhYJQl/8YtfyL///W/54osv5IUXXpA999zTBl3cf//9UwlCHfTxxhtvtN/nzJljD3fQZP27JgMPPvhgeeutt2xswjPOOEP+7//+L9Vd+Oijj5bPPvtMTjnlFPnggw/kj3/8o3VnPvHEE4d47QEAAAAAAIChMazGJPzmm28sIaizyOgsvJtvvrn897//tf8/88wz8tJLL9nrVl555bT3ff7557LCCitYQvGhhx6y2Yy1ZaBOWHLooYfKueeem3rttGnT5J///KclBa+44gpZZpll5IYbbrAZjv1K+55ra8pC+6ADIOaAkYIyDiDmAD+jnAOIt2IaVrMbjxTailGnrW5sbEzNbgwAAAAAAACMVMOquzEGh05/vWDBgh7TYAMg5oCRjjIOIOYAP6OcA4i3YiJJWCKi0ehQLwJQUog5gHgD/IoyDiDmAL+KlnjuhCQhAAAAAAAAUOJIEgIAAAAAAAAljiRhicyINXr0aGY3Bog5wHco4wBiDvAzyjmAeCumcFG/DUNWsJSXl7P1AWIO8B3KOICYA/yMcg4g3oqJloQlMiPW3Llzmd0YIOYA36GMA4g5wM8o5wDirZhIEpaIRCIx1IsAlBRiDiDeAL+ijAOIOcCvEiWeOyFJCAAAAAAAAJQ4koQAAAAAAABAiQskSr0tZT80NTVJfX29NDY2Sl1dnQx3uouj0aiEw2FmOAaIOcBXKOMAYg7wM8o5gHgrJloSlsiMWKFQiAQhQMwBvkMZBxBzgJ9RzgHEWzGRJCyRGbHmzZvH7MYAMQf4DmUcQMwBfkY5BxBvxUSSEAAAAAAAAChxJAkBAAAAAACAEkeSEAAAAAAAAChxzG5cArMbu2NZBIPkhAFiDvAfyjiAmAP8jHIOIN6KhaxRCUgkEhKLxewnAGIO8BPKOICYA/yMcg4g3oqJJGGJFCwLFy4kSQgQc4DvUMYBxBzgZ5RzAPFWTCQJAQAAAAAAgBJHkhAAAAAAAAAocSQJS0QgEBjqRQBKCjEHEG+AX1HGAcQc4FeBEs+dMLtxicxuDAAAAAAAAORCS8ISGey2s7OTiUsAYg7wHco4gJgD/IxyDiDeiokkYYkULA0NDSQJAWIO8B3KOICYA/yMcg4g3oqJJCEAAAAAAABQ4kgSAgAAAAAAACWOJGGJCIfDQ70IQEkh5gDiDfAryjiAmAP8KlziuRNmN+4HZjcGAAAAAACAn9CSsEQGu21ra2PiEoCYA3yHMg4g5gA/o5wDiLdiIklYIgWLtn7UnwCIOcBPKOMAYg7wM8o5gHgrJpKEAAAAAAAAQIkjSQgAAAAAAACUOJKEJSAQCEhZWZn9BEDMAX5CGQcQc4CfUc4BxFsxMbtxPzC7MQAAAAAAAPyEloQlMthtc3MzE5cAxBzgO5RxADEH+BnlHEC8jagkYUdHh3R2dg7M0mDQCpbW1laShECREHNA8RBvQHERcwAxB/hVgtyJhPu60Z555hm5//775T//+Y+899570t7ebs9XVVXJGmusIZtuuqnssccesvXWWw/GPgMAAAAAAAAwFEnC7u5uue666+TSSy+VL774QsaMGSPrrbeeHHTQQTJ69GjLtjY0NMjnn38ut9xyi/zhD3+Q5ZdfXk4++WT56U9/KpFIZKCXGwAAAAAAAEAxk4Qrr7yydHV1yaGHHir77ruvJQjzee211+Tuu++WCy64QC6++GJLLGJoZ8SqrKxkdmOAmAN8hzIOIOYAP6OcA4i3YTe7sbYiPOyww6S8vLxPH66JxZtuuslaE/oJsxsDAAAAAACg5JKEGNlJQt3Fusy6rHonCgAxB/gFZRxAzAF+RjkHEG8janZjjIyCRSeYIR8MEHOA31DGAcQc4GeUcwDxNqxnN37iiSfkrrvukjfeeENmz55tyScd727KlCkyc+ZMG7Pwe9/73uAsLQAAAAAAAIChSxK2trZaAvCRRx6R6upqSwhuvvnmUlFRIR0dHfLdd9/JnXfeKX/+859lxx13tIlL9HUAAAAAAAAAfJIkPP300+Wpp56S66+/Xg455BCJRCI9XtPd3S1//etf5dhjj7XXX3HFFQO9vOgHHYdQE7aMRwgUBzEHFA/xBhQXMQcQc4BfBcidFD5xyeTJk+XII4+Uc889t9fXnnHGGXLDDTfInDlzxI9G2sQlAAAAAAAAwIBMXKKJsWWWWaag1y677LLS3Nxc6EdjkGkeeNGiRUxcAhQJMQcUD/EGFBcxBxBzgF8lyJ0UniRcd911rauxjk2Yj/5dX7feeusNxD7CAB3oXV1dJAmBIiHmgOIh3oDiIuYAYg7wqwS5k8LHJPz9738v22+/vay22mpy8MEHy/rrr29dkMvLy6Wzs9MmLnn11VfllltusVZrOgsyAAAAAAAAAB8lCTfddFN54YUXbEKSSy65RKLRaNpEGJpxDYfDlkg8//zzreUhAAAAAAAAAB9NXOKl4w2+88471nqwvb1dKisrrVXhjBkzSmIij5E2cYnuYnc/McMxQMwBfkIZBxBzgJ9RzgHE27BPEpa6kZYkBAAAAAAAAAaku7HXt99+K2+++abMnj071UJtypQpMnPmTJk6dWp/PhKDKB6P2ziRY8aMkWCw4LlqABBzwLBHGQcQc4CfUc4BxNuwTRLqmISnnHKKvPjii1lnytWurJtssolNcrLZZpsN5HJiKekYkgCKh5gDiDfAryjjAGIO8KtoiedOCk4S6mzFO++8syy//PI2MclGG21k4xBWVFRIR0eHjU/43//+V26++WbZdttt5Z///KdNYgIAAAAAAADAJ2MSagtBnb34ySeflPLy8pyv6+rqkm222UZisZglDf1opI1JqE3U582bJxMmTKC7MUDMAb5CGQcQc4CfUc4BxFsxFTxA3dtvvy2HHXZY3gShKisrs9fp6zE8aDfw0aNHM7MxQMwBvkMZBxBzgJ9RzgHE27DsbqxJpk8++aSg1+rr9PUYPgVLb8ldAMQcMBJRxgHEHOBnlHMA8TYsWxIedNBBctlll9mjpaUl62v0+UsvvVQuv/xyez2GTxP1uXPn2k8AxBzgJ5RxADEH+BnlHEC8DcuWhOedd5589dVXcvLJJ8upp54qq666qk1coi3UOjs7beKSjz76yGaC2Weffez1GD4KHHoSADEHjDiUcQAxB/gZ5RxAvA27JKGONXj77bfLiSeeKPfcc4+8+eablhhsb2+XyspKmTJlis1+vPfee9vMxwAAAAAAAAB8liR0aQKQJCAAAAAAAADgH4EEbZf7rKmpSerr66WxsVHq6upkuNNdrN3Aw+EwMxwDxBzgK5RxADEH+BnlHEC8DcuJS/riueeek3PPPXcwPhr9nBErFAqRIASKhJgDiod4A4qLmAOIOcCvAuROBidJ+Oyzz8o555wzGB+Nfs6INW/ePGY3BoqEmAOKh3gDiouYA4g5wK/i5E4GJ0kIAAAAAAAAwIcTl2y77bYFf+iXX37Zr4U5++yze7RAXG211eSDDz6w/19//fVy2223yeuvvy7Nzc3S0NAgo0aNSnv9okWL5Nhjj5UHH3xQgsGg/PCHP5QrrrhCampqUq95++235f/+7//klVdekfHjx9vrTznllH4tMwAAAAAAAFAyScJnnnlGxowZI5MnT+71ta2trf1eoOnTp8sTTzyxZAHDSxaxra1Nvv/979vjtNNOy/r+Aw88UL777jt5/PHHpbu7Ww4//HA56qijLLnoTjqyww47yPbbby/XXnutvPPOO3LEEUdYslFfBwAAAAAAAJSagpOEK6+8siy33HJpCbxcfvOb38hZZ53VvwUKh2XSpElZ/3bCCSekEpbZvP/++/LII49YC8ENNtjAnrvyyitl5513losvvlimTJkit956q3R1dcmf//xnKSsrs6Tkm2++KZdeemnOJGFnZ6c9XJpodPur68Md4FIfOvuUd8Lo3p5339/f57W1ZOZnZ3t+3Lhxqb/1dRmH6zrle551Yj8N5bGnz2srZTVSzhHE09KdO9hPQ3fsecs4/Z39xDmC897g140mTJhg//fGPeUTZe7SnMspc/PX6zLLOep71GGJp8Grl0+YMGGp42y4nvf0bwOWJNx4443lgQceKOi1hXxxLh9//LEl8yoqKmTWrFly4YUXWnKyEC+++KK1CHQThEpbDOoGeemll2TPPfe012y55ZaWIHTtuOOO8rvf/c66L48ePbrH5+oyZJuIZf78+dLR0WH/r6yslPr6eksgtre3p15TXV0ttbW19tmanHTV1dVJVVWVdY+ORqOp5/X7y8vL7bO9B8fYsWNthmKdgCTzAI7FYrJw4cK07T9x4kT7Pv1e/Rx9jX6uJi50+dxEp9Jtoa1EW1pa0lqBDud18iaVtdBkndhPw+nY0+/V5/T84J4PiSfOEZz3Budcrr0MNG41pnVoEconylzqEYNbN9Ln9LMWL15ssTfUZS51WOrlfj/29GLfLed0WfywTn7cT6yTP/ZTIpGwZdEckh+PvUgkIr0JJDLToDncddddcs0118gtt9wiU6dOzfva5557Tp566qk+tyb817/+ZRtRxyHULsOamPv222/lf//7n20wl7Yk3GabbXqMSXjBBRfIX/7yF/nwww97bBD9rGOOOca6Gk+bNk2uu+661N/fe+89a1GoP9dYY42CWhIuu+yy9v2604ZDRjhfNlvfowePJgj1IB4pWW5aPrGfRuqxp7/PnTvXYk5fQzwNz/3Eec8f+0krPG4Zp5UiP6zTSDzvsU6ls5/0/xpzelHklnEjfZ38uJ9YJ//sJ++1nFvOjfR1KuR51on9NBTHXjwelwULFli86d/8Fk+Z67RULQn33XdfexRiiy22sEdf7bTTTqn/r7322tZ6cfnll7cE5Y9//GMZKpqh1Ucm3dDeypF3p2TK9Xzm+/vzfCHfqT8zkxX9+ZzhtE7FfJ51Yj/15ZhxT8Aj6RxRzOdZJ/bTQB5jejx5f3Ls9T3OOEdw3uvLceBeeGQr4zj2iKdinFNKsR6RWc75YZ0KeZ51Yj8N5bEXHEbH5ECtUyGyf+Iwoa0EV111Vfnkk08Ker2OZZjZrFKbZGrTTHecQ/2pLXy83N9zjYUIAAAAAAAA+NmwThJq1+NPP/20oBmVlY5hqOOjvPbaa6nntNuz3vHUVonua5599lmb+dilMyFrF+ds4xH6RaFZYwDEHDDSUMYBxBzgZ5RzAPFWLAWPSVgMv/jFL2S33XazLsazZ8+2MQ115mEdK1D7hM+ZM8cer776qhx55JGW7NOxCnViEx0U0u2yrC0Dr732WksEHn744TaRyW233WZ/b2xstISgjk146qmn2niHRxxxhFx22WU5ZzfOpGMS6mCT+lnumIQAAAAAAADASDWsWhJ+8803sv/++1sST8c/1JlZ/vvf/1qCUGnib91117UEodJZivV376zLt956q6y++uqy3Xbbyc477yybb765XH/99am/a3Lvsccek88//1zWX399Ofnkk+XMM88sOEE4EmkeWCdeGUb5YMDXiDmAeAP8ijIOIOYAv0qQOxleLQlHipHWklC7W+tYjTrLc66BLQEQc8BIRBkHEHOAn1HOAcRbMZExAgAAAAAAAErcgCcJdczAyy+/XDbccMOB/mgAAAAAAAAAgyA8ULMQ//3vf7fxAJ9++mmJxWKy4oorDsRHY4CEwwOyqwEQc8CwQxkHEHOAn1HOAcTbsB+TMBqNysMPP2yJwYceekg6OjpkrbXWkv3220923313mT59uvjVSBuTEAAAAAAAABjQ7sbPPfecHH300TJp0iTZY4895LPPPpOf/exnNguMzhJ82mmn+TpBOBLpvmlra2N2Y4CYA3yHMg4g5gA/o5wDiLdhmSTU5N8KK6wgW221lSUKTzjhBPnoo4/klVdesaQhhnfBoq0fmcgaIOYAv6GMA4g5wM8o5wDirZgKHqjud7/7nUybNs3GHNREIQAAAAAAAIASa0m49957y3fffSc77rijjTl4++23S2tr6+AuHQAAAAAAAIDhkyS86667ZO7cuXLNNddIe3u7HHzwwTJx4kT50Y9+JP/85z8lEAgM7pKi33TflJWVsY+AIiHmgOIh3oDiIuYAYg7wqwC5k/7PbqytCm+77TZ7vPHGG/bctttuK0cccYTsvPPOMmrUKPErZjcGAAAAAACAn/Q7Sej1wQcfyN/+9jfrgvzFF19IOByWzTbbzMYv9KORliTUXdzS0iI1NTW0JgSIOcBXKOMAYg7wM8o5gHgblt2N81l99dXl/PPPl88++0yeffZZ+fGPfyz/+9//BuKjMUAFi44fyezGQHEQc0DxEG9AcRFzADEH+FWC3MnAJAm9Nt98cxu3ULsjAwAAAAAAABj+woW+cNGiRX3+8DFjxvT5PQAAAAAAAACGaZJw/Pjxff7wWCzW5/dgcGboqaysZDxCoEiIOaB4iDeguIg5gJgD/CpA7qTwJKH2zdZE0y677CLTp08f3D2DAT/QdaIVAMVBzAHFQ7wBxUXMAcQc4FcBcieFJwlPO+00ueOOO+See+6Rjz76SA444ADZf//9Zdlllx3cvYSlpglenZFZZ2LWgx7A4CLmgOIh3oDiIuYAYg7wqwS5k8InLtHZiz/99FN5/vnnbXKSSy65RKZNmyZbbrmlXHfddf0asxDFO9Db29uZ3Rgg5gDfoYwDiDnAzyjnAOJtWM9uvOmmm8pVV10ls2fPlgceeECWW245+cUvfiGTJ0+W3XbbTV566aXBWVIAAAAAAAAAwyNJ6AqFQrLzzjvLLbfcIu+9955sttlm8vDDD8ujjz46sEsIAAAAAAAAYHiMSZipra1N7rvvPrn99tvl8ccfl7KyMjnwwANljz32GNglxFLTcQirq6sZjxAoEmIOKB7iDSguYg4g5gC/CpA7kUBCBzkoUDQalX/9619y2223yYMPPmi/f//737dJTHbffXepqKiQUqCTgOhswY2NjTYZCAAAAAAAAFAS3Y2POuoomThxouy1114yb948ufzyy2XOnDnWmnDfffctmQThSKR5YJ1Ypg/5YADEHDAiUMYBxBzgZ5RzAPE2LLsb33DDDVJZWWktBqdOnSpvv/22PfI107ziiisGajmxlAVLV1eX/dT9AmBwEXNA8RBvQHERcwAxB/hVgtxJ38YkbG9vl3vvvbeg15IkBAAAAAAAAHyWJIzH44O7JAAAAAAAAACG95iEGLm0VadOsEJXY4CYA/yGMg4g5gA/o5wDiLdhO7sxHMxuDAAAAAAAgJJrSbjmmmvKX//6V5v8olCdnZ1y00032XsxtLSr+IIFC+gyDhBzgO9QxgHEHOBnlHMA8TbsxiQ87LDD5KSTTpLjjz/eZjfefvvtZb311pNp06ZJVVWVvaa1tVU+//xzefXVV+WJJ56QBx98UMrKyuSXv/zlYK8DChCNRtlOQBERcwDxBvgVZRxAzAF+FS3x3ElBScJTTjlFjjnmGLnxxhvl5ptvlr/97W+p8e3C4XDahtTeyzNmzJBzzjlHjjjiCBsLDwAAAAAAAIAPZjeura2VE044wR5ffPGFvPDCC/LBBx/IwoUL7e9jx46V1VdfXWbNmmUtDAEAAAAAAACMDExcUgITl2jrTh1PUrt/M8MxQMwBfkIZBxBzgJ9RzgHE27BsSYiRSxOD5eXlQ70YQMkg5gDiDfAryjiAmAP8KkDupLDZjTHyZ8SaO3cusxsDxBzgO5RxADEH+BnlHEC8FRNJwhJqpg6AmAP8iDIOIOYAP6OcA4i3YiFJCAAAAAAAAJQ4koQAAAAAAABAiWN24xKZ3TgajUo4HGZ2Y4CYA3yFMg4g5gA/o5wDiLcR0ZJQE2W//e1vZccdd5R1111XXn75ZXt+0aJFcumll8onn3wykMuJpZyhJxQKkSAEioSYA4qHeAOKi5gDiDnArwLkTvqXJPzmm28sMXjmmWfa/99++21paWmxv40ZM0auu+46ufLKKwd6f2EpZsSaN28esxsDRULMAcVDvAHFRcwBxBzgV3FyJxLuz4b75S9/Kc3NzfLmm2/KhAkT7OG1xx57yEMPPTRQ+wkAAAAAAADAcGtJ+Nhjj8lxxx0na665ZtYurCuuuKJ8/fXXA7F8AAAAAAAAAIZjkrC9vV3Gjx+f8+/ayhAAAAAAAACAj5OE2oLw2Wefzfn3++67z8YsxPAQDAatS7j+BEDMAX5CGQcQc4CfUc4BxFsx9StrdMIJJ8gdd9whv/vd76SxsTE1wKPOaHzwwQfLiy++KCeeeOJALyv6KZFISCwWs58ABh8xBxQP8QYUFzEHEHOAXyXInUgg0c/M0fnnny9nn322bURNEOodDv2//vzNb34jp556qvhVU1OT1NfXW4K0rq5ORsoMPbQmBIg5/P/27gTOxrp9/Pg1i2EsM3ZStiJkSSlKUg+ytlGJVEKJeIp6kEeb5ymkngolJFv0iF9ZQkqWFllC2UKylXXsBmOYmfv/ur7/3zm/OWPUOM7cc+Z7f96v13Fm7nPPOfeZ+1xznXP5fr+XwDLkOICYA2xGngOIt7DvbqwGDBhgRg1++umnZgSh/vG66qqrpE2bNqZxCQAAAAAAAADLi4SqXLlyTCsGAAAAAAAAcjk6WXhERERETh8C4CnEHEC8AbYixwHEHGCrCI/XTrK0JqGuMxjML0qbZdgot61JCAAAAAAAAFzydOOXXnrpvCLhjBkzZOPGjdKsWTOpUqWK2bZ582b56quvpEaNGnLvvfdm5a7hAq0Dnz17VmJiYjxfFQeIOcAu5DiAmANsRp4DiLewKxJqF+P0xowZY7rlbtiwwV8g9Nm0aZM0atRIypQpE9ojxSUllqNHj5ruxl4fOgu4gZgD3EO8Ae4i5gBiDrCVQ+0kuDUJ33jjDenZs+d5BUJVrVo1c9vQoUNDcY4AAAAAAAAAhGORcPfu3ZInT54L3q636T4AAAAAAAAALC0S6pqDI0eOlD179px3mxYH9baaNWuG4vgQItHRWZpZDoCYA3IdchxAzAE2I88BxFtYdTfO6PvvvzcNS1Tr1q2lUqVK5uutW7fKzJkzzTxubWDSoEEDsRHdjQEAAAAAACBeLxIqbVry4osvmmJgUlKS2RYbG2uKhwMHDrR6JGFuKxLqKdZzpOeHxiUAMQfYhBwHEHOAzchzAPHmpqDnoOqU4xkzZkhaWpocPHjQbCtRooRERgY1gxnZnFi0sJkvXz6KhIALiDnAPcQb4C5iDiDmAFs51E6CLxL6aFHQN5qOAiEAAAAAAACQ+wQ97O/333+XTp06SalSpaRgwYLmol937txZdu3aFdqjBAAAAAAAABBeIwk3b95smpIcO3ZM7rjjDqlWrZp/+6RJk+Tzzz83zU2qVKkS6uNFEHQdwpiYGKYaAy4h5gD3EG+Au4g5gJgDbBVB7SS4xiX33nuv/PDDD7Jw4cLzGpRoQ5PGjRtL/fr1zZqFNsptjUsAAAAAAACAkE83/uabb+Tpp5/OtIOxNjTp2bOnLFmyJJi7RjbQOnBiYqK5BpD9iDnAPcQb4C5iDiDmAFs51E6CKxKeO3dOYmNjL3h7/vz5zT4Inxf6qVOnKBICxBxgHXIcQMwBNiPPAcRb2BcJr7vuOhk7dqyZbpvZVNwPP/xQrr/++lAcHwAAAAAAAIBwLBIOHDhQtm3bJlWrVpV//vOfMmHCBHPp37+/2aa36T4X65VXXjELRaa/6P35nDlzRnr06CHFihUz3ZTvu+8+OXDgwHldl1u1amVGM5YsWVL69OkjKSkpAfvoVGgtYubNm1cqVapkjh0AAAAAAADwqqC6Gzdq1EjmzZtnCnBDhgwJuK127dry0Ucfyd/+9regDqh69ery9ddf/98BRv/fIfbu3Vvmzp0r06dPN41DdO3DNm3ayNKlS83tqamppkBYunRp01hl37598uijj0qePHlk0KBBZp8dO3aYfbp16yZTpkwxzVcef/xxueyyy6RZs2ZiIy226vRwvQZAzAE2IccBxBxgM/IcQLyFfXfj9Pbv3y+7du0yX5cvX94U6IKlIwlnzpwpP//883m36dTmEiVKyMcffyz333+/2bZ582apVq2aLFu2TG666Sb54osv5M4775S9e/dKqVKlzD6jRo2Sfv36ycGDByUmJsZ8rYVG7cLs065dOzl27JjMnz8/S8dJd2MAAAAAAACI10cSpqdFwUspDGa0detWKVOmjOTLl09uvvlmGTx4sJQrV05Wr15tmqE0adLEv69ORdbbfEVCvdaOy74CodLRgd27d5eNGzeatRR1n/T34dunV69eFzym5ORkc0lfJFRpaWnmonzTo7Xmmr7u+lfbfT8f7PbIyMjz7jvjdr3oMcfFxUlUVNQFj+XMTz/K6ZlT0z9quq+di9iese6cE9uDPfYLbec5cZ4u7rWXknJOoqKj072yeO0RT9n9N8Wbf/ccccyyIjrzIMKS52TjeeI52XSeNMelSlR0VMAeufs5EU+cp/B97ekt5+c54inczpN723lO2XmeHJ2hmpIqeSteJYU697jo2ov/noOsD2V33Sgrs0uDKhLqFN01a9aY6cY+48aNMyMBtZj20EMPyZtvvmkKUhejXr16Zn3AKlWqmKnCuq7hrbfeakb96YhFHQlYuHDhgJ/RgqDepvQ6fYHQd7vvtj/bR4toSUlJmXZt1kJlZmss6uhEXSdR6c/pFGjf/fgUKFBAChUqJEePHpWzZ8/6t2vBTtdNPHLkSMCaiUWKFDFrJep9p39x6DqM+vtMSEgIOAZdd1GnWR8+fNi/TU+8Pid9PH1cfYHoSEwtsur+eny+QqfS32vRokXl9N49cnbNykzODIBgBP5pBpCdzvHrBVxFjgPcRZ4D3HP2zOmA2kvGGouPFu+LFy9+wRrLyZMn5dSpU/7tOV030qX4sqVIqMVAnVrss379ennyySelVq1aphHI8OHDzehCndp7MVq0aOH/Wu9Li4b6ONOmTcu0eOcWbcjy7LPP+r/XE1q2bFkz/VlPmvJVZPV7Pbk+vu16EjNWhJW+cNLzbdf7zrhdL3pyM1aEM9vue2Hqdi0S6j6+x9LfpY7UzPiY+fLllf97OQIAAAAAAHhLhPP/C2sXqrFkdKEaizbc1QJgxu05VTfKiqCKhJs2bTKdhX20UYk+ye+++85UObUpyKRJky66SJiRjhq8+uqr5bfffpM77rjDVFR17cD0owm1u7FvurNer1wZOBLO1/04/T4ZOyLr93r8FypEaoVWLxnpL1ovmZ2UjC60PePPB7M9K4+p176fvdD+sQ2bSL56DTJ9XABZp4X5Q4cOmf9VulAsAwgN4g1wFzEHEHOA7TmucMlSQddesmN7qOpG2VYk1OGSvhF0Sht+NG/e3BQI1Y033iiTJ0+WS6VDM7dt2yaPPPKI1KlTxwyN1KnOvgLlli1b5PfffzdrFyq9fu2118zQSl/ldMGCBeZYr7nmGv8+2pk5Pd3Hdx820heDVq//6kURkSePuQC4xJhzHCkQESlRBQvSVRzIZsQb4C5iDiDmAOtzXIH/G/3nNUENcdGptj/++KP5Wkf56ZqBTZs29d+u86UzG3n3V/7xj3/IN998Izt37pQffvhBWrdubeZTt2/f3szb7tKli5n2u3jxYtPIpFOnTqa4p01LlB6DFgO1qLh27Vr58ssv5YUXXpAePXr4j0dHOW7fvl369u1ruiOPHDnSTGfu3bu32EqLgzqUNauVYwDEHJBbkOMAYg6wGXkOIN7cFNRIwg4dOsi//vUv2bNnj+karPOm77nnHv/tWsDTacIXa/fu3aYgqE04dG51gwYNZPny5f551m+//bYZNqkjCbVBinYl1iKfjxYU58yZY7oZa/FQR8917NjRHKtPxYoVZe7cuaYoOGzYMLniiitk7Nix5r5spXPadQFMPU8UCgFiDrAJOQ4g5gCbkecA4s1NEU7G/s1ZoF1VXn75ZTNtV9cH1CKcdiH2jSLU0XzPPPOMafhhI21coiMbtWNw+mnX4Tyv3jcFm/XRAGIOsAk5DiDmAJuR5wDiLexHEmqbZ137Ty8ZadeV/fv3h+LYAAAAAAAAALiAtpsAAAAAAACAx2VpJGHnzp3NWnZjxowx6/7p939F9//www9DcYy4RHoudFo06xEC7iDmAPcQb4C7iDmAmANsFUHtJGtrElaoUMGsZbdlyxbJkyeP+f6vCk56u3YRtlFuW5MQAAAAAAAACHnjEq/LbUVCXexWG8roepE0LgGIOcAm5DiAmANsRp4DiDc3sSahR2hHagDEHGAjchxAzAE2I88BxFtYdzf22bBhg8ybN0927txpvtdpyC1atJCaNWuG6vgAAAAAAAAAhGORMDk5WZ588kn56KOPRGcr+6aw6lDo/v37S4cOHWTs2LESExMT6uMFAAAAAAAAEA7Tjfv16yeTJk2S7t27y6ZNm+TMmTOmcKhfd+vWTSZPnix9+/YN9bEiSNpEpkiRInQ3BlxCzAHuId4AdxFzADEH2CqC2klwjUuKFy8urVq1kokTJ2Z6+yOPPCJffPGFHDp0SGyU2xqXAAAAAAAAACEfSXju3Dm56aabLnh7/fr1WVw1jOg08AMHDphrAMQcYBNyHEDMATYjzwHEW9gXCZs1ayZffvnlBW+fP3++NG3a9FKOCyEWxIBRAMQckCuQ4wBiDrAZeQ4g3sK6ccm///1vadu2rbRp00Z69OghlSpVMtu3bt0q7733nuzatUs++eQTOXLkSMDPFS1aNDRHDQAAAAAAACBni4TVqlUz1+vXr5dZs2Zl+r8c11xzzXk/l5qaGtxRAgAAAAAAAAivIuFLL71Ep9xc1qGnWLFinDOAmAOsQ44DiDnAZuQ5gHgL++7GXpcbuxvrgreRkUEtQQmAmAPCGjkOIOYAm5HnAOLNLVSNPJJUEhIS6G4MEHOAdchxADEH2Iw8BxBvYVkk1DUG586d6//+9OnT8tRTT8mvv/563r5TpkyRqKio0B0lAAAAAAAAgJwvEm7evNlMr/VJSkqS0aNHy+7du7Pr2AAAAAAAAACE+3RjljMEAAAAAAAAcj/WJPQAbVhSsmRJGpcAxBxgHXIcQMwBNiPPAcSbmygSeoCO+ExNTWXkJ0DMAdYhxwHEHGAz8hxAvIVtkTAiIiJL2xB+ieXw4cMUCQFiDrAOOQ4g5gCbkecA4s1N0Rez8/PPPy+DBw82X+vINPX4449LgQIFAvZL3+AEAAAAAAAAgCVFwoYNG543alDXuctMsWLF5Morr7z0owMAAAAAAAAQPkXCJUuWZO+RIFsxLRxwFzEHEG+ArchxADEH2CrC40vqRTi6yAEuyokTJyQ+Pt5Mq46Li+O3BwAAAAAAgFyN7sYeoHXg5ORkGpcAxBxgHXIcQMwBNiPPAcSbmygSeiSxHD16lCIhQMwB1iHHAcQcYDPyHEC8uYkiIQAAAAAAAOBxFAkBAAAAAAAAj7voIqGubTd79mxZt25d9hwRskV0dJYbWQMg5oBchRwHEHOAzchzAPEWtt2Ndfd8+fLJsGHDpFu3buJFdDcGAAAAAACAp0cSRkRESOXKleXQoUPZc0QIOS3snj59msYlgEuIOcA9xBvgLmIOIOYAWznUToJbk/Cf//ynvPvuu7Jly5bQnxVkywtdRz9e5KBRAMQcEPbIcQAxB9iMPAcQb24KaqG65cuXS7FixaRGjRpy++23S4UKFSQ2Nva8EYc6JRkAAAAAAACAhUVCHUXos3Dhwkz3oUgIAAAAAAAAWFwkTEtLC/2RINtowTYmJsZcA8h+xBzgHuINcBcxBxBzgK0iqJ1cfHdj0N0YAAAAAAAAdgmqcUn6tQkHDx4svXv3lq1bt5pt2kV3zZo1cvLkyVAdIy6R1oETExNpXAK4hJgD3EO8Ae4i5gBiDrCVQ+0kuCLh2bNnpU2bNnLLLbfIgAEDZPjw4fLHH3+Y2yIjI6Vp06Y0LQmzF/qpU6coEgLEHGAdchxAzAE2I88BxFvYFwlffPFFmTNnjrz//vuyZcuWgOJTvnz55IEHHpBZs2aF8jgBAAAAAAAAhFOR8L///a90795dunbtKkWLFj3v9mrVqsn27dtDcXwAAAAAAAAAwrFImJCQIDVr1rzg7VFRUWZtQoRPh57Y2Fi6GwPEHGAdchxAzAE2I88BxJubooP5obJly8rmzZsvePvSpUulUqVKl3JcCHFiiY+P53cKuISYA9xDvAHuIuYAYg6wVQS1k+BGEj700EMyevRoWbZsWcAvU33wwQcybdo0efTRR0N3pnBJdM3I48eP07gEcAkxB7iHeAPcRcwBxBxgK4faSXAjCbWj8fLly6Vhw4Zm/UEtEPbu3VuOHDkiu3fvlpYtW5rvET4v9KSkJClUqBBTjgFiDrAKOQ4g5gCbkecA4i3sRxLGxMTI/PnzZfz48XLllVdK1apVJTk5WWrVqiUTJkyQzz//3KxLCAAAAAAAAMDSkYRKRw8+/PDD5gIAAAAAAADAg0VClZqaKqtXr5adO3ea7ytWrCjXX389owjDjBZ0CxQowFRjgJgDrEOOA4g5wGbkOYB4c1OEo4scBEGnFffv318SEhL8DTH0D1iJEiVk0KBB0rlzZ7HViRMnTLdgbQYSFxeX04cDAAAAAAAAuL8moXY21iLgZZddJiNHjpSFCxeay3vvvWe2PfHEEzJq1KhLOzKEjBZxtalMkPVgAMQcELbIcQAxB9iMPAcQb2E/klCblZQtW1a+/vpryZMnT8Bt586dk0aNGsmePXtk+/btYqPcNpIwLS3NjPgsWbKkREYGVRcGQMwBYYkcBxBzgM3IcwDx5qagKkb79++Xtm3bnlcgVLqtXbt2cuDAgVAcHwAAAAAAAIBwLBJed9118uuvv17wdr2tdu3al3JcAAAAAAAAAMK5u/GIESOkVatWZtpx165dJTY21mxPSkoyaxFOmzZN5s2bF+pjRZC0oYxOi9ZrANmPmAPcQ7wB7iLmAGIOsFUEtZPg1iSsVauWaYSxb98+iY6OljJlypjte/fulZSUFPN9kSJFzvtlr127VmyQ29YkBAAAAAAAAEI+krBo0aJSrFgxqVy5csD2ChUqBHN3cGGxWy3q6nmjcQmQ/Yg5wD3EG+AuYg4g5gBbpVE7Ca5IuGTJktCfDWQrHeEJwD3EHEC8AbYixwHEHGCrFI/XToJqXAIAAAAAAADAHhQJAQAAAAAAAI+jSOgB2jRGG8nQ3Rgg5gDbkOMAYg6wGXkOIN7Cfk1C5L7Ekjdv3pw+DMAziDmAeANsRY4DiDnAVhHUThhJ6JUOPQcOHDDXAIg5wCbkOICYA2xGngOINzcx3dgjHMfJ6UMAPIWYA4g3wFbkOICYA2zleLx2EtR048TERDl27JiULVvWv23v3r0yatQoSU5Olvvuu0/q1q0byuMEAAAAAAAAEE5Fwq5du8qOHTtk+fLl5vsTJ07ITTfdJLt375bIyEgZNmyYzJ8/X26//fZQHy8AAAAAAACAcJhu/P3338udd97p/37y5MlmJOEPP/wgR48elVq1asmrr756SQc2ZMgQs2hkr169/Nu2bdsmrVu3lhIlSkhcXJy0bdvWrLWX3pEjR6RDhw7m9sKFC0uXLl3k5MmTAfusW7dObr31VsmXL58ZDTl06FCxmf4eixUrRndjgJgDrEOOA4g5wGbkOYB4C/si4aFDh+Tyyy/3fz979mxp0KCBGU1YqFAhefTRR2Xt2rVBH9SPP/4oo0ePNsVGn1OnTknTpk3NH8lFixbJ0qVL5ezZs3LXXXcFNOTQAuHGjRtlwYIFMmfOHPn222/NyEcfHfWo91O+fHlZvXq1vPHGG/LKK6/ImDFjxFb6O4uKiqJICBBzgHXIcQAxB9iMPAcQb2FfJNQRevv37zdfJyUlyXfffWcKbz7R0dFy+vTpoA5IR/1poe+DDz6QIkWK+LdrUXDnzp0yYcIEqVmzprlMnDhRVq1aZYqGatOmTWaa89ixY6VevXqmcDlixAiZOnWqGemopkyZYoqL48aNk+rVq0u7du3k6aeflrfeektspUXUhIQEuhsDxBxgHXIcQMwBNiPPAcRb2K9JWL9+fRk5cqRUrVrVFOXOnDkj99xzj//2X3/9NWCk4cXo0aOHtGrVSpo0aRIwZVkbouj/ouTNm9e/TacL6xqIOv1Z91+2bJkpYN5www3+fXS77rNixQozVVn3adiwocTExPj3adasmbz++utmqnT6wmT6x9ZL+tGIvj/YvlGMemx60U446bvh/NX29KMgg9muzy3jfWfcrj/ju85s/2CPPSef019t5zlxnnLytad8MUc88TeCv3vZ+7c8fY4jP5FzeR+R/e+NfF/zfo/3sLwvd+fzU2Z5js9PfCbks3v21CPS/vfrjJ/lsuM9bE7UWHyfVUNeJNSCmo4c1C7G6rnnnjOj8lRqaqpMnz5dmjdvftH3qyP+1qxZY6YbZ6RTmQsUKCD9+vWTQYMGmSf4/PPPm8fbt2+f2UdHN5YsWTLwCUZHS9GiRf0jH/W6YsWKAfuUKlXKf1tmRcLBgwfLwIEDz9t+8OBBUyBVsbGxEh8fbwqIOrrSR49Zp2BrAVJHMPromon58+c3ayimpKT4t+vjayFU7zv9i0PXFNQpwzoiMD19vvo7OHz4sH+bnnh9Tvp4+rj6Ajl+/Lj5ed1fj89X6FRaMNXfkY7i1GndPuH8nNKf3+LFi/OcOE9h9drT/6zQvw16//oHmXjibwR/97Lvb7lu1xynMa65ifxEzuV9RPa+NypYsKC51pyb/kMI7/d4D8v78uz5/KTvTX15Ttfm5/MTnwn57J599Yi0/y3Ga9zp/dh2nvLkySN/JcLJWAbNonPnzskvv/xinmCFChX82xMTE83032uvvTZg+1/5448/zAhAXUvQtxahdkeuXbu2vPPOO+b7r776Srp37246K+sH7/bt25tjqFu3rrz//vumeKhTkLds2XLeL0SLfPqzWtzUIqGueeij96FFTr2uVq1alkYSasMTPYF60nLDSEJ98WhS0RdxbqlyM5KQ85RbX3v6vTZV0pjzFQmJp/A7T/zds+M86RseX47zrb+b259Tbvy7x3PyznnSrzXm9EORL8fl9udk43niOdlzntJ/lvPludz+nLKynefEecqJ115aWprpwaHxprfZFk8Zn1NIi4ShNnPmTDMdWP/w+egbf30S+oS0SOe7TU+aFrt0tE7p0qXNSMY+ffqYdQb16/SVXa226rRkHd2o969NVbTIp4/ns3jxYmnUqJGpzmY2kjAj/Xktjur/6PiKhOHON9UYADEH2IYcBxBzgM3IcwDx5pagphsrLdppc5F58+aZhiJKRw62bNlSHn/8cVOYuxiNGzeW9evXB2zr1KmTWfdQpxinLx7q/1wqHbGowyjvvvtu8/3NN98sx44dM12L69Sp499H/6hqIxPfPgMGDDAjIX1DLXX0YpUqVbJUIMyNfCMtfFVlAMQcYAtyHEDMATYjzwHEm5uCGlq2e/duMw1YuwKvXbvWDMXUi36t2/Q23edi6PzrGjVqBFx0XrbOqdav1fjx42X58uWybds2mTx5sjzwwAPSu3dvU+BTOlVY10J84oknZOXKlaYjcs+ePU0H4zJlyph9HnroITM/vEuXLrJx40b55JNPZNiwYfLss8+KzYlF11gLk0GjgPWIOYB4A2xFjgOIOcBWDrWT4EYSagfiXbt2ybRp0+T+++8PuE2n9Xbs2NHsM2vWLAklXWuwf//+ZlqwjlrUEYFaJExvypQppjCoIxN1eq02Vxk+fLj/dp0mrGsb6vHpaEMdlfjSSy9J165dQ3qsAAAAAAAAQG4R1JqE2tXsmWeekddeey3T27WQN2LECNPJxUa5bU1CnW6t07K1gQvrEgLEHGATchxAzAE2I88BxFvYTzfWqcFacLoQbSai+yB8sBYhQMwBtiLHAcQcYDPyHEC8hXWRUBuKTJgwQU6fPn3ebTp6UNcO1DX/EB509GCpUqUYRQgQc4B1yHEAMQfYjDwHEG9hvyahNiaZO3eu6Tys6w9WqlTJbN+6datMmjRJihYtKrVq1ZLPPvss4OfatGkTmqPGRdEZ5WfPnjUNW/hfKCD7EXOAe4g3wF3EHEDMAbZyqJ0EtyZhVta102JU+rvW71NTU8UGrEkI4M+wdgzgHuINcBcxBxBzgK3S6OcQ3EjCxYsXh/5sAAAAAAAAAMg9RcLbbrst9EcCAAAAAAAAIPcUCX2Sk5NlzZo1kpCQILfccosUL148dEeGkIqOvqRTDYCYA8IWOQ4g5gCbkecA4i2suxur4cOHy2WXXSYNGjQwDUnWrVtnth86dMgUC8eNGxfK48Ql0DUk9ZxkZS1JAJeOmAPcQ7wB7iLmAGIOsFUktZPgioTjx4+XXr16SfPmzeXDDz8MaFCixahGjRrJ1KlTQ3mucAn0/Jw+fTrgPAHIPsQc4B7iDXAXMQcQc4CtHGonwRUJ//Of/8g999wjH3/8sdx1113n3V6nTh3ZuHFjKM4RQvRC147MFAkBdxBzgHuIN8BdxBxAzAG2cqidBFck/O2336RFixYXvL1o0aJy+PDhSzk3AAAAAAAAAMK5SFi4cGGz9uCF/PLLL1K6dOlLOS4AAAAAAAAA4VwkbNmypYwZM0aOHTt23m06zfiDDz6Qu+++OxTHhxCIiIiQmJgYcw0g+xFzgHuIN8BdxBxAzAG2iqB2IhFOEAvV7d27V+rVq2fma+uahFowfPjhhyU1NVU+/fRT0/V45cqVpomJjXR9v/j4eDl+/LjExcXl9OEAAAAAAAAA7o8kLFOmjKxevdp0N/7kk09MsfCjjz6Szz//XNq3by/Lly+3tkCYG+n5SUxMpHEJQMwB1iHHAcQcYDPyHEC8hf1IwowOHjwoaWlpUqJECYmMDKrumKvktpGEem4SEhKkZMmSnjg/QE4j5gDiDbAVOQ4g5gBbpVE7CW4kYefOnWXFihX+77U4WKpUKX8BSqca6z4AAAAAAAAALC0STpgwQbZt23bB23fs2CETJ068lOMCAAAAAAAA4JJsmXuqjU1iY2Oz464RZIcePR90NwbcQcwB7iHeAHcRcwAxB9gqgtqJRGf1lzVr1ixz8dGOxl9//fV5+x07dsxsv/HGG0N3pnDJL3RdQxGAO4g5wD3EG+AuYg4g5gBbRVA7yXqR8JdffpHp06f7f3G6JqF2OM74Cy1QoIA0bNhQ3nrrrdCfMQRFe9NosxVtssJoQiD7EXOAe4g3wF3EHEDMAbZyqJ1kfbpx//79JTEx0Vz0F/fhhx/6v/ddtBC1b98+mTNnjlx99dXZe/aQZXq+kpKSzDWA7EfMAe4h3gB3EXMAMQfYyqF2kvWRhBnbQgMAAAAAAADwcJEwo82bN5upyDqKsEqVKtKpUycztRUAAAAAAABA+MtykfDdd9+V4cOHyw8//CDFixf3b//888/lgQcekLNnz/q3jRgxQpYvXx6wH3KOb61I1iMEiDnANuQ4gJgDbEaeA4i3sFyTcPbs2XLVVVcFFP5SUlLk8ccfl6ioKBk/frysX79ehgwZIrt27ZLXXnstu44ZQSSWQoUKUSQEXELMAe4h3gB3EXMAMQfYKoLaSdaLhNrd+KabbgrYtnjxYjl48KD07t1bOnbsKNWrV5e+fftK27ZtZd68edlxzhDk4ptHjhyhcQngEmIOcA/xBriLmAOIOcBWDrWTrBcJDx8+LGXLlg3YtnDhQlNpbd26dcD2W265RX7//ffQnSlc8gtdp4PT3RhwBzEHuId4A9xFzAHEHGArh9pJ1ouEpUqVkv379wds++677yR//vxy7bXXBmyPiYkxFwAAAAAAAAAWFQlvuOEGmThxoiQmJprvN27cKCtXrpRmzZpJdHT0ed2Or7jiitAfLQAAAAAAAICc62788ssvy4033iiVK1c2aw+uXr3aTDXu37//efvOmDFDGjVqFOpjRZD0PMXFxdG4BHAJMQe4h3gD3EXMAcQcYKsIaidZH0lYs2ZNWbRokdSpU0f27t1rmphocxL9Pr0lS5aYKcgPPPBAdpwzBPlC13Oi1wCyHzEHuId4A9xFzAHEHGCrCGonEuHQzeKinThxQuLj4+X48eNmhF64S0tLM92NixYtKpGRWa4LAyDmgLBHjgOIOcBm5DmAeHMTFSOPSElJyelDADyFmAOIN8BW5DiAmANsleLx2glFQgAAAAAAAMDjKBICAAAAAAAAHkeR0COLbxYpUoTGJQAxB1iHHAcQc4DNyHMA8eamaFcfDTmWWPLmzctvHyDmAOuQ4wBiDrAZeQ4g3tzESEKPdMQ6cOCAuQZAzAE2IccBxBxgM/IcQLy5iSKhRziOk9OHAHgKMQcQb4CtyHEAMQfYyvF47YQiIQAAAAAAAOBxFAkBAAAAAAAAj4twvD6WMggnTpyQ+Ph4OX78uMTFxUm401OckpIi0dHRdDgGiDnAKuQ4gJgDbEaeA4g3NzGS0CMdsaKioigQAsQcYB1yHEDMATYjzwHEm5soEnqkI1ZCQgLdjQFiDrAOOQ4g5gCbkecA4s1NFAkBAAAAAAAAj6NICAAAAAAAAHgcRUIAAAAAAADA4+hu7IHuxr61LCIjqQkDxBxgH3IcQMwBNiPPAcSbW6gaeYDjOJKammquARBzgE3IcQAxB9iMPAcQb26iSOiRxHL48GGKhAAxB1iHHAcQc4DNyHMA8eYmioQAAAAAAACAx1EkBAAAAAAAADyOIqFHRERE5PQhAJ5CzAHEG2ArchxAzAG2ivB47YTuxh7pbgwAAAAAAABcCCMJPbLYbXJyMo1LAGIOsA45DiDmAJuR5wDizU0UCT2SWI4ePUqRECDmAOuQ4wBiDrAZeQ4g3txEkRAAAAAAAADwOIqEAAAAAAAAgMdRJPSI6OjonD4EwFOIOYB4A2xFjgOIOcBW0R6vndDdOAh0NwYAAAAAAIBNGEnokcVuT58+TeMSgJgDrEOOA4g5wGbkOYB4cxNFQo8kFh39qNcAiDnAJuQ4gJgDbEaeA4g3N1EkBAAAAAAAADyOIiEAAAAAAADgcRQJPSAiIkJiYmLMNQBiDrAJOQ4g5gCbkecA4s1NdDcOAt2NAQAAAAAAYJOwHUk4ZMgQ878mvXr18m/bv3+/PPLII1K6dGkpUKCAXH/99fLpp58G/NyRI0ekQ4cOEhcXJ4ULF5YuXbrIyZMnA/ZZt26d3HrrrZIvXz4pW7asDB06VGxf7DYxMZHGJQAxB1iHHAcQc4DNyHMA8SZeLxL++OOPMnr0aKlVq1bA9kcffVS2bNkis2fPlvXr10ubNm2kbdu28tNPP/n30QLhxo0bZcGCBTJnzhz59ttvpWvXrgGjAJs2bSrly5eX1atXyxtvvCGvvPKKjBkzRmxOLKdOnaJICBBzgHXIcQAxB9iMPAcQb54uEuqoPy30ffDBB1KkSJGA23744Qf5+9//LnXr1pUrr7xSXnjhBTNaUIt9atOmTTJ//nwZO3as1KtXTxo0aCAjRoyQqVOnyt69e80+U6ZMkbNnz8q4ceOkevXq0q5dO3n66aflrbfeypHnCwAAAAAAAOS0aAkzPXr0kFatWkmTJk3k1VdfDbitfv368sknn5jbtTg4bdo0OXPmjNx+++3m9mXLlpntN9xwg/9n9H4iIyNlxYoV0rp1a7NPw4YNTSMPn2bNmsnrr78uR48ePa8wqZKTk80l/WhElZaWZi5Kp0brRf+nRy8+f7Xd9/PBbtfnlvG+M27Xn/FdZ7Z/sMeek8/pr7bznDhPOfnaU76YI574G8Hfvez9W54+x5GfyLm8j8j+90a+r3m/x3tY3pe78/kpszzH5yc+E/LZPXvqEWn/+3XGz3LZ8R42J2osWWlmG1ZFQh3xt2bNGjPdODNaFHzwwQelWLFiEh0dLfnz55cZM2ZIpUqV/GsWlixZMuBndL+iRYua23z7VKxYMWCfUqVK+W/LrEg4ePBgGThw4HnbDx48aIqUKjY2VuLj400BMSkpyb+Prp1YqFAhU4DUEYw+umaiHr+uoZiSkuLfro+fN29ec9/pXxz6nKOioiQhISHgGPT5pqamyuHDh/3b9MTrc9LH08fV+zl9+rT5ukSJEub4fIVOpQVT/R3pKE6dluwTzs8p/fktXrw4z4nzFFavPd/fEX1c3x9i4om/Efzdy56/5Zrf9KLxVrBgQfITOZf3Edn83ki36c/ods2ZOZ1zeQ/L+3LbX3v6Yd+X5/RYbHhONp4nnpMd58lxHNO7QuPOxtdenjx5JNd0N/7jjz/MCEBdS9C3FqGOEKxdu7a888475nudarxy5UoZNGiQOREzZ86Ut99+W7777jupWbOm2T5x4kSzbmHGX4gW+bp3727WI9Qioa556PPLL7+Yqcd6Xa1atSyNJNSGJ3oC9aSFQ0WYUXe5o3LPeeI88dojnvgbkfv/F5a/5ZwnXnvEE38j+FtOfiLn8j6C90Y2jiQMmyKhFvx0OrBWPX200qlPQp+QFv50xOCGDRtMQS/9dGLdPmrUKLPO4HPPPRdQ2dVqq1aCp0+fbu5fm59okU8fz2fx4sXSqFEjU53NbCRhRvrzWv09fvy4v0gYzvQU6zHrsWblRQGAmANyC3IcQMwBNiPPAcSbJxuXNG7c2HQs/vnnn/0XHVmoTUz0ax1irbRgmJ4WFX1V0ptvvlmOHTvmb2SiFi1aZG7XRia+fbTj8blz5/z76OjFKlWqZKlAmFsTiw5lDZN6MGA9Yg4g3gBbkeMAYg6wlUPtJHzWJNT51zVq1AjYpvOydU61bteino4YfPLJJ+XNN98023U0oBb45syZY/bXqcLNmzeXJ554wows1J/p2bOn6WBcpkwZs89DDz1kph536dJF+vXrZ0YmDhs2zExbBgAAAAAAALwobEYS/hVdYHHevHmm8cZdd91l1i2cNGmSWYOwZcuW/v2mTJkiVatWNSMTdXuDBg1kzJgx/tt1mvBXX30lO3bskDp16pjpyS+99JJ07do1h54ZAAAAAAAAkLPCZk3C3CQ3rkmoXXW06yNrEgLEHGATchxAzAE2I88BxJubKBJ6oEgIAAAAAAAAWDHdGJf2v0/auZlBo4A7iDnAPcQb4C5iDiDmAFs51E4oEnrlhX727FmKhAAxB1iHHAcQc4DNyHMA8eYmRhICAAAAAAAAHkeREAAAAAAAAPA4ioQeoB2NtcEKnY0BYg6wDTkOIOYAm5HnAOLNTXQ3DgLdjQEAAAAAAGATRhJ6QFpamhw6dMhcAyDmAJuQ4wBiDrAZeQ4g3txEkdAjUlJScvoQAE8h5gDiDbAVOQ4g5gBbpXi8dkKREAAAAAAAAPA4ioQAAAAAAACAx1Ek9EhHrCJFitDdGCDmAOuQ4wBiDrAZeQ4g3twU7eqjIccSS968efntA8QcYB1yHEDMATYjzwHEm5sYSeiRjlgHDhyguzFAzAHWIccBxBxgM/IcQLy5iSKhRziOk9OHAHgKMQcQb4CtyHEAMQfYyvF47YQiIQAAAAAAAOBxFAkBAAAAAAAAj4twvD6WMggnTpyQ+Ph4OX78uMTFxUm401OckpIi0dHRdDgGiDnAKuQ4gJgDbEaeA4g3NzGS0CMdsaKioigQAsQcYB1yHEDMATYjzwHEm5soEnqkI1ZCQgLdjQFiDrAOOQ4g5gCbkecA4s1NFAkBAAAAAAAAj6NICAAAAAAAAHgcRUIAAAAAAADA4+hu7IHuxr61LCIjqQkDxBxgH3IcQMwBNiPPAcSbW6gaeYDjOJKammquARBzgE3IcQAxB9iMPAcQb26iSOiRxHL48GGKhAAxB1iHHAcQc4DNyHMA8eYmioQAAAAAAACAx1EkBAAAAAAAADyOIqFHRERE5PQhAJ5CzAHEG2ArchxAzAG2ivB47YTuxh7pbgwAAAAAAABcCCMJPbLYbXJyMo1LAGIOsA45DiDmAJuR5wDizU0UCT2SWI4ePUqRECDmAOuQ4wBiDrAZeQ4g3txEkRAAAAAAAADwOIqEAAAAAAAAgMdRJPSI6OjonD4EwFOIOYB4A2xFjgOIOcBWXs9xdDcOAt2NAQAAAAAAYBNGEnpksdvTp0/TuAQg5gDrkOMAYg6wGXkOIN7cRJHQI4lFRz/qNQBiDrAJOQ4g5gCbkecA4s1NFAkBAAAAAAAAj6NICAAAAAAAAHgcRUIPiIiIkJiYGHMNgJgDbEKOA4g5wGbkOYB4cxPdjYNAd2MAAAAAAADYhJGEHlnsNjExkcYlADEHWIccBxBzgM3IcwDx5iaKhB5JLKdOnaJICBBzgHXIcQAxB9iMPAcQb26iSAgAAAAAAAB4HEVCAAAAAAAAwOMoEnqkI1ZsbCzdjQFiDrAOOQ4g5gCbkecA4s1NdDcOAt2NAQAAAAAAYBNGEnpksdvjx4/TuAQg5gDrkOMAYg6wGXkOIN7cRJHQI4klKSmJIiFAzAHWIccBxBxgM/IcQLy5iSIhAAAAAAAA4HHROX0AufV/c3xrE+YGaWlpkpiYKPny5ZPISOrCADEH2IMcBxBzgM3IcwDxFkqFChX606a2FAmDoAU3VbZs2eDPDAAAAAAAAOAS7VcRFxd3wdvpbhzk/+bs3bv3Lyuw4UJHPGpB848//vjTFwMAYg7IbchxADEH2Iw8BxBvocRIwmygU3avuOIKyW20QEiRECDmABuR4wBiDrAZeQ4g3tzAAnUAAAAAAACAx1EkBAAAAAAAADyOIqEH5M2bV15++WVzDYCYA2xCjgOIOcBm5DmAeHMTjUsAAAAAAAAAj2MkIQAAAAAAAOBxFAkBAAAAAAAAj6NICAAAAAAAAHgcRUIAAAAAAADA4ygSesB7770nFSpUkHz58km9evVk5cqVOX1IQFgbPHiw3HjjjVKoUCEpWbKk3HvvvbJly5aAfc6cOSM9evSQYsWKScGCBeW+++6TAwcOBOzz+++/S6tWrSR//vzmfvr06SMpKSkB+yxZskSuv/5607muUqVKMmHCBFeeIxDOhgwZIhEREdKrVy//NmIOCK09e/bIww8/bPJYbGys1KxZU1atWuW/3XEceemll+Syyy4ztzdp0kS2bt0acB9HjhyRDh06SFxcnBQuXFi6dOkiJ0+eDNhn3bp1cuutt5r3oWXLlpWhQ4dyKuEpqamp8uKLL0rFihVNLF111VXy73//28SYD/EGBO/bb7+Vu+66S8qUKWPeP86cOTPgdjfja/r06VK1alWzj+bVefPm5b5T68BqU6dOdWJiYpxx48Y5GzdudJ544gmncOHCzoEDB3L60ICw1axZM2f8+PHOhg0bnJ9//tlp2bKlU65cOefkyZP+fbp16+aULVvWWbhwobNq1SrnpptucurXr++/PSUlxalRo4bTpEkT56effnLmzZvnFC9e3Onfv79/n+3btzv58+d3nn32WeeXX35xRowY4URFRTnz5893/TkD4WLlypVOhQoVnFq1ajnPPPOMfzsxB4TOkSNHnPLlyzuPPfaYs2LFCpOPvvzyS+e3337z7zNkyBAnPj7emTlzprN27Vrn7rvvdipWrOgkJSX592nevLlz7bXXOsuXL3e+++47p1KlSk779u39tx8/ftwpVaqU06FDB5NT//vf/zqxsbHO6NGjOZ3wjNdee80pVqyYM2fOHGfHjh3O9OnTnYIFCzrDhg3z70O8AcHTz1kDBgxwPvvsM628OzNmzAi43a34Wrp0qfksN3ToUPPZ7oUXXnDy5MnjrF+/PledXoqElqtbt67To0cP//epqalOmTJlnMGDB+focQG5SUJCgkk433zzjfn+2LFj5g++vsnz2bRpk9ln2bJl/mQVGRnp7N+/37/P+++/78TFxTnJycnm+759+zrVq1cPeKwHH3zQFCkBL0pMTHQqV67sLFiwwLntttv8RUJiDgitfv36OQ0aNLjg7WlpaU7p0qWdN954w79N4zBv3rzmg5HSD0Ca93788Uf/Pl988YUTERHh7Nmzx3w/cuRIp0iRIv6853vsKlWqcErhGa1atXI6d+4csK1Nmzam2KCINyB0MhYJ3Yyvtm3bmnhPr169es6TTz6Zq04x040tdvbsWVm9erUZTusTGRlpvl+2bFmOHhuQmxw/ftxcFy1a1FxrXJ07dy4gtnRYebly5fyxpdc6xLxUqVL+fZo1ayYnTpyQjRs3+vdJfx++fYhPeJVO4dcp+hnjgpgDQmv27Nlyww03yAMPPGCWw7juuuvkgw8+8N++Y8cO2b9/f0AsxsfHm2Vr0uc5nZKl9+Oj++t7zRUrVvj3adiwocTExATkOV3C4+jRo5xWeEL9+vVl4cKF8uuvv5rv165dK99//720aNHCfE+8AdnHzfhaZslnO4qEFjt06JBZAyN9kULp9xooAP5aWlqaWRftlltukRo1aphtGj+aIDSZXCi29Dqz2PPd9mf7aCExKSmJ0wNPmTp1qqxZs8asCZoRMQeE1vbt2+X999+XypUry5dffindu3eXp59+WiZOnOiPOfVn7yH1WguM6UVHR5v/ULuYXAjY7vnnn5d27dqZ/1DOkyePKcrre0td/0wRb0D2cTO+9l9gn9yW76Jz+gAAINxHNm3YsMH8jy+A7PHHH3/IM888IwsWLDALPQPI/v8A0xETgwYNMt9r0UJz3ahRo6Rjx478+oEQmjZtmkyZMkU+/vhjqV69uvz888+mSKhNFog3AOGGkYQWK168uERFRZ3XcVW/L126dI4dF5Bb9OzZU+bMmSOLFy+WK664wr9d40en8x87duyCsaXXmcWe77Y/20e7amnnLcArdDpxQkKC6fSt/3Orl2+++UaGDx9uvtb/hSXmgNDRDo/XXHNNwLZq1arJ77//HpCn/uw9pF5r3KaXkpJiOkReTC4EbNenTx//aEJdiuaRRx6R3r17+0fOE29A9nEzvkpfYJ/clu8oElpMp0PWqVPHrIGR/n+O9fubb745R48NCGe65q0WCGfMmCGLFi2SihUrBtyucaXTRdLHlq5HoR+ufLGl1+vXrw9IODpKSguAvg9muk/6+/DtQ3zCaxo3bmziRUdX+C46ykmnYvm+JuaA0NElNDRvpafrpZUvX958rXlPP9Skz1G6FIauzZQ+z+l/lmmR30dzpr7X1LWefPt8++23Zh3f9HmuSpUqUqRIEU4pPOH06dNmbbP0dCCHxooi3oDs42Z83WzLZ7uc7pyC7DV16lTTuWfChAmma0/Xrl2dwoULB3RcBRCoe/fuTnx8vLNkyRJn3759/svp06f9+3Tr1s0pV66cs2jRImfVqlXOzTffbC4+KSkpTo0aNZymTZs6P//8szN//nynRIkSTv/+/f37bN++3cmfP7/Tp08f0x35vffec6Kiosy+gNel726siDkgdFauXOlER0c7r732mrN161ZnypQpJh9NnjzZv8+QIUPMe8ZZs2Y569atc+655x6nYsWKTlJSkn+f5s2bO9ddd52zYsUK5/vvvzfdydu3bx/QQbJUqVLOI4884mzYsMG8L9XHGT16NKcTntGxY0fn8ssvd+bMmePs2LHD+eyzz5zixYs7ffv29e9DvAHBS0xMdH766Sdz0RLXW2+9Zb7etWuXq/G1dOlSk1vffPNN89nu5ZdfdvLkyeOsX78+V51eioQeMGLECFPMiImJcerWressX748pw8JCGuaXDK7jB8/3r+PJpWnnnrKKVKkiEkQrVu3NoXE9Hbu3Om0aNHCiY2NNW8Gn3vuOefcuXMB+yxevNipXbu2ic8rr7wy4DEAL8tYJCTmgND6/PPPzX9m6X8mV61a1RkzZkzA7Wlpac6LL75oPhTpPo0bN3a2bNkSsM/hw4fNh6iCBQs6cXFxTqdOncyHtfTWrl3rNGjQwNyHFkr0wxrgJSdOnDD5TD+P5cuXz7zfGzBggJOcnOzfh3gDgqefpzL77KYFerfja9q0ac7VV19tPttVr17dmTt3bq47tRH6T06PZgQAAAAAAACQc1iTEAAAAAAAAPA4ioQAAAAAAACAx1EkBAAAAAAAADyOIiEAAAAAAADgcRQJAQAAAAAAAI+jSAgAAAAAAAB4HEVCAAAAAAAAwOMoEgIAAAAAAAAeR5EQAAAAIfXKK69IRESEq7/VnTt3msecMGGCq48LAABgC4qEAAAAHqeFNS2wXeiyfPnynD5EAAAAZLPo7H4AAAAA5A7/+te/pGLFiudtr1Sp0kXdzwsvvCDPP/98CI8MAAAA2Y0iIQAAAIwWLVrIDTfccOlvMKOjzQUAAAC5B9ONAQAAkOU1/9588015++23pXz58hIbGyu33XabbNiw4S/XJFywYIE0aNBAChcuLAULFpQqVarIP//5z4B9EhISpEuXLlKqVCnJly+fXHvttTJx4sTzjuXYsWPy2GOPSXx8vLm/jh07mm2Z2bx5s9x///1StGhRc59aBJ09e3bAPufOnZOBAwdK5cqVzT7FihUzx6rHDAAA4BX8Fy8AAACM48ePy6FDhwJ+G1rs06KZz6RJkyQxMVF69OghZ86ckWHDhkmjRo1k/fr1priXmY0bN8qdd94ptWrVMlOa8+bNK7/99pssXbrUv09SUpLcfvvtZnvPnj3NtOfp06ebYqAWAJ955hmzn+M4cs8998j3338v3bp1k2rVqsmMGTNMoTCzx73lllvk8ssvN9OfCxQoINOmTZN7771XPv30U2ndurW/qDl48GB5/PHHpW7dunLixAlZtWqVrFmzRu644w5eHQAAwBMoEgIAAMBo0qTJeb8JLehpMdBHi3hbt241hTfVvHlzqVevnrz++uvy1ltvZfqb1BF5Z8+elS+++EKKFy+e6T5jxoyRTZs2yeTJk6VDhw5mmxYBdaSirnHYuXNnKVSokBkF+O2338rQoUOlT58+Zr/u3bvL3/72t/PuUwuL5cqVkx9//NE8D/XUU0+ZUYL9+vXzFwnnzp0rLVu2NMcAAADgVUw3BgAAgPHee++Zgl76ixb20tNReL4CodKRd1oknDdv3gV/izolWM2aNUvS0tIy3Ud/vnTp0tK+fXv/tjx58sjTTz8tJ0+elG+++ca/n653qIVBn6ioKPn73/8ecH9HjhyRRYsWSdu2bc3IRx0hqZfDhw9Ls2bNTKFzz549/uPTUYe6DQAAwKsoEgIAAMBf8NPRhOkvGUfo6bp9GV199dVmzcILefDBB820X53Oq1OS27VrZ6b9pi8Y7tq1y9x3ZGTg21OdTuy73Xd92WWXmXUN09M1DtPTEY86NfnFF1+UEiVKBFxefvll/xqISqdA65RmfR41a9Y0IxTXrVvHqwIAAHgK040BAACQrbTBiU4RXrx4sZnaO3/+fPnkk0/MWoZfffWVGQkYar4C5D/+8Q8zcjAzlSpVMtcNGzaUbdu2mZGOejxjx441zVlGjRplCpsAAABeQJEQAAAAWZbZlNxff/1VKlSo8Kc/pyMEGzdubC66duGgQYNkwIABpnCoIxa1W7KO3tPiXvrRhNqdWOntvuuFCxeaKcjpRxNu2bIl4PGuvPJK/5TlzNZazEi7H3fq1Mlc9L61cKgNTSgSAgAAr2C6MQAAALJs5syZ/rX81MqVK2XFihXSokWLC/6Mrg+YUe3atc11cnKyudbGIfv37zcjDH1SUlJkxIgRphioDUx8++n2999/379famqq2S+9kiVLmm7Jo0ePln379p33+AcPHvR/resUpqePp6MMfccGAADgBYwkBAAAgKFNSnwj99KrX7++f3SfFs+0O7A2DtEi2jvvvCPFihWTvn37XvC3qGv+6XTjVq1amZGAuhbgyJEj5YorrjD3pbp27WoKeo899pisXr3ajEz8n//5H1m6dKl5DO1srO666y6zvuHzzz9v1kG85ppr5LPPPpPjx49n2ohF71/XGXziiSfM6MIDBw7IsmXLZPfu3bJ27Vqzn96HFhTr1KljRhSuWrXKPHbPnj15ZQAAAM+gSAgAAADjpZdeyvQ3MX78eFNEU48++qgpGGrhTot92uzk3XffNc1ELuTuu+82Bb1x48aZDsPFixc3IwMHDhwo8fHx/nULlyxZYop/EydOlBMnTphmJPrYWjj00ceePXu29OrVSyZPniwRERHm/v/zn//IddddF/C4WvzTgp8+zoQJE8yIQR1hqPulf67aQVnvU9cj1MKnFjJfffVV08AEAADAKyIcbfsGAAAA/Akt8lWsWFHeeOMN0wwEAAAAdmFNQgAAAAAAAMDjKBICAAAAAAAAHkeREAAAAAAAAPA41iQEAAAAAAAAPI6RhAAAAAAAAIDHUSQEAAAAAAAAPI4iIQAAAAAAAOBxFAkBAAAAAAAAj6NICAAAAAAAAHgcRUIAAAAAAADA4ygSAgAAAAAAAB5HkRAAAAAAAAAQb/t/P33lbPIBEvsAAAAASUVORK5CYII=",
|
||
"text/plain": [
|
||
"<Figure size 1300x1000 with 2 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"plot_training_curves(\n",
|
||
" training_histories,\n",
|
||
" f\"plots/{AGENT_TO_TRAIN}_training_curves.png\",\n",
|
||
" window=100,\n",
|
||
")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "56959ccd",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Multi-Agent Evaluation & Tournament"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "0698f673",
|
||
"metadata": {},
|
||
"source": [
|
||
"### Loading Checkpoints & Running Evaluation\n",
|
||
"\n",
|
||
"Before evaluation, we load the saved weights for each trained agent from the `checkpoints/` directory. If a checkpoint is missing, the agent will play with its initial weights (zeros), which is effectively random behavior."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 65,
|
||
"id": "5315eb02",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Warning: Missing checkpoint for Random\n",
|
||
"Warning: Missing checkpoint for MonteCarlo\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"for name, agent in agents.items():\n",
|
||
" checkpoint_path = get_path(name)\n",
|
||
" if checkpoint_path.exists():\n",
|
||
" agent.load(str(checkpoint_path))\n",
|
||
" else:\n",
|
||
" print(f\"Warning: Missing checkpoint for {name}\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "3047c4b0",
|
||
"metadata": {},
|
||
"source": [
|
||
"After individual pre-training against Atari's built-in AI, we move on to **head-to-head evaluation** between our agents.\n",
|
||
"\n",
|
||
"**PettingZoo Environment**: unlike Gymnasium (single player vs built-in AI), PettingZoo (`tennis_v3`) allows **two Python agents** to play against each other. The same preprocessing wrappers are applied (grayscale, resize to 84×84, 4-frame stacking) via **SuperSuit**.\n",
|
||
"\n",
|
||
"**Match Protocol**: each matchup is played over **two legs** with swapped positions (`first_0` / `second_0`) to eliminate any side-of-court advantage. Results are tallied as wins, losses, and draws."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 66,
|
||
"id": "d04d37e0",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def run_match(\n",
|
||
" env: tennis_v3.env,\n",
|
||
" agent_first: Agent,\n",
|
||
" agent_second: Agent,\n",
|
||
" episodes: int = 10,\n",
|
||
" max_steps: int = 4000,\n",
|
||
") -> dict[str, int]:\n",
|
||
" \"\"\"Run head-to-head matches between two agents and count wins/draws.\"\"\"\n",
|
||
" wins = {\"first\": 0, \"second\": 0, \"draw\": 0}\n",
|
||
"\n",
|
||
" for _ep in range(episodes):\n",
|
||
" env.reset()\n",
|
||
" rewards = {\"first_0\": 0.0, \"second_0\": 0.0}\n",
|
||
"\n",
|
||
" for step_idx, agent_id in enumerate(env.agent_iter()):\n",
|
||
" if step_idx >= max_steps:\n",
|
||
" break\n",
|
||
"\n",
|
||
" obs, reward, termination, truncation, _info = env.last()\n",
|
||
" done = termination or truncation\n",
|
||
" rewards[agent_id] += float(reward)\n",
|
||
"\n",
|
||
" if done:\n",
|
||
" action = None\n",
|
||
" else:\n",
|
||
" current_agent = agent_first if agent_id == \"first_0\" else agent_second\n",
|
||
" action = current_agent.get_action(np.asarray(obs), epsilon=0.0)\n",
|
||
"\n",
|
||
" env.step(action)\n",
|
||
"\n",
|
||
" if rewards[\"first_0\"] > rewards[\"second_0\"]:\n",
|
||
" wins[\"first\"] += 1\n",
|
||
" elif rewards[\"second_0\"] > rewards[\"first_0\"]:\n",
|
||
" wins[\"second\"] += 1\n",
|
||
" else:\n",
|
||
" wins[\"draw\"] += 1\n",
|
||
"\n",
|
||
" return wins\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "150e6764",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Evaluation against the Random Agent (Baseline)\n",
|
||
"\n",
|
||
"To quantify whether our agents have actually learned, we first evaluate them against the **Random agent** baseline. A properly trained agent should achieve a **win rate significantly above 50%** against a random opponent.\n",
|
||
"\n",
|
||
"Each agent plays **two legs** (one in each position) for a total of 20 episodes. Only decisive matches (excluding draws) are counted in the win rate calculation."
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 67,
|
||
"id": "1b85a88f",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def evaluate_vs_random(\n",
|
||
" agents: dict[str, Agent],\n",
|
||
" random_agent_name: str = \"Random\",\n",
|
||
" episodes_per_leg: int = 10,\n",
|
||
") -> dict[str, float]:\n",
|
||
" \"\"\"Evaluate each agent against the specified random agent and compute win rates.\"\"\"\n",
|
||
" win_rates = {}\n",
|
||
" env = create_env()\n",
|
||
" agent_random = agents[random_agent_name]\n",
|
||
"\n",
|
||
" for name, agent in agents.items():\n",
|
||
" if name == random_agent_name:\n",
|
||
" continue\n",
|
||
"\n",
|
||
" leg1 = run_match(env, agent, agent_random, episodes=episodes_per_leg)\n",
|
||
" leg2 = run_match(env, agent_random, agent, episodes=episodes_per_leg)\n",
|
||
"\n",
|
||
" total_wins = leg1[\"first\"] + leg2[\"second\"]\n",
|
||
" total_matches = (episodes_per_leg * 2) - (leg1[\"draw\"] + leg2[\"draw\"])\n",
|
||
"\n",
|
||
" if total_matches == 0:\n",
|
||
" win_rates[name] = 0.5\n",
|
||
" else:\n",
|
||
" win_rates[name] = total_wins / total_matches\n",
|
||
"\n",
|
||
" print(\n",
|
||
" f\"{name} vs {random_agent_name}: {total_wins} wins out of {total_matches} decisive matches (Win rate: {win_rates[name]:.1%})\",\n",
|
||
" )\n",
|
||
"\n",
|
||
" env.close()\n",
|
||
" return win_rates\n",
|
||
"\n",
|
||
"\n",
|
||
"def plot_evaluation_results(\n",
|
||
" random_win_rates: dict[str, float],\n",
|
||
" save_path: str = \"plots/evaluation_results.png\",\n",
|
||
") -> None:\n",
|
||
" \"\"\"Plot evaluation results: bar chart of win rates against Random agent.\"\"\"\n",
|
||
" agent_names = list(random_win_rates.keys())\n",
|
||
" rates_pct = [random_win_rates[n] * 100 for n in agent_names]\n",
|
||
" colors = [AGENT_COLORS.get(n, \"#7f8c8d\") for n in agent_names]\n",
|
||
"\n",
|
||
" fig, ax = plt.subplots(figsize=(8, 6))\n",
|
||
"\n",
|
||
" x = np.arange(len(agent_names))\n",
|
||
" bars = ax.bar(x, rates_pct, color=colors, edgecolor=\"white\", width=0.6)\n",
|
||
"\n",
|
||
" ax.set_xticks(x)\n",
|
||
" ax.set_xticklabels(agent_names, rotation=30, ha=\"right\", fontsize=11)\n",
|
||
" ax.set_ylabel(\"Win Rate (%)\", fontsize=12)\n",
|
||
" ax.set_title(\"Win Rate vs Random Baseline\", fontsize=14, fontweight=\"bold\")\n",
|
||
" ax.set_ylim(0, 110)\n",
|
||
" ax.axhline(\n",
|
||
" y=50,\n",
|
||
" color=\"gray\",\n",
|
||
" linestyle=\"--\",\n",
|
||
" linewidth=1.2,\n",
|
||
" alpha=0.7,\n",
|
||
" label=\"50 % baseline\",\n",
|
||
" )\n",
|
||
" ax.legend(fontsize=10, loc=\"upper right\")\n",
|
||
" ax.grid(axis=\"y\", alpha=0.3, linestyle=\"--\")\n",
|
||
" ax.spines[\"top\"].set_visible(False)\n",
|
||
" ax.spines[\"right\"].set_visible(False)\n",
|
||
"\n",
|
||
" for bar, wr in zip(bars, rates_pct, strict=False):\n",
|
||
" ax.text(\n",
|
||
" bar.get_x() + bar.get_width() / 2,\n",
|
||
" bar.get_height() + 2,\n",
|
||
" f\"{wr:.1f} %\",\n",
|
||
" ha=\"center\",\n",
|
||
" va=\"bottom\",\n",
|
||
" fontsize=10,\n",
|
||
" fontweight=\"bold\",\n",
|
||
" )\n",
|
||
"\n",
|
||
" fig.tight_layout()\n",
|
||
" plt.savefig(save_path, dpi=150, bbox_inches=\"tight\")\n",
|
||
" plt.show()\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 68,
|
||
"id": "a053644e",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Evaluation against the Random agent\n"
|
||
]
|
||
},
|
||
{
|
||
"ename": "ValueError",
|
||
"evalue": "matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 128 is different from 28224)",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||
"\u001b[31mValueError\u001b[39m Traceback (most recent call last)",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[68]\u001b[39m\u001b[32m, line 2\u001b[39m\n\u001b[32m 1\u001b[39m \u001b[38;5;28mprint\u001b[39m(\u001b[33m\"\u001b[39m\u001b[33mEvaluation against the Random agent\u001b[39m\u001b[33m\"\u001b[39m)\n\u001b[32m----> \u001b[39m\u001b[32m2\u001b[39m win_rates_vs_random = \u001b[43mevaluate_vs_random\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m \u001b[49m\u001b[43magents\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 4\u001b[39m \u001b[43m \u001b[49m\u001b[43mrandom_agent_name\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mRandom\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 5\u001b[39m \u001b[43m \u001b[49m\u001b[43mepisodes_per_leg\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m10\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 6\u001b[39m \u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[67]\u001b[39m\u001b[32m, line 15\u001b[39m, in \u001b[36mevaluate_vs_random\u001b[39m\u001b[34m(agents, random_agent_name, episodes_per_leg)\u001b[39m\n\u001b[32m 12\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m name == random_agent_name:\n\u001b[32m 13\u001b[39m \u001b[38;5;28;01mcontinue\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m15\u001b[39m leg1 = \u001b[43mrun_match\u001b[49m\u001b[43m(\u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magent\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magent_random\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepisodes\u001b[49m\u001b[43m=\u001b[49m\u001b[43mepisodes_per_leg\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 16\u001b[39m leg2 = run_match(env, agent_random, agent, episodes=episodes_per_leg)\n\u001b[32m 18\u001b[39m total_wins = leg1[\u001b[33m\"\u001b[39m\u001b[33mfirst\u001b[39m\u001b[33m\"\u001b[39m] + leg2[\u001b[33m\"\u001b[39m\u001b[33msecond\u001b[39m\u001b[33m\"\u001b[39m]\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[66]\u001b[39m\u001b[32m, line 27\u001b[39m, in \u001b[36mrun_match\u001b[39m\u001b[34m(env, agent_first, agent_second, episodes, max_steps)\u001b[39m\n\u001b[32m 25\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 26\u001b[39m current_agent = agent_first \u001b[38;5;28;01mif\u001b[39;00m agent_id == \u001b[33m\"\u001b[39m\u001b[33mfirst_0\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m agent_second\n\u001b[32m---> \u001b[39m\u001b[32m27\u001b[39m action = \u001b[43mcurrent_agent\u001b[49m\u001b[43m.\u001b[49m\u001b[43mget_action\u001b[49m\u001b[43m(\u001b[49m\u001b[43mnp\u001b[49m\u001b[43m.\u001b[49m\u001b[43masarray\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepsilon\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m0.0\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[32m 29\u001b[39m env.step(action)\n\u001b[32m 31\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m rewards[\u001b[33m\"\u001b[39m\u001b[33mfirst_0\u001b[39m\u001b[33m\"\u001b[39m] > rewards[\u001b[33m\"\u001b[39m\u001b[33msecond_0\u001b[39m\u001b[33m\"\u001b[39m]:\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[56]\u001b[39m\u001b[32m, line 59\u001b[39m, in \u001b[36mSarsaAgent.get_action\u001b[39m\u001b[34m(self, observation, epsilon)\u001b[39m\n\u001b[32m 53\u001b[39m \u001b[38;5;250m\u001b[39m\u001b[33;03m\"\"\"Select action using ε-greedy policy over linear Q-values.\u001b[39;00m\n\u001b[32m 54\u001b[39m \n\u001b[32m 55\u001b[39m \u001b[33;03mSame pattern as Lab 7 SarsaAgent.eps_greedy:\u001b[39;00m\n\u001b[32m 56\u001b[39m \u001b[33;03mcompute q-values for all actions, then apply epsilon_greedy.\u001b[39;00m\n\u001b[32m 57\u001b[39m \u001b[33;03m\"\"\"\u001b[39;00m\n\u001b[32m 58\u001b[39m phi = normalize_obs(observation)\n\u001b[32m---> \u001b[39m\u001b[32m59\u001b[39m q_vals = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_q_values\u001b[49m\u001b[43m(\u001b[49m\u001b[43mphi\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 60\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m epsilon_greedy(q_vals, epsilon, \u001b[38;5;28mself\u001b[39m.rng)\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[56]\u001b[39m\u001b[32m, line 50\u001b[39m, in \u001b[36mSarsaAgent._q_values\u001b[39m\u001b[34m(self, phi)\u001b[39m\n\u001b[32m 37\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34m_q_values\u001b[39m(\u001b[38;5;28mself\u001b[39m, phi: np.ndarray) -> np.ndarray:\n\u001b[32m 38\u001b[39m \u001b[38;5;250m \u001b[39m\u001b[33;03m\"\"\"Compute Q-values for all actions given feature vector phi(s).\u001b[39;00m\n\u001b[32m 39\u001b[39m \n\u001b[32m 40\u001b[39m \u001b[33;03m Equivalent to Lab 7's self.q(s, a) = self.w[idx].sum()\u001b[39;00m\n\u001b[32m (...)\u001b[39m\u001b[32m 48\u001b[39m \n\u001b[32m 49\u001b[39m \u001b[33;03m \"\"\"\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m50\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mW\u001b[49m\u001b[43m \u001b[49m\u001b[43m@\u001b[49m\u001b[43m \u001b[49m\u001b[43mphi\u001b[49m\n",
|
||
"\u001b[31mValueError\u001b[39m: matmul: Input operand 1 has a mismatch in its core dimension 0, with gufunc signature (n?,k),(k,m?)->(n?,m?) (size 128 is different from 28224)"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"print(\"Evaluation against the Random agent\")\n",
|
||
"win_rates_vs_random = evaluate_vs_random(\n",
|
||
" agents,\n",
|
||
" random_agent_name=\"Random\",\n",
|
||
" episodes_per_leg=10,\n",
|
||
")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "5fe97c3d",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "TypeError",
|
||
"evalue": "'float' object is not subscriptable",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||
"\u001b[31mTypeError\u001b[39m Traceback (most recent call last)",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[62]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mplot_evaluation_results\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[43matari_results\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mwin_rates_vs_random\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msave_path\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mplots/evaluation_results.png\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[61]\u001b[39m\u001b[32m, line 66\u001b[39m, in \u001b[36mplot_evaluation_results\u001b[39m\u001b[34m(atari_results, random_results, save_path)\u001b[39m\n\u001b[32m 63\u001b[39m fig, (ax1, ax2) = plt.subplots(\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m, figsize=(\u001b[32m14\u001b[39m, \u001b[32m5\u001b[39m))\n\u001b[32m 65\u001b[39m \u001b[38;5;66;03m# --- Left: Average reward vs Atari AI with error bars ---\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m66\u001b[39m means = [\u001b[43matari_results\u001b[49m\u001b[43m[\u001b[49m\u001b[43mn\u001b[49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmean\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m agent_names]\n\u001b[32m 67\u001b[39m stds = [atari_results[n][\u001b[33m\"\u001b[39m\u001b[33mstd\u001b[39m\u001b[33m\"\u001b[39m] \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m agent_names]\n\u001b[32m 68\u001b[39m x = np.arange(\u001b[38;5;28mlen\u001b[39m(agent_names))\n",
|
||
"\u001b[31mTypeError\u001b[39m: 'float' object is not subscriptable"
|
||
]
|
||
},
|
||
{
|
||
"ename": "TypeError",
|
||
"evalue": "'float' object is not subscriptable",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||
"\u001b[31mTypeError\u001b[39m Traceback (most recent call last)",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[62]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m \u001b[43mplot_evaluation_results\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 2\u001b[39m \u001b[43m \u001b[49m\u001b[43matari_results\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mwin_rates_vs_random\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43msave_path\u001b[49m\u001b[43m=\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mplots/evaluation_results.png\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[32m 3\u001b[39m \u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[61]\u001b[39m\u001b[32m, line 66\u001b[39m, in \u001b[36mplot_evaluation_results\u001b[39m\u001b[34m(atari_results, random_results, save_path)\u001b[39m\n\u001b[32m 63\u001b[39m fig, (ax1, ax2) = plt.subplots(\u001b[32m1\u001b[39m, \u001b[32m2\u001b[39m, figsize=(\u001b[32m14\u001b[39m, \u001b[32m5\u001b[39m))\n\u001b[32m 65\u001b[39m \u001b[38;5;66;03m# --- Left: Average reward vs Atari AI with error bars ---\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m66\u001b[39m means = [\u001b[43matari_results\u001b[49m\u001b[43m[\u001b[49m\u001b[43mn\u001b[49m\u001b[43m]\u001b[49m\u001b[43m[\u001b[49m\u001b[33;43m\"\u001b[39;49m\u001b[33;43mmean\u001b[39;49m\u001b[33;43m\"\u001b[39;49m\u001b[43m]\u001b[49m \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m agent_names]\n\u001b[32m 67\u001b[39m stds = [atari_results[n][\u001b[33m\"\u001b[39m\u001b[33mstd\u001b[39m\u001b[33m\"\u001b[39m] \u001b[38;5;28;01mfor\u001b[39;00m n \u001b[38;5;129;01min\u001b[39;00m agent_names]\n\u001b[32m 68\u001b[39m x = np.arange(\u001b[38;5;28mlen\u001b[39m(agent_names))\n",
|
||
"\u001b[31mTypeError\u001b[39m: 'float' object is not subscriptable"
|
||
]
|
||
},
|
||
{
|
||
"data": {
|
||
"image/png": "iVBORw0KGgoAAAANSUhEUgAABHsAAAGyCAYAAAB0jsg1AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjgsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvwVt1zgAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIQZJREFUeJzt3X2MFtXZB+CzgICmgloKCEWpWkWLgoJsQY2xoZJosPzRlKoBSvyo1RoLaQVEwW+srxqSukpErf5RC2rEGCFYpRJjpSGCJNoKRlGhRhao5aOooDBvZprdsrhYF3efnb33upIRZnZmn7PPkZ17f3vmnKosy7IEAAAAQAgdWrsBAAAAADQfYQ8AAABAIMIeAAAAgECEPQAAAACBCHsAAAAAAhH2AAAAAAQi7AEAAAAIRNgDAAAAEIiwBwAAACAQYQ8AAABAew57XnrppTR69OjUp0+fVFVVlZ5++un/ec3SpUvTaaedlrp06ZKOO+649MgjjxxoewEA2hS1EwBQ+rBnx44dadCgQammpuYrnf/uu++m888/P51zzjlp1apV6Ve/+lW69NJL03PPPXcg7QUAaFPUTgBApVVlWZYd8MVVVWnBggVpzJgx+z1nypQpaeHChemNN96oP/bTn/40bdmyJS1evPhAXxoAoM1ROwEAldCppV9g2bJlaeTIkQ2OjRo1qhjhsz87d+4stjp79uxJH330UfrmN79ZFEkAQDnlv0Pavn178bh3hw6mBqxU7ZRTPwFA25S1QP3U4mHPhg0bUq9evRocy/e3bduWPvnkk3TwwQd/4ZpZs2alm266qaWbBgC0kPXr16dvf/vb3t8K1U459RMAtG3rm7F+avGw50BMmzYtTZ48uX5/69at6aijjiq+8G7durVq2wCA/csDiX79+qVDDz3U21Rh6icAaJu2tUD91OJhT+/evVNtbW2DY/l+Htrs7zdT+apd+bav/BphDwCUn8euK1s75dRPANC2VTXjtDUt/jD98OHD05IlSxoce/7554vjAAConQCA5tXksOff//53sYR6vtUtrZ7/fd26dfVDiMePH19//hVXXJHWrl2brr322rR69ep03333pccffzxNmjSpOb8OAIBSUjsBAKUPe1599dV06qmnFlsun1sn//uMGTOK/Q8//LA++Ml95zvfKZZez0fzDBo0KN19993pwQcfLFaV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",
|
||
"text/plain": [
|
||
"<Figure size 1400x500 with 2 Axes>"
|
||
]
|
||
},
|
||
"metadata": {},
|
||
"output_type": "display_data"
|
||
}
|
||
],
|
||
"source": [
|
||
"plot_evaluation_results(\n",
|
||
" win_rates_vs_random,\n",
|
||
" save_path=\"plots/evaluation_results.png\",\n",
|
||
")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "b9cb30f5",
|
||
"metadata": {},
|
||
"source": [
|
||
"## Full Round-Robin Tournament\n",
|
||
"\n",
|
||
"All agents play a **round-robin tournament** where each pair faces off in both positions (`first_0` / `second_0`).\n",
|
||
"\n",
|
||
"The resulting $n \\times n$ matchup matrix is analyzed with:\n",
|
||
"- **Aggregated ranking**: wins from both positions are combined for each agent\n",
|
||
"- **Binomial significance test** ($H_0: p = 0.5$): determines whether the win rate for each matchup is statistically different from a coin flip"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "4031bde5",
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def run_all_matchups(\n",
|
||
" agents: dict[str, Agent],\n",
|
||
" episodes_per_leg: int = 20,\n",
|
||
") -> tuple[np.ndarray, list[str]]:\n",
|
||
" \"\"\"Run round-robin matches between all pairs of agents and return the matchup matrix.\"\"\"\n",
|
||
" env = create_env()\n",
|
||
" names = [name for name in agents if name != \"Random\"]\n",
|
||
" n = len(names)\n",
|
||
" matrix = np.full((n, n), np.nan)\n",
|
||
"\n",
|
||
" for i in range(n):\n",
|
||
" for j in range(n):\n",
|
||
" if i == j:\n",
|
||
" continue\n",
|
||
" result = run_match(\n",
|
||
" env,\n",
|
||
" agents[names[i]],\n",
|
||
" agents[names[j]],\n",
|
||
" episodes=episodes_per_leg,\n",
|
||
" )\n",
|
||
" matrix[i, j] = result[\"first\"]\n",
|
||
"\n",
|
||
" env.close()\n",
|
||
" return matrix, names\n",
|
||
"\n",
|
||
"\n",
|
||
"def plot_matchup_matrix(\n",
|
||
" matrix: np.ndarray,\n",
|
||
" agent_names: list[str],\n",
|
||
" episodes_per_leg: int = 20,\n",
|
||
" save_path: str = \"plots/championship_matrix.png\",\n",
|
||
") -> None:\n",
|
||
" \"\"\"Plot the matchup matrix as a heatmap with win counts and percentages.\"\"\"\n",
|
||
" n = len(agent_names)\n",
|
||
" pct_matrix = matrix / episodes_per_leg * 100\n",
|
||
"\n",
|
||
" fig, ax = plt.subplots(figsize=(8, 7))\n",
|
||
" masked = np.ma.array(pct_matrix, mask=np.isnan(pct_matrix))\n",
|
||
" cmap = plt.get_cmap(\"RdYlGn\").copy()\n",
|
||
" cmap.set_bad(color=\"#f0f0f0\")\n",
|
||
"\n",
|
||
" im = ax.imshow(masked, cmap=cmap, vmin=0, vmax=100, aspect=\"equal\")\n",
|
||
"\n",
|
||
" ax.set_xticks(np.arange(n))\n",
|
||
" ax.set_yticks(np.arange(n))\n",
|
||
" ax.set_xticklabels(agent_names, rotation=45, ha=\"right\", fontsize=10)\n",
|
||
" ax.set_yticklabels(agent_names, fontsize=10)\n",
|
||
" ax.set_xlabel(\"Opponent (second_0)\", fontsize=11)\n",
|
||
" ax.set_ylabel(\"Agent (first_0)\", fontsize=11)\n",
|
||
" ax.set_title(\"Championship Matchup Matrix\", fontsize=14, fontweight=\"bold\", pad=12)\n",
|
||
"\n",
|
||
" for i in range(n):\n",
|
||
" for j in range(n):\n",
|
||
" if i == j:\n",
|
||
" ax.text(j, i, \"—\", ha=\"center\", va=\"center\", color=\"#aaa\", fontsize=14)\n",
|
||
" else:\n",
|
||
" wins = int(matrix[i, j])\n",
|
||
" pct = pct_matrix[i, j]\n",
|
||
" txt_color = \"white\" if pct > 70 or pct < 30 else \"black\"\n",
|
||
" ax.text(\n",
|
||
" j,\n",
|
||
" i,\n",
|
||
" f\"{wins}\\n({pct:.0f} %)\",\n",
|
||
" ha=\"center\",\n",
|
||
" va=\"center\",\n",
|
||
" color=txt_color,\n",
|
||
" fontsize=9,\n",
|
||
" fontweight=\"bold\",\n",
|
||
" linespacing=1.4,\n",
|
||
" )\n",
|
||
"\n",
|
||
" cbar = fig.colorbar(im, ax=ax, fraction=0.046, pad=0.04)\n",
|
||
" cbar.set_label(f\"Win Rate (%) — {episodes_per_leg} eps per matchup\", fontsize=10)\n",
|
||
" cbar.set_ticks([0, 25, 50, 75, 100])\n",
|
||
" cbar.set_ticklabels([\"0 %\", \"25 %\", \"50 %\", \"75 %\", \"100 %\"])\n",
|
||
"\n",
|
||
" plt.tight_layout()\n",
|
||
" plt.savefig(save_path, dpi=150, bbox_inches=\"tight\")\n",
|
||
" plt.show()\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "b07b403c",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "KeyboardInterrupt",
|
||
"evalue": "",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||
"\u001b[31mTypeError\u001b[39m Traceback (most recent call last)",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/lambda_wrappers/observation_lambda.py:68\u001b[39m, in \u001b[36maec_observation_lambda._modify_observation\u001b[39m\u001b[34m(self, agent, observation)\u001b[39m\n\u001b[32m 67\u001b[39m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m68\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mchange_observation_fn\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobservation\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mold_obs_space\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magent\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n",
|
||
"\u001b[31mTypeError\u001b[39m: basic_obs_wrapper.<locals>.change_obs() takes 2 positional arguments but 3 were given",
|
||
"\nDuring handling of the above exception, another exception occurred:\n",
|
||
"\u001b[31mKeyboardInterrupt\u001b[39m Traceback (most recent call last)",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[41]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m matchup_matrix = \u001b[43mrun_all_matchups\u001b[49m\u001b[43m(\u001b[49m\u001b[43magents\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepisodes_per_leg\u001b[49m\u001b[43m=\u001b[49m\u001b[32;43m20\u001b[39;49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[40]\u001b[39m\u001b[32m, line 19\u001b[39m, in \u001b[36mrun_all_matchups\u001b[39m\u001b[34m(agents, episodes_per_leg)\u001b[39m\n\u001b[32m 17\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m i == j:\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mcontinue\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m19\u001b[39m result = \u001b[43mrun_match\u001b[49m\u001b[43m(\u001b[49m\n\u001b[32m 20\u001b[39m \u001b[43m \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magents\u001b[49m\u001b[43m[\u001b[49m\u001b[43mnames\u001b[49m\u001b[43m[\u001b[49m\u001b[43mi\u001b[49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43magents\u001b[49m\u001b[43m[\u001b[49m\u001b[43mnames\u001b[49m\u001b[43m[\u001b[49m\u001b[43mj\u001b[49m\u001b[43m]\u001b[49m\u001b[43m]\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mepisodes\u001b[49m\u001b[43m=\u001b[49m\u001b[43mepisodes_per_leg\u001b[49m\u001b[43m,\u001b[49m\n\u001b[32m 21\u001b[39m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 22\u001b[39m matrix[i, j] = result[\u001b[33m\"\u001b[39m\u001b[33mfirst\u001b[39m\u001b[33m\"\u001b[39m]\n\u001b[32m 24\u001b[39m env.close()\n",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[28]\u001b[39m\u001b[32m, line 37\u001b[39m, in \u001b[36mrun_match\u001b[39m\u001b[34m(env, agent_first, agent_second, episodes, max_steps)\u001b[39m\n\u001b[32m 34\u001b[39m current_agent = agent_first \u001b[38;5;28;01mif\u001b[39;00m agent_id == \u001b[33m\"\u001b[39m\u001b[33mfirst_0\u001b[39m\u001b[33m\"\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m agent_second\n\u001b[32m 35\u001b[39m action = current_agent.get_action(np.asarray(obs), epsilon=\u001b[32m0.0\u001b[39m)\n\u001b[32m---> \u001b[39m\u001b[32m37\u001b[39m \u001b[43menv\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m(\u001b[49m\u001b[43maction\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 39\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m step_idx + \u001b[32m1\u001b[39m >= max_steps:\n\u001b[32m 40\u001b[39m \u001b[38;5;28;01mbreak\u001b[39;00m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/utils/base_aec_wrapper.py:46\u001b[39m, in \u001b[36mBaseWrapper.step\u001b[39m\u001b[34m(self, action)\u001b[39m\n\u001b[32m 43\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m (\u001b[38;5;28mself\u001b[39m.terminations[agent] \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m.truncations[agent]):\n\u001b[32m 44\u001b[39m action = \u001b[38;5;28mself\u001b[39m._modify_action(agent, action)\n\u001b[32m---> \u001b[39m\u001b[32m46\u001b[39m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m(\u001b[49m\u001b[43maction\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 48\u001b[39m \u001b[38;5;28mself\u001b[39m._update_step(\u001b[38;5;28mself\u001b[39m.agent_selection)\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/pettingzoo/utils/wrappers/order_enforcing.py:70\u001b[39m, in \u001b[36mOrderEnforcingWrapper.step\u001b[39m\u001b[34m(self, action)\u001b[39m\n\u001b[32m 68\u001b[39m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[32m 69\u001b[39m \u001b[38;5;28mself\u001b[39m._has_updated = \u001b[38;5;28;01mTrue\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m(\u001b[49m\u001b[43maction\u001b[49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/pettingzoo/utils/wrappers/base.py:47\u001b[39m, in \u001b[36mBaseWrapper.step\u001b[39m\u001b[34m(self, action)\u001b[39m\n\u001b[32m 46\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mstep\u001b[39m(\u001b[38;5;28mself\u001b[39m, action: ActionType) -> \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m47\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43menv\u001b[49m\u001b[43m.\u001b[49m\u001b[43mstep\u001b[49m\u001b[43m(\u001b[49m\u001b[43maction\u001b[49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/generic_wrappers/utils/shared_wrapper_util.py:69\u001b[39m, in \u001b[36mshared_wrapper_aec.step\u001b[39m\u001b[34m(self, action)\u001b[39m\n\u001b[32m 66\u001b[39m \u001b[38;5;28msuper\u001b[39m().step(action)\n\u001b[32m 67\u001b[39m \u001b[38;5;28mself\u001b[39m.add_modifiers(\u001b[38;5;28mself\u001b[39m.agents)\n\u001b[32m 68\u001b[39m \u001b[38;5;28mself\u001b[39m.modifiers[\u001b[38;5;28mself\u001b[39m.agent_selection].modify_obs(\n\u001b[32m---> \u001b[39m\u001b[32m69\u001b[39m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mobserve\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43magent_selection\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 70\u001b[39m )\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/pettingzoo/utils/wrappers/order_enforcing.py:75\u001b[39m, in \u001b[36mOrderEnforcingWrapper.observe\u001b[39m\u001b[34m(self, agent)\u001b[39m\n\u001b[32m 73\u001b[39m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28mself\u001b[39m._has_reset:\n\u001b[32m 74\u001b[39m EnvLogger.error_observe_before_reset()\n\u001b[32m---> \u001b[39m\u001b[32m75\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[43m.\u001b[49m\u001b[43mobserve\u001b[49m\u001b[43m(\u001b[49m\u001b[43magent\u001b[49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/pettingzoo/utils/wrappers/base.py:41\u001b[39m, in \u001b[36mBaseWrapper.observe\u001b[39m\u001b[34m(self, agent)\u001b[39m\n\u001b[32m 40\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mobserve\u001b[39m(\u001b[38;5;28mself\u001b[39m, agent: AgentID) -> ObsType | \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m41\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43menv\u001b[49m\u001b[43m.\u001b[49m\u001b[43mobserve\u001b[49m\u001b[43m(\u001b[49m\u001b[43magent\u001b[49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/utils/base_aec_wrapper.py:38\u001b[39m, in \u001b[36mBaseWrapper.observe\u001b[39m\u001b[34m(self, agent)\u001b[39m\n\u001b[32m 34\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mobserve\u001b[39m(\u001b[38;5;28mself\u001b[39m, agent):\n\u001b[32m 35\u001b[39m obs = \u001b[38;5;28msuper\u001b[39m().observe(\n\u001b[32m 36\u001b[39m agent\n\u001b[32m 37\u001b[39m ) \u001b[38;5;66;03m# problem is in this line, the obs is sometimes a different size from the obs space\u001b[39;00m\n\u001b[32m---> \u001b[39m\u001b[32m38\u001b[39m observation = \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43m_modify_observation\u001b[49m\u001b[43m(\u001b[49m\u001b[43magent\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mobs\u001b[49m\u001b[43m)\u001b[49m\n\u001b[32m 39\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m observation\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/lambda_wrappers/observation_lambda.py:70\u001b[39m, in \u001b[36maec_observation_lambda._modify_observation\u001b[39m\u001b[34m(self, agent, observation)\u001b[39m\n\u001b[32m 68\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28mself\u001b[39m.change_observation_fn(observation, old_obs_space, agent)\n\u001b[32m 69\u001b[39m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mTypeError\u001b[39;00m:\n\u001b[32m---> \u001b[39m\u001b[32m70\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43mself\u001b[39;49m\u001b[43m.\u001b[49m\u001b[43mchange_observation_fn\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobservation\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mold_obs_space\u001b[49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/generic_wrappers/basic_wrappers.py:19\u001b[39m, in \u001b[36mbasic_obs_wrapper.<locals>.change_obs\u001b[39m\u001b[34m(obs, obs_space)\u001b[39m\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mchange_obs\u001b[39m(obs, obs_space):\n\u001b[32m---> \u001b[39m\u001b[32m19\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43mmodule\u001b[49m\u001b[43m.\u001b[49m\u001b[43mchange_observation\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mobs_space\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mparam\u001b[49m\u001b[43m)\u001b[49m\n",
|
||
"\u001b[36mFile \u001b[39m\u001b[32m~/Workspace/studies/.venv/lib/python3.13/site-packages/supersuit/utils/basic_transforms/resize.py:21\u001b[39m, in \u001b[36mchange_observation\u001b[39m\u001b[34m(obs, obs_space, resize)\u001b[39m\n\u001b[32m 17\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mchange_obs_space\u001b[39m(obs_space, param):\n\u001b[32m 18\u001b[39m \u001b[38;5;28;01mreturn\u001b[39;00m convert_box(\u001b[38;5;28;01mlambda\u001b[39;00m obs: change_observation(obs, obs_space, param), obs_space)\n\u001b[32m---> \u001b[39m\u001b[32m21\u001b[39m \u001b[38;5;28;01mdef\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34mchange_observation\u001b[39m(obs, obs_space, resize):\n\u001b[32m 22\u001b[39m \u001b[38;5;28;01mimport\u001b[39;00m\u001b[38;5;250m \u001b[39m\u001b[34;01mtinyscaler\u001b[39;00m\n\u001b[32m 24\u001b[39m xsize, ysize, linear_interp = resize\n",
|
||
"\u001b[31mKeyboardInterrupt\u001b[39m: "
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"matchup_matrix, tournament_names = run_all_matchups(agents, episodes_per_leg=20)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": null,
|
||
"id": "9668071a",
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"ename": "NameError",
|
||
"evalue": "name 'matchup_matrix' is not defined",
|
||
"output_type": "error",
|
||
"traceback": [
|
||
"\u001b[31m---------------------------------------------------------------------------\u001b[39m",
|
||
"\u001b[31mNameError\u001b[39m Traceback (most recent call last)",
|
||
"\u001b[36mCell\u001b[39m\u001b[36m \u001b[39m\u001b[32mIn[42]\u001b[39m\u001b[32m, line 1\u001b[39m\n\u001b[32m----> \u001b[39m\u001b[32m1\u001b[39m plot_matchup_matrix(\u001b[43mmatchup_matrix\u001b[49m, episodes_per_leg=EPISODES_PER_LEG)\n",
|
||
"\u001b[31mNameError\u001b[39m: name 'matchup_matrix' is not defined"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"plot_matchup_matrix(matchup_matrix, tournament_names, episodes_per_leg=20)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "32c37e5d",
|
||
"metadata": {},
|
||
"source": [
|
||
"# Conclusion\n",
|
||
"\n",
|
||
"This project implemented and compared five Reinforcement Learning agents on Atari Tennis:\n",
|
||
"\n",
|
||
"| Agent | Type | Policy | Update Rule |\n",
|
||
"|-------|------|--------|-------------|\n",
|
||
"| **Random** | Baseline | Uniform random | None |\n",
|
||
"| **SARSA** | TD(0), on-policy | ε-greedy | $W_a \\leftarrow W_a + \\alpha \\cdot (r + \\gamma \\hat{q}(s', a') - \\hat{q}(s, a)) \\cdot \\phi(s)$ |\n",
|
||
"| **Q-Learning** | TD(0), off-policy | ε-greedy | $W_a \\leftarrow W_a + \\alpha \\cdot (r + \\gamma \\max_{a'} \\hat{q}(s', a') - \\hat{q}(s, a)) \\cdot \\phi(s)$ |\n",
|
||
"| **Monte Carlo** | First-visit MC | ε-greedy | $W_a \\leftarrow W_a + \\alpha \\cdot (G_t - \\hat{q}(s, a)) \\cdot \\phi(s)$ |\n",
|
||
"| **DQN** | Deep Q-Network | ε-greedy | Neural network (MLP 256→256) with experience replay and target network |\n",
|
||
"\n",
|
||
"**Architecture**:\n",
|
||
"- **Linear agents** (SARSA, Q-Learning, Monte Carlo): $\\hat{q}(s, a; \\mathbf{W}) = \\mathbf{W}_a^\\top \\phi(s)$ with $\\phi(s) \\in \\mathbb{R}^{28\\,224}$ (4 grayscale 84×84 frames, normalized)\n",
|
||
"- **DQN**: MLP network (28,224 → 256 → 256 → 18) trained with Adam optimizer, Huber loss, and periodic target network sync\n",
|
||
"\n",
|
||
"**Methodology**:\n",
|
||
"1. **Sequential pre-training** of all agents against Atari's built-in AI (3,000 episodes, ε decaying linearly from 1.0 to 0.05 over 500k steps)\n",
|
||
"2. **Evaluation vs Random** to validate learning (expected win rate > 50%)\n",
|
||
"3. **Evaluation vs Atari AI** with per-episode reward statistics (mean ± std) and distributions\n",
|
||
"4. **Full round-robin tournament** via PettingZoo (20 episodes per matchup per position) with statistical significance analysis"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "markdown",
|
||
"id": "15f80f84",
|
||
"metadata": {},
|
||
"source": []
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": "studies (3.13.9)",
|
||
"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.9"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 5
|
||
}
|