From 8899ec087cbc36c10f4e9d6dfd369b46e4dcfff9 Mon Sep 17 00:00:00 2001 From: Khimya Date: Thu, 18 Jun 2020 14:31:31 -0400 Subject: [PATCH] affordances in discrete env --- .../AffordancesInDiscreteEnvironment.ipynb | 2449 +++++++++++++++++ affordances_theory/README.md | 7 +- 2 files changed, 2454 insertions(+), 2 deletions(-) create mode 100644 affordances_theory/AffordancesInDiscreteEnvironment.ipynb diff --git a/affordances_theory/AffordancesInDiscreteEnvironment.ipynb b/affordances_theory/AffordancesInDiscreteEnvironment.ipynb new file mode 100644 index 0000000..ac982be --- /dev/null +++ b/affordances_theory/AffordancesInDiscreteEnvironment.ipynb @@ -0,0 +1,2449 @@ +{ + "nbformat": 4, + "nbformat_minor": 0, + "metadata": { + "colab": { + "name": "AffordancesInDiscreteEnvironment.ipynb", + "provenance": [], + "collapsed_sections": [], + "toc_visible": true + }, + "kernelspec": { + "name": "python3", + "display_name": "Python 3" + } + }, + "cells": [ + { + "cell_type": "markdown", + "metadata": { + "id": "AXY3q8CdsGAR", + "colab_type": "text" + }, + "source": [ + "Copyright 2020 \"What Can I do Here? A Theory of Affordances In Reinforcement Learning\" Authors. All rights reserved." + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "lJB5fq1g81z2", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 343 + }, + "outputId": "83ca2438-9624-4a09-c936-3457a1b1ab9c" + }, + "source": [ + "#@title Click to Install and import libraries.\n", + "# Please follow the instructions in the README for dependencies installation.\n", + "!git clone https://github.com/kkhetarpal/emdp.git\n", + "%cd emdp/\n", + "!pip install -e .\n", + "!pip install matplotlib==3.0.2\n", + "\n", + "#@title General Imports\n", + "%tensorflow_version 2.x\n", + "from collections import defaultdict\n", + "import enum\n", + "import functools\n", + "import os\n", + "import random\n", + "import sys\n", + "import time\n", + "from datetime import datetime\n", + "\n", + "import emdp\n", + "from emdp import actions\n", + "from emdp.gridworld import GridWorldPlotter\n", + "from emdp.gridworld import build_simple_grid\n", + "from emdp.gridworld.builder_tools import TransitionMatrixBuilder\n", + "from emdp.gridworld.env import GridWorldMDP\n", + "from emdp.gridworld.helper_utilities import get_state_after_executing_action\n", + "from emdp.gridworld.helper_utilities import check_can_take_action\n", + "from emdp.gridworld.helper_utilities import get_possible_actions\n", + "from emdp.gridworld.helper_utilities import get_state_after_executing_action\n", + "from emdp.gridworld.txt_utilities import get_char_matrix\n", + "from emdp.gridworld.txt_utilities import build_gridworld_from_char_matrix\n", + "from emdp.examples.simple import build_four_rooms_example\n", + "from emdp.utils import convert_int_rep_to_onehot, convert_onehot_to_int\n", + "\n", + "import matplotlib\n", + "import matplotlib.pyplot as plt\n", + "import matplotlib.animation as animation\n", + "import numpy as np\n", + "from numpy.linalg import inv\n", + "\n", + "import seaborn as sns\n", + "import tensorflow as tf\n", + "\n", + "color_ls = [[102, 120, 173],\n", + " [118, 167, 125], \n", + " [198, 113, 113], \n", + " [230, 169, 132],\n", + " [169, 193, 213],\n", + " [192, 197, 182],\n", + " [210, 180, 226]]\n", + "colors = [[shade / 255.0 for shade in rgb] for rgb in color_ls]\n", + "markers = ['o', 's', 'D', '^', '*', 'x', 'p', '+', 'v','|']\n", + "\n", + "DEFAULT_ARROW_COLOR = '#a65628'" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "stream", + "text": [ + "fatal: destination path 'emdp' already exists and is not an empty directory.\n", + "/content/emdp\n", + "Obtaining file:///content/emdp\n", + "Requirement already satisfied: numpy>=1.9.1 in /usr/local/lib/python3.6/dist-packages (from emdp==0.0.4) (1.18.5)\n", + "Installing collected packages: emdp\n", + " Found existing installation: emdp 0.0.4\n", + " Can't uninstall 'emdp'. No files were found to uninstall.\n", + " Running setup.py develop for emdp\n", + "Successfully installed emdp\n", + "Requirement already satisfied: matplotlib==3.0.2 in /usr/local/lib/python3.6/dist-packages (3.0.2)\n", + "Requirement already satisfied: cycler>=0.10 in /usr/local/lib/python3.6/dist-packages (from matplotlib==3.0.2) (0.10.0)\n", + "Requirement already satisfied: kiwisolver>=1.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib==3.0.2) (1.2.0)\n", + "Requirement already satisfied: numpy>=1.10.0 in /usr/local/lib/python3.6/dist-packages (from matplotlib==3.0.2) (1.18.5)\n", + "Requirement already satisfied: python-dateutil>=2.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib==3.0.2) (2.8.1)\n", + "Requirement already satisfied: pyparsing!=2.0.4,!=2.1.2,!=2.1.6,>=2.0.1 in /usr/local/lib/python3.6/dist-packages (from matplotlib==3.0.2) (2.4.7)\n", + "Requirement already satisfied: six in /usr/local/lib/python3.6/dist-packages (from cycler>=0.10->matplotlib==3.0.2) (1.12.0)\n" + ], + "name": "stdout" + }, + { + "output_type": "stream", + "text": [ + "/usr/local/lib/python3.6/dist-packages/statsmodels/tools/_testing.py:19: FutureWarning: pandas.util.testing is deprecated. Use the functions in the public API at pandas.testing instead.\n", + " import pandas.util.testing as tm\n" + ], + "name": "stderr" + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "gDpLK58xElDQ", + "colab_type": "text" + }, + "source": [ + "# Helper functions" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "Y6_lV_vUIhs3", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Plotting and verifying matrices.\n", + "def plot_environment(\n", + " mdp, ax, wall_locs=None, plot_grid=False,\n", + " grid_kwargs=None,\n", + " wall_color=(0, 0, 0, 1), # R, G, B, alpha\n", + " ):\n", + " \"\"\"Function to plot emdp environment\n", + "\n", + " Args:\n", + " mdp: The MDP to use.\n", + " ax: The axes to plot this on.\n", + " wall_locs: Locations of the walls for plotting them in a different color.\n", + " plot_grid: Boolean indicating if the overlay grid should be plotted.\n", + " grid_kwargs: Grid keyword argrument specification.\n", + " wall_color: RGB color of the walls.\n", + "\n", + " Returns:\n", + " ax: The axes of the final plot.\n", + " imshow_ax: The final plot.\n", + " \"\"\"\n", + " grid_kwargs = grid_kwargs or {}\n", + "\n", + " # Plot states with white background.\n", + " state_background = np.ones((mdp.size, mdp.size))\n", + "\n", + " # Walls appear in a different color.\n", + " wall_img = np.ones((mdp.size, mdp.size, 4))\n", + " if wall_locs is not None:\n", + " for state in wall_locs:\n", + " y_coord = state[0]\n", + " x_coord = state[1]\n", + " wall_img[y_coord, x_coord, :] = np.array(wall_color)\n", + "\n", + " # Render the heatmap and overlay the walls.\n", + " imshow_ax = ax.imshow(state_background, interpolation=None)\n", + " imshow_ax = ax.imshow(wall_img, interpolation=None)\n", + " ax.grid(False)\n", + " \n", + " # Switch on flag if you want to plot grid \n", + " if plot_grid:\n", + " for i in range(mdp.size + 1):\n", + " ax.plot(\n", + " np.arange(mdp.size + 1) - 0.5,\n", + " np.ones(mdp.size + 1) * i - 0.5,\n", + " **grid_kwargs)\n", + "\n", + " for i in range(mdp.size + 1):\n", + " ax.plot(\n", + " np.ones(mdp.size + 1) * i - 0.5,\n", + " np.arange(mdp.size + 1) - 0.5,\n", + " **grid_kwargs)\n", + " ax.set_xlabel('x')\n", + " ax.set_ylabel('y')\n", + "\n", + " return ax, imshow_ax\n", + "\n", + "\n", + "def get_current_state_integer(state_):\n", + " return np.argmax(state_, axis=0)\n", + "\n", + "\n", + "def get_stateid(x, y, size):\n", + " \"\"\"Converts an (x, y) coordinate into the state id.\"\"\"\n", + " return size * x + y\n", + "\n", + "\n", + "def _is_absorbing(state_int, mdp_size):\n", + " \"\"\"Checks if the state_int is an absorbing state\"\"\"\n", + " return state_int == mdp_size * mdp_size\n", + "\n", + "\n", + "def _checking_P(P):\n", + " \"\"\"Checks if the P matrix is valid.\"\"\"\n", + " assert np.all(P <= 1.0) and np.all(P >= 0.0)\n", + " assert not np.allclose(P, 1.0)\n", + " assert not np.allclose(P, 0.0)\n" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "I9SJJN8aTuKL", + "colab_type": "text" + }, + "source": [ + "## Grid World Environment" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "bmdjTf1-Dd4a", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 332 + }, + "outputId": "8df6e3ee-a090-4a96-8f42-96e31bdfeed5" + }, + "source": [ + "#@title Create the one room example.\n", + "_ONE_ROOM_TXT = \"\"\"#############\n", + "# #\n", + "# g #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# ####### #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "#############\"\"\".split('\\n')\n", + "\n", + "\n", + "def build_one_room_example(gamma=0.99, seed=2017, p_success=1.0):\n", + " char_matrix = get_char_matrix(_ONE_ROOM_TXT)\n", + " return build_gridworld_from_char_matrix(\n", + " char_matrix, p_success=p_success, seed=seed, gamma=gamma)\n", + "\n", + "\n", + "mdp, mdp_wall_locs = build_one_room_example()\n", + "\n", + "gwp = GridWorldPlotter.from_mdp(mdp)\n", + "\n", + "fig = plt.figure(figsize=(10, 4))\n", + "ax = fig.add_subplot(121)\n", + "\n", + "plot_environment(mdp, ax, wall_locs=mdp_wall_locs, plot_grid=True, \n", + " grid_kwargs={'color':(220 / 255, 220 / 255, 220 / 255, 0.8)})\n" + ], + "execution_count": 48, + "outputs": [ + { + "output_type": "execute_result", + "data": { + "text/plain": [ + "(,\n", + " )" + ] + }, + "metadata": { + "tags": [] + }, + "execution_count": 48 + }, + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAS8AAAEZCAYAAADG/aNoAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAHP9JREFUeJzt3XtQVOf5B/Dv4mLQ1AVsBRRYLQhOV0UioDFVEby0muLQBsWmlahRNF6GFrQQ4qhDrWjVRoox4KUdg4mXBGKIovWGqUoSxyYENK0uOChT8TKIoCMYLvv7wx+MGxCX3bOc991+PzPOhHfP++yzDPl6OHv2UWMymUwgIpKMk9oNEBFZg+FFRFJieBGRlBheRCQlhhcRSYnhRURSYngRkZQYXkQkJYYXEUmJ4UVEUtKq3YAsNBqN2i0QObyufFqR4dUFfn5+uHfvXpf3ubm5ISMjAwkJCVbtd6QaIvQgSg0RelCihlI9XL16tUt7GF5dcO/ePdy9e9eqvU1NTTbtd6QaIvQgSg0RelCihhI9dJU017zu37+PVatWYcyYMRgxYgRmzZqFCxcuWLT39OnT+NWvfoXhw4dj7Nix2LBhAx49emTnjonInqQIL5PJhMWLF+PEiRNITk7Gu+++C3d3d8ybNw/ffvttp3uLiorwxhtvwN/fH9u3b8fSpUtx4MABJCcnd1P3RGQPUvzaWFhYiPPnz2P79u0IDw8HAISGhuIXv/gF3n77bezYseOpezdt2oThw4fjz3/+MzQaDcaMGQNnZ2ekpqZi3rx5CAoK6q6XQUQKkuLM6+TJk3Bzc8P48ePb1nr27Ilp06ahqKgIDx8+7HBfVVUVLl26hKioKLN3C19++WU4Ozvj+PHjdu+diOxDivAyGo0ICAhod7tCYGAgmpqanvouhdFoBAAEBASYrbu4uMDX17ftcSKSjxThVVtbC51O127d1dW17fGn7XvyuO/vtfZtXSJSnxThRUT0fVKEl6urK+rq6tqtd3Zm9eR6R2dmtbW1cHNzU7BLIupOUoTX4MGDUVZW1u6jA0ajEVqtFn5+fh3ua73WVVZWZrbe0NCAysrKdtfCiEgeUoTXxIkTUVNTgzNnzrStNTY2oqCgAGPGjEHv3r073Ne/f38YDAYcOnTILPgKCgrQ2NiISZMm2b13IrIPKcIrMjISoaGhSE1NxcGDB1FUVISEhATcuHEDv/vd79qOmzx5Ml577TWzvUlJSfjmm2+QkpKCL774Avv378e6devws5/9DCNGjOjul0JECpHiJlWNRoN3330XmzZtwvr16/Hw4UMYDAb87W9/w7Bhw9qOa25uRktLi9nesWPH4p133kFmZiYWLFgAV1dXxMTEmIUeEclHivACAJ1Oh7S0NKSlpT31mFOnTnW4HhkZicjISHu1RkQqkCa8RGDtu5Nubm7QarWoqqpCjx49rKrR3NyMW7duSV9DhB5EqSFCD0rUaN1vy7v3bm5uXZ5IoTF1ZfrX/zCNRoP8/Hw0Nzd3eW+PHj0waNAguLm5WT3U0GQyobGxEc7OzlLXEKEHUWqI0IMSNVr3X7p0yar/P4DH/49Mnz69S8MIGV4W0mg0Ng8jnDJlis1/O3p6ekpdQ4QeRKkhQg9K1GjdP2HCBJuHEXKSqp3YOqytR48eVv+AAY8D1BFqiNCDKDVE6EGJGhqNhsMIiYgswfAiIikxvIhISgwvIpISw4uIpMTwIiIpMbyISEoMLyKSEsOLiKTE8CIiKTG8iEhKDC8ikhLDi4ikxKkSXWDrMEJrZx0Bj8eOmEwm6WuI0IMoNUToQYkarfs5jFBQHEaoTA0RehClhgg9KFGDwwgFx2GEytQQoQdRaojQgxI1OIxQAhxGqEwNEXoQpYYIPShRg8MIiYgsxPAiIikxvIhISgwvIpISw4uIpMTwIiIpMbyISEoMLyKSkhQ3qX7++ec4ePAgvv76a9y6dQs//OEPERoaimXLlsHX17fTvbNnz8b58+fbrS9YsADLly+3V8tEZGdShNfevXtx//59zJ8/H4MGDUJVVRW2bduGV155BXl5efDx8el0v5+fH9LT083WPD097dkyEdmZFOG1Zs0a9O3b12wtJCQEkyZNwt69e7FixYpO9/fq1QvBwcH2bJGIupkU17y+H1wA4OPjA3d3d9y8eVOFjohIbVKceXXkypUruHv3LgICAp55bHl5OUJDQ1FfXw+9Xo/Y2FjExcXByalr2c15XmLNsAoMDLRpisHp06cd5nshwuvgPC8LNDY2Yvbs2aioqMCRI0fg7u7+1GO3bNkCLy8v+Pn54f79+zh69Cjy8/Px6quvYvXq1RY/J+d5KVNDyR5snR81dOhQh/leiPA6OM/rGUwmE1JSUnD48GFkZWVh7NixXa6xatUqHDhwACdOnHjmxf5WnOelTA0le7B1ftTp06cd5nshwuvgPK9nSEtLQ35+PjZv3mxVcAFAVFQU9u/fj9LSUovDC+A8L6VqKNWDrfOjHOl7oXYNNeZ5SRVe69evx969e7F27VpMmzbN6jqt6W7taTYRqU+KdxsB4O2338bf//53rFy5EjExMTbVys/Ph5OTE4YPH65Qd0TU3aQ489q5cyeysrIwbdo0DBs2DMXFxW2P/eAHP8DgwYMBAJMnT8aAAQOwe/duAMCFCxeQlZWFKVOmwNfXFw8ePMCRI0dw+PBhxMXFwdvbW5XXQ0S2kyK8PvvsMwBAQUEBCgoKzB4bNWoUcnJyADy+cNjS0tL2WL9+/aDRaJCZmYmamhpotVr4+/sjLS0NM2fO7L4XQESKkyK8WsPpWU6dOmX29cCBA7Fjxw57tEREKpPmmhcR0ZMYXkQkJYYXEUmJ4UVEUmJ4EZGUGF5EJCWGFxFJieFFRFKS4iZVUXAYoVgD+GwdfudI3wsRXgeHEQqKwwiVqcFhhMrtF6UGhxEKjsMIlanBYYTK7RelBocRSoDDCJWpwWGEYvWgRA01hhHygj0RSYnhRURSYngRkZQYXkQkJYYXEUmJ4UVEUmJ4EZGUGF5EJCWGFxFJieFFRFJieBGRlBheRCQlhhcRSYlTJbqAwwjFGsDHYYRi1OAwQsFxGKEyNTiMULn9otTgMELBcRihMjU4jFC5/aLU4DBCCXAYoTI1OIxQrB6UqKHGMEIpwuvLL79EXFxch4+VlJTgueee63R/Xl4edu3ahevXr6Nfv36IjY3FggUL4OTE9yuIZCVFeLVKTk7GyJEjzdZ69uzZ6Z7c3FykpqZi7ty5iIiIQElJCTIyMlBXV4cVK1bYs10isiOpwuvHP/4xgoODLT6+qakJmzdvxtSpU5GSkgIAGD16NOrr65GdnY24uDh4enraq10isiOH/r2puLgY1dXVmD59utl6dHQ0mpqaUFhYqFJnRGQrqc68UlNTUVtbi+effx6jR4/G73//e/j7+z/1eKPRCAAICAgwW9fr9XBxcWl7nIjkI0V49enTB3PmzMGoUaOg0+lw5coVZGdnIzY2Frm5uRg4cGCH+2prawEArq6u7R7T6XRWv61LROqTIrwMBgMMBkPb12FhYRg3bhyioqKQlZWF9PR0FbsjIjVIe81Lr9djxIgRKCkpeeoxrWdcrWdgT6qrq7Pp4wxEpC5pwwt4/LGEzj7O0Hqtq6yszGy9srISDQ0N7a6FEZE8pA2viooKlJSUICgo6KnHBAcHo2/fvsjPzzdbP3jwILRaLSZMmGDnLonIXqS45pWUlARvb28MHToUOp0ORqMR27dvh4uLCxYtWtR2nMFgQHR0NNatWwcA0Gq1SExMxMqVK+Hl5YWIiAiUlpa23ePl5eWl1ksiIhtJEV5DhgzB4cOH8cEHH6C+vh7u7u546aWXsHTpUuj1+rbjmpub0dLSYrZ3xowZ0Gg02LVrF3JycuDh4YElS5YgPj6+u18GESlIivCKj4+3KGwuX77c4XpMTAxiYmJs7oPzvMSaYcV5XmLU4DwvwXGelzI1OM9Luf2i1OA8L8FxnpcyNTjPS7n9otTgPC8JcJ6XMjU4z0usHpSoocY8L2lvlSCi/20MLyKSEsOLiKTUpfCaPXs2PvnkEzx69Mhe/RARWaRL4WU0GpGSkoKf/vSnWL16dacfiiYisqcuhdfZs2eRkZGBkJAQfPTRR4iNjUVUVBR2796Nmpoae/VIRNROl8JLq9ViypQpyM7OxunTp5GYmIjm5makp6dj/PjxSEhIwD//+c8u3atBRGQNqy/Y9+vXDwsWLEBBQQH27duHiIgIHDt2DAsXLsSECROQmZnZrfd8ENH/FpvfbTx37hxycnJw+vRpmEwmBAUFQa/X45133sGUKVNw9uxZJfokIjJj1R32lZWVyMvLwyeffIKqqirodDrExsZi5syZbQP+rly5gqSkJKxbtw4FBQWKNk1E1KXwOnjwIHJzc/Gvf/0LJpMJo0ePRlJSEiZPntzuH38NDAzEnDlzsGrVKkUbJiICuhheKSkpbde6YmJi4Ovr2+nxAQEB7f7NRCIiJXQpvLZt24YJEybAycmyS2VBQUGdjmkmIrJWl8IrMjLSXn1IwRGGERYXFyM8PNym0SUZGRlW17B1/5M1bB1+50jfCw8PD6v2AxxG6PAcZRjhvXv3UFFRYdPQuEGDBlldw9b9jlRDyR7U/NniMELBOcowwmPHjiEhIcHmv+mtrWHrfkeqoWQPav5scRihBBxhGGFTU5PNQ+NsrSFCD6LUUKoHtX+2OIyQiMhCDC8ikhLDi4ikxPAiIikxvIhISgwvIpISw4uIpMTwIiIpMbyISEpS3GGfkpKCjz/+uMPHevbsidLS0qfunT17Ns6fP99ufcGCBVi+fLliPRJR95IivBYvXoxZs2aZrdXV1WHhwoWYOHHiM/f7+fkhPT3dbM3T01PRHomoe0kRXnq9Hnq93mxt7969aGlpwS9/+ctn7u/VqxeCg4Pt1R4RqUDaa14ff/wxPDw8MHbsWLVbISIVSHHm9X3l5eX45ptvEB8fb9Gn4MvLyxEaGor6+nro9XrExsYiLi7O4omwrRxhGGFISAiqqqpsHp9ibQ1b9ztSDSV7UPNni8MIu2Djxo3YuXMnjhw5Aj8/v06P3bJlC7y8vODn54f79+/j6NGjyM/Px6uvvorVq1db/JyOMoxQ7Roi9CBKDRF6UKIGhxFaqLm5GeHh4fDx8cG+ffusqrFq1SocOHAAJ06cgI+Pj0V7HGUYodo1ROhBlBoi9KBEDQ4jtNDZs2dx584dLFu2zOoaUVFR2L9/P0pLSy0OL8AxhhGKUEOEHkSpIUIPStTgMEIL5ObmwsXFBdOmTbO6Rmu6W3uaTUTqkyq87t27h8LCQkyaNAl9+vSxuk5+fj6cnJwwfPhwBbsjou4k1a+Nhw4dwnfffYdXXnmlw8cnT56MAQMGYPfu3QCACxcuICsrC1OmTIGvry8ePHiAI0eO4PDhw4iLi4O3t3d3tk9ECpIqvPLy8tC/f3+8+OKLHT7e3NyMlpaWtq/79esHjUaDzMxM1NTUQKvVwt/fH2lpaZg5c2Z3tU1EdiBdeHXm1KlTZl8PHDgQO3bssGdLRKQSqa55ERG1YngRkZQYXkQkJYYXEUmJ4UVEUmJ4EZGUGF5EJCWGFxFJSaqbVNXmCMMI1a4hQg+i1BChByVqcBih4DiMUJkaIvQgSg0RelCiBocRCo7DCJWpIUIPotQQoQclanAYoQQ4jFCZGiL0IEoNEXpQogaHERIRWYjhRURSYngRkZQYXkQkJYYXEUmJ4UVEUmJ4EZGUGF5EJCWGFxFJieFFRFJieBGRlBheRCQlfjC7CzjPizOslKwhQg9K1OA8L8FxnpcyNUToQZQaIvSgRA3O8xIc53kpU0OEHkSpIUIPStTgPC8JcJ6XMjVE6EGUGiL0oEQNzvMiIrKQquF18+ZNrF27FrNmzcKIESMwZMgQXLlypcNj8/Ly8PLLL2P48OGIjIxEdnY2WlpaLHqeW7duITExEWFhYXjhhRfw+uuvw2g0KvlSiKibqRpe165dQ0FBAfr06YOwsLCnHpebm4s333wT48aNw86dO/HrX/8amZmZ2Lx58zOfo6GhAa+99houXbqEP/7xj9iyZQsePnyI3/72t7h586aSL4eIupGq17zCwsJQVFQE4PGZ1ZkzZ9od09TUhM2bN2Pq1KlISUkBAIwePRr19fXIzs5GXFwcPD09n/ocH374ISoqKvDpp58iICAAABAcHIyJEyciKysLa9asUf6FEZHdqXrm5eT07KcvLi5GdXU1pk+fbrYeHR2NpqYmFBYWdrr/5MmTMBgMbcEFAK6uroiIiMCJEyesa5yIVCf8BfvWa1NPhg8A6PV6uLi4PPPaldFobLe3td6dO3dQU1OjXLNE1G2ED6/a2loAj8+Wvk+n0z3zvpLa2lrodLp26613A7fWJyK5CB9eREQdET68Ws+4OjpDqqure+bnqVxdXVFXV9duvfWMraMzOiISn/Dh1Xq9qqyszGy9srISDQ0NHV7PetLgwYM7vC5WVlaGfv36wd3dXblmiajbCB9ewcHB6Nu3L/Lz883WDx48CK1WiwkTJnS6f+LEifj2229RXl7etlZXV4fCwkJMnDjRHi0TUTdQ/bONR48eBQBcvHgRAFBUVISrV6+iV69eCA8Ph1arRWJiIlauXAkvLy9ERESgtLS07R4vLy+vtlpbt27Ftm3bcPz4cXh7ewMAZsyYgffffx+LFy9GYmIiXFxckJ2dDScnJyxatKj7XzARKUL18EpISDD7Oj09HQDg7e2NU6dOAXgcQBqNBrt27UJOTg48PDywZMkSxMfHm+1tnUn05CfTe/Xqhffeew/p6el466230NTUhJEjR+K9995D//797fzqiMheVA+vy5cvW3RcTEwMYmJiOj1m2bJlWLZsWbt1T09PbNmyxar+nsRhhBzAp2QNEXpQogaHEQqOwwiVqSFCD6LUEKEHJWpwGKHgOIxQmRoi9CBKDRF6UKIGhxFKgMMIlakhQg+i1BChByVqcBghEZGFGF5EJCWGFxFJieFFRFJieBGRlBheRCQlhhcRSYnhRURSYngRkZQYXkQkJYYXEUmJ4UVEUmJ4EZGUOFWiC2wdRhgYGGjTyJCMjAyEh4dLXUOEHkSpIUIPStRo3c9hhIJSYhhhRUWFTcPaHKGGCD2IUkOEHpSooVQPHEZoJ0oMI0xISLD5b0fZa4jQgyg1ROhBiRpK9cBhhHZk6zBCW4e1OUoNEXoQpYYIPShRQ4keuooX7IlISgwvIpISw4uIpMTwIiIpMbyISEoMLyKSEsOLiKTE8CIiKal6k+rNmzexc+dOXLx4Ef/+97/R0NCATz/9FIGBgW3H3L59G3v27EFRURGuXbsGjUYDf39/vP7665g0adIznyMvLw9vvvlmu/Uf/ehHOHfunKKvh4i6j6rhde3aNRQUFGDo0KEICwvDmTNn2h1z6dIlHDp0CNHR0XjhhRdgMpmQn5+PJUuW4K233kJcXJxFz7Vp0yb4+vq2fe3s7KzY6yCi7qdqeIWFhaGoqAjA4zOkjsIrJCQE//jHP8zCZvz48bh9+zaysrIsDq8hQ4aYndERkdxUvebl5PTsp9fpdB2eJQ0bNgzV1dVobGy0R2tEJDhpP5h9/vx5DBo0yOJf/+bOnYu7d+/Czc0N48ePR2JiIjw9Pbv0nLbO87J13pEj1BChB1FqiNCDEjWU6kHaeV6tF9a/f8G+I++//z7S0tKwYcMGREdHd3rsmTNn8NVXX2HEiBHo1asXSkpKsGPHDjz//PPIy8uDu7u7Rf1pNBpcv34dGo3G4tfUymQyobGxEc7Ozlbtd6QaIvQgSg0RelCihlI96PX6Lo3EgUkQubm5psDAQNPly5c7Pe6zzz4zDR061PSHP/zB6ue6cOGCaciQIabMzEyL9wDgH/7hHzv/6Qqpfm08f/48li1bhvHjx+NPf/qT1XVCQkLg7e2NkpISi/eYxDhBJaL/J81Nql999RUWLlyI0NBQbNmyBVqtbblrMpmsPsUlIvVJEV6lpaWIj4/H0KFDsXXrVvTs2dOmehcuXMCNGzcQFBSkUIdE1N1U/7Xx6NGjAICLFy8CAIqKinD16lX06tUL4eHhuHr1KubPnw8XFxe88cYbuHz5stl+g8HQFmZbt27Ftm3bcPz4cXh7ewMA5syZg1GjRiEwMBC9e/dGaWkpduzYgQEDBuA3v/lNN75SIlKS6uGVkJBg9nV6ejoAwNvbG6dOnUJxcXHbUP958+a123/y5En4+PgAePyrYHNzs9n1qcDAQBw6dAhVVVVobGyEh4cHoqKisHTpUpve2iUidQlzqwQRUVdIcc2LiOj7GF5EJCXVr3nJ7P79+9i4cSOOHz+Ohw8f4ic/+QmWL1+O0NBQtVuTzpdffvnUD9mXlJTgueee6+aO5GHJaKlWeXl52LVrF65fv45+/fohNjYWCxYssOhzxqJheFnJZDJh8eLFKC8vR3JyMjw8PJCTk4N58+Zh3759MBgMarcopeTkZIwcOdJszdZbYxydJaOlACA3NxepqamYO3cuIiIiUFJSgoyMDNTV1WHFihXd3LUCunQ/PrU5efKkKTAw0HT69Om2tUePHpkmT55smj9/voqdyemLL74wBQYGmk6dOqV2K9Jpbm5u+++nfcyusbHRNGbMGFNCQoLZekZGhslgMJhu3rzZLb0qSb5zRUGcPHmybUJFq549e2LatGkoKirCw4cPVeyO/pdY8itfcXExqqurMX36dLP16OhoNDU1obCw0F7t2Q3Dy0pGoxEBAQHtPmIUGBiIpqYmXL16VaXO5JaamgqDwYCwsDAsXboU5eXlarfkEIxGIwAgICDAbF2v18PFxaXtcZnwmpeVamtr4e/v327d1dW17XGyXJ8+fdo+DaHT6XDlyhVkZ2cjNjYWubm5GDhwoNotSq3157H15/NJOp2u7UZwmTC8SAgGg8HsTY6wsDCMGzcOUVFRyMrKavvkBVEr/tpoJVdXV9TV1bVb7+xvOOoavV6PESNGdGl0EXWss98I6urqpPyoHMPLSoMHD0ZZWVm7OV9GoxFarRZ+fn4qdeZYTBxdpIjWa11lZWVm65WVlWhoaGh3LUwGDC8rTZw4ETU1NWb31DQ2NqKgoABjxoxB7969VezOMVRUVKCkpISjixQQHByMvn37Ij8/32z94MGD0Gq1mDBhgjqN2YDXvKwUGRmJ0NBQpKamYvny5fDw8MCePXtw48YNbN68We32pJOUlARvb28MHToUOp0ORqMR27dvh4uLCxYtWqR2e8J71mgprVaLxMRErFy5El5eXoiIiEBpaSmys7MRFxcHLy8vNdu3CqdK2KCurg6bNm3CsWPH8PDhQxgMBiQlJSEsLEzt1qSzfft2HD58GP/9739RX18Pd3d3vPTSS1i6dCn0er3a7QlvyJAhHa63jpZq9dFHH2HXrl2orKyEh4cHZsyYgfj4ePTo0aO7WlUMw4uIpMRrXkQkJYYXEUmJ4UVEUmJ4EZGUGF5EJCWGFxFJieFFRFJieBGRlBheRCQlhhcRSYnhRQ6hsbERMTExCAkJQWVlpdljf/3rXzFkyBB8+OGHKnVH9sDwIofg7OyMv/zlLzCZTFi+fDmampoAABcuXEBWVhamTp2KGTNmqNwlKanHmjVr1qjdBJESXF1d4enpiT179qClpQUGgwHz5s1Dnz59kJ2dzX+41sFwnhc5lOjoaJw7dw7bt2/H559/jtu3byMnJwc6nU7t1khhHIlDDufBgwf4+c9/jjt37mDx4sVISEhQuyWyA17zIodTWlqK6upqAMDly5dV7obsheFFDqWmpgbJycnw8fHBwoULcfLkSXzwwQdqt0V2wGte5FBWrlyJ6upq7N27F8OGDcPXX3+NDRs2YPTo0R3+I8EkL555kcPYu3cvTpw4gYSEBAQFBcHJyQkbN26Ei4sLEhMT8d1336ndIimI4UUOoby8HOvXr8eLL76I+fPnt617eXlh7dq1+M9//oONGzeq2CEpje82EpGUeOZFRFJieBGRlBheRCQlhhcRSYnhRURSYngRkZQYXkQkJYYXEUmJ4UVEUvo/Vxplh91UFV0AAAAASUVORK5CYII=\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "zui4ITyyAjFY", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "# @title Functions for building transition matrices and affordance grids.\n", + "\n", + "def build_simple_grid_stochastic_states(\n", + " size=5, terminal_states=[], p_success=1):\n", + " \"\"\"\n", + " Builds a simple grid where an agent can move LEFT, RIGHT, UP or DOWN\n", + " and actions success with probability p_success.\n", + " A terminal state is added if len(terminal_states) > 0 and will return matrix\n", + " of size (|S|+1)x|A|x(|S|+1). Moving into walls does nothing.\n", + "\n", + " Note that this is a function modified from emdp package to customise mdp\n", + " transitions.\n", + "\n", + " Args:\n", + " size: size of the grid world\n", + " terminal_state: the location of terminal states: a list of (x, y) tuples\n", + " p_success: the probabilty that an action will be successful.\n", + "\n", + " Returns:\n", + " P: transition matrix of size (|S|+1)x|A|x(|S|+1).\n", + " \"\"\"\n", + " n_actions = 4\n", + " p_fail = 1 - p_success\n", + "\n", + " n_states = size * size\n", + " # The number of entries in the state vector corresponding to grid itself.\n", + " grid_states = n_states\n", + " # Add an entry to state vector for terminal state.\n", + " if len(terminal_states) > 0:\n", + " n_states += 1\n", + " terminal_states = list(\n", + " map(lambda tupl: int(size * tupl[0] + tupl[1]), terminal_states))\n", + "\n", + " # this helper function creates the state transition list for\n", + " # taking an action in a state\n", + " def create_state_list_for_action(state_idx, action, p_success):\n", + " p_success = p_success\n", + " p_fail = 1 - p_success\n", + " transition_probs = np.zeros(n_states)\n", + " if state_idx in terminal_states:\n", + " # no matter what action you take you should go to the absorbing state\n", + " transition_probs[-1] = 1\n", + " elif state_idx == n_states - 1 and len(terminal_states) > 0:\n", + " # absorbing state, you should just transition back here whatever action you take.\n", + " transition_probs[-1] = 1\n", + "\n", + " elif action in [actions.LEFT, actions.RIGHT, actions.UP, actions.DOWN]:\n", + " # valid action, now see if we can actually execute this action\n", + " # in this state:\n", + " # TODO: distinguish between capability of slipping and taking wrong action vs failing to execute action.\n", + " if check_can_take_action(action, state_idx, size):\n", + " # yes we can\n", + " possible_actions = get_possible_actions(state_idx, size)\n", + " if action in possible_actions:\n", + " transition_probs[get_state_after_executing_action(\n", + " action, state_idx, size)] = p_success\n", + " possible_actions.remove(action)\n", + " for other_action in possible_actions:\n", + " transition_probs[get_state_after_executing_action(\n", + " other_action, state_idx, size)] = p_fail / len(possible_actions)\n", + "\n", + " else:\n", + " possible_actions = get_possible_actions(state_idx, size)\n", + " transition_probs[\n", + " state_idx] = p_success # cant take action, stay in same place\n", + " for other_action in possible_actions:\n", + " transition_probs[get_state_after_executing_action(\n", + " other_action, state_idx, size)] = p_fail / len(possible_actions)\n", + "\n", + " else:\n", + " raise InvalidActionError(\n", + " 'Invalid action {} in the 2D gridworld'.format(action))\n", + " return transition_probs\n", + "\n", + " P = np.zeros((n_states, n_actions, n_states))\n", + " for s in range(n_states):\n", + " for a in range(n_actions):\n", + " # MDP states are considered stochastic such that the probability\n", + " # of success of a s,a pair is different for diff states,\n", + " # in particular between the range of [0.1, 1.0]\n", + " p_success = random.uniform(0.10, 1.0)\n", + " P[s, a, :] = create_state_list_for_action(s, a, p_success)\n", + "\n", + " return P\n", + "\n", + "def _unit_test_P(P):\n", + " assert np.allclose(P.sum(axis=2), 1), 'P matrix is not stochastic!'\n", + "\n", + "\n", + "def build_affordance_grid(\n", + " affordances, size=13, p_success=1.0, terminal_states=()):\n", + " \"\"\"Builds a backbone for intent induced mdp transition matrix\n", + "\n", + " Args:\n", + " affordances: AF of shape |S| * |A|\n", + " size: The grid size of the mdp.\n", + " p_success: The probability of success for a transition.\n", + " mdp: Environment specified as mdp.\n", + " terminal_states: List of terminal states.\n", + "\n", + " Returns:\n", + " Returns model dynamics array of shape |S| x |A| x |S|\n", + " \"\"\"\n", + " p_fail = 1 - p_success\n", + "\n", + " n_states = size * size\n", + " grid_states = n_states # the number of entries of the state vector\n", + " # corresponding to the grid itself.\n", + " if len(terminal_states) > 0:\n", + " n_states += 1 # add an entry to state vector for terminal state\n", + " terminal_states = list(\n", + " map(lambda tupl: int(size * tupl[0] + tupl[1]), terminal_states))\n", + "\n", + " def create_state_list_for_action(state_idx, action):\n", + " transition_probs = np.zeros(n_states)\n", + " if state_idx in terminal_states:\n", + " # no matter what action you take you should go to the absorbing state\n", + " transition_probs[-1] = 1\n", + " elif state_idx == n_states - 1 and len(terminal_states) > 0:\n", + " # absorbing state, you should just transition back here whatever action you take.\n", + " transition_probs[-1] = 1\n", + "\n", + " elif action in [actions.LEFT, actions.RIGHT, actions.UP, actions.DOWN]:\n", + " # valid action, now see if we can actually execute this action\n", + " # in this state:\n", + " if check_can_take_action(action, state_idx, size):\n", + " # yes we can\n", + " possible_actions = get_possible_actions(state_idx, size)\n", + " if action in possible_actions:\n", + " transition_probs[get_state_after_executing_action(\n", + " action, state_idx, size)] = p_success\n", + " possible_actions.remove(action)\n", + " for other_action in possible_actions:\n", + " transition_probs[get_state_after_executing_action(\n", + " other_action, state_idx, size)] = p_fail / len(possible_actions)\n", + "\n", + " else:\n", + " possible_actions = get_possible_actions(state_idx, size)\n", + " transition_probs[\n", + " state_idx] = p_success # cant take action, stay in same place\n", + " for other_action in possible_actions:\n", + " transition_probs[get_state_after_executing_action(\n", + " other_action, state_idx, size)] = p_fail / len(possible_actions)\n", + " else:\n", + " raise InvalidActionError(\n", + " 'Invalid action {} in the 2D gridworld'.format(action))\n", + " return transition_probs\n", + "\n", + " n_states = size * size\n", + " n_actions = 4\n", + " if len(terminal_states) > 0:\n", + " n_states += 1 # add an entry to state vector for terminal state\n", + "\n", + " P = np.zeros((n_states, n_actions, n_states))\n", + " for s in range(n_states):\n", + " for a in range(n_actions):\n", + " if affordances[s, a] != 0.:\n", + " P[s, a, :] = create_state_list_for_action(s, a)\n", + " else:\n", + " P[s, a, s] = 1.0\n", + " return P\n", + "\n", + "\n", + "def _construct_dynamics(mdp, affordances, size, p_success=1.0, wall_locs=None):\n", + " \"\"\"\n", + " Function to construct transition dynamics P\n", + " Args:\n", + " mdp: The mdp.\n", + " affordances: The wall locations of the mdp.\n", + " size: The grid size of the mdp.\n", + " p_success: Probability of success.\n", + " wall_locs: The location of the walls in the mdp.\n", + "\n", + " Returns:\n", + " P: initialized transition matrix of shape |S| x |A| x |S|\n", + " \"\"\"\n", + "\n", + " if wall_locs is None:\n", + " raise ValueError('Please give me wall locations.')\n", + "\n", + " grid_size = mdp.size\n", + "\n", + " assert len(mdp.terminal_states) == 1, 'Only one terminal state supported.'\n", + " goal_loc = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(mdp.terminal_states[0], mdp.state_space))\n", + " # Attempt to make the desired gridworld.\n", + " reward_spec = {(goal_loc[0], goal_loc[1]): +1}\n", + "\n", + " tmb = TransitionMatrixBuilder(grid_size, has_terminal_state=True)\n", + "\n", + " # For the purposes of constructing the dynamics matrix\n", + " # and to match the way the library deals with MDPs, we set the\n", + " # walls to have affordable actions.\n", + " # affordances = affordances.copy()\n", + " # affordances[wall_locs, :] = 1\n", + " terminal_states = reward_spec.keys()\n", + " basic_affordance_grid = build_affordance_grid(\n", + " affordances,\n", + " size=mdp.size,\n", + " p_success=p_success,\n", + " terminal_states=terminal_states)\n", + "\n", + " tmb._P = basic_affordance_grid\n", + " for (r, c) in wall_locs:\n", + " tmb.add_wall_at((r, c))\n", + " P = tmb.P\n", + " _unit_test_P(P)\n", + " _checking_P(P)\n", + " return P\n" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "cT39F8t1VHLQ", + "colab_type": "text" + }, + "source": [ + "## Affordances" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "A4sm9YlzrSP3", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Function to compute intent completion.\n", + "def _get_intent_completed(\n", + " mdp, state, action, statet,\n", + " threshold=0.0,\n", + " intent_name='collection',\n", + " P_AF=None):\n", + " \"\"\"Determines if a transition completed an intent.\n", + "\n", + " Args:\n", + " mdp: The MDP to evaluate the intent on.\n", + " state: The one hot representation of the current state\n", + " action: The integer representation of the action (currently unused).\n", + " statet: The state after taking the action.\n", + " threshold: The threshold to select actions with\n", + " intent_name: The name of the intent to calculate. Three intents are\n", + " currently supported \"collection\", \"up\" and \"left\". Collection is the union\n", + " over all intents.\n", + " P_AF: A probability transition matrix for the affordances.\n", + "\n", + " Returns:\n", + " An integer that represents if the intent is completed.\n", + " \"\"\"\n", + " x_t, y_t = mdp.unflatten_state(state)\n", + " x_tp1, y_tp1 = mdp.unflatten_state(statet)\n", + "\n", + " delta_x = x_t - x_tp1\n", + " delta_y = y_t - y_tp1\n", + "\n", + " state_int = convert_onehot_to_int(state)\n", + " next_state_int = convert_onehot_to_int(statet)\n", + "\n", + " # Select affordable actions based on the threshold.\n", + " # Default value is True for default threshold=0.0\n", + " prob_gt_threshold = True\n", + " if P_AF is not None:\n", + " prob_of_going_there = P_AF[state_int, action, next_state_int]\n", + " else:\n", + " prob_of_going_there = mdp.P[state_int, action, next_state_int]\n", + " prob_gt_threshold = prob_of_going_there >= threshold\n", + "\n", + " if intent_name == 'collection':\n", + " # If the agent has moved in any direction return a 1.0.\n", + " if (int(delta_x) or int(delta_y)) and prob_gt_threshold:\n", + " return 1.0\n", + " else:\n", + " return 0.0\n", + " elif intent_name == 'up':\n", + " if not(_is_absorbing(next_state_int, mdp.size)):\n", + " if (x_tp1 < x_t) and prob_gt_threshold:\n", + " return 1.0\n", + " else:\n", + " return 0.0\n", + " else:\n", + " if action==actions.UP:\n", + " return 1.0\n", + " else:\n", + " return 0.0\n", + " elif intent_name == 'left':\n", + " if not(_is_absorbing(next_state_int, mdp.size)):\n", + " if (y_tp1 < y_t) and prob_gt_threshold:\n", + " return 1.0\n", + " else:\n", + " return 0.0\n", + " else:\n", + " if action==actions.LEFT:\n", + " return 1.0\n", + " else:\n", + " return 0.0\n", + " else:\n", + " print(\"Not a valid Intent, See _get_intent_completed\")" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "colab_type": "code", + "id": "nCjcsWdWTJmp", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Function to compute the affordances from an intent completion function.\n", + "def _compute_affordances(\n", + " mdp, n_states, n_actions, intent_name,\n", + " threshold, mdp_wall_locs):\n", + " '''\n", + " Args:\n", + " n_states: number of states in mdp\n", + " n_actions: number of actions in mdp \n", + " intent_name: name of the intent collection, up, etc.\n", + " threshold: float value between 0-1\n", + " mdp_wall_locs: list of mdp wall locations\n", + "\n", + " Returns:\n", + " Affordances in the form of a |S| * |A| array. The array has entries of\n", + " 1.0 or 0.0 based on the intent I_a(s') is true or not respectively.\n", + " '''\n", + " affordances = np.zeros((n_states, n_actions))\n", + " for s in range(n_states):\n", + " x_t, y_t = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(s, mdp.state_space))\n", + " if (x_t, y_t) in mdp_wall_locs:\n", + " # You're in a wall nothing is affordable.\n", + " continue\n", + " for a in range(n_actions):\n", + " if not(_is_absorbing(s, mdp.size)):\n", + " s_next = get_state_after_executing_action(a, s, mdp.size)\n", + " x, y = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(s_next, mdp.state_space))\n", + " if (x,y) in mdp_wall_locs:\n", + " s_next = s\n", + " intent = _get_intent_completed(\n", + " mdp, state=convert_int_rep_to_onehot(s, mdp.state_space),\n", + " action=a, statet=convert_int_rep_to_onehot(\n", + " s_next, mdp.state_space),\n", + " threshold=threshold, intent_name=intent_name)\n", + " affordances[s, a] = intent\n", + "\n", + " # Hard code terminal states to be affordable since all actions are possible.\n", + " if s in mdp.terminal_states:\n", + " affordances[s, :] = 1.0\n", + " # Hard code absorbing state to be affordable (Environmental quirk).\n", + " affordances[-1, :] = 1.0\n", + " return affordances" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "kUZ6lanOJcTV", + "colab_type": "text" + }, + "source": [ + "## Value Iteration and Policy Evaluation algorithms" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "E5delmlK9xFf", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Code for value iteration.\n", + "# We build this function on the following base code:\n", + "# https://github.com/andrecianflone/policy_value_iteration\n", + "def value_iteration(\n", + " r, p, theta=0.0001, gamma=0.99, max_iteration=100,\n", + " AF=None, seed=None, min_mask_value=-10, mdp_wall_locs=None):\n", + " \"\"\"Value iteration computes value & policy for a reward and transiton matrix.\n", + "\n", + " Args:\n", + " r: Rewards, array of shape |S| x |A|.\n", + " p: State transition probabilities, array of shape |S| x |A| x |S|.\n", + " theta: Stop if the change in value fn is less than this value.\n", + " gamma: Discount factor.\n", + " max_iteration: Maximum number of iterations to run VI.\n", + " AF: Affordances of shape |S| x |A|.\n", + " seed: Seed value for randomness.\n", + " min_mask_value: An optional check for values to not be negative.\n", + " mdp_wall_locs: Wall locations in the mdp.\n", + "\n", + " Returns:\n", + " pi: Policy, |S| x |A|.\n", + " v: State values, |S|.\n", + " it: Number of iterations.\n", + " seconds: Planning time in seconds.\n", + " v_log: Log of value functions from init to convergence.\n", + " \"\"\"\n", + " if AF is not None:\n", + " AF = AF.copy()\n", + " assert mdp_wall_locs is not None, 'If AF is given, wall locs must also be given.'\n", + " if seed is not None:\n", + " np.random.seed(seed)\n", + " random.seed(seed)\n", + " t1 = datetime.now()\n", + " n_states, n_actions = p.shape[:2]\n", + " v = np.zeros(n_states)\n", + " v_log = np.zeros((max_iteration + 1, n_states))\n", + "\n", + " if mdp_wall_locs:\n", + " wall_states_idx = list(\n", + " map(convert_onehot_to_int, map(mdp.flatten_state, mdp_wall_locs)))\n", + "\n", + " # Mask out walls to be zero. But uncovered states should be -inf.\n", + " if AF is not None:\n", + " # Accounting for emdp environment behaviour.\n", + " AF[wall_states_idx] = 1\n", + " mask = np.logical_not(AF).astype(np.float32)\n", + " for it in range(max_iteration+1):\n", + " q = r + gamma * np.einsum('ijk, k->ij', p, v)\n", + " minimum_v = np.min(q)\n", + "\n", + " if AF is not None:\n", + " q = AF * q + mask * minimum_v\n", + " \n", + " v_new = np.max(q, axis=1)\n", + " if np.all(np.absolute(v-v_new) < theta):\n", + " v = v_new\n", + " v_log[it, :] = v\n", + " v_iters = v_log[:it, :]\n", + " break\n", + " v = v_new\n", + " v_log[it, :] = v\n", + " v_iters = v_log[:it, :]\n", + "\n", + " # Greedy policy extraction.\n", + " q_values = r + gamma * np.einsum('ijk, k->ij', p, v_new)\n", + " if AF is not None:\n", + " q_values = q_values * AF + mask * minimum_v\n", + "\n", + " # Use \"random\" argmax with stochastic tie-breaking:\n", + " rargmax = lambda arr: np.random.choice(np.flatnonzero(arr))\n", + " best_actions = np.apply_along_axis(\n", + " rargmax, 1, np.isclose(q_values, q_values.max(-1, keepdims=True)))\n", + " pi = np.eye(r.shape[1])[best_actions]\n", + " assert pi.shape == r.shape\n", + " \n", + " t2 = datetime.now()\n", + " seconds = (t2 - t1).total_seconds()\n", + " return pi, v, it+1, seconds, v_log" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "72u1xS4tKM7R", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Code for policy evaluation.\n", + "\n", + "def _policy_evaluation_exact(pi, r, p, gamma=0.99):\n", + " \"\"\"\n", + " Evaluate policy by taking the inverse\n", + " Args:\n", + " pi: Policy, array of shape |S| x |A|.\n", + " r: Rewards, array of shape |S| x |A|.\n", + " p: State transition probabilities, array of shape |S| x |A| x |S|.\n", + " Return:\n", + " v: 1D array with updated state values\n", + " \"\"\"\n", + " # Rewards according to policy: Hadamard product and row-wise sum\n", + " r_pi = np.einsum('ij,ij->i', pi, r)\n", + "\n", + " # Policy-weighted transitions:\n", + " # multiply p by pi by broadcasting pi, then sum second axis\n", + " # result is an array of shape |S| x |S|\n", + " p_pi = np.einsum('ijk, ij->ik', p, pi)\n", + " v = np.dot(inv((np.eye(p_pi.shape[0]) - gamma*p_pi)), r_pi)\n", + " return v" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "xVH35aHyLfj9", + "colab_type": "text" + }, + "source": [ + "#Sec 6.1 Experiment 1: Planning with Intents\n", + "Evaluating the impact of intents and affordances on planning. $||V^{\\pi^{*}_{I}}_{M} - V^{\\pi^{*}}_{M}||_{n}$" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "LG2pxLBcSqC2", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 329 + }, + "outputId": "6af0376f-9a4d-4488-bd61-cdd0850ee95e" + }, + "source": [ + "#@title Evaluate policy obtained from MDP M vs I in original MDP M\n", + "#------------------------------------------------------------------------#\n", + "# 1. Compute optimal value function and optimal policy in I \n", + "# 2. Policy Evaluation in MDP M\n", + "# 3. Report planning time in M vs I \n", + "# 4. Report L2 value loss \n", + "#------------------------------------------------------------------------#\n", + "\n", + "p_success_probs = [1.0, 0.75, 0.50, 0.40, 0.30, 0.25]\n", + "thresholds = [0, 0.15, 0.25, 0.35, 0.45, 0.65, 0.75, 1.0]\n", + "max_iterations = 10000\n", + "\n", + "# Initialize empty arrays to store results.\n", + "valuefn_I = np.zeros(\n", + " (len(p_success_probs), len(thresholds), max_iterations+1, mdp.state_space))\n", + "v_pi_star_I_M_thresholds = np.zeros(\n", + " (len(p_success_probs), len(thresholds), mdp.state_space))\n", + "V_star_I_plan_time = np.zeros((len(p_success_probs), len(thresholds)))\n", + "V_star_M_plan_time = np.zeros(len(p_success_probs))\n", + "v_pi_star_M_M = np.zeros((len(p_success_probs), mdp.state_space))\n", + "\n", + "# Stores the ratio of |AF|/(|S| x |A|)\n", + "AF_SA_size_ratio = np.zeros((len(p_success_probs), len(thresholds)))\n", + "\n", + "# Iterate over the thresholds and success probs to create data for the plot.\n", + "for idx_p, p_success in enumerate(p_success_probs):\n", + " for ind, k in enumerate(thresholds):\n", + " # Create an mdp with a success prob of p_success\n", + " mdp, mdp_wall_locs = build_one_room_example(p_success=p_success)\n", + "\n", + " # |S| x |A| size in base MDP M.\n", + " state_action_space = mdp.state_space * mdp.action_space\n", + "\n", + " # Compute affordance matrix.\n", + " AF = _compute_affordances(\n", + " mdp=mdp,\n", + " n_states=mdp.state_space,\n", + " n_actions=mdp.action_space,\n", + " intent_name=\"collection\",\n", + " threshold=k,\n", + " mdp_wall_locs=mdp_wall_locs)\n", + " \n", + " # |S| x |A| size in intended MDP M_I\n", + " AF_size = np.count_nonzero(AF)\n", + " AF_SA_size_ratio[idx_p, ind] = AF_size/state_action_space\n", + " \n", + " #construct P_I with a determinsitic probability\n", + " P_affordances = _construct_dynamics(\n", + " mdp, affordances=AF, size=mdp.size, p_success=1.0,\n", + " wall_locs=mdp_wall_locs)\n", + "\n", + " _checking_P(P_affordances)\n", + "\n", + " try:\n", + " # Compute optimal value function in MDP I with affordances\n", + " (policy_star_I, V_star_I, _,\n", + " V_star_I_seconds, V_star_I_iters) = value_iteration(\n", + " mdp.R, P_affordances, max_iteration=max_iterations,\n", + " AF=AF, mdp_wall_locs=mdp_wall_locs)\n", + " except RuntimeError:\n", + " print(f'No affordances found for threshold={k}, p_success={p_success}.')\n", + " continue\n", + " \n", + " V_star_I_plan_time[idx_p, ind] = V_star_I_seconds\n", + " \n", + " # Evaluate the optimal policies from MDP I in the original mdp M.\n", + " v_pi_star_I_M_thresholds[idx_p, ind, :] = _policy_evaluation_exact(\n", + " pi=policy_star_I, \n", + " r=mdp.R, p=mdp.P, \n", + " gamma=mdp.gamma)\n", + " \n", + " # Compute optimal value function in original mdp.P\n", + " V_star_M = np.zeros(mdp.state_space)\n", + " (policy_star_M, V_star_M, _,\n", + " V_star_M_seconds, V_star_M_iters) = value_iteration(\n", + " mdp.R, mdp.P, max_iteration=max_iterations)\n", + "\n", + " V_star_M_plan_time[idx_p] = V_star_M_seconds\n", + "\n", + " # Evaluate the optimal policies in M in the original environment M \n", + " v_pi_star_M_M[idx_p] = _policy_evaluation_exact(\n", + " pi=policy_star_M, \n", + " r=mdp.R, \n", + " p=mdp.P, \n", + " gamma=mdp.gamma)\n", + "\n", + "# Compute the absolute error wrto the ground mdp.\n", + "value_loss_to_plot = np.zeros((len(p_success_probs), len(thresholds)))\n", + "for p_id in range(len(p_success_probs)):\n", + " for _thresh in range(len(thresholds)):\n", + " value_loss_to_plot[p_id, _thresh] = np.linalg.norm(\n", + " abs(v_pi_star_M_M[p_id, :] - \n", + " v_pi_star_I_M_thresholds[p_id, _thresh, :])\n", + " )\n", + "\n", + "fig, ax = plt.subplots()\n", + "x_axis = np.linspace(0, 1, len(thresholds))\n", + "for i in range(value_loss_to_plot.shape[0]):\n", + " ax.plot(\n", + " x_axis, value_loss_to_plot[i, :], color=colors[i],\n", + " label='p={}'.format(p_success_probs[i]), linewidth=3.00,\n", + " marker=markers[i], markersize=14)\n", + "\n", + "ax.legend(loc='upper center', bbox_to_anchor=(0.5, -0.19),\n", + " fancybox=True, shadow=True, ncol=7,\n", + " facecolor='w', fontsize=10)\n", + "ax.set_xlabel(\"Threshold $\\kappa$\", fontsize=18)\n", + "ax.set_ylabel(\"$ ||V^{\\pi^{*}_{I}}_M - V^{*}_M||_2$\", fontsize=18) \n", + "plt.title(\"Planning with Intents\")\n", + "matplotlib.rc('axes', edgecolor='black')\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeUAAAE4CAYAAABlkOuTAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzsvXl0JPd13/u5vaKxr7MPMMPZyZnhNrQomaRkyZZlyVaUJ7+YPrGTvJhP2RTbibckjhJbthVbebFjnyi2aMrPiS2bTrwvkp4s05RIWyNyhsPhMvsGzGDf1250d9V9f1Sj0Q00gAbQQHUD93NOHdTyq6qLQqO/de/v/u5PVBXDMAzDMPwn4LcBhmEYhmF4mCgbhmEYRplgomwYhmEYZYKJsmEYhmGUCSbKhmEYhlEmmCgbhmEYRplgomxsG0TkRRF5ZhPu82si8omNvs8y939SRK4uc/yAiKiIhDbTLsMwVsZE2dhSiMgdEYmLyJSI9IvIb4pI7WbaoKr/VFV/ZjPvueD+L6nqsbntzDP51rVeL/MMf7bItj8lIr+91nsVuN6mvEgZRrlgomxsRb5LVWuBR4AzwL/32R7DMIyiMFE2tiyq2g18ETi58JiIHBKRF0RkWESGROTzItKYc/yOiPyoiLwhIuMi8nsiUpU59h4RuSciPyIiAyLSKyL/V865Wc+yiLYtIvJnIjIhIq+KyM+KyMuFfh8R+R8i8iOZ9b2ZEPS/yPl9RkQkMHfPzP7fAtqBP8tED34855J/X0S6Mr//TxbzTHNC3/9w4bki8gHg3wHfk7nXxcz+BhH5XOZ37878jsHMsX8kIi+LyP8jIqMicltEviNz7OeAJ4H/lrnefxOPX8o8ywkReVNEFv19DaNSMVE2tiwish/4IHCh0GHgPwF7gBPAfuCnFrT5e8AHgIPAaeAf5RzbBTQAe4EfAD4jIk1LmLJc288A05k2/zCzLMVXgfdk1t8N3AKeytl+SVXd3BNU9fuBLjLRA1X9dM7hJ4BjwPuA/yAiJ5a590IWnauqXwI+Bfxe5l4PZtr+JpAGDgMPA+8HckPS7wCuAq3Ap4HPiYio6k8CLwEfz1zv45lznwKO4j3TvwcMr8JuwyhrTJSNrcgfi8gY8DKekH1qYQNVvaGqf6mqs6o6CPwinrDl8iuq2qOqI8CfAQ/lHEsBn1TVlKp+AZjCE6lCFGyb8RY/CvxHVZ1R1UvA/1jm9/oq8ISIBPCE6dPAN2eOvTtzfDX8tKrGVfUicBF4cKUTVnuuiOzEezH6YVWdVtUB4JeAp3Oadarqr6uqg/f77wZ2LnHfFFAHHAdEVS+rau8q7DaMssZE2diKfERVG1W1Q1X/uarGFzYQkZ0i8nwmnDoB/Daep5ZLX876DJCbMDasqulljlNE2zYgBNzNOZa7noeq3sTzqh/CC+v+OdAjIsdYmygv9/uV6twOIAz0ishY5mXps8COQtdS1ZnMasHrqeoLwH/DizAMiMizIlK/CrsNo6wxUTa2K58CFDilqvXA9+GFtDeTQbyw7r6cfftXOOerwHcDkUyf+VfxQt5NwOtLnLOZU8EtvNddYBZozbwoNapqvao+sMbroaq/oqqPAvfjhbF/bF0WG0YZYaJsbFfq8MLI4yKyFx++2DPh2j8EfkpEqkXkOPAPVjjtq8DHga9ltl/MbL+cuV4h+oH71m9xUfQDBzIhdjKh5S8D/0VE6jOJaIdEZGFXwXLXy9ouIo+JyDtEJIwXNUgA7lInG0alYaJsbFd+Gm/I1DjwF3ji6Acfx0tY6gN+C/hdPM9yKb6K90IxJ8ovA9U524X4T8C/z4SPf3TdFi/P/878HBaR1zLr/wCIAJeAUeD38fqNi+GXge/OZGb/ClAP/HrmOp14SV7/uUS2G4bviOpmRrYMw1gOEfkFYJeqLpeFbRjGFsU8ZcPwERE5LiKnM+NvvwlvyNQf+W2XYRj+YLVvDcNf6vBC1nvw+k//C/AnvlpkGIZvWPjaMAzDMMoEC18bhmEYRplgomwYhmEYZYL1Ka+S1tZWPXDggN9mGIZhVAznz58fUtU2v+2oBEyUV8mBAwc4d+6c32YYhmFUDCLS6bcNlYKFrw3DMAyjTDBP2TAMwwDgZ/7400wlplZ9Xm1VLZ/4yI+v3NBYEfOUDcMwDIA1CfJ6zjMWY6JsGIZhGGWCibJhGIZhlAkmyoZhGIZRJpgoG4ZhGEaZYKJsGIZhGGWCibJhGIZhlAkmyoZhGIZRJpgoG4ZhGEaZsK1FWUSOicjrOcuEiPyw33YZhmEY25NtXWZTVa8CDwGISBDoBv7IV6MMwzCMbcu29pQX8D7gpqrabCaGYRiGL5goz/M08Lt+G2EYhmFsX0yUARGJAB8G/vcSxz8mIudE5Nzg4ODmGmcYhrFJ1FbVbup5xmJEVf22wXdE5O8A/0JV379S2zNnzui5c+c2wSrDMAz/eOveJX7r5ecBONDazj/71mfWfC0ROa+qZ0pl21bGPGWP78VC14ZhGFm6hu5m1zta9/toyfZi24uyiNQA3wb8od+2GIZhlAudOaLcbqK8aWzrIVEAqjoNtPhth2EYRrmQdtLcG+3Jbne0mChvFtveUzYMwzDy6R3rJ+2kAWiqaaQuVuezRdsHE2XDMAwjj67hnP5k85I3FRNlwzAMIw/rT/YPE2XDMAwjj9zM63bzlDcVE2XDMAwjy0R8ktGZMQBCwRC7G3f6bNH2wkTZMAzDyJLbn7yvaQ+h4LYfpLOpmCgbhmEYWaxoiL+YKBuGYRhZLMnLX0yUDcMwDMCKhpQDJsqGYRgGYEVDygETZcMwDAOwoiHlgImyYRiGAVh/cjlgomwYhmEAlnldDpgoG4ZhGHlFQ8LBMLsbd/ls0fbERNkwDMPI85L3Ne8hGAj6aM32xUTZMAzDoHN46XrX49evc/5nfobx69c326xth4myYRhbAhOO9bFUf/L49etcee45kqOjXHnuOXu+G4yJsmEYFY8Jx/pIO2nujcwXDZnLvJ57rm4qBYCbStnz3WBMlA3DqGhMONZP71gfadcrGtJc00RdVe2i5zqHPd+NZduLsog0isjvi8gVEbksIu/02ybDMIrDhKM0LByfvNRzncOe78ax7UUZ+GXgS6p6HHgQuOyzPYZhFIEJR+nITfLqSESWfa5z2PPdGLa1KItIA/AU8DkAVU2q6pi/VhmGsRIrCfIcJhzFMZfk1TqRJvDFl1d8rnPY8y0921qUgYPAIPD/isgFEXlORGoWNhKRj4nIORE5Nzg4uPlWGoaRpVhBnsOEY3km4hOMzYzTOpHm8WsJNJ1e1fn2fEvLdhflEPAI8Kuq+jAwDfybhY1U9VlVPaOqZ9ra2jbbRsMwMqxWkOcw4ViazqG7WUEOuWu7hj3f0rHdRfkecE9Vv5HZ/n08kTYMo8xYqyDPYcJRmO63Lq5LkOew51satrUoq2ofcFdEjmV2vQ+45KNJhmEUYL2CPIcJx2JiL11ctyDP4aZS3Hj++dJcbJuyrUU5w78EPi8ibwAPAZ/y2R7DMBZw4/nn1y3Ic5hwzJN20rzWEcGR0lwvEA5z+OmnS3Oxbcq2F2VVfT3TX3xaVT+iqqN+22QYRj6Hn36aQDhckmtJKMQhEw6mu7u59PznefTGNEFd//UC4TDHn3mGhiNH1n+xbUzIbwMMwzBWouHIEY4/80xJQtiaTnPnD/6Atsceo/XRR4k2NpbIyvLHmZ1l6MIFBs6eZaqrC4BSvOqYIJcOUS3BK9I24syZM3ru3Dm/zTCMbUmp+paziNBw5Ahtjz1G86lTBCOR0ly3zJi6e5eBs2cZfO013NnZxcejQs3Dpzl2/GFufP7zq3q+xQiyiJxX1TNrMn6bYZ6yYRgVw1o95kA4zP4PfpCZnh6GL17ETSa9A6qMX7vG+LVrBKNRWh56iLbHHqPu4EFEStTR6hPpRILh116j/+xZpu/dW3RcgkH6WiJcb1aG6oL8y2/9Nlqa9xCKxYp+vuYhlx7zlFeJecqG4T+r8ZgXCoczO8vIm28y+OqrjN+4AQW+A6MtLbSdOUPbmTNUtbSU3P6NQlWZ6upi4OxZhi5cmH/5yCG2Ywc73vlOqh44yi985VcBCAfD/PRH/x3BQBAo7vmuRpDNUy4eE+VVYqJsGOXB+PXrXH72WdRxlmyzknDMjo4yeO4cg+fOkViiWl/9oUO0PfYYLadPE6yqKont4Nl/4/nnOfz00+v2NNPxOEPnz9N/9iwzPT2LjksoRMtDD7Hz8cezUYA3777Nb//N7wFwsK2Df/q+H1hk31LCvFoP2US5eEyUV4mJsmGUD5c++1nGr14teGw1wqGqTHV2MvjqqwxduICTSCy+XiRC86lTtD32GA2HDyOBtQ9eyRW8tYaA52zu//rXGX799YLiGdu1i52PP07ro48SrsmvIPznF77ES1f/FoD3nHiC73jw/cvaOcda7DVRLh7rUzYMoyJR12Wmu7vgsTV4ctQdOEDdgQMc+MhHGHnrLQZffZWxq1ez4W03mWTo/HmGzp8n0tiYDW/HduxYld1Lzf9crL3pmRkGz52j/+xZ4n19i44HwmHPK37nO6nt6Fiyb7wrZ2ao9pb9Bdss7MO3PuSNx0TZMIyKZOruXVJTUwAEqqrAcUoiHIFwmNaHH6b14YdJTkwwdP48A6++mieAybExur/yFbq/8hVqOzq84VUPPUSounrZa680//NSdqsqk7dve17xxYsFJ42o3r2bne96F62PPEIoFlvWjrSTpnukN7vd0VpYlGFemEsVajeWp6LC1yLy3cATwBvA/1TVdM6xv1DVD220DRa+Nozy4O6XvsS9L38ZgLbHHqPtzJkNEw5VZbq72wtvv/Ya6enpRW0kFKL55Enazpyh8dgxJBjMO76W5KnU1BSD584xcPYs8YGBxe0jEVoffpgdjz9ObXt70RnjXcN3+cxf/joAzTVN/MR3/auizlsrFr4unorxlEXk48C/B/4U+DHgn4jId6jqSKbJk74ZZxjGpjN6ab5MfdOJEzQcOcKjn/jEhtxLRKjdt4/affvo+K7vYuzyZQZefZWxS5dQ1yscrek0w6+/zvDrrxOuq6P10UdpO3OGmj17Vj3/8/4PfpCpri5G3nijYCJbzb597Hz8cVoeeYTQGpLPOofmQ9fLecnG5lMxogx8HPh2Vb0oIiHgM8ALIvLejDBX9qBCwzCKJjkxkR17K4EADceOrXBG6QiEQjSfOkXzqVOkpqYYunCBwVdfzRsLnJqcpPfFF+l98UWqWluZHR1dNks8FzeVovNP/mTxfaNR2h55xPOK969PSLuG5m1tN1EuKypJlHer6kWATNj6n4jILwJ/LSLvBSonDm8YxrrI9ZLrDh5csQ91owjX1rL7ySfZ/eSTzPT2MvDqqwydP09qcjLbJjE0tK571La3s+Pxx2l9+GGC0eh6TQagMyfJq2OJJC/DHypJlIdE5KCq3p7boar/WkT+K/DXVNbvYhjGOhi7fDm73nT//T5aMk/17t0c+PCH6fjQhxi7do2eF15g4ubNdV1TQiHaP/ShkvaRj89MMD4zDnhFQ3Y17izZtY31U0mzRP0V8I8W7lTVHwZeBEo3qt8wjLLFTacZu3Ytu10uojyHBIMEQqHshA/rQdPpks//nOsl72vek63iZZQHlSTKHwd+odABVf1B4MCmWmMYhi9M3LqVnVQh2tJC1SrHCW8G5Tz/c5cleZU1FSPKqppU1Zlljq//tdQwjLJn9O23s+tNJ06U5cQRpZz/ORAOc7iE8z/nZl4vVTTE8I+KEWXDMAwoz/7khcwV3FivMJe6glbaSdMzWlzREMMfTJQNw6gY4gMD2WzmQCRC/aFDPlu0NOsV5o0oadkz1kva9WouNdc0UVtVW7JrG6WhbDKWReSFElxGVfV9q7zvHWAScIC0VZ0xjHmc+CSjZ/+Ypsf/LsGY/1/gozlecsPRoyULEW8U65n/eSNqTFvRkPKnnDzlAF4BkPUsa/19vkVVHzJBNox8pi69TGrwLlOXXvbbFADGFlTxqgRW6zFv5KQPVjSk/CkbT1lV3+O3DYZhzOPEJ5m5/QagzNy5SO39T/jqLacTibxxv40VIspQvMe80bMwWdGQ8qecPGW/UODLInJeRD7mtzGGUS543nGmUJ6q797y+NWr2TrT1Xv3Em1s9NWe1bKSx7zRgmxFQyoDE2V4QlUfAb4D+Bci8tTCBiLyMRE5JyLnBgcHN99Cw9hksl6ym6nX7DrM3LmIE5/yzabc/uRKCV0vZClh3ox5inO95P3Ne61oSJmy7UVZVbszPweAPwK+qUCbZ1X1jKqeaWtr22wTDWPTyfOS5/DRW1bXrYihUMWwUJg3Q5Ahv2jIavuTnfgkQ3/9W76+lG0XyqZP2Y/saxGpAQKqOplZfz/wyRLYYRgVyyIveQ7XYebmeZzEJKH6VkI1jQTnlup6ZAM9r+nu7uwkD6GaGmrb2wvaXU6Z4ssxJ8wbNf9zIdaTeZ2b8Nfw6AdKbZqRQ9mIMp7Xvt6ZnlZb2mcn8EeZikAh4HdU9UvrtMEwKpqCXnIOs93XmO2+lr9ThGCsnmBNw7xQ1zRmhLuBQKxuXZW3cqt4NZ44gQQWB/kqTTg2cv7nhaSdNN2jPdnt9pZ9RZ9bbgl/W52yEWU/sq9V9Rbw4Gbf1zDKlSW95JVQxZkZx5kZh8ECFW8DQYLVDRmhzhVubz0QrV5WtFfqTzbhWJ7u0V6czN+0pbZ5VUVDCiX8VcJLT6VSNqJsGIb/rOQlAyBCqGEn4YZW0tNjONNjuCv1NboOztQIztQIyUKXDIUJVjfmedqh2kaC1Q04bpDpu5nQayBA4/Hjy9ttwrGIruG11bteKuHPXno2jooTZRH5UeAc8JqqTvhtj2FsFYr2klVJTw7R/OT3ZL+Y1UnjTI9nRdqZHs/8HCM9PYYm48tfMp0iPTFIeqLw6Ib9Z5pIJ1wkHGPm6t94wl3ribdI0IRjBYrtT1ZV3JkJ0lMjpKdGmblxHjJlOXMa2UvPBlJxogx8GpgFIiJyEzifs7ymquN+GmcYlUpRXvIcC76YJRgiVN9CqL6lYHM3NbtIqHPFW9OF/Od5gqEAwdoAkGL62jdWts91GHv1z6g/9S1eElokVnazSW1mYlpe5nXLPu8FamqE9OQIztQo6am5n6Mrv5TZS8+GUomi/Ht4w5b+O9ADnAG+E/gEUCsit1R141MZDWMLseq+5FV+MQfCUQKNOwg3Lp77WFXRZDwj1PPC7UyPkZ4aIz0xjARWKaiqJPtuMdR3y9sOhghW13vJaNX5S6C6gWCsnkA4srp7rJONTEzL9XgnR3o4nZimUUI0SYDAC/+TgdXmDCy+gXnLG0TFibKqfm+mwMevAN3AD6nqDQAROQY84qd9hlGJrMpLnqNEX8wigkSriUSroXlP3rHxa9e4+Wu/RjAsxNqaOPTdf8dLKMuIdnKkF1bwsgFw0jiTIziTI0vbEa4qINhzQt5AMFaHBEsz7KsUiWmqijMzgVOEx/uo5HzVryDIgWg1geoG0mN9oEt8Jsxb3jDKQpRF5OeBn8f7Vvi3qvpvlmuvql8TkUeAfwZ8TUR+E/gZVb0KXN1oew1jq5Ec7l59xrXrkBy+t3K7dTCXde2klJqDJ6g+cCp7zIlPMvAX/33liwTD4Kw8Q5OmEqTHE6THB5ZsE6iq8QQ6I9aBhSJeVVtUmLzYxLTVCO9qCESrCdY2E6ptIljn/QzVNhOsbSIQqWL8/Be956DLXN+85Q2hLEQZ+H3gJwEX+N/FnKCqLvAZEfldPEG/JiLvVdXrG2emYWxN2t7/DKrK4Bc+gzPtpWU0veujVO1bnOm8mYzmzgq1oIpXUd59IEjswCnqT32LJ27xCS9EHp/AnZnMDOOawIlPFiVwbmIaNzFNaqSncINAgGCsjmB1A4FsqLwuT8hdJ704Me3260R2tOPOxksuvDcmB+lOTDOG8sTD386Rgw8TiFQteV7RXRnmLW8IvouyiPzHzOp34v2HTYvIB1W1YGUtEWkFTgOncn6ewAtlV2+8xYaxNUkN3csKsoSriO4+7Ks98cFBEpla84FIhIbD8/asRjjid96g7v4nCS/Rpw2ZPtjEtCfaMxO4M95Pb8mIeDElJl030y++TL6pyOKwsOsw9vU/Wvn6C1jJ4007af70D34OB+85fU/HqWUFGdaX8GesH99FGXgx8/Pb8T4JL7J8Za5+vBD1S3hDo54D3lDV6Y0z0TC2PvHON7Prsf0nkKC/Xw+5cyc3HDmSN4lDqYVDRAjGaj2Pb0G/dvYyroMTn8Kd867nlvj8+kpDv+bsWQ2e8GbEtoDwLkf3aM+qioZsdMKfsTK+i7KqflVE/j7wW3j/ZftV9fPLnDIDHMULdVcBYUBE5HVVndlwgw1jC6JOmvi9+apZsY6TPlrjsVQVL7+EQwJBQjUNUNOw9K3SyQVh8Yl8AZ8cYbmXiWBtM7GOk4TqmosW3uXoyunzL6ZoiJ8Jf4aH76KcIQr8GrAP+NYV2tYDx4BHM8tHgZ8FYiJyDW+s8vdvoK2GseWY7buJJhMABKsbCK9ywoJS4yQSTNy8md1uzBHlchaOQChCYInx2tnEtIXFOPLaTFB938Ml8zpXOwlFuSb8bSfKQpRV9TcARORt4FMiElHVguMcVFWBK5nl85nzBM97nhNqwzBWQbzzrex6VccDvhfaGLt2DXU8cajes4doU1P2WKUKR1EvEyV+eVjtdI1t73+mJPc11k5ZiHIO3cDPAT8gIj+iqn9azEkZoZ4bDvU7G2ifYWw53GSCRM/8oIVYu/+h6+XmTq5E4fAjo3lsZpzxuFeJOBKKsKuhcJKbUV4snv/MX04C/xpoxptS8Usi4u+YDMPY4iTuXc6KRahpF+GGNl/tUdfNHwpVYFaoSmMtiWnrJddL3te8h+AGzndtlI6yEmVVdVT1l4HDwGeA9wIXReQXRaTeX+sMY2uSG7ouBy95urub1OQkAKGaGmo7Ony2aH2sNTHNKWYI1jJ05swM1bGKmaEMfykrUZ5DVUdV9QfxxiF/Bfhh4LqIVF7cyjDKGGd6nOTc/McixNof8Ncg8guGNB47hgTK8muqaNaTmLYeVtufbJQHZf1pV9Urqvoh4APAIPBZETknIu/y2TTD2BLEu97Orkd3HiyLsaZ5/ckP+P+SsF78SExLO2m6R3uz26uZQ9nwl3JL9CqIqn5ZRE4D/wT4JPBSprzmT6hqt7/WGUZloqp5BUOqymBscnJykqm7GQ8vEKDx2DF/DSoBfiSmLS4aUrPpNhhro6w95VxU1VXVXwUOAb8EfA9wefmzikNEgiJyQUT+vBTXM4xKID3WT3piCAAJhqna478Ajl2+nK14VXfgAKFqq5y7FlY7PtkoH8reUxaRZrz61idzlgeAIFCq178fwhN4SyYztg25CV7Rvcc2fT7hQixVxctYHaut5GWUD2UlyiLyDvLF9yQwN7hurprBIPAa8GZmWe899wEfwhsf/a/Xez3DqATUdYl35WRdl0Ho2nUcxq/Oz7y6cHyyUTzmKVcuZSXKwNfx0hQFSACXgC8yL8Bvqmp/ie/5X4EfB+pKfF3DKFuSA3dwE94cLoFoDdGdB322CCZv3cJJeKU+o01NxHbt8tmiymRsepyJnKIhO61oSEVRbqL8SeYF+EZmzuQNQ0S+ExhQ1fMi8p5l2n0M+BhAe3v7RppkGJtCXlnN9vvLYthRbui68f77fS/1Wankjk+2oiGVR1mJsqr+1Cbf8puBD4vIB/FmnKoXkd9W1e9bYNezwLMAZ86cWeWAQ8MoL9x0kkT3fJi4HELXwJar4uUXueOTrWhI5eH/67GPqOq/VdV9qnoAeBp4YaEgG8ZWY7bnOpr25nsJ1rUQbtrts0WQGBoiMTAAQCAcpv7wYZ8tqlxyPWUrGlJ5bGtRNoztSO7Y5FjHybIIE+d6yfVHjhCM+J8JXomknBQ9VjSkoimr8LWfqOqLwIs+m2EYG4qTmGa271Z2uxxqXcOCoVCWdb1mukd7rWhIhWOesmFsIxJ3L2WLc4Rb9xOqbfTZInBmZ5m4cSO7bf3Ja6fLhkJVPCbKhrGNyJsRqkwSvMavXUMdz7ur3r2baFOTzxZVLlY0pPKpCFEWkUYRuc9vOwyjkklPDJMa6fE2AgFi+8rDI82bFcq85HVhRUMqn4oQZbwymNf9NsIwKpncCl7R3YcJRGM+WuOhqtafXCKsaMjWoFJE2TCMdeDNCJUbuj7lozXzzHR3k5rwhCRUXU1dR4fPFlUuuUOh9jfvtaIhFYqJsmFsA1LD3TjTYwBIOErV7vIYB5xXxev4cSRoQrJWcpO8bHxy5WKibBjbgLx5k/edQILlMRpy9O23s+vWn7w+cj1lq+RVuZgoG8YWRx2H+N15j7Rcsq5TU1NM3c0IiQiNx4/7a1AFs6hoSOs+H60x1oOJsmFscWb7bqLJOACB6noibeUxqcrY5cvZMdN1Bw4QrrFCF2slt2hIa10LNVF7lpWKibJhbHHyErzay6OsJlgVr1KS159soeuKxkTZMLYwbjJBoudadrtcQteu4zB25Up22/qT14eNT946mCgbxhYmce8KZMKaocadhBvafLbIY/L2bZxEAoBIYyPVu/2fqapSUVW6hs1T3iqYKBvGFia3YEi5jE2GBXMn339/2YTUK5GxmXEm4pOAVzRklxUNqWgqRZQlsxiGUSTOzATJgU5vQ4RYe/n0247l9idb6HpddC0oGhIIVMrXulGISvnr/RJw0G8jDKOSyE3wiuw4QDBW56M18ySGh4n39wMgoRD1R474bFFl02lFQ7YU5VFBYAVUdRwY99sOw6gUFpfVLI8EL8gPXTccOUIwEvHRmsonb7pG60+ueCrFUzYMYxWkxwdITwx6G8EQVXuP+WtQDgv7k421k3JS9Iz1ZbetaEjlY6JsGFuQXC+5au8xAuGoj9bM48zOMnHzZnbb+pPXR/dIjxUN2WJsa1EWkSoReUVELorI2yLy037bZBjrRV13QdZ1+YSux69fR9NpAGK7dhFtbvbZosqm04ZCbTkqok95A5kF3quqUyISBl4WkS+q6lm/DTOMtZIc7MSNTwEQiFbRUU/lAAAgAElEQVQT3Vk+OZIWui4tXUP3sutWNGRrsKmiLN5gxAiAqs5u5r0LoaoKTGU2w5lF/bPIMNZPXuh6//1Imcyrq6r5Q6FMlNeFquZnXpunvCXY7PB1HfCDwL8UkfpNvndBRCQoIq8DA8Bfquo3/LbJMNaKplNeFa8M5RS6nunpITnuDaIIxmLUdXT4bFFlMzYzzmTCioZsNTZblP8V8P7M8sMLD4rIt4vIptbbU1VHVR8C9gHfJCKLvsVE5GMick5Ezg0ODm6meYaxKhI919F0EoBgbTPh5j0+WzRPbui68fhxJFgeHnylYkVDtiab+ldU1Z8Gvga8pKqfLNDk7wC/LyI3ReSvROS/isg/FpEzcw1E5EdF5D2l9rRVdQz4a+ADBY49q6pnVPVMW1t51A42jELEO9/Mrsc6ymdGKFgwK5RlXa8bm4Ria7LZfcpNwIuZ9caMEGZR1X+e0/Yg8CBwGvgO4P/MHPo0XoJWRERuAudzltcyhUaKtacNSKnqmIjEgG8DfmFtv51h+IuTmGa271Z2u5xC16mpKaY650t+Nh4/7q9BW4Auq+S1JdlUUVbVUeCluW0R+Q1V/cdLtL0N3Ab+eMGh3wO+CfjvQA9wBvhO4BNArYjcUtVi6/btBv6HiATxogb/S1X/fBW/kmGUDYm7l0FdAMIt+wjVNvls0TxjV66AejmUdR0dhGtrfbaosllUNMSSvLYMfg+J+j4RGQdeBy4Cb6tqSkR+TFX/c6ETVPV7ReQp4FeAbuCHVPUGgIgcAx4p9uaq+gbw8Hp/CcMoBxaGrsuJvP5ky7peN4uLhlT7bJFRKvzODHCBKPBPgb8BJkXkMvCTy52kql/DE98vAF8TkU+JSExVr6rq72600YZRbqQnR0iN9HgbEiC2v3z6bNVxGLt6Nbtt/cnrJ7doiNW73lr47Skn5/qRRSQAnACOAyumOKuqC3xGRH4X+Hngmoi8V1Wvb6TBhlGO5I5Nju4+RKCMPKfJO3dw4nEAIo2NVO8pn4zwSiW3aIj1J28t/BblLBmRfTuzLImItOIlf53K+XkCL5RdPt9EhrFJqOqCspqnfLRmMXlVvE6cKKuM8EpkYdEQy7zeWvgtyrUich2vP/n1zHJRVe8uc04/cBUvYewc8BzwhqpOb7SxhlGOpEZ6cKZGAZBwlKrdh322KJ/coVDWn7x+couGRENRdtZb0ZCthN+i3I9X4euhzPJ9wGERGVfVliXOmQGO4vVHV+GVxhQReV1VZzbBZsMoK3ITvKr2HUdCYR+tyScxMkK8z8sSllCIhsPl9cJQieR6yftbrGjIVsNvUd6TqT/9RQAR2QuM4oWkl6IeOAY8mlk+CvwsEBORa3hjlb9/Q602jDJBXYd413x4uNyyrsdyQtcNhw8TjJbHFJKVTFfezFA2f/JWw1dRzghyLpeBTwG/uMI5VzLL5yE70cVR5oXaMLYFs3230KSXRBWI1RFpK6960nlDoSzruiR0WtGQLU25xT268UT5bRH5cLEnqcdVVf0dVf2RjTPPMMqL3KzrWHt5ldV0kknGb9zIbtusUOsnlU7RM9qb3baiIVuPchPlk3iTVjQDfyQiXxIRq8dnGAVwU7Mkeq5lt2MHyit0PXH9OppOAxDbuZOqlqXSRIxiuTfag5up2mZFQ7YmZSXKmRmbfhk4DHwGeC9wUUR+sVymejSMciFx7wo4nuiFGncQLrOp+/KGQpmXXBJy611b0ZCtSVmJ8hyqOqqqP4g3DvkreNM8XheRZ/y1zDDKh/zQdXmNTVbV/KFQ1p9cEnIreVl/8takLEV5DlW9oqofwptOcRD4bGZe43f5bJph+IozM0Fy4E52O9ZeXp7oTG8vyTFvErhgVRV1Bw/6bFHlo6r5nrKJ8pakrEV5DlX9Mp7X/HGgA3hJRH47M4TKMLYd8a75wneRHQcIVpdX707uUKjG48cJBIM+WrM1GJ0eYzIxBVjRkK1MRYgyeGU4VfVXgUPALwHfgzeEyjC2HXmh6zIbmwz5VbysP7k05I5PtqIhWxe/i4esiIg04xUTOZmzPAAEgRofTTOMVTObSnP2SjePH99LNLy2f7/UWD/p8QFvIxiial95DVBITU8zeeeOtyFC4/Hysq9S6RrOmYRik5O8SvG5NYqjrJ6uiLyDfPE9CczFaOYGYA4CrwFvZhbDqBhu94/RPzrNnf5xju1b2xCheOd86Lpqz1EC4fKqkjV25Qpk6gLVtrcTrq312aKtgZ+TUJTic2sUR1mJMvB1QPEEOAFcwivBOSfAb6pqv3/mGcbaUVWu3xsB4Nq9YY7ubV51sY/FM0KVYejahkKVnMVFQzavvGYpPrdG8ZSbKH+SeQG+kZnO0TC2BEPjM6TSDgDJtMPQxAxtDavrgUkOduLGvRmCJBIjuuu+ktu5HtRxPE85g4lyacgtGtJW10r1JhYNKcXn1iiessoUUNWfUtU/UNVrmyHIIrJfRP5aRC6JyNsi8kMbfU9j+3Kte4S064V1HVe5lvE+VkP+2OT7kcDGZjXPptJ89c1OZlPpotpPdnbixL1a3JGGBqr37NlI87YNXT7Wuy7F59YonnLzlDebNPAjqvqaiNQB50XkL1X10konGsZyvPxWFz0jU3n7Agsifr0jk/yvr+V/1PY01/LEyfaC19R0yqvilSHWsfEFQ1bbl7hwAorNDHNu5WSk3KIhG1nJq9DnduGfcLWfW2N1lJWnvNmoaq+qvpZZn8QbYmVjn411c+rgDqqjYQI532jugjnRFm6HggGqIiGudQ9zp3+MnuFJhsZnmJiZJZFMM9NzHU3NAhCsbSLcvLFe6MK+xMWTui3Gz/7k3BeIrcTCoiEb6SkX+twu/LPnfm4DAaE6GubUQRszXSrK5nVSRF4owWVUVd+3xvsfAB4GvlECO4xtTkNNFR84c4hXrvbQOzKJs1CBC5B2XG71jS3bJrjjOwlpkkgkSuzNTiKhoLeEg8uuBwOyaq91tX2Js6OjxPv6AJBQiIYjR1Z1v/WwlZORFhcNaduQ+8ym0gxNxKmOhpiZTa3YPhgQ9rTU8djRPYSC29q/KyllI8p4XvvK31zLs6b/QhGpBf4A+GFVnShw/GPAxwDa2y1EYxRHKBjgXffv43LnIG92Dpbkmk4gjEOYWQcmx2aKPi8gkiPWgaVFPGf7yr3hRX2Jy4lyrpdcf+gQwejmDdXayslIG1k0JOW49AxN0jU4Tt/o1CKveCkCIjx0304O7WkumS2GR9mIsqq+x4/7ikgYT5A/r6p/WKiNqj4LPAtw5syZ9b44GNuI6USS671LJ8YEA8LJA21EwyGSaYdkyvF+pt2cdYfZ2VlSDos7+IrEVSWRTJNIFpewVYju4cV9idFwkLaGGkJBYWJ4luTJdyCOQ+DYUa7eGyYYEIKBAMGAEAoG8raDBbYDa/z9CiUjbRVRLvX4ZMd16RuZomtwgp7hpaM4TTVRxuNJ3ALHAwGhqS62bluMxZSNKPuBePGtzwGXVfUX/bbH2FpMxZO8+EYniaSTtz8YkLwvwmAgwIGdjctea+ivfpPkcDeOhKk6+T6C++5fIOJz627B/W6xLtAqmU053BvKBJcad3kLMAl03lp9SQERCAVyxDqYI9qZ7ZHJxKKXi4VSvpWSkXI95bVW8nJVGRybpmtggntDE6ScwoNbmuuqaG9rYH9bPd3Dk1zM+Rvmfm5VlZHJOM0mzCVnW4sy8M3A9wNvisjrmX3/TlW/4KNNxhZgMj7Lixc7ieeIhwjEImEePrSTCzf7SSTTOK4yND7D4WXCgOmpUVLD3QgQwqH54HGCVcV/GaoqjqsLxHuBcKddEsk0wxMzzKaclS+6Qah6IVXPhOLtWPjKkZeMJEJVJFiRyUhe0ZC+7PZqioaoKsMTcboGx7k3OEFiib9rfXWU9h31tLc1UBuLZPcPjs/guEogIFSFQ6v+3BprY1uLsqq+zBr7oQ1jKSZmZjMecr43t6+1PpsUs7Oplleu9tA9NMHwZHzZ6+WOTY7uuo9g1erCsiJCKOiFj6uj4RXb3+wZ4cLN/oLedUCEo3ub2d1Sh+O6OK7iOC59r7zC2O07aDBE7aHD1B096h3LHM+2zZ4zt+7mtNF1J5UUwlUlPpvm3LVeWupjtDZU01JfTSxS/l9/90a6V1U0RFUZn56la3CcroGJJRO2aqJh9u+op31HA401VQXbjEzGEWBvTjLXaj63xtoom0+l39nXhlEKxqcTfPWNzqxXEgwIDTVRDu1u4uCupmy7uSSw232j3B2cXPJ6qrpgRqiNH5vcWBsjEBBcp3Bf4r62+rywpaoyeOEsdaOjANz//idpOLhz1fdVVVzVPJFeKNpz2+nM9sDYFD3DUyuKuQLDk3GGJ+Nc6/b6+GuqwrTWV3tCXV9NfU10zX3aG0Xu+OTlhkJNxpPcHRina3CCiZnZgm2qwkH2tzXQvsP7+62UnV4Xi3J/e+uaPrfG2ikbUcbH7GvDKAVjU4lM9StPkEMB4YmT7exoXNqzPbgrX6wXkhrpwZnyRERCEar2bPwwo9GpeN6Y5JX6EuN9fSQzghysqqLu4ME13VdECIoQDACh4iqVNdfF6B+bIV2gjzQQEHY11jAZTzIZTy46Pp1IMZ0Yp3PAG9ccCgZoqZvzpGO01MUIF2nHRtE1ND8z1MKiIfHZFF2DE9wdHGdkMlHw/HAowL7Wetrb6mlrrFnVS8dTp5buf1/pc2usnbIRZb+yrw2jFIxOeR5yMjMsJxQM8OTJdtoa1lejONdLrtp3HAmtHH5eL6vtS8yr4nXsGIHg5gnZci8QAuxqruWJPc3MptIMT8QZmphheCLOyGR8UdZx2nHpH5umf2w6e35DTZSW+uqsR11TFS7Z+OeVKpCp6iJPeTaV5t7QJF0D4wyOFx4SNzd+uH1HA7uaagjavMsVRdmIsmFUKiOTcb72ZifJtOethYMBnjrVTkv9+gRZXYfE3XnB26wZoVbbl5gnyptcxavYF4hoOMSeljr2tNQB3rCpsakEwxMzDE3MMDQRX5QDoMDY9Cxj07Pc7PUiAVWREK31sYxQx2isjRFcWD+1SFYqYTo6PcZUYoqAhGit7uD63QT9Y9cKjiUOCOxqqqV9RwN7WuqsmEcFY6JsGOtgeGKGr73ZlR1iEg4FePepjpIMFZntv40763lDgVgdkbaOdV+zGFbTl5ianmbyzh1vQ4Sm48c3xcY51pqMFAyIF6Kuj3GUFlSVmdkUQxNxT6jH44xPJxb1pyWSnqd6b8h7BgERmuuqssljLXUxqopIIFupApnjurzddY+DTe+iMbqHQCBE3+j0ouvsaKymva2Bva31RMP+htqN0mCibBhrZGh8hq+91ZXtz4yEgrz7VHvJiirkzwj1ALJJYcjV9CWOX72aLY5cu38/4bq6Dbcvl1IlI4kINVURaqoidOxoACCVdhiZjGeFengivmh8r6vK0ITXBoYzNkUygu+FveurI4tC3oUqkLXUVzOQGUvcPTRBygnSHFv8t2iui9G+o579rfXEisimNyoLE2XDWAODY9O89FZXtopUNBzk3ac6aKwtPLxktbipWRLdV7PbmxW6Xi1+TkABG5uMFA4F2dlUy86mWsAT4ImZWYbH5/qmZ5hKLB5yNJdYNjcxRiQU8LzoTJZ3c11sUQWyV672kHbcJceIV0Xg8J62RWOJja2HibJhrJL+0WlefrsrmygUDQd5z+kOGpYY77kWEt1XwfH6OEMNOwg3rn6I0UajrsvYlfmpJDe7P3mzCYjQWFNFY00Vh/Z4Yh/PFFyZSyIbnUwsGt+dTLv0jkzRu2BKxFymC4i746YZmL7KSLyLn/jwP6c6YtWztgMmyoaxCvpGp/ibt+9mBbkqEuI9pzuory7t5Av5Y5PL00ue7OwkPeP1eYfr66nZu/1mPY1FQuxrrWdfaz3g9QWPTiayWd5Da6iQJngjwt4e+DKJ9ARt9a0myNsIE2XDKJLekUn+5u17WU8olhHkuhILshOfJNl/O7sda3+gpNcvFWO5oesTJ7bMVInrIRgI0NpQTWtmKJyqMpVIMTA6xZW7w0yvMCXi3HCmhN4mcderKb5wfLKxtbG8ecMogp7hfEGujob4lgcPlFyQAeJdb2fXIzs6CFbXl/wepcDv/uRKQESoi0U4tKeZD73jCI8e3rVkAY+56RDfeWIfd4fni4YsV8nL2HqYp2wYK3BvaIKvX76XHR9aEw3z7tMdG5Zwk591XZ6h69nRUWZ6ewGQYJCGo0d9tqgyWKmEaVNdbFHREPOUtxcmyoaxDHcHJzh7+V52vGpNVZj3nD5ATdXGDEVJjQ+QHstMlxcIUrVvc8f9Fsvo5cvZ9fpDhwhGSx8x2IoUU8IUSTCV8JLCouEoOxrafLHV8AcLXxvGEnQNjOcJcm0swrc8uHGCDBDvnA9dV+05SiBSuozuUjJmoes1kVuBrDoa5vHje6mOhgmIZCuQdQ7llNZs3kdA7Gt6O2GesmEU4E7/GK9e7ckKcl0swntOd2xosQZVJd5V/lnXTjLJ+PXr2W0T5eIppgJZeqa4maGMrYmJsmEs4FbfKOeu9Wa366ujvOd0R1HlE9dDcrALd8bLuJVIjOiuQxt6v7UycfMmbsrLIq7asYOq1lafLaociqlA9npPTn+yifK2w0TZMHK42TPK+RvzgtxQE+XdpzZekGFBgtf++5FNnG1pNYwuGAplFM9KFcj2ttbwxUt92X37W/ZthllGGWGibBgZrveMcOHG/BdiY20V7z7VXnBavVKjTprEvfnkqXINXauq9SdvIPdGenDVq69tRUO2JybKhoE3U8/rt/qz2811VTx1soPIJs28k+i9gaZmAQjWNBJuKc/qWPH+fmZHvWkMg9EodQcP+mzR1qLLhkJte7Z1Wp+I/IaIDIjIWyu3NrYqV+4O5QlyS12Mp05tniADxO+8mV2PdZws2+pYuaHrhuPHCYTsvb6U5GVeW3/ytmRbizLwm8AH/DbC8I9LXYO8cXsgu91aH+OpU+1EQpsnyO7sDLN9N7Lb5Rq6ButP3khUla4h85S3O9talFX1a8CI33YYm4+q8nbnIG/dGczua2uo5slTHYQ3UZAB4ncvg+v1I4ab9xCqa9nU+xdLemaGyTt3stuNJsolZWR6lKnZacCKhmxntrUoF4uIfExEzonIucHBwZVPMMoaVeWtO4O83Tn/t9zRWMOTJ9sJBzf/X6ISxiYDjF29mn15qNm/n0hdnc8WbS26rGiIgYlyUajqs6p6RlXPtLVt/bdXJz7J0F//Fk586flfKxVV5Y3bA1y+O5Tdt6uphice2E/IB0FOT42SGspMPiBC1f7yzWa2CSg2ltx619afvH0xUTYWMXXpZVKDd5m69LLfppQUVeXirX6u3hvO7tvdXMs3+yTIkD8jVHTXfQSranyxYyXUdRm7ciW7baJcevL6k02Uty0mykYeTnySmdtvAMrMnYtbxltWVS7c7Oda93wKwZ6WOt51/z6CAX/+DVQ1v2BIxylf7CiGqa4u0tNef2e4ro6aveU5ZKtSSaaT9I7NjwCwoiHbl20tyiLyu8DXgWMick9EfsBvm/zG844zFZ9Vt4S3rKq8dqOPGz3zgry3tY53nvBPkAFSo704k57XLqEIVXvKd/rD3NB144kTiI/PbSuSWzRkR32bFQ3ZxmzrQYaq+r1+21BOZL1k1/F2uA4zdy5Se/8TBGO1/hq3RlSVc9d7ud03lt23v62edxzbSyDg71jgXC+5at9xJLRxk12sF+tP3lhyi4a021Cobc22FuXNZDaV5uyVbh4/vndTyjYWgzpp0lOjOFMjpCdHmbn9Orjp/EaOw9g3/oSGRz9AsLa5bItaFMJV5dWrPXQOjGf3te9o4JuO7SHg8++hrkuia17oyjXrevz6dW78zu+QHPeeoQSDNB4tX4++Uum0/mQjQ3mowzbg1t1++kemuH1vgOMH92zafdV1cKbHSE+O4EyNkp4cIT01gjM1gjMzATkTri9xBZIDdxj84q8h4Sjhpl2Em3Z7S/NugjWNZSnUriqvXOmma3Aiu+/AzgbOHPVXkMevX+fG889z8IPvxs2MSQ1U1RJp6/DNpqUYv36dK889l50RCqD+0CGCVeU5x3OlsrBoiGVeb29MlDcBVeXq3UGQCFe7Bjh2YHdJhUxdF2dmPOPxjnje75z4To8VIbxF3ic1S3Kgk+RAZ3afhKsIN++eF+vm3QSrGzZVqBdGIVxX+cbVbu7mCPLBXY2cOVLa575ackVu5JW/oqYlAkCs/YGy66MtJMiATdO4AeQWDakKV7Gj3p7xdsZEeRO4/cabpFVAIKUB7rzxJgcfPL2qa6gq7swE6Yzwel7vsCfA06PZog6rJVjdQKC6ntTwvTWJt6YSJPtvk+y/nd0nkVhGoHcRadpNuHkPgVjdhgni5YuX6Z8OcPniZU49cpKzV+7RPTSZPX5odxOPHN5VFoIspNl1fz2Rmvl/vXILXS8lyAADr7xCy4MP0nDkiA+WbU1yveT9LXutaMg2x0R5gxm/fp3rXX249V7I2pUA17q6aa6OLfpiU1XcxJQnuhlPNz01gjM5Snp6FJx0oVusSCBWR6iumVBtM8HaZkJ1Td7P2iYkGGL8/BdJjfSAOstcJEjV/hPE9h4jOdpLaqSP1GgvmowvaqrJOMn+WyT7bzE9d3q0OutJZz3q2PorQo1du8btsVmIVHN7bJaRVy4zlJw/fmRPMw8d2lkWguymUjQfqCFaF8rak4w7TA9O0Ni0yzf7cllOkAE0nebKc89x/JlnTJhLRKfNDGXkYKJcYl5+q4uekfyxvVK/B+befiXARP0e/r/eFPTOJ/q0OMM8MPp1NF34y3AlAlU1nujOiW+dJ7qh2uZls3oXZVwvheuQuHeF+tPvo2rfccB7iXBmxkmN9JIa7c387ENTicWnz84w23eT2b6beTbnCXXT7lVleY9fv84bf/rnOGfeB0AqXJUnyEf3NfPgwfIR5GBYqN0RzbNnejBB3+c+VxYit5Igz+GmUibMJcT6k41cTJRLzKmDOxibniUxm2IuoKwLwlEq8xMeiDqE3ST7Ry+uKMiBaDXBjNAG65pzvN8mAuHomuyduvQyaH7oOyURrjY+yrGx84Q1R+Vcl6lLL9PwqDexlogQqmkkVNNIbL83OYGq4kyPzQv16JxQzy66t5uYZrb3BrO98zMkBWJ1OULt9VMXqnI1JyAz3/xe3LnnmfOcD9YEykqQARr2VrPQmqmhJG7K9V3kihXkOUyYS4MVDTEWYqJcYhpqqnhno/L1C7eJ79iHrjD2VCVIMhjj9db3EnHixNw41YE01ZEAtbEI9bW11DU2Ut3YSiBS2qxXJz7JzK3XF4lyf2w/o5Gd9Mf2s29m3rNFXWZuvb7suGURyXjoTcTavfGsqoozNZL1pLNCnU4uOt+NTzIbn2S251p2X7C6nnDTbi4GDzOQjMw3/tA/9Gwv0Ad3e9rl9kuXAdjTXMsTJ9uLfi6lYKHIZb3knLHRqprtx/dT5FYryHOYMK+feyPdVjTEyMNEucSMX7/Ojd/4HC2pFO6jp+nZ92ieZ7wcyWCMZDDGOIADTGWWvgnCoSlqqyLUxiLUVIWz67VVEWLR0Jo8wuG//QvUcfKFAuiuOQIidNccZu/MzTzvTp00w3/75+x439NF30dECNW1EKprySY1qeuSnhrJCXv3khodYFYDJANVJINV3s+59ekq4sE4hEKAwNzvu0xSjDhpoqEAx5sgOTfpgyqgXs2ybGKbZoqYaU4bb1t18b6FbbPpcZlrz/T20vPCC8TqBfBeImp3RBd5yajnPY/c8XreN0Lk1HVxUylvSafn1zPLZGcn9770JdRZoftiCUyY10fu+GQrGmKAiXJJWdh/2FQ1TZ+6OAVEWdShKdHHzBSkqutwqmuXFZhU2mV0KsHo1OL+2oCIJ9QZka6JhfMEvFApyfHr14nfvUakOt+2iXALafE+FmmJMBFuoSE1P4GDBIT43WuMX79e9Jew47okkmkSyTTxhT9nG0i4NSRiHSRCaxOGQgTcNM3JPo4OvUaq12F45VNKSut91Su2kYBQ2xZlvHsGJzXvMV9+9ll2PfEE0ebmRSKaK666xP7cZa1iuxpMmNdO1/C97LoVDTHARLlkFOo/nAo3oXP+kSoBdbz+TxFElabZAfZ3X2HkzjQqQrqmjuiRE7gNTcyGq0iGoswGI8wGw7jLCLarymQ8yWR8cTgYoCoI1aEANeEA1ZEgwYkxRl74S4IT0wQW9GMPftPDuHhC7RLg2ux+2l65tuiafZef48gP/ACR/QdyhDaVEdp0nggn06UXBnHTXgSiQIRA1OG+yTfZHb9T8vuWGpF8bxlAHYfer37VR6tWj5tKceP553n0E5/w25SKQVXzPWUTZQMT5ZKwVP9hd7QFNxBCXIeIm+C+yTe5VX+apERxAyEmoq3syvGUwlMTuBe+AUA0s9ThBUrdaIx0TX1mqctbd6PL90MlHEg4LiOzLpAGYvDUhws3dl2Y86wDARK7Orj7kWfymkjKE//OvjT03Vh4hXURctKE3RRh1yGiacLqIDNTzN66QTA+TTAxQ9hN0PRoB2+1PIEji/vsA+pSkxpjdjqdDTWrCoFQCATU9fpy1XWzYen8IdoKmreVt82CbS1wTvaAQHVzZMnuhULecqkJhMNIKEQgHM5b3HSaeF9fSYrLBMJhDj9dfJeGASNTo0xb0RBjASbKJeDG88/nJcnMZdlOhptBXVpmezk68RpBdWgaGuBa/SMMVe1hMtxc0FNaiADB2TjB2TjRkf5Fx91QuKBYp2vqcWK1Bb3JJVkY6i4Q+tZwZNG+ZVGXQMKzP5iYWbR4x2YIJuKIFi6CkhsMbjpQs2wUQhGmQo0w2bfsc90Mmg/UZMV5KQp+BgIBmk6coKqtbZGYBgoI7FJtJLR8vsFak7xyCW96bGwAAB9bSURBVITDFrpehp/5408zlVh+CtREKsG//b2fyttXW1XLJz7y4xtomVGOmCiXgMNPP714LGpAqE5P0j59hV3xrmzboDqcGH+VvmQ7Q9G9izwlCQbZ/eSTVLW24joO6rpo7s+5pdD+zLqbHkNHR9AhB0eVZDDihcLDc+HwCOnqOtI1dRBcx0fAdQgm4jniOpMV1zzRnU0gi9zItbHWKMRySDDoCVkkkhW1YM56wf0FjgfDYeKDg3R94QtoOl0w47rg/Rd8BjZT5BqOHOH4M8/wxmd/ldAaisKlA3B6kwW5GJErhF8itxZb13OeUdmYKJeAuS+2K889R8PeSNYpOjn29SXP2RXvyor1nKc01p3clC/jOe/ISaVwqqpJ19Qz1X6U+P7DBT1jXJeqvi6qezszIjtDKJ3i6P/xd6k7dcgLA7uu91Lguqiql2CUCRGv53h8YICBb3wjm7C0niiEhELc99GPUn/kSFZEA+EwEiwuO74YmoCavXt547O/SnOBcclLMWfrQNf0potcw5EjnD1axePXEqsS5nQAzh6t4slN9pBN5IytjIlyiZgT5juXXiBEgurZ4r8A4rE6Eh1tHP+O926qd3TxhS8QHJmgarAPcRwSew6ggcWhaXHS1F97nejYEG5bE87uek6+94ObJhwtDz7IleeeI314L4FWF0lNFx2FCOxrwY21EbrRvaneZ/e7DrNLkkiquPC5BITA/ha69+3edJEDqN/bzts14zxwob8oYU4H4O2Hd1Lf2LDxxi3gvro9jCWnGJmdWLlxhuZoPY0Rf+YEryR7u7pvUVfbQFNDC66rXLrez5e/do1bXSOk0i7hUID72pt5/1NHuf/ITgIBYXR8mMmpcdr33rfp9m5FTJRLSMORI3z9lT/kxJ7jhM6+QWBwdMVz3LYm0o8f4HLPFR7cZO/o0isJThw9TejsGyRrW9G5vkd1EcdBg0GQACpCqrGNcNgh/fhpLvdc4alNtvX4M8/Q+fIf0996jMDI7aKiEDPRWvqbD1I3fpUDm+x9NgcS9LUcZs/I7aJe0GaitfQ1H6R5uLSJc8Uylpzi4fYTvA0rCvOcIN/XfoILQ4sz8zeaseQUD7ce5cLQtaKErjlan23vB5Vkb11tA5evXmDXzmP8+vNvMT2TYjY5X3M/lXa5emuIO/fGqKkO838/fZK+/qucOPbwptu6VTFRLjG3g9PQc4UTj59eUZg9QfZE7nZw8xOScm11RoMQCoOTJpiI0/jWWcZOPY4TjUEoTHrvLtLNO9Zkq6teSNp1XVx1cVXRBT+zbXJ+5rVrjhGMxtk9cpve5oPsXkHsZqK12XaJaJy+WuifK+cpIJnAcja8LLnbkrsrf68Ud97O2WlqV2nrnpHbTM1O0znUlVfcZG59vmdcM4fyC5do7h7NO5otgrLU8ZHZCS4MXVtRmBcK8sjsBLcGbiMIIoJIgIBk1vG2JbMdyO4TAnP7yTkm89cQFuzLOS9raxFClytwxQiiqnrPJef55D27AvvdBQVpsq0zf6ONtLfUNDW0sGvnMW7cfpNwKMlssvDb2WwyTWO9y43bb3L44CmaGlo22dKti2gJhkNUMiLyAeCXgSDwnKr+/HLtz5w5o+fOnVvy+E88/x8AOOjULOsxFxLkhztOe1+sq/5iyP9CyGuzzDVuDd7J2nqs40HGnFZC3b00X3iJgJPGDYYYefhJ0nt30xgc4mrnxaytjdUNRYtrKWmdSPOOkRj6jqVfeuaerXzjDb7RHGeo3r93z+ZoPY+2HCXytxcJDyy2NbWjieS7HuT8sD9fwguZE4VbXZcXCXMhQfaTlQSs0PFgwMsfWOr/qdzs/YWnP7nhduXiuspP/ucvEQ7N8s2PRHj5fJKB4cX/wztaAjzxaIS/eS1JOl3Fz/7YtxNYJqFRRM6r6pmNtH2rsK09ZREJAp8Bvg24B7wqIn+qqpeWP3NllvOYl/KQL3S+sd7bronbwWkaZ4Rd0T7Cna8TyEwRGXDSNHe+zv/f3rnHR1md+/77TEKClBiBQGAnyMUkEFHkYitUi9BaodiGWj0F1O0VPNJjvV/aLe5Tu3X3uO3Bbj5CK16KtVqKtlujLcV9Dmi2FKQiogiEcCskhFtIQjBAMpm1/3hnxslkLu9M5p7n+/nMJzPvu973/a2VmfnN86z1rtVeJBxq7dVJa1Nrc1K0Hjs7mw84xSUffIIzRNumgiGDFYFuatjJxK9eBH7GnGqGDMEj5lQz5E5aAxhdMAPsCLcaWhyJRm+i2VZz2J2ydvH+pjYum9jVmD2G7Nmem9PG9l1HGFNWmDTdmUSPNmXgK8AuY8weABFZAcwCum3KENiYk52yDsbm4+us6D6A1h21n3Vbq0Mc3pSlw5vi7PzXu98RuFzt8YNAcGMOZcilhecBgVO63tc+E4D4R06+Zb/IPPhv+WLjgeN13vKBjDmUIQ8dUOxNh3un+Pamxr+IRsTKw3v3hd3vfu6bevdcZ/vB6k56fY259NMj1Fw4KKghjxg4zJ0VsTI8xngeLu9rl2cbVjeGb7nOx7kzLJ3O4z7Ovc+XQEYXC4PzbTvxdlF0bs9A2z1dHL7/v8/PtMZdb6x4p2qntw/5SENXY/Y3ZLBS2avfq1ZTjhE9On0tItcCM4wx89yv/xG4xBhzZ7Bj7KavfRnR8SXKR4ztvmBFUZQk8//+eqZLSrtXryye+emsoMdo+to+wSdUVryIyO0i8qGIfHj06NGIj0+liFhRFKU7BOpjdsZhfvueSk835TrAdxb4Yve2ThhjlhljLjbGXDxw4MCIL9I/9+zoFSqKoqQQgwZ0tY3s7NhNwNPT6el9yn8DSkVkBJYZzwGui+UFPP1FGw5/Frb/KNEjLf1T7f7aUkkrpJfedNIK6aU3nbRCeuld9FwV1XuOeV/79yEH6lMGGDm0X0J1ZjI9OlI2xjiBO4HVwHZgpTHms1idP9CHzXegRypF0OmkFdJLbzpphfTSm05aIfX1XjmljNwcK1YLZMC+g788EXNuTjbTLx+VNM2ZRo82ZQBjzJ+NMWXGmPOMMU/E6ryhfv2m0ocQ0kdr39wvAZHr9RynWoOTbnpB37fx4PzSQr7UpxeFBYEjYuhszIUFDvr2yaG8ZFDCtWYqPT19HXP69u5LjnGEvcXB/9aINontJBt2SCetAI9e/TB7/n6AXXs/5f2NbRw+Fniu3Sbg8wIXl008n5IRFzJyWOIXj08nrZBeevV9Gz8cDmH+nAssrUEmDgHLmNd9ZBlzyYgxIScOUSJDTTnG3DntNu/csWv/2otTfnPHesjNyaa5Tw4lE8bQN7dPUuaOTSetAI3NDRw6XE3JiAtZ+8FWcnOC63U6rS+LQ4er6XdOn4RPA5hOWtNNr75vVWsm06PvU46GcPcp+6+ysn3XEVa/V82eA404nR1kZ2cxcmg/pl8+ivKSQUldZSWdtKab3nTSmm5600lruumNl1a9T9k+asoREs6UFUVRlM6oKdtHTTlCROQo8PcoDy8AjoUtlVlonTOfnlZf0DpHyjBjTOSTPPRA1JQTiIh82NN+LWqdM5+eVl/QOivxo8ffEqUoiqIoqYKasqIoiqKkCGrKiWVZsgUkAa1z5tPT6gtaZyVOaJ+yoiiKoqQIGikriqIoSoqgpqwoiqIoKYKachwQkRkiUi0iu0TkRwH254rI7937PxCR4YlXGTts1Pc+EdkmIp+IyP8XkWHJ0BlLwtXZp9w1ImJEJO1vJbFTZxH5vvt//ZmIvJpojbHGxnv7XBFZKyKb3e/vmcnQGStE5EUROSIiW4PsFxFZ7G6PT0RkQqI1ZjzGGH3E8AFkAbuBkUAOsAU436/MD4BfuZ/PAX6fbN1xru80oI/7+YJ0rq/dOrvL5QFVwAbg4mTrTsD/uRTYDPRzvx6UbN0JqPMyYIH7+fnAvmTr7madpwATgK1B9s8EVgECTAI+SLbmTHtopBx7vgLsMsbsMca0ASuAWX5lZgEvuZ+/DnxDRNJ1mZWw9TXGrDXGtLpfbgCKE6wx1tj5HwP8C/AkcDqR4uKEnTrPB5YYYxoBjDFHEqwx1tipswE8a0PmAwcTqC/mGGOqgOMhiswCfmMsNgDniMiQxKjrGagpx54i4IDP61r3toBljDFOoBlI1yVW7NTXl9uwfmmnM2Hr7E7rDTXG/CmRwuKInf9zGVAmIutEZIOIzEiYuvhgp84/AW4QkVrgz8APEyMtaUT6eVciRJduVBKGiNwAXAxcnmwt8UREHMAi4OYkS0k02Vgp7KlY2ZAqEbnQGNOUVFXxZS6w3Bjzf0VkMvCyiFxgjEnO4s1K2qORcuypA3xXJy92bwtYRkSysdJeDQlRF3vs1BcRuQJ4BKgwxpxJkLZ4Ea7OecAFwLsisg+r760yzQd72fk/1wKVxph2Y8xeYCeWSacrdup8G7ASwBizHuiNtXBDpmLr865Ej5py7PkbUCoiI0QkB2sgV6VfmUrgJvfza4E1xj2KIg0JW18RGQ88i2XI6d7PCGHqbIxpNsYUGGOGG2OGY/WjVxhj0nnNTzvv6zewomREpAArnb0nkSJjjJ067we+ASAi5VimfDShKhNLJXCjexT2JKDZGFOfbFGZhKavY4wxxikidwKrsUZvvmiM+UxEfgp8aIypBF7ASnPtwhpUMSd5iruHzfo+BfQFXnOPZ9tvjKlImuhuYrPOGYXNOq8GrhSRbUAH8KAxJl0zQHbrfD/wnIjcizXo6+Y0/oGNiPwO64dVgbuf/H8DvQCMMb/C6jefCewCWoFbkqM0c9FpNhVFURQlRdD0taIoiqKkCGrKiqIoipIiqCkriqIoSoqgpqwoiqIoKYKasqIoiqKkCGrKihIGEbnZvdLT1GRrCUS89InIVPd5b7ZZfp+IvBtLDYrS01BTVnokbrOx9Ui2VkVReg46eYjSU/lHv9dfA27HWorvv/z25SdEkaIoPR41ZaVHYoz5re9r9xzktwPrA+y7ubvXE5EsINdnCUtFUZQuaPpaUezjEJEHRGS3iJwRkZ0icpN/IZ8+3itE5FER2Y21pvL33ftzReSfROQzETktIk0i8pZ7jnDf8/QWkZ+ISLWItLrLfSoiT3VTX4GILBGRAyLS5v67RERsLR8qIkNFZKWINIvICbf28+wc63OOB91tdInPtrHufum/iYguB6j0SDRSVhT7/CtwFtbiGmeABcByEdlljFkXoPzPseYNfg44AVSLSC/gL8BXgZeBZ7DS4/OBdSIyxWfhiiXArcBvsJaC9CyN+PVo9YlIPvBXoAR4EfgIGO8u+3UR+YoxpiVYA4jIOUAV1kpBvwK2YS3FudZ9bbuMx5of+xP3eb/rbo+3gNuMMaciOJeiZAxqyopin1zgy8aYNgAReR1rFaQ7gUCmfBYw3jdl7V64YCowwxiz2mf7UmArlpFPdW++GlhljOkS7XZD30NYxv6/jDFLfa7/MdYPhIeAR0Nc4yFgOHCrMebX7m1LReQXwN02dYJlyjuMMadE5BHgMeBRY8zPIjiHomQcmr5WFPss9RgegDGmjtBrBv8yQB/yDcAOYJM7jVzgXuYwB/hP4DIR8USczcAYEbkghvquxlpacJnfsc+6t18d5hrfBQ5jRe++PGlTIyLyJaxlHXe4VyX6KXC1GrKiaKSsKJEQaG3gBmBYkPI7A2wrx4qgQ625WwAcAO7BSul+KiJ7sFLEbwFvGWNcUeobgbXsoNO3kHuZwp3AhBC6AEYCfzPGdPgdXy8iTWGO9TAWKyD4LuByP9cAQVFQU1aUSOgIsl2CbA800lqAT4H7QlznKIAx5k0RGY61fu3lwBXAbcB/icgVvlFxlPqShcf4VwMPAx8Cj4tIsB8bitJjUFNWlMRSAwwE1tgxIGPMceC3wG9FRID/g9WvOwt4LYrr7wFGiUi2b7TsviWsjMDRtv/xpSKS5Rsti8gQ4BybGjyjzK83xjSJyDLgh8D1WJkBRemxaMpIURLLb4DBBImURaTQ/TfLPdLZizHGAJvdL/tHef03sH4UzPPbPt+9/T/CHP8mUAjc6Lf94Qg0jAf2GmM86e5/xcoqPCYiORGcR1EyDo2UFSWx/DvwTeApEfk6sAbrdqlzgW9g3c88DcgD6kWkEsuIj2D1By8AGrH6lqPh34D/ASwRkQnuc4/HSotXu/eHO/464DkRmQh8hjVafDJwLNzF3beEXQC87dlmjDkkIkuAB7EmcHkmsiopSuagkbKiJBBjTDtwFdbtQwOxbgV6GpiNlRr2jEBuBX6BZcQPAr/Emhq0ErjEGHMwyus3A5dijbaeCSx2//0VcFmoe5TdxzdiTUn6Bla0/CTQB+uHxOc2JJyPNdJ8s9/2J4EWYKF7dLai9EjEyogpiqIoipJsNFJWFEVRlBRBTVlRFEVRUgQ1ZUVRFEVJEdSUFUVRFCVFUFNWFEVRlBRBTVlRFEVRUgQ1ZUVRFEVJEdSUFUVRFCVF0Gk2lYxm/X33TQN+DdwyedGitcnWoyiKEgqd0UvJWNyG/DbWNJCtwLfVmBVFSWVCmvKmTZuKHQ7HOy6XazSptyarogTFdegQznffhQ6fJYazssieOhXH4MFJ06UoSo/HOByOHS6X68qJEyfW+u8Mmb52OBzvDB48uLSwsFAcjuDdzy6XYVvNYd6p2sme/cdpd7role1g5Ln9uXJKGeeXFuJwqKd7+Jc3/o2Tp09GfFzf3n159LsPxUFRZtFcU8OOlSs7GzJARweuqirK5s0jv7Q0OeLShI5TLTRueIN+k64m66y+yZaTNpxpd7JhRx2TRheR20t7BwOxv24PeX3z6Zc/wPYxjc0NtJxs5tyikXFUlhhcLpfU19ePOnDgwPqKioqyysrKU777Qw70crlcowsLC7NDGfLhoy088tRfWPbqRqr3HKPdaa3b3u50Ub3nGMte3cgjT/2Fw0dDLj7To4jGkLtzXE+iuaaGHc8/j6u9PeB+V3s7O55/nuaamgQrSy9Obnuf9qMHOLnt/WRLSSv2Hm7icOPn7DvcnGwpKUte33y2V2+msbnBVvnG5ga2V28mr29+nJUlBofDwZAhQxzZ2dnFwA8rKiqyOu0Pc3zICPnw0RZ+tnQtjc2nONPmDFjmTJuTxuZT/GzpWjVmJa6EM2QPasyh6TjVQuveTwBD674tdJzSH4N2MMZQU3scgJ21Deh4ncD0yx9A+ajxtozZY8jlo8ZHFFmnOg6HAxEBGAX077Qv2pO6XIZfvPg+p884CffeMwZOn3Hy7y+uw+VKzBt1x44dTJ48mdzcXH7+858HLbd3714uueQSSkpKmD17Nm1tbQnRl8oYY7jrrrsoKSlh7NixfPTRR13KtLS0MG7cOO+joKCAe+65B4Dly5czcOBA777nn38+7prtGrKHRBqznfYEmDp1KqNGjfK225EjR+KuLRBWdOz+nBqT1GjZbtt5qKio4IILLkiQus4ca26l3Wl1mbQ5Ozh2ojUpOsB+u82YMYOLLrqIMWPGcMcdd9Dh3+UTJ+wYc7IM2U7btba2ctVVVzF69GjGjBnDj370I+++CL//DNDLd0PUpryt5jCft7aHNWTvlQ2cbG1j+67EfNH079+fxYsX88ADD4Qs9/DDD3Pvvfeya9cu+vXrxwsvvJAQfanMqlWrqKmpoaamhmXLlrFgwYIuZfLy8vj444+9j2HDhvG9733Pu3/27NneffPmzYur3kgN2UOijNlOe3p45ZVXvO02aNCguOoKhDdKdrm/nF0dSY2WI2m7P/7xj/Ttm7z+7511x3G6g44Ol2GnO2pOBnbbbeXKlWzZsoWtW7dy9OhRXnvttYRpDGXMyYyQ7bbdAw88wI4dO9i8eTPr1q1j1apV3n3d+f6L2pTfqdoZNGUdjDNtTla/Vx3RMfv27WP06NFcf/31lJeXc+2119LaGv4X6KBBg/jyl79Mr169gpYxxrBmzRquvfZaAG666SbeeOONiPSlMtG23ZtvvsmNN96IiDBp0iSampqor68PWn7nzp0cOXKEr33ta7GUb4toDdlDJMacqPZMJp2iZA8xiJbj3XYnT55k0aJFLFy4sFs67fL+1v2srNrW6VF/vHP3XP3xli5l3t+6P6LrxLvdzj77bACcTidtbW2elGrCCGTMsTLkeLZdnz59mDZtGgA5OTlMmDCB2touA6mjwvbwwP/54z/G5ILVe451OdezP/tekNLuY6qreeGFF7j00ku59dZbWbp0KXV1daxd2/WW0zlz5nRKJYSioaGBc845h+xsqxmKi4upq6uzWRP7PLzin+N2rifn/DRk+Wjarq6ujqFDh3q3e9plyJAhAa+xYsUKZs+e3ekD/Yc//IGqqirKysp4+umnO50vFqy/776YncvV3s62X/7S+3ryokVBy8a7PW+55RaysrK45pprWLhwYcy/JOtXPhH5Qa4OWndvonX3ppDFhnz/kZD749l2jz76KPfffz99+vSJoGL2WVm1LWwZ/565QD11B4+f7HKu7085P+R54/2emz59Ohs3buRb3/qWN0CJJe/9dVX4QsAnn20M+ToQl3/1WyH3J+L7r6mpibfeeou7777bu607339pMWZ/6NChXHrppQDccMMNLF68OKMi2niSiLZbsWIFL7/8svf1d77zHebOnUtubi7PPvssN910E2vWrInpNZNFPNvzlVdeoaioiJaWFq655hpefvllbrzxxpicOxWIV9t9/PHH7N69m6effpp9+/Z1+3ypRrw/w6tXr+b06dNcf/31rFmzhm9+85sxO3eyiXfbOZ1O5s6dy1133cXIkdbtWt39/ksLU/aPFkSEe++9t9uR8oABA2hqasLpdJKdnU1tbS1FRUUx0ZwqRNN2RUVFHDhwwLs9VLts2bIFp9PJxIkTvdsGDPgi5TRv3jweeihz7q2OZ3t6tuXl5XHdddexcePGjDLleLXd+vXr+fDDDxk+fDhOp5MjR44wdepU3n333bjUI9HE+zMM0Lt3b2bNmsWbb76ZUaYc77a7/fbbKS0t9Q5yhe5//9k2Zf8U86LnqqjecyyiiwGMGlnAffOnRHTM/v37Wb9+PZMnT+bVV1/lsssu4/7774/42v6ICNOmTeP1119nzpw5vPTSS8yaNavb5/XHP8XcnXR2uHS1P9G0XUVFBc888wxz5szhgw8+ID8/P2jq5ne/+x1z587ttK2+vt5bvrKykvLy8og028GTYu5unzKAo1cvRtucUCRe7el0OmlqaqKgoID29nbefvttrrjiiqjrFIxgKebmTato3bvliwFegXBk0WfEOPInzojq2vFquwULFngH4+zbt49vf/vbMTfkUCnmhhOneO/Tv+PscHXZl53lYOrYYfTPOyvqa8er3U6ePElLSwtDhgzB6XTypz/9KS7jQsKlmP37kGM5yCue338LFy6kubm5y+jq7n7/RT3Q68opZeTmRBZo5+ZkM/3yURFfa9SoUSxZsoTy8nIaGxtDjsD0cOjQIYqLi1m0aBGPP/44xcXFnDhxAoCZM2dy8OBBAJ588kkWLVpESUkJDQ0N3HbbbRHrS2WiabuZM2cycuRISkpKmD9/PkuXLvXuGzduXKeyK1eu7GLKixcvZsyYMVx00UUsXryY5cuXx6QugcgvLWX0vHk4QgzoC0Ukhgzxa88zZ84wffp0xo4dy7hx4ygqKmL+/PlR1SlSuoy4DkY3R2LH+72YLBpPnup0T3KWz+yFxhiOt5wKdJht4tVun3/+ORUVFd733KBBg7jjjju6pTVSAhlwJPcxhyNebVdbW8sTTzzBtm3bmDBhQqdbn7r7/Rdu7mvjm5b0xeUyPPLUX2hsPmXrtigR6J/fh8cfnB7RlJueX75bt261fUyqk6hIORPbLhjRRMyRGnKmtqetKNlDlNFyprYdwPrttRw4egKHQ+jdK5vx5xWyefdhTrc5cRnDuQPPZlJ5cVTnzuR2CxcRdzdiTvW227RpE4899tgLwD9XVlYe9GyPOlJ2OIR7br2M3rnZhBsgKgK9c7O5+9ZLdQ5sJS5EGjFHasiZiu0o2UOS71tORY63nEKAogF5zLj4PIoKzmbGxefxDwPyEKChm5FyJmLHcGMZMacTUZsyQOHAPH78g2n0yz8raCo7Nyeb/vl9+PEPplE4MC/iawwfPjxlf+mkOj2t7ewac7SGnIntGfC+5HBEcd9yJradh7yzcrm4bAiTy4vJzrK+UrOzHHz1/GIuLhtC3lm5UZ87E9stkgi4O8acrm3X7dHXhQPzeOLBGWzfdYTV71Wz50AjTmcH2dlZjBzaj+mXj6K8ZJBGyD707d036lWilNB4jDlYKlsj5M60NdTZj5I9uDpoa4jNRAmZwJQLzw26b8TgfowY3C+BalKflpPNEaWkPcbccrI5o+a/DkY4UzYulyvkohRgpbLHlBUypqwwdsoyGF1+Mb4EM2Y15K4MvDK+U6Aqij/RLL/YL39ARhmyy+UKumBJSLd1OBw7Dh061OFydR3qryipjH8qWw1ZUZRUwOVyUV9f7zp9+nTAe4pDRsoul+vK+vr6NQcPHixN9JyoihILHFOm4NqwAcekSew6cQI2hZ4qUlEUJZ4YYzh9+vTxl1566ffAWUCnSdND3hIFUFFRIcBVwDVAYtb1UhRFUZTMxgEsr6ysrPLdGNaUwWvMg4Gz46NNURRFUXoMBmiqrKzsspaxLVNWFEVRFCX+dOs+ZUVRFEVRYoeasqIoiqKkCGrKiqIoipIi/DcDCH5gMg1xgwAAAABJRU5ErkJggg==\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [], + "needs_background": "light" + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "wWgD4QVJoNR3", + "colab_type": "text" + }, + "source": [ + "# Sec 6.2 Experiment 2: Planning time with & w/o Affordances" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "WlY_AIqwGiqf", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Utility to create One-room and Pachinko Environments\n", + "\n", + "# One-Room gridsize_dict\n", + "ONE_ROOM_DIFF_GRIDSIZES = {\n", + " 7: \"\"\"#######\n", + "# #\n", + "# g #\n", + "# # #\n", + "# ### #\n", + "#s #\n", + "#######\"\"\".split('\\n'),\n", + " 9: \"\"\"#########\n", + "# #\n", + "# g #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "#########\"\"\".split('\\n'),\n", + " 13: \"\"\"#############\n", + "# #\n", + "# g #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# ####### #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "#############\"\"\".split('\\n'),\n", + " 15: \"\"\"###############\n", + "# #\n", + "# g #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "###############\"\"\".split('\\n'),\n", + " 17: \"\"\"#################\n", + "# #\n", + "# g #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# ########### #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "#################\"\"\".split('\\n'),\n", + " 19: \"\"\"###################\n", + "# #\n", + "# g #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# ############# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "###################\"\"\".split('\\n'),\n", + " 25: \"\"\"#########################\n", + "# #\n", + "# g #\n", + "# #\n", + "# #\n", + "# #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# # #\n", + "# ################# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "# #\n", + "#s #\n", + "#########################\"\"\".split('\\n')\n", + "}\n", + "\n", + "def build_one_room_gridsize(gamma=0.99, seed=2017,p_success=1.0, grid_size=13):\n", + " _ONE_ROOM_TXT = ONE_ROOM_DIFF_GRIDSIZES[grid_size]\n", + " char_matrix = get_char_matrix(_ONE_ROOM_TXT)\n", + " return build_gridworld_from_char_matrix(\n", + " char_matrix, p_success=p_success, seed=seed, gamma=gamma)\n", + "\n", + "\n", + "# pachinko_gridsize_dict\n", + "PACHINKO_DIFF_GRIDSIZES = {\n", + " 7: \"\"\"#######\n", + "# #\n", + "# # # #\n", + "# g #\n", + "# # # #\n", + "# s #\n", + "#######\"\"\".split('\\n'),\n", + " 9: \"\"\"#########\n", + "# #\n", + "# # # # #\n", + "# g #\n", + "# # # # #\n", + "# #\n", + "# # # # #\n", + "# s #\n", + "#########\"\"\".split('\\n'),\n", + " 13: \"\"\"#############\n", + "# #\n", + "# # # # # # #\n", + "# g #\n", + "# # # # # # #\n", + "# #\n", + "# # # # # # #\n", + "# #\n", + "# # # # # # #\n", + "# #\n", + "# # # # # # #\n", + "# s #\n", + "#############\"\"\".split('\\n'),\n", + " 15: \"\"\"###############\n", + "# #\n", + "# # # # # # # #\n", + "# g #\n", + "# # # # # # # #\n", + "# #\n", + "# # # # # # # #\n", + "# #\n", + "# # # # # # # #\n", + "# #\n", + "# # # # # # # #\n", + "# #\n", + "# # # # # # # #\n", + "# s #\n", + "###############\"\"\".split('\\n'),\n", + " 17: \"\"\"#################\n", + "# #\n", + "# # # # # # # # #\n", + "# g #\n", + "# # # # # # # # #\n", + "# #\n", + "# # # # # # # # #\n", + "# #\n", + "# # # # # # # # #\n", + "# #\n", + "# # # # # # # # #\n", + "# #\n", + "# # # # # # # # #\n", + "# #\n", + "# # # # # # # # #\n", + "# s #\n", + "#################\"\"\".split('\\n'),\n", + " 19: \"\"\"###################\n", + "# #\n", + "# # # # # # # # # #\n", + "# g #\n", + "# # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # #\n", + "# s #\n", + "###################\"\"\".split('\\n'),\n", + " 25: \"\"\"#########################\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# g #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# #\n", + "# # # # # # # # # # # # #\n", + "# s #\n", + "#########################\"\"\".split('\\n')\n", + "}\n", + "\n", + "def build_pachinko_gridsize(gamma=0.99, seed=2017,p_success=1.0, grid_size=13):\n", + " _PACHINKO_ROOMS_TXT = PACHINKO_DIFF_GRIDSIZES[grid_size]\n", + " char_matrix = get_char_matrix(_PACHINKO_ROOMS_TXT)\n", + " return build_gridworld_from_char_matrix(\n", + " char_matrix, p_success=p_success, seed=seed, gamma=gamma)\n", + "\n" + ], + "execution_count": null, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "VYFvBmjloZ85", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 245 + }, + "outputId": "bad5093c-0733-4244-89d8-57dfa6d87cd5" + }, + "source": [ + "#@title Planning Time with Increasing Grid Size: One-Room\n", + "#------------------------------------------------------------------------#\n", + "# 1. MDP's will have p_success 0.5\n", + "# 2. Choose a threshold of 0.5 for AF computation\n", + "# 3. For different factors in range(factors):\n", + "# Compute V_star_M, V_star_M_I\n", + "# Planning time for each\n", + "# Return V_star_M, V_star_M_I plan times\n", + "# 4. Multiple runs to account for randomness\n", + "#------------------------------------------------------------------------#\n", + "\n", + "seed = 10000 \n", + "p_success = 0.5\n", + "threshold = 0.5\n", + "max_iterations = 10000\n", + "nruns = 10\n", + "grid_sizes = [7, 9, 13, 15, 17, 19, 25] \n", + "\n", + "V_star_I_plan_time = np.zeros((len(grid_sizes), nruns))\n", + "V_star_M_plan_time = np.zeros((len(grid_sizes), nruns))\n", + "\n", + "# Run VI for MDP M\n", + "for size_idx, size_val in enumerate(grid_sizes):\n", + " for run_id in range(nruns):\n", + " # Create an mdp with grid size size_val, fixed p\n", + " _mdp, _mdp_walls = build_one_room_gridsize(\n", + " grid_size=size_val, p_success=p_success)\n", + "\n", + " # Compute V* in original _mdp.P\n", + " (policy_star_M, V_star_M, _,\n", + " V_star_M_seconds, V_star_M_iters) = value_iteration(\n", + " _mdp.R, _mdp.P, max_iteration=max_iterations, \n", + " seed=seed+run_id)\n", + " \n", + " V_star_M_plan_time[size_idx, run_id] = V_star_M_seconds\n", + "\n", + " # Compute Affordances AF based on intent I\n", + " _AF = _compute_affordances(mdp=_mdp,\n", + " n_states=_mdp.state_space,\n", + " n_actions=_mdp.action_space,\n", + " intent_name=\"collection\",\n", + " threshold=threshold,\n", + " mdp_wall_locs=_mdp_walls)\n", + "\n", + " #construct P_I with a determinsitic probability\n", + " _P_affordances = _construct_dynamics(_mdp,\n", + " affordances=_AF,\n", + " size=_mdp.size,\n", + " p_success=1.0,\n", + " wall_locs=_mdp_walls)\n", + " _checking_P(_P_affordances)\n", + "\n", + " # Compute V*_I in mdp M_I with AF\n", + " (policy_star_I, V_star_I, _,\n", + " V_star_I_seconds, V_star_I_iters) = value_iteration(\n", + " _mdp.R, _P_affordances, max_iteration=max_iterations,\n", + " seed=seed+run_id)\n", + " \n", + " V_star_I_plan_time[size_idx, run_id] = V_star_I_seconds\n", + "\n", + "V_star_I_plan_time_avg = np.mean(V_star_I_plan_time, axis=1)\n", + "V_star_I_plan_time_std = np.std(V_star_I_plan_time, axis=1)\n", + "V_star_I_plan_time_CI = V_star_I_plan_time_std/np.sqrt(nruns)\n", + "\n", + "\n", + "V_star_M_plan_time_avg = np.mean(V_star_M_plan_time, axis=1)\n", + "V_star_M_plan_time_std = np.std(V_star_M_plan_time, axis=1)\n", + "V_star_M_plan_time_CI = V_star_M_plan_time_std/np.sqrt(nruns) \n", + "\n", + "#@title Plot Planning Time: TwoRooms\n", + "fig = plt.figure(figsize=(4,3.5))\n", + "ax1 = fig.add_subplot(1,1,1)\n", + "# sns.set_context(\"paper\")\n", + "sns.set_style('white')\n", + "sns.set_context(\"paper\", font_scale=1.85)\n", + "\n", + "fig.patch.set_facecolor('1.0')\n", + "\n", + "plt.grid(False)\n", + "x_axis = grid_sizes\n", + "xi = list(range(len(x_axis)))\n", + "\n", + "ax1.plot(xi, V_star_I_plan_time_avg, color = colors[1], label = '$V^{*}_{M_I}$', \n", + " linewidth=4.00, marker=markers[1], markersize=10)\n", + "ax1.fill_between(xi, V_star_I_plan_time_avg-V_star_I_plan_time_CI,\n", + " V_star_I_plan_time_avg+V_star_I_plan_time_CI,\n", + " facecolor=colors[1], edgecolor=colors[1], alpha=0.5)\n", + "\n", + "ax1.plot(xi, V_star_M_plan_time_avg, color = colors[3], label = '$V^{*}_{M}$',\n", + " linewidth=4.00, marker=markers[3], markersize=10)\n", + "ax1.fill_between(xi, V_star_M_plan_time_avg-V_star_M_plan_time_CI,\n", + " V_star_M_plan_time_avg+V_star_M_plan_time_CI,\n", + " facecolor=colors[3], edgecolor=colors[3], alpha=0.5)\n", + "\n", + "ax1.legend(loc='upper left', bbox_to_anchor=(0.05, 0.9),\n", + " fancybox=True, shadow=True, ncol=1,\n", + " facecolor='w', fontsize=15)\n", + "ax1.set_xlabel(\"Grid size\", fontsize=16)\n", + "ax1.set_ylabel(\"Planning Time\\n(seconds)\", fontsize=16) \n", + "xticks_pos = [0, 3, 6]\n", + "ax1.set_xticks(xticks_pos)\n", + "ax1.set_xticklabels([grid_sizes[i] for i in xticks_pos])\n", + "fig.tight_layout()\n", + "\n", + "plt.show()\n" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "CyIEYxUubTNw", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 245 + }, + "outputId": "e68a7f32-bc80-4d00-9a45-25e6bc673fa9" + }, + "source": [ + "#@title Planning Time with Increasing Grid Size: Pachinko\n", + "#------------------------------------------------------------------------#\n", + "# 1. MDP's will have p_success 0.5\n", + "# 2. Choose a threshold of 0.5 for AF computation\n", + "# 3. For different factors in range(factors):\n", + "# Compute V_star_M, V_star_M_I\n", + "# Planning time for each\n", + "# Return V_star_M, V_star_M_I plan times\n", + "#------------------------------------------------------------------------#\n", + "\n", + "seed = 10000\n", + "p_success = 0.5\n", + "threshold = 0.5\n", + "max_iterations = 20000\n", + "grid_sizes = [7, 9, 13, 15, 17, 19, 25] \n", + "nruns = 10\n", + "\n", + "outer_dir = os.path.dirname('/content/')\n", + "dir_name = \"planning_valueloss\"\n", + "dir_name = os.path.join(outer_dir, dir_name)\n", + "if not os.path.exists(dir_name):\n", + " os.makedirs(dir_name)\n", + "\n", + "V_star_I_plan_time = np.zeros((len(grid_sizes), nruns))\n", + "V_star_M_plan_time = np.zeros((len(grid_sizes), nruns))\n", + "\n", + "\n", + "# Run VI for MDP M\n", + "for size_idx, size_val in enumerate(grid_sizes):\n", + " for run_id in range(nruns):\n", + " # Create an mdp with grid size size_val, fixed p\n", + " _mdp, _mdp_walls = build_pachinko_gridsize(\n", + " grid_size=size_val, p_success=p_success)\n", + "\n", + " # Compute V* in original _mdp.P\n", + " (policy_star_M, V_star_M, _,\n", + " V_star_M_seconds, V_star_M_iters) = value_iteration(\n", + " _mdp.R, _mdp.P, max_iteration=max_iterations,\n", + " seed=seed+run_id)\n", + " \n", + " V_star_M_plan_time[size_idx, run_id] = V_star_M_seconds\n", + "\n", + " # Compute Affordances AF based on intent I\n", + " _AF = _compute_affordances(mdp=_mdp,\n", + " n_states=_mdp.state_space,\n", + " n_actions=_mdp.action_space,\n", + " intent_name=\"collection\",\n", + " threshold=threshold,\n", + " mdp_wall_locs=_mdp_walls)\n", + " \n", + " \n", + " #construct P_I with a determinsitic probability\n", + " _P_affordances = _construct_dynamics(_mdp,\n", + " affordances=_AF,\n", + " size=_mdp.size,\n", + " p_success=1.0,\n", + " wall_locs=_mdp_walls)\n", + " _checking_P(_P_affordances)\n", + "\n", + " # Compute V*_I in mdp M_I with AF\n", + " (policy_star_I, V_star_I, _,\n", + " V_star_I_seconds, V_star_I_iters) = value_iteration(\n", + " _mdp.R, _P_affordances, max_iteration=max_iterations,\n", + " seed=seed+run_id)\n", + " \n", + " V_star_I_plan_time[size_idx, run_id] = V_star_I_seconds\n", + "\n", + "\n", + "V_star_I_plan_time_avg = np.mean(V_star_I_plan_time, axis=1)\n", + "V_star_I_plan_time_std = np.std(V_star_I_plan_time, axis=1)\n", + "V_star_I_plan_time_CI = V_star_I_plan_time_std/np.sqrt(nruns)\n", + "\n", + "\n", + "V_star_M_plan_time_avg = np.mean(V_star_M_plan_time, axis=1)\n", + "V_star_M_plan_time_std = np.std(V_star_M_plan_time, axis=1)\n", + "V_star_M_plan_time_CI = V_star_M_plan_time_std/np.sqrt(nruns) \n", + "\n", + "\n", + "#@title Plot Planning Time: Pachinko\n", + "fig = plt.figure(figsize=(4,3.5))\n", + "ax1 = fig.add_subplot(1,1,1)\n", + "sns.set_style('white')\n", + "sns.set_context(\"paper\", font_scale=1.85)\n", + "fig.patch.set_facecolor('1.0')\n", + "plt.grid(False)\n", + "x_axis = grid_sizes\n", + "xi = list(range(len(x_axis)))\n", + "\n", + "ax1.plot(xi, V_star_I_plan_time_avg, color = colors[1], label = '$V^{*}_{M_I}$', \n", + " linewidth=4.00, marker=markers[1], markersize=10)\n", + "ax1.fill_between(xi, V_star_I_plan_time_avg-V_star_I_plan_time_CI,\n", + " V_star_I_plan_time_avg+V_star_I_plan_time_CI,\n", + " facecolor=colors[1], edgecolor=colors[1], alpha=0.5)\n", + "\n", + "ax1.plot(xi, V_star_M_plan_time_avg, color = colors[3], label = '$V^{*}_{M}$',\n", + " linewidth=4.00, marker=markers[3], markersize=10)\n", + "ax1.fill_between(xi, V_star_M_plan_time_avg-V_star_M_plan_time_CI,\n", + " V_star_M_plan_time_avg+V_star_M_plan_time_CI,\n", + " facecolor=colors[3], edgecolor=colors[3], alpha=0.5)\n", + "\n", + "ax1.legend(loc='upper left', bbox_to_anchor=(0.05, 0.9),\n", + " fancybox=True, shadow=True, ncol=1,\n", + " facecolor='w', fontsize=15)\n", + "ax1.set_xlabel(\"Grid size\", fontsize=16)\n", + "ax1.set_ylabel(\"Planning Time\\n(seconds)\", fontsize=16) \n", + "xticks_pos = [0, 3, 6]\n", + "ax1.set_xticks(xticks_pos)\n", + "ax1.set_xticklabels([grid_sizes[i] for i in xticks_pos])\n", + "fig.tight_layout()\n", + "\n", + "plt.show()" + ], + "execution_count": null, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "r05HA2UWhKz6", + "colab_type": "text" + }, + "source": [ + "# Sec 6.3 Experiment 3: Planning Loss Bound\n", + "$\\Big|\\Big|V^*_M - V^{\\pi^{*}_{\\hat{M}_{{{\\mathcal{A} \\mathcal{F}}_{\\cal I}}}}}_M \\Big|\\Big|_{\\infty}$" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "SAxhAKT3emQW", + "colab_type": "text" + }, + "source": [ + "## Model Learning" + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "CZQ7dOYZfJZD", + "colab_type": "text" + }, + "source": [ + "To learn the model from the experience-data, we consider the count based approach:\n", + "\n", + "Transition Dynamics computation $\\hat{P_{\\cal I}}(s' \\mid s,a) = \\frac{1}{n} \\sum \\text{COUNT}(s,a, s')$\n" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "fa0XlSG4co_v", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Helper functions to collect data and create transition matrices.\n", + "class Actions(enum.IntEnum):\n", + " LEFT = actions.LEFT\n", + " RIGHT = actions.RIGHT\n", + " UP = actions.UP\n", + " DOWN = actions.DOWN\n", + " \n", + "def get_randomized_state(mdp):\n", + " \"\"\"Generates a random state and sets mdp current state to that.\"\"\"\n", + " state_id = np.random.randint(0, mdp.state_space-1)\n", + " x, y = mdp.unflatten_state(convert_int_rep_to_onehot(state_id, mdp.state_space))\n", + " while (x,y) in mdp_wall_locs:\n", + " state_id = np.random.randint(0, mdp.state_space-1)\n", + " x, y = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(state_id, mdp.state_space))\n", + " mdp.set_current_state_to((x,y))\n", + " state_onehot = mdp.current_state\n", + " return state_onehot\n", + "\n", + "def get_trajectories_transitions(\n", + " mdp, num_trajectories=500, max_trajectory_length=50, policy=None,\n", + " intent_name='collection', random_starts=False, seed=None):\n", + " \"\"\"Takes transitions samples from an environment.\n", + "\n", + " Args:\n", + " mdp: The MDP to evaluate the intent on.\n", + " num_trajectories: The total number of trajectories to sample.\n", + " max_trajectory_length: The maximum length of the trajectory.\n", + " policy: The policy to sample using. If none is given a random policy\n", + " is used. The policy must take a single argument, the one hot\n", + " representation of the state. \n", + " intent_name: Name of the intent to be considered\n", + " random_starts: Data collection including random starts\n", + " seed: seed for randomness \n", + " Returns:\n", + " The trajectories collected from the environment:\n", + " This is a 4-tuple containing the batch of state, action, state' \n", + " and reward\n", + " Human Readable transitions:\n", + " A set containing the unique transitions in the trajectory batch.\n", + " \"\"\"\n", + " if seed is not None:\n", + " np.random.seed(seed)\n", + " random.seed(seed)\n", + " print(f'seed set to {seed}')\n", + "\n", + " trajectory = []\n", + " if random_starts:\n", + " s_t = get_randomized_state(mdp)\n", + " else:\n", + " s_t = mdp.reset()\n", + " trajectory_length = 0\n", + " human_readable = set()\n", + " if policy is None:\n", + " def policy(_):\n", + " return np.random.randint(mdp.action_space)\n", + "\n", + " for _ in range(num_trajectories):\n", + " action = policy(s_t)\n", + " s_tp1, reward, done, _ = mdp.step(action)\n", + " state_int = get_current_state_integer(s_t)\n", + " intent = _get_intent_completed(\n", + " mdp, s_t, action, s_tp1, intent_name=intent_name)\n", + "\n", + " # Human readable vesion:\n", + " human_readable.add((\n", + " mdp.unflatten_state(s_t),\n", + " Actions(action),\n", + " mdp.unflatten_state(s_tp1),\n", + " reward))\n", + "\n", + " trajectory.append((\n", + " convert_onehot_to_int(s_t), action,\n", + " convert_onehot_to_int(s_tp1), reward)\n", + " )\n", + " trajectory_length += 1\n", + " if done or trajectory_length > max_trajectory_length:\n", + " if random_starts:\n", + " s_t = get_randomized_state(mdp)\n", + " else:\n", + " s_t = mdp.reset()\n", + " else:\n", + " s_t = s_tp1\n", + "\n", + " return trajectory, human_readable\n", + "\n", + "def get_stochastic_states_P(mdp, wall_locs):\n", + " \"\"\"\n", + " Function to initialize P with non-uniform probability\n", + " of transitioning to the neighborhood states.\n", + " Introduces stochasticity in actions.\n", + "\n", + " Args:\n", + " mdp: The mdp to get the affordances from.\n", + " wall_locs: The wall locations of the mdp.\n", + "\n", + " Returns:\n", + " P: initialized transition matrix\n", + " \"\"\"\n", + " grid_size = mdp.size\n", + "\n", + " assert len(mdp.terminal_states) == 1, 'only one terminal state supported.'\n", + " goal_loc = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(\n", + " mdp.terminal_states[0], mdp.state_space))\n", + "\n", + " # Attempt to make the desired gridworld.\n", + " reward_spec = {(goal_loc[0], goal_loc[1]): +1}\n", + "\n", + " tmb = TransitionMatrixBuilder(grid_size, has_terminal_state=True)\n", + " terminal_state = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(mdp.terminal_states, mdp.state_space))\n", + " stochastic_P = build_simple_grid_stochastic_states(\n", + " size=grid_size,\n", + " terminal_states=[terminal_state])\n", + " tmb._P = stochastic_P\n", + " for (r, c) in wall_locs:\n", + " tmb.add_wall_at((r, c))\n", + " P = tmb.P\n", + " _unit_test_P(P)\n", + " _checking_P(P)\n", + " return P\n", + "\n", + "def get_uniform_phat(mdp, wall_locs):\n", + " \"\"\"\n", + " Function to initialize P with uniform probability\n", + " of transitioning to the neighborhood states\n", + "\n", + " Args:\n", + " mdp: The to get a uniform starting state for.\n", + " wall_locs: The locations of the walls.\n", + "\n", + " Returns:\n", + " P: initialized transition matrix\n", + " \"\"\"\n", + " grid_size = mdp.size\n", + "\n", + " assert len(mdp.terminal_states) == 1, 'only one terminal state supported.'\n", + " goal_loc = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(\n", + " mdp.terminal_states[0], mdp.state_space))\n", + " # Attempt to make the desired gridworld.\n", + " reward_spec = {(goal_loc[0], goal_loc[1]): +1}\n", + "\n", + " tmb = TransitionMatrixBuilder(grid_size, has_terminal_state=True)\n", + " terminal_state_idx = mdp.unflatten_state(\n", + " convert_int_rep_to_onehot(mdp.terminal_states, mdp.state_space))\n", + " uniform_P = build_simple_grid(\n", + " size=grid_size, \n", + " terminal_states=[terminal_state_idx],\n", + " p_success=0.25)\n", + " tmb._P = uniform_P\n", + " for (r, c) in wall_locs:\n", + " tmb.add_wall_at((r, c))\n", + " P = tmb.P\n", + " _unit_test_P(P)\n", + " _checking_P(P)\n", + " return P\n", + "\n" + ], + "execution_count": 35, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "9NV8NBmVeoj1", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Function: Code to learn a count based model from data.\n", + "\n", + "def learn_model_from_data(\n", + " mdp, mdp_wall_locs,\n", + " num_trajectories=50, \n", + " max_trajectory_length=10,\n", + " random_starts=False,\n", + " affordances=None,\n", + " policy=None,\n", + " seed=None,\n", + " trajectories=None):\n", + " \"\"\"Learns a transition dynamics from sampled data.\n", + "\n", + " Args:\n", + " mdp: The MDP to sample from.\n", + " mdp_wall_locs: The wall locations in that mdp.\n", + " num_trajectories: The total number of trajectories to sample.\n", + " max_trajectory_length: The maximum length of the trajectory.\n", + " random_starts: rollouts are performed with random starting state.\n", + " affordances: The affordances of shape |S| * |A|\n", + " policy: The policy to sample using. If none is given a random policy\n", + " is used. The policy must take a single argument, the one hot\n", + " representation of the state. \n", + " seed: seed for randomness in any sampling used.\n", + " passing_data: set True if previously collected data is being passed\n", + " trajectories: tf trajectories from previously collected data\n", + " Returns:\n", + " The transition model P_hat\n", + " \"\"\"\n", + " if seed is not None:\n", + " np.random.seed(seed)\n", + " random.seed(seed)\n", + "\n", + " Mhat = np.zeros((mdp.state_space, mdp.action_space, mdp.state_space))\n", + " Phat = np.zeros((mdp.state_space, mdp.action_space, mdp.state_space))\n", + "\n", + " if trajectories is None:\n", + " trajectories, _ = get_trajectories_transitions(\n", + " mdp=mdp, num_trajectories=num_trajectories,\n", + " max_trajectory_length=max_trajectory_length,\n", + " random_starts=random_starts)\n", + " else:\n", + " trajectories = trajectories\n", + "\n", + " for (s,a,s_dash, r) in trajectories:\n", + " Mhat[s,a,s_dash] += 1\n", + "\n", + " # Use get_random_phat instead of a naive initialization to take into account\n", + " # the wall locations and the fact that we cannot transition into walls.\n", + " Phat_init_uniform = get_uniform_phat(mdp, mdp_wall_locs)\n", + " \n", + " for (s, a, s_dash, r) in trajectories:\n", + " if affordances is None or affordances[s,a] !=0:\n", + " Phat[s,a,s_dash] = Mhat[s,a,s_dash]/np.sum(Mhat[s,a,:])\n", + "\n", + " # Copy over \"initialized uniform state transition\" here.\n", + " # i.e. if you have never seen a transition (s, a), \n", + " # P (s,a, s') = 1/4 (except walls)\n", + " P_visited_mask = np.sum(Phat, 2) == 0\n", + " for s in range(mdp.state_space):\n", + " for a in range(mdp.action_space):\n", + " if P_visited_mask[s, a]:\n", + " if affordances is None or affordances[s,a] >= 0.0:\n", + " Phat[s, a] = Phat_init_uniform[s, a]\n", + "\n", + " return Phat" + ], + "execution_count": 36, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "fv5ilraeV7gD", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Planning Value Loss Evaluation\n", + "#------------------------------------------------------------------------#\n", + "# 1. Computationally build affordances\n", + "# 2. Learn a model for those affordances from the data experience\n", + "# 3. Compute optimal value function and optimal policy in M_M and M_hat_I_M\n", + "# 4. Policy Evaluation in MDP M \n", + "# 5. Compare loss for different number of samples \n", + "# 6. Hypothesis: the planning value loss shrinks with more and more data\n", + "# for low data regime, there would be an intermediate value of |AF_I| which\n", + "# yields the optimal planning value loss.\n", + "#------------------------------------------------------------------------#\n", + "\n", + "n_trajectories = [250, 400, 500, 750, 2000, 10000]\n", + "thresholds = [0.0, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.9, 1.0]\n", + "\n", + "p_success = 0.70 #@param {type:\"slider\", min:0, max:1, step:0.1}\n", + "max_iterations = 10000 #@param {type:\"integer\"}\n", + "max_trajectory_length = 10#@param {type:\"integer\"}\n", + "seed = 10000 #@param {type:\"integer\"}\n", + "nruns = 10\n", + "\n", + "v_pi_star_Mhat_I_M_thresh_nsamples = np.zeros(\n", + " (nruns, len(n_trajectories), len(thresholds), mdp.state_space))\n", + "\n", + "# Create an mdp M\n", + "mdp, mdp_wall_locs = build_pachinko_gridsize(grid_size=19)\n", + "\n", + "mdp.P = get_stochastic_states_P(mdp, mdp_wall_locs)\n", + "\n", + "AF_sizes = np.zeros((len(thresholds)))\n", + "\n", + "for n_traj_idx, n_traj_val in enumerate(n_trajectories):\n", + " for ind, k in enumerate(thresholds):\n", + " for run_id in range(nruns):\n", + " # ------------Step 1: Compute Affordances ------------\n", + " # Compute Affordances AF based on intent I\n", + " AF = _compute_affordances(mdp=mdp,\n", + " n_states=mdp.state_space,\n", + " n_actions=mdp.action_space,\n", + " intent_name=\"collection\",\n", + " threshold=k,\n", + " mdp_wall_locs=mdp_wall_locs)\n", + " \n", + " \n", + " # ------------Step 2: Learn a Model for AF ------------\n", + " # Learn P_hat_I from data: M_hat_I - Model with AF\n", + " Phat_I = learn_model_from_data(\n", + " mdp, mdp_wall_locs,\n", + " num_trajectories=n_traj_val,\n", + " max_trajectory_length=max_trajectory_length,\n", + " random_starts=True,\n", + " affordances=AF,\n", + " policy=None,\n", + " seed=seed+run_id)\n", + "\n", + " # ------------Step 3: Value Iteration------------ \n", + " # Value iteration - pi*M_hat_I\n", + " (policy_star_Mhat_I, V_star_Mhat_I, _, \n", + " V_star_Mhat_I_seconds, V_star_Mhat_I_iters) = value_iteration(\n", + " mdp.R, Phat_I, max_iteration=max_iterations, seed=seed+run_id,\n", + " AF=AF, mdp_wall_locs=mdp_wall_locs)\n", + "\n", + " # # ------------Step 3: Policy Evaluation------------\n", + " # pi*M_hat_I evaluated in M\n", + " v_pi_star_Mhat_I_M_thresh_nsamples[run_id, n_traj_idx, ind, :] = _policy_evaluation_exact(pi=policy_star_Mhat_I,\n", + " r=mdp.R,\n", + " p=mdp.P,\n", + " gamma=mdp.gamma)\n", + "\n", + " AF_sizes[ind] = np.count_nonzero(AF) \n", + " \n", + "(policy_star_M, V_star_M, _, \n", + "V_star_M_seconds, V_star_M_iters) = value_iteration(mdp.R,\n", + " mdp.P,\n", + " max_iteration=max_iterations,\n", + " seed=seed) \n", + "# pi*M evaluated in M\n", + "v_pi_star_M_M = _policy_evaluation_exact(pi=policy_star_M,\n", + " r=mdp.R,\n", + " p=mdp.P,\n", + " gamma=mdp.gamma)\n", + "\n", + "value_loss_to_plot = np.zeros((len(n_trajectories), len(thresholds)))\n", + "value_loss_to_plot_std = np.zeros((len(n_trajectories), len(thresholds)))\n", + "value_loss_to_plot_CI = np.zeros((len(n_trajectories), len(thresholds)))\n", + "for n_id in range(len(n_trajectories)):\n", + " for _thresh in range(len(thresholds)):\n", + " value_loss_raw_values = [] \n", + " for run_id in range(nruns):\n", + " sliced_result = v_pi_star_Mhat_I_M_thresh_nsamples[run_id, n_id, _thresh, :]\n", + " value_loss_raw_values.append(np.linalg.norm(\n", + " abs(v_pi_star_M_M - sliced_result)))\n", + " value_loss_to_plot[n_id, _thresh] = np.mean(value_loss_raw_values)\n", + " value_loss_to_plot_std[n_id, _thresh] = np.std(value_loss_raw_values)\n", + " value_loss_to_plot_CI[n_id, _thresh] = value_loss_to_plot_std[n_id, _thresh]/np.sqrt(nruns)" + ], + "execution_count": 38, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "OwWVblmnYqv0", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 333 + }, + "outputId": "d2bf4025-a399-479f-ae64-764fc2c3b645" + }, + "source": [ + "#@title Plotting Planning Value Loss Evaluation\n", + "\n", + "fig, ax1 = plt.subplots()\n", + "x_axis = thresholds\n", + "for i in range(value_loss_to_plot.shape[0]):\n", + " plt.plot(x_axis, value_loss_to_plot[i, :], color = colors[i], label = 'n={}'.format(int(n_trajectories[i]/max_trajectory_length)),\n", + " linewidth = 3.00, marker=markers[i], markersize=10)\n", + " plt.fill_between(x_axis, value_loss_to_plot[i, :]-value_loss_to_plot_CI[i,:],\n", + " value_loss_to_plot[i, :]+value_loss_to_plot_CI[i,:],\n", + " facecolor=colors[i], edgecolor=colors[i], alpha=0.25)\n", + "\n", + "ax1.set_xticks([0, 0.25, 0.5, 0.75, 1.0])\n", + "ax1.legend(loc='upper center', bbox_to_anchor=(0.5, -0.19),\n", + " fancybox=True, shadow=True, ncol=7,\n", + " facecolor='w', fontsize=10)\n", + "ax1.set_xlabel(\"Threshold ($k$)\", fontsize=16)\n", + "ax1.set_ylabel(\"$||V^*_M - V^{\\pi^{*}_{\\hat{M}_{{{\\mathcal{A} \\mathcal{F}}_{\\cal I}}}}}_M ||_2$\", fontsize=18) \n", + "plt.title(\"Model Learning: Value Loss Analysis\")\n", + "matplotlib.rc('axes', edgecolor='black')\n", + "plt.show()" + ], + "execution_count": 39, + "outputs": [ + { + "output_type": "display_data", + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAeEAAAE8CAYAAAD36gn/AAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDMuMC4yLCBodHRwOi8vbWF0cGxvdGxpYi5vcmcvOIA7rQAAIABJREFUeJzsvXmcVNWZ//8+d6u9qveFpgFZujEiy4iAiChuo4NB3BKcxGWMk0lG49cYMxnzMyZGs05MonEyYxJNjDHBRBuMRtAghlFx17iLyNo0NL3Xvtzl/P6o7uouuoFGlla479erXrfq3HPvOffcqvrc5znnPEdIKSUuLi4uLi4uhxxlpCvg4uLi4uJypOKKsIuLi4uLywjhirCLi4uLi8sI4Yqwi4uLi4vLCOGKsIuLi4uLywjhirCLi4uLi8sI4YrwYcgll1zCqaee+qGPP/XUU7nkkksOYI0++hyJ1wzwk5/8hMbGRlpbW0e6Ki4fgkNx/+bPn8/ll19+0M5/pOOK8AHmhRdeoLGxkcbGRm677bYh8/z9738v5PnOd75ziGt44Oi71t/85jcjXZXDEsuymDdvHvPnz8dxnN3mW7t2LY2NjXz3u989hLU7cFx//fU0NjaSTCZHuiofiq997Ws0Nja6QuXyoXBF+CDh8Xh4+OGHh/zzbGpqwuPxjECtXHbHypUrufvuu0e6GkVomsaiRYvYuXMnzz777G7zPfTQQwBccMEFh6pqLr0kEgkef/xxxowZw/PPP09LS8tIV+mAs2rVKn75y1+OdDUOW1wRPkicdtppQ/55ZrNZVqxYwemnnz5CNTu8yeVy5HK5fT7OMAwMwzgINdo/+oR12bJlQ+5PJBKsWrWKY445hsbGxkNZNRdgxYoVpNNpbrvtNjRN2+19+jhjGAa6ro90NQ5bXBE+SEybNo3x48cP+lGuWrWKWCzGeeedN+RxlmXxi1/8grPPPpspU6YwZ84crr32WjZv3jwobzQa5cYbb2T27NlMnz6dSy65hLfeemu3dXr99df5whe+wKxZs5gyZQoLFy7kvvvu41BELo1Go/zgBz/gtNNOY8qUKZx44ol8/etfp729vSjfzp07+f73v8+iRYuYOXMmU6dOZdGiRfz+978fdM6f/exnNDY2sm7dOm699VbmzZvH1KlT2bBhA9u2baOxsZGf/exnPPnkk5x33nkce+yxnHzyyfzv//7voHMN1Sfc2NjIf/7nf/LKK69w8cUXM23aNObOnct3v/tdTNMcdI7HH3+cRYsWceyxx3LKKadw5513FlzFTU1NRXm3bt3Khg0b9tpuEyZMYNq0aaxatYp4PD5o/2OPPUYmkymygltbW/nud7/LokWLOO6445g6dSrnnnsuDzzwwF7LA7j44os544wzBqX3XcvDDz9clJ7JZLjzzjs566yzmDJlCrNnz+baa69l69atwypvX1i5ciWf+tSnmD59OjNmzOCSSy7hueeeG5Rv1apVXHzxxcyaNYtp06Zx6qmn8uUvf5m2trZCnvfee4+rrrqKefPmMWXKFObNm8cVV1zByy+/POz6NDU1MWPGDKZOncqCBQtYtmzZkL+nvjbdsWMH11xzDccddxwzZszgS1/60qDfwP7cv1tvvZWjjz6a7du3D9r3xhtv0NjYyJ133llI+9Of/sT5559fqM+ZZ57Jf/7nf5LJZAp5huoTHk77ugwPbaQrcDhz3nnnceeddxKPxwmFQkD+Rzt9+nSOOuqoIY+5/vrrWbFiBSeddBKf+cxnaG1t5f777+fZZ5/lj3/8Y+E40zT53Oc+x5tvvsl5553H1KlTeffdd/mXf/kXSkpKBp139erVXHPNNUyYMIErr7ySYDDI888/z6233sq2bdu44YYbDlo7xGIxlixZQltbGxdddBFHHXUULS0t3H///bz44os0NTURDocBWLduHX/9618588wzqa+vJ5vN8vjjj3PzzTfT09PDv//7vw86/1e/+lWCwSBXXnklUkoikUihG2DNmjU88MADLFmyhAsvvJBHHnmEn/zkJ9TU1LB48eK91v3dd9/lqquu4sILL2TRokU89dRT3HvvvUQiEa666qpCvscee4zrrruOsWPH8qUvfQlFUVi+fDmrV68e8ryXX345LS0trFu3bq91OP/88/nmN7/JX/7yF5YsWVK0r6mpCcMwOOecc4rq/NRTT3H66adTX19PJpPh8ccf56abbiIajfL5z39+r2UOl1wux+WXX84777zDeeedx+WXX05HRwd/+MMf+NSnPkVTUxOjRo06IGX99re/5Tvf+Q4NDQ1cffXVWJbFgw8+yBVXXMFPfvITzjrrLCD/sHD11Vczc+ZMrr76anw+Hzt27ODpp5+mra2Nqqoq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QQghhExG1B1NCCGElAkFYUIIIaRMKAgTQgghZUJBmBBCCCkTCsKEEEJImVAQJoQQQsqEgjAhhBBSJhSER6ivrw933303Fi9ejGOOOQaXXXYZ3nrrrUN67IYNG3DRRRfhqKOOwpIlS/DDH/4QmUxm0HkdHR341re+hUWLFuHYY4/Ftddeiy1bthT6RymJ0bxe8XgcDz/8MC6//HJ88pOfxHHHHYeLLroIv//978E5zzt33bp1aGpqGvR18sknF/PHKprRvr+uvPLKIV+H+++/f9C527Ztw1e/+lUce+yxWLhwIW6++Wa0tbUV48cpidG8Zrt37x7y9cp9PfbYY9a5E+k91t7ejpUrV+Kyyy7DMcccg6amJjQ3Nx/y4yfjZ1ix0YpZIyCEwI033oiWlhZ897vfRXV1NZ5++mlcc801WLt2LebPnz/sY//+97/j61//Os4991x85zvfwc6dO3Hfffehra0NDz74oHVeOp3GVVddBSEE/vu//xsulwuPPvoorrjiCvzhD39AbW1tKX7Ughjt67V3714888wzuOCCC3D99dfD4XDgtddew3/8x3+gubkZt99++6DH3H///aivr7du22y2ov1cxTKW9xcAzJo1C6tWrco7VlNTk3e7q6sLV1xxBaZOnYoHHngAuq7jJz/5CZYtW4bnn38eHo+n4D9XMY32NauursZvf/vbQcd//etf45VXXsHpp58+6L6J8B7buXMnXnrpJRx55JFYtGgR3njjjUN+7GT8DCsJQQ7Zq6++KhobG8WGDRusY5lMRpxxxhniuuuuO+BjL7zwQnHJJZcIzrl17NlnnxWNjY3i3XfftY499dRToqmpSTQ3N1vHent7xfHHHy/uueeewv0wJTDa1yuRSIhkMjno+G233Sbmz58v+vr6rGO///3vRWNjo9i8eXNhG18GY3l/XXHFFeLCCy886DVWrVolPvGJT4ju7m7r2I4dO8TcuXPFY489NvrGl8lYXrP9GYYhlixZIi6++OK84xPpPcYYs74f6c81GT/DSoG6o0fg1VdfRTAYxKc+9SnrmN1ux9lnn42///3vSCaTQz6ura0NmzZtwnnnnQdJkqzj55xzDmw2G/7v//4v7xrz58/HnDlzrGOBQACf+cxn8Oc//7kIP1XxjPb1crvdcLlcg44vWLAAhmFg3759RWtzOY329RrpNZYsWYJw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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "t3lzaMwFLi4D", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Helper function to plot affordance arrows.\n", + "\n", + "def plot_affordances_camera_ready(\n", + " nstates, nactions, affordances, mdp, mdp_wall_locs, ax=None,\n", + " headwidth=2.7, linewidths=1, scale=1.9, headlength=2,\n", + " wall_color=(0, 0, 0, 1), grid_kwargs=None, figsize=(14, 8)):\n", + " \"\"\"\n", + " Plots the environment with walls.\n", + " Args:\n", + " nstates: Number of state in the mdp\n", + " nactions: Number of actions in the mdp\n", + " affordances: Affordances of shape |S| * |A|\n", + " mdp: The mdp to plot\n", + " mdp_wall_locs:Locations of the walls for plotting them in a different color\n", + " ax: The axes to plot this on\n", + " headwidth: quiver arguments for arrows\n", + " linewidths: quiver arguments for arrows\n", + " scale: quiver arguments for arrows\n", + " headlength: quiver arguments for arrows\n", + " wall_color: RGB color of the walls\n", + " grid_kwargs: grid argrument specification.\n", + " figsize: Dimensions of the figure.\n", + "\n", + " Returns:\n", + " Visualization of the environment\n", + " \"\"\"\n", + " grid_kwargs = grid_kwargs or {}\n", + " if ax is None:\n", + " fig = plt.figure(figsize=figsize)\n", + " ax = fig.add_subplot(111)\n", + " plot_environment(\n", + " mdp, ax,\n", + " wall_locs=mdp_wall_locs,\n", + " plot_grid=True,\n", + " grid_kwargs=grid_kwargs,\n", + " wall_color=wall_color)\n", + " action_symbols = []\n", + " for s in range(nstates):\n", + " for a in range(nactions):\n", + " one_hot_state = convert_int_rep_to_onehot(s, nstates)\n", + " y_pos, x_pos = mdp.unflatten_state(one_hot_state)\n", + " if (y_pos, x_pos) not in mdp_wall_locs:\n", + " if affordances[s, a] == 1.0:\n", + " left_arrow = (-0.8, 0)\n", + " right_arrow = (0.8, 0)\n", + " up_arrow = (0, -0.8)\n", + " down_arrow = (0, 0.8)\n", + " if a == actions.LEFT: # Left\n", + " ax.quiver(\n", + " x_pos,y_pos,*left_arrow, color=DEFAULT_ARROW_COLOR, alpha=1.0,\n", + " angles='xy', scale_units='xy', scale=scale,\n", + " headwidth=headwidth, linewidths=linewidths,\n", + " headlength=headlength) #L\n", + " if a == actions.RIGHT: #Right\n", + " ax.quiver(\n", + " x_pos,y_pos,*right_arrow, color=DEFAULT_ARROW_COLOR, alpha=1.0,\n", + " angles='xy', scale_units='xy', scale=scale,\n", + " headwidth=headwidth, linewidths=linewidths,\n", + " headlength=headlength) #R\n", + " if a == actions.UP: #Up\n", + " ax.quiver(\n", + " x_pos,y_pos,*up_arrow, color=DEFAULT_ARROW_COLOR, alpha=1.0,\n", + " angles='xy', scale_units='xy', scale=scale,\n", + " headwidth=headwidth, linewidths=linewidths,\n", + " headlength=headlength) #U\n", + " if a == actions.DOWN: #Down\n", + " ax.quiver(\n", + " x_pos,y_pos,*down_arrow,color=DEFAULT_ARROW_COLOR, alpha=1.0,\n", + " angles='xy', scale_units='xy', scale=scale,\n", + " headwidth=headwidth, linewidths=linewidths,\n", + " headlength=headlength) #D\n", + "\n", + " return ax\n" + ], + "execution_count": 40, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "0bM1DormLHHr", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 390 + }, + "outputId": "7e2c289a-03bc-4602-e066-373e317d2d8d" + }, + "source": [ + "#@title Plot affordances computed from transition matrix.\n", + "intent_name = 'collection' #@param [\"collection\", \"up\", \"left\"]\n", + "world_name = 'pachinko' #@param [\"pachinko\", \"one_room\", \"four_room\"]\n", + "grid_size = 7#@param {type:'integer'}\n", + "headwidth = 5#@param {type:'number'}\n", + "\n", + "if world_name == 'one_room':\n", + " mdp, mdp_wall_locs = build_one_room_gridsize(grid_size=grid_size)\n", + "elif world_name == 'pachinko':\n", + " mdp, mdp_wall_locs = build_pachinko_gridsize(grid_size=grid_size)\n", + "elif world_name == 'four_room':\n", + " mdp, mdp_wall_locs = build_four_rooms_example()\n", + "else:\n", + " raise ValueError('Unknown environment!')\n", + "\n", + "AF = _compute_affordances(\n", + " mdp,\n", + " mdp.state_space,\n", + " mdp.action_space,\n", + " intent_name=intent_name,\n", + " threshold=0.0,\n", + " mdp_wall_locs=mdp_wall_locs)\n", + "\n", + "if intent_name == 'up':\n", + " AF[mdp.terminal_states[0], :] = 0\n", + " AF[mdp.terminal_states[0], actions.LEFT] = 1\n", + "\n", + "plot_affordances_camera_ready(\n", + " mdp.state_space, \n", + " mdp.action_space, \n", + " affordances=AF, mdp=mdp,\n", + " mdp_wall_locs=mdp_wall_locs,\n", + " linewidths=0,\n", + " headwidth=headwidth,\n", + " headlength=4,\n", + " wall_color=(220, 220, 220, 0.5),\n", + " figsize=(5,5),\n", + " grid_kwargs={'color':(220 / 255, 220 / 255, 220 / 255, 0.8)}\n", + " )\n", + "plt.axis('off')\n", + "plt.tight_layout()" + ], + "execution_count": 43, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).\n" + ], + "name": "stderr" + }, + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } + ] + }, + { + "cell_type": "markdown", + "metadata": { + "id": "HbXIGuRCX35O", + "colab_type": "text" + }, + "source": [ + "# Sec 7.1 Learn Affordances" + ] + }, + { + "cell_type": "code", + "metadata": { + "id": "K3p1iqV8waWh", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Helper function to collect data from environment.\n", + "class Intent(enum.IntEnum):\n", + " completed = 1.0\n", + " incomplete = 0.0\n", + "\n", + "def get_trajectories(\n", + " mdp, num_rollouts=500, max_trajectory_length=50, policy=None,\n", + " intent_name='collection', random_starts=False, seed=None):\n", + " \"\"\"Takes trajectory samples from an environment.\n", + "\n", + " Args:\n", + " mdp: The MDP to evaluate the intent on.\n", + " num_rollouts: The total number of trajectories to sample.\n", + " max_trajectory_length: The maximum length of the trajectory.\n", + " policy: The policy to sample using. If none is given a random policy\n", + " is used. The policy must take a single argument, the one hot\n", + " representation of the state. If using a tensorflow function make sure to\n", + " handle batching within the policy itself.\n", + " intent_name: The name of the intent to evalaute.\n", + " random_starts: randomly sampled start state per rollout\n", + " seed: set a specific seed\n", + "\n", + " Returns:\n", + " The trajectories collected from the environment:\n", + " This is a 4-tuple containing the batch of state, action, state' and intent\n", + " target.\n", + " Human Readable transitions:\n", + " A set containing the unique transitions in the trajectory batch and if the\n", + " intent was completed.\n", + " \"\"\"\n", + " if seed is not None:\n", + " np.random.seed(seed)\n", + " random.seed(seed)\n", + " mdp.set_seed(seed)\n", + "\n", + " trajectory = []\n", + " if random_starts:\n", + " s_t = get_randomized_state(mdp)\n", + " else:\n", + " s_t = mdp.reset()\n", + " trajectory_length = 0\n", + " human_readable = set()\n", + " if policy is None:\n", + " def policy(_):\n", + " return np.random.randint(mdp.action_space)\n", + "\n", + " for _ in range(num_rollouts):\n", + " action = policy(s_t)\n", + " s_tp1, reward, done, _ = mdp.step(action)\n", + " state_int = get_current_state_integer(s_t)\n", + " intent = _get_intent_completed(\n", + " mdp, s_t, action, s_tp1, intent_name=intent_name)\n", + "\n", + " # Human readable vesion:\n", + " human_readable.add((\n", + " mdp.unflatten_state(s_t),\n", + " Actions(action),\n", + " mdp.unflatten_state(s_tp1),\n", + " Intent(intent)))\n", + "\n", + " # Prepare things for tensorflow:\n", + " s_tf = tf.constant(s_t.astype(np.float32))\n", + " s_tp1_tf = tf.constant(s_tp1.astype(np.float32))\n", + " a_tf = tf.one_hot(action, mdp.action_space)\n", + " # The mask will only consider the action that was actually taken.\n", + " mask_tf = tf.cast(a_tf, tf.float32)\n", + " # Computing targets\n", + " if intent > 0.0:\n", + " # if a completed the intent I_a(s')\n", + " targets = tf.one_hot(action, mdp.action_space)\n", + " else:\n", + " # vector of zeros if action a completed no intent.\n", + " targets = tf.zeros_like(a_tf)\n", + "\n", + " trajectory.append((\n", + " s_tf, a_tf, s_tp1_tf, targets)\n", + " )\n", + " trajectory_length += 1\n", + " if done or trajectory_length > max_trajectory_length:\n", + " if random_starts:\n", + " s_t = get_randomized_state(mdp)\n", + " else:\n", + " s_t = mdp.reset()\n", + " else:\n", + " s_t = s_tp1\n", + "\n", + " trajectory = list(map(tf.stack, zip(*trajectory)))\n", + " \n", + " return trajectory, human_readable" + ], + "execution_count": 44, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "4GMq1t77eigL", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Function: Populate Learned Affordances\n", + "def get_learned_affordances(mdp, affordnet, classification_threshold=0.55):\n", + " '''Gets the learned affordances from the environment.\n", + " Args:\n", + " mdp: The mdp to get the affordances from.\n", + " affordnet: The affordance network.\n", + " classification_threshold: accuracy of the classifier\n", + "\n", + " Returns:\n", + " affordances in the form of a |S| * |A|\n", + " '''\n", + " n_states, n_actions = mdp.state_space, mdp.action_space\n", + " affordances = np.zeros((n_states, n_actions))\n", + " for s in range(n_states):\n", + " action_prob_predictions = affordnet(tf.eye(n_states))[s]\n", + " actions_affordable = tf.where(\n", + " tf.greater_equal(\n", + " action_prob_predictions, tf.constant(classification_threshold)))[:,-1]\n", + " for ind, a in enumerate(actions_affordable.numpy()):\n", + " affordances[s,a] = 1.0\n", + " return affordances" + ], + "execution_count": 45, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "t789yH3IkGjR", + "colab_type": "code", + "cellView": "form", + "colab": {} + }, + "source": [ + "#@title Function: Affordance training code.\n", + "def train_afford_net(\n", + " mdp, network, optimizer,\n", + " intent_name='collection',\n", + " num_train_steps=10,\n", + " fresh_data=True,\n", + " num_rollouts=1,\n", + " max_trajectory_length=100,\n", + " optimize_performance=False,\n", + " debug=False,\n", + " print_losses=False,\n", + " random_starts=False,\n", + " passing_data=False,\n", + " trajectories=None,\n", + " unique_transitions=None):\n", + " \"\"\"Trains an affordance network.\n", + "\n", + " Args:\n", + " mdp: The mdp to collect training data from.\n", + " network: The affordance network.\n", + " optimizer: The optimizer to use for training.\n", + " intent_name: The name of the intent to train affordances for.\n", + " num_train_steps: The total number of training steps.\n", + " fresh_data: Use fresh data at every before completing a training step.\n", + " num_rollouts: The number of rollout trajectories per training step.\n", + " max_trajectory_length: The maximum length of each trajectory\n", + " optimizer_performance: Use tf.function to speed up training. (Right now\n", + " there are no apparent speed benefits of this function...?)\n", + " debug: Debug mode prints out the human readable transitions.\n", + " print_losses: Prints out the losses at every training step.\n", + " random_starts: randomly sampled start state per rollout\n", + " passing_data: set True if previously collected data is being passed\n", + " trajectories: tf trajectories from previously collected data\n", + " unique_transitions: tf unique_transitions from previously collected data\n", + " \"\"\" \n", + "\n", + " def _train_step(trajectory):\n", + " with tf.GradientTape() as tape:\n", + " s_t, a_t, s_tp1, intent_target = trajectory\n", + " preds = network(s_t)\n", + " mask = tf.cast(a_t, tf.float32)\n", + "\n", + " intent_target = tf.reshape(intent_target, (-1, 1))\n", + " preds = tf.reshape(preds, (-1, 1))\n", + " mask = tf.reshape(mask, (-1,))\n", + "\n", + " loss = tf.keras.losses.binary_crossentropy(intent_target, preds)\n", + " masked_loss = (loss * mask)\n", + "\n", + " total_loss = tf.reduce_sum(masked_loss) / tf.reduce_sum(1-mask)\n", + " grads = tape.gradient(total_loss, network.trainable_variables)\n", + " optimizer.apply_gradients(zip(grads, network.trainable_variables))\n", + "\n", + " return total_loss\n", + "\n", + " if optimize_performance and not debug:\n", + " print('Training step has been optimized.')\n", + " _train_step = tf.function(_train_step)\n", + "\n", + " initial_data_collected = False\n", + " for i in range(num_train_steps):\n", + " if not initial_data_collected or fresh_data:\n", + " initial_data_collected = True\n", + " running_time = time.time()\n", + " if not passing_data:\n", + " trajectories, unique_transitions = get_trajectories(\n", + " mdp, num_rollouts=num_rollouts,\n", + " max_trajectory_length=max_trajectory_length,\n", + " intent_name=intent_name,\n", + " random_starts=random_starts)\n", + " else:\n", + " trajectories = trajectories\n", + " unique_transitions = unique_transitions\n", + " collection_running_time = time.time() - running_time\n", + " if debug: print('unique_transitions:', unique_transitions)\n", + " running_time = time.time()\n", + " loss = _train_step(trajectories)\n", + " if debug or print_losses: \n", + " print(\n", + " 'loss:', loss.numpy().item(),\n", + " 'collection_loop_time', collection_running_time,\n", + " 'train_loop_time', time.time() - running_time)\n" + ], + "execution_count": 46, + "outputs": [] + }, + { + "cell_type": "code", + "metadata": { + "id": "bNMt4bEJKFJz", + "colab_type": "code", + "cellView": "form", + "colab": { + "base_uri": "https://localhost:8080/", + "height": 407 + }, + "outputId": "873aedbb-2205-4261-e513-b64a69753349" + }, + "source": [ + "intent_name = 'collection' #@param {type:'string'}\n", + "world_name = 'pachinko' #@param {type:'string'}\n", + "grid_size = 9#@param {type:'integer'}\n", + "headwidth = 5#@param {type:'number'}\n", + "classification_threshold = 0.95#@param {type:'number'}\n", + "num_transitions = 2000#@param {type:'number'}\n", + "max_trajectory_length = 10#@param {type:'number'}\n", + "\n", + "if world_name == 'one_room':\n", + " mdp, mdp_wall_locs = build_one_room_gridsize(grid_size=grid_size)\n", + "elif world_name == 'pachinko':\n", + " mdp, mdp_wall_locs = build_pachinko_gridsize(grid_size=grid_size)\n", + "elif world_name == 'four_room':\n", + " mdp, mdp_wall_locs = build_four_rooms_example()\n", + "\n", + "\n", + "network = tf.keras.layers.Dense(\n", + " mdp.action_space,\n", + " activation=tf.keras.activations.sigmoid)\n", + "\n", + "sgd = tf.keras.optimizers.Adam(learning_rate=0.1)\n", + "\n", + "train_afford_net(\n", + " mdp, \n", + " network,\n", + " sgd,\n", + " intent_name=intent_name,\n", + " num_train_steps=5000,\n", + " num_rollouts=num_transitions,\n", + " fresh_data=False,\n", + " max_trajectory_length=max_trajectory_length,\n", + " debug=False,\n", + " print_losses=False,\n", + " optimize_performance=True,\n", + " random_starts=True)\n", + "\n", + "AF_learned = get_learned_affordances(\n", + " mdp,\n", + " affordnet=network, \n", + " classification_threshold=0.95)\n", + "\n", + "plot_affordances_camera_ready(\n", + " mdp.state_space, \n", + " mdp.action_space, \n", + " affordances=AF_learned, mdp=mdp,\n", + " mdp_wall_locs=mdp_wall_locs,\n", + " linewidths=0,\n", + " headwidth=headwidth,\n", + " headlength=4,\n", + " wall_color=(220,220,220,0.5),\n", + " figsize=(5,5),\n", + " grid_kwargs={'color':(220/255,220/255,220/255,0.8)}\n", + " )\n", + "plt.axis('off')\n", + "plt.tight_layout()" + ], + "execution_count": 47, + "outputs": [ + { + "output_type": "stream", + "text": [ + "Training step has been optimized.\n" + ], + "name": "stdout" + }, + { + "output_type": "stream", + "text": [ + "Clipping input data to the valid range for imshow with RGB data ([0..1] for floats or [0..255] for integers).\n" + ], + "name": "stderr" + }, + { + "output_type": "display_data", + "data": { + "image/png": "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\n", + "text/plain": [ + "
" + ] + }, + "metadata": { + "tags": [] + } + } + ] + } + ] +} \ No newline at end of file diff --git a/affordances_theory/README.md b/affordances_theory/README.md index 77eb8fd..84f105c 100644 --- a/affordances_theory/README.md +++ b/affordances_theory/README.md @@ -1,5 +1,8 @@ # Code for "What can I do here? A theory of affordances in reinforcement Learning. -This iPython notebook accompanies the paper "What can I do here? A theory of -affordances in reinforcmenet learning" and covers the experiments in Section 8. +This repository accompanies code for the paper "What can I do here? A theory of +affordances in reinforcmenet learning". +The iPython notebook AffordancesInDiscreteEnvironment.ipynb covers the experiments in Section 6 and Learning affordances in discrete environments. + +The iPython notebook AffordancesInContinuousEnvironment.ipynb covers the experiments in Section 7.