├── .gitignore ├── README.md ├── notebooks ├── 1-VPG.ipynb ├── 2-DQN.ipynb ├── 3-PPO.ipynb └── utils.py └── requirements.txt /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | env/ 3 | 4 | notebooks/\.ipynb_checkpoints/ 5 | 6 | notebooks/__pycache__/ 7 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RL Implementations 2 | 3 | ## What is it? 4 | 5 | This repo contains a set of notebooks to reproduce reinforcement learning algorithms. 6 | 7 | 8 | ## Overview 9 | This repo mostly serves (self-)educational purposes. thererfore, the notebooks are mostly self-contained and only general helper functions are outsourced, such that all relevant code is in one place. The models are built using Keras and TensorFlow. The repo is build with TensorFlow 2, however since I was experimenting with TF2 and Keras for the first time, the earlier notebooks might contain some TF1 looking code. 10 | 11 | In the process from (paper) --> (implementation) --> (production-grade code) these notebooks lie in the middle. They don't explain the theory in detail (there are plenty of blogs, papers and books one can consult on that subject) but are also not optimized for efficiency and scalability, such that the step from theory to implementation is easy to follow. Heavily optimized code can sometimes obscure the underlying principles of the algorithm which makes it harder to understand it. 12 | 13 | The following algorithms are implemented at the moment: 14 | 15 | - Vanilla Policy Gradient 16 | - Deep Q-Learning 17 | - Proximal Policy Optimisation 18 | 19 | ## Installation 20 | The code in the notebooks relies on TensorFlow 2. To install all dependencies run: 21 | 22 | ```bash 23 | pip install -r requirements.txt 24 | ``` -------------------------------------------------------------------------------- /notebooks/1-VPG.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Vanilla Policy Gradient\n", 8 | "\n", 9 | "In this notebook the Vanilla Policy Gradient algorithm is implemented in TensorFlow." 10 | ] 11 | }, 12 | { 13 | "cell_type": "markdown", 14 | "metadata": {}, 15 | "source": [ 16 | "### Import dependencies" 17 | ] 18 | }, 19 | { 20 | "cell_type": "code", 21 | "execution_count": 20, 22 | "metadata": {}, 23 | "outputs": [ 24 | { 25 | "name": "stderr", 26 | "output_type": "stream", 27 | "text": [ 28 | "WARNING: Logging before flag parsing goes to stderr.\n", 29 | "W0822 20:45:52.838207 4453098944 deprecation.py:323] From /Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/tensorflow/python/compat/v2_compat.py:65: disable_resource_variables (from tensorflow.python.ops.variable_scope) is deprecated and will be removed in a future version.\n", 30 | "Instructions for updating:\n", 31 | "non-resource variables are not supported in the long term\n" 32 | ] 33 | } 34 | ], 35 | "source": [ 36 | "import tensorflow.compat.v1 as tf\n", 37 | "import gym\n", 38 | "import numpy as np\n", 39 | "import matplotlib.pyplot as plt\n", 40 | "import pandas as pd\n", 41 | "from tqdm import tqdm\n", 42 | "from tensorflow.python.framework import ops\n", 43 | "tf.disable_v2_behavior()" 44 | ] 45 | }, 46 | { 47 | "cell_type": "markdown", 48 | "metadata": {}, 49 | "source": [ 50 | "### Setup policy network" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 21, 56 | "metadata": {}, 57 | "outputs": [], 58 | "source": [ 59 | "def mlp(input_dim, output_dim, shape=[128]):\n", 60 | " \"\"\"\n", 61 | " Setup multilayer perceptron policy network.\n", 62 | " \n", 63 | " args:\n", 64 | " input_dim: dimension of input vector\n", 65 | " output_dim: dimension of output vector\n", 66 | " shape: shape of hidden layers\n", 67 | " \n", 68 | " returns:\n", 69 | " x_ph: placeholder input\n", 70 | " r_ph: placeholder reward\n", 71 | " a_ph: placeholder action\n", 72 | " probs: probabilities output\n", 73 | " log_probs: log probabilities output\n", 74 | " train_step: training operation\n", 75 | " \"\"\"\n", 76 | " \n", 77 | " # setup tensorflow placeholders\n", 78 | " x_ph = tf.placeholder(tf.float64, shape=[None]+list(input_dim), name='x_ph')\n", 79 | " r_ph = tf.placeholder(tf.float64, shape=[None, 1], name='r_ph')\n", 80 | " a_ph = tf.placeholder(tf.float64, shape=[None, output_dim], name='a_ph')\n", 81 | " \n", 82 | " # setup feedforward network\n", 83 | " tmp_x = x_ph\n", 84 | " for dim in shape:\n", 85 | " tmp_x = tf.layers.dense(tmp_x, dim, activation=tf.nn.relu)\n", 86 | " probs = tf.layers.dense(tmp_x, output_dim, activation=tf.nn.softmax, name='p_actions')\n", 87 | " \n", 88 | " # setup losses and optimizer\n", 89 | " log_probs = tf.log(tf.reduce_sum(tf.multiply(probs, a_ph), axis=-1), name='log_prob')\n", 90 | " loss = tf.multiply(log_probs, r_ph)\n", 91 | " loss = -tf.reshape(loss, [-1])\n", 92 | " train_step = tf.train.AdamOptimizer().minimize(loss)\n", 93 | " \n", 94 | " return x_ph, r_ph, a_ph, probs, log_probs, train_step" 95 | ] 96 | }, 97 | { 98 | "cell_type": "markdown", 99 | "metadata": {}, 100 | "source": [ 101 | "### Setup environment" 102 | ] 103 | }, 104 | { 105 | "cell_type": "code", 106 | "execution_count": 22, 107 | "metadata": {}, 108 | "outputs": [], 109 | "source": [ 110 | "env = gym.make('CartPole-v0')" 111 | ] 112 | }, 113 | { 114 | "cell_type": "code", 115 | "execution_count": 23, 116 | "metadata": {}, 117 | "outputs": [ 118 | { 119 | "data": { 120 | "text/plain": [ 121 | "(Discrete(2), 2)" 122 | ] 123 | }, 124 | "execution_count": 23, 125 | "metadata": {}, 126 | "output_type": "execute_result" 127 | } 128 | ], 129 | "source": [ 130 | "env.action_space, env.action_space.n" 131 | ] 132 | }, 133 | { 134 | "cell_type": "code", 135 | "execution_count": 24, 136 | "metadata": {}, 137 | "outputs": [ 138 | { 139 | "data": { 140 | "text/plain": [ 141 | "(4,)" 142 | ] 143 | }, 144 | "execution_count": 24, 145 | "metadata": {}, 146 | "output_type": "execute_result" 147 | } 148 | ], 149 | "source": [ 150 | "env.observation_space.shape" 151 | ] 152 | }, 153 | { 154 | "cell_type": "code", 155 | "execution_count": 25, 156 | "metadata": {}, 157 | "outputs": [ 158 | { 159 | "data": { 160 | "text/plain": [ 161 | "array([-0.04322151, -0.0316813 , 0.04285916, 0.03108787])" 162 | ] 163 | }, 164 | "execution_count": 25, 165 | "metadata": {}, 166 | "output_type": "execute_result" 167 | } 168 | ], 169 | "source": [ 170 | "env.reset()" 171 | ] 172 | }, 173 | { 174 | "cell_type": "markdown", 175 | "metadata": {}, 176 | "source": [ 177 | "### Helper function" 178 | ] 179 | }, 180 | { 181 | "cell_type": "code", 182 | "execution_count": 1, 183 | "metadata": {}, 184 | "outputs": [], 185 | "source": [ 186 | "def get_action(action_probs, epsilon, env, stochastic=True):\n", 187 | " \"\"\"\n", 188 | " Get action from actions space. With probability 1-epsilon,\n", 189 | " a random action is sampled, otherwise the action_probs are\n", 190 | " used to get an action. If stochastic, the actions are sampled\n", 191 | " according to the probablities of each action, otherwise the\n", 192 | " action with the highest probability is returned.\n", 193 | " \"\"\"\n", 194 | " \n", 195 | " if np.random.rand()>epsilon:\n", 196 | " if stochastic:\n", 197 | " action = np.random.choice(list(range(len(action_probs))), p=action_probs)\n", 198 | " else:\n", 199 | " action = np.argmax(action_probs)\n", 200 | " else:\n", 201 | " action = env.action_space.sample()\n", 202 | " return action\n", 203 | "\n", 204 | "def calc_discounted_rewards(r,gamma=0.9):\n", 205 | " \"\"\"\n", 206 | " Calculate the discounted future rewards with \n", 207 | " a gamma factor.\n", 208 | " \"\"\"\n", 209 | " discounted_rewards = []\n", 210 | " \n", 211 | " for i in range(len(r)):\n", 212 | " tmp_rewards = []\n", 213 | " for j in range(len(r)-i):\n", 214 | " tmp_rewards.append(r[i+j]*(gamma**j))\n", 215 | " discounted_rewards.append(np.sum(tmp_rewards))\n", 216 | " \n", 217 | " return np.array(discounted_rewards) " 218 | ] 219 | }, 220 | { 221 | "cell_type": "markdown", 222 | "metadata": {}, 223 | "source": [ 224 | "### Show discounted reward example" 225 | ] 226 | }, 227 | { 228 | "cell_type": "code", 229 | "execution_count": 28, 230 | "metadata": {}, 231 | "outputs": [ 232 | { 233 | "data": { 234 | "image/png": "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\n", 235 | "text/plain": [ 236 | "
" 237 | ] 238 | }, 239 | "metadata": { 240 | "needs_background": "light" 241 | }, 242 | "output_type": "display_data" 243 | } 244 | ], 245 | "source": [ 246 | "test_array= [0]*30+[1,0,0,0,0,0,0,0,0,0,1,0,0,1,0]\n", 247 | "\n", 248 | "plt.plot(test_array)\n", 249 | "plt.plot(calc_discounted_rewards(test_array))\n", 250 | "plt.show()" 251 | ] 252 | }, 253 | { 254 | "cell_type": "markdown", 255 | "metadata": {}, 256 | "source": [ 257 | "### Training scheme for VPG" 258 | ] 259 | }, 260 | { 261 | "cell_type": "code", 262 | "execution_count": 29, 263 | "metadata": {}, 264 | "outputs": [], 265 | "source": [ 266 | "def VPG(env, n_steps=10*4, epsilon_range=[0.99, 0.1], render=False):\n", 267 | " \"\"\"\n", 268 | " Vanilla Policy Gradient implementation in TensorFlow.\n", 269 | " \n", 270 | " args:\n", 271 | " env: OpenAI gym environment\n", 272 | " n_steps=10*4: number of training steps\n", 273 | " epsilon_range=[0.99, 0.1]: epsilon decay range\n", 274 | " render=False: option to render environment\n", 275 | " \n", 276 | " returns:\n", 277 | " observations: list of observations during training\n", 278 | " actions: list of observations during training\n", 279 | " rewards: list of observations during training\n", 280 | " total_rewards: list of total rewards during episode\n", 281 | " total_discounted_rewards: list of discounted rewards at eatch step\n", 282 | " \"\"\"\n", 283 | " \n", 284 | " # get env information\n", 285 | " obs_shape = env.observation_space.shape\n", 286 | " action_space = env.action_space.n\n", 287 | " print('obs shape:',obs_shape,'| action space:', action_space)\n", 288 | " \n", 289 | " # setup tensorflow model\n", 290 | " ops.reset_default_graph()\n", 291 | " sess = tf.InteractiveSession()\n", 292 | " x_ph, r_ph, a_ph, probs, log_probs, train_step = mlp(obs_shape, action_space)\n", 293 | " init = tf.global_variables_initializer()\n", 294 | " sess = tf.Session()\n", 295 | " sess.run(init)\n", 296 | " \n", 297 | " # get epsilon decay\n", 298 | " epsilons = get_epsilons(epsilon_range, n_steps)\n", 299 | " \n", 300 | " # setup list for storage\n", 301 | " total_rewards = []\n", 302 | " total_discounted_rewards = []\n", 303 | " observations = []\n", 304 | " rewards = []\n", 305 | " actions = []\n", 306 | " \n", 307 | " # initialize baseline\n", 308 | " baseline=0\n", 309 | " \n", 310 | " # this is to reset env and get first obs\n", 311 | " game_done = True\n", 312 | " \n", 313 | " # rollout training\n", 314 | " for i in tqdm(range(n_steps)):\n", 315 | " if render:\n", 316 | " env.render()\n", 317 | " \n", 318 | " # when game is done, optimize network\n", 319 | " if game_done:\n", 320 | " if len(rewards)>0:\n", 321 | " \n", 322 | " # calculate discounted rewards and baseline\n", 323 | " discounted_rewards = calc_discounted_rewards(rewards, gamma=1)\n", 324 | " advantage = discounted_rewards-baseline\n", 325 | " \n", 326 | " # train policy network\n", 327 | " sess.run(train_step, feed_dict={x_ph: np.array(observations),\n", 328 | " r_ph: np.expand_dims(advantage,axis=1),\n", 329 | " a_ph: np.array(actions)})\n", 330 | " \n", 331 | " # update baseline\n", 332 | " baseline = np.mean(discounted_rewards)\n", 333 | " total_discounted_rewards += list(discounted_rewards)\n", 334 | " total_rewards.append(sum(rewards))\n", 335 | " \n", 336 | " obs = env.reset()\n", 337 | " observations = []\n", 338 | " rewards = []\n", 339 | " actions = []\n", 340 | " \n", 341 | " observations.append(obs)\n", 342 | " \n", 343 | " # get action probabilities from policy network\n", 344 | " action_probs = sess.run(probs, feed_dict={x_ph: np.expand_dims(obs, axis=0)})\n", 345 | " \n", 346 | " # get epsilon-greedy action\n", 347 | " action = get_action(np.squeeze(action_probs), epsilons[i], env)\n", 348 | " \n", 349 | " # update environment with chosen action\n", 350 | " obs, reward, game_done, info = env.step(action)\n", 351 | " \n", 352 | " # update lists\n", 353 | " rewards.append(reward)\n", 354 | " action_vec = np.zeros(action_space)\n", 355 | " action_vec[action]=1\n", 356 | " actions.append(action_vec)\n", 357 | " \n", 358 | " env.close()\n", 359 | " return observations, actions, rewards, total_rewards, total_discounted_rewards" 360 | ] 361 | }, 362 | { 363 | "cell_type": "markdown", 364 | "metadata": {}, 365 | "source": [ 366 | "### Run training" 367 | ] 368 | }, 369 | { 370 | "cell_type": "code", 371 | "execution_count": 30, 372 | "metadata": {}, 373 | "outputs": [ 374 | { 375 | "name": "stderr", 376 | "output_type": "stream", 377 | "text": [ 378 | "/Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/tensorflow/python/client/session.py:1735: UserWarning: An interactive session is already active. This can cause out-of-memory errors in some cases. You must explicitly call `InteractiveSession.close()` to release resources held by the other session(s).\n", 379 | " warnings.warn('An interactive session is already active. This can '\n", 380 | "W0822 20:46:04.030754 4453098944 deprecation.py:323] From :27: dense (from tensorflow.python.layers.core) is deprecated and will be removed in a future version.\n", 381 | "Instructions for updating:\n", 382 | "Use keras.layers.dense instead.\n" 383 | ] 384 | }, 385 | { 386 | "name": "stdout", 387 | "output_type": "stream", 388 | "text": [ 389 | "obs shape: (4,) | action space: 2\n" 390 | ] 391 | }, 392 | { 393 | "name": "stderr", 394 | "output_type": "stream", 395 | "text": [ 396 | "100%|██████████| 100000/100000 [00:36<00:00, 2738.38it/s]\n" 397 | ] 398 | } 399 | ], 400 | "source": [ 401 | "obs, actions, rewards,total_rewards, discounted_rewards = VPG(env, n_steps=100000, render=False, epsilon_range=[0,0])" 402 | ] 403 | }, 404 | { 405 | "cell_type": "markdown", 406 | "metadata": {}, 407 | "source": [ 408 | "### Show results" 409 | ] 410 | }, 411 | { 412 | "cell_type": "code", 413 | "execution_count": 31, 414 | "metadata": {}, 415 | "outputs": [ 416 | { 417 | "name": "stderr", 418 | "output_type": "stream", 419 | "text": [ 420 | "/Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/pandas/core/window.py:1833: FutureWarning: using a dict with renaming is deprecated and will be removed\n", 421 | "in a future version.\n", 422 | "\n", 423 | "For column-specific groupby renaming, use named aggregation\n", 424 | "\n", 425 | " >>> df.groupby(...).agg(name=('column', aggfunc))\n", 426 | "\n", 427 | " return super().aggregate(arg, *args, **kwargs)\n" 428 | ] 429 | }, 430 | { 431 | "data": { 432 | "image/png": "iVBORw0KGgoAAAANSUhEUgAAAbYAAAEmCAYAAAAOb7UzAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADh0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uMy4xLjAsIGh0dHA6Ly9tYXRwbG90bGliLm9yZy+17YcXAAAgAElEQVR4nOydd5gkV3X231NV3RM2zWZtknYVVjmvAsggFAARRZBthAFZBD3YJBt/JhgTjI2NwQaDEznZIBljkm0wKBCMAYmVUM4osLvS5p083V1V93x/3LpVt2JX93T3TM/e3z77zHR1ddXt6p771jn3BGJmGAwGg8GwULDmegAGg8FgMHQSI2wGg8FgWFAYYTMYDAbDgsIIm8FgMBgWFEbYDAaDwbCgcOZ6ALNh1apVvHnz5rkehsFgMBjmgFtvvXU/M69Obu9rYdu8eTO2b98+18MwGAwGwxxARI9nbTeuSIPBYDAsKIywGQwGg2FBYYTNYDAYDAsKI2wGg8FgWFB0TdiI6HNEtJeI7k5sfxMR3U9E9xDRh7Tt7ySih4noASJ6drfGZTAYDIaFTTejIr8A4B8AfEltIKKLAFwO4HRmrhPRmmD7SQBeBuBkAOsB3EBEW5nZ7+L4DAaDwbAA6ZrFxsw/BnAwsfn3AHyQmevBPnuD7ZcDuI6Z68z8KICHAZzbrbEZDAaDYeHS6zW2rQCeRkQ3E9GPiOicYPsGADu0/XYG2wwGg8FgaIleJ2g7AFYAOB/AOQC+SkRHt3IAIroGwDUAcOSRR3Z8gAaDwWDob3otbDsBfJ1ld9NbiEgAWAVgF4BN2n4bg20pmPlTAD4FANu2bTNdUg3zGmaGJxgWEQDAtih3PyJquq2V87ZD1hhaeb7scfPo1LjVsYgIQjDU08xAm5e07bEwMwQDBMDK+fyT+/eCvGvW6us6Od6870mrfwu9FrZvArgIwA+IaCuAKoD9AL4N4CtE9BHI4JHjANzS47EZFjC+SP/x5YlMp5iqe9g/WYdFhEUD8k9txaJqalyP7JvEVMPHaRuWAQAYwOh0Aw/umcSZR45gsGLnnsPzRfg7EYFZiuhDeydxcKrR8pjPPHIEVVuuULhC4LbHR8Pnli+qYOuaJWDISfqJsRnsODhT6rgrF1dhW4RViweweMCBJwRsIji2Bc8XofD/cscoGp5ocrQ0RywbxKblQ/CZYRPhsQNT2DfRwOZVwxhwbFQdC8yMu3eNt3zsVjl3ywqob5ZlEcZrHuqujINbtXgAACCYMe36+PWBaRyzejEGHHnNGcAtjyZDE7rDysVVbF27JHwsBOPmJucerFg4feOI3J8Z4zUPD+ye6NiYRoYr2LJqESq2BYK8Hr5g3Pr4IWxaMYT1y4YANL9B6JqwEdG1AJ4BYBUR7QTwXgCfA/C5IAWgAeCqwHq7h4i+CuBeAB6AN5iISEMnaXgCnhCo2NGysm3lC0Yn4OC8RATHzv46u74AQ04SfnDn6/kMJcNehiDrxJ9nCGY4lgXR5l10QxMZz48fQwjAZ4YfWECtnOLAZCSyVdvCZN3DcNWGY1twfYYTfhTtjVtZxnVPYMCxoPTe9RgEAYIU6l7g+iK8abJAmGn4cIMBqc/LFwzPZ0zUPNQ9H45NYEbbn1s7JE/V7LsGyPF5wefv+qLj1uX4jItH909h3bJBDFbs8HyA/P65Qn4/LcyRsDHzlTlPvSJn/w8A+EC3xmM4vGl4Ag1fBJOo/KPIsoRm4/5LIpjR8AUIhIpNoWAofCGfZ5bnVVZlwxfhBOf76YlDTSZEBF9IQazaFjzBcD0BqlBLoqNTdwUqlhT/lLAFYywzAebR8ARqro+JmouGL0XIFQJEVnCO9o7LkBOt5ws4FoXX0hMC5EvrvO72Rth070DFBuqeD8Fyu/4ZeyISO317r0iKaBlRFUp8Wd40+B0WNsHA6LSLkeEKmOWNlLopkL8zHEte1yL6urq/waAQgnPdE3XPx3jNhRtYI8MDNoQAlg5WUvt6glGxWxe2rPMzK3Fg1D3CoBP/a/QFY7LmyddzNCF6gdjJ8aQnOl/INZuqQ/BZ3vlXLMD1BOqegG0TuE3Lx/VFYLWlLRxWk7MvLcN25rTphocDUw0cmKwDACo2wbEsOBaH52gHda2V0Ic3BoJBxOFNRC9QFo1CuVYFy++iZRE8X4Q3DrrgZd3IdIvktS4jbPqNjStEpou/E9RdAddjeEJgf2Dt+0LeuFCJYH4jbIZ5iS+4pTUwTzCqOfv7grHzkFwLqjoWxmbkfkcsG8zct2IDNdcvXNsqdX6OhKnmIlxHiZ5mHJhsgBFNyOpYussqNcbAcqrCghAshbBiwfVF6NZqF89n1D0fFcsK75QVyl3qCgEhuC23WcNj7Juox84HFvCDMbfv2uLwurE2bJ8ZJAQ8Qan30y3kmmFggSYs3JorUHWk+9XXPmP9s+8Vyc+vzKVnljdQFhE8n1NWfacYr7nh75H1HXhASnhUjLAZ5iWtClvRJKvPFQ1PQK32ZEciyp+tTjD6+X3tjlb94QsWqYmDGZise+Fjz2dYJM8tRP5EJ4R0A6Ea3UEzA3VPyDU9+G1bPg1fyNXvClJBHD5La80X3DGRUNdKGYdty1pwE5FlsQGRNdcLfMHh+1HuW8V0wwORg4YnQotYWcEWUc/GmEXZG5Wa56NiyxufbgnxVD29Ju35jIYng46aYYTNMC9p1RoomhDyjpUV+u0nJsSy6OdQVprri1BUReC+U1GMjp12pzQ8AceWbir1ujyLre778IUTujCnGh7GZqS7darhtx88Eogjw0Y9IWzK+nCDtcpOBA4IZgg/sljadkUiEi/dTSpEEFhidc9tlsQVjIr6HiWEbaruS8vSj6cgeIJhW9mu526RXmMr97qZhg+qklwL7qEQ1z0fU/VyNUWMsBnmJa1OQj5zbuBH3mQpmFPRVZEF0b6wqrkpHXwRWWCOnR6XjKCUbiplu2RdB8Eymq5qW1HUmCswUfNS+7aKcgHpriCFdPWJwAXVWYvN9cWs8stURKGydtVV81WgQw8tNs8X8FX4PiMWYDHd8BEsL4bvV7fqemmxpU5V8tRTdRkMU3Pb9wy0Q8NjHPQakFlixRhh6yM8X2Te6S9EWo22cj0BUQWylpfyLIvkVle7Ay06f8OT6yQ6yjIgImkxBMeL7RNMvMqVkgzwmHH9MIxake2KjIIh5KQowNyZSM6iy84s79aV1dYJwmhAT8wqr5ChXLKBuGW4InsVSu/6cVdy3k2SGo7P8obBImtu19hKKttk3Yu50OcjRtj6BF+0G+fWn3CL86YKmbYoqzJC9muSf9g11w8FTQjODSDxhEA1EZklglBkFamoxpTcx/cZlpM9rqmGF0Ylhq60DEvUD9bU1GTtCwBWb74dB6ca4Iyxt4u6RnVPhInh7SAE4BOH1o9+/QC5dtirSHp102ERoeE1d4GqGxXfyhfBbpA81xwu73UcI2x9wmzvaOeSuudjwGkhwtAXqPs+gHQ4fh4NTwRWhIWqkxC2nNekElR9xnTdBxbLP/KGLzKFLek24iBa0BMC7EWTUzL4wvPlPiRktY2k1VN3RSrXyhcy6di2CJWgSkfNE9JCCdZpPCHATRJWO4Xb4Si4KOR9dvUYZGRptF6VdOX6Pbwt9IUMcqjYFhqeaCoYKj/LtjqfF1Z43sTAelXKqxcYYesTXCFgWf3phnR9xkAL3zRPcMvJtF4wmVhBWSndwsn7g03+YauEaiCKAMxCraGF63FBFJ7rM1xP5pgxcyr4QuUrcWCdJJ/PG2PN9eHYlhS24LGKApRltHq7NtNJZtxI0Gpu++Kmr2VJ12y0vdeomxHplm7+nuRnJ+CL3kZFqu8pha7xhYMRtj7BnaWrZi5peAJ1u5zV5vkilkxbdl3R9WX4tCMIDR+xc9VyBKTm+mENRyD6Q1fn5QxrTQYmRO4txwpKIYnIQgufT5y34Qu4guEguqtvhl69RL3PhifCYAnlxup2Yd9uoU/ks7HZ9OO4/ty67euej7qnEumbj0Stz7pW7wJcFKPTMkhosGJjprFwqhgaYesTVK5SPzJZk3UBy1D3RLhexiwLxS5tImyuL114jUD8kxHTeQKSdKspl6J6LsvS09eXpuoelgw64Rg8IUs3EbIrf3g+y2K4FRs+c8xayUPmjDFsUkLPmvtOJh37zKA+/W50Ct2F5/lz+7eivleuXy7AQkVullmP6zSjMy4oGENWJGy/YoStT5DFcufn7NUsmbru+Zhp5Ffy0N0hKvfL8zkWXVbEdMPHjOtj2LNRdUTKMsyL4EtuZ8jKEFN1L1i3it6fCkpREY+AvNtVgq1cocNVG1SgMiqARJR0t0brNRSeR0VPOjYFQRHz83vRS/RrwOz31XqRp3knev1Rqr8B1xeo9aiWZi8wwtYnuHN8F1rEjOtjuGLDCorPJkUuK/RdR0UTAlERWL1AcDNqrkxIrrk+hip26gYg79ypBGRmzDR8jM644V2/CJKfq7aFwYodFiwG5BqXYOV6UuJtgUV+zy/BUYWMMm4q1+cgDUEKaKyuYCB68/V70Uu6VdqpF3Q6IKe1cwfC5nUuhWM+0J+LNochrlbxfb4xNuNGa2IZlRMEM9yCW1H9D0oIoNaQ62XTda+UjarCu1UIvLpMdU+6+vImvWSwgnpdI3CHMjgsMhy2ztAsNiGk8I1Nuxif8VAPIuD0Wo9ZY1VFjMt+mio4BYhf33YLERsMCuXabvi9TbbuNkbY+oSZRi8DlltDhMISrY0plOgkIwz1fXRhY8giuw1P4MBUo5RLKQq7l9Gj6hXKIsu7E02FOwc/G56sIg5WtRhFbPxh8m+wJqeqWrC+f46whdUxuHzCsBDydXXPj1c4ma9fCEPfoG7CFpIbEjCuyL5BMLectNxt1NqTXPvmMIfIF1HPs7oXhabryOoVfsq6UcKoRKLM3B2Gdge5XUoM1VpWnqsnXd1cLfori01r1aK3VeFo/6RLkVmG/OfFuwgBMLXW9kWwdDkemGzEowiNshlmifoOlUk96SeMsPUJjN521y3DVMPDcLCmpYbmerJOnvpiydBrDvOxVACJLxiP7p8CM7BmyUB4zLoXVYgouw6l5I856kgNIFblPft1icccnVe5NkXginQsuQYx40aWs3I76qeQid0+KjnOEBUBp/q0lUHlyBGJmEjPt++Dof/QczEXEsYV2ScwA9OzSGDtBq5ai+LIxTbd8CGEdJ1O1b2o/FMiCtD1BcZnPEzUvDB/TOVk6a4+cPOCxOpphhRGXaCKau8l3ZxqzU29xhMCB6caoduw5sr3FE0CQS80xMXG9eV1yMLXAkfKziWqTYyXiIDsZfklw8JmgemaEbb+gbueQCkrWpT/hoeVNCDXpQ5ONbBrdAYNX2DHoWk8sm8qdEEmOxjrAR2+cj0mXHtCMPZO1FHzit93uL/yE2rCVlSsVRdUIOqNpo63Z7yOA1ON0L3q+qrwsHy9tBDjAqVbe1moyvjK1VkGPSBFv7M2umYwZGOErU9gjqL8usVEzWvpzs0V8YrqbhCer9bIZL+mSADj1SH0SEgOXa2ymobc7gvZnuXQtFsouKwJjX6d1NpUHr5g7J+UYqxbQ3rF+Zrrh5akF1T9mGn48iYAcdenPpY8BCPsodbKGpvfQoqAwXC40zVhI6LPEdFeIro747k/IiImolXBYyKijxPRw0R0JxGd1a1x9TNlSjDN6viJSboZSgwY8bUmwSqQQ7rpRBg9KEJ3n269qYrsqvNwZIHJ558cncGh6aKqCGqNTY5FuQGFKM6fY5apCnUvHsSia4ceEFNzZeHiA5P1IGITQR5Z3Posgx7k0gwpqlGagMFgKKabFtsXAFyW3EhEmwA8C8Cvtc3PAXBc8P8aAP/cxXH1HV4gOM3mtGQ4eKsIUX6yBQL3WGCl+Vq/K2WxiWDi12saKnefLtKCZZK37PWVbjevkpRzx625HuX+USHjZkmnMrlaFFpQao3r0HQjFJeZhgdmTlX+KHv99comzVDXMtmN2WAwZNM1YWPmHwM4mPHURwG8DfF5+nIAX2LJzwGMENG6bo2t31ABEc00p9aYXXkl5ubiqQtf3VW9wziowhF35QFy3UpZGzVPBl94voitfYlAgJRIZLnb8t676wtMBDXuRBBooqxB6YosfkcqQbXI0pLuTRE29gRk6S1GVPpLfy9lUG7bVki6cw0GQzY9XWMjossB7GLmOxJPbQCwQ3u8M9iWdYxriGg7EW3ft29fl0Y6v5gKrINmsuMzo3xIQhqGFJipnICL5MRa93wcmnJDV516arrhh+7A8RkvXFequQKHpl1Mu348KTsIqZdlfbil8kg11481kBQcBaao8P8iwqCMAsFQEZ/KcgLyhans2JsJbuY4jLAZDKXombAR0TCAPwHwntkch5k/xczbmHnb6tWrOzO4eU6tIYIIvuL9ZltiiVkGkIzNZK9n6eHzh6YaECwtsmi7fM4LKockqbsy6GK67sfWinwhX+NmlOMKx4b0pM4cVf3QicSMM0t86XhBGH2RXqh8OD2Cci6COIymGQzl6KXFdgyALQDuIKLHAGwEcBsRHQFgF4BN2r4bg20LnjLNFZUV1mxim7WwQa59NTyR2cIislw4VrtSBX9E1kz2IJQ4y5JVcfed18RSU7lybuD6VOtbWcKlLMvJetyayxuT4GKhUudj6Ot5xcc1GAxzR8+EjZnvYuY1zLyZmTdDuhvPYubdAL4N4FVBdOT5AMaY+clejW0uybOOdKJw9mLVqrvFhZKbJfQyy5YodU9gup4tuCoHy9esl2Ttw2bRm8n1LFVfsci64mA/FcBxaNrFdN3PFEMGMD7jYqZRPpimaD9fyP/JfDWDwTA/6Wa4/7UAfgbgeCLaSUSvKdj9OwAeAfAwgE8D+P1ujWu+USaEX02hzebSuucXBkEU1YNTYfuHphqYrLuZa3UqN01ZZ2o8YSh6cPhmVmhSFJQlWGSxqULLe8frGJtxZU6Zn+3yVMnerVBUocQEbRgM/UXXakUy85VNnt+s/c4A3tCtscxnyvRAilWbEAwrp6ln3RO5hZKbJfd6Qmgh+tkuTeUyVOtN+rFtK51/Vub9qNfL1jDNLT0RWJWOZcFn2SutzDmakew+YDAY+hdTeWQOGZt2Cy0FxUQtmryL9q+5MgBj30Q99ZyyyPLckb4W/JGHcgNmNbf0RXkxyRI2JZp5qDU2lfagEsDHZ7KFrdW1xqLAFYPB0F8YYZtDxmtu0/Bw5niNyEaBhef6stxTVk1JZWWp0ldJklXqs0alkq1n3HRTwlaqYqSjGLlpDh2Dw9JbStyKOg+3arF1u6qLwWDoHUbY5oiwOSeKc6iSFtp4TrCJqvYxUfMy15dkcEcUwRg7hy9dmPrrkoEqUe+xyGLKOkcZ8tycRS9XblIOcvVUBGcerS6JFRVLNhgM/YURtjlAL/qrahHmkRShvMk8jJxEQeUOzg6EUInduvWYPMRUw4/yuTKeV++rDFmBGGXW5fyYxcZhxZHM/VtUtlaSwg0Gw/zGCNscUPP8MC8KKK7an+rynLOf2q4m/eTELoK8L5/T5aOEaF6lY6LmBvsFicqZVmHuy1NjSY2/ibXHiLpxqxuCovOVWbs0GAwLEyNsc4CXsNiK+qwl5+c8AdC3q8k/eZyDUw1ZrFhw7HXKCqtpjUD11zc8IVvaQHdFZo21veARoHlemC84CBQpt5bnm2AQg+GwxQjbHOD78ei+vRlRjOG+yTJSOfvFLLas/VhGNY7X3NgxlRWXXlOLxNL1RVgbUb02S8PKxmtkCVMzsWp4eoHn5hVW2qnFaDAYFgZG2OYAoQIgOGrImUdWEEf2fsFPcI7FFrnvdBFRofvJozKicXmaEEdBHu0LR6vrXwDCaifNXJCKmRKlygwGw8LECNscoE/QKrovT7CSIe15lopasYsiLZPPq9fH18dcXw4ieVwVBamOrcRYiOyIyFZoZ/nL9QX8oIeZGksRJnzfYDh8McLWY/SGljExypmnnxidiT1mZFs80TGDQJCEsinhFIk1vYYnckP31WmUCzKZJN1LVN8z1brFBIcYDIY8jLD1GFUVnxlhlCHQfO0sfJwhWvHn5bFriYAUdRzXj3eoVpGGSXHT92FElUtU4MtcyEroShXFATcGg+Hwpmu1Ig3ZuEHvL1UNXwmEYIaNdA3IrLWyLF2LWWzg1BqTSuz2fIZF0TGVUCWVyhdRQInqe0Ykgir3rTUD7TSm/JXBYCjCCFuPaXgCQsT7mQEFa2eJ7bISvgBgx/dDJEJZOWmqskbDF7AtAjODiMLox+TplYUmj63GrqIkuTD3rtvUXQHKrgNtMBgMxhXZaw5MNcI6h/p6W34OWDp4pNBiS/xURE1ARWL9TApYUgj1lIDksZKiPBfM8ekNBsM8xghbj6m5fmh1CVXnCq1ZbIXdnhM/vaDjtODoPHo6gAo2Sa5ZeRlBLgrZk63oXRoMBsPcYYStx0wHNRdVlJ8Sj7zGmFkuwuS+NdcPgz2EJkhAJFD6Nr0QMrMMIJlOCpvPaZXUnjONNw0Gw3zFCFsPCSv6M4eCo/LUmhU3VuhiqKi7UR6c4ChJO/766EWCo1Y4DEbdE5lJ4nm5YkbUDAbDfMYIWw/R3YHJWoaj043M1yQTt/VoRUXd82NJz0rc1DkZSD2vgkmEkBZYXpSjEHnyZjAYDPMTExXZQ3TPXlJIyuaxAenKHWMzLkgLE9QtKtVgVNfCWMFkcG7DTpUzZwI1DAZDP9E1i42IPkdEe4nobm3bh4nofiK6k4i+QUQj2nPvJKKHiegBInp2t8Y1l+gRkMkoxFbEIxk80vAF9mmFlOPRlur4nPm8qiqSOV5kR2AaDAbDfKabrsgvALgsse16AKcw82kAHgTwTgAgopMAvAzAycFr/omIbCwwlCAJUb5eYpbgpZK2E+ITF650pRDlqqx7PhqJSiTxc8tAlaI+bQaDwTDf6JqwMfOPARxMbPs+M3vBw58D2Bj8fjmA65i5zsyPAngYwLndGttco9yDcdLiklcYuZ4INMls3NmkDqWMhoynAqSPETUoNRgMhn5hLoNHXg3gu8HvGwDs0J7bGWxLQUTXENF2Itq+b9++Lg+xs+juv5SsteCKTOacZXekDn4iHiWpv8b3Gb5Ir/fpx1BVUgwGg6FfmBNhI6J3AfAAfLnV1zLzp5h5GzNvW716decH10WUFaXa1jQjb+0rWc6qyK0pmFMWnhxL8woiqnWNaQFjMBj6iZ5HRRLR7wJ4PoBLOPK17QKwSdttY7BtQREmY2dEZGTJy2hQuDjvONHjInFKV/pX26cafqHAqjSBmmuEzWAw9A89tdiI6DIAbwPwQmae1p76NoCXEdEAEW0BcByAW3o5tl4QlbkqZ7FlWVpFx41tC6Id656fbbExYyxHOPVjFK3BGQwGw3ykaxYbEV0L4BkAVhHRTgDvhYyCHABwfZB39XNmfj0z30NEXwVwL6SL8g3MvGAbbgkGRInO2H7e2lfyeBm+SJVSsG+ijiWD6Y9Ztc4pwlcRlUbXDAZDH9E1YWPmKzM2f7Zg/w8A+EC3xjMfKHQZ5nSxzkMIhmVR8No0PjPqroDrZ3ebzgooSR3DZwjTHsZgMPQZpqRWDynSkcyQ/IJ9VbPNZFURheczJuoufJFdLksGphQrmyeEqQtpMBj6DiNsPaRVl16ehcccddHOO6YvGBM1L3efvPw2nbEZNzd522AwGOYrRtjmCa1YbEDU5qZoH9WKJqsljiqOXIRr2tMYDIY+xAhbLykMrc9Pss46TNRPLf+gKv8sS5z0slu5YzLltAwGQx9ihG2ekC0y+RVBuITFFu2f3svLaH9T7uwGg8EwvzFta3pIq53NinRH74DdjCyLLa+MVvL8JtTfYDD0G8Zi61OiNbbmyjObZTKTnG0wGPoNI2w9pLB8VYvBIyoqstt1HI2wGQyGfsMIWw8pzGNrIXgEiCy2qXp3C7SYoEiDwdBv5K6xEdFLil7IzF/v/HAOX7IttuLKIwC6HrXYLMDEYDAY5htFwSMvCH6uAfBUADcFjy8C8FMARthapEgkWnX5qYCQbrsKjcVmMBj6jVxhY+arAYCIvg/gJGZ+Mni8DsAXejK6w4i8Cv15KFdkt4XHrLEZDIZ+o8wa2yYlagF7ABzZpfEsaIprRaarfBRpiuiRxWZ0zWAw9Btl8thuJKLvAbg2ePzbAG7o3pAWLqPT+f3PBMuiw7ZllzqWV6LyiMFgMByONBU2Zn4jEb0YwNODTZ9i5m90d1gLEy+jc7ZCiAyLrcDGi9bYOjM2g8FgWCgUChsR2QBuYOaLABgxmyUFuhZYbOVVSrkgjcFmMBgMcQrX2IIu1oKIlvVoPAuaZuthrpZs3axIsev3Zo3NYDAY+o0ya2yTAO4iousBTKmNzPzmro2qz2FmEKVbTzfToIaWk+b6onB/zxcQJSr0GwwGw+FGGWH7OkzOWkvUPYHBSjwIhJkL19iAeGFiwVxojQkGRmfclgsrGwwGw0KnTPDIF9s5MBF9DsDzAexl5lOCbSsA/BuAzQAeA/BbzHyIpHnzMQDPBTAN4HeZ+bZ2zjvXeDmW1lTDR80tFraaG5XH8gVnNgjVOTBZNxabwWAwJGiax0ZExxHR14joXiJ6RP0vcewvALgsse0dAG5k5uMA3Bg8BoDnADgu+H8NgH8u+wbmG+M1L9OKmq57TV9b19bYBEe5anlMNbpbJ9JgMBj6kTKuyM8DeC+Aj0KW07oaJQSRmX9MRJsTmy8H8Izg9y8C+CGAtwfbv8QyKevnRDRCROsSieF9weh0A2uXDqa2z7jNRUhfY5Ouy2Jhq5U4pgHYdWgG/+9rd8S2nXDEErz3BSd39bxfvvlx/Ned8a/wJ19xNpYOVQDIz++tX70dh6ZdrFkygL0T9di+a5cOYM94Ha972tG4+IQ1qeNP1Fxc8y+3AgAWDziY1G6eHIuwavEAAIAo+A8KfsqNFgAQYAXrwQTgkf1TsXOsWzYIx7ZQsQhjMy4OTDVizx+xdBAWAUQEKziWZREIiH4S4YE9E5nX6GnHrsL/Prw/fLxiURUVO70+3Yw94/XUNnX9dEaGKxhwLMiRyesCqGuC2PZwFJsyB3kAACAASURBVCS3RvsgtoZO+nGI8PDeSQDAkkEHb7/sBPzpN+8uHDshXrxh7dLgc2syxtg4tc9QH6caU3KcAFCxLVz11M3YvHIRAOAnD+/Hf9y6M/y+WETxz5Vk0NqvD07Hxr926UDsuxWeL/hekHZu9ZwVjMPS9wXhyfEaxmfS+b4blw9h56EZAMBvbduED11xWtElLSVsQ8x8IxERMz8O4H1EdCuA95R4bZK1mljtBrA2+H0DgB3afjuDbX0nbI2cosRuicae+hpbmSafC9kNeXCqgcUDDqpO8+I4T4zO4PYdo3juqesyn/+nHz6c2nb/7gn89Ff78dRjVoXb7nliDH93w0P42MvOwHC1tR68Nz96AEcuH8a6kaFwW1LUAOCWxw7i0hPl1/7qL/wi3J4UNSCarD/9v4+EwuYLxru+cReeGJvBFWdtDPedTHgEPMFYPzKIgYoNsMyJZJYTKHMUdCTvnRiCgZ2H4hMWAEzUPJy4bgkankiJHiAn75WLq2BW68Ly+EJ7XOR50EUNkJ/7Bceuytk7m5mGlylsWUvao9OuPD5Lv0rw9sProyjazsGT6nqGzzHHrtFEzcNHr3+w6fiTV+fQlItztqxIjTE4azge1g7QbJz6/qrK0d1PjOPeJ8ZDYbt71xgOTTdw9lHLU5+l+nn7jtHU+B3LwlErh1PfLU5974Lng9eJ4InwdUCmqAEIRQ0Avrp9R0eErU5EFoCHiOiNAHYBWFzidYUwMxNRy1MzEV0D6a7EkUfOv8pevkg7IoUoDgRR6FVEDvcw/jd85TacsWkEb7/shKb7/vl/34vRaReXnrg2UwizJmQA+PubHo4J29du3YnJuodH909hfMbDeUevCO8om/F3NzwEALj2defj5kcPYPdYLXO/ZYG11i6j0w08Htwx/+CBfYX7Xn3BltBqK8NXt+/AN365K7Zt5aIq3vrM4wEAV37656nXPOeUdXjKMSubHnuy5mG85uKP/v2Opvu+8aJjS45Y8uTYDG77dXrCPXXjMtx0/95ZH78VPnL9A/jFY4fCx8kbjiQWpYssLBuqdHWMgIwFeOXnbol5feqej5WLqnjTxcflvu7VX/hFyvt0/tErcMXZmzoyru/dsxtf+Oljsz5OmVqRbwEwDODNAM4G8AoAV7V5vj1BEWVVTFl963YB0K/MxmBbCmb+FDNvY+Ztq1evbnMY3UPdlcS2IW6N5b4253cA2DdRx5OjMzgcUNcv6+4wC9Vsde9EtpiUpWLLP4fv37MHH7/pIdx4X3pSbMau0Rn83Q0P4bpf7Mh8/iPXP4jP/qTMEnU2lhUJ7UQtv0QbAFTt1totjgynRXeqUTwxV5xywr940MF6zZrtJJWc95mMTO4FTmIs9SaNgLPGWPJealY4tgXbotj46q6QFn4BSwbTtlCr37Minn3yEfjLF5/adL9myzBlRnSQmSeZeSczX83ML2Xm9K1bOb6NSBSvAvAtbfurSHI+gLF+XF8Dsi22MqH+SWJuBWa8+bpf4q0l7nYXAsnSYs1QE9sff+3OWZ1XfUZ7xqVAZglHwxM4MJl2eyn2ZbgUk9yQIZhDJSdh3a3XLHiojBtXZ/lwNbWtWSPbVie1pxzd3LprFcfKVoKy17ST5I0lj6zveq+cNYMVK2GxCQw0+c5kCVtSzGfLhsQN0IaRIfzZC+Pr4XkeEUWZEX2OiH5FRNcR0RuIqLmcAiCiawH8DMDxRLSTiF4D4IMAnklEDwG4NHgMAN8B8AiAhwF8GsDvlznHfCTybUcIBqZLRDDmfaG/e/fu2Q9sjvB8gS/+7DGMN7EuFD94YC9e+blbWjqHHmxw5ad/nrJshyo2jlg6iD+8dGvhZHffkzLAQd3FWhmT1N98/wG88dpfxrbpbuO//p/7S435Sz97LPY4y0WZDKJ4cnQmde4iWhWdLGFrFvSUZy3l8caLj8UXrz4XX37teanJql3yJtbhavqz7qR1kUWr16OZRddNBh07brF5ftOboUUDaWHr9HuoOhaOX7skfPwXLzoFW7XHALB7vFjYyuSxXUhEVQDnQEY0/jcRLWbmFU1ed2XOU5dk7MsA3tBsLP2AWhDVSS7slkG3++7fPd6Bkc0N2x8/hP+5ezemah5+v8S6wad+3LqbLvnH+JffvQ9/f+VZsW1nHTmCc7eswLlb5Nf2kz/6FX744D6Mz7hhlKKi5snJ/N9+sQMvOG09bE3g7to1BiBeXaZZWkYW6mZl2VAFn3jF2Xj3t+4GEh+zftipuoeP3/RQS+fIEuYilme4IpvR6kRuEaEauC87ZVHlRVFmufmGMsSuk7RqsWXRq6ILgxU7ZbGtXFR8fRZnCNtME3d1O7yvyU3PnibCViaP7TcA/BGAdwF4HoD/wgIRoW6gIoGS28q/PhnyhNjE2m+okSej3zpJ8i58/2QUkv6jB/dhxvWRvKdUovTBDAur7sZLm2Whu5CaJdK//sJj8DvnZQc6TQeTQtYkrJ/jtV/aXsrNqTjryOWl91UsyxC24xN3yknaCc1XdEpkKlb2NJb1d9Nt92SrQn9ERmpQr0i5It3mrsgsYSvjjeoEepDS3owoWJ0yUZE/BHArgL8C8B1mbhTvfrjTmfqN+iGcnD/cfkCfXHYcnMamFcMtvX7X6EzK557ksQPpMHXFdb/4NQCgnnCpqWE9kRGQo7vf/vEHD2P74zLK7a3P3Bpu9wTDCebIojXB87aswIVbV6Pm+vjyzb/O3W+4xITbbE3txWduwG9taz86zbEs/MOVZ2K85oXXR8/JrNiUSltppSNFkk6JTJ5lmmVJd9tiayb0X3nteeFN1c5D03j8wDT+4QfpdJReMODYsWpIdV80dUVecOwqfP/ePbFt2zYXOu86xpsvPg5vuuhYOLaFczYX37iVEbZVAC6A7Mf2ZiISAH7GzO+e/VAXDr5g2BbFcltmGn7Lf0jM6aiofrbY9Ennbf9xJ6593fm5+2Y1Tf1//34Hvvza80qH3SdR0ajNXp+XXqFEDQC+c1cUz+QLxpdvfhzX37sHJ6/Pb36hJv5md8KLBmY/4bZ7jXRWLh7AypwUgdM2juBW7XoAzd9XEd22nrLWfgYr3b1JbBZIoSd2b1w+jCebBEF0k8GKFUu4r7t+06jIrWuXFP4NdxsiwrKhSmaReZ0ya2yjQQmtTZBh+E8FMLtknAWIYIYNAge/C8GYqLsYqtptWXD6a3S/fV7ngPnKh7/3QNN9brxvD5YMVnDmkSOZz/uCYeXcCXs5rkIA+MN/uz03j0hdQiU8ZdIxdOFwfREmYN/260N5LwnH1+wzu/LcI/HYgWlM1T0wA0uHHGxeuQg3ZuRh5Y+v9K5t8aaLj8VdO+Uao8+MJYMVbFzemgWu0+oaYKtk3awsGeju1FVp8T1l3Yz0Kiry7l3jaPgilp+YlaTfjzQVtkDU7gfwE8gajlcbd2QavfEns/zDn6x5WLOktcXgsDqA9hrdYpuq+1icEXI7F3i+gG3RrIX2Mz95FADw2au2ZT7vC0bejWStICJLj5xKe6UoPDYQ5cIVce+TUXRHWcFpdm3UuJYMVlL5O9feku+6zKLbQjHg2F13O3XSorrkxDW4a9cYDkzWUXUsOJaFV//Glo4dP4ssi21kuII/v/yUzCjNuXTGZFVJejSnmEG/UWaGPJaZ5y4mtQ+ouT48wRhwVEkZWWmkTH3IPPSJWP9jef9/34sPvbS4nEwvGJtx8fp/vRVXPeUoXHZKdimrLB7eO4Fj12QHJDwxmu2WKVrHSa6dKZLrK0k3Z1Jv8kqh5fGzXx0otd/rnnZ04fNHr1qU+1yr9wudcEX2mn98+VkYm3EhmGERYfWS8pVSmjHg2KUq13SS1RluXNcTuRVgMvs2dnxU5VnTwes/l5S5PTqWiG4korsBgIhOI6I/7fK4+orxmhtziQlOJliXP5YSRj0JWPfC7Tg4P1wFKkHy/351ADfevwd1r5yIF5XLefe3sovFFoXT51lst+2IuweTR0hOJ3kCmUfZtaUVi9K5YTpvK5h4KTXKYvpxKXbFoiq2rFqEY1YvxpZVizKj7vqJbZuX4w0XHYtXX7A53JZVFF2R9ZllrTV3g9dfeExqW79ff0WZd/FpAH8M4JMAwMx3EtFXAPxFNwfWTySLj4qgMn9G5H4pfMGxig8lln96jrJwHts/hc/sncRn/vdRnLdlBfZP1vGe55+MqmNlrnEMJYoL/9V372t6rjIW22uftgWf+d9Hw+1/+/148dmkOCaF6dMtlrlqtaoHAFx8wppU7cKiiSRPPIerNk5Zvwy3PHYwtr2fg4wWCkSE3wiKOJ+6YQQP7pnA6Zuy144BpHIoAeDMNlI12iErb3E2a6bziTLCNszMtyRM5s5n5PUxvohXtHZ9Eeu/1sodGCO93lMUINFJfv7IAXiCwz/MIlR+ly46Nz8qJ9qrPn8Lrn3d+ZnrVncHCc6KO3eOpfZJUhROryy2tUtaywd68Zkb8Z9B8MdHr38wrDpSlqwKFsnWMUl+8+yNmUV583juqevw2IGp8LoqCOncuSOWDuLCrfOvdmqveOlZG/Eft+0MH7/snM4U5Z0NRywbxBHLir+Xm1cuwh8/63jMuD6OWjkcvq4XTNTS39X5cN06QRlh209ExyAwPIjoCvRhO5luEraWCNgzXse6Nr+czGlhK1s7cftjBzFe8zJ7d5XhYzfKyhZlhG2mSU7Vjx7ci+/clV0K7ME9E7j3iXHsL6i5qHPLowfx7JPXZi7MqwTTZgVvn5aY9PU0jKTlU4Y7EwJ9xdkbsWTQwef/77Hc17Rq5VUdC39w6dZ0VX2KvhNve/bxPbvDn89ccfZGXHH2xuY7zkPOOmpuPr/kzdmAY3W87uNcUUbY3gDgUwBOIKJdAB4F8DtdHVWfMFX3MFixIQSHa2OAnHR0MWrVkziq9STyBZeOwPvboPdTO8LWauHhqSbtOD7xo3zX3nu/fU9L5/rXmx/Hd+5+Ev/48rNSz33+/6T7sdma16kb8nPNsrjm6Ue3VN7rJWduwE0PxD+nZLWRTiXaE6IgiyxXlsFQhm2bl+ONFx0L1xcgknmKC4VCYQv6sG1j5kuJaBEAi5lb89ksYKbqXuiSSxY/9rm9CiQMjllDSdfdxuXdaf1RVLFeMTbjYv9kHZuWD+Or27PbsnSLg1ONzMolqnxWp1qUXHDMSkzWPVxwzCpcf++e0uHPRBTewVx0/Bpc8/R0NGTFJpy0bmksbaBdXnHeUThj4wiOWT3r1oiGwxQiarmpa79QeAsZhPm/Lfh9yohaBLNskX5oWoYqpyr66xZbS1GR8fqEA1pez8nrl2JRi52dy6JXafjsTx7NXNd7/b/eij/95t342I0PNi3vBLRXVFexdNDBx377DLzy/KPCbTfevzcWkKJbme1UwLggo0HmleceiXc850RUHQt/+eJTcdrG8paeGk1e1D0R4d3PP6nlcSaxSLop58qFZTDMd8rMBjcQ0f8jok1EtEL97/rI5jlTDR+uz2GlCE6kYfuc3JLNYwemYtaSSu5W6CHfFdtqOd9KcfuOQ/i6trieRBfTG+7bg1d+7pbcAsBZ3YqzOLqJNTFUsfHcU45IbT/7qOV4/+WnYM3SQVx28hE4do08zvfu2Y3f+czNuPLTP8ef/ec9eMVnbw5fM1Cx8J7nn5SZF5ZXn/LEdUtjjy89cW2qnFRWqsHVWih3ctxLhyq47OT0e9J5bZAkfElJl/G1rzsflwXX6aR1S/HmS/I7HBsMhnLC9tuQ62w/hiyGfCuA7d0cVD/gBhZOWBsyMf/F1qxy9I2Z8c6v3xXrsaXKcUXHicSlalu5EZLNXIN//T8P4N9v3YmDU9lFY971zXQOWZmIxST6pP6sk9YCAN7/wpPxD1eeid9OFOidcf3MBNULt64Oc38si/DSs9JBAffvjpwHZ2wawYBj48R1SzMtove9INtKesoxK7FeC/KpZpTtqjqRi/OkdUtxwhFL8KyTsoVr+XAVn3zF2U0LPV9y4lpc+7rz8domyds6rzzvKHzmVdvw7uefVFib0mAwlKsV2d0aNH2KK9TaWhA4gnhYfywFIEfZskLDBccDT1Q4/YVbV8P1Raq6uuKbv9wV/l5UT/INX7kNb710K07duKzputRIG65EvWHmaRtH8C+vPjeMtHrRmRvw44f2xQq/ZqVeHbcmbuk1q5iuuwsHKzYuOGYl/u9XB7DtqOV4zW9swXCO+3a46uBvf+sMfPnmx/Ffdz6ZGRH2uqdtwSd/zDj/6BW4cGt70aadwLIos8mjwWBIszBiO+cAJT7KYkv2F3X95o7I0el0V2mZ4B093nlItlV55klr4QnG7vEaRqfTVlfMDdokwvEjNzxYqtNzs+Osz0hpUD29Vi2WFTeSYqG7N8/dvCIlwCPDFYwkOjk363G1P9GnTI1669olqWNlMTIk98nKuxsZruLtl52QK2rnbVmBtxjXoMEwrzDC1iYitMaym4vqE3he8MjYTIawJebWf/n54wBkHcBfBPlW37r9icKxlVmH0115Ou98TlTiSX8PE7X0WF94xoZU0Iay2PIsy4a2/Q8uPS5lsdkZlmYzYTtxfXytTJ2hbNPHJUFR6az32Iw/uHQrzj86HYRiMBjmDiNsbaLcjjJKjzPrQxZVHJlueGH1dj1RMq8vmOeLsDp4s47NZSrV57Fy8QAuP2M9gPga2x0Z621V20oV3l06WAnHm4Vam3z5uUeCiFKvz+o03cwVue2oeCyTavPjlOzuHAmbKahjMCwEcp32RJTOhtVg5ts6P5z+QS+h1UzEsvj7mx7GI0GOlG7t5QkbA1hUdTBV95smR7/lutvx5kuOw9klwsH/7Rc7cMN9UUfcqk14ytEr8a3bn8C373gCV557JA5NN/CPGV1+q46FZM7x8uEKLAJeft5Rqf2ByJo8d4sUo6Qr8inHpPNqiiyvozICNVQDy7ICvz7o0K1KGpXhr196WlPBNRgMc0PRavTfBj8HAWwDcAdkmbrTIKMin9LuSYnoDwG8FnK+vgvA1QDWAbgOwErIyMtX9kPftyhwJO2O5MRPQE62/3nnE3hybCbcpgU+prr+rls2iCfHajh2zWK84vyj8JHrH8xMyiWKxLbhC3z8xofwxVef23T837x9V+yxY1uo2PH3cefO7PD+qmPh7COX48cP7Q+3DVVtfPm1+R121bqdClwpU7c3T9heef5RePpx6fqIyrIt2zZo7dJB/M1vno61S8u37DiySeSjwWCYO3JvhZn5Ima+CLIu5FnMvI2ZzwZwJoBdea9rBhFtAPBmyIompwCwAbwMwF8D+CgzHwvgEIDXtHuOXiI0yy1J1rZv3r4LX7t1J/aMRwEPumtxOpH4bBHh3C0rYBGF0YJZlsLpiXI47ea7CcEpIckrjzXgWHjd047GeVsiV2DZeojKqlKh6y85cwM++JJTM/fNs4w2rxzObLqqoiCnm1i2OhtGhjpW8spgMMwtZf6Sj2fmu9QDZr4bwImzPK8DYIiIHADDkOJ5MYCvBc9/EcCLZnmOrqK0SFUdEcwYn8meSHU3ZV6AgiqdlXSfTda9sLWJEpyswAzXF9iS07Qyz026dzzd2HNkuBoTkiIXa8WWRVOveurmcFtZcVDnOP6IJfjSq8/Fb27bhKNWZo8/z2LLS2m46IQ1OGPTCJ532vpSYzEYDAuLMrPQnUT0GSJ6RvD/0wDubPeEzLwLwN8A+DWkoI1Buh5HmVkpw04AG7JeT0TXENF2Itq+b9++docxa5TbUZXAEswZFe/TopAXQf+B78i+ZLHiycwYm3EzhC1tjXk+Z7ael/vLYzoJv19SIC86fg1si2JWV1EvNLXf8hIh9Yo3XXwsztw0EhOlZtGLec/npSMsHnDw9stOaNrk02AwLEzKCNvVAO4B8Jbg/73BtrYgouUALgewBcB6AIsAXFb29cz8qcAtum316vnRf2qy7mHG9bFnIm4BlXVP6uhuyW8ESddK2FSU33W/SFcZ8YRICZeqYHLj/TI45CWJCh7p6Er5WBeSvLJaQDya888vPwVXa5ZbHk89ZlVh1+gs8hpoFo3NYDAcvjQVNmauAfgEgHcw84uZ+aPBtna5FMCjzLyPmV0AXwdwAYCRwDUJABsxi3W8XpAM7Z+q+/ASFlBW8EgzdCvkUJDArSpw66HxSReh53MqGfobQWDIDx6Qlm1SIJJuT1U+ShdI1+dwPSyJbtkdu2YxntWkRuJsOWPTCD79qm3h43a6JxgMhoVPU2EjohcCuB3A/wSPzyCib8/inL8GcD4RDZP0R10CaQX+AMAVwT5XAfjWLM7Rc8r0M3N9gR88UNxbTQ/3F8wYGapkutSSbkRPcMpie3T/FL52607sODgNAHj2yWtjzze8uOtUiaXuJvR8kRuI0stw9y9efS7++FnHY/GAg3dcdgJO3bAMp20yNRMNBkOaMq7I9wI4F8AoADDz7ZBuxLZg5pshg0Rugwz1tyAbmb4dwFuJ6GHIkP/PtnuObpC0kMoYC3quGwDcV6IPlx8rgMyl3XCeECmLjRn4j6Ci/+IBBwNO3PJKphZkvae6J2Jj+sLV54S/J5Oru4nMmZPnO33TCP7kuSeaKEaDwZBJmaqqLjOPJSLQZuUEYub3QgqmziOQAjov8QWHa1x1r1x+VFIM86L44q+JfvcKhK3hC+gxhK6fttj06h/Kutq0fAg7gvqTSWHT8+NO2bAMd+8awx/9+x2xfdT62+kliigbDAbDXFBG2O4hopcDsInoOMgctJ92d1jzD93T6PpcqtKI2kO5F1v13PkZASEKfX1sx8FpHJxqpPa1rHTkof4+dGEbrtp4+nFR1Y/nnHxEqns3IK20a1+Xn4BtMBgMc00ZX86bAJwMoA7gK5Dh+W/p5qDmI0kXYRmU9j0xKi2kWkaJp0UDNi47+YjMAA0h4uKkowvb2/5DZl8kw+J1C0y5DV9/4THhtu/e/WT4+1Erh2MWZbLO4ovO2IAPvfS0zLEYDAbDfKKMsD2Pmd/FzOcE//8UwAu7PbD5BDPHyjMJUaY3dvTamitF6J8S9RZti/CZV52DgYqVWdewmSsyib6vTQRPq9W1O0jGPnbNYmxcLmsjPn5gOnw+KazVhEguGrCbNtA0GAyG+UAZYXtnyW0Llsm6F+s83ay6vo6e4HzKhngU34ag+G7VtuAzx4QIKHZFKjeiLoj62p9jU27rmNdckI79GUgIWzIQpShR22AwGOYTRdX9nwPguQA2ENHHtaeWAjis+nu4Psd6p9VcH9P1cgEkutsyGWzxJ8+VlclUPljDE3CqVuy1SYvt9Rceg0/86FeYDFqs3HR/lD7w3FPW4eZHD2Ki5qFiWxmVUCRZ9RWXJLozJ0P5y7pfDQaDYa4pstiegKziX4MseaX+fxvAs7s/tLnH8wWmGx58EQ8W2TNegyvKVb3Q89KmG/H7AdWUU1ldv3jsUPz8GcKmWqt89IYHAQAHpmQxZdsirBsZwoevOB0ffMmpqNiUmzOXbA76wtPX47fP2RTbtjghdHn91QwGg2G+kWuxMfMdAO4goq8EFUIOOw5Nuxiq2mFrGkCumTU8RsnmzDFLJ1m5X6HcfD+4fy8u3BqVCcvqcj2QOLFK3r4iKJe1bKiCZUOVsGpJFlUtn+2IpYO48tx0c08luopVS8q3dDEYDIa5pMz0fC4RXU9EDxLRI0T0KBFl9zFZYNRcX1bvRzrZuqxrTt/P8xmnrF+a2uei46WYPV0TtbyGo8m2MCpJWX9tFnpDzsFKdIzzjl6RtXtqje3i49cUHt9gMBjmC2Xy2D4L4A8h3ZDlFpYWCHXPhxAOBHMoNK2uNMWETQgMVtKX3AlzzKJ9VbDKyxPWVDLJW0Vr5lX2V7z5kuPC3/WIx5cmCiPnUSa53GAwGOYDZYRtjJm/2/WRzEPqnsBk3YNFpFlsrUmbnrsmg0Es/ObZG7F/Mmo0qiIf9SLKU0GTzLVLB2PHWz4cuQj3jNcw0/BBlF43S6ILXystYwwGg6HfKCNsPyCiD0NW4Q9nY2a+rWujmic0PAHXZ1QdKQSt5K8pprQuzn5QqDjZPka5E/VwfxWqn4xOJCJUbQsNX+Dvb3oIx6xejOGK3dSiUl2lDQaDYaFTZrY7L/i5TdvGkB2vFzSCVYdsDh5zqjVNM+puJFZ5CdeqyoeeK6aKHBdZVMzSFTmU4YZ84enr8e07nsBnXrUNk3UvtTbXCv/48rPafq3BYDD0mqbCxswX9WIg8xOWVlqgN57gVMh+MyYTFluWsNkZrsgiYbMtAnyACJhp+BjKsMZeds4mvOSsDRhwbCwamJ21ZjpRGwyGfqLpjEdE78nazszv7/xw5heCZcj/4gEpOFN1b1aJylk90wBZx9Emgl/CFalDRJhu+BjOqDNJRKk2NToblw9llvHSGRmuYLQgbcBgMBjmI2Vu5ae03wcBPB/Afd0ZzvyCWVpZyurSf2+Hov5qjk2lXZFKXAnArw9Oh6W5WuHDV5zedJ+/ueJ01NzDKhDWYDAsAMq4Iv9Wf0xEfwPge10b0TxCra0pIRE8u5qJRcJmW5TpisxaG9Otxsm6hwf2pBO5O8GiAWfWbkyDwWDoNe1EFAwDKJf81OckJczX8tnawSsoauxY8Wr8tzx6MNyexNeCWQwGg8EQp8wa212I5ngbwGoAfbe+xsyoe6Klrs9J3RifcVsOHtEpdkVaMYtt++OybmSlIJpxKijE/KIz1rc9JoPBYFholPEzPV/73QOwh5n7rrq/Jxj7J+vYuLxcT7GsRGzP59xWMGXHYFvZQiUttvSxk33RdNR6n+mTZjAYDBFNXZHM/DiAEQAvAPBiACfN9qRENEJEXyOi+4noPiJ6ChGtCGpSPhT8XD7b8+jIfLTy+2ftW/f8lo6Rdf48i80iwr6Jemp7spO1jkr+TlbiNxgMhsOZpsJGRG8B8GUAa4L/XyaiN83yvB8DT8DoVwAAIABJREFU8D/MfAKA0yGjLN8B4EZmPg7AjcHjjtGJ5ajZWGsq4CNvjW33eA0P7JnAvolabLtdUFFEjcYEeBgMBkNEmeCR1wA4j5nfw8zvAXA+gNe1e0IiWgbg6ZDFlcHMDWYeBXA5gC8Gu30RwIvaPUceLVlsHT63Wj8rssAApNrNZJXKOnrVotjjoRbWDQ0Gg2GhU0bYCPGq/n6wrV22ANgH4PNE9Esi+gwRLQKwlpmfDPbZDWBt5mCIriGi7US0fd++faVP2qsAwprrY3S6kdquwvfzXJGKJk8DAN71vBOxanFUDaRZAWSDwWA4nCgzI34ewM1E9D4ieh+AnyOwttrEAXAWgH9m5jMhE8BjbkeWkRuZUsTMn2Lmbcy8bfXq4h5ksdcF/5KInLy0Vqv4K9759bvwe19O14f+ycP7AQCHptKil0S5LS875YjM54erDvZPRseZTR1Ig8FgWGiUCR75CICrARwM/l/NzH83i3PuBLCTmW8OHn8NUuj2ENE6AAh+7p3FOVIIzgsIEaG41Vw/tKxUKH2r7B6vZW5XOWfnbllZ+Pr3/9e94RhWFtRofK4mekWlswwGg+Fwo0zwyPkAHmLmjzPzxwH8iojOa/a6PJh5N4AdRHR8sOkSAPcC+DaAq4JtVwH4VrvnyGKq7mWagJ4QmA7KRu2bqKPm+php+NgzkS1Q7eD6AtMNeY6Ny4vLX7k+h8Lm5KQGAMCSwagvW1E9SYPBYDjcKOPD+mcAk9rjyWDbbHgTZHTlnQDOAPCXAD4I4JlE9BCAS4PHHWN02s10LwqWFfIBab25PmPvRA0HJpu7DMvyl9+5D9/45S4A+XlpeqfsMgWQn3/aOmxeOYxlQxXT3dpgMBg0ysSJE2uKwMyCiGYVX87MtyPe301xyWyOW0TDE7CttMtOViTx4fkCDU/A8wUmau3lnyuBVMdVgnP/7qiWo5UTHXLsmsXh756y2AqSsx3bwl+95LS2xmkwGAwLmTIW2yNE9GYiqgT/3wLgkW4PrNP4OfEozMDBqQb2TtQx3fAw1fBjXa9b4YcPRsuCrea8bV4pQ/htItwXCGHVuBgNBoOhZcoI2+sBPBXALsjAj/MAXNPNQXUaXzB8wcgKgGTIQJGpugfXZ0zVvcz9yrBUW/fSCxqXYahq47SNy7B51TA+8aNfAUC4LmcwGAyG8pRpW7MXwMt6MJau8ci+STQ8kRkVqbysozMyMbrhtyZIOmMzUXJ1wxMYTgQ1XnzCmsLXOxbhV/ui9ncnrV/a9lgMBoPhcKVMVOSHiGhp4Ia8kYj2EdErejG4TjA27WImiHpU+WGuJl5KyFRlkNl0yNY7Uh+YauD/Ht4fO15ezpwiGQW5dslg22MxGAyGw5UyQSDPYua3EdGLATwG4CUAfgzgX7s5sE4xXnPDnLS6J39O1T2MBOZUzY1baF4b9SD3jtfw+MHpmGD+6TfvBgDc++R4uK1Z/7RkVZK8QBODwWAw5FNG2NQ+zwPw78w81k/h5bqYKBFzfQ57o83GQlP8yTfuwlTDxwtOW5d67qb796JiE1yfcf7RxcnZeQWSDQaDwVCeMsL2X0R0P4AZAL9HRKsBdC57ucvUvbhFJoQM76+5flAVv31h++B378PWtUswpeXBZbFmySA2jAzhzCOLO/HoFluzmpIGg8FgyKZM8Mg7iOhDAMaY2SeiKchK/H1BIyE2rhAYnXZRtS0sGnDajoCsez7u2DmGO3aOhdumMqIYN60YhusJ2CVC9/XK/0XtagwGg8GQT66wEdHFzHwTEb1E26bv8vVuDqxTJF2Nh6ZcTNQ8VGwLa5YOtl31f8fBmdS2yZqb2nbyuqW49fFDpdyMenftP3zm1vYGZjAYDIc5RRbbhQBuguycnYTRJ8KWDNjYG9SAVJX+mwV05PHub92d2pZVscQTAp4QpYRN7XPSuqU4Y9NIW+MyGAyGw51cYWPm9wY/r+7dcDpP0tWoIiSZZemqTiZBT9Y9bFw+hJ2HImvO0wJVmqH2GZ3pXJ1Kg8FgONwockW+teiFQTubeU3DE6k1NgWzTMp220jIVkndyajKiZqH9SODGBmqhAnfnmB4ggsr9StUKa8nRvsmNsdgMBjmHUWz7ZLg/zYAvwdgQ/D/9ZD90+Y9RW5GhmwP044nUrkcX3HekbHtM66PSqJwsSdEaYutliPCBoPBYChPkSvyzwCAiH4M4Cxmnggevw/Af/dkdLOkSLSY027KsqhKJkPV9OWr2laYQEAkXZFeSWFT3QFWFDQYNRgMBkMxZYogrwWgL/o0gm3zniKLTTA3LXGVh3JvZvVW04+4qOrACwowOyXC/WdcaQn+/jOOaWtcBoPBYCiXoP0lALcQ0TeCxy8C8IWujaiDFMlWu9aaJwR2jcrgkKqTFra7do1h2ZCs8j9ctcMyXmXy0irBOpxMHDcYDAZDO5RJ0P4AEX0XwNOCTVcz8y+7O6zOkNUxW+H5AlON1vuuffGnj+GG+2TftSxhA4CLjl+Nb97+BBYPOKi7zZuGKl7/jGPw04cP4KgVwy2Py2AwGAySUqYBM98G4LYuj6XjFBllrs8Ym04nVOfhCwYRcOvjh8JtlQz34svO2YQXnL4el5+xAR+9/kHsn6oDKFcHcvlwFc/LqDdpMBgMhvIsaJ9Xs4jHZu5IzxdwfcZQ1cYrPnszLty6OiaWVdvCcNWO5cJdfsYGAMBgxYZjUxi6n5W83QoWSasvL31hIWERUHGs0NotQ9Wx4Pki9zOtOhYsAiwieMFNiuul91cFq1ulYsvjMsugIfk+CBbld1MnAgYCq7/qWGHwkKrwU7UtTDfSjW+rjgXBnOpEUXUsVG0LDV/I70uJFJMkDNWUlwFQbB2ZCKgFgVOObcEXAlXbLnVcP/hjZJYNf9X18nwZWKXWwwccC4TWy8m5QsS+LwMVCxXLQt3zY9d/uGrD6nK5urrnh2lA+mdXdQi2ZYXPqYCyuuun1uZ7gWBO5fEubrIMYlmyJi4zh9/TShvfszyI5PWzLQuORcFSTvQ9bPh+rqdMZ86EjYhsANsB7GLm5xPRFgDXAVgJ4FYAr2Tm2WUqz7Jw/ys/dwsA4B+uPBMA8KMH92FkKOqS7dgWPvnKszE+4+HAZD0VzahPLPdp7WvaoeLIP9R+FDaLyq9pUiBq0o1b/uMfrFhgx8KM60MkuqVXHcKaJQPBfnYoIAem6qm2RYMVG67f+k3IgGODfAGAw4lzsGJjwLFwaLoBlS5pkRQuzxewLcKGkSFYFmHV4gE8vHcSri9QsS0MOBY2rRjGvU+MxxrYOjZh0YANz2dM+F7Ma7BsyMHigQoansDiQaet6NrdYzV4QmCm4cO2CEevXhw+5wvGg3smYJEcAwBsXF7Oba4CtRq+wO6xGpYOVcDMeHKshkUDDmquFIPj1i7GgFNOLHX2TdTx+IEpOLa8wdkwMoS1Swfx8N4JjE67oZgct3YxhrssHL/aN4npoBDEZN2DRVLEFg9UsGTQwVTdg2URFlUdHLFMjnGq7ofif+rGZV0dn2Km4eP2HaOxbSetX9o0gvvXB6aD1CbCYMXG+pGhjo5rvCbLHg44FuqegBCMTSuGMVn38OToDJYMNv/85tJiewuA+wCoNtF/DeCjzHwdEX0CwGsA/PNsTsCzULbJejS5vfHaaElx+aJqlHztCziWhRWLqpmTiF74+DmnHNH2WADpyuzgjVFPcWxCwyv3WTgWwbEIFdtqSRArtgWCbCIriGI3AESEpUMVuL7AgGOhYsvnq44VEzbbojB61bEptIiqjrzL1u+0kzVIiaS4ChE9X7EtEBEGHDtsaGuRvPucCY4zWI0mcWn9M6q2Fd6VDlQs2VdDuz5DFRsNEpii6K7fCs5TdSw4djs2j7qOhIptgzndD9C2KLIwS6wZ66hjDVo2Fg04GA7e96IBJ3yvs7lpsy0Kx8fM2vsnVB0rLMTQbWsNkFan53NohTo2wbHk93mwYofb1U2JY1kYrMj338va51mR2mVOP1S14TMH35XOT0pLByvhOAacyMKt2PI7XuacczJVEtFGyP5unwkeE4CLAXwt2OWLkNGXs6LdAscA8OHv3Z+5XTfVlze5I1bramdsGsF5TXqxNcO2qC3X0lwj71at0m141ATlBD/LnkOJoXqtjhKD4ar8oxis2Bio2KnJWX+tRRROMoMVq1RLoWpwfulyIlRs6YocqtqhYDuWnGgrdvC7bYXjqDoWHMtCxYneeyVx7RzLghMIXyXYX06M8r05tjx+mW4SWTi2PP5AxcpcQx4IzmO38PkkWTLohO97uGqjEgiSFOT2jqlcr+FkS9F2eZNEsc+0mziWFXyW0Xep6sibHMeW38VqcJ0BBN+Z2V3TdsiK1C7T3FhaavLzy/qOdIJK8B2vOhaWDEovWSX42yhzzrmy2P4OwNsgK5sA0v04yszKTNoJWeVkznhwz2Tm9prr45QNy/BHz9yKwUqxy+TOoKXN/sn6rMfjtCAO8wknmMChrSEV7h+8T8emwIff/O7ECSYzK5gY1A0Nkby5URNNhaPnlBDqyIklmmyY5XrbgGPB9aPGtI5FSDpJKZhAAelK9IWIWVON4FxyPBS+T30MjjbBhZOiFd1Z+4JhWUDFIlgUCSIRwklgtusd0aRhZ0YVV20LPpUrEZeH/nej3icFVna7wkMkPRpS3BAKJJG8xr202CxLXkfB8lzqJkd9rlbFhuuJ8Fqr74n8/nV9eNo4KfwbAVD62ju2hQGOfu8G+k2nFf4tyBvBMp9hz00AIno+gL3MfGubr7+GiLYT0fZ9+/YV7jv73thxjlwxjIf2TkIIbipqAMK1kU4Im21Ry+6f+YBFFI69zB+OZUWvketR5c9RCYRNubbUpKmEQU5+8r8TWDc6BN09ROEflBMsZKvLn3WDQZBirO66bSu46wzWywYC4VGiHd9XnwCtmJUm35eVmPwovKOV10lOmIMVKzx2mYIAWeguvaxJK7QsbGrbKsw6n5r42z2iWseKboqC41N0bHWtuo0duJudxOeqbmiq2menP29b8dZVvYByfi9C3YCFN61dQP2dJi3IwYpd6pxzMVNeAOCFRPQYZLDIxQA+BmCEiJQFuRHArqwXM/OnmHkbM29bvXp1uL0WBA10iqNXLUptGw/6ramk62b87lM3AwDWLZv94mplFpNVtyhjQVIwseh/yOl9ot8jkbLCCbsZ0j0nxUpNKmp8Re6dpMVmaa+1iEJXjZos1aSYNTkSIZjMrND1ZVuEJYMVLBmsYMWiKpYMOLCtSCiTLhU16ak1GUBOkkoEo8eWNlFG73HQibs826FiRe9BrafpKPfTbM6ho39Gyl3XDurzCq0i9dklbiB64fSwgxsnNYZK4Ha0tM9mIHAjh2MPvAXdEoo8dOEoe+3VjUjSTd4LBgrmEZ2eCxszv5OZNzLzZgAvA3ATM/8OgB8AuCLY7SoA32rhmNg1OoNp109tb5e1SwcxWIlfntEg7+30jeV6pT3zpLW49MS1eNX5R7U9DoUe2DBfKPUFC9bYKgV3d4424YQWSjAhlfH5KxeU2r/ixIWt6Lw6RAgtOctCOBHqa355Qqleq0+kFcvCUNXGUNXGyHAVQ0GouVovTVpEloVgDcEKA4XUjYFNcu1OTY5qLSk2aVvSfav+t4OlHS9rkV5tV2tWs0Ufv/49aBUihNdGt/xsonAdUgXzdBv1t6q+J1XtJkT3GESu6vh3rJfopyt7apvkd7vqWKUqKnUSp+RnOJ/y2N4O4Doi+gsAvwTw2bIvrHsyh6Xm+k3zMFo55shQFbvddAuZsiWvLCK85je2dGQ8FTu6w5svlBM2gh79ZVucGVHoBOsgTjApKHeHYEYzR25osalJP5wwCI6Vvw6QvJ7KhTfo2LCJwJYUFnU3bVsicBdlv081OSmLIXkjEhOiDKGXkXMyok/98apzW5bAANmxyU/+lNeoFy42OUa5vtep8+nHKTtpZUGIhKOCyO2tPoOK3TvRiH8+MrK1yBJT+zk29V4oLAtsl+9AApQLMJlr5lTYmPmHAH4Y/P4IgHPbOU7DFxDMGJ1uYNXigej4sxhb3fOxbKiC3eNS2NYvG8QTY/L30+egu7Wa8Jvup4Wpd5sybhMV2CCEtEZckQ6VVwmYzDJEXlkEZe9g9TU8dSdMFEQ22giFLjV+W63jReH5FUtGBNoWgYEwqVu5j1w/EpGBSuRKWjzgyKRjX92Rp8ceupwo25VnWwQ7sdJha9dBuSGTE6eF3k3alkWw2l4JS+ME1xlApuuzLOFnTwSm6MZABdOoNcleoL4fygIdrjigglNb2ufZa2FTN1t1T3QldH+uWBDvZPdYDRM1D4emXXg5jUMn6x7+7D/vyX0+yT1PjOPBPRN46VkbAQCnae7HMgmCncaxm0e8zXZxXLlEylB2zU+JjnIXDjp2KiDEIil6yg2m1uOqTjkrdeD/t3fmQZZV5QH/fXd7W++zMjNsE3EZUBFGggWalCyisRyTUAWJC9EQq4ym3MqIZVKJxqpoYhk1sVxKkoDiFjCCK+JIWTFRZFQcBRwZNbINMmGZhZ6e1++9kz/OOffdt/YyPW/r71fV1e+eu7x7T58+3/2+8y0u40YcBMSBjxmy5sSsW3Uzfl2uEIf1NbJQUrd9b97zpqVc1KgxeXf1YhK6Re0gnVzbmfK8CdKbOeNFTLTeK9IK1tA9X13YeQ211yaslSKr5R6Lc5RI3eHHh1kAGVNwzEQ+7n6RFaLuxBKwZabIRCFiPGd/2h7v1gfjNubp400SWacj7+A0KgySKXLZ+GBqGxRZb88usf3ZtbsAuHXPfi7a1r3qzke//Qt7PvDSMzcxVYzZfvI0X7/zIaB3KW88frJdKH4jDqWrlpp17W17fiYYciEWaxpNF/RN3UPw8NFKmmXB3pdfB6mlz+onurmg9UWk2UW5kITpW6/31vJOGn6hux3eWaOQhMyWKy54O0gDspPQamJeICUhzJarqdaUuOMFH9gslKvOqzAMW4RNdu0lDha3PlH/7jANa0jNlCJIxlw6jGRNj8eirAj1NUdj6lqT19KSHr7De008CQOKcbigeTXVwI+jl2EnfJaXao1lZXwZVEZCsGVZKNvIuvFcw/bnd93HdDHhom0beOCxI/zsoYN8++f1MIIoDLjwaY2CsNeTSCEO3Zu7/cftJHyy8Trt8G+07XIwes3KBDAf1NJMG1lBKGKF+my5QiEJO77hRaFQczZ7r3UZahScdrP/8NGGZ8i7YODACTjvHecnquxx81WTlgMqxHb4Zk1M3h14spAwXYw56jKMtCMMJL2nA2FAzoVw5OOQSq1GMYkYr9ZSM2cuCjg0V0mDvb2G5+9VsibGuP13pv0R1hY1jrxgNrG9fiGTqSQMhcB0d5BZLdg4QtsPxvS3L1IzaCiYRQjU1AJAH7wM3Tit1gz5RDW2gSU7EbcTcs1pe/7zRzaqYL5a45Pf+3XDPp8jMovPkbdSBLJw2qgkkxg2FwUtiUvr1+r+5h44TaEWmpbEvHEoTBZixnIR5WqNQ3MVDhyZb+gvnyOwagyBWJOsPd4mmjUGJgoRuShgohAzXUw4PFexgqsmqSkoCRsTHOeiMDXdZb0Ox/MRk4WYXBTwyOEySRTYnJDGCp/JQkwcCsVMTKGPL5wpJYzlIsYwXU1cJ0zmCcQm4fWCeqoQU6mZBoFYwF13LKEQh6wdy1E1Nljbm8Gy39/p5bcQe5f8xY2jOAioRsbmopTG4GZ/v+ExeEGOCoHYvIWD1AuLdYbxJusoMD3/O04VbPak8dzinUeGgdEWbG0Extx8e6HQLNTedslTWDPWqN194pXbVzwlTyEJeeJo97g4P3mCndg6CbZstoNO1wkDaZvoNwyEXBykE+dkIWa2XCFbss47gthJXCjlIsbyEXPzdvG5XKkxWYip1axm59NX+VRC6X20xG+5wOQmLcavN8yUEuarNSo1k2osR8pVCkmYOlS064vFOAt4D9fxSpw+exAISYd/ci+cJwoRh+cq6brmYtdG/CS22HEUBOLWAFtPGCXT0bHSzvFmEFiKsOjHy0mvHGp6zegJNgxz81UCkTTjR9bsNZcJru4W53bmidMtbcejsnUuWliw+XUc6D4Q4zCg0kX98y7ognCoaV8UBGlsStUYTKXW4ogi+GBTyeRnFAKx2oRgc2kema+Xlmh3v3EYMFWMqRmrOfq1ozhsv+6Uj60DwFy5lr6Vz1dN27RY2edZCkuNEQykHs91vOfT1a6NKcpSGTnBVjO2zlAU1OtgPXyoHos2V65y/2OzzJSSrkKgV/gg4G63kmTSG3VzIIlCIagssF/aO1PELkWSX6+pSOtkLyLphO7XlPJxmDrtCHYNaL5qUoHT7o01DgKSXMDasRzlSo2jrsZSpdZ5jcGamuphAHHQugbX/KxLYTnB70FQDxdQFGVwGEHB5gsaSqqR+WKfAHOVGm+9fjcnThc4/7R1ba/x9M29qYcEuJir7u6KWa1kIY0tkE7rb/Us57Fzhsh+ZVZbikNbBymrPYUujss7mXhBkI9Djpiq09yc52MHxwmPd1/3DiiV2RpFl/G80/MFIkRRPX9kN6EGi4uxy7LU5ME2Ts79/QbQBKYoq5mRE2wPH5xj3XieR2fLqRa070C9oNUXnbPIfY8dSYtPnrFpgp8+eJCNE3le8ZyTeerG8ZbrHg+SqJ5GqJsxMjvZd5uAo6CzE4FfW6u57PRCPYDdx1RlA399iqLs+V74edMhWGeIcqW+NiYuHqcbPs8h1FM4dVvXAmvt8w4U7bJ6tBy/RC1qqdkUfEopFWyKMniMnGB75HCZYhJx36Oz6RrRwSPzqZt8Vi/64M57ALjyuVsRYM1YrmeeQXEobJoqEAXec6qzxtauhEPba0ZBx3xvhSSkGIcYrFYbBELNmWoLsd2X1XKCJgHlC3SGoZDU6jFsPltC2CQEu9Fc32xR+SCdCbVm6vWt+onQ2keKogwGIyfYagYeOjiHMVA1hj0PHeJLu/cB1lW/naNGIQl7lpUAfNYOW3l7rty9BpWPz8lud7xuEDCWi3g8tm762SDmzVMFpooJlWqNw0crqY7hC1MWkrDBs8+XQfEUkzBNepp3AtIThkJYkzSPzUJmwLFc1LCmuKhAZddnxliXaOlz0hxbAscMRd48RVltjOTrpo+RKldq/O2X7gRg3VgujdloJt9jt+l6GiFb6bfb1LimlDRmZ6CzcLPrYCEbJ/KNMVhxXWj5Csn+Gj4AuDnYOnEBy965ZSwXpcHThSbtzmtsXkAtNNk3Z8pYjJacLUi5mO843vi1RkVRBo+R/s/Mrq194LIz08nQ10kD0gKQvaSe6LeeZLcTm6cba7lJFzNcIJBPggYTWSA+wLuODWi2Ld5lvnlNylerzce2NHvBa2xuPSwbKFwvO7K8flyMgBhEIaLekIoymAzebLGC7HPZ+C85fSNBILzrJafzh2dt4XkZb8jX/s5v9fy+0ngyp7lk0yQ10+yY4OPHOl3XJxz2ZkSf7ikrPL3DBtRzK7bDBzmXcqHNDtJhIvc5E+NoeRP94mquqRBRFGVxjNwaWxaf8/H809YC1jX90rO3NBzTj7fuKKxrVGFgM3g8crjc9tjm25MuJUrExVSFGa0uW9Ili3ebz0dhR20oCusFGqOgu800q8EpiqL0k5HW2LauKwFw6ppSx2OKXbSl40UhDlPtJnYZ5jspJC2CTdpn1fBCDequ+QAT+Ygkai0M6b0M4wVMsb48TVbLUxRFGWRGWmN74miVUqakSZbrrvxt7nzwIGf0MBgbSB01sk4Qft2q1qZAaEtaK2mt4CxN5klfHgVsGrBC3FpCxRbGtC78xS5leKIwSDW61Z5BXlGU4WCkBdsDjx9hqtjeEzIQ6WmGEY93tMhqSaFIR42tnaYVNmlscSicOFNs+I5iEhGFQimJmCy2hjKsG89xaK5CKYm6amLZZMKqsSmKMgyMtGB77IkyW5q8CvtN7NztswIrDKVjkHazKPG5EhuOEWH9eD7d9hWgN07kKXYosxO6rPELJXYuxCEFX4pFBZuiKEPASAu2o5XqwJU79xpbQ3kWV/okSyAuk0jQ2t7sRdnsrRil62txx+z3zVpjJ8Yzgevq3q4oyjDQ81lfRE4UkVtF5C4RuVNE3uDaZ0TkFhG5x/1urRvThlrN8OgTZd7ztbu599HZhn22evJgeev5gOdmwdaimQXCWBttqu6+X2/rlHS4k7YGVvNTOaUoyijSD3WmArzFGLMNOBd4nYhsA64CdhpjTgN2uu0FeeeX7uTKa3bx4/sP8LYbdqfZ/cFmHhk0ja0Qh2mGfU/UwR2/k8dm1KTxFTq42nfS1vz1uwWGK4qiDCs9n/WNMfuMMT90nw8BdwObgR3ANe6wa4CXLuZ613z31w3bV92w238PRyu1BUuo9JqJfNxWU2oWQoFYx492RGFjUPdyYsikQ102RVGUYaevs76InAI8C7gN2GCM2ed2PQRs6HDOa0Rkl4js2r9/f0N6LLDlaADKVZsvMjdgqZjiqH2Zk2bN0ocAtL1GEDQcv1xvRV0zUxRlFOnbrC8iY8ANwBuNMQez+4ytENq2josx5uPGmO3GmO3r1q3jLRc/mbFcxEdedlZ6zF0PHuCbdz0MMHBrbM3rY57mWLIk6lJNOhOAHbZxPFEURVnN9MUrUkRirFC7zhjzBdf8GxE5wRizT0ROAB5ezLXG8zHX/uk5HJ2v8eJnnMCXd+/j775yd7p/rtKthGfvCTrErGU9FEWsp2M393rv+ZjrUoNNURRlNdIPr0gBrgbuNsa8P7PrJuAK9/kK4MalXvs3B+da2s5/0tpl3OXxI5D2JsCsy34SBUyXkq6OL36fT3elKIqiWPphijwPeAXwfBG5w/28CHgPcJGI3ANc6LaXxGXbT2ppWzuWO8bbXTmkg1ADkMxfIg4CxvNR1zWwQmzLyCSCtQoqAAAMJUlEQVRhoFWcFUVRMvTcFGmM+Q6d88RfcCzX3jxdYMeZm7jxjgd504VP5ozNE8dyuRWn21pYdl8uDhasP1ZMbFb+yNVMUxRFUSwjl3nk8mefxOXPbtXcBoFuPh5Za2IxCRf0WBQRJgpR+llRFEWx6Kv+caBzQuPO5yRhQBIJSSRMFlqTFrdjLNc9gbGiKMpqRAXbcaBTDsZumpU1KYZpmZnFsGYs1zGIW1EUZbWigm2ZiHQ2LXZKZbVQvFkxiSglUerKvxim2pSkURRFWc2oYFsGgdg0VoU4bCvcvCt+EjUmGl7IaliIw7aJj7uh62uKoiiNqB2rA0kkzFcNYSBUmipbl3JRWqEaYLZcDwKPwnqCYpvDsUq5Ys9fSGMby0dLFmyKoihKI6qxtSGJhA0TeSbycdv1rg0TeUKXod8X6vRJiQtxmAq2YhKlwiwMZEGzoQo1RVGUY0cFWxvWj+fZMJGnlAtbtKwoFEo5646/abKQCisfd1ZI6ufMFJNU4CVRsGinEEVRFGX5qIrQhAisG88Rh0GqQR04Mk8SBVRrJlNPTVyiYivYfAmY8VxEpWaIQiEXB4znY6o1gzFa/0xRFKUXrHrBFgZCtWbXwERgPB+l9c2mignVml1nswmJDWO5iFwUUEisydGLqji0bT5w+rHZMkkYpDFph+cqDetyiqIoyvFhVZki/dqX/x0IrB/Ppd6Ka0oJGybyDceHgZCPA0q5kEJshZ6IsH7cHhcENlt/EgWsKSWEgVBKrHdjEAg5l88xFwUtpWkURVGUlWdVzbT5OOCJo1VmSjGH5ipMFxM2TuZ5/Mg81Zph/XiesXxjlxQTGzC9YSLPo0+U2+ZlLOUiZkoJInC0UkNEUq0vDm1dtQ0Tec0SoiiK0gNWlWAr5SJmy1U2ThZIwjLrJ3Lk45A1rkTMRKE1o34hCdkwmWcsF1Gr1b0fs6wft9eZr9ZSJ5JsLTWv9SmKoijHn1Uj2OJQOGmmyOzRKmO5CIFUq9o8VQA6BzsnYYCIUMyFbYt/+hRagUhq3I0zabXiUDSQWlEUpUesGsE2no+JwyB1zy9lYsYWKtSZNSu2w6fACgMhdO4k2bIzC5WgURRFUVaOkZtxvWKURI3Cyqe52jJdWPHvbBeflq1+vZTcj4qiKMqxMRIzbjY+bOvaEmvHEk7fNJnGlkF9bex4mATbOYWo6VFRFKU/jIQp8uSZIrPzVR49XGa6lDBTSojCgBOmCtz7yCwA+UizfiiKoqwGRkKwTZcSpoGNTS71XmNLooCceiUqiqKsCgZutheRS0Rkj4jsFZGrlnJus0lQsG72W9eWUgcQRVEUZbQZKMEmIiHwYeCFwDbgj0Rk2/KvB2vHci1B14qiKMroMlCCDTgH2GuM+aUxpgx8Ftix3Iv5WLVObvqKoijK6DFoM/5m4L7M9v2uLUVEXiMiu0Rk1/79+7teLBBpmylEURRFGV0GTbAtiDHm48aY7caY7evWret6bKw10BRFUVYdg7b49ABwYmZ7i2tbFsU4XDCriKIoijJaDJrGdjtwmoicKiIJcDlw03IvpkJNURRl9TFQGpsxpiIirwduBkLgX40xd/b5thRFUZQhYqAEG4Ax5qvAV/t9H4qiKMpwMmimSEVRFEU5JlSwKYqiKCOFCjZFURRlpFDBpiiKoowUKtgURVGUkUIFm6IoijJSqGBTFEVRRgoVbIqiKMpIIcaYft/DshGRQ8Ceft/HkLEW+L9+38SQoX22dLTPlo722dI52RjTkg1/4DKPLJE9xpjt/b6JYUJEdmmfLQ3ts6WjfbZ0tM9WDjVFKoqiKCOFCjZFURRlpBh2wfbxft/AEKJ9tnS0z5aO9tnS0T5bIYbaeURRFEVRmhl2jU1RFEVRGlDBpiiKoowUQyvYROQSEdkjIntF5Kp+308vEZETReRWEblLRO4UkTe49hkRuUVE7nG/p127iMiHXF/tFpGzMte6wh1/j4hckWk/W0R+4s75kIhI75905RGRUER+JCJfdtunisht7jk/JyKJa8+57b1u/ymZa7zdte8RkRdk2kduTIrIlIhcLyI/E5G7ReQ5Os66IyJvcv+XPxWRz4hIXsdZjzHGDN0PEAK/ALYCCfBjYFu/76uHz38CcJb7PA78HNgG/ANwlWu/Cniv+/wi4GuAAOcCt7n2GeCX7ve0+zzt9n3fHSvu3Bf2+7lXqO/eDHwa+LLb/jxwufv8UeC17vOfAx91ny8HPuc+b3PjLQec6sZhOKpjErgGuNJ9ToApHWdd+2sz8CugkBlff6LjrLc/w6qxnQPsNcb80hhTBj4L7OjzPfUMY8w+Y8wP3edDwN3Yf6gd2IkI9/ul7vMO4Fpj+R4wJSInAC8AbjHGPGqMeQy4BbjE7ZswxnzP2P+yazPXGlpEZAvwe8An3LYAzweud4c095nvy+uBC9zxO4DPGmOOGmN+BezFjseRG5MiMgk8D7gawBhTNsY8jo6zhYiAgohEQBHYh46znjKsgm0zcF9m+37XtupwpotnAbcBG4wx+9yuh4AN7nOn/urWfn+b9mHnA8BfAjW3vQZ43BhTcdvZ50z7xu0/4I5fal8OM6cC+4F/c+bbT4hICR1nHTHGPAC8D7gXK9AOAD9Ax1lPGVbBpgAiMgbcALzRGHMwu8+9AWssh0NEXgw8bIz5Qb/vZYiIgLOAjxhjngU8gTU9pug4a8StN+7AvhRsAkrAJX29qVXIsAq2B4ATM9tbXNuqQURirFC7zhjzBdf8G2fewf1+2LV36q9u7VvatA8z5wEvEZH/xZpvng98EGsu8zlTs8+Z9o3bPwk8wtL7cpi5H7jfGHOb274eK+h0nHXmQuBXxpj9xph54AvYsafjrIcMq2C7HTjNeRol2EXXm/p8Tz3D2eCvBu42xrw/s+smwHucXQHcmGl/pfNaOxc44ExJNwMXi8i0e9O8GLjZ7TsoIue673pl5lpDiTHm7caYLcaYU7Dj5VvGmJcBtwKXusOa+8z35aXueOPaL3febKcCp2EdIEZuTBpjHgLuE5GnuKYLgLvQcdaNe4FzRaTonsn3mY6zXtJv75Xl/mA9sH6O9RB6R7/vp8fPfj7W/LMbuMP9vAhrm98J3AN8E5hxxwvwYddXPwG2Z671auzC9F7gVZn27cBP3Tn/gstSMwo/wO9S94rcip0w9gL/AeRce95t73X7t2bOf4frlz1kvPhGcUwCZwK73Fj7ItarUcdZ9z57J/Az91yfxHo26jjr4Y+m1FIURVFGimE1RSqKoihKW1SwKYqiKCOFCjZFURRlpFDBpiiKoowUKtgURVGUkUIFm6IMICLyLhG5cAWuc3gl7kdRhgl191eUEUZEDhtjxvp9H4rSS1RjU5QeISIvF5Hvi8gdIvIxsbXhDovIP7n6XTtFZJ079t9F5FL3+T1ia+/tFpH3ubZTRORbrm2niJzk2k8Vke+6Gmfvbvr+t4rI7e6cd7q2koh8RUR+7OqHXdbbXlGUlUcFm6L0ABF5GnAZcJ4x5kygCrwMmyR3lzHmdODbwN80nbcG+H3gdGPMMwAvrP4ZuMa1XQd8yLV/EJu0+OnY7PL+Ohdj0zKdg80mcraIPA+boPdBY8wzjTFnAF9f8YdXlB6jgk1ResMFwNnA7SJyh9veii2h8zl3zKew6dKyHADmgKtF5A+AWdf+HGzBVLBpm/x55wGfybR7LnY/PwJ+CDwVK+h+AlwkIu8VkecaYw4c43MqSt+JFj5EUZQVQLAa1tsbGkX+uum4hkVvY0xFRM7BCsJLgddjKxN0o93CuQB/b4z5WMsOkbOw+QffLSI7jTHvWuD6ijLQqMamKL1hJ3CpiKwHEJEZETkZ+z/os77/MfCd7Emu5t6kMearwJuAZ7pd/4PN7A7WpPlf7vN/N7V7bgZe7a6HiGwWkfUisgmYNcZ8CvhHbFkaRRlqVGNTlB5gjLlLRP4K+IaIBMA88Dps8c5z3L6HsetwWcaBG0Ukj9W63uza/wJb2fqt2CrXr3LtbwA+LSJvI1MCxhjzDbfO911bTYXDwMuBJwH/KCI1d0+vXdknV5Teo+7+itJH1B1fUVYeNUUqiqIoI4VqbIqiKMpIoRqboiiKMlKoYFMURVFGChVsiqIoykihgk1RFEUZKVSwKYqiKCPF/wOqslHoyrcJNQAAAABJRU5ErkJggg==\n", 433 | "text/plain": [ 434 | "
" 435 | ] 436 | }, 437 | "metadata": { 438 | "needs_background": "light" 439 | }, 440 | "output_type": "display_data" 441 | }, 442 | { 443 | "data": { 444 | "image/png": "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\n", 445 | "text/plain": [ 446 | "
" 447 | ] 448 | }, 449 | "metadata": { 450 | "needs_background": "light" 451 | }, 452 | "output_type": "display_data" 453 | } 454 | ], 455 | "source": [ 456 | "plot_reward(discounted_rewards, window=1000, y_label='discounted reward')\n", 457 | "plot_reward(total_rewards, window=100)" 458 | ] 459 | }, 460 | { 461 | "cell_type": "code", 462 | "execution_count": 32, 463 | "metadata": {}, 464 | "outputs": [], 465 | "source": [ 466 | "import antigravity" 467 | ] 468 | }, 469 | { 470 | "cell_type": "code", 471 | "execution_count": null, 472 | "metadata": {}, 473 | "outputs": [], 474 | "source": [] 475 | } 476 | ], 477 | "metadata": { 478 | "kernelspec": { 479 | "display_name": "Python 3", 480 | "language": "python", 481 | "name": "python3" 482 | }, 483 | "language_info": { 484 | "codemirror_mode": { 485 | "name": "ipython", 486 | "version": 3 487 | }, 488 | "file_extension": ".py", 489 | "mimetype": "text/x-python", 490 | "name": "python", 491 | "nbconvert_exporter": "python", 492 | "pygments_lexer": "ipython3", 493 | "version": "3.7.3" 494 | } 495 | }, 496 | "nbformat": 4, 497 | "nbformat_minor": 4 498 | } 499 | -------------------------------------------------------------------------------- /notebooks/2-DQN.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "markdown", 5 | "metadata": {}, 6 | "source": [ 7 | "# Vanilla Policy Gradient\n", 8 | "In this notebook the Deep Q-Learning Network approach is implemented in TensorFlow." 9 | ] 10 | }, 11 | { 12 | "cell_type": "markdown", 13 | "metadata": {}, 14 | "source": [ 15 | "### Import dependencies" 16 | ] 17 | }, 18 | { 19 | "cell_type": "code", 20 | "execution_count": 20, 21 | "metadata": {}, 22 | "outputs": [], 23 | "source": [ 24 | "import tensorflow as tf\n", 25 | "import gym\n", 26 | "import numpy as np\n", 27 | "import matplotlib.pyplot as plt\n", 28 | "import pandas as pd\n", 29 | "\n", 30 | "from tqdm import tqdm_notebook\n", 31 | "\n", 32 | "from collections import deque\n", 33 | "from tensorflow.keras.models import Sequential\n", 34 | "from tensorflow.keras.layers import Dense\n", 35 | "from tensorflow.keras.optimizers import Adam\n", 36 | "import random\n", 37 | "\n", 38 | "from utils import plot_reward" 39 | ] 40 | }, 41 | { 42 | "cell_type": "markdown", 43 | "metadata": {}, 44 | "source": [ 45 | "### Setup environment" 46 | ] 47 | }, 48 | { 49 | "cell_type": "code", 50 | "execution_count": 21, 51 | "metadata": {}, 52 | "outputs": [], 53 | "source": [ 54 | "env = gym.make('CartPole-v0')\n" 55 | ] 56 | }, 57 | { 58 | "cell_type": "code", 59 | "execution_count": 22, 60 | "metadata": {}, 61 | "outputs": [ 62 | { 63 | "name": "stdout", 64 | "output_type": "stream", 65 | "text": [ 66 | "Discrete(2) 2\n" 67 | ] 68 | } 69 | ], 70 | "source": [ 71 | "print(env.action_space, env.action_space.n)" 72 | ] 73 | }, 74 | { 75 | "cell_type": "code", 76 | "execution_count": 23, 77 | "metadata": {}, 78 | "outputs": [ 79 | { 80 | "name": "stdout", 81 | "output_type": "stream", 82 | "text": [ 83 | "(4,)\n" 84 | ] 85 | } 86 | ], 87 | "source": [ 88 | "print(env.observation_space.shape)" 89 | ] 90 | }, 91 | { 92 | "cell_type": "markdown", 93 | "metadata": {}, 94 | "source": [ 95 | "### Helper functions" 96 | ] 97 | }, 98 | { 99 | "cell_type": "code", 100 | "execution_count": 24, 101 | "metadata": {}, 102 | "outputs": [], 103 | "source": [ 104 | "def get_action(action_probs, epsilon, env, stochastic=True):\n", 105 | " \"\"\"\n", 106 | " Get action from actions space. With probability 1-epsilon,\n", 107 | " a random action is sampled, otherwise the action_probs are\n", 108 | " used to get an action. If stochastic, the actions are sampled\n", 109 | " according to the probablities of each action, otherwise the\n", 110 | " action with the highest probability is returned.\n", 111 | " \"\"\"\n", 112 | " \n", 113 | " if np.random.rand()>epsilon:\n", 114 | " if stochastic:\n", 115 | " action = np.random.choice(list(range(len(action_probs))), p=action_probs)\n", 116 | " else:\n", 117 | " action = np.argmax(action_probs)\n", 118 | " else:\n", 119 | " action = env.action_space.sample()\n", 120 | " return action\n", 121 | "\n", 122 | "def calc_discounted_rewards(r,gamma=0.9):\n", 123 | " \"\"\"\n", 124 | " Calculate the discounted future rewards with \n", 125 | " a gamma factor.\n", 126 | " \"\"\"\n", 127 | " discounted_rewards = []\n", 128 | " \n", 129 | " for i in range(len(r)):\n", 130 | " tmp_rewards = []\n", 131 | " for j in range(len(r)-i):\n", 132 | " tmp_rewards.append(r[i+j]*(gamma**j))\n", 133 | " discounted_rewards.append(np.sum(tmp_rewards))\n", 134 | " \n", 135 | " return np.array(discounted_rewards)" 136 | ] 137 | }, 138 | { 139 | "cell_type": "markdown", 140 | "metadata": {}, 141 | "source": [ 142 | "### Memory for replay buffer" 143 | ] 144 | }, 145 | { 146 | "cell_type": "code", 147 | "execution_count": 25, 148 | "metadata": {}, 149 | "outputs": [], 150 | "source": [ 151 | "class Memory:\n", 152 | " \n", 153 | " def __init__(self, memory_size=None):\n", 154 | " self._memory = deque(maxlen=memory_size)\n", 155 | " \n", 156 | " def replay(self, n):\n", 157 | " return random.sample(self._memory, n)\n", 158 | " \n", 159 | " def memorize(self, elements):\n", 160 | " self._memory.append(elements)\n", 161 | " \n", 162 | " def __len__(self):\n", 163 | " return len(self._memory)" 164 | ] 165 | }, 166 | { 167 | "cell_type": "markdown", 168 | "metadata": {}, 169 | "source": [ 170 | "### Setup Q-network" 171 | ] 172 | }, 173 | { 174 | "cell_type": "code", 175 | "execution_count": 26, 176 | "metadata": {}, 177 | "outputs": [], 178 | "source": [ 179 | "def qforward(observation_space, action_space, shape=[24,24], lr=0.001):\n", 180 | " \n", 181 | " model = Sequential()\n", 182 | " \n", 183 | " model.add(Dense(shape[0], input_shape=observation_space, activation=\"relu\"))\n", 184 | " for dim in shape[1:]:\n", 185 | " model.add(Dense(dim, activation=\"relu\"))\n", 186 | " model.add(Dense(action_space, activation=\"linear\"))\n", 187 | " \n", 188 | " model.compile(loss=\"mse\", optimizer=Adam(lr=lr))\n", 189 | "\n", 190 | " return model" 191 | ] 192 | }, 193 | { 194 | "cell_type": "markdown", 195 | "metadata": {}, 196 | "source": [ 197 | "### Setup replay training" 198 | ] 199 | }, 200 | { 201 | "cell_type": "code", 202 | "execution_count": 27, 203 | "metadata": {}, 204 | "outputs": [], 205 | "source": [ 206 | "def model_replay(model, memory, batch_size, gamma):\n", 207 | " \n", 208 | " if len(memory)>> df.groupby(...).agg(name=('column', aggfunc))\n", 383 | "\n", 384 | " return super().aggregate(arg, *args, **kwargs)\n" 385 | ] 386 | }, 387 | { 388 | "data": { 389 | "image/png": "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\n", 390 | "text/plain": [ 391 | "
" 392 | ] 393 | }, 394 | "metadata": { 395 | "needs_background": "light" 396 | }, 397 | "output_type": "display_data" 398 | } 399 | ], 400 | "source": [ 401 | "plot_reward(total_rewards, window=10)" 402 | ] 403 | } 404 | ], 405 | "metadata": { 406 | "kernelspec": { 407 | "display_name": "Python 3", 408 | "language": "python", 409 | "name": "python3" 410 | }, 411 | "language_info": { 412 | "codemirror_mode": { 413 | "name": "ipython", 414 | "version": 3 415 | }, 416 | "file_extension": ".py", 417 | "mimetype": "text/x-python", 418 | "name": "python", 419 | "nbconvert_exporter": "python", 420 | "pygments_lexer": "ipython3", 421 | "version": "3.7.3" 422 | } 423 | }, 424 | "nbformat": 4, 425 | "nbformat_minor": 4 426 | } 427 | -------------------------------------------------------------------------------- /notebooks/3-PPO.ipynb: -------------------------------------------------------------------------------- 1 | { 2 | "cells": [ 3 | { 4 | "cell_type": "code", 5 | "execution_count": 16, 6 | "metadata": {}, 7 | "outputs": [ 8 | { 9 | "name": "stdout", 10 | "output_type": "stream", 11 | "text": [ 12 | "2.0.0-beta1\n" 13 | ] 14 | } 15 | ], 16 | "source": [ 17 | "import tensorflow.compat.v1 as tf\n", 18 | "\n", 19 | "#import tensorflow as tf\n", 20 | "import gym\n", 21 | "import numpy as np\n", 22 | "import matplotlib.pyplot as plt\n", 23 | "import pandas as pd\n", 24 | "\n", 25 | "from tqdm import tqdm_notebook\n", 26 | "\n", 27 | "from tensorflow.keras.models import Sequential, Model, clone_model\n", 28 | "from tensorflow.keras.layers import Dense, Input\n", 29 | "from tensorflow.keras.optimizers import Adam\n", 30 | "\n", 31 | "from utils import get_epsilons, plot_reward\n", 32 | "\n", 33 | "print(tf.__version__)\n", 34 | "tf.disable_v2_behavior()" 35 | ] 36 | }, 37 | { 38 | "cell_type": "markdown", 39 | "metadata": {}, 40 | "source": [ 41 | "## Create environment" 42 | ] 43 | }, 44 | { 45 | "cell_type": "code", 46 | "execution_count": 5, 47 | "metadata": {}, 48 | "outputs": [], 49 | "source": [ 50 | "env = gym.make('CartPole-v0')" 51 | ] 52 | }, 53 | { 54 | "cell_type": "code", 55 | "execution_count": 6, 56 | "metadata": {}, 57 | "outputs": [ 58 | { 59 | "data": { 60 | "text/plain": [ 61 | "2" 62 | ] 63 | }, 64 | "execution_count": 6, 65 | "metadata": {}, 66 | "output_type": "execute_result" 67 | } 68 | ], 69 | "source": [ 70 | "env.action_space.n" 71 | ] 72 | }, 73 | { 74 | "cell_type": "code", 75 | "execution_count": 7, 76 | "metadata": {}, 77 | "outputs": [ 78 | { 79 | "data": { 80 | "text/plain": [ 81 | "4" 82 | ] 83 | }, 84 | "execution_count": 7, 85 | "metadata": {}, 86 | "output_type": "execute_result" 87 | } 88 | ], 89 | "source": [ 90 | "env.observation_space.shape[0]" 91 | ] 92 | }, 93 | { 94 | "cell_type": "markdown", 95 | "metadata": {}, 96 | "source": [ 97 | "## Define policy network" 98 | ] 99 | }, 100 | { 101 | "cell_type": "code", 102 | "execution_count": 8, 103 | "metadata": {}, 104 | "outputs": [], 105 | "source": [ 106 | "def mlp(observation_space, action_space, shape=[64,64], lr=0.001):\n", 107 | " \n", 108 | " model = Sequential()\n", 109 | " \n", 110 | " model.add(Dense(shape[0], input_shape=observation_space, activation=\"tanh\"))\n", 111 | " for dim in shape[1:]:\n", 112 | " model.add(Dense(dim, activation=\"tanh\"))\n", 113 | " model.add(Dense(action_space, activation=\"softmax\"))\n", 114 | " \n", 115 | " return model" 116 | ] 117 | }, 118 | { 119 | "cell_type": "code", 120 | "execution_count": 9, 121 | "metadata": {}, 122 | "outputs": [], 123 | "source": [ 124 | "def ppo_loss_function(r, advantage, eps):\n", 125 | " def loss(y_true, y_pred):\n", 126 | " \n", 127 | " ppo_loss = - tf.reduce_mean(tf.math.minimum(tf.multiply(r, advantage),\n", 128 | " tf.multiply(tf.clip_by_value(r,\n", 129 | " tf.subtract(1.,eps),\n", 130 | " tf.add(1.,eps)),\n", 131 | " advantage)))\n", 132 | " return ppo_loss\n", 133 | " return loss" 134 | ] 135 | }, 136 | { 137 | "cell_type": "code", 138 | "execution_count": 10, 139 | "metadata": {}, 140 | "outputs": [], 141 | "source": [ 142 | "def get_ppo_model(policy_model, observation_space, action_space):\n", 143 | " \n", 144 | " # placeholders:\n", 145 | " obs = Input(shape=(observation_space,))\n", 146 | " action = Input(shape=(action_space,))\n", 147 | " old_prob = Input(shape=(1,))\n", 148 | " advantage = Input(shape=(1,))\n", 149 | " eps = Input(shape=(1,))\n", 150 | "\n", 151 | " out_action = policy_model(obs)\n", 152 | " \n", 153 | " # ppo loss\n", 154 | " prob = tf.reduce_sum(tf.multiply(out_action, action), axis=1)\n", 155 | " r = tf.divide(prob, old_prob)\n", 156 | " \n", 157 | " # ppo model\n", 158 | " ppo_model = Model(inputs=[obs, action, old_prob, advantage, eps], outputs=[out_action])\n", 159 | "\n", 160 | " # compile model\n", 161 | " optimizer = Adam(lr=0.001)\n", 162 | " ppo_model.compile(optimizer=optimizer, loss=ppo_loss_function(r, advantage, eps))\n", 163 | " \n", 164 | " return ppo_model" 165 | ] 166 | }, 167 | { 168 | "cell_type": "code", 169 | "execution_count": 11, 170 | "metadata": {}, 171 | "outputs": [], 172 | "source": [ 173 | "def get_action(action_probs, epsilon, env, stochastic=True):\n", 174 | " \"\"\"\n", 175 | " Get action from actions space. With probability 1-epsilon,\n", 176 | " a random action is sampled, otherwise the action_probs are\n", 177 | " used to get an action. If stochastic, the actions are sampled\n", 178 | " according to the probablities of each action, otherwise the\n", 179 | " action with the highest probability is returned.\n", 180 | " \"\"\"\n", 181 | " \n", 182 | " if np.random.rand()>epsilon:\n", 183 | " if stochastic:\n", 184 | " action = np.random.choice(list(range(len(action_probs))), p=action_probs)\n", 185 | " else:\n", 186 | " action = np.argmax(action_probs)\n", 187 | " else:\n", 188 | " action = env.action_space.sample()\n", 189 | " return action\n", 190 | "\n", 191 | "def calc_discounted_rewards(r,gamma=0.9):\n", 192 | " \"\"\"\n", 193 | " Calculate the discounted future rewards with \n", 194 | " a gamma factor.\n", 195 | " \"\"\"\n", 196 | " discounted_rewards = []\n", 197 | " \n", 198 | " for i in range(len(r)):\n", 199 | " tmp_rewards = []\n", 200 | " for j in range(len(r)-i):\n", 201 | " tmp_rewards.append(r[i+j]*(gamma**j))\n", 202 | " discounted_rewards.append(np.sum(tmp_rewards))\n", 203 | " \n", 204 | " return np.array(discounted_rewards)\n", 205 | " " 206 | ] 207 | }, 208 | { 209 | "cell_type": "code", 210 | "execution_count": 12, 211 | "metadata": {}, 212 | "outputs": [], 213 | "source": [ 214 | "def PPO(env, model, n_steps=10*4, epsilon_range=[0.99, 0.1], render=False, eps=0.2, max_steps=200):\n", 215 | " \"\"\"\n", 216 | " Proximal Policy Optimisation implementation in TensorFlow 2.\n", 217 | " \n", 218 | " args:\n", 219 | " env: OpenAI gym environment\n", 220 | " n_steps=10*4: number of training steps\n", 221 | " epsilon_range=[0.99, 0.1]: epsilon decay range\n", 222 | " \"\"\"\n", 223 | " \n", 224 | " \n", 225 | " obs_shape = env.observation_space\n", 226 | " action_space = env.action_space.n\n", 227 | " print('obs shape:',obs_shape,'| action space:', action_space)\n", 228 | " \n", 229 | " \n", 230 | " epsilons = get_epsilons(epsilon_range, n_steps)\n", 231 | " \n", 232 | " dummy_scalar = np.zeros((1,1))\n", 233 | " dummy_action = np.zeros((1, action_space))\n", 234 | " dummy_inputs = [dummy_action] + 3*[dummy_scalar]\n", 235 | " \n", 236 | " total_rewards = []\n", 237 | " total_discounted_rewards = []\n", 238 | " observations = []\n", 239 | " rewards = []\n", 240 | " actions = []\n", 241 | " old_prob = []\n", 242 | " losses = []\n", 243 | " gradients = []\n", 244 | " old_model = clone_model(model)\n", 245 | "\n", 246 | " baseline=0\n", 247 | " current_steps = 0\n", 248 | " game_done = True\n", 249 | " pbar = tqdm_notebook(range(n_steps))\n", 250 | " \n", 251 | " for i in pbar:\n", 252 | " if render:\n", 253 | " env.render()\n", 254 | "\n", 255 | " if game_done:\n", 256 | " old_model.set_weights(model.get_weights())\n", 257 | "\n", 258 | " if len(rewards)>0:\n", 259 | " discounted_rewards = calc_discounted_rewards(rewards, gamma=0.9)\n", 260 | " advantage = (discounted_rewards-baseline)\n", 261 | " \n", 262 | " samples = len(observations)\n", 263 | " model.fit(x=[np.array(observations),\n", 264 | " np.array(actions),\n", 265 | " np.array(old_prob),\n", 266 | " np.array(advantage),\n", 267 | " np.tile(eps, samples)],\n", 268 | " y=np.array(old_prob), verbose=0)\n", 269 | " \n", 270 | " baseline = np.mean(discounted_rewards)\n", 271 | " total_discounted_rewards += list(discounted_rewards)\n", 272 | " total_rewards.append(sum(rewards))\n", 273 | " pbar.set_description('reward: ' +str(total_rewards[-1]))\n", 274 | " \n", 275 | " obs = env.reset()\n", 276 | " observations = []\n", 277 | " rewards = []\n", 278 | " actions = []\n", 279 | " old_prob = []\n", 280 | " current_steps=0\n", 281 | " \n", 282 | " current_steps += 1\n", 283 | " observations.append(obs)\n", 284 | " action_probs = model.predict([np.expand_dims(obs, axis=0)] + dummy_inputs)\n", 285 | " \n", 286 | " action = get_action(np.squeeze(action_probs), epsilons[i], env, stochastic=False)\n", 287 | " old_prob.append(old_model.predict([np.expand_dims(obs, axis=0)] + dummy_inputs)[:, action])\n", 288 | " \n", 289 | " obs, reward, game_done,_ = env.step(action)\n", 290 | " rewards.append(reward)\n", 291 | " action_vec = np.zeros(action_space)\n", 292 | " action_vec[action]=1\n", 293 | " actions.append(action_vec)\n", 294 | " \n", 295 | " if current_steps>=max_steps:\n", 296 | " game_done=True\n", 297 | " \n", 298 | " #env.close()\n", 299 | "\n", 300 | " return model, total_rewards" 301 | ] 302 | }, 303 | { 304 | "cell_type": "code", 305 | "execution_count": 13, 306 | "metadata": {}, 307 | "outputs": [], 308 | "source": [ 309 | "policy_model = mlp([env.observation_space.shape[0]], env.action_space.n)\n", 310 | "ppo_model = get_ppo_model(policy_model, env.observation_space.shape[0], env.action_space.n)" 311 | ] 312 | }, 313 | { 314 | "cell_type": "code", 315 | "execution_count": 14, 316 | "metadata": {}, 317 | "outputs": [ 318 | { 319 | "name": "stderr", 320 | "output_type": "stream", 321 | "text": [ 322 | "W0824 21:51:53.352210 4704441792 deprecation.py:506] From /Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/tensorflow/python/ops/init_ops.py:97: calling GlorotUniform.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", 323 | "Instructions for updating:\n", 324 | "Call initializer instance with the dtype argument instead of passing it to the constructor\n", 325 | "W0824 21:51:53.353631 4704441792 deprecation.py:506] From /Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/tensorflow/python/ops/init_ops.py:97: calling Zeros.__init__ (from tensorflow.python.ops.init_ops) with dtype is deprecated and will be removed in a future version.\n", 326 | "Instructions for updating:\n", 327 | "Call initializer instance with the dtype argument instead of passing it to the constructor\n" 328 | ] 329 | }, 330 | { 331 | "name": "stdout", 332 | "output_type": "stream", 333 | "text": [ 334 | "obs shape: Box(4,) | action space: 2\n" 335 | ] 336 | }, 337 | { 338 | "data": { 339 | "application/vnd.jupyter.widget-view+json": { 340 | "model_id": "4d395e1ae41d41b09fd197037c86b917", 341 | "version_major": 2, 342 | "version_minor": 0 343 | }, 344 | "text/plain": [ 345 | "HBox(children=(IntProgress(value=0, max=100000), HTML(value='')))" 346 | ] 347 | }, 348 | "metadata": {}, 349 | "output_type": "display_data" 350 | }, 351 | { 352 | "name": "stderr", 353 | "output_type": "stream", 354 | "text": [ 355 | "W0824 21:51:53.735882 4704441792 deprecation.py:323] From /Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/tensorflow/python/ops/math_grad.py:1250: add_dispatch_support..wrapper (from tensorflow.python.ops.array_ops) is deprecated and will be removed in a future version.\n", 356 | "Instructions for updating:\n", 357 | "Use tf.where in 2.0, which has the same broadcast rule as np.where\n" 358 | ] 359 | }, 360 | { 361 | "name": "stdout", 362 | "output_type": "stream", 363 | "text": [ 364 | "\n" 365 | ] 366 | } 367 | ], 368 | "source": [ 369 | "model, total_rewards = PPO(env, ppo_model, n_steps=100000, render=False, epsilon_range=[1,0.1], eps=0.2)" 370 | ] 371 | }, 372 | { 373 | "cell_type": "code", 374 | "execution_count": 17, 375 | "metadata": {}, 376 | "outputs": [ 377 | { 378 | "name": "stderr", 379 | "output_type": "stream", 380 | "text": [ 381 | "/Users/leandro/git/reproduce-rl/env/lib/python3.7/site-packages/pandas/core/window.py:1833: FutureWarning: using a dict with renaming is deprecated and will be removed\n", 382 | "in a future version.\n", 383 | "\n", 384 | "For column-specific groupby renaming, use named aggregation\n", 385 | "\n", 386 | " >>> df.groupby(...).agg(name=('column', aggfunc))\n", 387 | "\n", 388 | " return super().aggregate(arg, *args, **kwargs)\n" 389 | ] 390 | }, 391 | { 392 | "data": { 393 | "image/png": "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\n", 394 | "text/plain": [ 395 | "
" 396 | ] 397 | }, 398 | "metadata": { 399 | "needs_background": "light" 400 | }, 401 | "output_type": "display_data" 402 | } 403 | ], 404 | "source": [ 405 | "plot_reward(total_rewards, window=100)" 406 | ] 407 | }, 408 | { 409 | "cell_type": "code", 410 | "execution_count": null, 411 | "metadata": {}, 412 | "outputs": [], 413 | "source": [] 414 | } 415 | ], 416 | "metadata": { 417 | "kernelspec": { 418 | "display_name": "Python 3", 419 | "language": "python", 420 | "name": "python3" 421 | }, 422 | "language_info": { 423 | "codemirror_mode": { 424 | "name": "ipython", 425 | "version": 3 426 | }, 427 | "file_extension": ".py", 428 | "mimetype": "text/x-python", 429 | "name": "python", 430 | "nbconvert_exporter": "python", 431 | "pygments_lexer": "ipython3", 432 | "version": "3.7.3" 433 | } 434 | }, 435 | "nbformat": 4, 436 | "nbformat_minor": 4 437 | } 438 | -------------------------------------------------------------------------------- /notebooks/utils.py: -------------------------------------------------------------------------------- 1 | import pandas as pd 2 | import numpy as np 3 | import matplotlib.pyplot as plt 4 | 5 | def plot_reward(rewards, window, x_label='episodes', y_label='reward'): 6 | """ 7 | Function to plot rewards with a rolling mean and standard deviation. 8 | """ 9 | steps = window 10 | 11 | df = pd.DataFrame({'rewards': rewards}) 12 | m = df.rolling(steps, center=True).agg({'mean':'mean', 'std':'std'}) 13 | m.columns = m.columns.droplevel(1) 14 | ax = m['mean'].plot() 15 | ax.fill_between(m.index, m['mean'] - m['std'], m['mean'] + m['std'], 16 | alpha=.25) 17 | plt.tight_layout() 18 | plt.ylabel(y_label) 19 | plt.xlabel(x_label) 20 | plt.show() 21 | 22 | def get_epsilons(epsilon_range, n_steps): 23 | """ 24 | Linear decay of epsilon in n_steps. 25 | """ 26 | epsilon = np.linspace(epsilon_range[0], epsilon_range[1], n_steps) 27 | return epsilon 28 | 29 | def get_exp_epsilons(epsilon_range, epsilon_decay, n_steps): 30 | """ 31 | Exponential decay of epsilon in n_steps. 32 | """ 33 | tmp_epsilon = epsilon_range[0] 34 | min_epsilon = epsilon_range[1] 35 | epsilons = [] 36 | for i in range(n_steps): 37 | epsilons.append(max([tmp_epsilon, min_epsilon])) 38 | tmp_epsilon *= epsilon_decay 39 | 40 | return epsilons -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | gym==0.14.0 2 | jupyterlab==1.0.9 3 | matplotlib==3.1.0 4 | numpy==1.17.0 5 | pandas==0.25.0 6 | tensorflow==2.0.0b1 7 | tqdm==4.34.0 --------------------------------------------------------------------------------