Last active
November 22, 2020 21:53
-
-
Save tomtung/c1992506941ba167dae5ca468a259820 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"cells": [ | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"# Multi-agent DDPG for Solving Unity's \"Tennis\" Problem\n", | |
"\n", | |
"In this notebook we report how we used Multi-agent Deep Deterministic Policy Gradient (MADDPG) algorithm to solve a modified version of Unitfy's \"Tennis\" environment, where we need to control two agents to play tennis in a 3D environment.\n", | |
"\n", | |
"This notebook contains all the code for training and running the agent.\n", | |
"\n", | |
"A demo of a pair of trained agents is shown in the gif below:\n", | |
"\n", | |
"![demo](https://user-images.githubusercontent.com/513210/99918167-fe370780-2cc9-11eb-9bc2-a5f5370083ee.gif)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Environment Setup\n", | |
"\n", | |
"The dependencies can be set up by following the instructions from the [DRLND](https://github.com/udacity/deep-reinforcement-learning#dependencies) repo. Once it's done, the following imports should work:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 1, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"import collections\n", | |
"import copy\n", | |
"import itertools\n", | |
"import math\n", | |
"!pip install -q dataclasses\n", | |
"from dataclasses import dataclass\n", | |
"from typing import Any, List, Tuple, Optional, Generator\n", | |
"import random\n", | |
"\n", | |
"import numpy\n", | |
"import torch\n", | |
"from unityagents import UnityEnvironment\n", | |
"\n", | |
"import matplotlib.pyplot as plt\n", | |
"%matplotlib inline" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Note that to make GPU training work on our machine, the following version of PyTorch is used:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 2, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Version: 1.7.0+cu110; Cuda available: True\n" | |
] | |
} | |
], | |
"source": [ | |
"print(f\"Version: {torch.__version__}; Cuda available: {torch.cuda.is_available()}\")" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Additionally, we also need to download the pre-built Unity environment, which can be downloaded for different platforms:\n", | |
"\n", | |
"- Linux: [click here](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P3/Tennis/Tennis_Linux.zip)\n", | |
"- Mac OSX: [click here](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P3/Tennis/Tennis.app.zip)\n", | |
"- Windows (32-bit): [click here](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P3/Tennis/Tennis_Windows_x86.zip)\n", | |
"- Windows (64-bit): [click here](https://s3-us-west-1.amazonaws.com/udacity-drlnd/P3/Tennis/Tennis_Windows_x86_64.zip)\n", | |
"\n", | |
"Once downloaded and extracted, please set the path below accordingly." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 3, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"ENV_PATH = '../Tennis_Linux/Tennis.x86_64'" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"If set up correctly, we should be able to initialize the environment:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 4, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stderr", | |
"output_type": "stream", | |
"text": [ | |
"INFO:unityagents:\n", | |
"'Academy' started successfully!\n", | |
"Unity Academy name: Academy\n", | |
" Number of Brains: 1\n", | |
" Number of External Brains : 1\n", | |
" Lesson number : 0\n", | |
" Reset Parameters :\n", | |
"\t\t\n", | |
"Unity brain name: TennisBrain\n", | |
" Number of Visual Observations (per agent): 0\n", | |
" Vector Observation space type: continuous\n", | |
" Vector Observation space size (per agent): 8\n", | |
" Number of stacked Vector Observation: 3\n", | |
" Vector Action space type: continuous\n", | |
" Vector Action space size (per agent): 2\n", | |
" Vector Action descriptions: , \n" | |
] | |
} | |
], | |
"source": [ | |
"class ParallelEnv:\n", | |
" \"\"\"A simple wrapper for the environment with parallel agents.\"\"\"\n", | |
"\n", | |
" def __init__(self):\n", | |
" self._env = UnityEnvironment(file_name=ENV_PATH)\n", | |
"\n", | |
" env_info = self._env.reset(train_mode=True)[self._brain_name]\n", | |
" self.num_agents = len(env_info.agents)\n", | |
"\n", | |
" @property\n", | |
" def _brain_name(self):\n", | |
" return self._env.brain_names[0]\n", | |
"\n", | |
" @property\n", | |
" def _brain(self):\n", | |
" return self._env.brains[self._brain_name]\n", | |
"\n", | |
" @property\n", | |
" def action_dim(self) -> int:\n", | |
" \"\"\"The size of a action vector.\"\"\"\n", | |
" return self._brain.vector_action_space_size\n", | |
"\n", | |
" @property\n", | |
" def action_scale(self) -> float:\n", | |
" \"\"\"The scale of action values.\n", | |
"\n", | |
" Each dimension of the action vector lies in range [-scale, scale].\n", | |
"\n", | |
" \"\"\"\n", | |
" return 1.0\n", | |
"\n", | |
" def sample_action(self) -> numpy.array:\n", | |
" \"\"\"Sample an action vector uniformly at random.\"\"\"\n", | |
" return numpy.random.uniform(\n", | |
" low=-self.action_scale,\n", | |
" high=self.action_scale,\n", | |
" size=(self.num_agents, self.action_dim),\n", | |
" )\n", | |
"\n", | |
" @property\n", | |
" def state_dim(self) -> int:\n", | |
" \"\"\"The size of a state vector.\"\"\"\n", | |
" return self._brain.vector_observation_space_size * self._brain.num_stacked_vector_observations\n", | |
"\n", | |
" def reset(self, train_mode=True) -> numpy.array:\n", | |
" \"\"\"Reset the environment and returns the initial state.\"\"\"\n", | |
" env_info = self._env.reset(train_mode=train_mode)[self._brain_name]\n", | |
" init_state = env_info.vector_observations\n", | |
" assert init_state.shape == (self.num_agents, self.state_dim)\n", | |
" return init_state\n", | |
"\n", | |
" def step(\n", | |
" self, actions: numpy.array\n", | |
" ) -> Tuple[numpy.array, numpy.array, numpy.array]:\n", | |
" \"\"\"Take a step with the given actions.\n", | |
"\n", | |
" Returns a tuple of (states, rewards, done-flags) for all parallel agents.\n", | |
"\n", | |
" \"\"\"\n", | |
" assert actions.shape == (self.num_agents, self.action_dim)\n", | |
" env_info = self._env.step(actions)[self._brain_name]\n", | |
" return (\n", | |
" numpy.array(env_info.vector_observations, dtype=numpy.float32),\n", | |
" numpy.array(env_info.rewards, dtype=numpy.float32),\n", | |
" numpy.array(env_info.local_done, dtype=bool),\n", | |
" )\n", | |
"\n", | |
" def close(self) -> None:\n", | |
" \"\"\"Closes the environment.\"\"\"\n", | |
" self._env.close()\n", | |
"\n", | |
"\n", | |
"env = ParallelEnv()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Environment description\n", | |
"\n", | |
"In this environment, two agents control rackets to bounce a ball over a net. If an agent hits the ball over the net, it receives a reward of +0.1. If an agent lets a ball hit the ground or hits the ball out of bounds, it receives a reward of -0.01. Thus, the goal of each agent is to keep the ball in play.\n", | |
"\n", | |
"The observation space consists of 8 variables corresponding to the position and velocity of the ball and racket. Each agent receives its own, local observation. Two continuous actions are available, corresponding to movement toward (or away from) the net, and jumping.\n", | |
"\n", | |
"The task is considered \"solved\" when the agents get an average score of +0.5 (over 100 consecutive episodes, after taking the maximum over both agents)." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 5, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Number of agents: 2\n", | |
"Action dimension: 2\n", | |
"States dimension: 24\n" | |
] | |
} | |
], | |
"source": [ | |
"print(f'Number of agents: {env.num_agents}')\n", | |
"print(f'Action dimension: {env.action_dim}')\n", | |
"print(f'States dimension: {env.state_dim}')" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Methodology & Implementation\n", | |
"\n", | |
"To solve this problem, we experimented with MADDPG as described by Lowe et al. (2017). Compared DDPG as described by Lillicrap et al. (2015), we allow critics access to observations and actions from all agents, so that it can better guide the actors which only have access to their local observations.\n", | |
"\n", | |
"As described in the paper, we let each agent to have its own actor and critic networks not shared with each other. For this simple problem, using MADDPG doesn't necessarily seem to improve convergence speed, but it's interesting to see how agents with different behaviors are able to cooperate and bounce the ball back and forth.\n", | |
"\n", | |
"Additionally, we also incorporate extensions to DDPG from TD3 (Fujimoto et al. 2018), namely:\n", | |
"\n", | |
"- Clipped double Q-Learning for actor-critic\n", | |
"- Delayed policy updates\n", | |
"- Target policy smoothing regularization\n", | |
"\n", | |
"The incorporation of TD3 tricks into MADDPG has also been explored by Ackermann et al. (2019), which managed to achieve better result in their tests. They called the approach MATD3.\n", | |
"\n", | |
"Additionally, we also used prioritized experience replay as described by Schaul et al. (2015)." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Hyper-parameters\n", | |
"\n", | |
"The hyper-parameters we used is shown as the default values in the following data class:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 6, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"AgentConfig(horizon_length=10000, replay_buffer_size=100000, batch_size=256, critic_lr=0.0001, actor_lr=0.0001, exploration_noise=0.1, policy_noise=0.2, policy_noise_clip=0.5, policy_update_freq=2, discount_factor=0.99, soft_update_factor=0.005, pure_exploration_steps=10000)" | |
] | |
}, | |
"execution_count": 6, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"@dataclass\n", | |
"class AgentConfig:\n", | |
" horizon_length: int = 10000\n", | |
" replay_buffer_size: int = 100_000\n", | |
" batch_size: int = 256\n", | |
" critic_lr: float = 1e-4\n", | |
" actor_lr: float = 1e-4\n", | |
" exploration_noise: float = 0.1\n", | |
" policy_noise: float = 0.2\n", | |
" policy_noise_clip: float = 0.5\n", | |
" policy_update_freq: int = 2\n", | |
" discount_factor: float = 0.99\n", | |
" soft_update_factor: float = 0.005\n", | |
" pure_exploration_steps: int = 10_000\n", | |
"\n", | |
"\n", | |
"AgentConfig()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Prioritized experience replay\n", | |
"Here we use prioritized experience replay as described in Schaul et al. (2015). Specifically, we implemented the proportional priorization variant with the sum-tree data structure.\n", | |
"\n", | |
"The code for the replay buffer is as follows:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 7, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class PrioritizedReplayBuffer:\n", | |
" \"\"\"A proportionally prioritized replay buffer implemented with sum-tree.\"\"\"\n", | |
"\n", | |
" _curr_index: int\n", | |
" _size: int\n", | |
" _max_priority: float\n", | |
" _sum_tree: List[float]\n", | |
" _priorities: List[float]\n", | |
" _samples: List[Any]\n", | |
"\n", | |
" def __init__(self, buffer_size: int):\n", | |
" assert buffer_size > 1\n", | |
" self._curr_index = 0\n", | |
" self._size = 0\n", | |
" self._max_priority = 1.0\n", | |
" self._sum_tree = [0] * (2 ** (math.floor(math.log2(buffer_size - 1)) + 1) - 1)\n", | |
" self._priorities = [0] * buffer_size\n", | |
" self._samples = [None] * buffer_size\n", | |
"\n", | |
" def _ancestor_indices(self, sample_index: int) -> Generator[int, None, None]:\n", | |
" assert 0 <= sample_index <= len(self._samples)\n", | |
" index = sample_index + len(self._sum_tree)\n", | |
" while index > 0:\n", | |
" index = (index - 1) // 2\n", | |
" yield index\n", | |
"\n", | |
" @staticmethod\n", | |
" def _children_indices(index: int) -> Tuple[int, int]:\n", | |
" # Note that it could go out-of-bounds for the sum tree array\n", | |
" left_index = index * 2 + 1\n", | |
" right_index = left_index + 1\n", | |
" return left_index, right_index\n", | |
"\n", | |
" def _set_priority(self, sample_index: int, priority: float):\n", | |
" assert priority > 0, \"Weights must be non-negative\"\n", | |
" delta = priority - self._priorities[sample_index]\n", | |
" self._priorities[sample_index] = priority\n", | |
" for index in self._ancestor_indices(sample_index):\n", | |
" self._sum_tree[index] += delta\n", | |
"\n", | |
" self._max_priority = max(self._max_priority, priority)\n", | |
"\n", | |
" def _set_sample(self, sample_index: int, sample: Any, priority: float):\n", | |
" self._set_priority(sample_index, priority)\n", | |
" self._samples[sample_index] = sample\n", | |
"\n", | |
" class _SampleHandle:\n", | |
" \"\"\"A handle that allows access to the value and priority of a sample in the buffer.\"\"\"\n", | |
"\n", | |
" _parent: \"PrioritizedReplayBuffer\"\n", | |
" _index: int\n", | |
"\n", | |
" def __init__(self, parent: \"PrioritizedReplayBuffer\", index: int):\n", | |
" assert 0 <= index <= len(parent._samples)\n", | |
" self._parent = parent\n", | |
" self._index = index\n", | |
"\n", | |
" @property\n", | |
" def value(self) -> Any:\n", | |
" \"\"\"The value of the sample.\"\"\"\n", | |
" return self._parent._samples[self._index]\n", | |
"\n", | |
" @property\n", | |
" def priority(self) -> float:\n", | |
" \"\"\"The priority of the sample.\"\"\"\n", | |
" return self._parent._priorities[self._index]\n", | |
"\n", | |
" @property\n", | |
" def sample_probability(self) -> float:\n", | |
" \"\"\"The probability of sampling this item.\"\"\"\n", | |
" return self.priority / self._parent.priority_sum\n", | |
"\n", | |
" @priority.setter\n", | |
" def priority(self, priority: float) -> None:\n", | |
" \"\"\"Modify the priority of the sample.\"\"\"\n", | |
" self._parent._set_priority(self._index, priority)\n", | |
"\n", | |
" def reset(self, value: Any, priority: float):\n", | |
" \"\"\"Modify both the value and the priority of the sample.\"\"\"\n", | |
" self._parent._set_sample(self._index, value, priority)\n", | |
"\n", | |
" def add(self, value: Any, priority: Optional[float] = None) -> None:\n", | |
" \"\"\"Add a new sample.\"\"\"\n", | |
" # By default, the current maximum priority is used for new samples to make sure they get picked at least once\n", | |
" if priority is None:\n", | |
" priority = self._max_priority\n", | |
"\n", | |
" self._SampleHandle(self, self._curr_index).reset(value, priority)\n", | |
"\n", | |
" buffer_size = len(self._samples)\n", | |
" self._curr_index = (self._curr_index + 1) % buffer_size\n", | |
" self._size = min(self._size + 1, buffer_size)\n", | |
"\n", | |
" @property\n", | |
" def priority_sum(self) -> float:\n", | |
" \"\"\"The sum of priority values of all samples in the buffer.\n", | |
"\n", | |
" This is used to compute sample probabilities.\n", | |
"\n", | |
" \"\"\"\n", | |
" return self._sum_tree[0]\n", | |
"\n", | |
" def sample_single(self, query: Optional[float] = None) -> _SampleHandle:\n", | |
" \"\"\"Draw a sample.\n", | |
"\n", | |
" The query parameter is a float in [0, 1] for stratified sampling.\n", | |
"\n", | |
" \"\"\"\n", | |
" assert self.priority_sum > 0.0, \"Nothing has been added\"\n", | |
"\n", | |
" if query is None:\n", | |
" query = random.random()\n", | |
"\n", | |
" assert 0.0 <= query <= 1.0\n", | |
" target = self.priority_sum * query\n", | |
" index = 0\n", | |
" while True:\n", | |
" assert 0.0 <= target <= self._sum_tree[index]\n", | |
" index_l, index_r = self._children_indices(index)\n", | |
"\n", | |
" assert (index_l < len(self._sum_tree)) == (index_r < len(self._sum_tree))\n", | |
" if index_l >= len(self._sum_tree):\n", | |
" # We've reached the leaves when both left & right indices are out of range for the tree structure.\n", | |
" # Offset both indices to get the indices into the sample list before breaking.\n", | |
" index_l -= len(self._sum_tree)\n", | |
" index_r -= len(self._sum_tree)\n", | |
" break\n", | |
"\n", | |
" # If the target is smaller than the sum of the left subtree, go left; otherwise go right\n", | |
" sum_l = self._sum_tree[index_l]\n", | |
" if target <= sum_l:\n", | |
" index = index_l\n", | |
" else:\n", | |
" target -= sum_l\n", | |
" index = index_r\n", | |
"\n", | |
" assert index_l < len(self._priorities)\n", | |
" if target <= self._priorities[index_l]:\n", | |
" index = index_l\n", | |
" else:\n", | |
" assert index_r < len(self._priorities)\n", | |
" index = index_r\n", | |
"\n", | |
" return self._SampleHandle(self, index)\n", | |
"\n", | |
" def sample_batch(self, batch_size: int) -> List[_SampleHandle]:\n", | |
" \"\"\"Draw a stratified batch of samples with the given size.\"\"\"\n", | |
" # Divide the sum range into batch_size buckets, and do a weighted sampling from each bucket.\n", | |
" end_points = numpy.linspace(0.0, 1.0, batch_size + 1).tolist()\n", | |
" return [\n", | |
" self.sample_single(query=numpy.random.uniform(l, r))\n", | |
" for l, r in zip(end_points[:-1], end_points[1:])\n", | |
" ]\n", | |
"\n", | |
" def __len__(self):\n", | |
" \"\"\"Return the current size of internal memory.\"\"\"\n", | |
" return self._size" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### Actor-critic\n", | |
"\n", | |
"Like in TD3 (Fujimoto et al. 2018), we have one actor / policy network and a pair of critic / Q networks for each agent.\n", | |
"\n", | |
"Andrychowicz et al. (2020) suggested that it's helpful to initialize the final layer of the actor network with small weights so that initially actions depend little on the observations. They suggested doing the same for the critic network doesn't seem to be useful, but it turns out that for this particular problem it does seem to help, too. This is probably because during the initial training state the expected accumulative reward is always close to 0 anyways." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 8, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class ActorNet(torch.nn.Module):\n", | |
" def __init__(\n", | |
" self,\n", | |
" state_dim: int,\n", | |
" action_dim: int,\n", | |
" action_scale=1.0,\n", | |
" hidden_dims: Tuple[int, ...] = (256, 256),\n", | |
" ):\n", | |
" super().__init__()\n", | |
" self.action_scale = action_scale\n", | |
" self.fcs = torch.nn.ModuleList(\n", | |
" [\n", | |
" torch.nn.Linear(in_size, out_size)\n", | |
" for in_size, out_size in zip(\n", | |
" (state_dim,) + hidden_dims, hidden_dims + (action_dim,)\n", | |
" )\n", | |
" ]\n", | |
" )\n", | |
" with torch.no_grad():\n", | |
" self.fcs[-1].weight.divide_(100.)\n", | |
"\n", | |
" def forward(self, state):\n", | |
" x = state\n", | |
" for i, fc in enumerate(self.fcs, start=1):\n", | |
" x = fc(x)\n", | |
" if i != len(self.fcs):\n", | |
" x = torch.relu(x)\n", | |
"\n", | |
" x = torch.tanh(x) * self.action_scale\n", | |
" return x\n", | |
"\n", | |
"\n", | |
"class CriticNet(torch.nn.Module):\n", | |
" def __init__(\n", | |
" self,\n", | |
" num_agents: int,\n", | |
" state_dim: int,\n", | |
" action_dim: int,\n", | |
" hidden_dims: Tuple[int, ...] = (256, 256),\n", | |
" ):\n", | |
" super().__init__()\n", | |
" self.fcs = torch.nn.ModuleList(\n", | |
" [\n", | |
" torch.nn.Linear(in_size, out_size)\n", | |
" for in_size, out_size in zip(\n", | |
" (num_agents * (state_dim + action_dim),) + hidden_dims,\n", | |
" hidden_dims + (1,)\n", | |
" )\n", | |
" ]\n", | |
" )\n", | |
" with torch.no_grad():\n", | |
" self.fcs[-1].weight.divide_(100.)\n", | |
"\n", | |
"\n", | |
" def forward(self, states: List[torch.FloatTensor], actions: List[torch.FloatTensor]):\n", | |
" x = torch.cat(states + actions, dim=1)\n", | |
" for i, fc in enumerate(self.fcs, start=1):\n", | |
" x = fc(x)\n", | |
" if i != len(self.fcs):\n", | |
" x = torch.relu(x)\n", | |
"\n", | |
" return x\n", | |
"\n", | |
"\n", | |
"class CriticPair(torch.nn.Module):\n", | |
" \"\"\"A simple wrapper for a pair of critics.\"\"\"\n", | |
"\n", | |
" def __init__(\n", | |
" self,\n", | |
" num_agents: int,\n", | |
" state_dim: int,\n", | |
" action_dim: int,\n", | |
" ):\n", | |
" super().__init__()\n", | |
" self._critics = torch.nn.ModuleList(\n", | |
" [CriticNet(num_agents, state_dim, action_dim) for _ in range(2)]\n", | |
" )\n", | |
"\n", | |
" def __getitem__(self, index: int) -> torch.nn.Module:\n", | |
" return self._critics[index]\n", | |
"\n", | |
" def forward(self, state, action):\n", | |
" return self._critics[0](state, action), self._critics[1](state, action)" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"### MATD3\n", | |
"\n", | |
"Here the agents are trained using the MADDPG framework, while also incorporating extensions introduced by TD3.\n", | |
"\n", | |
"Like in TD3, we start with 10,000 steps of \"pure exploration\", during which the agents take steps completely at random. This seems to cause the agents to take more unnecessary and eradic actions, but does seem to help with exploration and prevent stagnation." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 9, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"class MultiAgent:\n", | |
" device = torch.device(\"cuda:0\" if torch.cuda.is_available() else \"cpu\")\n", | |
"\n", | |
" env: ParallelEnv\n", | |
" config: AgentConfig\n", | |
"\n", | |
" replay_buffers: List[PrioritizedReplayBuffer]\n", | |
"\n", | |
" critics_local: List[CriticPair]\n", | |
" critics_target: List[CriticPair]\n", | |
" actors_local: List[ActorNet]\n", | |
" actors_target: List[ActorNet]\n", | |
"\n", | |
" critic_optimizers: List[torch.optim.Optimizer]\n", | |
" actor_optimizers: List[torch.optim.Optimizer]\n", | |
"\n", | |
" t_step: int\n", | |
"\n", | |
" def __init__(\n", | |
" self,\n", | |
" env: ParallelEnv,\n", | |
" config: Optional[AgentConfig] = None,\n", | |
" ):\n", | |
" self.env = env\n", | |
" self.config = config or AgentConfig()\n", | |
"\n", | |
" self.replay_buffers = [\n", | |
" PrioritizedReplayBuffer(self.config.replay_buffer_size)\n", | |
" for _ in range(self.env.num_agents)\n", | |
" ]\n", | |
"\n", | |
" self.critics_local = [\n", | |
" CriticPair(\n", | |
" env.num_agents,\n", | |
" env.state_dim,\n", | |
" env.action_dim,\n", | |
" ).to(self.device)\n", | |
" for _ in range(self.env.num_agents)\n", | |
" ]\n", | |
" self.actors_local = [\n", | |
" ActorNet(\n", | |
" env.state_dim,\n", | |
" env.action_dim,\n", | |
" action_scale=env.action_scale,\n", | |
" ).to(self.device)\n", | |
" for _ in range(self.env.num_agents)\n", | |
" ]\n", | |
"\n", | |
" self.critics_target = copy.deepcopy(self.critics_local)\n", | |
" for m in self.critics_target:\n", | |
" m.eval()\n", | |
"\n", | |
" self.actors_target = copy.deepcopy(self.actors_local)\n", | |
" for m in self.actors_target:\n", | |
" m.eval()\n", | |
"\n", | |
" self.critic_optimizers = [\n", | |
" torch.optim.Adam(\n", | |
" critics.parameters(),\n", | |
" lr=self.config.critic_lr,\n", | |
" )\n", | |
" for critics in self.critics_local\n", | |
" ]\n", | |
" self.actor_optimizers = [\n", | |
" torch.optim.Adam(\n", | |
" actor.parameters(),\n", | |
" lr=self.config.actor_lr,\n", | |
" )\n", | |
" for actor in self.actors_local\n", | |
" ]\n", | |
"\n", | |
" self.t_step = 0\n", | |
"\n", | |
" def _choose_action_inner(\n", | |
" self,\n", | |
" actor: ActorNet,\n", | |
" states: torch.FloatTensor,\n", | |
" *,\n", | |
" policy_noise=False,\n", | |
" exploration_noise=False,\n", | |
" ) -> numpy.array:\n", | |
" assert not (policy_noise and exploration_noise)\n", | |
"\n", | |
" actions = actor(states)\n", | |
" if policy_noise:\n", | |
" # Introduce policy noise to smooth the critic fit\n", | |
" actions += torch.clamp(\n", | |
" torch.randn_like(actions) * self.config.policy_noise,\n", | |
" min=-self.config.policy_noise_clip,\n", | |
" max=self.config.policy_noise_clip,\n", | |
" )\n", | |
"\n", | |
" if exploration_noise:\n", | |
" # Introduce noise for exploration\n", | |
" actions += torch.randn_like(actions) * self.config.exploration_noise\n", | |
"\n", | |
" return actions.clamp(\n", | |
" min=-self.env.action_scale,\n", | |
" max=self.env.action_scale,\n", | |
" )\n", | |
"\n", | |
" def choose_action(\n", | |
" self, all_states: List[numpy.array], *, exploration_noise=False\n", | |
" ) -> numpy.array:\n", | |
" \"\"\"Choose actions for the given batch of states.\"\"\"\n", | |
" assert len(all_states) == len(self.actors_local)\n", | |
" all_actions = []\n", | |
" for actor, states in zip(self.actors_local, all_states):\n", | |
" actor.eval()\n", | |
" with torch.no_grad():\n", | |
" states = torch.from_numpy(states).float().to(self.device)\n", | |
" actions = self._choose_action_inner(\n", | |
" actor, states, exploration_noise=exploration_noise\n", | |
" ).cpu().numpy()\n", | |
"\n", | |
" actor.train()\n", | |
" all_actions.append(actions)\n", | |
" \n", | |
" return numpy.array(all_actions)\n", | |
"\n", | |
" def _step_learn(self, agent_index):\n", | |
" if (\n", | |
" len(self.replay_buffers[agent_index]) < self.config.batch_size\n", | |
" # Only start learning after the pure exploration phase has ended\n", | |
" or self.t_step <= self.config.pure_exploration_steps\n", | |
" ):\n", | |
" return\n", | |
"\n", | |
" with torch.no_grad():\n", | |
" # Sample a batch of experiences\n", | |
" replay_buffer = self.replay_buffers[agent_index]\n", | |
" experiences = replay_buffer.sample_batch(self.config.batch_size)\n", | |
" states, actions, rewards, next_states, dones = [\n", | |
" [\n", | |
" torch.from_numpy(numpy.vstack([\n", | |
" v[i] for v in col\n", | |
" ])).to(dtype=torch.float, device=self.device)\n", | |
" for i in range(self.env.num_agents)\n", | |
" ]\n", | |
" for col in zip(*[e.value for e in experiences])\n", | |
" ]\n", | |
"\n", | |
" # Calculate importance sampling weights from sampling probs (for simply not using alpha / beta here)\n", | |
" sample_probs = torch.from_numpy(\n", | |
" numpy.vstack([e.sample_probability for e in experiences])\n", | |
" ).to(device=self.device, dtype=torch.float)\n", | |
" sample_weights = 1.0 / (sample_probs * len(replay_buffer))\n", | |
"\n", | |
" # Calculate target Q values\n", | |
" assert len(self.actors_target) == len(next_states)\n", | |
" next_actions = [\n", | |
" self._choose_action_inner(\n", | |
" actor, ns, policy_noise=True\n", | |
" )\n", | |
" for actor, ns in zip(self.actors_target, next_states)\n", | |
" ]\n", | |
" q_target = rewards[agent_index] + (\n", | |
" (1.0 - dones[agent_index])\n", | |
" * self.config.discount_factor\n", | |
" * torch.minimum(*self.critics_target[agent_index](next_states, next_actions))\n", | |
" )\n", | |
"\n", | |
" critic_losses = sum(\n", | |
" torch.nn.functional.mse_loss(q, q_target, reduction=\"none\")\n", | |
" for q in self.critics_local[agent_index](states, actions)\n", | |
" )\n", | |
"\n", | |
" # Update local critics\n", | |
" critic_loss = torch.mean(critic_losses * sample_weights)\n", | |
" if torch.isnan(critic_loss).cpu().item():\n", | |
" raise RuntimeError(\"NaN loss\")\n", | |
"\n", | |
" self.critic_optimizers[agent_index].zero_grad()\n", | |
" critic_loss.backward()\n", | |
" self.critic_optimizers[agent_index].step()\n", | |
"\n", | |
" # Update sampling priorities\n", | |
" with torch.no_grad():\n", | |
" new_sample_priorities = critic_losses.sqrt().squeeze().cpu().numpy()\n", | |
" assert len(experiences) == len(new_sample_priorities)\n", | |
" for e, p in zip(experiences, new_sample_priorities):\n", | |
" e.priority = p\n", | |
"\n", | |
" # Periodically update local actor as well as target networks\n", | |
" if self.t_step % self.config.policy_update_freq == 0:\n", | |
" # Update local actor to step towards maximizing the (first) local critic value\n", | |
" actor_loss = -torch.mean(\n", | |
" self.critics_local[agent_index][0](states, [\n", | |
" actions[i]\n", | |
" if i != agent_index\n", | |
" else self.actors_local[agent_index](states[agent_index])\n", | |
" for i in range(self.env.num_agents)\n", | |
" ])\n", | |
" )\n", | |
" if torch.isnan(actor_loss).cpu().item():\n", | |
" raise RuntimeError(\"NaN loss\")\n", | |
"\n", | |
" self.actor_optimizers[agent_index].zero_grad()\n", | |
" actor_loss.backward()\n", | |
" self.actor_optimizers[agent_index].step()\n", | |
"\n", | |
" # Soft-update target critics & actor\n", | |
" tau = self.config.soft_update_factor\n", | |
" for local_net, target_net in [\n", | |
" (self.actors_local[agent_index], self.actors_target[agent_index]),\n", | |
" (self.critics_local[agent_index], self.critics_target[agent_index]),\n", | |
" ]:\n", | |
" for local_param, target_param in zip(\n", | |
" local_net.parameters(), target_net.parameters()\n", | |
" ):\n", | |
" target_param.data.copy_(\n", | |
" tau * local_param.data + (1.0 - tau) * target_param.data\n", | |
" )\n", | |
"\n", | |
" def _train_episode(self):\n", | |
" states = self.env.reset()\n", | |
" episode_scores = numpy.zeros(self.env.num_agents, dtype=float)\n", | |
" for _ in range(self.config.horizon_length):\n", | |
" if numpy.any(numpy.isnan(states)):\n", | |
" print(\"\\nNaN State, episode terminated\")\n", | |
" break\n", | |
"\n", | |
" # If we're in pure exploration phase, sample an action uniformly at random\n", | |
" if self.t_step <= self.config.pure_exploration_steps:\n", | |
" actions = self.env.sample_action()\n", | |
" else:\n", | |
" actions = self.choose_action(states, exploration_noise=True)\n", | |
" if numpy.any(numpy.isnan(actions)):\n", | |
" raise RuntimeError(\"NaN Action\")\n", | |
"\n", | |
" next_states, rewards, dones = self.env.step(actions)\n", | |
" if numpy.any(numpy.isnan(rewards)):\n", | |
" print(\"\\nNaN Reward, episode terminated\")\n", | |
" break\n", | |
"\n", | |
" for i, replay_buffer in enumerate(self.replay_buffers):\n", | |
" replay_buffer.add((states, actions, rewards, next_states, dones))\n", | |
"\n", | |
" self.t_step += 1\n", | |
" for i in range(self.env.num_agents):\n", | |
" self._step_learn(i)\n", | |
"\n", | |
" states = next_states\n", | |
" episode_scores += rewards\n", | |
" if numpy.any(dones):\n", | |
" break\n", | |
"\n", | |
" return numpy.max(episode_scores)\n", | |
"\n", | |
" def train(\n", | |
" self,\n", | |
" max_steps=1_000_000,\n", | |
" solved_score=0.5,\n", | |
" ):\n", | |
" \"\"\"Train the agent until the max training steps or the solved score is reached.\n", | |
"\n", | |
" Returns a list of (step, score) pairs, one for each training episode.\n", | |
"\n", | |
" \"\"\"\n", | |
" scores = []\n", | |
" scores_window = collections.deque(maxlen=100)\n", | |
" solved = False\n", | |
" max_windowed_average_score = float(\"-inf\")\n", | |
"\n", | |
" stop_step = self.t_step + max_steps\n", | |
" while self.t_step < stop_step:\n", | |
" average_score = self._train_episode()\n", | |
" scores.append((self.t_step, average_score))\n", | |
" scores_window.append(average_score)\n", | |
" windowed_average_score = numpy.mean(scores_window)\n", | |
" print(\n", | |
" f\"\\rEpisode {len(scores)}\\tStep {self.t_step}\\tScore: {average_score:.2f}\\t\"\n", | |
" f\"Windowed average Score: {windowed_average_score:.2f}\",\n", | |
" end=\"\\n\" if len(scores) % 500 == 0 else \"\",\n", | |
" )\n", | |
"\n", | |
" if not solved and windowed_average_score >= solved_score:\n", | |
" print(\n", | |
" f\"\\nReached average score of {windowed_average_score:.2f} between \"\n", | |
" f\"episode {len(scores) - len(scores_window) + 1} and episode {len(scores)}\"\n", | |
" )\n", | |
" solved = True\n", | |
"\n", | |
" # Save the best model so far\n", | |
" if average_score >= windowed_average_score > max_windowed_average_score:\n", | |
" self.save()\n", | |
" max_windowed_average_score = windowed_average_score\n", | |
"\n", | |
" return scores\n", | |
"\n", | |
" def run_episode(self, horizon_length=10000):\n", | |
" \"\"\"Run one episode using the agent.\"\"\"\n", | |
" states = self.env.reset(train_mode=False)\n", | |
" score = 0.0\n", | |
" for _ in range(horizon_length):\n", | |
" if numpy.any(numpy.isnan(states)):\n", | |
" print(\"\\nNaN State, episode terminated\")\n", | |
" break\n", | |
"\n", | |
" actions = self.choose_action(states)\n", | |
" if numpy.any(numpy.isnan(actions)):\n", | |
" raise RuntimeError(\"NaN Action\")\n", | |
"\n", | |
" next_states, rewards, dones = self.env.step(actions)\n", | |
" if numpy.any(numpy.isnan(rewards)):\n", | |
" print(\"\\nNaN Reward, episode terminated\")\n", | |
" break\n", | |
"\n", | |
" states = next_states\n", | |
" score += numpy.sum(rewards)\n", | |
" if numpy.any(dones):\n", | |
" break\n", | |
"\n", | |
" return score / self.env.num_agents\n", | |
" \n", | |
" def save(self):\n", | |
" \"\"\"Save the trained model.\"\"\"\n", | |
" for i in range(self.env.num_agents):\n", | |
" torch.save(self.actors_local[i].state_dict(), f\"actor{i}.pt\")\n", | |
" torch.save(self.critics_local[i].state_dict(), f\"critics{i}.pt\")\n", | |
" \n", | |
" def load(self):\n", | |
" \"\"\"Load previously trained model.\"\"\"\n", | |
" for i in range(self.env.num_agents):\n", | |
" self.actors_local[i].load_state_dict(torch.load(f\"actor{i}.pt\"))\n", | |
" self.critics_local[i].load_state_dict(torch.load(f\"critics{i}.pt\"))" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Train & Evaluate\n", | |
"\n", | |
"With everything set up, we're now ready to train the agent. Here we train the agent for 1 million steps." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 10, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"agent = MultiAgent(env)" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 11, | |
"metadata": {}, | |
"outputs": [], | |
"source": [ | |
"agent.load()" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 12, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"name": "stdout", | |
"output_type": "stream", | |
"text": [ | |
"Episode 500\tStep 8778\tScore: 0.00\tWindowed average Score: 0.02\n", | |
"Episode 1000\tStep 23074\tScore: 0.20\tWindowed average Score: 0.11\n", | |
"Episode 1200\tStep 49364\tScore: 1.30\tWindowed average Score: 0.51\n", | |
"Reached average score of 0.51 between episode 1101 and episode 1200\n", | |
"Episode 1500\tStep 269528\tScore: 2.60\tWindowed average Score: 2.27\n", | |
"Episode 2000\tStep 670379\tScore: 0.00\tWindowed average Score: 2.13\n", | |
"Episode 2400\tStep 1000728\tScore: 2.60\tWindowed average Score: 2.16CPU times: user 10h 34min 39s, sys: 3min 7s, total: 10h 37min 47s\n", | |
"Wall time: 10h 51min 52s\n" | |
] | |
} | |
], | |
"source": [ | |
"%%time\n", | |
"scores = agent.train()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Since episodes can get terminated early, here we plot scores against the number of trianing steps instead of number of episodes.\n", | |
"\n", | |
"During this particular training run, the windowed average score reached 0.5 after around 1100 episodes, and continued climbing until stabalizing between 2 and 2.5." | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 19, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAmEAAAFzCAYAAAB2A95GAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjMuMywgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy/Il7ecAAAACXBIWXMAAAsTAAALEwEAmpwYAADCI0lEQVR4nOxdd7wdRfX/zt7y3svLe+m99x4CCYTeO6goIEVAFAQRIVhQEUTEgoCoIAgiqD8QRJQiSu81QEJIL6T38lJfXr/37vz+2Nv23i0zszO7e5P98gnv7u7MOWen7ZkzZ84QSikiRIgQIUKECBEi+AstaAEiRIgQIUKECBH2R0RKWIQIESJEiBAhQgCIlLAIESJEiBAhQoQAEClhESJEiBAhQoQIASBSwiJEiBAhQoQIEQJApIRFiBAhQoQIESIEgHjQAvCiZ8+edOjQoUGLESFChAgRIkSI4IpPPvlkO6W0l9WzilPChg4ditmzZwctRoQIESJEiBAhgisIIWvtnkXLkREiRIgQIUKECAEgUsIiRIgQIUKECBECQKSERYgQIUKECBEiBIBICYsQIUKECBEiRAgAkRIWIUKECBEiRIgQACIlLEKECBEiRIgQIQBESliECBEiRIgQIUIAiJSwCBEiRIgQIUKEABApYREiRIgQIUKECAEgUsIiRIgQIUKECBECQKSERYgQIUKECBEiBICKOztyf0Iqo2PtjmZohGB4r84AgOVb96KxLY1+XapRWxXH1sY2bN/bjoOHdceGXa3I6BS96qrQ2JoCAPTsXIWdLR3oXBXHjqZ2AEDfLtXYtLsVcU1DKqODEIBS5P8CQHNHBokYQVU8BoDaSEjyz3QKbN/bjp51VdCIcT+VoWjpSKO+OmHJI8ene6ckBvfohNXbm5HR9fyz1g4dVQkNBDDlBwjqq+NobDPesVunJHY2d+TT9K6rRls6g6b2NHSdoqUjg5hGUJ3Q8jLnZNi2tx0DutYgrevo2bkKDXvbQQHsaU2hrjqOuEZMb9y5KoHWVCYvZ0tHBp2SMYzsXYflW/cCAJra00jENDS2ptCzrgqtHRnU1ySgEaNO45qG5o400hmKfl2qsb2pA62pDDpXxbG7pQPDe3UGBUVjawqpDEVTexq966rQtVMSe1pSqK2KGeWTyqAtlTGVS+7dCMm1IYrm9jS61CTQo3MVdGrQbWpPQyMEXWoSqEpoJl7ThnQDIQSbdreipSON9rSOVIaiNhmDToGdzR3o0TmJmkQM7WmD/7a97Zg2tBt2Naews7kDbekM6qri0Cmwq6UD3TolEdMM+TJ64V5zRxrV8Ri61ybR1G606817WvM0e9dVAQDa0zp0StEpGStre0B52yIE6F5bhZ3N7ehSk0Svuqp8+2rpyEAjRnvI8elVV4WOLI/aKmNYHNGrc74McjKkMhSdq2ImfsV8R/Wpy/Pp26UGO5rakcroqK2Ko7k9bWpLPTtXYVeL0YaL230OGiHQs4TTOkVjaxpdOxntqLgP7W03yjAZJyX9EKZ3BYCqeAyDunfCim17Te2EUmNc2NrYZpKhuB4IAWKaZikrYPTDXJsEgLaUjrRulFdxn+3Z2WjLy7fuRbfaJHa3dORpFLeNmIaycgaAXnXV2NuWwsBunfJ9DjD6fUNTG2qr4mjtyOTLLtdWBnTthK2NbUiXyD+gayc07G1HRyaDtlRxOzPy6RTY3tSOnp2rkM5QtKUzOHBQV6xsaEJ9TSI/1ubqaGTvzvlxeHdLB/a0ptCR1k1jTUeaoiOjo0tNoqxNAkAipiGmEbSlMuhVV42GvUa9FMtSMjQBMNp8OqObxsZd2fLNlWFc05DWdbR2GOVQkyzYYuKahmRcy7f5HAghoJTmy4QQgrqqOPa2p7P3C22tS00C8RgpqQOjfPa0pNCtNlkke4EepdT0roDRBzol42hqT5nSF5d3104J9K6rwp7WFFIZPf+uuXabGzuqEzE05NsyQffaJPa0pjCsZ215QfqISAkLMW57YSn+8v5qAMD7Pzoea7c348KHPgIADOhag5aOdH4Q/8mZ4/Hz/y0uo3HUqJ54d/l2/4QWQHVCwwc/OgHH/eatoEURxlNXHY6z7/9ACq1pQ7ph9tpdUmiJ4L/fPhKTBnbB4b9+gyvfbV+ahBueXuCJd+eqOJpKlBUZmH3Tidzt690fHIej7niTK88vvzgRNz6zkCuP3/jNuQfg+/+aFxj/ZEzDb887AN9+/FNPdHj73NQh3fCJRb86dkwvvLWsgYv3xYcOwaMf2p7JDABY8+szMPUXryGj201iI4QBc35yErrXJgPjHylhIcYHKwvKU3N7GisbmvLXG3e3mtIu3tRoSSPsChhgzJhzs8lKxZrtzdJordvZIo2WCEpnwazY2dzhnsgFKhQwAELta28bvywfr97JncdvfLAi2DGhI6Nj/oY9numsKhoPWWClgAHgVsAA4LUlW5nSRQpY+JGzngWFyCesQlCTiDk+b8guNVYqgu4IYUJuOSxIpAXqo3S5LUxIZfg/hsk4//DYkQ5/Ow7DWNGeXbL0grYAyzrMbT0CHxKxYNWgSAmrEMRKHABKrxv2Bj+wesHuCreEyUTB7ykYUAAtAh9JEcuRXxCx7rWn+cugucO7cqEaYRgr2lLeFaiWABWhlgqo5whssHCt8xWRElZBKJ7Ll36owzCwesH2Cpc/Q+UtOwSthAFASzv/R2ZHc3jrUOSj2SxQBkEqBqwIw1ghouCWIkilP82wzEgljgkR9l1ESliFgMK81FH6od4egiUGL6h0+dskLK/kEIaxu1nAcrR9r3efMFUQWT4SyVMJFpIdEnz3vEKGJSwM7+GEjsjFIgIDIiWsgtBaNMB3SgbvNyQTDU3hHlDdIPPjG/SHnFJzW2NFmBVpkTIV2SSwJ1pWZ4IMS1iY2xsgZk2OsP8hUsIqBJRSk79JMmBnQtkIwxKJF8hchhKxQsmGiBUozHUoUqZCZRByxSAskGEJC3N7A8LtIxkhPNi3vuT7OIqdi1M2ARMrFWEfUN0g0yFbxBdJNkQsR3tD7A8lYpUQsYRVwu7IMECGJSzsY8b2EPtIRggPIiUsxCiNMVP8cd7XTN2VbkEQja2lmpYowmCNkwkxS9i+1cfChHYJymrYx4ywK4kRwoF9y7EoRNjblsK89XswbWg3pDI6drek0CkZQ0tHBh0ZHSOyxxAt3dIISoGxfevQsLcdH6/ZiS41CRw6vIfJGrFpd5vp47yvfSTnrd8dtAieMGuNvAj3QfuELdnciDnrgovYrwJ2gTqdMHf9vlUGYcKWPW3uiVwQdqtj0EFxI7BhyZZGHD6iZ2D8SaVto502bRqdPXt20GK44qg73sD6na04YWxvrN7ejFUlEdXn/fRkrNi2F2ffPxMAcPvZk/DErPX4dN1uAMBlRw7DM59uNEUhLz5eI64Rpm3SESJEiBBBHjRinOEYYd/AmZP74d4LD1LKgxDyCaV0mtWzyBKmCOt3GscKvbt8u+VW5daODDbuLswGl21pwvKthWM4Zq/dhVRaN3V4PWsx61VXhQ9W7gAA/OzzE/D3D9di+Ta+Izyc8MlNJyIZ1/Ctx+ZYHnt06eFDURXX8Kd3VuGMyf1w6+cnADAivY/9yUsAgCeuOBQbdrXanlH3yU0nAjC2mZ/8u3csn//xrZV4+L3VtnL+9dKDMXlgF+xpTWFVQzMuf6RcOf/kphORiGvIZChufHYBXliwBVcePRxXHD0cADD1F69Z5qlJxjDhpy+bwkV846hh+PO7q03y3/nyMjwxaz0A4wzM9394PKoSMbSnMvlDm5va0zjmzrdMPF6ccRROu/tdAMCTVx6GCf3r0ZbK4OKHP8bizcYRVI9edgj2tqXxrcfm2JbB+z86HtVxLf8e//32kejftdqUpjphvEsO9dVxHDmqJ15YsCV/7/++fgia28t5vfbdY9C9NolEjOQtD8X1fMNpY3HO1IH4wxsr8LcP1gAAnr36CIzs3TkfFb04/X0XHoRDh3fPl01VIoarH5uDtz8rHB3znRNH4/xDBmH6r17Pl3VMIyCEIJ3R8cjMtbj79eU4bHgPnDi+j+nM1GRcw30XHoRvWLSFHK14TAOlNL/cn5MlphH87YM1+P1ry015Xv/eMehakwAhBC8u3Mx0NuQFhwzCPz5e75quFO9cfxxqq2L5+nzs8ukY27cOAFCTjOG95dtxxaOf5N8FQL695VB8WHg8puGW5xbhmU834oqjh+PBd1ZZ8s2dV3rdiaOwensz/jN3E644ejiuPHp4nl5OpmevPgJn3fc+AKONju9Xb+pH7/7gOHRKxkAIwX/mbsTP/lt+pq0V7r3wQBw2vAdqq+L4dN1uXPDnD13zDO9Vi39deRgu+POH+GxrE246YxxOmdAXiZiG9nSmrN8V45ObTjTJXdzO/vzOKtz75gocM7oXfvvlA0xl+rW/zbI8dunPl0zDjCc+tbRkf/OYEXjg7ZUAgBeuPQpz1u3CTc8uzPONxzTc9coyPDJzLb52xFBccfRw6NQ4KeWuV5bhsY/W4ciRPXH3+VNM/ckO//rmYfjJswuxdMtey+ef3HQi4prhjXT368vxl/dX45ypA/H9k8cgreuoScQsx0YnzPvpyWhLZfL9thgPXDQVBw/thhXbmnDeg9b1etmRw0zj/emT+prGqFI8dMk0HD6yB6bc+qrJKnrVsSNw+ZHDTP06N3awlJ3fiJSwAJEoinpvtdmxI6Oje20S24vCN9QkY6hNxvOBAEUPHq1JxNBqE9uqR+cqAEDXTmbayZiGjoyO+poEarNxyuqr4/n0Jhq1SccwB7k8dr4hPTpXoUtNAoD9zLNH5yR6dK5Cj85VaLTZiVQsW7fs+3TplLCUuTRP905JUyyi7rVVZWnqqgtdqDoRy9/vnD16qDphfIxKUVxv3WuTqK2Ko7Yqjpqi+G+G8uPsttmjNonqoiOtumfLxAnJeCwvXzGdKotjenrUJtHNoo11r01iZ3MHutUa/LSid+xRm0TnqngZj1y+nHw5uXuU0O/eOWkqn9L36dHZeNa5Ol6Wt1My5tgn7MomJ0t9daI8T20y3xes3gko709WdFjQvXPSxKO4vIDCuxu/C/dL5SpuE7l+lPtrhU7Z/N06JbG7UyqfvrSuAHN9lcqXkzEXQqdUrkSM2B4hVUyr+D2dUB03+lyOX7dOSQzq3gmA4RLihHK5C9e5NtS5aHzLlUFcs46x3r02aRvjr2unQtn36Jw0lWGOfnE99etSk38+JquE19mMtVboUZtEr7oqLN2yF9UJrWw3qlWbqq9OoG8X8wSOB11qEqhOWI9XubG69Mzj0vyAcRpMRqeoq3LuQ92z7awmETMpYV1t2q0drMZnPxE55quGTf0SYj56SNOIKcIygXGeYvFH2GrpWLT9COUjpj+wuvJEnxOaIBMieFCFGzseqsVp7eiKyOml2K3y2slW2hZZq4IlXYyRmKW8bGJIh6z2XkpGZj9ioVWchim9RYk7tVvRZ7Z5iPO1V1iRi9tMjJx4E5vf1mnljqms46TKMVtkbBQdbwPWqbgRKWEBoljBihFiOpaIUgqdlh8inGtfprQCvFnaadD+grL7Eu/bsKQvLiJRpbAYZR9ht/QCLAnxHpU/l52U/BVCSeawhcATVdorCUH3da/wU3qnGI3URhJSooVZ9VuZVUAIyY9HrJOaSkWlt92QDXf7Hmy1dQDxWOFpqYk7Z7K3Wo4q/ogG8YFgmy3Lk8vOXCxsCRO1HsqkSyx/ctMrrX+2POy83NqXVd2wWgTsENPYhiUrPqXyWC2xckOwTIVYlVp2ZPYjBlpmXcHdImNdB4wMePIx5pFuCbMgWDxum9I60eGoR1mW8dwnhdlKzUWdDzz1Qhjltvsu8JZT0CpqpIQFiPLlyMKzdDYYq3k50rrhicwEWNbBwz6/4B1wK3HC5FZPMj5crLws83iQo0DDnFmmJUzGyRL7uCFh34CPndvJT9NODJPiCgLVn36CQn+O2fiwRQgHIiVMMWwHcGLuzHGNmEzZOUuY3XKkK303ucSyMdOR6stic1/cJ4wNLMptiWcUhwyFtLYzOgZypWXAOgssfTPbfDa3S4uGVYFjScfsw2Lpj2RGQoYlLEDI6EdckzRCuNLzWsJkqwNllmDJHKyoJWwsYV5ht5QpAkIKljAZbhLeITI2io1lvAi6eJSNUISQQYSQNwkhiwkhiwghMyzSHEsI2UMImZv9d7MqecKIeIklrBi53R7Jog5v10mFGiFLw7Ob1QVuwDXA33m8dVd387g8Wsx0hPLIXCoul8PrbiOZM3e7nWw8YKEga4eVyr7lJKKV3iVqMRXe+CKUywBrz5ZRTXGb5XJS4tfrJIOQosotO8nLxZQ6HMM6M/wwAPgBlSEq0gC+RymdQwipA/AJIeRVSmlp0Jh3KaVnKpQjUNj6VYCYPjbxkuXIVDa2WLElzFiOLKUjKpcc2PsveMvv9gwozPR4wdpJeR3zZdcFAeFW/Fh3wJVbsvisGjlrSf45q98JAw/m3ZEMtNxCfLDxCW5Ul8GZZ+pBuNNbWCM99GmvYNlRx/V+FvRE2lRxG7ITUapjPkh+fPRzo4uUnZ25iR3zkgUHbXt2gUFZ9VBKN1NK52R/7wWwBMAAVfzCCrtYXED5zrritpSLhl/a4Vk+zCxg8wlj2OkTKPgE8TrIuTup89CSAy9+XFL452bbcP/IsKLUKuwFMpaOmCxhnrlk6SjsW06krfq6/eRAnI9jPoGXz8nN2rdlKNR2bYoYArHJ4fTM48Q2RyNHhz1ERWgGdiZUmLi28EVHJoQMBXAggI8sHh9GCJlHCHmREDLBJv8VhJDZhJDZDQ0NVkkqEsX9tXTZJHdcUaljfikI79SuOF+FQ7W/KcvAXvzxkrGU5Fe9ePU/UekGLXNLvQxLWJCQ4xPGx68SN7CwQsaYYbc70gksy/Wyiz2nfIXDJ0wdvPuEBVs+ykcoQkhnAE8BuI5S2ljyeA6AIZTSAwD8AcCzVjQopQ9SSqdRSqf16tVLqbx+oWwJRiOWrcm0HAkavO0UJQOKrflZvt+RLB7KgrXyWMKKlyds34+/ukX9l0Rn31bbyXn9XUrvsfqEWfMx35QSu81PU5hCyPMJc07j1C8dg7X6UIYywhfYKfY8y7COZWS7UYdT9pwljPErr7L4BVYjGSyu6r89fkCpEkYIScBQwB6jlD5d+pxS2kgpbcr+fgFAghAS3HHmPqN0F5KVdaJ0i73F51NhsFYBwj6Cd1bL+z5MuyMl+IQFAquProf8Mt9d5nKkDAS7EcU7bx6rJwHxbCUNorRk7ix0g2OICjsXDpvfprxSg7UW6FWKJUz0/b0Gaw26dFTujiQAHgawhFL6W5s0fbPpQAg5JCvPDlUyhQmlq4gZnVo2wqTLcqQwfw8dk8XqIaNhK9uFpajX8ZQpkzVRSAiGJFbWC8FZZe65qU14tHawH1vknk7Gx5lps0MZX3W8ROFUXpaWMIdNRYXfFs85LEJmut7h2gc5mVjRs/cJY7NuiVi4Dfp8yNUp8/K+yrbHMzZaWNed0pXdZ+YUDqjcHXkEgIsBLCCEzM3e+zGAwQBAKX0AwDkAriKEpAG0AjifVvoZBIKwOqAaABLxQpOiMKw/5hhTgsFaGdKEpSaIzZ4m3g8W7weZ9/VVfEBVfZS9Vi1znDEGlL4j6/JJmCFrGKtEnzDn5Ug18HOssgtR4VUOuXHCChbNCjGECb9/SD5TwlCmhFFK34NLn6OU3gvgXlUyhBmEmENS6NS6CVrtjpTD30te55mwV/rlxHzgwSKGq1WIh5Z7PpH3YzqaxtKPx85C5savnKaz3O7yeZm5l2b16+Nc8XHCOCY57HVtlVeuT1hZqBV+Eo6w9gmzsxo70CmxFItYC3l3X/MuRypte1xpiekvNy/ebAErqfvAnHPfgE6p5ezZ5BNm48cj9p3xZynHXQovFhS+vGGx7DmhvDzkjxCqg7Vy0yjJLbL7TCWCtCT4zTpcJc8O640F5W8j4/2cfcLCh0o5tqgSxmcViJSwgGB0i0Kry8ZmLYMpRAWotA+CPDr8/hGyIB6slTEj56DAdUgv2z5GPgHAVq8U5Qo/r0VTdLmNRT72mbv7vf10XDeBtwz40vO1UdWjgnSF2YJeXCRYqwtNAFIbKyEFcn7uFpRixWP0CctBdfxH1YiUsADBshxZdnZkSXspXdZkRSX5hNmBt/N4jifj9rwyJpwO9cr+AvkBHuUjptdiCNvMXWSJV7Tv8IQyUAGRTQgSuQvn9HOocgoAbDs5KSlY5w0KdhNbdhCQouVIxjwBdztZPrjhGj3cESlhAaG0Aek2uyMtg7VKaGWqO5xM+nakhC1hjOnYji0qDtYqJoRjnDBenxuGNJRaONbbDmhsfnCsYlpar0pueonwXa4M+bM7MsxgCjpMrX+7IWz+NzJPtbCjJ3RsEYMM8hVJg2IYJjV8qwTmv27wHFIl8gnbf1HcdOx2RxZbwrbtbS9rzIZPmJp5oB3VoBttHpxyeN/15facY6AJSxl6RH7AJBY3BRGGj0YxRBaOhUNUCPBmpu3Q6Mosm07pFVWPlz7BqmzLWHqyOxS+eAnQ6llBBmc57CdlfONLrkiYD/Bmpq4InAN0FKw1gicUm4sBIGPTAIs7/LqdLfJ8uUKwy9IrRIMQsvsaMARrLaYrJI2TX508WsUwNoGUc7PbNWmF8l1p4tarUjBHzLe8V1kDsD9Q5xUWMkOY9PHIip6qo7BkRmcyJucG3LqTH1Gh+HzCiOmvG7z7hAWLSAkLCXQbU5hb9HDRuD5MDtx27g0+NVtXyxMnPeWRwHkGGsnpeCBjzC2NQVS6BZ8HosuRfoHlY1CaRtwnjN13iJu200MLVwe79Kr6vx+1LqM8nZbuWcZMQojjy9qXuxiYgx8H3O2k+YSFa/hwRaSEBQVinoHoNr1XVXuSRdfeJCyJgQMtYUsYY7pyv6nynDKOLSI2FyJmdZYc1EIdFa8vwsyXNZ2XsyPLfMKYKLnwkUAjSHD5ePGm52w4speKgogTpgpSDVJF3xe3cTJsG7D4J9ce+UU+YREA++VIt91S4pYwts81i0xe4IUWd95Q+YQF1/PtZ+v84FnCZAVzsFafILJjUNTqWkpHRUw3K4gGa5UJL32CdQxU2bIcdzyW+YQJ0OGytBfOFGY/wDvYfsf7HYt2R0bwhFInTp6lvzA1MhlOpK48JDtgyvQ1KP54ifuE2dyHGmVNp+V+ILbjPqtPGKOY1unMN9k/Ggaq0IF7E3fjzeR30JU2mhOFbJYfBHgtW7yWMx5U0q5smfSKycjYHXmmNhMHkc+Y0rpawkpvtDUCH96PK2P/xQiykUMq/nHEiQhzngrv4yrPjozgApNjvo1PWJklrPS54Kffm09YOMA7QKqOE8azPKpsmZmJsPdRq9SFiOUoK1a4LUfWtDdgLFkHoD8A4HTtI5wZ+wgA0Jc2eOReDjafMPO1vDhhYnQsaTs8sz7A21944efH6R5uYLWEGdcirgbZPLqOe5N/AAAMbXvclh/3sUW5ZLP/Arz2U9yQAG7AP7BK74s/Z87APzIncMvMA946tP328S6RB/xFiyxhAaG02v32CRMCx0AtQ263zqF6dyQLWD+2o8gGJJGylsfBr05F/VvHCbORwUUCbp8gK8tuyS3H5UhKce5bJ+Klqh+hWm8BAEzSVucfj6Tr0Gn7guwMPgyf5uDBUwqEIT2LVYclrxqob68iMDnm29DMh5Rw47lzJRPPwnIkp0/YuplAt6HooDEAwHBtC47QFjLxtINYnDDGFYsK7+WREhYgihuP3e5I15mx4BjBZAkTI+0buF3COM0T3DMzK4FadqL6o3vwatUPcHHsVee0EsAycNnHMhKZnefycme1haMlrLGwPJKgHejUvAFfj7+Uv3dj+g8Y99/P4fWq63EgWSFPqH0cYe/rdsjJHTbncjd46i4Ln87/fCRxGz6vfWBJv+CYn73HwpRSYMMsYMiRSJJM/vYIsgkjyEbEkHHI7A1B1WHQLqiREhYkihqdXbDWMkd8KzICrVdohse7Vq8YvocyEOH36s3o9M7PARi+S2GAndWVCw5xwrz6sTkqYVsXmYQ49MNvAgDm6iNwQceN+EHix9gw7QYAQD1p8SUGkgFJFhOFbZp3WdXWOqpIRj+6s0oWjmOqqVxd6Ditsi19AXjrVwCAxfoQHB1bgM/FLJSwIiK5cdLt3QkANG0DWnYAfSfh8fRxWKP3wQq9P8Zp6/F61fW4I/GgCxX/sK845kc+YSGB7e5Iq3sS/G+kOZzKWZb3lQezvyeTY74LNs1Fus9kxLfOxzXxZ/GyfjBW0gElSou1bATWAVQdwerrx7i1396JmDo+t4PlbsqSa8flk62FZRENFPVNqwAAs/XRmKlPwLpYDb7eN0c3OPOILOVPRj9iO7aIFv1mp80fE07tJ9KVvIT2KoKyBQwLukyW93UzAQAHtj2AXajHC8kbQL26bdAUCHTEM23A+3cb93qPxY/TgwAAA0kDppJluCb+LI7R5uH+xO/wSmYantGPcpc3i5wIbOewElMeN6g+CUU1IktYgDDvjrRbjiSO16V0WMFmmbZTDMMxEw6DY/7wpjk4RpuXlSebglLgk78BL98INCxFavBRmK2PRg3pwEnaJx6l8A77euVHfnC1USRF4OgTtqWghJGi95itjykTivjoLRL0QM4CplAbDBYbVa/qZVwJw2qkqjbQc/c8nKF9aJTO1kVA38nYhfr885Njn+CF5A1YVvVVXBZ7AciqZaUHeFsqwHMfx4z3D8Pbye/gjM9uBD68DyAxoM/EfJINtBf+ox+Jf2WOwXbaBUdr83FJ/NVyWh7BHazV7n4F9MViREpYSGC7O9LtWnGYBlE6zMfY2Pw2aLvlVegQB4ZBoXU3Ll95Lf4vebv5/pYFwH9nALMeBqq7IDX8JHyl48e2IsgcTFh9/UrVE9EPbq4OZI57rMuR5225AwDwZuYAvKQfbMhBCtIEagmTREdGP2WRhdr8dkPYHPMlG8IkrhiYLd9WY5fl3EjXcfpHl+C+5D2oyew1LMFFCtI/M8dijj4S47W1qCIp/CTxd0wjy7IhkAyC9bQR34j9D0eR+ZgRe8rs17XyTQDAYK0BI3a9Cxx4EXDVB0BtzzJR/pT5HE7tuB2z9LHcfYunHAtWMzZ472vBam3RcmSAKO509j5hfHRYwWQJs8tL1DhREk7CvOc8SzdbNyw1P8/92GxYxnDV+0CPEUg3dwB4HgDwo8QTeFefzM5TwQAhxSXMIU4Y04C7fhbQsh1It+HAXSswl3TCIjoUgMPySfN2YPtn2NjjcNRv/xQjmz9FR6ILrm/7Jqycbo7UFqJrujNa0AsN6Mb8biKohMk3i4zmXXw2aRS9rBe6/vn+2cNJfJ5XMyneu9fkf3ZJbweatgJ9JuTv/V/mFPxf5hSsqb4wf2+aZsQPyxXJFVt+htGJufnny+ggoCFrOc6NVTkc+V2gxwhH+XQQWyXMj4C7BV429yuiNxYQKWEBQsbuyKCbm1QrDjcPQSsga0KbQaEXdgEz7zPi6WTRBztxdMenwJ9vMZxbk3VAt2H55+1I4PnMITgj9jFO0OYICuQO1mVmqwO4Lf21bMo4n53D18OgB2DTXODhE/P3zgFwcKI3jun4PQDDEla2eeCF64F5TwCgmDvq27h6I8XZBw7EUaN6YvuThQ8JIUB73WDsoHXGrsn2lzAzMR4XpG5iki9skNE0eD9uKvUZ1UtF8n3OZK0YFFNk8PXcvQ746E/Akv/mb53Q8H/Gj74TATRbZttLazCAbAdpa0RNpgkAxfBWc3iJB5K/B+77fVneN4Zfj+NdFDDAUMI06K7piiEUooLZJ8xbgw16+TJSwkIC+zhh5QuSpXdEPF+8WthCMPHkt4R55Jcr+e/EnwJefsP07Muxt3B6+0Jg41JgyJHAod8sCf1OcHVqBs6IfQUxopvKP7Z7DbDyUwDAkS0rsY30xWraz6O09pBZdVYDpuuAu3mu8ffCJ4Gug/HBk3fh0IZ/owodaEcSGimxDLfuAj5+ENDiwOHXYkfncQCWZNd1ynllqrrihPbfYATZhFtq/onO2XhiKlEJG3VZMpmSeHQ18BOsbVrt7lO2Z3bpDtn2L1yS/AdWb/0+8Nr3jZuxZP75lD1vALW9gH4HADDviLyqYwbSiOHH8cdwcfw14J4ReBjA+4kJiCNdzuzI7wB9JwFEw59W98Zz73+Kk/qfjOMZ3pNCQ1+yCydrs5BCHCnE8ZE+Lv/8dO1DzNLHogFdGagV05XjFRa0UsWLSAkLEKaI+YLLkcI7BFVt+3G+7UyK179E2B9OKFse47W1wMCDgQv+iZ+8uAq3LDgJNaQDQ9NrgEOuBE6/w44z0lQrm0V2+c+lwPYlAIBrAEyMH4CvpX5op2M4gqVMKGU/dsjVx4ZFwF1rcKL2Cebrw40P+NbFQKIWGHkSoGnYXj0EGqGoRwsakARp2gpt3pOYWfU73JL6KrCtu0Hn/H8Ao08GZq5xlBkAdqMOn9Ax2E3q0B2t7jJKhqxJipTdkbmPG6W4If4YXshMxzw60pyGWqRXIp/cLyTr8VuhQkcT+i17FF+PrcRfM6fkb5++4XeABqR2vG/cuGom0Gc8Hpm5Bjf/ZxHOmzYIt59j7crwoj4dALAzVYcDtFW4/pQxmPvBKzii7Z18mgV0OCYRYzcxDrw4v+zYvHEZFtFhOIlR/FYk0YvswYPJ3+Xvzej4FoAvAE1b8cfkPXg/MwFfSd2Yfy7m38qWKQT2AE+IlLAAUdx42GM3ladTZZVyCupJOc3RIpA9oHoN1koIoEHHGLIeGHgKUNsDaW0jMtDwrfhzRqK+Ey0oFZBBDHFk8taEKnQgtmMZMOnLwFHfxY4HzkRnXa3SwG51tYdjWRaT0XXg0S/ioeQqvJKZCuDzwLbFQO+xeUuhTozI3HWkBQ20K/DOnYjNegj9CHBP4l7gmacMWr3HgRcUGjQfhmm/rUMi3Dq1b8fF8edxRuwjHNl+DzePS2Ivm3ehSoYnhVNyFYvJYmc51DHtkx/gpeR8/Dx9EbQFWzB81i24OQHsobXAxv6m9L33zAOGHgX0Gc8t0yd0DD7JjMF1h5yM3y+ZhuvXfAlHTx6Jx+c1oj6RxvzYJUbCbkMt5GR76V+kLsJ7+kRUIYVttBv+lPwdakm78XCbMZnsTATGMGk+YZx0ONPLRqSEhQR2PmGlTsppvXTmp9gvypWOPFOYQau8HGQvITiVmRurIWQrakhH3jmWUuBDfTyOji3IJjjCgl8BGWioRgfIkufw1dgb6E12gVAdGHsG0Hsc1iaGIZnekZWF/72ZfMLAbu2wjxNm5mcr63PfBnYas+9OaDMcerctBsacnk+S1qoAAG9UfR/Htt8FbJ4PfeAhuG9Nfwwnm3BGzy7A8OOALgPNslnJW3JXR7nl0Q/ICo4hRbnLitK1aTkAYDPtbpfE4iKLtj24NfF/aKBdAHJFQT7ONqreJ0wu/7L0sx7CYR8/hW/EhuKhzOmgDAEGhpCtGLTheUADpmtLQLZsBgDolOCu5APAnx8wpe/WvAqYwGqXspEbRjVuoL3REa8H0IgUqnBU+++QRBqva7F8Wt6Wug3d8GTmOABAL+wGAHwh9j7wn29D22ZsVlpPe5XJI/IOLAiDa4wXREpYgCi2JthaJ0paolUoC6E4YUw+YfZhM/xs+LLGbRaRi3mVO68je3A0TNvEb01fjNdiP8A2rRd6uzi2pqHh3NjbSDz1Mn6WyPKJV4MMnAYASJEkOtmcMSkN0uqOYsiCe4DFO3Ds5h3YFavDvZkvFtoWpcCyF4Be4zBnawpHxhZh99vfNyJyF+3wWtLlaNyTPgvXxp/Fo4lfA1v2gh54Ce5acSwA4IyLzhCW0HAi9sES5rtPGAPDpS8Ab/wCN+/chuurMug+12hXy/UBTHTzP3UdeOc3AIBdtDP6CEvt3Jf9iBPmuZrevA29W7bjxsQHeFk/GOtooTTsqmRcbswAcG38WWAOsKfPdHxt3WkYSrbgt189DiAET72/AOtWLMaR4wfi4MMuK5edV/hsoRT7ba6n9rUn0ob3oBYr9P4YRTYAKxqBuDGh2k07W2fQ06hDC+JI46/JO/D91Dexgg4sFpcZduKqjjcpG1GcsJAgYzNZLx2Y0hkqpdFIO5jW1hDGSJ/Y/FYIZwdaZyHGa2uRphrQa2z+3go6ECe234Ef1t/pyluHhs6kDbSqHge1PYDJbQ9i59XL8laeFEnmjzdS5VdnpfDb1qMDwVFko6GEzX0MI7a+hO8n/oXe2FVIsGeD4VQ/7euYqxs+SF2XPWk42A85PJ8sFavBb9NfxvWpK9CEaqDHKNBxn3d9D+vdnObrwCxhYZidz/sH0LgRy2oPxtuZyVjf19iR2oja8rROAr//e+ADY/lyAR3mKTBv0JYwbnrFF00NQMt27Oo6CQAQY2xXY7V1oNDwTOYIbKNdQev6YfuwszCHjsbT+tHAmFOB0adgXvdTcXfmbCwa+jWg2xBvcpNCqGKVZd6BBE7s+A0Oan8Q+N4S6Nd8ih20DhfFX8dgsjWfLtG0Ebj7AEz6y3AsqL4cv048hCnaKnwl9rrDS6iTO0yIlLAAwRYx33yd1vWy56EY8BVA+uDBu1W/5Lq6bRsuj72AeXQEkKg20mQTraADsT3WC27IHzHSdxJ2oh6N6AwkavLPUySBalKwhKkYQCms2wwPL0qBcWStcXHVTDx76L8AAMfF5hr31n4A3DPF+N1nAm5NX4IRbY9i6RWrgZsasju8snyzf/+VORanddwOXPUeqMWyrgioQ0wjK/RoXIy7EveDBKC4WcFT/W9bDAw7Ck/0/yF+lL4CH036GdppHKPIRtssxQ4BNa1bgLfvAGb/FehuWHgbqYUCFwKw+nsKuTbsXAU8+iXg94bytbfeKIsztA8xAA2YRFZhNFmPqs2zcEP8MVwTexo/j/8F/0zeii9o7+EobQGa6obiO6mrcUj7H0G/swTbRp1n8Q78otnBPLclZfdU8QWQ39n9rdh/8vdqNs0Edq1BU99DAQCnxGYDAKqQwjiyFjFkAov1FvRu32g5MkiYdkfaL/0Vo9QnTBReBndV0d7tGUqk5ULO6dlBS+5ADenAv9LHYKpVXgY5Z+rjcaS2ELVjPwcsy+UrZOxAkSVM0dhgNdiJfJzGaeugawloPUdhT10N9tBOmEhWI7ZjOfDaLcb2+lN/DQw+DMCLyCAGoiVKQneIw2rwLL2jU8JssQCAM2d9FbFYCr9IfcV0NIy7LGao/py41laq1VAeJp4DbMvlIagiaZwQ+xQ1qTa0ojqf3ErecWv+Dqx5BKjuApx2J3Y+/V1o0M0feM5mY3cyiAgtSxqSBwtCYMTqWvk60Hcy0LkPNvQ6Hf3W/hffT/wLo7SN+EL2AO32dyfhyvgCU/7pScNHamP9aUBDgaaYNZ7v3cp3QDvnl1FyBMCXO27GB1XXoFPOWR9AcsdSIJbE5kNvwqhnz8zfvzD+Bi6Mv5HdXTmyhJaLvBUUQsUJkSUsBCDE4dgiF58w47FInDD3huocJ6zyzG8sztLFxVL6jt0al+ITfRSeyDqlltJk6frfSl2Hye0PAdO/afk8RapQgw6IfsZZBiAqTN2McWQdWruMBGIJEI2gHUmcGvsYtX8+DFj/ETDyBOCQb0hTukSgW+2OpBTIpIFMue9djBr33A5F9gvCUjQsA6gO9B5nascPpg3/uuqsol/GjxTafde9y4F+U4AfrQMOOM+yLOtf+wFw22DMr7ocP4r/w1Ws7U3WfL1CqU/YtqVAXT/gm+8CF/0bDX2OxMT2h7FcH4A6FGLQJXZ+Zso2Xx+W/72ni/vOXpknnRJStHkm+9K2ljDJUwYdGnbQepyufYSFVV/HiqqL0G3uA0CvschU90CGGpLcnPoqbkx9HQDQlTSD6jo6oU2qLCwI2icssoQFiFzjT2iawwyROFwFA5aZsMzdMLLf2XEWasOtGu3o3LwO72S+JF2iYmo7Yz1RQzrQHXtBsv/5AREuY7V1aO52bN7DSAdBX9IIGkuCXPS0ackxz8fSj0vsHVlo6UQDIUV9K90BPHAEsD37wUzWAaRcSeQ/G8/MV/UcxcROzxQUSqIBegrYZAT/RZ8JwIKmfJ51tDcAYKy2HjP1wuaInLxDVz6Gs7csxJXJOei3Yx1wwAWFNNlNDoV3pahZ/l+g2xA0b96AA8hKee8kTMTypxC6oxHHbfkbsOM/wKBDTM/akUQaGo7PLb8D0DIFy8+Nqa/jscyJeC55IyZrq7F5wCnAnMKuZ0drvK1/JrvspuOn8p75LnkkDjW3pL6K42OfIoE02pDEBUeMRo9JpyKFATiq/W6kEEMDuqEeTfhl4i+IQceZa2/DTdX/w0H0n0zy2D5mfI/vnzwav3nlM/eEihEpYQEiN/AlYgRpm2itlh8a2wt2sGRzmiFVoCGMDcWWsKLbo8kGEFAspYNNyU3lwDGK2aXcEjdiBg0pcmrlAYsIBN7rrzsa0ZfswpquY/I0cwtVes+xiA07yhsDSdBBUF98xMv2z4x//Q8ERp0MtDWa0mc+fggxmvJlRyUT3Opz7xbg/sONHadW6D4cFPPzl5ns4sc/kr/El9t/go/pOPTEHhzfNgtdtGpMnHebkTCnl444IZ+39MzAvtgJrW0XcNCNWPPfv6CeWB+l4wdUjEdnx97BCVseB6rqgWlfK3v+kT4Ow8kWVGV9ODPV3fFw02E4UluId3XDf+w7qW9hAlmLkzoPBmBTR1nIfoeCBdRfnzAAmEXHYla6sHnptGnHoEevzsCGPdiEwuHgerahadAxZfv/AACHYCG+k/wres6N40vJFO5JfxH/1Q+HbMRjBu+gDRuREhYCxGNamcN9DmUNxOIrK3SAtyefsKJZlk0TFopxVZJFlRVIhO5YzdhmvpQOkiMDsf7dqHUFAHQhTb6ayXl5TdMMh7amXgfmCWyh3dGf7IQ+6FDEbPJJdRV0m6AAaEMV6kkr/pf8MWakrga2ZRWFs+63DP760Z5uOHzpr1BD2j2u2apV4vJteOHThgI2+XxjRx2lQDxpWMZ6jwOK4kERFJQwAIbyQIGfJf6KM5o/BpJmHm8e8mccN/nc/HVuk0OujMdp5nAtE7S1hc0aXt7JA9wo8LTzMdoGNMZ7oP6GVZbPb0lfilvSl+av3/r2sfjVb94ypVlJB2AlHYCTS8c2B0FkrAhYLke6+YQpHHDsKOfa41htff7ePfQ2tJAqbOl0GIa1vo3DtEWWSpiXVZiJZBXOnPt7HJ5swPstNwNwDrKtEpESFiBMljBbnzA3Z0p5SzkR7DvwWLIe6VhNfjknB5MhTAL/Zs2Ir9Mla71RUU9Gm2IPU2GFQ7SlaKcJNPc0jlEhAC7ouAkDSQOeO+GrSMgR1TMejp2HPe0Ul8dfxEHacmBrjbFhoMdIy/QZYmgibye/g5M77sBKah9Tyw849u8dK4GXbwBqewNffMC2AosnacPIlvzvn8X/hvf0SThOm2eZb3fdKNN1acy1fPyrPuPx98yJOCy2GL3JbuPeB38A1s7E0Tt24L6EjutS30aqwj43o8l6bKsZzrE9wx6svpoywXo0WZA23w4k0EyrcE6scLxSEmn8JXMqGkf/BF+deYqSqfgXY+9j4K6PMVAD0g3/BvAlBVzYUFm9Yh9FXNNslyNZDqlW1YnsLGzFsyyv8HMnC4vF0OyYX/g9lqxDY91I0GZJO/tsRsRMtksmSEYKH1WYpK3GIjoENGYEZyTE8JNZSQeAxOzsYP6CEGAPqcPD6dNxefxFYylt62Kg5xggZq0mrulzIt6avwI/SfwdZ2gf4Z4M2+BcWp3Kl+sJgBXZGEun38mkQRMCPJw+HcPJZhyhLcRwbQuGa4ZStjg+DlUdu1E9ZCqeo0dhyNqn0FHdw5Rfh4Yx2jrEP/4jLostxZmxD5GpH4RYdRdsoMYSkwbd8Lt77RagU0/U6nGcEduA29K7sIE6h3DxMuFgdy5nY5JIN2EsWYePao6AtbpuQZnHZ0vgXXnzlJ1qwc9SOTKI4fj2u9CL7MbnpwzEP+c2oK46gUXp7vgGgDRiuCD+JqpICsdqc/FQ+nT8MXMWAM5VmMe+DKybCVAdy6raUUVS2FI/GRc3XITT+o/CQepe0RXR7sgAkesk8RixX44sW6Jzfs6K0uOQRCHXMV/e0qYLI1tUIwW8fivw3LX4feJenKW9B4BirLYOu+tHl6Uv/tiKiln83sU+EkSAJmv68tMAOBhRinFkHRbrQ4oGeLb8LAFW2cGwHomSj9HWRaZo/aVIxTvj4czpaKVJdCL+79QqhWPZrHkX6DIYmHCWI43iqt6OLrgy9V1MbP8Lzmj/Zf7+wzVfxwkdd2H+Ib/B4trp+FbqujI66/XemKKtQtXrP8FPEo8ZgYt7GWXZCkMZ74xWYOdKQE8DJ92KxaOsdwGrgvuSGxudQQ3vIEkyWNZFTrw6thNKOBJz8HNbjvTDv9epXraiOxbS4djSaQxW0gFYSwYghTgIAX6euhgA8KXYe+hOmnBw1g2CC83bgeUvG759B16U9+FrqJ+A5XQgGpNezoDwjkgJCxA5x8lkzN4SZoXynVgCISoY0thawhyeccvRsh1rqi/E0TZLIjLBIvNRZB7w7l3A0udxqjYLX4u/hF7Yje6kCY115UqYbOjZnXo8sa14Ye9zwjj4716HetKCJXSI5eAaljg9uXaaU2yHk03A3k2uh6wDQCuS6IR213R5Xj6/MgGAzXOBgVYR6+zymIXUi4b/VfHhrum/kvoxJrY9hJbvrcZ63bBqpXsah0znlun/kLwX+KMRkLPY5+6uxP04TvvURT5xSFUkti7GkYt+inV6L2yotVfYS8HT7mVOUm2RLRS3YK15+hLasGxr5kv6IXkrKwD0II2IIePIy3S7ZSewYZbx+6z7gNNuzz/aVuceNsQPREpYCBB38glz6TpBf+5sP+iMgiXXvQcAuDj2arnVT9HLOZEdg7VGiuvm44PsFv6c0+geC0uYbIFosRImsmThh4/g1oUAYFjC8rNsZk48YjlTYjOE5V3Jr4w/D8RrgMnl0cpL0YJq1HAoYWU8OZWCXtiNH8SfAF76MfDSDbg+/gSGk022pVWPJmD3OiOAqCA20+7YSrvincwkdJDCsrIddGhoQieQqi4YpBmRR9O9jT7Shio8kD4Tn+ijgIGHAGPPBHqNRUOPaXg1MxUHkJX4Yuw9YVntYHW+qxOYWt+8xxGjKdyZPs84YksCuJzqpfAjZY75QcKL0pmmBfeGydpq/CL+FzYCnz4G3DEM+Mf5hgR9jB2rLdRo69vqg3PGL0bkExYg8suRmoZ0Js2Ux6pDCU0CWYK1OoWocOOqp9Fzwyv4krYAz+hHgtro+/GthgVsA+2lXKO0k/mrsZdxlDYfe9EJPUgL0H0YkDSiXw0jm/Fo8tcAgMb60QDW2tKUIX7xcqRBU02heDIcbFkInRLTTlEvUirbBZtt4+3Fw9zUS4HOva0zFKGNJlFD1AQWtcJF8dfwrfhzwEcaQGK4Op5CVzQDuMQy/Xiy2vhhEYutFHaW8t2ow/T2+wAQjKfu6Ytxd/qLOFWbhe4Dp+fv/Tp9IQBgzeWFA9dbOg3E1anv4fXk98Abe40HrEpvT7oTFCk0oIt9oq2LsL1uLP7bcDgulSKdhVuJZZO3fgnR3e/sjvnhCMdi9543pb+OA8hKrKV9cG/yD+hJGq0TlmJl1mfypJ8b1u9aw8fx3I6b0YvsxqGdhgJYGriSGlnCQoBEjGDTHjH/E+FAl0K58kytfxchseIVTHrvavw2+QDG53ZRlWLvFnSafR8AoAk1nq1qrCgts2viz+AobSG+GHsfR5NP8z5Dr+lTsZYW/AVSVV3lCpKXp/C7eDlSRDlhihNmY0FiLuct87Ga9kUrqgs+Ye5Nglk+VliRsuoPjeiMSzuuxw2py4ATb2Gi3YpkuSVs1xpMmv9LXB9/ouxQ8NK64v2wTSCrsVwfANy8E/hJAzbSHogjY9u/84FR+x/IzsSSVJmXqUt6ow5/lz4Xp3TcAXRyV2gBY3LxudiHmFN1Bb4Ve9aWrlfkaWRS0DZ8jClkBRIwJriXx57HC+lvYFb1t0y78cqwdRF2dua3estq2zKCtQKFNljI5raqok4b8bIR4T19Eu7LnIX/6YdhsT4E07XF6IZGkI5mYO0HOIh8lq9jAIhnWoHPXgHWzwLGnAEccS0w4vj880V0GN7SOfqNYkSWsACR0/xzQeMsUTqDcqDDAy5HUQGesS1z87+LO4gJuajegNJZcg5WMvfAHvQkjXgnMwlHx7LnvmWXWB7PnIDHMydgMlmJ7mQvPm9JtPBTxgYCPRthS3WwUE/HTm1diCXZoLWFI1FCsOZRAoLChyg36N6WqHbIUUArqgpKGKXGMUz/OB/DW3fh6jjw78wx+YOKvaAr9uKS2Ks4RFuK1/WDMCpboEZ0+nK/wETzFvwzeSuqSBroPhzo1N2VB0tNU5vfsvDb9Dk4MTYHZ8fexVRtOcC4+bcfdqCadGA17QuuqePrP0PNB3/As1XAj1KX44nMcbgk9gpaEt3QKbULPWBjTWlqAJq2Yme/UdbPheGg7GZh1yVFfX7PnToICzcuwoBuNcY9u+ILhyGMCY3ohHrSip8n/ooeLzwBrHsLT1cBL2em4crUdwEAY5Y/CCx/0Mgw/UpXmkGPXZElLFBkjy2K8TWC4tRBmFKJze9ixLYVDrI9LfYRjtXm4iDyGaaRpSDQgY2fAMtezKcxH4ciDuPsMedRpZjLBG0NAGAxHVK4WbJ7bj4dgbf0Kb74qJl2RxL++mVJbjnosPJpawR2rcESPVdexPTHnbfFPcFyZfUJE0ErrSosR85/EvjLKUDrLqwf9PksH3MbEw1R8fX4i/hu4t9YSQfgsXRRdHpKoBG97H36Lrgf07WlmEJWAAPYnfINmRnSuBvCbNM74UV9Or6Xugpz9eGWyqUVxwFowMzqa/Bm1ffw58Rd+LDqahxMljrLk6OxfhZownApqEczBpNtGKw1IH608aHuS3YCm+djLFlnlifr77izbgzX+8mE7S5xHhqE4JLDhmDVr05H99oqpvwq31XIqm9x76qOGUhTDZ3RhqqtnwL9pqCBdjHqM4tuuxcCvcYB184FDrtaXGifEFnCQoCEgyWMpemKzJRYQlQ4UXXjGNu2AHt6HIiO7atwZfx5XInn889+nLoM+PPDAIB091HI7FjtzRL25q8w+b3fY021Ybn4X+ZQfDt1rbvMC5/GI0ljt8yTmWPRFU1IxjV8afixzKxlB2v1Y3ck4GHyu3URgBKlFcFvELGClV+MG3LJW1GFPthlXKz/yPj7tZewdcECDFr/HLqgGd+LP4nfp89GxvZ8AHdMJGuwRB+ML3bcarpvefA4gJpdRVv0WZUwhjIwjSEKLSMttBpjtPWII420y+dnslaIVH9SbA4AYKS2EbMyY8vSUl3H2do7GLTgY2BjDbD+Q6QPuAiJeX/HUdoCjCSbjHQDDkYHjeFr8ZeBP72Ml6qAn6e+goczWT+2hU8BAHbUjgSwpYyPKFh8wmwtYcI8CduKhyB92WD5ju1CPRbSoTg2Ng9oB3DgRViw4e+YpK3GbfE/Yyfq0KVxGTDuVMO3lwGRT9h+jPxyJEtE1ixKrUWi7ceTI7WL/09P7IHWtBXbBp+G61LfKnt+qLbY+HH2w9hz3tPZ41DsZXKVdf6TpsNzR5KNjsnzMuccNwGsov3xo/QVuFW7Cqi2jpHtulNV2KJTyFgaJ8wLLa58YBuwscWwcC7RzcuRZhkc+Egc8azqQxb5ViQxVluPyWQlsHkeMORIYMhh+ee/SjyMa+LP4ghtYVYWM1g/bBO0NWUKLVCITl/6PtW7lhcuivxcWMBS9mYrO196FrykH4z+ZCcGkoZyWjliezaifvHjuCH+uCWNGDJZi3cBw+h63JV8AEPm/x54yzj/MjP0WCzTB+Ko2EJ8Of42ttN66H0PwJc6foYrOr4DnPcYdErQJXfmZXsT8OnfgSkXoT3ZLft+PGOzwzOBPOVpOWQR4KVSFxHqlzaZttFuhYuB09CGJHqRPbgg/iaujj+H6vYd+aO0HMkLiKQCkRIWIArBWr1Vg7KZjKMpzP7hFG0FAKCp69iik+YKmKp9BmgJYPwXQGv7ZJUwActPexOwaw2wazUa+xcOjB5AtmMcWYu+2GEccNy8HWjZiepME2rQZihsmRSwbQk/zxJ48q2yoldiCVMyUAhYiPLYugCo6Y4t6J4jZfz1oP3w5uSRnfc1c/X5XMY4q+742KeGEjYgF1PbkDZ3buJOWsfJoYCe2IM+ZDcW6+VKmJ1PWO4syB/q3wZ6jWHiw7tJwC29F0V3d7a8HH0e/3cd+r79AwzWyhW12xIPY2X1xVhc/XWcrM3K3x9Jjd2i8059GripAfjhGmTGfxGndNyB4W1/x4S2h3FE+z1AvAoL6XC8oh8MjDsTGqE4U/vQILJtMQAKjD2jjK9sWPr25hzpBZe2TfQtl+qtK07mGOZlHGCV4urUDJzQfie2fH0W0P9A3JL6ankihliAYUG0HBkCOPmEyYoALSsfYO7M5R2b4hvx56F37ovGngdhI92ef7JQH4pRZAMGkB3AoCOzR8e0F/YBFpNa+BSmrXgPF8UonsfpZTKMJeuMGDAZw2+nrftY1G96FwBQR1rxYtUNRsK7Cnn+AADVAJ7P/gOwlXbFNR3XFL2Pw3u7ziRFrVAF5EJ5xIgu/6QAJxlYWW1ZaAxwu0g2X/ZvMS2HcpD5RqrqAwDe1KegmVbhuvjTgA5gzGmW6f6avAPz9BH4Pb6P4rdj+a6N09YCKF/aBbJHBJH1iG1dCHTJzvxTLUi0NuDXqfPxv9hRuL0slzPYfLyK+zZfehbk7LtW7gcEMApu4xykOg9AosmwaB/bfhe6oBmTtNX4ReKv+fTDyBasydIZRdeincbR0n28cYB5PAm0p7M8NTSjxlam4doWrKi6CHg4q/T2nQQ0pLLvx/5ujody2zzz0xrj2l/CYhrKwk6cFOJYSQdArzdC5GxDNzTRanQuPuGCwRLmxscvREpYgCgc4M1uCStTVgAhUxjbgbJOccKscTBZhunaUrQddjv0WBXW0r4Y2vZ41gckhk5oRzU6MOeSc4poEZwZm4n78RUAxo4x/PtKTAEwJQG8QY9DHE2oJa1op0lsRg8crC01FLDDvg10G4pNfc7CjDm9sUwfhCO1hUhDQx1pxe1fHA/oGUDP4J8fr8bKrXvwhcl9MKFvZwAUZ7/U23SmHe9HRboVkhBkaMEKIsUxP92BKnSgHYl8Ciu5XdtEJm1YCw6+HCgxInpS6jnz8szcxSf5BD9IXYlp2jJ87cSpwCAjCvzO7lPwWuZATNTWoC/ZhV6kESfGPsWz+nosxGAuDuNJVgmzsIQ1oxpTtJXA/x1X9sxKaXMCSxmYXMJc0ntRbnNKmJXPI0m3AS//CmjZjtbRX0Lis6exRB+ENdldqPMyI3FD/HHUEsP1oHtRvKiR+hqsoANANb6j47/Qfiv+U3Uz4kQ3Asx2GQR0GQiataypgtU4kyt3CcO7zUkW1vDj2CIWeJHjnI5b8LXYS+hNdmHQmAMxkmHXcFgQKWEhQFxjd8wv71uipjCxbKUyEAJjWe/Vm4FMCv+qehMAkJ58AbCx4KeVc8JtQTVaUG06QLkzaUNntOEZfA80qeM53VgKWtfzaAze/g4+oJcYFqwsLui4EePIOqCmG3DyLwwh1u/GzGyE+1x+ALh9WmFp4dVls/Dapm0YP3oKJhw4AACw4cXChgGPxSKM0jrtQAJVSJlk6YOd2I3OaEeSj/j6WcBfT8Wy6jQeTx+PH6cvt/YXYaG1cyWQbjPNMgvLkUW0HH3CWBixwey/ZAziJgXBI6/n9UPxvH4ovnZsoQ21duqPGanr89dHafPxaPLXeVZDyBZcEHsDY7d0wuB4Ghp0/CV9Ki6Nv4JXMtOwhA7Gl2NvYZE+FOO1tdhAe2IPOpfx/mbHdZiircRdX56C2iL/p5V7dLz3Yj06CRQkUww5D+lZkIH9xpOaje8DH94H9B6P3eMvQv1nT+O36XNNaZbTAZhCDIf9K+PP41l6GQBgJF2Dt+hk9OcUqDgOIM5/rOw5DzlHK7rdfR4/L4/t2XVVReHo5yVOGMvzpXQwfpi+AgBw98QpzIeui8omE8qUMELIIACPAOgDQ5l/kFJ6d0kaAuBuAKcDaAFwKaV0jiqZwoacpYk3RIUMsHBkjhM293Fg5RtA/6Kz6JO1AOOxL7P00ThY+wxDsRnQgLOJsaw4a+S1WLC1De3xeqRTKWxGD8yIP40eaMRYbZ0Rz4ujB7HMtILukACwF51Qh5b89VdjL+Nnif/DBtoTR7bf45jXJH+6HXj1J0AsifXpbhhKHHZ7UR2dNr6PU7WP8bI+zfqEg83zjL99JwJYZ+LnZ6wdXg8nrtSCs/E7W2/G15ODcWDWHxI7gWOzo2scGVwV/y/6YCfe0yfh14mH8G5mIvqSXUWhPszYgh54Se+B28ecDNQUJiy71+6EjpliQjqAx2/MSx9JZT85P4w/gUtTPzQ9q2rIhrW57BW07QaGtpU75l/a8UP0IbtwrDYPNyT+gZGZVUDTZPTEbizRB6E/pzwt2dndSr0fRhTdlx1Fvmx3JEfa/QVhidzvN1Q65qcBfI9SOh7AoQCuJoSML0lzGoBR2X9XALhfoTyhQyFYq5MvgTMNQkTN1QKZLBDTOwwFbODBwOWv4uKOH+GM9l8yDzIEwI9S3zA9H0i2A3X9sLt2BK5OXYdfat/ED9JX4rmMsTttvLYWE8jasnherHB+d87lSAkxKkoVmGZahVrSDkKAWPNW/CzxfwCy5cKDpf8D1s0EJp6NjeiJw2OL0Qu7slYjc6tJrH0Hg/57Hh5I/h4Hk2XW9NZ/BCQ7GzF4SmRnbU/WOxoFfeks/Jd441x5xUJ9KF7OTMNn2si8AvZOZhJe7XFxPs1hmhHWQwfJb1pJIY7hZJPr0qKMfsqjWPKG9eCV7319IjbT7piglS/3JRsWAD1GAlX2mx12ow7L6GDM0o1NCZ1pUz621xI6hHsy0IEELu34Ac7p+Knlc9UKkdkf1BpeHefdXsEP1Uekj/s1sfPT99YKypQwSunmnFWLUroXhhfJgJJkXwDwCDXwIYCuhBDvYagrDE7LkaWQ1TDZfMLs8hqzFg06zlpwtTEIHnABAOBdfTIW0WFcsuylncpvjj41PwLm+shu2hntNI5vxZ9DFUkBfUp1emewDDZ+hVew5QGSjRFlLNd0nfsn0/NDtcWIF59AkGoFOppRhY5yGTfOAUgMOPN32EONwJUvVN1gHP5cgviWwukF/cgOa+HWzgQGTgNizgZ01aXEtTuSO04YX4ZdqMeVqe/iutrb8KFuKKdL6GCktMKy8ZRsvKtp2mc4SfsEAHB8bC5ihFr6g7FApIydmq9VOdkenyPAO4d2JPFk5hj0sjj/r6phAdNZmGZZaF4JW6rz+eTl8JY+BbtgDksj+xQSljhh+Wc2JaxaKQnaAsdb5rblxPgiYbG7+RKighAyFMCBAD4qeTQAwPqi6w0oV9RACLmCEDKbEDK7oaF823KlItcI+CPmmy0AssMksOJM7UMMaPwU+Nw9wFTzNmGemDnFYSz+nTkaD6TPBI68rizdDnTBwe1/xFx9uHFjwDRx4Rllc4OMYK1lW9JBQLL/19LmeEhPJH+Bb8b+CwA4SZsN/GoA8Kv+WFZ9Ka4uPpNv1xpg5r3G2YKxBL6bugq3pS5AL9KICSi3QsS3LUSmqgsAoAfZWy7kztXAtkXAiBNMt7k3Diga6C0dkX3+qNRnl5CX6oPxSf1J2ETNzsHdyV70IbugU4KVej/M04fjY905zISMV+BdauRKL6YOlt3pir2I793IrITl4ukZStgiNKAbdqJeep2rnngVk1c9jNu9ih+fD9mTBpkIevVXuWM+IaQzgKcAXEcpZTz+3AxK6YMAHgSAadOmhUWB9Yyc8uS4tVlRExFt4IPIVoxZMxd9mttxSeLv6IjVInngxe4ZHVCshN2U+hraUIVvdhsKWCgLjeiMcztuQW/swvsKYsGIBFxkycsDHQRnxj6C/sCBQMt2bKQ9cFz7b3GQthyPJ36JE2KfYju6GIFEaQY49gbgrdvMATA/e8X4e7DhuNyMGjyvT8cN+Ad+p/0eiQ2/x8LkILydmYyX9EMQ27YQrX0PQee1r5r80fJY8Zrxd9yZNu9OLH+zQOUg6NdgQQD8PH0RTtU+xuv6gTgh2Q8/Tl2OvyXvAABc2fEdvKwfLJchdxbxyZ7pvseGnqGFgMQ5ZSp3fBirEpavV0qBLQuxgohZFGXCOTQLcbwueWh927Njvrf8YYNXS21YdoUqVcIIIQkYCthjlNKnLZJsBDCo6Hpg9t5+Ba6+UWbWFjvwh6VDlvkNIYVnkj9Fz8WNeVkaqkejF8dyqhV2og7/yRyOPbHuaEOVa/oU4tiIXq7pSsFiMeT9UKmwQuZ2jtGqejT3PxS3zB+EDiTwoT4eL+oH44zYxwUH8MGHAcf+CFvefAD5lkCzSzQ13fLLxACwgfbCLalLMD62AdPjazCdLsV0bSnO099CbPc2tI0+B8k1b+A7iadwb+Ys83E8m+YCnXoA3ayXmv0c31lLXGQC46U6Z+oT8jt0AWCOPhKPpU8ABfCuPkmIpgxLDFOIihw/EOUfpwYYFtc+2IXN6AHAOL4JANB3MhONnPI2RF8PNCzFZ0RugFX/lPdC/dpZIGXVh22w1pAszAUmRcDKqcrdkQTAwwCWUEp/a5PsOQDfJoQ8AWA6gD2U0s2qZAornIP8KeIp0PLOJ6+jJ2nEx5N/gebd23DcunvQUDfOUh1ynBWWvJQODTNS30bXRAJAypKaTIiWt8rgoMXon/XJaj90BhoGn4FX576df/bt1LW4JdWI96pmGH5xfY2PexoxnB9/C/h5tjYyHcZROyahCf6WORU1Wgy/rfonhqQMX6Uh2jYAQEePCXhFPxhnxj5ELVrRWBw6YfNcoN+UskLI74702SHejmdQTrZWfBvRGTemL1PDTyQPS6biMuVMz4rN2WXamdXX4F/po/Hz9MW4Ov4fpOsHIc4Y36k5u6vxiva/AQCWxoaLiuMIHnqO5WX3jIOB13dz3+Slru8IhagQfF5pFj+VlrAjAFwMYAEhZG723o8BI6IhpfQBAC/ACE+xAkaIiq8plCe04IrKbHEt24k0h2Ky1WjHT2J/AwBs7Hsc1lS145kVOqYM+wL43OODA5NjvuOz8qcqZm93ps/DCdocTBp2HEimlJ+GBnTFRR03YKi2BXce/QMAwC2pr+IIbSG+Nn0YAGJUsE2Ud0IK8ZqK0d5rPGbq43Fm7EPUoAN534GOFiMW3OhTy2nldkd6+URwZmW1PooMxrLqU5aFVI5PGEOaInnd0nuVqdjCem78HbylT0E9acGe0ZdnbWTuWEX74Uvtt2BsV4pffXkaXv97K1C8YcUjZFsDy8Ztq0K0Ddaqti2FZVnObznCYgFUpoRRSt+DS3+lRs+/WpUMYYddhGQ3BKHpjyHG/okH0p9D92QXdMSa8Zx+OMYkrWeuIr5VTAOVBDgqWh6skqLylub7S+Y0/CVzGuZ36gbstY61NouOxazMWNzZ2bB8vaZPxWv6VHztNLZlmdc6n4mNu1txZdwIVqvXdEemtl8+eOXMqmtwfepKAGcYh3bTTNH5ifay291jecYL0waVnEWu6Dml/m1a8btLSj0I3YMVU0zRNWc6PmaEhmyc/j1mJQwgmENHY2+iMzDsaOjkNQBp+dYcxZYqaZY2pvzOBFS2YZFJmuv72vqEyfF99AvRAd4Bwu7A1mK4tkMiptGzDFbF36/cgcWPZY4ve+YFfi4fKfkey54xW/j8qcC2WD/clv5K/jrT5wCAELynT8JVHTOQQhwTc7GcNmXDV/QvV8IKcioRUwheRJHVRmQ1C7/ihBUncT22yKNMu0oOPT879p7xI8Z5GoQJ5UJ7kVN+sFaG8dYmrTyfsODhXAz+WqbCYgGMji0KAXg0cVkfO14yx2ufYjvtgg20lymvzOMobA+5lT25FbTUsCjEIrCPC6Qen2//OQ7WlmLGqd/P33tRn47b8WckkTai7m+cDXTuC9SXh/CzskBxH2QsOBiaLTgEADXfIz46WPv8hRPrd+7tjHeXq8hrL6JDcXDbfRilbcQYsh7DyBY8mTlGKFJ3af3KN4SpNYVxuaJ4NoXJk4WbtcT2mn9uu3uXlw9fetmIlLAAkV+OFHHozD8W283E4xNWj2acoM3Bw/Rz+eNswrKezoPKk1gNiqt+Ph2B+ZkRuLbLINPSZwfi+Er8deAXvY0bY063oZX1CfNxIFM5g5XVrlXvaJMOrij53mVqQDc06N3wAeSEmZHdJgLxCbNJu78gLJYpvxEpYQGioISFu9tN0lYhRig+0MsHTJHZSJCWHyf+AL8yIeOj7WQZVFUmpQNeqQzfS12Fg7TPMOOE0QAIMNba16yQj01Sy1TCFsTyi1L/pv11YDfDuRDKj3/iCe4arrFLtjTSVh4Y/Jfs/BelLW3b3PfDbzJcrcSMoGWLlLAQwNkQRsquS5cDheKEsSTKds4DiBHKYCHNbgP3aZ1HdufwGidMNERFyL5TTHhbPwBv6wdgxnFsjv6e3lHx0UJctBlJE2JOW1r3qn3CZDuBW8nrd7P15sMVPLjcSgTGGa/1EaRjPgu4jy3yWE5BnTRTisgxP0AUHDH9580zgz1AW4lVel9z3Kg8HRv6Tl3B1vLDlVwYMnfvyejHdixVNQsjwK9ZcAJBv42Sv+68nZ9fecxwdt4lVi/jr9mnKSwDbZBQeoC3G292UlIQ4OZIFzruEzS7slLtmO9LHSn2l/OCoCfJkRIWAnh2zBfxCXNLkG7HzXtuxt8Tv8QpsdmYR0eY8u6rnzYvc0UVnTnoAcINMuX77kmjccNp41zThUGvcvPxUa38iSwDOk1yrOT1f7OBOEPZ5S1Cr1j835zrfPyS0ETQq1++q2N+sIMNr4XbtpgYXyMM4wgQLUcGisLZkfZplMXKKvKhsWyMaz/A1NQnyMVVfDczyVJlF+kIDoZ4+0wS4bj8y1ngUixhPu0KzdOFlU+YKDPCld/5fD1OzqadfOa/OXohGWcDBU8ZGEfHc6QPmylM8hgivuOZjU7xbbuxRF6wVmsh/DnAW2DS4Nv3IFjlM7KEBYhc29c8tAHxD3XWBdeuAy5/Nf/zQ30cntaPNvH0IyK47JkZi8jO8kgTJZTw8np+Fo3KbwZ7NH7nNw6nT5i9sm/tE+Zvg/fRrdCdnuTVBValzJm+t/pwt4R5Iu8Z/D5hfjtyqEGkhIUAjnGVytKWpxEL1urwsHk7MPfv+cuPdPslIpGPhP1uQL77ovASEqQUxWUvqjTaDyWl2zDkoSy+kiAdKwsUS3reZ5bpTb9J2b3IFGaAZ8LEO8Fya59+h7IJNE6YgBzFY0beR7h0k4fiIvSjjsTihMmXoxhhGRqi5cggIS1IpTgslyPf+jWQasWtdT+BtnMVnskc6Z6nAsAy2EjUz8ILiS+SI7XPxAlj3R1Zel3mFCZFHKk7bp020Vi9dyUEoM1BepwwgTxWy+OFG6WX8nz62PM7Ewh6fOMtc68+YaLpZSNSwgJETinQPLYC2aZzbJgFDDkcH++ZjoUZZ0dp+4+Ek3XPJg9nenE4yObBJ8yrNcnqvrIBwiJOmJizd84CxeoT5k6LnXlx3tIfxmOdj6I0hCmYMZ9PGGd6lypTbsUpYSB9pBD2CePvDyw+wirgj0+YP3l4EBZDQrQcGQI4L9GYH5bFDRMdJCzyxZBBDBlg2xKgz0TbRhq0I6MovPuEVeZ7+wIvVgzu9ArjhDGmK20KqlqGfZMTUJg5H/jd2r2MK2EIRWJaHnexfHkJByMK1/xB7470OU6YaHrZiJSwAJGPmO+VjkCe0kHhJ/FHsbL6Ynw19gqQaQf6TfEkk4hPmGg6N35e6PoBv5U7K27Cvi/KMziRIgWfNHlk929UeEGGdaIUBh/yIIuGp178qsOwWKsjJSwE8OKs7NUSlst+WfxFAMDFsVeMu8OPFSNc4eAO1iqdf2UNQGFWgsL6QeaBjDdwszAYS5CFRDKNSqo/c+H4jPLDql7tg7V6e0u3oOCVWobSEPA4ESlhAcJuN4wTCLHaPeNtd+RAsi3/e5i2FRh1MtC5l/1yJKnMjsu2HOnky+aSt0K++bZxyURo5XYlSnh5FUEWefsGu2O+8/KS6tUxVbvNzCcO8PPwAk+O+fLEMOiJ+NmafBRL3Uhs8ljSsfOz5ZfJzCvsjvm8wVr5/ZFN/ELyEYuUsABROMDbPo3rh194GamQ73BtUf73BtoT+OID7HQcnMrteVun46E1bUg3V9lY+LPwcoLKjqzyI+h2gDcvWLPL9Ce0+ug5+eVUImQot25NtHS3cyU55pdCumO+onyW5WYXrNVjGbo5/IfWMd+n/hv0MBEpYRWI3a0dpmuvfehQbQkaaBeMafsbjm7/PdCpuyvdoGcR/77qcO48XpfgXJeGPVEX4xk0eOOEOdISKEG3dqjM6b/U8drBEnbm5H6cUnCzl5bL/B7B+yoyQ3qICoHVBYFd11b37a1m3uoj9JZ8STEqWF8jJIawKERFkMh1dN7ZbipTHCBUjHdxvkO0pZirj0Q7kmLELOkLDEilSzwlf2VBRDZ7eA/WaiuLwo+gZYR0D+yYt+RbJBP1eSEWv80x9MIBL+1Cjk+Yc/mWxgmLgrU6E2Rya3CVw4KuTVl5LUH3/CEN1upTDw5a+YyUsACR68xOxxaVbXUmBIlYqU8YP+/ch+FQsggDyXZsJ/UW8lkTJoSEZmcJD6Jji8INdp+wIidyl3bI2zeEg7WW+mkq7h/qfMK88fCECvcJM8WtYwxJwbUD3Gt9uAZrDXaA4zaESSjTMCBajgwBeNuMyXnWI+/TYh8DAGbrY4TyCwX4tKUlj4cIf/d8zrNiFZNwFTv8DB8gWnZPlBZPfplvY+JJTH+yz8MxGnuRwo9X8GQBDZ1PmPqxgunkjdLJs8tzwN5H2HMZuoRCCu0B3qrbftA+NVlESliAKGwddrS/lF3JmGXnqHZGGwDgF+mLuPKGpP1ygUVkjqpQAj/UBpk+J1YO8aJg9uWg1r8t03L2FeFgrbI/nG78ReqKJU3Ri/hvCBPnKDtYq9ixRUW/WfPw0OcRxiq/C4Gg5yy8dcg7mbenE+yLR0pYCOBlR55XC8ZB2md4KXMwRLu4UC6PDpVe4SUuWymKhw3ZgxiBujIpHe48B2tltYRJLCQri3CQSoQKSNkdyfBtMy3xcnwL3aTze64mvQ86WKx48rlZxorpyl7azvse2z33o5JC2BnDYkeIlLAgQZ07h0oQAD2wB0PIVszRR1qmcYoT5gtkM1IcJ2x/hozZJLNPWPFvN0sY9/ZItgx2m0jyZDjZ8kLMJ8w6k50fmO8nOYTIJ0xEMxERn6uMPdbHPneAN+MGL3c6nIwlI1LCAgTLcqTbjMpL1zlQWwEAmKOPEqYhtuul+HeR5cKn3qCOjWR/FEWCWm6LJ6IfdvNf1/T8LFx5G7/Ll0WDHlxzCIscThBVYgL3uwvhsm8up9M1z5gvC0HWlMxJgyyExaUmUsJCAC9LYKLQCMFB2nKkaAwL6HAbPvacwnBgLi9EHGpdaSouB1XjkCyx8xHz5ZBjAk9gUf44Yey8ZaTzA27tvnQJV+rOTp8KQjYbrzES3SfP2fsmnjYhKjwHa3V5HpKFuaD6TNBzpEgJCwhvLN1m6wNQDCffAcDDR5oAR2nzsZAO544P5nV3ZtCzZ9ElxzActB02XtxOsII+Npa0LOj6FWy0jJ+TZUMFf8l5rOqAhYdUy6YMGj74hNmnZR8XHccZgTwsCNIxX3Z7dXzO7NYQDuUzUsICwown5mLj7lYAznHCSiGrn/RpX4NJ2hr8V7ePPO/0caxEX06mjz3nSKTSMV8lZNdf3y7Vkinaw3zYtNyQ+fKsBuEY4AH3dypVZivpAO8CH8m7Iz2OfcxnRxY9sOMpK1ir3eQzLFbboMQIetyOlLAA0dqRAeDcCKytL0VR2gV5D2ldCgB4T5/Ender8y5vkD3pncSpvMWySYO5bOU4vJfxsOQrxieXa0iPWsb0Et/HwvLgW5Rtm79+QWa/A6yVGKbgrlKr0zsxs5VeBj2xtK5WHA/+l8Jws4QptRzLba9Oz1k5hUX5jJSwECCIOCX921aglSaxBvbn2nn1iwgbFBjClAZrVQVCINMpTBpY+wGrT5jIR0t2BP4wwFXmkiVcqZYwn8pLvk+YXH9YeyWkcL+wUauMITc/q+x2EoSlSQflZxy0e0ykhAWIfEfn9Dsw+4SJNaD+7SuwjA6ELtAErHxxRPOb7vtkCRO1dvndV/0cHMRdC3mdwgQZuZDKl5VPRZbfjZn/6542aMjw3ytPI+/dKt8nzD5fKRmxYLveXs71XSrOJ8w6RVj6GysiJSwE8L/JUPRrW4kl+hDnJQqHmUlYnBplg7cDq/YJU7Y70kdeskFtL8wgEIiYLynuWJgsZq67I4t/E7l92y/rhmwu3n3C2PhY+4SZM3v3CcvFo4x8woLkZ4dICQsQhbPC2L9+xofFfM2LvtiJ2kwjltDBArlLZnxC+a0JsB566xWOMXqc8rm8bdDHX3iBaFmrsK6I0PLbN4vHJ0yFTGIWaPtM1rsj/W3PYsvH3mk4gW9stvdHYw1Z4SwLfx6e/CprW0WcMK8+YWFBpIQFiMKuFYc0FqOj19nlOG0dAGCJPsQTHdkIg3UtdHHCFFENoUuYdMje6Qewz56Db8kFuO6OLI0TVoG7I32FxJeysnmVjkGy4oTZjW1hGHeDQFgsgJESFiByjUBzqAVXn1qBr+BEsho6CJa6WMLsect13vULLAqTYwwxwd06lQKxHUzeX5q7KRWfcyg7NAFrwpwLWv5v+K1GThqz5Xv77QPphWFAynYxTHVS5hPmvTC9UnC15Ac9gMmaGAZonRdBpIQFCue1eivIaDAHa8uwtXo49qKTd2IiH27b+zaOlpK/Bo7UAu6Q+eWtfXQTgEwu5iOvzH9zz4OaK1TSiRJeQ86ECWFxCShzxLdbOrO4H443kAuhjQiKCyIsFsBICQsQbmZi23xFv3kbdwwZTNU+w5raA/gYsT8KLZgcapVLwQlFzv6yBqAgyqtYcjtnZmHaro75AuVWAQYGnqOguHj7FaLC12CtDBZ1AX8m+2CtHkNUOPA0JQgYQSlFQSvukRIWIEQHKC8D20iyEbWkHes7jQfgfYYi5pjPdt9+C7IAU8b8js+8seVCEMOCzLq0Ty++3OuU3tKiQNRZpAoO+cR0nYPqz4lYiAO+NL63QRHH/JL6DTJEhSkfc7rylHZ9RLVlKHSO+YI0mftGSJTPSAkLAZw+TOXfEOddN26YrK0CAGzMKmFO2OeCtSqQWco5nj5DpvN1ELNIVVYbg55LsFYBmkHPtN2EJqWJKtAx399grd7pW435dmQ986M5h3/rdhiWoTywA7wjn7D9F4X4Lf7hALISjbQGO6sHCtMwzZaFhLfz/bJJXWYh8wbnA7zDYQrLBwEN+gMuGXJ9wsp/l5aXqnG9NDis7B1t7vxF8rhnCtI/TAY72RKL+uuyhqTgCwbr7e1ELUuBwW1JV9FKid+IlLAAkd8d6dhqLEJUeOA5WVuFBfpwgMRc0zov5UjyKfKxw8gMsligWaBaSQqTZVwoofVIz6JwKyzmA7zLn3v6WLnJEhazAQdcg7USUmJdjIK1evWHLYsTxtBRcmUle2nbzScsLJtI/BYjHG8dKWGhgHPU+vK0op2mCh0YS9ZhPh1eRJCfjjmukLxdL6wfT88zQmGfMBe+EnWwsPlnyKKlLFirzxapEkOYbxa4PH9FecwBR/2FDH5B+oRZ7dZ1z+MfgrSE+esTJpePakRKWIBgCdZaitK0PI37ktgrSJIMPtLHMuUL2ies9EPnGR7jhAmS5FzS8ObzxwpZ1RfEAMZqtRGRzd0Qxl9yQS+PuAZrhd2O08qBbGuO17HPbZy2DMJtm9adnxPcTmYJS3X7vTsyLBbASAkLEHreYdI+jaxmcpi2CDcmHgcAvK9PYva/skzDmd4pP9P9UsWEn6WU/H4e+xH0h1sVZC7ZWllk/Y+vlhfABOUnKQgF1rV/Ri3GosqMVSdXaK4JspWTokR43hXu+lxdhauIE2b7OEDrvAgiJSxAyI5x5IRjtbkAgDn6SKQQZxrwHGPlhGMSwQWvPmFWj5Q7YCuhSSyVBLGB0v8RjNVqIyKbm/K0L7b7MsuLTN6+xQmTTM9BcFXWULu4kZ7jhFVIm/bdJywk7x0pYQEioxutQOP2CSu+ZvvQ9CW7AADndvzUMR2T30jxbJmJu31+buZO+ZkFEMzmGoBRopUncE8FNnD7Xyh6rcLStb/lZhcnTD1fkTz2uay+R/6XJT9K5Q7WJ6z4t3z/Vq/14WpZCp1PmNh4y1tOQY+1ypQwQshfCCHbCCELbZ4fSwjZQwiZm/13sypZworCcqT6RjCRrMarmYOQQSzL0z2PY6wcH9bvZZdKWGY+EbxBaZyw/bKN8DuUhw3S44R5pOcWeNrRgqtMKQh35frd9cLS1VVawv4G4FSXNO9SSqdk/92qUJZQIm9+5shjnIdXHBbBHdfF/40R2mZ8oE8w0ZEBmTMcVgOZ5xmhxMFImTKas+wo+CrKiFnkJY8sWFlkVcsjFqw1WPD6p3nxZ5O9lMbNXzq9cooyHPOtYEvWq2O+iwx+1JDUFRPJCHrioUwJo5S+A2CnKvr7Apgc86120XD0mip04Lr40wCApzJHlz0vHywKd+z4lC6JeoGf7d/rmW9+OOYH/cHmhQyFlvdDbUrv0BBFBlfeZu13aIwCY4EsDMq3SJgFEy3+LJ74qYKQsl28UcTLy0icJDGQDQ14+4ztpJ3xRcNi9Q7aJ+wwQsg8QsiLhJAJdokIIVcQQmYTQmY3NDT4KZ9SsChhbnDLO4psAAB8q+NaNKKWOR8zf4m7XmwHrnJTmCfIHOyVxaFiSHPlMcPdE9lAmhId4E4ks8LgzydG6PzugL9+diIXyyVribe0Hvz+0MluB1bkRDb4lJKxcsK3s0B6DtZq4/DvxlcmhHbzKpAjjAhSCZsDYAil9AAAfwDwrF1CSumDlNJplNJpvXr18ks+5dBd4rcA3h1Px2trAQCL6RAzHZv0xfdtLWEgoVlP54HIMgLrM0DNx9aJ5HUnjPaNl0pwK/KMCoOQJUxwNl4erFVxiAqJeUwO5RZLvDxw2mTkhqAdpIshpGwL8BF1/BeBqKO7f+ArdFnBWoNGYEoYpbSRUtqU/f0CgAQhpGdQ8gQBu2Mq3PMVfrt1rPFkLZpoNdbSPqb70vqbxCULVj8K74ORPKj61CrdqQT/fXUKvOW9mFUZqf6QBFVuXmA/mSpOQ4t+i/MK+uQAXz7ATEGfS645fOXKrWYeQ1R4fC4DYfQJC0tfDkwJI4T0JdkRkxBySFaWHUHJEwSYLGEW7YS18dShBZfGX8nmMVe17fZeloZfaVMNDjjGCfPBJ4yHp+ggJTWURojbgh+WlfxB6z77hAnVIYtPmMksxs/CS5GHuS2xwDnQrc14y1Fgnn3CAhi/VML+G8b3JkFbAOOqCBNC/gHgWAA9CSEbAPwUQAIAKKUPADgHwFWEkDSAVgDn07CcI+ATdAZLWKnCVeZr4JD5ivj/AACbaI+yZ16bXSXWlBKZK7AcAJuyEPquex/A+B3zi37LDk3gIovYMlWwg7y9TxjJv5CpTD3wKl2OrMRxAnALzyOBvtXk2mZS7v3YIhff45DU0f4arJVZCSOE1AAYTCldxpKeUnqBy/N7AdzLyn9fRMESpob+dG0JdtNafLHDIvoHsebN8sHwPFnmnamU5WflI4e/vSSy6FpxqrR5KRvkOuaX0/W71PI+YX5bwoTysPRtYvlbJg+ZUH3WKlcwVQczooywMKpD81RasFZZvIIeaZmWIwkhnwMwF8BL2esphJDnFMq1X6DgE+bQDBxmTEZea5ytvYNDtGV4JnMkmlHjST5rsdy/MmFZc8+BaVcTN02+mG2u/AWUYhHIqpkgLPlm/yXJISpcCkYsdIFAJpmweam8AgkizbwYdJywYngpd69HtrnxtiKRK6synzB3dmywkSks43Q4pPAfrD5htwA4BMBuAKCUzgUwTIlE+xFYji0qBevOxBNjnyBDCe5Nn2VLx+aBuwyy4uGU0SoVxZsfhe0GAGFfKrF8qnhJn/ULWT7kpef35Sj/7bvCk7fAlSwhqd4dKWJZ4GxTlRAnrMxKLnnSIstmbjsW8dD06hPm+lxd55Hpwyg7W9CTJFYlLEUp3VNyb39VXKVBzzsBeCBimZdiqrYcz+mHYwe6WGdj4OlYwRVY+ywuh0E7aVYaAjnAmzVEhXJJKhvFiqus7qxF/QeA1TJpqZ+XXAvuvgC/XcLD4oLO6hO2iBByIYAYIWQUgGsBfKBOrP0DeZ8wh89FWTMhVjfNGEi2ozfZjTn6KNs0pqUID3DcVOAQcd+alo3lS9Dvw/6AV3lQHqxV0YAc1ACkym/Ot4O0GcstJOM7AD4Fi2F4cSdQzNvnGBXyfcLK7zFN5pjpF1LakfXumF/OSyZ9VfBLFw1a52W1hF0DYAKAdgCPA9gD4DpFMu03+Hi1caqTtxMuyjMfRJYDAObo9sE8WWJwOcUXCmm/9QzH5TIvmRXAL8dVZxm8g/cjwOq+JKLw8Sqn+cmMAys1QXy9Lx0XK65W7y0ithdLWJg2o8iepCjz8/IgQ9nzgIvfbz/VsHzDXC1hhJAYgOcppccBuFG9SPsfeK1Jbv4mB2rL0UyrsJQOcuApp8c5NXhuSxirhYstmXSfMCuoC9aq0j/DWm5Vfkam9PwspPGWAdb6DssAD/ApuUVRK4Tgd50EoTcwbfBxEax/F2Oz1HUnFlYrCt4pcv0L8w7/NjKF1hLmU2MK2gXFVQmjlGYIITohpIuFX1gEn1E2m7VoP1O1zzBfH4EMYvZ0GBQhu85vDNQh7bkO8Lqrya2zqpjJ+21h4UVYfMKsxBCRTE2r9rdd2OcpXbsr3LdWyr1b27jyhqA9y0LpWFD6bjXJGNb8+gw+ml43QXl8rhryPilBvwkfWH3CmgAsIIS8CqA5d5NSeq0SqfYjEOLcuawUIafG2gN7MFlbjfvSn3fmyyyhMxwtYba8+biXK55s+e2TyeukxcqonI+IX75NqhlYQ/WHVvmxRRVoCuOzpBBPlhceB3QZUB4nzOKeyGSOZcyzs1gp9wkLU2MtguoxMCx2BFYl7Onsvwg+w3o5soDShvpJ9VXGXwd/MCOjndO6u6OoXwd4yx4c2OjZd/0g5leefNQk8goTiuvR8SMv8EKiA7NTqJWwwMYQxpyeBd4O8A4PQvJ9NsFr+UQ+YXLSywaTEkYp/T9CSBJA7su+jFKaUifW/gMCvkZACGGaXX6qj3TlKwOOOzvtgkQWZSGm+4wWLqZU9rKp8gmT2pcVDgx+KdGWvB0Kn/eVrfqC+tmzvJ1xfsFPn7DSt/f75ADZllAreqoC9sqIWGRJNy+EM9+wQbVyFBYLIJMSRgg5FsD/AVgDoyoHEUK+Sil9R5lk+wkIcTZUlzYTp0GHQAcAdNAYdqGekb/9te1youeBOhh4lTnoGVMpgnYoDQqs9aiydEplcJpMKNkdKcFfy9TXLcpURGxPlrAQtecwjm8hKh4lkLV0zT2Zk8JVHKzLkXcBODl3biQhZDSAfwCYqkqwCPwYRrYAAG5MX+aaVlaHlhkI2fY+Kfyl1CGhR/7u+Vwc8yX25hwpVR8my5AEFTbKE1hMUkLwCoZc4fmKO7kVlN+TuztSeZgwziVWr/QBdX5udhYrecpJCDoHB5RbwkLSRVnjhCWKD+6mlH4GIKFGpP0LbsuRVh3QbglsIlkDAFigD2fga+cTVszbLq86k3wx7LZsi8Lr1vIwfOCLIaxMhuw9vEB6nDBBtcG0xK5YMRClaWf1NkKWWCnlInKJv22YmqVsJVpGn/PsE+ZKIEw1IA7ufh/2EBVZzCaEPATg79nrrwCYrUakCE5wai8TtdVopwmsoP090bFKK3PWYBvJ3kYm4Y+aLb3wDzaVIGPQsC6icJRbWGbZvPB+FqwkQULCX5ScJ2tiSNpw0FBdDmHpoqxK2FUAroZxXBEAvAvgj0ok2s9gDBp8ja24gxcPOhPJaiyhg5FmqFY2jvbNlMVEHpZGnoMKp2rZH1s/rCiATbBWRbxko7gepe+gdSHHZk0NV0m6yVw6yfJSoqUR8ytVGZXfr1k887luM8PN4T8sVRRUWwm6t7IqYXEAd1NKfwsgF0W/SplU+xHctH32dkkxUVuD5zKHsfH1YiUyLb2wN+HcMqZdjub2NFt+j7soVXU6mTM3pmoQZCe6nKwaMqLv2+28VQ1S8ttvXyjGXJwp/P08yVj+lG49CZFCrdpKGaJXBeBtfGNBWCYIrD5hrwOoKbquAfCafHH2Tzg1tgMGdjWnRalPmJF5MNmGetKCBdTdH8zgSfL03CDaWHnzrdnRIsYoQBS/4gXTBwcmBy/CMgBVGkTKLeiPm22omOI0DOlZUO6Yv+81NJbiKX1vpnFWVbDWHF0bKfb3sSDo/smqhFVTSptyF9nfndSItJ+BOHfQ7rVJJjKTyGoAwEJ9qFdx8pAdrNVqJscTcd/rTFC1Y/6UQV15xGGCszxi5RG25TJemBUG+3Qiryn6PSoN1qo+Ury8PMUfZ6cNBizwdIB3iNplGPUSr8XjagnzRt4zZCnsIWpGTGBVwpoJIQflLggh0wC0qhFp/4JQezE5hQFHagtwX/IedNAYltOBbHwlNVShxVTFg4lbOqmdVNHHVvU4Ims3XJCwVOoV82Q7c0HxEpkkmMpPUjOWbcXh5eeHY76IsuAlWKvnA7xds4dR5fQD4XhvVp+w6wD8ixCyKXvdD8B5SiTaD8E7AyxtOn9P3gYAeDpzFDq8Rg5hGJeJoNOL1/HR8/jKILPTBzOsH9P9DaxO5CL1JXxsUfFv4oNPmMC72fpImvzoiqxi3ByCt6bIgnTHfAkl43n8dPUJC7b2ZJU5/9nEwb63oyWMEHIwIaQvpXQWgLEA/gkgBeAlAKt9kC9CCUo7irbbqIb/ZQ7Fj9OXs9PJNjyVzp5OccY88WROZ51SZqdT9bHN1YsKSwJRRFcUokt3Vm1PfYDHfefYIrNPWNGOU0/BWs1v73czk1321sFaJTPJ0c3zLClDxYUYprGgGKqVwrC8t9ty5J8AdGR/HwbgxwDuA7ALwIMK5dqvwNPUStPGtswDANyf/jx05tVlJ/+QApwcelWZ5ItRdjSMxz5ZCcFag/yABz0jZAWrwiBWXzKCtRLfl+HY8thNSqzpyjgaqXIRvmCt3ok45/ez7lQqtxUWq9V1OTJGKd2Z/X0egAcppU8BeIoQMlepZPsJhJyHixprbOt8dNAYPmP0Bcvz5WdrQ8eekv1yJqe5uCQ9c4gKH3zCVH1scyKq+paHZRboBVbVGAZFslSCoAd52wlTseuBaYnXU6TREuaqNyiUOoVJpm9BkOWNRF47N+kt32HqDW7W27AOBcr9O0Py4m6mkxghJKeonQDgjaJnrP5kEVzAbSEq6jaxrQuwnA5EirM6WOKEOSlRQlv1eYPSlm3z9tYtmZaSAraElULJLrvg9RRLsIolTWFwoc0Hk2OVDz5h8vKwWMi88qg0yPcJC55GmOKEydrwIANBt1m3L/c/ALxNCNkOYzfkuwBACBkJYI9i2SJYgRQPEBTa1gVYqE8UICNigrOgE2qfMPVQNXAEYj0JejTiBWfIExnItemccmxV+6Q4IYIvVjafMPf0LPDbJ6zM6ii5tKUtmzGIlfcJk8GPI7/qcCqiUN6XQ2IDdFTCKKW/JIS8DmM35Cu0UFsagGtUC7e/QHTg6INd0Fq2YxEdws+TZVBwaKOB7GTxwydMxScz6K/wPgZZCoMl7XCMy77Czidsf8b+2A6CRmDHFoXcJwyU0g8t7n2mRpz9EzyNoFhJmKgZOyMXeQzQ6iZL/mw5Up6Gq/1a5OfIZpKHKZ/dMotgp/PD36g9rQMosrgo5xg8hAOkWt0j/ui8pSdOeHVoFxDAcxY3OYP+OLEgiDhhYnS8U/IcrFUxfSYZBL91KniERdFm304XITTINZ4JZC0oCJYIWMJycGqvTmZqFlOu7CCDXscIpk7H658nuSO//VkDAGBHs7EpuTYZR321XPdLAuu6rYSPLqB28HRrs2FZwuCBq8SlOzs98PLZL18ZnOpZVRvIB2st64cex1GPz1XAagIQVFMJejNPpIQFDKGAktm/U7QVoD1GogXV/Hw9OOOKLl/krQa8wfTKkjPujrS9r3aGJRMxjWD+LafgwMFdpdEMOiijLFhabYuDjfr4nqV9gpqu5csh5jRvzmUKmG+llAtw8btpKd4cKe9kEQl0vCoL7scWhWtcCPOKhUxESliFIdcwz4m9jeNjc5EZerQYHYY0TjMTpkNsZe8w8uwTJifQpvnjJS4PD6T7PsklJwXsykpxnDB/d0dWomXHrYxIyW9PB3iX9KBKtBwC8v1h+cbbkjJU7pjvjb4ILMvDZznC0jIjJSwE4FUueme24TeJPwEA9GHHS5bFXRhzEnbhc/lUH0RbSOgxPxu5fQZBvZ/4UUFFVi/L5/4h6DMTRfLY7Y70wsNvlFv3JO+OFGxFKqrfu0+YqyksVBAVh7ucAn7vSAmrQAzNrM7/1occIUSDqaE6zQa9ZWdCaX7vccLc0zApocU0xcXhgkw+hrVDIkFJEHGo5XkPph3BHp8DgY/pZXCTubjNez330m9lVBVkB0hlUg7tgrV6tYS5+jn6D8vQHz5LEpa2GSlhIQCPckEADMsqYf/NHApSXS9ZFr5UQex28WgIE7eEhe3ruo9AdPC1rA/i8lwRSn3RVH9QZLyaKTBzSD5IXiG9ykPU572K4u4TFi74NU4H/d6REhYwCOFvNMMya7BW741rUtdKEMD+kXefMG8R6r2kVQnzx6syncLC6KvD7BFmipgvl34Y/Wc88+exFsKjKUycdajg7BPGsjPcDJ6Fh7IdppJ3mZc/D0ct+S1GWMbASAmrQAzPrMZSOtgTDS9WItE4YQWfMLW7fArprBM6WeScSYdEC1SEoHZNivuEOd/z1RLmsISkQg6hw7VtLMqyLcZ+IqxxwsrC68jYHenZJ8yNvn8VzrYqIrhywr37PtiGHilhIQBPG0jSNvTXN2OJRyWMByGZKAWuAlXCR4kF+eC7YYPiApYx2MraYSvOX20e7z5hcnf2hRGq3smOrnefsAiWCEnBREpYhaF/20rEoGOJbgRoFV83t53/5n/ZmamNFPwtmFtUn8MPAM7lGaQOJtcxv7K1yWJFyKlOxWLwyfFPM1nCFJS3CEW7OGF2lu1Kbyde4H2pqsQxn2sXOb8F3wvdAn0fYemYL4l05BMWgRc8nWt486cAgFn6GEm8GdI4KSYiyyLcOUrzM24fsEmm2qxfqai097I+Ykt0qVke9hWLqQj8fvVSfvIP8Bajty9aAP2G6n4UliqKlLCAwdvOhjfPw2ptCHaiPpvfW0t1dL53ehZAsFY/wFuafvvlD+tZK4eeFCpywCuL2THfPrfQIC7FMZ/t6J9OyRgLMQH+pXnsMpHs/82mME/BWkstgqFqaRzwGJ5nTN861BUfN8bQFnNlVe6Y7xEu9RmWYK1BbRAIetIUKWEhAE8j6Ne2HJ/FRkjjWTaTZHLML9qOz8WTMPOQkd99wVUtfxW4+/wpwTFXDN5itZqAONFQWW8ik6F5Pz1ZnJ/Au3Av01SCRU/xuUWi5OqqE1hwyylSZVFtZgxbfQuP01KlUI9ICasgHKYtQn16J1Zqw/L3VJ5t5rhNm4Gm563VktOyhczgK1C/Zvoy+dg55gc1CPNOgIuTW+X1UlKySrm4rdkui3uky5zH5r61T5jHA7ylm3GCgddVgFJwxGpVEKzV7bn/lWQdrNVfhCU0R6SEBQyej/518acAAHPjk5Xx5904zBeslT+PZX7W9HYOrp75BzfX2pedpWUcN+IUPkVp2RXz9aGK/DhcO2yWESuIWPK56Mua5IaAhnuw1nBVuKg/XqW180gJqxhQjCNr8WH3L2ClNjR/V1b7sVyjd5ibMAUs9DqD48gvEkDRCmH3CZNIUTZB3+AWrNXLDNctrwhtmWO8UIgKm0xWcnkNX1J+gHdlwqmeVVmO8pawsjKUE6zVTskKxiesXBb/g7WGA5ESViH4euwl1JNWbKoZLaXx2PpLMfmE8aUvZap6q3UJO64nznw9ZZeCsPk1yYFgWAi3e4r9hexI+1KKIj5hvEEsQ2YZsYLqKpbn7uGdkOog10FbhEohLk5ltfNICasQ3Jx4FACwrtME031ZHdOKjmefMElTjTwdh1eVJg9ncfo1m5I9SwyJO4QJrIOhyTJg8SIqfcKCLjeh3ZE2b2XV94lDehaU+zOFsKExQLZPGA9P2X51rgd4B2EJC4VPmM8MbRApYRWAXtgNAHgucxi21njfGekElg+h6bBiniCEub8+TTxkxwnLvWvIJoz7LawmDk47d1XWm99Hn4hw88NXxu++Ue4TJlcCWVaSUPiEKabPAybfY9FxOvIJM0AI+QshZBshZKHNc0IIuYcQsoIQMp8QcpAqWcIMlvqfqK0GAPw9faKRwxSNWx1/z3HCeIUp41EaddopLQM9piNn+ErUr5m+1Ij5xNsOuMDh6hPmgbRbnDAGGqV+VTIHeRErlds7mTfaEKkWgrBYG3jhdRWgFGy7I60pex9HXZ4HsjvSyhTmrxxhaZoqLWF/A3Cqw/PTAIzK/rsCwP0KZalo/DV5JwBgER2qTGvnsVKZ0gQwW2YvA5vdkR75BjlzUuoTFtB7+XWAt1p/uuLf6gvSDyuV0Fv4bREU2N3NR18yQQ/wfNJHmExhDPC6i71SoEwJo5S+A2CnQ5IvAHiEGvgQQFdCSD9V8oQVe9vTzGmbUQPArMF775hijlZCu7Qs+bPnd15qkLNbM0yDbjFkWtxC+orMZe8WJ8zLHFdanLAiSn4vVbIiv7xevNEmIFnCBtlWkqCdv1VBpqIUxQnzHwMArC+63pC9VwZCyBWEkNmEkNkNDQ2+CBcaZNJoowk8nD4tf0tF41Ex67CVkzcgagkZ5oj5tj5hHjczFL21393Y62Det74aN5w+1nMbuv6UMThqVE9PNHIQtoS5mMLKfcL4y+6Wz403XctU5IXaoYhjPm8wXE8hKkpoiZMS4ueLvqv44y17c0PYN5vYwS/FNehJUtw9SfCglD4I4EEAmDZtWkibjCLsXIlqksIifQgA+Y6oTs7LXuOEeUUpB0ejnQ8+avsCPvzxCVLoXH3cSFx93EgptHJgbcnFbc+qjXrzCTNnvvSIYUz5ZEc5d+UnIVirVX8SDj+jCF7jlolC9vgmoyyVrnpAsE1BovVYEqGglSpeBGkJ2whgUNH1wOy9CMXYPB8AsJgOVcpGdNbh1ODt+pT3zQRsFOxSOeXm3rXj8wdC1vgSJoVU1DHYUoko3rlb6i8koeyCPpBaBXeZ3yy/ldEy/pKtJ5ahe6RyKKJrG6xVDl3b5x7pC4FpoFUrQljGwCCVsOcAXJLdJXkogD2U0s0ByhNObJmPdhrHCtofQHZmKJF8+ey4aKlNzmqiNHhl6/WDEOQEK6xLBjIh4hMmG6K0S/eqUJtnsiDWFt2d2EnZm3jh4B2VZdNQC89Brt2ei2z28OhaYnomqWf7sgFFIpQtRxJC/gHgWAA9CSEbAPwUQAIAKKUPAHgBwOkAVgBoAfA1VbJUNLbMx2d0INJFVaXkg6yiJUpS4gpBDPkyisQJY9odWvTbd58wWZYwC8Erb3ektcCqLLAAq0+YuoIUCtbqkqnM8iJxgFFtOfRzB2wOyg7wzpaVbGuiax1IqCLu2FyCz6QiJBNbZUoYpfQCl+cUwNWq+O8boMCWBVisTyq5G2zrEV669Mw3WFSar8H+Dj9rq7RtmBQZBYKIWS1YEnnloWA5cH8wAzPAc9G65BchH8YRMQrWGkEa+mAX0LIDi+iQ/D1pPg8M8XU8+yJIOniWjZecVEwnBpiWbP0K1iqXT1i2Z4vAdIC3xWvYL6MzmSO8PFYOoWCtLs/LLC/cHBx4B1hgXj6uTmKL1AHLuJL3CZNdHz4Ea+VeAuQ8Jk8FgjZm5BApYSHGBG0NAGCxPsQ5oSfwOy97jStk9jphp8AbLNWOdlCWPBlQuW076FhG3AdMh2h3Xykr9aEZ1PhryezbMuDqx6S4zcqiHrS1BXAvKxErJrdrCUe4IdEiq7SD6iMlLASwawLjyVoAwFI62HTfrxmDk7WEKSSEJDlZrDZ+yBPkobPSD/CWS84TeGWhNr/z92wKi2WodfefCXh3pII4YWUKZJgaR1BwciBXVD52ZCshTpgMf13fLWEhaeeREhZijNfWAt2Howmd8vdUuUjwWJm8WhvE/UZI0f9Z+PDdd+ce/HQ2DDNqZZDwbqJWVpmMy5zFbS2yHtgp6nem8vPBMuKZnmLHfFn0eMjYthevMSHdfMJE2hRnKy6d5Dg66Au+b+QTFkEaJpA1QN9Jrum8wcHa5ZhLvm+Kl/RM1jIGOry7I/2CH5O2StkdaQrWauUTZpeRxSVM1k6xkMyymRD0VyiECIu/UKWA2yfM4p7fJR5ZwiI4og4tGKJtA/pO9oWfuJ+UfT7ZoQIK1jo2Cso+LUWE/Q9GKQkhGYCKIcO513RLQQMQKTaZOo4Ifx6FghDiSQFRGe7Cmp8zf+/0LRzIRegwNIJcWcl2zHerA0/0PfuE2RPwa2oQ9BQkUsJCinFZf7BSJUx1gzGdi2i3ywzEF+WDZwBnSclCz2sMMVWo5N2MbuD96Jt9wsrz2rdbBtqSYpaZZAxJ1dm9P+99PxGUC4DsOpPxFl5pcK7sssFmR6erLJa7I0PSUXxGpISFFOO1nBJmXo4khEhprGUzSFE/KYd8qiLus/uE2flWyOPPokCoPBZGFGEc7ridey3vEcfnXsEUrFWh4iDS93myEM70rrzlkbKE6mCtfjqQ58iWsfQarNV1r4l/owEPJ9WT3rAsOUdKWEgxnqxFA60H6vr6ypfVMT9skxZ5B3iHYf5fDl98wnzgIQUu2yPtBlemJSFJ51gWf9hUfExkOs1L3cAi+10rxE/RDTLagOfJowLH/Fz98IeFMP+VicgxP4IUTNDWYIk+xLKFqPgge9uvaA3bj2GxtYKDMSn7IQbvu4zcl2zVQc6IsS+Y/oOIE+a35bOcv9o8hFRWsNbSj78fOqAyC4ptsFbFISo8UecDz7ijeik6LENgpISFEFXowGiyAQvpMMvnKhsPS7M3Nn+FpAVnwSIP27l/EoRRgXAVt1zw7o4symCZ1cMyuLhPmL0ISmb7Qnlsluc50zvzkIuguqPs8U3GcU7eD/CW7xUmOjG2Cokk67vGX9bBDvqREhZCjCdrkSAZzNNHKOMhTdkQ8AnzbMliTWe3zOKNvZKBQ4S3F1iJHfTZmPxDZ3kO5X4kQSvDCoK1FkP2phtRZYZVhnKfMMm7I61NYUpgH6zVI11XnzBv9EUQhglx0F05h0gJCyEma6sAAPP04b7xzA1eTAEdQ+gTxtKjKnkJrnIldwd3/Dha/NsitwflW7ScS4/7Uf2REQvWGj4eftNjRbBDhaINRQp8wrxvcipy7ZA0ynFP5iKfsAilmKytxFbaFVvQ3fJ5mJYC/YwTls/PGidMwOGYdTk2KMjiHUZ9VI6SINmpqQRBF5tY33fOU9yfjJ8y44SJ0WF9z/LTCWTDH59cwH6S6NkSlpXYrmxkvI8KxUf1OBuWMTBSwkKIKWQl5usjYNUMlR1bVPLXLbUf7ZfnPZnihAlLEjz8sOIF5n/D+W5mS5h9ujKnbaaAmVyiWPIqDXiqZHekfJKhRGBxwgLhakCVG4VbQ5TpZ2iHsnJV4hPGmV4OW2FESpgCPDJzDVf64rbXGS0YoW3GfN3aKZ+AoCrhb7U5NdJgTNis6ewcke0psIXoCK7bBu23pRIyBk/Zu/tEUGk1JFNev5tnuaItmX6lVWZA4C+nEJjCAh8pDERKmGSkMzpu/s8i4fwTspHyF9Byf7ABXWvw1cOH4KYzxgvTtwVvg2cyPclp5EwxwAIyhfnlZyabS5iWtLl9wtx2R2YhtlTlLA1Ldf/irImmdDKtOSLNjf9sTn4etrRE8wXcPJ34q+rzdsFaefrqlw4cgBtOG2vO7xqjwsPyM2fTLju2qPiZsBTeEPTENh4o930QusdBclLWKX+BhSXsreuPRSKmYUiPWlHx3MFkCeJKbsGiaOlGJD9jJjGfMIaNCWzslUAl70qb9VvGCUMwH/BiWQ4f2dMHfvyVxdIfvMZZU2WJYnXDkL47Uio1b2B5td+eN8U+vyt98bflDtbKZAgTkyeoJWxRRJYwyfBqYZikrcYG2hM70EWSRN4hPJuVxj88VptShFcyZwRtafADKo60YW2Lyo62EZnkKU7vSEvUMT/E7VN13ZYqQ6rLwgt5/i5l5hbsebzB8S5GpIRJhteKnURWYaGtP5h6sO4O9EMx4uHBFKyVgQ7rsU1+IywDhkqwzmDdHPO9tE1hx3zffaEE8tidpWqyTJs3GHDzkDxK8W0YkokwdjhvpeAaokIhbRZespZ5I8f8CMLogiYM17ZgGR0UtCjMcBqo1R3gzbqHk+++CPalYK1BQbwMrXcPF54qGF4FZJWpoAkFqODefSrVFiaYSyxEhWxYjW/KQlTYhJLwys+tPr1UN6+iXu4T5m2TlBeEZQyMlDDJEFsuMDKdFPsEANBA1S9F2u4cZArWKjeqtgywOe+7J/Km3qmDH5bHoB1UWdlT0+/ycrGvZoYQFWwiCNGWCSF/LYYHnv09pfuEEdNfvxC28Q1QP3mV2qbc8llklFXkvDIF7QsbKWGS4eVj2R87AADPZI60fB70R9IKTiLZKT2ed9QwO+bbeSLz8XeC3/5q0qw7IfrIiIpidiJ3torJgoisthZZAQFFrFR2OXjvi6DSfcKsl83U8LKj6z1Yq9tzL6YwvuSFHaDBb4AKywkqkRImGV62kE/WVmK5PgDNqJErFAfYfcLUI4hgrazHNvmNkIwXoYC7T5g12A7wFito/9uExN2Rdr9DMOcLyicsyO7mx5m31vS97I6UgIBMYUG380gJkwwPLsGYrK3GfGrtlA8E70BoBSeZbD+GPr2JvU+YPP6V6xMWHq1OWPGxvKe2bbHKqjqWFFceH+OE+b2zT/XqgPWymaK6tSXrjZ/KA7x5S59nVUN13YZlBIyUMMkQHXyHkK3oTXZnjysKDqy7A+XtZHFw7OegwyJPJVuTKll2VrAPuvKUNjmU/Z8gifnv2PmBFv+23ikZGLIi+G2tCHKpyou/rjNdl+ceyMtQmOQZwvhkCbqdR0qYZIjOVE/XPgYAvKdPtE0ncyDioeWUNJBji5idwtTwjyAX4j5h1n5gYfD1MEkQ0ganQirflVHV9MNZdaGBSsVQ+e7I4IcJAJESFhpM05Zhhd4fK+mAoEVhApMPlqRGLsvfy2oZ4cLpg3HTGeO45CkeG0LSj7kRlgFIBGafMPsXKf1AsPmEeXuuGmI+p+yZCGd6V96iISqCLmenZ8qXWOXyc6sDP8vaqW0FNnmKfML2LYgNkjomaysxl450TOfH7ki2XSt2rrz+gJWjvU9YAb/64iRcflThnE7n3Z6MjBUimpkXYO0TFoxi7PThDIvFyd7hm1imCcWxRfm/7kup5Xm9CxP0UlUxZDnmq1hREQ9VYZ9TnKZgxoAQKWGyIfAFiDdvQS/SaHleZFjBFJfLBx48zGQpUcWd3C/FTPYsMQT6ZBlEPMKc3qOUHssH1X07f9CQF6JCHocCSktYOERFwCUdxITLro+rFsXPV3W0MPomRTD87BApYZIhMnhUNywAYH1ot9/gPbZHbLbs0Y+A1SXM7pgWx9mX00aB4LttGGPFeYZgsVoe4E1IIKNrabtR3VZkBta0d8wPHsTF1CJLRtvyDEMhZCGr67O0A25agrLlLZ2WfdkbTVXpZSNSwiRDZAZVs30BMpRgMR0iXyBFYPMJ8+9rKOvsSBaYP7j+vKNsLmFwXi8Fc8T8ItmdXkPkAG+VR7zIgBB/H01hZSEqBOkEXs5OzxSHHylf2lYdosLHcdqBld91HpYxMFLCJEOkWmu2z8dyOhBtqJIuj2oI+aZ45eltc6Twjpww9NmgZ20qIGo1squrICyWjj5hCipNyFeJd7dwiBqbvfVGjpAVUATSdpWzHOTOTVNYKNMf64e8JDkLKujVhUgJkwx+7ZqiZvsCLLQI0hrqlSdZccKcWJR8TD3vWKponzDJ9OSSkwLWj4DZJ8xhd6TLtQiCXpIOuSFMok9YsHDcxaeMpxp+QZdlMZz6T9B9KyhESphk8DajvtiJeOt2zLfwBwtCB+M9tkdkFsGbRcTB2omP844qe4RhiAi1Yu4zrNqBEScsCFnMoKZn8itNarBWzvtBwIdjYG34+l8GMi1V1nTs+IrTEi0np/yRT1gEXzBJWw0AWGilhIX4ixu6OGE+fnj99wiTP0sMw9JqDl6O1XH2CSOO1yKyBF1uquOEiaQvRtmyrHCcMH8LmmesVXaAt01ZeY4TFqY2HSqfMH/52SFSwiSDt2InaqtBiWbplB+IJYwljccdVP75hPHPKJ19woLvtWGyTgQOqx1VIMFYwnw/M1FeHhGLsQMXkUwMVINp96Eagz37hJn/2j2XSdM9v71Pmejrcq+0BDykxoNlv2/g6sfnoGdtErVVcZx1IF/E+4lkDdq7jkRba7lTftCNIywIg/IDFD6sYbZQRiggqqUIrIjaij8Ix0huICw+aJESJgHPz9+c//3J2l1ceSdpq9HS8wRgc/kzlbM/r5TZgrWq3Vpt5sWSxj0Vr4Lll4IYEj1UKZhDVNj8lgEVA7NMnV1EPl8d852c43ySQQYcQykISnfu1IE4dHgPV57lS7pe4RJ2RcJWDK9Wq6B8OYHgVxciJUwy2tM6c9re2IXeZDc29phk+dwPg0tpw2cK1sqZviy/wkNfzXw8Eggh9kUjnOjga7mEQew/Of6WneJgrSLhBBSnVwm5S6be+YrgznMP8JTfsxtHXmEKbiMGT1/n/U7ENIKMTgNXqngR+YRJBs/QOzHrlN/Sc6IaYRzgdXBhCo7q48yGxSLFIg+bT1wRTYb0MhC0ZcAPsDZJ0WCtbLTZebNC5idBzDHf+blMfzZJhrDALb+OoRSUOeYbKDt9QVKwVtu4el7qO2/J8rg7UkIv4d+A4pmlFERKWICYRFZDpwRt3ccHLUoe3McWeZyRM0UxdxJAAKLZw9BpK2uOxwbxYK0W29phPxirtJo4BWtVw1AgS4UFsSyGw1YaOfRtQ2CEqAwUryD4svLC0deFxakwx/xICZMNjtH3sNhiLKcDkEnUWj4PunE4wY8DvHkgLVYrpxJaqcFawwix9u5UMPwEXS1h3BTlQixYq3OucuuVxBAVwg3X5xAVZdYn+7R+B2tVTVdGcF5xn7CcKU2CxY83fdCdOYtICZMM1npNII0DyXK8rdv7CQQxC2PhWZxG5lZ55vzMfOx8H8QQit00IVbMReGbT5jSjS4lH3FlnHL81OcJU1Oz7csV5BPmladnnzAXjcnPV2XbJCVG22swcL8RKWGSwfpBGUvWoYqkMVcfEcjyiVcwWcI8BxlkJ8Amj5xPY/EH17/wGSFQAJWDrcGzB2vll8Dt4yBS3VJ3RwoI4CUYLi9kKaNBWymCOWhaUbBW1zbtX2GXbQQr+utVCu52HpIxNVLCJIO1Yg/QVgIA5uojVYrDDV6fMEEu3nIzZrfdHOlAwMlaItNiI4ow+agEDV5HY399whTvjpR4XFhQOw95sA9udLaF/bjllW5ud6R8+qE5pB4CbWJf9gkjhJxKCFlGCFlBCPmRxfNLCSENhJC52X+Xq5THD7COvb9I/BUAsAk9HJZPwgtZcbmkMWPZrelZEAOB7I4Mx6QtFChuV/mdXxbpRPqPu/9M5VWEn3HCymhVSpwwrsaiRjr7A7zl7I60fe6JOh9KeQUZJywsY6qyOGGEkBiA+wCcBGADgFmEkOcopYtLkv6TUvptVXKEETVoK7pysMoonIrax4phyVv0O9Q+Yd7ylyIMfTZM1glZEC1X6wO87Rc2VBadrNAMovzY8vD1eaGdz6oKOSBrXSDdTdFOTfcjhrzQl19SXo9CYk4fsLlDpSXsEAArKKWrKKUdAJ4A8AWF/KThrfVvYcYbM9CSagEAvL72dcx4YwY6Mh0AgJdWv4Qz/3kZnpqzFv/3wRrE6+eiesCjAIBFmxoR7/IJqgc8nqeX6DIL1f2fyF8P6PYybuzZHVd2XAcAeHXDU6jq9+9C+m7voarvM4Xr7u+gqs9/8tcPLXgIVb3/m79O9nwdVb1fKLp+BcleLxWue72EZM9X8tdVvV/AOw2PF13/F+jyev76Vx/9Cn9d+NdC/j7PINHtvfz1b+b8AnuTb+avb3zvRjy57Mn8dXX/J/D08qcL1wMeR7zLJ/kv02f0Xjy/6nkAQEbPoHrgI4jXzTcekjSqBz6C19a+ZsxUSAf21P8Zsc5Z3V1rxaaq+/HuhncBALvadmHGGzPw2Z45RvbYXlQPfASxTsZy75bmLZjxxgzM2ToHlAIksRM/fPe7mN9g8FvbuBYz3pgBrXoDAGBPej1mvDEDy3YuM9hVbUb1wEegJbca19UbsbfLQ1izZ0228NaieuAjIImdAIBPt32KGW/MwJbmLQCAWVtm4UfvfQ8kthcAEOu0HNUDH8Gutl3Gde0yzHhjBho7Go3rzotRPfARgBhtLdf2dKQBAG9ueNV4DiMo8POrnsd33vxOvqyfW/kcvv/29/PXTy9/Gj9854f56yeXPYkb37sxf53o9oFj2/vbwr/hVx/9Kn/90IKHcPvHt+evH5j3AO6afVf+OtnzFdw95+789d1z7sa9n96bv/7t7N/i/nn3569v//h2bKT/A2AMuqVtr6rvM3h08aP5659+8FM0kDfy1/PaH0Ci64f56w8a78HH21/M08u3vSy+8+Z3TG1vxhsz8NIao690ZDqwLvFHxOsWGolJB2a8MQNvrX8LANDY0YiPmn+LWK3RNkisGbT33xDrtDx7bbS9WVtmGdfx3age+Ai2tC8xrhM7jbZUvQ6A0faMa6PtacmtZW1vxhszsHK30ZYX71iMx1bfCpJsAADMb5iPGW/MwIa9Rn6rtjfjjRlAtu3N2vqR0VZjzQCAdNUSVA98BGkY41ys82Ijfbbtvb3xDdO4F6+bj+qBjyCjZ4zr+rmmthfv8gmW6n/MXz+9/GksThfqOtH1I1T3K4wTpW3v0cWP4taZtxaed38HevfCOJLs8WbZuLdG/2fRtbnt3Tv3nrJx76GFf8pf3/7x7Uj2MMYxAnPbo6Co6vsMXttUkO+nH/wUjy8pjJvV/Z40tb0fvvNDJLrMyl9//+3vc7W9VM+/5NteS6rF1PagteKBpT/Jj3sk1pwd56zbXvG4Bxht739bfmVqezPemIFFOxYZ5JNb8es5P8y3vWU7l5WNe9UDH8m3Pa16HaoHPgI9tgMAoCdXmdperNPKbFsz2t7MTTMx440Z2Nux23j/bNujxDBIaJ2WoK37w/m2F69biLvm3QCQdPZ6vmncs2p7xd9clnFvY/wRhAEqlbABANYXXW/I3ivF2YSQ+YSQfxNCBlkRIoRcQQiZTQiZ3dDQoEJWE/Z27MWGpg3QqVHhjR2NpuuNjTuxavc6fO/J+fjpc4tAYq3QkjsL8mqt0BJF17EWkMSu/HWv+GZsiscxRx+d5bcHWnxPIX28CVpid14/1+JNIInC8x2tO0ASjUX094LEi67je6EVXWvxRpD43qLnjWhKF+QhiUYg1pS/3tq8FTvbiuRP7AaJF55va90CnRjXBASbmzfnlQoj/S7sbt+dN/dqiV0gsZb88zY05JUOQgi0xE6QWGv2KYWW2Im9HVl5iY5MbDuI1p5/ntJ2oDllfEgyNIMNTRvQrmfpk4xR9loHCAFSegobmjagJd2S5ZfGxuYNaE0b/Noz7djQtAGEpAx6MNK3Z7L8SCpLL53Nn4KubUeH3pGVp8OQPztYtKRasKFpA1KZVP56U9MGgGSyddUOLbETGZq91gz+uq7nr7XEToCY217uuqmj0dS28s+z2NO+Bxv3bsxf727fjc3Nm03Xm5o2Feoq1mzZ9nLY2bYzP7ACRtvb1rItf729dbvpmsT3mq63tWzD9tbt+eutLVuxo3WH6XkKhbZa2va0xG7T9eamzcgQo20QArTq2/NKBQC0ZBrQnNpblL/Q9ggh2NC0Id/2ABjX7cY1BUWKbAe0bFskOjY0bci3RUopWvRtIFrOkq0D8R0gsVxbMdpeS6olO4EwrlO0Lcs/bbQVzWg77Zl2dK3fixvPzPqFaumytld83ZZuw86OzSDZttSabsWGpg35tmjV9oy2YaRvSTdn247Rlihpy7Zdo6Pm2mLueVPJOEhipeNaq6ntEa0VbbQwPu9u341Wur0ofbNpHCxte8VtjVJj3EN8dyF9vKls3OtA8XNz22tobSgb93YUtcVtLdvy49pfLz3Ysu3tTRXk3dy0GbvadxWWwBO7TW1vc/Nm0zi3qWmT6dqt7dH4DkBrBQGBTgttz+BHsb1tU37cA/TsuFne9gDzuEdhtL096a0gWgcISH7ca0u3GZNjLY2tLZvyba0901427hltJTdudWTHqXS2sDpMbQ/559m2l22Lenbco1qbaZyD1go9vqPoug3bWjchN3NnaXvFz1nGvTTZkX03BAqiyoGUEHIOgFMppZdnry8GML146ZEQ0gNAE6W0nRByJYDzKKXHO9GdNm0anT17thKZWbFlTxsOve1194Q2uDtxLw7WluLwdsNC8NRVh+Ps+z8oS1dXHceCW07B0B8Zs6c1vz4j/6z4Xu43D+44ezJ+8NR89KhNYkdzB+qq49jblsYBg7riP1cfYeKhEUCnQDKuoSOt4+MbT8AlD3+MpVv24oVrj8L4/vWWct335grc+fKywvtUxbG3PY2Tx/fBg5dMK8uTAyHA6tvOwB9eX467Xv0Mw3vWYtX2ZvSqq0LD3nZMHdINT111uCnPCws241uPzTHdmzigHv+75qgyPotvPQWdkuaV+Nyzq44dgR+eOtZSrje/fyyO+81bGNqjE966/jgAwKSfvoy97en8O1thb1sKk255xXSvuN6s6rX0/oG3voJdLSl8ctOJmPqL12zz2clQ+rz03XL3L/vbLLy+dFvZfRY4yeD07PL/m43XlmzFgxdPxckT+gIAUhkdo2580TLPBQ9+iJmrduCxy6fj2U834l+fbMi3kSuPHo4pg7riqsfm4NQJffHSooICOaxnLd78/rGO7/DlB2bi4zWFwbyU943PLMBjH61Dl5oE9rSmUF8dR2NbGt84ahj+/O7qfJ4DfvYK9rQaH6TrTxlj6gdWtO3qp5T/s59uxHX/nIsvTOmPu88/0PFdchh944voyOiY99OTccDPCu2wf5dqbNrThuPH9sYb2Tpf8+szTOm71CQsZbST9/RJffHHr0zN57nmH5/iv/MKHz8nFL/rx6t34st/mpkv59z4VIpTJ/TFAxdPtSyvjrSO0Te9aEq/5NZTMe7ml2zfI4fz/jQTH63eice/MR2Hj+hpevajp+bjiVnrUQqncmHpRwf9/FXsbO7ArBtPRK+6qvz9C//8IT5YabT3I0b2NNG1kqEU3//XPPz7kw246Yxx+MXzS1CTiGHJz0/NPz/+N29h1fZmvP69YzCiV+f8fZbvSu+6Kmzb245edVWYdeOJtvlycr25bBu+9tdZ6FNfha2N7fkxvVMyhs5VcWzb257/Jv3vmiNx5h/eK6NVSrOU36KfnYLaKntPq1z6Q4Z1x8erd+KJKw51PNNTBgghn1BKp1k9U2kJ2wig2LI1MHsvD0rpDkppzsTxEICp2A8wmazEfH1E0Z0weBuJwQ+fsDJnTlY+AmekOdG2mrD4XXOqo5gH0xLFuFqWhGOcMO9glVTV5FbsAG8fg7VKOnKHNZ96n7ByBuqCqmatPmU7bb3SNV/LPyCcv28xxaNUbaEKyWdXpRI2C8AoQsgwQkgSwPkAnitOQAjpV3T5eQBLFMojDV4GqS5owjBtK+brw13T+mElLW3obI75ROnOkpwMPCz83OkSxHEuIRkvlIK1XE27IyXLIPMIJZUQcszn/PgKvdI+snEkjP3Na9G6tdEgq05GnLA8Lc4XCbrJKtsdSSlNE0K+DeBlADEAf6GULiKE3ApgNqX0OQDXEkI+DyANYCeAS1XJExacHDOWUufRghJmH9co6ObhDicRbYPQ+hUnTGBHleP7WN3zeZ+z8slhAPu2hVlaFAZxal0SCo9VVlWlqOYAb+88bHkrzhdExHxVYUrsqHoOUVGSv8zyKaHC+Vc33Hmq3rUYlnAzypQwAKCUvgDghZJ7Nxf9vgHADSplUAEvjePamLHzbIk+xJ1PADoYa7BWlQ1YRPn0s0MFoRqHJaZNGOAWMd9LUakIyKskkKVIsFbF6UXzhBIB9jdVkwi37L5M+kuXRot4yxrjeL/PQRs7lCph+yq8fPBX077o0OPYiYIzeyV/X0UizHPPmmjpTI6NgMxYSoYcFvcYaMqsX9XjRSW1Res4Yda/jfQyEGwJCVnCXFOU+HHxs/DC3DqbBCFk9BUrEn4ftO2ZX5lPmPz6VmG1Uj7WhWSwi44t8hUUE7Q1mK2PCVoQW7D5hCkk7pAsNLPs0Aiyb0FWsQYyuDotcav4QInk4Q1iKXQ0UtQ5vML2sHLFbhx+1FzZ0iiRz5vbJyzgJhspYT6iH3aiB9mLhXQoU3qlbSNLnOnga6t7gvl4UMrDiZ40U7ZjoVvsjmQpBwmy+eWrFYQCw8uSmn7b5y6zhDGMtm6yMJePKmuJSB4XoVWeeym6ahC0v44Tf3X+ftaUJRvCLPh6ZABZioy/dR4SQ1ikhPmJiZoRR2iRPjRYQQDbFsjyoSpOoyJEhevHkzlGhc2MUrFvhUoEfcSGSsiYwRq7rPwfXn2vF5F+p56F761TdbkHYdmzXQmQNG6pos+CsnAZOWms+rJPRR/0iBopYT5iorYGGUqwhA423a/E3ZFCnzlh/xD3jNK2NzvKIUhUhiXMO4lQ8THx5C1YavO7BKUfaJbeJMsKFJZZNguCXAoKK8LiL1QpkFHt/h/gHY5KjpQwHzGBrMZK2h+tqGZKr3I847EWOLVVEaXFs2+Dx3TefSsK+X23uuwjHzkryIjvQ4jawVyEtkzFROTDYZfDTiyZxad84hJEiArFS81lPD3yK2szpcvPXoLzOpT/bV+axJzfsi/7NNgFPXGIlDAfMVFbg4V0WNn9sGjkALtjvkqZc52PT1FUX4bi35PKMYUFEieMOz0t+u2AsmVt+bLY0VZdjiIfKD8clveVOUJ4RuQCvJate7BWubV3wSGDy+45dQtpqxkV1ggjJcwnDCFb0Jfswgrd6gxza6hsTH44stsqH159G1h3Wdqkc8zP4hNX9NtvnaXSBhgeyIjvQ0CUfkBFFGqZVSbmmO/8XGmwVlEXBMZ0qruDZYgKxbsuyo5+8hys1YwyQ5sUx3yxmsj7qVn1ZdUhKtSSZ0akhPmE47S5AIBP6ciyZw77u1SJU+AgYC0wgrUqBMfOTT8h/EGpHENYRcAcrJW997AoebKCtaquLzErFaeSK2Rt2zdmCWFanchBddkGEKvV/ExSmfO322DbbKSE+YTRZD320E74UB/HnEepJUwaJXsh7X3C+FA+k2OjYO8Txp/HlKYokd9D9b7xiZMDy92RRO0HNPBvswL+Kn1vhENUMGYLRDHxOfyI/AO8ieNzP5GTxW6ns0oE3peziJQwAYhU3omxTzFPHwFqUeRhaQwA24BMoNYUJtL5/ChD8ZhHEniHqZFIhqLNkWUfG399wgQJsfITyRP5hOXh9m6B9jYRN4oKgdM4FpRPWNDlGilhPqAOLehNdmM17cuVz4+24fVjwXvgtZHH6+5E1nTWCZ34My3HFteM7z5h+8BIbAcZuyOlCGIPkeqWujtSQYMLo+8p63vyWq75UZ65UoO1qqxnFbEXI5+wCNIwQVsDAHhDP8jyud2AE8j3lnF7pMoGnHtvHh5+hIoQ9wnzLltYBgwV4N4dWVSe0i1OggRlO1O78gurLh5WuUrgJmawS3Q29wPiKxPOPmFyeFSWR1ikhPmCSWQVAGCBXh6eIij40uBtmHjfas2YjvO+8YxhOdZkCPN3tN6X/SS4B0+X6bOKshKKEyZREhX1I/PjW66MioHdJ4whjaAMdvRVuQbY+4R53B1Zkl9FOBXPljDrzuyNqBtC4uIRKWEC4K26SdpqbKA9sRP1XASDOKKG1bzvR5ywHPw6n1EVDzk+YRKIhBS8bcnNJ8zLCRSV4xMmsnNRbfpKgltbCLK72U4eFdeHH64OjnHCZO2O9OGgepmIlDAB8DaWSWSVkBVM7e7IYBq8kccjT9bdkQIOrmH/8Kj3kwju88PflojlzwI9T+LsN5A52fO7zNkmjeJC7YtNSMU7qTgFJfIJi2ALHh2sGxoxTNsqpIT9+ZJp3Hn8AksR8Dbyuup4CQF2Cn5Yikb36YwvHjgAd59/YBnfP1081TafnDhhYRkygodbnDBbH0tO2pbPGevhH1ccypSOF6pawV8vPRjfPGaEdLpBWo89wYddfHYsy0JIeKXr8blc+F+ubgha0Y6UMMU4PfYxAOBtfYptGrvGN3FAF/kC5XjaLtm45yU2v1lhlycZ05hkYPcJE9gd6UAvHtPwu/OmYEzfurJnR4/qxSaUR6heoq6kZU+32EJ+Dq6lvA4a3A2XHWlMvFTM6KUGayXAcWN740enjWVL78jDX7BYubx4GwW9VGWGHFlsd417oukhs01+UZJhqjEWREqYYnwuNhOf6QOwiA4JWhSp8NNPi8X6EJTuwGYRlLA7soKUI9UQ9wljoe1qCmOGijqrvHYgGqw12D7jxy6+cp4G4dJm6pmfW5P2sU058vK5bYelL0VKmABYK0+DjgPISrynT0Kl6OdsuwOLdqCJzMht43flZJAEIdkEWflUvcr9JCppd6TVvWI3MT+tGD5bTMT6nc19H3gECbuTFZjyyhXFE2SVrQrHf++ilVMQPo+ywjagREqYAFgtG8PJJtSQjlCFpsjBj28trwXITgEIy4zFCkwz9hDLHwbIrl8vljBX2lxp5Vd82H0DRXY2W0HKjmJFZaXaJ0w2P7dy8LNNhan1hqUvRUqYAFgHlklkNQBgIXVWwsKkZLD6hKlswLkZENcHL6DtzX7BryYS7O5IxoQmz3wHegIyyDrAWz3k+Wup8BEKE6ys+24W//0yWKsHDjJOQZFV5NwhKqIDvPdd/C55PwBgFe0XsCQW8OHEelkHeJflZ+xkfnQtFbtE9zeIKn5W7YCAKC3voM/wDNOEzQpl8dJECTFmdOrjMsoqDMFavb5I+QHeUskLgYWn6vE7LH0pUsIEwFJ3BHr+dwYxF3ohaQ1gtIQRtQ1YyIIhXQo+KDegBf2CPoB1Rmp2zGc3hbHQF7aEOcaek984ZPpiykofWghsjwzUIiywq1sKXx+q22liHtQEJ+hmHilhAmBpLEPIVgDAXalzVIsjBHmmXwEenI2+bCbHmC/IQYU3DSuCHjBUQOYSYJgCPCrZHSmfpFSLg6wjcZiVIMX1baUQKVMVVPmElREo8dvzSL+cohzsy5uQihEpYQJgqbvJWX+w13T7IJ55eiFpDADj7kgQtTJnReDiEXAZKo/f5dMLhtEXphTmYK1Wz61fgi1EhRic6l/JB8qHPPuKvi8SJyyM/UC9T5h62AZSlugTVmmIuyfZf/HJ2l3477xNaOlI48nZGwAAg7rX4J6iiOl2mKStQjtNYDkdoFpMIQQbioB3WcT5WhYfHtpBIWgn0jDBPVhrVFYskNvmfQ7VoZhf2McDESh5JwVhIdRPbMOByBLmgLPv/wB/+2BNXgEDgPU7WzHjibmueSdpq7GYDkGaQc8NS2OQDa+KXm7W9IUp/fHFAwfg+pPHuKYNI/rUV+HL0wYypf3hqWMt73styzvPmYxff2mSa7ogSvH2syfj3KkDcciw7kzpi+va0hJmk49lSM9Z0S6cPhi/P2+KxXMGIlnMOGEUzprSH+cdPIg9kxskzZ7OmNwPVxw9XAqtYnzv5NGma1FpZbxmVVzD148Yhn998zC5/BUHa+WSxQNdVvq3fmEC7j5/Stn9c6cOxK/PnswnS5bXgYO64bxpg3DXlw9glkMVgla0IyVMAOt2ttg8oTiIfIZHErfhUG2J0vhg158yBpcePlQ4v5clG3NATH7erscSlVxXx2P43XlT0LMuaflclI81b/kBAgkhuPULE5noHDfW+fgj0QHj3GmDcP4hg8UyK8ag7p1w57kHIBHjG44sQw94bJs5XDR9CM46kN2KbcWrW20Svz//QNRWyV9w8Nrv7rvwIHTplDDuS+TRs3OV4/PzZSqkcO93N39uPCYP7MqVL6xQHazV7sElhw3FF6aU94U7zz0Ag7rVONO0QSxGcPs5kzGoeycAgGa107kC60gE0XKkRKyp/orpeo4+iimfiPPq1ceN5M7jGSZfHHeZZVunnFiGya/OC1QFbGQXwC9G4jCHCeMxhXkf1YMuHpn8/egzpTxYeao+Gsg9nXcavLDv+3JDVFhxUEfbmVPuO6I69qSlLCH5aESWMEk4TFtUdu9VBqf8oBBUYDzAfdZUSrPcJ4wxTpgCK51tPrFsZXAbF/aTySETrI+jKdz0UlbcR5944CUCMX9HvtAHlRCiQlRC1vILogjsHfPlCKOCPn+AVAO58S7Itha072hkCZOEfyR/CQC4sOPHqEYH1tC+aEYNU95w6OPuMPnisGXwxo9jFh32MmS2ANj6hfi0OzL0JcmwO9JuBxYnbevnwZaPTPa+WMLKrr1boryA3RJnn1CdlU4NP7fsqg46t+blf7mGHZESJgFV6Mj//kBn8/0JGvbn6zGEqCj2uxHg7eoT5mL5YvYJk3i0i2s+n2ZylWCdkAlPhwr7mNfvapHJr5JblLDlWuEOa6+wD9YaXvrc4U9yR9PlLWFy5eGTxR8+doiWIyXgdO0jAMA3Or4rRqBCZgBuFoiy9LL5Oz0LeRl69uuQJIcrn5CXI2AuCytxvc3sXXaSiZOWApmWOD/epVReGZYoL5BBVZklzIawZ0uYm3VXIW3X/FnuQcQJC8tYFylhEnBX4gEAwFx9RMCSsMPLNv7imZSKHYjuPmOMfCKfsIqH0/u6zZ5L2xlP3fLHsvM7PlZYifkL4d3MrOlC5BOmmn4QPpR5S5jlTme/Vhd8YWOLaDlSAlbTvminSTSgm1D+SvDDAUotEAy7I70ePMtBL+xlqHo2KwvhLkUDxe2AJ04YG223BB6IS4Dc3ZH+vwwrR3U+YYw+aY7+p/5a6bxzc4sT5mF3JKd0ZX6+2b+EwPe+FZZvRmQJ84ib449ghLYZ/8scGrQoXJAWJ0xBVHr3GZC6qYuKOGE8cLWEVbDVQgRObcFy9uxgpeUpurCXs0wrQdC7w7xA3CcsxLsj7e4rjhPmpU2JnoJSHKKinKY/CLr9R5YwCwz90fNM6a6NPY2vx18CADyaOUmlSKFAseKWa7hOswmvMWSC6Bp+z3JZQYhRnqqNF5X2OS47MFqx713Qs2epuyPlkbLnUcKEtX35vQOxFAUlwYKGMp8wu/se23R+yc/muSfqcqARUiagarnC4hMWKWElYG3wp2sf4ruJfwMAruy4DnvRyTLdmD51GNCtBm8s3YaffX4CfvrcIhwwsAvmbdgDAPj7ZdMxfXh3XHDIYFxx9HDc/9YK0zFJvLjjnMnYsLMFOgUG9+iEH/x7fv7Z9GHdcfbUgWjY226bn80nDHj0skPwxKz16FtfbXp26xcmoF8X59AcdhOuP108Fec+MDMvwzePHYGGve35kwGOG9sbFxwyCNedOLos79kHDcS89bvzZXfh9MH4dklA26euOhzvfNbgKNtFhw5xfG4HkVnkzz4/AQO7mctqQv96y7TPXX0kXly4GZomV0361RcnoVs2cjoA/O68KfjTOyvRu66a+RghFvz2ywcI+g86PCPAL86aiJ6dk7j+lLG47YUl+OphQxHTCD5dtws/OHUsTpnQF//+ZAOeX7BZGl87We4+fwo60jpnTn9x7fEjcezY3qZ7IvXyyy9ORI/aZP767vOnIJWxHztPGt8HX542CAcN7op/fSI+vgHA908eje1NHfhkrVHHAPDXSw/G6u3NzDRyr/y9k0bj0BE9yp7ff9FU/O391RjTp86WxtGje+HCQwbhm3+fk7/3hwsORGtHhlkOS9ns4rZ5nCL17VKDy44cZntqQSn1m84Yh5G9O+evc304EdPQ3J4GAIzuXYdLDx+Kr9qc3nLnOZORjBcW3E6b1BczVw7C908xjqAb2qMWXztiKC46dAhaOzL43/zNOPugAfjHx+vRv0s1bjx9HH75whIAwM/PmoifPLtQ8O0LuPH0cRjTtw4//99i470jn7BwYfm2JqZ0v0o8DAC4oONGzNQn4KXrjsLn730/PwhP6F+PRy+bju5FAxWAfGP91QtL0K1TEkeO6gkAuC17rt8d5xyAXS0pnH1Q4ZiI2740CXPX7baVJdc4n/7W4ThosNkv7cvTBuGTtbvw6xeX4NHLpuc7xI6mdjzz6UacPqkf7nx5GdM7F2NUnzr85MzxZfcvOWwoN60chvWsNV3XVydw57kH5K8TMQ23fcn6rLLqRAx3nHMAxvatx7qdLbjl8xPK0kwd0g1Thzj77XVK+tclrAYuOyVr0sAumDSwi3QZLpxuPspoUPdO+MVZ7mdM8uJLB7GdncmLPvXV+TZR3FbuOMf4fdzY3qirjuP5BZuZFGUvVgero13Chu8Wn7/qwRLwlenmyYrbu19y2BAcNco4kmt8v3os3twoxHds3zp8+/jyk0iOG9sbxwnQu+YE61NNhvWsxc9cjhq78JDBOHViX9O9zx3QX0AKf0AAyzHbDpcfZT5b1KoPaxopG2u/dNAAHDrcUGzPnWZW+KriMdx+zmRT/p9+rpB/4gBjjLv5c4ac3zh6eF4Ju/jQIVKUsG8oODPVCyIlrAQdaR1d0IQ9qIWTATeFOJ7JHIGZeqEBfe3wofjTO6vwo9PG4pvHOO+U/PHp42yf/fmSaabrCw4ZjAsczv27+NAhuNjBgjN1SDf865uHm+716FyF5689Cs98yj4r5Q7Mx5tegnn460cO805EAUxLuZW25hcAgj6iyq6Ogl7CUMFeZXO0Wr49dkwvBiVMkVuABLIhWcVihspgrVb47ZenyCWoCGGpx8gxvwSJ3Wswr/oKfDX2im2akWQjepE9mKkXZhVBD86ywBas1euw7RKiIlJSImThfEAzGw2erinajf127pXRR/z0b5N35I5i73QeEoqqXJVjPm+Q7P0FQb92pISVIN64BgBwgjbHNs1hmrGW/EGRFYw3kGlYwCMrt6M9t2N+BRUcJ4rfLOhOXwlQ0RY4929Z3g26b0sN1ppz2FbYIEVDiCgrZxmWML9DKXgOb6MuREUlIyzvHS1HliCT1UtjKHewPU37CPcn785fb6C9y9JUOlgd84OXonKxvx095AVWFhSvsYmcE3ORzsP3Y4sk9hE/rHghMmBJo6Oq1GyDqaq2hO3j464dgh6OIyWsBBlq1EiMmJWwy2PP46bEY/nrZlplel6pVhyVkwHbg5TDvFdaEUIy6aoYsJSXaGwiL2krtZ9bIYgQFXb3ytLIFyVLVwZlf9uA+rAr+yfC8t7RcmQJcjvMD9WWmO4XK2AAcH7HT0zXlGKfMODwBmvlIy5PhkrGPv56cuFzYYn7hPkLKT5hPixH5iDNgiXLoiaBkCqLtqoQFa7599uBaR8O1koIORXA3QBiAB6ilP665HkVgEcATAWwA8B5lNI1KmVyQ3usEO9rTfWF5mc0jvHtf8UEsgYLaLi2uYpC5WzAa7DWfQpFL7evK5mqwe+byN+ybI21gfuEyaep1CfMolezHXmmQhpJuyMrzifMLYE3+hWLkLy3MksYISQG4D4ApwEYD+ACQkhpkJLLAOyilI4E8DsAt6uShxVpGrN9dkLHXcgghvm0cg7q5of63ZFuuSMdJUIObsFa+WipixPmv0+Yd/i6tCrNghUeOhVn/Yx2R1oi6PdWaQk7BMAKSukqACCEPAHgCwAWF6X5AoBbsr//DeBeQgihAW5byOgFX7BGWoPfps/FCLIJj2ROxgbayzYfpdmjFxB8pfIgJ2pMc5ddI4DOUTNaCTEWHsXp9lWUlksEe1iVlWj7YCl2Ld9GrRPn5MnJkE/v0yc5J5YcJcL5XWXAqq5Y2n8uSS5/vrwlyeqFjuxTK9zgdTx0y597n6CtvH7D73q0g0olbACA9UXXGwBMt0tDKU0TQvYA6AFge3EiQsgVAK4AgMGD7YOWysCgvr3wTmYSfp8+G3No+fE4Vrj08KGY0L8eQ3p2wu6WFC45TOzomyBw5gH98Mm6XbjuxFH4w+srcM3xhaN+/nnFoZi3YTcOHtod7y3fjuPG9sYri7cy055x4ihsb2pHR1pHVULDT84cj7te+QzfL47aDeC7J43GMaN7oXddFa48ZjjOnaomuroT7jh7MroUHd8DAA9cdBD2tqXRrVMS22yOenru20fgjaXbXOnX18TxrWNHYMW2Jstjl4rxnRNH47ix9gr/3edPKbt30xnjMN7myCMrPHXVYfho9U7m9Cz46efGm4458YLqRAzfPm4kTp/Ur+zZny6eikdnrsXIXmy8pg3tjksPH8oUKfvhrx6Mxz9ai6E9rI8hu/WsCehZl8S1J4zC719bju+eNBp3vfKZ63FXPzlzPMb1rcOKhia8t3w7zj/EHEn8+lPG5KOMF6cvxVemD8HKhmbLqPF2ePbqI/CuxXFdXzpoABZu2oMfnDKGKf0tnxuP4YxlnsOd5xyAP729EtOHFd7tqmNHYHdLCuP716NHbRKpTPlO9JPG98FFhw7GNcePwh/fXIErjhmBh99dnT/CjBfXnTgKx4/tja6dErjq2BE4y8PpBr88ayL61lfjmDFGH73r3ANQk7ReQXnokmm2Y0cp/nfNkXjVYnz908VT8UhJe//FWRMxoGsNtu1tQ7dOSextS6M6YS3DbV+ahHvfWIGjRva0fP7klYfhP3M3or46nPv0fvb5CfnTVO44ezJeW7IVZx1YqL8HLjoIjW1pjOjVGR+u2sFM98+XTMNjH67F8JKTWvwGUWV0IoScA+BUSunl2euLAUynlH67KM3CbJoN2euV2TTbrWgCwLRp0+js2bOVyBwhQoQIESJEiCAThJBPKKXTrJ6p3B25EUDxdG9g9p5lGkJIHEAXGA76ESJEiBAhQoQI+zRUKmGzAIwihAwjhCQBnA/guZI0zwH4avb3OQDeCNIfLEKECBEiRIgQwS8oWwTO+nh9G8DLMEJU/IVSuogQciuA2ZTS5wA8DOBRQsgKADthKGoRIkSIECFChAj7PJR64lFKXwDwQsm9m4t+twE4V6UMESJEiBAhQoQIYUQUMT9ChAgRIkSIECEAREpYhAgRIkSIECFCAIiUsAgRIkSIECFChAAQKWERIkSIECFChAgBIFLCIkSIECFChAgRAkCkhEWIECFChAgRIgSASAmLECFChAgRIkQIAJESFiFChAgRIkSIEAAiJSxChAgRIkSIECEAkEo7qpEQ0gBgrQ+segLY7gOfCOyI6iR8iOoknIjqJXyI6iR88KtOhlBKe1k9qDglzC8QQmZTSqcFLUeEAqI6CR+iOgknonoJH6I6CR/CUCfRcmSECBEiRIgQIUIAiJSwCBEiRIgQIUKEABApYfZ4MGgBIpQhqpPwIaqTcCKql/AhqpPwIfA6iXzCIkSIECFChAgRAkBkCYsQIUKECBEiRAgA+7USRgg5lRCyjBCyghDyI4vnVYSQf2aff0QIGRqAmPsdGOrlu4SQxYSQ+YSQ1wkhQ4KQc3+CW50UpTubEEIJIdEuMMVgqRNCyJezfWURIeRxv2XcH8Ewfg0mhLxJCPk0O4adHoSc+xMIIX8hhGwjhCy0eU4IIfdk62w+IeQgv2Tbb5UwQkgMwH0ATgMwHsAFhJDxJckuA7CLUjoSwO8A3O6vlPsfGOvlUwDTKKWTAfwbwB3+Srl/gbFOQAipAzADwEf+Srj/gaVOCCGjANwA4AhK6QQA1/kt5/4Gxr5yE4AnKaUHAjgfwB/9lXK/xN8AnOrw/DQAo7L/rgBwvw8yAdiPlTD8f3t3F2NHWcdx/PvDan0pgrE3IMQa7WKxxQoESrhAAyHRkPZCLRIJ1Kz1Ckx9aYzBiKnxRqI3pr4RETWxIkbJBpVGQ7HY7EbRtJjWSDZtxbUm0krrCxFb/HnxPNXTdbs7xj0zbOf3STZnzswzZ/4n/5zZ/3nmOfPAFcCk7f22/wF8C1g3rc064Gt1+TvAtZLUYox9NGdebO+w/Ux9OgFc0HKMfdPkswLwScoXlb+3GVxPNcnJRmCr7acBbP+x5Rj7qEleDLy8Lp8DHGoxvl6yvRP40yxN1gFfdzEBnCvpvDZi63MR9irgdwPPp+q6GdvYPgEcA17ZSnT91SQvg0aBHw41opgzJ7X7/kLb328zsB5r8jkZAUYk7ZI0IWm2noCYH03y8gngZklTwA+A29sJLWbxv/7fmTeL2jhIxDBIuhm4HLim61j6TNJZwGeBDR2HEqdaRLm88mZKb/FOSatsH+0yqOAm4F7bn5F0FfANSStt/7PrwKJ9fe4J+z1w4cDzC+q6GdtIWkTpOj7SSnT91SQvSLoOuANYa/vZlmLrq7lycjawEnhE0kFgDTCWwflD1eRzMgWM2T5u+wDwBKUoi+FpkpdR4NsAtseBF1PmMIzuNPq/Mwx9LsJ+DiyX9BpJL6IMkByb1mYMuLUuvwN42Lmx2rDNmRdJbwK+RCnAMs5l+GbNie1jtpfaXmZ7GWWc3lrbj3UTbi80OX89QOkFQ9JSyuXJ/S3G2EdN8vIkcC2ApBWUIuypVqOM6caAW+qvJNcAx2z/oY0D9/ZypO0Tkm4DtgMvAO6xvVfSFuAx22PAVyhdxZOUQX3v6i7ifmiYl7uAJcD99XcST9pe21nQZ7iGOYkWNczJduB6SfuA54DNttOTP0QN8/Ih4G5JH6AM0t+QL/fDJWkb5QvJ0joW707ghQC2v0gZm/c2YBJ4BnhPa7El9xERERHt6/PlyIiIiIjOpAiLiIiI6ECKsIiIiIgOpAiLiIiI6ECKsIiIiIhp5pr4e4b26yXtk7RX0jeb7JMiLCLOCJLuqCe/xyXtlnRlXb9J0kuHcLzx+vi9tuaZi4hW3cvsE3//m6TlwEeBq22/AdjUZL8UYRGx4NXpX24ALrV9CXAd/5kLbhMwr0WYpNcBkyo3qju/rRs7RkR7Zpr4W9JrJT0k6ReSHpX0+rppI7DV9tN130Y3Ek8RFhFngvOAwyensLJ92PYhSe8Hzgd2SNoBIOl6SeOSfinpfklL6vqDkj4t6VeSflYLrVNIeomk3cDDlJs//ppyh/Tdkla38UYjolNfBm63fRnwYeDzdf0IMCJpl6QJSc160HKz1ohY6Goh9VNKj9ePgfts/6RuOwhcbvtwnb7nu8Bbbf9N0keAxba31HZ32/6UpFuA9bZvOM3xtgL3UObMXGJ765DfYkR0QNIy4EHbK+t55ingNwNNFtteIelB4DiwnjL35E5gle2js71+esIiYsGz/VfgMuB9lJPkfZI2zNB0DXAxsKv2aN0KvHpg+7aBx6tmOeQqYC9wCbDn/4k9IhaMs4CjtlcP/K2o26aAMdvHbR8AngCWN3nBiIgFz/Zzth+xfSdwG/D2GZoJ+NHACfRi26ODL3Oa5bKz9HFJe4BLKROVbwS+IOmu+XsnEfF8ZPvPwAFJ7wSoE36/sW5+gDJEgdrjPgLsn+s1U4RFxIIn6aL666STVgO/rct/Ac6uyxPA1SfHe0l6maSRgf1uHHgcn34c21uA9wJfBa4E9theZXvzfL2XiHh+qBN/jwMXSZqSNAq8GxitX8b2Autq8+3AEUn7gB3AZttH5jrGouGEHhHRqiXA5ySdC5wAJimXJqEMpH1I0iHbb6mXKbdJWly3f4xy6QDgFZIeB54FbjrNsa4BHgWuoBR1EXEGsn26c8B/Dbp3GWD/wfrXWAbmR0Rw6gD+rmOJiH7I5ciIiIiIDqQnLCIiIqID6QmLiIiI6ECKsIiIiIgOpAiLiIiI6ECKsIiIiIgOpAiLiIiI6ECKsIiIiIgO/AsjB/q6QwDCNwAAAABJRU5ErkJggg==\n", | |
"text/plain": [ | |
"<Figure size 720x432 with 1 Axes>" | |
] | |
}, | |
"metadata": { | |
"needs_background": "light" | |
}, | |
"output_type": "display_data" | |
} | |
], | |
"source": [ | |
"def plot_scores():\n", | |
" fig = plt.figure(figsize=(10, 6))\n", | |
" ax = fig.add_subplot(111)\n", | |
" \n", | |
" plt.plot(*zip(*scores))\n", | |
"\n", | |
" score_window = collections.deque(maxlen=100)\n", | |
" windowed_average_scores = []\n", | |
" for step, score in scores:\n", | |
" score_window.append(score)\n", | |
" windowed_average_scores.append((step, numpy.mean(score_window)))\n", | |
"\n", | |
" plt.plot(*zip(*windowed_average_scores))\n", | |
" \n", | |
" plt.plot(\n", | |
" list(map(lambda pair: pair[0], scores)),\n", | |
" [0.5] * len(scores),\n", | |
" \":\"\n", | |
" )\n", | |
"\n", | |
" plt.ylabel('Score')\n", | |
" plt.xlabel('Step #')\n", | |
" plt.show()\n", | |
"\n", | |
"\n", | |
"plot_scores()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"Finally, we can see how the agent performs:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 21, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"1.895000028423965" | |
] | |
}, | |
"execution_count": 21, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"agent.run_episode()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"We can also load the \"best\" agent found during the training process:" | |
] | |
}, | |
{ | |
"cell_type": "code", | |
"execution_count": 22, | |
"metadata": {}, | |
"outputs": [ | |
{ | |
"data": { | |
"text/plain": [ | |
"2.600000038743019" | |
] | |
}, | |
"execution_count": 22, | |
"metadata": {}, | |
"output_type": "execute_result" | |
} | |
], | |
"source": [ | |
"agent.load()\n", | |
"agent.run_episode()" | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## Next steps\n", | |
"\n", | |
"This environment stabalizes easily no only because the two agents are cooperating rather than competing with each other, but their local observations are also arguably sufficient for them to make decisions without caring much about what the other agent is doing. It would be interesting to try on the original Unity Tennis environment where the two agents are adversaries.\n", | |
"\n", | |
"A major challenge of this environment is the sparsity of the rewards, which can easily cause training progress to stagnate, especially in the early training phase. It would be interesting to experiment with reward shaping techniques to alleviate this issue." | |
] | |
}, | |
{ | |
"cell_type": "markdown", | |
"metadata": {}, | |
"source": [ | |
"## References\n", | |
"\n", | |
"- Lowe, R., Wu, Y., Tamar, A., Harb, J., Abbeel, P., & Mordatch, I. (2017). Multi-Agent Actor-Critic for Mixed Cooperative-Competitive Environments. https://arxiv.org/abs/1706.02275\n", | |
"- Lillicrap, T. P. et al. (2015) ‘Continuous control with deep reinforcement learning’. Available at: http://arxiv.org/abs/1509.02971\n", | |
"- Fujimoto, S., van Hoof, H. and Meger, D. (2018) ‘Addressing Function Approximation Error in Actor-Critic Methods’. Available at: https://arxiv.org/abs/1802.09477\n", | |
"- Ackermann, J., Gabler, V., Osa, T., & Sugiyama, M. (2019). Reducing Overestimation Bias in Multi-Agent Domains Using Double Centralized Critics. https://arxiv.org/abs/1910.01465\n", | |
"- Schaul, T. et al. (2015) ‘Prioritized Experience Replay’. Available at: http://arxiv.org/abs/1511.05952\n", | |
"- Andrychowicz, M., Raichuk, A., Stańczyk, P., Orsini, M., Girgin, S., Marinier, R., Hussenot, L., Geist, M., Pietquin, O., Michalski, M., Gelly, S., & Bachem, O. (2020). What Matters In On-Policy Reinforcement Learning? A Large-Scale Empirical Study. https://arxiv.org/abs/2006.05990" | |
] | |
} | |
], | |
"metadata": { | |
"kernelspec": { | |
"display_name": "drlnd", | |
"language": "python", | |
"name": "drlnd" | |
}, | |
"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.6.12" | |
} | |
}, | |
"nbformat": 4, | |
"nbformat_minor": 2 | |
} |
Author
tomtung
commented
Nov 22, 2020
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment