diff --git a/02-Discrete-Bayes.ipynb b/02-Discrete-Bayes.ipynb
index 9412ed7..fcd2632 100644
--- a/02-Discrete-Bayes.ipynb
+++ b/02-Discrete-Bayes.ipynb
@@ -361,7 +361,7 @@
"\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 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%.\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 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",
@@ -1162,7 +1162,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -1274,7 +1274,7 @@
"\n",
"Lets 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 correct that in a moment.\n",
"\n",
- "Let's look at that in Python code. Here I use the variable `z` to denote the measurement as that is a customary choice in the literature. "
+ "Let's look at that in Python code. Here I use the variable `z` to denote the measurement as that is a customary choice in the literature. I try to write this book from the perspective of a programmer who uses meaningful names, not a mathematician that uses one letter variables, but one goal I have is to have you reading the literature, so you need to become familiar with the naming conventions."
]
},
{
@@ -2059,7 +2059,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -2123,9 +2123,13 @@
"from filterpy.discrete_bayes import normalize\n",
"```\n",
"\n",
- "It is a bit odd to be talking about \"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 computing the scale factor from that. \n",
+ "It is a bit odd to be talking about \"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 computing the scale factor from that. The equation for that is\n",
"\n",
- "Also, the for loop is cumbersome. NumPy lets you index arrays with boolean arrays. You create a boolean array with logical operators:"
+ "$$scale = \\frac{prob_{correct}}{prob_{incorrect}} = \\frac{prob_{correct}} {1-prob_{correct}}$$\n",
+ "\n",
+ "\n",
+ "\n",
+ "Also, the `for` loop is cumbersome. NumPy lets you index arrays with boolean arrays. You create a boolean array with logical operators:"
]
},
{
@@ -2160,12 +2164,14 @@
"```python\n",
"belief[hall==z] *= scale\n",
"```\n",
- "and only the elements which equal `z` will be multiplied by `scale`. This is roughly 100 times faster than the `for` loop because the operation is implemented in C, not Python."
+ "and only the elements which equal `z` will be multiplied by `scale`. This is roughly 100 times faster than the `for` loop because the operation is performed in C, not Python.\n",
+ "\n",
+ "Here is our improved version:"
]
},
{
"cell_type": "code",
- "execution_count": 11,
+ "execution_count": 10,
"metadata": {
"collapsed": false,
"scrolled": true
@@ -2947,7 +2953,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -2981,9 +2987,13 @@
"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. Another term is the [*likelihood*](https://en.wikipedia.org/wiki/Likelihood_function) - this is the probability for the [*evidence*](https://en.wikipedia.org/wiki/Marginal_likelihood) (the measurement in this case) being true. That gives us this equation:\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",
- "$$\\mathtt{posterior} = \\frac{\\mathtt{prior}\\times \\mathtt{likelihood}}{\\mathtt{normalization}}$$ \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. This is not a probability distribution because it does not sum to one. Likelihoods are not probability distributions. \n",
+ "\n",
+ "The combination of these gives us 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",
@@ -2992,7 +3002,7 @@
},
{
"cell_type": "code",
- "execution_count": 12,
+ "execution_count": 11,
"metadata": {
"collapsed": false
},
@@ -3018,14 +3028,14 @@
" 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.\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": 13,
+ "execution_count": 12,
"metadata": {
"collapsed": false
},
@@ -3037,7 +3047,7 @@
" 0.0625, 0.188, 0.0625])"
]
},
- "execution_count": 13,
+ "execution_count": 12,
"metadata": {},
"output_type": "execute_result"
}
@@ -3049,7 +3059,10 @@
" \"\"\" compute likelihood that a measurement matches\n",
" positions in the hallway.\"\"\"\n",
" \n",
- " scale = z_prob / (1. - z_prob)\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",
@@ -3067,11 +3080,825 @@
"\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, the 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 lets go ahead and program the math.\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 lets 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 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 the move 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."
+ "I hope 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": {
+ "collapsed": false
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\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 = $('');\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",
+ " 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",
+ " this.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",
+ " '');\n",
+ " var titletext = $(\n",
+ " '');\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 = $('');\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 = $('');\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 rubberband = $('');\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);\n",
+ " canvas.attr('height', height);\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 = $('')\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 = $('');\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 = $('');\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 = $('');\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 = $('');\n",
+ "\n",
+ " var fmt_picker = $('');\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",
+ " '', {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 = $('');\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'];\n",
+ " var y0 = fig.canvas.height - msg['y0'];\n",
+ " var x1 = msg['x1'];\n",
+ " var y1 = fig.canvas.height - msg['y1'];\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;\n",
+ " var y = canvas_pos.y;\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(\"\");\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",
+ " 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('
');\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 dataURL = this.canvas.toDataURL();\n",
+ " this.cell_info[1]['text/html'] = '
';\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 = $('')\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 = $('');\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 = $('');\n",
+ " nav_element.append(status_bar);\n",
+ " this.message = status_bar[0];\n",
+ "\n",
+ " // Add the close button to the window.\n",
+ " var buttongrp = $('');\n",
+ " var 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= 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": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "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",
+ " belief[:] = result # copy back to original array\n",
+ " \n",
+ "with interactive_plot():\n",
+ " belief = np.array([.35, .1, .2, .3, 0, 0, 0, 0, 0, .05])\n",
+ " plt.subplot(121)\n",
+ " bp.bar_plot(belief, title='Before prediction', ylim=(0, .4))\n",
+ "\n",
+ " perfect_predict(belief, 1)\n",
+ " plt.subplot(122)\n",
+ " bp.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."
]
},
{
@@ -3848,7 +4675,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -3859,31 +4686,15 @@
}
],
"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",
- " belief[:] = result # copy back to original array\n",
- " \n",
- "with interactive_plot():\n",
- " belief = np.array([.35, .1, .2, .3, 0, 0, 0, 0, 0, .05])\n",
- " plt.subplot(121)\n",
- " bp.bar_plot(belief, title='Before prediction', ylim=(0, .4))\n",
+ "import time\n",
"\n",
- " perfect_predict(belief, 1)\n",
- " plt.subplot(122)\n",
- " bp.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."
+ "fig = plt.figure()\n",
+ "for _ in range(50):\n",
+ " perfect_predict(belief, 1) # Simon takes one step to the right\n",
+ " plt.cla()\n",
+ " bp.bar_plot(belief, ylim=(0, .4))\n",
+ " fig.canvas.draw()\n",
+ " time.sleep(0.05)"
]
},
{
@@ -4709,7 +5520,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -5518,7 +6329,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -5548,7 +6359,7 @@
},
{
"cell_type": "code",
- "execution_count": 39,
+ "execution_count": 17,
"metadata": {
"collapsed": false
},
@@ -6320,7 +7131,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -6333,7 +7144,8 @@
"name": "stdout",
"output_type": "stream",
"text": [
- "Final Belief: [ 0.104 0.103 0.101 0.099 0.097 0.096 0.097 0.099 0.101 0.103]\n"
+ "Final Belief: [ 0.104 0.103 0.101 0.0987 0.0967 0.0959 0.0967 0.0987 0.101\n",
+ " 0.103]\n"
]
}
],
@@ -6364,7 +7176,7 @@
"And, if you are viewing this in Github or nbviewer, here is an animation of that output.\n",
"
\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. This will allow you to run all of the cells and see the animations."
+ "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."
]
},
{
@@ -6373,7 +7185,7 @@
"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. \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. Again, 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",
@@ -6392,7 +7204,7 @@
},
{
"cell_type": "code",
- "execution_count": 40,
+ "execution_count": 18,
"metadata": {
"collapsed": true
},
@@ -6415,7 +7227,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "This illustrates the algorithm, but it is 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",
+ "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",
@@ -6426,7 +7238,7 @@
},
{
"cell_type": "code",
- "execution_count": 44,
+ "execution_count": 19,
"metadata": {
"collapsed": false
},
@@ -7198,7 +8010,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -7228,7 +8040,7 @@
},
{
"cell_type": "code",
- "execution_count": 45,
+ "execution_count": 20,
"metadata": {
"collapsed": false
},
@@ -8000,7 +8812,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -8020,7 +8832,7 @@
"cell_type": "markdown",
"metadata": {},
"source": [
- "The position was correctly shifted by 3 positions and we give more weight to the likelihood of an overshoot, so this looks correct.\n",
+ "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",
@@ -8052,7 +8864,7 @@
},
{
"cell_type": "code",
- "execution_count": 46,
+ "execution_count": 21,
"metadata": {
"collapsed": false,
"scrolled": false
@@ -8825,7 +9637,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -8855,7 +9667,7 @@
},
{
"cell_type": "code",
- "execution_count": 47,
+ "execution_count": 22,
"metadata": {
"collapsed": false
},
@@ -9627,7 +10439,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -9653,7 +10465,7 @@
},
{
"cell_type": "code",
- "execution_count": 48,
+ "execution_count": 23,
"metadata": {
"collapsed": false
},
@@ -10425,7 +11237,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -10451,16 +11263,782 @@
},
{
"cell_type": "code",
- "execution_count": 23,
+ "execution_count": 24,
"metadata": {
"collapsed": false
},
"outputs": [
{
"data": {
- "image/png": "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\nXKS8vBy5ubno16+fwXj//v2RlZVlVMjp06exfft2hIWF1WuRREQkP9Z3IiLlqIQQ4mETdDodPD09\nsW/fPoSGhurH4+PjsXbtWuTn5z/wWI1Gg4MHD+LOnTt4/fXXsXTp0mpzSkpK9H8+depUfR4DEVGj\n1qlTJ/2fnZ2dzbgSQ6zvyrpk54q4Q3X7BMe6mte9BR699ZuiGURUs9pqu6K7i6xfvx6HDh3C2rVr\nsWXLFl6vR0TUTLC+ExE9XK3XZLu4uMDKygpFRUUG40VFRXB3d3/osZ6engCAzp07o6KiAhMmTMC7\n774LSaq5tw8KCjJq0Tk5OXWaLydzZps731KzzZ1vqdnmzpcz+94zuo0J67uy2ZnnrwJQ9ky2k5MT\ngvzbN/h+mtPfe1PJNne+pWbLmV9bba/1TLaNjQ0CAwOh1WoNxrVaLTQajdELqays1H8REZH5sb4T\nESnHqN1FYmJiMHbsWAQHB0Oj0SA1NRU6nQ5RUVEAgNjYWGRnZ2Pnzp0AgDVr1sDOzg7dunWDra0t\nsrOzERcXh+HDh8PGxka5R0NERHXC+k5EpAyjmuwRI0bgypUrSEhIgE6ng7+/P7Zt26bfQ7WwsBAF\nBQV/3qm1NRITE3H69GkIIdChQwdMmTIF0dHRyjwKIiKqF9Z3IiJlGNVkA0BUVJT+zMb90tLSDL4P\nDw9HeHh4w1ZGREQmwfpORCQ/o5tsUsaZ4uvQ3agwev51O1cAVW+oMY6HgzV8XZzqvDYiIiIiqh82\n2Wamu1GBKfuK63Gk8e9YX/ycC3xd6hFBRERERPWi6D7ZRERERESWiE02EREREZHM2GQTEREREcmM\nTTYRERERkczYZBMRERERyYxNNhERERGRzNhkExERERHJjE02EREREZHM2GQTEREREcmMTTYRERER\nkczYZBMRERERyczoJjslJQU+Pj5Qq9UICgpCZmbmA+fu3bsXQ4cORdu2beHg4ICAgACkpaXJsmAi\nIpIX6zsRkfyMarLT09MRHR2NWbNm4fDhw+jZsycGDhyIX375pcb5WVlZeOKJJ7BhwwYcO3YMb7zx\nBiZOnIh169bJungiImoY1nciImVYGzMpKSkJkZGRiIyMBAAkJydj+/btSE1NRUJCQrX5sbGxBt9H\nRUUhIyMDGzZsQHh4uAzLJiIiObC+ExEpo9Yz2eXl5cjNzUW/fv0Mxvv374+srCyjg0pLS9G6deu6\n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\nuH37tqIZL730Eo4ePYojR47ov4KCgjBq1CgcOXIENjY2iubf79atWzh+/Dg8PDwUz9JoNPpr1qqc\nOHGi2sdWKy0tLQ12dnYIDw83Webvv/8OSTIsTZIkmWyLpypqtRpubm64evUqduzYgaFDh5o035Kw\nvjee+m6K2g6wvrO+/6lZ13dFWneFpKenixYtWohly5aJ/Px8MXXqVOHk5CQuXLigeHZZWZk4fPiw\nOHTokLC3txfx8fHi8OHDJsmePHmyaNmypcjIyBCFhYX6r7KyMsWzhRDivffeE/v37xfnzp0TR48e\nFe+9956wsrISO3bsMEn+vUz5cuLbb78t9u7dKwoKCsSBAwfE4MGDhbOzs0l+5tnZ2cLW1lYkJCSI\n06dPi/Xr1wtnZ2eRmpqqePa9Hn/88Wo7PigtIiJCtGvXTmzZskWcO3dObNy4UTz66KPinXfeMUn+\njh07xLZt20RBQYH48ccfxZNPPil69uwpKioqTJJvqVjfTV/fG1NtF4L1nfVdeaau702qyRZCiNTU\nVNGxY0dhZ2cngoKCRGZmpkly9+zZo38p696v1157TfHsmnIlSRIffvih4tlC/PE/hbe3t7CzsxNu\nbm6iX79+QqvVmiT7fr169TJZEQ4PDxeenp6iRYsWwsvLS7zyyisiPz/fJNlCCLF161YREBAg1Gq1\n8PPzE59//rnJsoUQIiMjQ0iSJHJyckyaW1ZWJqZPny68vb2Fvb298PX1FbNmzRK3b982Sf769euF\nr6+vsLOzE23bthVTp04VpaWlJsm2dKzvpq3vjam2C8H6bkqs76ap7/xYdSIiIiIimTWZa7KJiIiI\niJoKNtlERERERDJjk01EREREJDM22UREREREMmOTTUREREQkMzbZREREREQyY5NNRERERCQzNtlE\nRERERDL7f1wtQxr5WlguAAAAAElFTkSuQmCC\n",
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\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 = $('');\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",
+ " 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",
+ " this.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",
+ " '');\n",
+ " var titletext = $(\n",
+ " '');\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 = $('');\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 = $('');\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 rubberband = $('');\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);\n",
+ " canvas.attr('height', height);\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 = $('')\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 = $('');\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 = $('');\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 = $('');\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 = $('');\n",
+ "\n",
+ " var fmt_picker = $('');\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",
+ " '', {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 = $('');\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'];\n",
+ " var y0 = fig.canvas.height - msg['y0'];\n",
+ " var x1 = msg['x1'];\n",
+ " var y1 = fig.canvas.height - msg['y1'];\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;\n",
+ " var y = canvas_pos.y;\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(\"\");\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",
+ " 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('
');\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 dataURL = this.canvas.toDataURL();\n",
+ " this.cell_info[1]['text/html'] = '
';\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 = $('')\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 = $('');\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 = $('');\n",
+ " nav_element.append(status_bar);\n",
+ " this.message = status_bar[0];\n",
+ "\n",
+ " // Add the close button to the window.\n",
+ " var buttongrp = $('');\n",
+ " var 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= 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": [
- ""
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ],
+ "text/plain": [
+ ""
]
},
"metadata": {},
@@ -10484,7 +12062,7 @@
},
{
"cell_type": "code",
- "execution_count": 24,
+ "execution_count": 25,
"metadata": {
"collapsed": false,
"scrolled": true
@@ -10492,9 +12070,775 @@
"outputs": [
{
"data": {
- "image/png": "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\n",
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\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 = $('');\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",
+ " 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",
+ " this.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",
+ " '');\n",
+ " var titletext = $(\n",
+ " '');\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 = $('');\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 = $('');\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 rubberband = $('');\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);\n",
+ " canvas.attr('height', height);\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 = $('')\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 = $('');\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 = $('');\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 = $('');\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 = $('');\n",
+ "\n",
+ " var fmt_picker = $('');\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",
+ " '', {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 = $('');\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'];\n",
+ " var y0 = fig.canvas.height - msg['y0'];\n",
+ " var x1 = msg['x1'];\n",
+ " var y1 = fig.canvas.height - msg['y1'];\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;\n",
+ " var y = canvas_pos.y;\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(\"\");\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",
+ " 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('
');\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 dataURL = this.canvas.toDataURL();\n",
+ " this.cell_info[1]['text/html'] = '
';\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 = $('')\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 = $('');\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 = $('');\n",
+ " nav_element.append(status_bar);\n",
+ " this.message = status_bar[0];\n",
+ "\n",
+ " // Add the close button to the window.\n",
+ " var buttongrp = $('');\n",
+ " var 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= 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": [
- ""
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ],
+ "text/plain": [
+ ""
]
},
"metadata": {},
@@ -10555,8 +12899,826 @@
"\n",
"Algorithms in this form are sometimes called *predictor correctors*. We make a prediction, then correct them.\n",
"\n",
- "Finally, for those viewing this in a Jupyter Notebook or on the web, here is an animation of that algorithm.\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": 26,
+ "metadata": {
+ "collapsed": false,
+ "scrolled": true
+ },
+ "outputs": [
+ {
+ "data": {
+ "application/javascript": [
+ "/* Put everything inside the global mpl namespace */\n",
+ "window.mpl = {};\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 = $('');\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",
+ " 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",
+ " this.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",
+ " '');\n",
+ " var titletext = $(\n",
+ " '');\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 = $('');\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 = $('');\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 rubberband = $('');\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);\n",
+ " canvas.attr('height', height);\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 = $('')\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 = $('');\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 = $('');\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 = $('');\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 = $('');\n",
+ "\n",
+ " var fmt_picker = $('');\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",
+ " '', {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 = $('');\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'];\n",
+ " var y0 = fig.canvas.height - msg['y0'];\n",
+ " var x1 = msg['x1'];\n",
+ " var y1 = fig.canvas.height - msg['y1'];\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;\n",
+ " var y = canvas_pos.y;\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(\"\");\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",
+ " 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('
');\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 dataURL = this.canvas.toDataURL();\n",
+ " this.cell_info[1]['text/html'] = '
';\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 = $('')\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 = $('');\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 = $('');\n",
+ " nav_element.append(status_bar);\n",
+ " this.message = status_bar[0];\n",
+ "\n",
+ " // Add the close button to the window.\n",
+ " var buttongrp = $('');\n",
+ " var 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= 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": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ },
+ {
+ "data": {
+ "text/html": [
+ "
"
+ ],
+ "text/plain": [
+ ""
+ ]
+ },
+ "metadata": {},
+ "output_type": "display_data"
+ }
+ ],
+ "source": [
+ "def discrete_bayes_sim(pos, kernel, zs, z_prob_correct, sleep=0.4):\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",
+ " bp.bar_plot(hallway, c='k')\n",
+ " bp.bar_plot(prior, ylim=(0,1.0), c='#ff8015')\n",
+ " plt.axvline(i % N + 0.4, 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",
+ " bp.bar_plot(hallway, c='k')\n",
+ " bp.bar_plot(pos, ylim=(0,1.0))\n",
+ " plt.axvline(i % 10 + 0.4, lw=5)\n",
+ " fig.canvas.draw()\n",
+ " time.sleep(sleep)\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, sleep=.4)"
]
},
{
@@ -10567,12 +13729,12 @@
"\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 5 correct measurements:"
+ "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": 50,
+ "execution_count": 27,
"metadata": {
"collapsed": false
},
@@ -11344,7 +14506,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -11359,25 +14521,20 @@
"kernel = (.1, .8, .1)\n",
"prior = np.array([.1] * 10)\n",
"measurements = [1, 0, 1, 0, 0]\n",
- "\n",
- "for m in measurements:\n",
- " likelihood = lh_hallway(hallway, z=m, z_prob=.75)\n",
- " posterior = update(likelihood, prior)\n",
- " prior = predict(posterior, 1, kernel)\n",
- "with interactive_plot():\n",
- " bp.bar_plot(posterior, ylim=(0, .4))"
+ "z_prob = 0.75\n",
+ "discrete_bayes_sim(prior, kernel, measurements, z_prob, sleep=.35)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
- "We have identified the likely cases of having started at position 0 or 5, because we saw the following sequence of doors and walls 1,0,1,0,0. Now I inject a bad measurement:"
+ "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": 51,
+ "execution_count": 28,
"metadata": {
"collapsed": false
},
@@ -12149,7 +15306,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -12160,10 +15317,8 @@
}
],
"source": [
- "likelihood = lh_hallway(hallway, z=0, z_prob=.75)\n",
- "posterior = update(likelihood, prior)\n",
- "with interactive_plot():\n",
- " bp.bar_plot(posterior, ylim=(0, .4))"
+ "measurements = [1, 0, 1, 0, 0, 0]\n",
+ "discrete_bayes_sim(prior, kernel, measurements, z_prob, sleep=.25)"
]
},
{
@@ -12175,7 +15330,7 @@
},
{
"cell_type": "code",
- "execution_count": 55,
+ "execution_count": 29,
"metadata": {
"collapsed": false
},
@@ -12947,7 +16102,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -12966,7 +16121,8 @@
" posterior = update(likelihood, prior)\n",
" prior = predict(posterior, 1, kernel)\n",
" plt.subplot(3, 2, i+1)\n",
- " bp.bar_plot(posterior, ylim=(0, .4), title='step {}'.format(i+1))"
+ " bp.bar_plot(posterior, ylim=(0, .4), title='step {}'.format(i+1))\n",
+ " plt.tight_layout()"
]
},
{
@@ -13021,7 +16177,7 @@
},
{
"cell_type": "code",
- "execution_count": 56,
+ "execution_count": 30,
"metadata": {
"collapsed": false
},
@@ -13072,7 +16228,7 @@
},
{
"cell_type": "code",
- "execution_count": 57,
+ "execution_count": 31,
"metadata": {
"collapsed": false
},
@@ -13124,7 +16280,7 @@
},
{
"cell_type": "code",
- "execution_count": 59,
+ "execution_count": 32,
"metadata": {
"collapsed": false,
"scrolled": false
@@ -13897,7 +17053,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -13943,7 +17099,7 @@
},
{
"cell_type": "code",
- "execution_count": 60,
+ "execution_count": 33,
"metadata": {
"collapsed": false
},
@@ -14715,7 +17871,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -14760,7 +17916,7 @@
},
{
"cell_type": "code",
- "execution_count": 69,
+ "execution_count": 34,
"metadata": {
"collapsed": false,
"scrolled": false
@@ -15533,7 +18689,7 @@
{
"data": {
"text/html": [
- "
"
+ "
"
],
"text/plain": [
""
@@ -15548,12 +18704,11 @@
" with interactive_plot():\n",
" for i in range (4):\n",
" random.seed(3)\n",
- " plt.subplot(321+i)\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))\n",
- " plt.tight_layout()"
+ " plt.title ('iteration {}'.format(148+i))"
]
},
{
diff --git a/code/book_plots.py b/code/book_plots.py
index 2c36bad..796821e 100644
--- a/code/book_plots.py
+++ b/code/book_plots.py
@@ -47,6 +47,7 @@ def interactive_plot(close=True, fig=None):
if fig is None:
fig = plt.figure()
yield
+ plt.tight_layout()
if close:
end_interactive(fig)