{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "[Table of Contents](http://nbviewer.ipython.org/github/rlabbe/Kalman-and-Bayesian-Filters-in-Python/blob/master/table_of_contents.ipynb)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Discrete Bayes Filter"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "<style>\n",
       "@import url('http://fonts.googleapis.com/css?family=Source+Code+Pro');\n",
       "@import url('http://fonts.googleapis.com/css?family=Lora');\n",
       "\n",
       ".CodeMirror pre {\n",
       "    font-family: 'Source Code Pro', Consolas, monocco, monospace;\n",
       "}\n",
       "    div.cell{\n",
       "        //width: 950px;\n",
       "        margin-left: 0% !important;\n",
       "        margin-right: auto;\n",
       "    }\n",
       "    div.text_cell_render{\n",
       "        font-family: 'Lora';\n",
       "        line-height: 125%;\n",
       "        font-size: 100%;\n",
       "        text-align: justify;\n",
       "        text-justify:inter-word;\n",
       "    }\n",
       "    div.text_cell code {\n",
       "        background: transparent;\n",
       "        color: #000000;\n",
       "        font-weight: 400;\n",
       "        font-size: 11pt;\n",
       "        font-family:  'Source Code Pro', Consolas, monocco, monospace;\n",
       "   }\n",
       "    h1 {\n",
       "        font-family: 'Open sans',verdana,arial,sans-serif;\n",
       "\t}\n",
       "\n",
       "    div.input_area {\n",
       "        background: #F6F6F9;\n",
       "        border: 1px solid #586e75;\n",
       "    }\n",
       "\n",
       "    .text_cell_render h1 {\n",
       "        font-weight: 200;\n",
       "        font-size: 30pt;\n",
       "        line-height: 100%;\n",
       "        color:#c76c0c;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 1em;\n",
       "        display: block;\n",
       "        white-space: wrap;\n",
       "        text-align: left;\n",
       "    }\n",
       "    h2 {\n",
       "        font-family: 'Open sans',verdana,arial,sans-serif;\n",
       "        text-align: left;\n",
       "    }\n",
       "    .text_cell_render h2 {\n",
       "        font-weight: 200;\n",
       "        font-size: 16pt;\n",
       "        font-style: italic;\n",
       "        line-height: 100%;\n",
       "        color:#c76c0c;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 1.5em;\n",
       "        display: block;\n",
       "        white-space: wrap;\n",
       "        text-align: left;\n",
       "    }\n",
       "    h3 {\n",
       "        font-family: 'Open sans',verdana,arial,sans-serif;\n",
       "    }\n",
       "    .text_cell_render h3 {\n",
       "        font-weight: 200;\n",
       "        font-size: 14pt;\n",
       "        line-height: 100%;\n",
       "        color:#d77c0c;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 2em;\n",
       "        display: block;\n",
       "        white-space: wrap;\n",
       "        text-align: left;\n",
       "    }\n",
       "    h4 {\n",
       "        font-family: 'Open sans',verdana,arial,sans-serif;\n",
       "    }\n",
       "    .text_cell_render h4 {\n",
       "        font-weight: 100;\n",
       "        font-size: 14pt;\n",
       "        color:#d77c0c;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 0.5em;\n",
       "        display: block;\n",
       "        white-space: nowrap;\n",
       "    }\n",
       "    h5 {\n",
       "        font-family: 'Open sans',verdana,arial,sans-serif;\n",
       "    }\n",
       "\n",
       "    .text_cell_render h5 {\n",
       "        font-weight: 200;\n",
       "        font-style: normal;\n",
       "        color: #1d3b84;\n",
       "        font-size: 16pt;\n",
       "        margin-bottom: 0em;\n",
       "        margin-top: 0.5em;\n",
       "        display: block;\n",
       "        white-space: nowrap;\n",
       "    }\n",
       "    div.output_subarea.output_text.output_pyout {\n",
       "        overflow-x: auto;\n",
       "        overflow-y: visible;\n",
       "        max-height: 5000000px;\n",
       "    }\n",
       "    div.output_subarea.output_stream.output_stdout.output_text {\n",
       "        overflow-x: auto;\n",
       "        overflow-y: visible;\n",
       "        max-height: 5000000px;\n",
       "    }\n",
       "    div.output_wrapper{\n",
       "        margin-top:0.2em;\n",
       "        margin-bottom:0.2em;\n",
       "}\n",
       "\n",
       "    code{\n",
       "        font-size: 6pt;\n",
       "\n",
       "    }\n",
       "    .rendered_html code{\n",
       "    background-color: transparent;\n",
       "    }\n",
       "    ul{\n",
       "        margin: 2em;\n",
       "    }\n",
       "    ul li{\n",
       "        padding-left: 0.5em;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 0.5em;\n",
       "    }\n",
       "    ul li li{\n",
       "        padding-left: 0.2em;\n",
       "        margin-bottom: 0.2em;\n",
       "        margin-top: 0.2em;\n",
       "    }\n",
       "    ol{\n",
       "        margin: 2em;\n",
       "    }\n",
       "    ol li{\n",
       "        padding-left: 0.5em;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 0.5em;\n",
       "    }\n",
       "    ul li{\n",
       "        padding-left: 0.5em;\n",
       "        margin-bottom: 0.5em;\n",
       "        margin-top: 0.2em;\n",
       "    }\n",
       "    a:link{\n",
       "       color:#447adb;\n",
       "    }\n",
       "    a:visited{\n",
       "       color: #1d3b84;\n",
       "    }\n",
       "    a:hover{\n",
       "       color: #1d3b84;\n",
       "    }\n",
       "    a:focus{\n",
       "       color:#447adb;\n",
       "    }\n",
       "    a:active{\n",
       "       font-weight: bold;\n",
       "       color:#447adb;\n",
       "    }\n",
       "    .rendered_html :link {\n",
       "       text-decoration: underline;\n",
       "    }\n",
       "    .rendered_html :hover {\n",
       "       text-decoration: none;\n",
       "    }\n",
       "    .rendered_html :visited {\n",
       "      text-decoration: none;\n",
       "    }\n",
       "    .rendered_html :focus {\n",
       "      text-decoration: none;\n",
       "    }\n",
       "    .rendered_html :active {\n",
       "      text-decoration: none;\n",
       "    }\n",
       "    .warning{\n",
       "        color: rgb( 240, 20, 20 )\n",
       "    }\n",
       "    hr {\n",
       "      color: #f3f3f3;\n",
       "      background-color: #f3f3f3;\n",
       "      height: 1px;\n",
       "    }\n",
       "    blockquote{\n",
       "      display:block;\n",
       "      background: #fcfcfc;\n",
       "      border-left: 5px solid #c76c0c;\n",
       "      font-family: 'Open sans',verdana,arial,sans-serif;\n",
       "      width:680px;\n",
       "      padding: 10px 10px 10px 10px;\n",
       "      text-align:justify;\n",
       "      text-justify:inter-word;\n",
       "      }\n",
       "      blockquote p {\n",
       "        margin-bottom: 0;\n",
       "        line-height: 125%;\n",
       "        font-size: 100%;\n",
       "      }\n",
       "</style>\n",
       "<script>\n",
       "    MathJax.Hub.Config({\n",
       "                        TeX: {\n",
       "                           extensions: [\"AMSmath.js\"],\n",
       "                           equationNumbers: { autoNumber: \"AMS\", useLabelIds: true}\n",
       "                           },\n",
       "                tex2jax: {\n",
       "                    inlineMath: [ ['$','$'], [\"\\\\(\",\"\\\\)\"] ],\n",
       "                    displayMath: [ ['$$','$$'], [\"\\\\[\",\"\\\\]\"] ]\n",
       "                },\n",
       "                displayAlign: 'center', // Change this to 'center' to center equations.\n",
       "                \"HTML-CSS\": {\n",
       "                    scale:95,\n",
       "                        availableFonts: [],\n",
       "                        preferredFont:null,\n",
       "                        webFont: \"TeX\",\n",
       "                    styles: {'.MathJax_Display': {\"margin\": 4}}\n",
       "                }\n",
       "        });\n",
       "</script>\n"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "#format the book\n",
    "%matplotlib inline\n",
    "from __future__ import division, print_function\n",
    "from book_format import load_style\n",
    "load_style()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The Kalman filter belongs to a family of filters called *Bayesian filters*. Most textbook treatments of the Kalman filter present the Bayesian formula, perhaps shows how it factors into the Kalman filter equations, but mostly keeps the discussion at a very abstract level. \n",
    "\n",
    "That approach requires a fairly sophisticated understanding of several fields of mathematics, and it still leaves much of the work of understanding and forming an intuitive grasp of the situation in the hands of the reader.\n",
    "\n",
    "I will use a different way to develop the topic, to which I owe the work of Dieter Fox and Sebastian Thrun a great debt. It depends on building an intuition on how Bayesian statistics work by tracking an object through a hallway - they use a robot, I use a dog. I like dogs, and they are less predictable than robots which imposes interesting difficulties for filtering. The first published example of this that I can find seems to be Fox 1999 [1], with a fuller example in Fox 2003 [2]. Sebastian Thrun also uses this formulation in his excellent Udacity course Artificial Intelligence for Robotics [3]. In fact, if you like watching videos, I highly recommend pausing reading this book in favor of first few lessons of that course, and then come back to this book for a deeper dive into the topic.\n",
    "\n",
    "Let's now use a simple thought experiment, much like we did with the g-h filter, to see how we might reason about the use of probabilities for filtering and tracking."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tracking a Dog\n",
    "\n",
    "Let's begin with a simple problem. We have a dog friendly workspace, and so people bring their dogs to work. Occasionally the dogs wander out of offices and down the halls. We want to be able to track them. So during a hackathon somebody invented a sonar sensor to attach to the dog's collar. It emits a signal, listens for the echo, and based on how quickly an echo comes back we can tell whether the dog is in front of an open doorway or not. It also senses when the dog walks, and reports in which direction the dog has moved. It connects to the network via wifi and sends an update once a second.\n",
    "\n",
    "I want to track my dog Simon, so I attach the device to his collar and then fire up Python, ready to write code to track him through the building. At first blush this may appear impossible. If I start listening to the sensor of Simon's collar I might read **door**, **hall**, **hall**, and so on. How can I use that information to determine where Simon is?\n",
    "\n",
    "To keep the problem small enough to plot easily we will assume that there are only 10 positions in the hallway, which we will number 0 to 9, where 1 is to the right of 0. For reasons that will be clear later, we will also assume that the hallway is circular or rectangular. If you move right from position 9, you will be at position 0.  \n",
    "\n",
    "When I begin listening to the sensor I have no reason to believe that Simon is at any particular position in the hallway. From my perspective he is equally likely to be in any position. There are 10 positions, so the probability that he is in any given position is 1/10. \n",
    "\n",
    "Let's represent our belief of his position in a NumPy array. I could use a Python list, but NumPy arrays offer functionality that we will be using soon."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0.1  0.1  0.1  0.1  0.1  0.1  0.1  0.1  0.1  0.1]\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "belief = np.array([1./10]*10)\n",
    "print(belief)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In [Bayesian statistics](https://en.wikipedia.org/wiki/Bayesian_probability) this is called a [*prior*](https://en.wikipedia.org/wiki/Prior_probability). It is the probability prior to incorporating measurements or other information. More completely, this is called the *prior probability distribution*. A [*probability distribution*](https://en.wikipedia.org/wiki/Probability_distribution) is a collection of all possible probabilities for an event. Probability distributions always sum to 1 because something had to happen; the distribution lists all possible events and the probability of each.\n",
    "\n",
    "I'm sure you've used probabilities before - as in \"the probability of rain today is 30%\". The last paragraph sounds like more of that. But Bayesian statistics was a revolution in probability because it treats probability as a belief about a single event. Let's take an example. I know that if I flip a fair coin infinitely many times I will get 50% heads and 50% tails. This is called [*frequentist statistics*](https://en.wikipedia.org/wiki/Frequentist_inference) to distinguish it from Bayesian statistics. Computations are based on the frequency in which events occur.\n",
    "\n",
    "I flip the coin one more time and let it land. Which way do I believe it landed? Frequentist probability has nothing to say about that; it will merely state that 50% of coin flips land as heads. In some ways it is meaningless to assign a probability to the current state of the coin. It is either heads or tails, we just don't know which. Bayes treats this as a belief about a single event - the strength of my belief or knowledge that this specific coin flip is heads is 50%. Some object to the term \"belief\"; belief can imply holding something to be true without evidence. In this book it always is a measure of the strength of our knowledge. We'll learn more about this as we go.\n",
    "\n",
    "Bayesian statistics takes past information (the prior) into account. We observe that it rains 4 times every 100 days. From this I could state that the chance of rain tomorrow is 1/25. This is not how weather prediction is done. If I know it is raining today and the storm front is stalled, it is likely to rain tomorrow. Weather prediction is Bayesian.\n",
    "\n",
    "In practice statisticians use a mix of frequentist and Bayesian techniques. Sometimes finding the prior is difficult or impossible, and frequentist techniques rule. In this book we can find the prior. When I talk about the probability of something I am referring to the probability that some specific thing is true given past events. When I do that I'm taking the Bayesian approach.\n",
    "\n",
    "Now let's create a map of the hallway. We'll place the first two doors close together, and then another door further away. We will use 1 for doors, and 0 for walls:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "hallway = np.array([1, 1, 0, 0, 0, 0, 0, 0, 1, 0])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I start listening to Simon's transmissions on the network, and the first data I get from the sensor is **door**. For the moment assume the sensor always returns the correct answer. From this I conclude that he is in front of a door, but which one? I have no reason to believe he is in front of the first, second, or third door. What I can do is assign a probability to each door. All doors are equally likely, and there are three of them, so I assign a probability of 1/3 to each door. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nyv/uL730Ul166aVt/37llVcqLy9PGRkZmj9/viZMmOBgdd0vGAzq008/1eLFi/Wzn/1MkjR27Fj1\n6NFDxcXF2rp1q66++mqHqwyv1atXKzk5WXl5eU6XEhYHDx7U5MmTNXDgQD3zzDPq37+/ampq9MAD\nD6ilpUWrV692usRuN2fOHBUsuK0NAAAE40lEQVQWFuquu+5SSUmJgsGgli5dqvr6ekln/pTPSObe\nzg2QnJzc4U+KjY2NktThVXdErqVLl+qBBx7QL37xC911111OlxN2Q4YM0Xe/+11NmjRJf/jDHzRr\n1izdd999Hd4hIpK0tLRo9uzZmjNnjrxer44fP67jx4/r888/lyQdP35cra2tDlcZfn369NF1112n\nN998U5988onT5XSr5ORkSWr3Q8nEiRMlSa+99lrYa3LSm2++qd27d+vmm2/u8FhEJPrZz36mpqYm\nPf/887rhhht01VVXad68eXrooYf0+OOP68UXX3S6xG43Y8YMlZaW6sknn9TgwYM1ZMgQvfXWW20X\nbAYNGuRwhc4hrDsoIyND+/fvl9/vD1k/eS4rPT3dibLggKVLl2rJkiVasmSJFixY4HQ5Rrjiiivk\n9/v17rvvOl1Ktzp27Jjef/99rVixQn379m375fP51Nraqr59+4Z8joWbnDz+Eel/23DyfUpfdbJ/\nt11RPHkVeebMmQ5XEj5vvPGGRo4c2e5s+uWXXy7JPe9hu/fee3Xs2DHt3btXBw8e1Msvv6wPP/xQ\nCQkJrvvb5lO56/8AhsnLy1NLS4uqq6tD1isqKuT1epWVleVQZQinZcuWacmSJVq4cKEWL17sdDnG\n2LZtmzwej4YNG+Z0Kd3qggsu0LZt29r9mjBhguLj47Vt2zY98MADTpcZdh9++KGee+45ZWZmKj4+\n3ulyutUNN9wgSdq4cWPI+oYNGyRJ3/ve98Jek1M+++wzVVZW6oorrnDVBSuv16t9+/a1+1yFV155\nRZJc9abzuLg4paenKyUlRYcOHdJf/vIX3X777erZs6fTpTmGM+sOmjhxonJzc1VUVKSmpialpqbK\n5/Np06ZNqqysjOgPgDjVxo0b1draqubmZklffDjEM888I0maNGlSyG2cIs2KFSvaPg342muvbfcp\nfW74Q3rWrFk677zzdMUVV2jgwIE6duyYnn76af3lL3/RvHnz1L9/f6dL7Fbx8fEaO3Zsu/Xy8nJF\nR0d3+FikKSgoaDsG1a9fP73zzjtasWKF3n//fZWXlztdXrcbP368Jk+erPvvv1/BYFDf+973tHv3\nbi1dulTXXXedfvCDHzhdYtj893//txobG111VV2SiouLdf311ys3N1dz585Vv379tGPHDi1fvlwj\nR45sOxIVyWpra1VdXa3vfve7iouL0549e1RaWuqqT/LtlMP3eXe95uZm6+6777YuuOACq0ePHtao\nUaMsn8/ndFlhlZKSYknq8Nc///lPp8vrVmPGjOm0d7f85/n4449bV155pdWvXz8rJibG6tOnjzVm\nzBjrySefdLo0R7npQ5GWL19uZWZmWueff74VHR1t9e/f38rLy7N27tzpdGlh8/HHH1v33nuvdeGF\nF1oxMTHWkCFDrPvuu8/69NNPnS4trHJzc62EhASrqanJ6VLC7oUXXrDGjx9vXXDBBVbPnj2t4cOH\nWz/96U+tY8eOOV1aWLz99tvWVVddZSUlJVk9evSwUlNTrYULF1otLS1Ol+a4KMty4T2hAAAAgHMA\nZ9YBAAAAQxHWAQAAAEMR1gEAAABDEdYBAAAAQxHWAQAAAEMR1gEAAABDEdYBAAAAQxHWAQAAAEMR\n1gEAAABDEdYBAAAAQxHWAQAAAEP9P/dQT1zQHEjoAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec0bb4550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from kf_book.book_plots import figsize, set_figsize\n",
    "import kf_book.book_plots as book_plots\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "belief = np.array([1./3, 1./3, 0, 0, 0, 0, 0, 0, 1/3, 0])\n",
    "plt.figure()\n",
    "set_figsize(y=2)\n",
    "book_plots.bar_plot(belief)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This distribution is called a [*categorical distribution*](https://en.wikipedia.org/wiki/Categorical_distribution), which is a discrete distribution describing the probability of observing $n$ outcomes. It is a [*multimodal distribution*](https://en.wikipedia.org/wiki/Multimodal_distribution) because we have multiple beliefs about the position of our dog. Of course we are not saying that we think he is simultaneously in three different locations, merely that we have narrowed down our knowledge to one of these three locations. My (Bayesian) belief is that there is a 33.3% chance of being at door 0, 33.3% at door 1, and a 33.3% chance of being at door 8.\n",
    "\n",
    "This is an improvement in two ways. I've rejected a number of hallway positions as impossible, and the strength of my belief in the remaining positions has increased from 10% to 33%. This will always happen. As our knowledge improves the probabilities will get closer to 100%.\n",
    "\n",
    "A few words about the [*mode*](https://en.wikipedia.org/wiki/Mode_(statistics&#41;)\n",
    "of a distribution. Given a set of numbers, such as {1, 2, 2, 2, 3, 3, 4}, the *mode* is the number that occurs most often. For this set the mode is 2. A set can contain more than one mode. The set {1, 2, 2, 2, 3, 3, 4, 4, 4} contains the modes 2 and 4, because both occur three times. We say the former set is [*unimodal*](https://en.wikipedia.org/wiki/Unimodality), and the latter is *multimodal*.\n",
    "\n",
    "Another term used for this distribution is a [*histogram*](https://en.wikipedia.org/wiki/Histogram). Histograms graphically depict the distribution of a set of numbers. The bar chart above is a histogram.\n",
    "\n",
    "I hand coded the `belief` array in the code above. How would we implement this in code? We represent doors with 1, and walls as 0, so we will multiply the hallway variable by the percentage, like so;"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[ 0.333  0.333  0.0  0.0  0.0  0.0  0.0  0.0  0.333  0.0]\n"
     ]
    }
   ],
   "source": [
    "belief = hallway * (1./3)\n",
    "print(belief)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Extracting Information from Sensor Readings\n",
    "\n",
    "Let's put Python aside and think about the problem a bit. Suppose we were to read the following from Simon's sensor:\n",
    "\n",
    "  * door\n",
    "  * move right\n",
    "  * door\n",
    "  \n",
    "\n",
    "Can we deduce Simon's location? Of course! Given the hallway's layout there is only one place from which you can get this sequence, and that is at the left end. Therefore we can confidently state that Simon is in front of the second doorway. If this is not clear, suppose Simon had started at the second or third door. After moving to the right, his sensor would have returned 'wall'. That doesn't match the sensor readings, so we know he didn't start there. We can continue with that logic for all the remaining starting positions. The only possibility is that he is now in front of the second door. Our belief is:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "belief = np.array([0., 1., 0., 0., 0., 0., 0., 0., 0., 0.])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I designed the hallway layout and sensor readings to give us an exact answer quickly. Real problems are not so clear cut. But this should trigger your intuition - the first sensor reading only gave us low probabilities (0.333) for Simon's location, but after a position update and another sensor reading we know more about where he is. You might suspect, correctly, that if you had a very long hallway with a large number of doors that after several sensor readings and positions updates we would either be able to know where Simon was, or have the possibilities narrowed down to a small number of possibilities. This is possible when a set of sensor readings only matches one to a few starting locations.\n",
    "\n",
    "We could implement this solution now, but instead let's consider a real world complication to the problem."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Noisy Sensors\n",
    "\n",
    "Perfect sensors are rare. Perhaps the sensor would not detect a door if Simon sat in front of it while scratching himself, or misread if he is not facing down the hallway. Thus when I get **door** I cannot use 1/3 as the probability. I have to assign less than 1/3 to each door, and assign a small probability to each blank wall position. Something like\n",
    "\n",
    "```Python\n",
    "[.31, .31, .01, .01, .01, .01, .01, .01, .31, .01]\n",
    "```\n",
    "\n",
    "At first this may seem insurmountable. If the sensor is noisy it casts doubt on every piece of data. How can we conclude anything if we are always unsure?\n",
    "\n",
    "The answer, as for the problem above, is with probabilities. We are already comfortable assigning a probabilistic belief to the location of the dog; now we have to incorporate the additional uncertainty caused by the sensor noise. \n",
    "\n",
    "Say we get a reading of **door**, and suppose that testing shows that the sensor is 3 times more likely to be right than wrong. We should scale the probability distribution by 3 where there is a door. If we do that the result will no longer be a probability distribution, but we will learn how to fix that in a moment.\n",
    "\n",
    "Let's look at that in Python code. Here I use the variable `z` to denote the measurement. `z` or `y` are customary choices in the literature for the measurement. As a programmer I prefer meaningful variable names, but I want you to be able to read the literature and/or other filtering code, so I will start introducing these abbreviated names now."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "belief: [ 0.3  0.3  0.1  0.1  0.1  0.1  0.1  0.1  0.3  0.1]\n",
      "sum = 1.6\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nGRnauXNnu3N37NihjIwM9ejRQzExMZo5c6bOnj3rbbkAAACA3/M61E+aNEllZWVatmyZampq9MMf\n/lBTp05VRUXFVfe7dOmSRo0apZ07d6qkpETbtm1TbGysxo4dqz179njM3bNnj8aNG6fY2Fht27ZN\nJSUl2rFjh0aNGqVLly55WzIAAADg10K8mVxdXa23335bFRUVmjp1qiQpKytLJ0+eVFFRkSZPnqzg\n4OB2912/fr0OHz6sffv2KSMjw73v4MGDtWDBAu3fv989t6ioSImJidq0aZNCQv5W4g9+8AP96Ec/\n0oYNG1RQUOBxbMMw2pzP5XJ5s7S/m57dbKactytcTw/9af0SPQj09Uv0QPK+B4G+fokeSP7Vg0Bf\nv0QPzMiV7Z2zvczbymZcbesVHn30UVVVVenLL790h21Jqqys1LRp07R3714NGzas3X2zs7N16tQp\nHT161GN89erVWrRokf7yl7+oX79+On36tG677TatXr1aTz/9tMfcO++8U3Fxcfrtb3/rMe5wONTS\n0tLZZQAAAACWExER4ZHBv8ur228OHz6su+66q83BBg0a5N5+tX1b57W375EjRzyO0dHcq50DAAAA\nCERehfr6+npFRUW1GW8dq6+v/977tn7uaO7VzgEAAAAEIq8flLXZOr4/6mrbvN23o7nXOgcAAAAQ\naLx6UDY6OrrdK+UNDQ2S2r+67u2+0dHRktq/6t/Q0NDuOYKCghQREeExZrPZ+AUAAAAAlmQYRpsH\nY4OCOr4e71WoT01NVWVlpRwOh8d99XV1dZKklJSUq+7bOu+7rty39XNdXZ3Gjx/fZm575wgKCrrq\nIgEAAAB/5lUSzs3NVXNzszZv3uwxXlZWJrvdrvT09Kvue/ToUY+XrnQ4HCovL1d6errsdrskqV+/\nfhoyZIjKy8vldDrdc2tra3Xs2DFNmjTJm5IBAAAAv+fVS1pK0ujRo3Xw4EE999xzSkhIUGVlpV5+\n+WWVl5fr4YcfliTl5+errKxMx48fV1xcnKS/vfnUPffco8bGRhUXF6tPnz5au3attm/frh07duj+\n++93n2P37t3Kzs5WTk6OHn/8cZ09e1ZPP/20evbsqYMHD6p79+5/xxYAAAAA1ub1PStbtmzRjBkz\ntHTpUo0dO1b79+9XZWWlO9BLktPplNPp9LgPqHv37tq5c6eysrI0b9485eTk6LPPPlNNTY1HoJek\nzMxMVVdX67PPPlNOTo7mzZunrKws7dy5M2ADfXNzswoLC2W32xUWFqa0tDRVVVWZXZZPNTU1acGC\nBRo9erR69+4tm82m5cuXm12Wz/zud7/T7NmzlZSUpIiICPXr108//vGP9T//8z9ml+YTf/rTn/Tg\ngw+qf//+Cg8PV1RUlDIyMlReXm52aaZZt26dbDabIiMjzS7FJ3bv3u1+XurKj9raWrPL86l3331X\n48eP1y233KLw8HDdcccdWrlypdlldbmZM2d2+D0QSN8H77//viZOnCi73a4ePXooKSlJK1as0IUL\nF8wuzSfee+89jRkzRjfddJMiIyOVlZWlvXv3ml2W6by+Ug9zjB49WgcOHFBxcbESExNVUVGhdevW\naePGjZo2bZrZ5fnEiRMnlJaWpsGDBysxMVHr1q3TsmXLAibY5+Xlqb6+Xnl5eRo4cKDOnTun559/\nXgcPHtRbb72lkSNHml1il9q9e7eqqqo0fPhw9evXTy0tLdq4caOqqqq0cuVKLVmyxOwSfer06dNK\nTk5WRESEvv76azU3N5tdUpfbvXu3srKy9Mtf/lJZWVke21JSUgLml5uKigrNmDFDDz30kKZNm6bI\nyEgdP35cZ86c0dKlS80ur0sdP35c586dazOek5Oj7t276+TJkx2+s72/+PDDD3XPPffozjvv1KJF\nixQTE6Pf//73WrVqlR588EFt27bN7BK71IEDB3TfffdpyJAh+td//VcZhqE1a9bo/fff165du5SR\nkWF2ieYxcMN74403DElGRUWFx3h2drZht9sNh8NhUmW+5XK5DJfLZRiGYZw7d86QZCxbtszconzo\niy++aDPW1NRkxMbGGqNGjTKhohtDenq6cfvtt5tdhs9NmDDByMnJMR555BEjIiLC7HJ8YteuXYYk\n4/XXXze7FNP85S9/MSIiIoyCggKzS7lh7N6925BkLFmyxOxSfGLx4sWGJOP//u//PMYfe+wxQ5LR\n0NBgUmW+MWbMGCM2NtZoaWlxjzU2NhoxMTHGsGHDTKzMfLxkjAVs3bpVkZGRysvL8xifNWuWzpw5\n4/HwsT8L9Jcp7dOnT5uxyMhIDRw4UKdOnTKhohtDTExMh2+Z7a/Ky8u1Z88erV271uxS4GPr1q1T\nS0uLFi5caHYpN4z169fLZrNp9uzZZpfiE6GhoZKknj17eoz36tVLQUFB6tatmxll+czevXuVmZmp\nHj16uMduuukmjRgxQvv27dNnn31mYnXmItRbwOHDh3XXXXe1CS6DBg1yb0dg+vrrr/XHP/5RycnJ\nZpfiMy6XSw6HQ+fOndPatWv11ltvBVTAOXv2rAoLC1VcXKzbbrvN7HJMMXfuXIWEhOjmm2/WmDFj\n9O6775pdks/8/ve/V1RUlI4ePaq0tDSFhISoT58++ud//mc1NjaaXZ7Pff3119q0aZNGjRqlH/zg\nB2aX4xOPPPKIevXqpYKCAn3yySdqamrSb37zG7344ouaO3dum/ft8Td//etf232+snWsvZdPDxSE\neguor69v9023Wsfae6MuBIa5c+eqpaVFixcvNrsUn3n88ccVGhqqPn366Mknn9R//ud/6qc//anZ\nZfnM448/rjvvvFMFBQVml+JzPXv21L/8y7/oxRdf1K5du1RSUqJTp04pMzNTb731ltnl+cTp06d1\n4cIF5eXlafLkydqxY4eKior0X//1Xxo/fnybN6rxd5WVlbp48aLy8/PNLsVnBgwYoD/84Q86fPiw\n/uEf/kE333yzcnJy9Mgjj6ikpMTs8rrcwIEDVVtbK5fL5R5zOBzuuxYCORMF1t+sLexqt50E8i0p\ngewXv/iFNm7cqF/96le65557zC7HZxYtWqQ5c+bo7Nmz2r59u5544gm1tLRo/vz5ZpfW5TZv3qzt\n27fr/fffD8if+7vvvlt33323++v77rtPubm5Sk1N1YIFCzRmzBgTq/MNl8ulb775RsuWLdPTTz8t\n6W+vGNetWzcVFhZq586deuCBB0yu0nfWr1+v6Oho5ebmml2Kz5w4cUI5OTmKjY3Vpk2b1Lt3b+3f\nv1+rVq1Sc3Oz1q9fb3aJXWrevHnKz8/XE088ocWLF8vlcumZZ57RyZMnJV39HVf9XeCu3EKio6Pb\n/c2zoaFBktq9ig//9swzz2jVqlV69tln9cQTT5hdjk/1799f9957r8aPH69f//rXeuyxx/Tzn/+8\n3VfE8CfNzc2aO3eu5s2bJ7vdrq+++kpfffWV/vrXv0qSvvrqK7W0tJhcpe/16tVLEyZM0AcffKCL\nFy+aXU6Xi46OlqQ2v8CMGzdOkvTHP/7R5zWZ5YMPPtDBgwc1ffr0gHq566efflqNjY1666239JOf\n/EQjRoxQUVGR/uM//kMbNmzQnj17zC6xS82ePVvFxcV69dVXddttt6l///768MMP3Rd2+vXrZ3KF\n5iHUW0Bqaqo++ugjORwOj/HW+8ZSUlLMKAsmeeaZZ7R8+XItX75cixYtMrsc0w0ZMkQOh0OffPKJ\n2aV0qfPnz+uLL77Q888/r1tuucX9UVlZqZaWFt1yyy0e7xcSSFpvOQmEv160Pkt1pdYeBNJVytYr\n0nPmzDG5Et/605/+pIEDB7bX9C7/AAADV0lEQVS5d/6HP/yhpMB4zm7hwoU6f/686urqdOLECe3b\nt09ffvmlIiIiAuov11cKnJ9+C8vNzVVzc7M2b97sMV5WVia73a709HSTKoOvrVy5UsuXL9eSJUu0\nbNkys8u5IezatUtBQUGKj483u5Qu1bdvX+3atavNx5gxYxQWFqZdu3Zp1apVZpfpc19++aV+85vf\nKC0tTWFhYWaX0+V+8pOfSJJqamo8xqurqyVJQ4cO9XlNZrh06ZLKy8s1ZMiQgLuwZbfbdeTIkTbv\nTfGHP/xBkgLmAfru3bsrJSVFcXFx+vOf/6zXXntNjz76qMLDw80uzTTcU28B48aNU3Z2tgoKCtTY\n2KiEhARVVlbqzTffVHl5ud+/0cZ31dTUqKWlRU1NTZL+9iYcmzZtkiSNHz/e4yWu/M3zzz/vfifn\nBx98sM07J/r7P+aPPfaYbr75Zg0ZMkSxsbE6f/68Xn/9db322msqKipS7969zS6xS4WFhSkzM7PN\neGlpqYKDg9vd5m+mTZvmvv0qJiZGH3/8sZ5//nl98cUXKi0tNbs8nxg9erRycnK0YsUKuVwuDR06\nVAcPHtQzzzyjCRMmaPjw4WaX6BP//d//rYaGhoC7Si9JhYWFmjhxorKzs/Xkk08qJiZGtbW1Wr16\ntQYOHOi+FctfHT58WJs3b9a9996r7t2769ChQyouLg6Yd1W+KpNfJx+d1NTUZPzsZz8z+vbta3Tr\n1s0YNGiQUVlZaXZZPhcXF2dIavfj008/Nbu8LnX//fd3uPZA+FHesGGDcd999xkxMTFGSEiI0atX\nL+P+++83Xn31VbNLM1UgvfnU6tWrjbS0NKNnz55GcHCw0bt3byM3N9d47733zC7Npy5cuGAsXLjQ\nuP32242QkBCjf//+xs9//nPjm2++Mbs0n8nOzjYiIiKMxsZGs0sxxe9+9ztj9OjRRt++fY3w8HAj\nMTHReOqpp4zz58+bXVqXO3bsmDFixAgjKirK6Natm5GQkGAsWbLEaG5uNrs009kMI8Be/woAAADw\nM9xTDwAAAFgcoR4AAACwOEI9AAAAYHGEegAAAMDiCPUAAACAxRHqAQAAAIsj1AMAAAAWR6gHAAAA\nLI5QDwAAAFgcoR4AAACwOEI9AAAAYHH/D5WenTzJ3olUAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec0bc6e48>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def update_belief(hall, belief, z, correct_scale):\n",
    "    for i, val in enumerate(hall):\n",
    "        if val == z:\n",
    "            belief[i] *= correct_scale\n",
    "\n",
    "belief = np.array([0.1] * 10)\n",
    "reading = 1 # 1 is 'door'\n",
    "update_belief(hallway, belief, z=reading, correct_scale=3.)\n",
    "print('belief:', belief)\n",
    "print('sum =', sum(belief))\n",
    "plt.figure()\n",
    "book_plots.bar_plot(belief)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is not a probability distribution because it does not sum to 1.0. But the code is doing mostly the right thing - the doors are assigned a number (0.3) that is 3 times higher than the walls (0.1). All we need to do is normalize the result so that the probabilities correctly sum to 1.0. Normalization is done by dividing each element by the sum of all elements in the list. That is easy with NumPy:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.188,  0.188,  0.0625,  0.0625,  0.0625,  0.0625,  0.0625,\n",
       "        0.0625,  0.188,  0.0625])"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "belief / sum(belief)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "FilterPy implements this with the `normalize` function:\n",
    "\n",
    "```Python\n",
    "from filterpy.discrete_bayes import normalize\n",
    "normalize(belief)\n",
    "```\n",
    "\n",
    "It is a bit odd to say \"3 times as likely to be right as wrong\". We are working in probabilities, so let's specify the probability of the sensor being correct, and compute the scale factor from that. The equation for that is\n",
    "\n",
    "$$scale =  \\frac{prob_{correct}}{prob_{incorrect}} = \\frac{prob_{correct}} {1-prob_{correct}}$$\n",
    "\n",
    "\n",
    "\n",
    "Also, the `for` loop is cumbersome. As a general rule you will want to avoid using `for` loops in NumPy code. NumPy is implemented in C and Fortran, so if you avoid for loops the result often runs 100x faster than the equivalent loop.\n",
    "\n",
    "How do we get rid of this `for` loop? NumPy lets you index arrays with boolean arrays. You create a boolean array with logical operators. We can find all the doors in the hallway with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ True,  True, False, False, False, False, False, False,  True,\n",
       "       False], dtype=bool)"
      ]
     },
     "execution_count": 9,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "hallway == 1"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "When you use the boolean array as an index to another array it returns only the elements where the index is `True`. Thus we can replace the `for` loop with\n",
    "\n",
    "```python\n",
    "belief[hall==z] *= scale\n",
    "```\n",
    "and only the elements which equal `z` will be multiplied by `scale`.\n",
    "\n",
    "Teaching you NumPy is beyond the scope of this book. I will use idiomatic NumPy constructs and explain them the first time I present them. If you are new to NumPy there are many blog posts and videos on how to use NumPy efficiently and idiomatically.\n",
    "\n",
    "Here is our improved version:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "sum = 1.0\n",
      "probability of door = 0.1875\n",
      "probability of wall = 0.0625\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nPtPrr7+utLQ0u0sJKb/fL5/Pp8OHD+vxxx/Xhg0bHBVePvnkExUXF6usrEznnnuu3eXYYubMmYqK\nilKPHj00fvx4vfzyy3aXFBIvvfSSEhIS9M477ygzM1NRUVFKTk7Wr371KzU2Ntpdni0+++wzrVy5\nUldccYXOP/98u8vpdDfddJPi4+NVVFSkffv2qampSc8++6yWLFmimTNntrnvTDj6+uuvFRMT02b8\n+Fh7l/92CsK6jerr69u9kdTxsfZuIoXwN3PmTDU3N6ukpMTuUkLqtttuU3R0tJKTk3X33XfrT3/6\nk375y1/aXVbI3HbbbbrgggtUVFRkdykhd/bZZ+uuu+7SkiVLtHHjRpWXl+vgwYMaO3asNmzYYHd5\nne7DDz/U559/rry8PE2dOlXPP/+8Zs2apb/97W+aOHFimxuoOIHH49EXX3yhwsJCu0sJiX79+um1\n115TXV2dfvSjH6lHjx6aPHmybrrpJpWXl9tdXkgMGTJEW7Zskd/vbx3z+XytuwycnImc9W/MBjrR\nFhAnbw9xqt/+9rdasWKFHnvsMV144YV2lxNSc+bM0S233KJPPvlEa9as0e23367m5mb95je/sbu0\nTldbW6s1a9Zo586djvxzP3z4cA0fPrz1/y+77DLl5uYqIyNDs2fP1vjx422srvP5/X59+eWXKi0t\n1X333SdJGjt2rLp27ari4mK98MIL+ulPf2pzlaG1bNkyJSYmKjc31+5SQmL//v2aPHmyevXqpZUr\nV6pnz57aunWrFi5cKK/Xq2XLltldYqe74447VFhYqNtvv10lJSXy+/1asGCBDhw4IOnkd/kMZ85d\nuQESExPb/U2xoaFBkto9647wtWDBAi1cuFC/+93vdPvtt9tdTsj17dtXF110kSZOnKi//OUvmjFj\nhu6///52rxARTrxer2bOnKk77rhDbrdbR48e1dGjR/X1119Lko4eParm5mabqwy9+Ph4XXXVVXrz\nzTf1xRdf2F1Op0pMTJSkNr+UTJgwQZL0+uuvh7wmO7355pvasWOHbrjhhna3RYSj++67T42Njdqw\nYYOuvfZajR49WrNmzdIf//hHLV++XJs3b7a7xE43ffp0lZWV6cknn9S5556rvn376q233mo9YdOn\nTx+bK7QPYd1GGRkZevvtt+Xz+QLGj+/LSk9Pt6Ms2GDBggWaP3++5s+frzlz5thdjhFGjBghn8+n\nffv22V1Kpzpy5Ig+/vhjLVq0SOecc07rl8fjUXNzs84555yA+1g4yfHtH+H+rw3HP6f0XcfX77Qz\nisfPIt9yyy02VxI6b7zxhoYMGdJmb/rFF18syTmfYbv33nt15MgR7d69W/v379err76qTz/9VHFx\ncY771+Zvc9bfAIbJzc2V1+tVbW1twHhlZaXcbreysrJsqgyh9OCDD2r+/PmaO3euSktL7S7HGBs3\nblRkZKT69+9vdymdqnfv3tq4cWObr/Hjxys2NlYbN27UwoUL7S4z5D799FM9++yzyszMVGxsrN3l\ndKprr71WkrRu3bqA8bVr10qSLrnkkpDXZJevvvpKVVVVGjFihKNOWLndbu3Zs6fNfRVee+01SXLU\nh85jYmKUnp6ulJQUvf/++3rqqad06623qlu3bnaXZhv2rNtowoQJysnJUVFRkRobG5WamiqPx6P1\n69erqqoqrG8A8W3r1q1Tc3OzmpqaJP3/zSFWrlwpSZo4cWLAZZzCzaJFi1rvBjxp0qQ2d+lzwg/p\nGTNmqEePHhoxYoR69eqlI0eO6Omnn9ZTTz2lWbNmqWfPnnaX2KliY2M1duzYNuMVFRXq0qVLu4+F\nm4KCgtZtUElJSXr33Xe1aNEiffzxx6qoqLC7vE43btw4TZ48WQ888ID8fr8uueQS7dixQwsWLNBV\nV12lSy+91O4SQ+bvf/+7GhoaHHVWXZKKi4t1zTXXKCcnR3fffbeSkpK0ZcsWPfzwwxoyZEjrlqhw\nVldXp9raWl100UWKiYnRrl27VFZW5qg7+XbI5uu8O15TU5N15513Wr1797a6du1qDR061PJ4PHaX\nFVIpKSmWpHa//ve//9ldXqcaM2ZMh2t3yh/P5cuXW5dddpmVlJRkRUVFWfHx8daYMWOsJ5980u7S\nbOWkmyI9/PDDVmZmpnX22WdbXbp0sXr27Gnl5uZa27Zts7u0kPn888+te++91zrvvPOsqKgoq2/f\nvtb9999vffnll3aXFlI5OTlWXFyc1djYaHcpIffiiy9a48aNs3r37m1169bNGjhwoHXPPfdYR44c\nsbu0kPjPf/5jjR492kpISLC6du1qpaamWnPnzrW8Xq/dpdkuwrIceE0oAAAA4AzAnnUAAADAUIR1\nAAAAwFCEdQAAAMBQhHUAAADAUIR1AAAAwFCEdQAAAMBQhHUAAADAUIR1AAAAwFCEdQAAAMBQhHUA\nAADAUIR1AAAAwFD/B37nMeHwyGFnAAAAAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1376400>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from filterpy.discrete_bayes import normalize\n",
    "\n",
    "def scaled_update(hall, belief, z, z_prob): \n",
    "    scale = z_prob / (1. - z_prob)\n",
    "    belief[hall==z] *= scale\n",
    "    normalize(belief)\n",
    "\n",
    "belief = np.array([0.1] * 10)\n",
    "scaled_update(hallway, belief, z=1, z_prob=.75)\n",
    "\n",
    "print('sum =', sum(belief))\n",
    "print('probability of door =', belief[0])\n",
    "print('probability of wall =', belief[2])\n",
    "book_plots.bar_plot(belief, ylim=(0, .3))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    " We can see from the output that the sum is now 1.0, and that the probability of a door vs wall is still three times larger. The result also fits our intuition that the probability of a door must be less than 0.333, and that the probability of a wall must be greater than 0.0. Finally, it should fit our intuition that we have not yet been given any information that would allow us to distinguish between any given door or wall position, so all door positions should have the same value, and the same should be true for wall positions.\n",
    " \n",
    "This result is called the [*posterior*](https://en.wikipedia.org/wiki/Posterior_probability), which is short for *posterior probability distribution*. All this means is a probability distribution *after* incorporating the measurement information (posterior means 'after' in this context). To review, the *prior* is the probability distribution before including the measurement's information. \n",
    "\n",
    "Another term is the [*likelihood*](https://en.wikipedia.org/wiki/Likelihood_function). When we computed `belief[hall==z] *= scale` we were computing how *likely* each position was given the measurement. The likelihood is not a probability distribution because it does not sum to one.\n",
    "\n",
    "The combination of these gives the equation\n",
    "\n",
    "$$\\mathtt{posterior} = \\frac{\\mathtt{likelihood} \\times \\mathtt{prior}}{\\mathtt{normalization}}$$ \n",
    "\n",
    "It is very important to learn and internalize these terms as most of the literature uses them extensively.\n",
    "\n",
    "Does `scaled_update()` perform this computation? It does. Let me recast it into this form:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def scaled_update(hall, belief, z, z_prob): \n",
    "    scale = z_prob / (1. - z_prob)\n",
    "    likelihood = np.ones(len(hall))\n",
    "    likelihood[hall==z] *= scale\n",
    "    return normalize(likelihood * belief)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This function is not fully general. It contains knowledge about the hallway, and how we match measurements to it. We always strive to write general functions. Here we will remove the computation of the likelihood from the function, and require the caller to compute the likelihood themselves.\n",
    "\n",
    "Here is a full implementation of the algorithm:\n",
    "\n",
    "```python\n",
    "def update(likelihood, prior):\n",
    "    return normalize(likelihood * prior)\n",
    "```\n",
    "\n",
    "Computation of the likelihood varies per problem. For example, the sensor might not return  just 1 or 0, but a `float` between 0 and 1 indicating the probability of being in front of a door. It might use computer vision and report a blob shape that you then probabilistically match to a door. It might use sonar and return a distance reading. In each case the computation of the likelihood will be different. We will see many examples of this throughout the book, and learn how to perform these calculations.\n",
    "\n",
    "FilterPy implements `update`. Here is the previous example in a fully general form:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.188,  0.188,  0.0625,  0.0625,  0.0625,  0.0625,  0.0625,\n",
       "        0.0625,  0.188,  0.0625])"
      ]
     },
     "execution_count": 12,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from filterpy.discrete_bayes import update\n",
    "\n",
    "def lh_hallway(hall, z, z_prob):\n",
    "    \"\"\" compute likelihood that a measurement matches\n",
    "    positions in the hallway.\"\"\"\n",
    "    \n",
    "    try:\n",
    "        scale = z_prob / (1. - z_prob)\n",
    "    except ZeroDivisionError:\n",
    "        scale = 1e8\n",
    "\n",
    "    likelihood = np.ones(len(hall))\n",
    "    likelihood[hall==z] *= scale\n",
    "    return likelihood\n",
    "\n",
    "belief = np.array([0.1] * 10)\n",
    "likelihood = lh_hallway(hallway, z=1, z_prob=.75)\n",
    "update(likelihood, belief)  "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Incorporating Movement\n",
    "\n",
    "Recall how quickly we were able to find an exact solution when we incorporated a series of measurements and movement updates. However, that occurred in a fictional world of perfect sensors. Might we be able to find an exact solution with noisy sensors?\n",
    "\n",
    "Unfortunately, the answer is no. Even if the sensor readings perfectly match an extremely complicated hallway map, we cannot be 100% certain that the dog is in a specific position - there is, after all, a tiny possibility that every sensor reading was wrong! Naturally, in a more typical situation most sensor readings will be correct, and we might be close to 100% sure of our answer, but never 100% sure. This may seem complicated, but let's go ahead and program the math.\n",
    "\n",
    "First let's deal with the simple case - assume the movement sensor is perfect, and it reports that the dog has moved one space to the right. How would we alter our `belief` array?\n",
    "\n",
    "I hope that after a moment's thought it is clear that we should shift all the values one space to the right. If we previously thought there was a 50% chance of Simon being at position 3, then after he moved one position to the right we should believe that there is a 50% chance he is at position 4. The hallway is circular, so we will use modulo arithmetic to perform the shift."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nrq526a/KY/L4qiKmo+LWlJhVEVev16OwsNDu/VbUA5nvrcQcqCweX2VV1xxYU2JWJNfblAmHDx+O\noqIirF271qQ9NTUVgYGB6Nq1q03BRQR79+6Fr68v/P39bdqWiIiUw3xPRKQONl1ZHzhwIPr164f4\n+HgUFBSgWbNmSE9Px7Zt25CWlmachxMXF4fU1FScPn0awcHBAIChQ4ciNDQUYWFh8Pf3x6VLl5CS\nkoK9e/fio48+4uO8iIhUhPmeiEgdbM6Y69atw/Tp0zFz5kzk5eWhVatWSE9Px+jRo43rlJSUoKSk\nBCJibOvRowfWrFmDBQsWoKCgAL6+vujUqRM2b95s9dMBiIio6jDfExE5ns3FularRXJyMpKTk8tc\nJyUlBSkpKSZt06ZNw7Rp02weIBEROQbzPRGR46nr8QNERERERGTEYp2IiIiISKVYrBMRERERqRSL\ndSIiIiIilWKxTkRERESkUizWiYiIiIhUisU6EREREZFKsVgnIiIiIlIpFutERERERCrFYp2IiIiI\nSKVYrBMRERERqRSLdSIiIiIilWKxTkRERESkUizWiYiIiIhUisU6EREREZFKsVgnIiIiIlIpFutE\nRERERCrFYp2IiIiISKVYrBMRERERqRSLdSIiIiIilWKxTkRERESkUizWiYiIiIhUisU6EREREZFK\nsVgnIiIiIlIpFutERERERCrFYp2IiIiISKVYrBMRERERqZTNxXpRURESEhIQGBgIDw8PhIWFYdWq\nVffdbt26dYiOjkazZs3g6emJxo0bY+zYsTh16lSFBk5ERMpivicicjwXWzcYMWIEDh8+jKSkJLRo\n0QIrV65EdHQ09Ho9xowZU+Z2c+fORf369TF9+nQ0adIEv/zyC9555x08+uijyMzMRNu2bSu1I0RE\nZF/M90REjmdTsb5lyxbs3LnTmLABICIiAufPn8fUqVMRFRUFZ2dni9tu2rQJdevWNWl7/PHH0bhx\nY8ybNw+ffvppBXeBiIjsjfmeiEgdbJoGs379emi1WowaNcqkPSYmBpcuXcKhQ4fK3PbexA0AgYGB\naNiwIX755RdbhkFERApjviciUgebivXs7Gy0bt0aLi6mF+Tbt29vXG6LM2fO4Pz58/yTKBGRyjDf\nExGpg03TYHJzc9GkSROzdj8/P+Nya+l0OsTFxUGr1WLKlClWbZOTkwO9Xm91jLu8bFzfellZWZXu\n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\nxToRERERkUqxWCciIiIiUikW60REREREKsVinYiIiIhIpVisExERERGplIujB0BE5CgdVpyyYW2v\n//7zh/tvd+y55rYPiCpMyc8S4OdJRI7DK+tERERERCrFYp2IiIiISKVYrBMRERERqRSLdSIiIiIi\nlWKxTkRERESkUizWiYiIiIhUisU6EREREZFKsVgnIiIiIlIpFutERERERCrFYp2IiIiISKVYrBMR\nERERqRSLdSIiIiIilWKxTkRERESkUjYX60VFRUhISEBgYCA8PDwQFhaGVatWWbXt1atX8cILLyAg\nIABeXl7o1q0bdu3aZfOgiYhIecz3RESO52LrBiNGjMDhw4eRlJSEFi1aYOXKlYiOjoZer8eYMWPK\n3O7PP/9E3759cePGDSQnJ6Nu3br46KOPMGDAAHz99dfo3bt3pXaEiIjsi/meiMjxbCrWt2zZgp07\ndxoTNgBERETg/PnzmDp1KqKiouDs7Gxx2yVLliA7OxsHDhxAt27djNuGhoZi2rRpOHToUCV3hYiI\n7IX5nohIHWwq1tevXw+tVotRo0aZtMfExGDMmDE4dOgQunfvXua2LVu2NCZuAHBxccGzzz6LN954\nAxcvXsTDDz9sXCYiZn3o9XpbhgsA8HHT2LyNtcoaj5Ixy4rriJhKx63I530vJycnODs7w8nJyS79\nqTWmo+I+6DHV9P1raX1LebCqPGj5viblQJ5jlI1piwc9B6o9bnWMWZFcrxEbzgbdunVDSUkJvvvu\nO5P2nJwctGvXDosWLcKLL75ocdsGDRqgZ8+e+Pzzz03av/rqKwwePBjbt29H//79je06nQ7FxcXW\nDo2IqNrx9vaGi4vNsxXtgvmeiKhq3C/X23SDaW5uLvz8/MzaDW25ubmKbEtERFWL+Z6ISB1sfhqM\nRlP2n6TKW1bZbYmIqGox3xMROZ5Nxbq/v7/FKyJ5eXkAYPFKij22JSKiqsV8T0SkDjZNhgwJCUF6\nejp0Op3J3Jrjx48DANq1a1futob1SitrWycnJ3h7e5u0aTQaXpEhompJRMxuMnJyctx765jviYjs\nryK53qYbTLdu3YrIyEisWrUKUVFRxvaBAwfihx9+wIULF8p8lNf//d//4a9//SsyMzPRtWtXAHdv\nKgoLC4NWq0VmZqa1wyAiIoUx3xMRqYNNxToA9O/fH0eOHMHcuXPRrFkzpKenY/HixUhLS8PYsWMB\n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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1b1d358>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def perfect_predict(belief, move):\n",
    "    \"\"\" move the position by `move` spaces, where positive is \n",
    "    to the right, and negative is to the left\n",
    "    \"\"\"\n",
    "    n = len(belief)\n",
    "    result = np.zeros(n)\n",
    "    for i in range(n):\n",
    "        result[i] = belief[(i-move) % n]\n",
    "    return result\n",
    "        \n",
    "belief = np.array([.35, .1, .2, .3, 0, 0, 0, 0, 0, .05])\n",
    "plt.subplot(121)\n",
    "book_plots.bar_plot(belief, title='Before prediction', ylim=(0, .4))\n",
    "\n",
    "belief = perfect_predict(belief, 1)\n",
    "plt.subplot(122)\n",
    "book_plots.bar_plot(belief, title='After prediction', ylim=(0, .4))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that we correctly shifted all values one position to the right, wrapping from the end of the array back to the beginning. \n",
    "\n",
    "If you execute the next cell by pressing CTRL-Enter in it you can see this in action. This simulates Simon walking around and around the hallway. It does not (yet) incorporate new measurements so the probability distribution does not change."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        event.shiftKey = false;\n",
       "        // Send a \"J\" for go to next cell\n",
       "        event.which = 74;\n",
       "        event.keyCode = 74;\n",
       "        manager.command_mode();\n",
       "        manager.handle_keydown(event);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,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\" width=\"576\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import time\n",
    "\n",
    "%matplotlib notebook\n",
    "set_figsize(y=2)\n",
    "fig = plt.figure()\n",
    "for _ in range(50):\n",
    "    # Simon takes one step to the right\n",
    "    belief = perfect_predict(belief, 1)\n",
    "    plt.cla()\n",
    "    book_plots.bar_plot(belief, ylim=(0, .4))\n",
    "    fig.canvas.draw()\n",
    "    time.sleep(0.05)\n",
    "    \n",
    "# reset to noninteractive plot settings\n",
    "%matplotlib inline\n",
    "set_figsize(y=2);"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Terminology\n",
    "\n",
    "Let's pause a moment to review terminology. I introduced this terminology in the last chapter, but let's take a second to help solidify your knowledge. \n",
    "\n",
    "The *system* is what we are trying to model or filter. Here the system is our dog. The *state* is its current configuration or value. In this chapter the state is our dog's position. We rarely know the actual state, so we say our filters produce the *estimated state* of the system. In practice this often gets called the state, so be careful to understand the context.\n",
    " \n",
    "One cycle of prediction and updating with a measurement is called the state or system *evolution*, which is short for *time evolution* [7]. Another term is *system propagation*. It refers to how the state of the system changes over time. For filters, time is usually a discrete step, such as 1 second. For our dog tracker the system state is the position of the dog, and the state evolution is the position after a discrete amount of time has passed.\n",
    "\n",
    "We model the system behavior with the *process model*. Here, our process model is that the dog moves one or more positions at each time step. This is not a particularly accurate model of how dogs behave. The error in the model is called the *system error* or *process error*. \n",
    "\n",
    "The prediction is our new *prior*. Time has moved forward and we made a prediction without benefit of knowing the measurements. \n",
    "\n",
    "Let's work an example. The current position of the dog is 17 m. Our epoch is 2 seconds long, and the dog is traveling at 15 m/s. Where do we predict he will be in two seconds? \n",
    "\n",
    "Clearly,\n",
    "\n",
    "$$ \\begin{aligned}\n",
    "\\bar x &= 17 + (15*2) \\\\\n",
    "&= 47\n",
    "\\end{aligned}$$\n",
    "\n",
    "I use bars over variables to indicate that they are priors (predictions). We can write the equation for the process model like this:\n",
    "\n",
    "$$ \\bar x_{k+1} = f_x(\\bullet) + x_k$$\n",
    "\n",
    "$x_k$ is the current position or state. If the dog is at 17 m then $x_k = 17$.\n",
    "\n",
    "$f_x(\\bullet)$ is the state propagation function for x. It describes how much the $x_k$ changes over one time step. For our example it performs the computation $15 \\cdot 2$ so we would define it as \n",
    "\n",
    "$$f_x(v_x, t) = v_k t$$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Adding Uncertainty to the Prediction\n",
    "\n",
    "`perfect_predict()` assumes perfect measurements, but all sensors have noise. What if the sensor reported that our dog moved one space, but he actually moved two spaces, or zero? This may sound like an insurmountable problem, but let's model it and see what happens.\n",
    "\n",
    "Assume that the sensor's movement measurement is 80% likely to be correct, 10% likely to overshoot one position to the right, and 10% likely to undershoot to the left. That is, if the movement measurement is 4 (meaning 4 spaces to the right), the dog is 80% likely to have moved 4 spaces to the right, 10% to have moved 3 spaces, and 10% to have moved 5 spaces.\n",
    "\n",
    "Each result in the array now needs to incorporate probabilities for 3 different situations. For example, consider the reported movement of 2. If we are 100% certain the dog started from position 3, then there is an 80% chance he is at 5, and a 10% chance for either 4 or 6. Let's try coding that:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nQpIkNfP/Jbsb6AbUS8QAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1bc8eb8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "def predict_move(belief, move, p_under, p_correct, p_over):\n",
    "    n = len(belief)\n",
    "    prior = np.zeros(n)\n",
    "    for i in range(n):\n",
    "        prior[i] = (\n",
    "            belief[(i-move) % n]   * p_correct +\n",
    "            belief[(i-move-1) % n] * p_over +\n",
    "            belief[(i-move+1) % n] * p_under)      \n",
    "    return prior\n",
    "\n",
    "belief = [0., 0., 0., 1., 0., 0., 0., 0., 0., 0.]\n",
    "prior = predict_move(belief, 2, .1, .8, .1)\n",
    "book_plots.plot_belief_vs_prior(belief, prior)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It appears to work correctly. Now what happens when our belief is not 100% certain?"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([ 0.0,  0.0,  0.0,  0.04,  0.38,  0.52,  0.06,  0.0,  0.0,  0.0])"
      ]
     },
     "execution_count": 16,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "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\nAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1b24da0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "belief = [0, 0, .4, .6, 0, 0, 0, 0, 0, 0]\n",
    "prior = predict_move(belief, 2, .1, .8, .1)\n",
    "book_plots.plot_belief_vs_prior(belief, prior)\n",
    "prior"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Here the results are more complicated, but you should still be able to work it out in your head. The 0.04 is due to the possibility that the 0.4 belief undershot by 1. The 0.38 is due to the following: the 80% chance that we moved 2 positions (0.4 $\\times$ 0.8) and the 10% chance that we undershot (0.6 $\\times$ 0.1). Overshooting plays no role here because if we overshot both 0.4 and 0.6 would be past this position. **I strongly suggest working some examples until all of this is very clear, as so much of what follows depends on understanding this step.**\n",
    "\n",
    "If you look at the probabilities after performing the update you might be dismayed. In the example above we started with probabilities of 0.4 and 0.6 in two positions; after performing the update the probabilities are not only lowered, but they are strewn out across the map.\n",
    "\n",
    "This is not a coincidence, or the result of a carefully chosen example - it is always true of the prediction. If the sensor is noisy we lose some information on every prediction. Suppose we were to perform the prediction an infinite number of times - what would the result be? If we lose information on every step, we must eventually end up with no information at all, and our probabilities will be equally distributed across the `belief` array. Let's try this with 100 iterations. The plot is animated; recall that you put the cursor in the cell and press Ctrl-Enter to execute the code and see the animation."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        event.shiftKey = false;\n",
       "        // Send a \"J\" for go to next cell\n",
       "        event.which = 74;\n",
       "        event.keyCode = 74;\n",
       "        manager.command_mode();\n",
       "        manager.handle_keydown(event);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,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\" width=\"576\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final Belief: [ 0.104  0.103  0.101  0.0987  0.0967  0.0959  0.0967  0.0987  0.101\n",
      "  0.103]\n"
     ]
    }
   ],
   "source": [
    "%matplotlib notebook\n",
    "set_figsize(y=2)\n",
    "\n",
    "belief = np.array([1.0, 0, 0, 0, 0, 0, 0, 0, 0, 0])\n",
    "plt.figure()\n",
    "    \n",
    "for i in range(100):\n",
    "    plt.cla()\n",
    "    belief = predict_move(belief, 1, .1, .8, .1)\n",
    "    book_plots.bar_plot(belief)\n",
    "    plt.title('Step {}'.format(i+1))\n",
    "    plt.gcf().canvas.draw()\n",
    "print('Final Belief:', belief)\n",
    "\n",
    "# reset to noninteractive plot settings\n",
    "%matplotlib inline\n",
    "set_figsize(y=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After 100 iterations we have lost almost all information, even though we were 100% sure that we started in position 0. Feel free to play with the numbers to see the effect of differing number of updates. For example, after 100 updates a small amount of information is left, after 50 a lot is left, but by 200 iterations essentially all information is lost."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And, if you are viewing this online here is an animation of that output.\n",
    "<img src=\"animations/02_no_info.gif\">\n",
    "\n",
    "I will not generate these standalone animations through the rest of the book. Please see the preface for instructions to run this book on the web, for free, or install IPython on your computer. This will allow you to run all of the cells and see the animations. It's very important that you practice with this code, not just read passively."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Generalizing with Convolution\n",
    "\n",
    "We made the assumption that the movement error is at most one position. But it is possible for the error to be two, three, or more positions. As programmers we always want to generalize our code so that it works for all cases. \n",
    "\n",
    "This is easily solved with [*convolution*](https://en.wikipedia.org/wiki/Convolution). Convolution modifies one function with another function. In our case we are modifying a probability distribution with the error function of the sensor. The implementation of `predict_move()` is a convolution, though we did not call it that. Formally, convolution is defined as\n",
    "\n",
    "$$ (f \\ast g) (t) = \\int_0^t \\!f(\\tau) \\, g(t-\\tau) \\, \\mathrm{d}\\tau$$\n",
    "\n",
    "where $f\\ast g$ is the notation for convolving f by g. It does not mean multiply.\n",
    "\n",
    "Integrals are for continuous functions, but we are using discrete functions. We replace the integral with a summation, and the parenthesis with array brackets.\n",
    "\n",
    "$$ (f \\ast g) [t] = \\sum\\limits_{\\tau=0}^t \\!f[\\tau] \\, g[t-\\tau]$$\n",
    "\n",
    "Comparison shows that `predict_move()` is computing this equation - it computes the sum of a series of multiplications.\n",
    "\n",
    "[Khan Academy](https://www.khanacademy.org/math/differential-equations/laplace-transform/convolution-integral/v/introduction-to-the-convolution) [4] has a good introduction to convolution, and Wikipedia has some excellent animations of convolutions [5]. But the general idea is already clear. You slide an array called the *kernel* across another array, multiplying the neighbors of the current cell with the values of the second array. In our example above we used 0.8 for the probability of moving to the correct location, 0.1 for undershooting, and 0.1 for overshooting. We make a kernel of this with the array `[0.1, 0.8, 0.1]`. All we need to do is write a loop that goes over each element of our array, multiplying by the kernel, and summing the results. To emphasize that the belief is a probability distribution I have named it `pdf`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def predict_move_convolution(pdf, offset, kernel):\n",
    "    N = len(pdf)\n",
    "    kN = len(kernel)\n",
    "    width = int((kN - 1) / 2)\n",
    "\n",
    "    prior = np.zeros(N)\n",
    "    for i in range(N):\n",
    "        for k in range (kN):\n",
    "            index = (i + (width-k) - offset) % N\n",
    "            prior[i] += pdf[index] * kernel[k]\n",
    "    return prior"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This illustrates the algorithm, but it runs very slow. SciPy provides a convolution routine `convolve()` in the `ndimage.filters` module. We  need to shift the pdf by `offset` before convolution; `np.roll()` does that. The move and predict algorithm can be implemented with one line:\n",
    "\n",
    "```python\n",
    "convolve(np.roll(pdf, offset), kernel, mode='wrap')\n",
    "```\n",
    "\n",
    "FilterPy implements this with `discrete_bayes`' `predict()` function."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAACYCAYAAAC4YqBSAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4wLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvpW3flQAAD6NJREFUeJzt3XuQ3XV5x/H3hwRULl0reCURsI0O\nGcYpTIpYWqSKBbzEaWs70Eqlo2hnxGqpWrCOUqq1tRf7h9SxipdBEBG1pjYVtUq1tlKiYCVETEQ0\nESlUYb20isjTP84veFg2uyc5393s2d/7NbOTc/me53zPnj1PPr/rSVUhSZLUwj57ewKSJGn5MFhI\nkqRmDBaSJKkZg4UkSWrGYCFJkpoxWEiSpGYMFj2U5OYkJ43zuCSvTPK2ER/38CSfSvLdJH+9u88r\naXlI8ktJbtzb89DCWrm3J6DJVFV/thvDXwD8D/BT5YlTpN6qqk8Dj9vb89DCco2FFsNhwA2GCqm/\nkoy1IJtkRau5aGEZLPrr55PckOSOJO9I8kCAJM9Icl2SO5P8e5LHz/bgJOcneffQ9eO68Xcm+UKS\nE7vb3wk8F3hFku/tySYYSUtXt4n0vJn9JMmJSXYk+aMktwLv2Hnb0GOPTHJV1zc2J1k/dN87k7w5\nycYk3wd+eW+8Pu0+g0V//TZwMvAzwGOBVyU5Bng78ELgYOAtwIYkD5irUJJDgX8CXgs8BHgZ8P4k\nD62qM4FLgDdU1YFV9fEFej2S9p779ZPu9kcw6AmHMdgkeq8k+wL/CHwUeBjwYuCSJMObSn4LeB1w\nEPBvCzh/NWSw6K83VdX2qvo2gw/u6cBZwFuq6uqq+nFVvQv4IXDcPLWeA2ysqo1VdU9VfQzYBDxt\nIV+ApCVjtn4CcA/wmqr6YVX934zHHAccCPx5Vd1VVZ8APjz0WIAPVdVnur7yg4V+EWrDYNFf24cu\nfw14FIOlij/sVkvemeROYHV331wOA35jxuN+EXjkQkxc0pIzWz8BuH2OQPAoYHtV3TPjsYfuoq4m\nhEeF9NfqocuPBm5h8CF+XVW9bjdrbQcurqqzWk1O0kSZrZ8AzLXD9i3A6iT7DIWLRwNfHhrjDt8T\nyDUW/fWiJKuSPAR4JfBe4K3A7yV5QgYOSPL0JAfNU+vdwDOTnJxkxdCOW6sW+kVIWhJm6yfzuRr4\nPoMdu/ftdvh+JnDZwk1Ti8Fg0V+XMthp6qbu57VVtYnBfhZvAu4AtgFnzleoqrYDz2LQUG5nsAbj\n5fj3JfXF/frJfA+oqruA9cCpDM5z83fA71TVlxZwnloE8dQCkqQ9leRm4Pke8aWdXKKUJEnNjBQs\nkpyS5MYk25Kcu4sxv9mdIGVzkkvbTlPSJLOHSP0x76aQ7jSqXwaeCuwArgFOr6obhsasAS4HnlxV\ndyR5WFXdtnDTljQp7CFSv4yyxuJYYFtV3dTtbHMZgx31hp0FXFhVdwDYECQNsYdIPTLKeSwO5b4n\nKdkBPGHGmMcCJPkMsAI4v6o+MjxgenravUSlJWZqaiqL8DT2EGkZm9lHRgkWszWemR/wlcAa4ERg\nFfDpJEdV1Z17MklJy4o9ROqRUTaF7OC+Z1VbxU/OqjY85kNV9aOq+ipwI4MmIUn2EKlHRlljcQ2w\nJskRwDeA0xh849ywf2DwxTHvTHIIg9WaN+2q4NTU1J7NFti0aRMA69at2+Mai1XXmtZsqVXd6enp\nFtPZHUuqh8DkvO/WXPo1F6ruUq85Vx+Zd41FVd0NnA1cCWwBLq+qzUkuSLK+G3Yl8K0kNwCfBF5e\nVd8ae+aSJp49ROqXkb6ErKo2Ahtn3PbqocsFnNP9SNJ92EOk/vDMm5IkqRmDhSRJasZgIUmSmjFY\nSJKkZkbaeVOT7eiLt84zojt0b8uux117hqcUkCTNzzUWkiSpGYOFJElqxmAhSZKaMVhIkqRmDBaS\nJKkZg4UkSWrGYCFJkpoxWEiSpGYMFpIkqRmDhSRJasZgIUmSmjFYSJKkZgwWkiSpGYOFJElqxmAh\nSZKaMVhIkqRmDBaSJKmZlXt7ApKkfjr64q1z3Ds1+GfLXGPg2jPWtJuQmnCNhSRJasZgIUmSmjFY\nSJKkZgwWkiSpmZGCRZJTktyYZFuSc+cY9+wklWRduylKmnT2EKk/5g0WSVYAFwKnAmuB05OsnWXc\nQcDvA1e3nqSkyWUPkfpllDUWxwLbquqmqroLuAx41izj/hR4A/CDhvOTNPnsIVKPpKrmHpA8Gzil\nqp7fXT8DeEJVnT005mjgVVX160muAl5WVZuG60xPT9/7RFu3zn1csto6a8vU2DXeeuR0g5loKViz\n5ifH/U9NTWWhn88eol2xN02uufrIKCfImq3x3PsBT7IP8EbgzD2bnqRlzh4i9cgowWIHsHro+irg\nlqHrBwFHAVclAXgEsCHJ+plLHDutW7fn+2Vt2rRp7BqLVXfJ1JznzHWj2N3XsGRe+zKp2bLu9PSi\nL+EtqR4Ck/O+L/uai9yblvpnc5JqztVHRtnH4hpgTZIjkuwHnAZs2HlnVU1X1SFVdXhVHQ58Fthl\nQ5DUO/YQqUfmDRZVdTdwNnAlsAW4vKo2J7kgyfqFnqCkyWYPkfplpC8hq6qNwMYZt716F2NPHH9a\nkpYTe4jUH555U5IkNWOwkCRJzRgsJElSMwYLSZLUjMFCkiQ1Y7CQJEnNGCwkSVIzBgtJktSMwUKS\nJDVjsJAkSc0YLCRJUjMGC0mS1IzBQpIkNWOwkCRJzRgsJElSMwYLSZLUjMFCkiQ1Y7CQJEnNGCwk\nSVIzBgtJktSMwUKSJDVjsJAkSc0YLCRJUjMGC0mS1IzBQpIkNWOwkCRJzRgsJElSMwYLSZLUzEjB\nIskpSW5Msi3JubPcf06SG5L8V5J/SXJY+6lKmlT2EKk/5g0WSVYAFwKnAmuB05OsnTHsWmBdVT0e\nuAJ4Q+uJSppM9hCpX1JVcw9IngicX1Und9fPA6iq1+9i/NHAm6rq+OHbp6en732irVu3jjlt7Y6z\ntkyNXeOtR043mImWgjVr1tx7eWpqKgv9fPYQ7Yq9aXLN1UdG2RRyKLB96PqO7rZdeR7wz7sxP0nL\nmz1E6pGVI4yZbYlm1tUcSZ4DrAOeNFfBdevWjfC0s9u0adPYNRar7pKpuWX8pbvdfQ1L5rUvk5ot\n605PL/oS3pLqITA57/uyr7nIvWmpfzYnqeZcfWSUYLEDWD10fRVwy8xBSU4C/hh4UlX9cDfnKGn5\nsodIPTLKppBrgDVJjkiyH3AasGF4QLdN9C3A+qq6rf00JU0we4jUI/MGi6q6GzgbuBLYAlxeVZuT\nXJBkfTfsL4EDgfcluS7Jhl2Uk9Qz9hCpX0bZFEJVbQQ2zrjt1UOXT2o8L0nLiD1E6g/PvClJkpox\nWEiSpGYMFpIkqRmDhSRJasZgIUmSmjFYSJKkZgwWkiSpGYOFJElqxmAhSZKaMVhIkqRmDBaSJKkZ\ng4UkSWpmpC8hkyT129EXb53j3qnBP1vmGgPXnrGm3YR2YVLmuZy5xkKSJDVjsJAkSc0YLCRJUjMG\nC0mS1IzBQpIkNWOwkCRJzRgsJElSMwYLSZLUzJI8QdZCnOBk7pqj1Z3Umgtl3Pdptnku/ns/KTVH\nq+uJfQYm6bM5KZ+jPpuUv6el0kNcYyFJkpoxWEiSpGYMFpIkqRmDhSRJasZgIUmSmhkpWCQ5JcmN\nSbYlOXeW+x+Q5L3d/VcnObz1RCVNLnuI1B/zBoskK4ALgVOBtcDpSdbOGPY84I6q+lngjcBftJ6o\npMlkD5H6JVU194DkicD5VXVyd/08gKp6/dCYK7sx/5FkJXAr8NAaKj49PT33E0ladFNTU1no57CH\nSMvbzD4yyqaQQ4HtQ9d3dLfNOqaq7gamgYP3fJqSlhF7iNQjowSL2ZZoZi45jDJGUj/ZQ6QeGeWU\n3juA1UPXVwG37GLMjm415hTw7eEBi7HKVdKSZA+RemSUNRbXAGuSHJFkP+A0YMOMMRuA53aXnw18\noubbeUNSX9hDpB6ZN1h02zvPBq4EtgCXV9XmJBckWd8Nuwg4OMk24BzgfoeTtTLfYWt7UO/tSW5L\ncn2L+XU1Vyf5ZJItSTYneUmDmg9M8p9JvtDV/JMWc+1qr0hybZIPN6x5c5IvJrkuyaZGNR+c5Iok\nX+p+t08cs97juvnt/PlOkpc2mOcfdO/R9Unek+SBDWq+pKu3ucUcF9Ny7yFdTftI4z5iD5ngHlJV\nE/MDrAC+AjwG2A/4ArB2zJonAMcA1zec5yOBY7rLBwFfbjDPAAd2l/cFrgaOazTfc4BLgQ83/B3c\nDBzS+P1/F/D87vJ+wIMb/23dChw2Zp1Dga8CD+quXw6cOWbNo4Drgf0ZbL78OLCm5e+2Lz8L0UO6\nuvaRxn3EHjK5PWTSzrx5LLCtqm6qqruAy4BnjVOwqj7FjG2546qqb1bV57vL32WwlDZzL/jdrVlV\n9b3u6r7dz9iripOsAp4OvG3cWgspyU8xaN4XAVTVXVV1Z8OneArwlar6WoNaK4EHdfsK7M/99yfY\nXUcCn62q/63B0v+/Ar86Zs2+at5DwD4yCX3EHrJ4PWTSgsUoh60tKd0ZBI9msGQwbq0VSa4DbgM+\nVlVj1wT+FngFcE+DWsMK+GiSzyV5QYN6jwFuB97RrW59W5IDGtTd6TTgPeMWqapvAH8FfB34JjBd\nVR8ds+z1wAlJDk6yP/A07rszpEY3cT0EettH7CET2kMmLVhM1CFpSQ4E3g+8tKq+M269qvpxVf0c\ng73qj01y1JjzewZwW1V9bty5zeL4qjqGwdkWX5TkhDHrrWSwqvnNVXU08H0abYfvdihcD7yvQa2f\nZrAEfATwKOCAJM8Zp2ZVbWFwJsqPAR9hsPr+7jGn2lcT1UOg133EHjKhPWTSgsUoh60tCUn2ZdAM\nLqmqD7Ss3a2+uwo4ZcxSxwPrk9zMYJXwk5O8e8yaAFTVLd2/twEfZLAKehw7gB1DS1dXMGgSLZwK\nfL6q/rtBrZOAr1bV7VX1I+ADwC+MW7SqLqqqY6rqBAar3LeOW7OnJqaHQL/7iD1kcnvIpAWLUQ5b\n2+uShMF2vC1V9TeNaj40yYO7yw9i8Mf3pXFqVtV5VbWqqg5n8Lv8RFWNlYy7+R2Q5KCdl4FfYbAq\nbpy53gpsT/K47qanADeMNdGfOJ0GqzA7XweOS7J/93fwFAbbxseS5GHdv48Gfo128+2biegh0O8+\nYg+Z7B4yygmyloyqujvJzsPWVgBvr6rN49RM8h7gROCQJDuA11TVRWNO9XjgDOCL3bZMgFdW1cYx\naj4SeFcGX+i0D4ND9podHtrYw4EPDj4TrAQuraqPNKj7YuCS7j+Em4DfHbdgt73xqcALx60FUFVX\nJ7kC+DyDVY3XAn/foPT7kxwM/Ah4UVXd0aBm7yxEDwH7yAKwh0xwD5n3S8gkSZJGNWmbQiRJ0hJm\nsJAkSc0YLCRJUjMGC0mS1IzBQpIkNWOwkCRJzRgsJElSMwYLSZLUzP8D6CmopLsJyiUAAAAASUVO\nRK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1319278>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from filterpy.discrete_bayes import predict\n",
    "\n",
    "belief = [.05, .05, .05, .05, .55, .05, .05, .05, .05, .05]\n",
    "prior = predict(belief, offset=1, kernel=[.1, .8, .1])\n",
    "book_plots.plot_belief_vs_prior(belief, prior, ylim=(0,0.6))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "All of the elements are unchanged except the middle ones. The values in position 4 and 6 should be \n",
    "$$(0.1 \\times 0.05)+ (0.8 \\times 0.05) + (0.1 \\times 0.55) = 0.1$$\n",
    "\n",
    "Position 5 should be $$(0.1 \\times 0.05) + (0.8 \\times 0.55)+ (0.1 \\times 0.05) = 0.45$$\n",
    "\n",
    "Let's ensure that it shifts the positions correctly for movements greater than one and for asymmetric kernels."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\ngg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec2e73a20>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "prior = predict(belief, offset=3, kernel=[.05, .05, .6, .2, .1])\n",
    "book_plots.plot_belief_vs_prior(belief, prior, ylim=(0,0.6))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The position was correctly shifted by 3 positions and we give more weight to the likelihood of an overshoot vs an undershoot, so this looks correct.\n",
    "\n",
    "Make sure you understand what we are doing. We are making a prediction of where the dog is moving, and convolving the probabilities to get the prior.\n",
    "\n",
    "If we weren't using probabilities we would use this equation that I gave earlier:\n",
    "\n",
    "$$ \\bar x_{k+1} = x_k + f_{\\mathbf x}(\\bullet)$$\n",
    "\n",
    "The prior, our prediction of where the dog will be, is the amount the dog moved plus his current position. The dog was at 10, he moved 5 meters, so he is now at 15 m. It couldn't be simpler. But we are using probabilities to model this, so our equation is:\n",
    "\n",
    "$$ \\bar{ \\mathbf x}_{k+1} = \\mathbf x_k \\ast f_{\\mathbf x}(\\bullet)$$\n",
    "\n",
    "We are *convolving* the current probabilistic position estimate with a probabilistic estimate of how much we think the dog moved. It's the same concept, but the math is slightly different. $\\mathbf x$ is bold to denote that it is an array of numbers. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Integrating Measurements and Movement Updates\n",
    "\n",
    "The problem of losing information during a prediction may make it seem as if our system would quickly devolve into having no knowledge. However, each prediction is followed by an update where we incorporate the measurement into the estimate. The update improves our knowledge. The output of the update step is fed into the next prediction. The prediction degrades our certainty. That is passed into another update, where certainty is again increased.\n",
    "\n",
    "Let's think about this intuitively. Consider a simple case - you are tracking a dog while he sits still. During each prediction you predict he doesn't move. Your filter quickly *converges* on an accurate estimate of his position. Then the microwave in the kitchen turns on, and he goes streaking off. You don't know this, so at the next prediction you predict he is in the same spot. But the measurements tell a different story. As you incorporate the measurements your belief will be smeared along the hallway, leading towards the kitchen. On every epoch (cycle) your belief that he is sitting still will get smaller, and your belief that he is inbound towards the kitchen at a startling rate of speed increases.\n",
    "\n",
    "That is what intuition tells us. What does the math tell us?\n",
    "\n",
    "We have already programmed the update and predict steps. All we need to do is feed the result of one into the other, and we will have implemented a dog tracker!!! Let's see how it performs. We will input measurements as if the dog started at position 0 and moved right one position each epoch. As in a real world application, we will start with no knowledge of his position by assigning equal probability to all positions. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\nIQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1b24128>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from filterpy.discrete_bayes import update\n",
    "\n",
    "hallway = np.array([1, 1, 0, 0, 0, 0, 0, 0, 1, 0])\n",
    "prior = np.array([.1] * 10)\n",
    "likelihood = lh_hallway(hallway, z=1, z_prob=.75)\n",
    "posterior = update(likelihood, prior)\n",
    "book_plots.plot_prior_vs_posterior(prior, posterior, ylim=(0,.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "After the first update we have assigned a high probability to each door position, and a low probability to each wall position. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\ngg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec0f330f0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "kernel = (.1, .8, .1)\n",
    "prior = predict(posterior, 1, kernel)\n",
    "book_plots.plot_prior_vs_posterior(prior, posterior, True, ylim=(0,.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The predict step shifted these probabilities to the right, smearing them about a bit. Now let's look at what happens at the next sense."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec2d82da0>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "likelihood = lh_hallway(hallway, z=1, z_prob=.75)\n",
    "posterior = update(likelihood, prior)\n",
    "book_plots.plot_prior_vs_posterior(prior, posterior, ylim=(0,.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Notice the tall bar at position 1. This corresponds with the (correct) case of starting at position 0, sensing a door, shifting 1 to the right, and sensing another door. No other positions make this set of observations as likely. Now we will add an update and then sense the wall."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\nlMeMp+w8SQAAAABJRU5ErkJggg==\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec2f13278>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "prior = predict(posterior, 1, kernel)\n",
    "likelihood = lh_hallway(hallway, z=0, z_prob=.75)\n",
    "posterior = update(likelihood, prior)\n",
    "book_plots.plot_prior_vs_posterior(prior, posterior, ylim=(0,.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This is exciting! We have a very prominent bar at position 2 with a value of around 35%. It is over twice the value of any other bar in the plot, and is about 4% larger than our last plot, where the tallest bar was around 31%. Let's see one more cycle."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec304c6d8>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "prior = predict(posterior, 1, kernel)\n",
    "likelihood = lh_hallway(hallway, z=0, z_prob=.75)\n",
    "posterior = update(likelihood, prior)\n",
    "book_plots.plot_prior_vs_posterior(prior, posterior, ylim=(0,.5))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "I ignored an important issue. Earlier I assumed that we had a motion sensor for the predict step; then, when talking about the dog and the microwave I assumed that you had no knowledge that he suddenly began running. I mentioned that your belief that the dog is running would increase over time, but I did not provide any code for this. In short, how do we detect and/or estimate changes in the process model if aren't directly measuring it?\n",
    "\n",
    "For now I want to ignore this problem. In later chapters we will learn the mathematics behind this estimation; for now it is a large enough task just to learn this algorithm. It is profoundly important to solve this problem, but we haven't yet built enough of the mathematical apparatus that is required, and so for the remainder of the chapter we will ignore the problem by assuming we have a sensor that senses movement."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Discrete Bayes Algorithm\n",
    "\n",
    "This chart illustrates the algorithm:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec2d81e10>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "book_plots.predict_update_chart()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "This filter is a form of the g-h filter. Here we are using the percentages for the errors to implicitly compute the $g$ and $h$ parameters. We could express the discrete Bayes algorithm as a g-h filter, but that would obscure the logic of this filter.\n",
    "\n",
    "The filter equations are:\n",
    "\n",
    "$$\\begin{aligned} \\bar {\\mathbf x} &= \\mathbf x \\ast f_{\\mathbf x}(\\bullet)\\, \\, &\\text{Predict Step} \\\\\n",
    "\\mathbf x &= \\|\\mathcal L \\cdot \\bar{\\mathbf x}\\|\\, \\, &\\text{Update Step}\\end{aligned}$$\n",
    "\n",
    "$\\mathcal L$ is the usual way to write the likelihood function, so I use that. The $\\|\\|$ notation denotes taking the norm. We need to normalize the product of the likelihood with the prior to ensure $x$ is a probability distribution that sums to one.\n",
    "\n",
    "We can express this in pseudocode.\n",
    "\n",
    "**Initialization**\n",
    "\n",
    "    1. Initialize our belief in the state\n",
    "    \n",
    "**Predict**\n",
    "\n",
    "    1. Based on the system behavior, predict state at the next time step\n",
    "    2. Adjust belief to account for the uncertainty in prediction\n",
    "    \n",
    "**Update**\n",
    "\n",
    "    1. Get a measurement and associated belief about its accuracy\n",
    "    2. Compute residual between estimated state and measurement\n",
    "    3. Determine whether the measurement matches each state\n",
    "    4. Update state belief if it matches the measurement\n",
    "\n",
    "When we cover the Kalman filter we will use this exact same algorithm; only the details of the computation will differ. \n",
    "\n",
    "Algorithms in this form are sometimes called *predictor correctors*. We make a prediction, then correct them.\n",
    "\n",
    "Let's animate this. I've plotted the position of the doorways in black. Prior are drawn in orange, and the posterior in blue. You can see how the prior shifts the position and reduces certainty, and the posterior stays in the same position and increases certainty as it incorporates the information from the measurement. I've made the measurement perfect with the line `z_prob = 1.0`; we will explore the effect of imperfect measurements in the next section. finally, I draw a thick vertical line to indicate where Simon really is. This is not an output of the filter - we know where Simon really is only because we are simulating his movement."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        event.shiftKey = false;\n",
       "        // Send a \"J\" for go to next cell\n",
       "        event.which = 74;\n",
       "        event.keyCode = 74;\n",
       "        manager.command_mode();\n",
       "        manager.handle_keydown(event);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtAAAAC0CAYAAAC9rRv5AAAgAElEQVR4Xu2dDbBuVVnH/wjah9o5TGia5jBNlwZBk+kekCijkLiDjn1dJMhbajCTBlMxptBUEhNxddLhKpUIEmqTX1TGoJlJY+lMOgdknLowckbDhBRKObfMMj9u83DXuWe/h/e8a+291yf7t2cchHfttZ/1W89a+/eud+19jhAHBCAAAQhAAAIQgAAEIBBM4IjgkhSEAAQgAAEIQAACEIAABIRAkwQQgAAEIAABCEAAAhDoQQCB7gGLohCAAAQgAAEIQAACEECgyQEIQAACEIAABCAAAQj0IIBA94BFUQhAAAIQgAAEIAABCCDQ5AAEIAABCEAAAhCAAAR6EECge8CiKAQgAAEIQAACEIAABBBocgACEIAABCAAAQhAAAI9CCDQPWBRFAIQgAAEIAABCEAAAgg0OQABCEAAAhCAAAQgAIEeBBDoHrAoCgEIQAACEIAABCAAAQSaHIAABCAAAQhAAAIQgEAPAiUE+gZJz5f0gKQT58RqMe2TdLakr0h6saRP9GgTRSEAAQhAAAIQgAAEIJCMQAmBfo6kL0t62zYCbeJ8sRPoU5xM2z85IAABCEAAAhCAAAQgUJxACYG2Rh8r6ZZtBPpaSR+W9A5H51OSTpf0+eK0CAACEIAABCAAAQhAYPIEahRoE+u9kj7qeudWSa+SdNtGb62vrx+cfM8BAAIQgAAEIPAII7C8vFzKSx5hJGlOagKlEnXRCvT7JF21RaBfKel2BDp1OlA/BCAAAQhAoBwBBLoce67cj0CNAu3dwsEKdL9OpjQEIAABCECgBQIIdAu9RIxGoEaBfp6kizoPEb5B0snd7uoK9NLSUvSePOKIUli2b8rBg/5dKzXGbS0KiT1GJ95226FdPjt37oxRXdY6Woz92Evtx6LZ4569NnzbOFpkbmRbjbtU7DXOi7nmxFLM+84ABw4cOHwKAt2XHuVLEShhivZwoD0UeIyk+yW9WtKjHYA3Oam/RtIu9xq7l3T3P1s5BHp+utR4o0Cgw4Z2i1KEQIf1bexSLebKBoMSsdc4LyLQs6MCgY49S1BfDgIlBHp0uxBoBHoegRI359HJ7CpoMXYEOlbv96unxVxBoGf7GIFGoPuNekrXSACBntMrra5Y1Bg3K9Bhw75FKUKgw/o2dqkWcwWBRqAXjQNWoGPPEtSXgwACjUAnz7Ncqy2IRfKunLkAAp2Xd0kJjdXSEmO0xoWFXHOi9VsJ5n3zBYHuS4zyNRBAoBHo5HmY62bRwo1iO9gtxo5AJx86cy/QYq6UlH8Euv6HqxHoMnMJVx1HAIFGoMdlUMDZCLQfUotShED7+zVFiRZzBYGezYRccyIr0ClGIHVC4BABBBqBTj4Wct0sEIvkXTlzAQQ6L++SEhqrpSXGKCvQrEDHyl/qgUCXAAKNQCcfEQi0H3EJsfBHtbgEAj2W4LDzW8yVkvKPQCPQw0YaZ0FgMQEEGoFOPkYQaD/iFqUIgfb3a4oSLeYKAj2bCbnmRLtqC/nCHugUMwV1piaAQCPQqXOMv0QYQLiFm9zWZiDQAR2boEiLuYJAI9CLhgICnWCioMrkBBBoBDp5kuVabUEsknflzAUQ6Ly8S0porJaWGKNs4WALR6z8pR4IdAkg0Ah08hGBQPsRlxALf1SLSyDQYwkOO7/FXCkp/wg0Aj1spHEWBBYTQKAR6ORjBIH2I25RihBof7+mKNFiriDQs5mQa060q7aQL2zhSDFTUGdqAgg0Ap06x9gDHUC4hZvc1mYg0AEdm6BIi7mCQCPQi4YCAp1goqDK5AQQaAQ6eZLlWm1BLJJ35cwFEOi8vEtKaKyWlhijbOFgC0es/KUeCHQJlBDoXZL2STpS0vWS9m7pkqdJequkZVfmUknv75ZZX18/uPHvS0tL0Xu01Qm3xritcxBof4qWEAt/VItLINBjCQ47v8VcKSn/Nc6LueZE495CvrACPWwu4KyyBHILtEnz3ZLOlHSvpFVJ50m6s4PhzZLukPTHkp7u5PlYBPrwd4ZtM6bGGwUCHTbAW7jJbW0JAh3Wt7FLtZgrCPRsFiDQszwQ6NizBPXlIJBboE+VdLmks1zjLnP/vKrT2GslfUbSayRZ+ddJ+iEEGoH2DQjEwkco7ucIdFyeobWR56GkDpWrcWEBgUag+2UxpWskkFugd0uyLRwXOBh7JJ0i6aIOnCdL+qCkoyU9VtJzJd2+nUCvra1F57qyshK9zrEVrq7aYv3io8a4LeKQ2H1t4/P6COy+6f6HBXXT7u+qL1AimjSBGudF5sTZlNyxY8fh/7C8vJzbSyY9Pmj8cAK5E/Uct/rcFeiTJV3cacIltmjgVp5tBfotkk6U9M2NMt090Aj0JrkabxQI9PDBWfuZCHTtPUR8RqDGeRGBRqAZne0TyC3QIVs49rtV6s85vLad49mSHpgn0DxEuJmENf5UadHl+rmSn7bzTkhs4cjLe+Nq5Hk/7jXOi7nmRCPVQr6wB7pfTlO6DgK5Bfoo9xDhGZLucw8Rni/JpHnj+GtJ75J0o6TjJd0q6SnmYQj04qSp8UaBQIcN9BZucltbgkCH9W3sUi3mSkn5r3FeRKBnRwUCHXuWoL4cBHILtLXpbElXu1fU3SDpSklX2BdlSTe7N29cJ+lxTppf6fZEH+bBa+zmp0aNNwoEOmwYtyhFCHRY38Yu1WKuINCzWYBAI9Cx5wXqy0+ghECPbiUCjUDPI4BYjB5avSpAoHvhilaYPO+HssaFBQQage6XxZSukQACPadXWp1wa4ybFeiwYd+iFCHQYX0bu1SLucIKNCvQi8YBWzhizxLUl4MAAo1AJ8+zXKstiEXyrpy5AAKdl3dJCY3V0hJjtMaFhVxzovVbCeZ98wWB7kuM8jUQQKAR6OR5mOtm0cKNYjvYLcaOQCcfOnMv0GKulJR/BNoeL5J27txZJmEDropAB0CiSHUEEGgEOnlSItB+xC1KEQLt79cUJVrMFQR6NhNyzYmsQKcYgdQJgUMEEGgEOvlYyHWzQCySd+XMBRDovLxLSmislpYYo6xAswIdK3+pBwJdAgg0Ap18RCDQfsQlxMIf1eISCPRYgsPObzFXSso/Ao1ADxtpnAWBxQQQaAQ6+RhBoP2IW5QiBNrfrylKtJgrCPRsJuSaE+2qLeQLe6BTzBTUmZoAAo1Ap84x/pR3AOEWbnJbm4FAB3RsgiIt5goCjUAvGgoIdIKJgiqTE0CgEejkSZZrtQWxSN6VMxdAoPPyLimhsVpaYoyyhYMtHLHyl3og0CWAQCPQyUcEAu1HXEIs/FEtLoFAjyU47PwWc6Wk/CPQCPSwkcZZEFhMAIFGoJOPEQTaj7hFKUKg/f2aokSLuYJAz2ZCrjnRrtpCvrCFI8VMQZ2pCSDQCHTqHGMPdADhFm5yW5uBQAd0bIIiLeYKAo1ALxoKCHSCiYIqkxNAoBHo5EmWa7UFsUjelTMXQKDz8i4pobFaWmKMsoWDLRyx8pd6INAlUEKgd0naJ+lISddL2junS14o6XJJByV9UtL53TLr6+v23x86lpaWovdoqxNujXFb5yDQ/hQtIRb+qBaXQKDHEhx2fou5UlL+a5wXc82Jxr2FfGEFethcwFllCeQWaJPmuyWdKeleSauSzpN0ZwfDDknvlvTjkh6U9ERJDyDQh78zbJsxNd4oEOiwAd7CTW5rSxDosL6NXarFXEGgZ7MAgZ7lgUDHniWoLweB3AJ9qltZPss17jL3z6s6jX2tk2xbnZ57sAI9nwsCXf9PldvldItShEDnmKIffo0WcwWBRqAXjRYEusxcwlXHEcgt0Lsl2RaOC1zYeySdIumiTjPe6wT6NLfNw7ZyfGC7Fei1tbVxBOacvbKyEr3OsRWurtpi/eKjxrgt4pDYfW3j8/gELrxr3PanB/fbj0mzx9EnHDcq0OuOPzDqfE6GwFYCNc6LzImzvbRjh/3wfOhYXl7O7SUMGggMIpA7Uc+RZKvPXYE+WdLFnehvkfQ1SbYP+qmSPiLpREnrG2W6K9AI9Ca5Gm8UCPSgcZnlJAQ6C2YuUphAjfMiAo1AFx4WXD4CgdwCHbKF402SPibpRte+WyVd6vZLP/Sf2MIxv+fZwsEWjj5zwklvH/frTYoV6Dv2bK5E9WnLkLKtboVoNW7roxKx1zgvsgd6dsSyhWPIDMY5pQnkFuij3PaMMyTd56TY3rCxvwPCtnjYg4W/KOkYSXdIepakL85bgeYtHJvkarxRWHS5bhYlbs6xBnCJ2BHoNr9wlciVlvO8xnkx15xY6ktL33xBoPsSo3wNBHILtLX5bElXu/3NN0i6UtIVtjgh6WZJFtPr3F7pb7jP39mFxQo0K9DzCCAW/aYUBBqB7pcx40uXGKMIdP15jkCPH1vUkJ9ACYEe3UoEGoFGoEcPIyHQ9YsFeT4+zxHo+vMcgR6f59SQnwACPYd5qxNujXGzhSNsUJdYmUOg6xcLBDps/CwqVeO8yBaO2R5DoMfnOTXkJ4BAI9DJsy7XzaKEhMaCVyJ2BBqBjpW/ofWUyHMEuv48R6BDRxDlaiKAQCPQyfMRgfYjLiEWCHT9YsEKtH/s+Eog0PXnOQLty2I+r5EAAo1AJ89LBNqPGIE+xIjX2NWZK/6owkqUyHMEGoEOy05KQaAfAQQage6XMQNKI9B+aCXEghXo+sWCFWj/2PGVQKDrz3NWoH1ZzOc1EkCgEejkeYlA+xEj0KxA+7PkUIkSuRIam69cidgRaATal5d8DoEhBBBoBHpI3vQ6B4H24yohFqxA1y8WrED7x46vBAJdf56zAu3LYj6vkQACjUAnz0sE2o8YgWYF2p8lrECHMuqWQ6AR6CF5wzkQ8BFAoBFoX46M/hyB9iNEoBFof5Yg0KGMEOhNAiXmlr79xAp0X2KUr4EAAo1AJ89DBNqPuMRNji0c9a/MzcucErniz+CwEiViZwW6/jxHoMPGD6XqIoBAI9DJMxKB9iMuIRYIdP1igUD7x46vBAJdf54j0L4s5vMaCSDQCHTyvESg/YgRaLZw+LOELRyhjNjCwRaOIbnCORDoQwCBRqD75Mugsgi0HxsCjUD7swSBDmWEQCPQQ3KFcyDQh0AJgd4laZ+kIyVdL2nvNgHvlvQeSSv26tNumfX19YMb/760tNSnvUFlW/3Jr8a4DTgC7U87BBqB9mcJAh3KCIFGoIfkCudAoA+B3AJt0ny3pDMl3StpVdJ5ku7cEvTjJb1P0mMkXYRAh0koAl3/Xr/tBicCjUCHTtwlciU0Nl+5ErHXOC/mWlSw/ijB3JcHWz9nD3RfYpSvgUBugT5V0uWSznKNv8z986otMK6W9CFJr3D/YwX64OFF923zpsYbBSvQYcO8xE2Ohwjb/MJVIlfCsthfqkTsNc6LCPRsriDQ/rFDifoI5BZo25ZhWzgucCj2SDrFrTJv0DlJ0m9J+llJH/YJ9NraWnSqKyu2a6SuY3XVFusXHzXGbRGHxO5rG5/HJ3DhXeO2Pz24335Mmj2OPuG4UYFed/yBUedzMgS2EqhxXmROnO2lHTt2HP4Py8vLub2EQQOBQQRyJ+o5bvW5K9AnS7rYRf8oSX8n6cWS7kGgN/s0ZMKt8UaBQA8al1lOQqCzYOYihQnUOC+GzOeFsWW9PAKdFTcXi0Qgt0D7tnDYktinJX3Zte9Jkr4k6QXdfdA8RDi/92v8qdIizfVzZYmfhyONwyL7FNnCwRaOWPkbWk+JMVrjvJhrTrR+KcE8NB82yrGFoy8xytdAILdAH+UeIjxD0n3uIcLzJe3fBoZ3Cwdv4dgkV+ONAoEOG+YlbnIINAIdlp3xSpXI8xrnRQR6NqcQ6HhjjJryEcgt0NaysyXZQ4L2Ro4bJF0p6Qq3wnzzlqYj0A5IyIRb440CgQ4bzCXEAoFGoMOyM16pEnle47wYMp/Hol6Ced/YEei+xChfA4ESAj263WzhmI+wxhsFAh2W7iVucgg0Ah2WnfFKlcjzGudFBJoV6HijippKEUCg55BvdcKtMW4EOmxolxALBBqBDsvOeKVK5HmN8yICjUDHG1XUVIoAAo1AJ8+9XDeLEjfnWPBKxI5AI9Cx8je0nhJ5jkDXn+ds4QgdQZSriQACjUAnz0cE2o+4hFgg0PWLxbzMKZEr/gwOK1EidgS6/jxHoMPGD6XqIoBAI9DJMxKB9iMuIRYIdP1igUD7x46vBAJdf54j0L4s5vMaCSDQCHTyvESg/YgR6EOM7tiz+RfJ/NTGlSjBfFzEh85uNe5SsSPQCHSMcUcdENhKAIFGoJOPCgTaj7iEFLECXb9YsALtHzu+Egh0/XnOCrQvi/m8RgIINAKdPC8RaD9iBJoVaH+WsAIdyqhbDoFGoIfkDedAwEcAgUagfTky+nME2o8QgUag/VmCQIcyQqA3CZSYW/r2EyvQfYlRvgYCCDQCnTwPEWg/4hI3ObZw1L8yNy9zSuSKP4PDSpSInRXo+vMcgQ4bP5SqiwACjUAnz0gE2o+4hFgg0PWLBQLtHzu+Egh0/XmOQPuymM9rJIBAI9DJ8xKB9iNGoNnC4c8StnCEMmILB1s4huQK50CgDwEEGoHuky+DyiLQfmwINALtzxIEOpQRAo1AD8kVzoFAHwIlBHqXpH2SjpR0vaS9WwK+RNIFkr4u6d8lvVTSZ7tl1tfXD278+9LSUp/2BpVt9Se/GuM24Ai0P+0QaATanyUIdCgjBBqBHpIrnAOBPgRyC7RJ892SzpR0r6RVSedJurMT9I9J+rikr0h6maTTJZ2LQB/+zrBt/yLQ9e/1267zEGgEOnTiLpErobH5ypWIvcZ5MdeigvVHCea+PNj6OXug+xKjfA0Ecgv0qZIul3SWa/xl7p9XbQPjJEnXSDoNgUagfQOmhRsFAr24F/lLhL4sb0OIaspzBLr+hQUE2j/uKVEfgdwCvVuSbeGwLRp27JF0iqSLtkFj8vwFSb+3nUCvra1Fp7qyshK9zrEVrq7aYv3io8a4LeKQ2H1t4/P4BC68a9z2pwf3249Js8fRJxw3KtDrjj/gPX9s3N4LDCgQEveAajklAoEa50XmxNmO3bFjx+H/sLy8nNtLImQZVUyRQO5EPcetPncF+mRJF8+B/yIn1j8q6asINAI9xQGass1jRRSB3uwdBDplpo6rG4Eexy/H2Qh0DspcIzaB3AIduoXjuZLeKMnk+YGtjeYhwvlpUONPlRZprv1+bOHoNz20+h7osXH3oxRWmq0nYZxKjNEa58Vcc6L1SgnmYdmwWYotHH2JUb4GArkF+ij3EOEZku5zDxGeL2l/B4bte77JbfWYuz8DgUag5xFo4Uax3aAvEftYEU2xAh0iomPjTjHxhsQd67olcqXl2BFo9kDHyl/qgUCXQG6BtmufLelq9xq7GyRdKekK+6Is6WZJH5L0DEmfd4H+q6QXdINGoBFoBHr8RDZWRBHozT5AoMPysYT8I9AIdFh2UgoC/QiUEOh+Ec4pjUAj0Aj06GEkBHo8w40aEOgwlgj0IU5s4ZjNF7ZwhI0fStVFAIGe0x+trljUGHfOm0WJm3Os4VwidgQ6Vu9JCHQYyxJ5XuO8iEAj0GEjhlI1E0CgEejk+ZnrZlHi5jxWQlPBDxG6sbGzhWOz90J4x+rrEnnecuwINFs4YuUv9UCgSwCBRqCTjwgEOjnih10gROgQ6Hj9EsI71tUQ6H4kEWgEul/GUBoCYQQQaAQ6LFNGlEKgR8AbeGqI0CHQA+HOOS2Ed6yrIdD9SCLQCHS/jKE0BMIIINAIdFimjCiFQI+AN/DUEKFDoAfCRaAHgysh/wg0Aj04YTkRAgsIINAIdPIBgkAnR/ywCyDQeZmH8I4VUQkJbTl2BBqBjpW/1AOBLgEEGoFOPiIQ6OSIEej8iGeuiECHdUAJ+UegEeiw7KQUBPoRQKAR6H4ZM6A0Aj0A2shTQoSOLRwjIXdOD+Ed62olJLTl2BFoBDpW/lIPBFiB9uRAqxNujXEbagQ6/6QTInQIdLx+CeEd62oIdD+SNc6LueZEI9VCvvCHVPrlNKXrIMAKNCvQyTMx182ixI1irISmgh8idGNj5z3Qm70XwjtWX5fI85ZjR6BZgY6Vv9QDAVagWYHOOgoQ6Ky4H7pYiNAh0PH6JYR3rKsh0P1IItAIdL+MoTQEwgiwAs0KdFimjCiFQI+AN/DUEKFDoAfCnXNaCO9YV0Og+5FEoBHofhlDaQiEEUCgEeiwTBlRCoEeAW/gqSFCh0APhItADwZXQv4RaAR6cMJyIgQWECgh0Lsk7ZN0pKTrJe3dEt+3SHqbpB+U9EVJ50q6p1tmfX394Ma/Ly0tRe/gVifcGuO2zkGgo6eot0IE2osoaoEQ3rEuWEJCW469xnkx15xo/dZCvvAQYawRRj05CeQWaJPmuyWdKeleSauSzpN0Z6fRL5f0TEm/LOnnJP20k+jDRRDo+SlS440Cgc45nDevFSJ0rEDH65sQ3na1sczjRdwvV2Jdt4TM1TgvItCzGYVAxxph1JOTQG6BPlXS5ZLOco28zP3zqk6j/8aV+UdJR0n6gqQn2ELmRhkEGoGeR6DEzblGITI2IUI3NnbewtFfQscyT3FzCMmVWNctMUYRaLZwxMpf6oFAl0Bugd4tybZwXOCC2CPpFEkXdYL6Z1fGVqjt+LQr8x/zBJruhAAEIAABCEDgkUFgeXk5t5c8MsDRiuwEcifqOW71uSvQJ0u6uNPy/a5MV6CtjO2HfujorkBnJ8YFIQABCEAAAhBIQgCBToKVShMQyC3Q0bdwJGBClRCAAAQgAAEIFCCAQBeAziUHEcgt0Lan2R4iPEPSfe4hwvMl2arzxvErkp7ReYjwZyS9cFDrOAkCEIAABCAAAQhAAAKRCeQWaAv/bElXu9fY3SDpSklX2Nt2JN0s6Vslvd0eWJf0Jfcmjs9EbjfVQQACEIAABCAAAQhAYBCBEgI9KNAGT/K977rWJtmXmudLekDSibUGOSeu73HvD3+SpG9KerN733gLTbAvjf8gyd6Bbr/S3CTp1S0E7mK011PaF2D7Vclyp5XD3i//X5K+IenrknY2Eviye4e+jU97O9FLJdlbi2o+vl/SuzoBfq+k33GLKTXHvRHbr7uH3433P0l6iaT/bSDwX5V0oSS711/XEO8G0BLi1Akg0GkyIOR912muPL7W50j6spPRlgT6yZLsf5+Q9HhJt0v6qS3vGB9PJ00NNg4f67g/WtJHJdmN72NpLhe91kucfH5HgwJt0nz4DT/RyaSp8K2SPuIk+jGSvt2erU5zqSS12vxoX7bsDUyfTXKFuJU+xY3Jp0v6H0nvlvR+STfGvUz02mz+fqckewj//yR9QNLLJK1FvxIVQmCCBBDoNJ0e8rBkmivHqfVYSbc0tgK9teV/JekaSX8bB0m2WkyGTKDtRvfxbFcdfqGnSjKhs61YJtKtrUC3JtD2JeWTkmwF9/C78Yd3X5Ezf8L9wnJakav3v6gJtH2Z/QFJ/ynpvZLeIOmD/avKesbWt179tqSvSnpt1ii4GAQeoQQQ6DQdG/K+6zRXjlNr6wJt8duWCFuBsRteC4etytmq+fdJ+kNJr2ohaLfdxP4Qkq36v6Ixgf4XSQ86Eb3WbfupHfuzXJz211tN6Cxn7NeK/6498E58tk3MfimyL7itHMbYviTaCrSJ8883EPjxkmwhwRZ0LO5b3Var7mtjG2gGIUKgTgIIdJp+CXnfdZorx6m1ZYF+nKS/dze7v4iDI2sttr/1L9270e2PCtV82GqzPRT8ckmnNyjQ3y3p3yQ90f1SYWJhX7xqPmzF3FZDbfXWfqHY574k2upiC4dtOTHmJ0i6v4WAJR0t6c8lneu2yrzHfXH80wbi/yVJ9mYr25ZnX7pMpG0/NwcEIDCSAAI9EuA2p7OFIw1XX622f9i2ntifg3+9r3DFn9sDhLai+AcVx2ih2cqz/TVRewDPHoS07QX2peVFlcc9L7zLnWTUztwekjWBti+5dvyIpEslPa8R5j/phM62cbRy2IKIPRRuMmrHL0h6tvvi2EobLM7fl2R/oOyPWgqaWCFQKwEEOk3PhLzvOs2V49Ta4gq05bLtxbVXH/5aHAzZanmCpK+51a1vcz8Rv8Z9GcgWxMgLtbYCbQ9tPsq9hcP+v+2Vt9dp2oNWtR/2AKH9NddPSTLxt/h/o/agXXz2UJt9wf2TRuK1MO1hR9t2suJWcO3hQXvrzBsbaIP9umJvVHqam1dscce2LXFAAAIjCSDQIwEuOH3e+67TXS1eze9wP8cf435itdXQt8SrPllNP+zeTGCvmLLX2Nnxm+5p+WQXjVTxM5382z5okzp7yt9krqWjNYG2h/Bsq4wd9oX3z9y2nxaY2z7o6yXZdgh7R769Uq0FKbIHZD/nHoA80ALoToy/67Zw2K8td7gvMPZAXu2Hfdn6TvcF3R7ytX3QHBCAQAQCCHQEiFQBAQhAAAIQgAAEIDAdAgj0dPqalkIAAhCAAAQgAAEIRCCAQEeASBUQgAAEIAABCEAAAtMhgEBPp69pKQQgAAEIQAACEIBABAIIdASIVAEBCEAAAhCAAAQgMB0CCPR0+pqWQgACEIAABCAAAQhEIIBAR4BIFRCAAAQgAAEIQAAC0yGAQE+nr2kpBCAAAQhAAAIQgEAEAgh0BIhUAQEIQAACEIAABCAwHQII9HT6mpZCAAIQgAAEIAABCEQggEBHgEgVEIAABCAAAQhAAALTIYBAT6evaSkEIAABCEAAAhCAQAQCCHQEiFQBAQhAAAIQgAAEIDAdAgj0dPqalkIAAhCAAAQgAAEIRCCAQEeASBUQgAAEIAABCEAAAtMhgEBPp69pKQQgAAEIQAACEIBABAIIdASIVAEBCEAAAhCAAAQgMB0CCPR0+pqWQgACEIAABCAAAQhEIIBAR4BIFRCAAAQgAAEIQAAC0yGAQE+nr2kpBCAAAQhAAAIQgEAEAgh0BMT6khwAAAGqSURBVIhUAQEIQAACEIAABCAwHQII9HT6mpZCAAIQgAAEIAABCEQggEBHgEgVEIAABCAAAQhAAALTIYBAT6evaSkEIAABCEAAAhCAQAQCCHQEiFQBAQhAAAIQgAAEIDAdAgj0dPqalkIAAhCAAAQgAAEIRCCAQEeASBUQgAAEIAABCEAAAtMhgEBPp69pKQQgAAEIQAACEIBABAIIdASIVAEBCEAAAhCAAAQgMB0CCPR0+pqWQgACEIAABCAAAQhEIIBAR4BIFRCAAAQgAAEIQAAC0yGAQE+nr2kpBCAAAQhAAAIQgEAEAgh0BIhUAQEIQAACEIAABCAwHQII9HT6mpZCAAIQgAAEIAABCEQggEBHgEgVEIAABCAAAQhAAALTIYBAT6evaSkEIAABCEAAAhCAQAQCCHQEiFQBAQhAAAIQgAAEIDAdAgj0dPqalkIAAhCAAAQgAAEIRCCAQEeASBUQgAAEIAABCEAAAtMhgEBPp69pKQQgAAEIQAACEIBABAIIdASIVAEBCEAAAhCAAAQgMB0CCPR0+pqWQgACEIAABCAAAQhEIPD/6JJztFiE4kAAAAAASUVORK5CYII=\" width=\"576\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final posterior: [ 2.34e-19  2.92e-20  0.0089  0.144  0.605  0.215  0.0264  0.0011\n",
      "  6.75e-20  3.09e-12]\n"
     ]
    }
   ],
   "source": [
    "def discrete_bayes_sim(pos, kernel, zs, z_prob_correct, sleep=0.25):\n",
    "    %matplotlib notebook\n",
    "    N = len(hallway)\n",
    "    fig = plt.figure()\n",
    "    for i, z in enumerate(zs):\n",
    "        plt.cla()\n",
    "        prior = predict(pos, 1, kernel)\n",
    "        book_plots.bar_plot(hallway, c='k')\n",
    "        book_plots.bar_plot(prior, ylim=(0,1.0), c='#ff8015')\n",
    "        plt.axvline(i % N, lw=5)\n",
    "        fig.canvas.draw()\n",
    "        time.sleep(sleep)\n",
    "\n",
    "        plt.cla()\n",
    "        likelihood = lh_hallway(hallway, z=z, z_prob=z_prob_correct)\n",
    "        pos = update(likelihood, prior)\n",
    "        book_plots.bar_plot(hallway, c='k')\n",
    "        book_plots.bar_plot(pos, ylim=(0,1.0))\n",
    "        plt.axvline(i % 10, lw=5)\n",
    "        fig.canvas.draw()\n",
    "        time.sleep(sleep)\n",
    "    plt.show()\n",
    "    %matplotlib inline\n",
    "    set_figsize(y=2)\n",
    "    print('Final posterior:', pos)\n",
    "     \n",
    "# change these numbers to alter the simulation\n",
    "kernel = (.1, .8, .1)\n",
    "z_prob = 1.0\n",
    "\n",
    "# list of perfect measurements\n",
    "hallway = np.array([1, 1, 0, 0, 0, 0, 0, 0, 1, 0])\n",
    "measurements = [hallway[i % len(hallway)] for i in range(25)]\n",
    "pos = np.array([.1]*10)\n",
    "\n",
    "discrete_bayes_sim(pos, kernel, measurements, z_prob)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## The Effect of Bad Sensor Data\n",
    "\n",
    "You may be suspicious of the results above because I always passed correct sensor data into the functions. However, we are claiming that this code implements a *filter* - it should filter out bad sensor measurements. Does it do that?\n",
    "\n",
    "To make this easy to program and visualize I will change the layout of the hallway to mostly alternating doors and hallways, and run the algorithm on 15 correct measurements:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 28,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        event.shiftKey = false;\n",
       "        // Send a \"J\" for go to next cell\n",
       "        event.which = 74;\n",
       "        event.keyCode = 74;\n",
       "        manager.command_mode();\n",
       "        manager.handle_keydown(event);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,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\" width=\"576\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final posterior: [ 0.0372  0.0648  0.055  0.0765  0.267  0.0372  0.0648  0.055  0.0765\n",
      "  0.267]\n"
     ]
    }
   ],
   "source": [
    "hallway = np.array([1, 0, 1, 0, 0]*2)\n",
    "kernel = (.1, .8, .1)\n",
    "prior = np.array([.1] * 10)\n",
    "measurements = [1, 0, 1, 0, 0]\n",
    "z_prob = 0.75\n",
    "discrete_bayes_sim(prior, kernel, measurements, z_prob)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We have identified the likely cases of having started at position 0 or 5, because we saw this sequence of doors and walls: 1,0,1,0,0. Now I inject a bad measurement. The next measurement should be 1, but instead we get a 0:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 29,
   "metadata": {
    "scrolled": true
   },
   "outputs": [
    {
     "data": {
      "application/javascript": [
       "/* Put everything inside the global mpl namespace */\n",
       "window.mpl = {};\n",
       "\n",
       "\n",
       "mpl.get_websocket_type = function() {\n",
       "    if (typeof(WebSocket) !== 'undefined') {\n",
       "        return WebSocket;\n",
       "    } else if (typeof(MozWebSocket) !== 'undefined') {\n",
       "        return MozWebSocket;\n",
       "    } else {\n",
       "        alert('Your browser does not have WebSocket support.' +\n",
       "              'Please try Chrome, Safari or Firefox ≥ 6. ' +\n",
       "              'Firefox 4 and 5 are also supported but you ' +\n",
       "              'have to enable WebSockets in about:config.');\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure = function(figure_id, websocket, ondownload, parent_element) {\n",
       "    this.id = figure_id;\n",
       "\n",
       "    this.ws = websocket;\n",
       "\n",
       "    this.supports_binary = (this.ws.binaryType != undefined);\n",
       "\n",
       "    if (!this.supports_binary) {\n",
       "        var warnings = document.getElementById(\"mpl-warnings\");\n",
       "        if (warnings) {\n",
       "            warnings.style.display = 'block';\n",
       "            warnings.textContent = (\n",
       "                \"This browser does not support binary websocket messages. \" +\n",
       "                    \"Performance may be slow.\");\n",
       "        }\n",
       "    }\n",
       "\n",
       "    this.imageObj = new Image();\n",
       "\n",
       "    this.context = undefined;\n",
       "    this.message = undefined;\n",
       "    this.canvas = undefined;\n",
       "    this.rubberband_canvas = undefined;\n",
       "    this.rubberband_context = undefined;\n",
       "    this.format_dropdown = undefined;\n",
       "\n",
       "    this.image_mode = 'full';\n",
       "\n",
       "    this.root = $('<div/>');\n",
       "    this._root_extra_style(this.root)\n",
       "    this.root.attr('style', 'display: inline-block');\n",
       "\n",
       "    $(parent_element).append(this.root);\n",
       "\n",
       "    this._init_header(this);\n",
       "    this._init_canvas(this);\n",
       "    this._init_toolbar(this);\n",
       "\n",
       "    var fig = this;\n",
       "\n",
       "    this.waiting = false;\n",
       "\n",
       "    this.ws.onopen =  function () {\n",
       "            fig.send_message(\"supports_binary\", {value: fig.supports_binary});\n",
       "            fig.send_message(\"send_image_mode\", {});\n",
       "            if (mpl.ratio != 1) {\n",
       "                fig.send_message(\"set_dpi_ratio\", {'dpi_ratio': mpl.ratio});\n",
       "            }\n",
       "            fig.send_message(\"refresh\", {});\n",
       "        }\n",
       "\n",
       "    this.imageObj.onload = function() {\n",
       "            if (fig.image_mode == 'full') {\n",
       "                // Full images could contain transparency (where diff images\n",
       "                // almost always do), so we need to clear the canvas so that\n",
       "                // there is no ghosting.\n",
       "                fig.context.clearRect(0, 0, fig.canvas.width, fig.canvas.height);\n",
       "            }\n",
       "            fig.context.drawImage(fig.imageObj, 0, 0);\n",
       "        };\n",
       "\n",
       "    this.imageObj.onunload = function() {\n",
       "        fig.ws.close();\n",
       "    }\n",
       "\n",
       "    this.ws.onmessage = this._make_on_message_function(this);\n",
       "\n",
       "    this.ondownload = ondownload;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_header = function() {\n",
       "    var titlebar = $(\n",
       "        '<div class=\"ui-dialog-titlebar ui-widget-header ui-corner-all ' +\n",
       "        'ui-helper-clearfix\"/>');\n",
       "    var titletext = $(\n",
       "        '<div class=\"ui-dialog-title\" style=\"width: 100%; ' +\n",
       "        'text-align: center; padding: 3px;\"/>');\n",
       "    titlebar.append(titletext)\n",
       "    this.root.append(titlebar);\n",
       "    this.header = titletext[0];\n",
       "}\n",
       "\n",
       "\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(canvas_div) {\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_canvas = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var canvas_div = $('<div/>');\n",
       "\n",
       "    canvas_div.attr('style', 'position: relative; clear: both; outline: 0');\n",
       "\n",
       "    function canvas_keyboard_event(event) {\n",
       "        return fig.key_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    canvas_div.keydown('key_press', canvas_keyboard_event);\n",
       "    canvas_div.keyup('key_release', canvas_keyboard_event);\n",
       "    this.canvas_div = canvas_div\n",
       "    this._canvas_extra_style(canvas_div)\n",
       "    this.root.append(canvas_div);\n",
       "\n",
       "    var canvas = $('<canvas/>');\n",
       "    canvas.addClass('mpl-canvas');\n",
       "    canvas.attr('style', \"left: 0; top: 0; z-index: 0; outline: 0\")\n",
       "\n",
       "    this.canvas = canvas[0];\n",
       "    this.context = canvas[0].getContext(\"2d\");\n",
       "\n",
       "    var backingStore = this.context.backingStorePixelRatio ||\n",
       "\tthis.context.webkitBackingStorePixelRatio ||\n",
       "\tthis.context.mozBackingStorePixelRatio ||\n",
       "\tthis.context.msBackingStorePixelRatio ||\n",
       "\tthis.context.oBackingStorePixelRatio ||\n",
       "\tthis.context.backingStorePixelRatio || 1;\n",
       "\n",
       "    mpl.ratio = (window.devicePixelRatio || 1) / backingStore;\n",
       "\n",
       "    var rubberband = $('<canvas/>');\n",
       "    rubberband.attr('style', \"position: absolute; left: 0; top: 0; z-index: 1;\")\n",
       "\n",
       "    var pass_mouse_events = true;\n",
       "\n",
       "    canvas_div.resizable({\n",
       "        start: function(event, ui) {\n",
       "            pass_mouse_events = false;\n",
       "        },\n",
       "        resize: function(event, ui) {\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "        stop: function(event, ui) {\n",
       "            pass_mouse_events = true;\n",
       "            fig.request_resize(ui.size.width, ui.size.height);\n",
       "        },\n",
       "    });\n",
       "\n",
       "    function mouse_event_fn(event) {\n",
       "        if (pass_mouse_events)\n",
       "            return fig.mouse_event(event, event['data']);\n",
       "    }\n",
       "\n",
       "    rubberband.mousedown('button_press', mouse_event_fn);\n",
       "    rubberband.mouseup('button_release', mouse_event_fn);\n",
       "    // Throttle sequential mouse events to 1 every 20ms.\n",
       "    rubberband.mousemove('motion_notify', mouse_event_fn);\n",
       "\n",
       "    rubberband.mouseenter('figure_enter', mouse_event_fn);\n",
       "    rubberband.mouseleave('figure_leave', mouse_event_fn);\n",
       "\n",
       "    canvas_div.on(\"wheel\", function (event) {\n",
       "        event = event.originalEvent;\n",
       "        event['data'] = 'scroll'\n",
       "        if (event.deltaY < 0) {\n",
       "            event.step = 1;\n",
       "        } else {\n",
       "            event.step = -1;\n",
       "        }\n",
       "        mouse_event_fn(event);\n",
       "    });\n",
       "\n",
       "    canvas_div.append(canvas);\n",
       "    canvas_div.append(rubberband);\n",
       "\n",
       "    this.rubberband = rubberband;\n",
       "    this.rubberband_canvas = rubberband[0];\n",
       "    this.rubberband_context = rubberband[0].getContext(\"2d\");\n",
       "    this.rubberband_context.strokeStyle = \"#000000\";\n",
       "\n",
       "    this._resize_canvas = function(width, height) {\n",
       "        // Keep the size of the canvas, canvas container, and rubber band\n",
       "        // canvas in synch.\n",
       "        canvas_div.css('width', width)\n",
       "        canvas_div.css('height', height)\n",
       "\n",
       "        canvas.attr('width', width * mpl.ratio);\n",
       "        canvas.attr('height', height * mpl.ratio);\n",
       "        canvas.attr('style', 'width: ' + width + 'px; height: ' + height + 'px;');\n",
       "\n",
       "        rubberband.attr('width', width);\n",
       "        rubberband.attr('height', height);\n",
       "    }\n",
       "\n",
       "    // Set the figure to an initial 600x600px, this will subsequently be updated\n",
       "    // upon first draw.\n",
       "    this._resize_canvas(600, 600);\n",
       "\n",
       "    // Disable right mouse context menu.\n",
       "    $(this.rubberband_canvas).bind(\"contextmenu\",function(e){\n",
       "        return false;\n",
       "    });\n",
       "\n",
       "    function set_focus () {\n",
       "        canvas.focus();\n",
       "        canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    window.setTimeout(set_focus, 100);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items) {\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) {\n",
       "            // put a spacer in here.\n",
       "            continue;\n",
       "        }\n",
       "        var button = $('<button/>');\n",
       "        button.addClass('ui-button ui-widget ui-state-default ui-corner-all ' +\n",
       "                        'ui-button-icon-only');\n",
       "        button.attr('role', 'button');\n",
       "        button.attr('aria-disabled', 'false');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "\n",
       "        var icon_img = $('<span/>');\n",
       "        icon_img.addClass('ui-button-icon-primary ui-icon');\n",
       "        icon_img.addClass(image);\n",
       "        icon_img.addClass('ui-corner-all');\n",
       "\n",
       "        var tooltip_span = $('<span/>');\n",
       "        tooltip_span.addClass('ui-button-text');\n",
       "        tooltip_span.html(tooltip);\n",
       "\n",
       "        button.append(icon_img);\n",
       "        button.append(tooltip_span);\n",
       "\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    var fmt_picker_span = $('<span/>');\n",
       "\n",
       "    var fmt_picker = $('<select/>');\n",
       "    fmt_picker.addClass('mpl-toolbar-option ui-widget ui-widget-content');\n",
       "    fmt_picker_span.append(fmt_picker);\n",
       "    nav_element.append(fmt_picker_span);\n",
       "    this.format_dropdown = fmt_picker[0];\n",
       "\n",
       "    for (var ind in mpl.extensions) {\n",
       "        var fmt = mpl.extensions[ind];\n",
       "        var option = $(\n",
       "            '<option/>', {selected: fmt === mpl.default_extension}).html(fmt);\n",
       "        fmt_picker.append(option)\n",
       "    }\n",
       "\n",
       "    // Add hover states to the ui-buttons\n",
       "    $( \".ui-button\" ).hover(\n",
       "        function() { $(this).addClass(\"ui-state-hover\");},\n",
       "        function() { $(this).removeClass(\"ui-state-hover\");}\n",
       "    );\n",
       "\n",
       "    var status_bar = $('<span class=\"mpl-message\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.request_resize = function(x_pixels, y_pixels) {\n",
       "    // Request matplotlib to resize the figure. Matplotlib will then trigger a resize in the client,\n",
       "    // which will in turn request a refresh of the image.\n",
       "    this.send_message('resize', {'width': x_pixels, 'height': y_pixels});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_message = function(type, properties) {\n",
       "    properties['type'] = type;\n",
       "    properties['figure_id'] = this.id;\n",
       "    this.ws.send(JSON.stringify(properties));\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.send_draw_message = function() {\n",
       "    if (!this.waiting) {\n",
       "        this.waiting = true;\n",
       "        this.ws.send(JSON.stringify({type: \"draw\", figure_id: this.id}));\n",
       "    }\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    var format_dropdown = fig.format_dropdown;\n",
       "    var format = format_dropdown.options[format_dropdown.selectedIndex].value;\n",
       "    fig.ondownload(fig, format);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.figure.prototype.handle_resize = function(fig, msg) {\n",
       "    var size = msg['size'];\n",
       "    if (size[0] != fig.canvas.width || size[1] != fig.canvas.height) {\n",
       "        fig._resize_canvas(size[0], size[1]);\n",
       "        fig.send_message(\"refresh\", {});\n",
       "    };\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_rubberband = function(fig, msg) {\n",
       "    var x0 = msg['x0'] / mpl.ratio;\n",
       "    var y0 = (fig.canvas.height - msg['y0']) / mpl.ratio;\n",
       "    var x1 = msg['x1'] / mpl.ratio;\n",
       "    var y1 = (fig.canvas.height - msg['y1']) / mpl.ratio;\n",
       "    x0 = Math.floor(x0) + 0.5;\n",
       "    y0 = Math.floor(y0) + 0.5;\n",
       "    x1 = Math.floor(x1) + 0.5;\n",
       "    y1 = Math.floor(y1) + 0.5;\n",
       "    var min_x = Math.min(x0, x1);\n",
       "    var min_y = Math.min(y0, y1);\n",
       "    var width = Math.abs(x1 - x0);\n",
       "    var height = Math.abs(y1 - y0);\n",
       "\n",
       "    fig.rubberband_context.clearRect(\n",
       "        0, 0, fig.canvas.width, fig.canvas.height);\n",
       "\n",
       "    fig.rubberband_context.strokeRect(min_x, min_y, width, height);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_figure_label = function(fig, msg) {\n",
       "    // Updates the figure title.\n",
       "    fig.header.textContent = msg['label'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_cursor = function(fig, msg) {\n",
       "    var cursor = msg['cursor'];\n",
       "    switch(cursor)\n",
       "    {\n",
       "    case 0:\n",
       "        cursor = 'pointer';\n",
       "        break;\n",
       "    case 1:\n",
       "        cursor = 'default';\n",
       "        break;\n",
       "    case 2:\n",
       "        cursor = 'crosshair';\n",
       "        break;\n",
       "    case 3:\n",
       "        cursor = 'move';\n",
       "        break;\n",
       "    }\n",
       "    fig.rubberband_canvas.style.cursor = cursor;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_message = function(fig, msg) {\n",
       "    fig.message.textContent = msg['message'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_draw = function(fig, msg) {\n",
       "    // Request the server to send over a new figure.\n",
       "    fig.send_draw_message();\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_image_mode = function(fig, msg) {\n",
       "    fig.image_mode = msg['mode'];\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Called whenever the canvas gets updated.\n",
       "    this.send_message(\"ack\", {});\n",
       "}\n",
       "\n",
       "// A function to construct a web socket function for onmessage handling.\n",
       "// Called in the figure constructor.\n",
       "mpl.figure.prototype._make_on_message_function = function(fig) {\n",
       "    return function socket_on_message(evt) {\n",
       "        if (evt.data instanceof Blob) {\n",
       "            /* FIXME: We get \"Resource interpreted as Image but\n",
       "             * transferred with MIME type text/plain:\" errors on\n",
       "             * Chrome.  But how to set the MIME type?  It doesn't seem\n",
       "             * to be part of the websocket stream */\n",
       "            evt.data.type = \"image/png\";\n",
       "\n",
       "            /* Free the memory for the previous frames */\n",
       "            if (fig.imageObj.src) {\n",
       "                (window.URL || window.webkitURL).revokeObjectURL(\n",
       "                    fig.imageObj.src);\n",
       "            }\n",
       "\n",
       "            fig.imageObj.src = (window.URL || window.webkitURL).createObjectURL(\n",
       "                evt.data);\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "        else if (typeof evt.data === 'string' && evt.data.slice(0, 21) == \"data:image/png;base64\") {\n",
       "            fig.imageObj.src = evt.data;\n",
       "            fig.updated_canvas_event();\n",
       "            fig.waiting = false;\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        var msg = JSON.parse(evt.data);\n",
       "        var msg_type = msg['type'];\n",
       "\n",
       "        // Call the  \"handle_{type}\" callback, which takes\n",
       "        // the figure and JSON message as its only arguments.\n",
       "        try {\n",
       "            var callback = fig[\"handle_\" + msg_type];\n",
       "        } catch (e) {\n",
       "            console.log(\"No handler for the '\" + msg_type + \"' message type: \", msg);\n",
       "            return;\n",
       "        }\n",
       "\n",
       "        if (callback) {\n",
       "            try {\n",
       "                // console.log(\"Handling '\" + msg_type + \"' message: \", msg);\n",
       "                callback(fig, msg);\n",
       "            } catch (e) {\n",
       "                console.log(\"Exception inside the 'handler_\" + msg_type + \"' callback:\", e, e.stack, msg);\n",
       "            }\n",
       "        }\n",
       "    };\n",
       "}\n",
       "\n",
       "// from http://stackoverflow.com/questions/1114465/getting-mouse-location-in-canvas\n",
       "mpl.findpos = function(e) {\n",
       "    //this section is from http://www.quirksmode.org/js/events_properties.html\n",
       "    var targ;\n",
       "    if (!e)\n",
       "        e = window.event;\n",
       "    if (e.target)\n",
       "        targ = e.target;\n",
       "    else if (e.srcElement)\n",
       "        targ = e.srcElement;\n",
       "    if (targ.nodeType == 3) // defeat Safari bug\n",
       "        targ = targ.parentNode;\n",
       "\n",
       "    // jQuery normalizes the pageX and pageY\n",
       "    // pageX,Y are the mouse positions relative to the document\n",
       "    // offset() returns the position of the element relative to the document\n",
       "    var x = e.pageX - $(targ).offset().left;\n",
       "    var y = e.pageY - $(targ).offset().top;\n",
       "\n",
       "    return {\"x\": x, \"y\": y};\n",
       "};\n",
       "\n",
       "/*\n",
       " * return a copy of an object with only non-object keys\n",
       " * we need this to avoid circular references\n",
       " * http://stackoverflow.com/a/24161582/3208463\n",
       " */\n",
       "function simpleKeys (original) {\n",
       "  return Object.keys(original).reduce(function (obj, key) {\n",
       "    if (typeof original[key] !== 'object')\n",
       "        obj[key] = original[key]\n",
       "    return obj;\n",
       "  }, {});\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.mouse_event = function(event, name) {\n",
       "    var canvas_pos = mpl.findpos(event)\n",
       "\n",
       "    if (name === 'button_press')\n",
       "    {\n",
       "        this.canvas.focus();\n",
       "        this.canvas_div.focus();\n",
       "    }\n",
       "\n",
       "    var x = canvas_pos.x * mpl.ratio;\n",
       "    var y = canvas_pos.y * mpl.ratio;\n",
       "\n",
       "    this.send_message(name, {x: x, y: y, button: event.button,\n",
       "                             step: event.step,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "\n",
       "    /* This prevents the web browser from automatically changing to\n",
       "     * the text insertion cursor when the button is pressed.  We want\n",
       "     * to control all of the cursor setting manually through the\n",
       "     * 'cursor' event from matplotlib */\n",
       "    event.preventDefault();\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    // Handle any extra behaviour associated with a key event\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.key_event = function(event, name) {\n",
       "\n",
       "    // Prevent repeat events\n",
       "    if (name == 'key_press')\n",
       "    {\n",
       "        if (event.which === this._key)\n",
       "            return;\n",
       "        else\n",
       "            this._key = event.which;\n",
       "    }\n",
       "    if (name == 'key_release')\n",
       "        this._key = null;\n",
       "\n",
       "    var value = '';\n",
       "    if (event.ctrlKey && event.which != 17)\n",
       "        value += \"ctrl+\";\n",
       "    if (event.altKey && event.which != 18)\n",
       "        value += \"alt+\";\n",
       "    if (event.shiftKey && event.which != 16)\n",
       "        value += \"shift+\";\n",
       "\n",
       "    value += 'k';\n",
       "    value += event.which.toString();\n",
       "\n",
       "    this._key_event_extra(event, name);\n",
       "\n",
       "    this.send_message(name, {key: value,\n",
       "                             guiEvent: simpleKeys(event)});\n",
       "    return false;\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onclick = function(name) {\n",
       "    if (name == 'download') {\n",
       "        this.handle_save(this, null);\n",
       "    } else {\n",
       "        this.send_message(\"toolbar_button\", {name: name});\n",
       "    }\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.toolbar_button_onmouseover = function(tooltip) {\n",
       "    this.message.textContent = tooltip;\n",
       "};\n",
       "mpl.toolbar_items = [[\"Home\", \"Reset original view\", \"fa fa-home icon-home\", \"home\"], [\"Back\", \"Back to  previous view\", \"fa fa-arrow-left icon-arrow-left\", \"back\"], [\"Forward\", \"Forward to next view\", \"fa fa-arrow-right icon-arrow-right\", \"forward\"], [\"\", \"\", \"\", \"\"], [\"Pan\", \"Pan axes with left mouse, zoom with right\", \"fa fa-arrows icon-move\", \"pan\"], [\"Zoom\", \"Zoom to rectangle\", \"fa fa-square-o icon-check-empty\", \"zoom\"], [\"\", \"\", \"\", \"\"], [\"Download\", \"Download plot\", \"fa fa-floppy-o icon-save\", \"download\"]];\n",
       "\n",
       "mpl.extensions = [\"eps\", \"jpeg\", \"pdf\", \"png\", \"ps\", \"raw\", \"svg\", \"tif\"];\n",
       "\n",
       "mpl.default_extension = \"png\";var comm_websocket_adapter = function(comm) {\n",
       "    // Create a \"websocket\"-like object which calls the given IPython comm\n",
       "    // object with the appropriate methods. Currently this is a non binary\n",
       "    // socket, so there is still some room for performance tuning.\n",
       "    var ws = {};\n",
       "\n",
       "    ws.close = function() {\n",
       "        comm.close()\n",
       "    };\n",
       "    ws.send = function(m) {\n",
       "        //console.log('sending', m);\n",
       "        comm.send(m);\n",
       "    };\n",
       "    // Register the callback with on_msg.\n",
       "    comm.on_msg(function(msg) {\n",
       "        //console.log('receiving', msg['content']['data'], msg);\n",
       "        // Pass the mpl event to the overriden (by mpl) onmessage function.\n",
       "        ws.onmessage(msg['content']['data'])\n",
       "    });\n",
       "    return ws;\n",
       "}\n",
       "\n",
       "mpl.mpl_figure_comm = function(comm, msg) {\n",
       "    // This is the function which gets called when the mpl process\n",
       "    // starts-up an IPython Comm through the \"matplotlib\" channel.\n",
       "\n",
       "    var id = msg.content.data.id;\n",
       "    // Get hold of the div created by the display call when the Comm\n",
       "    // socket was opened in Python.\n",
       "    var element = $(\"#\" + id);\n",
       "    var ws_proxy = comm_websocket_adapter(comm)\n",
       "\n",
       "    function ondownload(figure, format) {\n",
       "        window.open(figure.imageObj.src);\n",
       "    }\n",
       "\n",
       "    var fig = new mpl.figure(id, ws_proxy,\n",
       "                           ondownload,\n",
       "                           element.get(0));\n",
       "\n",
       "    // Call onopen now - mpl needs it, as it is assuming we've passed it a real\n",
       "    // web socket which is closed, not our websocket->open comm proxy.\n",
       "    ws_proxy.onopen();\n",
       "\n",
       "    fig.parent_element = element.get(0);\n",
       "    fig.cell_info = mpl.find_output_cell(\"<div id='\" + id + \"'></div>\");\n",
       "    if (!fig.cell_info) {\n",
       "        console.error(\"Failed to find cell for figure\", id, fig);\n",
       "        return;\n",
       "    }\n",
       "\n",
       "    var output_index = fig.cell_info[2]\n",
       "    var cell = fig.cell_info[0];\n",
       "\n",
       "};\n",
       "\n",
       "mpl.figure.prototype.handle_close = function(fig, msg) {\n",
       "    var width = fig.canvas.width/mpl.ratio\n",
       "    fig.root.unbind('remove')\n",
       "\n",
       "    // Update the output cell to use the data from the current canvas.\n",
       "    fig.push_to_output();\n",
       "    var dataURL = fig.canvas.toDataURL();\n",
       "    // Re-enable the keyboard manager in IPython - without this line, in FF,\n",
       "    // the notebook keyboard shortcuts fail.\n",
       "    IPython.keyboard_manager.enable()\n",
       "    $(fig.parent_element).html('<img src=\"' + dataURL + '\" width=\"' + width + '\">');\n",
       "    fig.close_ws(fig, msg);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.close_ws = function(fig, msg){\n",
       "    fig.send_message('closing', msg);\n",
       "    // fig.ws.close()\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.push_to_output = function(remove_interactive) {\n",
       "    // Turn the data on the canvas into data in the output cell.\n",
       "    var width = this.canvas.width/mpl.ratio\n",
       "    var dataURL = this.canvas.toDataURL();\n",
       "    this.cell_info[1]['text/html'] = '<img src=\"' + dataURL + '\" width=\"' + width + '\">';\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.updated_canvas_event = function() {\n",
       "    // Tell IPython that the notebook contents must change.\n",
       "    IPython.notebook.set_dirty(true);\n",
       "    this.send_message(\"ack\", {});\n",
       "    var fig = this;\n",
       "    // Wait a second, then push the new image to the DOM so\n",
       "    // that it is saved nicely (might be nice to debounce this).\n",
       "    setTimeout(function () { fig.push_to_output() }, 1000);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._init_toolbar = function() {\n",
       "    var fig = this;\n",
       "\n",
       "    var nav_element = $('<div/>')\n",
       "    nav_element.attr('style', 'width: 100%');\n",
       "    this.root.append(nav_element);\n",
       "\n",
       "    // Define a callback function for later on.\n",
       "    function toolbar_event(event) {\n",
       "        return fig.toolbar_button_onclick(event['data']);\n",
       "    }\n",
       "    function toolbar_mouse_event(event) {\n",
       "        return fig.toolbar_button_onmouseover(event['data']);\n",
       "    }\n",
       "\n",
       "    for(var toolbar_ind in mpl.toolbar_items){\n",
       "        var name = mpl.toolbar_items[toolbar_ind][0];\n",
       "        var tooltip = mpl.toolbar_items[toolbar_ind][1];\n",
       "        var image = mpl.toolbar_items[toolbar_ind][2];\n",
       "        var method_name = mpl.toolbar_items[toolbar_ind][3];\n",
       "\n",
       "        if (!name) { continue; };\n",
       "\n",
       "        var button = $('<button class=\"btn btn-default\" href=\"#\" title=\"' + name + '\"><i class=\"fa ' + image + ' fa-lg\"></i></button>');\n",
       "        button.click(method_name, toolbar_event);\n",
       "        button.mouseover(tooltip, toolbar_mouse_event);\n",
       "        nav_element.append(button);\n",
       "    }\n",
       "\n",
       "    // Add the status bar.\n",
       "    var status_bar = $('<span class=\"mpl-message\" style=\"text-align:right; float: right;\"/>');\n",
       "    nav_element.append(status_bar);\n",
       "    this.message = status_bar[0];\n",
       "\n",
       "    // Add the close button to the window.\n",
       "    var buttongrp = $('<div class=\"btn-group inline pull-right\"></div>');\n",
       "    var button = $('<button class=\"btn btn-mini btn-primary\" href=\"#\" title=\"Stop Interaction\"><i class=\"fa fa-power-off icon-remove icon-large\"></i></button>');\n",
       "    button.click(function (evt) { fig.handle_close(fig, {}); } );\n",
       "    button.mouseover('Stop Interaction', toolbar_mouse_event);\n",
       "    buttongrp.append(button);\n",
       "    var titlebar = this.root.find($('.ui-dialog-titlebar'));\n",
       "    titlebar.prepend(buttongrp);\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._root_extra_style = function(el){\n",
       "    var fig = this\n",
       "    el.on(\"remove\", function(){\n",
       "\tfig.close_ws(fig, {});\n",
       "    });\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._canvas_extra_style = function(el){\n",
       "    // this is important to make the div 'focusable\n",
       "    el.attr('tabindex', 0)\n",
       "    // reach out to IPython and tell the keyboard manager to turn it's self\n",
       "    // off when our div gets focus\n",
       "\n",
       "    // location in version 3\n",
       "    if (IPython.notebook.keyboard_manager) {\n",
       "        IPython.notebook.keyboard_manager.register_events(el);\n",
       "    }\n",
       "    else {\n",
       "        // location in version 2\n",
       "        IPython.keyboard_manager.register_events(el);\n",
       "    }\n",
       "\n",
       "}\n",
       "\n",
       "mpl.figure.prototype._key_event_extra = function(event, name) {\n",
       "    var manager = IPython.notebook.keyboard_manager;\n",
       "    if (!manager)\n",
       "        manager = IPython.keyboard_manager;\n",
       "\n",
       "    // Check for shift+enter\n",
       "    if (event.shiftKey && event.which == 13) {\n",
       "        this.canvas_div.blur();\n",
       "        event.shiftKey = false;\n",
       "        // Send a \"J\" for go to next cell\n",
       "        event.which = 74;\n",
       "        event.keyCode = 74;\n",
       "        manager.command_mode();\n",
       "        manager.handle_keydown(event);\n",
       "    }\n",
       "}\n",
       "\n",
       "mpl.figure.prototype.handle_save = function(fig, msg) {\n",
       "    fig.ondownload(fig, null);\n",
       "}\n",
       "\n",
       "\n",
       "mpl.find_output_cell = function(html_output) {\n",
       "    // Return the cell and output element which can be found *uniquely* in the notebook.\n",
       "    // Note - this is a bit hacky, but it is done because the \"notebook_saving.Notebook\"\n",
       "    // IPython event is triggered only after the cells have been serialised, which for\n",
       "    // our purposes (turning an active figure into a static one), is too late.\n",
       "    var cells = IPython.notebook.get_cells();\n",
       "    var ncells = cells.length;\n",
       "    for (var i=0; i<ncells; i++) {\n",
       "        var cell = cells[i];\n",
       "        if (cell.cell_type === 'code'){\n",
       "            for (var j=0; j<cell.output_area.outputs.length; j++) {\n",
       "                var data = cell.output_area.outputs[j];\n",
       "                if (data.data) {\n",
       "                    // IPython >= 3 moved mimebundle to data attribute of output\n",
       "                    data = data.data;\n",
       "                }\n",
       "                if (data['text/html'] == html_output) {\n",
       "                    return [cell, data, j];\n",
       "                }\n",
       "            }\n",
       "        }\n",
       "    }\n",
       "}\n",
       "\n",
       "// Register the function which deals with the matplotlib target/channel.\n",
       "// The kernel may be null if the page has been refreshed.\n",
       "if (IPython.notebook.kernel != null) {\n",
       "    IPython.notebook.kernel.comm_manager.register_target('matplotlib', mpl.mpl_figure_comm);\n",
       "}\n"
      ],
      "text/plain": [
       "<IPython.core.display.Javascript object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/html": [
       "<img src=\"data:image/png;base64,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\" width=\"576\">"
      ],
      "text/plain": [
       "<IPython.core.display.HTML object>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Final posterior: [ 0.121  0.102  0.0329  0.0938  0.151  0.121  0.102  0.0329  0.0938\n",
      "  0.151]\n"
     ]
    }
   ],
   "source": [
    "measurements = [1, 0, 1, 0, 0, 0]\n",
    "discrete_bayes_sim(prior, kernel, measurements, z_prob)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That one bad measurement has significantly eroded our knowledge. Now let's continue with a series of correct measurements."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 30,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec305a550>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with figsize(y=5.5):\n",
    "    measurements = [0, 1, 0, 1, 0, 0]\n",
    "    for i, m in enumerate(measurements):\n",
    "        likelihood = lh_hallway(hallway, z=m, z_prob=.75)\n",
    "        posterior = update(likelihood, prior)\n",
    "        prior = predict(posterior, 1, kernel)\n",
    "        plt.subplot(3, 2, i+1)\n",
    "        book_plots.bar_plot(posterior, ylim=(0, .4), title='step {}'.format(i+1))\n",
    "    plt.tight_layout()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We quickly filtered out the bad sensor reading and converged on the most likely positions for our dog."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Drawbacks and Limitations\n",
    "\n",
    "Do not be mislead by the simplicity of the examples I chose. This is a robust and complete filter, and you may use the code in real world solutions. If you need a multimodal, discrete filter, this filter works.\n",
    "\n",
    "With that said, this filter it is not used often because it has several limitations. Getting around those limitations is the motivation behind the chapters in the rest of this book.\n",
    "\n",
    "The first problem is scaling. Our dog tracking problem used only one variable, $pos$, to denote the dog's position. Most interesting problems will want to track several things in a large space. Realistically, at a minimum we would want to track our dog's $(x,y)$ coordinate, and probably his velocity $(\\dot{x},\\dot{y})$ as well. We have not covered the multidimensional case, but instead of an array we use a multidimensional grid to store the probabilities at each discrete location. Each `update()` and `predict()` step requires updating all values in the grid, so a simple four variable problem would require $O(n^4)$ running time *per time step*. Realistic filters can have 10 or more variables to track, leading to exorbitant computation requirements.\n",
    "\n",
    "The second problem is that the filter is discrete, but we live in a continuous world. The histogram requires that you model the output of your filter as a set of discrete points. A 100 meter hallway requires 10,000 positions to model the hallway to 1cm accuracy. So each update and predict operation would entail performing calculations for 10,000 different probabilities. It gets exponentially worse as we add dimensions. A 100x100 m$^2$ courtyard requires 100,000,000 bins to get 1cm accuracy.\n",
    "\n",
    "A third problem is that the filter is multimodal. In the last example we ended up with strong beliefs that the dog was in position 4 or 9. This is not always a problem. Particle filters, which we will study later, are multimodal and are often used because of this property. But imagine if the GPS in your car reported to you that it is 40% sure that you are on D street, and 30% sure you are on Willow Avenue. \n",
    "\n",
    "A forth problem is that it requires a measurement of the change in state. We need a motion sensor to detect how much the dog moves. There are ways to work around this problem, but it would complicate the exposition of this chapter, so, given the aforementioned problems, I will not discuss it further.\n",
    "\n",
    "With that said, if I had a small problem that this technique could handle I would choose to use it; it is trivial to implement, debug, and understand, all virtues."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Tracking and Control\n",
    "\n",
    "We have been passively tracking an autonomously moving object. But consider this very similar problem. I am automating a warehouse and want to use robots to collect all of the items for a customer's order. Perhaps the easiest way to do this is to have the robots travel on a train track. I want to be able to send the robot a destination and have it go there. But train tracks and robot motors are imperfect. Wheel slippage and imperfect motors means that the robot is unlikely to travel to exactly the position you command. There is more than one robot, and we need to know where they all are so we do not cause them to crash.\n",
    "\n",
    "So we add sensors. Perhaps we mount magnets on the track every few feet, and use a Hall sensor to count how many magnets are passed. If we count 10 magnets then the robot should be at the 10th magnet. Of course it is possible to either miss a magnet or to count it twice, so we have to accommodate some degree of error. We can use the code from the previous section to track our robot since magnet counting is very similar to doorway sensing.\n",
    "\n",
    "But we are not done. We've learned to never throw information away. If you have information you should use it to improve your estimate. What information are we leaving out? We know what control inputs we are feeding to the wheels of the robot at each moment in time. For example, let's say that once a second we send a movement command to the robot - move left 1 unit, move right 1 unit, or stand still.  If I send the command 'move left 1 unit' I expect that in one second from now the robot will be 1 unit to the left of where it is now. This is a simplification because I am not taking acceleration into account, but I am not trying to teach control theory. Wheels and motors are imperfect. The robot might end up 0.9 units away, or maybe 1.2 units. \n",
    "\n",
    "Now the entire solution is clear. We assumed that the dog kept moving in whatever direction he was previously moving. That is a dubious assumption for my dog! Robots are far more predictable. Instead of making a dubious prediction based on assumption of behavior we will feed in the command that we sent to the robot! In other words, when we call `predict()` we will pass in the commanded movement that we gave the robot along with a kernel that describes the likelihood  of that movement."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Simulating the Train Behavior\n",
    "\n",
    "We need to simulate an imperfect train. When we command it to move it will sometimes make a small mistake, and its sensor will sometimes return the incorrect value."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 31,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "class Train(object):\n",
    "\n",
    "    def __init__(self, track_len, kernel=[1.], sensor_accuracy=.9):\n",
    "        self.track_len = track_len\n",
    "        self.pos = 0\n",
    "        self.kernel = kernel\n",
    "        self.sensor_accuracy = sensor_accuracy\n",
    "\n",
    "    def move(self, distance=1):\n",
    "        \"\"\" move in the specified direction\n",
    "        with some small chance of error\"\"\"\n",
    "\n",
    "        self.pos += distance\n",
    "        # insert random movement error according to kernel\n",
    "        r = random.random()\n",
    "        s = 0\n",
    "        offset = -(len(self.kernel) - 1) / 2\n",
    "        for k in self.kernel:\n",
    "            s += k\n",
    "            if r <= s:\n",
    "                break\n",
    "            offset += 1\n",
    "        self.pos = int((self.pos + offset) % self.track_len)\n",
    "        return self.pos\n",
    "\n",
    "    def sense(self):\n",
    "        pos = self.pos\n",
    "         # insert random sensor error\n",
    "        if random.random() > self.sensor_accuracy:\n",
    "            if random.random() > 0.5:\n",
    "                pos += 1\n",
    "            else:\n",
    "                pos -= 1\n",
    "        return pos"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "With that we are ready to write the filter. We will put it in a function so that we can run it with different assumptions. I will assume that the robot always starts at the beginning of the track. The track is implemented as being 10 units long, but think of it as a track of length, say 10,000, with the magnet pattern repeated every 10 units. A length of 10 makes it easier to plot and inspect."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 32,
   "metadata": {
    "collapsed": true
   },
   "outputs": [],
   "source": [
    "def train_filter(iterations, kernel, sensor_accuracy, \n",
    "             move_distance, do_print=True):\n",
    "    track = np.array([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])\n",
    "    prior = np.array([.9] + [0.01]*9)\n",
    "    normalize(prior)\n",
    "    \n",
    "    robot = Train(len(track), kernel, sensor_accuracy)\n",
    "    for i in range(iterations):\n",
    "        robot.move(distance=move_distance)\n",
    "        m = robot.sense()\n",
    "        if do_print:\n",
    "            print('''time {}: pos {}, sensed {}, '''\n",
    "                  '''at position {}'''.format(\n",
    "                    i, robot.pos, m, track[robot.pos]))\n",
    "\n",
    "        likelihood = lh_hallway(track, m, sensor_accuracy)\n",
    "        posterior = update(likelihood, prior)\n",
    "        index = np.argmax(posterior)\n",
    "        if i < iterations - 1:\n",
    "            prior = predict(posterior, move_distance, kernel)       \n",
    "\n",
    "        if do_print:\n",
    "            print('''        predicted position is {}'''\n",
    "                  ''' with confidence {:.4f}%:'''.format(\n",
    "                  index, posterior[index]*100))            \n",
    "\n",
    "    book_plots.bar_plot(posterior)\n",
    "    if do_print:\n",
    "        print()\n",
    "        print('final position is', robot.pos)\n",
    "        index = np.argmax(posterior)\n",
    "        print('''predicted position is {} with '''\n",
    "              '''confidence {:.4f}%:'''.format(\n",
    "                index, posterior[index]*100))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Read the code and make sure you understand it. Now let's do a run with no sensor or movement error. If the code is correct it should be able to locate the robot with no error. The output is a bit tedious to read, but if you are at all unsure of how the update/predict cycle works make sure you read through it carefully to solidify your understanding."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 33,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time 0: pos 4, sensed 4, at position 4\n",
      "        predicted position is 4 with confidence 91.0665%:\n",
      "time 1: pos 8, sensed 8, at position 8\n",
      "        predicted position is 8 with confidence 99.9902%:\n",
      "time 2: pos 2, sensed 2, at position 2\n",
      "        predicted position is 2 with confidence 100.0000%:\n",
      "time 3: pos 6, sensed 6, at position 6\n",
      "        predicted position is 6 with confidence 100.0000%:\n",
      "\n",
      "final position is 6\n",
      "predicted position is 6 with confidence 100.0000%:\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nZ/4PLUUv0TsVRkoAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec3274908>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import random\n",
    "\n",
    "random.seed(3)\n",
    "np.set_printoptions(precision=2, suppress=True, linewidth=60)\n",
    "train_filter(4, kernel=[1.], sensor_accuracy=.999,\n",
    "             move_distance=4, do_print=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that the code was able to perfectly track the robot so we should feel reasonably confident that the code is working. Now let's see how it fairs with some errors. "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "time 0: pos 4, sensed 4, at position 4\n",
      "        predicted position is 0 with confidence 84.1121%:\n",
      "time 1: pos 8, sensed 9, at position 8\n",
      "        predicted position is 4 with confidence 59.4728%:\n",
      "time 2: pos 3, sensed 3, at position 3\n",
      "        predicted position is 3 with confidence 53.5807%:\n",
      "time 3: pos 7, sensed 8, at position 7\n",
      "        predicted position is 8 with confidence 37.5836%:\n",
      "\n",
      "final position is 7\n",
      "predicted position is 8 with confidence 37.5836%:\n"
     ]
    },
    {
     "data": {
      "image/png": "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\nPXEAAAAASUVORK5CYII=\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1d84630>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "random.seed(5)\n",
    "train_filter(4, kernel=[.1, .8, .1], sensor_accuracy=.9,\n",
    "         move_distance=4, do_print=True)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "There was a sensing error at time 1, but we are still quite confident in our position. \n",
    "\n",
    "Now let's run a very long simulation and see how the filter responds to errors."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 35,
   "metadata": {
    "scrolled": false
   },
   "outputs": [
    {
     "data": {
      "image/png": "iVBORw0KGgoAAAANSUhEUgAAAhYAAAFWCAYAAAA199QiAAAABHNCSVQICAgIfAhkiAAAAAlwSFlz\nAAALEgAACxIB0t1+/AAAADl0RVh0U29mdHdhcmUAbWF0cGxvdGxpYiB2ZXJzaW9uIDIuMS4wLCBo\ndHRwOi8vbWF0cGxvdGxpYi5vcmcvpW3flQAAIABJREFUeJzt3X+UZHV95//nixlYBKFJJHqUAcHY\n5gtyVNg5KiGrbEAZzGbc5OvZwB5JzFHM7koSY9SFxK9O2GxYzRrdRJJ1EaOiYpComTUT0awaN1k1\nTESUYcSZIEILiEEsf8RVx7y/f9SdoWj7R3XXp3u6up6Pc+pM3apPve+7em6/+30/996qVBWSJEkt\nHHKwE5AkSeuHjYUkSWrGxkKSJDVjYyFJkpqxsZAkSc3YWEiSpGZsLNawJLuSnHUQ139Ckm8m2XCw\ncpC0fNYQHQw2FmtYVT2+qj4KkGRbkrev5PqS3J7knIH131FVD62q76/Auv5Tks8m2Zdk2wLj/jhJ\nJXnswGMnJtmR5P4k9yR5Q5KNrXOUxp01ZN4acnKSDyfpJdmb5Gda5zfJbCwmxBr8w7sXeDnw5/MN\nSPITwI/O8dQfAvcCjwSeBDwd+A8rkKOkznqpId37+DPg/cAPAy8E3p7kcSuX6mSxsVjD9nf/SbYA\nvwH8XDeteFP3/FSSq5LcneRLSX57/5Rjkucl+Zskr0vyVWBbkh/tuvT7kvxDknckOaYbfzVwAvA/\nu3W8vJsZqP0FJcmjkmxP8tWuy79oINdtSa5N8rYk3+imYDfP996q6q1V9RfAN+Z57xuBPwAunuPp\nk4Brq+r/VtU9wAeAxy/15yutd9aQOWvI/wM8CnhdVX2/qj4M/A1w4TJ+xJqDjcUYqKoPAL8D/Ek3\nrfjE7qm3AvuAxwKnAc8EXjDw0qcAtwEPB/4zEOBy+r9UJwPHA9u6dVwI3AH8dLeO18yRyjXATPf6\n5wC/k+Tsgee3Au8CjgG2A28Y4W3/GvCxqvrMHM/9N+D8JEckOQ44j35zIWkO1pAHyRxjA5w6wro0\nwMZiTCV5BP0/qC+uqm9V1b3A64DzB4bdVVV/UFX7qurbVbW3qj5UVd+pqq8Av0f/MMIw6zse+Ang\nP3YzBZ8G3sSDu/y/rqod3fHUq4EnzhFq2HX9EvDKeYb8Ff0Ziq/TL1I7gfctZ13SpJrgGvI5+odS\nX5bk0CTP7N7DEctZl36QjcX4ejRwKHB3kq8l+RrwRvp7FvvdOfiCJA9P8q5uyvPrwNuBY4dc36OA\nr1bV4LTjF4HjBpbvGbj/j8Dhyzwu+3rgsqrqzX4iySHA9cB7gCPp5/9DwKuXsR5pkk1kDamq7wH/\nGvipbn2/DlxLfydFDdhYjI/ZX0N7J/Ad4NiqOqa7HV1Vj1/gNZd3jz2hqo4GnsuDpwUX+qrbu4Af\nTnLUwGMnAF9aypsY0tnA76Z/xcf+QvPxJP+W/slWxwNv6Paa7gP+GHjWCuQhrSfWkH4Noao+U1VP\nr6qHVdW5wGOAv12BPCaSjcX4+DJwYrfHTlXdDXwQeG2So5Mc0p1YtdC05FHAN4GvdecmvGyOdTxm\nrhdW1Z3A/wEuT3J4kicAzwfesZw3001BHk5/G9zYxdx/rfvj6E+BPqm7Afw08N6q+gfgC8C/T7Kx\nO3HsF4CblpOHNEGsIfDe7rVP6MYfkeSl9K8we8ty8tAPsrEYH+/u/r0vyae6+z8PHAbcAtwPXEf/\nF2Q+vwWcDvToX6L1nlnPXw68opsWfekcr78AOJH+nsd7gVdV1YeW/lYAuBL4dhfzN7v7FwJU1b1V\ndc/+Wzf+H6rq2939nwW2AF+hf8nZPvonakmanzXkgRpyIXA3/XMtzgaeUVXfWWYemiVVC81cSZIk\nDc8ZC0mS1MyijUWSNye5N8nN8zyfJL/ffdjJZ5Kc3j5NSePMOiJNjmFmLN5C/3j2fM4DprvbC4E/\nGj0tSevMW7COSBNh0euDq+pjSU5cYMizgbdV/2SNTyQ5JskjuzOOD+j1ep7MIa0xU1NTc30KYXMt\n6og1RFqbZteRFudYHMeDP0Rlhgd/4IkkLcY6Iq0TLRqLufZ43LOQtBTWEWmdaPE1uDP0Pwlxv030\nr1Ge19TU1LJXtnPnTgA2b573S+/WTFxjGrOlVnF7vR/4lOO1YEl1ZJQaAuPz/27MtR9zpeKu9ZgL\n1ZEWMxbbgZ/vzup+KtCbfX6FJC3COiKtE4vOWCS5BjgLODbJDPAq+l9cQ1X9d2AH/e9p2Ev/S2N+\ncaWSlTSerCPS5BjmqpALFnm+gBc1y0jSumMdkSaHn7wpSZKasbGQJEnN2FhIkqRmbCwkSVIzNhaS\nJKkZGwtJktSMjYUkSWrGxkKSJDVjYyFJkpqxsZAkSc3YWEiSpGZafG26JGmZTrt6zwLPdl8Pv3v+\nMTdeON02IWlEzlhIkqRmbCwkSVIzNhaSJKmZoRqLJFuS3Jpkb5JL5nj+hCQfSXJjks8keVb7VCWN\nK2uINDkWbSySbACuAM4DTgEuSHLKrGGvAK6tqtOA84E/bJ2opPFkDZEmS6pq4QHJGcC2qjq3W74U\noKouHxjzRuC2qnp1N/61VfXjg3F6vd6BFe3Zs9BZ0JJW0vT0A1cRTE1NZaXXZw1Z2EW7p0Z6/ZUn\n9xplIg1voToyzOWmxwF3DizPAE+ZNWYb8MEkvwwcCZyznEQlrUvWEGmCDNNYzLVHM3ua4wLgLVX1\n2m5v4+okp1bVP80VcPPmzUtM8wE7d+4cOcZqxTWmMVtqFbfXW/U93DVVQ2CN/b8v8BkVw1jq+tbU\ne18HMVcq7lqPuVAdGebkzRng+IHlTcBds8Y8H7gWoKo+DhwOHLukLCWtV9YQaYIM01jcAEwnOSnJ\nYfRPrNo+a8wdwNkASU6mXxS+0jJRSWPLGiJNkEUbi6raB1wMXA/spn/m9q4klyXZ2g37deCiJDcB\n1wDPq8XOCpU0Eawh0mQZ6rtCqmoHsGPWY68cuH8LcGbb1CStF9YQaXL4yZuSJKkZGwtJktSMX5su\nLcOoX3UNft21pPXJGQtJktSMjYUkSWrGxkKSJDVjYyFJkpqxsZAkSc3YWEiSpGZsLCRJUjM2FpIk\nqRkbC0mS1IyNhSRJasbGQpIkNWNjIUmSmhmqsUiyJcmtSfYmuWSeMf8myS1JdiV5Z9s0JY0za4g0\nORb9dtMkG4ArgGcAM8ANSbZX1S0DY6aBS4Ezq+r+JA9fqYQljRdriDRZUlULD0jOALZV1bnd8qUA\nVXX5wJjXAJ+vqjfNF6fX6x1Y0Z49C3+dtLTWXbR7auQYV57ca5DJ0k1PP/B17VNTU1np9VlDFjbq\ntnSwtiNNtoXqyDCHQo4D7hxYnukeG/Q44HFJ/ibJJ5JsWWauktYfa4g0QRY9FALMtUcze5pjIzAN\nnAVsAv53klOr6mtzBdy8efNScnyQnTt3jhxjteIacx3H3D36HvNS19nq/fd6q76Hu6ZqCKyvbelg\nbUfGXNm4az3mQnVkmBmLGeD4geVNwF1zjPmzqvpeVX0BuJV+kZAka4g0QYZpLG4AppOclOQw4Hxg\n+6wx7wP+JUCSY+lPa97WMlFJY8saIk2QRRuLqtoHXAxcD+wGrq2qXUkuS7K1G3Y9cF+SW4CPAC+r\nqvtWKmlJ48MaIk2WYc6xoKp2ADtmPfbKgfsFvKS7SdKDWEOkyeEnb0qSpGZsLCRJUjM2FpIkqRkb\nC0mS1IyNhSRJasbGQpIkNWNjIUmSmrGxkCRJzdhYSJKkZmwsJElSMzYWkiSpGRsLSZLUjI2FJElq\nxsZCkiQ1Y2MhSZKaGaqxSLIlya1J9ia5ZIFxz0lSSTa3S1HSuLOGSJNj0cYiyQbgCuA84BTggiSn\nzDHuKOBXgE+2TlLS+LKGSJMlVbXwgOQMYFtVndstXwpQVZfPGvd64C+BlwIvraqdg8/3er0DK9qz\nZ0+T5KWD5aLdUyPHuPLkXoNMlm56evrA/ampqaz0+qwhCxt1WzpY25Em20J1ZJhDIccBdw4sz3SP\nHZDkNOD4qnr/8tOUtE5ZQ6QJsnGIMXPt0RzYc0hyCPA64HnDrnTz5uUfPt25c+fIMVYrrjHXcczd\no+8xL3Wdrd5/r7fqe7hrqobA+tqWDtZ2ZMyVjbvWYy5UR4aZsZgBjh9Y3gTcNbB8FHAq8NEktwNP\nBbZ78pWkjjVEmiDDNBY3ANNJTkpyGHA+sH3/k1XVq6pjq+rEqjoR+ASwdfbxUUkTyxoiTZBFG4uq\n2gdcDFwP7AaurapdSS5LsnWlE5Q03qwh0mQZ5hwLqmoHsGPWY6+cZ+xZo6claT2xhkiTw0/elCRJ\nzdhYSJKkZmwsJElSMzYWkiSpGRsLSZLUjI2FJElqxsZCkiQ1Y2MhSZKasbGQJEnN2FhIkqRmbCwk\nSVIzNhaSJKkZGwtJktSMjYUkSWrGxkKSJDUzVGORZEuSW5PsTXLJHM+/JMktST6T5H8leXT7VCWN\nK2uINDkWbSySbACuAM4DTgEuSHLKrGE3Apur6gnAdcBrWicqaTxZQ6TJkqpaeEByBrCtqs7tli8F\nqKrL5xl/GvCGqjpz8PFer3dgRXv27Bkxbengumj31Mgxrjy51yCTpZuenj5wf2pqKiu9PmvIwkbd\nlg7WdqTJtlAdGeZQyHHAnQPLM91j83k+8BdLyE/S+mYNkSbIxiHGzLVHM+c0R5LnApuBpy8UcPPm\nzUOsdm47d+4cOcZqxTXmOo65e/Q95qWus9X77/VWfQ93TdUQWF/b0sHajoy5snHXesyF6sgwjcUM\ncPzA8ibgrtmDkpwD/Cbw9Kr6zhJzlLR+WUOkCTLMoZAbgOkkJyU5DDgf2D44oDsm+kZga1Xd2z5N\nSWPMGiJNkEUbi6raB1wMXA/sBq6tql1JLkuytRv2u8BDgXcn+XSS7fOEkzRhrCHSZBnmUAhVtQPY\nMeuxVw7cP6dxXpLWEWuINDn85E1JktSMjYUkSWrGxkKSJDUz1DkW0jg77eqFPieg+9TDRT5L4MYL\npxd8XpLU54yFJElqxsZCkiQ1Y2MhSZKasbGQJEnN2FhIkqRmbCwkSVIzXm4qrRELXxYLw1wa62Wx\nK8f/H2k4zlhIkqRmbCwkSVIzHgqRpHXGT5vVweSMhSRJamaoGYskW4D/BmwA3lRV/2XW8/8MeBvw\nz4H7gJ+rqtuXm5TdtrS+rHYNkXTwLNpYJNkAXAE8A5gBbkiyvapuGRj2fOD+qnpskvOBVwM/txIJ\nL9dKnNHtWeLt2VSuP+ulhkgaTqpq4QHJGcC2qjq3W74UoKouHxhzfTfm40k2AvcAP1IDwXu93sIr\nkrTqpqamstLrsIZI69vsOjLMORbHAXcOLM90j805pqr2AT3gYctPU9I6Yg2RJsgwjcVcezSz9xyG\nGSNpMllDpAkyzMmbM8DxA8ubgLvmGTPTTWNOAV8dHLAaU66S1iRriDRBhpmxuAGYTnJSksOA84Ht\ns8ZsB36hu/8c4MO12MkbkiaFNUSaIIs2Ft3xzouB64HdwLVVtSvJZUm2dsOuAh6WZC/wEuCSlUo4\nyZYktybZm2Tk9SR5c5J7k9zcIr8u5vFJPpJkd5JdSX61QczDk/xtkpu6mL/VItcu9oYkNyZ5f8OY\ntyf5bJJPJ9nZKOYxSa5L8rnuZ3vGiPF+rMtv/+3rSV7cIM9f6/6Pbk5yTZLDG8T81S7erhY5rqb1\nXkO6mNaRxnXEGjLGNaSqxuZG/xr4vwceAxwG3AScMmLMpwGnAzc3zPORwOnd/aOAzzfIM8BDu/uH\nAp8Entoo35cA7wTe3/BncDtwbOP//7cCL+juHwYc03jbugd49IhxjgO+ADykW74WeN6IMU8FbgaO\noH/48i+B6ZY/20m5rUQN6eJaRxrXEWvI+NaQcfvkzScDe6vqtqr6LvAu4NmjBKyqjzHrWO6oquru\nqvpUd/8b9PfSZp8Fv9SYVVXf7BYP7W4jTxUn2QT8FPCmUWOtpCRH0y/eVwFU1Xer6msNV3E28PdV\n9cUGsTYCD+nOFTiCHzyfYKlOBj5RVf9Y/b3/vwJ+ZsSYk6p5DQHryDjUEWvI6tWQcWsshrlsbU1J\nciJwGv09g1FjbUjyaeBe4ENVNXJM4PXAy4F/ahBrUAEfTPJ3SV7YIN5jgK8Af9xNt74pyZEN4u53\nPnDNqEGq6kvAfwXuAO4GelX1wRHD3gw8LcnDkhwBPIsHnwyp4Y1dDYGJrSPWkDGtIePWWIzVJWlJ\nHgr8KfDiqvr6qPGq6vtV9ST6Z9U/OcmpI+b3r4B7q+rvRs1tDmdW1enAecCLkjxtxHgb6U81/1FV\nnQZ8i0bH4bsTCrcC724Q64fo7wGfBDwKODLJc0eJWVW76X8S5YeAD9Cfvt83YqqTaqxqCEx0HbGG\njGkNGbfGYpjL1taEJIfSLwbvqKr3tIzdTd99FNgyYqgzga1Jbqc/JfyTSd4+YkwAququ7t97gffS\nn4IexQwwM7B3dR39ItHCecCnqurLDWKdA3yhqr5SVd8D3gP8+KhBq+qqqjq9qp5Gf8p9sc+T19zG\npobAZNcRa8j41pBxayyGuWztoEsS+sfxdlfV7zWK+SNJjunuP4T+xve5UWJW1aVVtamqTqT/s/xw\nVY3UGXf5HZnkqP33gWfSn4obJdd7gDuT/Fj30NnALQu8ZCkuoMEUZucO4KlJjui2g7PpHxsfSZKH\nd/+eAPws7fKdNGNRQ2Cy64g1ZLxryFDfbrpWVNW+JPsvW9sAvLmqdo0SM8k1wFnAsUlmgFdV1VUj\npnomcCHw2e5YJsBvVNWOEWI+Enhr+l/odAj9S/aaXR7a2COA9/Z/J9gIvLOqPtAg7i8D7+j+INwG\n/OKoAbvjjc8AfmnUWABV9ckk1wGfoj/VeCPwPxqE/tMkDwO+B7yoqu5vEHPirEQNAevICrCGjHEN\nWfRLyCRJkoY1bodCJEnSGmZjIUmSmrGxkCRJzdhYSJKkZmwsJElSMzYWkiSpGRsLSZLUjI2FJElq\nxsZCkiQ1Y2MhSZKasbGQJEnN2FhIkqRmbCwkSVIzNhaSJKkZGwtJktSMjYUkSWrGxkKSJDVjYyFJ\nkpqxsZAkSc3YWEiSpGZsLCRJUjM2FpIkqRkbC0mS1IyNhSRJasbGQpIkNWNjIUmSmrGxkCRJzdhY\nSJKkZmwsJElSMzYWkiSpGRsLSZLUjI2FJElqxsZiDUuyK8lZB3H9JyT5ZpINBysHSctnDdHBYGOx\nhlXV46vqowBJtiV5+0quL8ntSc4ZWP8dVfXQqvr+CqzrPyX5bJJ9SbbNeu6sJP/UFaT9t18YeP6H\nk7w3ybeSfDHJv22dn7QeWEPmrSEXJ9mZ5DtJ3tI6t0m38WAnoNWRZGNV7TvYeQzYC7wc+HfzPH9X\nVW2a57krgO8CjwCeBPx5kpuqalf7NCXBuqshdwG/DZwLPGQFcptozlisYfu7/yRbgN8Afq7rvG/q\nnp9KclWSu5N8Kclv759yTPK8JH+T5HVJvgpsS/KjST6c5L4k/5DkHUmO6cZfDZwA/M9uHS9PcmKS\nSrKxG/OoJNuTfDXJ3iQXDeS6Lcm1Sd6W5BvdFOzm+d5bVb21qv4C+MYSfyZHAv8v8P9V1Ter6q+B\n7cCFS4kjTQJryLyvfU9VvQ+4b6mv1eJsLMZAVX0A+B3gT7ppxSd2T70V2Ac8FjgNeCbwgoGXPgW4\nDXg48J+BAJcDjwJOBo4HtnXruBC4A/jpbh2vmSOVa4CZ7vXPAX4nydkDz28F3gUcQ/+P/RtGeNsP\nT/LlJF/oCtuR3eOPA75fVZ8fGHsT8PgR1iWta9aQB9UQrTAbizGV5BHAecCLq+pbVXUv8Drg/IFh\nd1XVH1TVvqr6dlXtraoPVdV3quorwO8BTx9yfccDPwH8x6r6v1X1aeBNPHim4K+rakd3PPVq4Ilz\nhBrG5+gf4ngk8JPAP+9yBXgo0Js1vgcctcx1SRNpgmuIVpjnWIyvRwOHAncn2f/YIcCdA2MG75Pk\n4cDvA/+C/h/iQ4D7h1zfo4CvVtXgtOMXgcGpynsG7v8jcPhyjstW1T0Dsb6Q5OXAnwO/BHwTOHrW\nS45mGdOh0oSb1BqiFeaMxfioWct3At8Bjq2qY7rb0VX1+AVec3n32BOq6mjgufSnNucbP+gu4IeT\nDM4MnAB8aSlvYpmKB/L8PLAxyfTA808EPHFTWpg1RKvCxmJ8fBk4MckhAFV1N/BB4LVJjk5ySHdi\n1ULTkkfR3+P/WpLjgJfNsY7HzPXCqroT+D/A5UkOT/IE4PnAO5bzZpIcmuRw+tvgxi7m/pPGzkr/\n+vd006f/BfizLo9vAe8BLktyZJIzgWfTnzaVND9ryAOv3di9dgOwoXutM/iN2FiMj3d3/96X5FPd\n/Z8HDgNuoT8deR39Y4rz+S3gdPrnJPw5/T/Qgy4HXpHka0leOsfrLwBOpL/n8V7gVVX1oaW/FQCu\nBL7dxfzN7v7+Y62nAx8HvkW/EN0M/MrAa/8D/UvE7qV/Mti/91JTaVHWkAe8oht/Cf1Zl293j6mB\nVC00cyVJkjQ8ZywkSVIzizYWSd6c5N4kN8/zfJL8fvdhJ59Jcnr7NCWNM+uINDmGmbF4C7BlgefP\nA6a72wuBPxo9LUnrzFuwjkgTYdGzYKvqY0lOXGDIs4G3Vf9kjU8kOSbJI7szjg/o9XqezCGtMVNT\nU6tyCV6LOmINkdam2XWkxTkWx/HgD1GZ6R6TpGFZR6R1okVjMdcej3sWkpbCOiKtEy0+EGSG/hfR\n7LeJ/jXK85qamlr2ynbu3AnA5s3zfundmolrTGO21Cpurzf7q1bWhCXVkVFqCIzP/7sx137MlYq7\n1mMuVEdazFhsB36+O6v7qUBv9vkVkrQI64i0Tiw6Y5HkGuAs4NgkM8Cr6H9xDVX134EdwLOAvfS/\nNOYXVypZSePJOiJNjmGuCrlgkecLeFGzjCStO9YRaXL4yZuSJKkZGwtJktSMjYUkSWrGxkKSJDXT\n4nMsJGlNOe3qPYuM6D4HY/f84268cLpdQtIEsbHQmrLwH4TF/xiAfxAk6WDyUIgkSWrGxkKSJDVj\nYyFJkpqxsZAkSc3YWEiSpGZsLCRJUjM2FpIkqRkbC0mS1IyNhSRJamaoxiLJliS3Jtmb5JI5nj8h\nyUeS3JjkM0me1T5VSePKGiJNjkUbiyQbgCuA84BTgAuSnDJr2CuAa6vqNOB84A9bJyppPFlDpMmS\nqlp4QHIGsK2qzu2WLwWoqssHxrwRuK2qXt2Nf21V/fhgnF6vd2BFe/Ys9gVBmlQX7Z4aOcaVJ/ca\nZLJ+TU8/8F0qU1NTWen1HYwa4nYkrayF6sgwX0J2HHDnwPIM8JRZY7YBH0zyy8CRwDnLSVTSumQN\nkSbIMI3FXHs0s6c5LgDeUlWv7fY2rk5yalX901wBN2/evMQ0H7Bz586RY6xWXGMuI+Yi31w6jKWs\nc02991WK2+ut+p746teQVd6OYHy2JWOu3d/NcYq5UB0Z5uTNGeD4geVNwF2zxjwfuBagqj4OHA4c\nu6QsJa1X1hBpggzTWNwATCc5Kclh9E+s2j5rzB3A2QBJTqZfFL7SMlFJY8saIk2QRRuLqtoHXAxc\nD+ymf+b2riSXJdnaDft14KIkNwHXAM+rxc4KlTQRrCHSZBnmHAuqagewY9Zjrxy4fwtwZtvUJK0X\n1hBpcvjJm5IkqRkbC0mS1IyNhSRJasbGQpIkNWNjIUmSmrGxkCRJzdhYSJKkZmwsJElSMzYWkiSp\nGRsLSZLUjI2FJElqxsZCkiQ1Y2MhSZKasbGQJEnNDNVYJNmS5NYke5NcMs+Yf5PkliS7kryzbZqS\nxpk1RJocGxcbkGQDcAXwDGAGuCHJ9qq6ZWDMNHApcGZV3Z/k4SuVsKTxYg2RJsswMxZPBvZW1W1V\n9V3gXcCzZ425CLiiqu4HqKp726YpaYxZQ6QJkqpaeEDyHGBLVb2gW74QeEpVXTww5n3A54EzgQ3A\ntqr6wGCcXq93YEV79uxp9ga0vly0e2rkGFee3GuQyfo1PT194P7U1FRWen0Ho4a4HUkra6E6suih\nEGCuwjO7G9kITANnAZuA/53k1Kr62pIylbQeWUOkCTJMYzEDHD+wvAm4a44xn6iq7wFfSHIr/SJx\nw1wBN2/evIxU+3bu3DlyjNWKa8xlxNw9+mzWUta5pt77KsXt9VZ9T3z1a8gqb0cwPtuSMdfu7+Y4\nxVyojgxzjsUNwHSSk5IcBpwPbJ815n3AvwRIcizwOOC2ZWUrab2xhkgTZNHGoqr2ARcD1wO7gWur\naleSy5Js7YZdD9yX5BbgI8DLquq+lUpa0viwhkiTZZhDIVTVDmDHrMdeOXC/gJd0N0l6EGuINDn8\n5E1JktSMjYUkSWrGxkKSJDVjYyFJkpqxsZAkSc3YWEiSpGZsLCRJUjM2FpIkqRkbC0mS1IyNhSRJ\nasbGQpIkNWNjIUmSmrGxkCRJzdhYSJKkZmwsJElSM0M1Fkm2JLk1yd4klyww7jlJKsnmdilKGnfW\nEGlyLNpYJNkAXAGcB5wCXJDklDnGHQX8CvDJ1klKGl/WEGmypKoWHpCcAWyrqnO75UsBquryWeNe\nD/wl8FLgpVW1c/D5Xq93YEV79uxpkrzWn4t2T40c48qTew0yWb+mp6cP3J+amspKr+9g1BC3I2ll\nLVRHhjkUchxw58DyTPfYAUlOA46vqvcvP01J65Q1RJogG4cYM9cezYE9hySHAK8DnjfsSjdvXv7h\n0507d44cY7XiGnMZMXePPpu1lHWuqfe+SnF7vVXfE1/9GrLK2xGMz7ZkzLX7uzlOMReqI8PMWMwA\nxw8sbwLuGlg+CjgV+GiS24GnAts9+UpSxxoiTZBhGosbgOkkJyU5DDgf2L7/yarqVdWxVXViVZ0I\nfALYOvv4qKSJZQ2RJsiijUVV7QMuBq4HdgPXVtWuJJcl2brSCUoab9YQabIMc44FVbUD2DHrsVfO\nM/as0dOStJ5YQ6TJ4SdvSpKkZmwsJElSMzYWkiSpGRsLSZLUjI2FJElqxsZCkiQ1Y2MhSZKasbGQ\nJEnN2FhIkqRmbCwkSVIzNhaSJKkZGwtJktSMjYUkSWrGxkKSJDVjYyFJkpoZqrFIsiXJrUn2Jrlk\njudfkuSWJJ9J8r+SPLp9qpJ7sjztAAAHNUlEQVTGlTVEmhyLNhZJNgBXAOcBpwAXJDll1rAbgc1V\n9QTgOuA1rROVNJ6sIdJkSVUtPCA5A9hWVed2y5cCVNXl84w/DXhDVZ05+Hiv1zuwoj179oyYttar\ni3ZPjRzjypN7DTJZv6anpw/cn5qaykqv72DUELcjaWUtVEeGORRyHHDnwPJM99h8ng/8xRLyk7S+\nWUOkCbJxiDFz7dHMOc2R5LnAZuDpCwXcvHnzEKud286dO0eOsVpxjbmMmLtHn81ayjrX1Htfpbi9\n3qrvia9+DVnl7QjGZ1sy5tr93RynmAvVkWEaixng+IHlTcBdswclOQf4TeDpVfWdJeYoaf2yhkgT\nZJhDITcA00lOSnIYcD6wfXBAd0z0jcDWqrq3fZqSxpg1RJogizYWVbUPuBi4HtgNXFtVu5JclmRr\nN+x3gYcC707y6STb5wknacJYQ6TJMsyhEKpqB7Bj1mOvHLh/TuO8JK0j1hBpcvjJm5IkqRkbC0mS\n1IyNhSRJasbGQpIkNWNjIUmSmrGxkCRJzdhYSJKkZmwsJElSMzYWkiSpGRsLSZLUjI2FJElqxsZC\nkiQ1Y2MhSZKaGerbTaW5nHb1ngWener/s3v+MTdeON02IUnSQWdjoXVv1AYIbIK02HYENtM62NbK\nNjrUoZAkW5LcmmRvkkvmeP6fJfmT7vlPJjlx5MwkrRvWEGlyLNpYJNkAXAGcB5wCXJDklFnDng/c\nX1WPBV4HvLp1opLGkzVEmiypqoUHJGcA26rq3G75UoCqunxgzPXdmI8n2QjcA/xIDQTv9XoLr0jS\nqpuamspKr8MaIq1vs+vIMIdCjgPuHFie6R6bc0xV7QN6wMOWn6akdcQaIk2QYRqLufZoZu85DDNG\n0mSyhkgTZJirQmaA4weWNwF3zTNmppvGnAK+OjhgNaZcJa1J1hBpggwzY3EDMJ3kpCSHAecD22eN\n2Q78Qnf/OcCHa7GTNyRNCmuINEEWnbGoqn1JLgauBzYAb66qXUkuA3ZW1XbgKuDqJHvp72Wcv5JJ\nSxof1hBpwlTVWN2ALcCtwF7gkgbx3gzcC9zcMMfjgY8Au4FdwK82iHk48LfATV3M32qY7wbgRuD9\nDWPeDnwW+DT9Px4tYh4DXAd8rvvZnjFivB/r8tt/+zrw4gZ5/lr3f3QzcA1weIOYv9rF29Uix0m+\nta4hXUzrSOM6Yg0Z3xqyYoFXJNn+hvv3wGOAw7pfjlNGjPk04PTGBeGRwOnd/aOAzzfIM8BDu/uH\nAp8Entoo35cA71yBxuLYxv//bwVe0N0/DDim8bZ1D/DoEeMcB3wBeEi3fC3wvBFjntoVhCPozzL+\nJTDd8mc7KbeVqCFdXOtI4zpiDRnfGjJuX0L2ZGBvVd1WVd8F3gU8e5SAVfUxZp0kNqqquruqPtXd\n/wb9znj25XVLjVlV9c1u8dDuNvIx6CSbgJ8C3jRqrJWU5Gj6xfsqgKr6blV9reEqzgb+vqq+2CDW\nRuAh3UmIR/CDJyou1cnAJ6rqH6t/KeZfAT8zYsxJ1byGgHVkHOqINWT1asi4NRbDXA+/pnQfTXwa\n/T2DUWNtSPJp+lOuH6qqkWMCrwdeDvxTg1iDCvhgkr9L8sIG8R4DfAX44yQ3JnlTkiMbxN3vfPpT\njiOpqi8B/xW4A7gb6FXVB0cMezPwtCQPS3IE8CwefJWFhjd2NQQmto5YQ8a0hoxbYzFW17oneSjw\np/SPZ3191HhV9f2qehL9y/WenOTUEfP7V8C9VfV3o+Y2hzOr6nT6H+P8oiRPGzHeRvpTzX9UVacB\n3wJ+4DsnlqO7UmEr8O4GsX6I/h7wScCjgCOTPHeUmFW1m/5HXH8I+AD96ft9I6Y6qcaqhsBE1xFr\nyJjWkHFrLIa5Hn5NSHIo/WLwjqp6T8vY3fTdR+mfhDaKM4GtSW6nPyX8k0nePmJMAKrqru7fe4H3\n0p+CHsUMMDOwd3Ud/SLRwnnAp6rqyw1inQN8oaq+UlXfA94D/PioQavqqqo6vaqeRn/KfbGvMdTc\nxqaGwGTXEWvI+NaQcWsshrke/qBLEvrH8XZX1e81ivkjSY7p7j+E/sb3uVFiVtWlVbWpqk6k/7P8\ncFWN1Bl3+R2Z5Kj994Fn0p+KGyXXe4A7k/xY99DZwC0jJfqAC2gwhdm5A3hqkiO67eBs+sfGR5Lk\n4d2/JwA/S7t8J81Y1BCY7DpiDRnvGjLMJ2+uGTXP9fCjxExyDXAWcGySGeBVVXXViKmeCVwIfLY7\nlgnwG1W1Y4SYjwTe2n1T5CHAtVX1/hHzXCmPAN7b/51gI/DOqvpAg7i/DLyj+4NwG/CLowbsjjc+\nA/ilUWMBVNUnk1wHfIr+VOONwP9oEPpPkzwM+B7woqq6v0HMibMSNQSsIyvAGjLGNWTRbzeVJEka\n1rgdCpEkSWuYjYUkSWrGxkKSJDVjYyFJkpqxsZAkSc3YWEiSpGZsLCRJUjP/P4HwKpSuztd6AAAA\nAElFTkSuQmCC\n",
      "text/plain": [
       "<matplotlib.figure.Figure at 0x19ec1d75400>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "with figsize(y=5.5):\n",
    "    for i in range (4):\n",
    "        random.seed(3)\n",
    "        plt.subplot(221+i)\n",
    "        train_filter(148+i, kernel=[.1, .8, .1], \n",
    "                     sensor_accuracy=.8,\n",
    "                     move_distance=4, do_print=False)\n",
    "        plt.title ('iteration {}'.format(148+i))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We can see that there was a problem on iteration 149 as the confidence degrades. But within a few iterations the filter is able to correct itself and regain confidence in the estimated position."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Bayes Theorem"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We developed the math in this chapter merely by reasoning about the information we have at each moment. In the process we discovered [*Bayes Theorem*](https://en.wikipedia.org/wiki/Bayes%27_theorem). Bayes theorem tells us how to compute the probability of an event given previous information. That is exactly what we have been doing in this chapter. With luck our code should match the Bayes Theorem equation! \n",
    "\n",
    "We implemented the `update()` function with this probability calculation:\n",
    "\n",
    "$$ \\mathtt{posterior} = \\frac{\\mathtt{likelihood}\\times \\mathtt{prior}}{\\mathtt{normalization}}$$ \n",
    "\n",
    "To review, the *prior* is the probability of something happening before we include the probability of the measurement (the *likelihood*) and the *posterior* is the probability we compute after incorporating the information from the measurement.\n",
    "\n",
    "Bayes theorem is\n",
    "\n",
    "$$P(A \\mid B) = \\frac{P(B \\mid A)\\, P(A)}{P(B)}$$\n",
    "\n",
    "If you are not familiar with this notation, let's review. $P(A)$ means the probability of event $A$. If $A$ is the event of a fair coin landing heads, then $P(A) = 0.5$.\n",
    "\n",
    "$P(A \\mid B)$ is called a [*conditional probability*](https://en.wikipedia.org/wiki/Conditional_probability). That is, it represents the probability of $A$ happening *if* $B$ happened. For example, it is more likely to rain today if it also rained yesterday because rain systems usually last more than one day. We'd write the probability of it raining today given that it rained yesterday as $P(\\mathtt{rain\\_today} \\mid \\mathtt{rain\\_yesterday})$.\n",
    "\n",
    "In the Bayes theorem equation above $B$ is the *evidence*, $P(A)$ is the *prior*, $P(B \\mid A)$ is the *likelihood*, and $P(A \\mid B)$ is the *posterior*. By substituting the mathematical terms with the corresponding words  you can see that Bayes theorem matches out update equation. Let's rewrite the equation in terms of our problem. We will use $x_i$ for the position at *i*, and $Z$ for the measurement. Hence, we want to know $P(x_i \\mid Z)$, that is, the probability of the dog being at $x_i$ given the measurement $Z$. \n",
    "\n",
    "So, let's plug that into the equation and solve it.\n",
    "\n",
    "$$P(x_i \\mid Z) = \\frac{P(Z \\mid x_i) P(x_i)}{P(Z)}$$\n",
    "\n",
    "That looks ugly, but it is actually quite simple. Let's figure out what each term on the right means. First is $P(Z \\mid x_i)$. This is the the likelihood, or the probability for the measurement at every cell $x_i$. $P(x_i)$ is the *prior* - our belief before incorporating the measurements. We multiply those together. This is just the unnormalized multiplication in the `update()` function:\n",
    "\n",
    "```python\n",
    "def update(likelihood, prior):\n",
    "    posterior = prior * likelihood   # P(Z|x)*P(x)\n",
    "    return normalize(posterior)\n",
    "```\n",
    "\n",
    "The last term to consider is the denominator $P(Z)$. This is the probability of getting the measurement $Z$ without taking the location into account. It is often called the *evidence*. We compute that by taking the sum of $x$, or `sum(belief)` in the code. That is how we compute the normalization! So, the `update()` function is doing nothing more than computing Bayes theorem.\n",
    "\n",
    "The literature often gives you these equations in the form of integrals. After all, an integral is just a sum over a continuous function. So, you might see Bayes' theorem written as\n",
    "\n",
    "$$P(A \\mid B) = \\frac{P(B \\mid A)\\, P(A)}{\\int P(B \\mid A_j) P(A_j) \\mathtt{d}A_j}\\cdot$$\n",
    "\n",
    "In practice the denominator can be fiendishly difficult to solve analytically (a recent opinion piece for the Royal Statistical Society [called it](http://www.statslife.org.uk/opinion/2405-we-need-to-rethink-how-we-teach-statistics-from-the-ground-up) a \"dog's breakfast\" [8].  Filtering textbooks are filled with integral laden equations which you cannot be expected to solve. We will learn more techniques to handle this in the **Particle Filters** chapter. Until then, recognize that in practice it is just a normalization term over which we can sum. What I'm trying to say is that when you are faced with a page of integrals, just think of them as sums, and relate them back to this chapter, and often the difficulties will fade. Ask yourself \"why are we summing these values\", and \"why am I dividing by this term\". Surprisingly often the answer is readily apparent."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {
    "collapsed": true
   },
   "source": [
    "## Total Probability Theorem\n",
    "\n",
    "We now know the formal mathematics behind the `update()` function; what about the `predict()` function? `predict()` implements the [*total probability theorem*](https://en.wikipedia.org/wiki/Law_of_total_probability). Let's recall what `predict()` computed. It computed the probability of being at any given position given the probability of all the possible movement events. Let's express that as an equation. The probability of being at any position $i$ at time $t$ can be written as $P(X_i^t)$. We computed that as the sum of the prior at time $t-1$ $P(X_j^{t-1})$ multiplied by the probability of moving from cell $x_j$ to $x_i$. That is\n",
    "\n",
    "$$P(X_i^t) = \\sum_j P(X_j^{t-1})  P(x_i | x_j)$$\n",
    "\n",
    "That equation is called the *total probability theorem*. Quoting from Wikipedia [6] \"It expresses the total probability of an outcome which can be realized via several distinct events\". I could have given you that equation and implemented `predict()`, but your chances of understanding why the equation works would be slim. As a reminder, here is the code that computes this equation\n",
    "\n",
    "```python\n",
    "for i in range(N):\n",
    "    for k in range (kN):\n",
    "        index = (i + (width-k) - offset) % N\n",
    "        result[i] += prob_dist[index] * kernel[k]\n",
    "```"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Summary\n",
    "\n",
    "The code is very short, but the result is impressive! We have implemented a form of a Bayesian filter. We have learned how to start with no information and derive information from noisy sensors. Even though the sensors in this chapter are very noisy (most sensors are more than 80% accurate, for example) we quickly converge on the most likely position for our dog. We have learned how the predict step always degrades our knowledge, but the addition of another measurement, even when it might have noise in it, improves our knowledge, allowing us to converge on the most likely result.\n",
    "\n",
    "This book is mostly about the Kalman filter. The math it uses is different, but the logic is exactly the same as used in this chapter. It uses Bayesian reasoning to form estimates from a combination of measurements and process models. \n",
    "\n",
    "**If you can understand this chapter you will be able to understand and implement Kalman filters.** I cannot stress this enough. If anything is murky, go back and reread this chapter and play with the code. The rest of this book will build on the algorithms that we use here. If you don't understand why this filter works you will have little success with the rest of the material. However, if you grasp the fundamental insight - multiplying probabilities when we measure, and shifting probabilities when we update leads to a converging solution - then after learning a bit of math you are ready to implement a Kalman filter."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## References\n",
    "\n",
    " * [1] D. Fox, W. Burgard, and S. Thrun. \"Monte carlo localization: Efficient position estimation for mobile robots.\" In *Journal of Artifical Intelligence Research*, 1999.\n",
    " \n",
    " http://www.cs.cmu.edu/afs/cs/project/jair/pub/volume11/fox99a-html/jair-localize.html\n",
    "\n",
    "\n",
    " * [2] Dieter Fox, et. al. \"Bayesian Filters for Location Estimation\". In *IEEE Pervasive Computing*, September 2003.\n",
    " \n",
    " http://swarmlab.unimaas.nl/wp-content/uploads/2012/07/fox2003bayesian.pdf\n",
    " \n",
    " \n",
    " * [3] Sebastian Thrun. \"Artificial Intelligence for Robotics\".\n",
    " \n",
    " https://www.udacity.com/course/cs373\n",
    " \n",
    " \n",
    " * [4] Khan Acadamy. \"Introduction to the Convolution\"\n",
    " \n",
    " https://www.khanacademy.org/math/differential-equations/laplace-transform/convolution-integral/v/introduction-to-the-convolution\n",
    " \n",
    " \n",
    "* [5] Wikipedia. \"Convolution\"\n",
    "\n",
    " http://en.wikipedia.org/wiki/Convolution\n",
    "\n",
    "* [6] Wikipedia. \"Law of total probability\"\n",
    "\n",
    "  http://en.wikipedia.org/wiki/Law_of_total_probability\n",
    "  \n",
    "* [7] Wikipedia. \"Time Evolution\"\n",
    "\n",
    " https://en.wikipedia.org/wiki/Time_evolution\n",
    " \n",
    "* [8] We need to rethink how we teach statistics from the ground up\n",
    " \n",
    " http://www.statslife.org.uk/opinion/2405-we-need-to-rethink-how-we-teach-statistics-from-the-ground-up"
   ]
  }
 ],
 "metadata": {
  "anaconda-cloud": {},
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.3"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 1
}